superjs-core 0.4.4 → 0.6.0
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/CHANGELOG.md +26 -0
- package/CONTRIBUTING.md +1 -1
- package/PUBLISH.md +2 -2
- package/README.md +9 -5
- package/ROADMAP.md +1 -1
- package/SUMMARY.md +6 -2
- package/dist/async/index.d.ts +106 -1
- package/dist/async/index.js +128 -1
- package/dist/async/index.js.map +1 -1
- package/dist/collection/index.d.ts +135 -1
- package/dist/collection/index.js +145 -1
- package/dist/collection/index.js.map +1 -1
- package/dist/color/index.d.ts +79 -1
- package/dist/color/index.js +101 -0
- package/dist/color/index.js.map +1 -1
- package/dist/date/index.d.ts +316 -1
- package/dist/date/index.js +339 -1
- package/dist/date/index.js.map +1 -1
- package/dist/dep-exray/cli.js +95 -43
- package/dist/dep-exray/cli.js.map +1 -1
- package/dist/dep-exray/index.js +48 -34
- package/dist/dep-exray/index.js.map +1 -1
- package/dist/dep-exray/reporter/index.js +48 -34
- package/dist/dep-exray/reporter/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +50 -36
- package/dist/index.js.map +1 -1
- package/dist/isNoRekening-CHSpgD4P.d.ts +167 -0
- package/dist/math/index.d.ts +188 -1
- package/dist/math/index.js +199 -1
- package/dist/math/index.js.map +1 -1
- package/dist/string/index.d.ts +71 -1
- package/dist/string/index.js +94 -0
- package/dist/string/index.js.map +1 -1
- package/dist/validation/index.d.ts +22 -150
- package/dist/validation/index.js +27 -0
- package/dist/validation/index.js.map +1 -1
- package/package.json +2 -6
package/dist/math/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/math/index.ts"],"sourcesContent":["/**\r\n * Error thrown when attempting to divide by zero.\r\n */\r\nexport class DivisionByZeroError extends Error {\r\n constructor() {\r\n super('Division by zero')\r\n this.name = 'DivisionByZeroError'\r\n }\r\n}\r\n\r\nfunction getPrecision(value: number): number {\r\n if (!isFinite(value)) return 0\r\n const eIndex = String(value).indexOf('e')\r\n if (eIndex > -1) {\r\n const exp = parseInt(String(value).slice(eIndex + 1), 10)\r\n if (exp < 0) return Math.abs(exp)\r\n return 0\r\n }\r\n const str = String(value)\r\n const dot = str.indexOf('.')\r\n return dot === -1 ? 0 : str.length - dot - 1\r\n}\r\n\r\nfunction toPrecisionFactor(a: number, b: number): number {\r\n return Math.pow(10, Math.max(getPrecision(a), getPrecision(b)))\r\n}\r\n\r\n/**\r\n * Safely adds two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The sum.\r\n */\r\nexport function add(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) + Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely subtracts two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The difference.\r\n */\r\nexport function sub(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) - Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely multiplies two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The product.\r\n */\r\nexport function mul(a: number, b: number): number {\r\n const factorA = toPrecisionFactor(a, 1)\r\n const factorB = toPrecisionFactor(1, b)\r\n const result = (Math.round(a * factorA) * Math.round(b * factorB)) / (factorA * factorB)\r\n return result\r\n}\r\n\r\n/**\r\n * Safely divides two numbers.\r\n *\r\n * @param a - The dividend.\r\n * @param b - The divisor.\r\n * @returns The quotient.\r\n * @throws {DivisionByZeroError} If `b` is zero.\r\n */\r\nexport function div(a: number, b: number): number {\r\n if (b === 0) throw new DivisionByZeroError()\r\n const factor = toPrecisionFactor(a, b)\r\n return Math.round(a * factor) / Math.round(b * factor)\r\n}\r\n\r\n/**\r\n * Rounds a number to the given precision.\r\n *\r\n * @param value - The number to round.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The rounded value.\r\n */\r\nexport function round(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n // Use toPrecision to avoid floating-point multiplication errors\r\n // e.g. 1.005 * 100 = 100.49999999999999 without this fix\r\n const shifted = Number((value * factor).toPrecision(15))\r\n return Math.round(shifted) / factor\r\n}\r\n\r\n/**\r\n * Floors a number to the given precision.\r\n *\r\n * @param value - The number to floor.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The floored value.\r\n */\r\nexport function floor(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.floor(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Ceils a number to the given precision.\r\n *\r\n * @param value - The number to ceil.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The ceiled value.\r\n */\r\nexport function ceil(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.ceil(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Checks if two numbers are approximately equal within a tolerance.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @param tolerance - Maximum difference (default `Number.EPSILON`).\r\n * @returns Whether the numbers are approximately equal.\r\n */\r\nexport function approxEqual(a: number, b: number, tolerance: number = Number.EPSILON): boolean {\r\n return Math.abs(a - b) <= tolerance\r\n}\r\n\r\n/**\r\n * Clamps a value within the inclusive range [min, max].\r\n *\r\n * @param value - The value to clamp.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns The clamped value.\r\n * @throws {RangeError} If `min` exceeds `max`.\r\n */\r\nexport function clamp(value: number, min: number, max: number): number {\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.min(Math.max(value, min), max)\r\n}\r\n\r\n/**\r\n * Computes the sum of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The total sum.\r\n */\r\nexport function sum(values: number[]): number {\r\n let total = 0\r\n for (let i = 0; i < values.length; i++) {\r\n total += values[i]!\r\n }\r\n return total\r\n}\r\n\r\n/**\r\n * Computes the average (mean) of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The average.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function average(values: number[]): number {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute average of an empty array')\r\n }\r\n return sum(values) / values.length\r\n}\r\n\r\n/**\r\n * Generates a random integer between `min` and `max` (inclusive).\r\n *\r\n * @param min - The minimum integer.\r\n * @param max - The maximum integer.\r\n * @returns A random integer.\r\n * @throws {RangeError} If arguments are not integers or `min > max`.\r\n */\r\nexport function randomInt(min: number, max: number): number {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.floor(Math.random() * (max - min + 1)) + min\r\n}\r\n\r\n/**\r\n * Checks if a number is within the inclusive range [min, max].\r\n *\r\n * @param value - The number to check.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns Whether the value is in range.\r\n */\r\nexport function inRange(value: number, min: number, max: number): boolean {\r\n return value >= min && value <= max\r\n}\r\n\r\n// ─── Statistics ─────────────────────────────────────────\r\n\r\n/**\r\n * Computes the median of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The median value.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function median(values: number[]): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute median of an empty array')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const mid = Math.floor(sorted.length / 2)\r\n return sorted.length % 2 === 0 ? (sorted[mid - 1]! + sorted[mid]!) / 2 : sorted[mid]!\r\n}\r\n\r\n/**\r\n * Computes the population standard deviation.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function stddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / values.length)\r\n}\r\n\r\n/**\r\n * Computes the sample standard deviation (Bessel's correction).\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The sample standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function sampleStddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for sample stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / (values.length - 1))\r\n}\r\n\r\n/**\r\n * Computes the percentile value (0-100) using linear interpolation.\r\n *\r\n * @param values - Array of numbers.\r\n * @param p - Percentile (0-100).\r\n * @returns The percentile value.\r\n * @throws {RangeError} If p is outside [0, 100] or array is empty.\r\n */\r\nexport function percentile(values: number[], p: number): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute percentile of empty array')\r\n if (p < 0 || p > 100) throw new RangeError('Percentile must be between 0 and 100')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const rank = (p / 100) * (sorted.length - 1)\r\n const lower = Math.floor(rank)\r\n const upper = Math.ceil(rank)\r\n if (lower === upper) return sorted[lower]!\r\n return sorted[lower]! + (sorted[upper]! - sorted[lower]!) * (rank - lower)\r\n}\r\n\r\n/**\r\n * Computes the Pearson correlation coefficient between two arrays.\r\n *\r\n * @param x - First array.\r\n * @param y - Second array.\r\n * @returns The correlation coefficient (-1 to 1).\r\n * @throws {RangeError} If arrays have different lengths or fewer than 2 pairs.\r\n */\r\nexport function correlation(x: number[], y: number[]): number {\r\n if (x.length !== y.length) throw new RangeError('Arrays must have the same length')\r\n if (x.length < 2) throw new RangeError('Need at least 2 pairs for correlation')\r\n const n = x.length\r\n const meanX = sum(x) / n\r\n const meanY = sum(y) / n\r\n let num = 0\r\n let denX = 0\r\n let denY = 0\r\n for (let i = 0; i < n; i++) {\r\n const dx = x[i]! - meanX\r\n const dy = y[i]! - meanY\r\n num += dx * dy\r\n denX += dx * dx\r\n denY += dy * dy\r\n }\r\n if (denX === 0 || denY === 0) return 0\r\n return num / Math.sqrt(denX * denY)\r\n}\r\n\r\n/**\r\n * Formats a number as a currency string with locale support.\r\n *\r\n * @example formatCurrency(1500000) // \"Rp1.500.000\"\r\n * @example formatCurrency(1500000, { notation: 'compact' }) // \"Rp1,5 jt\"\r\n * @example formatCurrency(99.99, { locale: 'en-US', currency: 'USD' }) // \"$99.99\"\r\n *\r\n * @param value - The number to format.\r\n * @param options - Formatting options.\r\n * @returns The formatted currency string.\r\n */\r\nexport function formatCurrency(\r\n value: number,\r\n options?: { locale?: string; currency?: string; notation?: 'standard' | 'compact' },\r\n): string {\r\n const locale = options?.locale ?? 'id-ID'\r\n const currency = options?.currency ?? 'IDR'\r\n const notation = options?.notation ?? 'standard'\r\n\r\n try {\r\n return new Intl.NumberFormat(locale, {\r\n style: 'currency',\r\n currency,\r\n notation,\r\n minimumFractionDigits: 0,\r\n maximumFractionDigits: 2,\r\n }).format(value)\r\n } catch {\r\n return `${currency} ${value.toLocaleString(locale)}`\r\n }\r\n}\r\n"],"mappings":";AAGO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,cAAc;AACZ,UAAM,kBAAkB;AACxB,SAAK,OAAO;AAAA,EACd;AACF;AAEA,SAAS,aAAa,OAAuB;AAC3C,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,QAAM,SAAS,OAAO,KAAK,EAAE,QAAQ,GAAG;AACxC,MAAI,SAAS,IAAI;AACf,UAAM,MAAM,SAAS,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC,GAAG,EAAE;AACxD,QAAI,MAAM,EAAG,QAAO,KAAK,IAAI,GAAG;AAChC,WAAO;AAAA,EACT;AACA,QAAM,MAAM,OAAO,KAAK;AACxB,QAAM,MAAM,IAAI,QAAQ,GAAG;AAC3B,SAAO,QAAQ,KAAK,IAAI,IAAI,SAAS,MAAM;AAC7C;AAEA,SAAS,kBAAkB,GAAW,GAAmB;AACvD,SAAO,KAAK,IAAI,IAAI,KAAK,IAAI,aAAa,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC;AAChE;AASO,SAAS,IAAI,GAAW,GAAmB;AAChD,QAAM,SAAS,kBAAkB,GAAG,CAAC;AACrC,UAAQ,KAAK,MAAM,IAAI,MAAM,IAAI,KAAK,MAAM,IAAI,MAAM,KAAK;AAC7D;AASO,SAAS,IAAI,GAAW,GAAmB;AAChD,QAAM,SAAS,kBAAkB,GAAG,CAAC;AACrC,UAAQ,KAAK,MAAM,IAAI,MAAM,IAAI,KAAK,MAAM,IAAI,MAAM,KAAK;AAC7D;AASO,SAAS,IAAI,GAAW,GAAmB;AAChD,QAAM,UAAU,kBAAkB,GAAG,CAAC;AACtC,QAAM,UAAU,kBAAkB,GAAG,CAAC;AACtC,QAAM,SAAU,KAAK,MAAM,IAAI,OAAO,IAAI,KAAK,MAAM,IAAI,OAAO,KAAM,UAAU;AAChF,SAAO;AACT;AAUO,SAAS,IAAI,GAAW,GAAmB;AAChD,MAAI,MAAM,EAAG,OAAM,IAAI,oBAAoB;AAC3C,QAAM,SAAS,kBAAkB,GAAG,CAAC;AACrC,SAAO,KAAK,MAAM,IAAI,MAAM,IAAI,KAAK,MAAM,IAAI,MAAM;AACvD;AASO,SAAS,MAAM,OAAe,YAAoB,GAAW;AAClE,QAAM,SAAS,KAAK,IAAI,IAAI,SAAS;AAGrC,QAAM,UAAU,QAAQ,QAAQ,QAAQ,YAAY,EAAE,CAAC;AACvD,SAAO,KAAK,MAAM,OAAO,IAAI;AAC/B;AASO,SAAS,MAAM,OAAe,YAAoB,GAAW;AAClE,QAAM,SAAS,KAAK,IAAI,IAAI,SAAS;AACrC,SAAO,KAAK,MAAM,QAAQ,MAAM,IAAI;AACtC;AASO,SAAS,KAAK,OAAe,YAAoB,GAAW;AACjE,QAAM,SAAS,KAAK,IAAI,IAAI,SAAS;AACrC,SAAO,KAAK,KAAK,QAAQ,MAAM,IAAI;AACrC;AAUO,SAAS,YAAY,GAAW,GAAW,YAAoB,OAAO,SAAkB;AAC7F,SAAO,KAAK,IAAI,IAAI,CAAC,KAAK;AAC5B;AAWO,SAAS,MAAM,OAAe,KAAa,KAAqB;AACrE,MAAI,MAAM,KAAK;AACb,UAAM,IAAI,WAAW,2CAA2C;AAAA,EAClE;AACA,SAAO,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AAC3C;AAQO,SAAS,IAAI,QAA0B;AAC5C,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,aAAS,OAAO,CAAC;AAAA,EACnB;AACA,SAAO;AACT;AASO,SAAS,QAAQ,QAA0B;AAChD,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI,WAAW,0CAA0C;AAAA,EACjE;AACA,SAAO,IAAI,MAAM,IAAI,OAAO;AAC9B;AAUO,SAAS,UAAU,KAAa,KAAqB;AAC1D,MAAI,CAAC,OAAO,UAAU,GAAG,KAAK,CAAC,OAAO,UAAU,GAAG,GAAG;AACpD,UAAM,IAAI,WAAW,4BAA4B;AAAA,EACnD;AACA,MAAI,MAAM,KAAK;AACb,UAAM,IAAI,WAAW,2CAA2C;AAAA,EAClE;AACA,SAAO,KAAK,MAAM,KAAK,OAAO,KAAK,MAAM,MAAM,EAAE,IAAI;AACvD;AAUO,SAAS,QAAQ,OAAe,KAAa,KAAsB;AACxE,SAAO,SAAS,OAAO,SAAS;AAClC;AAWO,SAAS,OAAO,QAA0B;AAC/C,MAAI,OAAO,WAAW,EAAG,OAAM,IAAI,WAAW,yCAAyC;AACvF,QAAM,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC/C,QAAM,MAAM,KAAK,MAAM,OAAO,SAAS,CAAC;AACxC,SAAO,OAAO,SAAS,MAAM,KAAK,OAAO,MAAM,CAAC,IAAK,OAAO,GAAG,KAAM,IAAI,OAAO,GAAG;AACrF;AASO,SAAS,OAAO,QAA0B;AAC/C,MAAI,OAAO,SAAS,EAAG,OAAM,IAAI,WAAW,mCAAmC;AAC/E,QAAM,OAAO,IAAI,MAAM,IAAI,OAAO;AAClC,QAAM,UAAU,OAAO,IAAI,CAAC,OAAO,IAAI,SAAS,CAAC;AACjD,SAAO,KAAK,KAAK,QAAQ,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,OAAO,MAAM;AACrE;AASO,SAAS,aAAa,QAA0B;AACrD,MAAI,OAAO,SAAS,EAAG,OAAM,IAAI,WAAW,0CAA0C;AACtF,QAAM,OAAO,IAAI,MAAM,IAAI,OAAO;AAClC,QAAM,UAAU,OAAO,IAAI,CAAC,OAAO,IAAI,SAAS,CAAC;AACjD,SAAO,KAAK,KAAK,QAAQ,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,KAAK,OAAO,SAAS,EAAE;AAC3E;AAUO,SAAS,WAAW,QAAkB,GAAmB;AAC9D,MAAI,OAAO,WAAW,EAAG,OAAM,IAAI,WAAW,0CAA0C;AACxF,MAAI,IAAI,KAAK,IAAI,IAAK,OAAM,IAAI,WAAW,sCAAsC;AACjF,QAAM,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC/C,QAAM,OAAQ,IAAI,OAAQ,OAAO,SAAS;AAC1C,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,QAAM,QAAQ,KAAK,KAAK,IAAI;AAC5B,MAAI,UAAU,MAAO,QAAO,OAAO,KAAK;AACxC,SAAO,OAAO,KAAK,KAAM,OAAO,KAAK,IAAK,OAAO,KAAK,MAAO,OAAO;AACtE;AAUO,SAAS,YAAY,GAAa,GAAqB;AAC5D,MAAI,EAAE,WAAW,EAAE,OAAQ,OAAM,IAAI,WAAW,kCAAkC;AAClF,MAAI,EAAE,SAAS,EAAG,OAAM,IAAI,WAAW,uCAAuC;AAC9E,QAAM,IAAI,EAAE;AACZ,QAAM,QAAQ,IAAI,CAAC,IAAI;AACvB,QAAM,QAAQ,IAAI,CAAC,IAAI;AACvB,MAAI,MAAM;AACV,MAAI,OAAO;AACX,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,KAAK,EAAE,CAAC,IAAK;AACnB,UAAM,KAAK,EAAE,CAAC,IAAK;AACnB,WAAO,KAAK;AACZ,YAAQ,KAAK;AACb,YAAQ,KAAK;AAAA,EACf;AACA,MAAI,SAAS,KAAK,SAAS,EAAG,QAAO;AACrC,SAAO,MAAM,KAAK,KAAK,OAAO,IAAI;AACpC;AAaO,SAAS,eACd,OACA,SACQ;AACR,QAAM,SAAS,SAAS,UAAU;AAClC,QAAM,WAAW,SAAS,YAAY;AACtC,QAAM,WAAW,SAAS,YAAY;AAEtC,MAAI;AACF,WAAO,IAAI,KAAK,aAAa,QAAQ;AAAA,MACnC,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA,uBAAuB;AAAA,MACvB,uBAAuB;AAAA,IACzB,CAAC,EAAE,OAAO,KAAK;AAAA,EACjB,QAAQ;AACN,WAAO,GAAG,QAAQ,IAAI,MAAM,eAAe,MAAM,CAAC;AAAA,EACpD;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/math/index.ts"],"sourcesContent":["/**\r\n * Error thrown when attempting to divide by zero.\r\n */\r\nexport class DivisionByZeroError extends Error {\r\n constructor() {\r\n super('Division by zero')\r\n this.name = 'DivisionByZeroError'\r\n }\r\n}\r\n\r\nfunction getPrecision(value: number): number {\r\n if (!isFinite(value)) return 0\r\n const eIndex = String(value).indexOf('e')\r\n if (eIndex > -1) {\r\n const exp = parseInt(String(value).slice(eIndex + 1), 10)\r\n if (exp < 0) return Math.abs(exp)\r\n return 0\r\n }\r\n const str = String(value)\r\n const dot = str.indexOf('.')\r\n return dot === -1 ? 0 : str.length - dot - 1\r\n}\r\n\r\nfunction toPrecisionFactor(a: number, b: number): number {\r\n return Math.pow(10, Math.max(getPrecision(a), getPrecision(b)))\r\n}\r\n\r\n/**\r\n * Safely adds two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The sum.\r\n */\r\nexport function add(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) + Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely subtracts two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The difference.\r\n */\r\nexport function sub(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) - Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely multiplies two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The product.\r\n */\r\nexport function mul(a: number, b: number): number {\r\n const factorA = toPrecisionFactor(a, 1)\r\n const factorB = toPrecisionFactor(1, b)\r\n const result = (Math.round(a * factorA) * Math.round(b * factorB)) / (factorA * factorB)\r\n return result\r\n}\r\n\r\n/**\r\n * Safely divides two numbers.\r\n *\r\n * @param a - The dividend.\r\n * @param b - The divisor.\r\n * @returns The quotient.\r\n * @throws {DivisionByZeroError} If `b` is zero.\r\n */\r\nexport function div(a: number, b: number): number {\r\n if (b === 0) throw new DivisionByZeroError()\r\n const factor = toPrecisionFactor(a, b)\r\n return Math.round(a * factor) / Math.round(b * factor)\r\n}\r\n\r\n/**\r\n * Rounds a number to the given precision.\r\n *\r\n * @param value - The number to round.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The rounded value.\r\n */\r\nexport function round(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n // Use toPrecision to avoid floating-point multiplication errors\r\n // e.g. 1.005 * 100 = 100.49999999999999 without this fix\r\n const shifted = Number((value * factor).toPrecision(15))\r\n return Math.round(shifted) / factor\r\n}\r\n\r\n/**\r\n * Floors a number to the given precision.\r\n *\r\n * @param value - The number to floor.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The floored value.\r\n */\r\nexport function floor(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.floor(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Ceils a number to the given precision.\r\n *\r\n * @param value - The number to ceil.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The ceiled value.\r\n */\r\nexport function ceil(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.ceil(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Checks if two numbers are approximately equal within a tolerance.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @param tolerance - Maximum difference (default `Number.EPSILON`).\r\n * @returns Whether the numbers are approximately equal.\r\n */\r\nexport function approxEqual(a: number, b: number, tolerance: number = Number.EPSILON): boolean {\r\n return Math.abs(a - b) <= tolerance\r\n}\r\n\r\n/**\r\n * Clamps a value within the inclusive range [min, max].\r\n *\r\n * @param value - The value to clamp.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns The clamped value.\r\n * @throws {RangeError} If `min` exceeds `max`.\r\n */\r\nexport function clamp(value: number, min: number, max: number): number {\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.min(Math.max(value, min), max)\r\n}\r\n\r\n/**\r\n * Computes the sum of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The total sum.\r\n */\r\nexport function sum(values: number[]): number {\r\n let total = 0\r\n for (let i = 0; i < values.length; i++) {\r\n total += values[i]!\r\n }\r\n return total\r\n}\r\n\r\n/**\r\n * Computes the average (mean) of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The average.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function average(values: number[]): number {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute average of an empty array')\r\n }\r\n return sum(values) / values.length\r\n}\r\n\r\n/**\r\n * Generates a random integer between `min` and `max` (inclusive).\r\n *\r\n * @param min - The minimum integer.\r\n * @param max - The maximum integer.\r\n * @returns A random integer.\r\n * @throws {RangeError} If arguments are not integers or `min > max`.\r\n */\r\nexport function randomInt(min: number, max: number): number {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.floor(Math.random() * (max - min + 1)) + min\r\n}\r\n\r\n/**\r\n * Checks if a number is within the inclusive range [min, max].\r\n *\r\n * @param value - The number to check.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns Whether the value is in range.\r\n */\r\nexport function inRange(value: number, min: number, max: number): boolean {\r\n return value >= min && value <= max\r\n}\r\n\r\n// ─── Statistics ─────────────────────────────────────────\r\n\r\n/**\r\n * Computes the median of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The median value.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function median(values: number[]): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute median of an empty array')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const mid = Math.floor(sorted.length / 2)\r\n return sorted.length % 2 === 0 ? (sorted[mid - 1]! + sorted[mid]!) / 2 : sorted[mid]!\r\n}\r\n\r\n/**\r\n * Computes the population standard deviation.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function stddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / values.length)\r\n}\r\n\r\n/**\r\n * Computes the sample standard deviation (Bessel's correction).\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The sample standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function sampleStddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for sample stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / (values.length - 1))\r\n}\r\n\r\n/**\r\n * Computes the percentile value (0-100) using linear interpolation.\r\n *\r\n * @param values - Array of numbers.\r\n * @param p - Percentile (0-100).\r\n * @returns The percentile value.\r\n * @throws {RangeError} If p is outside [0, 100] or array is empty.\r\n */\r\nexport function percentile(values: number[], p: number): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute percentile of empty array')\r\n if (p < 0 || p > 100) throw new RangeError('Percentile must be between 0 and 100')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const rank = (p / 100) * (sorted.length - 1)\r\n const lower = Math.floor(rank)\r\n const upper = Math.ceil(rank)\r\n if (lower === upper) return sorted[lower]!\r\n return sorted[lower]! + (sorted[upper]! - sorted[lower]!) * (rank - lower)\r\n}\r\n\r\n/**\r\n * Computes the Pearson correlation coefficient between two arrays.\r\n *\r\n * @param x - First array.\r\n * @param y - Second array.\r\n * @returns The correlation coefficient (-1 to 1).\r\n * @throws {RangeError} If arrays have different lengths or fewer than 2 pairs.\r\n */\r\nexport function correlation(x: number[], y: number[]): number {\r\n if (x.length !== y.length) throw new RangeError('Arrays must have the same length')\r\n if (x.length < 2) throw new RangeError('Need at least 2 pairs for correlation')\r\n const n = x.length\r\n const meanX = sum(x) / n\r\n const meanY = sum(y) / n\r\n let num = 0\r\n let denX = 0\r\n let denY = 0\r\n for (let i = 0; i < n; i++) {\r\n const dx = x[i]! - meanX\r\n const dy = y[i]! - meanY\r\n num += dx * dy\r\n denX += dx * dx\r\n denY += dy * dy\r\n }\r\n if (denX === 0 || denY === 0) return 0\r\n return num / Math.sqrt(denX * denY)\r\n}\r\n\r\n/**\r\n * Formats a number as a currency string with locale support.\r\n *\r\n * @example formatCurrency(1500000) // \"Rp1.500.000\"\r\n * @example formatCurrency(1500000, { notation: 'compact' }) // \"Rp1,5 jt\"\r\n * @example formatCurrency(99.99, { locale: 'en-US', currency: 'USD' }) // \"$99.99\"\r\n *\r\n * @param value - The number to format.\r\n * @param options - Formatting options.\r\n * @returns The formatted currency string.\r\n */\r\nexport function formatCurrency(\r\n value: number,\r\n options?: { locale?: string; currency?: string; notation?: 'standard' | 'compact' },\r\n): string {\r\n const locale = options?.locale ?? 'id-ID'\r\n const currency = options?.currency ?? 'IDR'\r\n const notation = options?.notation ?? 'standard'\r\n\r\n try {\r\n return new Intl.NumberFormat(locale, {\r\n style: 'currency',\r\n currency,\r\n notation,\r\n minimumFractionDigits: 0,\r\n maximumFractionDigits: 2,\r\n }).format(value)\r\n } catch {\r\n return `${currency} ${value.toLocaleString(locale)}`\r\n }\r\n}\r\n\r\n// ─── Parity ─────────────────────────────────────────────\r\n\r\n/**\r\n * Checks if a number is even.\r\n *\r\n * @example isEven(4) // true\r\n * @example isEven(7) // false\r\n *\r\n * @param n - The number to check.\r\n * @returns Whether the number is even.\r\n */\r\nexport function isEven(n: number): boolean {\r\n return n % 2 === 0\r\n}\r\n\r\n/**\r\n * Checks if a number is odd.\r\n *\r\n * @example isOdd(3) // true\r\n * @example isOdd(8) // false\r\n *\r\n * @param n - The number to check.\r\n * @returns Whether the number is odd.\r\n */\r\nexport function isOdd(n: number): boolean {\r\n return n % 2 !== 0\r\n}\r\n\r\n// ─── Number Theory ──────────────────────────────────────\r\n\r\n/**\r\n * Computes the greatest common divisor (GCD) using the Euclidean algorithm.\r\n *\r\n * @example gcd(12, 8) // 4\r\n * @example gcd(17, 5) // 1\r\n *\r\n * @param a - First integer.\r\n * @param b - Second integer.\r\n * @returns The GCD.\r\n */\r\nexport function gcd(a: number, b: number): number {\r\n if (!Number.isInteger(a) || !Number.isInteger(b)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n a = Math.abs(a)\r\n b = Math.abs(b)\r\n while (b !== 0) {\r\n const t = b\r\n b = a % b\r\n a = t\r\n }\r\n return a\r\n}\r\n\r\n/**\r\n * Computes the least common multiple (LCM).\r\n *\r\n * @example lcm(4, 6) // 12\r\n * @example lcm(7, 5) // 35\r\n *\r\n * @param a - First integer.\r\n * @param b - Second integer.\r\n * @returns The LCM.\r\n * @throws {RangeError} If either argument is zero.\r\n */\r\nexport function lcm(a: number, b: number): number {\r\n if (!Number.isInteger(a) || !Number.isInteger(b)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (a === 0 || b === 0) {\r\n throw new RangeError('Arguments must be non-zero')\r\n }\r\n return Math.abs(a * b) / gcd(a, b)\r\n}\r\n\r\n/**\r\n * Computes the factorial of a non-negative integer.\r\n *\r\n * @example factorial(5) // 120\r\n * @example factorial(0) // 1\r\n *\r\n * @param n - Non-negative integer.\r\n * @returns The factorial.\r\n * @throws {RangeError} If n is negative or not an integer.\r\n */\r\nexport function factorial(n: number): number {\r\n if (!Number.isInteger(n)) {\r\n throw new RangeError('Argument must be an integer')\r\n }\r\n if (n < 0) {\r\n throw new RangeError('Factorial is not defined for negative numbers')\r\n }\r\n let result = 1\r\n for (let i = 2; i <= n; i++) {\r\n result *= i\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Tests whether a number is prime (optimized trial division).\r\n *\r\n * @example isPrime(7) // true\r\n * @example isPrime(10) // false\r\n *\r\n * @param n - Positive integer.\r\n * @returns Whether the number is prime.\r\n * @throws {RangeError} If n is not an integer or is less than 2.\r\n */\r\nexport function isPrime(n: number): boolean {\r\n if (!Number.isInteger(n)) {\r\n throw new RangeError('Argument must be an integer')\r\n }\r\n if (n < 2) return false\r\n if (n === 2 || n === 3) return true\r\n if (n % 2 === 0 || n % 3 === 0) return false\r\n const limit = Math.sqrt(n)\r\n for (let i = 5; i <= limit; i += 6) {\r\n if (n % i === 0 || n % (i + 2) === 0) return false\r\n }\r\n return true\r\n}\r\n\r\n// ─── Angle Conversion ───────────────────────────────────\r\n\r\n/**\r\n * Converts degrees to radians.\r\n *\r\n * @example toRadians(180) // ~3.14159\r\n *\r\n * @param degrees - Angle in degrees.\r\n * @returns Angle in radians.\r\n */\r\nexport function toRadians(degrees: number): number {\r\n return (degrees * Math.PI) / 180\r\n}\r\n\r\n/**\r\n * Converts radians to degrees.\r\n *\r\n * @example toDegrees(Math.PI) // 180\r\n *\r\n * @param radians - Angle in radians.\r\n * @returns Angle in degrees.\r\n */\r\nexport function toDegrees(radians: number): number {\r\n return (radians * 180) / Math.PI\r\n}\r\n\r\n// ─── Interpolation & Mapping ────────────────────────────\r\n\r\n/**\r\n * Linearly interpolates between `a` and `b` by `t`.\r\n *\r\n * @example lerp(0, 100, 0.5) // 50\r\n *\r\n * @param a - Start value.\r\n * @param b - End value.\r\n * @param t - Interpolation factor (typically 0–1).\r\n * @returns The interpolated value.\r\n */\r\nexport function lerp(a: number, b: number, t: number): number {\r\n return a + (b - a) * t\r\n}\r\n\r\n/**\r\n * Calculates what percentage `value` is of `total`.\r\n *\r\n * @example percentageOf(25, 200) // 12.5\r\n *\r\n * @param value - The part value.\r\n * @param total - The total value.\r\n * @returns The percentage.\r\n * @throws {RangeError} If total is zero.\r\n */\r\nexport function percentageOf(value: number, total: number): number {\r\n if (total === 0) {\r\n throw new RangeError('Total must be non-zero')\r\n }\r\n return (value / total) * 100\r\n}\r\n\r\n/**\r\n * Maps a value from one range to another.\r\n *\r\n * @example mapRange(0.5, 0, 1, 0, 100) // 50\r\n *\r\n * @param value - The value to map.\r\n * @param inMin - Lower bound of the input range.\r\n * @param inMax - Upper bound of the input range.\r\n * @param outMin - Lower bound of the output range.\r\n * @param outMax - Upper bound of the output range.\r\n * @returns The mapped value.\r\n * @throws {RangeError} If the input range is zero.\r\n */\r\nexport function mapRange(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number {\r\n if (inMin === inMax) {\r\n throw new RangeError('Input range must not be zero')\r\n }\r\n return ((value - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin\r\n}\r\n\r\n// ─── Statistics ─────────────────────────────────────────\r\n\r\n/**\r\n * Computes the statistical mode(s) — the most frequently occurring value(s).\r\n *\r\n * @example mode([1, 2, 2, 3]) // [2]\r\n * @example mode([1, 1, 2, 2]) // [1, 2]\r\n *\r\n * @param values - Array of numbers.\r\n * @returns Array of mode value(s).\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function mode(values: number[]): number[] {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute mode of an empty array')\r\n }\r\n const freq = new Map<number, number>()\r\n let maxFreq = 0\r\n for (const v of values) {\r\n const count = (freq.get(v) ?? 0) + 1\r\n freq.set(v, count)\r\n if (count > maxFreq) maxFreq = count\r\n }\r\n const result: number[] = []\r\n for (const [v, count] of freq) {\r\n if (count === maxFreq) result.push(v)\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Generates an array of numbers from `start` to `end` (inclusive),\r\n * incremented by `step`.\r\n *\r\n * @example range(1, 5) // [1, 2, 3, 4, 5]\r\n * @example range(0, 10, 2) // [0, 2, 4, 6, 8, 10]\r\n *\r\n * @param start - Start of the range.\r\n * @param end - End of the range (inclusive).\r\n * @param step - Increment (defaults to 1 or -1 based on direction).\r\n * @returns Array of numbers.\r\n * @throws {RangeError} If step is zero.\r\n */\r\nexport function range(start: number, end: number, step?: number): number[] {\r\n const dir = end >= start ? 1 : -1\r\n const s = step ?? dir\r\n if (s === 0) {\r\n throw new RangeError('Step must not be zero')\r\n }\r\n if ((end - start) * s < 0) {\r\n return []\r\n }\r\n const result: number[] = []\r\n let i = start\r\n if (s > 0) {\r\n while (i <= end) {\r\n result.push(i)\r\n i += s\r\n }\r\n } else {\r\n while (i >= end) {\r\n result.push(i)\r\n i += s\r\n }\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Computes the weighted mean of values with corresponding weights.\r\n *\r\n * @example weightedAverage([10, 20], [1, 4]) // 18\r\n *\r\n * @param values - Array of values.\r\n * @param weights - Array of weights.\r\n * @returns The weighted average.\r\n * @throws {RangeError} If arrays are empty, differ in length, or sum of weights is zero.\r\n */\r\nexport function weightedAverage(values: number[], weights: number[]): number {\r\n if (values.length === 0 || weights.length === 0) {\r\n throw new RangeError('Arrays must not be empty')\r\n }\r\n if (values.length !== weights.length) {\r\n throw new RangeError('Values and weights must have the same length')\r\n }\r\n let weightedSum = 0\r\n let weightSum = 0\r\n for (let i = 0; i < values.length; i++) {\r\n weightedSum += values[i]! * weights[i]!\r\n weightSum += weights[i]!\r\n }\r\n if (weightSum === 0) {\r\n throw new RangeError('Sum of weights must be non-zero')\r\n }\r\n return weightedSum / weightSum\r\n}\r\n\r\n/**\r\n * Computes the geometric mean of an array of numbers.\r\n *\r\n * @example geometricMean([4, 9]) // 6\r\n *\r\n * @param values - Array of positive numbers.\r\n * @returns The geometric mean.\r\n * @throws {RangeError} If the array is empty or contains negative values.\r\n */\r\nexport function geometricMean(values: number[]): number {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute geometric mean of an empty array')\r\n }\r\n for (const v of values) {\r\n if (v < 0) {\r\n throw new RangeError('Values must be non-negative for geometric mean')\r\n }\r\n }\r\n const logSum = values.reduce((acc, v) => (v === 0 ? acc : acc + Math.log(v)), 0)\r\n if (logSum === -Infinity) return 0\r\n return Math.exp(logSum / values.length)\r\n}\r\n\r\n/**\r\n * Computes the number of combinations (n choose k) — selecting k items from n without order.\r\n *\r\n * @example combinations(5, 2) // 10\r\n *\r\n * @param n - Total items.\r\n * @param k - Chosen items.\r\n * @returns The number of combinations.\r\n * @throws {RangeError} If n < k, or either is negative / non-integer.\r\n */\r\nexport function combinations(n: number, k: number): number {\r\n if (!Number.isInteger(n) || !Number.isInteger(k)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (n < 0 || k < 0) {\r\n throw new RangeError('Arguments must be non-negative')\r\n }\r\n if (k > n) {\r\n throw new RangeError('k must not exceed n')\r\n }\r\n if (k === 0 || k === n) return 1\r\n const r = Math.min(k, n - k)\r\n let result = 1\r\n for (let i = 1; i <= r; i++) {\r\n result = (result * (n - r + i)) / i\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Computes the number of permutations — selecting k items from n in order.\r\n *\r\n * @example permutations(5, 2) // 20\r\n *\r\n * @param n - Total items.\r\n * @param k - Chosen items.\r\n * @returns The number of permutations.\r\n * @throws {RangeError} If n < k, or either is negative / non-integer.\r\n */\r\nexport function permutations(n: number, k: number): number {\r\n if (!Number.isInteger(n) || !Number.isInteger(k)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (n < 0 || k < 0) {\r\n throw new RangeError('Arguments must be non-negative')\r\n }\r\n if (k > n) {\r\n throw new RangeError('k must not exceed n')\r\n }\r\n let result = 1\r\n for (let i = n; i > n - k; i--) {\r\n result *= i\r\n }\r\n return result\r\n}\r\n"],"mappings":"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","names":[]}
|
package/dist/string/index.d.ts
CHANGED
|
@@ -160,5 +160,75 @@ declare function randomBoolean(): boolean;
|
|
|
160
160
|
* @example pluralize(1, 'box') // "box"
|
|
161
161
|
*/
|
|
162
162
|
declare function pluralize(count: number, singular: string): string;
|
|
163
|
+
/**
|
|
164
|
+
* Removes all HTML tags from a string, including script and style content.
|
|
165
|
+
*
|
|
166
|
+
* @example stripHtml('<p>Hello <b>world</b></p>') // "Hello world"
|
|
167
|
+
* @example stripHtml('<script>alert("x")</script>hi') // "hi"
|
|
168
|
+
*/
|
|
169
|
+
declare function stripHtml(str: string): string;
|
|
170
|
+
/**
|
|
171
|
+
* Truncates a string by word count, appending a suffix when truncated.
|
|
172
|
+
*
|
|
173
|
+
* @example truncateWords('hello world foo bar', 2) // "hello world..."
|
|
174
|
+
* @example truncateWords('hello world foo bar', 2, '…') // "hello world…"
|
|
175
|
+
*/
|
|
176
|
+
declare function truncateWords(str: string, count: number, suffix?: string): string;
|
|
177
|
+
/**
|
|
178
|
+
* Checks if a string is a palindrome, ignoring case, spaces, and punctuation.
|
|
179
|
+
*
|
|
180
|
+
* @example isPalindrome('A man, a plan, a canal: Panama') // true
|
|
181
|
+
* @example isPalindrome('race a car') // false
|
|
182
|
+
*/
|
|
183
|
+
declare function isPalindrome(str: string): boolean;
|
|
184
|
+
/**
|
|
185
|
+
* Checks if two strings are anagrams, ignoring case and spaces.
|
|
186
|
+
*
|
|
187
|
+
* @example isAnagram('listen', 'silent') // true
|
|
188
|
+
* @example isAnagram('hello', 'world') // false
|
|
189
|
+
*/
|
|
190
|
+
declare function isAnagram(str1: string, str2: string): boolean;
|
|
191
|
+
/**
|
|
192
|
+
* Computes the Dice coefficient similarity between two strings (0-1).
|
|
193
|
+
*
|
|
194
|
+
* @example similarity('hello', 'hallo') // 0.666...
|
|
195
|
+
* @example similarity('abc', 'xyz') // 0
|
|
196
|
+
*/
|
|
197
|
+
declare function similarity(a: string, b: string): number;
|
|
198
|
+
/**
|
|
199
|
+
* Removes common leading whitespace from each line in a multi-line string.
|
|
200
|
+
*
|
|
201
|
+
* @example dedent(' hello\n world') // "hello\nworld"
|
|
202
|
+
* @example dedent(' foo\n bar') // "foo\n bar"
|
|
203
|
+
*/
|
|
204
|
+
declare function dedent(str: string): string;
|
|
205
|
+
/**
|
|
206
|
+
* Counts the number of words in a string, handling multiple spaces and punctuation.
|
|
207
|
+
*
|
|
208
|
+
* @example wordCount('hello world') // 2
|
|
209
|
+
* @example wordCount(' hello world! ') // 2
|
|
210
|
+
*/
|
|
211
|
+
declare function wordCount(str: string): number;
|
|
212
|
+
/**
|
|
213
|
+
* Swaps the case of each character in a string (upper to lower, lower to upper).
|
|
214
|
+
*
|
|
215
|
+
* @example swapCase('Hello World') // "hELLO wORLD"
|
|
216
|
+
* @example swapCase('ABC123') // "abc123"
|
|
217
|
+
*/
|
|
218
|
+
declare function swapCase(str: string): string;
|
|
219
|
+
/**
|
|
220
|
+
* Converts a string to COBOL / SCREAMING_SNAKE_CASE.
|
|
221
|
+
*
|
|
222
|
+
* @example toCobolCase('helloWorld') // "HELLO_WORLD"
|
|
223
|
+
* @example toCobolCase('getUserByID') // "GET_USER_BY_ID"
|
|
224
|
+
*/
|
|
225
|
+
declare function toCobolCase(str: string): string;
|
|
226
|
+
/**
|
|
227
|
+
* Counts character frequency in a string.
|
|
228
|
+
*
|
|
229
|
+
* @example charCount('hello') // { h: 1, e: 1, l: 2, o: 1 }
|
|
230
|
+
* @example charCount('aabb') // { a: 2, b: 2 }
|
|
231
|
+
*/
|
|
232
|
+
declare function charCount(str: string): Record<string, number>;
|
|
163
233
|
|
|
164
|
-
export { camelCase, capitalize, countOccurrences, escapeHtml, formatBytes, formatRupiah, fuzzyMatch, kebabCase, levenshtein, maskString, nanoid, pad, padEnd, padStart, pascalCase, pluralize, randomBoolean, randomString, reverse, slugify, snakeCase, template, terbilang, trim, trimEnd, trimStart, truncate, unescapeHtml, uuid, words };
|
|
234
|
+
export { camelCase, capitalize, charCount, countOccurrences, dedent, escapeHtml, formatBytes, formatRupiah, fuzzyMatch, isAnagram, isPalindrome, kebabCase, levenshtein, maskString, nanoid, pad, padEnd, padStart, pascalCase, pluralize, randomBoolean, randomString, reverse, similarity, slugify, snakeCase, stripHtml, swapCase, template, terbilang, toCobolCase, trim, trimEnd, trimStart, truncate, truncateWords, unescapeHtml, uuid, wordCount, words };
|
package/dist/string/index.js
CHANGED
|
@@ -265,14 +265,102 @@ function pluralize(count, singular) {
|
|
|
265
265
|
}
|
|
266
266
|
return singular + "s";
|
|
267
267
|
}
|
|
268
|
+
function stripHtml(str) {
|
|
269
|
+
return str.replace(/<[^>]*>/g, "");
|
|
270
|
+
}
|
|
271
|
+
function truncateWords(str, count, suffix = "...") {
|
|
272
|
+
const words2 = str.split(/\s+/).filter(Boolean);
|
|
273
|
+
if (words2.length <= count) return str;
|
|
274
|
+
return words2.slice(0, count).join(" ") + suffix;
|
|
275
|
+
}
|
|
276
|
+
function isPalindrome(str) {
|
|
277
|
+
const cleaned = str.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
|
278
|
+
if (cleaned.length <= 1) return true;
|
|
279
|
+
let left = 0;
|
|
280
|
+
let right = cleaned.length - 1;
|
|
281
|
+
while (left < right) {
|
|
282
|
+
if (cleaned[left] !== cleaned[right]) return false;
|
|
283
|
+
left++;
|
|
284
|
+
right--;
|
|
285
|
+
}
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
function isAnagram(str1, str2) {
|
|
289
|
+
const clean = (s) => s.replace(/\s/g, "").toLowerCase().split("").sort().join("");
|
|
290
|
+
return clean(str1) === clean(str2);
|
|
291
|
+
}
|
|
292
|
+
function similarity(a, b) {
|
|
293
|
+
if (a === b) return 1;
|
|
294
|
+
if (a.length < 2 || b.length < 2) return 0;
|
|
295
|
+
const bigrams = /* @__PURE__ */ new Map();
|
|
296
|
+
for (let i = 0; i < a.length - 1; i++) {
|
|
297
|
+
const bg = a.slice(i, i + 2);
|
|
298
|
+
bigrams.set(bg, (bigrams.get(bg) ?? 0) + 1);
|
|
299
|
+
}
|
|
300
|
+
let intersection = 0;
|
|
301
|
+
for (let i = 0; i < b.length - 1; i++) {
|
|
302
|
+
const bg = b.slice(i, i + 2);
|
|
303
|
+
const count = bigrams.get(bg) ?? 0;
|
|
304
|
+
if (count > 0) {
|
|
305
|
+
bigrams.set(bg, count - 1);
|
|
306
|
+
intersection++;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return 2 * intersection / (a.length + b.length - 2);
|
|
310
|
+
}
|
|
311
|
+
function dedent(str) {
|
|
312
|
+
const lines = str.split("\n");
|
|
313
|
+
if (lines.length === 0) return str;
|
|
314
|
+
const indent = lines.reduce((min, line) => {
|
|
315
|
+
if (line.trim().length === 0) return min;
|
|
316
|
+
const leading = line.match(/^[ \t]*/)?.[0]?.length ?? 0;
|
|
317
|
+
return min === null ? leading : Math.min(min, leading);
|
|
318
|
+
}, null);
|
|
319
|
+
if (indent === null || indent === 0) return str;
|
|
320
|
+
return lines.map((line) => line.slice(indent)).join("\n");
|
|
321
|
+
}
|
|
322
|
+
function wordCount(str) {
|
|
323
|
+
const match = str.match(/[a-zA-Z0-9]+(?:['\u2019][a-zA-Z]+)?/g);
|
|
324
|
+
return match ? match.length : 0;
|
|
325
|
+
}
|
|
326
|
+
function swapCase(str) {
|
|
327
|
+
let result = "";
|
|
328
|
+
for (let i = 0; i < str.length; i++) {
|
|
329
|
+
const ch = str[i];
|
|
330
|
+
if (ch >= "a" && ch <= "z") {
|
|
331
|
+
result += ch.toUpperCase();
|
|
332
|
+
} else if (ch >= "A" && ch <= "Z") {
|
|
333
|
+
result += ch.toLowerCase();
|
|
334
|
+
} else {
|
|
335
|
+
result += ch;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
function toCobolCase(str) {
|
|
341
|
+
const words2 = splitWords(str);
|
|
342
|
+
return words2.map((w) => w.toUpperCase()).join("_");
|
|
343
|
+
}
|
|
344
|
+
function charCount(str) {
|
|
345
|
+
const result = {};
|
|
346
|
+
for (let i = 0; i < str.length; i++) {
|
|
347
|
+
const ch = str[i];
|
|
348
|
+
result[ch] = (result[ch] ?? 0) + 1;
|
|
349
|
+
}
|
|
350
|
+
return result;
|
|
351
|
+
}
|
|
268
352
|
export {
|
|
269
353
|
camelCase,
|
|
270
354
|
capitalize,
|
|
355
|
+
charCount,
|
|
271
356
|
countOccurrences,
|
|
357
|
+
dedent,
|
|
272
358
|
escapeHtml,
|
|
273
359
|
formatBytes,
|
|
274
360
|
formatRupiah,
|
|
275
361
|
fuzzyMatch,
|
|
362
|
+
isAnagram,
|
|
363
|
+
isPalindrome,
|
|
276
364
|
kebabCase,
|
|
277
365
|
levenshtein,
|
|
278
366
|
maskString,
|
|
@@ -285,16 +373,22 @@ export {
|
|
|
285
373
|
randomBoolean,
|
|
286
374
|
randomString,
|
|
287
375
|
reverse,
|
|
376
|
+
similarity,
|
|
288
377
|
slugify,
|
|
289
378
|
snakeCase,
|
|
379
|
+
stripHtml,
|
|
380
|
+
swapCase,
|
|
290
381
|
template,
|
|
291
382
|
terbilang,
|
|
383
|
+
toCobolCase,
|
|
292
384
|
trim,
|
|
293
385
|
trimEnd,
|
|
294
386
|
trimStart,
|
|
295
387
|
truncate,
|
|
388
|
+
truncateWords,
|
|
296
389
|
unescapeHtml,
|
|
297
390
|
uuid,
|
|
391
|
+
wordCount,
|
|
298
392
|
words
|
|
299
393
|
};
|
|
300
394
|
//# sourceMappingURL=index.js.map
|
package/dist/string/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/math/index.ts","../../src/string/index.ts"],"sourcesContent":["/**\r\n * Error thrown when attempting to divide by zero.\r\n */\r\nexport class DivisionByZeroError extends Error {\r\n constructor() {\r\n super('Division by zero')\r\n this.name = 'DivisionByZeroError'\r\n }\r\n}\r\n\r\nfunction getPrecision(value: number): number {\r\n if (!isFinite(value)) return 0\r\n const eIndex = String(value).indexOf('e')\r\n if (eIndex > -1) {\r\n const exp = parseInt(String(value).slice(eIndex + 1), 10)\r\n if (exp < 0) return Math.abs(exp)\r\n return 0\r\n }\r\n const str = String(value)\r\n const dot = str.indexOf('.')\r\n return dot === -1 ? 0 : str.length - dot - 1\r\n}\r\n\r\nfunction toPrecisionFactor(a: number, b: number): number {\r\n return Math.pow(10, Math.max(getPrecision(a), getPrecision(b)))\r\n}\r\n\r\n/**\r\n * Safely adds two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The sum.\r\n */\r\nexport function add(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) + Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely subtracts two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The difference.\r\n */\r\nexport function sub(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) - Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely multiplies two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The product.\r\n */\r\nexport function mul(a: number, b: number): number {\r\n const factorA = toPrecisionFactor(a, 1)\r\n const factorB = toPrecisionFactor(1, b)\r\n const result = (Math.round(a * factorA) * Math.round(b * factorB)) / (factorA * factorB)\r\n return result\r\n}\r\n\r\n/**\r\n * Safely divides two numbers.\r\n *\r\n * @param a - The dividend.\r\n * @param b - The divisor.\r\n * @returns The quotient.\r\n * @throws {DivisionByZeroError} If `b` is zero.\r\n */\r\nexport function div(a: number, b: number): number {\r\n if (b === 0) throw new DivisionByZeroError()\r\n const factor = toPrecisionFactor(a, b)\r\n return Math.round(a * factor) / Math.round(b * factor)\r\n}\r\n\r\n/**\r\n * Rounds a number to the given precision.\r\n *\r\n * @param value - The number to round.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The rounded value.\r\n */\r\nexport function round(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n // Use toPrecision to avoid floating-point multiplication errors\r\n // e.g. 1.005 * 100 = 100.49999999999999 without this fix\r\n const shifted = Number((value * factor).toPrecision(15))\r\n return Math.round(shifted) / factor\r\n}\r\n\r\n/**\r\n * Floors a number to the given precision.\r\n *\r\n * @param value - The number to floor.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The floored value.\r\n */\r\nexport function floor(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.floor(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Ceils a number to the given precision.\r\n *\r\n * @param value - The number to ceil.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The ceiled value.\r\n */\r\nexport function ceil(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.ceil(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Checks if two numbers are approximately equal within a tolerance.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @param tolerance - Maximum difference (default `Number.EPSILON`).\r\n * @returns Whether the numbers are approximately equal.\r\n */\r\nexport function approxEqual(a: number, b: number, tolerance: number = Number.EPSILON): boolean {\r\n return Math.abs(a - b) <= tolerance\r\n}\r\n\r\n/**\r\n * Clamps a value within the inclusive range [min, max].\r\n *\r\n * @param value - The value to clamp.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns The clamped value.\r\n * @throws {RangeError} If `min` exceeds `max`.\r\n */\r\nexport function clamp(value: number, min: number, max: number): number {\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.min(Math.max(value, min), max)\r\n}\r\n\r\n/**\r\n * Computes the sum of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The total sum.\r\n */\r\nexport function sum(values: number[]): number {\r\n let total = 0\r\n for (let i = 0; i < values.length; i++) {\r\n total += values[i]!\r\n }\r\n return total\r\n}\r\n\r\n/**\r\n * Computes the average (mean) of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The average.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function average(values: number[]): number {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute average of an empty array')\r\n }\r\n return sum(values) / values.length\r\n}\r\n\r\n/**\r\n * Generates a random integer between `min` and `max` (inclusive).\r\n *\r\n * @param min - The minimum integer.\r\n * @param max - The maximum integer.\r\n * @returns A random integer.\r\n * @throws {RangeError} If arguments are not integers or `min > max`.\r\n */\r\nexport function randomInt(min: number, max: number): number {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.floor(Math.random() * (max - min + 1)) + min\r\n}\r\n\r\n/**\r\n * Checks if a number is within the inclusive range [min, max].\r\n *\r\n * @param value - The number to check.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns Whether the value is in range.\r\n */\r\nexport function inRange(value: number, min: number, max: number): boolean {\r\n return value >= min && value <= max\r\n}\r\n\r\n// ─── Statistics ─────────────────────────────────────────\r\n\r\n/**\r\n * Computes the median of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The median value.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function median(values: number[]): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute median of an empty array')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const mid = Math.floor(sorted.length / 2)\r\n return sorted.length % 2 === 0 ? (sorted[mid - 1]! + sorted[mid]!) / 2 : sorted[mid]!\r\n}\r\n\r\n/**\r\n * Computes the population standard deviation.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function stddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / values.length)\r\n}\r\n\r\n/**\r\n * Computes the sample standard deviation (Bessel's correction).\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The sample standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function sampleStddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for sample stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / (values.length - 1))\r\n}\r\n\r\n/**\r\n * Computes the percentile value (0-100) using linear interpolation.\r\n *\r\n * @param values - Array of numbers.\r\n * @param p - Percentile (0-100).\r\n * @returns The percentile value.\r\n * @throws {RangeError} If p is outside [0, 100] or array is empty.\r\n */\r\nexport function percentile(values: number[], p: number): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute percentile of empty array')\r\n if (p < 0 || p > 100) throw new RangeError('Percentile must be between 0 and 100')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const rank = (p / 100) * (sorted.length - 1)\r\n const lower = Math.floor(rank)\r\n const upper = Math.ceil(rank)\r\n if (lower === upper) return sorted[lower]!\r\n return sorted[lower]! + (sorted[upper]! - sorted[lower]!) * (rank - lower)\r\n}\r\n\r\n/**\r\n * Computes the Pearson correlation coefficient between two arrays.\r\n *\r\n * @param x - First array.\r\n * @param y - Second array.\r\n * @returns The correlation coefficient (-1 to 1).\r\n * @throws {RangeError} If arrays have different lengths or fewer than 2 pairs.\r\n */\r\nexport function correlation(x: number[], y: number[]): number {\r\n if (x.length !== y.length) throw new RangeError('Arrays must have the same length')\r\n if (x.length < 2) throw new RangeError('Need at least 2 pairs for correlation')\r\n const n = x.length\r\n const meanX = sum(x) / n\r\n const meanY = sum(y) / n\r\n let num = 0\r\n let denX = 0\r\n let denY = 0\r\n for (let i = 0; i < n; i++) {\r\n const dx = x[i]! - meanX\r\n const dy = y[i]! - meanY\r\n num += dx * dy\r\n denX += dx * dx\r\n denY += dy * dy\r\n }\r\n if (denX === 0 || denY === 0) return 0\r\n return num / Math.sqrt(denX * denY)\r\n}\r\n\r\n/**\r\n * Formats a number as a currency string with locale support.\r\n *\r\n * @example formatCurrency(1500000) // \"Rp1.500.000\"\r\n * @example formatCurrency(1500000, { notation: 'compact' }) // \"Rp1,5 jt\"\r\n * @example formatCurrency(99.99, { locale: 'en-US', currency: 'USD' }) // \"$99.99\"\r\n *\r\n * @param value - The number to format.\r\n * @param options - Formatting options.\r\n * @returns The formatted currency string.\r\n */\r\nexport function formatCurrency(\r\n value: number,\r\n options?: { locale?: string; currency?: string; notation?: 'standard' | 'compact' },\r\n): string {\r\n const locale = options?.locale ?? 'id-ID'\r\n const currency = options?.currency ?? 'IDR'\r\n const notation = options?.notation ?? 'standard'\r\n\r\n try {\r\n return new Intl.NumberFormat(locale, {\r\n style: 'currency',\r\n currency,\r\n notation,\r\n minimumFractionDigits: 0,\r\n maximumFractionDigits: 2,\r\n }).format(value)\r\n } catch {\r\n return `${currency} ${value.toLocaleString(locale)}`\r\n }\r\n}\r\n","import { formatCurrency } from '../math/index.js'\r\n\r\nconst WORD_SPLIT_RE = /[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|\\d|\\b)|\\d+/g\r\n\r\nfunction splitWords(str: string): string[] {\r\n return str.match(WORD_SPLIT_RE) ?? []\r\n}\r\n\r\n/**\r\n * Capitalizes the first character and lowercases the rest.\r\n */\r\nexport function capitalize(str: string): string {\r\n if (str.length === 0) return str\r\n return str[0]!.toUpperCase() + str.slice(1).toLowerCase()\r\n}\r\n\r\n/**\r\n * Converts a string to camelCase.\r\n */\r\nexport function camelCase(str: string): string {\r\n const words = splitWords(str)\r\n if (words.length === 0) return ''\r\n const [firstWord, ...rest] = words\r\n return firstWord!.toLowerCase() + rest.map(w => w[0]!.toUpperCase() + w.slice(1).toLowerCase()).join('')\r\n}\r\n\r\n/**\r\n * Converts a string to kebab-case.\r\n */\r\nexport function kebabCase(str: string): string {\r\n return splitWords(str).map(w => w.toLowerCase()).join('-')\r\n}\r\n\r\n/**\r\n * Converts a string to snake_case.\r\n */\r\nexport function snakeCase(str: string): string {\r\n return splitWords(str).map(w => w.toLowerCase()).join('_')\r\n}\r\n\r\n/**\r\n * Converts a string to PascalCase.\r\n */\r\nexport function pascalCase(str: string): string {\r\n return splitWords(str).map(w => w[0]!.toUpperCase() + w.slice(1).toLowerCase()).join('')\r\n}\r\n\r\n/**\r\n * Truncates a string to the specified length, appending a suffix (default \"...\").\r\n */\r\nexport function truncate(str: string, maxLength: number, suffix = '...'): string {\r\n if (str.length <= maxLength) return str\r\n return str.slice(0, Math.max(0, maxLength - suffix.length)) + suffix\r\n}\r\n\r\n/**\r\n * Simple string interpolation using {{key}} syntax.\r\n *\r\n * @example template(\"Hello {{name}}\", { name: \"world\" }) // => \"Hello world\"\r\n */\r\nexport function template(str: string, data: Record<string, string | number>): string {\r\n return str.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key: string) => {\r\n const value = data[key]\r\n return value !== undefined ? String(value) : `{{${key}}}`\r\n })\r\n}\r\n\r\n/**\r\n * Generates a UUID v4 string.\r\n * Uses crypto.randomUUID when available, falls back to manual implementation.\r\n */\r\nexport function uuid(): string {\r\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\r\n return crypto.randomUUID()\r\n }\r\n const hex = '0123456789abcdef'\r\n const chars: string[] = []\r\n for (let i = 0; i < 36; i++) {\r\n if (i === 8 || i === 13 || i === 18 || i === 23) {\r\n chars.push('-')\r\n } else if (i === 14) {\r\n chars.push('4')\r\n } else if (i === 19) {\r\n chars.push(hex[Math.floor(Math.random() * 4) + 8]!)\r\n } else {\r\n chars.push(hex[Math.floor(Math.random() * 16)]!)\r\n }\r\n }\r\n return chars.join('')\r\n}\r\n\r\n/**\r\n * Generates a short random ID with configurable length and alphabet.\r\n *\r\n * @default size = 21, alphabet = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-\"\r\n */\r\nexport function nanoid(size = 21, alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-'): string {\r\n const len = alphabet.length\r\n let result = ''\r\n for (let i = 0; i < size; i++) {\r\n result += alphabet[Math.floor(Math.random() * len)]!\r\n }\r\n return result\r\n}\r\n\r\nconst HTML_ESCAPE_MAP: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": ''',\r\n}\r\n\r\nconst HTML_UNESCAPE_MAP: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '"': '\"',\r\n ''': \"'\",\r\n ''': \"'\",\r\n}\r\n\r\n/**\r\n * Escapes HTML special characters (&, <, >, \", ').\r\n */\r\nexport function escapeHtml(str: string): string {\r\n return str.replace(/[&<>\"']/g, ch => HTML_ESCAPE_MAP[ch] ?? ch)\r\n}\r\n\r\n/**\r\n * Unescapes common HTML entities.\r\n */\r\nexport function unescapeHtml(str: string): string {\r\n return str.replace(/&(?:amp|lt|gt|quot|#39|#x27);/g, entity => HTML_UNESCAPE_MAP[entity] ?? entity)\r\n}\r\n\r\n/**\r\n * Removes whitespace from both ends of a string.\r\n */\r\nexport function trim(str: string): string {\r\n return str.trim()\r\n}\r\n\r\n/**\r\n * Removes whitespace from the start of a string.\r\n */\r\nexport function trimStart(str: string): string {\r\n return str.trimStart()\r\n}\r\n\r\n/**\r\n * Removes whitespace from the end of a string.\r\n */\r\nexport function trimEnd(str: string): string {\r\n return str.trimEnd()\r\n}\r\n\r\n/**\r\n * Pads a string to the given length by adding characters to both sides.\r\n */\r\nexport function pad(str: string, length: number, char = ' '): string {\r\n const totalPadding = Math.max(0, length - str.length)\r\n const leftPad = Math.floor(totalPadding / 2)\r\n const rightPad = totalPadding - leftPad\r\n return char.repeat(leftPad) + str + char.repeat(rightPad)\r\n}\r\n\r\n/**\r\n * Pads the start of a string to the given length.\r\n */\r\nexport function padStart(str: string, length: number, char = ' '): string {\r\n return str.padStart(length, char)\r\n}\r\n\r\n/**\r\n * Pads the end of a string to the given length.\r\n */\r\nexport function padEnd(str: string, length: number, char = ' '): string {\r\n return str.padEnd(length, char)\r\n}\r\n\r\n/**\r\n * Reverses a string.\r\n */\r\nexport function reverse(str: string): string {\r\n return str.split('').reverse().join('')\r\n}\r\n\r\n/**\r\n * Splits a string into words.\r\n */\r\nexport function words(str: string): string[] {\r\n return splitWords(str)\r\n}\r\n\r\n/**\r\n * Converts a string to a URL-friendly slug.\r\n */\r\nexport function slugify(str: string): string {\r\n return str\r\n .toLowerCase()\r\n .replace(/[^\\w\\s-]/g, '')\r\n .replace(/[\\s_]+/g, '-')\r\n .replace(/-+/g, '-')\r\n .replace(/^-+/, '')\r\n .replace(/-+$/, '')\r\n}\r\n\r\n/**\r\n * Counts occurrences of a substring in a string.\r\n */\r\nexport function countOccurrences(str: string, substring: string): number {\r\n if (substring.length === 0 || str.length === 0) return 0\r\n let count = 0\r\n let pos = 0\r\n while ((pos = str.indexOf(substring, pos)) !== -1) {\r\n count++\r\n pos += substring.length\r\n }\r\n return count\r\n}\r\n\r\n/**\r\n * Computes the Levenshtein distance between two strings.\r\n * Uses iterative DP with O(min(m,n)) space.\r\n */\r\nexport function levenshtein(a: string, b: string): number {\r\n const an = a.length\r\n const bn = b.length\r\n if (an === 0) return bn\r\n if (bn === 0) return an\r\n if (an < bn) return levenshtein(b, a)\r\n\r\n let prev = new Uint32Array(bn + 1)\r\n let curr = new Uint32Array(bn + 1)\r\n for (let j = 0; j <= bn; j++) prev[j] = j\r\n\r\n for (let i = 1; i <= an; i++) {\r\n curr[0] = i\r\n for (let j = 1; j <= bn; j++) {\r\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\r\n curr[j] = Math.min(\r\n prev[j]! + 1,\r\n curr[j - 1]! + 1,\r\n prev[j - 1]! + cost,\r\n )\r\n }\r\n ;[prev, curr] = [curr, prev]\r\n }\r\n return prev[bn]!\r\n}\r\n\r\n/**\r\n * Performs a simple fuzzy match: checks if all characters of query\r\n * appear in str in order (case-insensitive).\r\n */\r\nexport function fuzzyMatch(str: string, query: string): boolean {\r\n if (query.length === 0) return true\r\n if (str.length === 0) return false\r\n const sl = str.toLowerCase()\r\n const ql = query.toLowerCase()\r\n let si = 0\r\n for (let qi = 0; qi < ql.length; qi++) {\r\n si = sl.indexOf(ql[qi]!, si)\r\n if (si === -1) return false\r\n si++\r\n }\r\n return true\r\n}\r\n\r\n/**\r\n * Masks parts of a string, useful for data compliance (PDPA/GDPR).\r\n *\r\n * @example maskString('08123456789') // \"0812****789\"\r\n * @example maskString('hello@email.com') // \"h***@e***.com\"\r\n * @example maskString('1234567890', { start: 0, end: 4, char: '#' }) // \"####567890\"\r\n */\r\nexport function maskString(\r\n str: string,\r\n options?: {\r\n start?: number\r\n end?: number\r\n char?: string\r\n },\r\n): string {\r\n if (str.length === 0) return str\r\n const maskChar = options?.char ?? '*'\r\n const start = options?.start ?? Math.ceil(str.length * 0.25)\r\n const end = options?.end ?? Math.floor(str.length * 0.75)\r\n\r\n if (start >= end || start < 0) return str\r\n const clampedStart = Math.max(0, start)\r\n const clampedEnd = Math.min(str.length, end)\r\n return (\r\n str.slice(0, clampedStart) +\r\n maskChar.repeat(clampedEnd - clampedStart) +\r\n str.slice(clampedEnd)\r\n )\r\n}\r\n\r\n// ─── Indonesian Locale Utilities ────────────────────────\r\n\r\nconst SATUAN = ['', 'satu', 'dua', 'tiga', 'empat', 'lima', 'enam', 'tujuh', 'delapan', 'sembilan'] as const\r\nconst BELASAN = ['sepuluh', 'sebelas', 'dua belas', 'tiga belas', 'empat belas', 'lima belas', 'enam belas', 'tujuh belas', 'delapan belas', 'sembilan belas'] as const\r\nconst PULUHAN = ['', '', 'dua puluh', 'tiga puluh', 'empat puluh', 'lima puluh', 'enam puluh', 'tujuh puluh', 'delapan puluh', 'sembilan puluh'] as const\r\n\r\nfunction _terbilang(n: number): string {\r\n if (n < 0) return 'minus ' + _terbilang(-n)\r\n if (n === 0) return 'nol'\r\n if (n < 10) return SATUAN[n]!\r\n if (n < 20) return n === 10 ? 'sepuluh' : n === 11 ? 'sebelas' : BELASAN[n - 10]!\r\n if (n < 100) {\r\n const pul = Math.floor(n / 10)\r\n const sat = n % 10\r\n return PULUHAN[pul]! + (sat > 0 ? ' ' + SATUAN[sat]! : '')\r\n }\r\n if (n < 1000) {\r\n const ratus = Math.floor(n / 100)\r\n const sis = n % 100\r\n const ratusStr = ratus === 1 ? 'seratus' : SATUAN[ratus]! + ' ratus'\r\n return ratusStr + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n if (n < 1_000_000) {\r\n const rib = Math.floor(n / 1000)\r\n const sis = n % 1000\r\n const ribStr = rib === 1 ? 'seribu' : _terbilang(rib) + ' ribu'\r\n return ribStr + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n if (n < 1_000_000_000) {\r\n const jut = Math.floor(n / 1_000_000)\r\n const sis = n % 1_000_000\r\n return _terbilang(jut) + ' juta' + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n if (n < 1_000_000_000_000) {\r\n const mil = Math.floor(n / 1_000_000_000)\r\n const sis = n % 1_000_000_000\r\n return _terbilang(mil) + ' miliar' + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n const tril = Math.floor(n / 1_000_000_000_000)\r\n const sis = n % 1_000_000_000_000\r\n return _terbilang(tril) + ' triliun' + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n}\r\n\r\n/**\r\n * Converts a number to Indonesian words (terbilang).\r\n *\r\n * @example terbilang(1500000) // \"satu juta lima ratus ribu\"\r\n * @example terbilang(2024) // \"dua ribu dua puluh empat\"\r\n * @example terbilang(11) // \"sebelas\"\r\n * @example terbilang(100) // \"seratus\"\r\n */\r\nexport function terbilang(value: number): string {\r\n if (!Number.isFinite(value)) throw new RangeError('Input must be a finite number')\r\n if (value > Number.MAX_SAFE_INTEGER) throw new RangeError('Input terlalu besar')\r\n return _terbilang(Math.floor(Math.abs(value)))\r\n}\r\n\r\n/**\r\n * Formats a number as Indonesian Rupiah string.\r\n *\r\n * @example formatRupiah(1500000) // \"Rp1.500.000\"\r\n * @example formatRupiah(1500000, { notation: 'compact' }) // \"Rp1,5 jt\"\r\n */\r\nexport function formatRupiah(\r\n value: number,\r\n options?: { notation?: 'standard' | 'compact' },\r\n): string {\r\n return formatCurrency(value, { locale: 'id-ID', currency: 'IDR', notation: options?.notation })\r\n}\r\n\r\n/**\r\n * Formats a byte count into a human-readable string.\r\n *\r\n * @example formatBytes(1024) // \"1 KB\"\r\n * @example formatBytes(1536) // \"1.5 KB\"\r\n * @example formatBytes(1048576) // \"1 MB\"\r\n * @example formatBytes(0) // \"0 B\"\r\n */\r\nexport function formatBytes(bytes: number, options?: { decimals?: number }): string {\r\n if (bytes === 0) return '0 B'\r\n if (!Number.isFinite(bytes) || bytes < 0) return '0 B'\r\n const k = 1024\r\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']\r\n const i = Math.floor(Math.log(bytes) / Math.log(k))\r\n const dm = options?.decimals ?? (i === 0 ? 0 : 1)\r\n const index = Math.min(i, sizes.length - 1)\r\n return parseFloat((bytes / Math.pow(k, index)).toFixed(dm)) + ' ' + sizes[index]\r\n}\r\n\r\n/**\r\n * Generates a random alphanumeric string of the specified length.\r\n *\r\n * @example randomString() // \"a3F8k2...\"\r\n * @example randomString(8) // \"X7j2K9mQ\"\r\n */\r\nexport function randomString(length: number = 16): string {\r\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\r\n let result = ''\r\n for (let i = 0; i < length; i++) {\r\n result += chars[Math.floor(Math.random() * chars.length)]\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns a random boolean value.\r\n *\r\n * @example randomBoolean() // true or false\r\n */\r\nexport function randomBoolean(): boolean {\r\n return Math.random() >= 0.5\r\n}\r\n\r\n/**\r\n * Basic English pluralization helper. Adds 's' or 'es' based on simple rules.\r\n *\r\n * @example pluralize(1, 'apple') // \"apple\"\r\n * @example pluralize(3, 'apple') // \"apples\"\r\n * @example pluralize(0, 'box') // \"boxes\"\r\n * @example pluralize(1, 'box') // \"box\"\r\n */\r\nexport function pluralize(count: number, singular: string): string {\r\n if (count === 1) return singular\r\n const last = singular[singular.length - 1]\r\n const lastTwo = singular.slice(-2)\r\n if (last === 's' || last === 'x' || last === 'z' || lastTwo === 'ch' || lastTwo === 'sh') {\r\n return singular + 'es'\r\n }\r\n if (last === 'y' && singular.length > 2 && !'aeiou'.includes(singular[singular.length - 2]!)) {\r\n return singular.slice(0, -1) + 'ies'\r\n }\r\n return singular + 's'\r\n}\r\n"],"mappings":"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","names":["words","sis"]}
|
|
1
|
+
{"version":3,"sources":["../../src/math/index.ts","../../src/string/index.ts"],"sourcesContent":["/**\r\n * Error thrown when attempting to divide by zero.\r\n */\r\nexport class DivisionByZeroError extends Error {\r\n constructor() {\r\n super('Division by zero')\r\n this.name = 'DivisionByZeroError'\r\n }\r\n}\r\n\r\nfunction getPrecision(value: number): number {\r\n if (!isFinite(value)) return 0\r\n const eIndex = String(value).indexOf('e')\r\n if (eIndex > -1) {\r\n const exp = parseInt(String(value).slice(eIndex + 1), 10)\r\n if (exp < 0) return Math.abs(exp)\r\n return 0\r\n }\r\n const str = String(value)\r\n const dot = str.indexOf('.')\r\n return dot === -1 ? 0 : str.length - dot - 1\r\n}\r\n\r\nfunction toPrecisionFactor(a: number, b: number): number {\r\n return Math.pow(10, Math.max(getPrecision(a), getPrecision(b)))\r\n}\r\n\r\n/**\r\n * Safely adds two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The sum.\r\n */\r\nexport function add(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) + Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely subtracts two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The difference.\r\n */\r\nexport function sub(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) - Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely multiplies two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The product.\r\n */\r\nexport function mul(a: number, b: number): number {\r\n const factorA = toPrecisionFactor(a, 1)\r\n const factorB = toPrecisionFactor(1, b)\r\n const result = (Math.round(a * factorA) * Math.round(b * factorB)) / (factorA * factorB)\r\n return result\r\n}\r\n\r\n/**\r\n * Safely divides two numbers.\r\n *\r\n * @param a - The dividend.\r\n * @param b - The divisor.\r\n * @returns The quotient.\r\n * @throws {DivisionByZeroError} If `b` is zero.\r\n */\r\nexport function div(a: number, b: number): number {\r\n if (b === 0) throw new DivisionByZeroError()\r\n const factor = toPrecisionFactor(a, b)\r\n return Math.round(a * factor) / Math.round(b * factor)\r\n}\r\n\r\n/**\r\n * Rounds a number to the given precision.\r\n *\r\n * @param value - The number to round.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The rounded value.\r\n */\r\nexport function round(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n // Use toPrecision to avoid floating-point multiplication errors\r\n // e.g. 1.005 * 100 = 100.49999999999999 without this fix\r\n const shifted = Number((value * factor).toPrecision(15))\r\n return Math.round(shifted) / factor\r\n}\r\n\r\n/**\r\n * Floors a number to the given precision.\r\n *\r\n * @param value - The number to floor.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The floored value.\r\n */\r\nexport function floor(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.floor(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Ceils a number to the given precision.\r\n *\r\n * @param value - The number to ceil.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The ceiled value.\r\n */\r\nexport function ceil(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.ceil(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Checks if two numbers are approximately equal within a tolerance.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @param tolerance - Maximum difference (default `Number.EPSILON`).\r\n * @returns Whether the numbers are approximately equal.\r\n */\r\nexport function approxEqual(a: number, b: number, tolerance: number = Number.EPSILON): boolean {\r\n return Math.abs(a - b) <= tolerance\r\n}\r\n\r\n/**\r\n * Clamps a value within the inclusive range [min, max].\r\n *\r\n * @param value - The value to clamp.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns The clamped value.\r\n * @throws {RangeError} If `min` exceeds `max`.\r\n */\r\nexport function clamp(value: number, min: number, max: number): number {\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.min(Math.max(value, min), max)\r\n}\r\n\r\n/**\r\n * Computes the sum of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The total sum.\r\n */\r\nexport function sum(values: number[]): number {\r\n let total = 0\r\n for (let i = 0; i < values.length; i++) {\r\n total += values[i]!\r\n }\r\n return total\r\n}\r\n\r\n/**\r\n * Computes the average (mean) of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The average.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function average(values: number[]): number {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute average of an empty array')\r\n }\r\n return sum(values) / values.length\r\n}\r\n\r\n/**\r\n * Generates a random integer between `min` and `max` (inclusive).\r\n *\r\n * @param min - The minimum integer.\r\n * @param max - The maximum integer.\r\n * @returns A random integer.\r\n * @throws {RangeError} If arguments are not integers or `min > max`.\r\n */\r\nexport function randomInt(min: number, max: number): number {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.floor(Math.random() * (max - min + 1)) + min\r\n}\r\n\r\n/**\r\n * Checks if a number is within the inclusive range [min, max].\r\n *\r\n * @param value - The number to check.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns Whether the value is in range.\r\n */\r\nexport function inRange(value: number, min: number, max: number): boolean {\r\n return value >= min && value <= max\r\n}\r\n\r\n// ─── Statistics ─────────────────────────────────────────\r\n\r\n/**\r\n * Computes the median of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The median value.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function median(values: number[]): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute median of an empty array')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const mid = Math.floor(sorted.length / 2)\r\n return sorted.length % 2 === 0 ? (sorted[mid - 1]! + sorted[mid]!) / 2 : sorted[mid]!\r\n}\r\n\r\n/**\r\n * Computes the population standard deviation.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function stddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / values.length)\r\n}\r\n\r\n/**\r\n * Computes the sample standard deviation (Bessel's correction).\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The sample standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function sampleStddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for sample stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / (values.length - 1))\r\n}\r\n\r\n/**\r\n * Computes the percentile value (0-100) using linear interpolation.\r\n *\r\n * @param values - Array of numbers.\r\n * @param p - Percentile (0-100).\r\n * @returns The percentile value.\r\n * @throws {RangeError} If p is outside [0, 100] or array is empty.\r\n */\r\nexport function percentile(values: number[], p: number): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute percentile of empty array')\r\n if (p < 0 || p > 100) throw new RangeError('Percentile must be between 0 and 100')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const rank = (p / 100) * (sorted.length - 1)\r\n const lower = Math.floor(rank)\r\n const upper = Math.ceil(rank)\r\n if (lower === upper) return sorted[lower]!\r\n return sorted[lower]! + (sorted[upper]! - sorted[lower]!) * (rank - lower)\r\n}\r\n\r\n/**\r\n * Computes the Pearson correlation coefficient between two arrays.\r\n *\r\n * @param x - First array.\r\n * @param y - Second array.\r\n * @returns The correlation coefficient (-1 to 1).\r\n * @throws {RangeError} If arrays have different lengths or fewer than 2 pairs.\r\n */\r\nexport function correlation(x: number[], y: number[]): number {\r\n if (x.length !== y.length) throw new RangeError('Arrays must have the same length')\r\n if (x.length < 2) throw new RangeError('Need at least 2 pairs for correlation')\r\n const n = x.length\r\n const meanX = sum(x) / n\r\n const meanY = sum(y) / n\r\n let num = 0\r\n let denX = 0\r\n let denY = 0\r\n for (let i = 0; i < n; i++) {\r\n const dx = x[i]! - meanX\r\n const dy = y[i]! - meanY\r\n num += dx * dy\r\n denX += dx * dx\r\n denY += dy * dy\r\n }\r\n if (denX === 0 || denY === 0) return 0\r\n return num / Math.sqrt(denX * denY)\r\n}\r\n\r\n/**\r\n * Formats a number as a currency string with locale support.\r\n *\r\n * @example formatCurrency(1500000) // \"Rp1.500.000\"\r\n * @example formatCurrency(1500000, { notation: 'compact' }) // \"Rp1,5 jt\"\r\n * @example formatCurrency(99.99, { locale: 'en-US', currency: 'USD' }) // \"$99.99\"\r\n *\r\n * @param value - The number to format.\r\n * @param options - Formatting options.\r\n * @returns The formatted currency string.\r\n */\r\nexport function formatCurrency(\r\n value: number,\r\n options?: { locale?: string; currency?: string; notation?: 'standard' | 'compact' },\r\n): string {\r\n const locale = options?.locale ?? 'id-ID'\r\n const currency = options?.currency ?? 'IDR'\r\n const notation = options?.notation ?? 'standard'\r\n\r\n try {\r\n return new Intl.NumberFormat(locale, {\r\n style: 'currency',\r\n currency,\r\n notation,\r\n minimumFractionDigits: 0,\r\n maximumFractionDigits: 2,\r\n }).format(value)\r\n } catch {\r\n return `${currency} ${value.toLocaleString(locale)}`\r\n }\r\n}\r\n\r\n// ─── Parity ─────────────────────────────────────────────\r\n\r\n/**\r\n * Checks if a number is even.\r\n *\r\n * @example isEven(4) // true\r\n * @example isEven(7) // false\r\n *\r\n * @param n - The number to check.\r\n * @returns Whether the number is even.\r\n */\r\nexport function isEven(n: number): boolean {\r\n return n % 2 === 0\r\n}\r\n\r\n/**\r\n * Checks if a number is odd.\r\n *\r\n * @example isOdd(3) // true\r\n * @example isOdd(8) // false\r\n *\r\n * @param n - The number to check.\r\n * @returns Whether the number is odd.\r\n */\r\nexport function isOdd(n: number): boolean {\r\n return n % 2 !== 0\r\n}\r\n\r\n// ─── Number Theory ──────────────────────────────────────\r\n\r\n/**\r\n * Computes the greatest common divisor (GCD) using the Euclidean algorithm.\r\n *\r\n * @example gcd(12, 8) // 4\r\n * @example gcd(17, 5) // 1\r\n *\r\n * @param a - First integer.\r\n * @param b - Second integer.\r\n * @returns The GCD.\r\n */\r\nexport function gcd(a: number, b: number): number {\r\n if (!Number.isInteger(a) || !Number.isInteger(b)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n a = Math.abs(a)\r\n b = Math.abs(b)\r\n while (b !== 0) {\r\n const t = b\r\n b = a % b\r\n a = t\r\n }\r\n return a\r\n}\r\n\r\n/**\r\n * Computes the least common multiple (LCM).\r\n *\r\n * @example lcm(4, 6) // 12\r\n * @example lcm(7, 5) // 35\r\n *\r\n * @param a - First integer.\r\n * @param b - Second integer.\r\n * @returns The LCM.\r\n * @throws {RangeError} If either argument is zero.\r\n */\r\nexport function lcm(a: number, b: number): number {\r\n if (!Number.isInteger(a) || !Number.isInteger(b)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (a === 0 || b === 0) {\r\n throw new RangeError('Arguments must be non-zero')\r\n }\r\n return Math.abs(a * b) / gcd(a, b)\r\n}\r\n\r\n/**\r\n * Computes the factorial of a non-negative integer.\r\n *\r\n * @example factorial(5) // 120\r\n * @example factorial(0) // 1\r\n *\r\n * @param n - Non-negative integer.\r\n * @returns The factorial.\r\n * @throws {RangeError} If n is negative or not an integer.\r\n */\r\nexport function factorial(n: number): number {\r\n if (!Number.isInteger(n)) {\r\n throw new RangeError('Argument must be an integer')\r\n }\r\n if (n < 0) {\r\n throw new RangeError('Factorial is not defined for negative numbers')\r\n }\r\n let result = 1\r\n for (let i = 2; i <= n; i++) {\r\n result *= i\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Tests whether a number is prime (optimized trial division).\r\n *\r\n * @example isPrime(7) // true\r\n * @example isPrime(10) // false\r\n *\r\n * @param n - Positive integer.\r\n * @returns Whether the number is prime.\r\n * @throws {RangeError} If n is not an integer or is less than 2.\r\n */\r\nexport function isPrime(n: number): boolean {\r\n if (!Number.isInteger(n)) {\r\n throw new RangeError('Argument must be an integer')\r\n }\r\n if (n < 2) return false\r\n if (n === 2 || n === 3) return true\r\n if (n % 2 === 0 || n % 3 === 0) return false\r\n const limit = Math.sqrt(n)\r\n for (let i = 5; i <= limit; i += 6) {\r\n if (n % i === 0 || n % (i + 2) === 0) return false\r\n }\r\n return true\r\n}\r\n\r\n// ─── Angle Conversion ───────────────────────────────────\r\n\r\n/**\r\n * Converts degrees to radians.\r\n *\r\n * @example toRadians(180) // ~3.14159\r\n *\r\n * @param degrees - Angle in degrees.\r\n * @returns Angle in radians.\r\n */\r\nexport function toRadians(degrees: number): number {\r\n return (degrees * Math.PI) / 180\r\n}\r\n\r\n/**\r\n * Converts radians to degrees.\r\n *\r\n * @example toDegrees(Math.PI) // 180\r\n *\r\n * @param radians - Angle in radians.\r\n * @returns Angle in degrees.\r\n */\r\nexport function toDegrees(radians: number): number {\r\n return (radians * 180) / Math.PI\r\n}\r\n\r\n// ─── Interpolation & Mapping ────────────────────────────\r\n\r\n/**\r\n * Linearly interpolates between `a` and `b` by `t`.\r\n *\r\n * @example lerp(0, 100, 0.5) // 50\r\n *\r\n * @param a - Start value.\r\n * @param b - End value.\r\n * @param t - Interpolation factor (typically 0–1).\r\n * @returns The interpolated value.\r\n */\r\nexport function lerp(a: number, b: number, t: number): number {\r\n return a + (b - a) * t\r\n}\r\n\r\n/**\r\n * Calculates what percentage `value` is of `total`.\r\n *\r\n * @example percentageOf(25, 200) // 12.5\r\n *\r\n * @param value - The part value.\r\n * @param total - The total value.\r\n * @returns The percentage.\r\n * @throws {RangeError} If total is zero.\r\n */\r\nexport function percentageOf(value: number, total: number): number {\r\n if (total === 0) {\r\n throw new RangeError('Total must be non-zero')\r\n }\r\n return (value / total) * 100\r\n}\r\n\r\n/**\r\n * Maps a value from one range to another.\r\n *\r\n * @example mapRange(0.5, 0, 1, 0, 100) // 50\r\n *\r\n * @param value - The value to map.\r\n * @param inMin - Lower bound of the input range.\r\n * @param inMax - Upper bound of the input range.\r\n * @param outMin - Lower bound of the output range.\r\n * @param outMax - Upper bound of the output range.\r\n * @returns The mapped value.\r\n * @throws {RangeError} If the input range is zero.\r\n */\r\nexport function mapRange(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number {\r\n if (inMin === inMax) {\r\n throw new RangeError('Input range must not be zero')\r\n }\r\n return ((value - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin\r\n}\r\n\r\n// ─── Statistics ─────────────────────────────────────────\r\n\r\n/**\r\n * Computes the statistical mode(s) — the most frequently occurring value(s).\r\n *\r\n * @example mode([1, 2, 2, 3]) // [2]\r\n * @example mode([1, 1, 2, 2]) // [1, 2]\r\n *\r\n * @param values - Array of numbers.\r\n * @returns Array of mode value(s).\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function mode(values: number[]): number[] {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute mode of an empty array')\r\n }\r\n const freq = new Map<number, number>()\r\n let maxFreq = 0\r\n for (const v of values) {\r\n const count = (freq.get(v) ?? 0) + 1\r\n freq.set(v, count)\r\n if (count > maxFreq) maxFreq = count\r\n }\r\n const result: number[] = []\r\n for (const [v, count] of freq) {\r\n if (count === maxFreq) result.push(v)\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Generates an array of numbers from `start` to `end` (inclusive),\r\n * incremented by `step`.\r\n *\r\n * @example range(1, 5) // [1, 2, 3, 4, 5]\r\n * @example range(0, 10, 2) // [0, 2, 4, 6, 8, 10]\r\n *\r\n * @param start - Start of the range.\r\n * @param end - End of the range (inclusive).\r\n * @param step - Increment (defaults to 1 or -1 based on direction).\r\n * @returns Array of numbers.\r\n * @throws {RangeError} If step is zero.\r\n */\r\nexport function range(start: number, end: number, step?: number): number[] {\r\n const dir = end >= start ? 1 : -1\r\n const s = step ?? dir\r\n if (s === 0) {\r\n throw new RangeError('Step must not be zero')\r\n }\r\n if ((end - start) * s < 0) {\r\n return []\r\n }\r\n const result: number[] = []\r\n let i = start\r\n if (s > 0) {\r\n while (i <= end) {\r\n result.push(i)\r\n i += s\r\n }\r\n } else {\r\n while (i >= end) {\r\n result.push(i)\r\n i += s\r\n }\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Computes the weighted mean of values with corresponding weights.\r\n *\r\n * @example weightedAverage([10, 20], [1, 4]) // 18\r\n *\r\n * @param values - Array of values.\r\n * @param weights - Array of weights.\r\n * @returns The weighted average.\r\n * @throws {RangeError} If arrays are empty, differ in length, or sum of weights is zero.\r\n */\r\nexport function weightedAverage(values: number[], weights: number[]): number {\r\n if (values.length === 0 || weights.length === 0) {\r\n throw new RangeError('Arrays must not be empty')\r\n }\r\n if (values.length !== weights.length) {\r\n throw new RangeError('Values and weights must have the same length')\r\n }\r\n let weightedSum = 0\r\n let weightSum = 0\r\n for (let i = 0; i < values.length; i++) {\r\n weightedSum += values[i]! * weights[i]!\r\n weightSum += weights[i]!\r\n }\r\n if (weightSum === 0) {\r\n throw new RangeError('Sum of weights must be non-zero')\r\n }\r\n return weightedSum / weightSum\r\n}\r\n\r\n/**\r\n * Computes the geometric mean of an array of numbers.\r\n *\r\n * @example geometricMean([4, 9]) // 6\r\n *\r\n * @param values - Array of positive numbers.\r\n * @returns The geometric mean.\r\n * @throws {RangeError} If the array is empty or contains negative values.\r\n */\r\nexport function geometricMean(values: number[]): number {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute geometric mean of an empty array')\r\n }\r\n for (const v of values) {\r\n if (v < 0) {\r\n throw new RangeError('Values must be non-negative for geometric mean')\r\n }\r\n }\r\n const logSum = values.reduce((acc, v) => (v === 0 ? acc : acc + Math.log(v)), 0)\r\n if (logSum === -Infinity) return 0\r\n return Math.exp(logSum / values.length)\r\n}\r\n\r\n/**\r\n * Computes the number of combinations (n choose k) — selecting k items from n without order.\r\n *\r\n * @example combinations(5, 2) // 10\r\n *\r\n * @param n - Total items.\r\n * @param k - Chosen items.\r\n * @returns The number of combinations.\r\n * @throws {RangeError} If n < k, or either is negative / non-integer.\r\n */\r\nexport function combinations(n: number, k: number): number {\r\n if (!Number.isInteger(n) || !Number.isInteger(k)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (n < 0 || k < 0) {\r\n throw new RangeError('Arguments must be non-negative')\r\n }\r\n if (k > n) {\r\n throw new RangeError('k must not exceed n')\r\n }\r\n if (k === 0 || k === n) return 1\r\n const r = Math.min(k, n - k)\r\n let result = 1\r\n for (let i = 1; i <= r; i++) {\r\n result = (result * (n - r + i)) / i\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Computes the number of permutations — selecting k items from n in order.\r\n *\r\n * @example permutations(5, 2) // 20\r\n *\r\n * @param n - Total items.\r\n * @param k - Chosen items.\r\n * @returns The number of permutations.\r\n * @throws {RangeError} If n < k, or either is negative / non-integer.\r\n */\r\nexport function permutations(n: number, k: number): number {\r\n if (!Number.isInteger(n) || !Number.isInteger(k)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (n < 0 || k < 0) {\r\n throw new RangeError('Arguments must be non-negative')\r\n }\r\n if (k > n) {\r\n throw new RangeError('k must not exceed n')\r\n }\r\n let result = 1\r\n for (let i = n; i > n - k; i--) {\r\n result *= i\r\n }\r\n return result\r\n}\r\n","import { formatCurrency } from '../math/index.js'\r\n\r\nconst WORD_SPLIT_RE = /[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|\\d|\\b)|\\d+/g\r\n\r\nfunction splitWords(str: string): string[] {\r\n return str.match(WORD_SPLIT_RE) ?? []\r\n}\r\n\r\n/**\r\n * Capitalizes the first character and lowercases the rest.\r\n */\r\nexport function capitalize(str: string): string {\r\n if (str.length === 0) return str\r\n return str[0]!.toUpperCase() + str.slice(1).toLowerCase()\r\n}\r\n\r\n/**\r\n * Converts a string to camelCase.\r\n */\r\nexport function camelCase(str: string): string {\r\n const words = splitWords(str)\r\n if (words.length === 0) return ''\r\n const [firstWord, ...rest] = words\r\n return firstWord!.toLowerCase() + rest.map(w => w[0]!.toUpperCase() + w.slice(1).toLowerCase()).join('')\r\n}\r\n\r\n/**\r\n * Converts a string to kebab-case.\r\n */\r\nexport function kebabCase(str: string): string {\r\n return splitWords(str).map(w => w.toLowerCase()).join('-')\r\n}\r\n\r\n/**\r\n * Converts a string to snake_case.\r\n */\r\nexport function snakeCase(str: string): string {\r\n return splitWords(str).map(w => w.toLowerCase()).join('_')\r\n}\r\n\r\n/**\r\n * Converts a string to PascalCase.\r\n */\r\nexport function pascalCase(str: string): string {\r\n return splitWords(str).map(w => w[0]!.toUpperCase() + w.slice(1).toLowerCase()).join('')\r\n}\r\n\r\n/**\r\n * Truncates a string to the specified length, appending a suffix (default \"...\").\r\n */\r\nexport function truncate(str: string, maxLength: number, suffix = '...'): string {\r\n if (str.length <= maxLength) return str\r\n return str.slice(0, Math.max(0, maxLength - suffix.length)) + suffix\r\n}\r\n\r\n/**\r\n * Simple string interpolation using {{key}} syntax.\r\n *\r\n * @example template(\"Hello {{name}}\", { name: \"world\" }) // => \"Hello world\"\r\n */\r\nexport function template(str: string, data: Record<string, string | number>): string {\r\n return str.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key: string) => {\r\n const value = data[key]\r\n return value !== undefined ? String(value) : `{{${key}}}`\r\n })\r\n}\r\n\r\n/**\r\n * Generates a UUID v4 string.\r\n * Uses crypto.randomUUID when available, falls back to manual implementation.\r\n */\r\nexport function uuid(): string {\r\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\r\n return crypto.randomUUID()\r\n }\r\n const hex = '0123456789abcdef'\r\n const chars: string[] = []\r\n for (let i = 0; i < 36; i++) {\r\n if (i === 8 || i === 13 || i === 18 || i === 23) {\r\n chars.push('-')\r\n } else if (i === 14) {\r\n chars.push('4')\r\n } else if (i === 19) {\r\n chars.push(hex[Math.floor(Math.random() * 4) + 8]!)\r\n } else {\r\n chars.push(hex[Math.floor(Math.random() * 16)]!)\r\n }\r\n }\r\n return chars.join('')\r\n}\r\n\r\n/**\r\n * Generates a short random ID with configurable length and alphabet.\r\n *\r\n * @default size = 21, alphabet = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-\"\r\n */\r\nexport function nanoid(size = 21, alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-'): string {\r\n const len = alphabet.length\r\n let result = ''\r\n for (let i = 0; i < size; i++) {\r\n result += alphabet[Math.floor(Math.random() * len)]!\r\n }\r\n return result\r\n}\r\n\r\nconst HTML_ESCAPE_MAP: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": ''',\r\n}\r\n\r\nconst HTML_UNESCAPE_MAP: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '"': '\"',\r\n ''': \"'\",\r\n ''': \"'\",\r\n}\r\n\r\n/**\r\n * Escapes HTML special characters (&, <, >, \", ').\r\n */\r\nexport function escapeHtml(str: string): string {\r\n return str.replace(/[&<>\"']/g, ch => HTML_ESCAPE_MAP[ch] ?? ch)\r\n}\r\n\r\n/**\r\n * Unescapes common HTML entities.\r\n */\r\nexport function unescapeHtml(str: string): string {\r\n return str.replace(/&(?:amp|lt|gt|quot|#39|#x27);/g, entity => HTML_UNESCAPE_MAP[entity] ?? entity)\r\n}\r\n\r\n/**\r\n * Removes whitespace from both ends of a string.\r\n */\r\nexport function trim(str: string): string {\r\n return str.trim()\r\n}\r\n\r\n/**\r\n * Removes whitespace from the start of a string.\r\n */\r\nexport function trimStart(str: string): string {\r\n return str.trimStart()\r\n}\r\n\r\n/**\r\n * Removes whitespace from the end of a string.\r\n */\r\nexport function trimEnd(str: string): string {\r\n return str.trimEnd()\r\n}\r\n\r\n/**\r\n * Pads a string to the given length by adding characters to both sides.\r\n */\r\nexport function pad(str: string, length: number, char = ' '): string {\r\n const totalPadding = Math.max(0, length - str.length)\r\n const leftPad = Math.floor(totalPadding / 2)\r\n const rightPad = totalPadding - leftPad\r\n return char.repeat(leftPad) + str + char.repeat(rightPad)\r\n}\r\n\r\n/**\r\n * Pads the start of a string to the given length.\r\n */\r\nexport function padStart(str: string, length: number, char = ' '): string {\r\n return str.padStart(length, char)\r\n}\r\n\r\n/**\r\n * Pads the end of a string to the given length.\r\n */\r\nexport function padEnd(str: string, length: number, char = ' '): string {\r\n return str.padEnd(length, char)\r\n}\r\n\r\n/**\r\n * Reverses a string.\r\n */\r\nexport function reverse(str: string): string {\r\n return str.split('').reverse().join('')\r\n}\r\n\r\n/**\r\n * Splits a string into words.\r\n */\r\nexport function words(str: string): string[] {\r\n return splitWords(str)\r\n}\r\n\r\n/**\r\n * Converts a string to a URL-friendly slug.\r\n */\r\nexport function slugify(str: string): string {\r\n return str\r\n .toLowerCase()\r\n .replace(/[^\\w\\s-]/g, '')\r\n .replace(/[\\s_]+/g, '-')\r\n .replace(/-+/g, '-')\r\n .replace(/^-+/, '')\r\n .replace(/-+$/, '')\r\n}\r\n\r\n/**\r\n * Counts occurrences of a substring in a string.\r\n */\r\nexport function countOccurrences(str: string, substring: string): number {\r\n if (substring.length === 0 || str.length === 0) return 0\r\n let count = 0\r\n let pos = 0\r\n while ((pos = str.indexOf(substring, pos)) !== -1) {\r\n count++\r\n pos += substring.length\r\n }\r\n return count\r\n}\r\n\r\n/**\r\n * Computes the Levenshtein distance between two strings.\r\n * Uses iterative DP with O(min(m,n)) space.\r\n */\r\nexport function levenshtein(a: string, b: string): number {\r\n const an = a.length\r\n const bn = b.length\r\n if (an === 0) return bn\r\n if (bn === 0) return an\r\n if (an < bn) return levenshtein(b, a)\r\n\r\n let prev = new Uint32Array(bn + 1)\r\n let curr = new Uint32Array(bn + 1)\r\n for (let j = 0; j <= bn; j++) prev[j] = j\r\n\r\n for (let i = 1; i <= an; i++) {\r\n curr[0] = i\r\n for (let j = 1; j <= bn; j++) {\r\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\r\n curr[j] = Math.min(\r\n prev[j]! + 1,\r\n curr[j - 1]! + 1,\r\n prev[j - 1]! + cost,\r\n )\r\n }\r\n ;[prev, curr] = [curr, prev]\r\n }\r\n return prev[bn]!\r\n}\r\n\r\n/**\r\n * Performs a simple fuzzy match: checks if all characters of query\r\n * appear in str in order (case-insensitive).\r\n */\r\nexport function fuzzyMatch(str: string, query: string): boolean {\r\n if (query.length === 0) return true\r\n if (str.length === 0) return false\r\n const sl = str.toLowerCase()\r\n const ql = query.toLowerCase()\r\n let si = 0\r\n for (let qi = 0; qi < ql.length; qi++) {\r\n si = sl.indexOf(ql[qi]!, si)\r\n if (si === -1) return false\r\n si++\r\n }\r\n return true\r\n}\r\n\r\n/**\r\n * Masks parts of a string, useful for data compliance (PDPA/GDPR).\r\n *\r\n * @example maskString('08123456789') // \"0812****789\"\r\n * @example maskString('hello@email.com') // \"h***@e***.com\"\r\n * @example maskString('1234567890', { start: 0, end: 4, char: '#' }) // \"####567890\"\r\n */\r\nexport function maskString(\r\n str: string,\r\n options?: {\r\n start?: number\r\n end?: number\r\n char?: string\r\n },\r\n): string {\r\n if (str.length === 0) return str\r\n const maskChar = options?.char ?? '*'\r\n const start = options?.start ?? Math.ceil(str.length * 0.25)\r\n const end = options?.end ?? Math.floor(str.length * 0.75)\r\n\r\n if (start >= end || start < 0) return str\r\n const clampedStart = Math.max(0, start)\r\n const clampedEnd = Math.min(str.length, end)\r\n return (\r\n str.slice(0, clampedStart) +\r\n maskChar.repeat(clampedEnd - clampedStart) +\r\n str.slice(clampedEnd)\r\n )\r\n}\r\n\r\n// ─── Indonesian Locale Utilities ────────────────────────\r\n\r\nconst SATUAN = ['', 'satu', 'dua', 'tiga', 'empat', 'lima', 'enam', 'tujuh', 'delapan', 'sembilan'] as const\r\nconst BELASAN = ['sepuluh', 'sebelas', 'dua belas', 'tiga belas', 'empat belas', 'lima belas', 'enam belas', 'tujuh belas', 'delapan belas', 'sembilan belas'] as const\r\nconst PULUHAN = ['', '', 'dua puluh', 'tiga puluh', 'empat puluh', 'lima puluh', 'enam puluh', 'tujuh puluh', 'delapan puluh', 'sembilan puluh'] as const\r\n\r\nfunction _terbilang(n: number): string {\r\n if (n < 0) return 'minus ' + _terbilang(-n)\r\n if (n === 0) return 'nol'\r\n if (n < 10) return SATUAN[n]!\r\n if (n < 20) return n === 10 ? 'sepuluh' : n === 11 ? 'sebelas' : BELASAN[n - 10]!\r\n if (n < 100) {\r\n const pul = Math.floor(n / 10)\r\n const sat = n % 10\r\n return PULUHAN[pul]! + (sat > 0 ? ' ' + SATUAN[sat]! : '')\r\n }\r\n if (n < 1000) {\r\n const ratus = Math.floor(n / 100)\r\n const sis = n % 100\r\n const ratusStr = ratus === 1 ? 'seratus' : SATUAN[ratus]! + ' ratus'\r\n return ratusStr + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n if (n < 1_000_000) {\r\n const rib = Math.floor(n / 1000)\r\n const sis = n % 1000\r\n const ribStr = rib === 1 ? 'seribu' : _terbilang(rib) + ' ribu'\r\n return ribStr + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n if (n < 1_000_000_000) {\r\n const jut = Math.floor(n / 1_000_000)\r\n const sis = n % 1_000_000\r\n return _terbilang(jut) + ' juta' + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n if (n < 1_000_000_000_000) {\r\n const mil = Math.floor(n / 1_000_000_000)\r\n const sis = n % 1_000_000_000\r\n return _terbilang(mil) + ' miliar' + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n }\r\n const tril = Math.floor(n / 1_000_000_000_000)\r\n const sis = n % 1_000_000_000_000\r\n return _terbilang(tril) + ' triliun' + (sis > 0 ? ' ' + _terbilang(sis) : '')\r\n}\r\n\r\n/**\r\n * Converts a number to Indonesian words (terbilang).\r\n *\r\n * @example terbilang(1500000) // \"satu juta lima ratus ribu\"\r\n * @example terbilang(2024) // \"dua ribu dua puluh empat\"\r\n * @example terbilang(11) // \"sebelas\"\r\n * @example terbilang(100) // \"seratus\"\r\n */\r\nexport function terbilang(value: number): string {\r\n if (!Number.isFinite(value)) throw new RangeError('Input must be a finite number')\r\n if (value > Number.MAX_SAFE_INTEGER) throw new RangeError('Input terlalu besar')\r\n return _terbilang(Math.floor(Math.abs(value)))\r\n}\r\n\r\n/**\r\n * Formats a number as Indonesian Rupiah string.\r\n *\r\n * @example formatRupiah(1500000) // \"Rp1.500.000\"\r\n * @example formatRupiah(1500000, { notation: 'compact' }) // \"Rp1,5 jt\"\r\n */\r\nexport function formatRupiah(\r\n value: number,\r\n options?: { notation?: 'standard' | 'compact' },\r\n): string {\r\n return formatCurrency(value, { locale: 'id-ID', currency: 'IDR', notation: options?.notation })\r\n}\r\n\r\n/**\r\n * Formats a byte count into a human-readable string.\r\n *\r\n * @example formatBytes(1024) // \"1 KB\"\r\n * @example formatBytes(1536) // \"1.5 KB\"\r\n * @example formatBytes(1048576) // \"1 MB\"\r\n * @example formatBytes(0) // \"0 B\"\r\n */\r\nexport function formatBytes(bytes: number, options?: { decimals?: number }): string {\r\n if (bytes === 0) return '0 B'\r\n if (!Number.isFinite(bytes) || bytes < 0) return '0 B'\r\n const k = 1024\r\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']\r\n const i = Math.floor(Math.log(bytes) / Math.log(k))\r\n const dm = options?.decimals ?? (i === 0 ? 0 : 1)\r\n const index = Math.min(i, sizes.length - 1)\r\n return parseFloat((bytes / Math.pow(k, index)).toFixed(dm)) + ' ' + sizes[index]\r\n}\r\n\r\n/**\r\n * Generates a random alphanumeric string of the specified length.\r\n *\r\n * @example randomString() // \"a3F8k2...\"\r\n * @example randomString(8) // \"X7j2K9mQ\"\r\n */\r\nexport function randomString(length: number = 16): string {\r\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\r\n let result = ''\r\n for (let i = 0; i < length; i++) {\r\n result += chars[Math.floor(Math.random() * chars.length)]\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Returns a random boolean value.\r\n *\r\n * @example randomBoolean() // true or false\r\n */\r\nexport function randomBoolean(): boolean {\r\n return Math.random() >= 0.5\r\n}\r\n\r\n/**\r\n * Basic English pluralization helper. Adds 's' or 'es' based on simple rules.\r\n *\r\n * @example pluralize(1, 'apple') // \"apple\"\r\n * @example pluralize(3, 'apple') // \"apples\"\r\n * @example pluralize(0, 'box') // \"boxes\"\r\n * @example pluralize(1, 'box') // \"box\"\r\n */\r\nexport function pluralize(count: number, singular: string): string {\r\n if (count === 1) return singular\r\n const last = singular[singular.length - 1]\r\n const lastTwo = singular.slice(-2)\r\n if (last === 's' || last === 'x' || last === 'z' || lastTwo === 'ch' || lastTwo === 'sh') {\r\n return singular + 'es'\r\n }\r\n if (last === 'y' && singular.length > 2 && !'aeiou'.includes(singular[singular.length - 2]!)) {\r\n return singular.slice(0, -1) + 'ies'\r\n }\r\n return singular + 's'\r\n}\r\n\r\n/**\r\n * Removes all HTML tags from a string, including script and style content.\r\n *\r\n * @example stripHtml('<p>Hello <b>world</b></p>') // \"Hello world\"\r\n * @example stripHtml('<script>alert(\"x\")</script>hi') // \"hi\"\r\n */\r\nexport function stripHtml(str: string): string {\r\n return str.replace(/<[^>]*>/g, '')\r\n}\r\n\r\n/**\r\n * Truncates a string by word count, appending a suffix when truncated.\r\n *\r\n * @example truncateWords('hello world foo bar', 2) // \"hello world...\"\r\n * @example truncateWords('hello world foo bar', 2, '…') // \"hello world…\"\r\n */\r\nexport function truncateWords(str: string, count: number, suffix = '...'): string {\r\n const words = str.split(/\\s+/).filter(Boolean)\r\n if (words.length <= count) return str\r\n return words.slice(0, count).join(' ') + suffix\r\n}\r\n\r\n/**\r\n * Checks if a string is a palindrome, ignoring case, spaces, and punctuation.\r\n *\r\n * @example isPalindrome('A man, a plan, a canal: Panama') // true\r\n * @example isPalindrome('race a car') // false\r\n */\r\nexport function isPalindrome(str: string): boolean {\r\n const cleaned = str.replace(/[^a-zA-Z0-9]/g, '').toLowerCase()\r\n if (cleaned.length <= 1) return true\r\n let left = 0\r\n let right = cleaned.length - 1\r\n while (left < right) {\r\n if (cleaned[left] !== cleaned[right]) return false\r\n left++\r\n right--\r\n }\r\n return true\r\n}\r\n\r\n/**\r\n * Checks if two strings are anagrams, ignoring case and spaces.\r\n *\r\n * @example isAnagram('listen', 'silent') // true\r\n * @example isAnagram('hello', 'world') // false\r\n */\r\nexport function isAnagram(str1: string, str2: string): boolean {\r\n const clean = (s: string) => s.replace(/\\s/g, '').toLowerCase().split('').sort().join('')\r\n return clean(str1) === clean(str2)\r\n}\r\n\r\n/**\r\n * Computes the Dice coefficient similarity between two strings (0-1).\r\n *\r\n * @example similarity('hello', 'hallo') // 0.666...\r\n * @example similarity('abc', 'xyz') // 0\r\n */\r\nexport function similarity(a: string, b: string): number {\r\n if (a === b) return 1\r\n if (a.length < 2 || b.length < 2) return 0\r\n const bigrams = new Map<string, number>()\r\n for (let i = 0; i < a.length - 1; i++) {\r\n const bg = a.slice(i, i + 2)\r\n bigrams.set(bg, (bigrams.get(bg) ?? 0) + 1)\r\n }\r\n let intersection = 0\r\n for (let i = 0; i < b.length - 1; i++) {\r\n const bg = b.slice(i, i + 2)\r\n const count = bigrams.get(bg) ?? 0\r\n if (count > 0) {\r\n bigrams.set(bg, count - 1)\r\n intersection++\r\n }\r\n }\r\n return (2 * intersection) / (a.length + b.length - 2)\r\n}\r\n\r\n/**\r\n * Removes common leading whitespace from each line in a multi-line string.\r\n *\r\n * @example dedent(' hello\\n world') // \"hello\\nworld\"\r\n * @example dedent(' foo\\n bar') // \"foo\\n bar\"\r\n */\r\nexport function dedent(str: string): string {\r\n const lines = str.split('\\n')\r\n if (lines.length === 0) return str\r\n const indent = lines.reduce<number | null>((min, line) => {\r\n if (line.trim().length === 0) return min\r\n const leading = line.match(/^[ \\t]*/)?.[0]?.length ?? 0\r\n return min === null ? leading : Math.min(min, leading)\r\n }, null)\r\n if (indent === null || indent === 0) return str\r\n return lines.map(line => line.slice(indent)).join('\\n')\r\n}\r\n\r\n/**\r\n * Counts the number of words in a string, handling multiple spaces and punctuation.\r\n *\r\n * @example wordCount('hello world') // 2\r\n * @example wordCount(' hello world! ') // 2\r\n */\r\nexport function wordCount(str: string): number {\r\n const match = str.match(/[a-zA-Z0-9]+(?:['\\u2019][a-zA-Z]+)?/g)\r\n return match ? match.length : 0\r\n}\r\n\r\n/**\r\n * Swaps the case of each character in a string (upper to lower, lower to upper).\r\n *\r\n * @example swapCase('Hello World') // \"hELLO wORLD\"\r\n * @example swapCase('ABC123') // \"abc123\"\r\n */\r\nexport function swapCase(str: string): string {\r\n let result = ''\r\n for (let i = 0; i < str.length; i++) {\r\n const ch = str[i]!\r\n if (ch >= 'a' && ch <= 'z') {\r\n result += ch.toUpperCase()\r\n } else if (ch >= 'A' && ch <= 'Z') {\r\n result += ch.toLowerCase()\r\n } else {\r\n result += ch\r\n }\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Converts a string to COBOL / SCREAMING_SNAKE_CASE.\r\n *\r\n * @example toCobolCase('helloWorld') // \"HELLO_WORLD\"\r\n * @example toCobolCase('getUserByID') // \"GET_USER_BY_ID\"\r\n */\r\nexport function toCobolCase(str: string): string {\r\n const words = splitWords(str)\r\n return words.map(w => w.toUpperCase()).join('_')\r\n}\r\n\r\n/**\r\n * Counts character frequency in a string.\r\n *\r\n * @example charCount('hello') // { h: 1, e: 1, l: 2, o: 1 }\r\n * @example charCount('aabb') // { a: 2, b: 2 }\r\n */\r\nexport function charCount(str: string): Record<string, number> {\r\n const result: Record<string, number> = {}\r\n for (let i = 0; i < str.length; i++) {\r\n const ch = str[i]!\r\n result[ch] = (result[ch] ?? 0) + 1\r\n }\r\n return result\r\n}\r\n"],"mappings":"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","names":["words","sis"]}
|