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