@teselagen/sequence-utils 0.3.31 → 0.3.32-beta.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/index.umd.cjs CHANGED
@@ -2738,30 +2738,31 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
2738
2738
  anotherRange,
2739
2739
  maxLength
2740
2740
  );
2741
- if (trimmedRange) {
2742
- const nonCircularDeletionRanges = splitRangeIntoTwoPartsIfItIsCircular(
2743
- anotherRange,
2744
- maxLength
2745
- );
2746
- nonCircularDeletionRanges.forEach(function(nonCircularDeletionRange) {
2747
- const deletionLength = nonCircularDeletionRange.end - nonCircularDeletionRange.start + 1;
2748
- if (trimmedRange.start > trimmedRange.end) {
2749
- if (nonCircularDeletionRange.start < trimmedRange.end) {
2750
- trimmedRange.start -= deletionLength;
2751
- trimmedRange.end -= deletionLength;
2752
- } else if (nonCircularDeletionRange.start < trimmedRange.start) {
2753
- trimmedRange.start -= deletionLength;
2754
- } else ;
2755
- } else {
2756
- if (nonCircularDeletionRange.start < trimmedRange.start) {
2757
- trimmedRange.start -= deletionLength;
2758
- trimmedRange.end -= deletionLength;
2759
- } else if (nonCircularDeletionRange.start < trimmedRange.end) {
2760
- trimmedRange.end -= deletionLength;
2761
- } else ;
2762
- }
2763
- });
2741
+ if (!trimmedRange) {
2742
+ return null;
2764
2743
  }
2744
+ const nonCircularDeletionRanges = splitRangeIntoTwoPartsIfItIsCircular(
2745
+ anotherRange,
2746
+ maxLength
2747
+ );
2748
+ nonCircularDeletionRanges.forEach(function(nonCircularDeletionRange) {
2749
+ const deletionLength = nonCircularDeletionRange.end - nonCircularDeletionRange.start + 1;
2750
+ if (trimmedRange.start > trimmedRange.end) {
2751
+ if (nonCircularDeletionRange.start < trimmedRange.end) {
2752
+ trimmedRange.start -= deletionLength;
2753
+ trimmedRange.end -= deletionLength;
2754
+ } else if (nonCircularDeletionRange.start < trimmedRange.start) {
2755
+ trimmedRange.start -= deletionLength;
2756
+ } else ;
2757
+ } else {
2758
+ if (nonCircularDeletionRange.start < trimmedRange.start) {
2759
+ trimmedRange.start -= deletionLength;
2760
+ trimmedRange.end -= deletionLength;
2761
+ } else if (nonCircularDeletionRange.start < trimmedRange.end) {
2762
+ trimmedRange.end -= deletionLength;
2763
+ } else ;
2764
+ }
2765
+ });
2765
2766
  return trimmedRange;
2766
2767
  }
2767
2768
  __name(adjustRangeToDeletionOfAnotherRange, "adjustRangeToDeletionOfAnotherRange");
@@ -3668,307 +3669,15 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
3668
3669
  const getFeatureTypes = /* @__PURE__ */ __name(({ includeHidden } = {}) => filter(getMergedFeatureMap(), (f) => includeHidden ? true : !f.isHidden).map(
3669
3670
  (f) => f.name
3670
3671
  ), "getFeatureTypes");
