@genome-spy/core 0.23.0 → 0.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +38 -38
- package/package.json +2 -2
- package/src/gl/dataToVertices.js +32 -25
- package/src/marks/link.js +5 -1
- package/src/node_modules/.vitest/results.json +1 -0
- package/src/utils/binnedIndex.js +49 -29
- package/src/utils/binnedIndex.test.js +110 -28
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"contributors": [],
|
|
9
9
|
"license": "BSD-2-Clause",
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.24.2",
|
|
11
11
|
"main": "dist/index.js",
|
|
12
12
|
"module": "src/index.js",
|
|
13
13
|
"exports": {
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"vega-scale": "^7.1.1",
|
|
54
54
|
"vega-util": "^1.16.0"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "587ef91161457b17a039f82111d15df339e1e9f5"
|
|
57
57
|
}
|
package/src/gl/dataToVertices.js
CHANGED
|
@@ -131,13 +131,18 @@ export class GeometryBuilder {
|
|
|
131
131
|
* @param {number} [hi]
|
|
132
132
|
*/
|
|
133
133
|
prepareXIndexer(data, lo = 0, hi = lo + data.length) {
|
|
134
|
-
|
|
134
|
+
const disable = () => {
|
|
135
135
|
/**
|
|
136
136
|
* @param {import("../data/flowNode").Datum} datum
|
|
137
137
|
*/
|
|
138
138
|
this.addToXIndex = (datum) => {
|
|
139
139
|
// nop
|
|
140
140
|
};
|
|
141
|
+
this.xIndexer = undefined;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
if (!data.length || hi - lo < 0) {
|
|
145
|
+
disable();
|
|
141
146
|
return;
|
|
142
147
|
}
|
|
143
148
|
|
|
@@ -152,31 +157,33 @@ export class GeometryBuilder {
|
|
|
152
157
|
const xa = xe.accessor;
|
|
153
158
|
const x2a = x2e ? x2e.accessor : xa;
|
|
154
159
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
160
|
+
/** @type {[number, number]} */
|
|
161
|
+
const dataDomain = [xa(data[lo]), x2a(data[hi - 1])];
|
|
162
|
+
|
|
163
|
+
// No indexer for point domains that have zero extent
|
|
164
|
+
if (dataDomain[1] > dataDomain[0]) {
|
|
165
|
+
this.xIndexer = createBinningRangeIndexer(
|
|
166
|
+
50,
|
|
167
|
+
dataDomain,
|
|
168
|
+
xa,
|
|
169
|
+
x2a
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
let lastVertexCount = this.variableBuilder.vertexCount;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @param {any} datum
|
|
176
|
+
*/
|
|
177
|
+
this.addToXIndex = (datum) => {
|
|
178
|
+
let currentVertexCount = this.variableBuilder.vertexCount;
|
|
179
|
+
this.xIndexer(datum, lastVertexCount, currentVertexCount);
|
|
180
|
+
lastVertexCount = currentVertexCount;
|
|
181
|
+
};
|
|
182
|
+
} else {
|
|
183
|
+
disable();
|
|
184
|
+
}
|
|
172
185
|
} else {
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* @param {import("../data/flowNode").Datum} datum
|
|
176
|
-
*/
|
|
177
|
-
this.addToXIndex = (datum) => {
|
|
178
|
-
// nop
|
|
179
|
-
};
|
|
186
|
+
disable();
|
|
180
187
|
}
|
|
181
188
|
}
|
|
182
189
|
|
package/src/marks/link.js
CHANGED
|
@@ -140,7 +140,11 @@ export default class LinkMark extends Mark {
|
|
|
140
140
|
this.bufferInfo.attribs
|
|
141
141
|
)) {
|
|
142
142
|
const [attribute, attribInfo] = attribInfoObject;
|
|
143
|
-
if (
|
|
143
|
+
if (
|
|
144
|
+
attribInfo.buffer &&
|
|
145
|
+
attribInfo.numComponents &&
|
|
146
|
+
attribInfo.divisor
|
|
147
|
+
) {
|
|
144
148
|
attribInfo.offset =
|
|
145
149
|
offset * this.arrays[attribute].numComponents * 4; // gl.FLOAT in bytes
|
|
146
150
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":"0.23.1","results":[["/genome/genome.test.js",{"duration":17,"failed":false}],["/scale/scale.test.js",{"duration":27,"failed":false}],["/utils/layout/flexLayout.test.js",{"duration":41,"failed":false}],["/data/flowOptimizer.test.js",{"duration":12,"failed":false}],["/utils/layout/rectangle.test.js",{"duration":10,"failed":false}],["/utils/domainArray.test.js",{"duration":11,"failed":false}],["/data/transforms/regexFold.test.js",{"duration":9,"failed":false}],["/view/scaleResolution.test.js",{"duration":169,"failed":false}],["/view/view.test.js",{"duration":161,"failed":false}],["/view/axisResolution.test.js",{"duration":22,"failed":false}],["/view/flowBuilder.test.js",{"duration":11,"failed":false}],["/encoder/encoder.test.js",{"duration":15,"failed":false}],["/data/transforms/stack.test.js",{"duration":11,"failed":false}],["/data/transforms/identifier.test.js",{"duration":84,"failed":false}],["/data/transforms/coverage.test.js",{"duration":10,"failed":false}],["/data/transforms/flattenDelimited.test.js",{"duration":16,"failed":false}],["/genome/scaleIndex.test.js",{"duration":11,"failed":false}],["/utils/layout/grid.test.js",{"duration":7,"failed":false}],["/utils/topK.test.js",{"duration":95,"failed":false}],["/utils/binnedIndex.test.js",{"duration":6,"failed":false}],["/data/collector.test.js",{"duration":11,"failed":false}],["/data/flow.test.js",{"duration":11,"failed":false}],["/utils/propertyCacher.test.js",{"duration":8,"failed":false}],["/data/transforms/regexExtract.test.js",{"duration":10,"failed":false}],["/data/transforms/pileup.test.js",{"duration":7,"failed":false}],["/utils/indexer.test.js",{"duration":6,"failed":false}],["/encoder/accessor.test.js",{"duration":10,"failed":false}],["/data/sources/sequenceSource.test.js",{"duration":8,"failed":false}],["/data/sources/inlineSource.test.js",{"duration":10,"failed":false}],["/utils/mergeObjects.test.js",{"duration":13,"failed":false}],["/data/flowNode.test.js",{"duration":6,"failed":false}],["/scale/ticks.test.js",{"duration":10,"failed":false}],["/data/formats/fasta.test.js",{"duration":10,"failed":false}],["/data/transforms/flattenSequence.test.js",{"duration":10,"failed":false}],["/utils/addBaseUrl.test.js",{"duration":5,"failed":false}],["/data/transforms/project.test.js",{"duration":10,"failed":false}],["/utils/iterateNestedMaps.test.js",{"duration":5,"failed":false}],["/utils/reservationMap.test.js",{"duration":6,"failed":false}],["/utils/kWayMerge.test.js",{"duration":5,"failed":false}],["/data/transforms/sample.test.js",{"duration":326,"failed":false}],["/utils/propertyCoalescer.test.js",{"duration":5,"failed":false}],["/utils/coalesce.test.js",{"duration":4,"failed":false}],["/data/transforms/filter.test.js",{"duration":6,"failed":false}],["/utils/cloner.test.js",{"duration":6,"failed":false}],["/utils/variableTools.test.js",{"duration":6,"failed":false}],["/view/viewFactory.test.js",{"duration":4,"failed":false}],["/data/transforms/formula.test.js",{"duration":8,"failed":false}],["/utils/concatIterables.test.js",{"duration":18,"failed":false}],["/utils/numberExtractor.test.js",{"duration":30,"failed":false}],["/data/transforms/clone.test.js",{"duration":8,"failed":false}],["/genome/scaleLocus.test.js",{"duration":0,"failed":false}],["/data/dataFlow.test.js",{"duration":0,"failed":false}]]}
|
package/src/utils/binnedIndex.js
CHANGED
|
@@ -13,11 +13,12 @@ const MIN_INTEGER = -(2 ** 31);
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* A binned index for (overlapping) ranges that are sorted by their start position.
|
|
16
|
+
* Each indexed range is associated with respective vertex indices.
|
|
16
17
|
*
|
|
17
18
|
* @param {number} size Number of bins
|
|
18
|
-
* @param {[number, number]} domain
|
|
19
|
-
* @param {(datum: T) => number} accessor
|
|
20
|
-
* @param {(datum: T) => number} [accessor2]
|
|
19
|
+
* @param {[number, number]} domain Domain of positions
|
|
20
|
+
* @param {(datum: T) => number} accessor Accessor for range's start position
|
|
21
|
+
* @param {(datum: T) => number} [accessor2] Accessor for range's end position
|
|
21
22
|
* @template T
|
|
22
23
|
*/
|
|
23
24
|
export function createBinningRangeIndexer(
|
|
@@ -38,19 +39,32 @@ export function createBinningRangeIndexer(
|
|
|
38
39
|
const domainLength = domain[1] - domain[0];
|
|
39
40
|
const divisor = domainLength / size;
|
|
40
41
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
/**
|
|
43
|
+
* @param {number} pos
|
|
44
|
+
* @param {boolean} end
|
|
45
|
+
*/
|
|
46
|
+
const getBin = (pos, end) => {
|
|
47
|
+
const unfloored = (pos - start) / divisor;
|
|
48
|
+
const floored = Math.floor(unfloored);
|
|
49
|
+
|
|
50
|
+
// Special handling for the end coordinate because we are using half-open ranges.
|
|
51
|
+
return clamp(
|
|
52
|
+
end && floored == unfloored ? floored - 1 : floored,
|
|
53
|
+
0,
|
|
54
|
+
size - 1
|
|
55
|
+
);
|
|
56
|
+
};
|
|
44
57
|
|
|
45
58
|
/**
|
|
59
|
+
* Indexer for point items. Those have just a single coordinate.
|
|
46
60
|
*
|
|
47
61
|
* @param {T} datum
|
|
48
|
-
* @param {number}
|
|
49
|
-
* @param {number}
|
|
62
|
+
* @param {number} startVertexIndex
|
|
63
|
+
* @param {number} endVertexIndex
|
|
50
64
|
*/
|
|
51
|
-
function binningIndexer(datum,
|
|
52
|
-
if (
|
|
53
|
-
lastIndex =
|
|
65
|
+
function binningIndexer(datum, startVertexIndex, endVertexIndex) {
|
|
66
|
+
if (startVertexIndex > lastIndex) {
|
|
67
|
+
lastIndex = startVertexIndex;
|
|
54
68
|
} else if (!unordered) {
|
|
55
69
|
unordered = true;
|
|
56
70
|
// TODO: Contextual info like view path
|
|
@@ -60,26 +74,27 @@ export function createBinningRangeIndexer(
|
|
|
60
74
|
}
|
|
61
75
|
|
|
62
76
|
const value = accessor(datum);
|
|
63
|
-
const bin = getBin(value);
|
|
77
|
+
const bin = getBin(value, false);
|
|
64
78
|
|
|
65
|
-
if (startIndices[bin] >
|
|
66
|
-
startIndices[bin] =
|
|
79
|
+
if (startIndices[bin] > startVertexIndex) {
|
|
80
|
+
startIndices[bin] = startVertexIndex;
|
|
67
81
|
}
|
|
68
82
|
|
|
69
|
-
if (endIndices[bin] <
|
|
70
|
-
endIndices[bin] =
|
|
83
|
+
if (endIndices[bin] < endVertexIndex) {
|
|
84
|
+
endIndices[bin] = endVertexIndex;
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
87
|
|
|
74
88
|
/**
|
|
89
|
+
* Indexer for ranged items. Those have both start and end coordinates.
|
|
75
90
|
*
|
|
76
91
|
* @param {T} datum
|
|
77
|
-
* @param {number}
|
|
78
|
-
* @param {number}
|
|
92
|
+
* @param {number} startVertexIndex
|
|
93
|
+
* @param {number} endVertexIndex
|
|
79
94
|
*/
|
|
80
|
-
function binningRangeIndexer(datum,
|
|
81
|
-
if (
|
|
82
|
-
lastIndex =
|
|
95
|
+
function binningRangeIndexer(datum, startVertexIndex, endVertexIndex) {
|
|
96
|
+
if (startVertexIndex > lastIndex) {
|
|
97
|
+
lastIndex = startVertexIndex;
|
|
83
98
|
} else if (!unordered) {
|
|
84
99
|
unordered = true;
|
|
85
100
|
// TODO: Contextual info like view path
|
|
@@ -90,18 +105,18 @@ export function createBinningRangeIndexer(
|
|
|
90
105
|
|
|
91
106
|
const start = accessor(datum);
|
|
92
107
|
const end = accessor2(datum);
|
|
93
|
-
const startBin = getBin(start);
|
|
94
|
-
const endBin = getBin(end);
|
|
108
|
+
const startBin = getBin(start, false);
|
|
109
|
+
const endBin = getBin(end, true);
|
|
95
110
|
|
|
96
111
|
// TODO: This loop could probably be done as a more efficient post processing
|
|
97
112
|
// step.
|
|
98
113
|
for (let bin = startBin; bin <= endBin; bin++) {
|
|
99
|
-
if (startIndices[bin] >
|
|
100
|
-
startIndices[bin] =
|
|
114
|
+
if (startIndices[bin] > startVertexIndex) {
|
|
115
|
+
startIndices[bin] = startVertexIndex;
|
|
101
116
|
}
|
|
102
117
|
|
|
103
|
-
if (endIndices[bin] <
|
|
104
|
-
endIndices[bin] =
|
|
118
|
+
if (endIndices[bin] < endVertexIndex) {
|
|
119
|
+
endIndices[bin] = endVertexIndex;
|
|
105
120
|
}
|
|
106
121
|
}
|
|
107
122
|
}
|
|
@@ -110,8 +125,13 @@ export function createBinningRangeIndexer(
|
|
|
110
125
|
* @type {Lookup}
|
|
111
126
|
*/
|
|
112
127
|
const lookup = (start, end, arr = [0, 0]) => {
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
const startBin = getBin(start, false);
|
|
129
|
+
const endBin = getBin(end, true);
|
|
130
|
+
const startIndex = startIndices[startBin];
|
|
131
|
+
const endIndex = Math.max(endIndices[endBin], startIndex);
|
|
132
|
+
|
|
133
|
+
arr[0] = startIndex;
|
|
134
|
+
arr[1] = endIndex;
|
|
115
135
|
return arr;
|
|
116
136
|
};
|
|
117
137
|
|
|
@@ -2,29 +2,60 @@ import { describe, expect, test } from "vitest";
|
|
|
2
2
|
import { createBinningRangeIndexer } from "./binnedIndex.js";
|
|
3
3
|
|
|
4
4
|
describe("Binning Indexer", () => {
|
|
5
|
-
test("
|
|
6
|
-
const items = [
|
|
5
|
+
test("Single point is binned correctly", () => {
|
|
6
|
+
const items = [25];
|
|
7
7
|
const indexer = createBinningRangeIndexer(10, [0, 100], (x) => x);
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// Each item uses two vertices
|
|
10
|
+
items.forEach((x, i) => indexer(x, i * 2, (i + 1) * 2));
|
|
10
11
|
|
|
11
12
|
const index = indexer.getIndex();
|
|
12
13
|
|
|
13
|
-
expect(index(
|
|
14
|
-
expect(index(
|
|
15
|
-
expect(index(
|
|
16
|
-
|
|
17
|
-
expect(index(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
expect(index(1, 4)).toEqual([0, 0]);
|
|
15
|
+
expect(index(23, 27)).toEqual([0, 2]);
|
|
16
|
+
expect(index(13, 37)).toEqual([0, 2]);
|
|
17
|
+
// TODO: MAX_INT could be replaced with the actual maximum vertex number
|
|
18
|
+
expect(index(40, 42)).toEqual([2147483647, 2147483647]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("Multiple points are binned correctly", () => {
|
|
22
|
+
const items = [0, 1, 4, 10, 35, 35, 36, 80];
|
|
23
|
+
const indexer = createBinningRangeIndexer(10, [0, 100], (x) => x);
|
|
24
|
+
|
|
25
|
+
// Each item uses two vertices
|
|
26
|
+
items.forEach((x, i) => indexer(x, i * 2, (i + 1) * 2));
|
|
27
|
+
|
|
28
|
+
const index = indexer.getIndex();
|
|
29
|
+
|
|
30
|
+
expect(index(0, 0)).toEqual([0, 6]);
|
|
31
|
+
expect(index(0, 1)).toEqual([0, 6]);
|
|
32
|
+
expect(index(1, 2)).toEqual([0, 6]);
|
|
33
|
+
expect(index(1, 15)).toEqual([0, 8]);
|
|
34
|
+
expect(index(3, 6)).toEqual([0, 6]);
|
|
35
|
+
expect(index(10, 15)).toEqual([6, 8]);
|
|
36
|
+
expect(index(11, 38)).toEqual([6, 14]);
|
|
37
|
+
expect(index(11, 45)).toEqual([6, 14]);
|
|
38
|
+
expect(index(34, 36)).toEqual([8, 14]);
|
|
39
|
+
expect(index(35.5, 36.5)).toEqual([8, 14]);
|
|
40
|
+
expect(index(40, 50)).toEqual([14, 14]);
|
|
41
|
+
expect(index(40, 85)).toEqual([14, 16]);
|
|
42
|
+
expect(index(90, 100)).toEqual([16, 16]);
|
|
43
|
+
|
|
44
|
+
expect(index(0, 100)).toEqual([0, 16]);
|
|
45
|
+
expect(index(-1, 100)).toEqual([0, 16]);
|
|
46
|
+
expect(index(0, 101)).toEqual([0, 16]);
|
|
21
47
|
});
|
|
22
48
|
|
|
23
49
|
test("Non-overlapping ranges are binned correctly", () => {
|
|
24
50
|
const items = [
|
|
25
51
|
[0, 5],
|
|
26
|
-
[25,
|
|
52
|
+
[25, 48],
|
|
27
53
|
[50, 55],
|
|
54
|
+
[64, 67],
|
|
55
|
+
[72, 75],
|
|
56
|
+
[75, 78],
|
|
57
|
+
[86, 90],
|
|
58
|
+
[90, 93],
|
|
28
59
|
];
|
|
29
60
|
const indexer = createBinningRangeIndexer(
|
|
30
61
|
10,
|
|
@@ -33,41 +64,92 @@ describe("Binning Indexer", () => {
|
|
|
33
64
|
(x) => x[1]
|
|
34
65
|
);
|
|
35
66
|
|
|
36
|
-
|
|
67
|
+
// Each item uses two vertices
|
|
68
|
+
items.forEach((x, i) => indexer(x, i * 2, (i + 1) * 2));
|
|
69
|
+
|
|
70
|
+
const index = indexer.getIndex();
|
|
71
|
+
|
|
72
|
+
expect(index(0, 1)).toEqual([0, 2]);
|
|
73
|
+
expect(index(3, 40)).toEqual([0, 4]);
|
|
74
|
+
expect(index(6, 40)).toEqual([0, 4]);
|
|
75
|
+
expect(index(15, 30)).toEqual([2, 4]);
|
|
76
|
+
expect(index(50, 57)).toEqual([4, 6]);
|
|
77
|
+
expect(index(62, 69)).toEqual([6, 8]);
|
|
78
|
+
expect(index(69, 71)).toEqual([6, 12]);
|
|
79
|
+
expect(index(69, 79)).toEqual([6, 12]);
|
|
80
|
+
|
|
81
|
+
expect(index(80, 90)).toEqual([12, 14]);
|
|
82
|
+
expect(index(90, 100)).toEqual([14, 16]);
|
|
83
|
+
|
|
84
|
+
expect(index(0, 99)).toEqual([0, 16]);
|
|
85
|
+
expect(index(0, 100)).toEqual([0, 16]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("Overlapping ranges with the same start coordinate are binned correctly", () => {
|
|
89
|
+
const items = [
|
|
90
|
+
// Increasing lengths
|
|
91
|
+
[0, 5],
|
|
92
|
+
[0, 64],
|
|
93
|
+
[0, 80],
|
|
94
|
+
// Decreasing lengths
|
|
95
|
+
[100, 191],
|
|
96
|
+
[100, 167],
|
|
97
|
+
[100, 123],
|
|
98
|
+
];
|
|
99
|
+
const indexer = createBinningRangeIndexer(
|
|
100
|
+
100,
|
|
101
|
+
[0, 1000],
|
|
102
|
+
(x) => x[0],
|
|
103
|
+
(x) => x[1]
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// Each item uses two vertices
|
|
107
|
+
items.forEach((x, i) => indexer(x, i * 2, (i + 1) * 2));
|
|
37
108
|
|
|
38
109
|
const index = indexer.getIndex();
|
|
39
110
|
|
|
40
|
-
|
|
111
|
+
expect(index(0, 1)).toEqual([0, 6]);
|
|
112
|
+
expect(index(3, 40)).toEqual([0, 6]);
|
|
113
|
+
expect(index(0, 100)).toEqual([0, 6]);
|
|
114
|
+
expect(index(77, 78)).toEqual([4, 6]);
|
|
41
115
|
|
|
42
|
-
expect(index(
|
|
43
|
-
expect(index(
|
|
44
|
-
|
|
45
|
-
|
|
116
|
+
expect(index(90, 205)).toEqual([6, 12]);
|
|
117
|
+
expect(index(111, 115)).toEqual([6, 12]);
|
|
118
|
+
// Not optimal. Should be [6, 8], but [6, 12] is not wrong
|
|
119
|
+
expect(index(180, 190)).toEqual([6, 12]);
|
|
46
120
|
});
|
|
47
121
|
|
|
48
122
|
test("Overlapping ranges are binned correctly", () => {
|
|
49
123
|
const items = [
|
|
50
124
|
[10, 30],
|
|
51
125
|
[25, 50],
|
|
126
|
+
|
|
127
|
+
[102, 129],
|
|
128
|
+
[112, 139],
|
|
129
|
+
[121, 149],
|
|
52
130
|
];
|
|
53
131
|
const indexer = createBinningRangeIndexer(
|
|
54
|
-
|
|
55
|
-
[0,
|
|
132
|
+
100,
|
|
133
|
+
[0, 1000],
|
|
56
134
|
(x) => x[0],
|
|
57
135
|
(x) => x[1]
|
|
58
136
|
);
|
|
59
137
|
|
|
60
|
-
items.forEach((x) => indexer(x,
|
|
138
|
+
items.forEach((x, i) => indexer(x, i * 2, (i + 1) * 2));
|
|
61
139
|
|
|
62
140
|
const index = indexer.getIndex();
|
|
63
141
|
|
|
64
|
-
// TODO: More tests
|
|
142
|
+
// TODO: More tests
|
|
143
|
+
|
|
144
|
+
expect(index(0, 5)).toEqual([0, 0]);
|
|
145
|
+
expect(index(0, 15)).toEqual([0, 2]);
|
|
146
|
+
expect(index(27, 40)).toEqual([0, 4]);
|
|
147
|
+
expect(index(40, 50)).toEqual([2, 4]);
|
|
148
|
+
expect(index(40, 80)).toEqual([2, 4]);
|
|
149
|
+
expect(index(10, 29)).toEqual([0, 4]);
|
|
65
150
|
|
|
66
|
-
expect(index(
|
|
67
|
-
expect(index(
|
|
68
|
-
expect(index(
|
|
69
|
-
expect(index(40, 50)).toEqual([25, 50]);
|
|
70
|
-
expect(index(40, 80)).toEqual([25, 50]);
|
|
71
|
-
expect(index(10, 29)).toEqual([10, 50]);
|
|
151
|
+
expect(index(90, 160)).toEqual([4, 10]);
|
|
152
|
+
expect(index(115, 116)).toEqual([4, 8]);
|
|
153
|
+
expect(index(135, 145)).toEqual([6, 10]);
|
|
72
154
|
});
|
|
73
155
|
});
|