1 module dkh.algorithm;
2 
3 import std.traits : isFloatingPoint, isIntegral;
4 
5 /**
6 Do binary search, pred(l) must be false, pred(r) must be true, and pred must have monotonic
7 
8 $(D [pred(l) = false, false, ..., false, true, true, ..., pred(r) = true])
9     
10 Returns:
11     minimum x, s.t. $(D pred(x) = true)
12  */
13 T binSearch(alias pred, T)(T l, T r) if (isIntegral!T) {
14     while (r-l > 1) {
15         T md = l + (r-l) / 2;
16         if (!pred(md)) l = md;
17         else r = md;
18     }
19     return r;
20 }
21 
22 /// ditto
23 T binSearch(alias pred, T)(T l, T r, int cnt = 60) if (isFloatingPoint!T) {
24     foreach (i; 0..cnt) {
25         T md = (l+r)/2;
26         if (!pred(md)) l = md;
27         else r = md;
28     }
29     return r;
30 }
31 
32 ///
33 unittest {
34     assert(binSearch!(x => x*x >= 100)(0, 20) == 10);
35     assert(binSearch!(x => x*x >= 101)(0, 20) == 11);
36     assert(binSearch!(x => true)(0, 20) == 1);
37     assert(binSearch!(x => false)(0, 20) == 20);
38 }
39 
40 import std.range.primitives;
41 
42 /// Find minimum
43 E minimum(alias pred = "a < b", Range, E = ElementType!Range)(Range range, E seed)
44 if (isInputRange!Range && !isInfinite!Range) {
45     import std.algorithm, std.functional;
46     return reduce!((a, b) => binaryFun!pred(a, b) ? a : b)(seed, range);
47 }
48 
49 /// ditto
50 ElementType!Range minimum(alias pred = "a < b", Range)(Range range) {
51     assert(!range.empty, "minimum: range must not empty");
52     auto e = range.front; range.popFront;
53     return minimum!pred(range, e);
54 }
55 
56 ///
57 unittest {
58     assert(minimum([2, 1, 3]) == 1);
59     assert(minimum!"a > b"([2, 1, 3]) == 3);
60     assert(minimum([2, 1, 3], -1) == -1);
61     assert(minimum!"a > b"([2, 1, 3], 100) == 100);
62 }
63 
64 /// Find maximum
65 E maximum(alias pred = "a < b", Range, E = ElementType!Range)(Range range, E seed)
66 if (isInputRange!Range && !isInfinite!Range) {
67     import std.algorithm, std.functional;
68     return reduce!((a, b) => binaryFun!pred(a, b) ? b : a)(seed, range);
69 }
70 
71 /// ditto
72 ElementType!Range maximum(alias pred = "a < b", Range)(Range range) {
73     assert(!range.empty, "maximum: range must not empty");
74     auto e = range.front; range.popFront;
75     return maximum!pred(range, e);
76 }
77 
78 ///
79 unittest {
80     assert(maximum([2, 1, 3]) == 3);
81     assert(maximum!"a > b"([2, 1, 3]) == 1);
82     assert(maximum([2, 1, 3], 100) == 100);
83     assert(maximum!"a > b"([2, 1, 3], -1) == -1);
84 }
85 
86 /**
87 Range that rotate elements.
88  */
89 Rotator!Range rotator(Range)(Range r) {
90     return Rotator!Range(r);
91 }
92 
93 /// ditto
94 struct Rotator(Range)
95 if (isForwardRange!Range && hasLength!Range) {
96     size_t cnt;
97     Range start, now;
98     this(Range r) {
99         cnt = 0;
100         start = r.save;
101         now = r.save;
102     }
103     this(this) {
104         start = start.save;
105         now = now.save;
106     }
107     @property bool empty() const {
108         return now.empty;
109     }
110     @property auto front() const {
111         assert(!now.empty);
112         import std.range : take, chain;
113         return chain(now, start.take(cnt));
114     }
115     @property Rotator!Range save() {
116         return this;
117     }
118     void popFront() {
119         cnt++;
120         now.popFront;
121     }
122 }
123 
124 ///
125 unittest {
126     import std.algorithm : equal, cmp;
127     import std.array : array;
128     int[] a = [1, 2, 3];
129     assert(equal!equal(a.rotator, [
130         [1, 2, 3],
131         [2, 3, 1],
132         [3, 1, 2],
133     ]));
134     int[] b = [3, 1, 4, 1, 5];
135     assert(equal(b.rotator.maximum!"cmp(a, b) == -1", [5, 3, 1, 4, 1]));
136 }