3671
- function getDefaultExportFromCjs(x) {
3672
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
3673
- }
3674
- __name(getDefaultExportFromCjs, "getDefaultExportFromCjs");
3675
- var lib = { exports: {} };
3676
- var randomFromSeed;
3677
- var hasRequiredRandomFromSeed;
3678
- function requireRandomFromSeed() {
3679
- if (hasRequiredRandomFromSeed) return randomFromSeed;
3680
- hasRequiredRandomFromSeed = 1;
3681
- var seed = 1;
3682
- function getNextValue() {
3683
- seed = (seed * 9301 + 49297) % 233280;
3684
- return seed / 233280;
3685
- }
3686
- __name(getNextValue, "getNextValue");
3687
- function setSeed(_seed_) {
3688
- seed = _seed_;
3689
- }
3690
- __name(setSeed, "setSeed");
3691
- randomFromSeed = {
3692
- nextValue: getNextValue,
3693
- seed: setSeed
3694
- };
3695
- return randomFromSeed;
3696
- }
3697
- __name(requireRandomFromSeed, "requireRandomFromSeed");
3698
- var alphabet_1;
3699
- var hasRequiredAlphabet;
3700
- function requireAlphabet() {
3701
- if (hasRequiredAlphabet) return alphabet_1;
3702
- hasRequiredAlphabet = 1;
3703
- var randomFromSeed2 = requireRandomFromSeed();
3704
- var ORIGINAL = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-";
3705
- var alphabet;
3706
- var previousSeed;
3707
- var shuffled;
3708
- function reset() {
3709
- shuffled = false;
3710
- }
3711
- __name(reset, "reset");
3712
- function setCharacters(_alphabet_) {
3713
- if (!_alphabet_) {
3714
- if (alphabet !== ORIGINAL) {
3715
- alphabet = ORIGINAL;
3716
- reset();
3717
- }
3718
- return;
3719
- }
3720
- if (_alphabet_ === alphabet) {
3721
- return;
3722
- }
3723
- if (_alphabet_.length !== ORIGINAL.length) {
3724
- throw new Error("Custom alphabet for shortid must be " + ORIGINAL.length + " unique characters. You submitted " + _alphabet_.length + " characters: " + _alphabet_);
3725
- }
3726
- var unique = _alphabet_.split("").filter(function(item, ind, arr) {
3727
- return ind !== arr.lastIndexOf(item);
3728
- });
3729
- if (unique.length) {
3730
- throw new Error("Custom alphabet for shortid must be " + ORIGINAL.length + " unique characters. These characters were not unique: " + unique.join(", "));
3731
- }
3732
- alphabet = _alphabet_;
3733
- reset();
3734
- }
3735
- __name(setCharacters, "setCharacters");
3736
- function characters(_alphabet_) {
3737
- setCharacters(_alphabet_);
3738
- return alphabet;
3739
- }
3740
- __name(characters, "characters");
3741
- function setSeed(seed) {
3742
- randomFromSeed2.seed(seed);
3743
- if (previousSeed !== seed) {
3744
- reset();
3745
- previousSeed = seed;
3746
- }
3747
- }
3748
- __name(setSeed, "setSeed");
3749
- function shuffle() {
3750
- if (!alphabet) {
3751
- setCharacters(ORIGINAL);
3752
- }
3753
- var sourceArray = alphabet.split("");
3754
- var targetArray = [];
3755
- var r = randomFromSeed2.nextValue();
3756
- var characterIndex;
3757
- while (sourceArray.length > 0) {
3758
- r = randomFromSeed2.nextValue();
3759
- characterIndex = Math.floor(r * sourceArray.length);
3760
- targetArray.push(sourceArray.splice(characterIndex, 1)[0]);
3761
- }
3762
- return targetArray.join("");
3763
- }
3764
- __name(shuffle, "shuffle");
3765
- function getShuffled() {
3766
- if (shuffled) {
3767
- return shuffled;
3768
- }
3769
- shuffled = shuffle();
3770
- return shuffled;
3771
- }
3772
- __name(getShuffled, "getShuffled");
3773
- function lookup(index) {
3774
- var alphabetShuffled = getShuffled();
3775
- return alphabetShuffled[index];
3776
- }
3777
- __name(lookup, "lookup");
3778
- function get2() {
3779
- return alphabet || ORIGINAL;
3780
- }
3781
- __name(get2, "get");
3782
- alphabet_1 = {
3783
- get: get2,
3784
- characters,
3785
- seed: setSeed,
3786
- lookup,
3787
- shuffled: getShuffled
3788
- };
3789
- return alphabet_1;
3790
- }
3791
- __name(requireAlphabet, "requireAlphabet");
3792
- var randomByteBrowser;
3793
- var hasRequiredRandomByteBrowser;
3794
- function requireRandomByteBrowser() {
3795
- if (hasRequiredRandomByteBrowser) return randomByteBrowser;
3796
- hasRequiredRandomByteBrowser = 1;
3797
- var crypto = typeof window === "object" && (window.crypto || window.msCrypto);
3798
- var randomByte;
3799
- if (!crypto || !crypto.getRandomValues) {
3800
- randomByte = /* @__PURE__ */ __name(function(size) {
3801
- var bytes = [];
3802
- for (var i = 0; i < size; i++) {
3803
- bytes.push(Math.floor(Math.random() * 256));
3804
- }
3805
- return bytes;
3806
- }, "randomByte");
3807
- } else {
3808
- randomByte = /* @__PURE__ */ __name(function(size) {
3809
- return crypto.getRandomValues(new Uint8Array(size));
3810
- }, "randomByte");
3811
- }
3812
- randomByteBrowser = randomByte;
3813
- return randomByteBrowser;
3814
- }
3815
- __name(requireRandomByteBrowser, "requireRandomByteBrowser");
3816
- var format_browser;
3817
- var hasRequiredFormat_browser;
3818
- function requireFormat_browser() {
3819
- if (hasRequiredFormat_browser) return format_browser;
3820
- hasRequiredFormat_browser = 1;
3821
- format_browser = /* @__PURE__ */ __name(function(random, alphabet, size) {
3822
- var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
3823
- var step = -~(1.6 * mask * size / alphabet.length);
3824
- var id = "";
3825
- while (true) {
3826
- var bytes = random(step);
3827
- var i = step;
3828
- while (i--) {
3829
- id += alphabet[bytes[i] & mask] || "";
3830
- if (id.length === +size) return id;
3831
- }
3832
- }
3833
- }, "format_browser");
3834
- return format_browser;
3835
- }
3836
- __name(requireFormat_browser, "requireFormat_browser");
3837
- var generate_1;
3838
- var hasRequiredGenerate;
3839
- function requireGenerate() {
3840
- if (hasRequiredGenerate) return generate_1;
3841
- hasRequiredGenerate = 1;
3842
- var alphabet = requireAlphabet();
3843
- var random = requireRandomByteBrowser();
3844
- var format = /* @__PURE__ */ requireFormat_browser();
3845
- function generate(number) {
3846
- var loopCounter = 0;
3847
- var done;
3848
- var str = "";
3849
- while (!done) {
3850
- str = str + format(random, alphabet.get(), 1);
3851
- done = number < Math.pow(16, loopCounter + 1);
3852
- loopCounter++;
3853
- }
3854
- return str;
3855
- }
3856
- __name(generate, "generate");
3857
- generate_1 = generate;
3858
- return generate_1;
3859
- }
3860
- __name(requireGenerate, "requireGenerate");
3861
- var build_1;
3862
- var hasRequiredBuild;
3863
- function requireBuild() {
3864
- if (hasRequiredBuild) return build_1;
3865
- hasRequiredBuild = 1;
3866
- var generate = requireGenerate();
3867
- requireAlphabet();
3868
- var REDUCE_TIME = 1567752802062;
3869
- var version = 7;
3870
- var counter;
3871
- var previousSeconds;
3872
- function build(clusterWorkerId) {
3873
- var str = "";
3874
- var seconds = Math.floor((Date.now() - REDUCE_TIME) * 1e-3);
3875
- if (seconds === previousSeconds) {
3876
- counter++;
3877
- } else {
3878
- counter = 0;
3879
- previousSeconds = seconds;
3880
- }
3881
- str = str + generate(version);
3882
- str = str + generate(clusterWorkerId);
3883
- if (counter > 0) {
3884
- str = str + generate(counter);
3885
- }
3886
- str = str + generate(seconds);
3887
- return str;
3888
- }
3889
- __name(build, "build");
3890
- build_1 = build;
3891
- return build_1;
3892
- }
3893
- __name(requireBuild, "requireBuild");
3894
- var isValid;
3895
- var hasRequiredIsValid;
3896
- function requireIsValid() {
3897
- if (hasRequiredIsValid) return isValid;
3898
- hasRequiredIsValid = 1;
3899
- var alphabet = requireAlphabet();
3900
- function isShortId(id) {
3901
- if (!id || typeof id !== "string" || id.length < 6) {
3902
- return false;
3903
- }
3904
- var nonAlphabetic = new RegExp("[^" + alphabet.get().replace(/[|\\{}()[\]^$+*?.-]/g, "\\$&") + "]");
3905
- return !nonAlphabetic.test(id);
3906
- }
3907
- __name(isShortId, "isShortId");
3908
- isValid = isShortId;
3909
- return isValid;
3910
- }
3911
- __name(requireIsValid, "requireIsValid");
3912
- var clusterWorkerIdBrowser;
3913
- var hasRequiredClusterWorkerIdBrowser;
3914
- function requireClusterWorkerIdBrowser() {
3915
- if (hasRequiredClusterWorkerIdBrowser) return clusterWorkerIdBrowser;
3916
- hasRequiredClusterWorkerIdBrowser = 1;
3917
- clusterWorkerIdBrowser = 0;
3918
- return clusterWorkerIdBrowser;
3919
- }
3920
- __name(requireClusterWorkerIdBrowser, "requireClusterWorkerIdBrowser");
3921
- var hasRequiredLib;
3922
- function requireLib() {
3923
- if (hasRequiredLib) return lib.exports;
3924
- hasRequiredLib = 1;
3925
- (function(module2) {
3926
- var alphabet = requireAlphabet();
3927
- var build = requireBuild();
3928
- var isValid2 = requireIsValid();
3929
- var clusterWorkerId = requireClusterWorkerIdBrowser() || 0;
3930
- function seed(seedValue) {
3931
- alphabet.seed(seedValue);
3932
- return module2.exports;
3933
- }
3934
- __name(seed, "seed");
3935
- function worker(workerId) {
3936
- clusterWorkerId = workerId;
3937
- return module2.exports;
3938
- }
3939
- __name(worker, "worker");
3940
- function characters(newCharacters) {
3941
- if (newCharacters !== void 0) {
3942
- alphabet.characters(newCharacters);
3943
- }
3944
- return alphabet.shuffled();
3945
- }
3946
- __name(characters, "characters");
3947
- function generate() {
3948
- return build(clusterWorkerId);
3949
- }
3950
- __name(generate, "generate");
3951
- module2.exports = generate;
3952
- module2.exports.generate = generate;
3953
- module2.exports.seed = seed;
3954
- module2.exports.worker = worker;
3955
- module2.exports.characters = characters;
3956
- module2.exports.isValid = isValid2;
3957
- })(lib);
3958
- return lib.exports;
3959
- }
3960
- __name(requireLib, "requireLib");
3961
- var shortid$1;
3962
- var hasRequiredShortid;
3963
- function requireShortid() {
3964
- if (hasRequiredShortid) return shortid$1;
3965
- hasRequiredShortid = 1;
3966
- shortid$1 = requireLib();
3967
- return shortid$1;
3968
- }
3969
- __name(requireShortid, "requireShortid");
3970
- var shortidExports = requireShortid();
3971
- const shortid = /* @__PURE__ */ getDefaultExportFromCjs(shortidExports);
3672
+ const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
3673
+ let nanoid = /* @__PURE__ */ __name((size = 21) => {
3674
+ let id = "";
3675
+ let bytes = crypto.getRandomValues(new Uint8Array(size |= 0));
3676
+ while (size--) {
3677
+ id += urlAlphabet[bytes[size] & 63];
3678
+ }
3679
+ return id;
3680
+ }, "nanoid");
3972
3681
  function cutSequenceByRestrictionEnzyme(pSequence, circular, restrictionEnzyme) {
3973
3682
  if (restrictionEnzyme.forwardRegex.length === 0 || restrictionEnzyme.reverseRegex.length === 0) {
3974
3683
  const returnArray = [];
@@ -4176,7 +3885,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
4176
3885
  }
4177
3886
  const overhangBps = getSequenceWithinRange(cutRange, originalSequence);
4178
3887
  restrictionCutSite = {
4179
- id: shortid(),
3888
+ id: nanoid(),
4180
3889
  start,
4181
3890
  end,
4182
3891
  topSnipPosition,
@@ -4234,7 +3943,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
4234
3943
  });
