1 module nxt.numeric_ex; 2 3 import std.range: isInputRange, ElementType; 4 import std.traits: CommonType, isFloatingPoint; 5 import std.algorithm: reduce; 6 7 /* import std.numeric: sum; */ 8 9 /** TODO: Issue 4725: Remove when sum is standard in Phobos. 10 See_Also: http://d.puremagic.com/issues/show_bug.cgi?id=4725 11 See_Also: https://github.com/D-Programming-Language/phobos/pull/1205 12 See_Also: http://forum.dlang.org/thread/bug-4725-3@http.d.puremagic.com%2Fissues%2F 13 */ 14 auto sum(Range, SumType = ElementType!Range)(Range range) @safe pure nothrow 15 if (isInputRange!Range) 16 => reduce!"a+b"(0, range); 17 18 /// 19 unittest { assert([1, 2, 3, 4].sum == 10); } 20 21 /++ TODO: Remove when product is standard in Phobos. +/ 22 auto product(Range)(Range range) @safe pure nothrow 23 if (isInputRange!Range) 24 => reduce!"a*b"(1, range); 25 26 /// 27 unittest { assert([1, 2, 3, 4].product == 24); } 28 29 // ============================================================================================== 30 31 version (none) 32 { 33 34 /** Computes $(LUCKY Discrete Signal Entropy) of input range $(D range). 35 */ 36 auto signalEntropy(Range, RequestedBinType = double)(in Range range) @safe pure 37 if (isInputRange!Range && 38 !is(CommonType!(ElementType!Range, F, G) == void)) 39 { 40 enum normalized = true; // we need normalized histogram 41 import nxt.ngram: histogram, Kind, Storage, Symmetry; 42 auto hist = range.histogram!(Kind.saturated, 43 Storage.denseDynamic, 44 Symmetry.ordered, 45 RequestedBinType); 46 import std.numeric: entropy; 47 hist.normalize; 48 return hist[].entropy; 49 } 50 51 /// 52 unittest 53 { 54 const ubyte[] p1 = [ 0, 1, 0, 1 ]; 55 assert(p1.signalEntropy == 1); 56 57 const ubyte[] p2 = [ 0, 1, 2, 3 ]; 58 assert(p2.signalEntropy == 2); 59 60 const ubyte[] p3 = [ 0, 255, 0, 255]; 61 assert(p3.signalEntropy == 1); 62 } 63 64 /** Returns: Element in $(D r ) that minimizes $(D fun). 65 LaTeX: \underset{x}{\arg\min} */ 66 auto argmin_(alias fun, Range)(in Range r) @safe pure 67 if (isInputRange!Range && 68 is(typeof(fun(r.front) < 69 fun(r.front)) == bool)) 70 { 71 auto bestX = r.front; 72 auto bestY = fun(bestX); 73 r.popFront(); 74 foreach (e; r) { 75 auto candY = fun(e); // candidate 76 if (candY > bestY) continue; 77 bestX = e; 78 bestY = candY; 79 } 80 return bestX; 81 } 82 83 /// 84 unittest 85 { 86 assert(argmin!(x => x*x)([1, 2, 3]) == 1); 87 assert(argmin!(x => x*x)([3, 2, 1]) == 1); 88 } 89 90 /** Returns: Element in $(D r ) that maximizes $(D fun). 91 LaTeX: \underset{x}{\arg\max} */ 92 auto argmax_(alias fun, Range)(in Range r) @safe pure 93 if (isInputRange!Range && 94 is(typeof(fun(r.front) > 95 fun(r.front)) == bool)) 96 { 97 auto bestX = r.front; 98 auto bestY = fun(bestX); 99 r.popFront(); 100 foreach (e; r) { 101 auto candY = fun(e); // candidate 102 if (candY < bestY) continue; 103 bestX = e; 104 bestY = candY; 105 } 106 return bestX; 107 } 108 109 /// 110 unittest 111 { 112 assert(argmax!(x => x*x)([1, 2, 3]) == 3); 113 assert(argmax!(x => x*x)([3, 2, 1]) == 3); 114 } 115 116 } 117 118 /** Returns: Element in $(D r ) that minimizes $(D fun). 119 LaTeX: \underset{x}{\arg\min} */ 120 auto argmin(alias fun, Range)(in Range r) 121 @safe pure if (isInputRange!Range && 122 is(typeof(fun(r.front) < fun(r.front)) == bool)) 123 { 124 import std.front: front; 125 return typeof(r.front).max.reduce!((a,b) => fun(a) < fun(b) ? a : b)(r); 126 } 127 128 /// 129 unittest { 130 /* assert(argmin!(x => x*x)([1, 2, 3]) == 1); */ 131 /* assert(argmin!(x => x*x)([3, 2, 1]) == 1); */ 132 } 133 134 /** Returns: Element in $(D r ) that maximizes $(D fun). 135 LaTeX: \underset{x}{\arg\max} */ 136 auto argmax(alias fun, Range)(in Range r) @safe pure 137 if (isInputRange!Range && 138 is(typeof(fun(r.front) > 139 fun(r.front)) == bool)) 140 { 141 import std.front: front; 142 return typeof(r.front).min.reduce!((a,b) => fun(a) > fun(b) ? a : b)(r); 143 } 144 145 /// 146 unittest { 147 /* assert(argmax!(x => x*x)([1, 2, 3]) == 3); */ 148 /* assert(argmax!(x => x*x)([3, 2, 1]) == 3); */ 149 } 150 151 // ============================================================================================== 152 153 /// Returns: 0.0 if x < edge, otherwise it returns 1.0. 154 CommonType!(T1, T2) step(T1, T2)(T1 edge, T2 x) => x < edge ? 0 : 1; 155 156 /// 157 unittest { 158 assert(step(0, 1) == 1.0f); 159 assert(step(0, 10) == 1.0f); 160 assert(step(1, 0) == 0.0f); 161 assert(step(10, 0) == 0.0f); 162 assert(step(1, 1) == 1.0f); 163 } 164 165 // ============================================================================================== 166 167 /** Smoothstep from $(D edge0) to $(D edge1) at $(D x). 168 Returns: 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth 169 hermite interpolation between 0 and 1 when edge0 < x < edge1. 170 This is useful in cases where you would want a threshold function with a smooth transition. 171 */ 172 CommonType!(T1,T2,T3) smoothstep(T1, T2, T3) (T1 edge0, T2 edge1, T3 x) @safe pure nothrow 173 if (isFloatingPoint!(CommonType!(T1,T2,T3))) 174 { 175 import std.algorithm: clamp; 176 x = clamp((x - edge0) / (edge1 - edge0), 0, 1); 177 return x * x * (3 - 2 * x); 178 } 179 180 /// 181 unittest { 182 // assert(smoothstep(1, 0, 2) == 0); 183 assert(smoothstep(1.0, 0.0, 2.0) == 0); 184 assert(smoothstep(1.0, 0.0, 0.5) == 0.5); 185 // assert(almost_equal(smoothstep(0.0, 2.0, 0.5), 0.15625, 0.00001)); 186 } 187 188 /** Smootherstep from $(D edge0) to $(D edge1) at $(D x). */ 189 @safe pure nothrow E smootherstep(E)(E edge0, E edge1, E x) 190 if (isFloatingPoint!(E)) 191 { 192 // Scale, and clamp x to 0..1 range 193 import std.algorithm: clamp; 194 x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); 195 // Evaluate polynomial 196 return x*x*x*(x*(x*6 - 15) + 10); // evaluate polynomial 197 } 198 199 /// 200 unittest { 201 assert(smootherstep(1.0, 0.0, 2.0) == 0); 202 }