1 #!/usr/bin/env rdmd-dev-module
2 
3 /** Predicate extensions to std.algorithm.
4     Copyright: Per Nordlöw 2014-.
5     License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
6     Authors: $(WEB Per Nordlöw)
7    */
8 module predicates;
9 
10 import std.traits: CommonType;
11 import std.range: isIterable, ElementType;
12 import std.algorithm: among;
13 
14 /** Return true if $(D x) is a equal to any of $(D ys).
15     TODO Make $(D ys) lazy if any of $(D ys) is a delegate.
16     TODO Reuse $(D among)
17  */
18 bool of(S, T...)(in S x, in T ys) pure if (ys.length >= 1 &&
19                                            is(typeof({ return S.init ==
20                                                        CommonType!(T).init; })))
21 {
22     foreach (y; ys)
23     {
24         if (x == y)
25             return true;
26     }
27     return false;
28 }
29 
30 @safe @nogc pure nothrow unittest
31 {
32     assert(1.of(1, 2, 3));
33     assert(!4.of(1, 2, 3));
34 }
35 
36 /** Return true if $(D x) is a equal to any of $(D ys).
37     TODO Reuse $(D among)
38  */
39 bool of(E, R)(E x, R ys) pure if (isIterable!R/*  && */
40                                   /* is(E.init == ElementType!R.init) */)
41 {
42     import std.range: isNarrowString;
43     static if (isNarrowString!R)
44     {
45         import std.algorithm: canFind;
46         return ys.canFind(x);
47     }
48     else
49     {
50         foreach (y; ys)
51             if (x == y)
52                 return true;
53         return false;
54     }
55 }
56 
57 @safe pure nothrow unittest
58 {
59     assert(1.of([1, 2, 3]));
60     assert(!4.of([1, 2, 3]));
61 }
62 
63 @safe pure unittest
64 {
65     assert('a'.of("abc"));
66     assert(!'z'.of("abc"));
67     assert('å'.of("åäö"));
68 }
69 
70 alias isEither = of;
71 
72 // ==============================================================================================
73 
74 import std.range: isInputRange;
75 
76 /** Returns: true iff all elements in range are equal (or range is empty).
77     http://stackoverflow.com/questions/19258556/equality-of-all-elements-in-a-range/19292822?noredirect=1#19292822
78 
79     Possible alternatives or aliases: allElementsEqual, haveEqualElements
80 */
81 bool allEqual(R)(R range) /* pure @safe @nogc nothrow */ if (isInputRange!R)
82 {
83     import std.algorithm: findAdjacent;
84     import std.range: empty;
85     return range.findAdjacent!("a != b").empty;
86 }
87 unittest { assert([11, 11].allEqual); }
88 unittest { assert(![11, 12].allEqual); }
89 unittest { int[] x; assert(x.allEqual); }
90 
91 /* See also: http://forum.dlang.org/thread/febepworacvbapkpozjl@forum.dlang.org#post-gbqvablzsbdowqoijxpn:40forum.dlang.org */
92 /* import std.range: InputRange; */
93 /* bool allEqual_(T)(InputRange!T range) @safe pure nothrow */
94 /* { */
95 /*     import std.algorithm: findAdjacent; */
96 /*     import std.range: empty; */
97 /*     return range.findAdjacent!("a != b").empty; */
98 /* } */
99 /* unittest { assert([11, 11].allEqual_); } */
100 /* unittest { assert(![11, 12].allEqual_); } */
101 /* unittest { int[] x; assert(x.allEqual_); } */
102 
103 /** Returns: true iff all elements in range are equal (or range is empty) to $(D element).
104 
105     Possible alternatives or aliases: allElementsEqualTo
106 */
107 bool allEqualTo(R, E)(R range, E element) @safe pure nothrow if (isInputRange!R &&
108                                                                  is(ElementType!R == E))
109 {
110     import std.algorithm: all;
111     return range.all!(a => a == element);
112 }
113 unittest { assert([42, 42].allEqualTo(42)); }
114 
115 // ==============================================================================================
116 
117 import traits_ex: isStruct, isClass;
118 import std.traits: isStaticArray;
119 
120 /** Check if all Elements of $(D x) are zero. */
121 bool allZero(T, bool useStatic = true)(in T x) @safe @nogc pure nothrow
122 {
123     static if (isStruct!T || isClass!T)
124     {
125         foreach (const ref elt; x.tupleof)
126         {
127             if (!elt.allZero) { return false; }
128         }
129         return true;
130     }
131     else static if (useStatic && isStaticArray!T)
132     {
133         import range_ex: iota;
134         foreach (ix; iota!(0, x.length))
135         {
136             if (!x[ix].allZero) { return false; } // make use of iota?
137         }
138         return true;
139     }
140     else static if (isIterable!T)
141     {
142         foreach (const ref elt; x)
143         {
144             if (!elt.allZero) { return false; }
145         }
146         return true;
147     }
148     else
149     {
150         return x == 0;
151     }
152 }
153 
154 alias zeroed = allZero;
155 
156 unittest
157 {
158     ubyte[20] d;
159     assert(d.allZero);     // note that [] is needed here
160 
161     ubyte[2][2] zeros = [ [0, 0],
162                           [0, 0] ];
163     assert(zeros.allZero);
164 
165     ubyte[2][2] one = [ [0, 1],
166                         [0, 0] ];
167     assert(!one.allZero);
168 
169     ubyte[2][2] ones = [ [1, 1],
170                          [1, 1] ];
171     assert(!ones.allZero);
172 
173     ubyte[2][2][2] zeros3d = [ [ [0, 0],
174                                  [0, 0] ],
175                                [ [0, 0],
176                                  [0, 0] ] ];
177     assert(zeros3d.allZero);
178 
179     ubyte[2][2][2] ones3d = [ [ [1, 1],
180                                 [1, 1] ],
181                               [ [1, 1],
182                                 [1, 1] ] ];
183     assert(!ones3d.allZero);
184 }
185 
186 unittest
187 {
188     struct Vec { real x, y; }
189     const v0 = Vec(0, 0);
190     assert(v0.zeroed);
191     const v1 = Vec(1, 1);
192     assert(!v1.zeroed);
193 }
194 
195 unittest
196 {
197     class Vec
198     {
199         this(real x, real y) { this.x = x; this.y = y; }
200         real x, y;
201     }
202     const v0 = new Vec(0, 0);
203     assert(v0.zeroed);
204     const v1 = new Vec(1, 1);
205     assert(!v1.zeroed);
206 }
207 
208 /** Returns: true iff $(D a) is set to the default/initial value of its type $(D T).
209  */
210 bool isDefaulted(T)(in T a) @safe pure nothrow @nogc
211 {
212     import std.traits: isInstanceOf;
213     import std.typecons: Nullable;
214     static if (isInstanceOf!(Nullable, T))
215     {
216         return a.isNull;
217     }
218     else
219     {
220         return a == T.init;
221     }
222 }
223 alias isUntouched = isDefaulted;
224 alias isInited = isDefaulted;
225 
226 /** Check if $(D x) is a power of 2 (binary power).
227     See also: http://forum.dlang.org/thread/zumhmosfkvwjymjhmtlt@forum.dlang.org#post-fvnmurrctavpfkunssdf:40forum.dlang.org
228  */
229 bool isPowerOf2(uint x) @safe pure nothrow @nogc
230 {
231     return (x & -x) > (x - 1);
232 }
233 
234 @safe pure nothrow @nogc unittest
235 {
236     assert(!0.isPowerOf2);
237     assert(1.isPowerOf2);
238     assert(2.isPowerOf2);
239     assert(!3.isPowerOf2);
240     assert(4.isPowerOf2);
241     assert(!5.isPowerOf2);
242     assert(!6.isPowerOf2);
243     assert(!7.isPowerOf2);
244     assert(8.isPowerOf2);
245 }