Skip to content

Commit 3542e76

Browse files
committed
Add stableSort, stableSortInPlace from Belt to Array
1 parent e3d6249 commit 3542e76

File tree

5 files changed

+194
-7
lines changed

5 files changed

+194
-7
lines changed

src/Core__Array.mjs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,105 @@ function flatMap(a, f) {
112112
return Caml_splice_call.spliceObjApply([], "concat", [a.map(f)]);
113113
}
114114

115+
function blitUnsafe(a1, srcofs1, a2, srcofs2, blitLength) {
116+
if (srcofs2 <= srcofs1) {
117+
for(var j = 0; j < blitLength; ++j){
118+
a2[j + srcofs2 | 0] = a1[j + srcofs1 | 0];
119+
}
120+
return ;
121+
}
122+
for(var j$1 = blitLength - 1 | 0; j$1 >= 0; --j$1){
123+
a2[j$1 + srcofs2 | 0] = a1[j$1 + srcofs1 | 0];
124+
}
125+
}
126+
127+
function merge(src, src1ofs, src1len, src2, src2ofs, src2len, dst, dstofs, cmp) {
128+
var src1r = src1ofs + src1len | 0;
129+
var src2r = src2ofs + src2len | 0;
130+
var _i1 = src1ofs;
131+
var _s1 = src[src1ofs];
132+
var _i2 = src2ofs;
133+
var _s2 = src2[src2ofs];
134+
var _d = dstofs;
135+
while(true) {
136+
var d = _d;
137+
var s2 = _s2;
138+
var i2 = _i2;
139+
var s1 = _s1;
140+
var i1 = _i1;
141+
if (cmp(s1, s2) <= 0) {
142+
dst[d] = s1;
143+
var i1$1 = i1 + 1 | 0;
144+
if (i1$1 >= src1r) {
145+
return blitUnsafe(src2, i2, dst, d + 1 | 0, src2r - i2 | 0);
146+
}
147+
_d = d + 1 | 0;
148+
_s1 = src[i1$1];
149+
_i1 = i1$1;
150+
continue ;
151+
}
152+
dst[d] = s2;
153+
var i2$1 = i2 + 1 | 0;
154+
if (i2$1 >= src2r) {
155+
return blitUnsafe(src, i1, dst, d + 1 | 0, src1r - i1 | 0);
156+
}
157+
_d = d + 1 | 0;
158+
_s2 = src2[i2$1];
159+
_i2 = i2$1;
160+
continue ;
161+
};
162+
}
163+
164+
function insertionSort(src, srcofs, dst, dstofs, len, cmp) {
165+
for(var i = 0; i < len; ++i){
166+
var e = src[srcofs + i | 0];
167+
var j = (dstofs + i | 0) - 1 | 0;
168+
while(j >= dstofs && cmp(dst[j], e) > 0) {
169+
dst[j + 1 | 0] = dst[j];
170+
j = j - 1 | 0;
171+
};
172+
dst[j + 1 | 0] = e;
173+
}
174+
}
175+
176+
function sortTo(src, srcofs, dst, dstofs, len, cmp) {
177+
if (len <= 5) {
178+
return insertionSort(src, srcofs, dst, dstofs, len, cmp);
179+
}
180+
var l1 = len / 2 | 0;
181+
var l2 = len - l1 | 0;
182+
sortTo(src, srcofs + l1 | 0, dst, dstofs + l1 | 0, l2, cmp);
183+
sortTo(src, srcofs, src, srcofs + l2 | 0, l1, cmp);
184+
merge(src, srcofs + l2 | 0, l1, dst, dstofs + l1 | 0, l2, dst, dstofs, cmp);
185+
}
186+
187+
function stableSortInPlaceU(a, cmp) {
188+
var l = a.length;
189+
if (l <= 5) {
190+
return insertionSort(a, 0, a, 0, l, cmp);
191+
}
192+
var l1 = l / 2 | 0;
193+
var l2 = l - l1 | 0;
194+
var t = new Array(l2);
195+
sortTo(a, l1, t, 0, l2, cmp);
196+
sortTo(a, 0, a, l2, l1, cmp);
197+
merge(a, l2, l1, t, 0, l2, a, 0, cmp);
198+
}
199+
200+
function stableSortInPlace(a, cmp) {
201+
stableSortInPlaceU(a, Curry.__2(cmp));
202+
}
203+
204+
function stableSort(a, cmp) {
205+
var b = a.slice();
206+
stableSortInPlaceU(b, Curry.__2(cmp));
207+
return b;
208+
}
209+
115210
export {
116211
sort ,
212+
stableSort ,
213+
stableSortInPlace ,
117214
indexOfOpt ,
118215
lastIndexOfOpt ,
119216
reduce ,

src/Core__Array.res

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,95 @@ let filterMap = (a, f) => filterMapU(a, (. a) => f(a))
202202

203203
// TODO: Change this implementation?
204204
let flatMap = (a, f) => []->concatMany(map(a, f))
205+
206+
module SortArray = {
207+
@new external makeUninitializedUnsafe: int => array<'a> = "Array"
208+
209+
let cutoff = 5
210+
211+
let blitUnsafe = (
212+
~src as a1,
213+
~srcOffset as srcofs1,
214+
~dst as a2,
215+
~dstOffset as srcofs2,
216+
~len as blitLength,
217+
) =>
218+
if srcofs2 <= srcofs1 {
219+
for j in 0 to blitLength - 1 {
220+
a2->setUnsafe(j + srcofs2, a1->getUnsafe(j + srcofs1))
221+
}
222+
} else {
223+
for j in blitLength - 1 downto 0 {
224+
a2->setUnsafe(j + srcofs2, a1->getUnsafe(j + srcofs1))
225+
}
226+
}
227+
228+
let merge = (src, src1ofs, src1len, src2, src2ofs, src2len, dst, dstofs, cmp) => {
229+
let src1r = src1ofs + src1len and src2r = src2ofs + src2len
230+
let rec loop = (i1, s1, i2, s2, d) =>
231+
if cmp(. s1, s2) <= 0 {
232+
setUnsafe(dst, d, s1)
233+
let i1 = i1 + 1
234+
if i1 < src1r {
235+
loop(i1, getUnsafe(src, i1), i2, s2, d + 1)
236+
} else {
237+
blitUnsafe(~src=src2, ~srcOffset=i2, ~dst, ~dstOffset=d + 1, ~len=src2r - i2)
238+
}
239+
} else {
240+
setUnsafe(dst, d, s2)
241+
let i2 = i2 + 1
242+
if i2 < src2r {
243+
loop(i1, s1, i2, getUnsafe(src2, i2), d + 1)
244+
} else {
245+
blitUnsafe(~src, ~srcOffset=i1, ~dst, ~dstOffset=d + 1, ~len=src1r - i1)
246+
}
247+
}
248+
249+
loop(src1ofs, getUnsafe(src, src1ofs), src2ofs, getUnsafe(src2, src2ofs), dstofs)
250+
}
251+
252+
/* `<=` alone is not enough for stable sort */
253+
let insertionSort = (src, srcofs, dst, dstofs, len, cmp) =>
254+
for i in 0 to len - 1 {
255+
let e = getUnsafe(src, srcofs + i)
256+
let j = ref(dstofs + i - 1)
257+
while j.contents >= dstofs && cmp(. getUnsafe(dst, j.contents), e) > 0 {
258+
setUnsafe(dst, j.contents + 1, getUnsafe(dst, j.contents))
259+
j.contents = j.contents - 1
260+
}
261+
setUnsafe(dst, j.contents + 1, e)
262+
}
263+
264+
let rec sortTo = (src, srcofs, dst, dstofs, len, cmp) =>
265+
if len <= cutoff {
266+
insertionSort(src, srcofs, dst, dstofs, len, cmp)
267+
} else {
268+
let l1 = len / 2
269+
let l2 = len - l1
270+
sortTo(src, srcofs + l1, dst, dstofs + l1, l2, cmp)
271+
sortTo(src, srcofs, src, srcofs + l2, l1, cmp)
272+
merge(src, srcofs + l2, l1, dst, dstofs + l1, l2, dst, dstofs, cmp)
273+
}
274+
275+
let stableSortInPlaceU = (a, cmp) => {
276+
let l = length(a)
277+
if l <= cutoff {
278+
insertionSort(a, 0, a, 0, l, cmp)
279+
} else {
280+
let l1 = l / 2
281+
let l2 = l - l1
282+
let t = makeUninitializedUnsafe(l2)
283+
sortTo(a, l1, t, 0, l2, cmp)
284+
sortTo(a, 0, a, l2, l1, cmp)
285+
merge(a, l2, l1, t, 0, l2, a, 0, cmp)
286+
}
287+
}
288+
}
289+
290+
let stableSortInPlace = (a, cmp) => SortArray.stableSortInPlaceU(a, (. x, y) => cmp(x, y))
291+
292+
let stableSort = (a, cmp) => {
293+
let b = copy(a)
294+
SortArray.stableSortInPlaceU(b, (. x, y) => cmp(x, y))
295+
b
296+
}

src/Core__Array.resi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ external copyWithin: (array<'a>, ~target: int, ~start: int, ~end: int) => array<
2222
@send external shift: array<'a> => option<'a> = "shift"
2323
let sort: (array<'a>, ('a, 'a) => int) => array<'a>
2424
@send external sortInPlace: (array<'a>, ('a, 'a) => int) => unit = "sort"
25+
let stableSort: (array<'a>, ('a, 'a) => int) => array<'a>
26+
let stableSortInPlace: (array<'a>, ('a, 'a) => int) => unit
2527
@variadic @send
2628
external spliceInPlace: (array<'a>, ~start: int, ~remove: int, ~insert: array<'a>) => unit =
2729
"splice"

src/Core__List.mjs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import * as Curry from "rescript/lib/es6/curry.js";
44
import * as Belt_Array from "rescript/lib/es6/belt_Array.js";
55
import * as Caml_option from "rescript/lib/es6/caml_option.js";
66
import * as Core__Array from "./Core__Array.mjs";
7-
import * as Belt_SortArray from "rescript/lib/es6/belt_SortArray.js";
87

98
function head(x) {
109
if (x) {
@@ -1177,9 +1176,8 @@ function setAssoc(xs, x, k, eq) {
11771176
}
11781177

11791178
function sort(xs, cmp) {
1180-
var cmp$1 = Curry.__2(cmp);
11811179
var arr = toArray(xs);
1182-
Belt_SortArray.stableSortInPlaceByU(arr, cmp$1);
1180+
Core__Array.stableSortInPlace(arr, cmp);
11831181
return fromArray(arr);
11841182
}
11851183

src/Core__List.res

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -793,14 +793,12 @@ let setAssocU = (xs, x, k, eq) =>
793793

794794
let setAssoc = (xs, x, k, eq) => setAssocU(xs, x, k, (. a, b) => eq(a, b))
795795

796-
let sortU = (xs, cmp) => {
796+
let sort = (xs, cmp) => {
797797
let arr = toArray(xs)
798-
Belt_SortArray.stableSortInPlaceByU(arr, cmp)
798+
Core__Array.stableSortInPlace(arr, cmp)
799799
fromArray(arr)
800800
}
801801

802-
let sort = (xs, cmp) => sortU(xs, (. x, y) => cmp(x, y))
803-
804802
let rec getByU = (xs, p) =>
805803
switch xs {
806804
| list{} => None

0 commit comments

Comments
 (0)