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         {
23             return R(isEmpty, 1);
24         }
25         else
26         {
27             immutable nextDepth = (depth == -1 ? depth : depth - 1);
28             ulong nums, denoms;
29             foreach (const ref elt; x)
30             {
31                 const sub = elt.sparseness(nextDepth);
32                 nums += sub.numerator;
33                 denoms += sub.denominator;
34             }
35             return R(nums, denoms);
36         }
37     }
38     else static if (isFloatingPoint!T)
39     {
40         return R(x == 0, 1); // explicit zero because T.init is nan here
41     }
42     else
43     {
44         return R(x == T.init, 1);
45     }
46 }
47 
48 @safe pure nothrow unittest
49 {
50     assert(1.sparseness == 0);
51     assert(0.sparseness == 1);
52     assert(0.0.sparseness == 1);
53     assert(0.1.sparseness == 0);
54     assert(0.0f.sparseness == 1);
55     assert(0.1f.sparseness == 0);
56     alias Q = Rational!ulong;
57     { immutable ubyte[3]    x  = [1, 2, 3];    assert(x[].sparseness == Q(0, 3)); }
58     { immutable float[3]    x  = [1, 2, 3];    assert(x[].sparseness == Q(0, 3)); }
59     // TODO { immutable ubyte[2][2] x  = [0, 1, 0, 1]; assert(x[].sparseness == Q(2, 4)); }
60     // TODO immutable ubyte[2][2] x22z = [0, 0, 0, 0]; assert(x22z[].sparseness == Q(4, 4));
61     assert("".sparseness == 1); // TODO Is this correct?
62     assert(null.sparseness == 1);
63 }
64 
65 /** Returns: Number of Non-Zero Elements in $(D range) at recursion depth $(D
66     depth) defaulting infinite depth (-1). */
67 auto denseness(T)(const scope T x, int depth = -1)
68 {
69     return 1 - x.sparseness(depth);
70 }
71 
72 @safe pure nothrow @nogc unittest
73 {
74     immutable float[3] f = [1, 2, 3];
75     alias Q = Rational!ulong;
76     assert(f[].denseness == Q(1, 1)); // TODO should this be 3/3?
77     assert(f.denseness == Q(1, 1));   // TODO should this be 3/3?
78 }