4235
3944
  if (!circular && cutsites.length) {
4236
3945
  sortedCutsites.push({
4237
- id: "seqTerm_" + shortid(),
3946
+ id: "seqTerm_" + nanoid(),
4238
3947
  start: 0,
4239
3948
  end: 0,
4240
3949
  overhangBps: "",
@@ -4368,6 +4077,10 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
4368
4077
  });
4369
4078
  }
4370
4079
  __name(getDigestFragsForSeqAndEnzymes, "getDigestFragsForSeqAndEnzymes");
4080
+ function getDefaultExportFromCjs(x) {
4081
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
4082
+ }
4083
+ __name(getDefaultExportFromCjs, "getDefaultExportFromCjs");
4371
4084
  var jsondiffpatch_umd$1 = { exports: {} };
4372
4085
  var empty = {};
4373
4086
  var hasRequiredEmpty;
@@ -8966,10 +8679,10 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
8966
8679
  annotation.name = "Untitled annotation";
8967
8680
  }
8968
8681
  if (provideNewIdsForAnnotations) {
8969
- annotation.id = shortid();
8682
+ annotation.id = nanoid();
8970
8683
  }
8971
8684
  if (!annotation.id && annotation.id !== 0 && !doNotProvideIdsForAnnotations) {
8972
- annotation.id = shortid();
8685
+ annotation.id = nanoid();
8973
8686
  messages.push(
8974
8687
  "Unable to detect valid ID for annotation, setting ID to " + annotation.id
8975
8688
  );
@@ -9213,7 +8926,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
9213
8926
  if (item.id || item.id === 0) {
9214
8927
  itemId = item.id;
9215
8928
  } else {
9216
- itemId = shortid();
8929
+ itemId = nanoid();
9217
8930
  if (!doNotProvideIdsForAnnotations) {
9218
8931
  item.id = itemId;
9219
8932
  }
@@ -9516,6 +9229,36 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
9516
9229
  return refSeqWithGaps.join("");
9517
9230
  }
9518
9231
  __name(insertGapsIntoRefSeq, "insertGapsIntoRefSeq");
9232
+ function findApproxMatches(searchSeq, targetSeq, maxMismatches, circular = false) {
9233
+ const matches = [];
9234
+ const lenA = searchSeq.length;
9235
+ const lenB = targetSeq.length;
9236
+ const targetSeqExtended = circular ? targetSeq + targetSeq.slice(0, lenA - 1) : targetSeq;
9237
+ const limit = circular ? lenB : lenB - lenA + 1;
9238
+ for (let i = 0; i < limit; i++) {
9239
+ const window2 = targetSeqExtended.slice(i, i + lenA);
9240
+ let mismatchCount = 0;
9241
+ const mismatchPositions = [];
9242
+ for (let j = 0; j < lenA; j++) {
9243
+ if (searchSeq[j] !== window2[j]) {
9244
+ mismatchPositions.push(j);
9245
+ mismatchCount++;
9246
+ if (mismatchCount > maxMismatches) break;
9247
+ }
9248
+ }
9249
+ if (mismatchCount <= maxMismatches) {
9250
+ matches.push({
9251
+ index: i,
9252
+ match: window2,
9253
+ mismatchPositions,
9254
+ numMismatches: mismatchPositions.length
9255
+ // Keep for backwards compatibility
9256
+ });
9257
+ }
9258
+ }
9259
+ return matches;
9260
+ }
9261
+ __name(findApproxMatches, "findApproxMatches");
9519
9262
  var spliceString$1;
9520
9263
  var hasRequiredSpliceString;
9521
9264
  function requireSpliceString() {
@@ -17753,7 +17496,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
17753
17496
  return __spreadProps(__spreadValues({}, range), {
17754
17497
  name: getRandomInt(0, 1e5).toString(),
17755
17498
  type: "misc_feature",
17756
- id: shortid(),
17499
+ id: nanoid(),
17757
17500
  forward: Math.random() > 0.5,
17758
17501
  notes: {}
17759
17502
  });
@@ -17876,7 +17619,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
17876
17619
  forward,
17877
17620
  annotationTypePlural: "orfs",
17878
17621
  isOrf: true,
17879
- id: shortid()
17622
+ id: nanoid()
17880
17623
  });
