@teselagen/range-utils 0.1.21 → 0.1.22
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/index.js +6542 -18010
- package/index.mjs +6593 -18007
- package/index.umd.js +6836 -18306
- package/package.json +1 -1
- package/src/adjustRangeToDeletionOfAnotherRange.js +53 -0
- package/src/adjustRangeToDeletionOfAnotherRange.test.js +107 -0
- package/src/adjustRangeToInsert.js +28 -0
- package/src/adjustRangeToInsert.test.js +111 -0
- package/src/adjustRangeToRotation.js +24 -0
- package/src/adjustRangeToRotation.test.js +123 -0
- package/src/checkIfNonCircularRangesOverlap.js +30 -0
- package/src/checkIfNonCircularRangesOverlap.test.js +45 -0
- package/src/checkIfPotentiallyCircularRangesOverlap.js +16 -0
- package/src/checkIfPotentiallyCircularRangesOverlap.test.js +73 -0
- package/src/collapseOverlapsGeneratedFromRangeComparisonIfPossible.js +47 -0
- package/src/collapseOverlapsGeneratedFromRangeComparisonIfPossible.test.js +124 -0
- package/src/convertRangeIndices.js +22 -0
- package/src/convertRangeIndices.test.js +22 -0
- package/src/convertRangeTo0Based.js +5 -0
- package/src/convertRangeTo1Based.js +5 -0
- package/src/doesRangeSpanEntireSequence.js +7 -0
- package/src/doesRangeSpanOrigin.js +3 -0
- package/src/expandOrContractCircularRangeToPosition.js +41 -0
- package/src/expandOrContractNonCircularRangeToPosition.js +25 -0
- package/src/expandOrContractRangeByLength.js +12 -0
- package/src/expandOrContractRangeByLength.test.js +77 -0
- package/src/expandOrContractRangeToPosition.js +10 -0
- package/src/flipContainedRange.js +85 -0
- package/src/flipContainedRange.test.js +124 -0
- package/src/generateRandomRange.js +20 -0
- package/src/generateRandomRange.test.js +24 -0
- package/src/getAnnotationRangeType.js +24 -0
- package/src/getAnnotationRangeType.test.js +59 -0
- package/src/getEachPositionInRangeAsArray.js +15 -0
- package/src/getEachPositionInRangeAsArray.test.js +9 -0
- package/src/getLengthOfOverlappingRegionsBetweenTwoRanges.js +9 -0
- package/src/getLengthOfOverlappingRegionsBetweenTwoRanges.test.js +24 -0
- package/src/getMiddleOfRange.js +10 -0
- package/src/getMiddleOfRange.test.js +44 -0
- package/src/getOverlapOfNonCircularRanges.js +35 -0
- package/src/getOverlapsOfPotentiallyCircularRanges.js +54 -0
- package/src/getOverlapsOfPotentiallyCircularRanges.test.js +199 -0
- package/src/getPositionFromAngle.js +7 -0
- package/src/getRangeAngles.js +33 -0
- package/src/getRangeAngles.test.js +77 -0
- package/src/getRangeLength.js +14 -0
- package/src/getRangeLength.test.js +30 -0
- package/src/getRangesBetweenTwoRanges.js +28 -0
- package/src/getSequenceWithinRange.js +17 -0
- package/src/getSequenceWithinRange.test.js +49 -0
- package/src/getShortestDistanceBetweenTwoPositions.js +12 -0
- package/src/getShortestDistanceBetweenTwoPositions.test.js +14 -0
- package/src/getYOffsetForPotentiallyCircularRange.js +24 -0
- package/src/getYOffsetsForPotentiallyCircularRanges.js +20 -0
- package/src/getYOffsetsForPotentiallyCircularRanges.test.js +29 -0
- package/src/getZeroedRangeOverlaps.js +17 -0
- package/src/getZeroedRangeOverlaps.test.js +36 -0
- package/src/index.js +51 -0
- package/src/index.test.js +33 -0
- package/src/invertRange.js +21 -0
- package/src/invertRange.test.js +96 -0
- package/src/isPositionCloserToRangeStartThanRangeEnd.js +8 -0
- package/src/isPositionCloserToRangeStartThanRangeEnd.test.js +17 -0
- package/src/isPositionWithinRange.js +31 -0
- package/src/isRangeOrPositionWithinRange.js +29 -0
- package/src/isRangeOrPositionWithinRange.test.js +150 -0
- package/src/isRangeWithinRange.js +17 -0
- package/src/loopEachPositionInRange.js +5 -0
- package/src/modulatePositionByRange.js +10 -0
- package/src/modulatePositionByRange.test.js +12 -0
- package/src/modulateRangeBySequenceLength.js +11 -0
- package/src/modulateRangeBySequenceLength.test.js +16 -0
- package/src/normalizePositionByRangeLength.js +20 -0
- package/src/normalizePositionByRangeLength.test.js +23 -0
- package/src/normalizePositionByRangeLength1Based.js +5 -0
- package/src/normalizePositionByRangeLength1Based.test.js +9 -0
- package/src/normalizeRange.js +11 -0
- package/src/normalizeRange.test.js +9 -0
- package/src/provideInclusiveOptions.js +26 -0
- package/src/reversePositionInRange.js +13 -0
- package/src/splitRangeIntoTwoPartsIfItIsCircular.js +31 -0
- package/src/splitRangeIntoTwoPartsIfItIsCircular.test.js +33 -0
- package/src/translateRange.js +9 -0
- package/src/translateRange.test.js +20 -0
- package/src/trimNonCicularRangeByAnotherNonCircularRange.js +57 -0
- package/src/trimNumberToFitWithin0ToAnotherNumber.js +12 -0
- package/src/trimRangeByAnotherRange.js +102 -0
- package/src/trimRangeByAnotherRange.test.js +314 -0
- package/src/zeroSubrangeByContainerRange.js +36 -0
- package/src/zeroSubrangeByContainerRange.test.js +51 -0
@@ -0,0 +1,124 @@
|
|
1
|
+
import getOverlapsOfPotentiallyCircularRanges from './getOverlapsOfPotentiallyCircularRanges.js';
|
2
|
+
|
3
|
+
import collapseOverlapsGeneratedFromRangeComparisonIfPossible from './collapseOverlapsGeneratedFromRangeComparisonIfPossible.js';
|
4
|
+
import assert from 'assert';
|
5
|
+
|
6
|
+
|
7
|
+
;
|
8
|
+
describe('collapseOverlapsGeneratedFromRangeComparisonIfPossible', function() {
|
9
|
+
it('returns an empty array if passed an empty array of overlaps', function() {
|
10
|
+
assert.deepEqual(collapseOverlapsGeneratedFromRangeComparisonIfPossible([], 1000), []);
|
11
|
+
assert.deepEqual(collapseOverlapsGeneratedFromRangeComparisonIfPossible(getOverlapsOfPotentiallyCircularRanges({
|
12
|
+
start: 900,
|
13
|
+
end: 100
|
14
|
+
}, {
|
15
|
+
start: 900,
|
16
|
+
end: 100
|
17
|
+
}, 1000), 1000), [{
|
18
|
+
start: 900,
|
19
|
+
end: 100
|
20
|
+
}]);
|
21
|
+
});
|
22
|
+
it('collapses a split circular range', function() {
|
23
|
+
assert.deepEqual(collapseOverlapsGeneratedFromRangeComparisonIfPossible([{
|
24
|
+
start: 0,
|
25
|
+
end: 100
|
26
|
+
}, {
|
27
|
+
start: 105,
|
28
|
+
end: 999
|
29
|
+
}], 1000), [{
|
30
|
+
start: 105,
|
31
|
+
end: 100
|
32
|
+
}]);
|
33
|
+
assert.deepEqual(collapseOverlapsGeneratedFromRangeComparisonIfPossible(getOverlapsOfPotentiallyCircularRanges({
|
34
|
+
start: 900,
|
35
|
+
end: 100
|
36
|
+
}, {
|
37
|
+
start: 900,
|
38
|
+
end: 100
|
39
|
+
}, 1000), 1000), [{
|
40
|
+
start: 900,
|
41
|
+
end: 100
|
42
|
+
}]);
|
43
|
+
});
|
44
|
+
it('doesnt collapses a split range that could be circular if the originalRangeIsNotCircular', function() {
|
45
|
+
assert.deepEqual(collapseOverlapsGeneratedFromRangeComparisonIfPossible([{
|
46
|
+
start: 0,
|
47
|
+
end: 100
|
48
|
+
}, {
|
49
|
+
start: 105,
|
50
|
+
end: 999
|
51
|
+
}], 1000, {
|
52
|
+
start: 0,
|
53
|
+
end: 999
|
54
|
+
}), [{
|
55
|
+
start: 0,
|
56
|
+
end: 100
|
57
|
+
}, {
|
58
|
+
start: 105,
|
59
|
+
end: 999
|
60
|
+
}]);
|
61
|
+
assert.deepEqual(collapseOverlapsGeneratedFromRangeComparisonIfPossible(getOverlapsOfPotentiallyCircularRanges({
|
62
|
+
start: 900,
|
63
|
+
end: 100
|
64
|
+
}, {
|
65
|
+
start: 900,
|
66
|
+
end: 100
|
67
|
+
}, 1000), 1000), [{
|
68
|
+
start: 900,
|
69
|
+
end: 100
|
70
|
+
}]);
|
71
|
+
});
|
72
|
+
it('doesnt collapses a split range that doesnt line up correctly', function() {
|
73
|
+
assert.deepEqual(collapseOverlapsGeneratedFromRangeComparisonIfPossible([{
|
74
|
+
start: 0,
|
75
|
+
end: 100
|
76
|
+
}, {
|
77
|
+
start: 105,
|
78
|
+
end: 998
|
79
|
+
}], 1000),[{
|
80
|
+
start: 0,
|
81
|
+
end: 100
|
82
|
+
}, {
|
83
|
+
start: 105,
|
84
|
+
end: 998
|
85
|
+
}]);
|
86
|
+
});
|
87
|
+
it('collapses a split circular range with a third part', function() {
|
88
|
+
assert.deepEqual(collapseOverlapsGeneratedFromRangeComparisonIfPossible([{
|
89
|
+
start: 200,
|
90
|
+
end: 300
|
91
|
+
},{
|
92
|
+
start: 0,
|
93
|
+
end: 100
|
94
|
+
}, {
|
95
|
+
start: 500,
|
96
|
+
end: 999
|
97
|
+
}], 1000), [{
|
98
|
+
start: 500,
|
99
|
+
end: 100
|
100
|
+
},{
|
101
|
+
start: 200,
|
102
|
+
end: 300
|
103
|
+
}]);
|
104
|
+
});
|
105
|
+
|
106
|
+
it('collapses a split circular range with a third part in a different order', function() {
|
107
|
+
assert.deepEqual(collapseOverlapsGeneratedFromRangeComparisonIfPossible([{
|
108
|
+
start: 0,
|
109
|
+
end: 100
|
110
|
+
},{
|
111
|
+
start: 200,
|
112
|
+
end: 300
|
113
|
+
}, {
|
114
|
+
start: 500,
|
115
|
+
end: 999
|
116
|
+
}], 1000), [{
|
117
|
+
start: 500,
|
118
|
+
end: 100
|
119
|
+
},{
|
120
|
+
start: 200,
|
121
|
+
end: 300
|
122
|
+
}]);
|
123
|
+
});
|
124
|
+
});
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import {assign} from "lodash";
|
2
|
+
|
3
|
+
export default function convertRangeIndices(range, inputType, outputType) {
|
4
|
+
inputType = inputType || {}
|
5
|
+
outputType = outputType || {}
|
6
|
+
return assign({},range,{
|
7
|
+
start: Number(range.start) + (inputType.inclusive1BasedStart
|
8
|
+
? outputType.inclusive1BasedStart
|
9
|
+
? 0
|
10
|
+
: -1
|
11
|
+
: outputType.inclusive1BasedStart
|
12
|
+
? 1
|
13
|
+
: 0),
|
14
|
+
end: Number(range.end) + (inputType.inclusive1BasedEnd
|
15
|
+
? outputType.inclusive1BasedEnd
|
16
|
+
? 0
|
17
|
+
: -1
|
18
|
+
: outputType.inclusive1BasedEnd
|
19
|
+
? 1
|
20
|
+
: 0)
|
21
|
+
})
|
22
|
+
};
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import convertRangeIndices from './convertRangeIndices';
|
2
|
+
import chai from 'chai';
|
3
|
+
chai.should();
|
4
|
+
describe('convertRangeIndices', function () {
|
5
|
+
it('should correctly convert various types of ranges', function () {
|
6
|
+
convertRangeIndices({start: 9,end:0},{inclusive1BasedStart: true}).should.deep.equal({start: 8,end: 0})
|
7
|
+
convertRangeIndices({start: 9,end:0},{inclusive1BasedStart: true}, {inclusive1BasedEnd: true}).should.deep.equal({start: 8,end: 1})
|
8
|
+
convertRangeIndices({start: 9,end:0},{inclusive1BasedEnd: true}, {inclusive1BasedEnd: true}).should.deep.equal({start: 9,end: 0})
|
9
|
+
convertRangeIndices({start: 4,end:5},{inclusive1BasedEnd: true}, {inclusive1BasedStart: true}).should.deep.equal({start: 5,end: 4})
|
10
|
+
convertRangeIndices({
|
11
|
+
start: '1',
|
12
|
+
end: '28',
|
13
|
+
},
|
14
|
+
{inclusive1BasedStart: true, inclusive1BasedEnd: true},
|
15
|
+
{})
|
16
|
+
.should.deep.equal({start: 0,end: 27})
|
17
|
+
});
|
18
|
+
it('should not remove other attributes on the range object', function () {
|
19
|
+
convertRangeIndices({start: 4,end:5, someOtherAttribute: 'yay'},{inclusive1BasedEnd: true}, {inclusive1BasedStart: true})
|
20
|
+
.should.deep.equal({start: 5,end: 4, someOtherAttribute: 'yay'})
|
21
|
+
});
|
22
|
+
})
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import normalizePositionByRangeLength from './normalizePositionByRangeLength';
|
2
|
+
import {assign} from "lodash";
|
3
|
+
|
4
|
+
export default function expandOrContractCircularRangeToPosition(range, position, maxLength) {
|
5
|
+
|
6
|
+
// 0 1 2 3 4 5 6 7 8 9
|
7
|
+
// r r r r r - - r r r
|
8
|
+
//0 1 2 3 4 5 6 7 8 9 10
|
9
|
+
// |
|
10
|
+
const newRange = assign({}, range);
|
11
|
+
let endMoved = true;
|
12
|
+
if (range.end >= position) {
|
13
|
+
if (position + maxLength - range.start > range.end - position) {
|
14
|
+
newRange.end = normalizePositionByRangeLength(position - 1, maxLength, false);
|
15
|
+
} else {
|
16
|
+
newRange.start = position;
|
17
|
+
endMoved = false;
|
18
|
+
}
|
19
|
+
} else {
|
20
|
+
if (range.start < position) {
|
21
|
+
if (range.end + maxLength - position > position - range.start) {
|
22
|
+
newRange.start = position;
|
23
|
+
endMoved = false;
|
24
|
+
} else {
|
25
|
+
newRange.end = position - 1;
|
26
|
+
}
|
27
|
+
} else {
|
28
|
+
//position somewhere between end and start
|
29
|
+
if (range.start - position > position - range.end) {
|
30
|
+
newRange.end = position - 1;
|
31
|
+
} else {
|
32
|
+
endMoved = false;
|
33
|
+
newRange.start = position;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
return ({
|
38
|
+
newRange: newRange,
|
39
|
+
endMoved: endMoved
|
40
|
+
})
|
41
|
+
};
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import {assign} from "lodash";
|
2
|
+
|
3
|
+
export default function expandOrContractNonCircularRangeToPosition (range, position) {
|
4
|
+
const newRange = assign({},range);
|
5
|
+
let endMoved = true;
|
6
|
+
if (range.start > position) {
|
7
|
+
newRange.start = position;
|
8
|
+
endMoved = false;
|
9
|
+
} else {
|
10
|
+
if (range.end < position) {
|
11
|
+
newRange.end = position - 1;
|
12
|
+
} else {
|
13
|
+
if (position - range.start > range.end - position) {
|
14
|
+
newRange.end = position - 1;
|
15
|
+
} else {
|
16
|
+
newRange.start = position;
|
17
|
+
endMoved = false;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
return ({
|
22
|
+
newRange: newRange,
|
23
|
+
endMoved: endMoved
|
24
|
+
})
|
25
|
+
};
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import {clone} from 'lodash';
|
2
|
+
import normalizeRange from './normalizeRange';
|
3
|
+
|
4
|
+
export default function expandOrContractRangeByLength(range, shiftBy, shiftStart, sequenceLength) {
|
5
|
+
const rangeToReturn = clone(range);
|
6
|
+
if (shiftStart) {
|
7
|
+
rangeToReturn.start -=shiftBy
|
8
|
+
} else {
|
9
|
+
rangeToReturn.end +=shiftBy
|
10
|
+
}
|
11
|
+
return normalizeRange(rangeToReturn,sequenceLength)
|
12
|
+
};
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import expandOrContractRangeByLength from './expandOrContractRangeByLength';
|
2
|
+
import chai from 'chai';
|
3
|
+
const expect = chai.expect;
|
4
|
+
chai.should();
|
5
|
+
|
6
|
+
describe('expandOrContractRangeByLength', function () {
|
7
|
+
it('shift start by 1 ', function () {
|
8
|
+
const expandedRange = expandOrContractRangeByLength({start: 3,end:4}, 1, true, 10);
|
9
|
+
expandedRange.should.deep.equal({
|
10
|
+
start: 2,
|
11
|
+
end: 4
|
12
|
+
})
|
13
|
+
});
|
14
|
+
it('shift end by 1 ', function () {
|
15
|
+
const expandedRange = expandOrContractRangeByLength({start: 3,end:4}, 1, false, 10);
|
16
|
+
expandedRange.should.deep.equal({
|
17
|
+
start: 3,
|
18
|
+
end: 5
|
19
|
+
})
|
20
|
+
});
|
21
|
+
it('shift end by 6 ', function () {
|
22
|
+
const expandedRange = expandOrContractRangeByLength({start: 3,end:4}, 6, false, 10);
|
23
|
+
expandedRange.should.deep.equal({
|
24
|
+
start: 3,
|
25
|
+
end: 0
|
26
|
+
})
|
27
|
+
});
|
28
|
+
|
29
|
+
it('circular range', function () {
|
30
|
+
const expandedRange = expandOrContractRangeByLength({start: 6,end:4}, 1, false, 10);
|
31
|
+
expandedRange.should.deep.equal({
|
32
|
+
start: 6,
|
33
|
+
end: 5
|
34
|
+
})
|
35
|
+
});
|
36
|
+
it('circular range', function () {
|
37
|
+
const expandedRange = expandOrContractRangeByLength({start: 6,end:4}, 1, true, 10);
|
38
|
+
expandedRange.should.deep.equal({
|
39
|
+
start: 5,
|
40
|
+
end: 4
|
41
|
+
})
|
42
|
+
});
|
43
|
+
it('circular range', function () {
|
44
|
+
const expandedRange = expandOrContractRangeByLength({start: 6,end:4}, 1, true, 10);
|
45
|
+
expandedRange.should.deep.equal({
|
46
|
+
start: 5,
|
47
|
+
end: 4
|
48
|
+
})
|
49
|
+
});
|
50
|
+
|
51
|
+
it('negative shiftBy', function () {
|
52
|
+
const expandedRange = expandOrContractRangeByLength({start: 6,end:4}, -1, true, 10);
|
53
|
+
expandedRange.should.deep.equal({
|
54
|
+
start: 7,
|
55
|
+
end: 4
|
56
|
+
})
|
57
|
+
});
|
58
|
+
it('negative shiftBy', function () {
|
59
|
+
const expandedRange = expandOrContractRangeByLength({start: 6,end:4}, -1, false, 10);
|
60
|
+
expandedRange.should.deep.equal({
|
61
|
+
start: 6,
|
62
|
+
end: 3
|
63
|
+
})
|
64
|
+
});
|
65
|
+
|
66
|
+
// it('should error if trying to expand more than possible', function () {
|
67
|
+
// var error = false;
|
68
|
+
// try {
|
69
|
+
// var range = expandOrContractRangeByLength({start: 6,end:4}, 10, false, 10)
|
70
|
+
// console.log('range:', range)
|
71
|
+
// } catch (e) {
|
72
|
+
// error = true;
|
73
|
+
// }
|
74
|
+
// expect(error).to.be.true;
|
75
|
+
// });
|
76
|
+
});
|
77
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import expandOrContractCircularRangeToPosition from './expandOrContractCircularRangeToPosition';
|
2
|
+
import expandOrContractNonCircularRangeToPosition from './expandOrContractNonCircularRangeToPosition';
|
3
|
+
|
4
|
+
export default function expandOrContractRangeToPosition(range, position, maxLength) {
|
5
|
+
if (range.start > range.end) {
|
6
|
+
return expandOrContractCircularRangeToPosition(range, position, maxLength)
|
7
|
+
} else {
|
8
|
+
return expandOrContractNonCircularRangeToPosition(range, position, maxLength)
|
9
|
+
}
|
10
|
+
};
|
@@ -0,0 +1,85 @@
|
|
1
|
+
import expandOrContractRangeByLength from './expandOrContractRangeByLength';
|
2
|
+
import isRangeWithinRange from './isRangeWithinRange';
|
3
|
+
import getOverlapsOfPotentiallyCircularRanges from './getOverlapsOfPotentiallyCircularRanges';
|
4
|
+
import translateRange from './translateRange';
|
5
|
+
import getRangeLength from './getRangeLength';
|
6
|
+
|
7
|
+
export default function flipRelativeRange(innerRange, outerRange, sequenceLength, options) {
|
8
|
+
const isFullyContained = isRangeWithinRange(innerRange,outerRange,sequenceLength);
|
9
|
+
if (isFullyContained) {
|
10
|
+
return flipFullyContainedRange(innerRange,outerRange,sequenceLength)
|
11
|
+
}
|
12
|
+
else {
|
13
|
+
// flip not fully contained range
|
14
|
+
return flipNonFullyContainedRange(innerRange,outerRange,sequenceLength)
|
15
|
+
}
|
16
|
+
};
|
17
|
+
|
18
|
+
function flipNonFullyContainedRange(innerRange, outerRange, sequenceLength, options) {
|
19
|
+
const outerFullyContained = isRangeWithinRange(outerRange, innerRange,sequenceLength);
|
20
|
+
let flippedInnerRange;
|
21
|
+
if (outerFullyContained) {
|
22
|
+
//special logic
|
23
|
+
// flipFullyContainedRange(outerRange, outerRange, sequenceLength)
|
24
|
+
const expandBy1 = getRangeLength({
|
25
|
+
start: innerRange.start,
|
26
|
+
end: outerRange.start
|
27
|
+
},sequenceLength) - 1;
|
28
|
+
flippedInnerRange = expandOrContractRangeByLength(outerRange, expandBy1, false, sequenceLength)
|
29
|
+
|
30
|
+
const expandBy2 = getRangeLength({
|
31
|
+
end: innerRange.end,
|
32
|
+
start: outerRange.end
|
33
|
+
},sequenceLength) - 1;
|
34
|
+
flippedInnerRange = expandOrContractRangeByLength(flippedInnerRange, expandBy2, true, sequenceLength)
|
35
|
+
} else {
|
36
|
+
//find overlaps of ranges
|
37
|
+
const overlaps = getOverlapsOfPotentiallyCircularRanges(innerRange, outerRange, sequenceLength);
|
38
|
+
//take first overlap and determine which end of outer range it overlaps
|
39
|
+
if (overlaps.length >= 1) {
|
40
|
+
let overlapExtendsForward;
|
41
|
+
const firstOverlap = overlaps[0];
|
42
|
+
overlapExtendsForward = firstOverlap.start !== outerRange.start
|
43
|
+
//flip using fully contained logic
|
44
|
+
const flippedTruncatedInner = flipFullyContainedRange(firstOverlap, outerRange, sequenceLength);
|
45
|
+
//extend in the opposite direction
|
46
|
+
const lengthToExtend = getRangeLength(innerRange,sequenceLength) - getRangeLength(flippedTruncatedInner, sequenceLength);
|
47
|
+
flippedInnerRange = expandOrContractRangeByLength(flippedTruncatedInner, lengthToExtend, overlapExtendsForward, sequenceLength)
|
48
|
+
} else {
|
49
|
+
throw new Error('This case (relative ranges that do not overlap) is unsupported! ')
|
50
|
+
}
|
51
|
+
}
|
52
|
+
return flippedInnerRange
|
53
|
+
}
|
54
|
+
|
55
|
+
function flipFullyContainedRange(innerRange, outerRange, sequenceLength, options) {
|
56
|
+
//translate both ranges by offset such that outer range start = 0
|
57
|
+
const translateBy = -outerRange.start;
|
58
|
+
const translatedOuterRange = translateRange(outerRange, translateBy, sequenceLength);
|
59
|
+
const translatedInnerRange = translateRange(innerRange, translateBy, sequenceLength);
|
60
|
+
|
61
|
+
//flip like non origin spanning range
|
62
|
+
const translatedFlippedInnerRange = flipNonOriginSpanningContainedRange(translatedInnerRange, translatedOuterRange, sequenceLength);
|
63
|
+
|
64
|
+
//translate inner range back by negative offset
|
65
|
+
const flippedInnerRange = translateRange(translatedFlippedInnerRange, -translateBy, sequenceLength);
|
66
|
+
return flippedInnerRange
|
67
|
+
}
|
68
|
+
|
69
|
+
function flipNonOriginSpanningContainedRange(innerRange, outerRange, sequenceLength) {
|
70
|
+
//non origin spanning, fully contained inner
|
71
|
+
const offsetFromStart = innerRange.start - outerRange.start;
|
72
|
+
const newInnerEnd = outerRange.end - offsetFromStart;
|
73
|
+
const innerRangeLength = getRangeLength(innerRange, sequenceLength);
|
74
|
+
|
75
|
+
return {
|
76
|
+
end: newInnerEnd,
|
77
|
+
start: newInnerEnd - (innerRangeLength -1)
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
//take 2
|
85
|
+
|
@@ -0,0 +1,124 @@
|
|
1
|
+
/* eslint-disable no-var*/
|
2
|
+
import flipContainedRange from './flipContainedRange';
|
3
|
+
|
4
|
+
import chai from 'chai';
|
5
|
+
chai.should();
|
6
|
+
|
7
|
+
describe('flipContainedRange', function () {
|
8
|
+
it('non origin spanning, fully contained inner', function () {
|
9
|
+
const innerRange ={
|
10
|
+
start: 5,
|
11
|
+
end: 13
|
12
|
+
};
|
13
|
+
const outerRange = {
|
14
|
+
start: 0,
|
15
|
+
end:20
|
16
|
+
};
|
17
|
+
const sequenceLength = 40;
|
18
|
+
const flippedInnerRange = flipContainedRange(innerRange, outerRange, sequenceLength);
|
19
|
+
flippedInnerRange.should.deep.equal({
|
20
|
+
start: 7,
|
21
|
+
end:15
|
22
|
+
})
|
23
|
+
});
|
24
|
+
it('non origin spanning outer, origin spanning fully contained inner', function () {
|
25
|
+
const innerRange ={
|
26
|
+
start: 3,
|
27
|
+
end: 1
|
28
|
+
};
|
29
|
+
const outerRange = {
|
30
|
+
start: 0,
|
31
|
+
end:3
|
32
|
+
};
|
33
|
+
const sequenceLength = 4;
|
34
|
+
const flippedInnerRange = flipContainedRange(innerRange, outerRange, sequenceLength);
|
35
|
+
flippedInnerRange.should.deep.equal({
|
36
|
+
start: 2,
|
37
|
+
end:0
|
38
|
+
})
|
39
|
+
});
|
40
|
+
it('origin spanning outer, non-origin spanning, fully contained inner', function () {
|
41
|
+
const innerRange ={
|
42
|
+
start: 1,
|
43
|
+
end: 3
|
44
|
+
};
|
45
|
+
const outerRange = {
|
46
|
+
start: 8,
|
47
|
+
end:5
|
48
|
+
};
|
49
|
+
const sequenceLength = 10;
|
50
|
+
const flippedInnerRange = flipContainedRange(innerRange, outerRange, sequenceLength);
|
51
|
+
flippedInnerRange.should.deep.equal({
|
52
|
+
start: 0,
|
53
|
+
end:2
|
54
|
+
})
|
55
|
+
});
|
56
|
+
it('non-origin spanning outer, non-origin spanning, non-fully contained inner', function () {
|
57
|
+
const innerRange ={
|
58
|
+
start: 1,
|
59
|
+
end: 4
|
60
|
+
};
|
61
|
+
const outerRange = {
|
62
|
+
start: 3,
|
63
|
+
end:6
|
64
|
+
};
|
65
|
+
const sequenceLength = 10;
|
66
|
+
const flippedInnerRange = flipContainedRange(innerRange, outerRange, sequenceLength);
|
67
|
+
flippedInnerRange.should.deep.equal({
|
68
|
+
start: 5,
|
69
|
+
end:8
|
70
|
+
})
|
71
|
+
});
|
72
|
+
it('non-origin spanning outer, non-origin spanning, non-fully contained inner', function () {
|
73
|
+
const innerRange ={
|
74
|
+
start: 4,
|
75
|
+
end: 2
|
76
|
+
};
|
77
|
+
const outerRange = {
|
78
|
+
start: 2,
|
79
|
+
end:5
|
80
|
+
};
|
81
|
+
const sequenceLength = 10;
|
82
|
+
const flippedInnerRange = flipContainedRange(innerRange, outerRange, sequenceLength);
|
83
|
+
flippedInnerRange.should.deep.equal({
|
84
|
+
start: 5,
|
85
|
+
end:3
|
86
|
+
})
|
87
|
+
});
|
88
|
+
|
89
|
+
it('inner fully spans outer, does not wrap origin', function () {
|
90
|
+
const innerRange ={
|
91
|
+
start: 1,
|
92
|
+
end: 7
|
93
|
+
};
|
94
|
+
const outerRange = {
|
95
|
+
start: 2,
|
96
|
+
end:5
|
97
|
+
};
|
98
|
+
const sequenceLength = 10;
|
99
|
+
const flippedInnerRange = flipContainedRange(innerRange, outerRange, sequenceLength);
|
100
|
+
flippedInnerRange.should.deep.equal({
|
101
|
+
start: 0,
|
102
|
+
end:6
|
103
|
+
})
|
104
|
+
});
|
105
|
+
|
106
|
+
it('inner fully spans outer, does wrap origin', function () {
|
107
|
+
const innerRange ={
|
108
|
+
start: 4,
|
109
|
+
end: 2
|
110
|
+
};
|
111
|
+
const outerRange = {
|
112
|
+
start: 5,
|
113
|
+
end:2
|
114
|
+
};
|
115
|
+
const sequenceLength = 10;
|
116
|
+
const flippedInnerRange = flipContainedRange(innerRange, outerRange, sequenceLength);
|
117
|
+
flippedInnerRange.should.deep.equal({
|
118
|
+
start: 5,
|
119
|
+
end:3
|
120
|
+
})
|
121
|
+
});
|
122
|
+
|
123
|
+
});
|
124
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import normalizePositionByRangeLength from './normalizePositionByRangeLength';
|
2
|
+
|
3
|
+
export default function generateRandomRange(minStart, maxEnd, maxLength) {
|
4
|
+
const start = getRandomInt(minStart, maxEnd);
|
5
|
+
let end;
|
6
|
+
if (maxLength) {
|
7
|
+
end = normalizePositionByRangeLength(getRandomInt(start, start + maxLength), maxEnd)
|
8
|
+
} else {
|
9
|
+
end = getRandomInt(minStart, maxEnd);
|
10
|
+
}
|
11
|
+
return {
|
12
|
+
start: start,
|
13
|
+
end: end,
|
14
|
+
}
|
15
|
+
};
|
16
|
+
|
17
|
+
|
18
|
+
function getRandomInt(min, max) {
|
19
|
+
return Math.floor(Math.random() * (max - min)) + min;
|
20
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import getRangeLength from './getRangeLength';
|
2
|
+
import generateRandomRange from './generateRandomRange';
|
3
|
+
import chai from 'chai';
|
4
|
+
chai.should();
|
5
|
+
|
6
|
+
describe('generateRandomRange', function () {
|
7
|
+
it('should generate random ranges between a start and end', function () {
|
8
|
+
for (let i = 0; i < 1000; i++) {
|
9
|
+
const range = generateRandomRange(0,10);
|
10
|
+
range.start.should.be.below(11)
|
11
|
+
range.end.should.be.below(11)
|
12
|
+
}
|
13
|
+
});
|
14
|
+
|
15
|
+
it('should generate random ranges between a start and end and with length less than maxLength', function () {
|
16
|
+
for (let i = 0; i < 1000; i++) {
|
17
|
+
const range = generateRandomRange(0,10,5);
|
18
|
+
const length = getRangeLength(range);
|
19
|
+
if (length > -1) {
|
20
|
+
length.should.be.below(6)
|
21
|
+
}
|
22
|
+
}
|
23
|
+
});
|
24
|
+
});
|
@@ -0,0 +1,24 @@
|
|
1
|
+
//function that returns the annotation range type
|
2
|
+
export default function getAnnotationRangeType(annotationRange, enclosingRangeType, forward) {
|
3
|
+
if (annotationRange.start === enclosingRangeType.start) {
|
4
|
+
if (annotationRange.end === enclosingRangeType.end) {
|
5
|
+
return 'beginningAndEnd';
|
6
|
+
} else {
|
7
|
+
if (forward) {
|
8
|
+
return 'start';
|
9
|
+
} else {
|
10
|
+
return 'end';
|
11
|
+
}
|
12
|
+
}
|
13
|
+
} else {
|
14
|
+
if (annotationRange.end === enclosingRangeType.end) {
|
15
|
+
if (forward) {
|
16
|
+
return 'end';
|
17
|
+
} else {
|
18
|
+
return 'start';
|
19
|
+
}
|
20
|
+
} else {
|
21
|
+
return 'middle';
|
22
|
+
}
|
23
|
+
}
|
24
|
+
};
|