@@ -243,6 +243,50 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
243243 assert (series.data == [10 , 12 , 10 , 13 ]);
244244 }
245245
246+ /+ +
247+ This function uses a search with policy sp to find the largest left subrange on which
248+ `t < moment` is true for all `t`.
249+ The search schedule and its complexity are documented in `std.range.SearchPolicy`.
250+ +/
251+ auto lowerBound (SearchPolicy sp = SearchPolicy.binarySearch, Time )(Time moment)
252+ {
253+ return this [0 .. time.assumeSorted.lowerBound! sp(moment).length];
254+ }
255+
256+ /+ +
257+ This function uses a search with policy sp to find the largest left subrange on which
258+ `t > moment` is true for all `t`.
259+ The search schedule and its complexity are documented in `std.range.SearchPolicy`.
260+ +/
261+ auto upperBound (SearchPolicy sp = SearchPolicy.binarySearch, Time )(Time moment)
262+ {
263+ return this [$ - time.assumeSorted.upperBound! sp(moment).length .. $];
264+ }
265+
266+ // /
267+ bool contains (Time )(Time moment)
268+ {
269+ size_t idx = time.assumeSorted.lowerBound(moment).length;
270+ return idx < _data._lengths[0 ] && time[idx] == moment;
271+ }
272+
273+ // /
274+ auto opBinaryRight (string op : " in" , Time )(Time moment)
275+ {
276+ size_t idx = time.assumeSorted.lowerBound(moment).length;
277+ bool cond = idx < _data._lengths[0 ] && time[idx] == moment;
278+ static if (__traits(compiles, &_data[size_t .init]))
279+ {
280+ if (cond)
281+ return &_data[idx];
282+ return null ;
283+ }
284+ else
285+ {
286+ return bool (cond);
287+ }
288+ }
289+
246290 // / ndslice-like primitives
247291 bool empty (size_t dimension = 0 )() const @property
248292 if (dimension < packs[0 ])
@@ -378,36 +422,11 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
378422 }
379423 }
380424
381- /+ +
382- This function uses a search with policy sp to find the largest left subrange on which
383- `t < moment` is true for all `t`.
384- The search schedule and its complexity are documented in `std.range.SearchPolicy`.
385- +/
386- auto lowerBound (SearchPolicy sp = SearchPolicy.binarySearch, Time )(Time moment)
387- {
388- return this [0 .. time.assumeSorted.lowerBound! sp(moment).length];
389- }
390-
391- /+ +
392- This function uses a search with policy sp to find the largest left subrange on which
393- `t > moment` is true for all `t`.
394- The search schedule and its complexity are documented in `std.range.SearchPolicy`.
395- +/
396- auto upperBound (SearchPolicy sp = SearchPolicy.binarySearch, Time )(Time moment)
397- {
398- return this [$ - time.assumeSorted.upperBound! sp(moment).length .. $];
399- }
400-
401425 // / ditto
402426 auto save ()() @property
403427 {
404428 return this ;
405429 }
406-
407- bool contains (Time )(Time momemt)
408- {
409- return time.assumeSorted.contains(momemt);
410- }
411430}
412431
413432// / 1-dimensional data
@@ -418,7 +437,10 @@ struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)
418437 auto series = time.series(data);
419438
420439 assert (series.contains(2 ));
440+ assert ( ()@trusted { return (2 in series) is &data[1 ]; }() );
441+
421442 assert (! series.contains(5 ));
443+ assert ( ()@trusted { return (5 in series) is null ; }() );
422444
423445 assert (series.lowerBound(2 ) == series[0 .. 1 ]);
424446 assert (series.upperBound(2 ) == series[2 .. $]);
@@ -503,6 +525,72 @@ enum isSeries(U : Series!(TimeIterator, kind, packs, Iterator), TimeIterator, Sl
503525// / ditto
504526enum isSeries (U) = (size_t []).init;
505527
528+
529+ /+ +
530+ Finds an index such that `series.time[index] == moment`.
531+
532+ Params:
533+ series = timeseries
534+ moment = time moment to find in the series
535+ Returns:
536+ `size_t.max` if the series does not contain the moment and appropriate index otherwise.
537+ +/
538+ size_t findIndex (TimeIterator, SliceKind kind, size_t [] packs, Iterator, Time )(Series! (TimeIterator, kind, packs, Iterator) series, Time moment)
539+ {
540+ import std.range : assumeSorted;
541+
542+ auto idx = series.time.assumeSorted.lowerBound(moment).length;
543+ if (idx < series._data._lengths[0 ] && series.time[idx] == moment)
544+ {
545+ return idx;
546+ }
547+ return size_t .max;
548+ }
549+
550+ // /
551+ @safe pure nothrow version(mir_test) unittest
552+ {
553+ auto time = [1 , 2 , 3 , 4 ].sliced;
554+ auto data = [2.1 , 3.4 , 5.6 , 7.8 ].sliced;
555+ auto series = time.series(data);
556+
557+ assert (series.data[series.findIndex(3 )] == 5.6 );
558+ assert (series.findIndex(0 ) == size_t .max);
559+ }
560+
561+ /+ +
562+ Finds a backward index such that `series.time[$ - backward_index] == moment`.
563+
564+ Params:
565+ series = timeseries
566+ moment = time moment to find in the series
567+ Returns:
568+ `0` if the series does not contain the moment and appropriate backward index otherwise.
569+ +/
570+ size_t find (TimeIterator, SliceKind kind, size_t [] packs, Iterator, Time )(Series! (TimeIterator, kind, packs, Iterator) series, Time moment)
571+ {
572+ import std.range : assumeSorted;
573+
574+ auto idx = series.time.assumeSorted.lowerBound(moment).length;
575+ auto bidx = series._data._lengths[0 ] - idx;
576+ if (bidx && series.time[idx] == moment)
577+ {
578+ return bidx;
579+ }
580+ return 0 ;
581+ }
582+
583+ // /
584+ @safe pure nothrow version(mir_test) unittest
585+ {
586+ auto time = [1 , 2 , 3 , 4 ].sliced;
587+ auto data = [2.1 , 3.4 , 5.6 , 7.8 ].sliced;
588+ auto series = time.series(data);
589+
590+ assert (series.data[$ - series.find(3 )] == 5.6 );
591+ assert (series.find(0 ) == 0 );
592+ }
593+
506594/+ +
507595Sorts time-series according to the `less` predicate applied to time observations.
508596
0 commit comments