17881
17624
  }
17882
17625
  }
@@ -19324,6 +19067,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
19324
19067
  exports2.featureColors = featureColors;
19325
19068
  exports2.filterRnaString = filterRnaString;
19326
19069
  exports2.filterSequenceString = filterSequenceString;
19070
+ exports2.findApproxMatches = findApproxMatches;
19327
19071
  exports2.findNearestRangeOfSequenceOverlapToPosition = findNearestRangeOfSequenceOverlapToPosition;
19328
19072
  exports2.findOrfsInPlasmid = findOrfsInPlasmid;
19329
19073
  exports2.findSequenceMatches = findSequenceMatches;
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@teselagen/sequence-utils",
3
- "version": "0.3.31",
3
+ "version": "0.3.32-beta.2",
4
4
  "type": "module",
5
5
  "dependencies": {
6
+ "@teselagen/range-utils": "0.3.14-beta.1",
6
7
  "escape-string-regexp": "5.0.0",
7
8
  "jsondiffpatch": "0.4.1",
8
- "string-splice": "^1.3.0",
9
9
  "lodash-es": "^4.17.21",
10
- "shortid": "2.2.16",
11
- "@teselagen/range-utils": "0.3.13"
10
+ "nanoid": "^5.1.5",
11
+ "string-splice": "^1.3.0"
12
12
  },
