From 707d8d86f3bf3db2e25dd0855f87223510379665 Mon Sep 17 00:00:00 2001 From: Subhajit Sahu Date: Sat, 15 Apr 2023 11:28:39 +0530 Subject: [PATCH] :bug: feat: `i*method()`s return iterables and more (closes #12) --- README.md | 206 ++++--- TODO | 99 ++-- package-lock.json | 252 ++++---- package.json | 8 +- src/index.ts | 1333 +++++++++++++++++++++++++++++++++---------- tests/index.test.ts | 40 +- wiki | 2 +- 7 files changed, 1394 insertions(+), 546 deletions(-) diff --git a/README.md b/README.md index ede1b0929..18dec6097 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -An [Array] is a collection of values, stored contiguously.
+An [array] is a collection of values, stored contiguously.
📦 [Node.js](https://www.npmjs.com/package/extra-array), 🌐 [Web](https://www.npmjs.com/package/extra-array.web), 📜 [Files](https://unpkg.com/extra-array/), @@ -27,7 +27,7 @@ tag from the [jsDelivr CDN]. > Stability: [Experimental](https://www.youtube.com/watch?v=L1j93RnIxEo). -[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array +[array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array [jsDelivr CDN]: https://cdn.jsdelivr.net/npm/extra-array.web/index.js
@@ -49,7 +49,7 @@ var x = [1, 2, 3, 4]; xarray.rotate(x, 1); // → [4, 1, 2, 3] -[...xarray.permutations([1, 2, 3])]; +xarray.permutations([1, 2, 3]); // → [ // [], [ 1 ], // [ 2 ], [ 3 ], @@ -70,54 +70,66 @@ xarray.rotate(x, 1); | Property | Description | | ---- | ---- | -| [is] | Check if value is an array. | -| [keys] | List all indices. | -| [values] | List all values. | -| [entries] | List all index-value pairs. | -| | | -| [from] | Convert an iterable to array. | -| [from$] | Convert an iterable to array. | +| [fromIterable] | Convert an iterable to array. | +| [fromIterable$] | Convert an iterable to array! | | [fromRange] | Generate array from given number range. | | [fromInvocation] | Generate array from repeated function invocation. | | [fromApplication] | Generate array from repeated function application. | | | | -| [compare] | Compare two arrays (lexicographically). | -| [isEqual] | Check if two arrays are equal. | +| [shallowClone] | Shallow clone an array. | +| [deepClone] | Deep clone an array. | +| | | +| [is] | Check if value is an array. | +| [isSorted] | Examine if array is sorted. | +| [keys] | Obtain all indices. | +| [values] | Get all values. | +| [values$] | Get all values! | +| [entries] | Obtain all index-value pairs. | +| [ikeys] | List all indices. | +| [ivalues] | List all values. | +| [ientries] | List all index-value pairs. | | | | -| [index] | Get zero-based index for element in array. | -| [indexRange] | Get index range for part of array. | +| [index] | Get zero-based index for an element in array. | +| [indexRange] | Get zero-based index range for part of array. | | [length] | Find the length of an array. | | [isEmpty] | Check if an array is empty. | +| [resize$] | Resize an array to given length! | +| [clear$] | Remove all elements from an array! | | | | | [get] | Get value at index. | | [getAll] | Get values at indices. | | [getPath] | Get value at path in a nested array. | | [hasPath] | Check if nested array has a path. | | [set] | Set value at index. | -| [set$] | Set value at index. | -| [setPath$] | Set value at path in a nested array. | +| [set$] | Set value at index! | +| [setPath$] | Set value at path in a nested array! | | [swap] | Exchange two values. | -| [swap$] | Exchange two values. | +| [swap$] | Exchange two values! | +| [swapRanges] | Exchange two ranges of values. | +| [swapRanges$] | Exchange two ranges of values! | | [remove] | Remove value at index. | -| [remove$] | Remove value at index. | -| [removePath$] | Remove value at path in a nested array. | +| [remove$] | Remove value at index! | +| [removePath$] | Remove value at path in a nested array! | | | | | [count] | Count values which satisfy a test. | -| [countAs] | Count occurrences of values. | -| [min] | Find smallest value. | -| [minEntry] | Find smallest entry. | -| [max] | Find largest value. | -| [maxEntry] | Find largest entry. | +| [countEach] | Count occurrences of each distinct value. | +| [min] | Find first smallest value. | +| [minEntry] | Find first smallest entry. | +| [max] | Find first largest value. | +| [maxEntry] | Find first largest entry. | | [range] | Find smallest and largest values. | | [rangeEntries] | Find smallest and largest entries. | | | | -| [slice] | Get part of an array. | -| [slice$] | Get part of an array. | +| [compare] | Compare two arrays (lexicographically). | +| [isEqual] | Examine if two arrays are equal. | +| | | | [head] | Get first value. | | [last] | Get last value. | +| [middle] | Get values from middle. | | [tail] | Get values except first. | | [init] | Get values except last. | -| [middle] | Get values from middle. | +| [slice] | Get part of an array. | +| [slice$] | Get part of an array! | | [take] | Keep first n values only. | | [takeRight] | Keep last n values only. | | [takeWhile] | Keep values from left, while a test passes. | @@ -127,18 +139,23 @@ xarray.rotate(x, 1); | [dropWhile] | Discard values from left, while a test passes. | | [dropWhileRight] | Discard values from right, while a test passes. | | | | -| [prefixes] | List all possible prefixes. | -| [suffixes] | List all possible suffixes. | -| [infixes] | List all possible infixes. | -| [subsequences] | List all possible subsequences. | -| [permutations] | List all possible permutations. | +| [prefixes] | Obtain all possible prefixes. | +| [suffixes] | Obtain all possible suffixes. | +| [infixes] | Obtain all possible infixes. | +| [subsequences] | Obtain all possible subsequences. | +| [permutations] | Obtain all possible permutations. | +| [iprefixes] | List all possible prefixes. | +| [isuffixes] | List all possible suffixes. | +| [iinfixes] | List all possible infixes. | +| [isubsequences] | List all possible subsequences. | +| [ipermutations] | List all possible permutations. | | [randomValue] | Pick an arbitrary value. | | [randomPrefix] | Pick an arbitrary prefix. | | [randomSuffix] | Pick an arbitrary suffix. | | [randomInfix] | Pick an arbitrary infix. | | [randomSubsequence] | Pick an arbitrary subsequence. | | [randomPermutation] | Pick an arbitrary permutation. | -| [randomPermutation$] | Pick an arbitrary permutation. | +| [randomPermutation$] | Pick an arbitrary permutation! | | | | | [includes] | Check if array has a value. | | [indexOf] | Find first index of a value. | @@ -155,29 +172,41 @@ xarray.rotate(x, 1); | [searchValue] | Find first index of a value. | | [searchValueRight] | Find last index of a value. | | [searchValueAll] | Find indices of value. | +| [searchMinimumValue] | Find first index of minimum value. | +| [searchMaximumValue] | Find first index of maximum value. | +| [searchUnsortedValue] | Find first index of an unsorted value. | +| [searchAdjacentDuplicateValue] | Find first index of an adjacent duplicate value. | +| [searchMismatchedValue] | Find first index where two arrays differ. | | [searchInfix] | Find first index of an infix. | | [searchInfixRight] | Find last index of an infix. | | [searchInfixAll] | Find indices of an infix. | | [searchSubsequence] | Find first index of a subsequence. | -| [hasValue] | Check if array has a value. | -| [hasPrefix] | Check if array starts with a prefix. | -| [hasSuffix] | Check if array ends with a suffix. | -| [hasInfix] | Check if array contains an infix. | -| [hasSubsequence] | Check if array has a subsequence. | -| [hasPermutation] | Check if array has a permutation. | +| [hasValue] | Examine if array has a value. | +| [hasUnsortedValue] | Examine if array has an unsorted value. | +| [hasPrefix] | Examine if array starts with a prefix. | +| [hasSuffix] | Examine if array ends with a suffix. | +| [hasInfix] | Examine if array contains an infix. | +| [hasSubsequence] | Examine if array has a subsequence. | +| [hasPermutation] | Examine if array has a permutation. | | | | | [forEach] | Call a function for each value. | -| [some] | Check if any value satisfies a test. | -| [every] | Check if all values satisfy a test. | +| [some] | Examine if any value satisfies a test. | +| [every] | Examine if all values satisfy a test. | | [map] | Transform values of an array. | -| [map$] | Transform values of an array. | +| [map$] | Transform values of an array! | | [reduce] | Reduce values of array to a single value. | | [reduceRight] | Reduce values from right, to a single value. | +| [exclusiveScan] | Perform exclusive prefix scan from left to right. | +| [exclusiveScan$] | Perform exclusive prefix scan from left to right! | +| [inclusiveScan] | Perform inclusive prefix scan from left to right. | +| [inclusiveScan$] | Perform inclusive prefix scan from left to right! | +| [adjacentCombine] | Combine adjacent values of an array. | +| [adjacentCombine$] | Combine adjacent values of an array! | | [filter] | Keep values which pass a test. | -| [filter$] | Keep values which pass a test. | +| [filter$] | Keep values which pass a test! | | [filterAt] | Keep values at given indices. | | [reject] | Discard values which pass a test. | -| [reject$] | Discard values which pass a test. | +| [reject$] | Discard values which pass a test! | | [rejectAt] | Discard values at given indices. | | [accumulate] | Produce accumulating values. | | [flat] | Flatten nested array to given depth. | @@ -185,25 +214,25 @@ xarray.rotate(x, 1); | [zip] | Combine values from arrays. | | | | | [fill] | Fill with given value. | -| [fill$] | Fill with given value. | +| [fill$] | Fill with given value! | | [sort] | Arrange values in order. | -| [sort$] | Arrange values in order. | +| [sort$] | Arrange values in order! | | [push] | Add value to the end. | -| [push$] | Add values to the end. | +| [push$] | Add values to the end! | | [pop] | Remove last value. | -| [pop$] | Remove last value. | +| [pop$] | Remove last value! | | [shift] | Remove first value. | -| [shift$] | Remove first value. | +| [shift$] | Remove first value! | | [unshift] | Add values to the start. | -| [unshift$] | Add values to the start. | +| [unshift$] | Add values to the start! | | [copy] | Copy part of array to another. | -| [copy$] | Copy part of array to another. | +| [copy$] | Copy part of array to another! | | [copyWithin] | Copy part of array within. | -| [copyWithin$] | Copy part of array within. | +| [copyWithin$] | Copy part of array within! | | [moveWithin] | Move part of array within. | -| [moveWithin$] | Move part of array within. | +| [moveWithin$] | Move part of array within! | | [splice] | Remove or replace existing values. | -| [splice$] | Remove or replace existing values. | +| [splice$] | Remove or replace existing values! | | [split] | Break array considering test as separator. | | [splitAt] | Break array considering indices as separator. | | [cut] | Break array when test passes. | @@ -211,33 +240,44 @@ xarray.rotate(x, 1); | [cutAt] | Break array at given indices. | | [cutAtRight] | Break array after given indices. | | [group] | Keep similar values together and in order. | -| [partition] | Segregate values by test result. | -| [partitionAs] | Segregate values by similarity. | +| [segregate] | Segregate values by test result. | +| [segregateEach] | Segregate each distinct value. | | [chunk] | Break array into chunks of given size. | | [cycle] | Obtain values that cycle through array. | | [repeat] | Repeat an array given times. | | [reverse] | Reverse the values. | -| [reverse$] | Reverse the values. | +| [reverse$] | Reverse the values! | | [rotate] | Rotate values in array. | -| [rotate$] | Rotate values in array. | +| [rotate$] | Rotate values in array! | | [intersperse] | Place a separator between every value. | | [interpolate] | Estimate new values between existing ones. | | [intermix] | Place values of an array between another. | | [interleave] | Place values from iterables alternately. | | | | | [concat] | Append values from arrays. | -| [concat$] | Append values from arrays. | +| [concat$] | Append values from arrays! | | [join] | Join values together into a string. | | | | -| [isUnique] | Check if there are no duplicate values. | -| [isDisjoint] | Check if arrays have no value in common. | +| [isUnique] | Examine if there are no duplicate values. | +| [isDisjoint] | Examine if arrays have no value in common. | | [unique] | Remove duplicate values. | | [union] | Obtain values present in any array. | -| [union$] | Obtain values present in any array. | +| [union$] | Obtain values present in any array! | | [intersection] | Obtain values present in both arrays. | | [difference] | Obtain values not present in another array. | | [symmetricDifference] | Obtain values not present in both arrays. | -| [cartesianProduct] | List cartesian product of arrays. | +| [cartesianProduct] | Obtain cartesian product of arrays. | + +
+
+ + +## References + +- [Prefix sum array and difference array : Woburn C.I. PEGWiki](https://wcipeg.com/wiki/Prefix_sum_array_and_difference_array) +- [How to get the count of each distinct value in a column?](https://stackoverflow.com/q/7053902/1413259) +- [How to add region in java script file, visual studio](https://stackoverflow.com/a/51550649/1413259) +- [Operator precedence - JavaScript : MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence)

@@ -255,8 +295,8 @@ xarray.rotate(x, 1); [keys]: https://github.com/nodef/extra-array/wiki/keys [values]: https://github.com/nodef/extra-array/wiki/values [entries]: https://github.com/nodef/extra-array/wiki/entries -[from]: https://github.com/nodef/extra-array/wiki/from -[from$]: https://github.com/nodef/extra-array/wiki/from$ +[fromIterable]: https://github.com/nodef/extra-array/wiki/fromIterable +[fromIterable$]: https://github.com/nodef/extra-array/wiki/fromIterable$ [fromRange]: https://github.com/nodef/extra-array/wiki/fromRange [fromInvocation]: https://github.com/nodef/extra-array/wiki/fromInvocation [fromApplication]: https://github.com/nodef/extra-array/wiki/fromApplication @@ -279,7 +319,7 @@ xarray.rotate(x, 1); [remove$]: https://github.com/nodef/extra-array/wiki/remove$ [removePath$]: https://github.com/nodef/extra-array/wiki/removePath$ [count]: https://github.com/nodef/extra-array/wiki/count -[countAs]: https://github.com/nodef/extra-array/wiki/countAs +[countEach]: https://github.com/nodef/extra-array/wiki/countEach [min]: https://github.com/nodef/extra-array/wiki/min [minEntry]: https://github.com/nodef/extra-array/wiki/minEntry [max]: https://github.com/nodef/extra-array/wiki/max @@ -382,8 +422,8 @@ xarray.rotate(x, 1); [cutAt]: https://github.com/nodef/extra-array/wiki/cutAt [cutAtRight]: https://github.com/nodef/extra-array/wiki/cutAtRight [group]: https://github.com/nodef/extra-array/wiki/group -[partition]: https://github.com/nodef/extra-array/wiki/partition -[partitionAs]: https://github.com/nodef/extra-array/wiki/partitionAs +[segregate]: https://github.com/nodef/extra-array/wiki/segregate +[segregateEach]: https://github.com/nodef/extra-array/wiki/segregateEach [chunk]: https://github.com/nodef/extra-array/wiki/chunk [cycle]: https://github.com/nodef/extra-array/wiki/cycle [repeat]: https://github.com/nodef/extra-array/wiki/repeat @@ -407,3 +447,33 @@ xarray.rotate(x, 1); [difference]: https://github.com/nodef/extra-array/wiki/difference [symmetricDifference]: https://github.com/nodef/extra-array/wiki/symmetricDifference [cartesianProduct]: https://github.com/nodef/extra-array/wiki/cartesianProduct +[shallowClone]: https://github.com/nodef/extra-array/wiki/shallowClone +[deepClone]: https://github.com/nodef/extra-array/wiki/deepClone +[isSorted]: https://github.com/nodef/extra-array/wiki/isSorted +[ikeys]: https://github.com/nodef/extra-array/wiki/ikeys +[values$]: https://github.com/nodef/extra-array/wiki/values$ +[ivalues]: https://github.com/nodef/extra-array/wiki/ivalues +[ientries]: https://github.com/nodef/extra-array/wiki/ientries +[resize$]: https://github.com/nodef/extra-array/wiki/resize$ +[clear$]: https://github.com/nodef/extra-array/wiki/clear$ +[countEach]: https://github.com/nodef/extra-array/wiki/countEach +[iprefixes]: https://github.com/nodef/extra-array/wiki/iprefixes +[isuffixes]: https://github.com/nodef/extra-array/wiki/isuffixes +[iinfixes]: https://github.com/nodef/extra-array/wiki/iinfixes +[isubsequences]: https://github.com/nodef/extra-array/wiki/isubsequences +[ipermutations]: https://github.com/nodef/extra-array/wiki/ipermutations +[searchUnsortedValue]: https://github.com/nodef/extra-array/wiki/searchUnsortedValue +[hasUnsortedValue]: https://github.com/nodef/extra-array/wiki/hasUnsortedValue +[partitionEach]: https://github.com/nodef/extra-array/wiki/partitionEach +[swapRanges]: https://github.com/nodef/extra-array/wiki/swapRanges +[swapRanges$]: https://github.com/nodef/extra-array/wiki/swapRanges$ +[searchAdjacentDuplicateValue]: https://github.com/nodef/extra-array/wiki/searchAdjacentDuplicateValue +[searchMismatchedValue]: https://github.com/nodef/extra-array/wiki/searchMismatchedValue +[exclusiveScan]: https://github.com/nodef/extra-array/wiki/exclusiveScan +[exclusiveScan$]: https://github.com/nodef/extra-array/wiki/exclusiveScan$ +[inclusiveScan]: https://github.com/nodef/extra-array/wiki/inclusiveScan +[inclusiveScan$]: https://github.com/nodef/extra-array/wiki/inclusiveScan$ +[adjacentCombine]: https://github.com/nodef/extra-array/wiki/adjacentCombine +[searchMinimumValue]: https://github.com/nodef/extra-array/wiki/searchMinimumValue +[searchMaximumValue]: https://github.com/nodef/extra-array/wiki/searchMaximumValue +[adjacentCombine$]: https://github.com/nodef/extra-array/wiki/adjacentCombine$ diff --git a/TODO b/TODO index 42206197f..580d8dc22 100644 --- a/TODO +++ b/TODO @@ -1,12 +1,4 @@ -NAMING -- keys(), keyList() -- push(), push$(), pushTo$() - - -TESTS - - -SORTS, IN PAIR, ISSORTED +SORTS, IN PAIR Instead of mapFn, provide pair array to define sort mapping. selectionSort, insertionSort, bubbleSort, mergeSort, (are all stable?) @@ -32,41 +24,74 @@ PARSE, OTHERS INDEX -VE - moveWithin -knuth-shuffle-seeded -Description -The Fisher-Yates (aka Knuth) shuffle for Node.js, with seeding support - - +OLD SORT ALGORITHMS +/** + * Arrange values in order! + * @param x an array (updated!) + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j + */ +function bubbleSort$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; // TODO: use map function + var fs = fs || swap$; + var X = x.length; + for (var i=0; i 0) fs(x, i, j); + } + return x; +} -// HELPERS -// ------- -// Convert an array to set, using a map function. -function setFrom(x: T[], fm: MapFunction): Set { - var a = new Set(); - for (var i=0, I=x.length; i(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; // TODO: use map function + var fs = fs || swap$; + var X = x.length; + for (var i=0; i 0) l = j; + fs(x, i, l); + } + return x; } -// Find indices of an infix as a list. -function* iterableSearchInfixAll(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): IterableIterator { +// TODO: Check if this is correct! +/** + * Arrange values in order! + * @param x an array (updated!) + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j + */ +function insertionSortOld$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { var fc = fc || COMPARE; - var fm = fm || IDENTITY; - var y1 = [...map(y, fm)]; - var Y = y1.length; - if (Y===0) { yield* fromRange(0, x.length); return; } - var m = new Array(Y).fill(false); - var i = -1, J = 0; - for (var vx of x) { - var wx = fm(vx, ++i, x); - for (var j=J; j>0; --j) - m[j] = m[j-1] && fc(wx, y1[j])===0; - m[0] = fc(wx, y1[0])===0; - J = Math.min(J+1, Y-1); - if (m[Y-1]) yield i-Y+1; + var fm = fm || IDENTITY; // TODO: use map function + var fs = fs || swap$; // TODO: use swap function + var X = x.length; + for (var i=X-2; i>=0; --i) { + var xv = x[i], mv = x[i]; + for (var j=i+1; j=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", - "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", + "version": "7.21.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz", + "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-compilation-targets": "^7.21.4", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.4", + "@babel/generator": "^7.21.5", + "@babel/helper-compilation-targets": "^7.21.5", + "@babel/helper-module-transforms": "^7.21.5", + "@babel/helpers": "^7.21.5", + "@babel/parser": "^7.21.8", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.4", - "@babel/types": "^7.21.4", + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -103,12 +101,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", - "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz", + "integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==", "dev": true, "dependencies": { - "@babel/types": "^7.21.4", + "@babel/types": "^7.21.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -118,12 +116,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", - "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz", + "integrity": "sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.21.4", + "@babel/compat-data": "^7.21.5", "@babel/helper-validator-option": "^7.21.0", "browserslist": "^4.21.3", "lru-cache": "^5.1.1", @@ -146,9 +144,9 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz", + "integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -192,40 +190,40 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz", + "integrity": "sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-environment-visitor": "^7.21.5", + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-simple-access": "^7.21.5", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", + "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", + "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.21.5" }, "engines": { "node": ">=6.9.0" @@ -244,9 +242,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", + "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -271,14 +269,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.5.tgz", + "integrity": "sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==", "dev": true, "dependencies": { "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5" }, "engines": { "node": ">=6.9.0" @@ -370,9 +368,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "version": "7.21.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz", + "integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -573,19 +571,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz", + "integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", + "@babel/generator": "^7.21.5", + "@babel/helper-environment-visitor": "^7.21.5", "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", + "@babel/parser": "^7.21.5", + "@babel/types": "^7.21.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -594,12 +592,12 @@ } }, "node_modules/@babel/types": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", - "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz", + "integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-string-parser": "^7.21.5", "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, @@ -1071,9 +1069,9 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz", - "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==", + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.1.1.tgz", + "integrity": "sha512-/X7Gh/qWiWaooJmUnYD48SYy72fyrk2ceisOSe89JojK7r0j8YrTwYpDi76kI+c6QiqX1KSgdoBTMJvktsDkYw==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { @@ -1163,12 +1161,12 @@ } }, "node_modules/@octokit/types": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz", - "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.1.tgz", + "integrity": "sha512-Vx4keMiD/CAiwVFasLcH0xBSVbKIHebIZke9i7ZbUWGNN4vJFWSYH6Nvga7UY9NIJCGa6x3QG849XTbi5wYmkA==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^16.0.0" + "@octokit/openapi-types": "^17.1.1" } }, "node_modules/@rollup/plugin-commonjs": { @@ -1300,18 +1298,18 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.18.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.5.tgz", + "integrity": "sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q==", "dev": true, "dependencies": { "@babel/types": "^7.3.0" } }, "node_modules/@types/estree": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", - "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", "dev": true }, "node_modules/@types/graceful-fs": { @@ -1348,9 +1346,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "version": "29.5.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.1.tgz", + "integrity": "sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -1358,9 +1356,9 @@ } }, "node_modules/@types/node": { - "version": "18.15.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", - "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "version": "20.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.0.tgz", + "integrity": "sha512-O+z53uwx64xY7D6roOi4+jApDGFg0qn6WHcxe5QeqjMaTezBO/mxdfFXIVAVVyNWKx84OmPB3L8kbVYOTeN34A==", "dev": true }, "node_modules/@types/prettier": { @@ -1673,9 +1671,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001478", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz", - "integrity": "sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw==", + "version": "1.0.30001485", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001485.tgz", + "integrity": "sha512-8aUpZ7sjhlOyiNsg+pgcrTTPUXKh+rg544QYHSvQErljVEKJzvkYkCR/hUFeeVoEfTToUtY9cUKNRC7+c45YkA==", "dev": true, "funding": [ { @@ -1875,9 +1873,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.359", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.359.tgz", - "integrity": "sha512-OoVcngKCIuNXtZnsYoqlCvr0Cf3NIPzDIgwUfI9bdTFjXCrr79lI0kwQstLPZ7WhCezLlGksZk/BFAzoXC7GDw==", + "version": "1.4.385", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.385.tgz", + "integrity": "sha512-L9zlje9bIw0h+CwPQumiuVlfMcV4boxRjFIWDcLfFqTZNbkwOExBzfmswytHawObQX4OUhtNv8gIiB21kOurIg==", "dev": true }, "node_modules/emittery": { @@ -2014,12 +2012,6 @@ "integrity": "sha512-Njei3Q8nY+rwWpcUARkTAqqBmCzREHWWe1D5ysuuPKMD91yBss4TuR8+eawG7K9EO1BJRgtnVxne7S31dG04Fw==", "dev": true }, - "node_modules/extra-iterable": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/extra-iterable/-/extra-iterable-3.2.13.tgz", - "integrity": "sha512-yi9l6ZiMofA47MILQAg1kpow8uk7H9e3mVBB7JEDIiB4ZwLCPx8vBWClRt9E7OY8WHi1xBiHcpqrv0TshlbEgQ==", - "dev": true - }, "node_modules/extra-javascript-text": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/extra-javascript-text/-/extra-javascript-text-0.1.14.tgz", @@ -2044,12 +2036,6 @@ "integrity": "sha512-d2+ACy9mY0GcoNITnYM+bO+Omb509jiorUmXjZSt4oLDbnJlXDJElmGg8X7r2K58grTR8RPALct6FF7yMVG2XQ==", "dev": true }, - "node_modules/extra-set": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/extra-set/-/extra-set-3.1.6.tgz", - "integrity": "sha512-b61Z69/euJNCFTjp583dT8agtvrwdwS4nFyXphKozrby8wOX7Lt+8uvKA7SnVnms7GJ0hextzbU7uOcrRCzYlw==", - "dev": true - }, "node_modules/extra-typedoc-theme": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/extra-typedoc-theme/-/extra-typedoc-theme-0.1.10.tgz", @@ -3623,9 +3609,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz", - "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", "dev": true, "funding": [ { @@ -3701,9 +3687,9 @@ } }, "node_modules/rollup": { - "version": "3.20.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz", - "integrity": "sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==", + "version": "3.21.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.5.tgz", + "integrity": "sha512-a4NTKS4u9PusbUJcfF4IMxuqjFzjm6ifj76P54a7cKnvVzJaG12BLVR+hgU2YDGHzyMMQNxLAZWuALsn8q2oQg==", "dev": true, "peer": true, "bin": { @@ -3783,9 +3769,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", - "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3837,9 +3823,9 @@ } }, "node_modules/shiki": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.1.tgz", - "integrity": "sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.2.tgz", + "integrity": "sha512-ltSZlSLOuSY0M0Y75KA+ieRaZ0Trf5Wl3gutE7jzLuIcWxLp5i/uEnLoQWNvgKXQ5OMpGkJnVMRLAuzjc0LJ2A==", "dev": true, "dependencies": { "ansi-sequence-parser": "^1.1.0", @@ -4169,14 +4155,14 @@ } }, "node_modules/typedoc": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.1.tgz", - "integrity": "sha512-u4HwjZcSQhQSkkhLjgcs0ooAf6HrFVLDHHrwU2xZW8WxH0KnGZlNkaWxiOcK5Gagj7mxJSgwWx0dv8ACDAOXAQ==", + "version": "0.24.6", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.6.tgz", + "integrity": "sha512-c3y3h45xJv3qYwKDAwU6Cl+26CjT0ZvblHzfHJ+SjQDM4p1mZxtgHky4lhmG0+nNarRht8kADfZlbspJWdZarQ==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.2.12", - "minimatch": "^7.1.3", + "marked": "^4.3.0", + "minimatch": "^9.0.0", "shiki": "^0.14.1" }, "bin": { @@ -4190,15 +4176,15 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", + "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4225,9 +4211,9 @@ "dev": true }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { @@ -4237,6 +4223,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -4244,7 +4234,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -4374,9 +4364,9 @@ "dev": true }, "node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { "cliui": "^8.0.1", diff --git a/package.json b/package.json index efc0da25b..8e55effe0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "extra-array", - "version": "3.1.11", - "description": "An Array is a collection of values, stored contiguously.", + "version": "4.0.1", + "description": "An array is a collection of values, stored contiguously.", "main": "index.js", "module": "index.mjs", "sideEffects": false, @@ -32,12 +32,10 @@ "devDependencies": { "@rollup/plugin-commonjs": "^24.1.0", "@rollup/plugin-node-resolve": "^15.0.2", - "@types/jest": "^29.5.0", + "@types/jest": "^29.5.1", "extra-build": "^2.2.42", "extra-function": "^1.1.32", - "extra-iterable": "^3.2.13", "extra-math": "^1.3.55", - "extra-set": "^3.1.6", "jest": "^29.5.0", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-dts": "^5.3.0", diff --git a/src/index.ts b/src/index.ts index 05d00f5d3..2b12f593d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,23 +3,23 @@ import { IDENTITY, COMPARE, } from "extra-function"; -import { - MapFunction as IterableMapFunction, - searchInfixAll as iterableSearchInfixAll, -} from "extra-iterable"; -// TYPES -// ===== +// #region TYPES +// ============= -/** Entries is a list of key-value pairs, with unique keys (indices). */ -export type Entries = Iterable<[number, T]>; +/** Entries is an array of index-value pairs, with unique indices. */ +export type Entries = [number, T][]; +/** IEntries is a list of index-value pairs, with unique indices. */ +export type IEntries = Iterable<[number, T]>; -/** Lists is a pair of key list and value list, with unique keys (indices). */ -export type Lists = [Iterable, Iterable]; +/** Lists is a pair of index array and value array, with unique indices. */ +export type Lists = [number[], T[]]; +/** ILists is a pair of index iterable list and value iterable list, with unique indices. */ +export type ILists = [Iterable, Iterable]; /** @@ -95,92 +95,61 @@ export type ReduceFunction = (acc: U, v: T, i: number, x: T[]) => U; export type EndFunction = (dones: boolean[]) => boolean; +/** + * Handle swapping of two values in an array. + * @param x an array (updated!) + * @param i an index + * @param j another index + * @returns x | x[i] ⇔ x[j] + */ +type SwapFunction = (x: T[], i: number, j: number) => T[]; +// #endregion + + -// METHODS -// ======= +// #region METHODS +// =============== -// HELPERS -// ------- +// #region HELPERS +// --------------- /** Convert an iterable to set. */ -function toSet(x: Iterable, fm: IterableMapFunction | null=null): Set { +function toSet(x: T[], fm: MapFunction | null=null): Set { if (!fm) return new Set(x); var a = new Set(), i = -1; for (var v of x) a.add(fm(v, ++i, x)); return a; } +// #endregion -// ABOUT -// ----- - -/** - * Check if value is an array. - * @param v a value - * @returns v is an array? - */ -export function is(v: any): v is any[] { - return Array.isArray(v); -} - - -/** - * List all indices. - * @param x an array - * @returns 0, 1, ..., |x|-1 - */ -export function keys(x: T[]): IterableIterator { - return x.keys(); -} - - -/** - * List all values. - * @param x an array - * @returns v₀, v₁, ... | vᵢ = x[i] - */ -export function values(x: T[]): IterableIterator { - return x.values(); -} - - -/** - * List all index-value pairs. - * @param x an array - * @returns [0, v₀], [1, v₁], ... | vᵢ = x[i] - */ -export function entries(x: T[]): Entries { - return x.entries(); -} - - - - -// GENERATE -// -------- +// #region GENERATE +// ---------------- /** * Convert an iterable to array. * @param x an iterable * @returns x as array */ -export function from(x: Iterable): T[] { +export function fromIterable(x: Iterable): T[] { return [...x]; } +export {fromIterable as from}; /** - * Convert an iterable to array. + * Convert an iterable to array! * @param x an iterable (updatable if array!) * @returns x as array */ -export function from$(x: Iterable): T[] { +export function fromIterable$(x: Iterable): T[] { return Array.isArray(x)? x : [...x]; } +export {fromIterable$ as from$}; /** @@ -228,59 +197,143 @@ export function fromApplication(fm: MapFunction, v: T, n: number): T[] return a; } export {fromApplication as fromApply}; +// #endregion -// COMPARE -// ------- +// #region CLONE +// ------------- /** - * Compare two arrays (lexicographically). + * Shallow clone an array. * @param x an array - * @param y another array - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @returns xy: +ve + * @returns shallow clone of x */ -export function compare(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { - var fc = fc || COMPARE; - var fm = fm || IDENTITY; - var X = x.length; - var Y = y.length; - for (var i=0, I=Math.min(X, Y); i(x: T[]): T[] { + return x.slice(); } +export {shallowClone as clone}; /** - * Check if two arrays are equal. + * Deep clone an array. + * @param x an array + * @returns deep clone of x + */ +export function deepClone(x: T[]): T[] { + return structuredClone(x); +} +// #endregion + + + + +// #region ABOUT +// ------------- + +/** + * Check if value is an array. + * @param v a value + * @returns v is an array? + */ +export function is(v: any): v is any[] { + return Array.isArray(v); +} + + +/** + * Examine if array is sorted. * @param x an array - * @param y another array * @param fc compare function (a, b) * @param fm map function (v, i, x) - * @returns x = y? + * @returns x is sorted? */ -export function isEqual(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { - var X = x.length, Y = y.length; - return X===Y && compare(x, y, fc, fm)===0; +export function isSorted(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { + return searchUnsortedValue(x, fc, fm) === -1; } +/** + * Obtain all indices. + * @param x an array + * @returns [0, 1, ..., |x|-1] + */ +export function keys(x: T[]): number[] { + return [...x.keys()]; +} -// LENGTH -// ------ +/** + * List all indices. + * @param x an array + * @returns 0, 1, ..., |x|-1 + */ +export function ikeys(x: T[]): IterableIterator { + return x.keys(); +} + /** - * Get zero-based index for element in array. + * Get all values. * @param x an array - * @param i index (-ve: from right) + * @returns [v₀, v₁, ...] | vᵢ = x[i] + */ +export function values(x: T[]): T[] { + return x.slice(); +} + + +/** + * Get all values! + * @param x an array + * @returns x (reference!) + */ +export function values$(x: T[]): T[] { + return x; +} + + +/** + * List all values. + * @param x an array + * @returns v₀, v₁, ... | vᵢ = x[i] + */ +export function ivalues(x: T[]): IterableIterator { + return x.values(); +} + + +/** + * Obtain all index-value pairs. + * @param x an array + * @returns [[0, v₀], [1, v₁], ...] | vᵢ = x[i] + */ +export function entries(x: T[]): Entries { + return [...x.entries()]; +} + + +/** + * List all index-value pairs. + * @param x an array + * @returns [0, v₀], [1, v₁], ... | vᵢ = x[i] + */ +export function ientries(x: T[]): IEntries { + return x.entries(); +} +// #endregion + + + + +// #region LENGTH +// -------------- + +/** + * Get zero-based index for an element in array. + * @param x an array + * @param i ±index * @returns i' | x[i'] = x[i]; i' ∈ [0, |x|] */ export function index(x: T[], i: number): number { @@ -290,10 +343,10 @@ export function index(x: T[], i: number): number { /** - * Get index range for part of array. + * Get zero-based index range for part of array. * @param x an array - * @param i start index (-ve: from right) [0] - * @param I end index (-ve: from right) [|x|] + * @param i start ±index [0] + * @param I end ±index (exclusive) [|x|] * @returns [i', I'] | i' ≤ I'; i', I' ∈ [0, |x|] */ export function indexRange(x: T[], i: number=0, I: number=x.length): [number, number] { @@ -307,8 +360,8 @@ export function indexRange(x: T[], i: number=0, I: number=x.length): [number, /** * Find the length of an array. * @param x an array - * @param i start index [0] - * @param I end index [X] + * @param i start ±index [0] + * @param I end ±index (exclusive) [X] * @returns |x[i..I]| */ export function length(x: T[], i: number=0, I: number=x.length): number { @@ -328,10 +381,36 @@ export function isEmpty(x: T[]): boolean { } +/** + * Resize an array to given length! + * @param x an array + * @param n new length + * @param vd default value + * @returns resized x + */ +export function resize$(x: T[], n: number, vd: T) { + var X = x.length; x.length = n; + if (n>X) x.fill(vd, X); + return x; +} -// GET/SET -// ------- +/** + * Remove all elements from an array! + * @param x an array (updated!) + * @returns cleared x + */ +export function clear$(x: T[]) { + x.length = 0; + return x; +} +// #endregion + + + + +// #region GET/SET +// --------------- /** * Get value at index. @@ -342,6 +421,11 @@ export function isEmpty(x: T[]): boolean { export function get(x: T[], i: number): T { return x[index(x, i)]; } +export {get as at}; + + +// Get values at index range. +// export {slice as getRange}; /** @@ -393,11 +477,12 @@ export function hasPath(x: any[], p: number[]): boolean { export function set(x: T[], i: number, v: T): T[] { return set$(x.slice(), i, v); } +export {set as with}; /** - * Set value at index. - * @param x an array (updated) + * Set value at index! + * @param x an array (updated!) * @param i index * @param v value * @returns x | x[i] = v @@ -408,9 +493,13 @@ export function set$(x: T[], i: number, v: T): T[] { } +// TODO: setRange$() +// TODO: setAll$() + + /** - * Set value at path in a nested array. - * @param x a nested array (updated) + * Set value at path in a nested array! + * @param x a nested array (updated!) * @param p path * @param v value * @returns x | x[i₀][i₁][...] = v; [i₀, i₁, ...] = p @@ -435,11 +524,11 @@ export function swap(x: T[], i: number, j: number): T[] { /** - * Exchange two values. - * @param x an array (updated) + * Exchange two values! + * @param x an array (updated!) * @param i an index * @param j another index - * @returns x | x[i] ↔ x[j] + * @returns x | x[i] ⇔ x[j] */ export function swap$(x: T[], i: number, j: number): T[] { var i = index(x, i), j = index(x, j); @@ -448,6 +537,60 @@ export function swap$(x: T[], i: number, j: number): T[] { } +/** + * Exchange two values! + * @param x an array (updated!) + * @param i an +ve index + * @param j another +ve index + * @returns x | x[i] ⇔ x[j] + */ +function swapRaw$(x: T[], i: number, j: number): T[] { + var t = x[i]; x[i] = x[j]; x[j] = t; + return x; +} +// NOTE: May also be called swapUnchecked$(). + + +/** + * Exchange two ranges of values. + * @param x an array + * @param i start index of first range + * @param I end index of first range (exclusive) + * @param j start index of second range + * @param J end index of second range (exclusive) + * @returns x' | x' = x; x'[i..I] = x[j..J]; x'[j..J] = x[i..I] + */ +export function swapRanges(x: T[], i: number, I: number, j: number, J: number): T[] { + var [i, I] = indexRange(x, i, I); + var [j, J] = indexRange(x, j, J); + if (j(x: T[], i: number, I: number, j: number, J: number): T[] { + var [i, I] = indexRange(x, i, I); + var [j, J] = indexRange(x, j, J); + if (j(x: T[], i: number): T[] { /** - * Remove value at index. - * @param x an array (updated) + * Remove value at index! + * @param x an array (updated!) * @param i index * @returns x \\: [i] */ @@ -473,8 +616,8 @@ export function remove$(x: T[], i: number): T[] { /** - * Remove value at path in a nested array. - * @param x a nested array (updated) + * Remove value at path in a nested array! + * @param x a nested array (updated!) * @param p path * @returns x \\: [i₀][i₁][...] | [i₀, i₁, ...] = p */ @@ -483,12 +626,13 @@ export function removePath$(x: any[], p: number[]): any[] { if (is(y)) y.splice(last(p), 1); return x; } +// #endregion -// PROPERTY -// -------- +// #region PROPERTY +// ---------------- /** * Count values which satisfy a test. @@ -505,12 +649,12 @@ export function count(x: T[], ft: TestFunction): number { /** - * Count occurrences of values. + * Count occurrences of each distinct value. * @param x an array * @param fm map function (v, i, x) * @returns Map \{value ⇒ count\} */ -export function countAs(x: T[], fm: MapFunction | null=null): Map { +export function countEach(x: T[], fm: MapFunction | null=null): Map { var fm = fm || IDENTITY; var i = -1, a = new Map(); for (var v of x) { @@ -519,53 +663,58 @@ export function countAs(x: T[], fm: MapFunction | null=null): Ma } return a; } +export {countEach as countAs}; // DEPRECATED /** - * Find smallest value. + * Find first smallest value. * @param x an array * @param fc compare function (a, b) * @param fm map function (v, i, x) * @returns v | v ≤ vᵢ; vᵢ ∈ x */ export function min(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { - return rangeEntries(x, fc, fm)[0][1]; + var i = searchMinimumValue(x, fc, fm); + return x[i]; } /** - * Find smallest entry. + * Find first smallest entry. * @param x an array * @param fc compare function (a, b) * @param fm map function (v, i, x) * @returns [min_index, min_value] */ export function minEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { - return rangeEntries(x, fc, fm)[0]; + var i = searchMinimumValue(x, fc, fm); + return [i, x[i]]; } /** - * Find largest value. + * Find first largest value. * @param x an array * @param fc compare function (a, b) * @param fm map function (v, i, x) * @returns v | v ≥ vᵢ; vᵢ ∈ x */ export function max(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { - return rangeEntries(x, fc, fm)[1][1]; + var i = searchMaximumValue(x, fc, fm); + return x[i]; } /** - * Find largest entry. + * Find first largest entry. * @param x an array * @param fc compare function (a, b) * @param fm map function (v, i, x) * @returns [max_index, max_value] */ export function maxEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { - return rangeEntries(x, fc, fm)[1]; + var i = searchMaximumValue(x, fc, fm); + return [i, x[i]]; } @@ -604,38 +753,13 @@ export function rangeEntries(x: T[], fc: CompareFunction | null=nul } return [[mi, mv], [ni, nv]]; } +// #endregion -// PART -// ---- - -/** - * Get part of an array. - * @param x an array - * @param i start index [0] - * @param I end index [|x|] - * @returns x[i..I] - */ -export function slice(x: T[], i: number=0, I: number=x.length): T[] { - return x.slice(i, I); -} - - -/** - * Get part of an array. - * @param x an array (updated) - * @param i start index [0] - * @param I end index [|x|] - * @returns x = x[i..I] - */ -export function slice$(x: T[], i: number=0, I: number=x.length): T[] { - x.copyWithin(0, i, I); - x.length = length(x, i, I); - return x; -} - +// #region PART +// ------------ /** * Get first value. @@ -662,6 +786,18 @@ export function last(x: T[], vd?: T): T { export {last as back}; +/** + * Get values from middle. + * @param x an array + * @param i start index + * @param n number of values [1] + * @returns x[i..i+n] + */ +export function middle(x: T[], i: number, n: number=1): T[] { + return x.slice(i, i+n); +} + + /** * Get values except first. * @param x an array @@ -683,14 +819,28 @@ export function init(x: T[]): T[] { /** - * Get values from middle. + * Get part of an array. * @param x an array - * @param i start index - * @param n number of values [1] - * @returns x[i..i+n] + * @param i start index [0] + * @param I end index [|x|] + * @returns x[i..I] */ -export function middle(x: T[], i: number, n: number=1): T[] { - return x.slice(i, i+n); +export function slice(x: T[], i: number=0, I: number=x.length): T[] { + return x.slice(i, I); +} + + +/** + * Get part of an array! + * @param x an array (updated!) + * @param i start index [0] + * @param I end index [|x|] + * @returns x = x[i..I] + */ +export function slice$(x: T[], i: number=0, I: number=x.length): T[] { + x.copyWithin(0, i, I); + x.length = length(x, i, I); + return x; } @@ -782,12 +932,24 @@ export function dropWhile(x: T[], ft: TestFunction): T[] { export function dropWhileRight(x: T[], ft: TestFunction): T[] { return x.slice(0, scanWhileRight(x, ft)); } +// #endregion -// ARRANGEMENTS -// ------------ +// #region ARRANGEMENTS +// -------------------- + +/** + * Obtain all possible prefixes. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [[], x[..1], x[..2], ...] if n<0; [x[..n]] otherwise + */ +export function prefixes(x: T[], n: number=-1): T[][] { + return [...iprefixes(x, n)]; +} + /** * List all possible prefixes. @@ -795,7 +957,7 @@ export function dropWhileRight(x: T[], ft: TestFunction): T[] { * @param n number of values [-1 ⇒ any] * @returns [], x[..1], x[..2], ... if n<0; x[..n] otherwise */ -export function* prefixes(x: T[], n: number=-1): IterableIterator { +export function* iprefixes(x: T[], n: number=-1): IterableIterator { var X = x.length; if (n>X) return; if (n>=0) { yield x.slice(0, n); return; } @@ -804,13 +966,24 @@ export function* prefixes(x: T[], n: number=-1): IterableIterator { } +/** + * Obtain all possible suffixes. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [x[0..], x[1..], x[2..], ...] if n<0; [x[-n..]] otherwise + */ +export function suffixes(x: T[], n: number=-1): T[][] { + return [...isuffixes(x, n)]; +} + + /** * List all possible suffixes. * @param x an array * @param n number of values [-1 ⇒ any] * @returns x[0..], x[1..], x[2..], ... if n<0; x[-n..] otherwise */ -export function* suffixes(x: T[], n: number=-1): IterableIterator { +export function* isuffixes(x: T[], n: number=-1): IterableIterator { var X = x.length; if (n>X) return; if (n>=0) { yield x.slice(x.length - n); return; } @@ -819,13 +992,24 @@ export function* suffixes(x: T[], n: number=-1): IterableIterator { } +/** + * Obtain all possible infixes. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [[], x[0..1], x[0..2], ..., x[1..2], ...] if n<0; [only of length n] otherwise + */ +export function infixes(x: T[], n: number=-1): T[][] { + return [...iinfixes(x, n)]; +} + + /** * List all possible infixes. * @param x an array * @param n number of values [-1 ⇒ any] * @returns [], x[0..1], x[0..2], ..., x[1..2], ... if n<0; only of length n otherwise */ -export function infixes(x: T[], n: number=-1): IterableIterator { +export function iinfixes(x: T[], n: number=-1): IterableIterator { if (n>=0) return infixesFixed(x, n); else return infixesAll(x); } @@ -847,47 +1031,69 @@ function* infixesAll(x: T[]): IterableIterator { } +/** + * Obtain all possible subsequences. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [elements selected by bit from 0..2^|x|] if n<0; [only of length n] otherwise + */ +export function subsequences(x: T[], n: number=-1): T[][] { + return [...isubsequences(x, n)]; +} + + /** * List all possible subsequences. * @param x an array * @param n number of values [-1 ⇒ any] * @returns elements selected by bit from 0..2^|x| if n<0; only of length n otherwise */ -export function* subsequences(x: T[], n: number=-1): IterableIterator { +export function* isubsequences(x: T[], n: number=-1): IterableIterator { var X = x.length; if (n>X) return; if (n===X) { yield x; return; } if (n===0 || X===0) { yield []; return; } var y = x.slice(0, -1); - yield* subsequences(y, n); - for (var s of subsequences(y, n-1)) { + yield* isubsequences(y, n); + for (var s of isubsequences(y, n-1)) { s.push(x[X-1]); yield s; } } +/** + * Obtain all possible permutations. + * @param x an array + * @param n number of values [-1 ⇒ any] + * @returns [[], arrangements of length 1, of length 2, ...] if n<0; [only of length n] otherwise + */ +export function permutations(x: T[], n: number=-1): T[][] { + return [...ipermutations(x, n)]; +} + + /** * List all possible permutations. * @param x an array * @param n number of values [-1 ⇒ any] * @returns [], arrangements of length 1, of length 2, ... if n<0; only of length n otherwise */ -export function* permutations(x: T[], n: number=-1): IterableIterator { +export function* ipermutations(x: T[], n: number=-1): IterableIterator { var X = x.length; if (n>X) return; var i = n<0? 0 : n; var I = n<0? X : n for (; i<=I; ++i) - yield* permutationsFixed(x, i); + yield* ipermutationsFixed(x, i); } -function* permutationsFixed(x: T[], n: number): IterableIterator { +function* ipermutationsFixed(x: T[], n: number): IterableIterator { var X = x.length; if (X===0 || n===0) { yield []; return; } for (var i=0; i(x: T[], n: number=-1, fr: ReadFunction(x: T[], ft: TestFunction): number { export function search(x: T[], ft: TestFunction): number { return x.findIndex(ft); } +export {search as findIndex}; /** @@ -1169,6 +1378,7 @@ export function searchRight(x: T[], ft: TestFunction): number { if (ft(x[i], i, x)) return i; return -1; } +export {searchRight as findLastIndex}; /** @@ -1246,6 +1456,116 @@ export function searchValueAll(x: T[], v: T, fc: CompareFunction | } +/** + * Find first index of minimum value. + * @param x an array + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns first index of minimum value, -1 if empty + */ +export function searchMinimumValue(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length; + if (X===0) return -1; + var mi = 0, mw = fm(x[0], 0, x); + for (var i=1; i(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length; + if (X===0) return -1; + var ni = 0, nw = fm(x[0], 0, x); + for (var i=1; i0) { ni = i; nw = w; } + } + return ni; +} + + +/** + * Find first index of an unsorted value. + * @param x an array + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns index of first unsorted value, -1 if sorted + */ +export function searchUnsortedValue(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length; + if (X<=1) return -1; + var w0 = fm(x[0], 0, x); + for (var i=1; i0) return i; + w0 = w; + } + return -1; +} + + +/** + * Find first index of an adjacent duplicate value. + * @param x an array + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns index of first adjacent duplicate value, -1 if none + */ +export function searchAdjacentDuplicateValue(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length; + if (X<=1) return -1; + var w0 = fm(x[0], 0, x); + for (var i=1; i(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length; + var Y = y.length; + for (var i=0, I=Math.min(X, Y); i(x: T[], v: T, fc: CompareFunction | * @returns first i | x[i..i+|y|] = y else -1 */ export function searchInfix(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { - for (var i of iterableSearchInfixAll(x, y, fc, fm)) - return i; + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length, Y = y.length; + for (var i=0; i<=X-Y; ++i) + if (isInfixAt(x, y, i, fc, fm)) return i; return -1; } @@ -1270,10 +1593,12 @@ export function searchInfix(x: T[], y: T[], fc: CompareFunction | n * @returns first i | x[i..i+|y|] = y else -1 */ export function searchInfixRight(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { - var a = -1; - for (var i of iterableSearchInfixAll(x, y, fc, fm)) - a = i; - return a; + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length, Y = y.length; + for (var i=X-Y; i>=0; --i) + if (isInfixAt(x, y, i, fc, fm)) return i; + return -1; } @@ -1286,7 +1611,23 @@ export function searchInfixRight(x: T[], y: T[], fc: CompareFunction(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number[] { - return [...iterableSearchInfixAll(x, y, fc, fm)]; + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length, Y = y.length, a = []; + for (var i=0; i<=X-Y; ++i) + if (isInfixAt(x, y, i, fc, fm)) a.push(i); + return a; +} + + +function isInfixAt(x: T[], y: T[], i: number, fc: CompareFunction, fm: MapFunction): boolean { + var Y = y.length; + for (var j=0; j(x: T[], y: T[], fc: CompareFunction(x: T[], v: T, fc: CompareFunction | null=n /** - * Check if array starts with a prefix. + * Examine if array has an unsorted value. + * @param x an array + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns x is not sorted? + */ +export function hasUnsortedValue(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { + return searchUnsortedValue(x, fc, fm) >= 0; +} + + +/** + * Examine if array starts with a prefix. * @param x an array * @param y search prefix * @param fc compare function (a, b) @@ -1338,10 +1691,11 @@ export function hasPrefix(x: T[], y: T[], fc: CompareFunction | nul var Y = y.length; return Y===0 || compare(x.slice(0, Y), y, fc, fm)===0; } +export {hasPrefix as startsWith}; /** - * Check if array ends with a suffix. + * Examine if array ends with a suffix. * @param x an array * @param y search suffix * @param fc compare function (a, b) @@ -1352,10 +1706,11 @@ export function hasSuffix(x: T[], y: T[], fc: CompareFunction | nul var Y = y.length; return Y===0 || compare(x.slice(-Y), y, fc, fm)===0; } +export {hasSuffix as endsWith}; /** - * Check if array contains an infix. + * Examine if array contains an infix. * @param x an array * @param y search infix * @param fc compare function (a, b) @@ -1368,37 +1723,81 @@ export function hasInfix(x: T[], y: T[], fc: CompareFunction | null /** - * Check if array has a subsequence. + * Examine if array has a subsequence. + * @param x an array + * @param y search subsequence + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns x[i₀] ⧺ x[i₁] ⧺ ... = y, for some i₀, i₁, ...? | i₀ < i₁ < ... + */ +export function hasSubsequence(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { + return searchSubsequence(x, y, fc, fm) >= 0; +} + + +/** + * Examine if array has a permutation. + * @param x an array + * @param y search permutation + * @param fc map function (v, i, x) + * @param fm compare function (a, b) + * @returns x contains a shuffled version of y? + */ +export function hasPermutation(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { + var x1 = fm? x.map(fm) : x.slice(); + var y1 = fm? y.map(fm) : y.slice(); + return hasSubsequence(x1.sort(), y1.sort(), fc, fm); +} +// #endregion + + + + +// #region COMPARE +// --------------- + +/** + * Compare two arrays (lexicographically). + * @param x an array + * @param y another array + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns xy: +ve + */ +export function compare(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): number { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length; + var Y = y.length; + for (var i=0, I=Math.min(X, Y); i(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { - return searchSubsequence(x, y, fc, fm) >= 0; -} - - -/** - * Check if array has a permutation. - * @param x an array - * @param y search permutation - * @param fc map function (v, i, x) - * @param fm compare function (a, b) - * @returns x contains a shuffled version of y? + * @returns x = y? */ -export function hasPermutation(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { - var x1 = fm? x.map(fm) : x.slice(); - var y1 = fm? y.map(fm) : y.slice(); - return hasSubsequence(x1.sort(), y1.sort(), fc, fm); +export function isEqual(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): boolean { + var X = x.length, Y = y.length; + return X===Y && compare(x, y, fc, fm)===0; } +// #endregion -// FUNCTIONAL -// ---------- +// #region FUNCTIONAL +// ------------------ /** * Call a function for each value. @@ -1411,7 +1810,7 @@ export function forEach(x: T[], fp: ProcessFunction): void { /** - * Check if any value satisfies a test. + * Examine if any value satisfies a test. * @param x an array * @param ft test function (v, i, x) * @returns true if ft(vᵢ) = true for some vᵢ ∈ x @@ -1430,7 +1829,7 @@ function someBoolean(x: T[]): boolean { /** - * Check if all values satisfy a test. + * Examine if all values satisfy a test. * @param x an array * @param ft test function (v, i, x) * @returns true if ft(vᵢ) = true for all vᵢ ∈ x @@ -1460,8 +1859,8 @@ export function map(x: T[], fm: MapFunction): (T|U)[] { /** - * Transform values of an array. - * @param x an array (updated) + * Transform values of an array! + * @param x an array (updated!) * @param fm map function (v, i, x) * @returns x = [fm(v₀), fm(v₁), ...]; vᵢ ∈ x */ @@ -1502,6 +1901,109 @@ export function reduceRight(x: T[], fr: ReduceFunction, acc?: T| } +/** + * Perform exclusive prefix scan from left to right. + * @param x an array + * @param fr reduce function (acc, v, i, x) + * @param acc initial value + * @returns [acc, fr(acc, v₀), fr(fr(acc, v₀), v₁)...] + */ +export function exclusiveScan(x: T[], fr: ReduceFunction, acc: T|U): (T|U)[] { + var a = []; + for (var i=0, I=x.length; i(x: T[], fr: ReduceFunction, acc: T): T[] { + for (var i=0, I=x.length; i(x: T[], fr: ReduceFunction, acc: T|U): (T|U)[] { + var a = []; + for (var i=0, I=x.length; i(x: T[], fr: ReduceFunction, acc: T): T[] { + for (var i=0, I=x.length; i(x: T[], fc: CombineFunction, acc: T): T[] { + var a = []; + if (x.length>0) a.push(fc(acc, x[0])); + for (var i=1, I=x.length; i(x: T[], fc: CombineFunction, acc: T): T[] { + var X = x.length; + if (X===0) return x; + var v = x[0]; + x[0] = fc(acc, v); + for (var i=1; i(x: T[], ft: TestFunction): T[] { /** - * Discard values which pass a test. - * @param x an array (updated) + * Discard values which pass a test! + * @param x an array (updated!) * @param ft test function (v, i, x) * @returns x = [v₀, v₁, ...] | ft(vᵢ) = false; vᵢ ∈ x */ @@ -1675,10 +2177,14 @@ export function zip(xs: T[][], fm: MapFunction | null=null } +// TODO: zip2()? +// #endregion -// MANIPULATION -// ------------ + + +// #region MANIPULATION +// -------------------- /** * Fill with given value. @@ -1694,8 +2200,8 @@ export function fill(x: T[], v: T, i: number=0, I: number=x.length): T[] { /** - * Fill with given value. - * @param x an array (updated) + * Fill with given value! + * @param x an array (updated!) * @param v value * @param i start index [0] * @param I end index [|x|] @@ -1706,39 +2212,6 @@ export function fill$(x: T[], v: T, i: number=0, I: number=x.length): T[] { } -/** - * Arrange values in order. - * @param x an array - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @returns x' | x' = x; x'[i] ≤ x'[j] ∀ i ≤ j - */ -export function sort(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { - return sort$(x.slice(), fc, fm); -} - - -/** - * Arrange values in order. - * @param x an array (updated) - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -export function sort$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { - var fc = fc || COMPARE; - if (fm) return sortDual$(x, fc, fm); - else return x.sort(fc); -} - -function sortDual$(x: T[], fc: CompareFunction, fm: MapFunction): T[] { - var i = -1, m = new Map(); - for (var v of x) - m.set(v, fm(v, ++i, x)); - return x.sort((a, b) => fc(m.get(a), m.get(b))); -} - - /** * Add value to the end. * @param x an array @@ -1748,11 +2221,13 @@ function sortDual$(x: T[], fc: CompareFunction, fm: MapFunction(x: T[], ...vs: T[]): T[] { return x.concat(vs); } +export {push as pushBack}; +export {push as append}; /** - * Add values to the end. - * @param x an array (updated) + * Add values to the end! + * @param x an array (updated!) * @param vs values to add * @returns x | x = [...x, ...vs] */ @@ -1760,6 +2235,8 @@ export function push$(x: T[], ...vs: T[]): T[] { x.push(...vs); return x; } +export {push$ as pushBack$}; +export {push$ as append$}; /** @@ -1770,17 +2247,19 @@ export function push$(x: T[], ...vs: T[]): T[] { export function pop(x: T[]): T[] { return x.slice(0, -1); } +export {pop as popBack}; /** - * Remove last value. - * @param x an array (updated) + * Remove last value! + * @param x an array (updated!) * @returns x = x[0..|x|-1] */ export function pop$(x: T[]): T[] { x.pop(); return x; } +export {pop as popBack$}; /** @@ -1791,17 +2270,19 @@ export function pop$(x: T[]): T[] { export function shift(x: T[]): T[] { return x.slice(1); } +export {shift as popFront}; /** - * Remove first value. - * @param x an array (updated) + * Remove first value! + * @param x an array (updated!) * @returns x = x[1..] */ export function shift$(x: T[]): T[] { x.shift(); return x; } +export {shift$ as popFront$}; /** @@ -1813,11 +2294,13 @@ export function shift$(x: T[]): T[] { export function unshift(x: Iterable, ...vs: T[]): T[] { return concat$(vs, x); } +export {unshift as pushFront}; +export {unshift as prepend}; /** - * Add values to the start. - * @param x an array (updated) + * Add values to the start! + * @param x an array (updated!) * @param vs values to add * @returns x = [...vs, ...x] */ @@ -1825,6 +2308,8 @@ export function unshift$(x: T[], ...vs: T[]): T[] { x.unshift(...vs); return x; } +export {unshift$ as pushFront$}; +export {unshift$ as prepend$}; /** @@ -1842,8 +2327,8 @@ export function copy(x: T[], y: T[], j: number=0, i: number=0, I: number=y.le /** - * Copy part of array to another. - * @param x target array (updated) + * Copy part of array to another! + * @param x target array (updated!) * @param y source array * @param j write index [0] * @param i read start index [0] @@ -1877,8 +2362,8 @@ export function copyWithin(x: T[], j: number=0, i: number=0, I: number=x.leng /** - * Copy part of array within. - * @param x an array (updated) + * Copy part of array within! + * @param x an array (updated!) * @param j write index [0] * @param i read start index [0] * @param I read end index [|x|] @@ -1912,8 +2397,8 @@ function movePart(x: T[], i: number, j: number, k: number): T[] { /** - * Move part of array within. - * @param x an array (updated) + * Move part of array within! + * @param x an array (updated!) * @param j write index [0] * @param i read start index [0] * @param I read end index [|x|] @@ -1938,11 +2423,12 @@ export function moveWithin$(x: T[], j: number=0, i: number=0, I: number=x.len export function splice(x: T[], i: number, n: number=x.length-i, ...vs: T[]): T[] { return concat$(x.slice(0, i), vs, x.slice(i+n)); } +export {splice as toSpliced}; /** - * Remove or replace existing values. - * @param x an array (updated) + * Remove or replace existing values! + * @param x an array (updated!) * @param i remove index * @param n number of values to remove [rest] * @param vs values to insert @@ -2082,7 +2568,7 @@ export function group(x: T[], fc: CompareFunction | null=null, fm: * @param ft test function (v, i, x) * @returns [satisfies, doesnt] */ -export function partition(x: T[], ft: TestFunction): [T[], T[]] { +export function segregate(x: T[], ft: TestFunction): [T[], T[]] { var t: T[] = [], f: T[] = [], i = -1; for (var v of x) { if (ft(v, ++i, x)) t.push(v); @@ -2093,12 +2579,12 @@ export function partition(x: T[], ft: TestFunction): [T[], T[]] { /** - * Segregate values by similarity. + * Segregate each distinct value. * @param x an array * @param fm map function (v, i, x) * @returns Map \{key ⇒ values\} */ -export function partitionAs(x: T[], fm: MapFunction | null=null): Map { +export function segregateEach(x: T[], fm: MapFunction | null=null): Map { var fm = fm || IDENTITY; var i = -1, a = new Map(); for (var v of x) { @@ -2108,6 +2594,8 @@ export function partitionAs(x: T[], fm: MapFunction | null=null) } return a; } +export {segregateEach as groupToMap}; +export {segregateEach as segregateAs}; // DEPRECATED /** @@ -2164,11 +2652,12 @@ export function repeat(x: T[], n: number=1): T[] { export function reverse(x: T[]): T[] { return x.slice().reverse(); } +export {reverse as toReversed}; /** - * Reverse the values. - * @param x an array (updated) + * Reverse the values! + * @param x an array (updated!) * @returns x = [x[|x|-1], x[|x|-2], ..., x[1], x[0]] */ export function reverse$(x: T[]): T[] { @@ -2189,8 +2678,8 @@ export function rotate(x: T[], n: number=0): T[] { /** - * Rotate values in array. - * @param x an array (updated) + * Rotate values in array! + * @param x an array (updated!) * @param n rotate amount (+ve: left, -ve: right) [0] * @returns x = x[n..] ⧺ x[0..n] */ @@ -2273,12 +2762,13 @@ export function interleave(xs: T[][]): T[] { } return a; } +// #endregion -// COMBINE -// ------- +// #region COMBINE +// --------------- /** * Append values from arrays. @@ -2286,13 +2776,13 @@ export function interleave(xs: T[][]): T[] { * @returns ...x₀, ...x₁, ... | [x₀, x₁, ...] = xs */ export function concat(...xs: T[][]): T[] { - return concat$([], ...xs); + return [].concat(...xs); } /** - * Append values from arrays. - * @param x an array (updated) + * Append values from arrays! + * @param x an array (updated!) * @param ys arrays to append * @returns x = [...x, ...y₀, ...y₁, ...] | [y₀, y₁, ...] = ys */ @@ -2312,15 +2802,16 @@ export function concat$(x: T[], ...ys: Iterable[]): T[] { export function join(x: T[], sep: string=","): string { return x.join(sep); } +// #endregion -// SET OPERATIONS -// -------------- +// #region SET OPERATIONS +// ------------------------ /** - * Check if there are no duplicate values. + * Examine if there are no duplicate values. * @param x an array * @param fc compare function (a, b) * @param fm map function (v, i, x) @@ -2355,7 +2846,7 @@ function isUniqueDual(x: T[], fc: CompareFunction | null=null, fm: /** - * Check if arrays have no value in common. + * Examine if arrays have no value in common. * @param x an array * @param y another array * @param fc compare function (a, b) @@ -2442,8 +2933,8 @@ export function union(x: T[], y: T[], fc: CompareFunction | null=nu /** - * Obtain values present in any array. - * @param x an array (updated) + * Obtain values present in any array! + * @param x an array (updated!) * @param y another array * @param fc compare function (a, b) * @param fm map function (v, i, x) @@ -2564,7 +3055,8 @@ function differenceDual(x: T[], y: T[], fc: CompareFunction | null= * @returns x-y ∪ y-x */ export function symmetricDifference(x: T[], y: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { - var x0 = from$(x), y0 = from$(y); + var x0 = fromIterable$(x); + var y0 = fromIterable$(y); var ax = difference(x0, y0, fc, fm); var ay = difference(y0, x0, fc, fm); return concat$(ax, ay); @@ -2572,7 +3064,7 @@ export function symmetricDifference(x: T[], y: T[], fc: CompareFunction< /** - * List cartesian product of arrays. + * Obtain cartesian product of arrays. * @param xs arrays * @param fm map function (vs, i) * @returns x₀ × x₁ × ... = \{[v₀, v₁, ...] | v₀ ∈ x₀, v₁ ∈ x₁, ...] \} @@ -2596,3 +3088,268 @@ export function cartesianProduct(xs: T[][], fm: MapFunction } return a; } +// #endregion + + + + +// #region SORT +// ------------ + +/** + * Arrange values in order. + * @param x an array + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x' | x' = x; x'[i] ≤ x'[j] ∀ i ≤ j + */ +export function sort(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { + return sort$(x.slice(), fc, fm, fs); +} +export {sort as toSorted}; + + +/** + * Arrange values in order! + * @param x an array (updated!) + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j + */ +export function sort$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { + var fc = fc || COMPARE; + if (!fm) return x.sort(fc); + var X = x.length; + var fm = fm || IDENTITY; + var fs = fs || swapRaw$; + return partialHeapSort$(x, 0, X, X, fc, fm, fs); + return partialIntroSort$(x, 0, X, X, fc, fm, fs); +} + + +/** + * Partially arrange values in order. + * @param x an array + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x' | x' = x; x'[i] ≤ x'[j] ∀ i ≤ j + */ +export function partialSort(x: T[], i: number, I: number, n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { + return partialSort$(x.slice(), i, I, n, fc, fm, fs); +} + + +/** + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j + */ +export function partialSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var fs = fs || swapRaw$; + // TODO: Check various sort functions. + return partialIntroSort$(x, i, I, n, fc, fm, fs); +} + + +/** + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j + */ +function partialIntroSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + var d = Math.floor(Math.log2(I-i)*2); // Maximum depth of recursion + var s = 16; // When to switch to insertion sort + return partialIntroSortHelper$(x, i, I, d, s, n, fc, fm, fs); +} + + +// Partially arrange values in order with hybrid quick sort, heap sort, and insertion sort. +function partialIntroSortHelper$(x: T[], i: number, I: number, d: number, s: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + if (n<=0 || I-i<=1) return x; // Nothing to sort + if (I-i<=s) return partialInsertionSort$(x, i, I, n, fc, fm, fs); // Insertion sort + if (d<=0) return partialHeapSort$(x, i, I, n, fc, fm, fs); // Heap sort + var p = i + Math.floor((I-i)*Math.random()); // Choose pivot + var p = quickSortPartition$(x, i, I, p, fc, fm, fs); // Partition array + partialIntroSortHelper$(x, i, p, d, s, Math.min(p-i, n), fc, fm, fs); // Sort left part + partialIntroSortHelper$(x, p+1, I, d, s, Math.min(I-p-1, n), fc, fm, fs); // Sort right part + return x; +} + + +/** + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j + */ +function partialQuickSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + if (n<=0 || I-i<=1) return x; // Nothing to sort + var p = i + Math.floor((I-i)*Math.random()); // Choose pivot + var p = quickSortPartition$(x, i, I, p, fc, fm, fs); // Partition array + partialQuickSort$(x, i, p, Math.min(p-i, n), fc, fm, fs); // Sort left part + partialQuickSort$(x, p+1, I, Math.min(I-p-1, n), fc, fm, fs); // Sort right part + return x; +} + + +// TODO: Make this a generic function. +// Partition the array into two parts, such that values in the first part are less than values in the second part. +function quickSortPartition$(x: T[], i: number, I: number, p: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): number { + var wp = fm(x[p], p, x); // Pivot value + var j = i-1; // Last index of values ≤ pivot + fs(x, p, I-1); // Move pivot to end + for (var k=i; k 0) continue; + fs(x, ++j, k); // Move value ≤ pivot to left + } + fs(x, ++j, I-1); // Move pivot to middle + return j; // Return pivot index +} + + +/** + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j + */ +function partialHeapSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + buildReverseMinHeap$(x, i, I, fc, fm, fs); + for (var r=I-1; n>0 && i(x: T[], i: number, I: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { + for (var r=I-Math.floor((I-i)/2); r(x: T[], i: number, I: number, r: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { + var s = r; // Index of smallest value + var lt = 2*r - I; // Reverse of lt = 2*r+1 + var rt = lt - 1; // Reverse of rt = 2*r+2 + var ws = fm(x[s], s, x); + if (lt>=i && fc(fm(x[lt], lt, x), ws) < 0) s = lt; // Left child is smaller? + if (rt>=i && fc(fm(x[rt], rt, x), ws) < 0) s = rt; // Right child is smaller? + if (s !== r) { // Smallest is not root? + fs(x, s, r); // Swap root with smallest + reverseMinHeapify$(x, i, I, s, fc, fm, fs); // Rebuild heap + } +} + + +/** + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort (ignored) + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j + */ +function partialInsertionSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + // NOTE: Insertion sort does not support partial sorting, so we ignore n. + if (fs===swapRaw$) return partialInsertionSortSwapless$(x, i, I, n, fc, fm, fs); + else return partialInsertionSortSwap$ (x, i, I, n, fc, fm, fs); +} + + +// Sort values in order with swap-enabled version of insertion sort. +function partialInsertionSortSwap$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + for (var j=i+1; j=i && fc(fm(x[k], k, x), wkey)>0; --k) + fs(x, k, k+1); + } + return x; +} + + +// Sort values in order with swapless version of insertion sort. +function partialInsertionSortSwapless$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + for (var j=i+1; j=i && fc(fm(x[k], k, x), wkey)>0; --k) + x[k+1] = x[k]; + x[k+1] = key; + } + return x; +} + + +/** + * Partially arrange values in order! + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param n minimum number of values to sort + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + * @returns x | x[i] ≤ x[j] ∀ i ≤ j + */ +function partialSelectionSort$(x: T[], i: number, I: number, n: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): T[] { + for (var j=i; n>0 && j 0) { l = k; wl = wk; } + } + fs(x, j, l); + } + return x; +} +// #endregion +// #endregion diff --git a/tests/index.test.ts b/tests/index.test.ts index 5f4233b46..3582b6884 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -27,7 +27,7 @@ import { remove$, removePath$, count, - countAs, + countEach, min, minEntry, max, @@ -130,8 +130,8 @@ import { cutAt, cutAtRight, group, - partition, - partitionAs, + segregate, + segregateEach, chunk, cycle, repeat, @@ -453,12 +453,12 @@ test("count", () => { }); -test("countAs", () => { +test("countEach", () => { var x = [1, 1, 2, 2, 4]; - var a = countAs(x); + var a = countEach(x); expect(a).toStrictEqual(new Map([[1, 2], [2, 2], [4, 1]])); var x = [1, 2, 3, 4]; - var a = countAs(x, v => v % 2); + var a = countEach(x, v => v % 2); expect(a).toStrictEqual(new Map([[1, 2], [0, 2]])); }); @@ -1317,16 +1317,24 @@ test("fill$", () => { test("sort", () => { - var x = [-2, -3, 1, 4]; + // var x = [-2, -3, 1, 4]; + // var a = sort(x); + // expect(a).toStrictEqual([-3, -2, 1, 4]); // (compares numbers) + // var a = sort(x, (a, b) => Math.abs(a) - Math.abs(b)); + // expect(a).toStrictEqual([1, -2, -3, 4]); + // var a = sort(x, null, v => Math.abs(v)); + // expect(a).toStrictEqual([1, -2, -3, 4]); + var x = [2, 3, 1, 4]; var a = sort(x); - expect(a).toStrictEqual([-3, -2, 1, 4]); // (compares numbers) + expect(a).toStrictEqual([1, 2, 3, 4]); // (compares numbers) var a = sort(x, (a, b) => Math.abs(a) - Math.abs(b)); - expect(a).toStrictEqual([1, -2, -3, 4]); + expect(a).toStrictEqual([1, 2, 3, 4]); var a = sort(x, null, v => Math.abs(v)); - expect(a).toStrictEqual([1, -2, -3, 4]); + expect(a).toStrictEqual([1, 2, 3, 4]); }); +// TODO: Check various array lengths. test("sort$", () => { var x = [-2, -3, 1, 4]; var a = sort$(x); @@ -1592,22 +1600,22 @@ test("group", () => { }); -test("partition", () => { +test("segregate", () => { var x = [1, 2, 3, 4]; - var a = partition(x, v => v % 2 == 0); + var a = segregate(x, v => v % 2 == 0); expect(a).toStrictEqual([[2, 4], [1, 3]]); var x = [1, 2, 3, 4, 5]; - var a = partition(x, v => v % 2 == 1); + var a = segregate(x, v => v % 2 == 1); expect(a).toStrictEqual([[1, 3, 5], [2, 4]]); }); -test("partitionAs", () => { +test("segregateEach", () => { var x = [1, 2, 3, 4]; - var a = partitionAs(x, v => v % 2 == 0); + var a = segregateEach(x, v => v % 2 == 0); expect(a).toStrictEqual(new Map([[false, [1, 3]], [true, [2, 4]]])); var x = [1, 2, 3, 4, 5]; - var b = partitionAs(x, v => v % 3); + var b = segregateEach(x, v => v % 3); expect(b).toStrictEqual(new Map([[1, [1, 4]], [2, [2, 5]], [0, [3]]])); }); diff --git a/wiki b/wiki index 88da15c50..102f3cb03 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 88da15c50b4ed31f972063a00c289952871b7ae6 +Subproject commit 102f3cb03fbed7578370e566bf34fab039d53581