@teselagen/range-utils 0.1.20 → 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.
Files changed (90) hide show
  1. package/index.js +6542 -18010
  2. package/index.mjs +6593 -18007
  3. package/index.umd.js +6836 -18306
  4. package/package.json +1 -1
  5. package/src/adjustRangeToDeletionOfAnotherRange.js +53 -0
  6. package/src/adjustRangeToDeletionOfAnotherRange.test.js +107 -0
  7. package/src/adjustRangeToInsert.js +28 -0
  8. package/src/adjustRangeToInsert.test.js +111 -0
  9. package/src/adjustRangeToRotation.js +24 -0
  10. package/src/adjustRangeToRotation.test.js +123 -0
  11. package/src/checkIfNonCircularRangesOverlap.js +30 -0
  12. package/src/checkIfNonCircularRangesOverlap.test.js +45 -0
  13. package/src/checkIfPotentiallyCircularRangesOverlap.js +16 -0
  14. package/src/checkIfPotentiallyCircularRangesOverlap.test.js +73 -0
  15. package/src/collapseOverlapsGeneratedFromRangeComparisonIfPossible.js +47 -0
  16. package/src/collapseOverlapsGeneratedFromRangeComparisonIfPossible.test.js +124 -0
  17. package/src/convertRangeIndices.js +22 -0
  18. package/src/convertRangeIndices.test.js +22 -0
  19. package/src/convertRangeTo0Based.js +5 -0
  20. package/src/convertRangeTo1Based.js +5 -0
  21. package/src/doesRangeSpanEntireSequence.js +7 -0
  22. package/src/doesRangeSpanOrigin.js +3 -0
  23. package/src/expandOrContractCircularRangeToPosition.js +41 -0
  24. package/src/expandOrContractNonCircularRangeToPosition.js +25 -0
  25. package/src/expandOrContractRangeByLength.js +12 -0
  26. package/src/expandOrContractRangeByLength.test.js +77 -0
  27. package/src/expandOrContractRangeToPosition.js +10 -0
  28. package/src/flipContainedRange.js +85 -0
  29. package/src/flipContainedRange.test.js +124 -0
  30. package/src/generateRandomRange.js +20 -0
  31. package/src/generateRandomRange.test.js +24 -0
  32. package/src/getAnnotationRangeType.js +24 -0
  33. package/src/getAnnotationRangeType.test.js +59 -0
  34. package/src/getEachPositionInRangeAsArray.js +15 -0
  35. package/src/getEachPositionInRangeAsArray.test.js +9 -0
  36. package/src/getLengthOfOverlappingRegionsBetweenTwoRanges.js +9 -0
  37. package/src/getLengthOfOverlappingRegionsBetweenTwoRanges.test.js +24 -0
  38. package/src/getMiddleOfRange.js +10 -0
  39. package/src/getMiddleOfRange.test.js +44 -0
  40. package/src/getOverlapOfNonCircularRanges.js +35 -0
  41. package/src/getOverlapsOfPotentiallyCircularRanges.js +54 -0
  42. package/src/getOverlapsOfPotentiallyCircularRanges.test.js +199 -0
  43. package/src/getPositionFromAngle.js +7 -0
  44. package/src/getRangeAngles.js +33 -0
  45. package/src/getRangeAngles.test.js +77 -0
  46. package/src/getRangeLength.js +14 -0
  47. package/src/getRangeLength.test.js +30 -0
  48. package/src/getRangesBetweenTwoRanges.js +28 -0
  49. package/src/getSequenceWithinRange.js +17 -0
  50. package/src/getSequenceWithinRange.test.js +49 -0
  51. package/src/getShortestDistanceBetweenTwoPositions.js +12 -0
  52. package/src/getShortestDistanceBetweenTwoPositions.test.js +14 -0
  53. package/src/getYOffsetForPotentiallyCircularRange.js +24 -0
  54. package/src/getYOffsetsForPotentiallyCircularRanges.js +20 -0
  55. package/src/getYOffsetsForPotentiallyCircularRanges.test.js +29 -0
  56. package/src/getZeroedRangeOverlaps.js +17 -0
  57. package/src/getZeroedRangeOverlaps.test.js +36 -0
  58. package/src/index.js +51 -0
  59. package/src/index.test.js +33 -0
  60. package/src/invertRange.js +21 -0
  61. package/src/invertRange.test.js +96 -0
  62. package/src/isPositionCloserToRangeStartThanRangeEnd.js +8 -0
  63. package/src/isPositionCloserToRangeStartThanRangeEnd.test.js +17 -0
  64. package/src/isPositionWithinRange.js +31 -0
  65. package/src/isRangeOrPositionWithinRange.js +29 -0
  66. package/src/isRangeOrPositionWithinRange.test.js +150 -0
  67. package/src/isRangeWithinRange.js +17 -0
  68. package/src/loopEachPositionInRange.js +5 -0
  69. package/src/modulatePositionByRange.js +10 -0
  70. package/src/modulatePositionByRange.test.js +12 -0
  71. package/src/modulateRangeBySequenceLength.js +11 -0
  72. package/src/modulateRangeBySequenceLength.test.js +16 -0
  73. package/src/normalizePositionByRangeLength.js +20 -0
  74. package/src/normalizePositionByRangeLength.test.js +23 -0
  75. package/src/normalizePositionByRangeLength1Based.js +5 -0
  76. package/src/normalizePositionByRangeLength1Based.test.js +9 -0
  77. package/src/normalizeRange.js +11 -0
  78. package/src/normalizeRange.test.js +9 -0
  79. package/src/provideInclusiveOptions.js +26 -0
  80. package/src/reversePositionInRange.js +13 -0
  81. package/src/splitRangeIntoTwoPartsIfItIsCircular.js +31 -0
  82. package/src/splitRangeIntoTwoPartsIfItIsCircular.test.js +33 -0
  83. package/src/translateRange.js +9 -0
  84. package/src/translateRange.test.js +20 -0
  85. package/src/trimNonCicularRangeByAnotherNonCircularRange.js +57 -0
  86. package/src/trimNumberToFitWithin0ToAnotherNumber.js +12 -0
  87. package/src/trimRangeByAnotherRange.js +102 -0
  88. package/src/trimRangeByAnotherRange.test.js +314 -0
  89. package/src/zeroSubrangeByContainerRange.js +36 -0
  90. package/src/zeroSubrangeByContainerRange.test.js +51 -0
