Repository navigation
Expand file tree
/
Copy pathsetops.d
More file actions
224 lines (201 loc) · 6.51 KB
/
Copy pathsetops.d
File metadata and controls
224 lines (201 loc) · 6.51 KB
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
// Written in the D programming language.
/**
This is a submodule of `mir.algorithm`. It contains `nothrow` `@nogc` BetterC alternative to `MultiwayMerge` from `std.algorithm.setops`.
License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilia Ki (Mir & BetterC rework, optimization).
*/
module mir.algorithm.setops;
import core.lifetime: move;
import mir.functional: naryFun;
import mir.primitives;
import mir.qualifier;
import std.range.primitives: isRandomAccessRange;
/**
Merges multiple sets. The input sets are passed as a
range of ranges and each is assumed to be sorted by $(D
less). Computation is done lazily, one union element at a time. The
complexity of one $(D popFront) operation is $(BIGOH
log(ror.length)). However, the length of $(D ror) decreases as ranges
in it are exhausted, so the complexity of a full pass through $(D
MultiwayMerge) is dependent on the distribution of the lengths of ranges
contained within $(D ror). If all ranges have the same length $(D n)
(worst case scenario), the complexity of a full pass through $(D
MultiwayMerge) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D
log(ror.length)) times worse than just spanning all ranges in
turn. The output comes sorted (unstably) by $(D less).
The length of the resulting range is the sum of all lengths of
the ranges passed as input. This means that all elements (duplicates
included) are transferred to the resulting range.
For backward compatibility, `multiwayMerge` is available under
the name `nWayUnion` and `MultiwayMerge` under the name of `NWayUnion` .
Future code should use `multiwayMerge` and `MultiwayMerge` as `nWayUnion`
and `NWayUnion` will be deprecated.
Params:
less = Predicate the given ranges are sorted by.
ror = A range of ranges sorted by `less` to compute the union for.
Returns:
A range of the union of the ranges in `ror`.
Warning: Because $(D MultiwayMerge) does not allocate extra memory, it
will leave $(D ror) modified. Namely, $(D MultiwayMerge) assumes ownership
of $(D ror) and discretionarily swaps and advances elements of it. If
you want $(D ror) to preserve its contents after the call, you may
want to pass a duplicate to $(D MultiwayMerge) (and perhaps cache the
duplicate in between calls).
*/
struct MultiwayMerge(alias less, RangeOfRanges)
if (isRandomAccessRange!RangeOfRanges)
{
import mir.primitives;
import mir.container.binaryheap;
///
@disable this();
///
@disable this(this);
///
static bool compFront(ElementType!RangeOfRanges a, ElementType!RangeOfRanges b)
{
// revert comparison order so we get the smallest elements first
return less(b.front, a.front);
}
/// Heap
BinaryHeap!(compFront, RangeOfRanges) _heap;
///
this(scope return RangeOfRanges ror)
{
// Preemptively get rid of all empty ranges in the input
// No need for stability either
auto temp = ror.lightScope;
for (;!temp.empty;)
{
if (!temp.front.empty)
{
temp.popFront;
continue;
}
import mir.utility: swap;
() @trusted {swap(temp.back, temp.front);} ();
temp.popBack;
ror.popBack;
}
//Build the heap across the range
_heap = typeof(_heap)(ror.move);
}
///
@property bool empty() scope const { return _heap.empty; }
///
@property auto ref front()
{
assert(!empty);
return _heap.front.front;
}
///
void popFront() scope @safe
{
_heap._store.front.popFront;
if (!_heap._store.front.empty)
_heap.siftDown(_heap._store[], 0, _heap._length);
else
_heap.removeFront;
}
}
/// Ditto
MultiwayMerge!(naryFun!less, RangeOfRanges) multiwayMerge
(alias less = "a < b", RangeOfRanges)
(scope RangeOfRanges ror)
{
return typeof(return)(move(ror));
}
///
@safe nothrow @nogc version(mir_test) unittest
{
import mir.algorithm.iteration: equal;
static a =
[
[ 1, 4, 7, 8 ],
[ 1, 7 ],
[ 1, 7, 8],
[ 4 ],
[ 7 ],
];
static witness = [
1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8
];
assert(a.multiwayMerge.equal(witness));
static b =
[
// range with duplicates
[ 1, 1, 4, 7, 8 ],
[ 7 ],
[ 1, 7, 8],
[ 4 ],
[ 7 ],
];
// duplicates are propagated to the resulting range
assert(b.multiwayMerge.equal(witness));
}
/**
Computes the union of multiple ranges. The input ranges are passed
as a range of ranges and each is assumed to be sorted by $(D
less). Computation is done lazily, one union element at a time.
`multiwayUnion(ror)` is functionally equivalent to `multiwayMerge(ror).uniq`.
"The output of multiwayUnion has no duplicates even when its inputs contain duplicates."
Params:
less = Predicate the given ranges are sorted by.
ror = A range of ranges sorted by `less` to compute the intersection for.
Returns:
A range of the union of the ranges in `ror`.
See also: $(LREF multiwayMerge)
*/
auto multiwayUnion(alias less = "a < b", RangeOfRanges)(scope RangeOfRanges ror)
{
import mir.functional: not;
import mir.algorithm.iteration : Uniq;
return Uniq!(not!less, typeof(multiwayMerge!less(ror)))(multiwayMerge!less(move(ror)));
}
///
@safe version(mir_test) unittest
{
import mir.algorithm.iteration: equal;
// sets
double[][] a =
[
[ 1, 4, 7, 8 ],
[ 1, 7 ],
[ 1, 7, 8],
[ 4 ],
[ 7 ],
];
auto witness = [1, 4, 7, 8];
assert(a.multiwayUnion.equal(witness));
// multisets
double[][] b =
[
[ 1, 1, 1, 4, 7, 8 ],
[ 1, 7 ],
[ 1, 7, 7, 8],
[ 4 ],
[ 7 ],
];
assert(b.multiwayUnion.equal(witness));
}
/++
Computes the length of union of multiple ranges. The input ranges are passed
as a range of ranges and each is assumed to be sorted by `less`.
Params:
less = Predicate the given ranges are sorted by.
ror = A range of ranges sorted by `less` to compute the intersection for.
Returns:
A length of the union of the ranges in `ror`.
+/
pragma(inline, false)
size_t unionLength(alias less = "a < b", RangeOfRanges)(scope RangeOfRanges ror)
{
size_t length;
auto u = move(ror).multiwayUnion!less;
if (!u.empty) do {
length++;
u.popFront;
} while(!u.empty);
return length;
}