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 @safe pure nothrow unittest
43 {
44     assert(1.sparseness == 0);
45     assert(0.sparseness == 1);
46     assert(0.0.sparseness == 1);
47     assert(0.1.sparseness == 0);
48     assert(0.0f.sparseness == 1);
49     assert(0.1f.sparseness == 0);
50     alias Q = Rational!ulong;
51     { immutable ubyte[3]    x  = [1, 2, 3];    assert(x[].sparseness == Q(0, 3)); }
52     { immutable float[3]    x  = [1, 2, 3];    assert(x[].sparseness == Q(0, 3)); }
53     // TODO: { immutable ubyte[2][2] x  = [0, 1, 0, 1]; assert(x[].sparseness == Q(2, 4)); }
54     // TODO: immutable ubyte[2][2] x22z = [0, 0, 0, 0]; assert(x22z[].sparseness == Q(4, 4));
55     assert("".sparseness == 1); // TODO: Is this correct?
56     assert(null.sparseness == 1);
57 }
58 
59 /** Returns: Number of Non-Zero Elements in $(D range) at recursion depth $(D
60     depth) defaulting infinite depth (-1). */
61 auto denseness(T)(const scope T x, int depth = -1)
62 	=> 1 - x.sparseness(depth);
63 
64 @safe pure nothrow @nogc unittest
65 {
66     immutable float[3] f = [1, 2, 3];
67     alias Q = Rational!ulong;
68     assert(f[].denseness == Q(1, 1)); // TODO: should this be 3/3?
69     assert(f.denseness == Q(1, 1));   // TODO: should this be 3/3?
70 }