1 /++ 2 Polynomial ref-counted structure. 3 4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 Authors: Ilia Ki 6 +/ 7 module mir.polynomial; 8 9 import mir.math.common: fmamath; 10 import mir.rc.array; 11 12 @fmamath: 13 14 /++ 15 Polynomial callable ref-counted structure. 16 +/ 17 struct Polynomial(F) 18 { 19 /// 20 RCArray!(const F) coefficients; 21 22 /++ 23 Params: 24 coefficients = coefficients `c[i]` for polynomial function `f(x)=c[0]+c[1]*x^^1+...+c[n]*x^^n` 25 +/ 26 this(RCArray!(const F) coefficients) 27 { 28 import core.lifetime: move; 29 this.coefficients = coefficients.move; 30 } 31 32 /++ 33 Params: 34 derivative = derivative order 35 +/ 36 template opCall(uint derivative = 0) 37 { 38 /++ 39 Params: 40 x = `x` point 41 +/ 42 @fmamath typeof(F.init * X.init * 1f + F.init) opCall(X)(in X x) const 43 { 44 return x.poly!derivative(this.coefficients[]); 45 } 46 } 47 } 48 49 /// ditto 50 Polynomial!F polynomial(F)(RCArray!(const F) coefficients) 51 { 52 import core.lifetime: move; 53 return typeof(return)(coefficients.move); 54 } 55 56 /// 57 version (mir_test) @safe pure nothrow @nogc unittest 58 { 59 import mir.test; 60 import mir.rc.array; 61 auto a = rcarray!(const double)(3.0, 4.5, 1.9, 2); 62 auto p = a.polynomial; 63 64 alias f = (x) => 3.0 + 4.5 * x^^1 + 1.9 * x^^2 + 2 * x^^3; 65 alias df = (x) => 4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2; 66 alias d2f = (x) => 2 * 1.9 + 6 * 2 * x^^1; 67 68 p(3.3).shouldApprox == f(3.3); 69 p(7.2).shouldApprox == f(7.2); 70 71 p.opCall!1(3.3).shouldApprox == df(3.3); 72 p.opCall!1(7.2).shouldApprox == df(7.2); 73 74 p.opCall!2(3.3).shouldApprox == d2f(3.3); 75 p.opCall!2(7.2).shouldApprox == d2f(7.2); 76 } 77 78 /++ 79 Evaluate polynomial. 80 81 Coefficients assumed to be in the order a0 + a1 * x ^^ 1 + ... + aN * x ^^ N 82 83 Params: 84 F = controls type of output 85 derivative = order of derivatives (default = 0) 86 87 Returns: 88 Value of the polynomial, evaluated at `x` 89 90 See_also: 91 $(WEB en.wikipedia.org/wiki/Polynomial, Polynomial). 92 +/ 93 template poly(uint derivative = 0) 94 { 95 import std.traits: ForeachType; 96 /++ 97 Params: 98 x = value to evaluate 99 coefficients = coefficients of polynomial 100 +/ 101 @fmamath typeof(F.init * X.init * 1f + F.init) poly(X, F)(in X x, scope const F[] coefficients...) 102 { 103 import mir.internal.utility: Iota; 104 auto ret = cast(typeof(return))0; 105 if (coefficients.length > 0) 106 { 107 ptrdiff_t i = coefficients.length - 1; 108 assert(i >= 0); 109 auto c = cast()coefficients[i]; 110 static foreach (d; Iota!derivative) 111 c *= i - d; 112 ret = cast(typeof(return)) c; 113 while (--i >= cast(ptrdiff_t)derivative) 114 { 115 assert(i < coefficients.length); 116 c = cast()coefficients[i]; 117 static foreach (d; Iota!derivative) 118 c *= i - d; 119 ret *= x; 120 ret += c; 121 } 122 } 123 return ret; 124 } 125 } 126 127 /// 128 version (mir_test) @safe pure nothrow unittest 129 { 130 import mir.math.common: approxEqual; 131 132 double[] x = [3.0, 4.5, 1.9, 2]; 133 134 alias f = (x) => 3.0 + 4.5 * x^^1 + 1.9 * x^^2 + 2 * x^^3; 135 alias df = (x) => 4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2; 136 alias d2f = (x) => 2 * 1.9 + 6 * 2 * x^^1; 137 138 assert(poly(3.3, x).approxEqual(f(3.3))); 139 assert(poly(7.2, x).approxEqual(f(7.2))); 140 141 assert(poly!1(3.3, x).approxEqual(df(3.3))); 142 assert(poly!1(7.2, x).approxEqual(df(7.2))); 143 144 assert(poly!2(3.3, x).approxEqual(d2f(3.3))); 145 assert(poly!2(7.2, x).approxEqual(d2f(7.2))); 146 } 147 148 // static array test 149 version (mir_test) @safe pure @nogc nothrow unittest 150 { 151 import mir.math.common: approxEqual; 152 153 double[4] x = [3.0, 4.5, 1.9, 2]; 154 155 alias f = (x) => 3.0 + 4.5 * x^^1 + 1.9 * x^^2 + 2 * x^^3; 156 alias df = (x) => 4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2; 157 alias d2f = (x) => 2 * 1.9 + 6 * 2 * x^^1; 158 159 assert(poly(3.3, x).approxEqual(f(3.3))); 160 assert(poly(7.2, x).approxEqual(f(7.2))); 161 162 assert(poly!1(3.3, x).approxEqual(df(3.3))); 163 assert(poly!1(7.2, x).approxEqual(df(7.2))); 164 165 assert(poly!2(3.3, x).approxEqual(d2f(3.3))); 166 assert(poly!2(7.2, x).approxEqual(d2f(7.2))); 167 } 168 169 // Check coefficient.length = 3 170 version (mir_test) @safe pure nothrow unittest 171 { 172 import mir.math.common: approxEqual; 173 174 double[] x = [3.0, 4.5, 1.9]; 175 176 alias f = (x) => 3.0 + 4.5 * x^^1 + 1.9 * x^^2; 177 alias df = (x) => 4.5 + 2 * 1.9 * x^^1; 178 alias d2f = (x) => 2 * 1.9; 179 180 assert(poly(3.3, x).approxEqual(f(3.3))); 181 assert(poly(7.2, x).approxEqual(f(7.2))); 182 183 assert(poly!1(3.3, x).approxEqual(df(3.3))); 184 assert(poly!1(7.2, x).approxEqual(df(7.2))); 185 186 assert(poly!2(3.3, x).approxEqual(d2f(3.3))); 187 assert(poly!2(7.2, x).approxEqual(d2f(7.2))); 188 } 189 190 // Check coefficient.length = 2 191 version (mir_test) @safe pure nothrow unittest 192 { 193 import mir.math.common: approxEqual; 194 195 double[] x = [3.0, 4.5]; 196 197 alias f = (x) => 3.0 + 4.5 * x^^1; 198 alias df = (x) => 4.5; 199 alias d2f = (x) => 0.0; 200 201 assert(poly(3.3, x).approxEqual(f(3.3))); 202 assert(poly(7.2, x).approxEqual(f(7.2))); 203 204 assert(poly!1(3.3, x).approxEqual(df(3.3))); 205 assert(poly!1(7.2, x).approxEqual(df(7.2))); 206 207 assert(poly!2(3.3, x).approxEqual(d2f(3.3))); 208 assert(poly!2(7.2, x).approxEqual(d2f(7.2))); 209 } 210 211 // Check coefficient.length = 1 212 version (mir_test) @safe pure nothrow unittest 213 { 214 import mir.math.common: approxEqual; 215 216 double[] x = [3.0]; 217 218 alias f = (x) => 3.0; 219 alias df = (x) => 0.0; 220 221 assert(poly(3.3, x).approxEqual(f(3.3))); 222 assert(poly(7.2, x).approxEqual(f(7.2))); 223 224 assert(poly!1(3.3, x).approxEqual(df(3.3))); 225 assert(poly!1(7.2, x).approxEqual(df(7.2))); 226 }