-
Notifications
You must be signed in to change notification settings - Fork 0
/
perf.d
324 lines (292 loc) · 7.7 KB
/
perf.d
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
/*
* D calendar
*
* An example of how using component-style programming with ranges simplifies a
* complex task into manageable pieces. The task is, given a year, to produce a
* range of lines representing a nicely laid-out calendar of that year.
*
* This example shows how something is complex as calendar layout can be
* written in a clear, readable way that allows individual components to be
* reused.
*/
import std.algorithm;
import std.conv;
import std.datetime;
import std.functional;
import std.range;
import std.stdio : writeln, writefln, stderr;
import std.string;
// Used by groupBy.
/**
* Specifies whether a predicate is an equivalence relation.
*/
import std.typecons : Flag;
alias EquivRelation = Flag!"equivRelation";
// Used by implementation of groupBy.
private struct GroupByChunkImpl(alias pred, EquivRelation equivRelation, Range)
{
alias fun = binaryFun!pred;
private Range r;
static if (!equivRelation)
private bool first = true;
else
private enum first = false;
/* For forward ranges, using .save is more reliable than blindly assuming
* that the current value of .front will persist past a .popFront. However,
* if Range is only an input range, then we have no choice but to save the
* value of .front. */
static if (isForwardRange!Range)
{
private Range prev;
this(Range _r, Range _prev)
{
r = _r.save;
prev = _prev.save;
}
private void savePrev() { prev = r.save; }
@property bool empty()
{
return r.empty || (!first && !fun(prev.front, r.front));
}
}
else
{
private ElementType!Range prev;
this(Range _r, ElementType!Range _prev)
{
r = _r;
prev = _prev;
}
private void savePrev() { prev = r.front; }
@property bool empty()
{
return r.empty || (!first && !fun(prev, r.front));
}
}
@property ElementType!Range front() { return r.front; }
void popFront()
in
{
import core.exception : RangeError;
if (r.empty) throw new RangeError();
}
body
{
// If this is a non-equivalence relation, we cannot assume transitivity
// so we have to update .prev at every step.
static if (!equivRelation)
{
savePrev();
first = false;
}
r.popFront();
}
static if (isForwardRange!Range)
{
@property typeof(this) save()
{
typeof(this) copy;
copy.r = r.save;
copy.prev = prev.save;
return copy;
}
}
}
// Implementation of groupBy.
private struct GroupByImpl(alias pred, EquivRelation equivRelation, Range)
{
alias fun = binaryFun!pred;
private Range r;
/* For forward ranges, using .save is more reliable than blindly assuming
* that the current value of .front will persist past a .popFront. However,
* if Range is only an input range, then we have no choice but to save the
* value of .front. */
static if (isForwardRange!Range)
{
private Range _prev;
private void savePrev() { _prev = r.save; }
private @property ElementType!Range prev() { return _prev.front; }
}
else
{
private ElementType!Range _prev;
private void savePrev() { _prev = r.front; }
private alias prev = _prev;
}
this(Range _r)
{
r = _r;
if (!empty)
{
// Check reflexivity if predicate is claimed to be an equivalence
// relation.
assert(!equivRelation || pred(r.front, r.front),
"predicate " ~ pred.stringof ~ " is claimed to be "~
"equivalence relation yet isn't reflexive");
savePrev();
}
}
@property bool empty() { return r.empty; }
@property auto front()
in
{
import core.exception : RangeError;
if (r.empty) throw new RangeError();
}
body
{
return GroupByChunkImpl!(pred, equivRelation, Range)(r, _prev);
}
void popFront()
{
while (!r.empty)
{
static if (equivRelation)
{
if (!fun(prev, r.front))
{
savePrev();
break;
}
r.popFront();
}
else
{
// For non-equivalence relations, we cannot assume transitivity
// so we must update prev each time.
savePrev();
r.popFront();
if (!r.empty && !fun(prev, r.front))
break;
}
}
}
static if (isForwardRange!Range)
{
@property typeof(this) save()
{
typeof(this) copy;
copy.r = r.save;
copy._prev = _prev.save;
return copy;
}
}
}
/**
* Chunks an input range into subranges of equivalent adjacent elements.
*
* Equivalence is defined by the predicate $(D pred), which can be either
* binary or unary. In the binary form, two _range elements $(D a) and $(D b)
* are considered equivalent if $(D pred(a,b)) is true. In unary form, two
* elements are considered equivalent if $(D pred(a) == pred(b)) is true.
*
* The optional parameter $(D equivRelation), which defaults to
* $(D EquivRelation.no) for binary predicates if not specified, specifies
* whether $(D pred) is an equivalence relation, that is, whether it is
* reflexive ($(D pred(x,x)) is always true), symmetric ($(D pred(x,y) ==
* pred(y,x))), and transitive ($(D pred(x,y) && pred(y,z)) implies
* $(D pred(x,z))). When this is the case, $(D groupBy) can take advantage of
* these three properties for a slight performance improvement.
*
* Note that it is not an error to specify $(D EquivRelation.no) even when
* $(D pred) is an equivalence relation; the resulting range will just be
* slightly slower than it could be. However, if $(D EquivRelation.yes) is
* specified yet $(D pred) is actually $(I not) an equivalence relation, the
* behaviour of the resulting range is undefined.
*
* Unary predicates always imply $(D equivRelation.yes), since they are
* internally converted to the binary equivalence relation $(D pred(a) ==
* pred(b)).
*
* Params:
* pred = Predicate for determining equivalence.
* r = The range to be chunked.
*
* Returns: A range of ranges in which all elements in a given subrange are
* equivalent under the given predicate.
*
* Notes:
*
* Equivalent elements separated by an intervening non-equivalent element will
* appear in separate subranges; this function only considers adjacent
* equivalence. Elements in the subranges will always appear in the same order
* they appear in the original range.
*
* See_also:
* $(XREF algorithm,group), which collapses adjacent equivalent elements into a
* single element.
*/
auto groupBy(alias pred, Range)(Range r)
if (isInputRange!Range)
{
return groupBy!(pred, EquivRelation.no, Range)(r);
}
/// ditto
auto groupBy(alias pred, EquivRelation equivRelation, Range)(Range r)
if (isInputRange!Range)
{
static if (is(typeof(binaryFun!pred(ElementType!Range.init,
ElementType!Range.init)) : bool))
return GroupByImpl!(pred, equivRelation, Range)(r);
else static if (is(typeof(
unaryFun!pred(ElementType!Range.init) ==
unaryFun!pred(ElementType!Range.init))))
return GroupByImpl!((a,b) => pred(a) == pred(b), EquivRelation.yes, Range)(r);
else
static assert(0, "groupBy expects either a binary predicate or "~
"a unary predicate on range elements of type: "~
ElementType!Range.stringof);
}
int cnt_empty = 0;
int cnt_front = 0;
int cnt_popfront = 0;
int cnt_save = 0;
auto statRange(Range)(Range r)
{
static struct StatRange {
private Range r_;
this(Range r) {
r_ = r;
}
@property bool empty() { cnt_empty++ ; return r_.empty; }
@property auto front() { cnt_front++; return r_.front; }
@property void popFront() { cnt_popfront++; r_.popFront; }
@property StatRange save() {
cnt_save++;
StatRange copy = this;
copy.r_ = r_;
return copy;
}
}
return StatRange( r );
}
int cnt_eq = 0;
bool equal_stat(truc)(truc a, truc b)
{
cnt_eq++;
return a == b;
}
int main()
{
int[] a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
auto s = statRange(a).groupBy!((a, b) => equal_stat(a, b));
writeln(s);
writeln("empty ", cnt_empty);
writeln("front ", cnt_front);
writeln("popFront ", cnt_popfront);
writeln("save ", cnt_save);
writeln("equal ", cnt_eq);
cnt_empty = 0;
cnt_front = 0;
cnt_popfront = 0;
cnt_save = 0;
cnt_eq = 0;
auto ss = statRange(a).groupBy!((a, b) => equal_stat(a, b), EquivRelation.yes);
writeln(ss);
writeln("empty ", cnt_empty);
writeln("front ", cnt_front);
writeln("popFront ", cnt_popfront);
writeln("save ", cnt_save);
writeln("equal ", cnt_eq);
return 0;
}