1 /** Sparseness and denseness of ranges.
2  */
3 module nxt.nesses;
4 
5 import nxt.rational: Rational;
6 import std.traits : isIterable, isFloatingPoint;
7 
8 /** Returns: number of default-initialized (zero) elements in $(D x) at
9  * recursion depth $(D depth).
10  *
11  * Depth defaults -1 meaning infinite depth.
12  */
13 Rational!ulong sparseness(T)(const scope T x,
14 							 int depth = -1)
15 {
16 	alias R = typeof(return); // rational shorthand
17 	static if (isIterable!T)
18 	{
19 		import std.range: empty;
20 		immutable isEmpty = x.empty;
21 		if (isEmpty || depth == 0)
22 			return R(isEmpty, 1);
23 		else
24 		{
25 			immutable nextDepth = (depth == -1 ? depth : depth - 1);
26 			ulong nums, denoms;
27 			foreach (const ref elt; x)
28 			{
29 				const sub = elt.sparseness(nextDepth);
30 				nums += sub.numerator;
31 				denoms += sub.denominator;
32 			}
33 			return R(nums, denoms);
34 		}
35 	}
36 	else static if (isFloatingPoint!T)
37 		return R(x == 0, 1); // explicit zero because T.init is nan here
38 	else
39 		return R(x == T.init, 1);
40 }
41 
42 pure nothrow @safe unittest {
43 	assert(1.sparseness == 0);
44 	assert(0.sparseness == 1);
45 	assert(0.0.sparseness == 1);
46 	assert(0.1.sparseness == 0);
47 	assert(0.0f.sparseness == 1);
48 	assert(0.1f.sparseness == 0);
49 	alias Q = Rational!ulong;
50 	{ immutable ubyte[3]	x  = [1, 2, 3];	assert(x[].sparseness == Q(0, 3)); }
51 	{ immutable float[3]	x  = [1, 2, 3];	assert(x[].sparseness == Q(0, 3)); }
52 	/+ TODO: { immutable ubyte[2][2] x  = [0, 1, 0, 1]; assert(x[].sparseness == Q(2, 4)); } +/
53 	/+ TODO: immutable ubyte[2][2] x22z = [0, 0, 0, 0]; assert(x22z[].sparseness == Q(4, 4)); +/
54 	assert("".sparseness == 1); /+ TODO: Is this correct? +/
55 	assert(null.sparseness == 1);
56 }
57 
58 /** Returns: Number of Non-Zero Elements in $(D range) at recursion depth $(D
59 	depth) defaulting infinite depth (-1). */
60 auto denseness(T)(const scope T x, int depth = -1)
61 	=> 1 - x.sparseness(depth);
62 
63 pure nothrow @safe @nogc unittest {
64 	immutable float[3] f = [1, 2, 3];
65 	alias Q = Rational!ulong;
66 	assert(f[].denseness == Q(1, 1)); /+ TODO: should this be 3/3? +/
67 	assert(f.denseness == Q(1, 1));   /+ TODO: should this be 3/3? +/
68 }