@ixfx/components 0.2.5 → 0.2.6
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/bundle/index.d.ts +155 -29
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +1116 -310
- package/bundle/index.js.map +1 -1
- package/dist/ac-text.d.ts +2 -3
- package/dist/ac-text.d.ts.map +1 -1
- package/dist/ac-text.js +4 -4
- package/dist/ac-text.js.map +1 -1
- package/dist/{button-DgKUzE5o.js → button-B9ztaKyl.js} +4 -4
- package/dist/button-B9ztaKyl.js.map +1 -0
- package/dist/button.d.ts +3 -4
- package/dist/button.d.ts.map +1 -1
- package/dist/button.js +1 -1
- package/dist/checkbox.d.ts +1 -2
- package/dist/checkbox.d.ts.map +1 -1
- package/dist/checkbox.js +3 -3
- package/dist/checkbox.js.map +1 -1
- package/dist/colour-C3MQIjFJ-BJV-QC-3.js.map +1 -1
- package/dist/{colour-picker-Bw-AZoWx.js → colour-picker-BNqDz4wK.js} +5 -5
- package/dist/colour-picker-BNqDz4wK.js.map +1 -0
- package/dist/colour-picker.d.ts +2 -2
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.d.ts +2 -3
- package/dist/crumbs.d.ts.map +1 -1
- package/dist/crumbs.js +4 -4
- package/dist/crumbs.js.map +1 -1
- package/dist/data-display.d.ts +1 -2
- package/dist/data-display.d.ts.map +1 -1
- package/dist/data-display.js +2 -3
- package/dist/data-display.js.map +1 -1
- package/dist/decorate-O4m1U4l3.js +25 -0
- package/dist/decorate-O4m1U4l3.js.map +1 -0
- package/dist/defaults-FEZaGiYZ.js.map +1 -1
- package/dist/dist-By5URG0Y.js.map +1 -1
- package/dist/dist-D7kJgnSE.js.map +1 -1
- package/dist/editable-label.d.ts.map +1 -1
- package/dist/editable-label.js +3 -3
- package/dist/editable-label.js.map +1 -1
- package/dist/{hex-editor-Gib1Grg2.d.ts → hex-editor-2_s0Ua5t.d.ts} +3 -4
- package/dist/hex-editor-2_s0Ua5t.d.ts.map +1 -0
- package/dist/{hex-editor-Wd6ZtZA2.js → hex-editor-gH3mteMr.js} +4 -4
- package/dist/hex-editor-gH3mteMr.js.map +1 -0
- package/dist/hex.d.ts +1 -1
- package/dist/hex.js +1 -1
- package/dist/highlight-CAjwQ4GE.js.map +1 -1
- package/dist/{icon-BR5aTIiP.js → icon-CYACf6o8.js} +4 -4
- package/dist/icon-CYACf6o8.js.map +1 -0
- package/dist/{icon-DQKdfR7U.d.ts → icon-CeTDJhC7.d.ts} +3 -4
- package/dist/icon-CeTDJhC7.d.ts.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +1 -1
- package/dist/icons.js.map +1 -1
- package/dist/{incr-search-BjCNClnC.js → incr-search-BZawvrfl.js} +109 -109
- package/dist/incr-search-BZawvrfl.js.map +1 -0
- package/dist/incr-search.d.ts +2 -2
- package/dist/incr-search.js +1 -1
- package/dist/index-B1hNR7Z9.d.ts.map +1 -1
- package/dist/{index-CpbBI3n7.d.ts → index-CnL9krsH.d.ts} +3 -4
- package/dist/index-CnL9krsH.d.ts.map +1 -0
- package/dist/{index-CPVcTTXo.d.ts → index-CwGkb9s1.d.ts} +6 -7
- package/dist/index-CwGkb9s1.d.ts.map +1 -0
- package/dist/{index-C5dK8RZa.d.ts → index-DrQjA8Gs.d.ts} +3 -4
- package/dist/index-DrQjA8Gs.d.ts.map +1 -0
- package/dist/{index-C9iBN908.d.ts → index-V2cJorlA.d.ts} +29 -29
- package/dist/index-V2cJorlA.d.ts.map +1 -0
- package/dist/{index-CQxanqXF.d.ts → index-kY5gxbkI.d.ts} +16 -17
- package/dist/index-kY5gxbkI.d.ts.map +1 -0
- package/dist/index.d.ts +144 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +813 -23
- package/dist/index.js.map +1 -1
- package/dist/keyboard-De_vOHLg.js.map +1 -1
- package/dist/{labelled-input-base-DS6-6C0r.js → labelled-input-base-BvgH7ldm.js} +2 -2
- package/dist/{labelled-input-base-DS6-6C0r.js.map → labelled-input-base-BvgH7ldm.js.map} +1 -1
- package/dist/{labelled-input-base-DnRh4reU.d.ts → labelled-input-base-D-Y7agky.d.ts} +2 -3
- package/dist/labelled-input-base-D-Y7agky.d.ts.map +1 -0
- package/dist/labelled-radial-input.d.ts +4 -5
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-radial-input.js +5 -5
- package/dist/labelled-radial-input.js.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-range-input.js +4 -4
- package/dist/labelled-range-input.js.map +1 -1
- package/dist/led.d.ts +2 -3
- package/dist/led.d.ts.map +1 -1
- package/dist/led.js +3 -3
- package/dist/led.js.map +1 -1
- package/dist/{menu-BV6UFx_z.js → menu-C3eANB5M.js} +12 -12
- package/dist/menu-C3eANB5M.js.map +1 -0
- package/dist/{menu-item-DEzL_Yjk.js → menu-item-6IU5weHi.js} +7 -7
- package/dist/menu-item-6IU5weHi.js.map +1 -0
- package/dist/{menu-item-Cikacp4F.d.ts → menu-item-ClQF2DKN.d.ts} +7 -8
- package/dist/menu-item-ClQF2DKN.d.ts.map +1 -0
- package/dist/menu.d.ts +3 -3
- package/dist/menu.js +2 -2
- package/dist/miller.d.ts +2 -3
- package/dist/miller.d.ts.map +1 -1
- package/dist/miller.js +4 -4
- package/dist/miller.js.map +1 -1
- package/dist/narrowed-text.d.ts +2 -3
- package/dist/narrowed-text.d.ts.map +1 -1
- package/dist/narrowed-text.js +4 -4
- package/dist/narrowed-text.js.map +1 -1
- package/dist/panel.d.ts +2 -3
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +4 -4
- package/dist/panel.js.map +1 -1
- package/dist/plots.d.ts +1 -1
- package/dist/plots.js +1 -1
- package/dist/polar-pad.d.ts +1 -2
- package/dist/polar-pad.d.ts.map +1 -1
- package/dist/polar-pad.js +3 -3
- package/dist/polar-pad.js.map +1 -1
- package/dist/{radial-input-CNJ8asY2.js → radial-input-Dbc6fgCK.js} +4 -4
- package/dist/radial-input-Dbc6fgCK.js.map +1 -0
- package/dist/{radial-input-xSTd4kEz.d.ts → radial-input-LUSBEycd.d.ts} +2 -3
- package/dist/radial-input-LUSBEycd.d.ts.map +1 -0
- package/dist/radial-input.d.ts +1 -1
- package/dist/radial-input.js +1 -1
- package/dist/range-input.d.ts +2 -3
- package/dist/range-input.d.ts.map +1 -1
- package/dist/range-input.js +3 -3
- package/dist/range-input.js.map +1 -1
- package/dist/range.d.ts +2 -3
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js +3 -3
- package/dist/range.js.map +1 -1
- package/dist/registry-CZEB1YI9.js.map +1 -1
- package/dist/selecthorizontal.d.ts +2 -3
- package/dist/selecthorizontal.d.ts.map +1 -1
- package/dist/selecthorizontal.js +3 -3
- package/dist/selecthorizontal.js.map +1 -1
- package/dist/snap-container.d.ts +1 -2
- package/dist/snap-container.d.ts.map +1 -1
- package/dist/snap-container.js +3 -3
- package/dist/snap-container.js.map +1 -1
- package/dist/split-layout.d.ts +2 -3
- package/dist/split-layout.d.ts.map +1 -1
- package/dist/split-layout.js +3 -3
- package/dist/split-layout.js.map +1 -1
- package/dist/swipe.d.ts.map +1 -1
- package/dist/swipe.js +3 -3
- package/dist/swipe.js.map +1 -1
- package/dist/{tab-list-CFF7uSYH.d.ts → tab-list-BnqISNvO.d.ts} +5 -6
- package/dist/tab-list-BnqISNvO.d.ts.map +1 -0
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +3 -3
- package/dist/tabs.js.map +1 -1
- package/dist/tickled-controller-Cq1Va0rR.d.ts.map +1 -1
- package/dist/tickled-styles-Bg3QbcrD.js.map +1 -1
- package/dist/{timeline-DBWJsSjd.js → timeline-B62Sz6J_.js} +5 -5
- package/dist/timeline-B62Sz6J_.js.map +1 -0
- package/dist/timeline.d.ts +2 -2
- package/dist/timeline.js +1 -1
- package/dist/{tooltip-kfUBWYVB.d.ts → tooltip-CwGfb4lp.d.ts} +2 -3
- package/dist/tooltip-CwGfb4lp.d.ts.map +1 -0
- package/dist/tree-component-CQpoPo4s.d.ts.map +1 -1
- package/dist/tree-data-model-DGvRVOFY.js.map +1 -1
- package/dist/tree.d.ts +3 -4
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +4 -4
- package/dist/tree.js.map +1 -1
- package/dist/types-BKaqnpwx.d.ts.map +1 -1
- package/dist/{xy-axis-BANbgrI5.js → xy-axis-C7fNxDwY.js} +5 -5
- package/dist/xy-axis-C7fNxDwY.js.map +1 -0
- package/dist/{xy-axis-CIFok7MF.d.ts → xy-axis-D9uwpGTT.d.ts} +5 -6
- package/dist/xy-axis-D9uwpGTT.d.ts.map +1 -0
- package/dist/xy-pad.d.ts +1 -2
- package/dist/xy-pad.d.ts.map +1 -1
- package/dist/xy-pad.js +3 -3
- package/dist/xy-pad.js.map +1 -1
- package/package.json +15 -11
- package/dist/button-DgKUzE5o.js.map +0 -1
- package/dist/colour-picker-Bw-AZoWx.js.map +0 -1
- package/dist/decorate-BZbkqIhA.js +0 -9
- package/dist/hex-editor-Gib1Grg2.d.ts.map +0 -1
- package/dist/hex-editor-Wd6ZtZA2.js.map +0 -1
- package/dist/icon-BR5aTIiP.js.map +0 -1
- package/dist/icon-DQKdfR7U.d.ts.map +0 -1
- package/dist/incr-search-BjCNClnC.js.map +0 -1
- package/dist/index-C5dK8RZa.d.ts.map +0 -1
- package/dist/index-C9iBN908.d.ts.map +0 -1
- package/dist/index-CPVcTTXo.d.ts.map +0 -1
- package/dist/index-CQxanqXF.d.ts.map +0 -1
- package/dist/index-CpbBI3n7.d.ts.map +0 -1
- package/dist/labelled-input-base-DnRh4reU.d.ts.map +0 -1
- package/dist/menu-BV6UFx_z.js.map +0 -1
- package/dist/menu-item-Cikacp4F.d.ts.map +0 -1
- package/dist/menu-item-DEzL_Yjk.js.map +0 -1
- package/dist/radial-input-CNJ8asY2.js.map +0 -1
- package/dist/radial-input-xSTd4kEz.d.ts.map +0 -1
- package/dist/tab-list-CFF7uSYH.d.ts.map +0 -1
- package/dist/timeline-DBWJsSjd.js.map +0 -1
- package/dist/tooltip-kfUBWYVB.d.ts.map +0 -1
- package/dist/xy-axis-BANbgrI5.js.map +0 -1
- package/dist/xy-axis-CIFok7MF.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dist-D7kJgnSE.js","names":["resultThrow","IxfxError","resultToError","functionTest"],"sources":["../node_modules/.pnpm/@ixfx+guards@0.56.12/node_modules/@ixfx/guards/dist/index.js","../node_modules/.pnpm/@ixfx+arrays@0.56.12/node_modules/@ixfx/arrays/dist/index.js","../node_modules/.pnpm/@ixfx+numbers@0.56.12/node_modules/@ixfx/numbers/dist/index.js"],"sourcesContent":["//#region src/result.ts\nfunction getErrorMessage(ex) {\n\tif (typeof ex === `string`) return ex;\n\tif (ex instanceof Error) return ex.message;\n\treturn String(ex);\n}\n/**\n* Throws an error if any result is a failure.\n* Error message will be the combined from all errors.\n* @param results\n*/\nfunction throwIfFailed(...results) {\n\tconst failed = results.filter((r) => resultIsError(r));\n\tif (failed.length === 0) return;\n\tconst messages = failed.map((f) => resultErrorToString(f));\n\tthrow new Error(messages.join(`, `));\n}\n/**\n* If any of `results` is an error, throws it, otherwise ignored.\n* @param results\n* @returns _true_ or throws\n*/\nfunction resultThrow(...results) {\n\tfor (const r of results) {\n\t\tif (r === void 0) continue;\n\t\tif (typeof r === `boolean`) if (!r) throw IxfxError.fromString(`Guard failed: false result`);\n\t\telse continue;\n\t\tconst rr = typeof r === `object` ? r : r();\n\t\tif (rr === void 0) continue;\n\t\tif (rr.success) continue;\n\t\tthrow resultToError(rr);\n\t}\n\treturn true;\n}\nfunction resultThrowSingle(result) {\n\tif (result.success) return true;\n\tthrow resultToError(result);\n}\n/**\n* Returns the first failed result, or _undefined_ if there are no fails\n* @param results\n*/\nfunction resultFirstFail_(...results) {\n\tfor (const r of results) {\n\t\tif (typeof r === `boolean`) {\n\t\t\tif (r) continue;\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Guard failed: false result`\n\t\t\t};\n\t\t}\n\t\tconst rr = typeof r === `object` ? r : r();\n\t\tif (rr === void 0) continue;\n\t\tif (!rr.success) return rr;\n\t}\n}\n/**\n* Returns _true_ if `result` is an error\n* @param result\n*/\nfunction resultIsError(result) {\n\tif (typeof result !== `object` || result === null) return false;\n\treturn !result.success;\n}\n/**\n* Returns _true_ if `result` is OK and has a value\n* @param result\n*/\nfunction resultIsOk(result) {\n\tif (typeof result !== `object` || result === null) return false;\n\treturn result.success;\n}\nvar IxfxError = class IxfxError extends Error {\n\tcause;\n\tconstructor(message, cause) {\n\t\tsuper(message);\n\t\tthis.cause = cause;\n\t}\n\tstatic fromError(error, cause) {\n\t\tconst message = error.message;\n\t\tconst stack = error.stack;\n\t\tconst name = error.name;\n\t\tconst newError = new IxfxError(message, cause);\n\t\tnewError.stack = stack;\n\t\tnewError.name = `IxfxError(${name})`;\n\t\treturn newError;\n\t}\n\tstatic fromString(message, cause) {\n\t\tconst newError = new IxfxError(message, cause);\n\t\tnewError.name = `IxfxError`;\n\t\treturn newError;\n\t}\n};\n/**\n* Gets the result as an Error\n* @param result\n*/\nfunction resultToError(result) {\n\tif (typeof result.error === `string`) return IxfxError.fromString(result.error, result.info);\n\tif (result.error instanceof Error) return IxfxError.fromError(result.error, result.info);\n\treturn IxfxError.fromString(JSON.stringify(result.error), result.info);\n}\n/**\n* Unwraps the result, returning its value if OK.\n* If not, an exception is thrown.\n* @param result\n*/\nfunction resultToValue(result) {\n\tif (resultIsOk(result)) return result.value;\n\tthrow resultToError(result);\n}\n/**\n* Returns the error as a string.\n* @param result\n*/\nfunction resultErrorToString(result) {\n\tif (result.error instanceof Error) return getErrorMessage(result.error);\n\tif (typeof result.error === `string`) return result.error;\n\treturn JSON.stringify(result.error);\n}\n/**\n* Returns a {@link ResultError} using 'error' as the message.\n* @param error\n* @param info\n*/\nfunction errorResult(error, info) {\n\treturn {\n\t\tsuccess: false,\n\t\terror,\n\t\tinfo\n\t};\n}\n/**\n* Returns first failed result or final value.\n* @param results\n*/\nfunction resultsCollate(...results) {\n\tlet rr;\n\tfor (const r of results) {\n\t\tif (typeof r === `boolean`) {\n\t\t\tif (r) continue;\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Guard failed: false result`\n\t\t\t};\n\t\t}\n\t\trr = typeof r === `object` ? r : r();\n\t\tif (rr === void 0) continue;\n\t\tif (!rr.success) return rr;\n\t}\n\tif (!rr) throw new Error(`No results`);\n\treturn rr;\n}\n/**\n* If `result` is an error, calls `callback`, passing the error.\n* Otherwise does nothing\n* @param result\n* @param callback\n*/\nfunction resultWithFail(result, callback) {\n\tif (resultIsError(result)) callback(result);\n}\n//#endregion\n//#region src/numbers.ts\n/**\n* Returns true if `x` is a power of two\n* @param x\n* @returns True if `x` is a power of two\n*/\nconst isPowerOfTwo = (x) => Math.log2(x) % 1 === 0;\n/**\n* Returns `fallback` if `v` is NaN, otherwise returns `v`.\n* \n* Throws if `v` is not a number type, null or undefined\n* @param v\n* @param fallback\n* @returns\n*/\nconst ifNaN = (v, fallback) => {\n\tif (typeof v !== `number`) throw new TypeError(`v is not a number. Got: ${typeof v}`);\n\tif (Number.isNaN(v)) return fallback;\n\treturn v;\n};\n/**\n* Parses `value` as an integer, returning it if it meets the `range` criteria.\n* If not, `defaultValue` is returned.\n*\n* ```js\n* const i = integerParse('10', 'positive'); // 10\n* const i = integerParse('10.5', 'positive'); // 10\n* const i = integerParse('0', 'nonZero', 100); // 100\n* ```\n*\n* NaN is returned if criteria does not match and no default is given\n* ```js\n* const i = integerParse('10', 'negative'); // NaN\n* ```\n*\n* @param value\n* @param range\n* @param defaultValue\n* @returns\n*/\nconst integerParse = (value, range = ``, defaultValue = NaN) => {\n\tif (typeof value === `undefined`) return defaultValue;\n\tif (value === null) return defaultValue;\n\ttry {\n\t\tconst parsed = Number.parseInt(typeof value === `number` ? value.toString() : value);\n\t\treturn integerTest(parsed, range, `parsed`).success ? parsed : defaultValue;\n\t} catch {\n\t\treturn defaultValue;\n\t}\n};\n/**\n* Checks if `t` is not a number or within specified range.\n* Returns `[false, reason:string]` if invalid or `[true]` if valid.\n* \n* Alternatives: {@link integerTest} for additional integer check, {@link percentTest} for percentage-range.\n*\n* * (empty, default): must be a number type and not NaN.\n* * finite: must be a number, not NaN and not infinite\n* * positive: must be at least zero\n* * negative: must be zero or lower\n* * aboveZero: must be above zero\n* * belowZero: must be below zero\n* * percentage: must be within 0-1, inclusive\n* * nonZero: can be anything except zero\n* * bipolar: can be -1 to 1, inclusive\n* @param value Value to check\n* @param parameterName Name of parameter (for more helpful exception messages)\n* @param range Range to enforce\n* @returns\n*/\nconst numberTest = (value, range = ``, parameterName = `?`, info) => {\n\tif (value === null) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is null`,\n\t\tinfo\n\t};\n\tif (typeof value === `undefined`) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is undefined`,\n\t\tinfo\n\t};\n\tif (Number.isNaN(value)) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is NaN`,\n\t\tinfo\n\t};\n\tif (typeof value !== `number`) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is not a number (${JSON.stringify(value)})`,\n\t\tinfo\n\t};\n\tswitch (range) {\n\t\tcase `finite`:\n\t\t\tif (!Number.isFinite(value)) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName} must be finite (Got: ${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `positive`:\n\t\t\tif (value < 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be at least zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `negative`:\n\t\t\tif (value > 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be zero or lower (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `aboveZero`:\n\t\t\tif (value <= 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be above zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `belowZero`:\n\t\t\tif (value >= 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be below zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `percentage`:\n\t\t\tif (value > 1 || value < 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be in percentage range (0 to 1). (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `nonZero`:\n\t\t\tif (value === 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must non-zero. (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `bipolar`:\n\t\t\tif (value > 1 || value < -1) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be in bipolar percentage range (-1 to 1). (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t}\n\treturn {\n\t\tsuccess: true,\n\t\tvalue,\n\t\tinfo\n\t};\n};\n/**\n* Checks if `t` is not a number or within specified range.\n* Throws if invalid. Use {@link numberTest} to test without throwing.\n*\n* * (empty, default): must be a number type and not NaN.\n* * positive: must be at least zero\n* * negative: must be zero or lower\n* * aboveZero: must be above zero\n* * belowZero: must be below zero\n* * percentage: must be within 0-1, inclusive\n* * nonZero: can be anything except zero\n* * bipolar: can be -1 to 1, inclusive\n* \n* Alternatives: {@link integerTest} for additional integer check, {@link percentTest} for percentage-range.\n* @param value Value to test\n* @param range Range\n* @param parameterName Name of parameter \n*/\n/**\n* Compares two numbers with a given number of decimal places\n* ```js\n* a: 10.123 b: 10.1 decimals: 1 = true\n* a: 10.123 b: 10.2 decimals: 0 = true\n* a: 10.123 b: 10.14 decimals: 1 = true\n* a: 10.123 b: 10.14 decimals: 2 = false\n* ``\n* @param a \n* @param b \n* @param decimals How many decimals to include\n* @returns \n*/\nconst numberDecimalTest = (a, b, decimals = 3) => {\n\tif (decimals === 0) {\n\t\ta = Math.floor(a);\n\t\tb = Math.floor(b);\n\t\tif (a === b) return {\n\t\t\tsuccess: true,\n\t\t\tvalue: a\n\t\t};\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: `A is not identical to B`\n\t\t};\n\t}\n\tconst mult = Math.pow(10, decimals);\n\tif (Math.floor(a * mult) !== Math.floor(b * mult)) return {\n\t\tsuccess: false,\n\t\terror: `A is not close enough to B. A: ${a} B: ${b} Decimals: ${decimals}`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: a\n\t};\n};\n/**\n* Returns test of `value` being in the range of 0-1.\n* Equiv to `number(value, `percentage`);`\n*\n* This is the same as calling ```number(t, `percentage`)```\n* @param value Value to check\n* @param parameterName Param name for customising exception message\n* @returns\n*/\nconst percentTest = (value, parameterName = `?`, info) => numberTest(value, `percentage`, parameterName, info);\n/**\n* Checks if `value` an integer and meets additional criteria.\n* See {@link numberTest} for guard details, or use that if integer checking is not required.\n*\n* Note:\n* * `bipolar` will mean -1, 0 or 1.\n* * positive: must be at least zero\n* * negative: must be zero or lower\n* * aboveZero: must be above zero\n* * belowZero: must be below zero\n* * percentage: must be within 0-1, inclusive\n* * nonZero: can be anything except zero\n* @param value Value to check\n* @param parameterName Param name for customising exception message\n* @param range Guard specifier.\n*/\nconst integerTest = (value, range = ``, parameterName = `?`) => {\n\treturn resultsCollate(numberTest(value, range, parameterName), () => {\n\t\tif (!Number.isInteger(value)) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is not an integer`\n\t\t};\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tvalue\n\t\t};\n\t});\n};\nconst integerArrayTest = (numbers) => {\n\tfor (const v of numbers) if (Math.abs(v) % 1 !== 0) return {\n\t\tsuccess: false,\n\t\terror: `Value is not an integer: ${v}`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: numbers\n\t};\n};\n/**\n* Returns _true_ if `value` is an integer in number or string form\n* @param value \n* @returns \n*/\nconst isInteger = (value) => {\n\tif (typeof value === `string`) value = Number.parseFloat(value);\n\treturn integerTest(value).success;\n};\nconst numberInclusiveRangeTest = (value, min, max, parameterName = `?`) => {\n\tif (typeof value !== `number`) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not a number type. Got type: '${typeof value}' value: '${JSON.stringify(value)}'`\n\t};\n\tif (Number.isNaN(value)) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not within range ${min}-${max}. Got: NaN`\n\t};\n\tif (Number.isFinite(value)) {\n\t\tif (value < min) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is below range ${min}-${max}. Got: ${value}`\n\t\t};\n\t\telse if (value > max) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is above range ${min}-${max}. Got: ${value}`\n\t\t};\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tvalue\n\t\t};\n\t} else return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not within range ${min}-${max}. Got: infinite`\n\t};\n};\n/**\n* Returns a success if values are equal, considering the set digits of precision (1..21)\n* \n* @param expected Expected value\n* @param got Received value\n* @param precision Precision in terms of decimal digits. 1...21, default 21\n* @param parameterName \n* @returns \n*/\nconst equalWithPrecisionTest = (expected, got, precision = 21, parameterName = `?`) => {\n\tif (expected.toPrecision(precision) === got.toPrecision(precision)) return {\n\t\tsuccess: true,\n\t\tvalue: got\n\t};\n\telse return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is '${got}', expected '${expected}' (using precision: ${precision})`\n\t};\n};\n//#endregion\n//#region src/arrays.ts\n/**\n* Throws an error if parameter is not an array\n* @param value\n* @param parameterName\n*/\nconst arrayTest = (value, parameterName = `?`) => {\n\tif (!Array.isArray(value)) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is expected to be an array'`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n/**\n* Throws if `index` is an invalid array index for `array`, and if\n* `array` itself is not a valid array.\n* @param array\n* @param index\n*/\nconst arrayIndexTest = (array, index, name = `index`) => {\n\treturn resultsCollate(arrayTest(array), integerTest(index, `positive`, name), numberInclusiveRangeTest(index, 0, array.length - 1, name));\n};\n/**\n* Returns true if parameter is an array of strings\n* @param value\n* @returns\n*/\nconst arrayStringsTest = (value) => {\n\tif (!Array.isArray(value)) return {\n\t\tsuccess: false,\n\t\terror: `Value is not an array`\n\t};\n\tif (value.some((v) => typeof v !== `string`)) return {\n\t\tsuccess: false,\n\t\terror: `Contains something not a string`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n//#endregion\n//#region src/empty.ts\nconst nullUndefTest = (value, parameterName = `?`) => {\n\tif (typeof value === `undefined`) return {\n\t\tsuccess: false,\n\t\terror: `${parameterName} param is undefined`\n\t};\n\tif (value === null) return {\n\t\tsuccess: false,\n\t\terror: `${parameterName} param is null`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\nconst isDefined = (argument) => argument !== void 0;\n//#endregion\n//#region src/function.ts\nconst isFunction = (object) => object instanceof Function;\nconst functionTest = (value, parameterName = `?`) => {\n\tif (value === void 0) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is undefined. Expected: function.`\n\t};\n\tif (value === null) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is null. Expected: function.`\n\t};\n\tif (typeof value !== `function`) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is type '${typeof value}'. Expected: function`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n//#endregion\n//#region src/object.ts\n/**\n* Tests_if `value` is a plain object\n* \n* ```js\n* isPlainObject(`text`); // false\n* isPlainObject(document); // false\n* isPlainObject({ hello: `there` }); // true\n* ```\n* @param value \n* @returns \n*/\nconst testPlainObject = (value) => {\n\tif (typeof value !== `object` || value === null) return {\n\t\tsuccess: false,\n\t\terror: `Value is null or not object type`\n\t};\n\tconst prototype = Object.getPrototypeOf(value);\n\tif ((prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value)) return {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n\treturn {\n\t\tsuccess: false,\n\t\terror: `Fancy object`\n\t};\n};\n/**\n* Tests if `value` is primitive value (bigint,number,string or boolean) or plain object\n* @param value \n* @returns \n*/\nconst testPlainObjectOrPrimitive = (value) => {\n\tconst t = typeof value;\n\tif (t === `symbol`) return {\n\t\tsuccess: false,\n\t\terror: `Symbol type`\n\t};\n\tif (t === `function`) return {\n\t\tsuccess: false,\n\t\terror: `Function type`\n\t};\n\tif (t === `bigint`) return {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n\tif (t === `number`) return {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n\tif (t === `string`) return {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n\tif (t === `boolean`) return {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n\treturn testPlainObject(value);\n};\n//#endregion\n//#region src/range.ts\nconst rangeIntegerTest = (v, expected) => {\n\treturn resultsCollate(rangeTest(v, expected), integerArrayTest(v));\n};\n/**\n* Inclusive range 4-6 = 4, 5, 6\n* Exclusive range 4-6 = 5\n* \n* @param numbers \n* @param expected \n* @returns \n*/\nconst rangeTest = (numbers, expected) => {\n\tfor (const v of numbers) {\n\t\tif (expected.minExclusive !== void 0) {\n\t\t\tif (v <= expected.minExclusive) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Value '${v}' must be higher than minExclusive: '${expected.minExclusive}'`\n\t\t\t};\n\t\t}\n\t\tif (expected.minInclusive !== void 0) {\n\t\t\tif (v < expected.minInclusive) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Value '${v}' must be equal or higher than minInclusive: '${expected.minInclusive}'`\n\t\t\t};\n\t\t}\n\t\tif (expected.maxExclusive !== void 0) {\n\t\t\tif (v >= expected.maxExclusive) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Value '${v}' must be less than maxExclusive: '${expected.maxExclusive}'`\n\t\t\t};\n\t\t}\n\t\tif (expected.maxInclusive !== void 0) {\n\t\t\tif (v > expected.maxInclusive) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Value '${v}' must be equal or less than maxInclusive: '${expected.maxInclusive}'`\n\t\t\t};\n\t\t}\n\t}\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: numbers\n\t};\n};\n//#endregion\n//#region src/string.ts\n/**\n* Throws an error if parameter is not an string\n* @param value\n* @param parameterName\n*/\nconst stringTest = (value, range = ``, parameterName = `?`) => {\n\tif (typeof value !== `string`) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName} is not type string. Got: ${typeof value}`\n\t};\n\tswitch (range) {\n\t\tcase `non-empty`:\n\t\t\tif (value.length === 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Param '${parameterName} is empty`\n\t\t\t};\n\t\t\tbreak;\n\t}\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n//#endregion\nexport { IxfxError, arrayIndexTest, arrayStringsTest, arrayTest, equalWithPrecisionTest, errorResult, functionTest, getErrorMessage, ifNaN, integerArrayTest, integerParse, integerTest, isDefined, isFunction, isInteger, isPowerOfTwo, nullUndefTest, numberDecimalTest, numberInclusiveRangeTest, numberTest, percentTest, rangeIntegerTest, rangeTest, resultErrorToString, resultFirstFail_, resultIsError, resultIsOk, resultThrow, resultThrowSingle, resultToError, resultToValue, resultWithFail, resultsCollate, stringTest, testPlainObject, testPlainObjectOrPrimitive, throwIfFailed };\n","//#region ../guards/src/result.ts\nfunction getErrorMessage(ex) {\n\tif (typeof ex === `string`) return ex;\n\tif (ex instanceof Error) return ex.message;\n\treturn String(ex);\n}\n/**\n* Throws an error if any result is a failure.\n* Error message will be the combined from all errors.\n* @param results\n*/\nfunction throwIfFailed(...results) {\n\tconst failed = results.filter((r) => resultIsError(r));\n\tif (failed.length === 0) return;\n\tconst messages = failed.map((f) => resultErrorToString(f));\n\tthrow new Error(messages.join(`, `));\n}\n/**\n* If any of `results` is an error, throws it, otherwise ignored.\n* @param results\n* @returns _true_ or throws\n*/\nfunction resultThrow(...results) {\n\tfor (const r of results) {\n\t\tif (r === void 0) continue;\n\t\tif (typeof r === `boolean`) if (!r) throw IxfxError.fromString(`Guard failed: false result`);\n\t\telse continue;\n\t\tconst rr = typeof r === `object` ? r : r();\n\t\tif (rr === void 0) continue;\n\t\tif (rr.success) continue;\n\t\tthrow resultToError(rr);\n\t}\n\treturn true;\n}\n/**\n* Returns _true_ if `result` is an error\n* @param result\n*/\nfunction resultIsError(result) {\n\tif (typeof result !== `object` || result === null) return false;\n\treturn !result.success;\n}\nvar IxfxError = class IxfxError extends Error {\n\tcause;\n\tconstructor(message, cause) {\n\t\tsuper(message);\n\t\tthis.cause = cause;\n\t}\n\tstatic fromError(error, cause) {\n\t\tconst message = error.message;\n\t\tconst stack = error.stack;\n\t\tconst name = error.name;\n\t\tconst newError = new IxfxError(message, cause);\n\t\tnewError.stack = stack;\n\t\tnewError.name = `IxfxError(${name})`;\n\t\treturn newError;\n\t}\n\tstatic fromString(message, cause) {\n\t\tconst newError = new IxfxError(message, cause);\n\t\tnewError.name = `IxfxError`;\n\t\treturn newError;\n\t}\n};\n/**\n* Gets the result as an Error\n* @param result\n*/\nfunction resultToError(result) {\n\tif (typeof result.error === `string`) return IxfxError.fromString(result.error, result.info);\n\tif (result.error instanceof Error) return IxfxError.fromError(result.error, result.info);\n\treturn IxfxError.fromString(JSON.stringify(result.error), result.info);\n}\n/**\n* Returns the error as a string.\n* @param result\n*/\nfunction resultErrorToString(result) {\n\tif (result.error instanceof Error) return getErrorMessage(result.error);\n\tif (typeof result.error === `string`) return result.error;\n\treturn JSON.stringify(result.error);\n}\n/**\n* Returns first failed result or final value.\n* @param results\n*/\nfunction resultsCollate(...results) {\n\tlet rr;\n\tfor (const r of results) {\n\t\tif (typeof r === `boolean`) {\n\t\t\tif (r) continue;\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Guard failed: false result`\n\t\t\t};\n\t\t}\n\t\trr = typeof r === `object` ? r : r();\n\t\tif (rr === void 0) continue;\n\t\tif (!rr.success) return rr;\n\t}\n\tif (!rr) throw new Error(`No results`);\n\treturn rr;\n}\n//#endregion\n//#region ../guards/src/numbers.ts\n/**\n* Checks if `t` is not a number or within specified range.\n* Returns `[false, reason:string]` if invalid or `[true]` if valid.\n* \n* Alternatives: {@link integerTest} for additional integer check, {@link percentTest} for percentage-range.\n*\n* * (empty, default): must be a number type and not NaN.\n* * finite: must be a number, not NaN and not infinite\n* * positive: must be at least zero\n* * negative: must be zero or lower\n* * aboveZero: must be above zero\n* * belowZero: must be below zero\n* * percentage: must be within 0-1, inclusive\n* * nonZero: can be anything except zero\n* * bipolar: can be -1 to 1, inclusive\n* @param value Value to check\n* @param parameterName Name of parameter (for more helpful exception messages)\n* @param range Range to enforce\n* @returns\n*/\nconst numberTest = (value, range = ``, parameterName = `?`, info) => {\n\tif (value === null) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is null`,\n\t\tinfo\n\t};\n\tif (typeof value === `undefined`) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is undefined`,\n\t\tinfo\n\t};\n\tif (Number.isNaN(value)) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is NaN`,\n\t\tinfo\n\t};\n\tif (typeof value !== `number`) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is not a number (${JSON.stringify(value)})`,\n\t\tinfo\n\t};\n\tswitch (range) {\n\t\tcase `finite`:\n\t\t\tif (!Number.isFinite(value)) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName} must be finite (Got: ${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `positive`:\n\t\t\tif (value < 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be at least zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `negative`:\n\t\t\tif (value > 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be zero or lower (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `aboveZero`:\n\t\t\tif (value <= 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be above zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `belowZero`:\n\t\t\tif (value >= 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be below zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `percentage`:\n\t\t\tif (value > 1 || value < 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be in percentage range (0 to 1). (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `nonZero`:\n\t\t\tif (value === 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must non-zero. (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `bipolar`:\n\t\t\tif (value > 1 || value < -1) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be in bipolar percentage range (-1 to 1). (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t}\n\treturn {\n\t\tsuccess: true,\n\t\tvalue,\n\t\tinfo\n\t};\n};\n/**\n* Checks if `value` an integer and meets additional criteria.\n* See {@link numberTest} for guard details, or use that if integer checking is not required.\n*\n* Note:\n* * `bipolar` will mean -1, 0 or 1.\n* * positive: must be at least zero\n* * negative: must be zero or lower\n* * aboveZero: must be above zero\n* * belowZero: must be below zero\n* * percentage: must be within 0-1, inclusive\n* * nonZero: can be anything except zero\n* @param value Value to check\n* @param parameterName Param name for customising exception message\n* @param range Guard specifier.\n*/\nconst integerTest = (value, range = ``, parameterName = `?`) => {\n\treturn resultsCollate(numberTest(value, range, parameterName), () => {\n\t\tif (!Number.isInteger(value)) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is not an integer`\n\t\t};\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tvalue\n\t\t};\n\t});\n};\nconst numberInclusiveRangeTest = (value, min, max, parameterName = `?`) => {\n\tif (typeof value !== `number`) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not a number type. Got type: '${typeof value}' value: '${JSON.stringify(value)}'`\n\t};\n\tif (Number.isNaN(value)) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not within range ${min}-${max}. Got: NaN`\n\t};\n\tif (Number.isFinite(value)) {\n\t\tif (value < min) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is below range ${min}-${max}. Got: ${value}`\n\t\t};\n\t\telse if (value > max) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is above range ${min}-${max}. Got: ${value}`\n\t\t};\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tvalue\n\t\t};\n\t} else return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not within range ${min}-${max}. Got: infinite`\n\t};\n};\n//#endregion\n//#region ../guards/src/arrays.ts\n/**\n* Throws an error if parameter is not an array\n* @param value\n* @param parameterName\n*/\nconst arrayTest = (value, parameterName = `?`) => {\n\tif (!Array.isArray(value)) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is expected to be an array'`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n/**\n* Throws if `index` is an invalid array index for `array`, and if\n* `array` itself is not a valid array.\n* @param array\n* @param index\n*/\nconst arrayIndexTest = (array, index, name = `index`) => {\n\treturn resultsCollate(arrayTest(array), integerTest(index, `positive`, name), numberInclusiveRangeTest(index, 0, array.length - 1, name));\n};\n//#endregion\n//#region ../guards/src/function.ts\nconst functionTest = (value, parameterName = `?`) => {\n\tif (value === void 0) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is undefined. Expected: function.`\n\t};\n\tif (value === null) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is null. Expected: function.`\n\t};\n\tif (typeof value !== `function`) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is type '${typeof value}'. Expected: function`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n//#endregion\n//#region src/at-wrap.ts\n/**\n* Similar to Javascript's in-built Array.at function, but allows offsets\n* to wrap.\n* \n* @remarks\n* ```js\n* const test = [1,2,3,4,5,6];\n* atWrap(0); // 1\n* atWrap(-1); // 6\n* atWrap(-6); // 1\n* ```\n* \n* These values would return _undefined_ using Array.at since its beyond\n* the length of the array\n* ```js\n* atWrap(6); // 1\n* atWrap(-7); // 6\n* ```\n* @param array Array\n* @param index Index\n* @returns \n*/\nconst atWrap = (array, index) => {\n\tresultThrow(numberTest(index, ``, `index`));\n\tif (!Array.isArray(array)) throw new Error(`Param 'array' is not an array`);\n\tindex = index % array.length;\n\treturn array.at(index);\n};\n//#endregion\n//#region src/chunks.ts\n/**\n* Return `array` broken up into chunks of `size` values\n*\n* ```js\n* chunks([1,2,3,4,5,6,7,8,9,10], 3);\n* // Yields: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]\n* ```\n* @param array\n* @param size\n* @returns\n*/\nfunction chunks(array, size) {\n\tthrowIfFailed(integerTest(size, \"aboveZero\", `size`), arrayTest(array, `array`));\n\tconst output = [];\n\tfor (let index = 0; index < array.length; index += size) output.push(array.slice(index, index + size));\n\treturn output;\n}\n//#endregion\n//#region src/compare-to.ts\n/**\n* Yields the result of comparing a value with a sibling.\n*\n* ```js\n* const data = [ 1, 2, 4, 8, 16 ];\n* // Compare values with its previous sibling (-1)\n* // Since -1 is the offset, the first A and B values will be 2 and 1,\n* // then 4 and 2, etc.\n* compareTo(data, -1, (a, b) => b-a)];\n* // Yields: -1, -2, -4, -8\n* ```\n*\n* Note that one less value is yielded compared to the input array.\n*\n* You can just as well go forward as well:\n* ```js\n* const data = [ 1, 2, 4, 8, 16 ];\n* // Compare values with its next-next sibling (2)\n* // With an offset of 2, the first A and B values will be 1 and 4, then\n* // 8 and 2 etc.\n* // then 4 and 2, etc.\n* compareTo(data, 2, (a, b) => b-a)];\n* // Yields: 3, 6, 12\n* ```\n* @param data\n* @param offset\n* @param fn\n*/\nfunction* compareTo(data, offset, fn) {\n\tif (offset === 0) throw new TypeError(`Offset cannot be 0.`);\n\tif (offset < 0) {\n\t\toffset = Math.abs(offset);\n\t\tfor (let i = offset; i < data.length; i++) yield fn(data[i], data[i - offset]);\n\t} else for (let i = 0; i < data.length - offset; i++) yield fn(data[i], data[i + offset]);\n}\n//#endregion\n//#region src/util/to-string.ts\n/**\n* A default converter to string that uses JSON.stringify if its an object, or the thing itself if it's a string\n*/\nconst toStringDefault = (itemToMakeStringFor) => typeof itemToMakeStringFor === `string` ? itemToMakeStringFor : JSON.stringify(itemToMakeStringFor);\n//#endregion\n//#region src/util/is-equal.ts\n/**\n* If input is a string, it is returned.\n* Otherwise, it returns the result of JSON.stringify() with fields ordered.\n* \n* This allows for more consistent comparisons when object field orders are different but values the same.\n* @param itemToMakeStringFor \n* @returns \n*/\n/**\n* Default comparer function is equiv to checking `a === b`.\n* Use {@link isEqualValueDefault} to compare by value, via comparing JSON string representation.\n*/\nconst isEqualDefault = (a, b) => a === b;\n/**\n* Comparer returns true if string representation of `a` and `b` are equal.\n* Use {@link isEqualDefault} to compare using === semantics\n* Uses `toStringDefault` to generate a string representation (via `JSON.stringify`).\n* \n* Returns _false_ if the ordering of fields is different, even though values are identical:\n* ```js\n* isEqualValueDefault({ a: 10, b: 20}, { b: 20, a: 10 }); // false\n* ```\n* \n* Use {@link isEqualValueIgnoreOrder} to ignore order (with an overhead of additional processing).\n* ```js\n* isEqualValueIgnoreOrder({ a: 10, b: 20}, { b: 20, a: 10 }); // true\n* ```\n* \n* Use {@link isEqualValuePartial} to partially match `b` against `a`.\n* @returns True if the contents of `a` and `b` are equal\n*/\nconst isEqualValueDefault = (a, b) => {\n\tif (a === b) return true;\n\treturn toStringDefault(a) === toStringDefault(b);\n};\n//#endregion\n//#region src/contains.ts\n/**\n* Returns _true_ if all value in `needles` is contained in `haystack`, \n* by default using === semantics. \n* \n* ```js\n* const a = ['apples','oranges','pears','mandarins'];\n* const b = ['pears', 'apples'];\n* contains(a, b); // True\n*\n* const c = ['pears', 'bananas'];\n* contains(a, b); // False ('bananas' does not exist in a)\n* ```\n* \n* If `needles` is empty, `contains` will return true.\n* \n* Compare by value using ixfx's `isEqualValueDefault`, or a custom function of your own\n* ```js\n* contains(a, b, isEqualValueDefault);\n* contains(a, b, (valueA, valueV) => {\n* return valueA.name === valueB.name\n* })\n* ```\n* @throws {TypeError} If parameters are not valid\n* @param haystack Array to search\n* @param needles Things to look for\n* @param eq Optional function to compare equality. By default uses === semantics\n*/\nconst contains = (haystack, needles, eq = isEqualDefault) => {\n\tresultThrow(arrayTest(haystack, `haystack`), arrayTest(needles, `needles`), functionTest(eq, `eq`));\n\tfor (const needle of needles) {\n\t\tlet found = false;\n\t\tfor (const element of haystack) if (eq(needle, element)) {\n\t\t\tfound = true;\n\t\t\tbreak;\n\t\t}\n\t\tif (!found) return false;\n\t}\n\treturn true;\n};\n/**\n* Returns _true_ if array contains duplicate values.\n*\n* ```js\n* containsDuplicateValues(['a','b','a']); // True\n* containsDuplicateValues([\n* { name: 'Apple' },\n* { name: 'Apple' }\n* ]); // True\n* ```\n* \n* Uses JSON.toString() by default to compare values.\n* \n* See also:\n* * {@link unique}: Get unique set of values in an array\n* * {@link containsDuplicateInstances}: Compare based on reference, rather than value\n* * {@link containsDuplicateValues}: Returns _true_ if every item in array is the same\n* @param data Array to examine\n* @param keyFunction Function to generate key string for object, uses JSON.stringify by default.\n* @returns\n*/\nconst containsDuplicateValues = (data, keyFunction = toStringDefault) => {\n\tif (typeof data !== `object`) throw new Error(`Param 'data' is expected to be an Iterable. Got type: ${typeof data}`);\n\tconst set = /* @__PURE__ */ new Set();\n\tfor (const v of data) {\n\t\tconst string_ = keyFunction(v);\n\t\tif (set.has(string_)) return true;\n\t\tset.add(string_);\n\t}\n\treturn false;\n};\n/**\n* Returns _true_ if array contains duplicate instances based on `===` equality checking.\n* \n* ```js\n* const o1 = { hello: `there` };\n* const o2 = { hello: `there` };\n* containsDuplicateInstances([ o1, o2 ]); // False\n* containsDuplicateInstances([ o1, o1 ]); // True\n* ```\n* \n* Primitive values are compared by value:\n* ```js\n* containsDuplicateInstances([ 1, 2, 1 ]); // True\n* containsDuplicateInstances([ `a`, `b`, `a` ]); // True\n* ```\n* \n* Use {@link containsDuplicateValues} if you'd rather compare by value.\n* @param array \n* @throws {TypeError} If `array` parameter is not an array\n* @returns \n*/\nconst containsDuplicateInstances = (array) => {\n\tresultThrow(arrayTest(array, `array`));\n\tfor (let index = 0; index < array.length; index++) for (let x = 0; x < array.length; x++) {\n\t\tif (index === x) continue;\n\t\tif (array[index] === array[x]) return true;\n\t}\n\treturn false;\n};\n//#endregion\n//#region src/cycle.ts\n/**\n* Returns a function that cycles through the contents of an array. By default starts at index 0.\n* \n* ```js\n* const c = arrayCycle([`apples`, `oranges`, `pears`]);\n* c.current; // `apples`\n* c.next(); // `oranges`\n* c.next(); // `pears`\n* c.next(); // `apples`\n* c.prev(); // `pears`\n* ```\n* \n* You can select an item by index or value:\n* ```\n* c.select(1); // `oranges`\n* c.select(`pears`); // `pears`\n* ```\n* \n* Other features:\n* ```js\n* c.current; // Current value\n* c.toArray(); // Copy of array being cycled over\n* ```\n* \n* Additional info:\n* * Selecting by value uses === semantics.\n* * Works with a copy of input array\n* @param options Array to cycle over \n* @returns \n*/\nconst cycle = (options) => {\n\tthrowIfFailed(arrayTest(options, `options`));\n\tconst opts = [...options];\n\tlet index = 0;\n\tconst next = () => {\n\t\tindex++;\n\t\tif (index === opts.length) index = 0;\n\t\treturn value();\n\t};\n\tconst prev = () => {\n\t\tindex--;\n\t\tif (index === -1) index = opts.length - 1;\n\t\treturn value();\n\t};\n\tconst value = () => {\n\t\treturn opts.at(index);\n\t};\n\tconst select = (indexOrValue) => {\n\t\tif (typeof indexOrValue === `number`) index = indexOrValue;\n\t\telse {\n\t\t\tconst found = opts.indexOf(indexOrValue);\n\t\t\tif (found === -1) throw new Error(`Could not find value`);\n\t\t\tindex = found;\n\t\t}\n\t};\n\tconst toArray = () => [...opts];\n\treturn {\n\t\ttoArray,\n\t\tnext,\n\t\tprev,\n\t\tget current() {\n\t\t\treturn value();\n\t\t},\n\t\tselect\n\t};\n};\n//#endregion\n//#region src/ensure-length.ts\n/**\n* Returns a copy of an array with specified length - padded or truncated as needed.\n*\n* If the input array is too short, it will be expanded based on the `expand` strategy:\n* - 'undefined': fill with _undefined_ (default)\n* - 'repeat': repeat array elements, starting from position 0\n* - 'first': repeat with first element from `data`\n* - 'last': repeat with last element from `data`\n*\n* Truncate:\n* ```js\n* ensureLength([1,2,3], 2); // [1,2]\n* ```\n* \n* Padded:\n* ```js\n* ensureLength([1,2,3], 5, `undefined`); // [1,2,3,undefined,undefined]\n* ensureLength([1,2,3], 5, `repeat`); // [1,2,3,1,2]\n* ensureLength([1,2,3], 5, `first`); // [1,2,3,1,1]\n* ensureLength([1,2,3], 5, `last`); // [1,2,3,3,3]\n* ```\n* @param data Input array to expand\n* @param length Desired length\n* @param expandStrategy Expand strategy\n* @param truncateStrategy Truncation strategy. By default removes from end ('from-end')\n* @typeParam V Type of array\n*/\nfunction ensureLength(data, length, expandStrategy = `undefined`, truncateStrategy = `from-end`) {\n\tif (data === void 0) throw new Error(`Data undefined`);\n\tif (!Array.isArray(data)) throw new Error(`data is not an array`);\n\tif (data.length === length) return [...data];\n\tif (data.length > length) if (truncateStrategy === `from-end`) return data.slice(0, length);\n\telse return data.slice(data.length - length);\n\tconst d = [...data];\n\tconst add = length - d.length;\n\tfor (let index = 0; index < add; index++) switch (expandStrategy) {\n\t\tcase `undefined`:\n\t\t\td.push(void 0);\n\t\t\tbreak;\n\t\tcase `repeat`:\n\t\t\td.push(data[index % data.length]);\n\t\t\tbreak;\n\t\tcase `first`:\n\t\t\td.push(data[0]);\n\t\t\tbreak;\n\t\tcase `last`:\n\t\t\td.push(data.at(-1));\n\t\t\tbreak;\n\t}\n\treturn d;\n}\n//#endregion\n//#region src/intersection.ts\n/**\n* Returns the _intersection_ of two arrays: the elements that are in common. Duplicates are removed in the process.\n* \n* By default compares based on a string representation of object.\n* \n* ```js\n* intersection([1, 2, 3], [2, 4, 6]); // returns [2]\n* ```\n* \n* To compare object instances:\n* ```js\n* intersection(arrayA, arrayB, (a,b) => a === b)\n* ```\n* \n* To use a custom string representation, eg, to only compare based on 'name' property of objects:\n* ```js\n* intersection(arrayA, arrayB, (v) => v.name)\n* ```\n* \n* See also: \n* * `uniqueByKey`/`uniqueByComparer`: Get unique items across one or more arrays, including within the array\n* @param arrayA First array\n* @param arrayB Second array\n* @param comparerOrKey Comparer or key-generating function \n* @returns \n*/\nfunction intersection(arrayA, arrayB, comparerOrKey) {\n\tif (arrayA.length === 0) return arrayB;\n\tif (arrayB.length === 0) return arrayA;\n\tcomparerOrKey ??= toStringDefault;\n\tif (typeof comparerOrKey(arrayA[0], arrayB[0]) === `string`) return intersectionByKeyImpl(arrayA, arrayB, comparerOrKey);\n\telse return intersectionByComparerImpl(arrayA, arrayB, comparerOrKey);\n}\nconst intersectionByComparerImpl = (arrayA, arrayB, equality) => {\n\treturn arrayA.filter((valueFromA) => arrayB.some((valueFromB) => equality(valueFromA, valueFromB)));\n};\nconst intersectionByKeyImpl = (arrayA, arrayB, key) => {\n\tconst aKeys = /* @__PURE__ */ new Set();\n\tconst result = [];\n\tfor (const v of arrayA) aKeys.add(key(v));\n\tconst bUsed = /* @__PURE__ */ new Set();\n\tfor (const v of arrayB) {\n\t\tconst bKey = key(v);\n\t\tif (bUsed.has(bKey)) continue;\n\t\tif (aKeys.has(bKey)) {\n\t\t\tresult.push(v);\n\t\t\tbUsed.add(bKey);\n\t\t}\n\t}\n\treturn result;\n};\n//#endregion\n//#region src/equality.ts\n/**\n* Returns _true_ if the two arrays have the same length, and have the same items at the same indexes. \n* \n* By default uses === semantics for equality checking.\n* \n* Use {@link isEqualIgnoreOrder} if you don't care whether items are in same order.\n* \n* ```js\n* isEqual([ 1, 2, 3], [ 1, 2, 3 ]); // true\n* isEqual([ 1, 2, 3], [ 3, 2, 1 ]); // false\n* ```\n* \n* Compare by value instead:\n* ```js\n* // Eg. compare objects based on their 'name' property\n* isEqual(a, b, v => v.name);\n* ```\n* \n* @param arrayA \n* @param arrayB \n* @param comparerOrKey Function to compare values or produce a string key\n* @throws {TypeError} If inputs are not arrays\n*/\nfunction isEqual(arrayA, arrayB, comparerOrKey = isEqualDefault) {\n\tresultThrow(arrayTest(arrayA, `arrayA`), arrayTest(arrayB, `arrayB`), functionTest(comparerOrKey));\n\tif (arrayA.length !== arrayB.length) return false;\n\tif (typeof comparerOrKey(arrayA[0], arrayB[0]) === `string`) {\n\t\tconst c = comparerOrKey;\n\t\tfor (let indexA = 0; indexA < arrayA.length; indexA++) if (c(arrayA[indexA]) !== c(arrayB[indexA])) return false;\n\t} else {\n\t\tconst c = comparerOrKey;\n\t\tfor (let indexA = 0; indexA < arrayA.length; indexA++) if (!c(arrayA[indexA], arrayB[indexA])) return false;\n\t}\n\treturn true;\n}\n/**\n* Returns _true_ if arrays contain same value items, regardless of order. Will return _false_ if\n* arrays are of different length.\n* \n* By default uses === semantics to compare items. Pass in a comparer function or key generating function otherwise:\n* ```js\n* isEqualIgnoreOrder(arrayA, arrayB, (v) => v.name);\n* ```\n* \n* @param arrayA Array\n* @param arrayB Array\n* @param comparerOrKey Function to compare objects or produce a string representation. Defaults to {@link isEqualDefault}\n* @throws {TypeError} If input parameters are not correct\n*/\nfunction isEqualIgnoreOrder(arrayA, arrayB, comparerOrKey = isEqualDefault) {\n\tresultThrow(arrayTest(arrayA, `arrayA`), arrayTest(arrayB, `arrayB`), functionTest(comparerOrKey));\n\tif (arrayA.length !== arrayB.length) return false;\n\treturn intersection(arrayA, arrayB, comparerOrKey).length === arrayA.length;\n}\n/**\n* Returns _true_ if all values in the array are the same. Uses value-based equality checking by default.\n* \n* @example Using default equality function\n* ```js\n* const a1 = [ 10, 10, 10 ];\n* containsIdenticalValues(a1); // True\n*\n* const a2 = [ { name:`Jane` }, { name:`John` } ];\n* containsIdenticalValues(a2); // True, even though object references are different\n* ```\n*\n* If we want to compare by value for objects that aren't readily\n* converted to JSON, you need to provide a function:\n*\n* ```js\n* containsIdenticalValues(someArray, (a, b) => {\n* return (a.eventType === b.eventType);\n* });\n* ```\n*\n* Returns _true_ if `array` is empty.\n* @param array Array\n* @param equality Equality checker. Uses string-conversion checking by default\n* @throws {TypeError} If input is not an array\n* @returns\n*/\nconst containsIdenticalValues = (array, equality) => {\n\tif (!Array.isArray(array)) throw new TypeError(`Param 'array' is not an array.`);\n\tif (array.length === 0) return true;\n\tconst eq = equality ?? isEqualValueDefault;\n\tconst a = array[0];\n\tif (array.some((v) => !eq(a, v))) return false;\n\treturn true;\n};\n//#endregion\n//#region src/filter.ts\n/**\n* Like Array.findIndex but with optional `startAt` and `length` parameters to limit the search to a specific section of the array.\n*\n* ```js\n* const data = [\"red\",\"blue\",\"red\",\"blue\"]\n* data.findIndex(v => v === `red`); // 0 - finds first match\n* findIndex(data, v => v === `red`, 1); // 2 - finds first match after start index of 1\n* ```\n*\n* Use {@link findIndexReverse} to search backwards through the array.\n* @param array\n* @param predicate\n* @param startInclusive\n* @param endExclusive End index (exclusive). By default, uses array.length\n*/\nfunction findIndex(array, predicate, startInclusive, endExclusive) {\n\tconst _start = startInclusive ?? 0;\n\tconst _end = endExclusive ?? array.length;\n\tif (_start >= array.length) throw new RangeError(`Start ${_start} is out of bounds for array of length ${array.length}`);\n\tif (_end > array.length) throw new RangeError(`End ${_end} is out of bounds for array of length ${array.length}`);\n\tif (_start > _end) throw new RangeError(`Start ${_start} is greater than end ${_end}`);\n\tfor (let i = _start; i < _end; i++) if (predicate(array[i], i, array)) return i;\n\treturn -1;\n}\n/**\n* Returns a matching index, starting at index `start` and working backwards up until `end` (both inclusive).\n* ```\n* const data = [\"red\",\"blue\",\"red\",\"blue\",\"red\"];\n* findIndexReverse(data, v=> v === `red`); // 4\n* findIndexReverse(data, v=> v === `red`, 3); // 2\n* findIndexReverse(data, v=> v === `red`, 2); // 2\n* ```\n* @param array\n* @param predicate\n* @param startInclusive\n* @param endInclusive\n* @returns\n*/\nfunction findIndexReverse(array, predicate, startInclusive, endInclusive) {\n\tconst _start = startInclusive ?? array.length - 1;\n\tconst _end = endInclusive ?? 0;\n\tif (_start >= array.length) throw new RangeError(`Start ${_start} is out of bounds for array of length ${array.length}`);\n\tif (_end < 0) throw new RangeError(`End ${_end} is out of bounds`);\n\tif (_start < _end) throw new RangeError(`Start ${_start} is less than end ${_end}`);\n\tfor (let i = _start; i >= _end; i--) if (predicate(array[i], i, array)) return i;\n\treturn -1;\n}\n/**\n* Enumerates the index of all array values that match `predicate`.\n*\n* ```js\n* const data = [`red`,`blue`,`red`,`blue`,`red`];\n* for (const index of filterWithIndex(data, v=> v === `red`)) {\n* // Yields 0, 2, 4\n* }\n* ```\n* @param array\n* @param predicate\n*/\nfunction* filterWithIndex(array, predicate) {\n\tfor (let i = 0; i < array.length; i++) if (predicate(array[i], i, array)) yield i;\n}\n/**\n* Returns two separate arrays of everything that `filter` returns _true_,\n* and everything it returns _false_ on.\n*\n* Same idea as the in-built Array.filter, but that only returns values for one case.\n*\n* ```js\n* const [ matching, nonMatching ] = filterAB(data, v => v.enabled);\n* // `matching` is a list of items from `data` where .enabled is true\n* // `nonMatching` is a list of items from `data` where .enabled is false\n* ```\n* @param data Array of data to filter\n* @param filter Function which returns _true_ to add items to the A list, or _false_ for items to add to the B list\n* @returns Array of two elements. The first is items that match `filter`, the second is items that do not.\n*/\nfunction filterAB(data, filter) {\n\tconst a = [];\n\tconst b = [];\n\tfor (const datum of data) if (filter(datum)) a.push(datum);\n\telse b.push(datum);\n\treturn [a, b];\n}\n/**\n* Yields elements from `array` that match a given `predicate`, and moreover are between\n* the given `startIndex` (inclusive) and `endIndex` (exclusive).\n*\n* While this can be done with in the in-built `array.filter` function, it will\n* needlessly iterate through the whole array. It also avoids another alternative\n* of slicing the array before using `filter`.\n*\n* ```js\n* // Return 'registered' people between and including array indexes 5-10\n* const filtered = [...filterBetween(people, person => person.registered, 5, 10)];\n* ```\n* @param array Array to filter\n* @param predicate Filter function\n* @param startIndex Start index (defaults to 0)\n* @param endIndex End index (by default runs until end)\n*/\nfunction* filterBetween(array, predicate, startIndex, endIndex) {\n\tresultThrow(arrayTest(array, `array`));\n\tif (typeof startIndex === `undefined`) startIndex = 0;\n\tif (typeof endIndex === `undefined`) endIndex = array.length;\n\tresultThrow(arrayIndexTest(array, startIndex, `startIndex`));\n\tresultThrow(arrayIndexTest(array, endIndex - 1, `endIndex`));\n\tfor (let index = startIndex; index < endIndex; index++) if (predicate(array[index], index, array)) yield array[index];\n}\n//#endregion\n//#region src/flatten.ts\n/**\n* Returns a 'flattened' copy of array, un-nesting arrays one level\n* ```js\n* flatten([1, [2, 3], [[4]] ]);\n* // Yields: [ 1, 2, 3, [4]];\n* ```\n* @param array\n* @returns\n*/\nconst flatten = (array) => [...array].flat();\n//#endregion\n//#region src/for-each.ts\n/**\n* Returns the array.map() output, or a value if `array`\n* is not an array or empty.\n* \n* ```js\n* mapWithEmptyFallback([1,2,3], v => v+2, 100); // Yields: [3,4,5]\n* mapWithEmptyFallback([], v=>v+2, 100); // Yields: [100]\n* mapWithEmptyFallback({}, v=>v+2, [100]); // Yields: [100]\n* ```\n* \n* If the fallback value is an array, it is returned as an\n* array if needed. If it's a single value, it is wrapped as an array.\n* @param array Array of values\n* @param fn Function to use for mapping values\n* @param fallback Fallback single value or array of values\n* @returns \n*/\nconst mapWithEmptyFallback = (array, fn, fallback) => {\n\tif (typeof array !== `object` || !Array.isArray(array) || array.length === 0) {\n\t\tif (Array.isArray(fallback)) return fallback;\n\t\treturn [fallback];\n\t}\n\treturn array.map(fn);\n};\n//#endregion\n//#region src/frequency.ts\n/**\n* Computes the frequency of values by a grouping function.\n*\n* ```js\n* const data = [1,2,3,4,5,6,7,8,9,10];\n* // Returns 'odd' or 'even' for an input value\n*\n* const groupBy = v => v % 2 === 0 ? `even`:`odd`;\n*\n* FrequencyByGroup.fromArray(data, groupBy);\n* // Yields map with:\n* // key: 'even', value: 5\n* // key: 'odd', value: 5\n* ```\n*\n* Or for example, group by the value itself:\n* ```js\n* const data = [1,2,3,1,2,0];\n* const groupBy = v => v.toString();\n* FrequencyByGroup.fromArray(data, groupBy);\n* // \"1\" = 2, \"2\" = 2, \"3\" = 1, \"0\" = 1\n* ```\n* @param groupBy\n* @param data\n*/\nvar FrequencyByGroup = class FrequencyByGroup {\n\t#store = /* @__PURE__ */ new Map();\n\t#groupBy;\n\t#total = 0;\n\tconstructor(groupBy = (v) => v.toString()) {\n\t\tthis.#groupBy = groupBy;\n\t}\n\tadd(data) {\n\t\tif (!Array.isArray(data)) throw new TypeError(`Param 'array' is expected to be an array. Got type: '${typeof data}'`);\n\t\tfor (const value of data) {\n\t\t\tconst group = this.#groupBy(value);\n\t\t\tif (typeof group !== `string` && typeof group !== `number`) throw new TypeError(`groupBy function is expected to return type string or number. Got type: '${typeof group}' for value: '${value}'`);\n\t\t\tconst groupValue = (this.#store.get(group) ?? 0) + 1;\n\t\t\tthis.#total++;\n\t\t\tthis.#store.set(group, groupValue);\n\t\t}\n\t}\n\t/**\n\t* Creates a new FrequencyByGroup instance, adds data to it and returns the instance.\n\t* If you just want the computed frequencies, consider using {@link entriesFromArray}.\n\t* @param data\n\t* @param groupBy\n\t* @returns FrequencyGroup instance with data added\n\t*/\n\tstatic fromArray(data, groupBy) {\n\t\tconst instance = new FrequencyByGroup(groupBy);\n\t\tinstance.add(data);\n\t\treturn instance;\n\t}\n\t/**\n\t* Computes the frequency of `data`, yielding results as entries consisting of the key and frequency.\n\t* ```js\n\t* const v = [...FrequencyByGroup.entriesFromArray([1, 2, 3, 1, 2, 3, 0, 1, 1, 1, 4], v => v.toString())];\n\t* // Yields: [ [\"1\", 5], [\"2\", 2], [\"3\", 2], [\"0\", 1], [\"4\", 1] ]\n\t* ```\n\t*\n\t* It's a generator, so you can also use it like this:\n\t* ```js\n\t* for (const [key,freq] of FrequencyByGroup.entriesFromArray(data, v => v.toString())) {\n\t* console.log(key, freq);// Logs key and frequency for each group\n\t* }\n\t* ```\n\t* @param data\n\t* @param groupBy\n\t* @returns Iterator over entries\n\t*/\n\tstatic *entriesFromArray(data, groupBy) {\n\t\treturn yield* FrequencyByGroup.fromArray(data, groupBy).entries();\n\t}\n\t/**\n\t* Returns the relative frequency for a group, or _undefined_ if not found.\n\t* @param group\n\t* @returns Relative frequency or _undefined_ if not found\n\t*/\n\tgetRelative(group) {\n\t\tconst freq = this.#store.get(group);\n\t\tif (typeof freq === `undefined`) return void 0;\n\t\treturn freq / this.#total;\n\t}\n\t/**\n\t* Returns _true_ if group was found.\n\t* @param group\n\t* @returns _True_ if group was found\n\t*/\n\thas(group) {\n\t\treturn this.#store.has(group);\n\t}\n\t/**\n\t* Gets the frequency for this group, or _undefined_ if the group does not exist\n\t* @param group\n\t* @returns Frequency for this group, or _undefined_ if the group does not exist\n\t*/\n\tget(group) {\n\t\treturn this.#store.get(group);\n\t}\n\t/**\n\t* Returns an iterator over the entries, ie `[group, frequency]` pairs.\n\t* Use {@link entriesRelative} to get the relative frequency instead of the absolute frequency.\n\t* @returns Iterator\n\t*/\n\tentries() {\n\t\treturn this.#store.entries();\n\t}\n\t/**\n\t* Returns an iterator over the entries, ie `[group, relativeFrequency]` pairs.\n\t* Use {@link entries} to get the absolute frequency instead.\n\t* @returns Iterator\n\t*/\n\t*entriesRelative() {\n\t\tfor (const [group, freq] of this.#store.entries()) yield [group, freq / this.#total];\n\t}\n\t/**\n\t* Returns an iterator over keys (ie. groups).\n\t*/\n\tkeys() {\n\t\treturn this.#store.keys();\n\t}\n\t/**\n\t* Returns an iterator over values (ie. absolute frequencies)\n\t* @returns\n\t*/\n\tvalues() {\n\t\treturn this.#store.values();\n\t}\n\t/**\n\t* Gets the average frequency across all groups.\n\t* @returns Average frequency\n\t*/\n\taverageFrequency() {\n\t\tlet total = 0;\n\t\tfor (const freq of this.#store.values()) total += freq;\n\t\treturn total / this.#store.size;\n\t}\n};\n//#endregion\n//#region src/group-by.ts\n/**\n* Groups data by a function `grouper`, returning data as a map with string\n* keys and array values. Multiple values can be assigned to the same group.\n*\n* `grouper` must yield a string designated group for a given item.\n*\n* @example\n* ```js\n* const data = [\n* { age: 39, city: `London` },\n* { age: 14, city: `Copenhagen` },\n* { age: 23, city: `Stockholm` },\n* { age: 56, city: `London` }\n* ];\n*\n* // Whatever the function returns will be the designated group\n* // for an item\n* const map = Arrays.groupBy(data, item => item.city);\n* ```\n*\n* This yields a Map with keys London, Stockholm and Copenhagen, and the corresponding values.\n*\n* ```\n* London: [{ age: 39, city: `London` }, { age: 56, city: `London` }]\n* Stockhom: [{ age: 23, city: `Stockholm` }]\n* Copenhagen: [{ age: 14, city: `Copenhagen` }]\n* ```\n* @param array Array to group\n* @param grouper Function that returns a key for a given item\n* @typeParam K Type of key to group by. Typically string.\n* @typeParam V Type of values\n* @returns Map\n*/\nconst groupBy = (array, grouper) => {\n\tconst map = /* @__PURE__ */ new Map();\n\tfor (const a of array) {\n\t\tconst key = grouper(a);\n\t\tlet existing = map.get(key);\n\t\tif (!existing) {\n\t\t\texisting = [];\n\t\t\tmap.set(key, existing);\n\t\t}\n\t\texisting.push(a);\n\t}\n\treturn map;\n};\n//#endregion\n//#region src/insert-at.ts\n/**\n* Inserts `values` at position `index`, shuffling remaining\n* items further down and returning changed result.\n* \n* Does not modify the input array.\n* \n* ```js\n* const data = [ 1, 2, 3 ]\n* \n* // Inserts 20,30,40 at index 1\n* Arrays.insertAt(data, 1, 20, 30, 40);\n* \n* // Yields: 1, 20, 30, 40, 2, 3\n* ```\n* @param data \n* @param index \n* @param values \n* @returns \n*/\nconst insertAt = (data, index, ...values) => {\n\tthrowIfFailed(arrayTest(data, `data`), arrayIndexTest(data, index, `index`));\n\tif (index === data.length - 1) return [...data, ...values];\n\tif (index === 0) return [...values, ...data];\n\treturn [\n\t\t...data.slice(0, index),\n\t\t...values,\n\t\t...data.slice(index)\n\t];\n};\n//#endregion\n//#region src/interleave.ts\n/**\n* Returns an interleaving of two or more arrays. All arrays must be the same length.\n*\n* ```js\n* const a = [`a`, `b`, `c`];\n* const b = [`1`, `2`, `3`];\n* const c = Arrays.interleave(a, b);\n* // Yields:\n* // [`a`, `1`, `b`, `2`, `c`, `3`]\n* ```\n* @param arrays\n* @returns\n*/\nconst interleave = (...arrays) => {\n\tif (arrays.some((a) => !Array.isArray(a))) throw new Error(`All parameters must be an array`);\n\tconst lengths = arrays.map((a) => a.length);\n\tif (!containsIdenticalValues(lengths)) throw new Error(`Arrays must be of same length`);\n\tconst returnValue = [];\n\tconst length = lengths[0];\n\tfor (let index = 0; index < length; index++) for (const array of arrays) returnValue.push(array[index]);\n\treturn returnValue;\n};\n//#endregion\n//#region src/merge-by-key.ts\n/**\n* Merges arrays left to right, using the provided\n* `reconcile` function to choose a winner when keys overlap.\n*\n* There's also Core.Maps.mergeByKey if the input data is in Map form.\n*\n* For example, if we have the array A:\n* [`A-1`, `A-2`, `A-3`]\n*\n* And array B:\n* [`B-1`, `B-2`, `B-4`]\n*\n* And with the key function:\n* ```js\n* // Make a key for value based on last char\n* const keyFn = (v) => v.substr(-1, 1);\n* ```\n*\n* If they are merged with the reconile function:\n* ```js\n* const reconcile = (a, b) => b.replace(`-`, `!`);\n* const output = mergeByKey(keyFn, reconcile, arrayA, arrayB);\n* ```\n*\n* The final result will be:\n*\n* [`B!1`, `B!2`, `A-3`, `B-4`]\n*\n* In this toy example, it's obvious how the reconciler transforms\n* data where the keys overlap. For the keys that do not overlap -\n* 3 and 4 in this example - they are copied unaltered.\n*\n* A practical use for `mergeByKey` has been in smoothing keypoints\n* from a TensorFlow pose. In this case, we want to smooth new keypoints\n* with older keypoints. But if a keypoint is not present, for it to be\n* passed through.\n*\n* @param keyFunction Function to generate a unique key for data\n* @param reconcile Returns value to decide 'winner' when keys conflict.\n* @param arrays Arrays of data to merge\n*/\nconst mergeByKey = (keyFunction, reconcile, ...arrays) => {\n\tconst result = /* @__PURE__ */ new Map();\n\tfor (const m of arrays) for (const mv of m) {\n\t\tif (mv === void 0) continue;\n\t\tconst mk = keyFunction(mv);\n\t\tlet v = result.get(mk);\n\t\tv = v ? reconcile(v, mv) : mv;\n\t\tresult.set(mk, v);\n\t}\n\treturn [...result.values()];\n};\n//#endregion\n//#region src/moving-window.ts\n/**\n* Creates a moving window\n* \n* ```js\n* // Create a moving window of 3 samples\n* const window = movingWindow(3);\n* \n* window(1); // [ 1 ]\n* window(2); // [ 1, 2 ]\n* window(3); // [ 1, 2, 3 ]\n* window(4); // [ 2, 3, 4 ]\n* ```\n* \n* 'reject' option allows values to be discarded:\n* ```js\n* // Reject all NaN values\n* const window = movingWindow({ samples: 3, reject: (v) => Number.isNaN(v) });\n* ```\n* \n* 'allow' is similar, but is applied after 'reject' (if provided). Instead, values\n* must pass _true_\n* \n* If a reject/disallow is triggered, the current state of the queue is returned.\n* \n* @param samplesOrOptions\n* @returns \n*/\nconst movingWindow = (samplesOrOptions) => movingWindowWithContext(samplesOrOptions).seen;\n/**\n* As {@link movingWindow} but also allows access to context, namely you \n* can access the window at any time without adding to it.\n* \n* ```js\n* const window = movingWindowWithContext(3);\n* window.seen(1); // [ 1 ]\n* window.data; // [ 1 ]\n* ```\n* @param samplesOrOptions \n* @returns \n*/\nconst movingWindowWithContext = (samplesOrOptions) => {\n\tconst q = [];\n\tconst reject = typeof samplesOrOptions === `object` ? samplesOrOptions.reject : void 0;\n\tconst allow = typeof samplesOrOptions === `object` ? samplesOrOptions.allow : void 0;\n\tconst samples = typeof samplesOrOptions === `number` ? samplesOrOptions : samplesOrOptions.samples;\n\tconst seen = (value) => {\n\t\tif (reject) {\n\t\t\tif (reject(value)) return q;\n\t\t}\n\t\tif (allow) {\n\t\t\tif (!allow(value)) return q;\n\t\t}\n\t\tq.push(value);\n\t\twhile (q.length > samples) q.shift();\n\t\treturn q;\n\t};\n\treturn {\n\t\tseen,\n\t\tget data() {\n\t\t\treturn [...q];\n\t\t}\n\t};\n};\n//#endregion\n//#region src/pairwise.ts\n/**\n* Yields pairs made up of overlapping items from the input array.\n* \n* Throws an error if there are less than two entries.\n* \n* ```js\n* pairwise([1, 2, 3, 4, 5]);\n* Yields:\n* [ [1,2], [2,3], [3,4], [4,5] ]\n* ```\n* @param values \n*/\nfunction* pairwise(values) {\n\tresultThrow(arrayTest(values, `values`));\n\tif (values.length < 2) throw new Error(`Array needs to have at least two entries. Length: ${values.length}`);\n\tfor (let index = 1; index < values.length; index++) yield [values[index - 1], values[index]];\n}\n/**\n* Reduces in a pairwise fashion.\n*\n* Eg, if we have input array of [1, 2, 3, 4, 5], the\n* `reducer` fn will run with 1,2 as parameters, then 2,3, then 3,4 etc.\n* ```js\n* const values = [1, 2, 3, 4, 5]\n* reducePairwise(values, (acc, a, b) => {\n* return acc + (b - a);\n* }, 0);\n* ```\n*\n* If input array has less than two elements, the initial value is returned.\n*\n* ```js\n* const reducer = (acc:string, a:string, b:string) => acc + `[${a}-${b}]`;\n* const result = reducePairwise(`a b c d e f g`.split(` `), reducer, `!`);\n* Yields: `![a-b][b-c][c-d][d-e][e-f][f-g]`\n* ```\n* @param array\n* @param reducer\n* @param initial\n* @returns\n*/\nconst pairwiseReduce = (array, reducer, initial) => {\n\tresultThrow(arrayTest(array, `arr`));\n\tif (array.length < 2) return initial;\n\tfor (let index = 0; index < array.length - 1; index++) initial = reducer(initial, array[index], array[index + 1]);\n\treturn initial;\n};\n//#endregion\n//#region src/random.ts\n/**\n* Returns a shuffled copy of the input array.\n* @example\n* ```js\n* const d = [1, 2, 3, 4];\n* const s = shuffle(d);\n* // d: [1, 2, 3, 4], s: [3, 1, 2, 4]\n* ```\n* \n* It can be useful to randomly access each item from an array exactly once:\n* ```js\n* for (const value of shuffle(inputArray)) {\n* // Do something with the value...\n* }\n* ```\n* \n* @throws {TypeError} If `array` is not an array and `rand` is not a function\n* @param dataToShuffle Input array\n* @param rand Random generator. `Math.random` by default.\n* @returns Copy with items moved around randomly\n* @typeParam V - Type of array items\n*/\nconst shuffle = (dataToShuffle, rand = Math.random) => {\n\tresultThrow(arrayTest(dataToShuffle, `dataToShuffle`), functionTest(rand, `rand`));\n\tconst array = [...dataToShuffle];\n\tfor (let index = array.length - 1; index > 0; index--) {\n\t\tconst randomIndex = Math.floor(rand() * (index + 1));\n\t\t[array[index], array[randomIndex]] = [array[randomIndex], array[index]];\n\t}\n\treturn array;\n};\n/**\n* Returns a random element of an array\n*\n* ```js\n* const v = [`blue`, `red`, `orange`];\n* randomElement(v); // Yields `blue`, `red` or `orange`\n* ```\n*\n* Note that repeated calls might yield the same value\n* multiple times. If you want to random unique values, consider using {@link shuffle}.\n* \n* See also:\n* * {@link randomIndex} if you want a random index rather than value.\n* \n* @throws {TypeError} If `array` is not an array and `rand` is not a function\n* @param array\n* @param rand Random generator. `Math.random` by default.\n* @returns\n*/\nconst randomElement = (array, rand = Math.random) => {\n\tresultThrow(arrayTest(array, `array`), functionTest(rand, `rand`));\n\treturn array[Math.floor(rand() * array.length)];\n};\n/**\n* Returns a random array index.\n*\n* ```js\n* const v = [`blue`, `red`, `orange`];\n* randomIndex(v); // Yields 0, 1 or 2\n* ```\n*\n* Use {@link randomElement} if you want a value from `array`, not index.\n*\n* @throws {TypeError} If `array` is not an array and `rand` is not a function\n* @param array Array\n* @param rand Random generator. `Math.random` by default.\n* @returns\n*/\nconst randomIndex = (array, rand = Math.random) => {\n\tresultThrow(arrayTest(array, `array`), functionTest(rand, `rand`));\n\treturn Math.floor(rand() * array.length);\n};\n//#endregion\n//#region src/remove.ts\n/**\n* Removes an element at `index` index from `data`, returning the resulting array without modifying the original.\n*\n* ```js\n* const v = [ 100, 20, 50 ];\n* const vv = Arrays.remove(2);\n*\n* Yields:\n* v: [ 100, 20, 50 ]\n* vv: [ 100, 20 ]\n* ```\n*\n* Consider {@link without} if you want to remove an item by value.\n*\n* Throws an exception if `index` is outside the range of `data` array.\n* @param data Input array\n* @param index Index to remove\n* @typeParam V Type of array\n* @returns\n*/\nfunction remove(data, index) {\n\tif (!Array.isArray(data)) throw new TypeError(`Parameter 'data' should be an array`);\n\tresultThrow(arrayIndexTest(data, index, `index`));\n\treturn [...data.slice(0, index), ...data.slice(index + 1)];\n}\n/**\n* Removes items from `input` array that match `predicate`.\n* A modified array is returned along with the number of items removed.\n*\n* If `predicate` matches no items, a new array will still be returned, and the removed count will be 0.\n*\n* @param input\n* @param predicate\n* @returns\n*/\nfunction removeByFilter(input, predicate) {\n\tif (!Array.isArray(input)) throw new TypeError(`Parameter 'input' should be an array`);\n\tif (typeof predicate !== `function`) throw new TypeError(`Parameter 'prediate' should be a function. Got type: ${typeof predicate}`);\n\tconst count = input.length;\n\tconst changed = input.filter((v) => !predicate(v));\n\treturn [changed, count - changed.length];\n}\n//#endregion\n//#region src/sample.ts\n/**\n* Samples values from an array. \n* \n* If `amount` is less or equal to 1, it's treated as a percentage to sample.\n* Otherwise it's treated as every _n_th value to sample.\n*\n* @example \n* By percentage - get half of the items\n* ```\n* const list = [1,2,3,4,5,6,7,8,9,10];\n* const sub = Arrays.sample(list, 0.5);\n* // Yields: [2, 4, 6, 8, 10]\n* ```\n*\n* @example\n* By steps - every third value\n* ```\n* const list = [1,2,3,4,5,6,7,8,9,10];\n* const sub = Arrays.sample(list, 3);\n* // Yields:\n* // [3, 6, 9]\n* ```\n* @param array Array to sample\n* @param amount Amount, given as a percentage (0..1) or the number of interval (ie 3 for every third item)\n* @returns\n*/\nconst sample = (array, amount) => {\n\tif (!Array.isArray(array)) throw new TypeError(`Param 'array' is not actually an array. Got type: ${typeof array}`);\n\tlet subsampleSteps = 1;\n\tif (amount <= 1) {\n\t\tconst numberOfItems = array.length * amount;\n\t\tsubsampleSteps = Math.round(array.length / numberOfItems);\n\t} else subsampleSteps = amount;\n\tresultThrow(integerTest(subsampleSteps, `positive`, `amount`));\n\tif (subsampleSteps > array.length - 1) throw new Error(`Subsample steps exceeds array length`);\n\tconst r = [];\n\tfor (let index = subsampleSteps - 1; index < array.length; index += subsampleSteps) r.push(array[index]);\n\treturn r;\n};\n//#endregion\n//#region src/sort.ts\n/**\n* Sorts an array of objects in ascending order\n* by the given property name, assuming it is a number.\n*\n* ```js\n* const data = [\n* { size: 10, colour: `red` },\n* { size: 20, colour: `blue` },\n* { size: 5, colour: `pink` }\n* ];\n* const sorted = Arrays.sortByNumericProperty(data, `size`);\n*\n* Yields items ascending order:\n* [ { size: 5, colour: `pink` }, { size: 10, colour: `red` }, { size: 20, colour: `blue` } ]\n* ```\n* @param data\n* @param propertyName\n* @throws {TypeError} If data is not an array\n*/\nconst sortByNumericProperty = (data, propertyName) => [...data].sort((a, b) => {\n\tresultThrow(arrayTest(data, `data`));\n\tconst av = a[propertyName];\n\tconst bv = b[propertyName];\n\tif (av < bv) return -1;\n\tif (av > bv) return 1;\n\treturn 0;\n});\n/**\n* Sorts an array of objects by some named property.\n* \n* ```js\n* const data = [\n* { size: 10, colour: `red` },\n* { size: 20, colour: `blue` },\n* { size: 5, colour: `pink` }\n* ];\n* sortByProperty(data, `colour`);\n* \n* Yields [\n* { size: 20, colour: `blue` },\n* { size: 5, colour: `pink` }\n* { size: 10, colour: `red` },\n* ]\n* ```\n* \n* You can also provide a custom comparer that is passed property values.\n* This function should return 0 if values are equal, 1 if `a > b` and -1 if `a < b`.\n* @param data \n* @param propertyName \n* @throws {TypeError} If data is not an array\n* @returns \n*/\nconst sortByProperty = (data, propertyName, comparer) => [...data].sort((a, b) => {\n\tresultThrow(arrayTest(data, `data`));\n\tconst av = a[propertyName];\n\tconst bv = b[propertyName];\n\tif (comparer === void 0) {\n\t\tif (av < bv) return -1;\n\t\tif (av > bv) return 1;\n\t\treturn 0;\n\t} else return comparer(av, bv);\n});\n//#endregion\n//#region src/clamp.ts\n/**\n* Clamps integer `v` between 0 (inclusive) and array length or length (exclusive).\n* Returns value then will always be at least zero, and a valid array index.\n*\n* @example Usage\n* ```js\n* // Array of length 4\n* const myArray = [`a`, `b`, `c`, `d`];\n* clampIndex(0, myArray); // 0\n* clampIndex(5, 3); // 2\n* ```\n*\n* Throws an error if `v` is not an integer.\n*\n* For some data it makes sense that data might 'wrap around' if it exceeds the\n* range. For example rotation angle. Consider using {@link wrap} for this.\n*\n* @param v Value to clamp (must be an interger)\n* @param arrayOrLength Array, or length of bounds (must be an integer)\n* @returns Clamped value, minimum will be 0, maximum will be one less than `length`.\n*/\nfunction clampIndex(v, arrayOrLength) {\n\tif (!Number.isInteger(v)) throw new TypeError(`v parameter must be an integer (${v})`);\n\tconst length = Array.isArray(arrayOrLength) ? arrayOrLength.length : arrayOrLength;\n\tif (!Number.isInteger(length)) throw new TypeError(`length parameter must be an integer (${length}, ${typeof length})`);\n\tv = Math.round(v);\n\tif (v < 0) return 0;\n\tif (v >= length) return length - 1;\n\treturn v;\n}\n//#endregion\n//#region src/index-wrap.ts\n/**\n* Returns a valid index within the given range.\n*\n* Logic:\n* 'brickwall': if limit is reached, return limit\n* 'bounce': if limit is reached, continue stepping in opposite direction (default)\n* 'cycle': if limit is reached, wrap around to the other side and continue\n*\n* Examples:\n* ```js\n* // Within range\n* indexWrap(3, 2, 5) // 3\n* indexWrap(5, 2, 5) // 5\n*\n* // Bounce logic (default)\n* indexWrap(1, 2, 5, `bounce`) // 3\n* indexWrap(0, 2, 5, `bounce`) // 4\n* indexWrap(6, 2, 5, `bounce`) // 4\n*\n* // Cycle logic\n* indexWrap(1, 2, 5, `cycle`) // 4\n* indexWrap(0, 2, 5, `cycle`) // 3\n* indexWrap(6, 2, 5, `cycle`) // 3\n* ```\n*\n* @param index\n* @param startIndex\n* @param endIndex\n* @param wrapLogic\n*/\nfunction indexWrap(index, startIndex, endIndex, wrapLogic, iterations = 0) {\n\tif (typeof wrapLogic === `undefined`) throw new TypeError(`Param 'wrapLogic' is required.`);\n\tif (startIndex > endIndex) throw new TypeError(`startIndex must be less than or equal to endIndex.`);\n\tif (index >= startIndex && index <= endIndex) return {\n\t\tindex,\n\t\titerations\n\t};\n\tif (wrapLogic === `brickwall`) {\n\t\tif (index < startIndex) return {\n\t\t\tindex: startIndex,\n\t\t\titerations\n\t\t};\n\t\treturn {\n\t\t\tindex: endIndex,\n\t\t\titerations\n\t\t};\n\t}\n\tif (wrapLogic === `bounce`) if (index < startIndex) return indexWrap(startIndex - index + startIndex, startIndex, endIndex, `bounce`, iterations + 1);\n\telse return indexWrap(endIndex - (index - endIndex), startIndex, endIndex, `bounce`, iterations + 1);\n\tif (wrapLogic === `cycle`) if (index < startIndex) return indexWrap(endIndex - (startIndex - index), startIndex, endIndex, `cycle`, iterations + 1);\n\telse return indexWrap(startIndex + (index - endIndex), startIndex, endIndex, `cycle`, iterations + 1);\n\tthrow new TypeError(`Invalid wrapLogic: ${wrapLogic}`);\n}\n//#endregion\n//#region src/util/random.ts\n/**\n* Returns a random integer based on a chance probability.\n*\n* If `chance` is less than 0, `minInclusive` is returned.\n* If `chance` is greater than 1, `maxInclusive` is returned.\n*\n* Otherwise, we compute a random number to see if it's less than `chance`. It this is the case,\n* we return a random integer in the inclusive min-max range. Eg. a chance of 0.9 means that 90% of the time\n* (assuming even random distribution) we will return a random integer.\n*\n* If the random number is greater than `chance`, then we return `minInclusive`.\n* @param chance\n* @param maxInclusive Maximum value\n* @param minInclusive Minimum value. By default 0.\n* @param randomSource Random source, by default Math.random\n* @returns\n*/\nfunction randomChanceInteger(chance, maxInclusive, minInclusive = 0, randomSource = Math.random) {\n\tif (minInclusive > maxInclusive) throw new Error(`minInclusive (${minInclusive}) cannot be greater than maxInclusive (${maxInclusive})`);\n\tif (minInclusive === maxInclusive) throw new Error(`minInclusive (${minInclusive}) cannot be equal to maxInclusive (${maxInclusive})`);\n\tif (chance <= 0) return minInclusive;\n\tif (chance > 1) return maxInclusive;\n\tif (randomSource() <= chance) return randomInteger(maxInclusive, minInclusive, randomSource);\n\treturn minInclusive;\n}\nfunction randomInteger(maxInclusive, minInclusive = 0, randomSource = Math.random) {\n\treturn Math.floor(randomSource() * (maxInclusive - minInclusive + 1)) + minInclusive;\n}\n//#endregion\n//#region src/traverse.ts\n/**\n* Given an input step state, take a step and return the new state.\n* This is a lower-level function, you probably want to use {@link arrayIndexStepper} instead.\n* @param state Current step state\n* @param options How to step\n* @param context Context in which we are stepping\n* @returns New step state\n*/\nfunction step(state, options, context) {\n\tif (context.startIndex > context.endIndex) throw new TypeError(`startIndex must be less than or equal to endIndex. startIndex: ${context.startIndex} endIndex: ${context.endIndex}`);\n\tif (context.startIndex === context.endIndex) throw new TypeError(`startIndex cannot be the same as endIndex (${context.startIndex}).`);\n\tlet incrementing = state.incrementing;\n\tconst delta = incrementing ? options.steps : -options.steps;\n\tlet index = state.index + delta;\n\tlet done = false;\n\tconst wrapLogic = options.loop === `none` ? `brickwall` : `bounce`;\n\tif (options.debug ?? false) console.log(`Step: index: ${state.index} incrementing: ${state.incrementing} delta: ${delta} index after step: ${index} start: ${context.startIndex} end: ${context.endIndex} loop: ${options.loop}`);\n\tconst r = indexWrap(index, context.startIndex, context.endIndex, wrapLogic);\n\tindex = r.index;\n\tif (r.iterations > 0) {\n\t\tif (r.iterations % 2 === 1) incrementing = !state.incrementing;\n\t}\n\tif (options.loop === `none` && (index === context.endIndex || index === context.startIndex)) done = true;\n\treturn {\n\t\tindex,\n\t\tincrementing,\n\t\tdone\n\t};\n}\n/**\n* Creates a generator to step through array indices.\n*\n* Supports moving forward/backward through an array, looping, 'drunken walk', and random step lengths.\n*\n* ```js\n* const data [ `a`, `b`, `c`, `d`, `e` ];\n*\n* // Step one by one through each index\n* for (const index of arrayIndexStepper({step:1, loop:`none`}, data)) {\n* console.log(`index: ${index} value: ${data[index]}`);\n* }\n* ```\n*\n* More examples:\n* ```js\n* // A generator that never ends, going back and forth between start and end\n* arrayIndexStepper({ steps: 1, loop: `pingpong` }, data);\n* // As above, but when we hit the end/start, repeat that index\n* arrayIndexStepper({ steps: 1, loop: `pingpong`, repeatLoopedIndex:true }, data);\n*\n* // Move backwards. from the end, through the indicies, jumping by two\n* arrayIndexStepper({ steps: 2, forward:false }, data);\n*\n* ```\n* @param optionsP\n* @param context\n* @returns Iterator over array indicies\n*/\nfunction arrayIndexStepper(optionsP, context) {\n\tconst options = {\n\t\tsteps: 1,\n\t\tloop: `none`,\n\t\trepeatLoopedIndex: false,\n\t\tdebug: false,\n\t\tforward: true,\n\t\trandomDirectionFlip: 0,\n\t\trandomChanceSteps: 0,\n\t\trandomStepsMax: 2,\n\t\t...optionsP\n\t};\n\tconst range = {\n\t\tstartIndex: 0,\n\t\tendIndex: context.data.length - 1,\n\t\trandomSource: Math.random,\n\t\t...context\n\t};\n\tconst fn = function* (overrideOptions = {}, overrideContext = {}) {\n\t\tconst _options = {\n\t\t\t...options,\n\t\t\t...overrideOptions\n\t\t};\n\t\tconst data = overrideContext.data ?? context.data;\n\t\tif (!Array.isArray(data)) throw new TypeError(`Param 'data' must be an array. Got: ${typeof data}`);\n\t\tif (data.length === 0) return;\n\t\tif (data.length === 1) {\n\t\t\tyield 0;\n\t\t\treturn;\n\t\t}\n\t\tconst startIndex = clampIndex(overrideContext.startIndex ?? range.startIndex, data);\n\t\tconst endIndex = clampIndex(overrideContext.endIndex ?? range.endIndex, data);\n\t\tif (startIndex === endIndex) throw new Error(`startIndex and endIndex cannot be the same. startIndex: ${startIndex} endIndex: ${endIndex}. Data length: ${data.length} Override: ${JSON.stringify(overrideContext)}`);\n\t\tconst _context = {\n\t\t\tstartIndex,\n\t\t\tendIndex\n\t\t};\n\t\tlet state = {\n\t\t\tindex: _options.forward ? startIndex : endIndex,\n\t\t\tincrementing: _options.forward,\n\t\t\tdone: false\n\t\t};\n\t\tlet lastIndex = NaN;\n\t\tconst debug = _options.debug ?? false;\n\t\twhile (!state.done) {\n\t\t\tyield state.index;\n\t\t\tif (_options.randomDirectionFlip > 0 && range.randomSource() < _options.randomDirectionFlip) state.incrementing = !state.incrementing;\n\t\t\tif (_options.randomChanceSteps > 0) {\n\t\t\t\tconst randomSteps = randomChanceInteger(_options.randomChanceSteps, _options.randomStepsMax, _options.steps, range.randomSource);\n\t\t\t\tstate = step(state, {\n\t\t\t\t\t..._options,\n\t\t\t\t\tsteps: randomSteps\n\t\t\t\t}, _context);\n\t\t\t} else state = step(state, _options, _context);\n\t\t\tif (debug) console.log(`index: ${state.index} state: ${JSON.stringify(state)}`);\n\t\t\tif (state.done && state.index !== lastIndex) yield state.index;\n\t\t\tif (_options.repeatLoopedIndex && _options.loop !== `none`) {\n\t\t\t\tif (state.index === startIndex || state.index === endIndex) yield state.index;\n\t\t\t}\n\t\t\tlastIndex = state.index;\n\t\t}\n\t};\n\treturn fn;\n}\n//#endregion\n//#region src/unique.ts\n/**\n* Combines the values of one or more arrays, removing duplicates.\n* \n* By default compares values based on a JSON string representation.\n* \n* @param arrays Array (or array of arrays) to examine\n* @param toString Function to convert values to a string for comparison purposes. By default uses JSON formatting.\n* @returns\n*/\nfunction unique(arrays, comparer) {\n\tconst flattened = arrays.flat(10);\n\tif (flattened.length <= 1) return flattened;\n\tcomparer ??= toStringDefault;\n\tif (typeof comparer(flattened[0], flattened[1]) === `string`) return uniqueByKeyImpl(flattened, comparer);\n\telse return uniqueByComparerImpl(flattened, comparer);\n}\nconst uniqueByKeyImpl = (flattened, toString) => {\n\tconst matching = /* @__PURE__ */ new Set();\n\tconst t = [];\n\tfor (const a of flattened) {\n\t\tconst stringRepresentation = toString(a);\n\t\tif (matching.has(stringRepresentation)) continue;\n\t\tmatching.add(stringRepresentation);\n\t\tt.push(a);\n\t}\n\treturn t;\n};\nconst uniqueByComparerImpl = (flattened, comparer) => {\n\tconst t = [];\n\tconst contains = (v) => {\n\t\tfor (const tValue of t) if (comparer(tValue, v)) return true;\n\t\treturn false;\n\t};\n\tfor (const v of flattened) if (!contains(v)) t.push(v);\n\treturn t;\n};\n//#endregion\n//#region src/until.ts\n/**\n* Yields all items in the input array for as long as `predicate` returns true.\n*\n* `predicate` yields arrays of `[stop:boolean, acc:A]`. The first value\n* is _true_ when the iteration should stop, and the `acc` is the accumulated value.\n* This allows `until` to be used to carry over some state from item to item.\n*\n* @example Stop when we hit an item with value of 3\n* ```js\n* const v = [...until([1,2,3,4,5], v => v === 3];\n* // [ 1, 2 ]\n* ```\n*\n* @example Stop when we reach a total, using 0 as initial value\n* ```js\n* // Stop when accumulated value reaches 6\n* const v = Arrays.until[1,2,3,4,5], (v, acc) => [acc >= 7, v+acc], 0);\n* // [1, 2, 3]\n* ```\n* @param data\n* @param predicate\n*/\nfunction* until(data, predicate, initial) {\n\tlet total = initial;\n\tfor (const datum of data) {\n\t\tconst r = predicate(datum, total);\n\t\tif (typeof r === `boolean`) {\n\t\t\tif (r) break;\n\t\t} else {\n\t\t\tconst [stop, accumulator] = r;\n\t\t\tif (stop) break;\n\t\t\ttotal = accumulator;\n\t\t}\n\t\tyield datum;\n\t}\n}\n/**\n* Returns up to `count` items from the generator. If the generator finishes before `count` items are returned, then only the available items are returned.\n* @param generator\n* @param count\n*/\nfunction takeFromGenerator(generator, count) {\n\tconst result = [];\n\tfor (let i = 0; i < count; i++) {\n\t\tconst { value, done } = generator.next();\n\t\tif (done) break;\n\t\tresult.push(value);\n\t}\n\tif (result.length > count) throw new Error(`Bug: takeFromGenerator returned more items than requested.`);\n\treturn result;\n}\n//#endregion\n//#region src/without.ts\n/**\n* Returns a copy of an input array with _undefined_ values removed.\n* @param data \n* @returns \n*/\nconst withoutUndefined = (data) => {\n\tresultThrow(arrayTest(data, `sourceArray`));\n\treturn data.filter((v) => v !== void 0);\n};\n/**\n* Returns an array with value(s) omitted. \n* \n* If value is not found, result will be a copy of input.\n* Value checking is completed via the provided `comparer` function.\n* By default checking whether `a === b`. To compare based on value, use the `isEqualValueDefault` comparer.\n*\n* @example\n* ```js\n* const data = [100, 20, 40];\n* const filtered = without(data, 20); // [100, 40]\n* ```\n*\n* @example Using value-based comparison\n* ```js\n* const data = [{ name: `Alice` }, { name:`Sam` }];\n*\n* // This wouldn't work as expected, because the default comparer uses instance,\n* // not value:\n* without(data, { name: `Alice` });\n*\n* // So instead we can use a value comparer:\n* without(data, { name:`Alice` }, isEqualValueDefault);\n* ```\n*\n* @example Use a function\n* ```js\n* const data = [ { name: `Alice` }, { name:`Sam` }];\n* without(data, { name:`ALICE` }, (a, b) => {\n* return (a.name.toLowerCase() === b.name.toLowerCase());\n* });\n* ```\n*\n* Consider {@link remove} to remove an item by index.\n*\n* @typeParam V - Type of array items\n* @param sourceArray Source array\n* @param toRemove Value(s) to remove\n* @param comparer Comparison function. If not provided `isEqualDefault` is used, which compares using `===`\n* @throws {TypeError} If `sourceArray` is not an array, or compare function is not a function\n* @return Copy of array without value.\n*/\nconst without = (sourceArray, toRemove, comparer = isEqualDefault) => {\n\tresultThrow(arrayTest(sourceArray, `sourceArray`), functionTest(comparer, `comparer`));\n\tif (Array.isArray(toRemove)) {\n\t\tconst returnArray = [];\n\t\tfor (const source of sourceArray) if (!toRemove.some((v) => comparer(source, v))) returnArray.push(source);\n\t\treturn returnArray;\n\t} else return sourceArray.filter((v) => !comparer(v, toRemove));\n};\n//#endregion\n//#region src/zip.ts\n/**\n* Zip combines the elements of two or more arrays based on their index.\n*\n* ```js\n* const a = [ 1, 2, 3 ];\n* const b = [ `red`, `blue`, `green` ];\n*\n* const c = Arrays.zip(a, b);\n* // Yields:\n* // [\n* // [ 1, `red` ],\n* // [ 2, `blue` ],\n* // [ 3, `green` ]\n* // ]\n* ```\n*\n* Typically the arrays you zip together are all about the same logical item. Eg, in the above example\n* perhaps `a` is size and `b` is colour. So thing #1 (at array index 0) is a red thing of size 1. Before\n* zipping we'd access it by `a[0]` and `b[0]`. After zipping, we'd have c[0], which is array of [1, `red`].\n* @param arrays\n* @returns Zipped together array\n* @throws {TypeError} If any of the parameters are not arrays\n* @throws {Error} If the arrays are not all of the same length\n*/\nconst zip = (...arrays) => {\n\tif (arrays.some((a) => !Array.isArray(a))) throw new TypeError(`All parameters must be an array`);\n\tconst lengths = arrays.map((a) => a.length);\n\tif (!containsIdenticalValues(lengths)) throw new Error(`Arrays must be of same length`);\n\tconst returnValue = [];\n\tconst length = lengths[0];\n\tfor (let index = 0; index < length; index++) returnValue.push(arrays.map((a) => a[index]));\n\treturn returnValue;\n};\n//#endregion\nexport { FrequencyByGroup, arrayIndexStepper, atWrap, chunks, compareTo, contains, containsDuplicateInstances, containsDuplicateValues, containsIdenticalValues, cycle, ensureLength, filterAB, filterBetween, filterWithIndex, findIndex, findIndexReverse, flatten, groupBy, insertAt, interleave, intersection, isEqual, isEqualIgnoreOrder, mapWithEmptyFallback, mergeByKey, movingWindow, movingWindowWithContext, pairwise, pairwiseReduce, randomElement, randomIndex, remove, removeByFilter, sample, shuffle, sortByNumericProperty, sortByProperty, step, takeFromGenerator, unique, until, without, withoutUndefined, zip };\n","import { t as __exportAll } from \"./chunk-pbuEa-1d.js\";\nimport { movingWindowWithContext, zip } from \"@ixfx/arrays\";\nimport { integerTest, numberTest, resultThrow } from \"@ixfx/guards\";\n//#region src/apply-to-values.ts\n/**\n* Apples `fn` to every key of `obj` which is numeric.\n* ```js\n* const o = {\n* name: 'john',\n* x: 10,\n* y: 20\n* };\n* const o2 = applyToValues(o, (v) => v * 2);\n* \n* // Yields: { name: 'john', x: 20, y: 40 }\n* ```\n* @param object \n* @param apply \n* @returns \n*/\nconst applyToValues = (object, apply) => {\n\tconst o = { ...object };\n\tfor (const [key, value] of Object.entries(object)) if (typeof value === `number`) o[key] = apply(value);\n\telse o[key] = value;\n\treturn o;\n};\n//#endregion\n//#region src/numeric-arrays.ts\n/**\n* Applies a function `fn` to the elements of an array, weighting them based on their relative position.\n*\n* ```js\n* // Six items\n* weight([1,1,1,1,1,1], Modulation.gaussian());\n*\n* // Yields:\n* // [0.02, 0.244, 0.85, 0.85, 0.244, 0.02]\n* ```\n*\n* `fn` is expected to map (0..1) => (0..1), such as an easing function. The input to the\n* `fn` is the relative position of an element. Thus the first element will be 0, the middle 0.5 and so on.\n* The output of `fn` is then multiplied by the original value.\n*\n* In the below example (which is also the default if `fn` is not specified), the relative position is\n* how values are weighted:\n*\n* ```js\n* weight([1,1,1,1,1,1], (relativePos) => relativePos);\n* // Yields:\n* // [0, 0.2, 0.4, 0.6, 0.8, 1]\n* ```\n*\n* Throws TypeError if `data` is not an array or for any element not a number.\n* @param data Array of numbers\n* @param fn Returns a weighting based on the given relative position. If unspecified, `(x) => x` is used.\n*/\nconst weight = (data, fn) => {\n\tif (!Array.isArray(data)) throw new TypeError(`Param 'data' is expected to be an array. Got type: ${typeof data}`);\n\tconst weightingFunction = fn ?? ((x) => x);\n\treturn data.map((value, index) => {\n\t\tif (typeof value !== `number`) throw new TypeError(`Param 'data' contains non-number at index: '${index}'. Type: '${typeof value}' value: '${value}'`);\n\t\tconst relativePos = index / (data.length - 1);\n\t\tconst weightForPosition = weightingFunction(relativePos);\n\t\tif (typeof weightForPosition !== `number`) throw new TypeError(`Weighting function returned type '${typeof weightForPosition}' rather than number for input: '${relativePos}'`);\n\t\treturn value * weightForPosition;\n\t});\n};\n/**\n* Returns an array of all valid numbers from `data`\n*\n* @param data\n* @returns\n*/\nconst validNumbers = (data) => data.filter((d) => typeof d === `number` && !Number.isNaN(d));\n/**\n* Returns the dot product of arbitrary-sized arrays. Assumed they are of the same length.\n* @param values\n* @param nonNumber What to do if array contains an invalid number. Error: throw an exception, 'treat-as-zero' use as 0 instead, 'ignore', let math run with invalid number\n* @returns\n*/\nconst dotProduct = (values, nonNumber = `ignore`) => {\n\tlet r = 0;\n\tconst length = values[0].length;\n\tfor (let index = 0; index < length; index++) {\n\t\tlet t = 0;\n\t\tfor (const [p, value] of values.entries()) {\n\t\t\tlet v = value[index];\n\t\t\tif (Number.isNaN(v) || !Number.isFinite(v)) {\n\t\t\t\tif (nonNumber === `treat-as-zero`) v = 0;\n\t\t\t\telse if (nonNumber === `error`) throw new TypeError(`Invalid number at index ${index},${p}`);\n\t\t\t}\n\t\t\tif (p === 0) t = v;\n\t\t\telse t *= v;\n\t\t}\n\t\tr += t;\n\t}\n\treturn r;\n};\n/**\n* Calculates the average of all numbers in an array.\n* Array items which aren't a valid number are ignored and do not factor into averaging.\n*\n* Use {@link numberArrayCompute} if you want min, max and total as well.\n*\n* @example\n* ```js\n* // Average of a list\n* const avg = Numbers.average([1, 1.4, 0.9, 0.1]);\n*\n* // Average of a variable\n* const data = [100,200];\n* Numbers.average(data);\n* ```\n*\n* @see {@link averageWeighted} To weight items based on position in array\n* @param data Data to average.\n* @returns Average of array\n*/\nconst average = (data) => {\n\tif (typeof data !== `object`) throw new Error(`Param 'data' should be an array. Got: ${typeof data}`);\n\tif (!Array.isArray(data)) throw new TypeError(`Param 'data' is not an array`);\n\tconst valid = validNumbers(data);\n\treturn valid.reduce((accumulator, v) => accumulator + v, 0) / valid.length;\n};\n/**\n* Returns the minimum number out of `data`.\n* Undefined and non-numbers are silently ignored.\n*\n* ```js\n* Numbers.min([10, 20, 0]); // Yields 0\n* ```\n* @param data\n* @returns Minimum number\n*/\nconst min = (data) => Math.min(...validNumbers(data));\n/**\n* Returns the index of the largest value.\n* ```js\n* const v = [ 10, 40, 5 ];\n* Numbers.maxIndex(v); // Yields 1\n* ```\n* @param data Array of numbers\n* @returns Index of largest value\n*/\nconst maxIndex = (data) => data.reduce((bestIndex, value, index, array) => value > array[bestIndex] ? index : bestIndex, 0);\n/**\n* Returns the index of the smallest value.\n*\n* ```js\n* const v = [ 10, 40, 5 ];\n* Numbers.minIndex(v); // Yields 2\n* ```\n* @param data Array of numbers\n* @returns Index of smallest value\n*/\nconst minIndex = (data) => data.reduce((bestIndex, value, index, array) => value < array[bestIndex] ? index : bestIndex, 0);\n/**\n* Returns the maximum number out of `data`.\n* Undefined and non-numbers are silently ignored.\n*\n* ```js\n* Numbers.max(100, 200, 50); // 200\n* ```\n* @param data List of numbers\n* @returns Maximum number\n*/\nconst max = (data) => Math.max(...validNumbers(data));\n/**\n* Returns the total of `data`.\n* Undefined and non-numbers are silently ignored.\n*\n* ```js\n* Numbers.total([1, 2, 3]); // 6\n* ```\n* @param data Array of numbers\n* @returns Total\n*/\nconst total = (data) => data.reduce((previous, current) => {\n\tif (typeof current !== `number`) return previous;\n\tif (Number.isNaN(current)) return previous;\n\tif (!Number.isFinite(current)) return previous;\n\treturn previous + current;\n}, 0);\n/**\n* Returns the maximum out of `data` without pre-filtering for speed.\n*\n* For most uses, {@link max} should suffice.\n*\n* ```js\n* Numbers.maxFast([ 10, 0, 4 ]); // 10\n* ```\n* @param data\n* @returns Maximum\n*/\nconst maxFast = (data) => {\n\tlet m = Number.MIN_SAFE_INTEGER;\n\tfor (const datum of data) m = Math.max(m, datum);\n\treturn m;\n};\n/**\n* Returns the total of `data` without pre-filtering for speed.\n*\n* For most uses, {@link total} should suffice.\n*\n* ```js\n* Numbers.totalFast([ 10, 0, 4 ]); // 14\n* ```\n* @param data\n* @returns Maximum\n*/\nconst totalFast = (data) => {\n\tlet m = 0;\n\tfor (const datum of data) m += datum;\n\treturn m;\n};\n/**\n* Returns the maximum out of `data` without pre-filtering for speed.\n*\n* For most uses, {@link max} should suffice.\n*\n* ```js\n* Numbers.minFast([ 10, 0, 100 ]); // 0\n* ```\n* @param data\n* @returns Maximum\n*/\nconst minFast = (data) => {\n\tlet m = Number.MAX_SAFE_INTEGER;\n\tfor (const datum of data) m = Math.min(m, datum);\n\treturn m;\n};\n//#endregion\n//#region src/average.ts\n/**\n* Calculate median value of an array of numbers\n* @param data \n* @returns \n*/\nconst median = (data) => {\n\tif (!Array.isArray(data)) throw new TypeError(`Param 'data' is expected to be an array`);\n\tconst n = data.length;\n\tdata.sort((a, b) => a - b);\n\tlet result = 0;\n\tif (n % 2 === 0) result = Math.floor((data[n / 2] + data[n / 2 - 1]) / 2);\n\telse result = data[Math.floor(n / 2)];\n\treturn result;\n};\n/**\n* Calculate the mean of `array`.\n* @param array \n* @returns \n*/\nconst mean = (array) => array.reduce((accumulator, value) => accumulator + value, 0) / array.length;\n/**\n* Computes an average of an array with a set of weights applied.\n*\n* Weights can be provided as an array, expected to be on 0..1 scale, with indexes\n* matched up to input data. Ie. data at index 2 will be weighed by index 2 in the weightings array.\n*\n* ```js\n* // All items weighted evenly\n* averageWeighted([1,2,3], [1,1,1]); // 2\n*\n* // First item has full weight, second half, third quarter\n* averageWeighted([1,2,3], [1, 0.5, 0.25]); // 1.57\n*\n* // With reversed weighting of [0.25,0.5,1] value is 2.42\n* ```\n*\n* A function can alternatively be provided to compute the weighting based on array index, via {@link weight}.\n*\n* ```js\n* averageWeighted[1,2,3], Random.gaussian()); // 2.0\n* ```\n*\n* This is the same as:\n*\n* ```js\n* const data = [ 1, 2, 3 ];\n* const w = weight(data, Random.gaussian());\n* const avg = averageWeighted(data, w); // 2.0\n* ```\n* @param data Data to average\n* @param weightings Array of weightings that match up to data array, or an easing function\n* @see {@link average} Compute averages without weighting.\n*/\nconst averageWeighted = (data, weightings) => {\n\tif (typeof weightings === `function`) weightings = weight(data, weightings);\n\tconst [totalV, totalW] = zip(data, weightings).reduce((accumulator, v) => [accumulator[0] + v[0] * v[1], accumulator[1] + v[1]], [0, 0]);\n\treturn totalV / totalW;\n};\n/**\n* Returns a function that computes a weighted average of an array\n* \n* ```js\n* const w = averageWeigher(v => Math.random() * v);\n* \n* // Give each array index a random\n* w([1,2,3,4]);\n* ```\n* @param weigher \n* @returns \n*/\nconst averageWeigher = (weigher) => {\n\treturn (data) => averageWeighted(data, weigher);\n};\n//#endregion\n//#region src/clamp.ts\n/**\n* Clamps a value between min and max (both inclusive)\n* Defaults to a 0-1 range, useful for percentages.\n*\n* @example Usage\n* ```js\n* // 0.5 - just fine, within default of 0 to 1\n* clamp(0.5);\n* // 1 - above default max of 1\n* clamp(1.5);\n* // 0 - below range\n* clamp(-50, 0, 100);\n* // 50 - within range\n* clamp(50, 0, 50);\n* ```\n*\n* For clamping integer ranges, consider {@link clampIndex }\n* For clamping `{ x, y }` points, consider {@link https://api.ixfx.fun/_ixfx/geometry/Points/clamp/ @ixfx/geometry/Points.clamp}.\n* For clamping bipolar values: {@link Bipolar.clamp}\n* @param value Value to clamp\n* @param min value (inclusive)\n* @param max value (inclusive)\n* @returns Clamped value\n*/\nfunction clamp(value, min = 0, max = 1) {\n\tif (Number.isNaN(value)) throw new Error(`Param 'value' is NaN`);\n\tif (Number.isNaN(min)) throw new Error(`Param 'min' is NaN`);\n\tif (Number.isNaN(max)) throw new Error(`Param 'max' is NaN`);\n\tif (value < min) return min;\n\tif (value > max) return max;\n\treturn value;\n}\n/**\n* Returns a function that clamps values.\n*\n* ```js\n* const c = clamper(0,100);\n* c(50); // 50\n* c(101); // 100\n* c(-5); // 0\n* ```\n* @param min Minimum value. Default: 0\n* @param max Maximum value. Default: 1\n*/\nfunction clamper(min = 0, max = 1) {\n\tif (Number.isNaN(min)) throw new Error(`Param 'min' is NaN`);\n\tif (Number.isNaN(max)) throw new Error(`Param 'max' is NaN`);\n\treturn (v) => {\n\t\tif (v > max) return max;\n\t\tif (v < min) return min;\n\t\treturn v;\n\t};\n}\n/**\n* Clamps integer `v` between 0 (inclusive) and array length or length (exclusive).\n* Returns value then will always be at least zero, and a valid array index.\n*\n* @example Usage\n* ```js\n* // Array of length 4\n* const myArray = [`a`, `b`, `c`, `d`];\n* clampIndex(0, myArray); // 0\n* clampIndex(5, 3); // 2\n* ```\n*\n* Throws an error if `v` is not an integer.\n*\n* For some data it makes sense that data might 'wrap around' if it exceeds the\n* range. For example rotation angle. Consider using {@link wrap} for this.\n*\n* @param v Value to clamp (must be an interger)\n* @param arrayOrLength Array, or length of bounds (must be an integer)\n* @returns Clamped value, minimum will be 0, maximum will be one less than `length`.\n*/\nfunction clampIndex(v, arrayOrLength) {\n\tif (!Number.isInteger(v)) throw new TypeError(`v parameter must be an integer (${v})`);\n\tconst length = Array.isArray(arrayOrLength) ? arrayOrLength.length : arrayOrLength;\n\tif (!Number.isInteger(length)) throw new TypeError(`length parameter must be an integer (${length}, ${typeof length})`);\n\tv = Math.round(v);\n\tif (v < 0) return 0;\n\tif (v >= length) return length - 1;\n\treturn v;\n}\n/**\n* Returns the largest value, ignoring the sign of numbers\n*\n* ```js\n* maxAbs(1, 5); // 5\n* maxAbs(-10, 5); // -10 (since sign is ignored)\n* maxAbs(arrayOfNumbers);\n* ```\n*\n* Non-valid numbers are silently ignored.\n* @param values\n* @returns\n*/\nfunction maxAbs(...values) {\n\tlet maxA = Number.MIN_SAFE_INTEGER;\n\tlet max = Number.MIN_SAFE_INTEGER;\n\tconst checkV = (v) => {\n\t\tif (!Number.isNaN(v) && Number.isFinite(v)) {\n\t\t\tconst va = Math.abs(v);\n\t\t\tif (va > maxA) {\n\t\t\t\tmaxA = va;\n\t\t\t\tmax = v;\n\t\t\t}\n\t\t}\n\t};\n\tfor (const v of values) if (typeof v === `number`) checkV(v);\n\telse for (const subV of v) checkV(subV);\n\treturn max;\n}\n//#endregion\n//#region src/count.ts\n/**\n* Yields `amount` integers, counting by one from zero. If a negative amount is used,\n* count decreases. If `offset` is provided, this is added to the return result.\n* @example\n* ```js\n* const a = [...count(5)]; // Yields five numbers: [0,1,2,3,4]\n* const b = [...count(-5)]; // Yields five numbers: [0,-1,-2,-3,-4]\n* for (const v of count(5, 5)) {\n* // Yields: 5, 6, 7, 8, 9\n* }\n* const c = [...count(5,1)]; // Yields [1,2,3,4,5]\n* ```\n*\n* @example Used with forEach\n* ```js\n* // Prints `Hi` 5x\n* forEach(count(5), () => // do something);\n* ```\n*\n* If you want to accumulate return values, consider using Flow.repeat.\n*\n* @example Run some code every 100ms, 10 times:\n* ```js\n* import { interval } from '@ixfx/flow.js'\n* import { count } from '@ixfx/numbers.js'\n* const counter = count(10);\n* for await (const v of interval(counter, { fixedIntervalMs: 100 })) {\n* // Do something\n* }\n* ```\n* @param amount Number of integers to yield\n* @param offset Added to result\n*/\nfunction* count(amount, offset = 0) {\n\tresultThrow(integerTest(amount, ``, `amount`), integerTest(offset, ``, `offset`));\n\tif (amount === 0) return;\n\tlet index = 0;\n\tdo\n\t\tyield amount < 0 ? -index + offset : index + offset;\n\twhile (index++ < Math.abs(amount) - 1);\n}\n//#endregion\n//#region src/difference.ts\n/**\n* Returns the difference from the `initial` value. Defaults to absolute difference.\n* ```js\n* const rel = differenceFromFixed(100);\n* rel(100); // 0\n* rel(150); // 50\n* rel(50); // 50\n* ```\n*\n* 'numerical' gives sign:\n* ```js\n* const rel = differenceFromFixed(100, `numerical`);\n* rel(100); // 0\n* rel(150); // 50\n* rel(50); // -50\n* ```\n* \n* 'relative' gives proportion to initial\n* ```js\n* const rel = differenceFromFixed(100, `relative`);\n* rel(100); // 0\n* rel(150); // 0.5\n* rel(10); // 0.90\n* ```\n* \n* Using 'relativeSigned', we get negative relative result\n* when value is below the initial value.\n* \n* Use {@link differenceFromLast} to compare against the last value,\n* rather than the same fixed value.\n* @param {number} initial Value to compare against\n* @returns Difference from initial value\n*/\nconst differenceFromFixed = (initial, kind = `absolute`) => (value) => differenceFrom(kind, value, initial);\n/**\n* Returns a function which yields difference compared to last value.\n* \n* If no initial value is provided, the first difference will be returned as 0.\n* \n* Difference can be returned in various formats:\n* * 'absolute': numerical difference, without sign\n* * 'numerical': numerical difference, with sign, so you can see if difference is higher or lower\n* * 'relative': difference divided by last value, giving a proportional difference. Unsigned.\n* * 'relativeSigned': as above, but with sign\n* \n* Use {@link differenceFromFixed} to compare against a fixed value instead of the last value.\n* \n* ```js\n* let d = differenceFromLast(`absolute`);\n* d(10); // 0\n* d(11); // 1\n* d(10); // 1\n* ```\n* \n* ```js\n* let d = differenceFromLast(`numerical`);\n* d(10); // 0\n* d(11); // 1\n* d(10); // -1\n* ```\n* \n* ```js\n* let d = differenceFromLast(`relative`);\n* d(10); // 0\n* d(11); // 0.1\n* d(10); // 0.1\n* ```\n* ```js\n* let d = differenceFromLast(`relativeSigned`);\n* d(10); // 0\n* d(11); // 0.1\n* d(10); // -0.1\n* ```\n* \n* An initial value can be provided, eg:\n* ```js\n* let d = differenceFromLast(`absolute`, 10);\n* d(11); // 1\n* ```\n* @param kind Kind of output value\n* @param initialValue Optional initial value \n* @returns \n*/\nconst differenceFromLast = (kind = `absolute`, initialValue = NaN) => {\n\tlet lastValue = initialValue;\n\treturn (value) => {\n\t\tconst x = differenceFrom(kind, value, lastValue);\n\t\tlastValue = value;\n\t\treturn x;\n\t};\n};\nconst differenceFrom = (kind = `absolute`, value, from) => {\n\tif (Number.isNaN(from)) return 0;\n\tconst d = value - from;\n\tlet r = 0;\n\tif (kind === `absolute`) r = Math.abs(d);\n\telse if (kind === `numerical`) r = d;\n\telse if (kind === `relative`) r = Math.abs(d / from);\n\telse if (kind === `relativeSigned`) r = d / from;\n\telse throw new TypeError(`Unknown kind: '${kind}' Expected: 'absolute', 'relative', 'relativeSigned' or 'numerical'`);\n\treturn r;\n};\n//#endregion\n//#region src/guard.ts\n/**\n* Returns true if `possibleNumber` is a number and not NaN\n* @param possibleNumber\n* @returns\n*/\nconst isValid = (possibleNumber) => {\n\tif (typeof possibleNumber !== `number`) return false;\n\tif (Number.isNaN(possibleNumber)) return false;\n\treturn true;\n};\n//#endregion\n//#region src/filter.ts\n/**\n* Filters an iterator of values, only yielding\n* those that are valid numbers\n*\n* ```js\n* const data = [true, 10, '5', { x: 5 }];\n* for (const n of Numbers.filterIterable(data)) {\n* // 10\n* }\n* ```\n* @param it\n*/\nfunction* filterIterable(it) {\n\tfor (const v of it) if (isValid(v)) yield v;\n}\n/**\n* Returns a function that yields _true_ if a value\n* is at least `threshold`\n* ```js\n* const t = thresholdAtLeast(50);\n* t(50); // true\n* t(0); // false\n* t(55); // true\n* ```\n* @param threshold \n* @returns \n*/\nconst thresholdAtLeast = (threshold) => {\n\treturn (v) => {\n\t\treturn v >= threshold;\n\t};\n};\n/**\n* Returns a function that yields _true_\n* if a number is at least _min_ and no greater than _max_\n* \n* ```js\n* const t = rangeInclusive(50, 100);\n* t(40); // false\n* t(50); // true\n* t(60); // true\n* t(100); // true\n* t(101); // false\n* ```\n* @param min \n* @param max \n* @returns \n*/\nconst rangeInclusive = (min, max) => {\n\treturn (v) => {\n\t\treturn v >= min && v <= max;\n\t};\n};\n//#endregion\n//#region src/flip.ts\n/**\n* Flips a percentage-scale number: `1 - v`.\n*\n* The utility of this function is that it sanity-checks\n* that `v` is in 0..1 scale.\n*\n* ```js\n* flip(1); // 0\n* flip(0.5); // 0.5\n* flip(0); // 1\n* ```\n* @param v\n* @returns\n*/\nconst flip = (v) => {\n\tif (typeof v === `function`) v = v();\n\tresultThrow(numberTest(v, `percentage`, `v`));\n\treturn 1 - v;\n};\n//#endregion\n//#region src/generate.ts\n/**\n* Generates a range of numbers, starting from `start` and counting by `interval`.\n* If `end` is provided, generator stops when reached\n*\n* Unlike {@link numericRange}, numbers might contain rounding errors\n*\n* ```js\n* for (const c of numericRangeRaw(10, 100)) {\n* // 100, 110, 120 ...\n* }\n* ```\n* \n* Get results as an array\n* ```js\n* const c = [...numericRangeRaw(1,0,5)]; // [0,1,2,3,4]\n* ```\n* @param interval Interval between numbers\n* @param start Start\n* @param end End (if undefined, range never ends). Inclusive.\n*/\nconst numericRangeRaw = function* (interval, start = 0, end, repeating = false) {\n\tif (interval <= 0) throw new Error(`Interval is expected to be above zero`);\n\tif (typeof end === `undefined`) end = Number.MAX_SAFE_INTEGER;\n\tlet v = start;\n\tdo\n\t\twhile (v <= end) {\n\t\t\tyield v;\n\t\t\tv += interval;\n\t\t}\n\twhile (repeating);\n};\n/**\n* Generates a range of numbers, with a given interval.\n*\n* @example For-loop\n* ```\n* let loopForever = numericRange(0.1); // By default starts at 0 and counts upwards forever\n* for (v of loopForever) {\n* console.log(v);\n* }\n* ```\n*\n* @example If you want more control over when/where incrementing happens...\n* ```js\n* let percent = numericRange(0.1, 0, 1);\n*\n* let percentResult = percent.next().value;\n* ```\n*\n* Note that computations are internally rounded to avoid floating point math issues. So if the `interval` is very small (eg thousandths), specify a higher rounding\n* number.\n*\n* @param interval Interval between numbers\n* @param start Start. Defaults to 0\n* @param end End (if undefined, range never ends). Inclusive.\n* @param repeating Range loops from start indefinately. Default _false_\n* @param rounding A rounding that matches the interval avoids floating-point math hikinks. Eg if the interval is 0.1, use a rounding of 10\n*/\nconst numericRange = function* (interval, start = 0, end, repeating = false, rounding) {\n\tresultThrow(numberTest(interval, `nonZero`));\n\tconst negativeInterval = interval < 0;\n\tif (end === void 0) {} else {\n\t\tif (negativeInterval && start < end) throw new Error(`Interval of ${interval.toString()} will never go from ${start.toString()} to ${end.toString()}`);\n\t\tif (!negativeInterval && start > end) throw new Error(`Interval of ${interval.toString()} will never go from ${start.toString()} to ${end.toString()}`);\n\t}\n\trounding = rounding ?? 1e3;\n\tif (end === void 0) end = Number.MAX_SAFE_INTEGER;\n\telse end *= rounding;\n\tinterval = interval * rounding;\n\tdo {\n\t\tlet v = start * rounding;\n\t\twhile (!negativeInterval && v <= end || negativeInterval && v >= end) {\n\t\t\tyield v / rounding;\n\t\t\tv += interval;\n\t\t}\n\t} while (repeating);\n};\n/**\n* Yields numeric range between 0.0-1.0.\n*\n* ```\n* // Yields: [0, 0.2, 0.4, 0.6, 0.8, 1]\n* const a = [...numericPercent(0.2)];\n*\n* // Repeating flag set to true:\n* for (const v of numericPercent(0.2, true)) {\n* // Infinite loop. V loops back to 0 after hitting 1\n* }\n* ```\n*\n* If `repeating` is true, it loops back to 0 after reaching 1\n* @param interval Interval (default: 0.01, ie. 1%)\n* @param repeating Whether generator should loop (default: false)\n* @param start Start (default: 0)\n* @param end End (default: 1)\n* @returns\n*/\nconst numericPercent = function(interval = .01, repeating = false, start = 0, end = 1) {\n\tresultThrow(numberTest(interval, `percentage`, `interval`), numberTest(start, `percentage`, `start`), numberTest(end, `percentage`, `end`));\n\treturn numericRange(interval, start, end, repeating);\n};\n//#endregion\n//#region src/is-approx.ts\n/**\n* Checks if a value is within range of a base value\n* \n* ```js\n* // Check if 101 is within 10% of 100\n* isApprox(0.1, 100, 101);\n* \n* // Gets a function to compare some value of 10% range to 100\n* const c = isApprox(0.1,100);\n* c(101);\n* \n* // Gets a function to compare some base value and value to 10% range\n* const c = isApprox(0.1);\n* c(100, 101);\n* ```\n* \n* Throws an error if range or base values are NaN.\n* If value being checked is NaN or infinity, _false_ is returned.\n* @param rangePercent \n* @param baseValue \n* @param v \n* @returns \n*/\nfunction isApprox(rangePercent, baseValue, v) {\n\tresultThrow(numberTest(rangePercent, `percentage`, `rangePercent`));\n\tconst range = Math.floor(rangePercent * 100);\n\tconst test = (base, value) => {\n\t\ttry {\n\t\t\tif (typeof value !== `number`) return false;\n\t\t\tif (Number.isNaN(value)) return false;\n\t\t\tif (!Number.isFinite(value)) return false;\n\t\t\tconst diff = Math.abs(value - base);\n\t\t\treturn (base === 0 ? Math.floor(diff * 100) : Math.floor(diff / base * 100)) <= range;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t};\n\tif (baseValue === void 0) return test;\n\tresultThrow(numberTest(baseValue, ``, `baseValue`));\n\tif (v === void 0) return (value) => test(baseValue, value);\n\telse return test(baseValue, v);\n}\n/**\n* Yields a function that checks if a value is close to any target value\n* ```js\n* const c = isCloseToAny(1, 10, 20, 30, 40);\n* c(11); // True - within 1 range of 10\n* c(19); // True - within 1 range of 20\n* c(0); // False\n* ```\n* \n* Returned function accepts multiple values, returning\n* _true_ if any of them are within range\n* ```js\n* c(0, 1, 11); // Would return true based on 11\n* ```\n* @param allowedRangeAbsolute \n* @param targets \n* @returns \n*/\nconst isCloseToAny = (allowedRangeAbsolute, ...targets) => {\n\tconst targetsMin = targets.map((t) => t - allowedRangeAbsolute);\n\tconst targetsMax = targets.map((t) => t + allowedRangeAbsolute);\n\treturn (...values) => {\n\t\tfor (const v of values) for (let index = 0; index < targets.length; index++) if (v >= targetsMin[index] && v <= targetsMax[index]) return true;\n\t\treturn false;\n\t};\n};\n//#endregion\n//#region src/kalman.ts\n/**\n* KalmanFilter\n* \n* author: Wouter Bulten\n* see {@link http://github.com/wouterbulten/kalmanjs}\n* version Version: 1.0.0-beta\n* copyright Copyright 2015-2018 Wouter Bulten\n* license MIT License\n*/\nvar Kalman1dFilter = class {\n\tR;\n\tQ;\n\tA;\n\tC;\n\tB;\n\tcov;\n\tx;\n\t/**\n\t* Create 1-dimensional kalman filter\n\t*/\n\tconstructor(options = {}) {\n\t\tthis.R = options.r ?? 1;\n\t\tthis.Q = options.q ?? 1;\n\t\tthis.A = options.a ?? 1;\n\t\tthis.C = options.c ?? 1;\n\t\tthis.B = options.b ?? 0;\n\t\tthis.cov = NaN;\n\t\tthis.x = NaN;\n\t}\n\t/**\n\t* Filter a new value\n\t* @param {Number} z Measurement\n\t* @param {Number} u Control\n\t* @return {Number}\n\t*/\n\tfilter(z, u = 0) {\n\t\tif (isNaN(this.x)) {\n\t\t\tthis.x = 1 / this.C * z;\n\t\t\tthis.cov = 1 / this.C * this.Q * (1 / this.C);\n\t\t} else {\n\t\t\tconst predX = this.predict(u);\n\t\t\tconst predCov = this.uncertainty();\n\t\t\tconst K = predCov * this.C * (1 / (this.C * predCov * this.C + this.Q));\n\t\t\tthis.x = predX + K * (z - this.C * predX);\n\t\t\tthis.cov = predCov - K * this.C * predCov;\n\t\t}\n\t\treturn this.x;\n\t}\n\t/**\n\t* Predict next value\n\t* @param {Number} [u] Control\n\t* @return {Number}\n\t*/\n\tpredict(u = 0) {\n\t\treturn this.A * this.x + this.B * u;\n\t}\n\t/**\n\t* Return uncertainty of filter\n\t* @return {Number}\n\t*/\n\tuncertainty() {\n\t\treturn this.A * this.cov * this.A + this.R;\n\t}\n\t/**\n\t* Return the last filtered measurement\n\t* @return {Number}\n\t*/\n\tlastMeasurement() {\n\t\treturn this.x;\n\t}\n\t/**\n\t* Set measurement noise Q\n\t* @param {Number} noise\n\t*/\n\tsetMeasurementNoise(noise) {\n\t\tthis.Q = noise;\n\t}\n\t/**\n\t* Set the process noise R\n\t* @param {Number} noise\n\t*/\n\tsetProcessNoise(noise) {\n\t\tthis.R = noise;\n\t}\n};\n/**\n* Returns a function that performs 1D Kalman filtering.\n* \n* ```js\n* const f = kalman1dFilter();\n* f(10); // 10\n* ```\n* \n* Under the hood creates a {@link Kalman1dFilter} instance and returns its `filter` method.\n* @param options \n* @returns \n*/\nconst kalman1dFilter = (options = {}) => {\n\tconst f = new Kalman1dFilter(options);\n\treturn f.filter.bind(f);\n};\n//#endregion\n//#region src/bipolar.ts\nvar bipolar_exports = /* @__PURE__ */ __exportAll({\n\tclamp: () => clamp$1,\n\tfromScalar: () => fromScalar,\n\timmutable: () => immutable,\n\tscale: () => scale$1,\n\tscaleUnclamped: () => scaleUnclamped,\n\ttoScalar: () => toScalar,\n\ttowardZero: () => towardZero\n});\n/**\n* Wrapper for bipolar-based values. Immutable.\n* All functions will clamp to keep it in legal range.\n* \n* ```js\n* let v = immutable(); // Starts with 0 by default\n* v = v.add(0.1); // v.value is 0.1\n* v = v.inverse(); // v.value is -0.1\n* v = v.multiply(0.2); // v.value is -0.02\n* \n* v = immutable(1);\n* v = v.towardZero(0.1); // 0.9\n* v = v.interpolate(0.1, 1);\n* ```\n* \n* Wrapped values can be coerced into number:\n* ```js\n* const v = immutable(1);\n* const x = +v+10;\n* // x = 11\n* ```\n* @param startingValueOrBipolar Initial numeric value or BipolarWrapper instance\n* @throws {TypeError} If start value is out of bipolar range or invalid\n* @returns \n*/\nconst immutable = (startingValueOrBipolar = 0) => {\n\tconst startingValue = typeof startingValueOrBipolar === `number` ? startingValueOrBipolar : startingValueOrBipolar.value;\n\tif (startingValue > 1) throw new TypeError(`Start value cannot be larger than 1`);\n\tif (startingValue < -1) throw new TypeError(`Start value cannot be smaller than -1`);\n\tif (Number.isNaN(startingValue)) throw new TypeError(`Start value is NaN`);\n\tconst v = startingValue;\n\treturn {\n\t\t[Symbol.toPrimitive](hint) {\n\t\t\tif (hint === `number` || hint === `default`) return v;\n\t\t\telse if (hint === `string`) return v.toString();\n\t\t\treturn true;\n\t\t},\n\t\tvalue: v,\n\t\ttowardZero: (amount) => {\n\t\t\treturn immutable(towardZero(v, amount));\n\t\t},\n\t\tadd: (amount) => {\n\t\t\treturn immutable(clamp$1(v + amount));\n\t\t},\n\t\tmultiply: (amount) => {\n\t\t\treturn immutable(clamp$1(v * amount));\n\t\t},\n\t\tinverse: () => {\n\t\t\treturn immutable(-v);\n\t\t},\n\t\tinterpolate: (amount, target) => {\n\t\t\treturn immutable(clamp$1(interpolate(amount, v, target)));\n\t\t},\n\t\tasScalar: (max = 1, min = 0) => {\n\t\t\treturn toScalar(v, max, min);\n\t\t}\n\t};\n};\n/**\n* Converts bipolar value to a scalar. That is, converts from\n* -1..1 range to 0..1.\n* \n* ```js\n* Bipolar.toScalar(-1); // 0.0\n* Bipolar.toScalar( 0); // 0.5\n* Bipolar.toScalar( 1); // 1.0\n* ```\n* \n* Range can be changed:\n* ```js\n* Bipolar.toScalar(0, 100); // Uses 0..100 scale, so output is 50\n* Bipolar.toScalar(0, 100, 50); // Uses 50..1000 scale, so output is 75\n* ```\n* \n* Throws an error if `bipolarValue` is not a number or NaN\n* @param bipolarValue Value to convert to scalar\n* @returns Scalar value on 0..1 range.\n*/\nconst toScalar = (bipolarValue, max = 1, min = 0) => {\n\tif (typeof bipolarValue !== `number`) throw new Error(`Param 'bipolarValue' to be a number. Got: ${typeof bipolarValue}`);\n\tif (Number.isNaN(bipolarValue)) throw new Error(`Param 'bipolarValue' is NaN`);\n\treturn scale(bipolarValue, -1, 1, min, max);\n};\n/**\n* Makes a scalar into a bipolar value.\n* \n* That is, input range is 0..1, output range is -1...1\n*\n* ```js\n* Bipolar.fromScalar(1); // 1\n* Bipolar.fromScalar(0); // -1\n* Bipolar.fromScalar(0.5); // 0\n* ```\n* \n* Throws an error if `scalarValue` is outside 0..1 scale.\n* @param scalarValue Scalar value to convert\n* @returns Bipolar value on -1..1 scale\n*/\nconst fromScalar = (scalarValue) => {\n\tresultThrow(numberTest(scalarValue, `percentage`, `v`));\n\treturn scalarValue * 2 - 1;\n};\n/**\n* Scale & clamp value to bipolar range (-1..1).\n* ```js\n* // Scale 100 on 0..100 scale\n* Bipolar.scale(100, 0, 100); // 1\n* Bipolar.scale(50, 0, 100); // 0\n* Bipolar.scale(0, 0, 100); // -1\n* ```\n* \n* Return value is clamped.\n* @param inputValue Value to scale\n* @param inMin Minimum of scale\n* @param inMax Maximum of scale\n* @returns Bipolar value on -1..1 scale\n*/\nconst scale$1 = (inputValue, inMin, inMax) => {\n\treturn clamp$1(scaler(inMin, inMax, -1, 1)(inputValue));\n};\n/**\n* Scale a number to bipolar range (-1..1). Not clamped, so we might exceed range.\n* \n* ```js\n* // Scale 100 on 0..100 scale\n* Bipolar.scaleUnclamped(100, 0, 100); // 1\n* Bipolar.scaleUnclamped(50, 0, 100); // 0\n* Bipolar.scaleUnclamped(0, 0, 100); // -1\n* ```\n* \n* @param inputValue Value to scale\n* @param inMin Minimum of scale\n* @param inMax Maximum of scale\n* @returns Bipolar value on -1..1 scale\n*/\nconst scaleUnclamped = (inputValue, inMin, inMax) => {\n\treturn scaler(inMin, inMax, -1, 1)(inputValue);\n};\n/**\n* Clamp a bipolar value\n* ```js\n* Bipolar.clamp(-1); // -1\n* Bipolar.clamp(-1.1); // -1\n* ```\n* \n* Throws an error if `bipolarValue` is not a number or NaN.\n* @param bipolarValue Value to clamp\n* @returns Clamped value on -1..1 scale\n*/\nconst clamp$1 = (bipolarValue) => {\n\tif (typeof bipolarValue !== `number`) throw new Error(`Param 'bipolarValue' must be a number. Got: ${typeof bipolarValue}`);\n\tif (Number.isNaN(bipolarValue)) throw new Error(`Param 'bipolarValue' is NaN`);\n\tif (bipolarValue > 1) return 1;\n\tif (bipolarValue < -1) return -1;\n\treturn bipolarValue;\n};\n/**\n* Pushes a bipolar value toward zero by `amount`.\n* Return value is clamped on bipolar range of -1..1\n* \n* ```js\n* Bipolar.towardZero(-1, 0.1); // -0.9\n* Bipolar.towardZero( 1, 0.1); // 0.9\n* Bipolar.towardZero( 0, 0.1); // 0.0\n* Bipolar.towardZero( 1, 1.1); // 0.0\n* ```\n* \n* If `amount` is greater than 1, 0 is returned.\n* Throws an error if `bipolarValue` or `amount` are not numbers.\n* Throws an error if `amount` is below zero.\n* @param bipolarValue Bipolar value to nudge toward zero\n* @param amount Amount to nudge by\n* @returns Bipolar value -1...1\n*/\nconst towardZero = (bipolarValue, amount) => {\n\tif (typeof bipolarValue !== `number`) throw new Error(`Parameter 'bipolarValue' must be a number. Got: ${typeof bipolarValue}`);\n\tif (typeof amount !== `number`) throw new Error(`Parameter 'amount' must be a number. Got: ${typeof amount}`);\n\tif (amount < 0) throw new Error(`Parameter 'amount' must be positive`);\n\tif (bipolarValue < 0) {\n\t\tbipolarValue += amount;\n\t\tif (bipolarValue > 0) bipolarValue = 0;\n\t} else if (bipolarValue > 0) {\n\t\tbipolarValue -= amount;\n\t\tif (bipolarValue < 0) bipolarValue = 0;\n\t}\n\treturn bipolarValue;\n};\n//#endregion\n//#region src/wrap.ts\n/**\n* Wraps an integer number within a specified range, defaulting to degrees (0-360). Use {@link wrap} for floating-point wrapping.\n*\n* This is useful for calculations involving degree angles and hue, which wrap from 0-360.\n* Eg: to add 200 to 200, we don't want 400, but 40.\n*\n* ```js\n* const v = wrapInteger(200+200, 0, 360); // 40\n* ```\n*\n* Or if we minus 100 from 10, we don't want -90 but 270\n* ```js\n* const v = wrapInteger(10-100, 0, 360); // 270\n* ```\n*\n* `wrapInteger` uses 0-360 as a default range, so both of these\n* examples could just as well be:\n*\n* ```js\n* wrapInteger(200+200); // 40\n* wrapInteger(10-100); // 270\n* ```\n*\n* Non-zero starting points can be used. A range of 20-70:\n* ```js\n* const v = wrapInteger(-20, 20, 70); // 50\n* ```\n*\n* Note that the minimum value is inclusive, while the maximum is _exclusive_.\n* So with the default range of 0-360, 360 is never reached:\n*\n* ```js\n* wrapInteger(360); // 0\n* wrapInteger(361); // 1\n* ```\n*\n* If you just want to lock values to a range without wrapping, consider {@link clamp}.\n*\n* @param v Value to wrap\n* @param min Integer minimum of range (default: 0). Inclusive\n* @param max Integer maximum of range (default: 360). Exlusive\n* @returns\n*/\nconst wrapInteger = (v, min = 0, max = 360) => {\n\tresultThrow(integerTest(v, void 0, `v`), integerTest(min, void 0, `min`), integerTest(max, void 0, `max`));\n\tif (v === min) return min;\n\tif (v === max) return min;\n\tif (v > 0 && v < min) v += min;\n\tv -= min;\n\tmax -= min;\n\tv = v % max;\n\tif (v < 0) v = max - Math.abs(v) + min;\n\treturn v + min;\n};\n/**\n* Wraps floating point numbers to be within a range (default: 0..1). Use {@link wrapInteger} if you want to wrap integer values.\n*\n* This logic makes sense for some things like rotation angle.\n*\n* If you just want to lock values to a range without wrapping, consider {@link clamp}.\n*\n* ```js\n* wrap(1.2); // 0.2\n* wrap(2); // 1.0\n* wrap(-0.2); // 0.8\n* ```\n*\n* A range can be provided too:\n* ```js\n* wrap(30, 20, 50); \t // 30\n* wrap(60, 20, 50); // 30\n* ```\n* @param v\n* @param min\n* @param max\n* @returns\n*/\nconst wrap = (v, min = 0, max = 1) => {\n\tresultThrow(numberTest(v, ``, `min`), numberTest(min, ``, `min`), numberTest(max, ``, `max`));\n\tif (v === min) return min;\n\tif (v === max) return min;\n\twhile (v <= min || v >= max) {\n\t\tif (v === max) break;\n\t\tif (v === min) break;\n\t\tif (v > max) v = min + (v - max);\n\t\telse if (v < min) v = max - (min - v);\n\t}\n\treturn v;\n};\n/**\n* Performs a calculation within a wrapping number range. This is a lower-level function.\n* See also: {@link wrapInteger} for simple wrapping within a range.\n*\n* `min` and `max` define the start and end of the valid range, inclusive. Eg for hue degrees it'd be 0, 360.\n* `a` and `b` is the range you want to work in.\n*\n* For example, let's say you want to get the middle point between a hue of 30 and a hue of 330 (ie warmer colours):\n* ```js\n* wrapRange(0,360, (distance) => {\n* // for a:0 and b:330, distance would be 90 from 30 degrees to 330 (via zero)\n* return distance * 0.5; // eg return middle point\n* }, 30, 330);\n* ```\n*\n* The return value of the callback should be in the range of 0-distance. `wrapRange` will subsequently\n* conform it to the `min` and `max` range before it's returned to the caller.\n*\n* @param a Output start (eg. 60)\n* @param b Output end (eg 300)\n* @param min Range start (eg 0)\n* @param max Range end (eg 360)\n* @param fn Returns a computed value from 0 to `distance`.\n* @returns\n*/\nconst wrapRange = (min, max, fn, a, b) => {\n\tlet r = 0;\n\tconst distF = Math.abs(b - a);\n\tconst distFwrap = Math.abs(max - a + b);\n\tconst distBWrap = Math.abs(a + (360 - b));\n\tconst distMin = Math.min(distF, distFwrap, distBWrap);\n\tif (distMin === distBWrap) r = a - fn(distMin);\n\telse if (distMin === distFwrap) r = a + fn(distMin);\n\telse if (a > b) r = a - fn(distMin);\n\telse r = a + fn(distMin);\n\treturn wrapInteger(r, min, max);\n};\n//#endregion\n//#region src/pi-pi.ts\nconst piPi = Math.PI * 2;\n//#endregion\n//#region src/interpolate.ts\n/**\n* Interpolates between `a` and `b` by `amount`. Aka `lerp`.\n*\n* [ixfx Guide on Interpolation](https://ixfx.fun/data/interpolation/overview/)\n*\n* @example Get the halfway point between 30 and 60\n* ```js\n* interpolate(0.5, 30, 60);\n* ```\n*\n* See also {@link interpolatorStepped} and {@link https://api.ixfx.fun/_ixfx/modulation/interpolatorInterval/} for functions\n* which help to manage progression from A->B over steps or interval.\n* \n* Usually interpolation amount is on a 0...1 scale, inclusive. What is the interpolation result\n* if this scale is exceeded? By default it is clamped to 0..1, so the return value is always between `a` and `b` (inclusive).\n* \n* Alternatively, set the `limits` option to process `amount`:\n* * 'wrap': wrap amount, eg 1.5 is the same as 0.5, 2 is the same as 1\n* * 'ignore': allow exceeding values. eg 1.5 will yield b*1.5.\n* * 'clamp': default behaviour of clamping interpolation amount to 0..1\n* \n* Interpolation can be non-linear using 'easing' option or 'transform' funciton.\n* ```js\n* interpolate(0.1, 0, 100, { easing: `quadIn` });\n* ```\n* There are a few variations when calling `interpolate`, depending on what parameters are fixed.\n* * `interpolate(amount)`: returns a function that needs a & b \n* * `interpolate(a, b)`: returns a function that needs the interpolation amount\n*/\nfunction interpolate(pos1, pos2, pos3, pos4) {\n\tlet amountProcess;\n\tlet limits = `clamp`;\n\tconst handleAmount = (amount) => {\n\t\tif (amountProcess) amount = amountProcess(amount);\n\t\tif (limits === void 0 || limits === `clamp`) amount = clamp(amount);\n\t\telse if (limits === `wrap`) {\n\t\t\tif (amount > 1) amount = amount % 1;\n\t\t\telse if (amount < 0) amount = 1 + amount % 1;\n\t\t}\n\t\treturn amount;\n\t};\n\tconst doTheEase = (_amt, _a, _b) => {\n\t\tresultThrow(numberTest(_a, ``, `a`), numberTest(_b, ``, `b`), numberTest(_amt, ``, `amount`));\n\t\t_amt = handleAmount(_amt);\n\t\treturn (1 - _amt) * _a + _amt * _b;\n\t};\n\tconst readOpts = (o = {}) => {\n\t\tif (o.transform !== void 0) {\n\t\t\tif (typeof o.transform !== `function`) throw new Error(`Param 'transform' is expected to be a function. Got: ${typeof o.transform}`);\n\t\t\tamountProcess = o.transform;\n\t\t}\n\t\tlimits = o.limits ?? `clamp`;\n\t};\n\tconst rawEase = (_amt, _a, _b) => (1 - _amt) * _a + _amt * _b;\n\tif (typeof pos1 !== `number`) throw new TypeError(`First param is expected to be a number. Got: ${typeof pos1}`);\n\tif (typeof pos2 === `number`) {\n\t\tlet a;\n\t\tlet b;\n\t\tif (pos3 === void 0 || typeof pos3 === `object`) {\n\t\t\ta = pos1;\n\t\t\tb = pos2;\n\t\t\treadOpts(pos3);\n\t\t\treturn (amount) => doTheEase(amount, a, b);\n\t\t} else if (typeof pos3 === `number`) {\n\t\t\ta = pos2;\n\t\t\tb = pos3;\n\t\t\treadOpts(pos4);\n\t\t\treturn doTheEase(pos1, a, b);\n\t\t} else throw new Error(`Values for 'a' and 'b' not defined`);\n\t} else if (pos2 === void 0 || typeof pos2 === `object`) {\n\t\tconst amount = handleAmount(pos1);\n\t\treadOpts(pos2);\n\t\tresultThrow(numberTest(amount, ``, `amount`));\n\t\treturn (aValue, bValue) => rawEase(amount, aValue, bValue);\n\t}\n}\n/**\n* Returns a function that interpolates from A to B.\n* It steps through the interpolation with each call to the returned function.\n* This means that the `incrementAmount` will hinge on the rate\n* at which the function is called. Alternatively, consider {@link https://api.ixfx.fun/_ixfx/modulation/interpolatorInterval/}\n* which steps on the basis of clock time.\n* \n* ```js\n* // Interpolate from 0..1 by 0.01\n* const v = interpolatorStepped(0.01, 100, 200);\n* v(); // Each call returns a value closer to target\n* // Eg: 100, 110, 120, 130 ...\n* ```\n* \n* Under the hood, it calls `interpolate` with an amount that\n* increases by `incrementAmount` each time.\n* \n* When calling `v()` to step the interpolator, you can also pass\n* in new B and A values. Note that the order is swapped: the B (target) is provided first, and\n* then optionally A.\n* \n* ```js\n* const v = interpolatorStepped(0.1, 100, 200); // Interpolate 100->200\n* v(300, 200); // Retarget to 200->300 and return result\n* v(150); // Retarget 200->150 and return result\n* ```\n* \n* This allows you to maintain the current interpolation progress.\n* @param incrementAmount Amount to increment by\n* @param a Start value. Default: 0\n* @param b End value. Default: 1\n* @param startInterpolationAt Starting interpolation amount. Default: 0\n* @param options Options for interpolation\n* @returns \n*/\nconst interpolatorStepped = (incrementAmount, a = 0, b = 1, startInterpolationAt = 0, options) => {\n\tlet amount = startInterpolationAt;\n\treturn (retargetB, retargetA) => {\n\t\tif (retargetB !== void 0) b = retargetB;\n\t\tif (retargetA !== void 0) a = retargetA;\n\t\tif (amount >= 1) return b;\n\t\tconst value = interpolate(amount, a, b, options);\n\t\tamount += incrementAmount;\n\t\treturn value;\n\t};\n};\n/**\n* Interpolate between angles `a` and `b` by `amount`. Angles are in radians.\n*\n* ```js\n* interpolateAngle(0.5, Math.PI, Math.PI/2);\n* ```\n* @param amount\n* @param aRadians Start angle (radian)\n* @param bRadians End angle (radian)\n* @returns\n*/\nconst interpolateAngle = (amount, aRadians, bRadians, options) => {\n\tconst t = wrap(bRadians - aRadians, 0, piPi);\n\treturn interpolate(amount, aRadians, aRadians + (t > Math.PI ? t - piPi : t), options);\n};\n//#endregion\n//#region src/iqr.ts\n/**\n* Calculate interquartile range.\n* \n* If `n` is unspecified, `data.length` is used.\n* @param data \n* @param n \n* @returns \n*/\nconst interquartileRange = (data, n) => {\n\treturn getQuantile(data, .75) - getQuantile(data, .25);\n};\n/**\n* Returns a function which itself returns _true_ if a value is an outlier.\n* \n* This can be used for example to get a copy of an array without outliers:\n* ```js\n* const p = computeIsOutlier(someData);\n* const someDataWithoutOutliers = someData.filter(value => !p(value));\n* ```\n* \n* Outliers are defined as: \"a point which falls more than 1.5 times the interquartile range above the third quartile or below the first quartile.\" [Wolfram](https://mathworld.wolfram.com/Outlier.html)\n* \n* If array length is less than 4, no value will be considered an outlier.\n* @param data Data to filter\n* @param multiplier Multiplier of Q3 Q1. Default: 1.5 \n* @returns \n*/\nconst computeIsOutlier = (data, multiplier = 1.5) => {\n\tif (data.length < 4) return (value) => false;\n\tconst values = data.toSorted((a, b) => a - b);\n\tconst q1 = getQuantile(values, .25, true);\n\tconst q3 = getQuantile(values, .75, true);\n\tconst iqr = q3 - q1;\n\tconst maxValue = q3 + iqr * multiplier;\n\tconst minValue = q1 - iqr * multiplier;\n\treturn (value) => value < minValue || value > maxValue;\n};\n/**\n* Gets the value at a specific quantile\n* ```js\n* getQuantile(data, 25); // 1st quartile\n* getQuantile(data, 75); // 3rd quartile\n* ```\n* @param data \n* @param quantile \n* @param presorted Pass _true_ if `data` is already sorted\n* @returns \n*/\nconst getQuantile = (data, quantile, presorted = false) => {\n\tif (quantile > 1 || quantile < 0) throw new TypeError(`Param 'quantile' is expected to be in 0..1 range. Got: '${quantile}'`);\n\tif (!Array.isArray(data)) throw new TypeError(`Param 'data' is expected to be an array. Got: ${typeof data}`);\n\tconst index = quantile * (data.length - 1);\n\tif (!presorted) data = data.toSorted((a, b) => a - b);\n\tif (quantile === 0) return data[0];\n\tif (quantile === 1) return data[data.length - 1];\n\tif (index % 1 === 0) return data[index];\n\tconst lowerIndex = Math.floor(index);\n\tif (data[lowerIndex + 1] !== void 0) return (data[lowerIndex] + data[lowerIndex + 1]) / 2;\n\treturn data[lowerIndex];\n};\n//#endregion\n//#region src/round.ts\n/**\n* Rounds a number.\n*\n* If one parameter is given, it's the decimal places,\n* and a rounding function is returned:\n* ```js\n* const r = round(2);\n* r(10.12355); // 10.12\n* ```\n*\n* If two parameters are given, the first is decimal places,\n* the second the value to round.\n* ```js\n* round(2, 10.12355); // 10.12\n* ```\n* @param decimalPlaces\n* @returns\n*/\nfunction round(a, b, roundUp) {\n\tresultThrow(integerTest(a, `positive`, `decimalPlaces`));\n\tconst up = typeof b === `boolean` ? b : roundUp ?? false;\n\tlet rounder;\n\tif (a === 0) rounder = Math.round;\n\telse {\n\t\tconst p = Math.pow(10, a);\n\t\tif (up) rounder = (v) => Math.ceil(v * p) / p;\n\t\telse rounder = (v) => Math.floor(v * p) / p;\n\t}\n\tif (typeof b === `number`) return rounder(b);\n\treturn rounder;\n}\n//#endregion\n//#region src/linear-space.ts\n/**\n* Generates a `step`-length series of values between `start` and `end` (inclusive).\n* Each value will be equally spaced.\n*\n* ```js\n* for (const v of linearSpace(1, 5, 6)) {\n* // Yields: [ 1, 1.8, 2.6, 3.4, 4.2, 5 ]\n* }\n* ```\n*\n* Numbers can be produced from large to small as well\n* ```js\n* const values = [...linearSpace(10, 5, 3)];\n* // Yields: [10, 7.5, 5]\n* ```\n* @param start Start number (inclusive)\n* @param end End number (inclusive)\n* @param steps How many steps to make from start -> end\n* @param precision Number of decimal points to round to\n*/\nfunction* linearSpace(start, end, steps, precision) {\n\tresultThrow(numberTest(start, ``, `start`), numberTest(end, ``, `end`), numberTest(steps, ``, `steps`));\n\tconst r = precision ? round(precision) : (v) => v;\n\tconst step = (end - start) / (steps - 1);\n\tresultThrow(numberTest(step, ``, `step`));\n\tif (!Number.isFinite(step)) throw new TypeError(`Calculated step value is infinite`);\n\tfor (let index = 0; index < steps; index++) yield r(start + step * index);\n}\n//#endregion\n//#region src/moving-average.ts\nconst PiPi = Math.PI * 2;\n/**\n* A moving average calculator (exponential weighted moving average) which does not keep track of\n* previous samples. Less accurate, but uses less system resources.\n*\n* The `scaling` parameter determines smoothing. A value of `1` means that\n* the latest value is used as the average - that is, no smoothing. Higher numbers\n* introduce progressively more smoothing by weighting the accumulated prior average more heavily.\n*\n* ```\n* const ma = movingAverageLight(); // default scaling of 3\n* ma(50); // 50\n* ma(100); // 75\n* ma(75); // 75\n* ma(0); // 50\n* ```\n*\n* Note that the final average of 50 is pretty far from the last value of 0. To make it more responsive,\n* we could use a lower scaling factor: `movingAverageLight(2)`. This yields a final average of `37.5` instead.\n*\n* @param scaling Scaling factor. 1 is no smoothing. Default: 3\n* @returns Function that adds to average.\n*/\nconst movingAverageLight = (scaling = 3) => {\n\tresultThrow(numberTest(scaling, `aboveZero`, `scaling`));\n\tlet average = 0;\n\tlet count = 0;\n\treturn (v) => {\n\t\tif (numberTest(v, ``, `v`).success && v !== void 0) {\n\t\t\tcount++;\n\t\t\taverage = average + (v - average) / Math.min(count, scaling);\n\t\t}\n\t\treturn average;\n\t};\n};\n/**\n* Creates a moving average for a set number of `samples`.\n* It returns a function which in turn yields an average value.\n* \n* Moving average are useful for computing the average over a recent set of numbers.\n* A lower number of samples produces a computed value that is lower-latency yet more jittery.\n* A higher number of samples produces a smoother computed value which takes longer to respond to\n* changes in data.\n*\n* Sample size is considered with respect to the level of latency/smoothness trade-off, and also\n* the rate at which new data is added to the moving average.\n*\n*\n* ```js\n* const ma = movingAverage(10);\n* ma(10); // 10\n* ma(5); // 7.5\n* ```\n*\n* A weighting function can be provided to shape how the average is\n* calculated - eg privileging the most recent data over older data.\n* It uses `Arrays.averageWeighted` under the hood.\n*\n* ```js\n* import { movingAverage } from '@ixfx/numbers.js';\n* import { gaussian } from '@ixfx/modulation.js';\n* \n* // Give more weight to data in middle of sampling window\n* const ma = movingAverage(100, gaussian());\n* ```\n*\n* Because it keeps track of `samples` previous data, there is a memory impact. A lighter version is {@link movingAverageLight} which does not keep a buffer of prior data, but can't be as easily fine-tuned.\n* @param samplesOrOptions Number of samples to compute average from, or object of options\n* @returns\n*/\nconst movingAverage = (samplesOrOptions) => movingAverageWithContext(samplesOrOptions).seen;\nconst movingAverageWithContext = (samplesOrOptions) => {\n\tconst nanPolicy = typeof samplesOrOptions === `number` ? `ignore` : samplesOrOptions.nanPolicy ?? `ignore`;\n\tconst w = movingWindowWithContext(samplesOrOptions);\n\tconst averageFunction = typeof samplesOrOptions === `number` ? average : samplesOrOptions.weighter ? averageWeigher(samplesOrOptions.weighter) : average;\n\tconst seen = (value) => {\n\t\tif (Number.isNaN(value)) {\n\t\t\tif (nanPolicy === `throw`) throw new TypeError(`Value is NaN`);\n\t\t\tif (nanPolicy === `ignore`) return w.data;\n\t\t}\n\t\treturn averageFunction(w.seen(value));\n\t};\n\treturn {\n\t\tseen,\n\t\tget data() {\n\t\t\treturn [...w.data];\n\t\t},\n\t\tget average() {\n\t\t\treturn averageFunction(w.data);\n\t\t}\n\t};\n};\nconst smoothingFactor = (timeDelta, cutoff) => {\n\tconst r = PiPi * cutoff * timeDelta;\n\treturn r / (r + 1);\n};\nconst exponentialSmoothing = (smoothingFactor, value, previous) => {\n\treturn smoothingFactor * value + (1 - smoothingFactor) * previous;\n};\n/**\n* Noise filtering\n* \n* Algorithm: https://gery.casiez.net/1euro/\n* \n* Based on [Jaan Tollander de Balsch's implementation](https://jaantollander.com/post/noise-filtering-using-one-euro-filter/)\n* @param cutoffMin Default: 1\n* @param speedCoefficient Default: 0\n* @param cutoffDefault Default: 1\n*/\nconst noiseFilter = (cutoffMin = 1, speedCoefficient = 0, cutoffDefault = 1) => {\n\tlet previousValue = 0;\n\tlet derivativeLast = 0;\n\tlet timestampLast = 0;\n\tconst compute = (value, timestamp) => {\n\t\ttimestamp ??= performance.now();\n\t\tconst timeDelta = timestamp - timestampLast;\n\t\tconst derivative = exponentialSmoothing(smoothingFactor(timeDelta, cutoffDefault), (value - previousValue) / timeDelta, derivativeLast);\n\t\tconst smoothed = exponentialSmoothing(smoothingFactor(timeDelta, cutoffMin + speedCoefficient * Math.abs(derivative)), value, previousValue);\n\t\tpreviousValue = smoothed;\n\t\tderivativeLast = derivative;\n\t\ttimestampLast = timestamp;\n\t\treturn smoothed;\n\t};\n\treturn compute;\n};\n//#endregion\n//#region src/number-array-compute.ts\n/**\n* Calculate the min, max, total, average and count of input array `data`.\n* ```js\n* const { total, min, max, avg, count } = numberArrayCompute([ 1, 2, 3 ]);\n* ```\n* @param data \n* @param opts \n* @returns \n*/\nconst numberArrayCompute = (data, opts = {}) => {\n\tif (data.length === 0) return {\n\t\ttotal: NaN,\n\t\tmin: NaN,\n\t\tmax: NaN,\n\t\tavg: NaN,\n\t\tcount: NaN\n\t};\n\tconst nonNumbers = opts.nonNumbers ?? `throw`;\n\tlet total = 0;\n\tlet min = Number.MAX_SAFE_INTEGER;\n\tlet max = Number.MIN_SAFE_INTEGER;\n\tlet count = 0;\n\tfor (let index = 0; index < data.length; index++) {\n\t\tlet value = data[index];\n\t\tif (typeof value !== `number`) {\n\t\t\tif (nonNumbers === `ignore`) continue;\n\t\t\tif (nonNumbers === `throw`) throw new Error(`Param 'data' contains a non-number at index: ${index.toString()}`);\n\t\t\tif (nonNumbers === `nan`) value = NaN;\n\t\t}\n\t\tif (Number.isNaN(value)) continue;\n\t\tif (value !== void 0) {\n\t\t\tmin = Math.min(min, value);\n\t\t\tmax = Math.max(max, value);\n\t\t\ttotal += value;\n\t\t\tcount++;\n\t\t}\n\t}\n\treturn {\n\t\ttotal,\n\t\tmax,\n\t\tmin,\n\t\tcount,\n\t\tavg: total / count\n\t};\n};\n//#endregion\n//#region src/normalise-minmax.ts\nvar normalise_minmax_exports = /* @__PURE__ */ __exportAll({\n\tarray: () => array$3,\n\tarrayWithContext: () => arrayWithContext$3,\n\tcompute: () => compute$2,\n\tstream: () => stream$1,\n\tstreamWithContext: () => streamWithContext$1\n});\n/**\n* Returns a function which can do min-max normalisation, baking-in the min and max values.\n* ```js\n* // Normalise with min value of 20, max of 100\n* const fn = compute(20, 100);\n* \n* // Use function with input value of 40\n* fn(40);\n* ```\n* \n* @param min Minimum value of range\n* @param max Maximum value of range\n* @param clamp Whether to clamp input value to min/max range. Default: _false_\n* @returns \n*/\nconst compute$2 = (min, max, clamp = false) => {\n\tconst range = max - min;\n\treturn (value) => {\n\t\tif (clamp && value < min) value = min;\n\t\tif (clamp && value > max) value = max;\n\t\treturn (value - min) / range;\n\t};\n};\n/**\n* Normalises an array using the [min-max](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization)) technique.\n* \n* This version returns additional context of the normalisation, alternatively use {@link array}\n*\n* ```js\n* const c = arrayWithContext(someValues);\n* c.values; // Array of normalised values\n* c.original; // Original input array\n* c.min / c.max / c.range\n* ```\n* \n* By default, computes min and max values based on contents of `values`. Clamping is not required\n* for this case, so it's _false_ by default.\n* \n* @param values Values\n* @param options Optionally uses 'minForced' and 'maxForced' properties to scale values instead of actual min/max values of data.\n*/\nconst arrayWithContext$3 = (values, options = {}) => {\n\tif (!Array.isArray(values)) throw new TypeError(`Param 'values' should be an array. Got: ${typeof values}`);\n\tlet clamp = false;\n\tlet minForced = NaN;\n\tlet maxForced = NaN;\n\tif (typeof options.minForced === `undefined` || typeof options.maxForced === `undefined`) {\n\t\tconst c = numberArrayCompute(values);\n\t\tminForced = options.minForced ?? c.min;\n\t\tmaxForced = options.maxForced ?? c.max;\n\t\tclamp = options.clamp ?? false;\n\t} else {\n\t\tclamp = options.clamp ?? true;\n\t\tminForced = options.minForced;\n\t\tmaxForced = options.maxForced;\n\t}\n\tresultThrow(numberTest(minForced), numberTest(maxForced));\n\tconst fn = compute$2(minForced, maxForced, clamp);\n\treturn {\n\t\tvalues: values.map(fn),\n\t\toriginal: values,\n\t\tmin: minForced,\n\t\tmax: maxForced,\n\t\trange: Math.abs(maxForced - minForced)\n\t};\n};\n/**\n* Normalises an array using the [min-max](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization)) technique.\n* By default uses the actual min/max of the array as the normalisation range. \n* \n* [ixfx Guide on Normalising](https://ixfx.fun/cleaning/normal/)\n*\n* Use {@link arrayWithContext} to get back the min/max/range and original values\n* \n* ```js\n* // Yields: [0.5, 0.1, 0.0, 0.9, 1]\n* Normalise.MinMax.array([5,1,0,9,10]);\n* ```\n*\n* `minForced` and/or `maxForced` can\n* be provided to use an arbitrary range.\n* \n* ```js\n* // Forced range 0-100\n* // Yields: [0.05, 0.01, 0.0, 0.09, 0.10]\n* Normalise.MinMax.array([5,1,0,9,10], { minForced: 0, maxForced: 100 });\n* ```\n*\n* Return values are clamped to always be 0-1, inclusive.\n*\n* @param values Values\n* @param options Options to override or min/max values.\n*/\nconst array$3 = (values, options = {}) => {\n\treturn arrayWithContext$3(values, options).values;\n};\n/**\n* [Min-max scaling](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization))\n* \n* A more advanced form of {@link stream}\n* \n* With this version\n* @example\n* ```js\n* const s = Normalise.MinMax.streamWithContext();\n* s.seen(2); // 1 (because 2 is highest seen)\n* s.seen(1); // 0 (because 1 is the lowest so far)\n* s.seen(1.5); // 0.5 (50% of range 1-2)\n* s.seen(0.5); // 0 (because it's the new lowest)\n* ```\n* \n* And the more advanced features\n* ```js\n* s.min / s.max / s.range\n* s.reset();\n* s.reset(10, 100);\n* ```\n* @returns\n*/\nconst streamWithContext$1 = (options = {}) => {\n\tlet min = options.minDefault ?? Number.MAX_SAFE_INTEGER;\n\tlet max = options.maxDefault ?? Number.MIN_SAFE_INTEGER;\n\tresultThrow(numberTest(min), numberTest(max));\n\treturn {\n\t\tseen: (v) => {\n\t\t\tresultThrow(numberTest(v));\n\t\t\tmin = Math.min(min, v);\n\t\t\tmax = Math.max(max, v);\n\t\t\tif (v === min && v === max) return 1;\n\t\t\tconst result = (v - min) / (max - min);\n\t\t\tif (Number.isNaN(result)) throw new Error(`Would return NaN. v: ${v} min: ${min} max: ${max}`);\n\t\t\treturn result;\n\t\t},\n\t\treset: (minDefault, maxDefault) => {\n\t\t\tmin = minDefault ?? Number.MAX_SAFE_INTEGER;\n\t\t\tmax = maxDefault ?? Number.MIN_SAFE_INTEGER;\n\t\t},\n\t\tget min() {\n\t\t\treturn min;\n\t\t},\n\t\tget max() {\n\t\t\treturn max;\n\t\t},\n\t\tget range() {\n\t\t\treturn Math.abs(max - min);\n\t\t}\n\t};\n};\n/**\n* Normalises numbers using the [min-max](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization)) technique.\n* \n* Adjusts min/max as new values are processed. Return values will be in the range of 0-1 (inclusive).\n*\n* [ixfx Guide on Normalising](https://ixfx.fun/cleaning/normal/)\n*\n* Use {@link streamWithContext} if you want to be able to check the min/max or reset the normaliser.\n* \n* @example\n* ```js\n* const s = Normalise.MinMax.stream();\n* s(2); // 1 (because 2 is highest seen)\n* s(1); // 0 (because 1 is the lowest so far)\n* s(1.5); // 0.5 (50% of range 1-2)\n* s(0.5); // 0 (because it's the new lowest)\n* ```\n*\n* Since normalisation is being adjusted as new min/max are encountered, it might\n* be that value normalised to 1 at one time is different to what normalises to 1\n* at a later time.\n*\n* If you already know what to expect of the number range, passing in `minDefault`\n* and `maxDefault` primes the normalisation.\n* ```js\n* const s = Normalise.MinMax.stream();\n* s(5); // 1, because it's the highest seen\n*\n* // With priming:\n* const s = Normalise.MinMax.stream({ minDefault:0, maxDefault:10 });\n* s(5); // 0.5, because we're expecting range 0-10\n* ```\n*\n* If a value exceeds the default range, normalisation adjusts.\n* Errors are thrown if min/max defaults are NaN or if one attempts to\n* normalise NaN.\n* \n* @returns\n*/\nconst stream$1 = (options) => streamWithContext$1(options).seen;\n//#endregion\n//#region src/standard-deviation.ts\n/**\n* Calculates the standard deviation of an array of numbers.\n* \n* If you already have the mean value of the array, this can be passed in.\n* Otherwise it will be computed.\n* \n* If `usePopulation` is true, `array` is assumed to be the entire population (same as Excel's STDEV.P function)\n* Otherwise, it's like Excel's STDEV.S function which assumes data represents a sample of entire population.\n* \n* @param array Array of values\n* @param meanValue Mean value if pre-computed, otherwise skip this parameter for it to be computed automatically\n* @param usePopulation If _true_ result is similar to Excel's STDEV.P. Otherwise like STDEV.S\n* @returns \n*/\nconst standardDeviation = (array, usePopulation = false, meanValue) => {\n\tconst meanV = typeof meanValue === `undefined` ? mean(array) : meanValue;\n\treturn Math.sqrt(array.reduce((accumulator, value) => accumulator.concat((value - meanV) ** 2), []).reduce((accumulator, value) => accumulator + value, 0) / (array.length - (usePopulation ? 0 : 1)));\n};\n//#endregion\n//#region src/normalise-zscore.ts\nvar normalise_zscore_exports = /* @__PURE__ */ __exportAll({\n\tarray: () => array$2,\n\tarrayWithContext: () => arrayWithContext$2,\n\tcompute: () => compute$1\n});\n/**\n* Returns a function that computes zscore-based normalisation.\n* \n* ```js\n* // Calculate necessary components\n* const m = mean(data);\n* const s = standardDeviation(data);\n* \n* // Get the function\n* const fn = compute(m, s);\n* \n* // Use it\n* fn(10); // Yields the normalised value\n* ```\n* \n* It can be used to normalise a whole array\n* ```js\n* const normalised = someData.map(fn);\n* ```\n* \n* If you want to calculate for a whole array, use {@link array}.\n* @param mean Mean of data\n* @param standardDeviation Standard deviation of data\n* @returns \n*/\nconst compute$1 = (mean, standardDeviation) => (value) => (value - mean) / standardDeviation;\n/**\n* Returns the an array of normalised values, along with the mean and standard deviation of `array`.\n* If you just want the computed results, use {@link Normalise.ZScore.array}.\n* \n* By default it will compute mean and std.dev based on `array`. If you have these already, they\n* can be passed as options.\n* @param array \n* @returns \n*/\nconst arrayWithContext$2 = (array, options = {}) => {\n\tconst m = options.meanForced ?? mean(array);\n\tconst s = options.standardDeviationForced ?? standardDeviation(array);\n\tconst fn = compute$1(m, s);\n\treturn {\n\t\tmean: m,\n\t\tstandardDeviation: s,\n\t\tvalues: array.map(fn),\n\t\toriginal: array\n\t};\n};\n/**\n* Returns an array of normalised values using the 'z score' algorithm.\n* \n* By default it will compute mean and std.dev based on `array`. If you have these already, they\n* can be passed as options.\n* @param values \n* @param options \n* @returns \n*/\nconst array$2 = (values, options = {}) => arrayWithContext$2(values, options).values;\n//#endregion\n//#region src/normalise-robust.ts\nvar normalise_robust_exports = /* @__PURE__ */ __exportAll({\n\tarray: () => array$1,\n\tarrayWithContext: () => arrayWithContext$1,\n\tcompute: () => compute\n});\n/**\n* Calculates 'robust scaling' of a single value, `x`, based on provided mean and standard deviation.\n* \n* ```js\n* const m = median(someData);\n* const i = interquartileRange(someData);\n* const fn = compute(m, i);\n* \n* // Use normaliser function\n* fn(10);\n* ```\n* If you want to calculate for a whole array, use {@link array}.\n* @param median Median of data\n* @param iqr Interquartile range of data\n* @returns \n*/\nconst compute = (median, iqr) => (value) => (value - median) / iqr;\n/**\n* Returns the an array of normalised values, along with the mean and standard deviation of `array`.\n* If you just want the computed results, use {@link Normalise.Robust.array}.\n* \n* By default it will compute mean and std.dev based on `array`. If you have these already, they\n* can be passed as options.\n* @param array \n* @returns \n*/\nconst arrayWithContext$1 = (array, options = {}) => {\n\tif (!Array.isArray(array)) throw new TypeError(`Param 'array' is expected to be an array. Got: ${typeof array}`);\n\tconst m = options.medianForced ?? median(array);\n\tconst iqr = options.iqrForced ?? interquartileRange(array);\n\tconst fn = compute(m, iqr);\n\treturn {\n\t\tmedian: m,\n\t\tiqr,\n\t\tvalues: array.map(fn),\n\t\toriginal: array\n\t};\n};\n/**\n* Returns an array of normalised values using the 'z score' algorithm.\n* \n* By default it will compute mean and std.dev based on `array`. If you have these already, they\n* can be passed as options.\n* @param values \n* @param options \n* @returns \n*/\nconst array$1 = (values, options = {}) => arrayWithContext$1(values, options).values;\n//#endregion\n//#region src/normalise.ts\nvar normalise_exports = /* @__PURE__ */ __exportAll({\n\tMinMax: () => normalise_minmax_exports,\n\tRobust: () => normalise_robust_exports,\n\tZScore: () => normalise_zscore_exports,\n\tarray: () => array,\n\tarrayWithContext: () => arrayWithContext,\n\tstream: () => stream,\n\tstreamWithContext: () => streamWithContext\n});\n/**\n* Normalises numbers with additional context on the range.\n* \n* For more details, see:\n* * {@link MinMax.streamWithContext}\n* \n* @param strategy \n* @param options \n* @returns \n*/\nconst streamWithContext = (strategy, options = {}) => {\n\tswitch (strategy) {\n\t\tcase `minmax`: return streamWithContext$1(options);\n\t\tdefault: throw new Error(`Param 'strategy' has an unknown value: '${strategy}'. Expected: minmax`);\n\t}\n};\n/**\n* Normalises numbers. Return values will be in the range of 0-1 (inclusive).\n*\n* [ixfx Guide on Normalising](https://ixfx.fun/cleaning/normal/)\n*\n* Use {@link streamWithContext} if you want to be able to check the min/max or reset the normaliser.\n* \n* @example\n* ```js\n* const s = Normalise.stream(`minmax`);\n* s(2); // 1 (because 2 is highest seen)\n* s(1); // 0 (because 1 is the lowest so far)\n* s(1.5); // 0.5 (50% of range 1-2)\n* s(0.5); // 0 (because it's the new lowest)\n* ```\n*\n* For more details, see:\n* * {@link MinMax.stream}\n* @returns\n*/\nconst stream = (strategy = `minmax`, options = {}) => {\n\tswitch (strategy) {\n\t\tcase `minmax`: return stream$1(options);\n\t\tdefault: throw new Error(`Param 'strategy' has an unknown value: '${strategy}'. Expected: minmax`);\n\t}\n};\n/**\n* Normalise an array of values with added context, depending on strategy.\n* \n* Strategies are available: minmax, zscore & robust\n* \n* [ixfx Guide on Normalising](https://ixfx.fun/cleaning/normal/)\n*\n* Use {@link array} to get back the min/max/range and original values\n* \n* ```js\n* const { values, min, max, range } = Normalise.arrayWithContext(`minmax`, [5,1,0,9,10]);\n* // values will be normalised output\n* ```\n* \n* For more details, see:\n* * {@link MinMax.array}\n* * {@link ZScore.array}\n* * {@link Robust.array}\n* @param strategy \n* @param values \n* @param options \n* @returns \n*/\nconst arrayWithContext = (strategy, values, options = {}) => {\n\tswitch (strategy) {\n\t\tcase `minmax`: return arrayWithContext$3(values, options);\n\t\tcase `zscore`: return arrayWithContext$2(values, options);\n\t\tcase `robust`: return arrayWithContext$1(values, options);\n\t\tdefault: throw new Error(`Param 'strategy' has an unknown value: '${strategy}'. Expected: minmax|zscore`);\n\t}\n};\n/**\n* Normalise an array of values.\n* \n* Strategies are available: minmax, zscore & robust\n* \n* [ixfx Guide on Normalising](https://ixfx.fun/cleaning/normal/)\n*\n* Use {@link arrayWithContext} to get back the min/max/range and original values\n* \n* ```js\n* // Yields: [0.5, 0.1, 0.0, 0.9, 1]\n* Normalise.array(`minmax`, [5,1,0,9,10]);\n* ```\n* \n* For more details, see:\n* * {@link MinMax.array} [Wikipedia](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization))\n* * {@link ZScore.array} [Wikipedia](https://en.wikipedia.org/wiki/Feature_scaling#Standardization_(Z-score_Normalization))\n* * {@link Robust.array} [Wikipedia](https://en.wikipedia.org/wiki/Feature_scaling#Robust_Scaling)\n* \n* @param strategy \n* @param values \n* @param options \n* @returns \n*/\nconst array = (strategy, values, options = {}) => arrayWithContext(strategy, values, options).values;\n//#endregion\n//#region src/proportion.ts\n/**\n* Scales a percentage-scale number, ie: `v * t`.\n* \n* The utility of this function is that it sanity-checks that\n* both parameters are in the 0..1 scale.\n* \n* Parameters can also be a function that takes no parameters\n* and returns a number. It will be invoked when `proportion` is called.\n* @param v Value\n* @param t Scale amount\n* @returns Scaled value\n*/\nconst proportion = (v, t) => {\n\tif (typeof v === `function`) v = v();\n\tif (typeof t === `function`) t = t();\n\tresultThrow(numberTest(v, `percentage`, `v`), numberTest(t, `percentage`, `t`));\n\treturn v * t;\n};\n//#endregion\n//#region src/quantise.ts\n/**\n* Rounds `v` by `every`. Middle values are rounded up by default.\n*\n* ```js\n* quantiseEvery(11, 10); // 10\n* quantiseEvery(25, 10); // 30\n* quantiseEvery(0, 10); // 0\n* quantiseEvery(4, 10); // 0\n* quantiseEvery(100, 10); // 100\n* ```\n* \n* Also works with decimals\n* ```js\n* quantiseEvery(1.123, 0.1); // 1.1\n* quantiseEvery(1.21, 0.1); // 1.2\n* ```\n*\n* @param v Value to quantise\n* @param every Number to quantise to\n* @param middleRoundsUp If _true_ (default), the exact middle rounds up to next step.\n* @returns\n*/\nconst quantiseEvery = (v, every, middleRoundsUp = true) => {\n\tconst everyString = every.toString();\n\tconst decimal = everyString.indexOf(`.`);\n\tlet multiplier = 1;\n\tif (decimal >= 0) {\n\t\tmultiplier = 10 * everyString.substring(decimal + 1).length;\n\t\tevery = Math.floor(multiplier * every);\n\t\tv = v * multiplier;\n\t}\n\tresultThrow(numberTest(v, ``, `v`), integerTest(every, ``, `every`));\n\tlet div = v / every;\n\tconst divModule = div % 1;\n\tdiv = Math.floor(div);\n\tif (divModule === .5 && middleRoundsUp || divModule > .5) div++;\n\treturn every * div / multiplier;\n};\n//#endregion\n//#region src/scale.ts\n/**\n* Scales `v` from an input range to an output range (aka `map`)\n*\n* For example, if a sensor's useful range is 100-500, scale it to a percentage:\n*\n* ```js\n*\n* scale(sensorReading, 100, 500, 0, 1);\n* ```\n*\n* `scale` defaults to a percentage-range output, so you can get away with:\n* ```js\n* scale(sensorReading, 100, 500);\n* ```\n*\n* If `v` is outside of the input range, it will likewise be outside of the output range.\n* Use {@link scaleClamped} to clip value to range.\n*\n* If inMin and inMax are equal, outMax will be returned.\n*\n* An easing function can be provided for non-linear scaling. In this case\n* the input value is 'pre scaled' using the function before it is applied to the\n* output range.\n*\n* ```js\n* scale(sensorReading, 100, 500, 0, 1, Easings.gaussian());\n* ```\n* @param v Value to scale\n* @param inMin Input minimum\n* @param inMax Input maximum\n* @param outMin Output minimum. If not specified, 0\n* @param outMax Output maximum. If not specified, 1\n* @param easing Easing function\n* @returns Scaled value\n*/\nconst scale = (v, inMin, inMax, outMin, outMax, easing) => scaler(inMin, inMax, outMin, outMax, easing)(v);\n/**\n* Returns a scaling function\n* @param inMin Input minimum\n* @param inMax Input maximum\n* @param outMin Output minimum. If not specified, 0\n* @param outMax Output maximum. If not specified, 1\n* @param easing Easing function\n* @param clamped If true, value is clamped. Default: false\n* @returns\n*/\nconst scaler = (inMin, inMax, outMin, outMax, easing, clamped) => {\n\tresultThrow(numberTest(inMin, `finite`, `inMin`), numberTest(inMax, `finite`, `inMax`));\n\tconst oMax = outMax ?? 1;\n\tconst oMin = outMin ?? 0;\n\tconst clampFunction = clamped ? clamper(outMin, outMax) : void 0;\n\treturn (v) => {\n\t\tif (inMin === inMax) return oMax;\n\t\tlet a = (v - inMin) / (inMax - inMin);\n\t\tif (easing !== void 0) a = easing(a);\n\t\tconst x = a * (oMax - oMin) + oMin;\n\t\tif (clampFunction) return clampFunction(x);\n\t\treturn x;\n\t};\n};\n/**\n* Returns a 'null' scaler that does nothing - the input value is returned as output.\n* @returns \n*/\nconst scalerNull = () => (v) => v;\n/**\n* As {@link scale}, but result is clamped to be\n* within `outMin` and `outMax`. Useful if you can't be sure\n* that `v` is in 0..1 range.\n*\n* @param value\n* @param inMin\n* @param inMax\n* @param outMin 1 by default\n* @param outMax 0 by default d\n* @param easing\n* @returns\n*/\nconst scaleClamped = (value, inMin, inMax, outMin, outMax, easing) => {\n\tif (typeof outMax === `undefined`) outMax = 1;\n\tif (typeof outMin === `undefined`) outMin = 0;\n\tif (inMin === inMax) return outMax;\n\treturn clamp(scale(value, inMin, inMax, outMin, outMax, easing), outMin, outMax);\n};\n/**\n* Scales an input percentage to a new percentage range.\n*\n* If you have an input percentage (0-1), `scalePercentageOutput` maps it to an\n* _output_ percentage of `outMin`-`outMax`.\n*\n* ```js\n* // Scales 50% to a range of 0-10%\n* scalePercentages(0.5, 0, 0.10); // 0.05 - 5%\n* ```\n*\n* An error is thrown if any parameter is outside of percentage range. This added\n* safety is useful for catching bugs. Otherwise, you could just as well call\n* `scale(percentage, 0, 1, outMin, outMax)`.\n*\n* If you want to scale some input range to percentage output range, just use `scale`:\n* ```js\n* // Yields 0.5\n* scale(2.5, 0, 5);\n* ```\n* @param percentage Input value, within percentage range\n* @param outMin Output minimum, between 0-1\n* @param outMax Output maximum, between 0-1\n* @returns Scaled value between outMin-outMax.\n*/\nconst scalePercentages = (percentage, outMin, outMax = 1) => {\n\tresultThrow(numberTest(percentage, `percentage`, `v`), numberTest(outMin, `percentage`, `outMin`), numberTest(outMax, `percentage`, `outMax`));\n\treturn scale(percentage, 0, 1, outMin, outMax);\n};\n/**\n* Scales an input percentage value to an output range\n* If you have an input percentage (0-1), `scalePercent` maps it to an output range of `outMin`-`outMax`.\n* ```js\n* scalePercent(0.5, 10, 20); // 15\n* ```\n*\n* @see {@link scalerPercent} Returns a function\n* @param v Value to scale\n* @param outMin Minimum for output\n* @param outMax Maximum for output\n* @returns\n*/\nconst scalePercent = (v, outMin, outMax) => scalerPercent(outMin, outMax)(v);\n/**\n* Returns a function that scales an input percentage value to an output range\n* @see {@link scalePercent} Calculates value\n* @param outMin\n* @param outMax\n* @returns Function that takes a single argument\n*/\nconst scalerPercent = (outMin, outMax) => {\n\treturn (v) => {\n\t\tresultThrow(numberTest(v, `percentage`, `v`));\n\t\treturn scale(v, 0, 1, outMin, outMax);\n\t};\n};\n/**\n* Returns a two-way scaler\n* ```js\n* // Input range 0..100, output range 0..1\n* const s = scalerTwoWay(0,100,0,1);\n* \n* // Scale from input to output\n* s.out(50); // 0.5\n* \n* // Scale from output range to input\n* s.in(1); // 100\n* ```\n* @param inMin \n* @param inMax \n* @param outMin \n* @param outMax \n* @returns \n*/\nconst scalerTwoWay = (inMin, inMax, outMin = 0, outMax = 1, clamped = false, easing) => {\n\treturn {\n\t\tout: scaler(inMin, inMax, outMin, outMax, easing, clamped),\n\t\tin: scaler(outMin, outMax, inMin, inMax, easing, clamped)\n\t};\n};\n//#endregion\n//#region src/range.ts\n/**\n* Computes min/max based on a new value and previous range.\n* Returns existing object reference if value is within existing range.\n* \n* If `value` is not a number, by default it will be ignored. Use the 'nonNumberHandling' param to set it\n* to throw an error instead if you want to catch that\n* @param value Value to compare against range\n* @param previous Previous range\n* @param nonNumberHandling 'skip' (default), non numbers are ignored; 'error' an error is thrown\n* @returns \n*/\nfunction rangeMergeValue(value, previous, nonNumberHandling = `skip`) {\n\tif (typeof value === `number`) {\n\t\tif (Number.isNaN(value) || !Number.isFinite(value)) {\n\t\t\tif (nonNumberHandling === `error`) throw new TypeError(`Param 'value' is NaN or infinite, and nonNumberHandling is set to 'error'`);\n\t\t\treturn previous;\n\t\t}\n\t\tif (value >= previous.min && value <= previous.max) return previous;\n\t\treturn {\n\t\t\tmin: Math.min(value, previous.min),\n\t\t\tmax: Math.max(value, previous.max)\n\t\t};\n\t} else if (nonNumberHandling === `error`) throw new TypeError(`Param 'value' is not a number (type: '${typeof value}') and nonNumberHandling is set to 'error'`);\n\treturn previous;\n}\n/**\n* Returns a function that scales values in a range, by default on 0..1 scale.\n* ```js\n* const range = { min: 10, max: 20 }\n* const s = rangeScaler(range);\n* s(15); // 0.5\n* ```\n* @param range Range to scale on\n* @param outMax Output range max. Default: 1\n* @param outMin Output range min. Default: 0\n* @param easing Easing function: Default: none\n* @param clamped Whether input values should be clamped if they exceed range. Default: true\n* @returns \n*/\nfunction rangeScaler(range, outMax = 1, outMin = 0, easing, clamped = true) {\n\treturn scaler(range.min, range.max, outMin, outMax, easing, clamped);\n}\n/**\n* Expands a range to encompass a new range.\n* Returns `existingRange` if `newRange` is within it.\n* @param newRange \n* @param existingRange \n* @returns \n*/\nfunction rangeMergeRange(newRange, existingRange) {\n\tif (newRange.max <= existingRange.max && newRange.min >= existingRange.min) return existingRange;\n\treturn {\n\t\tmin: Math.min(newRange.min, existingRange.min),\n\t\tmax: Math.max(newRange.max, existingRange.max)\n\t};\n}\n/**\n* Returns an empty range:\n* ```js\n* { \n* min: Number.MAX_SAFE_INTEGER, \n* max: Number.MIN_SAFE_INTEGER \n* }\n* ```\n* @returns \n*/\nconst rangeInit = () => ({\n\tmin: Number.MAX_SAFE_INTEGER,\n\tmax: Number.MIN_SAFE_INTEGER\n});\n/**\n* Returns _true_ if ranges `a` and `b` have identical min/max values.\n* Returns _false_ if not, or if either/both values are _undefined_\n* @param a \n* @param b \n* @returns \n*/\nconst rangeIsEqual = (a, b) => {\n\tif (typeof a === `undefined`) return false;\n\tif (typeof b === `undefined`) return false;\n\treturn a.max === b.max && a.min === b.min;\n};\n/**\n* Returns _true_ if range 'a' is within or same as range 'b'.\n* Returns _false_ if not or if either/both ranges are _undefined_\n* \n* ```js\n* rangeIsWithin({ min: 5, max: 10 }, { min: 0, max: 10 }); // true\n* rangeIsWithin({ min: 5, max: 10 }, { min: 6, max: 20 }); // false\n* ```\n* \n* By default the matching is inclusive, in that `a` could share a min/max with `b`.\n* If you want to check whether `a` is strictly within `b`, with a higher min and lower max, set `exclusive` to _true_.\n* \n* ```js\n* rangeIsWithin({ min: 5, max: 10 }, { min: 0, max: 10 }, true); // false\n* rangeIsWithin({ min: 5, max: 9 }, { min: 0, max: 10 }, true); // true\n* ```\n* \n* If either `a` or `b` is _undefined_, the function returns _false_.\n* @param a Range\n* @param b Parent\n* @param exclusive If _true_, \n* @returns \n*/\nconst rangeIsWithin = (a, b, exclusive = false) => {\n\tif (typeof a === `undefined`) return false;\n\tif (typeof b === `undefined`) return false;\n\tif (exclusive) return a.min > b.min && a.max < b.max;\n\treturn a.min >= b.min && a.max <= b.max;\n};\n/**\n* Keeps track of min/max values.\n* \n* ```js\n* const s = rangeStream();\n* s.seen(10); // { min: 10, max: 10 }\n* s.seen(5); // { min: 5, max: 10 }\n* ```\n* \n* When calling `seen()`, non-numbers, or non-finite numbers are silently ignored.\n* \n* ```js\n* s.reset(); // Reset\n* s.min/s.max; // Current min/max\n* s.range; // Current { min, max }\n* ```\n* @param initWith \n* @returns \n*/\nconst rangeStream = (initWith = rangeInit()) => {\n\tlet { min, max } = initWith;\n\tconst seen = (v) => {\n\t\tif (typeof v === `number`) {\n\t\t\tif (!Number.isNaN(v) && Number.isFinite(v)) {\n\t\t\t\tmin = Math.min(min, v);\n\t\t\t\tmax = Math.max(max, v);\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tmin,\n\t\t\tmax\n\t\t};\n\t};\n\tconst reset = () => {\n\t\tmin = Number.MAX_SAFE_INTEGER;\n\t\tmax = Number.MIN_SAFE_INTEGER;\n\t\treturn {\n\t\t\tmin,\n\t\t\tmax\n\t\t};\n\t};\n\treturn {\n\t\tseen,\n\t\treset,\n\t\tget range() {\n\t\t\treturn {\n\t\t\t\tmin,\n\t\t\t\tmax\n\t\t\t};\n\t\t},\n\t\tget min() {\n\t\t\treturn min;\n\t\t},\n\t\tget max() {\n\t\t\treturn max;\n\t\t}\n\t};\n};\n/**\n* Iterates over `values` finding the min/max.\n* By default non-numbers, as well as NaN and infinite values are skipped.\n* @param values \n* @param nonNumberHandling \n* @returns \n*/\nfunction rangeCompute(values, nonNumberHandling = `skip`) {\n\tlet min = Number.MAX_SAFE_INTEGER;\n\tlet max = Number.MIN_SAFE_INTEGER;\n\tlet position = 0;\n\tfor (const v of values) {\n\t\tif (typeof v === `number`) {\n\t\t\tif (Number.isNaN(v) || !Number.isFinite(v)) {\n\t\t\t\tif (nonNumberHandling === `error`) throw new Error(`Value NaN or infinite at position: ${position}`);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else {\n\t\t\tif (nonNumberHandling === `error`) throw new Error(`Contains non number value. Type: '${typeof v}' Position: ${position}`);\n\t\t\tcontinue;\n\t\t}\n\t\tif (v < min) min = v;\n\t\tif (v > max) max = v;\n\t\tposition++;\n\t}\n\treturn {\n\t\tmin,\n\t\tmax\n\t};\n}\n//#endregion\n//#region src/softmax.ts\n/**\n* Via: https://gist.github.com/cyphunk/6c255fa05dd30e69f438a930faeb53fe\n* @param logits \n* @returns \n*/\nconst softmax = (logits) => {\n\tconst maxLogit = logits.reduce((a, b) => Math.max(a, b), Number.NEGATIVE_INFINITY);\n\tconst scores = logits.map((l) => Math.exp(l - maxLogit));\n\tconst denom = scores.reduce((a, b) => a + b);\n\treturn scores.map((s) => s / denom);\n};\n//#endregion\n//#region src/track-simple.ts\n/**\n* Track values\n* \n* When not yet used:\n* total: 0\n* count: 0\n* min: MAX_SAFE_INTEGER,\n* max: MIN_SAFE_INTEGER\n* @returns \n*/\nconst trackSimple = () => {\n\tlet count = 0;\n\tlet min = Number.MAX_SAFE_INTEGER;\n\tlet max = Number.MIN_SAFE_INTEGER;\n\tlet total = 0;\n\tconst seen = (v) => {\n\t\tmin = Math.min(v, min);\n\t\tmax = Math.max(v, max);\n\t\ttotal += v;\n\t\tcount++;\n\t};\n\tconst reset = () => {\n\t\tcount = 0;\n\t\tmin = Number.MAX_SAFE_INTEGER;\n\t\tmax = Number.MIN_SAFE_INTEGER;\n\t\ttotal = 0;\n\t};\n\tconst rangeToString = (digits = 2) => {\n\t\treturn `${min.toFixed(2)} - ${max.toFixed(2)}`;\n\t};\n\treturn {\n\t\tseen,\n\t\treset,\n\t\trangeToString,\n\t\tget avg() {\n\t\t\treturn total / count;\n\t\t},\n\t\tget min() {\n\t\t\treturn min;\n\t\t},\n\t\tget max() {\n\t\t\treturn max;\n\t\t},\n\t\tget total() {\n\t\t\treturn total;\n\t\t},\n\t\tget count() {\n\t\t\treturn count;\n\t\t}\n\t};\n};\n//#endregion\nexport { bipolar_exports as Bipolar, Kalman1dFilter, normalise_exports as Normalise, applyToValues, average, averageWeigher, averageWeighted, clamp, clampIndex, clamper, computeIsOutlier, count, differenceFromFixed, differenceFromLast, dotProduct, filterIterable, flip, getQuantile, interpolate, interpolateAngle, interpolatorStepped, interquartileRange, isApprox, isCloseToAny, isValid, kalman1dFilter, linearSpace, max, maxAbs, maxFast, maxIndex, mean, median, min, minFast, minIndex, movingAverage, movingAverageLight, movingAverageWithContext, noiseFilter, numberArrayCompute, numericPercent, numericRange, numericRangeRaw, proportion, quantiseEvery, rangeCompute, rangeInclusive, rangeInit, rangeIsEqual, rangeIsWithin, rangeMergeRange, rangeMergeValue, rangeScaler, rangeStream, round, scale, scaleClamped, scalePercent, scalePercentages, scaler, scalerNull, scalerPercent, scalerTwoWay, softmax, standardDeviation, thresholdAtLeast, total, totalFast, trackSimple, validNumbers, weight, wrap, wrapInteger, wrapRange };\n"],"x_google_ignoreList":[0,1,2],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dist-D7kJgnSE.js","names":["resultThrow","IxfxError","resultToError","functionTest"],"sources":["../node_modules/.pnpm/@ixfx+guards@0.56.12/node_modules/@ixfx/guards/dist/index.js","../node_modules/.pnpm/@ixfx+arrays@0.56.12/node_modules/@ixfx/arrays/dist/index.js","../node_modules/.pnpm/@ixfx+numbers@0.56.12/node_modules/@ixfx/numbers/dist/index.js"],"sourcesContent":["//#region src/result.ts\nfunction getErrorMessage(ex) {\n\tif (typeof ex === `string`) return ex;\n\tif (ex instanceof Error) return ex.message;\n\treturn String(ex);\n}\n/**\n* Throws an error if any result is a failure.\n* Error message will be the combined from all errors.\n* @param results\n*/\nfunction throwIfFailed(...results) {\n\tconst failed = results.filter((r) => resultIsError(r));\n\tif (failed.length === 0) return;\n\tconst messages = failed.map((f) => resultErrorToString(f));\n\tthrow new Error(messages.join(`, `));\n}\n/**\n* If any of `results` is an error, throws it, otherwise ignored.\n* @param results\n* @returns _true_ or throws\n*/\nfunction resultThrow(...results) {\n\tfor (const r of results) {\n\t\tif (r === void 0) continue;\n\t\tif (typeof r === `boolean`) if (!r) throw IxfxError.fromString(`Guard failed: false result`);\n\t\telse continue;\n\t\tconst rr = typeof r === `object` ? r : r();\n\t\tif (rr === void 0) continue;\n\t\tif (rr.success) continue;\n\t\tthrow resultToError(rr);\n\t}\n\treturn true;\n}\nfunction resultThrowSingle(result) {\n\tif (result.success) return true;\n\tthrow resultToError(result);\n}\n/**\n* Returns the first failed result, or _undefined_ if there are no fails\n* @param results\n*/\nfunction resultFirstFail_(...results) {\n\tfor (const r of results) {\n\t\tif (typeof r === `boolean`) {\n\t\t\tif (r) continue;\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Guard failed: false result`\n\t\t\t};\n\t\t}\n\t\tconst rr = typeof r === `object` ? r : r();\n\t\tif (rr === void 0) continue;\n\t\tif (!rr.success) return rr;\n\t}\n}\n/**\n* Returns _true_ if `result` is an error\n* @param result\n*/\nfunction resultIsError(result) {\n\tif (typeof result !== `object` || result === null) return false;\n\treturn !result.success;\n}\n/**\n* Returns _true_ if `result` is OK and has a value\n* @param result\n*/\nfunction resultIsOk(result) {\n\tif (typeof result !== `object` || result === null) return false;\n\treturn result.success;\n}\nvar IxfxError = class IxfxError extends Error {\n\tcause;\n\tconstructor(message, cause) {\n\t\tsuper(message);\n\t\tthis.cause = cause;\n\t}\n\tstatic fromError(error, cause) {\n\t\tconst message = error.message;\n\t\tconst stack = error.stack;\n\t\tconst name = error.name;\n\t\tconst newError = new IxfxError(message, cause);\n\t\tnewError.stack = stack;\n\t\tnewError.name = `IxfxError(${name})`;\n\t\treturn newError;\n\t}\n\tstatic fromString(message, cause) {\n\t\tconst newError = new IxfxError(message, cause);\n\t\tnewError.name = `IxfxError`;\n\t\treturn newError;\n\t}\n};\n/**\n* Gets the result as an Error\n* @param result\n*/\nfunction resultToError(result) {\n\tif (typeof result.error === `string`) return IxfxError.fromString(result.error, result.info);\n\tif (result.error instanceof Error) return IxfxError.fromError(result.error, result.info);\n\treturn IxfxError.fromString(JSON.stringify(result.error), result.info);\n}\n/**\n* Unwraps the result, returning its value if OK.\n* If not, an exception is thrown.\n* @param result\n*/\nfunction resultToValue(result) {\n\tif (resultIsOk(result)) return result.value;\n\tthrow resultToError(result);\n}\n/**\n* Returns the error as a string.\n* @param result\n*/\nfunction resultErrorToString(result) {\n\tif (result.error instanceof Error) return getErrorMessage(result.error);\n\tif (typeof result.error === `string`) return result.error;\n\treturn JSON.stringify(result.error);\n}\n/**\n* Returns a {@link ResultError} using 'error' as the message.\n* @param error\n* @param info\n*/\nfunction errorResult(error, info) {\n\treturn {\n\t\tsuccess: false,\n\t\terror,\n\t\tinfo\n\t};\n}\n/**\n* Returns first failed result or final value.\n* @param results\n*/\nfunction resultsCollate(...results) {\n\tlet rr;\n\tfor (const r of results) {\n\t\tif (typeof r === `boolean`) {\n\t\t\tif (r) continue;\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Guard failed: false result`\n\t\t\t};\n\t\t}\n\t\trr = typeof r === `object` ? r : r();\n\t\tif (rr === void 0) continue;\n\t\tif (!rr.success) return rr;\n\t}\n\tif (!rr) throw new Error(`No results`);\n\treturn rr;\n}\n/**\n* If `result` is an error, calls `callback`, passing the error.\n* Otherwise does nothing\n* @param result\n* @param callback\n*/\nfunction resultWithFail(result, callback) {\n\tif (resultIsError(result)) callback(result);\n}\n//#endregion\n//#region src/numbers.ts\n/**\n* Returns true if `x` is a power of two\n* @param x\n* @returns True if `x` is a power of two\n*/\nconst isPowerOfTwo = (x) => Math.log2(x) % 1 === 0;\n/**\n* Returns `fallback` if `v` is NaN, otherwise returns `v`.\n* \n* Throws if `v` is not a number type, null or undefined\n* @param v\n* @param fallback\n* @returns\n*/\nconst ifNaN = (v, fallback) => {\n\tif (typeof v !== `number`) throw new TypeError(`v is not a number. Got: ${typeof v}`);\n\tif (Number.isNaN(v)) return fallback;\n\treturn v;\n};\n/**\n* Parses `value` as an integer, returning it if it meets the `range` criteria.\n* If not, `defaultValue` is returned.\n*\n* ```js\n* const i = integerParse('10', 'positive'); // 10\n* const i = integerParse('10.5', 'positive'); // 10\n* const i = integerParse('0', 'nonZero', 100); // 100\n* ```\n*\n* NaN is returned if criteria does not match and no default is given\n* ```js\n* const i = integerParse('10', 'negative'); // NaN\n* ```\n*\n* @param value\n* @param range\n* @param defaultValue\n* @returns\n*/\nconst integerParse = (value, range = ``, defaultValue = NaN) => {\n\tif (typeof value === `undefined`) return defaultValue;\n\tif (value === null) return defaultValue;\n\ttry {\n\t\tconst parsed = Number.parseInt(typeof value === `number` ? value.toString() : value);\n\t\treturn integerTest(parsed, range, `parsed`).success ? parsed : defaultValue;\n\t} catch {\n\t\treturn defaultValue;\n\t}\n};\n/**\n* Checks if `t` is not a number or within specified range.\n* Returns `[false, reason:string]` if invalid or `[true]` if valid.\n* \n* Alternatives: {@link integerTest} for additional integer check, {@link percentTest} for percentage-range.\n*\n* * (empty, default): must be a number type and not NaN.\n* * finite: must be a number, not NaN and not infinite\n* * positive: must be at least zero\n* * negative: must be zero or lower\n* * aboveZero: must be above zero\n* * belowZero: must be below zero\n* * percentage: must be within 0-1, inclusive\n* * nonZero: can be anything except zero\n* * bipolar: can be -1 to 1, inclusive\n* @param value Value to check\n* @param parameterName Name of parameter (for more helpful exception messages)\n* @param range Range to enforce\n* @returns\n*/\nconst numberTest = (value, range = ``, parameterName = `?`, info) => {\n\tif (value === null) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is null`,\n\t\tinfo\n\t};\n\tif (typeof value === `undefined`) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is undefined`,\n\t\tinfo\n\t};\n\tif (Number.isNaN(value)) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is NaN`,\n\t\tinfo\n\t};\n\tif (typeof value !== `number`) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is not a number (${JSON.stringify(value)})`,\n\t\tinfo\n\t};\n\tswitch (range) {\n\t\tcase `finite`:\n\t\t\tif (!Number.isFinite(value)) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName} must be finite (Got: ${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `positive`:\n\t\t\tif (value < 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be at least zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `negative`:\n\t\t\tif (value > 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be zero or lower (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `aboveZero`:\n\t\t\tif (value <= 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be above zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `belowZero`:\n\t\t\tif (value >= 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be below zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `percentage`:\n\t\t\tif (value > 1 || value < 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be in percentage range (0 to 1). (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `nonZero`:\n\t\t\tif (value === 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must non-zero. (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `bipolar`:\n\t\t\tif (value > 1 || value < -1) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be in bipolar percentage range (-1 to 1). (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t}\n\treturn {\n\t\tsuccess: true,\n\t\tvalue,\n\t\tinfo\n\t};\n};\n/**\n* Checks if `t` is not a number or within specified range.\n* Throws if invalid. Use {@link numberTest} to test without throwing.\n*\n* * (empty, default): must be a number type and not NaN.\n* * positive: must be at least zero\n* * negative: must be zero or lower\n* * aboveZero: must be above zero\n* * belowZero: must be below zero\n* * percentage: must be within 0-1, inclusive\n* * nonZero: can be anything except zero\n* * bipolar: can be -1 to 1, inclusive\n* \n* Alternatives: {@link integerTest} for additional integer check, {@link percentTest} for percentage-range.\n* @param value Value to test\n* @param range Range\n* @param parameterName Name of parameter \n*/\n/**\n* Compares two numbers with a given number of decimal places\n* ```js\n* a: 10.123 b: 10.1 decimals: 1 = true\n* a: 10.123 b: 10.2 decimals: 0 = true\n* a: 10.123 b: 10.14 decimals: 1 = true\n* a: 10.123 b: 10.14 decimals: 2 = false\n* ``\n* @param a \n* @param b \n* @param decimals How many decimals to include\n* @returns \n*/\nconst numberDecimalTest = (a, b, decimals = 3) => {\n\tif (decimals === 0) {\n\t\ta = Math.floor(a);\n\t\tb = Math.floor(b);\n\t\tif (a === b) return {\n\t\t\tsuccess: true,\n\t\t\tvalue: a\n\t\t};\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: `A is not identical to B`\n\t\t};\n\t}\n\tconst mult = Math.pow(10, decimals);\n\tif (Math.floor(a * mult) !== Math.floor(b * mult)) return {\n\t\tsuccess: false,\n\t\terror: `A is not close enough to B. A: ${a} B: ${b} Decimals: ${decimals}`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: a\n\t};\n};\n/**\n* Returns test of `value` being in the range of 0-1.\n* Equiv to `number(value, `percentage`);`\n*\n* This is the same as calling ```number(t, `percentage`)```\n* @param value Value to check\n* @param parameterName Param name for customising exception message\n* @returns\n*/\nconst percentTest = (value, parameterName = `?`, info) => numberTest(value, `percentage`, parameterName, info);\n/**\n* Checks if `value` an integer and meets additional criteria.\n* See {@link numberTest} for guard details, or use that if integer checking is not required.\n*\n* Note:\n* * `bipolar` will mean -1, 0 or 1.\n* * positive: must be at least zero\n* * negative: must be zero or lower\n* * aboveZero: must be above zero\n* * belowZero: must be below zero\n* * percentage: must be within 0-1, inclusive\n* * nonZero: can be anything except zero\n* @param value Value to check\n* @param parameterName Param name for customising exception message\n* @param range Guard specifier.\n*/\nconst integerTest = (value, range = ``, parameterName = `?`) => {\n\treturn resultsCollate(numberTest(value, range, parameterName), () => {\n\t\tif (!Number.isInteger(value)) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is not an integer`\n\t\t};\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tvalue\n\t\t};\n\t});\n};\nconst integerArrayTest = (numbers) => {\n\tfor (const v of numbers) if (Math.abs(v) % 1 !== 0) return {\n\t\tsuccess: false,\n\t\terror: `Value is not an integer: ${v}`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: numbers\n\t};\n};\n/**\n* Returns _true_ if `value` is an integer in number or string form\n* @param value \n* @returns \n*/\nconst isInteger = (value) => {\n\tif (typeof value === `string`) value = Number.parseFloat(value);\n\treturn integerTest(value).success;\n};\nconst numberInclusiveRangeTest = (value, min, max, parameterName = `?`) => {\n\tif (typeof value !== `number`) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not a number type. Got type: '${typeof value}' value: '${JSON.stringify(value)}'`\n\t};\n\tif (Number.isNaN(value)) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not within range ${min}-${max}. Got: NaN`\n\t};\n\tif (Number.isFinite(value)) {\n\t\tif (value < min) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is below range ${min}-${max}. Got: ${value}`\n\t\t};\n\t\telse if (value > max) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is above range ${min}-${max}. Got: ${value}`\n\t\t};\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tvalue\n\t\t};\n\t} else return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not within range ${min}-${max}. Got: infinite`\n\t};\n};\n/**\n* Returns a success if values are equal, considering the set digits of precision (1..21)\n* \n* @param expected Expected value\n* @param got Received value\n* @param precision Precision in terms of decimal digits. 1...21, default 21\n* @param parameterName \n* @returns \n*/\nconst equalWithPrecisionTest = (expected, got, precision = 21, parameterName = `?`) => {\n\tif (expected.toPrecision(precision) === got.toPrecision(precision)) return {\n\t\tsuccess: true,\n\t\tvalue: got\n\t};\n\telse return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is '${got}', expected '${expected}' (using precision: ${precision})`\n\t};\n};\n//#endregion\n//#region src/arrays.ts\n/**\n* Throws an error if parameter is not an array\n* @param value\n* @param parameterName\n*/\nconst arrayTest = (value, parameterName = `?`) => {\n\tif (!Array.isArray(value)) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is expected to be an array'`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n/**\n* Throws if `index` is an invalid array index for `array`, and if\n* `array` itself is not a valid array.\n* @param array\n* @param index\n*/\nconst arrayIndexTest = (array, index, name = `index`) => {\n\treturn resultsCollate(arrayTest(array), integerTest(index, `positive`, name), numberInclusiveRangeTest(index, 0, array.length - 1, name));\n};\n/**\n* Returns true if parameter is an array of strings\n* @param value\n* @returns\n*/\nconst arrayStringsTest = (value) => {\n\tif (!Array.isArray(value)) return {\n\t\tsuccess: false,\n\t\terror: `Value is not an array`\n\t};\n\tif (value.some((v) => typeof v !== `string`)) return {\n\t\tsuccess: false,\n\t\terror: `Contains something not a string`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n//#endregion\n//#region src/empty.ts\nconst nullUndefTest = (value, parameterName = `?`) => {\n\tif (typeof value === `undefined`) return {\n\t\tsuccess: false,\n\t\terror: `${parameterName} param is undefined`\n\t};\n\tif (value === null) return {\n\t\tsuccess: false,\n\t\terror: `${parameterName} param is null`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\nconst isDefined = (argument) => argument !== void 0;\n//#endregion\n//#region src/function.ts\nconst isFunction = (object) => object instanceof Function;\nconst functionTest = (value, parameterName = `?`) => {\n\tif (value === void 0) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is undefined. Expected: function.`\n\t};\n\tif (value === null) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is null. Expected: function.`\n\t};\n\tif (typeof value !== `function`) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is type '${typeof value}'. Expected: function`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n//#endregion\n//#region src/object.ts\n/**\n* Tests_if `value` is a plain object\n* \n* ```js\n* isPlainObject(`text`); // false\n* isPlainObject(document); // false\n* isPlainObject({ hello: `there` }); // true\n* ```\n* @param value \n* @returns \n*/\nconst testPlainObject = (value) => {\n\tif (typeof value !== `object` || value === null) return {\n\t\tsuccess: false,\n\t\terror: `Value is null or not object type`\n\t};\n\tconst prototype = Object.getPrototypeOf(value);\n\tif ((prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value)) return {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n\treturn {\n\t\tsuccess: false,\n\t\terror: `Fancy object`\n\t};\n};\n/**\n* Tests if `value` is primitive value (bigint,number,string or boolean) or plain object\n* @param value \n* @returns \n*/\nconst testPlainObjectOrPrimitive = (value) => {\n\tconst t = typeof value;\n\tif (t === `symbol`) return {\n\t\tsuccess: false,\n\t\terror: `Symbol type`\n\t};\n\tif (t === `function`) return {\n\t\tsuccess: false,\n\t\terror: `Function type`\n\t};\n\tif (t === `bigint`) return {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n\tif (t === `number`) return {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n\tif (t === `string`) return {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n\tif (t === `boolean`) return {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n\treturn testPlainObject(value);\n};\n//#endregion\n//#region src/range.ts\nconst rangeIntegerTest = (v, expected) => {\n\treturn resultsCollate(rangeTest(v, expected), integerArrayTest(v));\n};\n/**\n* Inclusive range 4-6 = 4, 5, 6\n* Exclusive range 4-6 = 5\n* \n* @param numbers \n* @param expected \n* @returns \n*/\nconst rangeTest = (numbers, expected) => {\n\tfor (const v of numbers) {\n\t\tif (expected.minExclusive !== void 0) {\n\t\t\tif (v <= expected.minExclusive) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Value '${v}' must be higher than minExclusive: '${expected.minExclusive}'`\n\t\t\t};\n\t\t}\n\t\tif (expected.minInclusive !== void 0) {\n\t\t\tif (v < expected.minInclusive) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Value '${v}' must be equal or higher than minInclusive: '${expected.minInclusive}'`\n\t\t\t};\n\t\t}\n\t\tif (expected.maxExclusive !== void 0) {\n\t\t\tif (v >= expected.maxExclusive) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Value '${v}' must be less than maxExclusive: '${expected.maxExclusive}'`\n\t\t\t};\n\t\t}\n\t\tif (expected.maxInclusive !== void 0) {\n\t\t\tif (v > expected.maxInclusive) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Value '${v}' must be equal or less than maxInclusive: '${expected.maxInclusive}'`\n\t\t\t};\n\t\t}\n\t}\n\treturn {\n\t\tsuccess: true,\n\t\tvalue: numbers\n\t};\n};\n//#endregion\n//#region src/string.ts\n/**\n* Throws an error if parameter is not an string\n* @param value\n* @param parameterName\n*/\nconst stringTest = (value, range = ``, parameterName = `?`) => {\n\tif (typeof value !== `string`) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName} is not type string. Got: ${typeof value}`\n\t};\n\tswitch (range) {\n\t\tcase `non-empty`:\n\t\t\tif (value.length === 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Param '${parameterName} is empty`\n\t\t\t};\n\t\t\tbreak;\n\t}\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n//#endregion\nexport { IxfxError, arrayIndexTest, arrayStringsTest, arrayTest, equalWithPrecisionTest, errorResult, functionTest, getErrorMessage, ifNaN, integerArrayTest, integerParse, integerTest, isDefined, isFunction, isInteger, isPowerOfTwo, nullUndefTest, numberDecimalTest, numberInclusiveRangeTest, numberTest, percentTest, rangeIntegerTest, rangeTest, resultErrorToString, resultFirstFail_, resultIsError, resultIsOk, resultThrow, resultThrowSingle, resultToError, resultToValue, resultWithFail, resultsCollate, stringTest, testPlainObject, testPlainObjectOrPrimitive, throwIfFailed };\n","//#region ../guards/src/result.ts\nfunction getErrorMessage(ex) {\n\tif (typeof ex === `string`) return ex;\n\tif (ex instanceof Error) return ex.message;\n\treturn String(ex);\n}\n/**\n* Throws an error if any result is a failure.\n* Error message will be the combined from all errors.\n* @param results\n*/\nfunction throwIfFailed(...results) {\n\tconst failed = results.filter((r) => resultIsError(r));\n\tif (failed.length === 0) return;\n\tconst messages = failed.map((f) => resultErrorToString(f));\n\tthrow new Error(messages.join(`, `));\n}\n/**\n* If any of `results` is an error, throws it, otherwise ignored.\n* @param results\n* @returns _true_ or throws\n*/\nfunction resultThrow(...results) {\n\tfor (const r of results) {\n\t\tif (r === void 0) continue;\n\t\tif (typeof r === `boolean`) if (!r) throw IxfxError.fromString(`Guard failed: false result`);\n\t\telse continue;\n\t\tconst rr = typeof r === `object` ? r : r();\n\t\tif (rr === void 0) continue;\n\t\tif (rr.success) continue;\n\t\tthrow resultToError(rr);\n\t}\n\treturn true;\n}\n/**\n* Returns _true_ if `result` is an error\n* @param result\n*/\nfunction resultIsError(result) {\n\tif (typeof result !== `object` || result === null) return false;\n\treturn !result.success;\n}\nvar IxfxError = class IxfxError extends Error {\n\tcause;\n\tconstructor(message, cause) {\n\t\tsuper(message);\n\t\tthis.cause = cause;\n\t}\n\tstatic fromError(error, cause) {\n\t\tconst message = error.message;\n\t\tconst stack = error.stack;\n\t\tconst name = error.name;\n\t\tconst newError = new IxfxError(message, cause);\n\t\tnewError.stack = stack;\n\t\tnewError.name = `IxfxError(${name})`;\n\t\treturn newError;\n\t}\n\tstatic fromString(message, cause) {\n\t\tconst newError = new IxfxError(message, cause);\n\t\tnewError.name = `IxfxError`;\n\t\treturn newError;\n\t}\n};\n/**\n* Gets the result as an Error\n* @param result\n*/\nfunction resultToError(result) {\n\tif (typeof result.error === `string`) return IxfxError.fromString(result.error, result.info);\n\tif (result.error instanceof Error) return IxfxError.fromError(result.error, result.info);\n\treturn IxfxError.fromString(JSON.stringify(result.error), result.info);\n}\n/**\n* Returns the error as a string.\n* @param result\n*/\nfunction resultErrorToString(result) {\n\tif (result.error instanceof Error) return getErrorMessage(result.error);\n\tif (typeof result.error === `string`) return result.error;\n\treturn JSON.stringify(result.error);\n}\n/**\n* Returns first failed result or final value.\n* @param results\n*/\nfunction resultsCollate(...results) {\n\tlet rr;\n\tfor (const r of results) {\n\t\tif (typeof r === `boolean`) {\n\t\t\tif (r) continue;\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Guard failed: false result`\n\t\t\t};\n\t\t}\n\t\trr = typeof r === `object` ? r : r();\n\t\tif (rr === void 0) continue;\n\t\tif (!rr.success) return rr;\n\t}\n\tif (!rr) throw new Error(`No results`);\n\treturn rr;\n}\n//#endregion\n//#region ../guards/src/numbers.ts\n/**\n* Checks if `t` is not a number or within specified range.\n* Returns `[false, reason:string]` if invalid or `[true]` if valid.\n* \n* Alternatives: {@link integerTest} for additional integer check, {@link percentTest} for percentage-range.\n*\n* * (empty, default): must be a number type and not NaN.\n* * finite: must be a number, not NaN and not infinite\n* * positive: must be at least zero\n* * negative: must be zero or lower\n* * aboveZero: must be above zero\n* * belowZero: must be below zero\n* * percentage: must be within 0-1, inclusive\n* * nonZero: can be anything except zero\n* * bipolar: can be -1 to 1, inclusive\n* @param value Value to check\n* @param parameterName Name of parameter (for more helpful exception messages)\n* @param range Range to enforce\n* @returns\n*/\nconst numberTest = (value, range = ``, parameterName = `?`, info) => {\n\tif (value === null) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is null`,\n\t\tinfo\n\t};\n\tif (typeof value === `undefined`) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is undefined`,\n\t\tinfo\n\t};\n\tif (Number.isNaN(value)) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is NaN`,\n\t\tinfo\n\t};\n\tif (typeof value !== `number`) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is not a number (${JSON.stringify(value)})`,\n\t\tinfo\n\t};\n\tswitch (range) {\n\t\tcase `finite`:\n\t\t\tif (!Number.isFinite(value)) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName} must be finite (Got: ${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `positive`:\n\t\t\tif (value < 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be at least zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `negative`:\n\t\t\tif (value > 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be zero or lower (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `aboveZero`:\n\t\t\tif (value <= 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be above zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `belowZero`:\n\t\t\tif (value >= 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be below zero (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `percentage`:\n\t\t\tif (value > 1 || value < 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be in percentage range (0 to 1). (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `nonZero`:\n\t\t\tif (value === 0) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must non-zero. (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t\tcase `bipolar`:\n\t\t\tif (value > 1 || value < -1) return {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Parameter '${parameterName}' must be in bipolar percentage range (-1 to 1). (${value})`,\n\t\t\t\tinfo\n\t\t\t};\n\t\t\tbreak;\n\t}\n\treturn {\n\t\tsuccess: true,\n\t\tvalue,\n\t\tinfo\n\t};\n};\n/**\n* Checks if `value` an integer and meets additional criteria.\n* See {@link numberTest} for guard details, or use that if integer checking is not required.\n*\n* Note:\n* * `bipolar` will mean -1, 0 or 1.\n* * positive: must be at least zero\n* * negative: must be zero or lower\n* * aboveZero: must be above zero\n* * belowZero: must be below zero\n* * percentage: must be within 0-1, inclusive\n* * nonZero: can be anything except zero\n* @param value Value to check\n* @param parameterName Param name for customising exception message\n* @param range Guard specifier.\n*/\nconst integerTest = (value, range = ``, parameterName = `?`) => {\n\treturn resultsCollate(numberTest(value, range, parameterName), () => {\n\t\tif (!Number.isInteger(value)) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is not an integer`\n\t\t};\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tvalue\n\t\t};\n\t});\n};\nconst numberInclusiveRangeTest = (value, min, max, parameterName = `?`) => {\n\tif (typeof value !== `number`) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not a number type. Got type: '${typeof value}' value: '${JSON.stringify(value)}'`\n\t};\n\tif (Number.isNaN(value)) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not within range ${min}-${max}. Got: NaN`\n\t};\n\tif (Number.isFinite(value)) {\n\t\tif (value < min) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is below range ${min}-${max}. Got: ${value}`\n\t\t};\n\t\telse if (value > max) return {\n\t\t\tsuccess: false,\n\t\t\terror: `Param '${parameterName}' is above range ${min}-${max}. Got: ${value}`\n\t\t};\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tvalue\n\t\t};\n\t} else return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is not within range ${min}-${max}. Got: infinite`\n\t};\n};\n//#endregion\n//#region ../guards/src/arrays.ts\n/**\n* Throws an error if parameter is not an array\n* @param value\n* @param parameterName\n*/\nconst arrayTest = (value, parameterName = `?`) => {\n\tif (!Array.isArray(value)) return {\n\t\tsuccess: false,\n\t\terror: `Parameter '${parameterName}' is expected to be an array'`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n/**\n* Throws if `index` is an invalid array index for `array`, and if\n* `array` itself is not a valid array.\n* @param array\n* @param index\n*/\nconst arrayIndexTest = (array, index, name = `index`) => {\n\treturn resultsCollate(arrayTest(array), integerTest(index, `positive`, name), numberInclusiveRangeTest(index, 0, array.length - 1, name));\n};\n//#endregion\n//#region ../guards/src/function.ts\nconst functionTest = (value, parameterName = `?`) => {\n\tif (value === void 0) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is undefined. Expected: function.`\n\t};\n\tif (value === null) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is null. Expected: function.`\n\t};\n\tif (typeof value !== `function`) return {\n\t\tsuccess: false,\n\t\terror: `Param '${parameterName}' is type '${typeof value}'. Expected: function`\n\t};\n\treturn {\n\t\tsuccess: true,\n\t\tvalue\n\t};\n};\n//#endregion\n//#region src/at-wrap.ts\n/**\n* Similar to Javascript's in-built Array.at function, but allows offsets\n* to wrap.\n* \n* @remarks\n* ```js\n* const test = [1,2,3,4,5,6];\n* atWrap(0); // 1\n* atWrap(-1); // 6\n* atWrap(-6); // 1\n* ```\n* \n* These values would return _undefined_ using Array.at since its beyond\n* the length of the array\n* ```js\n* atWrap(6); // 1\n* atWrap(-7); // 6\n* ```\n* @param array Array\n* @param index Index\n* @returns \n*/\nconst atWrap = (array, index) => {\n\tresultThrow(numberTest(index, ``, `index`));\n\tif (!Array.isArray(array)) throw new Error(`Param 'array' is not an array`);\n\tindex = index % array.length;\n\treturn array.at(index);\n};\n//#endregion\n//#region src/chunks.ts\n/**\n* Return `array` broken up into chunks of `size` values\n*\n* ```js\n* chunks([1,2,3,4,5,6,7,8,9,10], 3);\n* // Yields: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]\n* ```\n* @param array\n* @param size\n* @returns\n*/\nfunction chunks(array, size) {\n\tthrowIfFailed(integerTest(size, \"aboveZero\", `size`), arrayTest(array, `array`));\n\tconst output = [];\n\tfor (let index = 0; index < array.length; index += size) output.push(array.slice(index, index + size));\n\treturn output;\n}\n//#endregion\n//#region src/compare-to.ts\n/**\n* Yields the result of comparing a value with a sibling.\n*\n* ```js\n* const data = [ 1, 2, 4, 8, 16 ];\n* // Compare values with its previous sibling (-1)\n* // Since -1 is the offset, the first A and B values will be 2 and 1,\n* // then 4 and 2, etc.\n* compareTo(data, -1, (a, b) => b-a)];\n* // Yields: -1, -2, -4, -8\n* ```\n*\n* Note that one less value is yielded compared to the input array.\n*\n* You can just as well go forward as well:\n* ```js\n* const data = [ 1, 2, 4, 8, 16 ];\n* // Compare values with its next-next sibling (2)\n* // With an offset of 2, the first A and B values will be 1 and 4, then\n* // 8 and 2 etc.\n* // then 4 and 2, etc.\n* compareTo(data, 2, (a, b) => b-a)];\n* // Yields: 3, 6, 12\n* ```\n* @param data\n* @param offset\n* @param fn\n*/\nfunction* compareTo(data, offset, fn) {\n\tif (offset === 0) throw new TypeError(`Offset cannot be 0.`);\n\tif (offset < 0) {\n\t\toffset = Math.abs(offset);\n\t\tfor (let i = offset; i < data.length; i++) yield fn(data[i], data[i - offset]);\n\t} else for (let i = 0; i < data.length - offset; i++) yield fn(data[i], data[i + offset]);\n}\n//#endregion\n//#region src/util/to-string.ts\n/**\n* A default converter to string that uses JSON.stringify if its an object, or the thing itself if it's a string\n*/\nconst toStringDefault = (itemToMakeStringFor) => typeof itemToMakeStringFor === `string` ? itemToMakeStringFor : JSON.stringify(itemToMakeStringFor);\n//#endregion\n//#region src/util/is-equal.ts\n/**\n* If input is a string, it is returned.\n* Otherwise, it returns the result of JSON.stringify() with fields ordered.\n* \n* This allows for more consistent comparisons when object field orders are different but values the same.\n* @param itemToMakeStringFor \n* @returns \n*/\n/**\n* Default comparer function is equiv to checking `a === b`.\n* Use {@link isEqualValueDefault} to compare by value, via comparing JSON string representation.\n*/\nconst isEqualDefault = (a, b) => a === b;\n/**\n* Comparer returns true if string representation of `a` and `b` are equal.\n* Use {@link isEqualDefault} to compare using === semantics\n* Uses `toStringDefault` to generate a string representation (via `JSON.stringify`).\n* \n* Returns _false_ if the ordering of fields is different, even though values are identical:\n* ```js\n* isEqualValueDefault({ a: 10, b: 20}, { b: 20, a: 10 }); // false\n* ```\n* \n* Use {@link isEqualValueIgnoreOrder} to ignore order (with an overhead of additional processing).\n* ```js\n* isEqualValueIgnoreOrder({ a: 10, b: 20}, { b: 20, a: 10 }); // true\n* ```\n* \n* Use {@link isEqualValuePartial} to partially match `b` against `a`.\n* @returns True if the contents of `a` and `b` are equal\n*/\nconst isEqualValueDefault = (a, b) => {\n\tif (a === b) return true;\n\treturn toStringDefault(a) === toStringDefault(b);\n};\n//#endregion\n//#region src/contains.ts\n/**\n* Returns _true_ if all value in `needles` is contained in `haystack`, \n* by default using === semantics. \n* \n* ```js\n* const a = ['apples','oranges','pears','mandarins'];\n* const b = ['pears', 'apples'];\n* contains(a, b); // True\n*\n* const c = ['pears', 'bananas'];\n* contains(a, b); // False ('bananas' does not exist in a)\n* ```\n* \n* If `needles` is empty, `contains` will return true.\n* \n* Compare by value using ixfx's `isEqualValueDefault`, or a custom function of your own\n* ```js\n* contains(a, b, isEqualValueDefault);\n* contains(a, b, (valueA, valueV) => {\n* return valueA.name === valueB.name\n* })\n* ```\n* @throws {TypeError} If parameters are not valid\n* @param haystack Array to search\n* @param needles Things to look for\n* @param eq Optional function to compare equality. By default uses === semantics\n*/\nconst contains = (haystack, needles, eq = isEqualDefault) => {\n\tresultThrow(arrayTest(haystack, `haystack`), arrayTest(needles, `needles`), functionTest(eq, `eq`));\n\tfor (const needle of needles) {\n\t\tlet found = false;\n\t\tfor (const element of haystack) if (eq(needle, element)) {\n\t\t\tfound = true;\n\t\t\tbreak;\n\t\t}\n\t\tif (!found) return false;\n\t}\n\treturn true;\n};\n/**\n* Returns _true_ if array contains duplicate values.\n*\n* ```js\n* containsDuplicateValues(['a','b','a']); // True\n* containsDuplicateValues([\n* { name: 'Apple' },\n* { name: 'Apple' }\n* ]); // True\n* ```\n* \n* Uses JSON.toString() by default to compare values.\n* \n* See also:\n* * {@link unique}: Get unique set of values in an array\n* * {@link containsDuplicateInstances}: Compare based on reference, rather than value\n* * {@link containsDuplicateValues}: Returns _true_ if every item in array is the same\n* @param data Array to examine\n* @param keyFunction Function to generate key string for object, uses JSON.stringify by default.\n* @returns\n*/\nconst containsDuplicateValues = (data, keyFunction = toStringDefault) => {\n\tif (typeof data !== `object`) throw new Error(`Param 'data' is expected to be an Iterable. Got type: ${typeof data}`);\n\tconst set = /* @__PURE__ */ new Set();\n\tfor (const v of data) {\n\t\tconst string_ = keyFunction(v);\n\t\tif (set.has(string_)) return true;\n\t\tset.add(string_);\n\t}\n\treturn false;\n};\n/**\n* Returns _true_ if array contains duplicate instances based on `===` equality checking.\n* \n* ```js\n* const o1 = { hello: `there` };\n* const o2 = { hello: `there` };\n* containsDuplicateInstances([ o1, o2 ]); // False\n* containsDuplicateInstances([ o1, o1 ]); // True\n* ```\n* \n* Primitive values are compared by value:\n* ```js\n* containsDuplicateInstances([ 1, 2, 1 ]); // True\n* containsDuplicateInstances([ `a`, `b`, `a` ]); // True\n* ```\n* \n* Use {@link containsDuplicateValues} if you'd rather compare by value.\n* @param array \n* @throws {TypeError} If `array` parameter is not an array\n* @returns \n*/\nconst containsDuplicateInstances = (array) => {\n\tresultThrow(arrayTest(array, `array`));\n\tfor (let index = 0; index < array.length; index++) for (let x = 0; x < array.length; x++) {\n\t\tif (index === x) continue;\n\t\tif (array[index] === array[x]) return true;\n\t}\n\treturn false;\n};\n//#endregion\n//#region src/cycle.ts\n/**\n* Returns a function that cycles through the contents of an array. By default starts at index 0.\n* \n* ```js\n* const c = arrayCycle([`apples`, `oranges`, `pears`]);\n* c.current; // `apples`\n* c.next(); // `oranges`\n* c.next(); // `pears`\n* c.next(); // `apples`\n* c.prev(); // `pears`\n* ```\n* \n* You can select an item by index or value:\n* ```\n* c.select(1); // `oranges`\n* c.select(`pears`); // `pears`\n* ```\n* \n* Other features:\n* ```js\n* c.current; // Current value\n* c.toArray(); // Copy of array being cycled over\n* ```\n* \n* Additional info:\n* * Selecting by value uses === semantics.\n* * Works with a copy of input array\n* @param options Array to cycle over \n* @returns \n*/\nconst cycle = (options) => {\n\tthrowIfFailed(arrayTest(options, `options`));\n\tconst opts = [...options];\n\tlet index = 0;\n\tconst next = () => {\n\t\tindex++;\n\t\tif (index === opts.length) index = 0;\n\t\treturn value();\n\t};\n\tconst prev = () => {\n\t\tindex--;\n\t\tif (index === -1) index = opts.length - 1;\n\t\treturn value();\n\t};\n\tconst value = () => {\n\t\treturn opts.at(index);\n\t};\n\tconst select = (indexOrValue) => {\n\t\tif (typeof indexOrValue === `number`) index = indexOrValue;\n\t\telse {\n\t\t\tconst found = opts.indexOf(indexOrValue);\n\t\t\tif (found === -1) throw new Error(`Could not find value`);\n\t\t\tindex = found;\n\t\t}\n\t};\n\tconst toArray = () => [...opts];\n\treturn {\n\t\ttoArray,\n\t\tnext,\n\t\tprev,\n\t\tget current() {\n\t\t\treturn value();\n\t\t},\n\t\tselect\n\t};\n};\n//#endregion\n//#region src/ensure-length.ts\n/**\n* Returns a copy of an array with specified length - padded or truncated as needed.\n*\n* If the input array is too short, it will be expanded based on the `expand` strategy:\n* - 'undefined': fill with _undefined_ (default)\n* - 'repeat': repeat array elements, starting from position 0\n* - 'first': repeat with first element from `data`\n* - 'last': repeat with last element from `data`\n*\n* Truncate:\n* ```js\n* ensureLength([1,2,3], 2); // [1,2]\n* ```\n* \n* Padded:\n* ```js\n* ensureLength([1,2,3], 5, `undefined`); // [1,2,3,undefined,undefined]\n* ensureLength([1,2,3], 5, `repeat`); // [1,2,3,1,2]\n* ensureLength([1,2,3], 5, `first`); // [1,2,3,1,1]\n* ensureLength([1,2,3], 5, `last`); // [1,2,3,3,3]\n* ```\n* @param data Input array to expand\n* @param length Desired length\n* @param expandStrategy Expand strategy\n* @param truncateStrategy Truncation strategy. By default removes from end ('from-end')\n* @typeParam V Type of array\n*/\nfunction ensureLength(data, length, expandStrategy = `undefined`, truncateStrategy = `from-end`) {\n\tif (data === void 0) throw new Error(`Data undefined`);\n\tif (!Array.isArray(data)) throw new Error(`data is not an array`);\n\tif (data.length === length) return [...data];\n\tif (data.length > length) if (truncateStrategy === `from-end`) return data.slice(0, length);\n\telse return data.slice(data.length - length);\n\tconst d = [...data];\n\tconst add = length - d.length;\n\tfor (let index = 0; index < add; index++) switch (expandStrategy) {\n\t\tcase `undefined`:\n\t\t\td.push(void 0);\n\t\t\tbreak;\n\t\tcase `repeat`:\n\t\t\td.push(data[index % data.length]);\n\t\t\tbreak;\n\t\tcase `first`:\n\t\t\td.push(data[0]);\n\t\t\tbreak;\n\t\tcase `last`:\n\t\t\td.push(data.at(-1));\n\t\t\tbreak;\n\t}\n\treturn d;\n}\n//#endregion\n//#region src/intersection.ts\n/**\n* Returns the _intersection_ of two arrays: the elements that are in common. Duplicates are removed in the process.\n* \n* By default compares based on a string representation of object.\n* \n* ```js\n* intersection([1, 2, 3], [2, 4, 6]); // returns [2]\n* ```\n* \n* To compare object instances:\n* ```js\n* intersection(arrayA, arrayB, (a,b) => a === b)\n* ```\n* \n* To use a custom string representation, eg, to only compare based on 'name' property of objects:\n* ```js\n* intersection(arrayA, arrayB, (v) => v.name)\n* ```\n* \n* See also: \n* * `uniqueByKey`/`uniqueByComparer`: Get unique items across one or more arrays, including within the array\n* @param arrayA First array\n* @param arrayB Second array\n* @param comparerOrKey Comparer or key-generating function \n* @returns \n*/\nfunction intersection(arrayA, arrayB, comparerOrKey) {\n\tif (arrayA.length === 0) return arrayB;\n\tif (arrayB.length === 0) return arrayA;\n\tcomparerOrKey ??= toStringDefault;\n\tif (typeof comparerOrKey(arrayA[0], arrayB[0]) === `string`) return intersectionByKeyImpl(arrayA, arrayB, comparerOrKey);\n\telse return intersectionByComparerImpl(arrayA, arrayB, comparerOrKey);\n}\nconst intersectionByComparerImpl = (arrayA, arrayB, equality) => {\n\treturn arrayA.filter((valueFromA) => arrayB.some((valueFromB) => equality(valueFromA, valueFromB)));\n};\nconst intersectionByKeyImpl = (arrayA, arrayB, key) => {\n\tconst aKeys = /* @__PURE__ */ new Set();\n\tconst result = [];\n\tfor (const v of arrayA) aKeys.add(key(v));\n\tconst bUsed = /* @__PURE__ */ new Set();\n\tfor (const v of arrayB) {\n\t\tconst bKey = key(v);\n\t\tif (bUsed.has(bKey)) continue;\n\t\tif (aKeys.has(bKey)) {\n\t\t\tresult.push(v);\n\t\t\tbUsed.add(bKey);\n\t\t}\n\t}\n\treturn result;\n};\n//#endregion\n//#region src/equality.ts\n/**\n* Returns _true_ if the two arrays have the same length, and have the same items at the same indexes. \n* \n* By default uses === semantics for equality checking.\n* \n* Use {@link isEqualIgnoreOrder} if you don't care whether items are in same order.\n* \n* ```js\n* isEqual([ 1, 2, 3], [ 1, 2, 3 ]); // true\n* isEqual([ 1, 2, 3], [ 3, 2, 1 ]); // false\n* ```\n* \n* Compare by value instead:\n* ```js\n* // Eg. compare objects based on their 'name' property\n* isEqual(a, b, v => v.name);\n* ```\n* \n* @param arrayA \n* @param arrayB \n* @param comparerOrKey Function to compare values or produce a string key\n* @throws {TypeError} If inputs are not arrays\n*/\nfunction isEqual(arrayA, arrayB, comparerOrKey = isEqualDefault) {\n\tresultThrow(arrayTest(arrayA, `arrayA`), arrayTest(arrayB, `arrayB`), functionTest(comparerOrKey));\n\tif (arrayA.length !== arrayB.length) return false;\n\tif (typeof comparerOrKey(arrayA[0], arrayB[0]) === `string`) {\n\t\tconst c = comparerOrKey;\n\t\tfor (let indexA = 0; indexA < arrayA.length; indexA++) if (c(arrayA[indexA]) !== c(arrayB[indexA])) return false;\n\t} else {\n\t\tconst c = comparerOrKey;\n\t\tfor (let indexA = 0; indexA < arrayA.length; indexA++) if (!c(arrayA[indexA], arrayB[indexA])) return false;\n\t}\n\treturn true;\n}\n/**\n* Returns _true_ if arrays contain same value items, regardless of order. Will return _false_ if\n* arrays are of different length.\n* \n* By default uses === semantics to compare items. Pass in a comparer function or key generating function otherwise:\n* ```js\n* isEqualIgnoreOrder(arrayA, arrayB, (v) => v.name);\n* ```\n* \n* @param arrayA Array\n* @param arrayB Array\n* @param comparerOrKey Function to compare objects or produce a string representation. Defaults to {@link isEqualDefault}\n* @throws {TypeError} If input parameters are not correct\n*/\nfunction isEqualIgnoreOrder(arrayA, arrayB, comparerOrKey = isEqualDefault) {\n\tresultThrow(arrayTest(arrayA, `arrayA`), arrayTest(arrayB, `arrayB`), functionTest(comparerOrKey));\n\tif (arrayA.length !== arrayB.length) return false;\n\treturn intersection(arrayA, arrayB, comparerOrKey).length === arrayA.length;\n}\n/**\n* Returns _true_ if all values in the array are the same. Uses value-based equality checking by default.\n* \n* @example Using default equality function\n* ```js\n* const a1 = [ 10, 10, 10 ];\n* containsIdenticalValues(a1); // True\n*\n* const a2 = [ { name:`Jane` }, { name:`John` } ];\n* containsIdenticalValues(a2); // True, even though object references are different\n* ```\n*\n* If we want to compare by value for objects that aren't readily\n* converted to JSON, you need to provide a function:\n*\n* ```js\n* containsIdenticalValues(someArray, (a, b) => {\n* return (a.eventType === b.eventType);\n* });\n* ```\n*\n* Returns _true_ if `array` is empty.\n* @param array Array\n* @param equality Equality checker. Uses string-conversion checking by default\n* @throws {TypeError} If input is not an array\n* @returns\n*/\nconst containsIdenticalValues = (array, equality) => {\n\tif (!Array.isArray(array)) throw new TypeError(`Param 'array' is not an array.`);\n\tif (array.length === 0) return true;\n\tconst eq = equality ?? isEqualValueDefault;\n\tconst a = array[0];\n\tif (array.some((v) => !eq(a, v))) return false;\n\treturn true;\n};\n//#endregion\n//#region src/filter.ts\n/**\n* Like Array.findIndex but with optional `startAt` and `length` parameters to limit the search to a specific section of the array.\n*\n* ```js\n* const data = [\"red\",\"blue\",\"red\",\"blue\"]\n* data.findIndex(v => v === `red`); // 0 - finds first match\n* findIndex(data, v => v === `red`, 1); // 2 - finds first match after start index of 1\n* ```\n*\n* Use {@link findIndexReverse} to search backwards through the array.\n* @param array\n* @param predicate\n* @param startInclusive\n* @param endExclusive End index (exclusive). By default, uses array.length\n*/\nfunction findIndex(array, predicate, startInclusive, endExclusive) {\n\tconst _start = startInclusive ?? 0;\n\tconst _end = endExclusive ?? array.length;\n\tif (_start >= array.length) throw new RangeError(`Start ${_start} is out of bounds for array of length ${array.length}`);\n\tif (_end > array.length) throw new RangeError(`End ${_end} is out of bounds for array of length ${array.length}`);\n\tif (_start > _end) throw new RangeError(`Start ${_start} is greater than end ${_end}`);\n\tfor (let i = _start; i < _end; i++) if (predicate(array[i], i, array)) return i;\n\treturn -1;\n}\n/**\n* Returns a matching index, starting at index `start` and working backwards up until `end` (both inclusive).\n* ```\n* const data = [\"red\",\"blue\",\"red\",\"blue\",\"red\"];\n* findIndexReverse(data, v=> v === `red`); // 4\n* findIndexReverse(data, v=> v === `red`, 3); // 2\n* findIndexReverse(data, v=> v === `red`, 2); // 2\n* ```\n* @param array\n* @param predicate\n* @param startInclusive\n* @param endInclusive\n* @returns\n*/\nfunction findIndexReverse(array, predicate, startInclusive, endInclusive) {\n\tconst _start = startInclusive ?? array.length - 1;\n\tconst _end = endInclusive ?? 0;\n\tif (_start >= array.length) throw new RangeError(`Start ${_start} is out of bounds for array of length ${array.length}`);\n\tif (_end < 0) throw new RangeError(`End ${_end} is out of bounds`);\n\tif (_start < _end) throw new RangeError(`Start ${_start} is less than end ${_end}`);\n\tfor (let i = _start; i >= _end; i--) if (predicate(array[i], i, array)) return i;\n\treturn -1;\n}\n/**\n* Enumerates the index of all array values that match `predicate`.\n*\n* ```js\n* const data = [`red`,`blue`,`red`,`blue`,`red`];\n* for (const index of filterWithIndex(data, v=> v === `red`)) {\n* // Yields 0, 2, 4\n* }\n* ```\n* @param array\n* @param predicate\n*/\nfunction* filterWithIndex(array, predicate) {\n\tfor (let i = 0; i < array.length; i++) if (predicate(array[i], i, array)) yield i;\n}\n/**\n* Returns two separate arrays of everything that `filter` returns _true_,\n* and everything it returns _false_ on.\n*\n* Same idea as the in-built Array.filter, but that only returns values for one case.\n*\n* ```js\n* const [ matching, nonMatching ] = filterAB(data, v => v.enabled);\n* // `matching` is a list of items from `data` where .enabled is true\n* // `nonMatching` is a list of items from `data` where .enabled is false\n* ```\n* @param data Array of data to filter\n* @param filter Function which returns _true_ to add items to the A list, or _false_ for items to add to the B list\n* @returns Array of two elements. The first is items that match `filter`, the second is items that do not.\n*/\nfunction filterAB(data, filter) {\n\tconst a = [];\n\tconst b = [];\n\tfor (const datum of data) if (filter(datum)) a.push(datum);\n\telse b.push(datum);\n\treturn [a, b];\n}\n/**\n* Yields elements from `array` that match a given `predicate`, and moreover are between\n* the given `startIndex` (inclusive) and `endIndex` (exclusive).\n*\n* While this can be done with in the in-built `array.filter` function, it will\n* needlessly iterate through the whole array. It also avoids another alternative\n* of slicing the array before using `filter`.\n*\n* ```js\n* // Return 'registered' people between and including array indexes 5-10\n* const filtered = [...filterBetween(people, person => person.registered, 5, 10)];\n* ```\n* @param array Array to filter\n* @param predicate Filter function\n* @param startIndex Start index (defaults to 0)\n* @param endIndex End index (by default runs until end)\n*/\nfunction* filterBetween(array, predicate, startIndex, endIndex) {\n\tresultThrow(arrayTest(array, `array`));\n\tif (typeof startIndex === `undefined`) startIndex = 0;\n\tif (typeof endIndex === `undefined`) endIndex = array.length;\n\tresultThrow(arrayIndexTest(array, startIndex, `startIndex`));\n\tresultThrow(arrayIndexTest(array, endIndex - 1, `endIndex`));\n\tfor (let index = startIndex; index < endIndex; index++) if (predicate(array[index], index, array)) yield array[index];\n}\n//#endregion\n//#region src/flatten.ts\n/**\n* Returns a 'flattened' copy of array, un-nesting arrays one level\n* ```js\n* flatten([1, [2, 3], [[4]] ]);\n* // Yields: [ 1, 2, 3, [4]];\n* ```\n* @param array\n* @returns\n*/\nconst flatten = (array) => [...array].flat();\n//#endregion\n//#region src/for-each.ts\n/**\n* Returns the array.map() output, or a value if `array`\n* is not an array or empty.\n* \n* ```js\n* mapWithEmptyFallback([1,2,3], v => v+2, 100); // Yields: [3,4,5]\n* mapWithEmptyFallback([], v=>v+2, 100); // Yields: [100]\n* mapWithEmptyFallback({}, v=>v+2, [100]); // Yields: [100]\n* ```\n* \n* If the fallback value is an array, it is returned as an\n* array if needed. If it's a single value, it is wrapped as an array.\n* @param array Array of values\n* @param fn Function to use for mapping values\n* @param fallback Fallback single value or array of values\n* @returns \n*/\nconst mapWithEmptyFallback = (array, fn, fallback) => {\n\tif (typeof array !== `object` || !Array.isArray(array) || array.length === 0) {\n\t\tif (Array.isArray(fallback)) return fallback;\n\t\treturn [fallback];\n\t}\n\treturn array.map(fn);\n};\n//#endregion\n//#region src/frequency.ts\n/**\n* Computes the frequency of values by a grouping function.\n*\n* ```js\n* const data = [1,2,3,4,5,6,7,8,9,10];\n* // Returns 'odd' or 'even' for an input value\n*\n* const groupBy = v => v % 2 === 0 ? `even`:`odd`;\n*\n* FrequencyByGroup.fromArray(data, groupBy);\n* // Yields map with:\n* // key: 'even', value: 5\n* // key: 'odd', value: 5\n* ```\n*\n* Or for example, group by the value itself:\n* ```js\n* const data = [1,2,3,1,2,0];\n* const groupBy = v => v.toString();\n* FrequencyByGroup.fromArray(data, groupBy);\n* // \"1\" = 2, \"2\" = 2, \"3\" = 1, \"0\" = 1\n* ```\n* @param groupBy\n* @param data\n*/\nvar FrequencyByGroup = class FrequencyByGroup {\n\t#store = /* @__PURE__ */ new Map();\n\t#groupBy;\n\t#total = 0;\n\tconstructor(groupBy = (v) => v.toString()) {\n\t\tthis.#groupBy = groupBy;\n\t}\n\tadd(data) {\n\t\tif (!Array.isArray(data)) throw new TypeError(`Param 'array' is expected to be an array. Got type: '${typeof data}'`);\n\t\tfor (const value of data) {\n\t\t\tconst group = this.#groupBy(value);\n\t\t\tif (typeof group !== `string` && typeof group !== `number`) throw new TypeError(`groupBy function is expected to return type string or number. Got type: '${typeof group}' for value: '${value}'`);\n\t\t\tconst groupValue = (this.#store.get(group) ?? 0) + 1;\n\t\t\tthis.#total++;\n\t\t\tthis.#store.set(group, groupValue);\n\t\t}\n\t}\n\t/**\n\t* Creates a new FrequencyByGroup instance, adds data to it and returns the instance.\n\t* If you just want the computed frequencies, consider using {@link entriesFromArray}.\n\t* @param data\n\t* @param groupBy\n\t* @returns FrequencyGroup instance with data added\n\t*/\n\tstatic fromArray(data, groupBy) {\n\t\tconst instance = new FrequencyByGroup(groupBy);\n\t\tinstance.add(data);\n\t\treturn instance;\n\t}\n\t/**\n\t* Computes the frequency of `data`, yielding results as entries consisting of the key and frequency.\n\t* ```js\n\t* const v = [...FrequencyByGroup.entriesFromArray([1, 2, 3, 1, 2, 3, 0, 1, 1, 1, 4], v => v.toString())];\n\t* // Yields: [ [\"1\", 5], [\"2\", 2], [\"3\", 2], [\"0\", 1], [\"4\", 1] ]\n\t* ```\n\t*\n\t* It's a generator, so you can also use it like this:\n\t* ```js\n\t* for (const [key,freq] of FrequencyByGroup.entriesFromArray(data, v => v.toString())) {\n\t* console.log(key, freq);// Logs key and frequency for each group\n\t* }\n\t* ```\n\t* @param data\n\t* @param groupBy\n\t* @returns Iterator over entries\n\t*/\n\tstatic *entriesFromArray(data, groupBy) {\n\t\treturn yield* FrequencyByGroup.fromArray(data, groupBy).entries();\n\t}\n\t/**\n\t* Returns the relative frequency for a group, or _undefined_ if not found.\n\t* @param group\n\t* @returns Relative frequency or _undefined_ if not found\n\t*/\n\tgetRelative(group) {\n\t\tconst freq = this.#store.get(group);\n\t\tif (typeof freq === `undefined`) return void 0;\n\t\treturn freq / this.#total;\n\t}\n\t/**\n\t* Returns _true_ if group was found.\n\t* @param group\n\t* @returns _True_ if group was found\n\t*/\n\thas(group) {\n\t\treturn this.#store.has(group);\n\t}\n\t/**\n\t* Gets the frequency for this group, or _undefined_ if the group does not exist\n\t* @param group\n\t* @returns Frequency for this group, or _undefined_ if the group does not exist\n\t*/\n\tget(group) {\n\t\treturn this.#store.get(group);\n\t}\n\t/**\n\t* Returns an iterator over the entries, ie `[group, frequency]` pairs.\n\t* Use {@link entriesRelative} to get the relative frequency instead of the absolute frequency.\n\t* @returns Iterator\n\t*/\n\tentries() {\n\t\treturn this.#store.entries();\n\t}\n\t/**\n\t* Returns an iterator over the entries, ie `[group, relativeFrequency]` pairs.\n\t* Use {@link entries} to get the absolute frequency instead.\n\t* @returns Iterator\n\t*/\n\t*entriesRelative() {\n\t\tfor (const [group, freq] of this.#store.entries()) yield [group, freq / this.#total];\n\t}\n\t/**\n\t* Returns an iterator over keys (ie. groups).\n\t*/\n\tkeys() {\n\t\treturn this.#store.keys();\n\t}\n\t/**\n\t* Returns an iterator over values (ie. absolute frequencies)\n\t* @returns\n\t*/\n\tvalues() {\n\t\treturn this.#store.values();\n\t}\n\t/**\n\t* Gets the average frequency across all groups.\n\t* @returns Average frequency\n\t*/\n\taverageFrequency() {\n\t\tlet total = 0;\n\t\tfor (const freq of this.#store.values()) total += freq;\n\t\treturn total / this.#store.size;\n\t}\n};\n//#endregion\n//#region src/group-by.ts\n/**\n* Groups data by a function `grouper`, returning data as a map with string\n* keys and array values. Multiple values can be assigned to the same group.\n*\n* `grouper` must yield a string designated group for a given item.\n*\n* @example\n* ```js\n* const data = [\n* { age: 39, city: `London` },\n* { age: 14, city: `Copenhagen` },\n* { age: 23, city: `Stockholm` },\n* { age: 56, city: `London` }\n* ];\n*\n* // Whatever the function returns will be the designated group\n* // for an item\n* const map = Arrays.groupBy(data, item => item.city);\n* ```\n*\n* This yields a Map with keys London, Stockholm and Copenhagen, and the corresponding values.\n*\n* ```\n* London: [{ age: 39, city: `London` }, { age: 56, city: `London` }]\n* Stockhom: [{ age: 23, city: `Stockholm` }]\n* Copenhagen: [{ age: 14, city: `Copenhagen` }]\n* ```\n* @param array Array to group\n* @param grouper Function that returns a key for a given item\n* @typeParam K Type of key to group by. Typically string.\n* @typeParam V Type of values\n* @returns Map\n*/\nconst groupBy = (array, grouper) => {\n\tconst map = /* @__PURE__ */ new Map();\n\tfor (const a of array) {\n\t\tconst key = grouper(a);\n\t\tlet existing = map.get(key);\n\t\tif (!existing) {\n\t\t\texisting = [];\n\t\t\tmap.set(key, existing);\n\t\t}\n\t\texisting.push(a);\n\t}\n\treturn map;\n};\n//#endregion\n//#region src/insert-at.ts\n/**\n* Inserts `values` at position `index`, shuffling remaining\n* items further down and returning changed result.\n* \n* Does not modify the input array.\n* \n* ```js\n* const data = [ 1, 2, 3 ]\n* \n* // Inserts 20,30,40 at index 1\n* Arrays.insertAt(data, 1, 20, 30, 40);\n* \n* // Yields: 1, 20, 30, 40, 2, 3\n* ```\n* @param data \n* @param index \n* @param values \n* @returns \n*/\nconst insertAt = (data, index, ...values) => {\n\tthrowIfFailed(arrayTest(data, `data`), arrayIndexTest(data, index, `index`));\n\tif (index === data.length - 1) return [...data, ...values];\n\tif (index === 0) return [...values, ...data];\n\treturn [\n\t\t...data.slice(0, index),\n\t\t...values,\n\t\t...data.slice(index)\n\t];\n};\n//#endregion\n//#region src/interleave.ts\n/**\n* Returns an interleaving of two or more arrays. All arrays must be the same length.\n*\n* ```js\n* const a = [`a`, `b`, `c`];\n* const b = [`1`, `2`, `3`];\n* const c = Arrays.interleave(a, b);\n* // Yields:\n* // [`a`, `1`, `b`, `2`, `c`, `3`]\n* ```\n* @param arrays\n* @returns\n*/\nconst interleave = (...arrays) => {\n\tif (arrays.some((a) => !Array.isArray(a))) throw new Error(`All parameters must be an array`);\n\tconst lengths = arrays.map((a) => a.length);\n\tif (!containsIdenticalValues(lengths)) throw new Error(`Arrays must be of same length`);\n\tconst returnValue = [];\n\tconst length = lengths[0];\n\tfor (let index = 0; index < length; index++) for (const array of arrays) returnValue.push(array[index]);\n\treturn returnValue;\n};\n//#endregion\n//#region src/merge-by-key.ts\n/**\n* Merges arrays left to right, using the provided\n* `reconcile` function to choose a winner when keys overlap.\n*\n* There's also Core.Maps.mergeByKey if the input data is in Map form.\n*\n* For example, if we have the array A:\n* [`A-1`, `A-2`, `A-3`]\n*\n* And array B:\n* [`B-1`, `B-2`, `B-4`]\n*\n* And with the key function:\n* ```js\n* // Make a key for value based on last char\n* const keyFn = (v) => v.substr(-1, 1);\n* ```\n*\n* If they are merged with the reconile function:\n* ```js\n* const reconcile = (a, b) => b.replace(`-`, `!`);\n* const output = mergeByKey(keyFn, reconcile, arrayA, arrayB);\n* ```\n*\n* The final result will be:\n*\n* [`B!1`, `B!2`, `A-3`, `B-4`]\n*\n* In this toy example, it's obvious how the reconciler transforms\n* data where the keys overlap. For the keys that do not overlap -\n* 3 and 4 in this example - they are copied unaltered.\n*\n* A practical use for `mergeByKey` has been in smoothing keypoints\n* from a TensorFlow pose. In this case, we want to smooth new keypoints\n* with older keypoints. But if a keypoint is not present, for it to be\n* passed through.\n*\n* @param keyFunction Function to generate a unique key for data\n* @param reconcile Returns value to decide 'winner' when keys conflict.\n* @param arrays Arrays of data to merge\n*/\nconst mergeByKey = (keyFunction, reconcile, ...arrays) => {\n\tconst result = /* @__PURE__ */ new Map();\n\tfor (const m of arrays) for (const mv of m) {\n\t\tif (mv === void 0) continue;\n\t\tconst mk = keyFunction(mv);\n\t\tlet v = result.get(mk);\n\t\tv = v ? reconcile(v, mv) : mv;\n\t\tresult.set(mk, v);\n\t}\n\treturn [...result.values()];\n};\n//#endregion\n//#region src/moving-window.ts\n/**\n* Creates a moving window\n* \n* ```js\n* // Create a moving window of 3 samples\n* const window = movingWindow(3);\n* \n* window(1); // [ 1 ]\n* window(2); // [ 1, 2 ]\n* window(3); // [ 1, 2, 3 ]\n* window(4); // [ 2, 3, 4 ]\n* ```\n* \n* 'reject' option allows values to be discarded:\n* ```js\n* // Reject all NaN values\n* const window = movingWindow({ samples: 3, reject: (v) => Number.isNaN(v) });\n* ```\n* \n* 'allow' is similar, but is applied after 'reject' (if provided). Instead, values\n* must pass _true_\n* \n* If a reject/disallow is triggered, the current state of the queue is returned.\n* \n* @param samplesOrOptions\n* @returns \n*/\nconst movingWindow = (samplesOrOptions) => movingWindowWithContext(samplesOrOptions).seen;\n/**\n* As {@link movingWindow} but also allows access to context, namely you \n* can access the window at any time without adding to it.\n* \n* ```js\n* const window = movingWindowWithContext(3);\n* window.seen(1); // [ 1 ]\n* window.data; // [ 1 ]\n* ```\n* @param samplesOrOptions \n* @returns \n*/\nconst movingWindowWithContext = (samplesOrOptions) => {\n\tconst q = [];\n\tconst reject = typeof samplesOrOptions === `object` ? samplesOrOptions.reject : void 0;\n\tconst allow = typeof samplesOrOptions === `object` ? samplesOrOptions.allow : void 0;\n\tconst samples = typeof samplesOrOptions === `number` ? samplesOrOptions : samplesOrOptions.samples;\n\tconst seen = (value) => {\n\t\tif (reject) {\n\t\t\tif (reject(value)) return q;\n\t\t}\n\t\tif (allow) {\n\t\t\tif (!allow(value)) return q;\n\t\t}\n\t\tq.push(value);\n\t\twhile (q.length > samples) q.shift();\n\t\treturn q;\n\t};\n\treturn {\n\t\tseen,\n\t\tget data() {\n\t\t\treturn [...q];\n\t\t}\n\t};\n};\n//#endregion\n//#region src/pairwise.ts\n/**\n* Yields pairs made up of overlapping items from the input array.\n* \n* Throws an error if there are less than two entries.\n* \n* ```js\n* pairwise([1, 2, 3, 4, 5]);\n* Yields:\n* [ [1,2], [2,3], [3,4], [4,5] ]\n* ```\n* @param values \n*/\nfunction* pairwise(values) {\n\tresultThrow(arrayTest(values, `values`));\n\tif (values.length < 2) throw new Error(`Array needs to have at least two entries. Length: ${values.length}`);\n\tfor (let index = 1; index < values.length; index++) yield [values[index - 1], values[index]];\n}\n/**\n* Reduces in a pairwise fashion.\n*\n* Eg, if we have input array of [1, 2, 3, 4, 5], the\n* `reducer` fn will run with 1,2 as parameters, then 2,3, then 3,4 etc.\n* ```js\n* const values = [1, 2, 3, 4, 5]\n* reducePairwise(values, (acc, a, b) => {\n* return acc + (b - a);\n* }, 0);\n* ```\n*\n* If input array has less than two elements, the initial value is returned.\n*\n* ```js\n* const reducer = (acc:string, a:string, b:string) => acc + `[${a}-${b}]`;\n* const result = reducePairwise(`a b c d e f g`.split(` `), reducer, `!`);\n* Yields: `![a-b][b-c][c-d][d-e][e-f][f-g]`\n* ```\n* @param array\n* @param reducer\n* @param initial\n* @returns\n*/\nconst pairwiseReduce = (array, reducer, initial) => {\n\tresultThrow(arrayTest(array, `arr`));\n\tif (array.length < 2) return initial;\n\tfor (let index = 0; index < array.length - 1; index++) initial = reducer(initial, array[index], array[index + 1]);\n\treturn initial;\n};\n//#endregion\n//#region src/random.ts\n/**\n* Returns a shuffled copy of the input array.\n* @example\n* ```js\n* const d = [1, 2, 3, 4];\n* const s = shuffle(d);\n* // d: [1, 2, 3, 4], s: [3, 1, 2, 4]\n* ```\n* \n* It can be useful to randomly access each item from an array exactly once:\n* ```js\n* for (const value of shuffle(inputArray)) {\n* // Do something with the value...\n* }\n* ```\n* \n* @throws {TypeError} If `array` is not an array and `rand` is not a function\n* @param dataToShuffle Input array\n* @param rand Random generator. `Math.random` by default.\n* @returns Copy with items moved around randomly\n* @typeParam V - Type of array items\n*/\nconst shuffle = (dataToShuffle, rand = Math.random) => {\n\tresultThrow(arrayTest(dataToShuffle, `dataToShuffle`), functionTest(rand, `rand`));\n\tconst array = [...dataToShuffle];\n\tfor (let index = array.length - 1; index > 0; index--) {\n\t\tconst randomIndex = Math.floor(rand() * (index + 1));\n\t\t[array[index], array[randomIndex]] = [array[randomIndex], array[index]];\n\t}\n\treturn array;\n};\n/**\n* Returns a random element of an array\n*\n* ```js\n* const v = [`blue`, `red`, `orange`];\n* randomElement(v); // Yields `blue`, `red` or `orange`\n* ```\n*\n* Note that repeated calls might yield the same value\n* multiple times. If you want to random unique values, consider using {@link shuffle}.\n* \n* See also:\n* * {@link randomIndex} if you want a random index rather than value.\n* \n* @throws {TypeError} If `array` is not an array and `rand` is not a function\n* @param array\n* @param rand Random generator. `Math.random` by default.\n* @returns\n*/\nconst randomElement = (array, rand = Math.random) => {\n\tresultThrow(arrayTest(array, `array`), functionTest(rand, `rand`));\n\treturn array[Math.floor(rand() * array.length)];\n};\n/**\n* Returns a random array index.\n*\n* ```js\n* const v = [`blue`, `red`, `orange`];\n* randomIndex(v); // Yields 0, 1 or 2\n* ```\n*\n* Use {@link randomElement} if you want a value from `array`, not index.\n*\n* @throws {TypeError} If `array` is not an array and `rand` is not a function\n* @param array Array\n* @param rand Random generator. `Math.random` by default.\n* @returns\n*/\nconst randomIndex = (array, rand = Math.random) => {\n\tresultThrow(arrayTest(array, `array`), functionTest(rand, `rand`));\n\treturn Math.floor(rand() * array.length);\n};\n//#endregion\n//#region src/remove.ts\n/**\n* Removes an element at `index` index from `data`, returning the resulting array without modifying the original.\n*\n* ```js\n* const v = [ 100, 20, 50 ];\n* const vv = Arrays.remove(2);\n*\n* Yields:\n* v: [ 100, 20, 50 ]\n* vv: [ 100, 20 ]\n* ```\n*\n* Consider {@link without} if you want to remove an item by value.\n*\n* Throws an exception if `index` is outside the range of `data` array.\n* @param data Input array\n* @param index Index to remove\n* @typeParam V Type of array\n* @returns\n*/\nfunction remove(data, index) {\n\tif (!Array.isArray(data)) throw new TypeError(`Parameter 'data' should be an array`);\n\tresultThrow(arrayIndexTest(data, index, `index`));\n\treturn [...data.slice(0, index), ...data.slice(index + 1)];\n}\n/**\n* Removes items from `input` array that match `predicate`.\n* A modified array is returned along with the number of items removed.\n*\n* If `predicate` matches no items, a new array will still be returned, and the removed count will be 0.\n*\n* @param input\n* @param predicate\n* @returns\n*/\nfunction removeByFilter(input, predicate) {\n\tif (!Array.isArray(input)) throw new TypeError(`Parameter 'input' should be an array`);\n\tif (typeof predicate !== `function`) throw new TypeError(`Parameter 'prediate' should be a function. Got type: ${typeof predicate}`);\n\tconst count = input.length;\n\tconst changed = input.filter((v) => !predicate(v));\n\treturn [changed, count - changed.length];\n}\n//#endregion\n//#region src/sample.ts\n/**\n* Samples values from an array. \n* \n* If `amount` is less or equal to 1, it's treated as a percentage to sample.\n* Otherwise it's treated as every _n_th value to sample.\n*\n* @example \n* By percentage - get half of the items\n* ```\n* const list = [1,2,3,4,5,6,7,8,9,10];\n* const sub = Arrays.sample(list, 0.5);\n* // Yields: [2, 4, 6, 8, 10]\n* ```\n*\n* @example\n* By steps - every third value\n* ```\n* const list = [1,2,3,4,5,6,7,8,9,10];\n* const sub = Arrays.sample(list, 3);\n* // Yields:\n* // [3, 6, 9]\n* ```\n* @param array Array to sample\n* @param amount Amount, given as a percentage (0..1) or the number of interval (ie 3 for every third item)\n* @returns\n*/\nconst sample = (array, amount) => {\n\tif (!Array.isArray(array)) throw new TypeError(`Param 'array' is not actually an array. Got type: ${typeof array}`);\n\tlet subsampleSteps = 1;\n\tif (amount <= 1) {\n\t\tconst numberOfItems = array.length * amount;\n\t\tsubsampleSteps = Math.round(array.length / numberOfItems);\n\t} else subsampleSteps = amount;\n\tresultThrow(integerTest(subsampleSteps, `positive`, `amount`));\n\tif (subsampleSteps > array.length - 1) throw new Error(`Subsample steps exceeds array length`);\n\tconst r = [];\n\tfor (let index = subsampleSteps - 1; index < array.length; index += subsampleSteps) r.push(array[index]);\n\treturn r;\n};\n//#endregion\n//#region src/sort.ts\n/**\n* Sorts an array of objects in ascending order\n* by the given property name, assuming it is a number.\n*\n* ```js\n* const data = [\n* { size: 10, colour: `red` },\n* { size: 20, colour: `blue` },\n* { size: 5, colour: `pink` }\n* ];\n* const sorted = Arrays.sortByNumericProperty(data, `size`);\n*\n* Yields items ascending order:\n* [ { size: 5, colour: `pink` }, { size: 10, colour: `red` }, { size: 20, colour: `blue` } ]\n* ```\n* @param data\n* @param propertyName\n* @throws {TypeError} If data is not an array\n*/\nconst sortByNumericProperty = (data, propertyName) => [...data].sort((a, b) => {\n\tresultThrow(arrayTest(data, `data`));\n\tconst av = a[propertyName];\n\tconst bv = b[propertyName];\n\tif (av < bv) return -1;\n\tif (av > bv) return 1;\n\treturn 0;\n});\n/**\n* Sorts an array of objects by some named property.\n* \n* ```js\n* const data = [\n* { size: 10, colour: `red` },\n* { size: 20, colour: `blue` },\n* { size: 5, colour: `pink` }\n* ];\n* sortByProperty(data, `colour`);\n* \n* Yields [\n* { size: 20, colour: `blue` },\n* { size: 5, colour: `pink` }\n* { size: 10, colour: `red` },\n* ]\n* ```\n* \n* You can also provide a custom comparer that is passed property values.\n* This function should return 0 if values are equal, 1 if `a > b` and -1 if `a < b`.\n* @param data \n* @param propertyName \n* @throws {TypeError} If data is not an array\n* @returns \n*/\nconst sortByProperty = (data, propertyName, comparer) => [...data].sort((a, b) => {\n\tresultThrow(arrayTest(data, `data`));\n\tconst av = a[propertyName];\n\tconst bv = b[propertyName];\n\tif (comparer === void 0) {\n\t\tif (av < bv) return -1;\n\t\tif (av > bv) return 1;\n\t\treturn 0;\n\t} else return comparer(av, bv);\n});\n//#endregion\n//#region src/clamp.ts\n/**\n* Clamps integer `v` between 0 (inclusive) and array length or length (exclusive).\n* Returns value then will always be at least zero, and a valid array index.\n*\n* @example Usage\n* ```js\n* // Array of length 4\n* const myArray = [`a`, `b`, `c`, `d`];\n* clampIndex(0, myArray); // 0\n* clampIndex(5, 3); // 2\n* ```\n*\n* Throws an error if `v` is not an integer.\n*\n* For some data it makes sense that data might 'wrap around' if it exceeds the\n* range. For example rotation angle. Consider using {@link wrap} for this.\n*\n* @param v Value to clamp (must be an interger)\n* @param arrayOrLength Array, or length of bounds (must be an integer)\n* @returns Clamped value, minimum will be 0, maximum will be one less than `length`.\n*/\nfunction clampIndex(v, arrayOrLength) {\n\tif (!Number.isInteger(v)) throw new TypeError(`v parameter must be an integer (${v})`);\n\tconst length = Array.isArray(arrayOrLength) ? arrayOrLength.length : arrayOrLength;\n\tif (!Number.isInteger(length)) throw new TypeError(`length parameter must be an integer (${length}, ${typeof length})`);\n\tv = Math.round(v);\n\tif (v < 0) return 0;\n\tif (v >= length) return length - 1;\n\treturn v;\n}\n//#endregion\n//#region src/index-wrap.ts\n/**\n* Returns a valid index within the given range.\n*\n* Logic:\n* 'brickwall': if limit is reached, return limit\n* 'bounce': if limit is reached, continue stepping in opposite direction (default)\n* 'cycle': if limit is reached, wrap around to the other side and continue\n*\n* Examples:\n* ```js\n* // Within range\n* indexWrap(3, 2, 5) // 3\n* indexWrap(5, 2, 5) // 5\n*\n* // Bounce logic (default)\n* indexWrap(1, 2, 5, `bounce`) // 3\n* indexWrap(0, 2, 5, `bounce`) // 4\n* indexWrap(6, 2, 5, `bounce`) // 4\n*\n* // Cycle logic\n* indexWrap(1, 2, 5, `cycle`) // 4\n* indexWrap(0, 2, 5, `cycle`) // 3\n* indexWrap(6, 2, 5, `cycle`) // 3\n* ```\n*\n* @param index\n* @param startIndex\n* @param endIndex\n* @param wrapLogic\n*/\nfunction indexWrap(index, startIndex, endIndex, wrapLogic, iterations = 0) {\n\tif (typeof wrapLogic === `undefined`) throw new TypeError(`Param 'wrapLogic' is required.`);\n\tif (startIndex > endIndex) throw new TypeError(`startIndex must be less than or equal to endIndex.`);\n\tif (index >= startIndex && index <= endIndex) return {\n\t\tindex,\n\t\titerations\n\t};\n\tif (wrapLogic === `brickwall`) {\n\t\tif (index < startIndex) return {\n\t\t\tindex: startIndex,\n\t\t\titerations\n\t\t};\n\t\treturn {\n\t\t\tindex: endIndex,\n\t\t\titerations\n\t\t};\n\t}\n\tif (wrapLogic === `bounce`) if (index < startIndex) return indexWrap(startIndex - index + startIndex, startIndex, endIndex, `bounce`, iterations + 1);\n\telse return indexWrap(endIndex - (index - endIndex), startIndex, endIndex, `bounce`, iterations + 1);\n\tif (wrapLogic === `cycle`) if (index < startIndex) return indexWrap(endIndex - (startIndex - index), startIndex, endIndex, `cycle`, iterations + 1);\n\telse return indexWrap(startIndex + (index - endIndex), startIndex, endIndex, `cycle`, iterations + 1);\n\tthrow new TypeError(`Invalid wrapLogic: ${wrapLogic}`);\n}\n//#endregion\n//#region src/util/random.ts\n/**\n* Returns a random integer based on a chance probability.\n*\n* If `chance` is less than 0, `minInclusive` is returned.\n* If `chance` is greater than 1, `maxInclusive` is returned.\n*\n* Otherwise, we compute a random number to see if it's less than `chance`. It this is the case,\n* we return a random integer in the inclusive min-max range. Eg. a chance of 0.9 means that 90% of the time\n* (assuming even random distribution) we will return a random integer.\n*\n* If the random number is greater than `chance`, then we return `minInclusive`.\n* @param chance\n* @param maxInclusive Maximum value\n* @param minInclusive Minimum value. By default 0.\n* @param randomSource Random source, by default Math.random\n* @returns\n*/\nfunction randomChanceInteger(chance, maxInclusive, minInclusive = 0, randomSource = Math.random) {\n\tif (minInclusive > maxInclusive) throw new Error(`minInclusive (${minInclusive}) cannot be greater than maxInclusive (${maxInclusive})`);\n\tif (minInclusive === maxInclusive) throw new Error(`minInclusive (${minInclusive}) cannot be equal to maxInclusive (${maxInclusive})`);\n\tif (chance <= 0) return minInclusive;\n\tif (chance > 1) return maxInclusive;\n\tif (randomSource() <= chance) return randomInteger(maxInclusive, minInclusive, randomSource);\n\treturn minInclusive;\n}\nfunction randomInteger(maxInclusive, minInclusive = 0, randomSource = Math.random) {\n\treturn Math.floor(randomSource() * (maxInclusive - minInclusive + 1)) + minInclusive;\n}\n//#endregion\n//#region src/traverse.ts\n/**\n* Given an input step state, take a step and return the new state.\n* This is a lower-level function, you probably want to use {@link arrayIndexStepper} instead.\n* @param state Current step state\n* @param options How to step\n* @param context Context in which we are stepping\n* @returns New step state\n*/\nfunction step(state, options, context) {\n\tif (context.startIndex > context.endIndex) throw new TypeError(`startIndex must be less than or equal to endIndex. startIndex: ${context.startIndex} endIndex: ${context.endIndex}`);\n\tif (context.startIndex === context.endIndex) throw new TypeError(`startIndex cannot be the same as endIndex (${context.startIndex}).`);\n\tlet incrementing = state.incrementing;\n\tconst delta = incrementing ? options.steps : -options.steps;\n\tlet index = state.index + delta;\n\tlet done = false;\n\tconst wrapLogic = options.loop === `none` ? `brickwall` : `bounce`;\n\tif (options.debug ?? false) console.log(`Step: index: ${state.index} incrementing: ${state.incrementing} delta: ${delta} index after step: ${index} start: ${context.startIndex} end: ${context.endIndex} loop: ${options.loop}`);\n\tconst r = indexWrap(index, context.startIndex, context.endIndex, wrapLogic);\n\tindex = r.index;\n\tif (r.iterations > 0) {\n\t\tif (r.iterations % 2 === 1) incrementing = !state.incrementing;\n\t}\n\tif (options.loop === `none` && (index === context.endIndex || index === context.startIndex)) done = true;\n\treturn {\n\t\tindex,\n\t\tincrementing,\n\t\tdone\n\t};\n}\n/**\n* Creates a generator to step through array indices.\n*\n* Supports moving forward/backward through an array, looping, 'drunken walk', and random step lengths.\n*\n* ```js\n* const data [ `a`, `b`, `c`, `d`, `e` ];\n*\n* // Step one by one through each index\n* for (const index of arrayIndexStepper({step:1, loop:`none`}, data)) {\n* console.log(`index: ${index} value: ${data[index]}`);\n* }\n* ```\n*\n* More examples:\n* ```js\n* // A generator that never ends, going back and forth between start and end\n* arrayIndexStepper({ steps: 1, loop: `pingpong` }, data);\n* // As above, but when we hit the end/start, repeat that index\n* arrayIndexStepper({ steps: 1, loop: `pingpong`, repeatLoopedIndex:true }, data);\n*\n* // Move backwards. from the end, through the indicies, jumping by two\n* arrayIndexStepper({ steps: 2, forward:false }, data);\n*\n* ```\n* @param optionsP\n* @param context\n* @returns Iterator over array indicies\n*/\nfunction arrayIndexStepper(optionsP, context) {\n\tconst options = {\n\t\tsteps: 1,\n\t\tloop: `none`,\n\t\trepeatLoopedIndex: false,\n\t\tdebug: false,\n\t\tforward: true,\n\t\trandomDirectionFlip: 0,\n\t\trandomChanceSteps: 0,\n\t\trandomStepsMax: 2,\n\t\t...optionsP\n\t};\n\tconst range = {\n\t\tstartIndex: 0,\n\t\tendIndex: context.data.length - 1,\n\t\trandomSource: Math.random,\n\t\t...context\n\t};\n\tconst fn = function* (overrideOptions = {}, overrideContext = {}) {\n\t\tconst _options = {\n\t\t\t...options,\n\t\t\t...overrideOptions\n\t\t};\n\t\tconst data = overrideContext.data ?? context.data;\n\t\tif (!Array.isArray(data)) throw new TypeError(`Param 'data' must be an array. Got: ${typeof data}`);\n\t\tif (data.length === 0) return;\n\t\tif (data.length === 1) {\n\t\t\tyield 0;\n\t\t\treturn;\n\t\t}\n\t\tconst startIndex = clampIndex(overrideContext.startIndex ?? range.startIndex, data);\n\t\tconst endIndex = clampIndex(overrideContext.endIndex ?? range.endIndex, data);\n\t\tif (startIndex === endIndex) throw new Error(`startIndex and endIndex cannot be the same. startIndex: ${startIndex} endIndex: ${endIndex}. Data length: ${data.length} Override: ${JSON.stringify(overrideContext)}`);\n\t\tconst _context = {\n\t\t\tstartIndex,\n\t\t\tendIndex\n\t\t};\n\t\tlet state = {\n\t\t\tindex: _options.forward ? startIndex : endIndex,\n\t\t\tincrementing: _options.forward,\n\t\t\tdone: false\n\t\t};\n\t\tlet lastIndex = NaN;\n\t\tconst debug = _options.debug ?? false;\n\t\twhile (!state.done) {\n\t\t\tyield state.index;\n\t\t\tif (_options.randomDirectionFlip > 0 && range.randomSource() < _options.randomDirectionFlip) state.incrementing = !state.incrementing;\n\t\t\tif (_options.randomChanceSteps > 0) {\n\t\t\t\tconst randomSteps = randomChanceInteger(_options.randomChanceSteps, _options.randomStepsMax, _options.steps, range.randomSource);\n\t\t\t\tstate = step(state, {\n\t\t\t\t\t..._options,\n\t\t\t\t\tsteps: randomSteps\n\t\t\t\t}, _context);\n\t\t\t} else state = step(state, _options, _context);\n\t\t\tif (debug) console.log(`index: ${state.index} state: ${JSON.stringify(state)}`);\n\t\t\tif (state.done && state.index !== lastIndex) yield state.index;\n\t\t\tif (_options.repeatLoopedIndex && _options.loop !== `none`) {\n\t\t\t\tif (state.index === startIndex || state.index === endIndex) yield state.index;\n\t\t\t}\n\t\t\tlastIndex = state.index;\n\t\t}\n\t};\n\treturn fn;\n}\n//#endregion\n//#region src/unique.ts\n/**\n* Combines the values of one or more arrays, removing duplicates.\n* \n* By default compares values based on a JSON string representation.\n* \n* @param arrays Array (or array of arrays) to examine\n* @param toString Function to convert values to a string for comparison purposes. By default uses JSON formatting.\n* @returns\n*/\nfunction unique(arrays, comparer) {\n\tconst flattened = arrays.flat(10);\n\tif (flattened.length <= 1) return flattened;\n\tcomparer ??= toStringDefault;\n\tif (typeof comparer(flattened[0], flattened[1]) === `string`) return uniqueByKeyImpl(flattened, comparer);\n\telse return uniqueByComparerImpl(flattened, comparer);\n}\nconst uniqueByKeyImpl = (flattened, toString) => {\n\tconst matching = /* @__PURE__ */ new Set();\n\tconst t = [];\n\tfor (const a of flattened) {\n\t\tconst stringRepresentation = toString(a);\n\t\tif (matching.has(stringRepresentation)) continue;\n\t\tmatching.add(stringRepresentation);\n\t\tt.push(a);\n\t}\n\treturn t;\n};\nconst uniqueByComparerImpl = (flattened, comparer) => {\n\tconst t = [];\n\tconst contains = (v) => {\n\t\tfor (const tValue of t) if (comparer(tValue, v)) return true;\n\t\treturn false;\n\t};\n\tfor (const v of flattened) if (!contains(v)) t.push(v);\n\treturn t;\n};\n//#endregion\n//#region src/until.ts\n/**\n* Yields all items in the input array for as long as `predicate` returns true.\n*\n* `predicate` yields arrays of `[stop:boolean, acc:A]`. The first value\n* is _true_ when the iteration should stop, and the `acc` is the accumulated value.\n* This allows `until` to be used to carry over some state from item to item.\n*\n* @example Stop when we hit an item with value of 3\n* ```js\n* const v = [...until([1,2,3,4,5], v => v === 3];\n* // [ 1, 2 ]\n* ```\n*\n* @example Stop when we reach a total, using 0 as initial value\n* ```js\n* // Stop when accumulated value reaches 6\n* const v = Arrays.until[1,2,3,4,5], (v, acc) => [acc >= 7, v+acc], 0);\n* // [1, 2, 3]\n* ```\n* @param data\n* @param predicate\n*/\nfunction* until(data, predicate, initial) {\n\tlet total = initial;\n\tfor (const datum of data) {\n\t\tconst r = predicate(datum, total);\n\t\tif (typeof r === `boolean`) {\n\t\t\tif (r) break;\n\t\t} else {\n\t\t\tconst [stop, accumulator] = r;\n\t\t\tif (stop) break;\n\t\t\ttotal = accumulator;\n\t\t}\n\t\tyield datum;\n\t}\n}\n/**\n* Returns up to `count` items from the generator. If the generator finishes before `count` items are returned, then only the available items are returned.\n* @param generator\n* @param count\n*/\nfunction takeFromGenerator(generator, count) {\n\tconst result = [];\n\tfor (let i = 0; i < count; i++) {\n\t\tconst { value, done } = generator.next();\n\t\tif (done) break;\n\t\tresult.push(value);\n\t}\n\tif (result.length > count) throw new Error(`Bug: takeFromGenerator returned more items than requested.`);\n\treturn result;\n}\n//#endregion\n//#region src/without.ts\n/**\n* Returns a copy of an input array with _undefined_ values removed.\n* @param data \n* @returns \n*/\nconst withoutUndefined = (data) => {\n\tresultThrow(arrayTest(data, `sourceArray`));\n\treturn data.filter((v) => v !== void 0);\n};\n/**\n* Returns an array with value(s) omitted. \n* \n* If value is not found, result will be a copy of input.\n* Value checking is completed via the provided `comparer` function.\n* By default checking whether `a === b`. To compare based on value, use the `isEqualValueDefault` comparer.\n*\n* @example\n* ```js\n* const data = [100, 20, 40];\n* const filtered = without(data, 20); // [100, 40]\n* ```\n*\n* @example Using value-based comparison\n* ```js\n* const data = [{ name: `Alice` }, { name:`Sam` }];\n*\n* // This wouldn't work as expected, because the default comparer uses instance,\n* // not value:\n* without(data, { name: `Alice` });\n*\n* // So instead we can use a value comparer:\n* without(data, { name:`Alice` }, isEqualValueDefault);\n* ```\n*\n* @example Use a function\n* ```js\n* const data = [ { name: `Alice` }, { name:`Sam` }];\n* without(data, { name:`ALICE` }, (a, b) => {\n* return (a.name.toLowerCase() === b.name.toLowerCase());\n* });\n* ```\n*\n* Consider {@link remove} to remove an item by index.\n*\n* @typeParam V - Type of array items\n* @param sourceArray Source array\n* @param toRemove Value(s) to remove\n* @param comparer Comparison function. If not provided `isEqualDefault` is used, which compares using `===`\n* @throws {TypeError} If `sourceArray` is not an array, or compare function is not a function\n* @return Copy of array without value.\n*/\nconst without = (sourceArray, toRemove, comparer = isEqualDefault) => {\n\tresultThrow(arrayTest(sourceArray, `sourceArray`), functionTest(comparer, `comparer`));\n\tif (Array.isArray(toRemove)) {\n\t\tconst returnArray = [];\n\t\tfor (const source of sourceArray) if (!toRemove.some((v) => comparer(source, v))) returnArray.push(source);\n\t\treturn returnArray;\n\t} else return sourceArray.filter((v) => !comparer(v, toRemove));\n};\n//#endregion\n//#region src/zip.ts\n/**\n* Zip combines the elements of two or more arrays based on their index.\n*\n* ```js\n* const a = [ 1, 2, 3 ];\n* const b = [ `red`, `blue`, `green` ];\n*\n* const c = Arrays.zip(a, b);\n* // Yields:\n* // [\n* // [ 1, `red` ],\n* // [ 2, `blue` ],\n* // [ 3, `green` ]\n* // ]\n* ```\n*\n* Typically the arrays you zip together are all about the same logical item. Eg, in the above example\n* perhaps `a` is size and `b` is colour. So thing #1 (at array index 0) is a red thing of size 1. Before\n* zipping we'd access it by `a[0]` and `b[0]`. After zipping, we'd have c[0], which is array of [1, `red`].\n* @param arrays\n* @returns Zipped together array\n* @throws {TypeError} If any of the parameters are not arrays\n* @throws {Error} If the arrays are not all of the same length\n*/\nconst zip = (...arrays) => {\n\tif (arrays.some((a) => !Array.isArray(a))) throw new TypeError(`All parameters must be an array`);\n\tconst lengths = arrays.map((a) => a.length);\n\tif (!containsIdenticalValues(lengths)) throw new Error(`Arrays must be of same length`);\n\tconst returnValue = [];\n\tconst length = lengths[0];\n\tfor (let index = 0; index < length; index++) returnValue.push(arrays.map((a) => a[index]));\n\treturn returnValue;\n};\n//#endregion\nexport { FrequencyByGroup, arrayIndexStepper, atWrap, chunks, compareTo, contains, containsDuplicateInstances, containsDuplicateValues, containsIdenticalValues, cycle, ensureLength, filterAB, filterBetween, filterWithIndex, findIndex, findIndexReverse, flatten, groupBy, insertAt, interleave, intersection, isEqual, isEqualIgnoreOrder, mapWithEmptyFallback, mergeByKey, movingWindow, movingWindowWithContext, pairwise, pairwiseReduce, randomElement, randomIndex, remove, removeByFilter, sample, shuffle, sortByNumericProperty, sortByProperty, step, takeFromGenerator, unique, until, without, withoutUndefined, zip };\n","import { t as __exportAll } from \"./chunk-pbuEa-1d.js\";\nimport { movingWindowWithContext, zip } from \"@ixfx/arrays\";\nimport { integerTest, numberTest, resultThrow } from \"@ixfx/guards\";\n//#region src/apply-to-values.ts\n/**\n* Apples `fn` to every key of `obj` which is numeric.\n* ```js\n* const o = {\n* name: 'john',\n* x: 10,\n* y: 20\n* };\n* const o2 = applyToValues(o, (v) => v * 2);\n* \n* // Yields: { name: 'john', x: 20, y: 40 }\n* ```\n* @param object \n* @param apply \n* @returns \n*/\nconst applyToValues = (object, apply) => {\n\tconst o = { ...object };\n\tfor (const [key, value] of Object.entries(object)) if (typeof value === `number`) o[key] = apply(value);\n\telse o[key] = value;\n\treturn o;\n};\n//#endregion\n//#region src/numeric-arrays.ts\n/**\n* Applies a function `fn` to the elements of an array, weighting them based on their relative position.\n*\n* ```js\n* // Six items\n* weight([1,1,1,1,1,1], Modulation.gaussian());\n*\n* // Yields:\n* // [0.02, 0.244, 0.85, 0.85, 0.244, 0.02]\n* ```\n*\n* `fn` is expected to map (0..1) => (0..1), such as an easing function. The input to the\n* `fn` is the relative position of an element. Thus the first element will be 0, the middle 0.5 and so on.\n* The output of `fn` is then multiplied by the original value.\n*\n* In the below example (which is also the default if `fn` is not specified), the relative position is\n* how values are weighted:\n*\n* ```js\n* weight([1,1,1,1,1,1], (relativePos) => relativePos);\n* // Yields:\n* // [0, 0.2, 0.4, 0.6, 0.8, 1]\n* ```\n*\n* Throws TypeError if `data` is not an array or for any element not a number.\n* @param data Array of numbers\n* @param fn Returns a weighting based on the given relative position. If unspecified, `(x) => x` is used.\n*/\nconst weight = (data, fn) => {\n\tif (!Array.isArray(data)) throw new TypeError(`Param 'data' is expected to be an array. Got type: ${typeof data}`);\n\tconst weightingFunction = fn ?? ((x) => x);\n\treturn data.map((value, index) => {\n\t\tif (typeof value !== `number`) throw new TypeError(`Param 'data' contains non-number at index: '${index}'. Type: '${typeof value}' value: '${value}'`);\n\t\tconst relativePos = index / (data.length - 1);\n\t\tconst weightForPosition = weightingFunction(relativePos);\n\t\tif (typeof weightForPosition !== `number`) throw new TypeError(`Weighting function returned type '${typeof weightForPosition}' rather than number for input: '${relativePos}'`);\n\t\treturn value * weightForPosition;\n\t});\n};\n/**\n* Returns an array of all valid numbers from `data`\n*\n* @param data\n* @returns\n*/\nconst validNumbers = (data) => data.filter((d) => typeof d === `number` && !Number.isNaN(d));\n/**\n* Returns the dot product of arbitrary-sized arrays. Assumed they are of the same length.\n* @param values\n* @param nonNumber What to do if array contains an invalid number. Error: throw an exception, 'treat-as-zero' use as 0 instead, 'ignore', let math run with invalid number\n* @returns\n*/\nconst dotProduct = (values, nonNumber = `ignore`) => {\n\tlet r = 0;\n\tconst length = values[0].length;\n\tfor (let index = 0; index < length; index++) {\n\t\tlet t = 0;\n\t\tfor (const [p, value] of values.entries()) {\n\t\t\tlet v = value[index];\n\t\t\tif (Number.isNaN(v) || !Number.isFinite(v)) {\n\t\t\t\tif (nonNumber === `treat-as-zero`) v = 0;\n\t\t\t\telse if (nonNumber === `error`) throw new TypeError(`Invalid number at index ${index},${p}`);\n\t\t\t}\n\t\t\tif (p === 0) t = v;\n\t\t\telse t *= v;\n\t\t}\n\t\tr += t;\n\t}\n\treturn r;\n};\n/**\n* Calculates the average of all numbers in an array.\n* Array items which aren't a valid number are ignored and do not factor into averaging.\n*\n* Use {@link numberArrayCompute} if you want min, max and total as well.\n*\n* @example\n* ```js\n* // Average of a list\n* const avg = Numbers.average([1, 1.4, 0.9, 0.1]);\n*\n* // Average of a variable\n* const data = [100,200];\n* Numbers.average(data);\n* ```\n*\n* @see {@link averageWeighted} To weight items based on position in array\n* @param data Data to average.\n* @returns Average of array\n*/\nconst average = (data) => {\n\tif (typeof data !== `object`) throw new Error(`Param 'data' should be an array. Got: ${typeof data}`);\n\tif (!Array.isArray(data)) throw new TypeError(`Param 'data' is not an array`);\n\tconst valid = validNumbers(data);\n\treturn valid.reduce((accumulator, v) => accumulator + v, 0) / valid.length;\n};\n/**\n* Returns the minimum number out of `data`.\n* Undefined and non-numbers are silently ignored.\n*\n* ```js\n* Numbers.min([10, 20, 0]); // Yields 0\n* ```\n* @param data\n* @returns Minimum number\n*/\nconst min = (data) => Math.min(...validNumbers(data));\n/**\n* Returns the index of the largest value.\n* ```js\n* const v = [ 10, 40, 5 ];\n* Numbers.maxIndex(v); // Yields 1\n* ```\n* @param data Array of numbers\n* @returns Index of largest value\n*/\nconst maxIndex = (data) => data.reduce((bestIndex, value, index, array) => value > array[bestIndex] ? index : bestIndex, 0);\n/**\n* Returns the index of the smallest value.\n*\n* ```js\n* const v = [ 10, 40, 5 ];\n* Numbers.minIndex(v); // Yields 2\n* ```\n* @param data Array of numbers\n* @returns Index of smallest value\n*/\nconst minIndex = (data) => data.reduce((bestIndex, value, index, array) => value < array[bestIndex] ? index : bestIndex, 0);\n/**\n* Returns the maximum number out of `data`.\n* Undefined and non-numbers are silently ignored.\n*\n* ```js\n* Numbers.max(100, 200, 50); // 200\n* ```\n* @param data List of numbers\n* @returns Maximum number\n*/\nconst max = (data) => Math.max(...validNumbers(data));\n/**\n* Returns the total of `data`.\n* Undefined and non-numbers are silently ignored.\n*\n* ```js\n* Numbers.total([1, 2, 3]); // 6\n* ```\n* @param data Array of numbers\n* @returns Total\n*/\nconst total = (data) => data.reduce((previous, current) => {\n\tif (typeof current !== `number`) return previous;\n\tif (Number.isNaN(current)) return previous;\n\tif (!Number.isFinite(current)) return previous;\n\treturn previous + current;\n}, 0);\n/**\n* Returns the maximum out of `data` without pre-filtering for speed.\n*\n* For most uses, {@link max} should suffice.\n*\n* ```js\n* Numbers.maxFast([ 10, 0, 4 ]); // 10\n* ```\n* @param data\n* @returns Maximum\n*/\nconst maxFast = (data) => {\n\tlet m = Number.MIN_SAFE_INTEGER;\n\tfor (const datum of data) m = Math.max(m, datum);\n\treturn m;\n};\n/**\n* Returns the total of `data` without pre-filtering for speed.\n*\n* For most uses, {@link total} should suffice.\n*\n* ```js\n* Numbers.totalFast([ 10, 0, 4 ]); // 14\n* ```\n* @param data\n* @returns Maximum\n*/\nconst totalFast = (data) => {\n\tlet m = 0;\n\tfor (const datum of data) m += datum;\n\treturn m;\n};\n/**\n* Returns the maximum out of `data` without pre-filtering for speed.\n*\n* For most uses, {@link max} should suffice.\n*\n* ```js\n* Numbers.minFast([ 10, 0, 100 ]); // 0\n* ```\n* @param data\n* @returns Maximum\n*/\nconst minFast = (data) => {\n\tlet m = Number.MAX_SAFE_INTEGER;\n\tfor (const datum of data) m = Math.min(m, datum);\n\treturn m;\n};\n//#endregion\n//#region src/average.ts\n/**\n* Calculate median value of an array of numbers\n* @param data \n* @returns \n*/\nconst median = (data) => {\n\tif (!Array.isArray(data)) throw new TypeError(`Param 'data' is expected to be an array`);\n\tconst n = data.length;\n\tdata.sort((a, b) => a - b);\n\tlet result = 0;\n\tif (n % 2 === 0) result = Math.floor((data[n / 2] + data[n / 2 - 1]) / 2);\n\telse result = data[Math.floor(n / 2)];\n\treturn result;\n};\n/**\n* Calculate the mean of `array`.\n* @param array \n* @returns \n*/\nconst mean = (array) => array.reduce((accumulator, value) => accumulator + value, 0) / array.length;\n/**\n* Computes an average of an array with a set of weights applied.\n*\n* Weights can be provided as an array, expected to be on 0..1 scale, with indexes\n* matched up to input data. Ie. data at index 2 will be weighed by index 2 in the weightings array.\n*\n* ```js\n* // All items weighted evenly\n* averageWeighted([1,2,3], [1,1,1]); // 2\n*\n* // First item has full weight, second half, third quarter\n* averageWeighted([1,2,3], [1, 0.5, 0.25]); // 1.57\n*\n* // With reversed weighting of [0.25,0.5,1] value is 2.42\n* ```\n*\n* A function can alternatively be provided to compute the weighting based on array index, via {@link weight}.\n*\n* ```js\n* averageWeighted[1,2,3], Random.gaussian()); // 2.0\n* ```\n*\n* This is the same as:\n*\n* ```js\n* const data = [ 1, 2, 3 ];\n* const w = weight(data, Random.gaussian());\n* const avg = averageWeighted(data, w); // 2.0\n* ```\n* @param data Data to average\n* @param weightings Array of weightings that match up to data array, or an easing function\n* @see {@link average} Compute averages without weighting.\n*/\nconst averageWeighted = (data, weightings) => {\n\tif (typeof weightings === `function`) weightings = weight(data, weightings);\n\tconst [totalV, totalW] = zip(data, weightings).reduce((accumulator, v) => [accumulator[0] + v[0] * v[1], accumulator[1] + v[1]], [0, 0]);\n\treturn totalV / totalW;\n};\n/**\n* Returns a function that computes a weighted average of an array\n* \n* ```js\n* const w = averageWeigher(v => Math.random() * v);\n* \n* // Give each array index a random\n* w([1,2,3,4]);\n* ```\n* @param weigher \n* @returns \n*/\nconst averageWeigher = (weigher) => {\n\treturn (data) => averageWeighted(data, weigher);\n};\n//#endregion\n//#region src/clamp.ts\n/**\n* Clamps a value between min and max (both inclusive)\n* Defaults to a 0-1 range, useful for percentages.\n*\n* @example Usage\n* ```js\n* // 0.5 - just fine, within default of 0 to 1\n* clamp(0.5);\n* // 1 - above default max of 1\n* clamp(1.5);\n* // 0 - below range\n* clamp(-50, 0, 100);\n* // 50 - within range\n* clamp(50, 0, 50);\n* ```\n*\n* For clamping integer ranges, consider {@link clampIndex }\n* For clamping `{ x, y }` points, consider {@link https://api.ixfx.fun/_ixfx/geometry/Points/clamp/ @ixfx/geometry/Points.clamp}.\n* For clamping bipolar values: {@link Bipolar.clamp}\n* @param value Value to clamp\n* @param min value (inclusive)\n* @param max value (inclusive)\n* @returns Clamped value\n*/\nfunction clamp(value, min = 0, max = 1) {\n\tif (Number.isNaN(value)) throw new Error(`Param 'value' is NaN`);\n\tif (Number.isNaN(min)) throw new Error(`Param 'min' is NaN`);\n\tif (Number.isNaN(max)) throw new Error(`Param 'max' is NaN`);\n\tif (value < min) return min;\n\tif (value > max) return max;\n\treturn value;\n}\n/**\n* Returns a function that clamps values.\n*\n* ```js\n* const c = clamper(0,100);\n* c(50); // 50\n* c(101); // 100\n* c(-5); // 0\n* ```\n* @param min Minimum value. Default: 0\n* @param max Maximum value. Default: 1\n*/\nfunction clamper(min = 0, max = 1) {\n\tif (Number.isNaN(min)) throw new Error(`Param 'min' is NaN`);\n\tif (Number.isNaN(max)) throw new Error(`Param 'max' is NaN`);\n\treturn (v) => {\n\t\tif (v > max) return max;\n\t\tif (v < min) return min;\n\t\treturn v;\n\t};\n}\n/**\n* Clamps integer `v` between 0 (inclusive) and array length or length (exclusive).\n* Returns value then will always be at least zero, and a valid array index.\n*\n* @example Usage\n* ```js\n* // Array of length 4\n* const myArray = [`a`, `b`, `c`, `d`];\n* clampIndex(0, myArray); // 0\n* clampIndex(5, 3); // 2\n* ```\n*\n* Throws an error if `v` is not an integer.\n*\n* For some data it makes sense that data might 'wrap around' if it exceeds the\n* range. For example rotation angle. Consider using {@link wrap} for this.\n*\n* @param v Value to clamp (must be an interger)\n* @param arrayOrLength Array, or length of bounds (must be an integer)\n* @returns Clamped value, minimum will be 0, maximum will be one less than `length`.\n*/\nfunction clampIndex(v, arrayOrLength) {\n\tif (!Number.isInteger(v)) throw new TypeError(`v parameter must be an integer (${v})`);\n\tconst length = Array.isArray(arrayOrLength) ? arrayOrLength.length : arrayOrLength;\n\tif (!Number.isInteger(length)) throw new TypeError(`length parameter must be an integer (${length}, ${typeof length})`);\n\tv = Math.round(v);\n\tif (v < 0) return 0;\n\tif (v >= length) return length - 1;\n\treturn v;\n}\n/**\n* Returns the largest value, ignoring the sign of numbers\n*\n* ```js\n* maxAbs(1, 5); // 5\n* maxAbs(-10, 5); // -10 (since sign is ignored)\n* maxAbs(arrayOfNumbers);\n* ```\n*\n* Non-valid numbers are silently ignored.\n* @param values\n* @returns\n*/\nfunction maxAbs(...values) {\n\tlet maxA = Number.MIN_SAFE_INTEGER;\n\tlet max = Number.MIN_SAFE_INTEGER;\n\tconst checkV = (v) => {\n\t\tif (!Number.isNaN(v) && Number.isFinite(v)) {\n\t\t\tconst va = Math.abs(v);\n\t\t\tif (va > maxA) {\n\t\t\t\tmaxA = va;\n\t\t\t\tmax = v;\n\t\t\t}\n\t\t}\n\t};\n\tfor (const v of values) if (typeof v === `number`) checkV(v);\n\telse for (const subV of v) checkV(subV);\n\treturn max;\n}\n//#endregion\n//#region src/count.ts\n/**\n* Yields `amount` integers, counting by one from zero. If a negative amount is used,\n* count decreases. If `offset` is provided, this is added to the return result.\n* @example\n* ```js\n* const a = [...count(5)]; // Yields five numbers: [0,1,2,3,4]\n* const b = [...count(-5)]; // Yields five numbers: [0,-1,-2,-3,-4]\n* for (const v of count(5, 5)) {\n* // Yields: 5, 6, 7, 8, 9\n* }\n* const c = [...count(5,1)]; // Yields [1,2,3,4,5]\n* ```\n*\n* @example Used with forEach\n* ```js\n* // Prints `Hi` 5x\n* forEach(count(5), () => // do something);\n* ```\n*\n* If you want to accumulate return values, consider using Flow.repeat.\n*\n* @example Run some code every 100ms, 10 times:\n* ```js\n* import { interval } from '@ixfx/flow.js'\n* import { count } from '@ixfx/numbers.js'\n* const counter = count(10);\n* for await (const v of interval(counter, { fixedIntervalMs: 100 })) {\n* // Do something\n* }\n* ```\n* @param amount Number of integers to yield\n* @param offset Added to result\n*/\nfunction* count(amount, offset = 0) {\n\tresultThrow(integerTest(amount, ``, `amount`), integerTest(offset, ``, `offset`));\n\tif (amount === 0) return;\n\tlet index = 0;\n\tdo\n\t\tyield amount < 0 ? -index + offset : index + offset;\n\twhile (index++ < Math.abs(amount) - 1);\n}\n//#endregion\n//#region src/difference.ts\n/**\n* Returns the difference from the `initial` value. Defaults to absolute difference.\n* ```js\n* const rel = differenceFromFixed(100);\n* rel(100); // 0\n* rel(150); // 50\n* rel(50); // 50\n* ```\n*\n* 'numerical' gives sign:\n* ```js\n* const rel = differenceFromFixed(100, `numerical`);\n* rel(100); // 0\n* rel(150); // 50\n* rel(50); // -50\n* ```\n* \n* 'relative' gives proportion to initial\n* ```js\n* const rel = differenceFromFixed(100, `relative`);\n* rel(100); // 0\n* rel(150); // 0.5\n* rel(10); // 0.90\n* ```\n* \n* Using 'relativeSigned', we get negative relative result\n* when value is below the initial value.\n* \n* Use {@link differenceFromLast} to compare against the last value,\n* rather than the same fixed value.\n* @param {number} initial Value to compare against\n* @returns Difference from initial value\n*/\nconst differenceFromFixed = (initial, kind = `absolute`) => (value) => differenceFrom(kind, value, initial);\n/**\n* Returns a function which yields difference compared to last value.\n* \n* If no initial value is provided, the first difference will be returned as 0.\n* \n* Difference can be returned in various formats:\n* * 'absolute': numerical difference, without sign\n* * 'numerical': numerical difference, with sign, so you can see if difference is higher or lower\n* * 'relative': difference divided by last value, giving a proportional difference. Unsigned.\n* * 'relativeSigned': as above, but with sign\n* \n* Use {@link differenceFromFixed} to compare against a fixed value instead of the last value.\n* \n* ```js\n* let d = differenceFromLast(`absolute`);\n* d(10); // 0\n* d(11); // 1\n* d(10); // 1\n* ```\n* \n* ```js\n* let d = differenceFromLast(`numerical`);\n* d(10); // 0\n* d(11); // 1\n* d(10); // -1\n* ```\n* \n* ```js\n* let d = differenceFromLast(`relative`);\n* d(10); // 0\n* d(11); // 0.1\n* d(10); // 0.1\n* ```\n* ```js\n* let d = differenceFromLast(`relativeSigned`);\n* d(10); // 0\n* d(11); // 0.1\n* d(10); // -0.1\n* ```\n* \n* An initial value can be provided, eg:\n* ```js\n* let d = differenceFromLast(`absolute`, 10);\n* d(11); // 1\n* ```\n* @param kind Kind of output value\n* @param initialValue Optional initial value \n* @returns \n*/\nconst differenceFromLast = (kind = `absolute`, initialValue = NaN) => {\n\tlet lastValue = initialValue;\n\treturn (value) => {\n\t\tconst x = differenceFrom(kind, value, lastValue);\n\t\tlastValue = value;\n\t\treturn x;\n\t};\n};\nconst differenceFrom = (kind = `absolute`, value, from) => {\n\tif (Number.isNaN(from)) return 0;\n\tconst d = value - from;\n\tlet r = 0;\n\tif (kind === `absolute`) r = Math.abs(d);\n\telse if (kind === `numerical`) r = d;\n\telse if (kind === `relative`) r = Math.abs(d / from);\n\telse if (kind === `relativeSigned`) r = d / from;\n\telse throw new TypeError(`Unknown kind: '${kind}' Expected: 'absolute', 'relative', 'relativeSigned' or 'numerical'`);\n\treturn r;\n};\n//#endregion\n//#region src/guard.ts\n/**\n* Returns true if `possibleNumber` is a number and not NaN\n* @param possibleNumber\n* @returns\n*/\nconst isValid = (possibleNumber) => {\n\tif (typeof possibleNumber !== `number`) return false;\n\tif (Number.isNaN(possibleNumber)) return false;\n\treturn true;\n};\n//#endregion\n//#region src/filter.ts\n/**\n* Filters an iterator of values, only yielding\n* those that are valid numbers\n*\n* ```js\n* const data = [true, 10, '5', { x: 5 }];\n* for (const n of Numbers.filterIterable(data)) {\n* // 10\n* }\n* ```\n* @param it\n*/\nfunction* filterIterable(it) {\n\tfor (const v of it) if (isValid(v)) yield v;\n}\n/**\n* Returns a function that yields _true_ if a value\n* is at least `threshold`\n* ```js\n* const t = thresholdAtLeast(50);\n* t(50); // true\n* t(0); // false\n* t(55); // true\n* ```\n* @param threshold \n* @returns \n*/\nconst thresholdAtLeast = (threshold) => {\n\treturn (v) => {\n\t\treturn v >= threshold;\n\t};\n};\n/**\n* Returns a function that yields _true_\n* if a number is at least _min_ and no greater than _max_\n* \n* ```js\n* const t = rangeInclusive(50, 100);\n* t(40); // false\n* t(50); // true\n* t(60); // true\n* t(100); // true\n* t(101); // false\n* ```\n* @param min \n* @param max \n* @returns \n*/\nconst rangeInclusive = (min, max) => {\n\treturn (v) => {\n\t\treturn v >= min && v <= max;\n\t};\n};\n//#endregion\n//#region src/flip.ts\n/**\n* Flips a percentage-scale number: `1 - v`.\n*\n* The utility of this function is that it sanity-checks\n* that `v` is in 0..1 scale.\n*\n* ```js\n* flip(1); // 0\n* flip(0.5); // 0.5\n* flip(0); // 1\n* ```\n* @param v\n* @returns\n*/\nconst flip = (v) => {\n\tif (typeof v === `function`) v = v();\n\tresultThrow(numberTest(v, `percentage`, `v`));\n\treturn 1 - v;\n};\n//#endregion\n//#region src/generate.ts\n/**\n* Generates a range of numbers, starting from `start` and counting by `interval`.\n* If `end` is provided, generator stops when reached\n*\n* Unlike {@link numericRange}, numbers might contain rounding errors\n*\n* ```js\n* for (const c of numericRangeRaw(10, 100)) {\n* // 100, 110, 120 ...\n* }\n* ```\n* \n* Get results as an array\n* ```js\n* const c = [...numericRangeRaw(1,0,5)]; // [0,1,2,3,4]\n* ```\n* @param interval Interval between numbers\n* @param start Start\n* @param end End (if undefined, range never ends). Inclusive.\n*/\nconst numericRangeRaw = function* (interval, start = 0, end, repeating = false) {\n\tif (interval <= 0) throw new Error(`Interval is expected to be above zero`);\n\tif (typeof end === `undefined`) end = Number.MAX_SAFE_INTEGER;\n\tlet v = start;\n\tdo\n\t\twhile (v <= end) {\n\t\t\tyield v;\n\t\t\tv += interval;\n\t\t}\n\twhile (repeating);\n};\n/**\n* Generates a range of numbers, with a given interval.\n*\n* @example For-loop\n* ```\n* let loopForever = numericRange(0.1); // By default starts at 0 and counts upwards forever\n* for (v of loopForever) {\n* console.log(v);\n* }\n* ```\n*\n* @example If you want more control over when/where incrementing happens...\n* ```js\n* let percent = numericRange(0.1, 0, 1);\n*\n* let percentResult = percent.next().value;\n* ```\n*\n* Note that computations are internally rounded to avoid floating point math issues. So if the `interval` is very small (eg thousandths), specify a higher rounding\n* number.\n*\n* @param interval Interval between numbers\n* @param start Start. Defaults to 0\n* @param end End (if undefined, range never ends). Inclusive.\n* @param repeating Range loops from start indefinately. Default _false_\n* @param rounding A rounding that matches the interval avoids floating-point math hikinks. Eg if the interval is 0.1, use a rounding of 10\n*/\nconst numericRange = function* (interval, start = 0, end, repeating = false, rounding) {\n\tresultThrow(numberTest(interval, `nonZero`));\n\tconst negativeInterval = interval < 0;\n\tif (end === void 0) {} else {\n\t\tif (negativeInterval && start < end) throw new Error(`Interval of ${interval.toString()} will never go from ${start.toString()} to ${end.toString()}`);\n\t\tif (!negativeInterval && start > end) throw new Error(`Interval of ${interval.toString()} will never go from ${start.toString()} to ${end.toString()}`);\n\t}\n\trounding = rounding ?? 1e3;\n\tif (end === void 0) end = Number.MAX_SAFE_INTEGER;\n\telse end *= rounding;\n\tinterval = interval * rounding;\n\tdo {\n\t\tlet v = start * rounding;\n\t\twhile (!negativeInterval && v <= end || negativeInterval && v >= end) {\n\t\t\tyield v / rounding;\n\t\t\tv += interval;\n\t\t}\n\t} while (repeating);\n};\n/**\n* Yields numeric range between 0.0-1.0.\n*\n* ```\n* // Yields: [0, 0.2, 0.4, 0.6, 0.8, 1]\n* const a = [...numericPercent(0.2)];\n*\n* // Repeating flag set to true:\n* for (const v of numericPercent(0.2, true)) {\n* // Infinite loop. V loops back to 0 after hitting 1\n* }\n* ```\n*\n* If `repeating` is true, it loops back to 0 after reaching 1\n* @param interval Interval (default: 0.01, ie. 1%)\n* @param repeating Whether generator should loop (default: false)\n* @param start Start (default: 0)\n* @param end End (default: 1)\n* @returns\n*/\nconst numericPercent = function(interval = .01, repeating = false, start = 0, end = 1) {\n\tresultThrow(numberTest(interval, `percentage`, `interval`), numberTest(start, `percentage`, `start`), numberTest(end, `percentage`, `end`));\n\treturn numericRange(interval, start, end, repeating);\n};\n//#endregion\n//#region src/is-approx.ts\n/**\n* Checks if a value is within range of a base value\n* \n* ```js\n* // Check if 101 is within 10% of 100\n* isApprox(0.1, 100, 101);\n* \n* // Gets a function to compare some value of 10% range to 100\n* const c = isApprox(0.1,100);\n* c(101);\n* \n* // Gets a function to compare some base value and value to 10% range\n* const c = isApprox(0.1);\n* c(100, 101);\n* ```\n* \n* Throws an error if range or base values are NaN.\n* If value being checked is NaN or infinity, _false_ is returned.\n* @param rangePercent \n* @param baseValue \n* @param v \n* @returns \n*/\nfunction isApprox(rangePercent, baseValue, v) {\n\tresultThrow(numberTest(rangePercent, `percentage`, `rangePercent`));\n\tconst range = Math.floor(rangePercent * 100);\n\tconst test = (base, value) => {\n\t\ttry {\n\t\t\tif (typeof value !== `number`) return false;\n\t\t\tif (Number.isNaN(value)) return false;\n\t\t\tif (!Number.isFinite(value)) return false;\n\t\t\tconst diff = Math.abs(value - base);\n\t\t\treturn (base === 0 ? Math.floor(diff * 100) : Math.floor(diff / base * 100)) <= range;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t};\n\tif (baseValue === void 0) return test;\n\tresultThrow(numberTest(baseValue, ``, `baseValue`));\n\tif (v === void 0) return (value) => test(baseValue, value);\n\telse return test(baseValue, v);\n}\n/**\n* Yields a function that checks if a value is close to any target value\n* ```js\n* const c = isCloseToAny(1, 10, 20, 30, 40);\n* c(11); // True - within 1 range of 10\n* c(19); // True - within 1 range of 20\n* c(0); // False\n* ```\n* \n* Returned function accepts multiple values, returning\n* _true_ if any of them are within range\n* ```js\n* c(0, 1, 11); // Would return true based on 11\n* ```\n* @param allowedRangeAbsolute \n* @param targets \n* @returns \n*/\nconst isCloseToAny = (allowedRangeAbsolute, ...targets) => {\n\tconst targetsMin = targets.map((t) => t - allowedRangeAbsolute);\n\tconst targetsMax = targets.map((t) => t + allowedRangeAbsolute);\n\treturn (...values) => {\n\t\tfor (const v of values) for (let index = 0; index < targets.length; index++) if (v >= targetsMin[index] && v <= targetsMax[index]) return true;\n\t\treturn false;\n\t};\n};\n//#endregion\n//#region src/kalman.ts\n/**\n* KalmanFilter\n* \n* author: Wouter Bulten\n* see {@link http://github.com/wouterbulten/kalmanjs}\n* version Version: 1.0.0-beta\n* copyright Copyright 2015-2018 Wouter Bulten\n* license MIT License\n*/\nvar Kalman1dFilter = class {\n\tR;\n\tQ;\n\tA;\n\tC;\n\tB;\n\tcov;\n\tx;\n\t/**\n\t* Create 1-dimensional kalman filter\n\t*/\n\tconstructor(options = {}) {\n\t\tthis.R = options.r ?? 1;\n\t\tthis.Q = options.q ?? 1;\n\t\tthis.A = options.a ?? 1;\n\t\tthis.C = options.c ?? 1;\n\t\tthis.B = options.b ?? 0;\n\t\tthis.cov = NaN;\n\t\tthis.x = NaN;\n\t}\n\t/**\n\t* Filter a new value\n\t* @param {Number} z Measurement\n\t* @param {Number} u Control\n\t* @return {Number}\n\t*/\n\tfilter(z, u = 0) {\n\t\tif (isNaN(this.x)) {\n\t\t\tthis.x = 1 / this.C * z;\n\t\t\tthis.cov = 1 / this.C * this.Q * (1 / this.C);\n\t\t} else {\n\t\t\tconst predX = this.predict(u);\n\t\t\tconst predCov = this.uncertainty();\n\t\t\tconst K = predCov * this.C * (1 / (this.C * predCov * this.C + this.Q));\n\t\t\tthis.x = predX + K * (z - this.C * predX);\n\t\t\tthis.cov = predCov - K * this.C * predCov;\n\t\t}\n\t\treturn this.x;\n\t}\n\t/**\n\t* Predict next value\n\t* @param {Number} [u] Control\n\t* @return {Number}\n\t*/\n\tpredict(u = 0) {\n\t\treturn this.A * this.x + this.B * u;\n\t}\n\t/**\n\t* Return uncertainty of filter\n\t* @return {Number}\n\t*/\n\tuncertainty() {\n\t\treturn this.A * this.cov * this.A + this.R;\n\t}\n\t/**\n\t* Return the last filtered measurement\n\t* @return {Number}\n\t*/\n\tlastMeasurement() {\n\t\treturn this.x;\n\t}\n\t/**\n\t* Set measurement noise Q\n\t* @param {Number} noise\n\t*/\n\tsetMeasurementNoise(noise) {\n\t\tthis.Q = noise;\n\t}\n\t/**\n\t* Set the process noise R\n\t* @param {Number} noise\n\t*/\n\tsetProcessNoise(noise) {\n\t\tthis.R = noise;\n\t}\n};\n/**\n* Returns a function that performs 1D Kalman filtering.\n* \n* ```js\n* const f = kalman1dFilter();\n* f(10); // 10\n* ```\n* \n* Under the hood creates a {@link Kalman1dFilter} instance and returns its `filter` method.\n* @param options \n* @returns \n*/\nconst kalman1dFilter = (options = {}) => {\n\tconst f = new Kalman1dFilter(options);\n\treturn f.filter.bind(f);\n};\n//#endregion\n//#region src/bipolar.ts\nvar bipolar_exports = /* @__PURE__ */ __exportAll({\n\tclamp: () => clamp$1,\n\tfromScalar: () => fromScalar,\n\timmutable: () => immutable,\n\tscale: () => scale$1,\n\tscaleUnclamped: () => scaleUnclamped,\n\ttoScalar: () => toScalar,\n\ttowardZero: () => towardZero\n});\n/**\n* Wrapper for bipolar-based values. Immutable.\n* All functions will clamp to keep it in legal range.\n* \n* ```js\n* let v = immutable(); // Starts with 0 by default\n* v = v.add(0.1); // v.value is 0.1\n* v = v.inverse(); // v.value is -0.1\n* v = v.multiply(0.2); // v.value is -0.02\n* \n* v = immutable(1);\n* v = v.towardZero(0.1); // 0.9\n* v = v.interpolate(0.1, 1);\n* ```\n* \n* Wrapped values can be coerced into number:\n* ```js\n* const v = immutable(1);\n* const x = +v+10;\n* // x = 11\n* ```\n* @param startingValueOrBipolar Initial numeric value or BipolarWrapper instance\n* @throws {TypeError} If start value is out of bipolar range or invalid\n* @returns \n*/\nconst immutable = (startingValueOrBipolar = 0) => {\n\tconst startingValue = typeof startingValueOrBipolar === `number` ? startingValueOrBipolar : startingValueOrBipolar.value;\n\tif (startingValue > 1) throw new TypeError(`Start value cannot be larger than 1`);\n\tif (startingValue < -1) throw new TypeError(`Start value cannot be smaller than -1`);\n\tif (Number.isNaN(startingValue)) throw new TypeError(`Start value is NaN`);\n\tconst v = startingValue;\n\treturn {\n\t\t[Symbol.toPrimitive](hint) {\n\t\t\tif (hint === `number` || hint === `default`) return v;\n\t\t\telse if (hint === `string`) return v.toString();\n\t\t\treturn true;\n\t\t},\n\t\tvalue: v,\n\t\ttowardZero: (amount) => {\n\t\t\treturn immutable(towardZero(v, amount));\n\t\t},\n\t\tadd: (amount) => {\n\t\t\treturn immutable(clamp$1(v + amount));\n\t\t},\n\t\tmultiply: (amount) => {\n\t\t\treturn immutable(clamp$1(v * amount));\n\t\t},\n\t\tinverse: () => {\n\t\t\treturn immutable(-v);\n\t\t},\n\t\tinterpolate: (amount, target) => {\n\t\t\treturn immutable(clamp$1(interpolate(amount, v, target)));\n\t\t},\n\t\tasScalar: (max = 1, min = 0) => {\n\t\t\treturn toScalar(v, max, min);\n\t\t}\n\t};\n};\n/**\n* Converts bipolar value to a scalar. That is, converts from\n* -1..1 range to 0..1.\n* \n* ```js\n* Bipolar.toScalar(-1); // 0.0\n* Bipolar.toScalar( 0); // 0.5\n* Bipolar.toScalar( 1); // 1.0\n* ```\n* \n* Range can be changed:\n* ```js\n* Bipolar.toScalar(0, 100); // Uses 0..100 scale, so output is 50\n* Bipolar.toScalar(0, 100, 50); // Uses 50..1000 scale, so output is 75\n* ```\n* \n* Throws an error if `bipolarValue` is not a number or NaN\n* @param bipolarValue Value to convert to scalar\n* @returns Scalar value on 0..1 range.\n*/\nconst toScalar = (bipolarValue, max = 1, min = 0) => {\n\tif (typeof bipolarValue !== `number`) throw new Error(`Param 'bipolarValue' to be a number. Got: ${typeof bipolarValue}`);\n\tif (Number.isNaN(bipolarValue)) throw new Error(`Param 'bipolarValue' is NaN`);\n\treturn scale(bipolarValue, -1, 1, min, max);\n};\n/**\n* Makes a scalar into a bipolar value.\n* \n* That is, input range is 0..1, output range is -1...1\n*\n* ```js\n* Bipolar.fromScalar(1); // 1\n* Bipolar.fromScalar(0); // -1\n* Bipolar.fromScalar(0.5); // 0\n* ```\n* \n* Throws an error if `scalarValue` is outside 0..1 scale.\n* @param scalarValue Scalar value to convert\n* @returns Bipolar value on -1..1 scale\n*/\nconst fromScalar = (scalarValue) => {\n\tresultThrow(numberTest(scalarValue, `percentage`, `v`));\n\treturn scalarValue * 2 - 1;\n};\n/**\n* Scale & clamp value to bipolar range (-1..1).\n* ```js\n* // Scale 100 on 0..100 scale\n* Bipolar.scale(100, 0, 100); // 1\n* Bipolar.scale(50, 0, 100); // 0\n* Bipolar.scale(0, 0, 100); // -1\n* ```\n* \n* Return value is clamped.\n* @param inputValue Value to scale\n* @param inMin Minimum of scale\n* @param inMax Maximum of scale\n* @returns Bipolar value on -1..1 scale\n*/\nconst scale$1 = (inputValue, inMin, inMax) => {\n\treturn clamp$1(scaler(inMin, inMax, -1, 1)(inputValue));\n};\n/**\n* Scale a number to bipolar range (-1..1). Not clamped, so we might exceed range.\n* \n* ```js\n* // Scale 100 on 0..100 scale\n* Bipolar.scaleUnclamped(100, 0, 100); // 1\n* Bipolar.scaleUnclamped(50, 0, 100); // 0\n* Bipolar.scaleUnclamped(0, 0, 100); // -1\n* ```\n* \n* @param inputValue Value to scale\n* @param inMin Minimum of scale\n* @param inMax Maximum of scale\n* @returns Bipolar value on -1..1 scale\n*/\nconst scaleUnclamped = (inputValue, inMin, inMax) => {\n\treturn scaler(inMin, inMax, -1, 1)(inputValue);\n};\n/**\n* Clamp a bipolar value\n* ```js\n* Bipolar.clamp(-1); // -1\n* Bipolar.clamp(-1.1); // -1\n* ```\n* \n* Throws an error if `bipolarValue` is not a number or NaN.\n* @param bipolarValue Value to clamp\n* @returns Clamped value on -1..1 scale\n*/\nconst clamp$1 = (bipolarValue) => {\n\tif (typeof bipolarValue !== `number`) throw new Error(`Param 'bipolarValue' must be a number. Got: ${typeof bipolarValue}`);\n\tif (Number.isNaN(bipolarValue)) throw new Error(`Param 'bipolarValue' is NaN`);\n\tif (bipolarValue > 1) return 1;\n\tif (bipolarValue < -1) return -1;\n\treturn bipolarValue;\n};\n/**\n* Pushes a bipolar value toward zero by `amount`.\n* Return value is clamped on bipolar range of -1..1\n* \n* ```js\n* Bipolar.towardZero(-1, 0.1); // -0.9\n* Bipolar.towardZero( 1, 0.1); // 0.9\n* Bipolar.towardZero( 0, 0.1); // 0.0\n* Bipolar.towardZero( 1, 1.1); // 0.0\n* ```\n* \n* If `amount` is greater than 1, 0 is returned.\n* Throws an error if `bipolarValue` or `amount` are not numbers.\n* Throws an error if `amount` is below zero.\n* @param bipolarValue Bipolar value to nudge toward zero\n* @param amount Amount to nudge by\n* @returns Bipolar value -1...1\n*/\nconst towardZero = (bipolarValue, amount) => {\n\tif (typeof bipolarValue !== `number`) throw new Error(`Parameter 'bipolarValue' must be a number. Got: ${typeof bipolarValue}`);\n\tif (typeof amount !== `number`) throw new Error(`Parameter 'amount' must be a number. Got: ${typeof amount}`);\n\tif (amount < 0) throw new Error(`Parameter 'amount' must be positive`);\n\tif (bipolarValue < 0) {\n\t\tbipolarValue += amount;\n\t\tif (bipolarValue > 0) bipolarValue = 0;\n\t} else if (bipolarValue > 0) {\n\t\tbipolarValue -= amount;\n\t\tif (bipolarValue < 0) bipolarValue = 0;\n\t}\n\treturn bipolarValue;\n};\n//#endregion\n//#region src/wrap.ts\n/**\n* Wraps an integer number within a specified range, defaulting to degrees (0-360). Use {@link wrap} for floating-point wrapping.\n*\n* This is useful for calculations involving degree angles and hue, which wrap from 0-360.\n* Eg: to add 200 to 200, we don't want 400, but 40.\n*\n* ```js\n* const v = wrapInteger(200+200, 0, 360); // 40\n* ```\n*\n* Or if we minus 100 from 10, we don't want -90 but 270\n* ```js\n* const v = wrapInteger(10-100, 0, 360); // 270\n* ```\n*\n* `wrapInteger` uses 0-360 as a default range, so both of these\n* examples could just as well be:\n*\n* ```js\n* wrapInteger(200+200); // 40\n* wrapInteger(10-100); // 270\n* ```\n*\n* Non-zero starting points can be used. A range of 20-70:\n* ```js\n* const v = wrapInteger(-20, 20, 70); // 50\n* ```\n*\n* Note that the minimum value is inclusive, while the maximum is _exclusive_.\n* So with the default range of 0-360, 360 is never reached:\n*\n* ```js\n* wrapInteger(360); // 0\n* wrapInteger(361); // 1\n* ```\n*\n* If you just want to lock values to a range without wrapping, consider {@link clamp}.\n*\n* @param v Value to wrap\n* @param min Integer minimum of range (default: 0). Inclusive\n* @param max Integer maximum of range (default: 360). Exlusive\n* @returns\n*/\nconst wrapInteger = (v, min = 0, max = 360) => {\n\tresultThrow(integerTest(v, void 0, `v`), integerTest(min, void 0, `min`), integerTest(max, void 0, `max`));\n\tif (v === min) return min;\n\tif (v === max) return min;\n\tif (v > 0 && v < min) v += min;\n\tv -= min;\n\tmax -= min;\n\tv = v % max;\n\tif (v < 0) v = max - Math.abs(v) + min;\n\treturn v + min;\n};\n/**\n* Wraps floating point numbers to be within a range (default: 0..1). Use {@link wrapInteger} if you want to wrap integer values.\n*\n* This logic makes sense for some things like rotation angle.\n*\n* If you just want to lock values to a range without wrapping, consider {@link clamp}.\n*\n* ```js\n* wrap(1.2); // 0.2\n* wrap(2); // 1.0\n* wrap(-0.2); // 0.8\n* ```\n*\n* A range can be provided too:\n* ```js\n* wrap(30, 20, 50); \t // 30\n* wrap(60, 20, 50); // 30\n* ```\n* @param v\n* @param min\n* @param max\n* @returns\n*/\nconst wrap = (v, min = 0, max = 1) => {\n\tresultThrow(numberTest(v, ``, `min`), numberTest(min, ``, `min`), numberTest(max, ``, `max`));\n\tif (v === min) return min;\n\tif (v === max) return min;\n\twhile (v <= min || v >= max) {\n\t\tif (v === max) break;\n\t\tif (v === min) break;\n\t\tif (v > max) v = min + (v - max);\n\t\telse if (v < min) v = max - (min - v);\n\t}\n\treturn v;\n};\n/**\n* Performs a calculation within a wrapping number range. This is a lower-level function.\n* See also: {@link wrapInteger} for simple wrapping within a range.\n*\n* `min` and `max` define the start and end of the valid range, inclusive. Eg for hue degrees it'd be 0, 360.\n* `a` and `b` is the range you want to work in.\n*\n* For example, let's say you want to get the middle point between a hue of 30 and a hue of 330 (ie warmer colours):\n* ```js\n* wrapRange(0,360, (distance) => {\n* // for a:0 and b:330, distance would be 90 from 30 degrees to 330 (via zero)\n* return distance * 0.5; // eg return middle point\n* }, 30, 330);\n* ```\n*\n* The return value of the callback should be in the range of 0-distance. `wrapRange` will subsequently\n* conform it to the `min` and `max` range before it's returned to the caller.\n*\n* @param a Output start (eg. 60)\n* @param b Output end (eg 300)\n* @param min Range start (eg 0)\n* @param max Range end (eg 360)\n* @param fn Returns a computed value from 0 to `distance`.\n* @returns\n*/\nconst wrapRange = (min, max, fn, a, b) => {\n\tlet r = 0;\n\tconst distF = Math.abs(b - a);\n\tconst distFwrap = Math.abs(max - a + b);\n\tconst distBWrap = Math.abs(a + (360 - b));\n\tconst distMin = Math.min(distF, distFwrap, distBWrap);\n\tif (distMin === distBWrap) r = a - fn(distMin);\n\telse if (distMin === distFwrap) r = a + fn(distMin);\n\telse if (a > b) r = a - fn(distMin);\n\telse r = a + fn(distMin);\n\treturn wrapInteger(r, min, max);\n};\n//#endregion\n//#region src/pi-pi.ts\nconst piPi = Math.PI * 2;\n//#endregion\n//#region src/interpolate.ts\n/**\n* Interpolates between `a` and `b` by `amount`. Aka `lerp`.\n*\n* [ixfx Guide on Interpolation](https://ixfx.fun/data/interpolation/overview/)\n*\n* @example Get the halfway point between 30 and 60\n* ```js\n* interpolate(0.5, 30, 60);\n* ```\n*\n* See also {@link interpolatorStepped} and {@link https://api.ixfx.fun/_ixfx/modulation/interpolatorInterval/} for functions\n* which help to manage progression from A->B over steps or interval.\n* \n* Usually interpolation amount is on a 0...1 scale, inclusive. What is the interpolation result\n* if this scale is exceeded? By default it is clamped to 0..1, so the return value is always between `a` and `b` (inclusive).\n* \n* Alternatively, set the `limits` option to process `amount`:\n* * 'wrap': wrap amount, eg 1.5 is the same as 0.5, 2 is the same as 1\n* * 'ignore': allow exceeding values. eg 1.5 will yield b*1.5.\n* * 'clamp': default behaviour of clamping interpolation amount to 0..1\n* \n* Interpolation can be non-linear using 'easing' option or 'transform' funciton.\n* ```js\n* interpolate(0.1, 0, 100, { easing: `quadIn` });\n* ```\n* There are a few variations when calling `interpolate`, depending on what parameters are fixed.\n* * `interpolate(amount)`: returns a function that needs a & b \n* * `interpolate(a, b)`: returns a function that needs the interpolation amount\n*/\nfunction interpolate(pos1, pos2, pos3, pos4) {\n\tlet amountProcess;\n\tlet limits = `clamp`;\n\tconst handleAmount = (amount) => {\n\t\tif (amountProcess) amount = amountProcess(amount);\n\t\tif (limits === void 0 || limits === `clamp`) amount = clamp(amount);\n\t\telse if (limits === `wrap`) {\n\t\t\tif (amount > 1) amount = amount % 1;\n\t\t\telse if (amount < 0) amount = 1 + amount % 1;\n\t\t}\n\t\treturn amount;\n\t};\n\tconst doTheEase = (_amt, _a, _b) => {\n\t\tresultThrow(numberTest(_a, ``, `a`), numberTest(_b, ``, `b`), numberTest(_amt, ``, `amount`));\n\t\t_amt = handleAmount(_amt);\n\t\treturn (1 - _amt) * _a + _amt * _b;\n\t};\n\tconst readOpts = (o = {}) => {\n\t\tif (o.transform !== void 0) {\n\t\t\tif (typeof o.transform !== `function`) throw new Error(`Param 'transform' is expected to be a function. Got: ${typeof o.transform}`);\n\t\t\tamountProcess = o.transform;\n\t\t}\n\t\tlimits = o.limits ?? `clamp`;\n\t};\n\tconst rawEase = (_amt, _a, _b) => (1 - _amt) * _a + _amt * _b;\n\tif (typeof pos1 !== `number`) throw new TypeError(`First param is expected to be a number. Got: ${typeof pos1}`);\n\tif (typeof pos2 === `number`) {\n\t\tlet a;\n\t\tlet b;\n\t\tif (pos3 === void 0 || typeof pos3 === `object`) {\n\t\t\ta = pos1;\n\t\t\tb = pos2;\n\t\t\treadOpts(pos3);\n\t\t\treturn (amount) => doTheEase(amount, a, b);\n\t\t} else if (typeof pos3 === `number`) {\n\t\t\ta = pos2;\n\t\t\tb = pos3;\n\t\t\treadOpts(pos4);\n\t\t\treturn doTheEase(pos1, a, b);\n\t\t} else throw new Error(`Values for 'a' and 'b' not defined`);\n\t} else if (pos2 === void 0 || typeof pos2 === `object`) {\n\t\tconst amount = handleAmount(pos1);\n\t\treadOpts(pos2);\n\t\tresultThrow(numberTest(amount, ``, `amount`));\n\t\treturn (aValue, bValue) => rawEase(amount, aValue, bValue);\n\t}\n}\n/**\n* Returns a function that interpolates from A to B.\n* It steps through the interpolation with each call to the returned function.\n* This means that the `incrementAmount` will hinge on the rate\n* at which the function is called. Alternatively, consider {@link https://api.ixfx.fun/_ixfx/modulation/interpolatorInterval/}\n* which steps on the basis of clock time.\n* \n* ```js\n* // Interpolate from 0..1 by 0.01\n* const v = interpolatorStepped(0.01, 100, 200);\n* v(); // Each call returns a value closer to target\n* // Eg: 100, 110, 120, 130 ...\n* ```\n* \n* Under the hood, it calls `interpolate` with an amount that\n* increases by `incrementAmount` each time.\n* \n* When calling `v()` to step the interpolator, you can also pass\n* in new B and A values. Note that the order is swapped: the B (target) is provided first, and\n* then optionally A.\n* \n* ```js\n* const v = interpolatorStepped(0.1, 100, 200); // Interpolate 100->200\n* v(300, 200); // Retarget to 200->300 and return result\n* v(150); // Retarget 200->150 and return result\n* ```\n* \n* This allows you to maintain the current interpolation progress.\n* @param incrementAmount Amount to increment by\n* @param a Start value. Default: 0\n* @param b End value. Default: 1\n* @param startInterpolationAt Starting interpolation amount. Default: 0\n* @param options Options for interpolation\n* @returns \n*/\nconst interpolatorStepped = (incrementAmount, a = 0, b = 1, startInterpolationAt = 0, options) => {\n\tlet amount = startInterpolationAt;\n\treturn (retargetB, retargetA) => {\n\t\tif (retargetB !== void 0) b = retargetB;\n\t\tif (retargetA !== void 0) a = retargetA;\n\t\tif (amount >= 1) return b;\n\t\tconst value = interpolate(amount, a, b, options);\n\t\tamount += incrementAmount;\n\t\treturn value;\n\t};\n};\n/**\n* Interpolate between angles `a` and `b` by `amount`. Angles are in radians.\n*\n* ```js\n* interpolateAngle(0.5, Math.PI, Math.PI/2);\n* ```\n* @param amount\n* @param aRadians Start angle (radian)\n* @param bRadians End angle (radian)\n* @returns\n*/\nconst interpolateAngle = (amount, aRadians, bRadians, options) => {\n\tconst t = wrap(bRadians - aRadians, 0, piPi);\n\treturn interpolate(amount, aRadians, aRadians + (t > Math.PI ? t - piPi : t), options);\n};\n//#endregion\n//#region src/iqr.ts\n/**\n* Calculate interquartile range.\n* \n* If `n` is unspecified, `data.length` is used.\n* @param data \n* @param n \n* @returns \n*/\nconst interquartileRange = (data, n) => {\n\treturn getQuantile(data, .75) - getQuantile(data, .25);\n};\n/**\n* Returns a function which itself returns _true_ if a value is an outlier.\n* \n* This can be used for example to get a copy of an array without outliers:\n* ```js\n* const p = computeIsOutlier(someData);\n* const someDataWithoutOutliers = someData.filter(value => !p(value));\n* ```\n* \n* Outliers are defined as: \"a point which falls more than 1.5 times the interquartile range above the third quartile or below the first quartile.\" [Wolfram](https://mathworld.wolfram.com/Outlier.html)\n* \n* If array length is less than 4, no value will be considered an outlier.\n* @param data Data to filter\n* @param multiplier Multiplier of Q3 Q1. Default: 1.5 \n* @returns \n*/\nconst computeIsOutlier = (data, multiplier = 1.5) => {\n\tif (data.length < 4) return (value) => false;\n\tconst values = data.toSorted((a, b) => a - b);\n\tconst q1 = getQuantile(values, .25, true);\n\tconst q3 = getQuantile(values, .75, true);\n\tconst iqr = q3 - q1;\n\tconst maxValue = q3 + iqr * multiplier;\n\tconst minValue = q1 - iqr * multiplier;\n\treturn (value) => value < minValue || value > maxValue;\n};\n/**\n* Gets the value at a specific quantile\n* ```js\n* getQuantile(data, 25); // 1st quartile\n* getQuantile(data, 75); // 3rd quartile\n* ```\n* @param data \n* @param quantile \n* @param presorted Pass _true_ if `data` is already sorted\n* @returns \n*/\nconst getQuantile = (data, quantile, presorted = false) => {\n\tif (quantile > 1 || quantile < 0) throw new TypeError(`Param 'quantile' is expected to be in 0..1 range. Got: '${quantile}'`);\n\tif (!Array.isArray(data)) throw new TypeError(`Param 'data' is expected to be an array. Got: ${typeof data}`);\n\tconst index = quantile * (data.length - 1);\n\tif (!presorted) data = data.toSorted((a, b) => a - b);\n\tif (quantile === 0) return data[0];\n\tif (quantile === 1) return data[data.length - 1];\n\tif (index % 1 === 0) return data[index];\n\tconst lowerIndex = Math.floor(index);\n\tif (data[lowerIndex + 1] !== void 0) return (data[lowerIndex] + data[lowerIndex + 1]) / 2;\n\treturn data[lowerIndex];\n};\n//#endregion\n//#region src/round.ts\n/**\n* Rounds a number.\n*\n* If one parameter is given, it's the decimal places,\n* and a rounding function is returned:\n* ```js\n* const r = round(2);\n* r(10.12355); // 10.12\n* ```\n*\n* If two parameters are given, the first is decimal places,\n* the second the value to round.\n* ```js\n* round(2, 10.12355); // 10.12\n* ```\n* @param decimalPlaces\n* @returns\n*/\nfunction round(a, b, roundUp) {\n\tresultThrow(integerTest(a, `positive`, `decimalPlaces`));\n\tconst up = typeof b === `boolean` ? b : roundUp ?? false;\n\tlet rounder;\n\tif (a === 0) rounder = Math.round;\n\telse {\n\t\tconst p = Math.pow(10, a);\n\t\tif (up) rounder = (v) => Math.ceil(v * p) / p;\n\t\telse rounder = (v) => Math.floor(v * p) / p;\n\t}\n\tif (typeof b === `number`) return rounder(b);\n\treturn rounder;\n}\n//#endregion\n//#region src/linear-space.ts\n/**\n* Generates a `step`-length series of values between `start` and `end` (inclusive).\n* Each value will be equally spaced.\n*\n* ```js\n* for (const v of linearSpace(1, 5, 6)) {\n* // Yields: [ 1, 1.8, 2.6, 3.4, 4.2, 5 ]\n* }\n* ```\n*\n* Numbers can be produced from large to small as well\n* ```js\n* const values = [...linearSpace(10, 5, 3)];\n* // Yields: [10, 7.5, 5]\n* ```\n* @param start Start number (inclusive)\n* @param end End number (inclusive)\n* @param steps How many steps to make from start -> end\n* @param precision Number of decimal points to round to\n*/\nfunction* linearSpace(start, end, steps, precision) {\n\tresultThrow(numberTest(start, ``, `start`), numberTest(end, ``, `end`), numberTest(steps, ``, `steps`));\n\tconst r = precision ? round(precision) : (v) => v;\n\tconst step = (end - start) / (steps - 1);\n\tresultThrow(numberTest(step, ``, `step`));\n\tif (!Number.isFinite(step)) throw new TypeError(`Calculated step value is infinite`);\n\tfor (let index = 0; index < steps; index++) yield r(start + step * index);\n}\n//#endregion\n//#region src/moving-average.ts\nconst PiPi = Math.PI * 2;\n/**\n* A moving average calculator (exponential weighted moving average) which does not keep track of\n* previous samples. Less accurate, but uses less system resources.\n*\n* The `scaling` parameter determines smoothing. A value of `1` means that\n* the latest value is used as the average - that is, no smoothing. Higher numbers\n* introduce progressively more smoothing by weighting the accumulated prior average more heavily.\n*\n* ```\n* const ma = movingAverageLight(); // default scaling of 3\n* ma(50); // 50\n* ma(100); // 75\n* ma(75); // 75\n* ma(0); // 50\n* ```\n*\n* Note that the final average of 50 is pretty far from the last value of 0. To make it more responsive,\n* we could use a lower scaling factor: `movingAverageLight(2)`. This yields a final average of `37.5` instead.\n*\n* @param scaling Scaling factor. 1 is no smoothing. Default: 3\n* @returns Function that adds to average.\n*/\nconst movingAverageLight = (scaling = 3) => {\n\tresultThrow(numberTest(scaling, `aboveZero`, `scaling`));\n\tlet average = 0;\n\tlet count = 0;\n\treturn (v) => {\n\t\tif (numberTest(v, ``, `v`).success && v !== void 0) {\n\t\t\tcount++;\n\t\t\taverage = average + (v - average) / Math.min(count, scaling);\n\t\t}\n\t\treturn average;\n\t};\n};\n/**\n* Creates a moving average for a set number of `samples`.\n* It returns a function which in turn yields an average value.\n* \n* Moving average are useful for computing the average over a recent set of numbers.\n* A lower number of samples produces a computed value that is lower-latency yet more jittery.\n* A higher number of samples produces a smoother computed value which takes longer to respond to\n* changes in data.\n*\n* Sample size is considered with respect to the level of latency/smoothness trade-off, and also\n* the rate at which new data is added to the moving average.\n*\n*\n* ```js\n* const ma = movingAverage(10);\n* ma(10); // 10\n* ma(5); // 7.5\n* ```\n*\n* A weighting function can be provided to shape how the average is\n* calculated - eg privileging the most recent data over older data.\n* It uses `Arrays.averageWeighted` under the hood.\n*\n* ```js\n* import { movingAverage } from '@ixfx/numbers.js';\n* import { gaussian } from '@ixfx/modulation.js';\n* \n* // Give more weight to data in middle of sampling window\n* const ma = movingAverage(100, gaussian());\n* ```\n*\n* Because it keeps track of `samples` previous data, there is a memory impact. A lighter version is {@link movingAverageLight} which does not keep a buffer of prior data, but can't be as easily fine-tuned.\n* @param samplesOrOptions Number of samples to compute average from, or object of options\n* @returns\n*/\nconst movingAverage = (samplesOrOptions) => movingAverageWithContext(samplesOrOptions).seen;\nconst movingAverageWithContext = (samplesOrOptions) => {\n\tconst nanPolicy = typeof samplesOrOptions === `number` ? `ignore` : samplesOrOptions.nanPolicy ?? `ignore`;\n\tconst w = movingWindowWithContext(samplesOrOptions);\n\tconst averageFunction = typeof samplesOrOptions === `number` ? average : samplesOrOptions.weighter ? averageWeigher(samplesOrOptions.weighter) : average;\n\tconst seen = (value) => {\n\t\tif (Number.isNaN(value)) {\n\t\t\tif (nanPolicy === `throw`) throw new TypeError(`Value is NaN`);\n\t\t\tif (nanPolicy === `ignore`) return w.data;\n\t\t}\n\t\treturn averageFunction(w.seen(value));\n\t};\n\treturn {\n\t\tseen,\n\t\tget data() {\n\t\t\treturn [...w.data];\n\t\t},\n\t\tget average() {\n\t\t\treturn averageFunction(w.data);\n\t\t}\n\t};\n};\nconst smoothingFactor = (timeDelta, cutoff) => {\n\tconst r = PiPi * cutoff * timeDelta;\n\treturn r / (r + 1);\n};\nconst exponentialSmoothing = (smoothingFactor, value, previous) => {\n\treturn smoothingFactor * value + (1 - smoothingFactor) * previous;\n};\n/**\n* Noise filtering\n* \n* Algorithm: https://gery.casiez.net/1euro/\n* \n* Based on [Jaan Tollander de Balsch's implementation](https://jaantollander.com/post/noise-filtering-using-one-euro-filter/)\n* @param cutoffMin Default: 1\n* @param speedCoefficient Default: 0\n* @param cutoffDefault Default: 1\n*/\nconst noiseFilter = (cutoffMin = 1, speedCoefficient = 0, cutoffDefault = 1) => {\n\tlet previousValue = 0;\n\tlet derivativeLast = 0;\n\tlet timestampLast = 0;\n\tconst compute = (value, timestamp) => {\n\t\ttimestamp ??= performance.now();\n\t\tconst timeDelta = timestamp - timestampLast;\n\t\tconst derivative = exponentialSmoothing(smoothingFactor(timeDelta, cutoffDefault), (value - previousValue) / timeDelta, derivativeLast);\n\t\tconst smoothed = exponentialSmoothing(smoothingFactor(timeDelta, cutoffMin + speedCoefficient * Math.abs(derivative)), value, previousValue);\n\t\tpreviousValue = smoothed;\n\t\tderivativeLast = derivative;\n\t\ttimestampLast = timestamp;\n\t\treturn smoothed;\n\t};\n\treturn compute;\n};\n//#endregion\n//#region src/number-array-compute.ts\n/**\n* Calculate the min, max, total, average and count of input array `data`.\n* ```js\n* const { total, min, max, avg, count } = numberArrayCompute([ 1, 2, 3 ]);\n* ```\n* @param data \n* @param opts \n* @returns \n*/\nconst numberArrayCompute = (data, opts = {}) => {\n\tif (data.length === 0) return {\n\t\ttotal: NaN,\n\t\tmin: NaN,\n\t\tmax: NaN,\n\t\tavg: NaN,\n\t\tcount: NaN\n\t};\n\tconst nonNumbers = opts.nonNumbers ?? `throw`;\n\tlet total = 0;\n\tlet min = Number.MAX_SAFE_INTEGER;\n\tlet max = Number.MIN_SAFE_INTEGER;\n\tlet count = 0;\n\tfor (let index = 0; index < data.length; index++) {\n\t\tlet value = data[index];\n\t\tif (typeof value !== `number`) {\n\t\t\tif (nonNumbers === `ignore`) continue;\n\t\t\tif (nonNumbers === `throw`) throw new Error(`Param 'data' contains a non-number at index: ${index.toString()}`);\n\t\t\tif (nonNumbers === `nan`) value = NaN;\n\t\t}\n\t\tif (Number.isNaN(value)) continue;\n\t\tif (value !== void 0) {\n\t\t\tmin = Math.min(min, value);\n\t\t\tmax = Math.max(max, value);\n\t\t\ttotal += value;\n\t\t\tcount++;\n\t\t}\n\t}\n\treturn {\n\t\ttotal,\n\t\tmax,\n\t\tmin,\n\t\tcount,\n\t\tavg: total / count\n\t};\n};\n//#endregion\n//#region src/normalise-minmax.ts\nvar normalise_minmax_exports = /* @__PURE__ */ __exportAll({\n\tarray: () => array$3,\n\tarrayWithContext: () => arrayWithContext$3,\n\tcompute: () => compute$2,\n\tstream: () => stream$1,\n\tstreamWithContext: () => streamWithContext$1\n});\n/**\n* Returns a function which can do min-max normalisation, baking-in the min and max values.\n* ```js\n* // Normalise with min value of 20, max of 100\n* const fn = compute(20, 100);\n* \n* // Use function with input value of 40\n* fn(40);\n* ```\n* \n* @param min Minimum value of range\n* @param max Maximum value of range\n* @param clamp Whether to clamp input value to min/max range. Default: _false_\n* @returns \n*/\nconst compute$2 = (min, max, clamp = false) => {\n\tconst range = max - min;\n\treturn (value) => {\n\t\tif (clamp && value < min) value = min;\n\t\tif (clamp && value > max) value = max;\n\t\treturn (value - min) / range;\n\t};\n};\n/**\n* Normalises an array using the [min-max](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization)) technique.\n* \n* This version returns additional context of the normalisation, alternatively use {@link array}\n*\n* ```js\n* const c = arrayWithContext(someValues);\n* c.values; // Array of normalised values\n* c.original; // Original input array\n* c.min / c.max / c.range\n* ```\n* \n* By default, computes min and max values based on contents of `values`. Clamping is not required\n* for this case, so it's _false_ by default.\n* \n* @param values Values\n* @param options Optionally uses 'minForced' and 'maxForced' properties to scale values instead of actual min/max values of data.\n*/\nconst arrayWithContext$3 = (values, options = {}) => {\n\tif (!Array.isArray(values)) throw new TypeError(`Param 'values' should be an array. Got: ${typeof values}`);\n\tlet clamp = false;\n\tlet minForced = NaN;\n\tlet maxForced = NaN;\n\tif (typeof options.minForced === `undefined` || typeof options.maxForced === `undefined`) {\n\t\tconst c = numberArrayCompute(values);\n\t\tminForced = options.minForced ?? c.min;\n\t\tmaxForced = options.maxForced ?? c.max;\n\t\tclamp = options.clamp ?? false;\n\t} else {\n\t\tclamp = options.clamp ?? true;\n\t\tminForced = options.minForced;\n\t\tmaxForced = options.maxForced;\n\t}\n\tresultThrow(numberTest(minForced), numberTest(maxForced));\n\tconst fn = compute$2(minForced, maxForced, clamp);\n\treturn {\n\t\tvalues: values.map(fn),\n\t\toriginal: values,\n\t\tmin: minForced,\n\t\tmax: maxForced,\n\t\trange: Math.abs(maxForced - minForced)\n\t};\n};\n/**\n* Normalises an array using the [min-max](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization)) technique.\n* By default uses the actual min/max of the array as the normalisation range. \n* \n* [ixfx Guide on Normalising](https://ixfx.fun/cleaning/normal/)\n*\n* Use {@link arrayWithContext} to get back the min/max/range and original values\n* \n* ```js\n* // Yields: [0.5, 0.1, 0.0, 0.9, 1]\n* Normalise.MinMax.array([5,1,0,9,10]);\n* ```\n*\n* `minForced` and/or `maxForced` can\n* be provided to use an arbitrary range.\n* \n* ```js\n* // Forced range 0-100\n* // Yields: [0.05, 0.01, 0.0, 0.09, 0.10]\n* Normalise.MinMax.array([5,1,0,9,10], { minForced: 0, maxForced: 100 });\n* ```\n*\n* Return values are clamped to always be 0-1, inclusive.\n*\n* @param values Values\n* @param options Options to override or min/max values.\n*/\nconst array$3 = (values, options = {}) => {\n\treturn arrayWithContext$3(values, options).values;\n};\n/**\n* [Min-max scaling](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization))\n* \n* A more advanced form of {@link stream}\n* \n* With this version\n* @example\n* ```js\n* const s = Normalise.MinMax.streamWithContext();\n* s.seen(2); // 1 (because 2 is highest seen)\n* s.seen(1); // 0 (because 1 is the lowest so far)\n* s.seen(1.5); // 0.5 (50% of range 1-2)\n* s.seen(0.5); // 0 (because it's the new lowest)\n* ```\n* \n* And the more advanced features\n* ```js\n* s.min / s.max / s.range\n* s.reset();\n* s.reset(10, 100);\n* ```\n* @returns\n*/\nconst streamWithContext$1 = (options = {}) => {\n\tlet min = options.minDefault ?? Number.MAX_SAFE_INTEGER;\n\tlet max = options.maxDefault ?? Number.MIN_SAFE_INTEGER;\n\tresultThrow(numberTest(min), numberTest(max));\n\treturn {\n\t\tseen: (v) => {\n\t\t\tresultThrow(numberTest(v));\n\t\t\tmin = Math.min(min, v);\n\t\t\tmax = Math.max(max, v);\n\t\t\tif (v === min && v === max) return 1;\n\t\t\tconst result = (v - min) / (max - min);\n\t\t\tif (Number.isNaN(result)) throw new Error(`Would return NaN. v: ${v} min: ${min} max: ${max}`);\n\t\t\treturn result;\n\t\t},\n\t\treset: (minDefault, maxDefault) => {\n\t\t\tmin = minDefault ?? Number.MAX_SAFE_INTEGER;\n\t\t\tmax = maxDefault ?? Number.MIN_SAFE_INTEGER;\n\t\t},\n\t\tget min() {\n\t\t\treturn min;\n\t\t},\n\t\tget max() {\n\t\t\treturn max;\n\t\t},\n\t\tget range() {\n\t\t\treturn Math.abs(max - min);\n\t\t}\n\t};\n};\n/**\n* Normalises numbers using the [min-max](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization)) technique.\n* \n* Adjusts min/max as new values are processed. Return values will be in the range of 0-1 (inclusive).\n*\n* [ixfx Guide on Normalising](https://ixfx.fun/cleaning/normal/)\n*\n* Use {@link streamWithContext} if you want to be able to check the min/max or reset the normaliser.\n* \n* @example\n* ```js\n* const s = Normalise.MinMax.stream();\n* s(2); // 1 (because 2 is highest seen)\n* s(1); // 0 (because 1 is the lowest so far)\n* s(1.5); // 0.5 (50% of range 1-2)\n* s(0.5); // 0 (because it's the new lowest)\n* ```\n*\n* Since normalisation is being adjusted as new min/max are encountered, it might\n* be that value normalised to 1 at one time is different to what normalises to 1\n* at a later time.\n*\n* If you already know what to expect of the number range, passing in `minDefault`\n* and `maxDefault` primes the normalisation.\n* ```js\n* const s = Normalise.MinMax.stream();\n* s(5); // 1, because it's the highest seen\n*\n* // With priming:\n* const s = Normalise.MinMax.stream({ minDefault:0, maxDefault:10 });\n* s(5); // 0.5, because we're expecting range 0-10\n* ```\n*\n* If a value exceeds the default range, normalisation adjusts.\n* Errors are thrown if min/max defaults are NaN or if one attempts to\n* normalise NaN.\n* \n* @returns\n*/\nconst stream$1 = (options) => streamWithContext$1(options).seen;\n//#endregion\n//#region src/standard-deviation.ts\n/**\n* Calculates the standard deviation of an array of numbers.\n* \n* If you already have the mean value of the array, this can be passed in.\n* Otherwise it will be computed.\n* \n* If `usePopulation` is true, `array` is assumed to be the entire population (same as Excel's STDEV.P function)\n* Otherwise, it's like Excel's STDEV.S function which assumes data represents a sample of entire population.\n* \n* @param array Array of values\n* @param meanValue Mean value if pre-computed, otherwise skip this parameter for it to be computed automatically\n* @param usePopulation If _true_ result is similar to Excel's STDEV.P. Otherwise like STDEV.S\n* @returns \n*/\nconst standardDeviation = (array, usePopulation = false, meanValue) => {\n\tconst meanV = typeof meanValue === `undefined` ? mean(array) : meanValue;\n\treturn Math.sqrt(array.reduce((accumulator, value) => accumulator.concat((value - meanV) ** 2), []).reduce((accumulator, value) => accumulator + value, 0) / (array.length - (usePopulation ? 0 : 1)));\n};\n//#endregion\n//#region src/normalise-zscore.ts\nvar normalise_zscore_exports = /* @__PURE__ */ __exportAll({\n\tarray: () => array$2,\n\tarrayWithContext: () => arrayWithContext$2,\n\tcompute: () => compute$1\n});\n/**\n* Returns a function that computes zscore-based normalisation.\n* \n* ```js\n* // Calculate necessary components\n* const m = mean(data);\n* const s = standardDeviation(data);\n* \n* // Get the function\n* const fn = compute(m, s);\n* \n* // Use it\n* fn(10); // Yields the normalised value\n* ```\n* \n* It can be used to normalise a whole array\n* ```js\n* const normalised = someData.map(fn);\n* ```\n* \n* If you want to calculate for a whole array, use {@link array}.\n* @param mean Mean of data\n* @param standardDeviation Standard deviation of data\n* @returns \n*/\nconst compute$1 = (mean, standardDeviation) => (value) => (value - mean) / standardDeviation;\n/**\n* Returns the an array of normalised values, along with the mean and standard deviation of `array`.\n* If you just want the computed results, use {@link Normalise.ZScore.array}.\n* \n* By default it will compute mean and std.dev based on `array`. If you have these already, they\n* can be passed as options.\n* @param array \n* @returns \n*/\nconst arrayWithContext$2 = (array, options = {}) => {\n\tconst m = options.meanForced ?? mean(array);\n\tconst s = options.standardDeviationForced ?? standardDeviation(array);\n\tconst fn = compute$1(m, s);\n\treturn {\n\t\tmean: m,\n\t\tstandardDeviation: s,\n\t\tvalues: array.map(fn),\n\t\toriginal: array\n\t};\n};\n/**\n* Returns an array of normalised values using the 'z score' algorithm.\n* \n* By default it will compute mean and std.dev based on `array`. If you have these already, they\n* can be passed as options.\n* @param values \n* @param options \n* @returns \n*/\nconst array$2 = (values, options = {}) => arrayWithContext$2(values, options).values;\n//#endregion\n//#region src/normalise-robust.ts\nvar normalise_robust_exports = /* @__PURE__ */ __exportAll({\n\tarray: () => array$1,\n\tarrayWithContext: () => arrayWithContext$1,\n\tcompute: () => compute\n});\n/**\n* Calculates 'robust scaling' of a single value, `x`, based on provided mean and standard deviation.\n* \n* ```js\n* const m = median(someData);\n* const i = interquartileRange(someData);\n* const fn = compute(m, i);\n* \n* // Use normaliser function\n* fn(10);\n* ```\n* If you want to calculate for a whole array, use {@link array}.\n* @param median Median of data\n* @param iqr Interquartile range of data\n* @returns \n*/\nconst compute = (median, iqr) => (value) => (value - median) / iqr;\n/**\n* Returns the an array of normalised values, along with the mean and standard deviation of `array`.\n* If you just want the computed results, use {@link Normalise.Robust.array}.\n* \n* By default it will compute mean and std.dev based on `array`. If you have these already, they\n* can be passed as options.\n* @param array \n* @returns \n*/\nconst arrayWithContext$1 = (array, options = {}) => {\n\tif (!Array.isArray(array)) throw new TypeError(`Param 'array' is expected to be an array. Got: ${typeof array}`);\n\tconst m = options.medianForced ?? median(array);\n\tconst iqr = options.iqrForced ?? interquartileRange(array);\n\tconst fn = compute(m, iqr);\n\treturn {\n\t\tmedian: m,\n\t\tiqr,\n\t\tvalues: array.map(fn),\n\t\toriginal: array\n\t};\n};\n/**\n* Returns an array of normalised values using the 'z score' algorithm.\n* \n* By default it will compute mean and std.dev based on `array`. If you have these already, they\n* can be passed as options.\n* @param values \n* @param options \n* @returns \n*/\nconst array$1 = (values, options = {}) => arrayWithContext$1(values, options).values;\n//#endregion\n//#region src/normalise.ts\nvar normalise_exports = /* @__PURE__ */ __exportAll({\n\tMinMax: () => normalise_minmax_exports,\n\tRobust: () => normalise_robust_exports,\n\tZScore: () => normalise_zscore_exports,\n\tarray: () => array,\n\tarrayWithContext: () => arrayWithContext,\n\tstream: () => stream,\n\tstreamWithContext: () => streamWithContext\n});\n/**\n* Normalises numbers with additional context on the range.\n* \n* For more details, see:\n* * {@link MinMax.streamWithContext}\n* \n* @param strategy \n* @param options \n* @returns \n*/\nconst streamWithContext = (strategy, options = {}) => {\n\tswitch (strategy) {\n\t\tcase `minmax`: return streamWithContext$1(options);\n\t\tdefault: throw new Error(`Param 'strategy' has an unknown value: '${strategy}'. Expected: minmax`);\n\t}\n};\n/**\n* Normalises numbers. Return values will be in the range of 0-1 (inclusive).\n*\n* [ixfx Guide on Normalising](https://ixfx.fun/cleaning/normal/)\n*\n* Use {@link streamWithContext} if you want to be able to check the min/max or reset the normaliser.\n* \n* @example\n* ```js\n* const s = Normalise.stream(`minmax`);\n* s(2); // 1 (because 2 is highest seen)\n* s(1); // 0 (because 1 is the lowest so far)\n* s(1.5); // 0.5 (50% of range 1-2)\n* s(0.5); // 0 (because it's the new lowest)\n* ```\n*\n* For more details, see:\n* * {@link MinMax.stream}\n* @returns\n*/\nconst stream = (strategy = `minmax`, options = {}) => {\n\tswitch (strategy) {\n\t\tcase `minmax`: return stream$1(options);\n\t\tdefault: throw new Error(`Param 'strategy' has an unknown value: '${strategy}'. Expected: minmax`);\n\t}\n};\n/**\n* Normalise an array of values with added context, depending on strategy.\n* \n* Strategies are available: minmax, zscore & robust\n* \n* [ixfx Guide on Normalising](https://ixfx.fun/cleaning/normal/)\n*\n* Use {@link array} to get back the min/max/range and original values\n* \n* ```js\n* const { values, min, max, range } = Normalise.arrayWithContext(`minmax`, [5,1,0,9,10]);\n* // values will be normalised output\n* ```\n* \n* For more details, see:\n* * {@link MinMax.array}\n* * {@link ZScore.array}\n* * {@link Robust.array}\n* @param strategy \n* @param values \n* @param options \n* @returns \n*/\nconst arrayWithContext = (strategy, values, options = {}) => {\n\tswitch (strategy) {\n\t\tcase `minmax`: return arrayWithContext$3(values, options);\n\t\tcase `zscore`: return arrayWithContext$2(values, options);\n\t\tcase `robust`: return arrayWithContext$1(values, options);\n\t\tdefault: throw new Error(`Param 'strategy' has an unknown value: '${strategy}'. Expected: minmax|zscore`);\n\t}\n};\n/**\n* Normalise an array of values.\n* \n* Strategies are available: minmax, zscore & robust\n* \n* [ixfx Guide on Normalising](https://ixfx.fun/cleaning/normal/)\n*\n* Use {@link arrayWithContext} to get back the min/max/range and original values\n* \n* ```js\n* // Yields: [0.5, 0.1, 0.0, 0.9, 1]\n* Normalise.array(`minmax`, [5,1,0,9,10]);\n* ```\n* \n* For more details, see:\n* * {@link MinMax.array} [Wikipedia](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization))\n* * {@link ZScore.array} [Wikipedia](https://en.wikipedia.org/wiki/Feature_scaling#Standardization_(Z-score_Normalization))\n* * {@link Robust.array} [Wikipedia](https://en.wikipedia.org/wiki/Feature_scaling#Robust_Scaling)\n* \n* @param strategy \n* @param values \n* @param options \n* @returns \n*/\nconst array = (strategy, values, options = {}) => arrayWithContext(strategy, values, options).values;\n//#endregion\n//#region src/proportion.ts\n/**\n* Scales a percentage-scale number, ie: `v * t`.\n* \n* The utility of this function is that it sanity-checks that\n* both parameters are in the 0..1 scale.\n* \n* Parameters can also be a function that takes no parameters\n* and returns a number. It will be invoked when `proportion` is called.\n* @param v Value\n* @param t Scale amount\n* @returns Scaled value\n*/\nconst proportion = (v, t) => {\n\tif (typeof v === `function`) v = v();\n\tif (typeof t === `function`) t = t();\n\tresultThrow(numberTest(v, `percentage`, `v`), numberTest(t, `percentage`, `t`));\n\treturn v * t;\n};\n//#endregion\n//#region src/quantise.ts\n/**\n* Rounds `v` by `every`. Middle values are rounded up by default.\n*\n* ```js\n* quantiseEvery(11, 10); // 10\n* quantiseEvery(25, 10); // 30\n* quantiseEvery(0, 10); // 0\n* quantiseEvery(4, 10); // 0\n* quantiseEvery(100, 10); // 100\n* ```\n* \n* Also works with decimals\n* ```js\n* quantiseEvery(1.123, 0.1); // 1.1\n* quantiseEvery(1.21, 0.1); // 1.2\n* ```\n*\n* @param v Value to quantise\n* @param every Number to quantise to\n* @param middleRoundsUp If _true_ (default), the exact middle rounds up to next step.\n* @returns\n*/\nconst quantiseEvery = (v, every, middleRoundsUp = true) => {\n\tconst everyString = every.toString();\n\tconst decimal = everyString.indexOf(`.`);\n\tlet multiplier = 1;\n\tif (decimal >= 0) {\n\t\tmultiplier = 10 * everyString.substring(decimal + 1).length;\n\t\tevery = Math.floor(multiplier * every);\n\t\tv = v * multiplier;\n\t}\n\tresultThrow(numberTest(v, ``, `v`), integerTest(every, ``, `every`));\n\tlet div = v / every;\n\tconst divModule = div % 1;\n\tdiv = Math.floor(div);\n\tif (divModule === .5 && middleRoundsUp || divModule > .5) div++;\n\treturn every * div / multiplier;\n};\n//#endregion\n//#region src/scale.ts\n/**\n* Scales `v` from an input range to an output range (aka `map`)\n*\n* For example, if a sensor's useful range is 100-500, scale it to a percentage:\n*\n* ```js\n*\n* scale(sensorReading, 100, 500, 0, 1);\n* ```\n*\n* `scale` defaults to a percentage-range output, so you can get away with:\n* ```js\n* scale(sensorReading, 100, 500);\n* ```\n*\n* If `v` is outside of the input range, it will likewise be outside of the output range.\n* Use {@link scaleClamped} to clip value to range.\n*\n* If inMin and inMax are equal, outMax will be returned.\n*\n* An easing function can be provided for non-linear scaling. In this case\n* the input value is 'pre scaled' using the function before it is applied to the\n* output range.\n*\n* ```js\n* scale(sensorReading, 100, 500, 0, 1, Easings.gaussian());\n* ```\n* @param v Value to scale\n* @param inMin Input minimum\n* @param inMax Input maximum\n* @param outMin Output minimum. If not specified, 0\n* @param outMax Output maximum. If not specified, 1\n* @param easing Easing function\n* @returns Scaled value\n*/\nconst scale = (v, inMin, inMax, outMin, outMax, easing) => scaler(inMin, inMax, outMin, outMax, easing)(v);\n/**\n* Returns a scaling function\n* @param inMin Input minimum\n* @param inMax Input maximum\n* @param outMin Output minimum. If not specified, 0\n* @param outMax Output maximum. If not specified, 1\n* @param easing Easing function\n* @param clamped If true, value is clamped. Default: false\n* @returns\n*/\nconst scaler = (inMin, inMax, outMin, outMax, easing, clamped) => {\n\tresultThrow(numberTest(inMin, `finite`, `inMin`), numberTest(inMax, `finite`, `inMax`));\n\tconst oMax = outMax ?? 1;\n\tconst oMin = outMin ?? 0;\n\tconst clampFunction = clamped ? clamper(outMin, outMax) : void 0;\n\treturn (v) => {\n\t\tif (inMin === inMax) return oMax;\n\t\tlet a = (v - inMin) / (inMax - inMin);\n\t\tif (easing !== void 0) a = easing(a);\n\t\tconst x = a * (oMax - oMin) + oMin;\n\t\tif (clampFunction) return clampFunction(x);\n\t\treturn x;\n\t};\n};\n/**\n* Returns a 'null' scaler that does nothing - the input value is returned as output.\n* @returns \n*/\nconst scalerNull = () => (v) => v;\n/**\n* As {@link scale}, but result is clamped to be\n* within `outMin` and `outMax`. Useful if you can't be sure\n* that `v` is in 0..1 range.\n*\n* @param value\n* @param inMin\n* @param inMax\n* @param outMin 1 by default\n* @param outMax 0 by default d\n* @param easing\n* @returns\n*/\nconst scaleClamped = (value, inMin, inMax, outMin, outMax, easing) => {\n\tif (typeof outMax === `undefined`) outMax = 1;\n\tif (typeof outMin === `undefined`) outMin = 0;\n\tif (inMin === inMax) return outMax;\n\treturn clamp(scale(value, inMin, inMax, outMin, outMax, easing), outMin, outMax);\n};\n/**\n* Scales an input percentage to a new percentage range.\n*\n* If you have an input percentage (0-1), `scalePercentageOutput` maps it to an\n* _output_ percentage of `outMin`-`outMax`.\n*\n* ```js\n* // Scales 50% to a range of 0-10%\n* scalePercentages(0.5, 0, 0.10); // 0.05 - 5%\n* ```\n*\n* An error is thrown if any parameter is outside of percentage range. This added\n* safety is useful for catching bugs. Otherwise, you could just as well call\n* `scale(percentage, 0, 1, outMin, outMax)`.\n*\n* If you want to scale some input range to percentage output range, just use `scale`:\n* ```js\n* // Yields 0.5\n* scale(2.5, 0, 5);\n* ```\n* @param percentage Input value, within percentage range\n* @param outMin Output minimum, between 0-1\n* @param outMax Output maximum, between 0-1\n* @returns Scaled value between outMin-outMax.\n*/\nconst scalePercentages = (percentage, outMin, outMax = 1) => {\n\tresultThrow(numberTest(percentage, `percentage`, `v`), numberTest(outMin, `percentage`, `outMin`), numberTest(outMax, `percentage`, `outMax`));\n\treturn scale(percentage, 0, 1, outMin, outMax);\n};\n/**\n* Scales an input percentage value to an output range\n* If you have an input percentage (0-1), `scalePercent` maps it to an output range of `outMin`-`outMax`.\n* ```js\n* scalePercent(0.5, 10, 20); // 15\n* ```\n*\n* @see {@link scalerPercent} Returns a function\n* @param v Value to scale\n* @param outMin Minimum for output\n* @param outMax Maximum for output\n* @returns\n*/\nconst scalePercent = (v, outMin, outMax) => scalerPercent(outMin, outMax)(v);\n/**\n* Returns a function that scales an input percentage value to an output range\n* @see {@link scalePercent} Calculates value\n* @param outMin\n* @param outMax\n* @returns Function that takes a single argument\n*/\nconst scalerPercent = (outMin, outMax) => {\n\treturn (v) => {\n\t\tresultThrow(numberTest(v, `percentage`, `v`));\n\t\treturn scale(v, 0, 1, outMin, outMax);\n\t};\n};\n/**\n* Returns a two-way scaler\n* ```js\n* // Input range 0..100, output range 0..1\n* const s = scalerTwoWay(0,100,0,1);\n* \n* // Scale from input to output\n* s.out(50); // 0.5\n* \n* // Scale from output range to input\n* s.in(1); // 100\n* ```\n* @param inMin \n* @param inMax \n* @param outMin \n* @param outMax \n* @returns \n*/\nconst scalerTwoWay = (inMin, inMax, outMin = 0, outMax = 1, clamped = false, easing) => {\n\treturn {\n\t\tout: scaler(inMin, inMax, outMin, outMax, easing, clamped),\n\t\tin: scaler(outMin, outMax, inMin, inMax, easing, clamped)\n\t};\n};\n//#endregion\n//#region src/range.ts\n/**\n* Computes min/max based on a new value and previous range.\n* Returns existing object reference if value is within existing range.\n* \n* If `value` is not a number, by default it will be ignored. Use the 'nonNumberHandling' param to set it\n* to throw an error instead if you want to catch that\n* @param value Value to compare against range\n* @param previous Previous range\n* @param nonNumberHandling 'skip' (default), non numbers are ignored; 'error' an error is thrown\n* @returns \n*/\nfunction rangeMergeValue(value, previous, nonNumberHandling = `skip`) {\n\tif (typeof value === `number`) {\n\t\tif (Number.isNaN(value) || !Number.isFinite(value)) {\n\t\t\tif (nonNumberHandling === `error`) throw new TypeError(`Param 'value' is NaN or infinite, and nonNumberHandling is set to 'error'`);\n\t\t\treturn previous;\n\t\t}\n\t\tif (value >= previous.min && value <= previous.max) return previous;\n\t\treturn {\n\t\t\tmin: Math.min(value, previous.min),\n\t\t\tmax: Math.max(value, previous.max)\n\t\t};\n\t} else if (nonNumberHandling === `error`) throw new TypeError(`Param 'value' is not a number (type: '${typeof value}') and nonNumberHandling is set to 'error'`);\n\treturn previous;\n}\n/**\n* Returns a function that scales values in a range, by default on 0..1 scale.\n* ```js\n* const range = { min: 10, max: 20 }\n* const s = rangeScaler(range);\n* s(15); // 0.5\n* ```\n* @param range Range to scale on\n* @param outMax Output range max. Default: 1\n* @param outMin Output range min. Default: 0\n* @param easing Easing function: Default: none\n* @param clamped Whether input values should be clamped if they exceed range. Default: true\n* @returns \n*/\nfunction rangeScaler(range, outMax = 1, outMin = 0, easing, clamped = true) {\n\treturn scaler(range.min, range.max, outMin, outMax, easing, clamped);\n}\n/**\n* Expands a range to encompass a new range.\n* Returns `existingRange` if `newRange` is within it.\n* @param newRange \n* @param existingRange \n* @returns \n*/\nfunction rangeMergeRange(newRange, existingRange) {\n\tif (newRange.max <= existingRange.max && newRange.min >= existingRange.min) return existingRange;\n\treturn {\n\t\tmin: Math.min(newRange.min, existingRange.min),\n\t\tmax: Math.max(newRange.max, existingRange.max)\n\t};\n}\n/**\n* Returns an empty range:\n* ```js\n* { \n* min: Number.MAX_SAFE_INTEGER, \n* max: Number.MIN_SAFE_INTEGER \n* }\n* ```\n* @returns \n*/\nconst rangeInit = () => ({\n\tmin: Number.MAX_SAFE_INTEGER,\n\tmax: Number.MIN_SAFE_INTEGER\n});\n/**\n* Returns _true_ if ranges `a` and `b` have identical min/max values.\n* Returns _false_ if not, or if either/both values are _undefined_\n* @param a \n* @param b \n* @returns \n*/\nconst rangeIsEqual = (a, b) => {\n\tif (typeof a === `undefined`) return false;\n\tif (typeof b === `undefined`) return false;\n\treturn a.max === b.max && a.min === b.min;\n};\n/**\n* Returns _true_ if range 'a' is within or same as range 'b'.\n* Returns _false_ if not or if either/both ranges are _undefined_\n* \n* ```js\n* rangeIsWithin({ min: 5, max: 10 }, { min: 0, max: 10 }); // true\n* rangeIsWithin({ min: 5, max: 10 }, { min: 6, max: 20 }); // false\n* ```\n* \n* By default the matching is inclusive, in that `a` could share a min/max with `b`.\n* If you want to check whether `a` is strictly within `b`, with a higher min and lower max, set `exclusive` to _true_.\n* \n* ```js\n* rangeIsWithin({ min: 5, max: 10 }, { min: 0, max: 10 }, true); // false\n* rangeIsWithin({ min: 5, max: 9 }, { min: 0, max: 10 }, true); // true\n* ```\n* \n* If either `a` or `b` is _undefined_, the function returns _false_.\n* @param a Range\n* @param b Parent\n* @param exclusive If _true_, \n* @returns \n*/\nconst rangeIsWithin = (a, b, exclusive = false) => {\n\tif (typeof a === `undefined`) return false;\n\tif (typeof b === `undefined`) return false;\n\tif (exclusive) return a.min > b.min && a.max < b.max;\n\treturn a.min >= b.min && a.max <= b.max;\n};\n/**\n* Keeps track of min/max values.\n* \n* ```js\n* const s = rangeStream();\n* s.seen(10); // { min: 10, max: 10 }\n* s.seen(5); // { min: 5, max: 10 }\n* ```\n* \n* When calling `seen()`, non-numbers, or non-finite numbers are silently ignored.\n* \n* ```js\n* s.reset(); // Reset\n* s.min/s.max; // Current min/max\n* s.range; // Current { min, max }\n* ```\n* @param initWith \n* @returns \n*/\nconst rangeStream = (initWith = rangeInit()) => {\n\tlet { min, max } = initWith;\n\tconst seen = (v) => {\n\t\tif (typeof v === `number`) {\n\t\t\tif (!Number.isNaN(v) && Number.isFinite(v)) {\n\t\t\t\tmin = Math.min(min, v);\n\t\t\t\tmax = Math.max(max, v);\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tmin,\n\t\t\tmax\n\t\t};\n\t};\n\tconst reset = () => {\n\t\tmin = Number.MAX_SAFE_INTEGER;\n\t\tmax = Number.MIN_SAFE_INTEGER;\n\t\treturn {\n\t\t\tmin,\n\t\t\tmax\n\t\t};\n\t};\n\treturn {\n\t\tseen,\n\t\treset,\n\t\tget range() {\n\t\t\treturn {\n\t\t\t\tmin,\n\t\t\t\tmax\n\t\t\t};\n\t\t},\n\t\tget min() {\n\t\t\treturn min;\n\t\t},\n\t\tget max() {\n\t\t\treturn max;\n\t\t}\n\t};\n};\n/**\n* Iterates over `values` finding the min/max.\n* By default non-numbers, as well as NaN and infinite values are skipped.\n* @param values \n* @param nonNumberHandling \n* @returns \n*/\nfunction rangeCompute(values, nonNumberHandling = `skip`) {\n\tlet min = Number.MAX_SAFE_INTEGER;\n\tlet max = Number.MIN_SAFE_INTEGER;\n\tlet position = 0;\n\tfor (const v of values) {\n\t\tif (typeof v === `number`) {\n\t\t\tif (Number.isNaN(v) || !Number.isFinite(v)) {\n\t\t\t\tif (nonNumberHandling === `error`) throw new Error(`Value NaN or infinite at position: ${position}`);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else {\n\t\t\tif (nonNumberHandling === `error`) throw new Error(`Contains non number value. Type: '${typeof v}' Position: ${position}`);\n\t\t\tcontinue;\n\t\t}\n\t\tif (v < min) min = v;\n\t\tif (v > max) max = v;\n\t\tposition++;\n\t}\n\treturn {\n\t\tmin,\n\t\tmax\n\t};\n}\n//#endregion\n//#region src/softmax.ts\n/**\n* Via: https://gist.github.com/cyphunk/6c255fa05dd30e69f438a930faeb53fe\n* @param logits \n* @returns \n*/\nconst softmax = (logits) => {\n\tconst maxLogit = logits.reduce((a, b) => Math.max(a, b), Number.NEGATIVE_INFINITY);\n\tconst scores = logits.map((l) => Math.exp(l - maxLogit));\n\tconst denom = scores.reduce((a, b) => a + b);\n\treturn scores.map((s) => s / denom);\n};\n//#endregion\n//#region src/track-simple.ts\n/**\n* Track values\n* \n* When not yet used:\n* total: 0\n* count: 0\n* min: MAX_SAFE_INTEGER,\n* max: MIN_SAFE_INTEGER\n* @returns \n*/\nconst trackSimple = () => {\n\tlet count = 0;\n\tlet min = Number.MAX_SAFE_INTEGER;\n\tlet max = Number.MIN_SAFE_INTEGER;\n\tlet total = 0;\n\tconst seen = (v) => {\n\t\tmin = Math.min(v, min);\n\t\tmax = Math.max(v, max);\n\t\ttotal += v;\n\t\tcount++;\n\t};\n\tconst reset = () => {\n\t\tcount = 0;\n\t\tmin = Number.MAX_SAFE_INTEGER;\n\t\tmax = Number.MIN_SAFE_INTEGER;\n\t\ttotal = 0;\n\t};\n\tconst rangeToString = (digits = 2) => {\n\t\treturn `${min.toFixed(2)} - ${max.toFixed(2)}`;\n\t};\n\treturn {\n\t\tseen,\n\t\treset,\n\t\trangeToString,\n\t\tget avg() {\n\t\t\treturn total / count;\n\t\t},\n\t\tget min() {\n\t\t\treturn min;\n\t\t},\n\t\tget max() {\n\t\t\treturn max;\n\t\t},\n\t\tget total() {\n\t\t\treturn total;\n\t\t},\n\t\tget count() {\n\t\t\treturn count;\n\t\t}\n\t};\n};\n//#endregion\nexport { bipolar_exports as Bipolar, Kalman1dFilter, normalise_exports as Normalise, applyToValues, average, averageWeigher, averageWeighted, clamp, clampIndex, clamper, computeIsOutlier, count, differenceFromFixed, differenceFromLast, dotProduct, filterIterable, flip, getQuantile, interpolate, interpolateAngle, interpolatorStepped, interquartileRange, isApprox, isCloseToAny, isValid, kalman1dFilter, linearSpace, max, maxAbs, maxFast, maxIndex, mean, median, min, minFast, minIndex, movingAverage, movingAverageLight, movingAverageWithContext, noiseFilter, numberArrayCompute, numericPercent, numericRange, numericRangeRaw, proportion, quantiseEvery, rangeCompute, rangeInclusive, rangeInit, rangeIsEqual, rangeIsWithin, rangeMergeRange, rangeMergeValue, rangeScaler, rangeStream, round, scale, scaleClamped, scalePercent, scalePercentages, scaler, scalerNull, scalerPercent, scalerTwoWay, softmax, standardDeviation, thresholdAtLeast, total, totalFast, trackSimple, validNumbers, weight, wrap, wrapInteger, wrapRange };\n"],"x_google_ignoreList":[0,1,2],"mappings":"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"}
|