1 /++ 2 $(H2 Constant Interpolation) 3 4 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate) 5 6 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 8 Authors: Ilia Ki 9 10 Macros: 11 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP) 12 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 13 +/ 14 module mir.interpolate.constant; 15 16 @fmamath: 17 18 /// 19 version(mir_test) 20 @safe pure @nogc unittest 21 { 22 import mir.ndslice; 23 import mir.math.common: approxEqual; 24 25 static immutable x = [0, 1, 2, 3]; 26 static immutable y = [10, 20, 30, 40]; 27 28 auto interpolant = constant!int(x.rcslice, y.rcslice!(const int)); 29 30 assert(interpolant(-1) == 10); 31 assert(interpolant(0) == 10); 32 assert(interpolant(0.5) == 10); 33 34 assert(interpolant(1) == 20); 35 36 assert(interpolant(3) == 40); 37 assert(interpolant(4) == 40); 38 } 39 40 41 import core.lifetime: move; 42 import mir.internal.utility; 43 import mir.functional; 44 import mir.interpolate; 45 import mir.math.common: fmamath; 46 import mir.ndslice.slice; 47 import mir.primitives; 48 import mir.rc.array; 49 import mir.utility: min, max; 50 import std.meta: AliasSeq, staticMap; 51 import std.traits; 52 53 /// 54 public import mir.interpolate: atInterval; 55 56 /++ 57 Constructs multivariate constant interpolant with nodes on rectilinear grid. 58 59 Params: 60 grid = `x` values for interpolant 61 values = `f(x)` values for interpolant 62 63 Constraints: 64 `grid`, `values` must have the same length >= 1 65 66 Returns: $(LREF Constant) 67 +/ 68 Constant!(F, N, X) constant(F, size_t N = 1, X = F) 69 (Repeat!(N, Slice!(RCI!(immutable X))) grid, Slice!(RCI!(const F), N) values) 70 { 71 return typeof(return)(forward!grid, values.move); 72 } 73 74 /++ 75 Multivariate constant interpolant with nodes on rectilinear grid. 76 +/ 77 struct Constant(F, size_t N = 1, X = F) 78 if (N && N <= 6) 79 { 80 @fmamath: 81 82 /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.) 83 Slice!(RCI!(const F), N) _data; 84 /// Grid iterators. $(RED For internal use.) 85 Repeat!(N, RCI!(immutable X)) _grid; 86 87 extern(D): 88 89 bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc 90 { 91 if (rhs._data != this._data) 92 return false; 93 static foreach (d; 0 .. N) 94 if (gridScopeView!d != rhs.gridScopeView!d) 95 return false; 96 return true; 97 } 98 99 /++ 100 +/ 101 this(Repeat!(N, Slice!(RCI!(immutable X))) grid, Slice!(RCI!(const F), N) data) @safe @nogc 102 { 103 enum msg_min = "constant interpolant: minimal allowed length for the grid equals 1."; 104 enum msg_eq = "constant interpolant: X and Y values length should be equal."; 105 version(D_Exceptions) 106 { 107 static immutable exc_min = new Exception(msg_min); 108 static immutable exc_eq = new Exception(msg_eq); 109 } 110 foreach(i, ref x; grid) 111 { 112 if (x.length < 1) 113 { 114 version(D_Exceptions) { import mir.exception : toMutable; throw exc_min.toMutable; } 115 else assert(0, msg_min); 116 } 117 if (x.length != data._lengths[i]) 118 { 119 version(D_Exceptions) { import mir.exception : toMutable; throw exc_eq.toMutable; } 120 else assert(0, msg_eq); 121 } 122 _grid[i] = x._iterator; 123 } 124 _data = data; 125 } 126 127 @trusted: 128 129 /// 130 Constant lightConst()() const @property { return *cast(Constant*)&this; } 131 132 /// 133 Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() return scope const @property 134 if (dimension < N) 135 { 136 return _grid[dimension].lightConst.sliced(_data._lengths[dimension]); 137 } 138 139 /// 140 immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted 141 if (dimension < N) 142 { 143 return _grid[dimension]._iterator[0 .. _data._lengths[dimension]]; 144 } 145 146 /++ 147 Returns: intervals count. 148 +/ 149 size_t intervalCount(size_t dimension = 0)() scope const @property 150 { 151 assert(_data._lengths[dimension] > 0); 152 return _data._lengths[dimension] - 0; 153 } 154 155 /// 156 size_t[N] gridShape()() scope const @property 157 { 158 return _data.shape; 159 } 160 161 /// 162 enum uint derivativeOrder = 0; 163 164 /// 165 enum uint dimensionCount = N; 166 167 /// 168 template opCall(uint derivative = 0) 169 // if (derivative <= derivativeOrder) 170 { 171 /++ 172 `(x)` operator. 173 Complexity: 174 `O(log(grid.length))` 175 +/ 176 auto opCall(X...)(in X xs) scope const @trusted 177 if (X.length == N) 178 { 179 size_t[N] indices; 180 foreach(i; Iota!N) 181 { 182 static if (isInterval!(typeof(xs[i]))) 183 indices[i] = xs[i][1]; 184 else 185 indices[i] = _data._lengths[i] > 1 ? this.findInterval!i(xs[i]) : 0; 186 } 187 static if (derivative == 0) 188 { 189 return _data[indices]; 190 } 191 else 192 { 193 F[derivative + 1] ret = 0; 194 ret[0] = _data[indices]; 195 return ret; 196 } 197 } 198 } 199 } 200 201 /++ 202 Single value interpolation 203 +/ 204 SingleConstant!F singleConstant(F)(const F value) 205 { 206 return typeof(return)(value); 207 } 208 209 /// ditto 210 struct SingleConstant(F, X = F) 211 { 212 /// 213 enum uint derivativeOrder = 0; 214 215 /// 216 enum uint dimensionCount = 1; 217 218 /// 219 F value; 220 221 /// 222 this(F value) 223 { 224 this.value = value; 225 } 226 227 228 /// 229 immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted 230 if (dimension == 0) 231 { 232 return null; 233 } 234 235 /// 236 template opCall(uint derivative = 0) 237 { 238 /++ 239 `(x)` operator. 240 Complexity: 241 `O(1)` 242 +/ 243 auto opCall(X...)(in X xs) scope const @trusted 244 { 245 static if (derivative == 0) 246 { 247 return F(value); 248 } 249 else 250 { 251 F[derivative + 1] ret = 0; 252 ret[0] = value; 253 return ret; 254 } 255 } 256 } 257 } 258 259 /// 260 @safe pure nothrow 261 version (mir_test) 262 unittest 263 { 264 auto sc = singleConstant(34.1); 265 assert(sc(100) == 34.1); 266 assert(sc.opCall!2(100) == [34.1, 0, 0]); 267 } 268 269 /++ 270 Interpolator used for non-rectiliner trapezoid-like greeds. 271 Params: 272 grid = rc-array of interpolation grid 273 data = rc-array of interpolator-like structures 274 +/ 275 auto metaSingleConstant(T)(T data) 276 { 277 import core.lifetime: move; 278 return MetaSingleConstant!T(data.move); 279 } 280 281 /// ditto 282 struct MetaSingleConstant(T, X = double) 283 // if (T.derivativeOrder >= 1) 284 { 285 import mir.interpolate.utility: DeepType; 286 287 /// 288 T data; 289 290 /// 291 this(T data) 292 { 293 import core.lifetime: move; 294 this.data = data.move; 295 } 296 297 /// 298 MetaSingleConstant lightConst()() const @property { return *cast(MetaSingleConstant*)&this; } 299 300 /// 301 immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted 302 if (dimension == 0) 303 { 304 return null; 305 } 306 307 /// 308 enum uint derivativeOrder = 1; 309 310 static if (__traits(compiles, {enum N = T.dimensionCount;})) 311 /// 312 enum uint dimensionCount = T.dimensionCount + 1; 313 314 /// 315 template opCall(uint derivative = 0) 316 { 317 /++ 318 `(x)` operator. 319 Complexity: 320 `O(log(grid.length))` 321 +/ 322 auto opCall(X...)(const X xs) scope const @trusted 323 if (xs.length >= 1) 324 { 325 static if (derivative == 0) 326 { 327 return data(xs[1.. $]); 328 } 329 else 330 { 331 auto iv = data.opCall!derivative(xs[1.. $]); 332 typeof(iv)[derivative + 1] ret = 0; 333 ret[0] = iv; 334 return ret; 335 } 336 } 337 } 338 } 339 340 /// Ignores the first dimension parameter 341 version(mir_test) 342 unittest 343 { 344 import mir.interpolate.linear; 345 346 auto x = [0.0, 1, 2, 3, 5]; 347 auto y = [4.0, 0, 9, 23, 40]; 348 349 auto g = [7.0, 10, 15]; 350 351 import mir.ndslice.allocation: rcslice; 352 353 auto d = linear!double(x.rcslice!(immutable double), y.rcslice!(const double)); 354 355 auto ignoresFirstDim = d.metaSingleConstant; 356 357 assert(ignoresFirstDim(9.0, 1.8) == d(1.8)); 358 assert(ignoresFirstDim.opCall!1(9.0, 1.8) == [d.opCall!1(1.8), [0.0, 0.0]]); 359 }