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 }