The OpenD Programming Language

1 /++
2 $(H2 Generic Piecewise Interpolant)
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.generic;
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     struct PieceInterpolant
26     {
27         int value;
28 
29         this()(int value)
30         {
31             this.value = value;
32         }
33 
34         int opCall(uint derivative : 0, X)(int x0, int x1, X x) const
35         {
36             return value;
37         }
38 
39         enum uint derivativeOrder = 0;
40     }
41 
42     alias S = PieceInterpolant;
43     static immutable x = [0, 1, 2, 3]; // can be also an array of floating point numbers
44     static immutable y = [S(10), S(20), S(30)];
45 
46     auto interpolant = generic(x.rcslice, y.rcslice!(const S));
47 
48     assert(interpolant(-1) == 10);
49     assert(interpolant(0) == 10);
50     assert(interpolant(0.5) == 10);
51 
52     assert(interpolant(1) == 20);
53     assert(interpolant(1.5) == 20);
54 
55     assert(interpolant(2) == 30);
56     assert(interpolant(3) == 30);
57     assert(interpolant(3.4) == 30);
58     assert(interpolant(3) == 30);
59     assert(interpolant(4) == 30);
60 }
61 
62 
63 import core.lifetime: move; 
64 import mir.internal.utility;
65 import mir.functional;
66 import mir.interpolate;
67 import mir.math.common: fmamath;
68 import mir.ndslice.slice;
69 import mir.primitives;
70 import mir.rc.array;
71 import mir.utility: min, max;
72 import std.meta: AliasSeq, staticMap;
73 import std.traits;
74 
75 ///
76 public import mir.interpolate: atInterval;
77 
78 /++
79 Constructs multivariate generic interpolant with nodes on rectilinear grid.
80 
81 Params:
82     grid = `x` values for interpolant
83     values = `f(x)` values for interpolant
84 
85 Constraints:
86     `grid`, `values` must have the same length >= 1
87 
88 Returns: $(LREF Generic)
89 +/
90 Generic!(X, F) generic(X, F)
91     (Slice!(RCI!(immutable X)) grid, Slice!(RCI!(const F)) values)
92 {
93     return typeof(return)(forward!grid, values.move);
94 }
95 
96 /++
97 Multivariate generic interpolant with nodes on rectilinear grid.
98 +/
99 struct Generic(X, F)
100 {
101 @fmamath:
102 
103     /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.)
104     Slice!(RCI!(const F)) _data;
105     /// Grid iterators. $(RED For internal use.)
106     RCI!(immutable X) _grid;
107 
108     bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc
109     {
110         if (rhs._data != this._data)
111             return false;
112         static foreach (d; 0 .. 1)
113             if (gridScopeView!d != rhs.gridScopeView!d)
114                 return false;
115         return true;
116     }
117 
118 extern(D):
119 
120     /++
121     +/
122     this(Slice!(RCI!(immutable X)) grid, Slice!(RCI!(const F)) data) @safe @nogc
123     {
124         import core.lifetime: move;
125         enum  msg_min =  "generic interpolant: minimal allowed length for the grid equals 2.";
126         enum  msg_eq =  "generic interpolant: X length and Y values length + 1 should be equal.";
127         version(D_Exceptions)
128         {
129             static immutable exc_min = new Exception(msg_min);
130             static immutable exc_eq = new Exception(msg_eq);
131         }
132         if (grid.length < 2)
133         {
134             version(D_Exceptions) { import mir.exception : toMutable; throw exc_min.toMutable; }
135             else assert(0, msg_min);
136         }
137         if (grid.length != data._lengths[0] + 1)
138         {
139             version(D_Exceptions) { import mir.exception : toMutable; throw exc_eq.toMutable; }
140             else assert(0, msg_eq);
141         }
142         _grid = move(grid._iterator);
143         _data = move(data);
144     }
145 
146 @trusted:
147 
148     ///
149     Generic lightConst()() const @property { return *cast(Generic*)&this; }
150 
151     ///
152     Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() return scope const @property
153         if (dimension == 0)
154     {
155         return _grid.lightConst.sliced(_data._lengths[0]);
156     }
157 
158     ///
159     immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted
160         if (dimension == 0)
161     {
162         return _grid._iterator[0 .. _data._lengths[0]];
163     }
164 
165     /++
166     Returns: intervals count.
167     +/
168     size_t intervalCount(size_t dimension = 0)() scope const @property
169         if (dimension == 0)
170     {
171         assert(_data._lengths[0] > 1);
172         return _data._lengths[0] - 0;
173     }
174 
175     ///
176     size_t[1] gridShape()() scope const @property
177     {
178         return _data.shape;
179     }
180 
181     ///
182     enum uint derivativeOrder = F.derivativeOrder;
183 
184     ///
185     template opCall(uint derivative = 0)
186         if (derivative == 0)
187     {
188         /++
189         `(x)` operator.
190         Complexity:
191             `O(log(grid.length))`
192         +/
193         auto opCall(X)(in X x) const
194         {
195             return opCall!(X)(Tuple!(size_t, X)(this.findInterval(x), x));
196         }
197 
198         ///
199         auto opCall(X)(Tuple!(size_t, X) tuple) const
200         {
201             X x = tuple[1];
202             size_t idx = tuple[0];
203             return _data[idx].opCall!derivative(_grid[idx], _grid[idx + 1], x);
204         }
205     }
206 }