@@ -16,7 +16,7 @@ See_also: $(SUBREF topology, flattened)
1616
1717License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
1818Copyright: Andrei Alexandrescu 2008-2016, Ilya Yaroshenko 2016-,
19- Authors: Ilya Yaroshenko, Andrei Alexandrescu
19+ Authors: Andrei Alexandrescu (Phobos), Ilya Yaroshenko (API, rework, Mir adoptation)
2020
2121Macros:
2222 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
@@ -118,17 +118,20 @@ import mir.math.common: optmath;
118118
119119
120120/+ +
121- Sorts 1D ndslice.
121+ Sorts ndslice, array, or series .
122122
123123See_also: $(SUBREF topology, flattened).
124124+/
125125template sort (alias less = " a < b" )
126126{
127127 import mir.functional: naryFun;
128+ import mir.series: Series;
128129 static if (__traits(isSame, naryFun! less, less))
129130 {
130131@optmath:
131- // /
132+ /+ +
133+ Sort one-dimensional series.
134+ +/
132135 Slice! (Iterator, N, kind) sort(Iterator, size_t N, SliceKind kind)
133136 (Slice! (Iterator, N, kind) slice)
134137 {
@@ -146,10 +149,62 @@ template sort(alias less = "a < b")
146149 return slice;
147150 }
148151
149- // /
152+ /+ +
153+ Sort for arrays
154+ +/
150155 T[] sort (T)(T[] ar)
151156 {
152- return ar.sliced.sort.field;
157+ return .sort! less(ar.sliced).field;
158+ }
159+
160+ /+ +
161+ Sort for one-dimensional Series.
162+ +/
163+ Series! (IndexIterator, Iterator, N, kind)
164+ sort(IndexIterator, Iterator, size_t N, SliceKind kind)
165+ (Series! (IndexIterator, Iterator, N, kind) series)
166+ if (N == 1 )
167+ {
168+ import mir.ndslice.sorting: sort;
169+ import mir.ndslice.topology: zip;
170+ with (series)
171+ index.zip(data).sort! ((a, b) => less(a.a, b.a));
172+ return series;
173+ }
174+
175+ /+ +
176+ Sort for n-dimensional Series.
177+ +/
178+ Series! (IndexIterator, Iterator, N, kind)
179+ sort(
180+ IndexIterator,
181+ Iterator,
182+ size_t N,
183+ SliceKind kind,
184+ SortIndexIterator,
185+ DataIterator,
186+ )
187+ (
188+ Series! (IndexIterator, Iterator, N, kind) series,
189+ Slice! SortIndexIterator indexBuffer,
190+ Slice! DataIterator dataBuffer,
191+ )
192+ {
193+ import mir.algorithm.iteration: each;
194+ import mir.ndslice.sorting: sort;
195+ import mir.ndslice.topology: iota, zip, ipack, evertPack;
196+
197+ assert (indexBuffer.length == series.length);
198+ assert (dataBuffer.length == series.length);
199+ indexBuffer[] = indexBuffer.length.iota! (typeof (indexBuffer.front));
200+ series.index.zip(indexBuffer).sort! ((a, b) => less(a.a, b.a));
201+ series.data.ipack! 1. evertPack.each! ((sl){
202+ {
203+ assert (sl.shape == dataBuffer.shape);
204+ dataBuffer[] = sl[indexBuffer];
205+ sl[] = dataBuffer;
206+ }});
207+ return series;
153208 }
154209 }
155210 else
@@ -171,6 +226,57 @@ template sort(alias less = "a < b")
171226 assert (data.pairwise! " a <= b" .all);
172227}
173228
229+ // / one-dimensional series
230+ pure version (mir_test) unittest
231+ {
232+ import mir.series;
233+
234+ auto index = [1 , 2 , 4 , 3 ].sliced;
235+ auto data = [2.1 , 3.4 , 5.6 , 7.8 ].sliced;
236+ auto series = index.series(data);
237+ series.sort;
238+ assert (series.index == [1 , 2 , 3 , 4 ]);
239+ assert (series.data == [2.1 , 3.4 , 7.8 , 5.6 ]);
240+ // / initial index and data are the same
241+ assert (index.iterator is series.index.iterator);
242+ assert (data.iterator is series.data.iterator);
243+
244+ foreach (obs; series)
245+ {
246+ static assert (is (typeof (obs) == Observation! (int , double )));
247+ }
248+ }
249+
250+ // / two-dimensional series
251+ pure version (mir_test) unittest
252+ {
253+ import mir.series;
254+ import mir.ndslice.allocation: uninitSlice;
255+
256+ auto index = [4 , 2 , 3 , 1 ].sliced;
257+ auto data =
258+ [2.1 , 3.4 ,
259+ 5.6 , 7.8 ,
260+ 3.9 , 9.0 ,
261+ 4.0 , 2.0 ].sliced(4 , 2 );
262+ auto series = index.series(data);
263+
264+ series.sort(
265+ uninitSlice! size_t (series.length), // index buffer
266+ uninitSlice! double (series.length), // data buffer
267+ );
268+
269+ assert (series.index == [1 , 2 , 3 , 4 ]);
270+ assert (series.data ==
271+ [[4.0 , 2.0 ],
272+ [5.6 , 7.8 ],
273+ [3.9 , 9.0 ],
274+ [2.1 , 3.4 ]]);
275+ // / initial index and data are the same
276+ assert (index.iterator is series.index.iterator);
277+ assert (data.iterator is series.data.iterator);
278+ }
279+
174280void quickSortImpl (alias less, Iterator)(Slice! Iterator slice) @trusted
175281{
176282 import mir.utility : swap, swapStars;
@@ -358,3 +464,73 @@ void medianOf(alias less, Iterator)
358464 if (less(* c, * b)) swapStars(b, c);
359465 }
360466}
467+
468+
469+
470+
471+ /+ +
472+ Computes transition index using binary search.
473+ It is low-level API for lower and upper bounds of a sorted array.
474+
475+ See_also: $(SUBREF topology, flattened).
476+ +/
477+ template transitionIndex (alias test = " a < b" )
478+ {
479+ import mir.functional: naryFun;
480+ static if (__traits(isSame, naryFun! test, test))
481+ {
482+ @optmath:
483+ /+ +
484+ Params:
485+ slice = sorted one-dimensional slice.
486+ v = value to test with. It is passed to second argument.
487+ +/
488+ size_t transitionIndex (Iterator, SliceKind kind, V)
489+ (scope Slice! (Iterator, 1 , kind) slice, V v)
490+ {
491+ size_t first = 0 , count = slice.length;
492+ while (count > 0 )
493+ {
494+ immutable step = count / 2 , it = first + step;
495+ if (test(slice[it], v))
496+ {
497+ first = it + 1 ;
498+ count -= step + 1 ;
499+ }
500+ else
501+ {
502+ count = step;
503+ }
504+ }
505+ return first;
506+ }
507+
508+ /+ +
509+ Params:
510+ ar = sorted array.
511+ v = value to test with. It is passed to second argument.
512+ +/
513+ size_t transitionIndex (T, V)(scope T[] ar, V v)
514+ {
515+ return .transitionIndex! test(ar.sliced, v);
516+ }
517+
518+ }
519+ else
520+ alias transitionIndex = .transitionIndex! (naryFun! test);
521+ }
522+
523+ // /
524+ @safe pure unittest
525+ {
526+ // sorted: a < b
527+ auto a = [0 , 1 , 2 , 3 , 4 , 6 ];
528+
529+ auto i = a.transitionIndex(2 );
530+ assert (i == 2 );
531+ auto lowerBound = a[0 .. i];
532+
533+ auto j = a.transitionIndex! " a <= b" (2 );
534+ assert (j == 3 );
535+ auto upperBound = a[j .. $];
536+ }
0 commit comments