Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
W
wendelin.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
Kirill Smelkov
wendelin.core
Commits
d99b1b41
Commit
d99b1b41
authored
Jun 11, 2021
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
.
parent
37c2e806
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
116 additions
and
51 deletions
+116
-51
wcfs/internal/set/set.go.in
wcfs/internal/set/set.go.in
+9
-0
wcfs/internal/set/zset_i64.go
wcfs/internal/set/zset_i64.go
+9
-0
wcfs/internal/set/zset_oid.go
wcfs/internal/set/zset_oid.go
+9
-0
wcfs/internal/xbtree/δbtail.go
wcfs/internal/xbtree/δbtail.go
+88
-50
wcfs/internal/xbtree/δbtail_test.go
wcfs/internal/xbtree/δbtail_test.go
+1
-1
No files found.
wcfs/internal/set/set.go.in
View file @
d99b1b41
...
@@ -136,6 +136,15 @@ func (a Set) Equal(b Set) bool {
...
@@ -136,6 +136,15 @@ func (a Set) Equal(b Set) bool {
return
true
return
true
}
}
//
Clone
returns
copy
of
the
set
.
func
(
orig
Set
)
Clone
()
Set
{
klon
:=
make
(
Set
,
len
(
orig
))
for
v
:=
range
orig
{
klon
.
Add
(
v
)
}
return
klon
}
//
--------
//
--------
func
(
s
Set
)
SortedElements
()
[]
VALUE
{
func
(
s
Set
)
SortedElements
()
[]
VALUE
{
...
...
wcfs/internal/set/zset_i64.go
View file @
d99b1b41
...
@@ -138,6 +138,15 @@ func (a SetI64) Equal(b SetI64) bool {
...
@@ -138,6 +138,15 @@ func (a SetI64) Equal(b SetI64) bool {
return
true
return
true
}
}
// Clone returns copy of the set.
func
(
orig
SetI64
)
Clone
()
SetI64
{
klon
:=
make
(
SetI64
,
len
(
orig
))
for
v
:=
range
orig
{
klon
.
Add
(
v
)
}
return
klon
}
// --------
// --------
func
(
s
SetI64
)
SortedElements
()
[]
int64
{
func
(
s
SetI64
)
SortedElements
()
[]
int64
{
...
...
wcfs/internal/set/zset_oid.go
View file @
d99b1b41
...
@@ -138,6 +138,15 @@ func (a SetOid) Equal(b SetOid) bool {
...
@@ -138,6 +138,15 @@ func (a SetOid) Equal(b SetOid) bool {
return
true
return
true
}
}
// Clone returns copy of the set.
func
(
orig
SetOid
)
Clone
()
SetOid
{
klon
:=
make
(
SetOid
,
len
(
orig
))
for
v
:=
range
orig
{
klon
.
Add
(
v
)
}
return
klon
}
// --------
// --------
func
(
s
SetOid
)
SortedElements
()
[]
_Oid
{
func
(
s
SetOid
)
SortedElements
()
[]
_Oid
{
...
...
wcfs/internal/xbtree/δbtail.go
View file @
d99b1b41
...
@@ -93,8 +93,6 @@ type ΔBtail struct {
...
@@ -93,8 +93,6 @@ type ΔBtail struct {
// includes all changed objects, not only tracked ones.
// includes all changed objects, not only tracked ones.
δZtail
*
zodb
.
ΔTail
δZtail
*
zodb
.
ΔTail
// XXX vvv keys ∈ tracked -> keys ∈ kadj[tracked] ?
// vδB []ΔB // data with δB changes; Noted only by keys ∈ tracked subset
byRoot
map
[
zodb
.
Oid
]
*
ΔTtail
// {} root -> [] k/v change history; only for keys ∈ tracked subset
byRoot
map
[
zodb
.
Oid
]
*
ΔTtail
// {} root -> [] k/v change history; only for keys ∈ tracked subset
// handle to make connections to access database.
// handle to make connections to access database.
...
@@ -102,17 +100,13 @@ type ΔBtail struct {
...
@@ -102,17 +100,13 @@ type ΔBtail struct {
db
*
zodb
.
DB
// to open connections to load new/old tree|buckets
db
*
zodb
.
DB
// to open connections to load new/old tree|buckets
// set of tracked nodes as of @head state.
// set of tracked nodes as of @head state.
// For this set all vδT are fully computed.
// The set of nodes that were requested to be tracked, but were not yet
// taken into account, is kept in ΔTtail.trackNew & co.
trackSet
PPTreeSubSet
trackSet
PPTreeSubSet
// set of nodes that were requested to be tracked, but for which vδB was not yet rebuilt
// set of trees for which trackNew is non-empty
trackNew
PPTreeSubSet
trackNewRoots
SetOid
}
// ΔB represents a change in BTrees space.
type
ΔB
struct
{
Rev
zodb
.
Tid
ByRoot
map
[
zodb
.
Oid
]
map
[
Key
]
ΔValue
// {} root -> {}(key, δvalue)
}
}
// ΔTtail represent tail of revisional changes to one BTree.
// ΔTtail represent tail of revisional changes to one BTree.
...
@@ -121,6 +115,11 @@ type ΔB struct {
...
@@ -121,6 +115,11 @@ type ΔB struct {
type
ΔTtail
struct
{
type
ΔTtail
struct
{
vδT
[]
ΔTree
// changes to tree keys; rev↑. covers keys ∈ tracked subset
vδT
[]
ΔTree
// changes to tree keys; rev↑. covers keys ∈ tracked subset
// set of nodes that were requested to be tracked in this tree, but for
// which vδT was not yet rebuilt
trackNew
PPTreeSubSet
// XXX + trackNewKeys RangedKeySet
// {}k/v @tail for keys that are changed in (tail, head].
// {}k/v @tail for keys that are changed in (tail, head].
KVAtTail
map
[
Key
]
Value
// XXX not needed since vδT has ΔValue ?
KVAtTail
map
[
Key
]
Value
// XXX not needed since vδT has ΔValue ?
...
@@ -128,6 +127,13 @@ type ΔTtail struct {
...
@@ -128,6 +127,13 @@ type ΔTtail struct {
lastRevOf
map
[
Key
]
zodb
.
Tid
// {} key -> last
lastRevOf
map
[
Key
]
zodb
.
Tid
// {} key -> last
}
}
// ΔB represents a change in BTrees space.
type
ΔB
struct
{
Rev
zodb
.
Tid
ByRoot
map
[
zodb
.
Oid
]
map
[
Key
]
ΔValue
// {} root -> {}(key, δvalue)
}
// ΔTree describes changes to one BTree in one revision.
// ΔTree describes changes to one BTree in one revision.
// XXX -> ΔT ?
// XXX -> ΔT ?
type
ΔTree
struct
{
type
ΔTree
struct
{
...
@@ -148,8 +154,17 @@ func NewΔBtail(at0 zodb.Tid, db *zodb.DB) *ΔBtail {
...
@@ -148,8 +154,17 @@ func NewΔBtail(at0 zodb.Tid, db *zodb.DB) *ΔBtail {
δZtail
:
zodb
.
NewΔTail
(
at0
),
δZtail
:
zodb
.
NewΔTail
(
at0
),
byRoot
:
map
[
zodb
.
Oid
]
*
ΔTtail
{},
byRoot
:
map
[
zodb
.
Oid
]
*
ΔTtail
{},
trackSet
:
PPTreeSubSet
{},
trackSet
:
PPTreeSubSet
{},
trackNew
:
PPTreeSubSet
{},
trackNewRoots
:
SetOid
{},
db
:
db
,
db
:
db
,
}
}
// newΔTtail creates new empty ΔTtail object.
func
newΔTtail
()
*
ΔTtail
{
return
&
ΔTtail
{
trackNew
:
PPTreeSubSet
{},
KVAtTail
:
make
(
map
[
Key
]
Value
),
lastRevOf
:
make
(
map
[
Key
]
zodb
.
Tid
),
}
}
}
}
...
@@ -163,15 +178,16 @@ func (orig *ΔBtail) clone() *ΔBtail {
...
@@ -163,15 +178,16 @@ func (orig *ΔBtail) clone() *ΔBtail {
klon
.
δZtail
.
Append
(
δZ
.
Rev
,
δZ
.
Changev
)
klon
.
δZtail
.
Append
(
δZ
.
Rev
,
δZ
.
Changev
)
}
}
// trackSet, trackNew
// trackSet, trackNew
Roots
klon
.
trackSet
=
orig
.
trackSet
.
Clone
()
klon
.
trackSet
=
orig
.
trackSet
.
Clone
()
klon
.
trackNew
=
orig
.
trackNew
.
Clone
()
klon
.
trackNew
Roots
=
orig
.
trackNewRoots
.
Clone
()
// byRoot
// byRoot
klon
.
byRoot
=
make
(
map
[
zodb
.
Oid
]
*
ΔTtail
,
len
(
orig
.
byRoot
))
klon
.
byRoot
=
make
(
map
[
zodb
.
Oid
]
*
ΔTtail
,
len
(
orig
.
byRoot
))
for
root
,
origΔTtail
:=
range
orig
.
byRoot
{
for
root
,
origΔTtail
:=
range
orig
.
byRoot
{
klonΔTtail
:=
&
ΔTtail
{}
klonΔTtail
:=
&
ΔTtail
{}
klonΔTtail
.
vδT
=
append
(
klonΔTtail
.
vδT
,
origΔTtail
.
vδT
...
)
klonΔTtail
.
vδT
=
append
(
klonΔTtail
.
vδT
,
origΔTtail
.
vδT
...
)
klonΔTtail
.
trackNew
=
origΔTtail
.
trackNew
.
Clone
()
klonΔTtail
.
KVAtTail
=
make
(
map
[
Key
]
Value
,
len
(
origΔTtail
.
KVAtTail
))
klonΔTtail
.
KVAtTail
=
make
(
map
[
Key
]
Value
,
len
(
origΔTtail
.
KVAtTail
))
for
k
,
v
:=
range
origΔTtail
.
KVAtTail
{
for
k
,
v
:=
range
origΔTtail
.
KVAtTail
{
klonΔTtail
.
KVAtTail
[
k
]
=
v
klonΔTtail
.
KVAtTail
[
k
]
=
v
...
@@ -186,14 +202,6 @@ func (orig *ΔBtail) clone() *ΔBtail {
...
@@ -186,14 +202,6 @@ func (orig *ΔBtail) clone() *ΔBtail {
return
klon
return
klon
}
}
// newΔTtail creates new empty ΔTtail object.
func
newΔTtail
()
*
ΔTtail
{
return
&
ΔTtail
{
KVAtTail
:
make
(
map
[
Key
]
Value
),
lastRevOf
:
make
(
map
[
Key
]
zodb
.
Tid
),
}
}
// (tail, head] coverage
// (tail, head] coverage
func
(
δBtail
*
ΔBtail
)
Head
()
zodb
.
Tid
{
return
δBtail
.
δZtail
.
Head
()
}
func
(
δBtail
*
ΔBtail
)
Head
()
zodb
.
Tid
{
return
δBtail
.
δZtail
.
Head
()
}
func
(
δBtail
*
ΔBtail
)
Tail
()
zodb
.
Tid
{
return
δBtail
.
δZtail
.
Tail
()
}
func
(
δBtail
*
ΔBtail
)
Tail
()
zodb
.
Tid
{
return
δBtail
.
δZtail
.
Tail
()
}
...
@@ -231,38 +239,59 @@ func (δBtail *ΔBtail) track(key Key, keyPresent bool, path []zodb.Oid) error {
...
@@ -231,38 +239,59 @@ func (δBtail *ΔBtail) track(key Key, keyPresent bool, path []zodb.Oid) error {
// XXX locking
// XXX locking
root
:=
path
[
0
]
root
:=
path
[
0
]
δBtail
.
trackNew
.
AddPath
(
path
)
δTtail
,
ok
:=
δBtail
.
byRoot
[
root
]
// track is track of path[-1] (i.e. leaf)
// XXX hack - until rebuild is implemented
if
XXX_killWhenRebuildWorks
{
_
,
ok
:=
δBtail
.
byRoot
[
root
]
if
!
ok
{
if
!
ok
{
δBtail
.
byRoot
[
root
]
=
newΔTtail
()
δTtail
=
newΔTtail
()
δBtail
.
byRoot
[
root
]
=
δTtail
}
}
}
δBtail
.
trackNewRoots
.
Add
(
root
)
δTtail
.
trackNew
.
AddPath
(
path
)
// track is track of path[-1] (i.e. leaf)
// XXX update diff XXX here? or as separate step?
// XXX update diff XXX here? or as separate step?
// XXX update lastRevOf
// XXX update lastRevOf
return
nil
return
nil
}
}
// rebuild rebuilds ΔBtail taking trackNew requests into account.
// rebuildAll rebuilds ΔBtail taking all trackNew requests into account.
func
(
δBtail
*
ΔBtail
)
rebuildAll
()
(
err
error
)
{
defer
xerr
.
Context
(
&
err
,
"ΔBtail rebuildAll"
)
// XXX locking
trackNewRoots
:=
δBtail
.
trackNewRoots
for
root
:=
range
trackNewRoots
{
δTtail
:=
δBtail
.
byRoot
[
root
]
// must be there
δtrackSet
,
err
:=
δTtail
.
rebuild
()
if
err
!=
nil
{
return
err
}
δBtail
.
trackSet
.
UnionInplace
(
δtrackSet
)
}
δBtail
.
trackNewRoots
=
SetOid
{}
return
nil
}
// rebuild rebuilds ΔTtail taking trackNew requests into account.
//
// It returns set of nodes that must be added to ΔBtail.trackSet to account for
// keys that becomes tracked. Note: this set is potentially wider compared to .trackNew.
// XXX place
// XXX place
func
(
δ
Btail
*
ΔBtail
)
rebuild
()
(
err
error
)
{
func
(
δ
Ttail
*
ΔTtail
)
rebuild
()
(
δtrackSet
PPTreeSubSet
,
err
error
)
{
defer
xerr
.
Context
(
&
err
,
"Δ
B
tail rebuild"
)
defer
xerr
.
Context
(
&
err
,
"Δ
T
tail rebuild"
)
// XXX locking
// XXX locking
tracefΔBtail
(
"
\n
Rebuild @%s .. @%s
\n
"
,
δBtail
.
Tail
(),
δBtail
.
Head
())
tracefΔBtail
(
"
\n
Rebuild @%s .. @%s
\n
"
,
δBtail
.
Tail
(),
δBtail
.
Head
())
tracefΔBtail
(
"trackSet: %v
\n
"
,
δBtail
.
trackSet
)
tracefΔBtail
(
"trackNew: %v
\n
"
,
δTtail
.
trackNew
)
tracefΔBtail
(
"trackNew: %v
\n
"
,
δBtail
.
trackNew
)
trackNew
:=
δ
B
tail
.
trackNew
trackNew
:=
δ
T
tail
.
trackNew
δ
B
tail
.
trackNew
=
PPTreeSubSet
{}
δ
T
tail
.
trackNew
=
PPTreeSubSet
{}
if
len
(
trackNew
)
==
0
{
if
len
(
trackNew
)
==
0
{
return
return
nil
,
nil
}
}
// go backwards and merge vδT <- treediff(lo..hi/trackNew)
// go backwards and merge vδT <- treediff(lo..hi/trackNew)
...
@@ -285,7 +314,6 @@ func (δBtail *ΔBtail) rebuild() (err error) {
...
@@ -285,7 +314,6 @@ func (δBtail *ΔBtail) rebuild() (err error) {
debugfΔBtail
(
"
\n
rebuild @%s <- @%s
\n
"
,
atPrev
,
δZ
.
Rev
)
debugfΔBtail
(
"
\n
rebuild @%s <- @%s
\n
"
,
atPrev
,
δZ
.
Rev
)
debugfΔBtail
(
" δZ:
\t
%v
\n
"
,
δZ
.
Changev
)
debugfΔBtail
(
" δZ:
\t
%v
\n
"
,
δZ
.
Changev
)
debugfΔBtail
(
" trackNew: %v
\n
"
,
trackNew
)
debugfΔBtail
(
" trackNew: %v
\n
"
,
trackNew
)
debugfΔBtail
(
" trackSet: %v
\n
"
,
δBtail
.
trackSet
)
// XXX needed?
defer
debugfΔBtail
(
"
\n\n
"
)
defer
debugfΔBtail
(
"
\n\n
"
)
// XXX len(δtopsByRoot) == 0 -> skip
// XXX len(δtopsByRoot) == 0 -> skip
...
@@ -297,18 +325,18 @@ func (δBtail *ΔBtail) rebuild() (err error) {
...
@@ -297,18 +325,18 @@ func (δBtail *ΔBtail) rebuild() (err error) {
zconnPrev
,
err
:=
δBtail
.
db
.
Open
(
ctx
,
&
zodb
.
ConnOptions
{
At
:
atPrev
})
zconnPrev
,
err
:=
δBtail
.
db
.
Open
(
ctx
,
&
zodb
.
ConnOptions
{
At
:
atPrev
})
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
nil
,
err
}
}
zconnCurr
,
err
:=
δBtail
.
db
.
Open
(
ctx
,
&
zodb
.
ConnOptions
{
At
:
δZ
.
Rev
})
zconnCurr
,
err
:=
δBtail
.
db
.
Open
(
ctx
,
&
zodb
.
ConnOptions
{
At
:
δZ
.
Rev
})
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
nil
,
err
}
}
for
root
,
δtops
:=
range
δtopsByRoot
{
for
root
,
δtops
:=
range
δtopsByRoot
{
// diff backwards curr -> prev
// diff backwards curr -> prev
δT
,
δtrack
,
δtkeycov
,
err
:=
treediff
(
ctx
,
root
,
δtops
,
δZTC
,
trackNew
,
zconnCurr
,
zconnPrev
)
δT
,
δtrack
,
δtkeycov
,
err
:=
treediff
(
ctx
,
root
,
δtops
,
δZTC
,
trackNew
,
zconnCurr
,
zconnPrev
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
nil
,
err
}
}
// FIXME use δtkeycov to recompute track coverage
// FIXME use δtkeycov to recompute track coverage
...
@@ -323,12 +351,14 @@ func (δBtail *ΔBtail) rebuild() (err error) {
...
@@ -323,12 +351,14 @@ func (δBtail *ΔBtail) rebuild() (err error) {
continue
continue
}
}
/* XXX kill
δTtail, ok := δBtail.byRoot[root]
δTtail, ok := δBtail.byRoot[root]
if !ok {
if !ok {
// this root was not tracked before -> create δTtail for it with empty changes
// this root was not tracked before -> create δTtail for it with empty changes
δTtail = newΔTtail()
δTtail = newΔTtail()
δBtail.byRoot[root] = δTtail
δBtail.byRoot[root] = δTtail
}
}
*/
// δTtail.vδT <- merge δT*
// δTtail.vδT <- merge δT*
l
:=
len
(
δTtail
.
vδT
)
l
:=
len
(
δTtail
.
vδT
)
...
@@ -363,6 +393,7 @@ func (δBtail *ΔBtail) rebuild() (err error) {
...
@@ -363,6 +393,7 @@ func (δBtail *ΔBtail) rebuild() (err error) {
}
}
}
}
/*
// trackNew was adjusted to correspond to @tail potentially growing its key coverage.
// trackNew was adjusted to correspond to @tail potentially growing its key coverage.
// Remap it back to @head and merge to .trackSet
// Remap it back to @head and merge to .trackSet
for _, δtrack := range vδtrack {
for _, δtrack := range vδtrack {
...
@@ -370,8 +401,8 @@ func (δBtail *ΔBtail) rebuild() (err error) {
...
@@ -370,8 +401,8 @@ func (δBtail *ΔBtail) rebuild() (err error) {
trackNew.ApplyΔ(δtrack)
trackNew.ApplyΔ(δtrack)
}
}
δBtail.trackSet.UnionInplace(trackNew)
δBtail.trackSet.UnionInplace(trackNew)
*/
return
nil
return
δtrackSet
,
nil
}
}
// Update updates δB with per-object level ZODB changes.
// Update updates δB with per-object level ZODB changes.
...
@@ -398,18 +429,25 @@ func (δBtail *ΔBtail) _Update(δZ *zodb.EventCommit) (_ ΔB, δTKeyCov _ΔTrac
...
@@ -398,18 +429,25 @@ func (δBtail *ΔBtail) _Update(δZ *zodb.EventCommit) (_ ΔB, δTKeyCov _ΔTrac
tracefΔBtail
(
"
\n
Update @%s -> @%s δZ: %v
\n
"
,
δBtail
.
Head
(),
δZ
.
Tid
,
δZ
.
Changev
)
tracefΔBtail
(
"
\n
Update @%s -> @%s δZ: %v
\n
"
,
δBtail
.
Head
(),
δZ
.
Tid
,
δZ
.
Changev
)
tracefΔBtail
(
"trackSet: %v
\n
"
,
δBtail
.
trackSet
)
tracefΔBtail
(
"trackSet: %v
\n
"
,
δBtail
.
trackSet
)
tracefΔBtail
(
"trackNew: %v
\n
"
,
δBtail
.
trackNew
)
for
_
,
root
:=
range
δBtail
.
trackNewRoots
.
SortedElements
()
{
δTtail
:=
δBtail
.
byRoot
[
root
]
tracefΔBtail
(
"[%s].trackNew: %v
\n
"
,
δTtail
.
trackNew
)
}
δTKeyCov
=
_ΔTrackKeyCov
{
ByRoot
:
make
(
map
[
zodb
.
Oid
]
*
RangedKeySet
)}
δTKeyCov
=
_ΔTrackKeyCov
{
ByRoot
:
make
(
map
[
zodb
.
Oid
]
*
RangedKeySet
)}
if
XXX_killWhenRebuildWorks
{
if
XXX_killWhenRebuildWorks
{
// XXX hack - until vvv is reenabled
// XXX hack - until vvv is reenabled
δBtail
.
trackSet
.
UnionInplace
(
δBtail
.
trackNew
)
for
root
:=
range
δBtail
.
trackNewRoots
{
δBtail
.
trackNew
=
PPTreeSubSet
{}
δTtail
:=
δBtail
.
byRoot
[
root
]
// must succeed
δBtail
.
trackSet
.
UnionInplace
(
δTtail
.
trackNew
)
δTtail
.
trackNew
=
PPTreeSubSet
{}
}
δBtail
.
trackNewRoots
=
SetOid
{}
}
else
{
}
else
{
// XXX reenable (currently breaks wcfs tests)
// XXX reenable (currently breaks wcfs tests)
// update .trackSet and vδB from .trackNew
// update .trackSet and vδB from .trackNew
err
=
δBtail
.
rebuild
()
err
=
δBtail
.
rebuild
All
()
if
err
!=
nil
{
if
err
!=
nil
{
return
ΔB
{},
δTKeyCov
,
err
return
ΔB
{},
δTKeyCov
,
err
}
}
...
...
wcfs/internal/xbtree/δbtail_test.go
View file @
d99b1b41
...
@@ -911,7 +911,7 @@ func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
...
@@ -911,7 +911,7 @@ func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
// t1 := t2.prev
// t1 := t2.prev
// t0 := t1.prev
// t0 := t1.prev
t
.
Run
(
fmt
.
Sprintf
(
"rebuild/%s→%s"
,
t0
.
tree
,
t1
.
tree
),
func
(
t
*
testing
.
T
)
{
t
.
Run
(
fmt
.
Sprintf
(
"rebuild/%s→%s"
,
t0
.
tree
,
t1
.
tree
),
func
(
t
*
testing
.
T
)
{
t
.
Skip
(
"TODO"
)
// FIXME rebuild is currently broken
//
t.Skip("TODO") // FIXME rebuild is currently broken
tAllKeys
:=
allTestKeys
(
t0
,
t1
,
t2
)
tAllKeys
:=
allTestKeys
(
t0
,
t1
,
t2
)
tAllKeyv
:=
tAllKeys
.
SortedElements
()
tAllKeyv
:=
tAllKeys
.
SortedElements
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment