Skip to content

Commit 80b91d7

Browse files
committed
Merge branches 'w/8.1/feature/ARSN-64-sorted-set' and 'q/1714/7.10/feature/ARSN-64-sorted-set' into tmp/octopus/q/8.1
3 parents 40843d4 + b3fd77d + 29bab6f commit 80b91d7

File tree

5 files changed

+261
-0
lines changed

5 files changed

+261
-0
lines changed

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ module.exports = {
2323
stream: {
2424
MergeStream: require('./lib/algos/stream/MergeStream'),
2525
},
26+
SortedSet: require('./lib/algos/set/SortedSet'),
2627
},
2728
policies: {
2829
evaluators: require('./lib/policyEvaluator/evaluator.js'),

lib/algos/set/ArrayUtils.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
function indexOf(arr, value) {
2+
if (!arr.length) {
3+
return -1;
4+
}
5+
let lo = 0;
6+
let hi = arr.length - 1;
7+
8+
while (hi - lo > 1) {
9+
const i = lo + ((hi - lo) >> 1);
10+
if (arr[i] > value) {
11+
hi = i;
12+
} else {
13+
lo = i;
14+
}
15+
}
16+
if (arr[lo] === value) {
17+
return lo;
18+
}
19+
if (arr[hi] === value) {
20+
return hi;
21+
}
22+
return -1;
23+
}
24+
25+
function indexAtOrBelow(arr, value) {
26+
let i;
27+
let lo;
28+
let hi;
29+
30+
if (!arr.length || arr[0] > value) {
31+
return -1;
32+
}
33+
if (arr[arr.length - 1] <= value) {
34+
return arr.length - 1;
35+
}
36+
37+
lo = 0;
38+
hi = arr.length - 1;
39+
40+
while (hi - lo > 1) {
41+
i = lo + ((hi - lo) >> 1);
42+
if (arr[i] > value) {
43+
hi = i;
44+
} else {
45+
lo = i;
46+
}
47+
}
48+
49+
return lo;
50+
}
51+
52+
/*
53+
* perform symmetric diff in O(m + n)
54+
*/
55+
function symDiff(k1, k2, v1, v2, cb) {
56+
let i = 0;
57+
let j = 0;
58+
const n = k1.length;
59+
const m = k2.length;
60+
61+
while (i < n && j < m) {
62+
if (k1[i] < k2[j]) {
63+
cb(v1[i]);
64+
i++;
65+
} else if (k2[j] < k1[i]) {
66+
cb(v2[j]);
67+
j++;
68+
} else {
69+
i++;
70+
j++;
71+
}
72+
}
73+
while (i < n) {
74+
cb(v1[i]);
75+
i++;
76+
}
77+
while (j < m) {
78+
cb(v2[j]);
79+
j++;
80+
}
81+
}
82+
83+
module.exports = {
84+
indexOf,
85+
indexAtOrBelow,
86+
symDiff,
87+
};

lib/algos/set/SortedSet.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const ArrayUtils = require('./ArrayUtils');
2+
3+
class SortedSet {
4+
5+
constructor(obj) {
6+
if (obj) {
7+
this.keys = obj.keys;
8+
this.values = obj.values;
9+
} else {
10+
this.clear();
11+
}
12+
}
13+
14+
clear() {
15+
this.keys = [];
16+
this.values = [];
17+
}
18+
19+
get size() {
20+
return this.keys.length;
21+
}
22+
23+
set(key, value) {
24+
const index = ArrayUtils.indexAtOrBelow(this.keys, key);
25+
if (this.keys[index] === key) {
26+
this.values[index] = value;
27+
return;
28+
}
29+
this.keys.splice(index + 1, 0, key);
30+
this.values.splice(index + 1, 0, value);
31+
}
32+
33+
isSet(key) {
34+
const index = ArrayUtils.indexOf(this.keys, key);
35+
return index >= 0;
36+
}
37+
38+
get(key) {
39+
const index = ArrayUtils.indexOf(this.keys, key);
40+
return index >= 0 ? this.values[index] : undefined;
41+
}
42+
43+
del(key) {
44+
const index = ArrayUtils.indexOf(this.keys, key);
45+
if (index >= 0) {
46+
this.keys.splice(index, 1);
47+
this.values.splice(index, 1);
48+
}
49+
}
50+
}
51+
52+
module.exports = SortedSet;

tests/unit/algos/set/ArrayUtils.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
const assert = require('assert');
2+
const { indexOf, indexAtOrBelow, symDiff } = require('../../../../lib/algos/set/ArrayUtils');
3+
const crypto = require('crypto');
4+
5+
describe('ArrayUtils', () => {
6+
it('indexOf basic', () => {
7+
const arr = [];
8+
assert.strictEqual(indexOf(arr, 42), -1);
9+
arr.push(42);
10+
arr.sort();
11+
assert.strictEqual(indexOf(arr, 42), 0);
12+
arr.push(41);
13+
arr.sort();
14+
assert.strictEqual(indexOf(arr, 41), 0);
15+
assert.strictEqual(indexOf(arr, 42), 1);
16+
arr.push(43);
17+
arr.sort();
18+
assert.strictEqual(indexOf(arr, 41), 0);
19+
assert.strictEqual(indexOf(arr, 42), 1);
20+
assert.strictEqual(indexOf(arr, 43), 2);
21+
});
22+
23+
it('indexOf', () => {
24+
const numOps = 10000;
25+
const arr = [];
26+
const refMap = new Map();
27+
for (let i = 0; i < numOps; i++) {
28+
const val = crypto.randomBytes(20).toString('hex');
29+
if (!refMap.get(val)) {
30+
arr.push(val);
31+
refMap.set(val);
32+
}
33+
}
34+
arr.sort();
35+
const refMap2 = new Map([...refMap.entries()].sort());
36+
let i = 0;
37+
for (const key of refMap2.keys()) {
38+
assert.strictEqual(indexOf(arr, key), i++);
39+
}
40+
});
41+
42+
it('indexAtOrBelow basic', () => {
43+
const arr = [];
44+
assert.strictEqual(indexAtOrBelow(arr, 42), -1);
45+
arr.push(42);
46+
arr.sort();
47+
assert.strictEqual(indexAtOrBelow(arr, 43), 0);
48+
arr.push(40);
49+
arr.sort();
50+
assert.strictEqual(indexAtOrBelow(arr, 41), 0);
51+
assert.strictEqual(indexAtOrBelow(arr, 43), 1);
52+
arr.push(44);
53+
arr.sort();
54+
assert.strictEqual(indexAtOrBelow(arr, 41), 0);
55+
assert.strictEqual(indexAtOrBelow(arr, 43), 1);
56+
assert.strictEqual(indexAtOrBelow(arr, 45), 2);
57+
});
58+
59+
it('indexAtOrBelow', () => {
60+
const numOps = 10000;
61+
const arr = [];
62+
const refMap = new Map();
63+
for (let i = 0; i < numOps; i++) {
64+
const val = crypto.randomBytes(20).toString('hex');
65+
if (!refMap.get(val)) {
66+
arr.push(val);
67+
let c = '';
68+
if (Math.random() < 0.5) {
69+
c = 'z';
70+
}
71+
refMap.set(val + c);
72+
}
73+
}
74+
arr.sort();
75+
const refMap2 = new Map([...refMap.entries()].sort());
76+
let i = 0;
77+
for (const key of refMap2.keys()) {
78+
assert.strictEqual(indexAtOrBelow(arr, key), i++);
79+
}
80+
});
81+
82+
it('shall find symmetric difference', () => {
83+
const arr1 = [2, 4, 5, 7, 8, 10, 12, 15];
84+
const arr2 = [5, 8, 11, 12, 14, 15];
85+
const arr3 = [];
86+
symDiff(arr1, arr2, arr1, arr2, x => arr3.push(x));
87+
assert.deepEqual(arr3, [2, 4, 7, 10, 11, 14]);
88+
});
89+
});

tests/unit/algos/set/SortedSet.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const assert = require('assert');
2+
const SortedSet = require('../../../../lib/algos/set/SortedSet');
3+
4+
describe('SortedSet', () => {
5+
it('basic', () => {
6+
const set = new SortedSet();
7+
set.set('foo', 'bar');
8+
assert(set.isSet('foo'));
9+
assert(!set.isSet('foo2'));
10+
assert.strictEqual(set.get('foo'), 'bar');
11+
set.set('foo', 'bar2');
12+
assert.strictEqual(set.get('foo'), 'bar2');
13+
set.del('foo');
14+
assert(!set.isSet('foo'));
15+
});
16+
17+
it('size', () => {
18+
const set = new SortedSet();
19+
set.set('foo', 'bar');
20+
assert.strictEqual(set.size, 1);
21+
set.set('foo2', 'bar');
22+
assert.strictEqual(set.size, 2);
23+
set.set('foo3', 'bar');
24+
assert.strictEqual(set.size, 3);
25+
set.del('foo');
26+
assert.strictEqual(set.size, 2);
27+
set.del('foo2');
28+
assert.strictEqual(set.size, 1);
29+
set.del('foo3');
30+
assert.strictEqual(set.size, 0);
31+
});
32+
});

0 commit comments

Comments
 (0)