@@ -16,6 +16,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
1616module mir.timeseries ;
1717
1818public import mir.ndslice.slice;
19+ import std.traits ;
1920
2021// /
2122version (mir_test) unittest
@@ -111,7 +112,7 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
111112 }
112113
113114 // /
114- bool opEquals ()(typeof (this ) rhs)
115+ bool opEquals ()(const typeof (this ) rhs) const
115116 {
116117 return this .time == rhs.time && this .data == rhs.data;
117118 }
@@ -122,7 +123,19 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
122123 `TimeIterator` is an iterator on top of date, date-time, time, or integer types.
123124 For example, `Date*`, `DateTime*`, `immutable(long)*`, `mir.ndslice.iterator.IotaIterator`.
124125 +/
125- Slice! (Contiguous, [1 ], TimeIterator) time() @property @trusted
126+ auto time ()() @property @trusted
127+ {
128+ return _time.sliced(_data._lengths[0 ]);
129+ }
130+
131+ // / ditto
132+ auto time ()() @property @trusted const
133+ {
134+ return _time.sliced(_data._lengths[0 ]);
135+ }
136+
137+ // / ditto
138+ auto time ()() @property @trusted immutable
126139 {
127140 return _time.sliced(_data._lengths[0 ]);
128141 }
@@ -131,11 +144,23 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
131144 Data is any ndslice with only one constraints,
132145 `data` and `time` lengths should be equal.
133146 +/
134- Slice ! (kind, packs, Iterator) data() @property @trusted
147+ auto data () () @property @trusted
135148 {
136149 return _data;
137150 }
138151
152+ // / ditto
153+ auto data ()() @property @trusted const
154+ {
155+ return _data[];
156+ }
157+
158+ // / ditto
159+ auto data ()() @property @trusted immutable
160+ {
161+ return _data[];
162+ }
163+
139164 /+ +
140165 Special `[] =` index-assign operator for time-series.
141166 Assigns data from `r` with time intersection.
@@ -205,7 +230,7 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
205230 if (lf == rf)
206231 {
207232 E:
208- static if (isSlice ! ( typeof (l.data.front)) )
233+ static if (packs != [ 1 ] )
209234 mixin (" l.data.front[] " ~ op ~ " = r.data.front;" );
210235 else
211236 mixin (" l.data.front " ~ op ~ " = r.data.front;" );
@@ -253,6 +278,13 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
253278 return this [0 .. time.assumeSorted.lowerBound! sp(moment).length];
254279 }
255280
281+ // / ditto
282+ auto lowerBound (SearchPolicy sp = SearchPolicy.binarySearch, Time )(Time moment) const
283+ {
284+ return this [0 .. time.assumeSorted.lowerBound! sp(moment).length];
285+ }
286+
287+
256288 /+ +
257289 This function uses a search with policy sp to find the largest left subrange on which
258290 `t > moment` is true for all `t`.
@@ -263,6 +295,12 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
263295 return this [$ - time.assumeSorted.upperBound! sp(moment).length .. $];
264296 }
265297
298+ // / ditto
299+ auto upperBound (SearchPolicy sp = SearchPolicy.binarySearch, Time )(Time moment) const
300+ {
301+ return this [$ - time.assumeSorted.upperBound! sp(moment).length .. $];
302+ }
303+
266304 // /
267305 bool contains (Time )(Time moment)
268306 {
@@ -412,6 +450,49 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
412450 auto opIndex (Slices... )(Slices slices)
413451 if (allSatisfy! (templateOr! (is_Slice, isIndex), Slices))
414452 {
453+ static if (Slices.length == 0 )
454+ {
455+ return this ;
456+ }
457+ else
458+ static if (is_Slice! (Slices[0 ]))
459+ {
460+ return time[slices[0 ]].series(data[slices]);
461+ }
462+ else
463+ {
464+ return time[slices[0 ]].observation(data[slices]);
465+ }
466+ }
467+
468+ // / ditto
469+ auto opIndex (Slices... )(Slices slices) const
470+ if (allSatisfy! (templateOr! (is_Slice, isIndex), Slices))
471+ {
472+ static if (Slices.length == 0 )
473+ {
474+ return time.series(_data[]);
475+ }
476+ else
477+ static if (is_Slice! (Slices[0 ]))
478+ {
479+ return time[slices[0 ]].series(data[slices]);
480+ }
481+ else
482+ {
483+ return time[slices[0 ]].observation(data[slices]);
484+ }
485+ }
486+
487+ // / ditto
488+ auto opIndex (Slices... )(Slices slices) immutable
489+ if (allSatisfy! (templateOr! (is_Slice, isIndex), Slices))
490+ {
491+ static if (Slices.length == 0 )
492+ {
493+ return time.series(_data[]);
494+ }
495+ else
415496 static if (is_Slice! (Slices[0 ]))
416497 {
417498 return time[slices[0 ]].series(data[slices]);
@@ -435,6 +516,7 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
435516 auto time = [1 , 2 , 3 , 4 ].sliced;
436517 auto data = [2.1 , 3.4 , 5.6 , 7.8 ].sliced;
437518 auto series = time.series(data);
519+ const cseries = series;
438520
439521 assert (series.contains(2 ));
440522 assert ( ()@trusted { return (2 in series) is &data[1 ]; }() );
@@ -445,6 +527,17 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
445527 assert (series.lowerBound(2 ) == series[0 .. 1 ]);
446528 assert (series.upperBound(2 ) == series[2 .. $]);
447529
530+ assert (cseries.lowerBound(2 ) == cseries[0 .. 1 ]);
531+ assert (cseries.upperBound(2 ) == cseries[2 .. $]);
532+
533+ // slicing type deduction for const / immutable series
534+ static assert (is (typeof (series[]) ==
535+ Series! (int * , cast (SliceKind)2 , [1LU], double * )));
536+ static assert (is (typeof (cseries[]) ==
537+ Series! (const (int )* , cast (SliceKind)2 , [1LU], const (double )* )));
538+ static assert (is (typeof ((cast (immutable ) series)[]) ==
539+ Series! (immutable (int )* , cast (SliceKind)2 , [1LU], immutable (double )* )));
540+
448541 // / slicing
449542 auto seriesSlice = series[1 .. $ - 1 ];
450543 assert (seriesSlice.time == time[1 .. $ - 1 ]);
0 commit comments