1 /** Predicate extensions to std.algorithm.
2 	Copyright: Per Nordlöw 2022-.
3 	License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 	Authors: $(WEB Per Nordlöw)
5    */
6 module nxt.predicates;
7 
8 import std.range.primitives : ElementType;
9 
10 // ==============================================================================================
11 
12 import std.range: isInputRange;
13 
14 /** Returns: `true` iff all elements in range are equal (or range is empty).
15 	http://stackoverflow.com/questions/19258556/equality-of-all-elements-in-a-range/19292822?noredirect=1#19292822
16 
17 	Possible alternatives or aliases: allElementsEqual, haveEqualElements
18 */
19 bool allEqual(R)(R range)
20 if (isInputRange!R)
21 {
22 	import std.algorithm: findAdjacent;
23 	import std.range: empty;
24 	return range.findAdjacent!("a != b").empty;
25 }
26 pure nothrow @safe unittest { assert([11, 11].allEqual); }
27 pure nothrow @safe unittest { assert(![11, 12].allEqual); }
28 pure nothrow @safe unittest { int[] x; assert(x.allEqual); }
29 
30 /* See_Also: http://forum.dlang.org/thread/febepworacvbapkpozjl@forum.dlang.org#post-gbqvablzsbdowqoijxpn:40forum.dlang.org */
31 /* import std.range: InputRange; */
32 /* bool allEqual_(T)(InputRange!T range) @safe pure nothrow */
33 /* { */
34 /*	 import std.algorithm: findAdjacent; */
35 /*	 import std.range: empty; */
36 /*	 return range.findAdjacent!("a != b").empty; */
37 /* } */
38 /* pure nothrow @safe unittest { assert([11, 11].allEqual_); } */
39 /* pure nothrow @safe unittest { assert(![11, 12].allEqual_); } */
40 /* pure nothrow @safe unittest { int[] x; assert(x.allEqual_); } */
41 
42 /** Returns: `true` iff all elements in range are equal (or range is empty) to $(D element).
43 
44 	Possible alternatives or aliases: allElementsEqualTo
45 */
46 bool allEqualTo(R, E)(R range, E element)
47 if (isInputRange!R &&
48 	is(ElementType!R == E))
49 {
50 	import std.algorithm: all;
51 	return range.all!(a => a == element);
52 }
53 
54 ///
55 pure nothrow @safe unittest {
56 	assert([42, 42].allEqualTo(42));
57 }
58 
59 // ==============================================================================================
60 
61 /** Check if all Elements of $(D x) are zero. */
62 bool allZero(T, bool useStatic = true)(in T x)
63 {
64 	static if (is(T == struct) || is(T == class))
65 	{
66 		foreach (const ref elt; x.tupleof)
67 			if (!elt.allZero)
68 				return false;
69 		return true;
70 	}
71 	else
72 	{
73 		import std.traits : isIterable;
74 		static if (useStatic && __traits(isStaticArray, T))
75 		{
76 			static foreach (ix; 0 .. x.length) /+ TODO: do we need static iota here? +/
77 				if (!x[ix].allZero)
78 					return false; // make use of iota?
79 			return true;
80 		}
81 		else static if (isIterable!T)
82 		{
83 			foreach (const ref elt; x)
84 				if (!elt.allZero)
85 					return false;
86 			return true;
87 		}
88 		else
89 			return x == 0;
90 	}
91 }
92 /// ditto
93 alias zeroed = allZero;
94 
95 ///
96 pure nothrow @safe unittest {
97 	ubyte[20] d;
98 	assert(d.allZero);	 // note that [] is needed here
99 
100 	ubyte[2][2] zeros = [ [0, 0],
101 						  [0, 0] ];
102 	assert(zeros.allZero);
103 
104 	ubyte[2][2] one = [ [0, 1],
105 						[0, 0] ];
106 	assert(!one.allZero);
107 
108 	ubyte[2][2] ones = [ [1, 1],
109 						 [1, 1] ];
110 	assert(!ones.allZero);
111 
112 	ubyte[2][2][2] zeros3d = [ [ [0, 0],
113 								 [0, 0] ],
114 							   [ [0, 0],
115 								 [0, 0] ] ];
116 	assert(zeros3d.allZero);
117 
118 	ubyte[2][2][2] ones3d = [ [ [1, 1],
119 								[1, 1] ],
120 							  [ [1, 1],
121 								[1, 1] ] ];
122 	assert(!ones3d.allZero);
123 }
124 
125 ///
126 pure nothrow @safe unittest {
127 	struct Vec { real x, y; }
128 	const v0 = Vec(0, 0);
129 	assert(v0.zeroed);
130 	const v1 = Vec(1, 1);
131 	assert(!v1.zeroed);
132 }
133 
134 ///
135 pure nothrow @safe unittest {
136 	class Vec
137 	{
138 		this(real x, real y) { this.x = x; this.y = y; }
139 		real x, y;
140 	}
141 	const v0 = new Vec(0, 0);
142 	assert(v0.zeroed);
143 	const v1 = new Vec(1, 1);
144 	assert(!v1.zeroed);
145 }