Skip to content

Commit 45aaf8d

Browse files
committed
fix name conflic for sort, add transitionIndex
1 parent 238fcc0 commit 45aaf8d

File tree

4 files changed

+197
-131
lines changed

4 files changed

+197
-131
lines changed

source/mir/graph/package.d

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ GraphSeries!(T, I, J) graphSeries(I = uint, J = size_t, T, Range)(in Range[T] aa
5252
components[i] = cast(J) dataIndex;
5353
foreach(ref elem; aaGraph[keys[i]])
5454
{
55-
import std.range: assumeSorted;
56-
auto index = keys.assumeSorted.lowerBound(elem).length;
55+
import mir.ndslice.sorting: transitionIndex;
56+
auto index = keys.transitionIndex(elem);
5757
assert(index < keys.length, "graphSeries: aaGraph should contains keys for all vertixes");
5858
data[dataIndex++] = cast(I) index;
5959
}

source/mir/interpolate/package.d

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ template findInterval(size_t dimension = 0)
5858
size_t findInterval(Interpolant, X)(auto ref const Interpolant interpolant, in X x) @trusted
5959
{
6060
import mir.ndslice.slice: sliced;
61-
import std.range: assumeSorted;
61+
import mir.ndslice.sorting: transitionIndex;
6262
static if (dimension)
6363
{
6464
immutable sizediff_t len = interpolant.intervalCount!dimension - 1;
@@ -70,7 +70,7 @@ template findInterval(size_t dimension = 0)
7070
auto grid = interpolant.grid[1 .. $][0 .. len];
7171
}
7272
assert(len >= 0);
73-
return len - grid.assumeSorted.upperBound(x).length;
73+
return grid.transitionIndex!"a <= b"(x);
7474
}
7575
}
7676

source/mir/ndslice/sorting.d

Lines changed: 181 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ See_also: $(SUBREF topology, flattened)
1616
1717
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
1818
Copyright: 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
2121
Macros:
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
123123
See_also: $(SUBREF topology, flattened).
124124
+/
125125
template 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+
174280
void 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

Comments
 (0)