{"version":3,"sources":["../../src/currency/format.ts","../../src/currency/parse.ts","../../src/currency/parse-compact.ts","../../src/currency/words.ts","../../src/currency/utils.ts","../../src/currency/types.ts","../../src/currency/calc.ts","../../src/currency/validate.ts"],"names":["prefix","result","roundToClean"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Currency formatting utilities for Indonesian Rupiah.\n *\n * @module currency/format\n * @packageDocumentation\n */\n\nimport type { CompactOptions, PercentageOptions, RupiahOptions } from './types';\n\n/**\n * Formats a number as Indonesian Rupiah currency.\n *\n * Provides flexible formatting options including symbol display,\n * decimal places, and custom separators.\n *\n * @param amount - The amount to format\n * @param options - Formatting options\n * @returns Formatted Rupiah string\n *\n * @example\n * Basic formatting:\n * ```typescript\n * formatRupiah(1500000); // 'Rp 1.500.000'\n * ```\n *\n * @example\n * With decimals:\n * ```typescript\n * formatRupiah(1500000.50, { decimal: true }); // 'Rp 1.500.000,50'\n * ```\n *\n * @example\n * Without symbol:\n * ```typescript\n * formatRupiah(1500000, { symbol: false }); // '1.500.000'\n * ```\n *\n * @example\n * Custom separators:\n * ```typescript\n * formatRupiah(1500000, { separator: ',' }); // 'Rp 1,500,000'\n * ```\n *\n * @public\n */\nexport function formatRupiah(amount: number, options?: RupiahOptions): string {\n  const {\n    symbol = true,\n    decimal = false,\n    separator = '.',\n    decimalSeparator = ',',\n    spaceAfterSymbol = true,\n  } = options || {};\n\n  // Default precision: 2 for decimals, 0 otherwise\n  const precision =\n    options?.precision !== undefined ? options.precision : decimal ? 2 : 0;\n\n  const isNegative = amount < 0;\n  const absAmount = Math.abs(amount);\n\n  let result: string;\n\n  if (decimal) {\n    const factor = Math.pow(10, precision);\n    const rounded = Math.round(absAmount * factor) / factor;\n\n    if (precision > 0) {\n      const [intPart, decPart] = rounded.toFixed(precision).split('.');\n      const formattedInt = intPart.replace(/\\B(?=(\\d{3})+(?!\\d))/g, separator);\n      result = `${formattedInt}${decimalSeparator}${decPart}`;\n    } else {\n      const intPart = rounded.toString();\n      result = intPart.replace(/\\B(?=(\\d{3})+(?!\\d))/g, separator);\n    }\n  } else {\n    const intAmount = Math.floor(absAmount);\n    result = intAmount.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, separator);\n  }\n\n  if (symbol) {\n    const space = spaceAfterSymbol ? ' ' : '';\n    if (isNegative) {\n      result = `-Rp${space}${result}`;\n    } else {\n      result = `Rp${space}${result}`;\n    }\n  } else if (isNegative) {\n    result = `-${result}`;\n  }\n\n  return result;\n}\n\n/**\n * Formats a number in compact Indonesian format.\n *\n * Uses Indonesian units: ribu, juta, miliar, triliun.\n * Follows Indonesian grammar rules (e.g., \"1 juta\" not \"1,0 juta\").\n *\n * @param amount - The amount to format\n * @param options - Compact formatting options\n * @returns Compact formatted string\n *\n * @example\n * Millions:\n * ```typescript\n * formatCompact(1500000); // 'Rp 1,5 juta'\n * formatCompact(1000000); // 'Rp 1 juta'\n * ```\n *\n * @example\n * Thousands:\n * ```typescript\n * formatCompact(500000); // 'Rp 500 ribu'\n * ```\n *\n * @example\n * Small numbers:\n * ```typescript\n * formatCompact(1500); // 'Rp 1.500'\n * ```\n *\n * @example\n * Without symbol:\n * ```typescript\n * formatCompact(1500000, { symbol: false }); // '1,5 juta'\n * ```\n *\n * @public\n */\nexport function formatCompact(\n  amount: number,\n  options?: CompactOptions\n): string {\n  const { symbol = true, spaceAfterSymbol = true } = options || {};\n\n  const isNegative = amount < 0;\n  const abs = Math.abs(amount);\n\n  let result: string;\n\n  if (abs >= 1_000_000_000_000) {\n    result = formatCompactValue(abs / 1_000_000_000_000, 'triliun');\n  } else if (abs >= 1_000_000_000) {\n    result = formatCompactValue(abs / 1_000_000_000, 'miliar');\n  } else if (abs >= 1_000_000) {\n    result = formatCompactValue(abs / 1_000_000, 'juta');\n  } else if (abs >= 100_000) {\n    result = formatCompactValue(abs / 1000, 'ribu');\n  } else if (abs >= 1_000) {\n    result = abs.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, '.');\n  } else {\n    result = abs.toString();\n  }\n\n  if (symbol) {\n    const space = spaceAfterSymbol ? ' ' : '';\n    if (isNegative) {\n      result = `-Rp${space}${result}`;\n    } else {\n      result = `Rp${space}${result}`;\n    }\n  } else if (isNegative) {\n    result = `-${result}`;\n  }\n\n  return result;\n}\n\n/**\n * Formats a value with Indonesian unit, applying grammar rules.\n *\n * Automatically removes trailing \".0\" to follow proper Indonesian grammar.\n * For example: \"1 juta\" instead of \"1,0 juta\".\n *\n * @param value - The numeric value to format\n * @param unit - The Indonesian unit (ribu, juta, miliar, triliun)\n * @returns Formatted string with unit\n * @internal\n */\nfunction formatCompactValue(value: number, unit: string): string {\n  const rounded = Math.round(value * 10) / 10;\n\n  if (rounded % 1 === 0) {\n    return `${rounded.toFixed(0)} ${unit}`;\n  }\n\n  return `${rounded.toString().replace('.', ',')} ${unit}`;\n}\n\n/**\n * Formats a number as an Indonesian percentage string.\n *\n * Uses Indonesian conventions: comma as decimal separator.\n *\n * @param value - The decimal value (0-1) to format as percentage\n * @param options - Formatting options\n * @returns Formatted percentage string\n *\n * @example\n * Basic formatting:\n * ```typescript\n * formatPercentage(0.115); // '11,5%'\n * ```\n *\n * @example\n * Custom decimals:\n * ```typescript\n * formatPercentage(0.1152, { decimals: 2 }); // '11,52%'\n * ```\n *\n * @example\n * Without symbol:\n * ```typescript\n * formatPercentage(0.11, { symbol: false }); // '11'\n * ```\n *\n * @example\n * isPercentage mode (value already in percentage form):\n * ```typescript\n * formatPercentage(11.5, { isPercentage: true }); // '11,5%'\n * ```\n *\n * @public\n */\nexport function formatPercentage(\n  value: number,\n  options?: PercentageOptions\n): string {\n  const {\n    decimals = 1,\n    symbol = true,\n    decimalSeparator = ',',\n    isPercentage = false,\n  } = options || {};\n\n  if (!Number.isFinite(value) || Number.isNaN(value)) {\n    const prefix = value < 0 ? '-' : '';\n    return symbol ? `${prefix}0%` : `${prefix}0`;\n  }\n\n  const percentageValue = isPercentage ? value : value * 100;\n\n  const isNegative = percentageValue < 0;\n  const absValue = Math.abs(percentageValue);\n\n  const factor = Math.pow(10, decimals);\n  const rounded = Math.round(absValue * factor) / factor;\n\n  let formatted: string;\n  if (decimals === 0) {\n    formatted = Math.round(rounded).toString();\n  } else {\n    formatted = rounded.toFixed(decimals).replace('.', decimalSeparator);\n\n    if (!formatted.includes(decimalSeparator)) {\n      formatted = rounded.toString();\n    } else if (formatted.endsWith(`${decimalSeparator}0`)) {\n      formatted = formatted.slice(0, -2);\n    }\n  }\n\n  const prefix = isNegative ? '-' : '';\n  const suffix = symbol ? '%' : '';\n\n  return `${prefix}${formatted}${suffix}`;\n}\n","/**\n * Currency parsing utilities for Indonesian Rupiah.\n *\n * @module currency/parse\n * @packageDocumentation\n */\n\n/**\n * Parses a formatted Rupiah string back to a number.\n *\n * Handles multiple formats:\n * - Standard: \"Rp 1.500.000\"\n * - No symbol: \"1.500.000\"\n * - With decimals: \"Rp 1.500.000,50\"\n * - Compact: \"Rp 1,5 juta\", \"Rp 500 ribu\"\n *\n * @param formatted - The formatted Rupiah string to parse\n * @returns Parsed number, or null if invalid\n *\n * @example\n * Standard format:\n * ```typescript\n * parseRupiah('Rp 1.500.000'); // 1500000\n * ```\n *\n * @example\n * With decimals:\n * ```typescript\n * parseRupiah('Rp 1.500.000,50'); // 1500000.50\n * ```\n *\n * @example\n * Compact format:\n * ```typescript\n * parseRupiah('Rp 1,5 juta'); // 1500000\n * parseRupiah('Rp 500 ribu'); // 500000\n * ```\n *\n * @example\n * Invalid input:\n * ```typescript\n * parseRupiah('invalid'); // null\n * ```\n *\n * @public\n */\nexport function parseRupiah(formatted: string): number | null {\n  if (!formatted || typeof formatted !== 'string') {\n    return null;\n  }\n\n  const cleaned = formatted.trim().toLowerCase();\n\n  // Check for compact units (juta, ribu, miliar, triliun)\n  const compactUnits = {\n    triliun: 1_000_000_000_000,\n    miliar: 1_000_000_000,\n    juta: 1_000_000,\n    ribu: 1_000,\n  };\n\n  for (const [unit, multiplier] of Object.entries(compactUnits)) {\n    if (cleaned.includes(unit)) {\n      const match = cleaned.match(/(-?\\d+[,.]?\\d*)/);\n      if (match) {\n        const num = parseFloat(match[1].replace(',', '.'));\n        return num * multiplier;\n      }\n    }\n  }\n\n  // Standard format: remove 'Rp' and spaces\n  let numStr = cleaned.replace(/rp/gi, '').trim();\n\n  const hasDot = numStr.includes('.');\n  const hasComma = numStr.includes(',');\n\n  if (hasDot && hasComma) {\n    // Determine format based on last separator position\n    // Indonesian: 1.500.000,50 vs International: 1,500,000.50\n    const lastDot = numStr.lastIndexOf('.');\n    const lastComma = numStr.lastIndexOf(',');\n\n    if (lastComma > lastDot) {\n      numStr = numStr.replace(/\\./g, '').replace(',', '.');\n    } else {\n      numStr = numStr.replace(/,/g, '');\n    }\n  } else if (hasComma) {\n    const parts = numStr.split(',');\n    // Decimal if only 1-2 digits after comma\n    if (parts.length === 2 && parts[1].length <= 2) {\n      numStr = numStr.replace(',', '.');\n    } else {\n      numStr = numStr.replace(/,/g, '');\n    }\n  } else if (hasDot) {\n    const parts = numStr.split('.');\n    // If not decimal format, remove dots (thousands separator)\n    if (parts.length > 2 || (parts.length === 2 && parts[1].length > 2)) {\n      numStr = numStr.replace(/\\./g, '');\n    }\n  }\n\n  const parsed = parseFloat(numStr);\n  return isNaN(parsed) ? null : parsed;\n}\n","/**\n * Compact amount parser for Indonesian Rupiah shorthand.\n *\n * @module currency/parse-compact\n * @packageDocumentation\n */\n\n/**\n * Multiplier shorthands accepted by {@link parseCompact}.\n *\n * Keys are matched case-insensitively as a single trailing word\n * (letters only) on the input. Values are the numeric multiplier\n * applied to the prefix number. Linguistic, not regulatory — the\n * Indonesian vocabulary is stable.\n *\n * @internal\n */\nconst COMPACT_MULTIPLIERS: Readonly<Record<string, number>> = {\n  triliun: 1_000_000_000_000,\n  T: 1_000_000_000_000,\n  miliar: 1_000_000_000,\n  milyar: 1_000_000_000,\n  M: 1_000_000_000,\n  juta: 1_000_000,\n  jt: 1_000_000,\n  ribu: 1_000,\n  rb: 1_000,\n  k: 1_000,\n};\n\n/**\n * Returns the multiplier for a lowercased trailing word, picking the\n * longest matching key so that e.g. \"milyar\" is never misread as \"m\".\n *\n * @param word - Lowercased trailing word from the input\n * @returns The multiplier, or `null` if no shorthand matches\n * @internal\n */\nfunction resolveMultiplier(word: string): number | null {\n  // Keys sorted by descending length so the longest match wins.\n  const keys = Object.keys(COMPACT_MULTIPLIERS).sort(\n    (a, b) => b.length - a.length\n  );\n\n  for (const key of keys) {\n    if (word === key.toLowerCase()) {\n      return COMPACT_MULTIPLIERS[key];\n    }\n  }\n\n  return null;\n}\n\n/**\n * Parses a compact Indonesian amount string into a number.\n *\n * Inverse of {@link formatCompact}. Accepts the common multiplier\n * shorthands (`rb`, `ribu`, `k`, `jt`, `juta`, `M`, `miliar`, `milyar`,\n * `T`, `triliun`) case-insensitively, with optional whitespace\n * between the number and the multiplier.\n *\n * Comma (`,`) is the decimal separator and dot (`.`) is the\n * thousands separator, matching the rest of the currency module.\n *\n * Returns `NaN` for any input that cannot be unambiguously parsed:\n * missing multiplier, unknown multiplier, garbage text, or empty\n * string. Bare numeric input (e.g. `\"1.5\"`, `\"1500\"`) is **not** a\n * compact amount and also returns `NaN`.\n *\n * @param input - The compact amount string to parse\n * @returns The parsed number, or `NaN` if the input is invalid\n *\n * @example\n * Basic parse with a juta multiplier:\n * ```ts\n * parseCompact('1,5jt'); // 1500000\n * ```\n *\n * @example\n * Long-form million and thousand:\n * ```ts\n * parseCompact('3 miliar'); // 3000000000\n * parseCompact('2rb');      // 2000\n * ```\n *\n * @example\n * Case-insensitive and whitespace-tolerant:\n * ```ts\n * parseCompact('  1,5JT  '); // 1500000\n * ```\n *\n * @example\n * Returns `NaN` for invalid input:\n * ```ts\n * parseCompact('abc'); // NaN\n * parseCompact('1500'); // NaN  (no multiplier)\n * ```\n *\n * @public\n */\nexport function parseCompact(input: string): number {\n  if (typeof input !== 'string') {\n    return Number.NaN;\n  }\n\n  const trimmed = input.trim();\n  if (trimmed.length === 0) {\n    return Number.NaN;\n  }\n\n  // Split into number part (prefix) and trailing multiplier word.\n  // The number part may contain digits, dots (thousands), and at most\n  // one comma (decimal). The multiplier is a single contiguous run of\n  // letters at the end, optionally separated from the number by\n  // whitespace.\n  const match = trimmed.match(/^([\\d.,]+)\\s*([A-Za-z]+)\\s*$/);\n  if (!match) {\n    return Number.NaN;\n  }\n\n  const [, rawNumber, rawWord] = match;\n  const word = rawWord.toLowerCase();\n\n  const multiplier = resolveMultiplier(word);\n  if (multiplier === null) {\n    return Number.NaN;\n  }\n\n  // Normalise Indonesian format: dot = thousands, comma = decimal.\n  // The regex above already ensures at most one comma; we only need\n  // to strip the thousand-separators and swap the decimal separator.\n  const hasComma = rawNumber.includes(',');\n  const normalised = hasComma\n    ? rawNumber.replace(/\\./g, '').replace(',', '.')\n    : rawNumber.replace(/\\./g, '');\n\n  return Number(normalised) * multiplier;\n}\n","/**\n * Convert numbers to Indonesian words (terbilang).\n *\n * @module currency/words\n * @packageDocumentation\n */\n\nimport type { WordOptions } from './types';\n\n/**\n * Basic Indonesian number words (0-9).\n * @internal\n */\nconst BASIC_NUMBERS = [\n  '',\n  'satu',\n  'dua',\n  'tiga',\n  'empat',\n  'lima',\n  'enam',\n  'tujuh',\n  'delapan',\n  'sembilan',\n];\n\n/**\n * Indonesian words for 10-19.\n * @internal\n */\nconst TEENS = [\n  'sepuluh',\n  'sebelas',\n  'dua belas',\n  'tiga belas',\n  'empat belas',\n  'lima belas',\n  'enam belas',\n  'tujuh belas',\n  'delapan belas',\n  'sembilan belas',\n];\n\n/**\n * Indonesian words for tens (20, 30, 40, etc).\n * @internal\n */\nconst TENS = [\n  '',\n  '',\n  'dua puluh',\n  'tiga puluh',\n  'empat puluh',\n  'lima puluh',\n  'enam puluh',\n  'tujuh puluh',\n  'delapan puluh',\n  'sembilan puluh',\n];\n\n/**\n * Converts a number to Indonesian words (terbilang).\n *\n * Supports numbers up to trillions (triliun).\n * Follows Indonesian language rules for number pronunciation.\n *\n * Special rules:\n * - 1 = \"satu\" in most cases, but \"se-\" for 100, 1000\n * - 11 = \"sebelas\" (not \"satu belas\")\n * - 100 = \"seratus\" (not \"satu ratus\")\n * - 1000 = \"seribu\" (not \"satu ribu\")\n *\n * @param amount - The number to convert\n * @param options - Conversion options\n * @returns Indonesian words representation\n *\n * @example\n * Basic numbers:\n * ```typescript\n * toWords(123); // 'seratus dua puluh tiga rupiah'\n * ```\n *\n * @example\n * Large numbers:\n * ```typescript\n * toWords(1500000); // 'satu juta lima ratus ribu rupiah'\n * ```\n *\n * @example\n * With options:\n * ```typescript\n * toWords(1500000, { uppercase: true });\n * // 'Satu juta lima ratus ribu rupiah'\n *\n * toWords(1500000, { withCurrency: false });\n * // 'satu juta lima ratus ribu'\n * ```\n *\n * @public\n */\nexport function toWords(amount: number, options?: WordOptions): string {\n  const {\n    uppercase = false,\n    withCurrency = true,\n    withDecimals = false,\n  } = options || {};\n\n  if (amount === 0) {\n    let result = 'nol';\n    if (withCurrency) result += ' rupiah';\n    return uppercase ? capitalize(result) : result;\n  }\n\n  const isNegative = amount < 0;\n  const absAmount = Math.abs(amount);\n  const intPart = Math.floor(absAmount);\n\n  let words = convertInteger(intPart);\n\n  if (isNegative) {\n    words = 'minus ' + words;\n  }\n\n  if (withCurrency) {\n    words += ' rupiah';\n  }\n\n  if (withDecimals) {\n    const decimalPart = Math.round((absAmount - intPart) * 100);\n    if (decimalPart > 0) {\n      words += ' koma ' + convertDecimal(decimalPart);\n    }\n  }\n\n  return uppercase ? capitalize(words) : words;\n}\n\n/**\n * Converts the integer part to Indonesian words.\n */\nfunction convertInteger(num: number): string {\n  if (num === 0) return 'nol';\n\n  let words = '';\n\n  const triliun = Math.floor(num / 1_000_000_000_000);\n  const miliar = Math.floor((num % 1_000_000_000_000) / 1_000_000_000);\n  const juta = Math.floor((num % 1_000_000_000) / 1_000_000);\n  const ribu = Math.floor((num % 1_000_000) / 1_000);\n  const sisa = num % 1_000;\n\n  if (triliun > 0) {\n    words += convertGroup(triliun) + ' triliun';\n  }\n\n  if (miliar > 0) {\n    if (words) words += ' ';\n    words += convertGroup(miliar) + ' miliar';\n  }\n\n  if (juta > 0) {\n    if (words) words += ' ';\n    words += convertGroup(juta) + ' juta';\n  }\n\n  if (ribu > 0) {\n    if (words) words += ' ';\n    words += ribu === 1 ? 'seribu' : convertGroup(ribu) + ' ribu';\n  }\n\n  if (sisa > 0) {\n    if (words) words += ' ';\n    words += convertGroup(sisa);\n  }\n\n  return words;\n}\n\n/**\n * Converts decimal part (0-99) to Indonesian words.\n */\nfunction convertDecimal(num: number): string {\n  if (num === 0) return '';\n  if (num < 10) return BASIC_NUMBERS[num];\n  if (num < 20) return TEENS[num - 10];\n\n  const tens = Math.floor(num / 10);\n  const ones = num % 10;\n\n  let result = TENS[tens];\n  if (ones > 0) {\n    result += ' ' + BASIC_NUMBERS[ones];\n  }\n\n  return result;\n}\n\n/**\n * Converts a group of 1-3 digits (0-999) to Indonesian words.\n *\n * @param num - Number to convert (0-999)\n * @returns Indonesian words for the number\n * @internal\n */\nfunction convertGroup(num: number): string {\n  if (num === 0) return '';\n\n  let result = '';\n\n  const hundreds = Math.floor(num / 100);\n  if (hundreds > 0) {\n    // Special rule: 100 = \"seratus\" not \"satu ratus\"\n    result = hundreds === 1 ? 'seratus' : BASIC_NUMBERS[hundreds] + ' ratus';\n  }\n\n  const remainder = num % 100;\n  if (remainder > 0) {\n    if (result) result += ' ';\n    result += convertTwoDigits(remainder);\n  }\n\n  return result;\n}\n\n/**\n * Converts numbers 1-99 to Indonesian words.\n *\n * @param num - Number to convert (1-99)\n * @returns Indonesian words for the number\n * @internal\n */\nfunction convertTwoDigits(num: number): string {\n  if (num === 0) return '';\n  if (num < 10) return BASIC_NUMBERS[num];\n  if (num >= 10 && num < 20) return TEENS[num - 10];\n\n  const tens = Math.floor(num / 10);\n  const ones = num % 10;\n\n  let result = TENS[tens];\n  if (ones > 0) {\n    result += ' ' + BASIC_NUMBERS[ones];\n  }\n\n  return result;\n}\n\n/**\n * Capitalizes the first letter of a string.\n *\n * @param str - String to capitalize\n * @returns String with first letter capitalized\n * @internal\n */\nfunction capitalize(str: string): string {\n  return str.charAt(0).toUpperCase() + str.slice(1);\n}\n","/**\n * Currency utility functions.\n *\n * @module currency/utils\n * @packageDocumentation\n */\n\nimport { formatRupiah } from './format';\nimport type { RoundUnit, RupiahOptions } from './types';\n\n/**\n * Rounds a number to a clean currency amount.\n *\n * Common use case: displaying approximate prices or budgets\n * in clean, rounded numbers.\n *\n * @param amount - The amount to round\n * @param unit - The unit to round to (default: 'ribu')\n * @returns Rounded amount\n *\n * @example\n * Round to thousands:\n * ```typescript\n * roundToClean(1234567, 'ribu'); // 1235000\n * ```\n *\n * @example\n * Round to hundred thousands:\n * ```typescript\n * roundToClean(1234567, 'ratus-ribu'); // 1200000\n * ```\n *\n * @example\n * Round to millions:\n * ```typescript\n * roundToClean(1234567, 'juta'); // 1000000\n * ```\n *\n * @public\n */\nexport function roundToClean(amount: number, unit: RoundUnit = 'ribu'): number {\n  const divisors: Record<RoundUnit, number> = {\n    ribu: 1000,\n    'ratus-ribu': 100000,\n    juta: 1000000,\n  };\n\n  const divisor = divisors[unit];\n\n  // Math.round handles both positive and negative numbers\n  return Math.round(amount / divisor) * divisor;\n}\n\n/**\n * Formats a number as Indonesian Rupiah in accounting style.\n * Negative numbers are wrapped in parentheses.\n *\n * @param amount - The amount to format\n * @param options - Formatting options\n * @returns Formatted accounting string\n *\n * @example\n * ```typescript\n * formatAccounting(-1500000); // '(Rp 1.500.000)'\n * ```\n */\nexport function formatAccounting(\n  amount: number,\n  options?: RupiahOptions\n): string {\n  const isNegative = amount < 0;\n  const formatted = formatRupiah(Math.abs(amount), options);\n\n  if (isNegative) {\n    return `(${formatted})`;\n  }\n\n  return formatted;\n}\n\n/**\n * Calculates tax (PPN) for a given amount.\n *\n * @param amount - The base amount\n * @param rate - The tax rate (e.g., 0.11 for 11% PPN)\n * @returns The calculated tax amount\n *\n * @example\n * ```typescript\n * calculateTax(1000000, 0.11); // 110000\n * ```\n */\nexport function calculateTax(amount: number, rate: number): number {\n  return amount * rate;\n}\n\n/**\n * Helper to ensure a string or number has the 'Rp ' prefix.\n * If already prefixed, it returns the input as is.\n *\n * @param amount - The amount or formatted string\n * @returns String with Rupiah prefix\n */\nexport function addRupiahSymbol(amount: string | number): string {\n  if (typeof amount === 'number') {\n    const formatted = amount.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, '.');\n    return `Rp ${formatted}`;\n  }\n\n  if (amount.trim().startsWith('Rp')) {\n    return amount.trim();\n  }\n\n  return `Rp ${amount.trim()}`;\n}\n","/**\n * Currency module types for Indonesian Rupiah utilities.\n *\n * @module currency/types\n * @packageDocumentation\n */\n\n/**\n * Options for formatting Rupiah currency.\n *\n * @example\n * Default formatting:\n * ```typescript\n * const options: RupiahOptions = {\n *   symbol: true,\n *   decimal: false,\n *   separator: '.',\n * };\n * formatRupiah(1500000, options); // 'Rp 1.500.000'\n * ```\n *\n * @example\n * With decimals:\n * ```typescript\n * const options: RupiahOptions = {\n *   symbol: true,\n *   decimal: true,\n *   precision: 2,\n * };\n * formatRupiah(1500000.50, options); // 'Rp 1.500.000,50'\n * ```\n *\n * @public\n */\nexport interface RupiahOptions {\n  /**\n   * Whether to show 'Rp' symbol.\n   *\n   * @defaultValue true\n   */\n  symbol?: boolean;\n\n  /**\n   * Whether to show decimal places.\n   *\n   * @defaultValue false\n   */\n  decimal?: boolean;\n\n  /**\n   * Thousands separator character.\n   *\n   * @defaultValue '.'\n   *\n   * @example\n   * ```typescript\n   * '.' // Indonesian standard\n   * ',' // International standard\n   * ' ' // Space separator\n   * ```\n   */\n  separator?: string;\n\n  /**\n   * Decimal separator character.\n   *\n   * @defaultValue ','\n   *\n   * @example\n   * ```typescript\n   * ',' // Indonesian standard\n   * '.' // International standard\n   * ```\n   */\n  decimalSeparator?: string;\n\n  /**\n   * Number of decimal places to show.\n   *\n   * @defaultValue 0\n   */\n  precision?: number;\n\n  /**\n   * Whether to add space after 'Rp' symbol.\n   *\n   * @defaultValue true\n   *\n   * @example\n   * ```typescript\n   * true  // 'Rp 1.500.000'\n   * false // 'Rp1.500.000'\n   * ```\n   */\n  spaceAfterSymbol?: boolean;\n}\n\n/**\n * Options for converting numbers to Indonesian words (terbilang).\n *\n * @example\n * Default:\n * ```typescript\n * toWords(1500000); // 'satu juta lima ratus ribu rupiah'\n * ```\n *\n * @example\n * Uppercase:\n * ```typescript\n * toWords(1500000, { uppercase: true });\n * // 'Satu juta lima ratus ribu rupiah'\n * ```\n *\n * @example\n * Without currency suffix:\n * ```typescript\n * toWords(1500000, { withCurrency: false });\n * // 'satu juta lima ratus ribu'\n * ```\n *\n * @public\n */\nexport interface WordOptions {\n  /**\n   * Whether to capitalize the first letter.\n   *\n   * @defaultValue false\n   *\n   * @example\n   * ```typescript\n   * false // 'satu juta'\n   * true  // 'Satu juta'\n   * ```\n   */\n  uppercase?: boolean;\n\n  /**\n   * Whether to add 'rupiah' at the end.\n   *\n   * @defaultValue true\n   *\n   * @example\n   * ```typescript\n   * true  // 'satu juta rupiah'\n   * false // 'satu juta'\n   * ```\n   */\n  withCurrency?: boolean;\n\n  /**\n   * Whether to include decimal words with 'koma' separator.\n   *\n   * @defaultValue false\n   *\n   * @example\n   * ```typescript\n   * false // 'satu juta lima ratus ribu rupiah'\n   * true  // 'satu juta lima ratus ribu rupiah koma lima puluh'\n   * ```\n   */\n  withDecimals?: boolean;\n}\n\n/**\n * Unit for rounding currency amounts.\n *\n * Common Indonesian currency rounding units:\n * - `'ribu'`: Round to thousands (1.000)\n * - `'ratus-ribu'`: Round to hundred thousands (100.000)\n * - `'juta'`: Round to millions (1.000.000)\n *\n * @example\n * ```typescript\n * roundToClean(1234567, 'ribu');      // 1235000\n * roundToClean(1234567, 'ratus-ribu'); // 1200000\n * roundToClean(1234567, 'juta');       // 1000000\n * ```\n *\n * @public\n */\nexport type RoundUnit = 'ribu' | 'ratus-ribu' | 'juta';\n\n/**\n * Options for compact currency formatting.\n *\n * @example\n * Default:\n * ```typescript\n * formatCompact(1500000); // 'Rp 1,5 juta'\n * ```\n *\n * @example\n * Without symbol:\n * ```typescript\n * formatCompact(1500000, { symbol: false }); // '1,5 juta'\n * ```\n *\n * @public\n */\nexport interface CompactOptions {\n  /**\n   * Whether to show 'Rp' symbol.\n   *\n   * @defaultValue true\n   */\n  symbol?: boolean;\n\n  /**\n   * Whether to add space after 'Rp' symbol.\n   *\n   * @defaultValue true\n   */\n  spaceAfterSymbol?: boolean;\n}\n\n/**\n * Options for splitting an amount into parts.\n *\n * @example\n * Equal split:\n * ```typescript\n * splitAmount(1500000, 3); // [500000, 500000, 500000]\n * ```\n *\n * @example\n * Custom ratios:\n * ```typescript\n * splitAmount(1000000, 2, { ratios: [70, 30] }); // [700000, 300000]\n * ```\n *\n * @public\n */\nexport interface SplitOptions {\n  /**\n   * Custom percentage ratios (must sum to 100).\n   * Length must match `parts` count.\n   */\n  ratios?: number[];\n\n  /**\n   * Round each part to a clean amount.\n   */\n  roundTo?: RoundUnit;\n}\n\n/**\n * Options for formatting percentages.\n *\n * @example\n * Default formatting:\n * ```typescript\n * formatPercentage(0.115); // '11,5%'\n * ```\n *\n * @example\n * With custom decimals:\n * ```typescript\n * formatPercentage(0.1152, { decimals: 2 }); // '11,52%'\n * ```\n *\n * @example\n * Without symbol:\n * ```typescript\n * formatPercentage(0.11, { symbol: false }); // '11'\n * ```\n *\n * @example\n * isPercentage mode (value already in percentage form):\n * ```typescript\n * formatPercentage(11.5, { isPercentage: true }); // '11,5%'\n * ```\n *\n * @public\n */\nexport interface PercentageOptions {\n  /**\n   * Number of decimal places.\n   *\n   * @defaultValue 1\n   */\n  decimals?: number;\n\n  /**\n   * Whether to include '%' symbol.\n   *\n   * @defaultValue true\n   */\n  symbol?: boolean;\n\n  /**\n   * Decimal separator character.\n   *\n   * @defaultValue ','\n   */\n  decimalSeparator?: string;\n\n  /**\n   * Whether to interpret value as already a percentage (not decimal).\n   *\n   * When true, multiplies by 100 internally.\n   * e.g., formatPercentage(11.5, { isPercentage: true }) → '11,5%'\n   * instead of formatPercentage(0.115) → '11,5%'\n   *\n   * @defaultValue false\n   */\n  isPercentage?: boolean;\n}\n\n// Error classes co-located with their related types per `architecture.md` \"Custom Error Classes\".\n/**\n * Invalid split error thrown when split parameters are invalid.\n *\n * @public\n */\nexport class InvalidSplitError extends Error {\n  readonly code = 'INVALID_SPLIT' as const;\n\n  constructor(message: string = 'Invalid split parameters') {\n    super(message);\n    this.name = 'InvalidSplitError';\n  }\n}\n","import type { RoundUnit, SplitOptions } from './types';\nimport { InvalidSplitError } from './types';\n\n// Re-export so legacy/internal imports of `InvalidSplitError` from `./calc` keep resolving.\nexport { InvalidSplitError } from './types';\n\n/**\n * Splits an amount into equal or custom-ratio parts.\n *\n * @param amount - The amount to split\n * @param parts - Number of parts to split into\n * @param options - Split options (ratios, rounding)\n * @returns Array of split amounts\n *\n * @example\n * Equal split:\n * ```typescript\n * splitAmount(1500000, 3); // [500000, 500000, 500000]\n * ```\n *\n * @example\n * Custom ratios:\n * ```typescript\n * splitAmount(1000000, 2, { ratios: [70, 30] }); // [700000, 300000]\n * ```\n *\n * @example\n * With rounding:\n * ```typescript\n * splitAmount(1234567, 3, { roundTo: 'ribu' }); // [412000, 411000, 411000]\n * ```\n *\n * @public\n */\nexport function splitAmount(\n  amount: number,\n  parts: number,\n  options?: SplitOptions\n): number[] {\n  if (parts < 1) {\n    throw new InvalidSplitError('Parts must be at least 1');\n  }\n\n  if (parts === 1) {\n    return [amount];\n  }\n\n  const { ratios, roundTo } = options || {};\n\n  if (ratios) {\n    if (ratios.length !== parts) {\n      throw new InvalidSplitError(\n        `Ratios length (${ratios.length}) must match parts count (${parts})`\n      );\n    }\n\n    const sum = ratios.reduce((a, b) => a + b, 0);\n    if (Math.abs(sum - 100) > 0.01) {\n      throw new InvalidSplitError(`Ratios must sum to 100 (got ${sum})`);\n    }\n\n    let result = ratios.map((r) => amount * (r / 100));\n\n    if (roundTo) {\n      result = result.map((v) => roundToClean(v, roundTo));\n    }\n\n    return result;\n  }\n\n  const base = Math.floor(amount / parts);\n  const remainder = amount - base * parts;\n\n  const result: number[] = [];\n  for (let i = 0; i < parts; i++) {\n    result.push(base + (i < remainder ? 1 : 0));\n  }\n\n  if (roundTo) {\n    return result.map((v) => roundToClean(v, roundTo));\n  }\n\n  return result;\n}\n\n/**\n * Calculates what percentage a part is of a total.\n *\n * @param part - The part value\n * @param total - The total value\n * @returns Percentage as number (e.g., 15 for 15%)\n *\n * @example\n * ```typescript\n * percentageOf(150000, 1000000); // 15\n * percentageOf(0, 1000000); // 0\n * percentageOf(100, 0); // 0 (not NaN)\n * ```\n *\n * @public\n */\nexport function percentageOf(part: number, total: number): number {\n  if (total === 0) return 0;\n  return (part / total) * 100;\n}\n\n/**\n * Calculates absolute and percentage difference between two amounts.\n *\n * @param amount1 - The new/current amount\n * @param amount2 - The original/reference amount\n * @returns Object with absolute difference, percentage, and direction\n *\n * @example\n * ```typescript\n * difference(1200000, 1000000);\n * // { absolute: 200000, percentage: 20, direction: 'increase' }\n *\n * difference(0, 1000000);\n * // { absolute: -1000000, percentage: null, direction: 'decrease' }\n * ```\n *\n * @public\n */\nexport function difference(\n  amount1: number,\n  amount2: number\n): {\n  absolute: number;\n  percentage: number | null;\n  direction: 'increase' | 'decrease' | 'same';\n} {\n  const absolute = amount1 - amount2;\n\n  let percentage: number | null;\n  if (amount2 === 0) {\n    percentage = amount1 === 0 ? 0 : null;\n  } else {\n    percentage = (absolute / amount2) * 100;\n  }\n\n  const direction: 'increase' | 'decrease' | 'same' =\n    absolute > 0 ? 'increase' : absolute < 0 ? 'decrease' : 'same';\n\n  return { absolute, percentage, direction };\n}\n\n/**\n * Rounds a number to a clean currency amount.\n * Internal helper — also exported from utils.ts for public API.\n */\nfunction roundToClean(amount: number, unit: RoundUnit): number {\n  const divisors: Record<RoundUnit, number> = {\n    ribu: 1000,\n    'ratus-ribu': 100000,\n    juta: 1000000,\n  };\n\n  return Math.round(amount / divisors[unit]) * divisors[unit];\n}\n","/**\n * Validates whether a string is a valid Rupiah format.\n *\n * Accepts standard, compact, and negative formats.\n *\n * @param formatted - The string to validate\n * @returns `true` if valid Rupiah format, `false` otherwise\n *\n * @example\n * ```typescript\n * validateRupiah('Rp 1.500.000'); // true\n * validateRupiah('1.500.000'); // true\n * validateRupiah('Rp 1,5 juta'); // true\n * validateRupiah('abc'); // false\n * validateRupiah(''); // false\n * ```\n *\n * @public\n */\nexport function validateRupiah(formatted: string): boolean {\n  if (!formatted || typeof formatted !== 'string') {\n    return false;\n  }\n\n  const trimmed = formatted.trim();\n\n  if (!trimmed) return false;\n\n  const compactUnits = ['triliun', 'miliar', 'juta', 'ribu'];\n\n  // Check for compact format\n  for (const unit of compactUnits) {\n    if (trimmed.toLowerCase().includes(unit)) {\n      return /-?\\d+[,.]?\\d*\\s*(ribu|juta|miliar|triliun)/i.test(trimmed);\n    }\n  }\n\n  // Standard format: remove optional Rp prefix and optional negative sign\n  let cleaned = trimmed.replace(/^(-?\\s*)?Rp\\s*/i, '');\n\n  // Remove negative sign if still present\n  cleaned = cleaned.replace(/^\\s*-/, '');\n\n  cleaned = cleaned.trim();\n\n  if (!cleaned) return false;\n\n  // Must be digits with dots and/or commas\n  if (!/^[0-9.,]+$/.test(cleaned)) return false;\n\n  // Must have at least one digit\n  if (!/\\d/.test(cleaned)) return false;\n\n  return true;\n}\n"]}