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 { 131 /* assert(argmin!(x => x*x)([1, 2, 3]) == 1); */ 132 /* assert(argmin!(x => x*x)([3, 2, 1]) == 1); */ 133 } 134 135 /** Returns: Element in $(D r ) that maximizes $(D fun). 136 LaTeX: \underset{x}{\arg\max} */ 137 auto argmax(alias fun, Range)(in Range r) @safe pure 138 if (isInputRange!Range && 139 is(typeof(fun(r.front) > 140 fun(r.front)) == bool)) 141 { 142 import std.front: front; 143 return typeof(r.front).min.reduce!((a,b) => fun(a) > fun(b) ? a : b)(r); 144 } 145 146 /// 147 unittest 148 { 149 /* assert(argmax!(x => x*x)([1, 2, 3]) == 3); */ 150 /* assert(argmax!(x => x*x)([3, 2, 1]) == 3); */ 151 } 152 153 // ============================================================================================== 154 155 /// Returns: 0.0 if x < edge, otherwise it returns 1.0. 156 CommonType!(T1, T2) step(T1, T2)(T1 edge, T2 x) => x < edge ? 0 : 1; 157 158 /// 159 unittest 160 { 161 assert(step(0, 1) == 1.0f); 162 assert(step(0, 10) == 1.0f); 163 assert(step(1, 0) == 0.0f); 164 assert(step(10, 0) == 0.0f); 165 assert(step(1, 1) == 1.0f); 166 } 167 168 // ============================================================================================== 169 170 /** Smoothstep from $(D edge0) to $(D edge1) at $(D x). 171 Returns: 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth 172 hermite interpolation between 0 and 1 when edge0 < x < edge1. 173 This is useful in cases where you would want a threshold function with a smooth transition. 174 */ 175 CommonType!(T1,T2,T3) smoothstep(T1, T2, T3) (T1 edge0, T2 edge1, T3 x) @safe pure nothrow 176 if (isFloatingPoint!(CommonType!(T1,T2,T3))) 177 { 178 import std.algorithm: clamp; 179 x = clamp((x - edge0) / (edge1 - edge0), 0, 1); 180 return x * x * (3 - 2 * x); 181 } 182 183 /// 184 unittest 185 { 186 // assert(smoothstep(1, 0, 2) == 0); 187 assert(smoothstep(1.0, 0.0, 2.0) == 0); 188 assert(smoothstep(1.0, 0.0, 0.5) == 0.5); 189 // assert(almost_equal(smoothstep(0.0, 2.0, 0.5), 0.15625, 0.00001)); 190 } 191 192 /** Smootherstep from $(D edge0) to $(D edge1) at $(D x). */ 193 @safe pure nothrow E smootherstep(E)(E edge0, E edge1, E x) 194 if (isFloatingPoint!(E)) 195 { 196 // Scale, and clamp x to 0..1 range 197 import std.algorithm: clamp; 198 x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); 199 // Evaluate polynomial 200 return x*x*x*(x*(x*6 - 15) + 10); // evaluate polynomial 201 } 202 203 /// 204 unittest 205 { 206 assert(smootherstep(1.0, 0.0, 2.0) == 0); 207 }