@@ -0,0 +1,17 @@
1
+ //TNR: THIS METHOD ONLY WORKS FOR COMPARING 0-BASED RANGES!!!!!!
2
+ import trimRangeByAnotherRange from "./trimRangeByAnotherRange";
3
+
4
+ export default function isRangeWithinRange(
5
+ rangeToCheck,
6
+ containingRange,
7
+ maxLength
8
+ ) {
9
+
10
+ const ranges = trimRangeByAnotherRange(
11
+ rangeToCheck,
12
+ containingRange,
13
+ maxLength
14
+ );
15
+ if (ranges === null) return false
16
+ return !ranges;
17
+ };
@@ -0,0 +1,5 @@
1
+ import getEachPositionInRangeAsArray from './getEachPositionInRangeAsArray';
2
+
3
+ export default function loopEachPositionInRange(range, rangeMax, func) {
4
+ getEachPositionInRangeAsArray(range,rangeMax).map(func)
5
+ };
@@ -0,0 +1,10 @@
1
+ //this function takes a position that might not fit in a given range and puts it into that range
2
+ export default function modulatePositionByRange(position, range) {
3
+ let returnVal = position;
4
+ if (position < range.start) {
5
+ returnVal = range.end - (range.start - (position + 1))
6
+ } else if (position > range.end) {
7
+ returnVal = range.start + (position - range.end - 1)
8
+ }
9
+ return returnVal
10
+ };
@@ -0,0 +1,12 @@
1
+ import modulatePositionByRange from './modulatePositionByRange';
2
+ import {expect} from 'chai';
3
+
4
+ describe('modulatePositionByRange', function() {
5
+ it('should modulate positions by their min and max', function() {
6
+ expect(modulatePositionByRange(0,{start: 1, end: 10})).to.equal(10)
7
+ expect(modulatePositionByRange(11,{start: 1, end: 10})).to.equal(1)
8
+ expect(modulatePositionByRange(0,{start: 0, end: 10})).to.equal(0)
9
+ expect(modulatePositionByRange(10,{start: 0, end: 10})).to.equal(10)
10
+ })
11
+
12
+ })
@@ -0,0 +1,11 @@
1
+ import normalizePositionByRangeLength from './normalizePositionByRangeLength';
2
+ import provideInclusiveOptions from './provideInclusiveOptions';
3
+ import {assign} from "lodash";
4
+ export default provideInclusiveOptions(modulateRangeBySequenceLength);
5
+
6
+ function modulateRangeBySequenceLength(range, seqLen) {
7
+ return assign(range, {
8
+ start: normalizePositionByRangeLength(range.start, seqLen),
9
+ end: normalizePositionByRangeLength(range.end, seqLen)
10
+ })
11
+ }
@@ -0,0 +1,16 @@
1
+ import {expect} from 'chai';
2
+ import modulateRangeBySequenceLength from './modulateRangeBySequenceLength';
3
+ describe('modulateRangeBySequenceLength', function() {
4
+ it('should modulate ranges that are outside of the sequence length', function() {
5
+ //agtc
6
+ //0123
7
+ expect(modulateRangeBySequenceLength({start: 2, end: 5},4)).to.deep.equal({start: 2, end:1})
8
+ expect(modulateRangeBySequenceLength({start: 2, end: 4},4)).to.deep.equal({start: 2, end:0})
9
+ //agtc
10
+ //12345
11
+ expect(modulateRangeBySequenceLength({start: 2, end: 4},4,{inclusive1BasedEnd: true})).to.deep.equal({start: 2, end:4})
12
+ expect(modulateRangeBySequenceLength({start: 2, end: 4},4,{inclusive1BasedEnd: true,inclusive1BasedStart: true})).to.deep.equal({start: 2, end:4})
13
+ expect(modulateRangeBySequenceLength({start: 5, end: 4},4,{inclusive1BasedEnd: true,inclusive1BasedStart: true})).to.deep.equal({start: 1, end:4})
14
+ expect(modulateRangeBySequenceLength({start: 5, end: 5},4,{inclusive1BasedEnd: true,inclusive1BasedStart: true})).to.deep.equal({start: 1, end:1})
15
+ })
16
+ })
@@ -0,0 +1,20 @@
1
+ //
2
+ // ac.throw([ac.posInt, ac.posInt, ac.bool], arguments);
3
+ export default function normalizePositionByRangeLength(pPosition, sequenceLength, isInBetweenPositions) {
4
+ //isInBetweenPositions refers to:
5
+ // A T G C
6
+ // 0 1 2 3 <-- isInBetweenPositions = false is counting the positions themselves
7
+ //0 1 2 3 4 <-- isInBetweenPositions = true is counting the spaces between positions
8
+ // ac.throw([ac.number, ac.posInt, ac.bool], arguments);
9
+ let position = pPosition;
10
+ if (position < 0) {
11
+ position += sequenceLength;
12
+ } else if (position + (isInBetweenPositions ? 0 : 1) > sequenceLength) {
13
+ position -= sequenceLength;
14
+ }
15
+ return position < 0
16
+ ? 0
17
+ : position > (sequenceLength - (isInBetweenPositions ? 0 : 1))
18
+ ? sequenceLength - (isInBetweenPositions ? 0 : 1)
19
+ : position
20
+ };
@@ -0,0 +1,23 @@
1
+ import {expect} from 'chai';
2
+ import normalizePositionByRangeLength from './normalizePositionByRangeLength';
3
+ describe('normalizePositionByRangeLength', function() {
4
+ it('should normalize positions by their max length, assuming 0-based inclusive indices', function() {
5
+ expect(normalizePositionByRangeLength(10,9)).to.equal(1)
6
+ expect(normalizePositionByRangeLength(9,9)).to.equal(0)
7
+ expect(normalizePositionByRangeLength(3572,2000)).to.equal(1572)
8
+ })
9
+ it('should set something more than twice outside the sequence length to either 0 or sequenceLength-1', function () {
10
+ expect(normalizePositionByRangeLength(-3572,2000)).to.equal(0)
11
+ expect(normalizePositionByRangeLength(33572,2000)).to.equal(1999)
12
+ })
13
+ it('should handle isInBetweenPositions === true being passed in ', function() {
14
+ expect(normalizePositionByRangeLength(9,9,true)).to.equal(9)
15
+ })
16
+ it('should set something more than twice outside the sequence length to either 0 or sequenceLength when isInBetweenPositions is true', function () {
17
+ expect(normalizePositionByRangeLength(-3572,2000, true)).to.equal(0)
18
+ expect(normalizePositionByRangeLength(33572,2000, true)).to.equal(2000)
19
+ })
20
+ it('should not do anything if it does not have to', function() {
21
+ expect(normalizePositionByRangeLength(10,99)).to.equal(10)
22
+ })
23
+ })
@@ -0,0 +1,5 @@
1
+ import modulatePositionByRange from './modulatePositionByRange';
2
+
3
+ export default function normalizePositionByRangeLength1Based (position, sequenceLength) {
4
+ return modulatePositionByRange(position, {start: 1, end: sequenceLength})
5
+ };
@@ -0,0 +1,9 @@
1
+ import normalizePositionByRangeLength1Based from './normalizePositionByRangeLength1Based';
2
+ import {expect} from 'chai';
3
+ describe('normalizePositionByRangeLength1Based', function () {
4
+ it('should normalize a 1 based position by a 1 based range length', function() {
5
+ expect(normalizePositionByRangeLength1Based(0,10)).to.equal(10)
6
+ expect(normalizePositionByRangeLength1Based(-1,10)).to.equal(9)
7
+ expect(normalizePositionByRangeLength1Based(11,10)).to.equal(1)
8
+ })
9
+ });
@@ -0,0 +1,11 @@
1
+ //normalize range takes in a range that might be slightly outside of the rangeMax and wraps the start/end as necessary to fit
2
+ import {assign} from "lodash";
3
+
4
+ import normalizePositionByRangeLength from './normalizePositionByRangeLength';
5
+
6
+ export default function normalizeRange(range, sequenceLength) {
7
+ return assign({}, range, {
8
+ start: normalizePositionByRangeLength(range.start, sequenceLength),
9
+ end: normalizePositionByRangeLength(range.end, sequenceLength),
10
+ })
11
+ };
@@ -0,0 +1,9 @@
1
+ import normalizeRange from './normalizeRange';
2
+ import {expect} from 'chai';
3
+ describe('normalizeRange', function () {
4
+ it('should normalize range correctly', function() {
5
+ const normalizedRange = normalizeRange({start: 0, end: -1},10)
6
+ expect(normalizedRange.start).to.equal(0)
7
+ expect(normalizedRange.end).to.equal(9)
8
+ })
9
+ });
@@ -0,0 +1,26 @@
1
+ import {assign} from "lodash";
2
+
3
+ export default function provideInclusiveOptions (funToWrap) {
4
+ return function () {
5
+ const args = Array.prototype.slice.call(arguments);
6
+ const options = args[args.length-1];
7
+ if (options && (options.inclusive1BasedEnd || options.inclusive1BasedStart)) {
8
+ args.forEach(function (arg,index) {
9
+ if (arg && arg.start > -1 && options.inclusive1BasedStart) {
10
+ args[index] = assign(arg, {start: arg.start-1})
11
+ }
12
+ if (arg && arg.end > -1 && options.inclusive1BasedEnd) {
13
+ args[index] = assign(arg, {end: arg.end-1})
14
+ }
15
+ })
16
+ }
17
+ let returnVal = funToWrap.apply(this,args);
18
+ if (returnVal && returnVal.start > -1 && options && options.inclusive1BasedStart) {
19
+ returnVal = assign(returnVal, {start: returnVal.start+1})
20
+ }
21
+ if (returnVal && returnVal.end > -1 && options && options.inclusive1BasedEnd) {
22
+ returnVal = assign(returnVal, {end: returnVal.end+1})
23
+ }
24
+ return returnVal
25
+ };
26
+ };
@@ -0,0 +1,13 @@
1
+ //
2
+ export default function reversePositionInRange(position, rangeLength, isInBetweenPositions) {
3
+ //isInBetweenPositions refers to:
4
+ // A T G C
5
+ // 0 1 2 3 <-- isInBetweenPositions = false is counting the positions themselves
6
+ //0 1 2 3 4 <-- isInBetweenPositions = true is counting the spaces between positions
7
+ // ac.throw([
8
+ // ac.posInt,
9
+ // ac.posInt,
10
+ // ac.bool
11
+ // ], arguments);
12
+ return rangeLength - position - (isInBetweenPositions ? 0 : 1);
13
+ };
@@ -0,0 +1,31 @@
1
+ //
2
+ // ac.throw([ac.posInt, ac.posInt, ac.bool], arguments);
3
+ //takes a potentially circular range and returns an array containing the range split on the origin
4
+ export default function splitRangeIntoTwoPartsIfItIsCircular(range, sequenceLength) {
5
+ // ac.throw([ac.range, ac.number], arguments);
6
+ if (sequenceLength !== 0) {
7
+ sequenceLength = sequenceLength || Infinity
8
+ }
9
+ const ranges = [];
10
+ if (range.start > range.end) {
11
+ //the range is cicular, so we return an array of two ranges
12
+ ranges.push({
13
+ start: 0,
14
+ end: range.end,
15
+ type: "end",
16
+ });
17
+ ranges.push({
18
+ start: range.start,
19
+ end: sequenceLength - 1,
20
+ type: "beginning",
21
+ });
22
+ } else {
23
+ //the range isn't circular, so we just return the range
24
+ ranges.push({
25
+ start: range.start,
26
+ end: range.end,
27
+ type: "beginningAndEnd",
28
+ });
29
+ }
30
+ return ranges;
31
+ };
@@ -0,0 +1,33 @@
1
+ // //takes a potentially circular range and returns an array containing the range split on the origin
2
+ // module.exports = function splitRangeIntoTwoPartsIfItIsCircular(range, maxLength) {
3
+ // if (range.start <= range.end) {
4
+ // //the range isn't circular, so we just return the range
5
+ // return [{
6
+ // start: range.start,
7
+ // end: range.end
8
+ // }];
9
+ // } else {
10
+ // //the range is cicular, so we return an array of two ranges
11
+ // return [{
12
+ // start: 0,
13
+ // end: range.end
14
+ // }, {
15
+ // start: range.start,
16
+ // end: maxLength - 1
17
+ // }];
18
+ // }
19
+ // };
20
+
21
+
22
+ import splitRangeIntoTwoPartsIfItIsCircular from './splitRangeIntoTwoPartsIfItIsCircular.js';
23
+
24
+ import assert from 'assert';
25
+ describe('splitRangeIntoTwoPartsIfItIsCircular', function() {
26
+ it('returns an array with one range in it if the array is non-circular', function() {
27
+ assert.deepEqual(splitRangeIntoTwoPartsIfItIsCircular({start: 0, end: 100}, 1000), [{start: 0, end: 100, type: 'beginningAndEnd'}]);
28
+ assert.deepEqual(splitRangeIntoTwoPartsIfItIsCircular({start: 10, end: 909}, 1000), [{start: 10, end: 909, type: 'beginningAndEnd'}]);
29
+ });
30
+ it('returns an array with two ranges in it if the array is circular', function() {
31
+ assert.deepEqual(splitRangeIntoTwoPartsIfItIsCircular({start: 110, end: 100},1000), [{start: 0, end: 100, type: 'end'},{start: 110, end: 999, type: 'beginning'}]);
32
+ });
33
+ });
@@ -0,0 +1,9 @@
1
+ import {assign} from "lodash";
2
+ import normalizePositionByRangeLength from './normalizePositionByRangeLength';
3
+
4
+ export default function translateRange(rangeToBeAdjusted, translateBy, rangeLength) {
5
+ return assign({}, rangeToBeAdjusted, {
6
+ start: normalizePositionByRangeLength(rangeToBeAdjusted.start + translateBy, rangeLength),
7
+ end: normalizePositionByRangeLength(rangeToBeAdjusted.end + translateBy, rangeLength)
8
+ });
9
+ };
@@ -0,0 +1,20 @@
1
+ import translateRange from './translateRange';
2
+ import {expect} from 'chai';
3
+
4
+ describe('translateRange', function() {
5
+ it('should correctly translate a range', function() {
6
+ expect(translateRange({start: 0, end: 10}, 10, 30)).to.deep.equal({
7
+ start: 10,
8
+ end: 20,
9
+ })
10
+ expect(translateRange({start: 0, end: 10}, 10, 15)).to.deep.equal({
11
+ start: 10,
12
+ end: 5,
13
+ })
14
+ expect(translateRange({start: 9, end: 10}, 10, 15)).to.deep.equal({
15
+ start: 4,
16
+ end: 5,
17
+ })
18
+
19
+ })
20
+ })
@@ -0,0 +1,57 @@
1
+ //
2
+ export default function trimNonCicularRangeByAnotherNonCircularRange(rangeToBeTrimmed, trimmingRange) {
3
+ // ac.throw([ac.range, ac.range], arguments);
4
+ let outputTrimmedRange;
5
+ if (!rangeToBeTrimmed) {
6
+ return outputTrimmedRange;
7
+ }
8
+ if (rangeToBeTrimmed.start < trimmingRange.start) {
9
+ if (rangeToBeTrimmed.end < trimmingRange.start) {
10
+ // rrr <range to be trimmed
11
+ // ttt <trimming range
12
+ outputTrimmedRange = {
13
+ start: rangeToBeTrimmed.start,
14
+ end: rangeToBeTrimmed.end
15
+ };
16
+ } else {
17
+ if (rangeToBeTrimmed.end > trimmingRange.end) {
18
+ // rrrrrr <range to be trimmed
19
+ // ttt <trimming range
20
+ outputTrimmedRange = {
21
+ start: rangeToBeTrimmed.start,
22
+ end: rangeToBeTrimmed.end
23
+ };
24
+ } else {
25
+ // rrrrrr <range to be trimmed
26
+ // ttt <trimming range
27
+ outputTrimmedRange = {
28
+ start: rangeToBeTrimmed.start,
29
+ end: trimmingRange.start - 1
30
+ };
31
+ }
32
+ }
33
+ } else {
34
+ if (rangeToBeTrimmed.end <= trimmingRange.end) {
35
+ // rrr <range to be trimmed
36
+ // ttttt <trimming range
37
+ //fully deleting the range, so do nothing
38
+ } else {
39
+ if (rangeToBeTrimmed.start > trimmingRange.end) {
40
+ // rrrrrr <range to be trimmed
41
+ // ttt <trimming range
42
+ outputTrimmedRange = {
43
+ end: rangeToBeTrimmed.end,
44
+ start: rangeToBeTrimmed.start
45
+ };
46
+ } else {
47
+ // rrrrrr <range to be trimmed
48
+ // ttt <trimming range
49
+ outputTrimmedRange = {
50
+ end: rangeToBeTrimmed.end,
51
+ start: trimmingRange.end + 1
52
+ };
53
+ }
54
+ }
55
+ }
56
+ return outputTrimmedRange;
57
+ };
@@ -0,0 +1,12 @@
1
+ //
2
+ // ac.throw([ac.posInt, ac.posInt, ac.bool], arguments);
3
+ export default function trimNumberToFitWithin0ToAnotherNumber(numberToBeTrimmed, max) {
4
+ // ac.throw([ac.number, ac.number], arguments);
5
+ if (numberToBeTrimmed < 0) {
6
+ numberToBeTrimmed = 0;
7
+ }
8
+ if (numberToBeTrimmed > max) {
9
+ numberToBeTrimmed = max;
10
+ }
11
+ return numberToBeTrimmed;
12
+ };
@@ -0,0 +1,102 @@
1
+ import getOverlapsOfPotentiallyCircularRanges from "./getOverlapsOfPotentiallyCircularRanges";
2
+ import splitRangeIntoTwoPartsIfItIsCircular from "./splitRangeIntoTwoPartsIfItIsCircular";
3
+ import trimNonCicularRangeByAnotherNonCircularRange from "./trimNonCicularRangeByAnotherNonCircularRange";
4
+ import {extend} from "lodash";
5
+
6
+ /**
7
+ * trims range, but does *not* adjust it
8
+ * returns a new range if there is one, or null, if it is trimmed completely
9
+ * @param {object} subRange {start:
10
+ * end:
11
+ * }
12
+ * @param {object} containerRange {start:
13
+ * end:
14
+ * }
15
+ * @param {int} sequenceLength
16
+ * @return {object} || null {start:
17
+ * end:
18
+ * }
19
+ */
20
+ export default function trimRangeByAnotherRange(
21
+ rangeToBeTrimmed,
22
+ trimmingRange,
23
+ sequenceLength
24
+ ) {
25
+ if (!rangeToBeTrimmed || !trimmingRange) {
26
+ console.warn("invalid range input");
27
+ return null; //a null return val means something went wrong with this function
28
+ }
29
+ let position;
30
+ for (position of [
31
+ rangeToBeTrimmed.start,
32
+ rangeToBeTrimmed.end,
33
+ trimmingRange.start,
34
+ trimmingRange.end,
35
+ ]) {
36
+ if (position < 0 || (!position && position !== 0)) {
37
+ console.warn("invalid range input");
38
+ return null; //a null return val means something went wrong with this function
39
+ }
40
+ }
41
+ //get the overlaps of the ranges
42
+ const overlaps = getOverlapsOfPotentiallyCircularRanges(
43
+ rangeToBeTrimmed,
44
+ trimmingRange,
45
+ sequenceLength
46
+ );
47
+ //split the range to be trimmed into pieces if necessary
48
+ if (!overlaps.length) {
49
+ //just return the range to be trimmed
50
+ return rangeToBeTrimmed;
51
+ }
52
+ //and trim both pieces by the already calculated overlaps
53
+ const splitRangesToBeTrimmed = splitRangeIntoTwoPartsIfItIsCircular(
54
+ rangeToBeTrimmed,
55
+ sequenceLength
56
+ );
57
+ splitRangesToBeTrimmed.forEach(function(nonCircularRangeToBeTrimmed, index) {
58
+ overlaps.forEach(function(overlap) {
59
+ if (nonCircularRangeToBeTrimmed) {
60
+ nonCircularRangeToBeTrimmed = trimNonCicularRangeByAnotherNonCircularRange(
61
+ nonCircularRangeToBeTrimmed,
62
+ overlap
63
+ );
64
+ }
65
+ });
66
+ splitRangesToBeTrimmed[index] = nonCircularRangeToBeTrimmed;
67
+ });
68
+ //filter out any of the split ranges that have been fully deleted!
69
+ const outputSplitRanges = splitRangesToBeTrimmed.filter(function(
70
+ trimmedRange
71
+ ) {
72
+ if (trimmedRange) {
73
+ return true;
74
+ }
75
+ return false;
76
+ });
77
+
78
+ let outputTrimmedRange;
79
+ if (outputSplitRanges.length < 0) {
80
+ //do nothing to the output trimmed range
81
+ } else if (outputSplitRanges.length === 1) {
82
+ outputTrimmedRange = outputSplitRanges[0];
83
+ } else if (outputSplitRanges.length === 2) {
84
+ if (outputSplitRanges[0].start < outputSplitRanges[1].start) {
85
+ outputTrimmedRange = {
86
+ start: outputSplitRanges[1].start,
87
+ end: outputSplitRanges[0].end,
88
+ };
89
+ } else {
90
+ outputTrimmedRange = {
91
+ start: outputSplitRanges[0].start,
92
+ end: outputSplitRanges[1].end,
93
+ };
94
+ }
95
+ }
96
+ if (outputTrimmedRange) {
97
+ return extend({}, rangeToBeTrimmed, {
98
+ start: outputTrimmedRange.start,
99
+ end: outputTrimmedRange.end,
100
+ });
101
+ }
102
+ };