/
sorted_container_iteration.jl
1189 lines (918 loc) · 37.6 KB
/
sorted_container_iteration.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
## Functions in this file implement many kinds of iterations over
## SortedDict, SortedMultiDict, and SortedSet.
## The iteration state is a "semitoken".
## A "token" is a (container,index_into_container) 2-tuple,
## while semitokens are the second part, index_into_container.
# From tokens.jl:
# abstract type AbstractSemiToken end
# struct IntSemiToken <: AbstractSemiToken
# address::Int
# end
const SDMContainer = Union{SortedDict, SortedMultiDict}
const SortedContainer = Union{SDMContainer, SortedSet}
const Token = Tuple{SortedContainer, IntSemiToken}
const SortedDictToken = Tuple{SortedDict, IntSemiToken}
const SortedMultiDictToken = Tuple{SortedMultiDict, IntSemiToken}
const SDMToken = Tuple{SDMContainer, IntSemiToken}
const SortedSetToken = Tuple{SortedSet, IntSemiToken}
Base.:(==)(t1::Token, t2::Token) = (t1[1] === t2[1] && t1[2] == t2[2])
"""
Base.firstindex(m::SortedContainer)
Return the semitoken of
the first entry of the container `m`, or the past-end semitoken
if the container is empty. This function was called
`startof` (now deprecated) in previous versions of the package.
Time: O(log *n*)
"""
Base.firstindex(m::SortedContainer) = IntSemiToken(beginloc(m.bt))
"""
token_firstindex(m::SortedContainer)
Return the token of
the first entry of the sorted container `m`, or the past-end token
if the container is empty. Time: O(log *n*)
"""
token_firstindex(m::SortedContainer) = (m, firstindex(m))
"""
Base.lastindex(m::SortedContainer)
Return the semitoken of the
last entry of the sorted container `m`, or the before-start semitoken
if the container is empty. This function was called `endof` (now
deprecated) in previous versions of the package.
Time: O(log *n*)
"""
Base.lastindex(m::SortedContainer) = IntSemiToken(endloc(m.bt))
"""
token_lastindex(m::SortedContainer)
Return the token of the
last entry of the sorted container `m`, or the before-start semitoken
if the container is empty. Time: O(log *n*)
"""
token_lastindex(m::SortedContainer) = (m, lastindex(m))
"""
pastendsemitoken(m::SortedContainer)
Return the semitoken
of the entry that is one past the end of the sorted container `m`.
Time: O(1)
"""
pastendsemitoken(::SortedContainer) = IntSemiToken(2)
"""
pastendtoken(m::SortedContainer)
Return the token
of the entry that is one past the end of the sorted container `m`.
Time: O(1)
"""
pastendtoken(m::SortedContainer) = (m, pastendsemitoken(m))
"""
beforestartsemitoken(m::SortedContainer)
Return the semitoken
of the entry that is one before the beginning of the
sorted container `m`. Time: O(1)
"""
beforestartsemitoken(::SortedContainer) = IntSemiToken(1)
"""
beforestarttoken(m::SortedContainer)
Return the token
of the entry that is one before the beginning of the
sorted container `m`. Time: O(1)
"""
beforestarttoken(m::SortedContainer) = (m, beforestartsemitoken(m))
"""
Base.delete!(token::Token)
Delete the item indexed by the token from a sorted container.
A `BoundsError` is thrown if the token is invalid.
Prepending with
`@inbounds` may elide the correctness check and will result
in undefined behavior if the token is invalid.
Time: O(log *n*).
"""
Base.@propagate_inbounds function Base.delete!(ii::Token)
Base.@boundscheck has_data(ii)
delete!(ii[1].bt, ii[2].address)
end
"""
advance(token::Token)
advance((m,st))
Return the semitoken of the item in a sorted container
one after the given token. A `BoundsError` is thrown if the token is
the past-end token.
Prepending with
`@inbounds` may elide the correctness check and will result
in undefined behavior if the token is invalid or
points to the past-end token.
The second form creates the token
in-place as a tuple of a container `m` and a semitoken `st`.
Time: O(log *n*)
"""
Base.@propagate_inbounds function advance(ii::Token)
Base.@boundscheck not_pastend(ii)
IntSemiToken(nextloc0(ii[1].bt, ii[2].address))
end
"""
regress(token::Token)
regress((m,st))
Return the semitoken of the item in a sorted container
one before the given token. A `BoundsError` is thrown if the token is
the before-start token.
Prepending with
`@inbounds` may elide the correctness check and will result
in undefined behavior if the token is invalid or
points to the before-start token.
The second form creates the token
in-place as a tuple of a container `m` and a semitoken `st`.
Time: O(log *n*)
"""
Base.@propagate_inbounds function regress(ii::Token)
Base.@boundscheck not_beforestart(ii)
IntSemiToken(prevloc0(ii[1].bt, ii[2].address))
end
"""
status(token::Token)
status((m, st))
Determine the status of a token. Return values are:
- 0 = invalid token
- 1 = valid and points to live data
- 2 = before-start token
- 3 = past-end token
The second form creates the token
in-place as a tuple of a sorted container `m` and a semitoken `st`.
Time: O(1)
"""
status(ii::Token) =
!(ii[2].address in ii[1].bt.useddatacells) ? 0 :
ii[2].address == 1 ? 2 :
ii[2].address == 2 ? 3 : 1
"""
compare(m::SortedContainer, s::IntSemiToken, t::IntSemiToken)
Determine the relative position according to the
sort order of the data items indexed
by tokens `(m,s)` and `(m,t)`. Return:
- `-1`if`(m,s)` precedes `(m,t)`,
- `0` if `s == t`
- `1` if `(m,s)`succeeds `(m,t)`.
The relative positions are determined
from the tree topology without any key
comparisons. Time: O(log *n*)
"""
compare(m::SortedContainer, s::IntSemiToken, t::IntSemiToken) =
compareInd(m.bt, s.address, t.address)
"""
ordtype(sc::SortedSet)
ordtype(sc::SortedDict)
ordtype(sc::SortedMultiDict)
Return the order type for a sorted container.
This function may also be applied to the type itself.
Time: O(1)
"""
@inline ordtype(::SortedSet{K,Ord}) where {K, Ord} = Ord
@inline ordtype(::Type{SortedSet{K,Ord}}) where {K, Ord} = Ord
@inline ordtype(::SortedDict{K,D,Ord}) where {K, D, Ord} = Ord
@inline ordtype(::Type{SortedDict{K,D,Ord}}) where {K, D, Ord} = Ord
@inline ordtype(::SortedMultiDict{K,D, Ord}) where {K, D, Ord} = Ord
@inline ordtype(::Type{SortedMultiDict{K,D, Ord}}) where {K, D, Ord} = Ord
"""
orderobject(sc::SortedContainer)
Return the order object used to construct the container. Time: O(1)
"""
@inline orderobject(m::SortedContainer) = m.bt.ord
"""
deref(token::Token)
deref((m,st))
Return the data item indexed by the token. If
the container is a `SortedSet`, then this is a key in the set.
If the container is a `SortedDict` or `SortedMultiDict`, then
this is a key=>value pair. It is a `BoundsError` if the token
is invalid or is the before-start or past-end token.
Prepending with
`@inbounds` may elide the correctness check and will result
in undefined behavior if the token is invalid or
points to the before-start or past-end token.
The
second form creates the token in-place as a tuple of a
sorted container `m`
and a semitoken `st`. Time: O(1)
"""
function deref(ii::Token)
error("This is not reachable because the specialized methods below will always be selected but is here to make the doc work")
end
Base.@propagate_inbounds function deref(ii::SortedDictToken)
Base.@boundscheck has_data(ii)
@inbounds kdrec = ii[1].bt.data[ii[2].address]
return Pair(kdrec.k, kdrec.d)
end
Base.@propagate_inbounds function deref(ii::SortedMultiDictToken)
Base.@boundscheck has_data(ii)
@inbounds kdrec = ii[1].bt.data[ii[2].address]
return Pair(kdrec.k, kdrec.d)
end
Base.@propagate_inbounds function deref(ii::SortedSetToken)
Base.@boundscheck has_data(ii)
@inbounds k = ii[1].bt.data[ii[2].address].k
return k
end
"""
deref_key(token::Token)
deref_key((m,st))
Return the key portion of a data item (a key=>value pair) in a
`SortedDict` or `SortedMultiDict` indexed by the token.
It is a `BoundsError` if the token
is invalid or is the before-start or past-end token.
Prepending with
`@inbounds` may elide the correctness check and will result
in undefined behavior if the token is invalid or
points to the before-start or past-end token.
The
second form creates the token in-place as a tuple of a container `m`
and a semitoken `st`. Time: O(1)
"""
function deref_key(ii::Token)
error("Cannot invoke deref_key on a SortedSet")
end
Base.@propagate_inbounds function deref_key(ii::SDMToken)
Base.@boundscheck has_data(ii)
@inbounds k = ii[1].bt.data[ii[2].address].k
return k
end
"""
deref_value(token::Token)
deref_value((m,st))
Returns the value portion of a data item (a key=>value pair)
in a `SortedDict` or `SortedMultiDict`
indexed by the token.
It is a `BoundsError` if the token
is invalid or is the before-start or past-end token.
Prepending with
`@inbounds` may elide the correctness check and will result
in undefined behavior if the token is invalid or
points to the before-start or past-end token.
The
second form creates the token in-place as a tuple of a container `m`
and a semitoken `st`. Time: O(1)
"""
function deref_value(ii::Token)
error("Cannot invoke deref_key on a SortedSet")
end
Base.@propagate_inbounds function deref_value(ii::SDMToken)
Base.@boundscheck has_data(ii)
@inbounds d = ii[1].bt.data[ii[2].address].d
return d
end
"""
Base.first(sc::SortedContainer)
Return the
first item (a `k=>v` pair for SortedDict and
SortedMultiDict or an element for SortedSet) in `sc` according to the sorted
order in the container. It is a `BoundsError` to call this function on
an empty container. Equivalent to `deref(token_startindex(sc))`. Time: O(log *n*)
"""
Base.first(m::SortedContainer) = deref(token_firstindex(m))
"""
Base.last(sc::SortedContainer)
Return the last item (a `k=>v` pair for SortedDict and
SortedMultiDict or a key for SortedSet) in `sc` according to the sorted
order in the container. It is a `BoundsError` to call this function on an
empty container. Equivalent to `deref(token_lastindex(sc))`. Time: O(log *n*)
"""
Base.last(m::SortedContainer) = deref(token_lastindex(m))
"""
Base.getindex(m::SortedDict, st::IntSemiToken)
Base.getindex(m::SortedMultiDict, st::IntSemiToken)
Retrieve value portion of item from SortedDict or SortedMultiDict
`m` indexed by `st`, a semitoken. Notation `m[st]` appearing in
an expression
is equivalent to [`deref_value(token::Token)`](@ref) where `token=(m,st)`.
It is a `BoundsError` if the token is invalid. Prepending with
`@inbounds` may elide the correctness check and results in undefined
behavior if the token is invalid.
Time: O(1)
"""
Base.@propagate_inbounds function Base.getindex(m::SortedDict,
i::IntSemiToken)
@boundscheck has_data((m,i))
@inbounds d = m.bt.data[i.address].d
return d
end
# Must repeat this to break ambiguity; cannot use SDMContainer.
Base.@propagate_inbounds function Base.getindex(m::SortedMultiDict,
i::IntSemiToken)
@boundscheck has_data((m,i))
@inbounds d = m.bt.data[i.address].d
return d
end
"""
Base.setindex!(m::SortedDict, newvalue, st::IntSemiToken)
Base.setindex!(m::SortedMultiDict, newvalue, st::IntSemiToken)
Set the value portion of item from SortedDict or SortedMultiDict
`m` indexed by `st`, a semitoken to `newvalue`.
A `BoundsError` is thrown if the token is invalid.
Prepending with `@inbounds` may elide the correctness check and
results in undefined behavior if the token is invalid.
Time: O(1)
"""
Base.@propagate_inbounds function Base.setindex!(m::SortedDict,
d_,
i::IntSemiToken)
@boundscheck has_data((m,i))
@inbounds m.bt.data[i.address] =
KDRec{keytype(m),valtype(m)}(m.bt.data[i.address].parent,
m.bt.data[i.address].k,
convert(valtype(m),d_))
return m
end
## Must repeat this to break ambiguity; cannot use SDMContainer
Base.@propagate_inbounds function Base.setindex!(m::SortedMultiDict,
d_,
i::IntSemiToken)
@boundscheck has_data((m,i))
@inbounds m.bt.data[i.address] =
KDRec{keytype(m),valtype(m)}(m.bt.data[i.address].parent,
m.bt.data[i.address].k,
convert(valtype(m),d_))
return m
end
"""
Base.searchsortedfirst(m::SortedContainer, k)
Return the semitoken of the first item in the
sorted container `m` that is greater than or equal to
`k` in the sort order.
If there is no
such item, then the past-end semitoken is returned. Time: O(*c* log *n*)
"""
function Base.searchsortedfirst(m::SortedContainer, k_)
i = findkeyless(m.bt, convert(keytype(m), k_))
IntSemiToken(nextloc0(m.bt, i))
end
"""
searchsortedafter(m::SortedContainer, k)
Return the semitoken of the first item in the container that is greater than
`k` in the sort order. If there is no
such item, then the past-end semitoken is returned. Time: O(*c* log *n*)
"""
function searchsortedafter(m::SortedContainer, k_)
i, exactfound = findkey(m.bt, convert(keytype(m), k_))
IntSemiToken(nextloc0(m.bt, i))
end
"""
Base.searchsortedlast(m::SortedContainer, k)
Return the semitoken of the last item in the container that is less than or equal
to `k` in sort order. If there is no
such item, then the before-start semitoken is returned. Time: O(*c* log *n*)
"""
function Base.searchsortedlast(m::SortedContainer, k_)
i, exactfound = findkey(m.bt, convert(keytype(m),k_))
IntSemiToken(i)
end
## The next four are correctness-checking routines. They are
## not exported.
not_beforestart(i::Token) =
(!(i[2].address in i[1].bt.useddatacells) ||
i[2].address == 1) && throw(BoundsError())
not_pastend(i::Token) =
(!(i[2].address in i[1].bt.useddatacells) ||
i[2].address == 2) && throw(BoundsError())
has_data(i::Token) =
(!(i[2].address in i[1].bt.useddatacells) ||
i[2].address < 3) && throw(BoundsError())
# Container iterables IterableObject{C, R, KV, T, D}
# have five independent parameters as follows
# C is the type of container, i.e., SortedSet{K,Ord}, SortedDict{K,D,Ord}
# or SortedMultiDict{K,D,Ord}
# R indicates whether the iteration is over the entire container,
# an exclusive range (i.e., a:b where b is omitted) or
# over an inclusive range (i.e., a:b where b is included)
# KV indicates whether the iteration is supposed to return keys, values, or both
# If T is set to onlysemitokens or onlytokens, then this parameter
# has no effect
# T indicates whether to return tokens or semitokens in addition to
# keys and values
# D indicates whether the iteration is forward or reverse
#
# This struct indicates iteration over the entire container is specified
abstract type RangeTypes end
struct EntireContainer <: RangeTypes
end
# This struct stores an exclusive range
struct ExclusiveRange <: RangeTypes
first::Int
pastlast::Int
end
# This struct stores an inclusive range
struct InclusiveRange <: RangeTypes
first::Int
last::Int
end
abstract type KVIterTypes end
# This struct indicates 'keys' iteration
struct KeysIter <: KVIterTypes
end
# This struct indicates 'vals' iteration
struct ValsIter <: KVIterTypes
end
# This struct indicates keys+vals iteration
struct KeysValsIter <: KVIterTypes
end
default_KVIterType(::SDMContainer) = KeysValsIter
default_KVIterType(::SortedSet) = KeysIter
abstract type TokenIterTypes end
# This struct indicates 'semitokens' iteration
struct SemiTokenIter <: TokenIterTypes
end
# This struct indicates 'tokens' iteration
struct TokenIter <: TokenIterTypes
end
# This struct indicates 'onlysemitokens' iteration
struct OnlySemiTokenIter <: TokenIterTypes
end
# This struct indicates 'onlytokens' iteration
struct OnlyTokenIter <: TokenIterTypes
end
# This struct indicates neither tokens nor semitokens iteration
struct NoTokens <: TokenIterTypes
end
# This struct indicates forward iteration
abstract type IterDirection end
struct ForwardIter <: IterDirection
end
# This struct indicates reverse iteration
struct ReverseIter <: IterDirection
end
struct IterableObject{C <: SortedContainer,
R <: RangeTypes,
Kv <: KVIterTypes,
T <: TokenIterTypes,
D <: IterDirection}
m::C
r::R
end
const SortedContainerIterable = Union{IterableObject, SortedContainer}
base_iterable_object(m::SortedContainer) =
IterableObject{typeof(m),
EntireContainer,
default_KVIterType(m),
NoTokens,
ForwardIter}(m, EntireContainer())
exclusive(m::SortedContainer, (ii1, ii2)::Tuple{IntSemiToken, IntSemiToken}) =
IterableObject{typeof(m),
ExclusiveRange,
default_KVIterType(m),
NoTokens,
ForwardIter}(m, ExclusiveRange(ii1.address, ii2.address))
exclusive(m::SortedContainer, ii1::IntSemiToken, ii2::IntSemiToken) =
exclusive(m, (ii1, ii2))
exclusive_key(m::SortedContainer, key1, key2) =
exclusive(m, (searchsortedfirst(m, key1), searchsortedfirst(m, key2)))
inclusive(m::SortedContainer, (ii1, ii2)::Tuple{IntSemiToken, IntSemiToken}) =
IterableObject{typeof(m),
InclusiveRange,
default_KVIterType(m),
NoTokens,
ForwardIter}(m, InclusiveRange(ii1.address, ii2.address))
inclusive(m::SortedContainer,ii1::IntSemiToken, ii2::IntSemiToken) =
inclusive(m, (ii1, ii2))
inclusive_key(m::SortedContainer, key1, key2) =
inclusive(m, (searchsortedfirst(m, key1), searchsortedlast(m, key2)))
Base.keys(ito::IterableObject{C, R, KeysValsIter, T, D}) where
{C <: SDMContainer, R, T, D} =
IterableObject{C, R, KeysIter, T, D}(ito.m, ito.r)
Base.keys(m::SDMContainer) = keys(base_iterable_object(m))
Base.pairs(ito::IterableObject{<: SDMContainer, R, KV, T, D}) where {R, KV, T, D} = ito
Base.pairs(m::SDMContainer) = base_iterable_object(m)
Base.values(ito::IterableObject{C, R, KeysValsIter, T, D}) where
{C <: SDMContainer, R, T, D} =
IterableObject{C, R, ValsIter, T, D}(ito.m, ito.r)
Base.values(m::SDMContainer) = values(base_iterable_object(m))
semitokens(ito::IterableObject{C, R, KV, NoTokens, D}) where {C, R, KV, D} =
IterableObject{C, R, KV, SemiTokenIter, D}(ito.m, ito.r)
semitokens(m::SortedContainer) = semitokens(base_iterable_object(m))
tokens(ito::IterableObject{C, R, KV, NoTokens, D}) where {C, R, KV, D} =
IterableObject{C, R, KV, TokenIter, D}(ito.m, ito.r)
tokens(m::SortedContainer) = tokens(base_iterable_object(m))
onlysemitokens(ito::IterableObject{C, R, KV, NoTokens, D}) where {C, R, KV, D} =
IterableObject{C, R, KV, OnlySemiTokenIter, D}(ito.m, ito.r)
onlysemitokens(m::SortedContainer) = onlysemitokens(base_iterable_object(m))
onlytokens(ito::IterableObject{C, R, KV, NoTokens, D}) where {C, R, KV, D} =
IterableObject{C, R, KV, OnlyTokenIter, D}(ito.m, ito.r)
onlytokens(m::SortedContainer) = onlytokens(base_iterable_object(m))
Base.Iterators.reverse(ito::IterableObject{C, R, KV, T, ForwardIter}) where {C, R, KV, T} =
IterableObject{C, R, KV, T, ReverseIter}(ito.m, ito.r)
Base.Iterators.reverse(ito::IterableObject{C, R, KV, T, ReverseIter}) where {C, R, KV, T} =
IterableObject{C, R, KV, T, ForwardIter}(ito.m, ito.r)
Base.Iterators.reverse(m::SortedContainer) = Iterators.reverse(base_iterable_object(m))
struct SAIterationState
next::Int
final::Int
end
# The iterate function is decomposed into three pieces:
# The iteration_init function initializes the iteration state and
# also stores the final state. It
# does different things depending on the parameter R (range) and D (direction).
# The get_item function retrieves the requested data from the
# the container and depends on the KV and D parameters.
# The next function updates the iteration state to the next item
# and depends on the D (direction) parameter.
iteration_init(ito::IterableObject{C, EntireContainer, KV, T, ForwardIter}) where
{C, KV, T} = SAIterationState(beginloc(ito.m.bt), 2)
iteration_init(ito::IterableObject{C, EntireContainer, KV, T, ReverseIter}) where
{C, KV, T} = SAIterationState(endloc(ito.m.bt), 1)
function iteration_init(ito::IterableObject{C, ExclusiveRange, KV, T, ForwardIter}) where
{C, KV, T}
(!(ito.r.first in ito.m.bt.useddatacells) || ito.r.first == 1 ||
!(ito.r.pastlast in ito.m.bt.useddatacells)) &&
throw(BoundsError())
if compareInd(ito.m.bt, ito.r.first, ito.r.pastlast) < 0
return SAIterationState(ito.r.first, ito.r.pastlast)
else
return SAIterationState(2, 2)
end
end
function iteration_init(ito::IterableObject{C, ExclusiveRange, KV, T, ReverseIter}) where
{C, KV, T}
(!(ito.r.first in ito.m.bt.useddatacells) || ito.r.first == 2 ||
!(ito.r.pastlast in ito.m.bt.useddatacells)) &&
throw(BoundsError())
if compareInd(ito.m.bt, ito.r.first, ito.r.pastlast) < 0
return SAIterationState(prevloc0(ito.m.bt, ito.r.pastlast),
prevloc0(ito.m.bt, ito.r.first))
else
return SAIterationState(2, 2)
end
end
function iteration_init(ito::IterableObject{C, InclusiveRange, KV, T, ForwardIter}) where
{C, KV, T}
(!(ito.r.first in ito.m.bt.useddatacells) || ito.r.first == 1 ||
!(ito.r.last in ito.m.bt.useddatacells) || ito.r.last == 2) &&
throw(BoundsError())
if compareInd(ito.m.bt, ito.r.first, ito.r.last) <= 0
return SAIterationState(ito.r.first, nextloc0(ito.m.bt, ito.r.last))
else
return SAIterationState(2, 2)
end
end
function iteration_init(ito::IterableObject{C, InclusiveRange, KV, T, ReverseIter}) where
{C, KV, T}
(!(ito.r.first in ito.m.bt.useddatacells) || ito.r.first == 2 ||
!(ito.r.last in ito.m.bt.useddatacells) || ito.r.last == 1) &&
throw(BoundsError())
if compareInd(ito.m.bt, ito.r.first, ito.r.last) <= 0
return SAIterationState(ito.r.last, prevloc0(ito.m.bt, ito.r.first))
else
return SAIterationState(2, 2)
end
end
iteration_init(m::SortedContainer) = iteration_init(base_iterable_object(m))
@inline function get_item0(ito::IterableObject{C, R, KeysIter, T, D},
state::SAIterationState) where {C, R, T, D}
@inbounds k = ito.m.bt.data[state.next].k
return k
end
@inline function get_item0(ito::IterableObject{C, R, ValsIter, T, D},
state::SAIterationState) where {C, R, T, D}
@inbounds v = ito.m.bt.data[state.next].d
return v
end
@inline function get_item0(ito::IterableObject{C, R, KeysValsIter, T, D},
state::SAIterationState) where {C, R, T, D}
@inbounds dt = ito.m.bt.data[state.next]
return (dt.k => dt.d)
end
get_item(ito::IterableObject{C, R, KeysIter, TokenIter, D},
state::SAIterationState) where {C, R, D} =
((ito.m, IntSemiToken(state.next)), get_item0(ito, state))
Base.eltype(::Type{IterableObject{C, R, KeysIter, TokenIter, D}}) where {C, R, D} =
Tuple{Tuple{C,IntSemiToken}, keytype(C)}
get_item(ito::IterableObject{C, R, ValsIter, TokenIter, D},
state::SAIterationState) where {C, R, D} =
((ito.m, IntSemiToken(state.next)), get_item0(ito, state))
Base.eltype(::Type{IterableObject{C, R, ValsIter, TokenIter, D}}) where {C, R, D} =
Tuple{Tuple{C,IntSemiToken}, valtype(C)}
function get_item(ito::IterableObject{C, R, KeysValsIter, TokenIter, D},
state::SAIterationState) where {C, R, D}
i = get_item0(ito, state)
((ito.m, IntSemiToken(state.next)), i.first, i.second)
end
Base.eltype(::Type{IterableObject{C, R, KeysValsIter, TokenIter, D}}) where {C, R, D} =
Tuple{Tuple{C,IntSemiToken}, keytype(C), valtype(C)}
get_item(ito::IterableObject{C, R, KeysIter, SemiTokenIter, D},
state::SAIterationState) where {C, R, D} =
(IntSemiToken(state.next), get_item0(ito, state))
Base.eltype(::Type{IterableObject{C, R, KeysIter, SemiTokenIter, D}}) where {C, R, D} =
Tuple{IntSemiToken, keytype(C)}
get_item(ito::IterableObject{C, R, ValsIter, SemiTokenIter, D},
state::SAIterationState) where {C, R, D} =
(IntSemiToken(state.next), get_item0(ito, state))
Base.eltype(::Type{IterableObject{C, R, ValsIter, SemiTokenIter, D}}) where {C, R, D} =
Tuple{IntSemiToken, valtype(C)}
function get_item(ito::IterableObject{C, R, KeysValsIter, SemiTokenIter, D},
state::SAIterationState) where {C, R, D}
i = get_item0(ito, state)
(IntSemiToken(state.next), i.first, i.second)
end
Base.eltype(::Type{IterableObject{C, R, KeysValsIter, SemiTokenIter, D}}) where {C, R, D} =
Tuple{IntSemiToken, keytype(C), valtype(C)}
get_item(ito::IterableObject{C, R, KV, OnlyTokenIter, D},
state::SAIterationState) where {C, R, KV, D} =
(ito.m, IntSemiToken(state.next))
Base.eltype(::Type{IterableObject{C, R, KV, OnlyTokenIter, D}}) where {C, KV, R, D} =
Tuple{C, IntSemiToken}
get_item(ito::IterableObject{C, R, KV, OnlySemiTokenIter, D},
state::SAIterationState) where {C, R, KV, D} =
IntSemiToken(state.next)
Base.eltype(::Type{IterableObject{C, R, KV, OnlySemiTokenIter, D}}) where {C, R, KV, D} = IntSemiToken
get_item(ito::IterableObject{C, R, KV, NoTokens, D},
state::SAIterationState) where {C, R, KV, D} =
get_item0(ito, state)
Base.eltype(::Type{IterableObject{C, R, KeysIter, NoTokens, D}}) where {C, R, D} =
keytype(C)
Base.eltype(::Type{IterableObject{C, R, ValsIter, NoTokens, D}}) where {C, R, D} =
valtype(C)
Base.eltype(::Type{IterableObject{C, R, KeysValsIter, NoTokens, D}}) where {C, R, D} =
eltype(C)
Base.eltype(::ItObj) where {ItObj <: IterableObject} = eltype(ItObj)
get_item(m::SortedContainer, state::SAIterationState) =
get_item(base_iterable_object(m), state)
function next(ito::IterableObject{C, R, KV, T, ForwardIter},
state::SAIterationState) where {C, R, KV, T}
sn = state.next
(sn < 3 || !(sn in ito.m.bt.useddatacells)) && throw(BoundsError())
SAIterationState(nextloc0(ito.m.bt, sn), state.final)
end
function next(ito::IterableObject{C, R, KV, T, ReverseIter},
state::SAIterationState) where {C, R, KV, T}
sn = state.next
(sn < 3 || !(sn in ito.m.bt.useddatacells)) && throw(BoundsError())
SAIterationState(prevloc0(ito.m.bt, sn), state.final)
end
next(m::SortedContainer, state::SAIterationState) =
next(base_iterable_object(m), state)
"""
Base.iterate(iter::SortedContainerIterable)
with the following helper functions to construct a `SortedContainerIterable`:
inclusive(m::SortedContainer, st1, st2)
inclusive(m::SortedContainer, (st1, st2))
inclusive_key(m::SortedContainer, key1, key2)
inclusive_key(m::SortedContainer, (key1, key2))
exclusive(m::SortedContainer, st1, st2)
exclusive(m::SortedContainer, (st1, st2))
exclusive_key(m::SortedContainer, key1, key2)
exclusive_key(m::SortedContainer, (key1, key2))
Base.keys(b)
Base.values(b)
Base.pairs(b)
Base.eachindex(b)
tokens(kv)
semitokens(kv)
onlytokens(kv)
onlysemitokens(kv)
Base.Iterators.reverse(m)
(:)(a,b)
Iterate over a sorted container, typically
within a for-loop, comprehension, or generator.
Here, `iter` is an iterable object constructed from a sorted
container. The possible iterable objects are constructed from
the helper functions as follows:
A *basic* iterable object is either
- an entire sorted container `m`,
- `inclusive(m, (st1, st2))` or equivalently `inclusive(m, st1, st2)`,
- `inclusive_key(m, (k1, k2))` or equivalently `inclusive_key(m, k1, k2)`
- `a:b`, where `a` and `b` are tokens addressing the same container
- `exclusive(m, (st1, st2))` or equivalently `exclusive(m, st1, st2)`
- `exclusive_key(m, (k1, k2))` or equivalently `exclusive_key(m, k1, k2)`
These extract ranges of consecutive items in the containers. In the
`inclusive` and `exclusive` constructions,
constructions, `m` is a container, `st1` and `st2` are semitokens. The
`inclusive` range includes both endpoints `st1` and `st2`.
The inclusive
iteration is empty if `compare(m,st1,st2)<0`. The `exclusive` range includes
endpoint `st1` but not `st2`. The exclusive iteration is empty if
`compare(m,st1,st2)<=0`. In the exclusive iteration, it is acceptable
if `st2` is the past-end semitoken.
The range `exclusive_key` means all data items with keys between `k1` up to but
excluding items with key `k2`. For this range to be nonempty,
`k1<k2` must hold (in the sort order).
The range `inclusive_key` means all data items with
keys between `k1` and `k2` inclusive. For this range to be nonempty, `k1<=k2`
must hold.
A *kv* iterable object has the form
- `b`, a basic iterable object
- `keys(b)` where `b` is a basic object. Extract keys only (not applicable
to SortedSet)
- `values(b)` where `b` is a basic object. Extract values only
(not applicable to SortedSet).
- `pairs(b)` where `b` is a basic object. Extracts key=>value pairs
(not applicable to SortedSet).
This is the same as just specifying `b` and is provided only for compatibility
with `Base.pairs`.
A *tkv* object has the form
- `kv`, a kv iterable object
- `tokens(kv)` where `kv` is a kv iterable object.
Return 2-tuples of the form `(t,w)`, where `t` is the
token of the item and `w` is a key or value if `kv` is a keys or values
iteration, or `(t,k,v)` if `kv` is a pairs iteration.
- `semitokens(kv)` where `kv` is a kv iterable object.
Return pairs of the form `(st,w)`, where `st` is the
token of the item and `w` is a key or value if `kv` is a keys or values
iteration, or `(st,k,v)` if `kv` is a pairs iteration.
- `onlytokens(kv)` where `kv` is a kv iterable object. Return only tokens
of the data items but not the items themselves.
The `keys`, `values`, or `pairs` modifiers described above
are ignored.
- `onlysemitokens(kv)` where `kv` is a kv iterable object. Return only semitokens
of the data items but not the items themselves.
The `keys`, `values`, or `pairs` modifiers described above
are ignored.
Finally, a tkv iteration can be reversed by the `Iterators.reverse` function. The
`Iterators.reverse` function
may be nested in an arbitrary position with respect to the other operations described
above. Two reverse operations cancel each other out. For example,
`Iterators.reverse(keys(Iterators.reverse(m)))` is the same iteration as `keys(m)`.
For compatibility with `Base`, there is also an `eachindex` function:
`eachindex(b)` where the base object `b` a SortedDict is
the same as `keys(b)` (to be compatible with Dict).
On the other hand, `eachindex(b)` where the
base object `b` is a SortedSet or SortedMultiDict is the
same as `onlysemitokens(b)`.
Colon notation `a:b` is equivalent
to `onlytokens(inclusive(a[1], a[2], b[2]))`, in other words, it yields
an iteration that provides all the tokens of items in the sort order ranging
from token `a` up to token `b`. It is required that `a[1]===b[1]` (i.e.,
`a` and `b` are tokens for the same container). Exclusive iteration using
colon notation is obtained via `a : b-1`.
# Examples:
```julia
for (k,v) in sd
<body>
end
```
Here, `sd` is a `SortedDict` or `SortedMultiDict`. The variables `(k,v)`
are set to consecutive key-value pairs. All items in the container are
produced in order.
```julia
for k in inclusive(ss, st1, st2)
<body>
end
```
Here, `ss` is a `SortedSet`, and `st1`, and `st2` are semitokens indexing `ss`.
The elements of the set between `st1` and `st2` inclusive are returned.
```julia
for (t,k) in tokens(keys(exclusive_key(sd, key1, key2)))
<body>
end
```
Here, `sd` is a `SortedDict` or `SortedMultiDict`, `key1` and `key2` are keys
indexing `sd`. In this case, `t` will be tokens of consecutive items,
while `k` will be the corresponding keys. The returned keys lie between `key1` and
`key2` excluding `key2`.
```julia
for (t,k) in Iterators.reverse(tokens(keys(exclusive_key(sd, key1, key2))))
<body>
end
```
Same as above, except the iteration is in the reverse order.
Writing on the objects returned by `values` is not currently supported, e.g.,
the following `map!` statement is not implemented even though the