13
13
  "exports": {
14
14
  ".": {
@@ -16,5 +16,9 @@
16
16
  "require": "./index.cjs"
17
17
  }
18
18
  },
19
+ "volta": {
20
+ "node": "18.18.0",
21
+ "yarn": "1.22.22"
22
+ },
19
23
  "license": "MIT"
20
24
  }
@@ -1,4 +1,4 @@
1
- import shortid from "shortid";
1
+ import { nanoid } from "nanoid";
2
2
  import { flatMap, cloneDeep } from "lodash-es";
3
3
  import {
4
4
  normalizePositionByRangeLength,
@@ -27,7 +27,7 @@ function computeDigestFragments({
27
27
  });
28
28
  if (!circular && cutsites.length) {
29
29
  sortedCutsites.push({
30
- id: "seqTerm_" + shortid(),
30
+ id: "seqTerm_" + nanoid(),
31
31
  start: 0,
32
32
  end: 0,
33
33
  overhangBps: "",
@@ -1,5 +1,5 @@
1
1
  import { assign } from "lodash-es";
2
- import shortid from "shortid";
2
+ import { nanoid } from "nanoid";
3
3
  import getReverseComplementSequenceString from "./getReverseComplementSequenceString";
4
4
 
5
5
  import {
@@ -268,7 +268,7 @@ function cutSequence(
268
268
  const overhangBps = getSequenceWithinRange(cutRange, originalSequence);
269
269
 
270
270
  restrictionCutSite = {
271
- id: shortid(),
271
+ id: nanoid(),
272
272
  start,
273
273
  end,
274
274
  topSnipPosition,
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Find approximate matches of a search sequence within a target sequence
3
+ *
4
+ * @param {string} searchSeq - The sequence to search for
5
+ * @param {string} targetSeq - The sequence to search within
6
+ * @param {number} maxMismatches - Maximum number of mismatches allowed
7
+ * @param {boolean} circular - Whether to treat the target sequence as circular (default: false)
8
+ * @returns {Array} - Array of objects containing { index, match, mismatchPositions }
9
+ */
10
+ export default function findApproxMatches(
11
+ searchSeq,
12
+ targetSeq,
13
+ maxMismatches,
14
+ circular = false
15
+ ) {
16
+ const matches = [];
17
+ const lenA = searchSeq.length;
18
+ const lenB = targetSeq.length;
19
+
20
+ // Extend targetSeq to simulate circularity, in case circular = true
21
+ const targetSeqExtended = circular
22
+ ? targetSeq + targetSeq.slice(0, lenA - 1)
23
+ : targetSeq;
24
+ const limit = circular ? lenB : lenB - lenA + 1;
25
+
26
+ for (let i = 0; i < limit; i++) {
27
+ const window = targetSeqExtended.slice(i, i + lenA);
28
+ let mismatchCount = 0;
29
+ const mismatchPositions = [];
30
+
31
+ for (let j = 0; j < lenA; j++) {
32
+ if (searchSeq[j] !== window[j]) {
33
+ mismatchPositions.push(j);
34
+ mismatchCount++;
35
+ if (mismatchCount > maxMismatches) break;
36
+ }
37
+ }
38
+
39
+ if (mismatchCount <= maxMismatches) {
40
+ matches.push({
41
+ index: i,
42
+ match: window,
43
+ mismatchPositions,
44
+ numMismatches: mismatchPositions.length // Keep for backwards compatibility
45
+ });
46
+ }
47
+ }
48
+
49
+ return matches;
50
+ }