n2words 3.1.0 → 4.0.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 +44 -0
- package/LICENSE +1 -1
- package/README.md +64 -184
- package/dist/am-ET.js +2 -0
- package/dist/am-ET.umd.js +2 -0
- package/dist/am-Latn-ET.js +2 -0
- package/dist/am-Latn-ET.umd.js +2 -0
- package/dist/ar-SA.js +2 -0
- package/dist/ar-SA.umd.js +2 -0
- package/dist/az-AZ.js +2 -0
- package/dist/az-AZ.umd.js +2 -0
- package/dist/bn-BD.js +2 -0
- package/dist/bn-BD.umd.js +2 -0
- package/dist/cs-CZ.js +2 -0
- package/dist/cs-CZ.umd.js +2 -0
- package/dist/da-DK.js +2 -0
- package/dist/da-DK.umd.js +2 -0
- package/dist/de-DE.js +2 -0
- package/dist/de-DE.umd.js +2 -0
- package/dist/el-GR.js +2 -0
- package/dist/el-GR.umd.js +2 -0
- package/dist/en-AU.js +2 -0
- package/dist/en-AU.umd.js +2 -0
- package/dist/en-BD.js +2 -0
- package/dist/en-BD.umd.js +2 -0
- package/dist/en-CA.js +2 -0
- package/dist/en-CA.umd.js +2 -0
- package/dist/en-GB.js +2 -0
- package/dist/en-GB.umd.js +2 -0
- package/dist/en-GH.js +2 -0
- package/dist/en-GH.umd.js +2 -0
- package/dist/en-IE.js +2 -0
- package/dist/en-IE.umd.js +2 -0
- package/dist/en-IN.js +2 -0
- package/dist/en-IN.umd.js +2 -0
- package/dist/en-KE.js +2 -0
- package/dist/en-KE.umd.js +2 -0
- package/dist/en-MY.js +2 -0
- package/dist/en-MY.umd.js +2 -0
- package/dist/en-NG.js +2 -0
- package/dist/en-NG.umd.js +2 -0
- package/dist/en-NZ.js +2 -0
- package/dist/en-NZ.umd.js +2 -0
- package/dist/en-PH.js +2 -0
- package/dist/en-PH.umd.js +2 -0
- package/dist/en-PK.js +2 -0
- package/dist/en-PK.umd.js +2 -0
- package/dist/en-SG.js +2 -0
- package/dist/en-SG.umd.js +2 -0
- package/dist/en-US.js +2 -0
- package/dist/en-US.umd.js +2 -0
- package/dist/en-ZA.js +2 -0
- package/dist/en-ZA.umd.js +2 -0
- package/dist/es-ES.js +2 -0
- package/dist/es-ES.umd.js +2 -0
- package/dist/es-MX.js +2 -0
- package/dist/es-MX.umd.js +2 -0
- package/dist/es-US.js +2 -0
- package/dist/es-US.umd.js +2 -0
- package/dist/fa-IR.js +2 -0
- package/dist/fa-IR.umd.js +2 -0
- package/dist/fi-FI.js +2 -0
- package/dist/fi-FI.umd.js +2 -0
- package/dist/fil-PH.js +2 -0
- package/dist/fil-PH.umd.js +2 -0
- package/dist/fr-BE.js +2 -0
- package/dist/fr-BE.umd.js +2 -0
- package/dist/fr-FR.js +2 -0
- package/dist/fr-FR.umd.js +2 -0
- package/dist/gu-IN.js +2 -0
- package/dist/gu-IN.umd.js +2 -0
- package/dist/ha-NG.js +2 -0
- package/dist/ha-NG.umd.js +2 -0
- package/dist/hbo-IL.js +2 -0
- package/dist/hbo-IL.umd.js +2 -0
- package/dist/he-IL.js +2 -0
- package/dist/he-IL.umd.js +2 -0
- package/dist/hi-IN.js +2 -0
- package/dist/hi-IN.umd.js +2 -0
- package/dist/hr-HR.js +2 -0
- package/dist/hr-HR.umd.js +2 -0
- package/dist/hu-HU.js +2 -0
- package/dist/hu-HU.umd.js +2 -0
- package/dist/id-ID.js +2 -0
- package/dist/id-ID.umd.js +2 -0
- package/dist/it-IT.js +2 -0
- package/dist/it-IT.umd.js +2 -0
- package/dist/ja-JP.js +2 -0
- package/dist/ja-JP.umd.js +2 -0
- package/dist/ka-GE.js +2 -0
- package/dist/ka-GE.umd.js +2 -0
- package/dist/kn-IN.js +2 -0
- package/dist/kn-IN.umd.js +2 -0
- package/dist/ko-KR.js +2 -0
- package/dist/ko-KR.umd.js +2 -0
- package/dist/lt-LT.js +2 -0
- package/dist/lt-LT.umd.js +2 -0
- package/dist/lv-LV.js +2 -0
- package/dist/lv-LV.umd.js +2 -0
- package/dist/mr-IN.js +2 -0
- package/dist/mr-IN.umd.js +2 -0
- package/dist/ms-MY.js +2 -0
- package/dist/ms-MY.umd.js +2 -0
- package/dist/nb-NO.js +2 -0
- package/dist/nb-NO.umd.js +2 -0
- package/dist/nl-NL.js +2 -0
- package/dist/nl-NL.umd.js +2 -0
- package/dist/pa-IN.js +2 -0
- package/dist/pa-IN.umd.js +2 -0
- package/dist/pl-PL.js +2 -0
- package/dist/pl-PL.umd.js +2 -0
- package/dist/pt-PT.js +2 -0
- package/dist/pt-PT.umd.js +2 -0
- package/dist/ro-RO.js +2 -0
- package/dist/ro-RO.umd.js +2 -0
- package/dist/ru-RU.js +2 -0
- package/dist/ru-RU.umd.js +2 -0
- package/dist/sr-Cyrl-RS.js +2 -0
- package/dist/sr-Cyrl-RS.umd.js +2 -0
- package/dist/sr-Latn-RS.js +2 -0
- package/dist/sr-Latn-RS.umd.js +2 -0
- package/dist/sv-SE.js +2 -0
- package/dist/sv-SE.umd.js +2 -0
- package/dist/sw-KE.js +2 -0
- package/dist/sw-KE.umd.js +2 -0
- package/dist/ta-IN.js +2 -0
- package/dist/ta-IN.umd.js +2 -0
- package/dist/te-IN.js +2 -0
- package/dist/te-IN.umd.js +2 -0
- package/dist/th-TH.js +2 -0
- package/dist/th-TH.umd.js +2 -0
- package/dist/tr-TR.js +2 -0
- package/dist/tr-TR.umd.js +2 -0
- package/dist/uk-UA.js +2 -0
- package/dist/uk-UA.umd.js +2 -0
- package/dist/ur-PK.js +2 -0
- package/dist/ur-PK.umd.js +2 -0
- package/dist/vi-VN.js +2 -0
- package/dist/vi-VN.umd.js +2 -0
- package/dist/yo-NG.js +2 -0
- package/dist/yo-NG.umd.js +2 -0
- package/dist/zh-Hans-CN.js +2 -0
- package/dist/zh-Hans-CN.umd.js +2 -0
- package/dist/zh-Hant-TW.js +2 -0
- package/dist/zh-Hant-TW.umd.js +2 -0
- package/package.json +47 -82
- package/src/am-ET.d.ts +40 -0
- package/src/am-ET.js +269 -0
- package/src/am-Latn-ET.d.ts +35 -0
- package/src/am-Latn-ET.js +264 -0
- package/src/ar-SA.d.ts +49 -0
- package/{lib/languages/ar.js → src/ar-SA.js} +177 -15
- package/src/az-AZ.d.ts +37 -0
- package/src/az-AZ.js +312 -0
- package/src/bn-BD.d.ts +36 -0
- package/{lib/languages/bn.js → src/bn-BD.js} +110 -6
- package/src/cs-CZ.d.ts +49 -0
- package/{lib/languages/cs.js → src/cs-CZ.js} +240 -14
- package/src/da-DK.d.ts +44 -0
- package/{lib/languages/da.js → src/da-DK.js} +131 -11
- package/src/de-DE.d.ts +57 -0
- package/src/de-DE.js +603 -0
- package/src/el-GR.d.ts +40 -0
- package/src/el-GR.js +418 -0
- package/src/en-AU.d.ts +47 -0
- package/src/en-AU.js +423 -0
- package/src/en-BD.d.ts +49 -0
- package/src/en-BD.js +415 -0
- package/src/en-CA.d.ts +63 -0
- package/src/en-CA.js +518 -0
- package/src/en-GB.d.ts +56 -0
- package/src/en-GB.js +469 -0
- package/src/en-GH.d.ts +11 -0
- package/src/en-GH.js +345 -0
- package/src/en-IE.d.ts +56 -0
- package/src/en-IE.js +479 -0
- package/src/en-IN.d.ts +49 -0
- package/src/en-IN.js +415 -0
- package/src/en-KE.d.ts +11 -0
- package/src/en-KE.js +345 -0
- package/src/en-MY.d.ts +11 -0
- package/src/en-MY.js +347 -0
- package/src/en-NG.d.ts +56 -0
- package/src/en-NG.js +479 -0
- package/src/en-NZ.d.ts +11 -0
- package/{lib/languages/en.js → src/en-NZ.js} +164 -31
- package/src/en-PH.d.ts +11 -0
- package/src/en-PH.js +345 -0
- package/src/en-PK.d.ts +49 -0
- package/src/en-PK.js +415 -0
- package/src/en-SG.d.ts +11 -0
- package/src/en-SG.js +345 -0
- package/src/en-US.d.ts +63 -0
- package/src/en-US.js +516 -0
- package/src/en-ZA.d.ts +56 -0
- package/src/en-ZA.js +478 -0
- package/src/es-ES.d.ts +65 -0
- package/src/es-ES.js +541 -0
- package/src/es-MX.d.ts +58 -0
- package/{lib/languages/es.js → src/es-MX.js} +228 -18
- package/src/es-US.d.ts +58 -0
- package/src/es-US.js +446 -0
- package/src/fa-IR.d.ts +38 -0
- package/src/fa-IR.js +246 -0
- package/src/fi-FI.d.ts +46 -0
- package/{lib/languages/fi.js → src/fi-FI.js} +152 -11
- package/src/fil-PH.d.ts +37 -0
- package/{lib/languages/fil.js → src/fil-PH.js} +144 -8
- package/src/fr-BE.d.ts +49 -0
- package/{lib/languages → src}/fr-BE.js +175 -13
- package/src/fr-FR.d.ts +63 -0
- package/{lib/languages/fr.js → src/fr-FR.js} +182 -16
- package/src/gu-IN.d.ts +35 -0
- package/{lib/languages/gu.js → src/gu-IN.js} +115 -6
- package/src/ha-NG.d.ts +37 -0
- package/{lib/languages/ha.js → src/ha-NG.js} +107 -8
- package/src/hbo-IL.d.ts +39 -0
- package/{lib/languages/hbo.js → src/hbo-IL.js} +211 -19
- package/src/he-IL.d.ts +37 -0
- package/src/he-IL.js +537 -0
- package/src/hi-IN.d.ts +36 -0
- package/{lib/languages/hi.js → src/hi-IN.js} +116 -6
- package/src/hr-HR.d.ts +42 -0
- package/src/hr-HR.js +463 -0
- package/src/hu-HU.d.ts +38 -0
- package/{lib/languages/hu.js → src/hu-HU.js} +164 -6
- package/src/id-ID.d.ts +38 -0
- package/{lib/languages/id.js → src/id-ID.js} +99 -8
- package/src/it-IT.d.ts +59 -0
- package/{lib/languages/it.js → src/it-IT.js} +179 -15
- package/src/ja-JP.d.ts +49 -0
- package/{lib/languages/ja.js → src/ja-JP.js} +111 -12
- package/src/ka-GE.d.ts +44 -0
- package/{lib/languages/ka.js → src/ka-GE.js} +113 -11
- package/src/kn-IN.d.ts +35 -0
- package/{lib/languages/kn.js → src/kn-IN.js} +115 -6
- package/src/ko-KR.d.ts +45 -0
- package/{lib/languages/ko.js → src/ko-KR.js} +94 -12
- package/src/lt-LT.d.ts +49 -0
- package/src/lt-LT.js +543 -0
- package/src/lv-LV.d.ts +49 -0
- package/src/lv-LV.js +595 -0
- package/src/mr-IN.d.ts +36 -0
- package/{lib/languages/mr.js → src/mr-IN.js} +116 -6
- package/src/ms-MY.d.ts +37 -0
- package/{lib/languages/ms.js → src/ms-MY.js} +111 -8
- package/src/nb-NO.d.ts +44 -0
- package/{lib/languages/nb.js → src/nb-NO.js} +153 -11
- package/src/nl-NL.d.ts +54 -0
- package/{lib/languages/nl.js → src/nl-NL.js} +260 -18
- package/src/pa-IN.d.ts +36 -0
- package/{lib/languages/pa.js → src/pa-IN.js} +116 -6
- package/src/pl-PL.d.ts +55 -0
- package/src/pl-PL.js +585 -0
- package/src/pt-PT.d.ts +45 -0
- package/{lib/languages/pt.js → src/pt-PT.js} +234 -12
- package/src/ro-RO.d.ts +44 -0
- package/{lib/languages/ro.js → src/ro-RO.js} +212 -18
- package/src/ru-RU.d.ts +50 -0
- package/src/ru-RU.js +535 -0
- package/src/sr-Cyrl-RS.d.ts +49 -0
- package/src/sr-Cyrl-RS.js +503 -0
- package/src/sr-Latn-RS.d.ts +49 -0
- package/src/sr-Latn-RS.js +503 -0
- package/src/sv-SE.d.ts +44 -0
- package/{lib/languages/sv.js → src/sv-SE.js} +136 -11
- package/src/sw-KE.d.ts +37 -0
- package/{lib/languages/sw.js → src/sw-KE.js} +117 -6
- package/src/ta-IN.d.ts +35 -0
- package/{lib/languages/ta.js → src/ta-IN.js} +109 -6
- package/src/te-IN.d.ts +35 -0
- package/{lib/languages/te.js → src/te-IN.js} +115 -6
- package/src/th-TH.d.ts +38 -0
- package/{lib/languages/th.js → src/th-TH.js} +99 -6
- package/src/tr-TR.d.ts +48 -0
- package/{lib/languages/tr.js → src/tr-TR.js} +168 -23
- package/src/uk-UA.d.ts +42 -0
- package/src/uk-UA.js +463 -0
- package/src/ur-PK.d.ts +36 -0
- package/{lib/languages/ur.js → src/ur-PK.js} +116 -6
- package/src/utils/expand-scientific.d.ts +32 -0
- package/src/utils/expand-scientific.js +65 -0
- package/src/utils/parse-cardinal.d.ts +14 -0
- package/{lib/utils/parse-numeric.js → src/utils/parse-cardinal.js} +14 -44
- package/src/utils/parse-currency.d.ts +14 -0
- package/src/utils/parse-currency.js +91 -0
- package/src/utils/parse-ordinal.d.ts +10 -0
- package/src/utils/parse-ordinal.js +103 -0
- package/src/vi-VN.d.ts +48 -0
- package/{lib/languages/vi.js → src/vi-VN.js} +102 -11
- package/src/yo-NG.d.ts +37 -0
- package/{lib/languages/yo.js → src/yo-NG.js} +109 -9
- package/src/zh-Hans-CN.d.ts +48 -0
- package/{lib/languages/zh-Hans.js → src/zh-Hans-CN.js} +140 -8
- package/src/zh-Hant-TW.d.ts +50 -0
- package/{lib/languages/zh-Hant.js → src/zh-Hant-TW.js} +139 -8
- package/dist/languages/am-Latn.js +0 -3
- package/dist/languages/am-Latn.js.map +0 -1
- package/dist/languages/am.js +0 -3
- package/dist/languages/am.js.map +0 -1
- package/dist/languages/ar.js +0 -3
- package/dist/languages/ar.js.map +0 -1
- package/dist/languages/az.js +0 -3
- package/dist/languages/az.js.map +0 -1
- package/dist/languages/bn.js +0 -3
- package/dist/languages/bn.js.map +0 -1
- package/dist/languages/cs.js +0 -3
- package/dist/languages/cs.js.map +0 -1
- package/dist/languages/da.js +0 -3
- package/dist/languages/da.js.map +0 -1
- package/dist/languages/de.js +0 -3
- package/dist/languages/de.js.map +0 -1
- package/dist/languages/el.js +0 -3
- package/dist/languages/el.js.map +0 -1
- package/dist/languages/en.js +0 -3
- package/dist/languages/en.js.map +0 -1
- package/dist/languages/es.js +0 -3
- package/dist/languages/es.js.map +0 -1
- package/dist/languages/fa.js +0 -3
- package/dist/languages/fa.js.map +0 -1
- package/dist/languages/fi.js +0 -3
- package/dist/languages/fi.js.map +0 -1
- package/dist/languages/fil.js +0 -3
- package/dist/languages/fil.js.map +0 -1
- package/dist/languages/fr-BE.js +0 -3
- package/dist/languages/fr-BE.js.map +0 -1
- package/dist/languages/fr.js +0 -3
- package/dist/languages/fr.js.map +0 -1
- package/dist/languages/gu.js +0 -3
- package/dist/languages/gu.js.map +0 -1
- package/dist/languages/ha.js +0 -3
- package/dist/languages/ha.js.map +0 -1
- package/dist/languages/hbo.js +0 -3
- package/dist/languages/hbo.js.map +0 -1
- package/dist/languages/he.js +0 -3
- package/dist/languages/he.js.map +0 -1
- package/dist/languages/hi.js +0 -3
- package/dist/languages/hi.js.map +0 -1
- package/dist/languages/hr.js +0 -3
- package/dist/languages/hr.js.map +0 -1
- package/dist/languages/hu.js +0 -3
- package/dist/languages/hu.js.map +0 -1
- package/dist/languages/id.js +0 -3
- package/dist/languages/id.js.map +0 -1
- package/dist/languages/it.js +0 -3
- package/dist/languages/it.js.map +0 -1
- package/dist/languages/ja.js +0 -3
- package/dist/languages/ja.js.map +0 -1
- package/dist/languages/ka.js +0 -3
- package/dist/languages/ka.js.map +0 -1
- package/dist/languages/kn.js +0 -3
- package/dist/languages/kn.js.map +0 -1
- package/dist/languages/ko.js +0 -3
- package/dist/languages/ko.js.map +0 -1
- package/dist/languages/lt.js +0 -3
- package/dist/languages/lt.js.map +0 -1
- package/dist/languages/lv.js +0 -3
- package/dist/languages/lv.js.map +0 -1
- package/dist/languages/mr.js +0 -3
- package/dist/languages/mr.js.map +0 -1
- package/dist/languages/ms.js +0 -3
- package/dist/languages/ms.js.map +0 -1
- package/dist/languages/nb.js +0 -3
- package/dist/languages/nb.js.map +0 -1
- package/dist/languages/nl.js +0 -3
- package/dist/languages/nl.js.map +0 -1
- package/dist/languages/pa.js +0 -3
- package/dist/languages/pa.js.map +0 -1
- package/dist/languages/pl.js +0 -3
- package/dist/languages/pl.js.map +0 -1
- package/dist/languages/pt.js +0 -3
- package/dist/languages/pt.js.map +0 -1
- package/dist/languages/ro.js +0 -3
- package/dist/languages/ro.js.map +0 -1
- package/dist/languages/ru.js +0 -3
- package/dist/languages/ru.js.map +0 -1
- package/dist/languages/sr-Cyrl.js +0 -3
- package/dist/languages/sr-Cyrl.js.map +0 -1
- package/dist/languages/sr-Latn.js +0 -3
- package/dist/languages/sr-Latn.js.map +0 -1
- package/dist/languages/sv.js +0 -3
- package/dist/languages/sv.js.map +0 -1
- package/dist/languages/sw.js +0 -3
- package/dist/languages/sw.js.map +0 -1
- package/dist/languages/ta.js +0 -3
- package/dist/languages/ta.js.map +0 -1
- package/dist/languages/te.js +0 -3
- package/dist/languages/te.js.map +0 -1
- package/dist/languages/th.js +0 -3
- package/dist/languages/th.js.map +0 -1
- package/dist/languages/tr.js +0 -3
- package/dist/languages/tr.js.map +0 -1
- package/dist/languages/uk.js +0 -3
- package/dist/languages/uk.js.map +0 -1
- package/dist/languages/ur.js +0 -3
- package/dist/languages/ur.js.map +0 -1
- package/dist/languages/vi.js +0 -3
- package/dist/languages/vi.js.map +0 -1
- package/dist/languages/yo.js +0 -3
- package/dist/languages/yo.js.map +0 -1
- package/dist/languages/zh-Hans.js +0 -3
- package/dist/languages/zh-Hans.js.map +0 -1
- package/dist/languages/zh-Hant.js +0 -3
- package/dist/languages/zh-Hant.js.map +0 -1
- package/dist/n2words.js +0 -3
- package/dist/n2words.js.map +0 -1
- package/lib/languages/am-Latn.d.ts +0 -7
- package/lib/languages/am-Latn.js +0 -159
- package/lib/languages/am.d.ts +0 -7
- package/lib/languages/am.js +0 -159
- package/lib/languages/ar.d.ts +0 -17
- package/lib/languages/az.d.ts +0 -7
- package/lib/languages/az.js +0 -171
- package/lib/languages/bn.d.ts +0 -7
- package/lib/languages/cs.d.ts +0 -18
- package/lib/languages/da.d.ts +0 -14
- package/lib/languages/de.d.ts +0 -17
- package/lib/languages/de.js +0 -320
- package/lib/languages/el.d.ts +0 -14
- package/lib/languages/el.js +0 -236
- package/lib/languages/en.d.ts +0 -17
- package/lib/languages/es.d.ts +0 -21
- package/lib/languages/fa.d.ts +0 -7
- package/lib/languages/fa.js +0 -134
- package/lib/languages/fi.d.ts +0 -14
- package/lib/languages/fil.d.ts +0 -7
- package/lib/languages/fr-BE.d.ts +0 -11
- package/lib/languages/fr.d.ts +0 -21
- package/lib/languages/gu.d.ts +0 -7
- package/lib/languages/ha.d.ts +0 -7
- package/lib/languages/hbo.d.ts +0 -13
- package/lib/languages/he.d.ts +0 -13
- package/lib/languages/he.js +0 -265
- package/lib/languages/hi.d.ts +0 -7
- package/lib/languages/hr.d.ts +0 -11
- package/lib/languages/hr.js +0 -224
- package/lib/languages/hu.d.ts +0 -7
- package/lib/languages/id.d.ts +0 -7
- package/lib/languages/it.d.ts +0 -19
- package/lib/languages/ja.d.ts +0 -17
- package/lib/languages/ka.d.ts +0 -17
- package/lib/languages/kn.d.ts +0 -7
- package/lib/languages/ko.d.ts +0 -14
- package/lib/languages/lt.d.ts +0 -18
- package/lib/languages/lt.js +0 -301
- package/lib/languages/lv.d.ts +0 -18
- package/lib/languages/lv.js +0 -312
- package/lib/languages/mr.d.ts +0 -7
- package/lib/languages/ms.d.ts +0 -7
- package/lib/languages/nb.d.ts +0 -14
- package/lib/languages/nl.d.ts +0 -26
- package/lib/languages/pa.d.ts +0 -7
- package/lib/languages/pl.d.ts +0 -22
- package/lib/languages/pl.js +0 -317
- package/lib/languages/pt.d.ts +0 -17
- package/lib/languages/ro.d.ts +0 -18
- package/lib/languages/ru.d.ts +0 -11
- package/lib/languages/ru.js +0 -245
- package/lib/languages/sr-Cyrl.d.ts +0 -11
- package/lib/languages/sr-Cyrl.js +0 -221
- package/lib/languages/sr-Latn.d.ts +0 -11
- package/lib/languages/sr-Latn.js +0 -221
- package/lib/languages/sv.d.ts +0 -14
- package/lib/languages/sw.d.ts +0 -7
- package/lib/languages/ta.d.ts +0 -7
- package/lib/languages/te.d.ts +0 -7
- package/lib/languages/th.d.ts +0 -7
- package/lib/languages/tr.d.ts +0 -18
- package/lib/languages/uk.d.ts +0 -11
- package/lib/languages/uk.js +0 -224
- package/lib/languages/ur.d.ts +0 -7
- package/lib/languages/vi.d.ts +0 -17
- package/lib/languages/yo.d.ts +0 -7
- package/lib/languages/zh-Hans.d.ts +0 -11
- package/lib/languages/zh-Hant.d.ts +0 -11
- package/lib/n2words.d.ts +0 -55
- package/lib/n2words.js +0 -126
- package/lib/utils/parse-numeric.d.ts +0 -17
- /package/{lib → src}/utils/is-plain-object.d.ts +0 -0
- /package/{lib → src}/utils/is-plain-object.js +0 -0
- /package/{lib → src}/utils/validate-options.d.ts +0 -0
- /package/{lib → src}/utils/validate-options.js +0 -0
package/dist/n2words.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"n2words.js","sources":["../lib/utils/parse-numeric.js","../lib/languages/am.js","../lib/languages/am-Latn.js","../lib/utils/validate-options.js","../lib/utils/is-plain-object.js","../lib/languages/ar.js","../lib/languages/az.js","../lib/languages/bn.js","../lib/languages/cs.js","../lib/languages/da.js","../lib/languages/de.js","../lib/languages/el.js","../lib/languages/en.js","../lib/languages/es.js","../lib/languages/fa.js","../lib/languages/fi.js","../lib/languages/fil.js","../lib/languages/fr.js","../lib/languages/fr-BE.js","../lib/languages/gu.js","../lib/languages/ha.js","../lib/languages/hbo.js","../lib/languages/he.js","../lib/languages/hi.js","../lib/languages/hr.js","../lib/languages/hu.js","../lib/languages/id.js","../lib/languages/it.js","../lib/languages/ja.js","../lib/languages/ka.js","../lib/languages/kn.js","../lib/languages/ko.js","../lib/languages/lt.js","../lib/languages/lv.js","../lib/languages/mr.js","../lib/languages/ms.js","../lib/languages/nb.js","../lib/languages/nl.js","../lib/languages/pa.js","../lib/languages/pl.js","../lib/languages/pt.js","../lib/languages/ro.js","../lib/languages/ru.js","../lib/languages/sr-Cyrl.js","../lib/languages/sr-Latn.js","../lib/languages/sv.js","../lib/languages/sw.js","../lib/languages/ta.js","../lib/languages/te.js","../lib/languages/th.js","../lib/languages/tr.js","../lib/languages/uk.js","../lib/languages/ur.js","../lib/languages/vi.js","../lib/languages/yo.js","../lib/languages/zh-Hans.js","../lib/languages/zh-Hant.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Amharic language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n * Native Ge'ez script (ግዕዝ) output.\n *\n * Key features:\n * - Ge'ez/Ethiopic script numerals\n * - Teens formed with \"አስራ\" prefix\n * - Keeps \"one\" before hundred: \"አንድ መቶ\" (100)\n * - Short scale naming\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'አንድ', 'ሁለት', 'ሶስት', 'አራት', 'አምስት', 'ስድስት', 'ሰባት', 'ስምንት', 'ዘጠኝ']\nconst TEENS = ['አስር', 'አስራ አንድ', 'አስራ ሁለት', 'አስራ ሶስት', 'አስራ አራት', 'አስራ አምስት', 'አስራ ስድስት', 'አስራ ሰባት', 'አስራ ስምንት', 'አስራ ዘጠኝ']\nconst TENS = ['', '', 'ሃያ', 'ሰላሳ', 'አርባ', 'ሃምሳ', 'ስልሳ', 'ሰባ', 'ሰማንያ', 'ዘጠና']\n\nconst HUNDRED = 'መቶ'\nconst THOUSAND = 'ሺ'\n\nconst ZERO = 'ዜሮ'\nconst NEGATIVE = 'አሉታዊ'\nconst DECIMAL_SEP = 'ነጥብ'\n\n// Short scale\nconst SCALE_WORDS = ['', THOUSAND, 'ሚሊዮን', 'ቢሊዮን']\n\n// ============================================================================\n// Precomputed Lookup Table\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Amharic keeps \"one\" before hundred: \"አንድ መቶ\" (100)\n if (hundredsDigit > 0) {\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[ones])\n } else {\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n if (ones > 0) {\n parts.push(ONES[ones])\n }\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n return buildLargeNumberWords(n)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const scaleWord = SCALE_WORDS[scaleIndex] || ''\n\n if (scaleIndex === 0) {\n parts.push(buildSegment(segment))\n } else {\n parts.push(buildSegment(segment) + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Amharic words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Amharic words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Amharic Latin language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n * Latin/ASCII romanization of Amharic numerals.\n *\n * Key features:\n * - Romanized numerals (and, hulet, sost)\n * - Teens formed with \"asra\" prefix\n * - Keeps \"one\" before hundred: \"and meto\" (100)\n * - Short scale naming\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'and', 'hulet', 'sost', 'arat', 'amist', 'siddist', 'sebat', 'siment', 'zeteny']\nconst TEENS = ['asir', 'asra and', 'asra hulet', 'asra sost', 'asra arat', 'asra amist', 'asra siddist', 'asra sebat', 'asra siment', 'asra zeteny']\nconst TENS = ['', '', 'haya', 'selasa', 'arba', 'hamsa', 'silsa', 'seba', 'semanya', 'zetena']\n\nconst HUNDRED = 'meto'\nconst THOUSAND = 'shi'\n\nconst ZERO = 'zero'\nconst NEGATIVE = 'asitegna'\nconst DECIMAL_SEP = 'netib'\n\n// Short scale\nconst SCALE_WORDS = ['', THOUSAND, 'miliyon', 'billiyon']\n\n// ============================================================================\n// Precomputed Lookup Table\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Amharic keeps \"one\" before hundred: \"and meto\" (100)\n if (hundredsDigit > 0) {\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[ones])\n } else {\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n if (ones > 0) {\n parts.push(ONES[ones])\n }\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n return buildLargeNumberWords(n)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const scaleWord = SCALE_WORDS[scaleIndex] || ''\n\n if (scaleIndex === 0) {\n parts.push(buildSegment(segment))\n } else {\n parts.push(buildSegment(segment) + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Amharic (Latin script) words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Amharic Latin words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","import { isPlainObject } from './is-plain-object.js'\n\n/**\n * Validates and normalizes the options parameter.\n *\n * @param {*} options The options value to validate\n * @returns {Object} A valid options object (empty object if undefined)\n * @throws {TypeError} If options is not undefined or a plain object\n */\nexport function validateOptions (options) {\n if (options === undefined) return {}\n if (isPlainObject(options)) return options\n throw new TypeError(\n `Invalid options: expected plain object or undefined, got ${typeof options}`\n )\n}\n","/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * A plain object is one created by:\n * - Object literal: `{}`\n * - Object.create(null): null-prototype object\n *\n * This excludes arrays, class instances, Map, Set, and other object types.\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nexport function isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === null || proto === Object.prototype\n}\n","/**\n * Arabic language converter - Functional Implementation\n *\n * Self-contained converter with gender agreement and complex pluralization.\n *\n * Key features:\n * - Gender agreement (masculine/feminine forms)\n * - Complex pluralization (singular/dual/plural)\n * - Traditional Arabic number naming conventions\n * - \"و\" (and) conjunction between segments\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst TENS = ['عشرون', 'ثلاثون', 'أربعون', 'خمسون', 'ستون', 'سبعون', 'ثمانون', 'تسعون']\nconst HUNDREDS = ['', 'مائة', 'مئتان', 'ثلاثمائة', 'أربعمائة', 'خمسمائة', 'ستمائة', 'سبعمائة', 'ثمانمائة', 'تسعمائة']\n\n// Magnitude words with three forms: singular, appended (tanween), plural\nconst SCALE_WORDS = ['مائة', 'ألف', 'مليون', 'مليار', 'تريليون', 'كوادريليون', 'كوينتليون', 'سكستيليون']\nconst SCALE_APPENDED = ['', 'ألفاً', 'مليوناً', 'ملياراً', 'تريليوناً', 'كوادريليوناً', 'كوينتليوناً', 'سكستيليوناً']\nconst SCALE_PLURAL = ['', 'آلاف', 'ملايين', 'مليارات', 'تريليونات', 'كوادريليونات', 'كوينتليونات', 'سكستيليونات']\n\n// Dual forms\nconst DUAL = ['مئتان', 'ألفان', 'مليونان', 'ملياران', 'تريليونان', 'كوادريليونان', 'كوينتليونان', 'سكستيليونان']\nconst DUAL_APPENDED = ['مئتا', 'ألفا', 'مليونا', 'مليارا', 'تريليونا', 'كوادريليونا', 'كوينتليونا', 'سكستيليونا']\n\n// Gender-specific forms (1-19)\nconst ONES_MASC = ['واحد', 'اثنان', 'ثلاثة', 'أربعة', 'خمسة', 'ستة', 'سبعة', 'ثمانية', 'تسعة', 'عشرة', 'أحد عشر', 'اثنا عشر', 'ثلاثة عشر', 'أربعة عشر', 'خمسة عشر', 'ستة عشر', 'سبعة عشر', 'ثمانية عشر', 'تسعة عشر']\nconst ONES_FEM = ['واحدة', 'اثنتان', 'ثلاث', 'أربع', 'خمس', 'ست', 'سبع', 'ثمان', 'تسع', 'عشر', 'إحدى عشرة', 'اثنتا عشرة', 'ثلاث عشرة', 'أربع عشرة', 'خمس عشرة', 'ست عشرة', 'سبع عشرة', 'ثماني عشرة', 'تسع عشرة']\n\nconst ZERO = 'صفر'\nconst NEGATIVE = 'ناقص'\nconst DECIMAL_SEP = 'فاصلة'\nconst AND = 'و'\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Convert a 3-digit group to words.\n * Returns a clean string with no leading/trailing spaces.\n * Arabic \"و\" (and) is attached to following word: \"مائة وخمسة\" not \"مائة و خمسة\"\n */\nfunction segmentToWords (groupNumber, groupLevel, fullNumber, ones) {\n const tensValue = groupNumber % 100\n const hundredsDigit = Math.trunc(groupNumber / 100)\n let result = ''\n\n // Process hundreds\n if (hundredsDigit > 0) {\n if (tensValue === 0 && hundredsDigit === 2) {\n result = DUAL[0]\n } else {\n const hundredsWord = HUNDREDS[hundredsDigit]\n if (hundredsWord) {\n result = hundredsWord\n if (tensValue !== 0) {\n result += ' ' + AND // \"مائة و\" - و attaches to next word\n }\n }\n }\n }\n\n // Process tens and ones\n if (tensValue > 0) {\n if (tensValue < 20) {\n if (tensValue === 2 && hundredsDigit === 0 && groupLevel > 0) {\n const numValue = Number(fullNumber)\n const pow = Math.trunc(Math.log10(numValue))\n if (pow % 3 === 0 && fullNumber === BigInt(2 * Math.pow(10, pow))) {\n result += (groupNumber === 2 ? DUAL[groupLevel] : DUAL_APPENDED[groupLevel])\n } else {\n result += DUAL[groupLevel]\n }\n } else if (tensValue === 1 && groupLevel > 0) {\n result += SCALE_WORDS[groupLevel]\n } else {\n result += ones[tensValue - 1]\n }\n } else {\n const onesDigit = tensValue % 10\n const tensIndex = Math.trunc(tensValue / 10) - 2\n\n if (onesDigit > 0) {\n result += ones[onesDigit - 1] + ' ' + AND // \"ستة و\" attaches to tens\n }\n result += TENS[tensIndex]\n }\n }\n\n return result\n}\n\nfunction integerToWords (n, options) {\n if (n === 0n) return ZERO\n\n const gender = options.gender || 'masculine'\n const ones = gender === 'feminine' ? ONES_FEM : ONES_MASC\n\n let temp = n\n let group = 0\n const groups = []\n\n while (temp > 0n) {\n const numberToProcess = Number(temp % 1000n)\n temp = temp / 1000n\n\n if (numberToProcess > 0) {\n const groupDescription = segmentToWords(numberToProcess, group, n, ones)\n\n if (groupDescription) {\n let groupText = groupDescription\n\n // Add scale word for groups > 0\n if (group > 0 && numberToProcess > 2) {\n const remainder = numberToProcess % 100\n if (remainder === 1) {\n groupText += ' ' + SCALE_WORDS[group]\n } else if (numberToProcess >= 3 && numberToProcess <= 10) {\n groupText += ' ' + SCALE_PLURAL[group]\n } else {\n groupText += ' ' + (groups.length > 0 ? SCALE_APPENDED[group] : SCALE_WORDS[group])\n }\n }\n\n groups.unshift(groupText)\n }\n }\n\n group++\n }\n\n // Join groups with و (and) - space before و, attaches to next word\n // Use simple join since segmentToWords returns clean strings\n if (groups.length === 1) return groups[0]\n\n // Build result: \"group1 وgroup2 وgroup3\"\n let result = groups[0]\n for (let i = 1; i < groups.length; i++) {\n result += ' ' + AND + groups[i]\n }\n return result\n}\n\nfunction decimalPartToWords (decimalPart, options) {\n const parts = []\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n parts.push(ZERO)\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n parts.push(integerToWords(BigInt(remainder), options))\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts a numeric value to Arabic words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @param {string} [options.negativeWord] - Custom word for negative numbers\n * @returns {string} The number in Arabic words\n *\n * @example\n * toWords(1) // 'واحد'\n * toWords(1, {gender: 'feminine'}) // 'واحدة'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n const parts = []\n\n if (isNegative) {\n parts.push(options.negativeWord || NEGATIVE)\n }\n\n parts.push(integerToWords(integerPart, options))\n\n if (decimalPart) {\n parts.push(DECIMAL_SEP)\n parts.push(decimalPartToWords(decimalPart, options))\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Azerbaijani language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Turkic language patterns\n * - Implicit \"bir\" (one) omission before hundreds and thousands\n * - Short scale naming\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'bir', 'iki', 'üç', 'dörd', 'beş', 'altı', 'yeddi', 'səkkiz', 'doqquz']\nconst TEENS = ['on', 'on bir', 'on iki', 'on üç', 'on dörd', 'on beş', 'on altı', 'on yeddi', 'on səkkiz', 'on doqquz']\nconst TENS = ['', '', 'iyirmi', 'otuz', 'qırx', 'əlli', 'altmış', 'yetmiş', 'səksən', 'doxsan']\n\nconst HUNDRED = 'yüz'\nconst THOUSAND = 'min'\n\nconst ZERO = 'sıfır'\nconst NEGATIVE = 'mənfi'\nconst DECIMAL_SEP = 'nöqtə'\n\n// Short scale\nconst SCALE_WORDS = ['', THOUSAND, 'milyon', 'milyar', 'trilyon', 'katrilyon', 'kentilyon']\n\n// ============================================================================\n// Precomputed Lookup Table\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n if (hundredsDigit === 1) {\n parts.push(HUNDRED)\n } else {\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)\n }\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[ones])\n } else {\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n if (ones > 0) {\n parts.push(ONES[ones])\n }\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n return buildLargeNumberWords(n)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const scaleWord = SCALE_WORDS[scaleIndex] || ''\n\n if (scaleIndex === 0) {\n parts.push(buildSegment(segment))\n } else if (scaleIndex === 1 && segment === 1) {\n // Omit \"bir\" before thousand\n parts.push(scaleWord)\n } else {\n parts.push(buildSegment(segment) + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Azerbaijani words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Azerbaijani words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Bangla language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Indian numbering system (হাজার, লাখ, কোটি)\n * - Bangla script (Bengali)\n * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)\n * - Complete word forms for 0-99\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'শূন্য'\nconst NEGATIVE = 'মাইনাস'\nconst DECIMAL_SEP = 'দশমিক'\nconst HUNDRED = 'শত'\n\nconst BELOW_HUNDRED = [\n 'শূন্য', 'এক', 'দুই', 'তিন', 'চার', 'পাঁচ', 'ছয়', 'সাত', 'আট', 'নয়',\n 'দশ', 'এগারো', 'বারো', 'তেরো', 'চৌদ্দ', 'পনেরো', 'ষোল', 'সতেরো', 'আঠারো', 'উনিশ',\n 'বিশ', 'একুশ', 'বাইশ', 'তেইশ', 'চব্বিশ', 'পঁচিশ', 'ছাব্বিশ', 'সাতাশ', 'আঠাশ', 'উনত্রিশ',\n 'ত্রিশ', 'একত্রিশ', 'বত্রিশ', 'তেত্রিশ', 'চৌত্রিশ', 'পঁয়ত্রিশ', 'ছত্রিশ', 'সাঁইত্রিশ', 'আটত্রিশ', 'উনচল্লিশ',\n 'চল্লিশ', 'একচল্লিশ', 'বেয়াল্লিশ', 'তেতাল্লিশ', 'চুয়াল্লিশ', 'পঁয়তাল্লিশ', 'ছেচল্লিশ', 'সাতচল্লিশ', 'আটচল্লিশ', 'উনপঞ্চাশ',\n 'পঞ্চাশ', 'একান্ন', 'বাহান্ন', 'তিপ্পান্ন', 'চুয়ান্ন', 'পঞ্চান্ন', 'ছাপ্পান্ন', 'সাতান্ন', 'আটান্ন', 'উনষাট',\n 'ষাট', 'একষট্টি', 'বাষট্টি', 'তেষট্টি', 'চৌষট্টি', 'পঁয়ষট্টি', 'ছেষট্টি', 'সাতষট্টি', 'আটষট্টি', 'ঊনসত্তর',\n 'সত্তর', 'একাত্তর', 'বাহাত্তর', 'তেহাত্তর', 'চুয়াত্তর', 'পঁচাত্তর', 'ছিয়াত্তর', 'সাতাত্তর', 'আটাত্তর', 'উনআশি',\n 'আশি', 'একাশি', 'বিরাশি', 'তিরাশি', 'চুরাশি', 'পঁচাশি', 'ছিয়াশি', 'সাতাশি', 'আটাশি', 'উননব্বই',\n 'নব্বই', 'একানব্বই', 'বিরানব্বই', 'তিরানব্বই', 'চুরানব্বই', 'পঁচানব্বই', 'ছিয়ানব্বই', 'সাতানব্বই', 'আটানব্বই', 'নিরানব্বই'\n]\n\n// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.\nconst SCALE_WORDS = ['', 'হাজার', 'লাখ', 'কোটি', 'আরব', 'খরব', 'নীল', 'পদ্ম', 'শঙ্খ']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds words for a 0-999 segment.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED\n }\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Bengali words.\n *\n * Uses BigInt modulo for segment extraction (faster than string slicing).\n * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Bengali words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Extract segments using BigInt modulo\n // First segment is 3 digits (thousands), rest are 2 digits (lakhs, crores, etc.)\n // Segments stored least-significant first\n const segments = []\n\n // First segment: last 3 digits\n segments.push(Number(n % 1000n))\n let temp = n / 1000n\n\n // Remaining segments: 2 digits each (lakh = 100k, crore = 10M, etc.)\n while (temp > 0n) {\n segments.push(Number(temp % 100n))\n temp = temp / 100n\n }\n\n // Build result string (process from most-significant to least)\n const words = []\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n // First segment (units place) can be 0-999\n words.push(buildSegment(segment))\n } else {\n // Other segments are 0-99\n words.push(BELOW_HUNDRED[segment])\n }\n\n // Add scale word if not the units segment\n if (i > 0 && SCALE_WORDS[i]) {\n words.push(SCALE_WORDS[i])\n }\n }\n\n return words.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Bengali words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Bengali words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Czech language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Czech-specific rules:\n * - Three-form pluralization: 1 = singular, 2-4 = few, 5+ = many\n * - Irregular hundreds: sto, dvě stě, tři sta, čtyři sta, pět set...\n * - Gender: dva (masc) vs dvě (fem) for 2\n * - Omit \"one\" before scale words: \"tisíc\" not \"jedna tisíc\"\n * - Dynamic decimal separator: celá/celé/celých based on integer\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Ones words (masculine form)\nconst ONES = ['', 'jedna', 'dva', 'tři', 'čtyři', 'pět', 'šest', 'sedm', 'osm', 'devět']\n\n// Teens (10-19)\nconst TEENS = ['deset', 'jedenáct', 'dvanáct', 'třináct', 'čtrnáct', 'patnáct', 'šestnáct', 'sedmnáct', 'osmnáct', 'devatenáct']\n\n// Tens (20-90)\nconst TENS = ['', '', 'dvacet', 'třicet', 'čtyřicet', 'padesát', 'šedesát', 'sedmdesát', 'osmdesát', 'devadesát']\n\n// Irregular hundreds\nconst HUNDREDS = ['', 'sto', 'dvě stě', 'tři sta', 'čtyři sta', 'pět set', 'šest set', 'sedm set', 'osm set', 'devět set']\n\n// Scale plural forms [singular, few (2-4), many (5+)]\nconst PLURAL_FORMS = {\n 1: ['tisíc', 'tisíce', 'tisíc'], // 10^3\n 2: ['milion', 'miliony', 'milionů'], // 10^6\n 3: ['miliarda', 'miliardy', 'miliard'], // 10^9\n 4: ['bilion', 'biliony', 'bilionů'], // 10^12\n 5: ['biliarda', 'biliardy', 'biliard'], // 10^15\n 6: ['trilion', 'triliony', 'trilionů'], // 10^18\n 7: ['triliarda', 'triliardy', 'triliard'], // 10^21\n 8: ['kvadrilion', 'kvadriliony', 'kvadrilionů'], // 10^24\n 9: ['kvadriliarda', 'kvadriliardy', 'kvadriliard'] // 10^27\n}\n\nconst ZERO = 'nula'\nconst NEGATIVE = 'mínus'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999 (masculine, default form).\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds (irregular)\n if (hundreds > 0) {\n parts.push(HUNDREDS[hundreds])\n }\n\n // Tens and ones\n if (tens === 1) {\n // Teens\n parts.push(TEENS[ones])\n } else if (tens >= 2) {\n parts.push(TENS[tens])\n if (ones > 0) {\n parts.push(ONES[ones])\n }\n } else if (ones > 0) {\n parts.push(ONES[ones])\n }\n\n return parts.join(' ')\n}\n\n/**\n * Builds segment word for 0-999 with feminine hundreds.\n * Hundreds use irregular forms (dvě stě, tři sta) but ones remain masculine.\n */\nfunction buildSegmentWithHundreds (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds use the irregular HUNDREDS array (already has \"dvě stě\" etc.)\n if (hundreds > 0) {\n parts.push(HUNDREDS[hundreds])\n }\n\n // Tens and ones use masculine form\n if (tens === 1) {\n parts.push(TEENS[ones])\n } else if (tens >= 2) {\n parts.push(TENS[tens])\n if (ones > 0) {\n parts.push(ONES[ones]) // masculine\n }\n } else if (ones > 0) {\n parts.push(ONES[ones]) // masculine\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Czech pluralization: 1 = singular, 2-4 = few, else = many.\n * Teens (11-19) always use \"many\" form.\n *\n * @param {bigint} n - The number\n * @param {string[]} forms - [singular, few, many]\n * @returns {string} The appropriate form\n */\nfunction pluralize (n, forms) {\n if (n === 1n) return forms[0]\n\n const lastDigit = n % 10n\n const lastTwoDigits = n % 100n\n\n // 2-4, but not 12-14 (teens use \"many\")\n if (lastDigit >= 2n && lastDigit <= 4n && (lastTwoDigits < 10n || lastTwoDigits > 20n)) {\n return forms[1]\n }\n\n return forms[2]\n}\n\n/**\n * Gets the decimal separator word based on integer part.\n * celá (0-1), celé (2-4), celých (5+)\n */\nfunction getDecimalSeparator (integerPart) {\n if (integerPart === 0n || integerPart === 1n) {\n return 'celá'\n } else if (integerPart >= 2n && integerPart <= 4n) {\n return 'celé'\n } else {\n return 'celých'\n }\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Czech words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Czech words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = n / 1000n\n const remainder = Number(n % 1000n)\n\n const scaleWord = pluralize(thousands, PLURAL_FORMS[1])\n\n let result\n if (thousands === 1n) {\n // Omit \"one\" before tisíc\n result = scaleWord\n } else {\n result = buildSegment(Number(thousands)) + ' ' + scaleWord\n }\n\n if (remainder > 0) {\n // Use form with irregular hundreds (for \"dvě stě\" etc.)\n result += ' ' + buildSegmentWithHundreds(remainder)\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n * Uses BigInt division for faster segment extraction.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Czech words\n */\nfunction buildLargeNumberWords (n) {\n // Extract segments using BigInt division (faster than string slicing)\n // Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)\n const segmentValues = []\n let temp = n\n while (temp > 0n) {\n segmentValues.push(temp % 1000n)\n temp = temp / 1000n\n }\n\n // Build result string directly\n let result = ''\n\n for (let i = segmentValues.length - 1; i >= 0; i--) {\n const segment = segmentValues[i]\n if (segment === 0n) continue\n\n if (result) result += ' '\n\n if (i === 0) {\n // Units segment (no scale word) - use form with irregular hundreds\n result += buildSegmentWithHundreds(Number(segment))\n } else {\n // Scale word needed\n const forms = PLURAL_FORMS[i]\n if (forms) {\n const scaleWord = pluralize(segment, forms)\n\n if (segment === 1n) {\n // Omit \"one\" before scale words\n result += scaleWord\n } else {\n // Use masculine form for multiplier before scale words\n result += buildSegment(Number(segment)) + ' ' + scaleWord\n }\n } else {\n // Fallback for very large scales without defined forms\n result += buildSegment(Number(segment))\n }\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to Czech words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Czech words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Czech words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Czech words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'dvacet jedna'\n * toWords(1000) // 'tisíc'\n * toWords(2000) // 'dva tisíce'\n * toWords(5000) // 'pět tisíc'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n const separator = getDecimalSeparator(integerPart)\n result += ' ' + separator + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Danish language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Vigesimal (base-20) tens naming: halvtreds (50), treds (60), etc.\n * - Units-before-tens: \"enogtyve\" (21) = one-and-twenty\n * - Compound thousands: \"ettusind\", \"firetusinde\"\n * - \"og\" conjunction after hundreds and thousands\n * - Long scale for millions+\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'et', 'to', 'tre', 'fire', 'fem', 'seks', 'syv', 'otte', 'ni']\n// \"en\" form used in vigesimal pattern (X og Y) and before millions\nconst ONES_VIGESIMAL = ['', 'en', 'to', 'tre', 'fire', 'fem', 'seks', 'syv', 'otte', 'ni']\n\nconst TEENS = ['ti', 'elleve', 'tolv', 'tretten', 'fjorten', 'femten', 'seksten', 'sytten', 'atten', 'nitten']\n\n// Danish vigesimal tens (base-20 derived names)\nconst TENS = ['', '', 'tyve', 'tredive', 'fyrre', 'halvtreds', 'treds', 'halvfjerds', 'firs', 'halvfems']\n\nconst HUNDRED = 'hundrede'\nconst THOUSAND = 'tusind'\n\nconst ZERO = 'nul'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'komma'\n\n// Long scale: millioner, millarder, billioner, etc.\nconst SCALES = ['millioner', 'millarder', 'billioner', 'billarder', 'trillioner', 'trillarder', 'quadrillioner', 'quadrillarder']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds: \"ethundrede\", \"tohundrede\" (compound, no space)\n if (hundreds > 0) {\n parts.push(ONES[hundreds] + HUNDRED)\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n // Single digit\n parts.push(ONES[ones])\n } else if (tensOnes < 20) {\n // Teens\n parts.push(TEENS[ones])\n } else if (ones === 0) {\n // Even tens\n parts.push(TENS[tens])\n } else {\n // Units-before-tens: \"enogtyve\", \"treogfyrre\"\n parts.push(ONES_VIGESIMAL[ones] + 'og' + TENS[tens])\n }\n\n // Combine with \" og \" between hundreds and remainder\n if (parts.length === 2) {\n return parts[0] + ' og ' + parts[1]\n }\n return parts[0] || ''\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Danish words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Danish words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n // Compound thousands: \"ettusind\", \"firetusind\"\n let result = buildSegment(thousands) + THOUSAND\n\n if (remainder > 0) {\n // Add 'e' suffix and \" og \" for remainder: \"firetusinde og ...\"\n result += 'e og ' + buildSegment(remainder)\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Danish words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words with scale tracking\n // scaleIndex: 0 = units, 1 = thousands, 2 = millions, etc.\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const segmentWord = buildSegment(segment)\n\n if (scaleIndex === 0) {\n // Units segment\n parts.push({ word: segmentWord, type: 'units' })\n } else if (scaleIndex === 1) {\n // Thousands - compound form\n parts.push({ word: segmentWord + THOUSAND, type: 'thousand' })\n } else {\n // Millions+ - space-separated, use \"en\" for 1\n const scaleWord = SCALES[scaleIndex - 2]\n let numWord = segmentWord\n // \"et\" → \"en\" before millions+\n if (segment === 1) {\n numWord = 'en'\n }\n parts.push({ word: numWord + ' ' + scaleWord, type: 'million' })\n }\n }\n\n scaleIndex--\n }\n\n // Join parts with Danish rules\n return joinDanishParts(parts)\n}\n\n/**\n * Joins parts with Danish spacing rules.\n * - After thousands with remainder: \"tusinde og\"\n * - Millions are space-separated\n *\n * @param {Array} parts - Parts with type metadata\n * @returns {string} Joined string\n */\nfunction joinDanishParts (parts) {\n if (parts.length === 0) return ZERO\n if (parts.length === 1) return parts[0].word\n\n const result = []\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n const nextPart = parts[i + 1]\n\n if (part.type === 'thousand' && nextPart && nextPart.type === 'units') {\n // Thousands followed by units: add \"e og\"\n result.push(part.word + 'e og ' + nextPart.word)\n i++ // Skip the units part\n } else if (part.type === 'million') {\n if (result.length > 0) {\n result.push(' ')\n }\n result.push(part.word)\n if (nextPart) {\n result.push(' ')\n }\n } else {\n if (result.length > 0 && !result[result.length - 1].endsWith(' ')) {\n result.push(' ')\n }\n result.push(part.word)\n }\n }\n\n return result.join('')\n}\n\n/**\n * Converts decimal digits to Danish words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Danish words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Danish words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Danish words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'enogtyve'\n * toWords(1000) // 'ettusind'\n * toWords(1000000) // 'en millioner'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * German language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Inverted tens-ones order: \"einundzwanzig\" (one-and-twenty) for 21-99\n * - Compound words without spaces below million level\n * - Three forms of 1: \"eins\" (standalone), \"ein\" (before hundert/tausend), \"eine\" (before Million+)\n * - Scale pluralization: Million → Millionen, Milliarde → Milliarden\n * - Spaces only around million+ scale words\n * - BigInt modulo for efficient segment extraction\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Ones words (1-9), index 0 unused\nconst ONES = ['', 'eins', 'zwei', 'drei', 'vier', 'fünf', 'sechs', 'sieben', 'acht', 'neun']\n\n// \"ein\" form for use before hundert/und\nconst EIN = 'ein'\n\n// Teens (10-19)\nconst TEENS = ['zehn', 'elf', 'zwölf', 'dreizehn', 'vierzehn', 'fünfzehn', 'sechzehn', 'siebzehn', 'achtzehn', 'neunzehn']\n\n// Tens (20-90)\nconst TENS = ['', '', 'zwanzig', 'dreißig', 'vierzig', 'fünfzig', 'sechzig', 'siebzig', 'achtzig', 'neunzig']\n\n// Scale words (index 0 = thousand, 1 = million, etc.)\nconst SCALES = ['tausend', 'Million', 'Milliarde', 'Billion', 'Billiarde', 'Trillion', 'Trilliarde', 'Quadrillion', 'Quadrilliarde']\n\n// Pluralized scale words (million+)\nconst SCALES_PLURAL = ['tausend', 'Millionen', 'Milliarden', 'Billionen', 'Billiarden', 'Trillionen', 'Trilliarden', 'Quadrillionen', 'Quadrilliarden']\n\nconst HUNDRED = 'hundert'\nconst ZERO = 'null'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'komma'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999 (standalone form, uses \"eins\").\n * German inverts ones and tens: \"einundzwanzig\" = one-and-twenty\n *\n * @param {number} n - Number 0-999\n * @returns {string} German words for the segment\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.trunc(n / 10) % 10\n const hundreds = Math.trunc(n / 100)\n\n let result = ''\n\n // Hundreds: \"ein\" before hundert, not \"eins\"\n if (hundreds > 0) {\n result += (hundreds === 1 ? EIN : ONES[hundreds]) + HUNDRED\n }\n\n // Tens and ones\n if (tens === 1) {\n // Teens\n result += TEENS[ones]\n } else if (tens >= 2 && ones > 0) {\n // Inverted: \"einundzwanzig\" (one-and-twenty)\n // Use \"ein\" before \"und\", not \"eins\"\n result += (ones === 1 ? EIN : ONES[ones]) + 'und' + TENS[tens]\n } else if (tens >= 2) {\n // Just tens\n result += TENS[tens]\n } else if (ones > 0) {\n // Just ones (no tens, possibly after hundreds)\n // Use \"eins\" for standalone/after hundreds\n result += ONES[ones]\n }\n\n return result\n}\n\n/**\n * Builds segment word for use before \"tausend\".\n * Uses \"ein\" instead of \"eins\" for 1.\n *\n * @param {number} n - Number 0-999\n * @returns {string} German words for thousand context\n */\nfunction buildSegmentForThousand (n) {\n if (n === 0) return ''\n if (n === 1) return EIN // \"eintausend\"\n\n const ones = n % 10\n const tens = Math.trunc(n / 10) % 10\n const hundreds = Math.trunc(n / 100)\n\n let result = ''\n\n if (hundreds > 0) {\n result += (hundreds === 1 ? EIN : ONES[hundreds]) + HUNDRED\n }\n\n if (tens === 1) {\n result += TEENS[ones]\n } else if (tens >= 2 && ones > 0) {\n result += (ones === 1 ? EIN : ONES[ones]) + 'und' + TENS[tens]\n } else if (tens >= 2) {\n result += TENS[tens]\n } else if (ones > 0 && hundreds > 0) {\n result += ONES[ones]\n } else if (ones > 0) {\n result += ONES[ones]\n }\n\n return result\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to German words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} German words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 1,000,000\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n // Compound: \"eintausendzweihundert\" (no spaces)\n let result = buildSegmentForThousand(thousands) + SCALES[0]\n\n if (remainder > 0) {\n result += buildSegment(remainder)\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} German words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n // Units segment (no scale word)\n parts.push({ words: buildSegment(segment), isScale: false, scaleLevel: 0 })\n } else if (scaleIndex === 1) {\n // Thousands: compound without space\n const segWords = buildSegmentForThousand(segment)\n parts.push({ words: segWords + SCALES[0], isScale: false, scaleLevel: 1 })\n } else {\n // Million+ : space around scale word\n let segWords\n if (segment === 1) {\n segWords = 'eine' // \"eine Million\"\n } else {\n segWords = buildSegment(segment)\n }\n const scaleWord = segment === 1 ? SCALES[scaleIndex - 1] : SCALES_PLURAL[scaleIndex - 1]\n parts.push({ words: segWords, isScale: false, scaleLevel: scaleIndex })\n parts.push({ words: scaleWord, isScale: true, scaleLevel: scaleIndex })\n }\n }\n\n scaleIndex--\n }\n\n // Join with German spacing rules: space around million+ scale words\n return joinGermanParts(parts)\n}\n\n/**\n * Joins parts with German spacing rules.\n * Spaces only around million+ scale words.\n *\n * @param {Array} parts - Parts with metadata\n * @returns {string} Joined string\n */\nfunction joinGermanParts (parts) {\n if (parts.length === 0) return ZERO\n\n let result = ''\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n const prevPart = i > 0 ? parts[i - 1] : null\n\n // Add space before if:\n // - Current is a million+ scale word\n // - Previous was a million+ scale word\n if (i > 0) {\n const needsSpace = part.isScale || (prevPart && prevPart.isScale)\n if (needsSpace) {\n result += ' '\n }\n }\n\n result += part.words\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to German words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} German words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to German words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in German words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'einundzwanzig'\n * toWords(1000) // 'eintausend'\n * toWords(1000000) // 'eine Million'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Greek language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Space-separated number composition\n * - Implicit \"one\" (ένα) omission before scale words\n * - Irregular hundreds (διακόσια, τριακόσια, etc.)\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'ένα', 'δύο', 'τρία', 'τέσσερα', 'πέντε', 'έξι', 'επτά', 'οκτώ', 'εννέα']\n\nconst TEENS = ['δέκα', 'έντεκα', 'δώδεκα', 'δεκατρία', 'δεκατέσσερα', 'δεκαπέντε', 'δεκαέξι', 'δεκαεπτά', 'δεκαοκτώ', 'δεκαεννέα']\n\nconst TENS = ['', '', 'είκοσι', 'τριάντα', 'σαράντα', 'πενήντα', 'εξήντα', 'εβδομήντα', 'ογδόντα', 'ενενήντα']\n\n// Greek has irregular hundreds\nconst HUNDREDS = ['', 'εκατό', 'διακόσια', 'τριακόσια', 'τετρακόσια', 'πεντακόσια', 'εξακόσια', 'επτακόσια', 'οκτακόσια', 'εννιακόσια']\n\nconst THOUSAND = 'χίλια'\n\nconst ZERO = 'μηδέν'\nconst NEGATIVE = 'μείον'\nconst DECIMAL_SEP = 'κόμμα'\n\n// Short scale\nconst SCALES = ['εκατομμύριο', 'δισεκατομμύριο', 'τρισεκατομμύριο']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds (irregular forms)\n if (hundreds > 0) {\n parts.push(HUNDREDS[hundreds])\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n parts.push(ONES[ones])\n } else if (tensOnes < 20) {\n parts.push(TEENS[ones])\n } else if (ones === 0) {\n parts.push(TENS[tens])\n } else {\n parts.push(TENS[tens] + ' ' + ONES[ones])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Greek words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Greek words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n // Omit \"ένα\" before χίλια\n let result\n if (thousands === 1) {\n result = THOUSAND\n } else {\n result = buildSegment(thousands) + ' ' + THOUSAND\n }\n\n if (remainder > 0) {\n result += ' ' + buildSegment(remainder)\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Greek words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const segmentWord = buildSegment(segment)\n\n if (scaleIndex === 0) {\n // Units segment\n parts.push(segmentWord)\n } else if (scaleIndex === 1) {\n // Thousands - omit \"ένα\" before χίλια\n if (segment === 1) {\n parts.push(THOUSAND)\n } else {\n parts.push(segmentWord + ' ' + THOUSAND)\n }\n } else {\n // Millions+ - omit \"ένα\" before scale words\n const scaleWord = SCALES[scaleIndex - 2]\n if (segment === 1) {\n parts.push(scaleWord)\n } else {\n parts.push(segmentWord + ' ' + scaleWord)\n }\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts decimal digits to Greek words (per-digit).\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Greek words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n const parts = []\n\n for (const digit of decimalPart) {\n const d = parseInt(digit, 10)\n if (d === 0) {\n parts.push(ZERO)\n } else {\n parts.push(ONES[d])\n }\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts a numeric value to Greek words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Greek words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'είκοσι ένα'\n * toWords(1000) // 'χίλια'\n * toWords('3.14') // 'τρία κόμμα ένα τέσσερα'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * English language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Western numbering system (thousand, million, billion)\n * - \"and\" after hundreds: \"one hundred and twenty-three\"\n * - Hyphenated tens-ones: \"twenty-one\", \"forty-two\"\n * - \"and\" before final segment when following scale word\n * - BigInt modulo for efficient segment extraction\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']\nconst TEENS = ['ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen']\nconst TENS = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']\n\nconst SCALES = ['thousand', 'million', 'billion', 'trillion', 'quadrillion', 'quintillion', 'sextillion', 'septillion', 'octillion']\n\nconst HUNDRED = 'hundred'\nconst ZERO = 'zero'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'point'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n// Reusable result object to avoid allocation per call\nconst segmentResult = { word: '', hasHundred: false }\n\n/**\n * Builds words for a 0-999 segment.\n *\n * @param {number} n - Number 0-999\n * @returns {{ word: string, hasHundred: boolean }}\n */\nfunction buildSegment (n) {\n if (n === 0) {\n segmentResult.word = ''\n segmentResult.hasHundred = false\n return segmentResult\n }\n\n const ones = n % 10\n const tens = Math.trunc(n / 10) % 10\n const hundreds = Math.trunc(n / 100)\n\n // Build tens-ones part first\n let tensOnes = ''\n if (tens === 1) {\n tensOnes = TEENS[ones]\n } else if (tens >= 2) {\n tensOnes = ones > 0 ? TENS[tens] + '-' + ONES[ones] : TENS[tens]\n } else if (ones > 0) {\n tensOnes = ONES[ones]\n }\n\n // Hundreds place\n if (hundreds > 0) {\n if (tensOnes) {\n segmentResult.word = ONES[hundreds] + ' ' + HUNDRED + ' and ' + tensOnes\n } else {\n segmentResult.word = ONES[hundreds] + ' ' + HUNDRED\n }\n segmentResult.hasHundred = true\n } else {\n segmentResult.word = tensOnes\n segmentResult.hasHundred = false\n }\n\n return segmentResult\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to English words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} English words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n)).word\n }\n\n // Fast path: numbers < 1,000,000\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n const { word: thousandsWord } = buildSegment(thousands)\n let result = thousandsWord + ' ' + SCALES[0]\n\n if (remainder > 0) {\n const { word: remainderWord, hasHundred } = buildSegment(remainder)\n result += hasHundred ? ' ' + remainderWord : ' and ' + remainderWord\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n * Uses BigInt division for faster segment extraction.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} English words\n */\nfunction buildLargeNumberWords (n) {\n // Extract segments using BigInt division\n // Segments are stored least-significant first (index 0 = ones, 1 = thousands, etc.)\n const segments = []\n let temp = n\n while (temp > 0n) {\n segments.push(Number(temp % 1000n))\n temp = temp / 1000n\n }\n\n // Find the first (smallest index) non-zero segment - this is processed last\n let firstNonZeroIdx = -1\n for (let i = 0; i < segments.length; i++) {\n if (segments[i] !== 0) {\n firstNonZeroIdx = i\n break\n }\n }\n\n // Build result string (process from most-significant to least)\n let result = ''\n let prevWasScale = false\n\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n const { word, hasHundred } = buildSegment(segment)\n const isLastSegment = (i === firstNonZeroIdx)\n\n // Add \"and\" only before FINAL segment if it follows scale and doesn't have hundred\n if (result && isLastSegment && prevWasScale && !hasHundred) {\n result += ' and'\n }\n\n // Add segment word\n if (result) result += ' '\n result += word\n\n // Add scale word (i=0 is units, i=1 is thousands, etc.)\n if (i > 0) {\n result += ' ' + SCALES[i - 1]\n prevWasScale = true\n } else {\n prevWasScale = false\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to English words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} English words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to English words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in English words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(42) // 'forty-two'\n * toWords(-3.14) // 'minus three point one four'\n * toWords('1000000') // 'one million'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Spanish language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Spanish-specific rules:\n * - Gender agreement: uno/una, veintiuno/veintiuna, hundreds\n * - Special twenties: veinte, veintiuno, veintidós, ... veintinueve\n * - \"y\" conjunction: treinta y uno (only 30-99 with ones)\n * - \"cien\" for exact 100, \"ciento/cienta\" otherwise\n * - Irregular hundreds: quinientos, setecientos, novecientos\n * - Compound long scale: millón, mil millones, billón, mil billones\n * - \"un\" before millón (not \"uno\"), omit before mil\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES_MASC = ['', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve']\nconst ONES_FEM = ['', 'una', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve']\n\nconst TEENS = ['diez', 'once', 'doce', 'trece', 'catorce', 'quince', 'dieciseis', 'diecisiete', 'dieciocho', 'diecinueve']\n\n// 20-29 have special compound forms\nconst TWENTIES_MASC = ['veinte', 'veintiuno', 'veintidós', 'veintitrés', 'veinticuatro', 'veinticinco', 'veintiséis', 'veintisiete', 'veintiocho', 'veintinueve']\nconst TWENTIES_FEM = ['veinte', 'veintiuna', 'veintidós', 'veintitrés', 'veinticuatro', 'veinticinco', 'veintiséis', 'veintisiete', 'veintiocho', 'veintinueve']\n\nconst TENS = ['', '', '', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa']\n\n// Irregular hundreds\nconst HUNDREDS_MASC = ['', 'ciento', 'doscientos', 'trescientos', 'cuatrocientos', 'quinientos', 'seiscientos', 'setecientos', 'ochocientos', 'novecientos']\nconst HUNDREDS_FEM = ['', 'cienta', 'doscientas', 'trescientas', 'cuatrocientas', 'quinientas', 'seiscientas', 'setecientas', 'ochocientas', 'novecientas']\n\n// Scale words (compound long scale)\nconst SCALES = ['millón', 'billón', 'trillón', 'cuatrillón']\nconst SCALES_PLURAL = ['millones', 'billones', 'trillones', 'cuatrillones']\n\nconst THOUSAND = 'mil'\nconst ZERO = 'cero'\nconst NEGATIVE = 'menos'\nconst DECIMAL_SEP = 'punto'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n * @param {number} n - Segment value\n * @param {boolean} feminine - Use feminine forms\n * @returns {string} Spanish word\n */\nfunction buildSegment (n, feminine) {\n if (n === 0) return ''\n\n // Special case: exact 100 is \"cien\" (no gender)\n if (n === 100) return 'cien'\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n const tensOnes = n % 100\n\n const parts = []\n\n // Hundreds\n if (hundreds > 0) {\n const hundredsArr = feminine ? HUNDREDS_FEM : HUNDREDS_MASC\n parts.push(hundredsArr[hundreds])\n }\n\n // Tens and ones\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n // Single digit\n const onesArr = feminine ? ONES_FEM : ONES_MASC\n parts.push(onesArr[tensOnes])\n } else if (tensOnes < 20) {\n // 10-19: teens\n parts.push(TEENS[ones])\n } else if (tensOnes < 30) {\n // 20-29: special twenties\n const twentiesArr = feminine ? TWENTIES_FEM : TWENTIES_MASC\n parts.push(twentiesArr[ones])\n } else {\n // 30-99: tens y ones\n if (ones === 0) {\n parts.push(TENS[tens])\n } else {\n const onesArr = feminine ? ONES_FEM : ONES_MASC\n parts.push(TENS[tens] + ' y ' + onesArr[ones])\n }\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Gets scale word for Spanish compound long scale.\n *\n * @param {number} scaleIndex - Scale level (1 = thousand, 2 = million, etc.)\n * @param {bigint} segment - Segment value for pluralization\n * @returns {string} Scale word\n */\nfunction getScaleWord (scaleIndex, segment) {\n if (scaleIndex === 1) return THOUSAND\n\n // Even indices (2, 4, 6, 8): millón, billón, trillón, cuatrillón\n // Odd indices > 1 (3, 5, 7): mil millones, mil billones, mil trillones\n if (scaleIndex % 2 === 0) {\n const arrayIndex = (scaleIndex / 2) - 1\n const baseWord = SCALES[arrayIndex]\n if (!baseWord) return ''\n return segment > 1n ? SCALES_PLURAL[arrayIndex] : baseWord\n } else {\n // Compound: \"mil millones\" pattern\n const arrayIndex = ((scaleIndex - 1) / 2) - 1\n const pluralWord = SCALES_PLURAL[arrayIndex]\n if (!pluralWord) return THOUSAND\n return THOUSAND + ' ' + pluralWord\n }\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Spanish words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {boolean} feminine - Use feminine forms\n * @returns {string} Spanish words\n */\nfunction integerToWords (n, feminine) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n), feminine)\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n let result\n if (thousands === 1) {\n // \"mil\" not \"uno mil\"\n result = THOUSAND\n } else {\n // Use masculine for thousands segment, but check for \"uno\" → omit before mil\n const thousandsWord = buildSegment(thousands, false)\n // \"uno mil\" → \"mil\" (handled in joinSegments equivalent)\n if (thousandsWord === 'uno' || thousandsWord === 'una') {\n result = THOUSAND\n } else {\n result = thousandsWord + ' ' + THOUSAND\n }\n }\n\n if (remainder > 0) {\n result += ' ' + buildSegment(remainder, feminine)\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n, feminine)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n * Uses BigInt division for faster segment extraction.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @param {boolean} feminine - Use feminine forms\n * @returns {string} Spanish words\n */\nfunction buildLargeNumberWords (n, feminine) {\n // Extract segments using BigInt division (faster than string slicing)\n // Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)\n const segmentValues = []\n let temp = n\n while (temp > 0n) {\n segmentValues.push(temp % 1000n)\n temp = temp / 1000n\n }\n\n // Build result string directly\n let result = ''\n\n for (let i = segmentValues.length - 1; i >= 0; i--) {\n const segment = segmentValues[i]\n if (segment === 0n) continue\n\n const scaleWord = i > 0 ? getScaleWord(i, segment) : ''\n\n if (result) result += ' '\n\n if (i === 0) {\n // Units segment\n result += buildSegment(Number(segment), feminine)\n } else if (i === 1) {\n // Thousands: omit \"uno\" before mil\n if (segment === 1n) {\n result += THOUSAND\n } else {\n result += buildSegment(Number(segment), false) + ' ' + scaleWord\n }\n } else if (i % 2 === 1) {\n // Odd scale indices (3, 5, 7): \"mil millones\", \"mil billones\", etc.\n // Omit \"uno\" before these compound scales\n if (segment === 1n) {\n result += scaleWord\n } else {\n result += buildSegment(Number(segment), false) + ' ' + scaleWord\n }\n } else {\n // Even scale indices (2, 4, 6): millón, billón, trillón\n if (segment === 1n) {\n // \"un millón\" not \"uno millón\"\n result += 'un ' + scaleWord\n } else {\n // Use masculine for scale segment\n result += buildSegment(Number(segment), false) + ' ' + scaleWord\n }\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to Spanish words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {boolean} feminine - Use feminine forms\n * @returns {string} Spanish words for decimal part\n */\nfunction decimalPartToWords (decimalPart, feminine) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder), feminine)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Spanish words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @returns {string} The number in Spanish words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'veintiuno'\n * toWords(21, {gender: 'feminine'}) // 'veintiuna'\n * toWords(1000000) // 'un millón'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n const feminine = options.gender === 'feminine'\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, feminine)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, feminine)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Persian language converter - Functional Implementation\n *\n * Self-contained converter using recursive decomposition.\n *\n * Key features:\n * - \"و\" (and) conjunction for compound numbers\n * - Omit \"یک\" (one) before thousand\n * - Pre-composed hundreds (دویست, سيصد, etc.)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = { 1: 'یک', 2: 'دو', 3: 'سه', 4: 'چهار', 5: 'پنج', 6: 'شش', 7: 'هفت', 8: 'هشت', 9: 'نه' }\nconst TEENS = { 10: 'ده', 11: 'یازده', 12: 'دوازده', 13: 'سیزده', 14: 'چهارده', 15: 'پانزده', 16: 'شانزده', 17: 'هفده', 18: 'هجده', 19: 'نوزده' }\nconst TENS = { 20: 'بیست', 30: 'سی', 40: 'چهل', 50: 'پنجاه', 60: 'شصت', 70: 'هفتاد', 80: 'هشتاد', 90: 'نود' }\nconst HUNDREDS = { 100: 'صد', 200: 'دویست', 300: 'سيصد', 400: 'چهار صد', 500: 'پانصد', 600: 'ششصد', 700: 'هفتصد', 800: 'هشتصد', 900: 'نهصد' }\n\nconst THOUSAND = 'هزار'\nconst MILLION = 'میلیون'\n\nconst ZERO = 'صفر'\nconst NEGATIVE = 'منفى'\nconst DECIMAL_SEP = 'ممیّز'\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // 1-9\n if (n <= 9n) {\n return ONES[Number(n)]\n }\n\n // 10-19\n if (n <= 19n) {\n return TEENS[Number(n)]\n }\n\n // 20-99\n if (n < 100n) {\n const ones = n % 10n\n const tens = n - ones\n if (ones === 0n) {\n return TENS[Number(tens)]\n }\n return `${TENS[Number(tens)]} و ${ONES[Number(ones)]}`\n }\n\n // 100-999\n if (n < 1000n) {\n const hundreds = 100n * (n / 100n)\n const remainder = n - hundreds\n if (remainder === 0n) {\n return HUNDREDS[Number(hundreds)]\n }\n return `${HUNDREDS[Number(hundreds)]} و ${integerToWords(remainder)}`\n }\n\n // 1000-999999\n if (n < 1_000_000n) {\n const thousandMultiplier = n / 1000n\n // Persian omits \"one\" before thousand: 1000 is just \"هزار\", not \"یک هزار\"\n const thousandPrefix = thousandMultiplier === 1n\n ? ''\n : integerToWords(thousandMultiplier) + ' '\n const remainder = n % 1000n\n const suffix = remainder === 0n ? '' : ' ' + integerToWords(remainder)\n return `${thousandPrefix}${THOUSAND}${suffix}`\n }\n\n // 1000000+\n const millionMultiplier = n / 1_000_000n\n const millionPrefix = integerToWords(millionMultiplier) + ' ' + MILLION\n const remainder = n % 1_000_000n\n const suffix = remainder === 0n ? '' : ' و ' + integerToWords(remainder)\n return `${millionPrefix}${suffix}`\n}\n\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Persian words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Persian words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Finnish language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Compound tens+ones without spaces: \"kaksikymmentäyksi\" (21)\n * - Teens with \"-toista\" suffix\n * - Omit \"yksi\" before sata/tuhat but keep before miljoona+\n * - Long scale: miljoona, miljardi, biljoona\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'yksi', 'kaksi', 'kolme', 'neljä', 'viisi', 'kuusi', 'seitsemän', 'kahdeksan', 'yhdeksän']\n\nconst TEENS = ['kymmenen', 'yksitoista', 'kaksitoista', 'kolmetoista', 'neljätoista', 'viisitoista', 'kuusitoista', 'seitsemäntoista', 'kahdeksantoista', 'yhdeksäntoista']\n\n// Tens use \"kymmentä\" suffix\nconst TENS = ['', '', 'kaksikymmentä', 'kolmekymmentä', 'neljäkymmentä', 'viisikymmentä', 'kuusikymmentä', 'seitsemänkymmentä', 'kahdeksankymmentä', 'yhdeksänkymmentä']\n\nconst HUNDRED = 'sata'\nconst THOUSAND = 'tuhat'\n\nconst ZERO = 'nolla'\nconst NEGATIVE = 'miinus'\nconst DECIMAL_SEP = 'pilkku'\n\n// Long scale\nconst SCALES = ['miljoona', 'miljardi', 'biljoona', 'triljoona']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n * Omits \"yksi\" before \"sata\" (hundred).\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds - omit \"yksi\" before sata\n if (hundreds > 0) {\n if (hundreds === 1) {\n parts.push(HUNDRED)\n } else {\n parts.push(ONES[hundreds] + ' ' + HUNDRED)\n }\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n parts.push(ONES[ones])\n } else if (tensOnes < 20) {\n parts.push(TEENS[ones])\n } else if (ones === 0) {\n parts.push(TENS[tens])\n } else {\n // Compound: \"kaksikymmentäyksi\" (no space)\n parts.push(TENS[tens] + ONES[ones])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Finnish words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Finnish words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n // Omit \"yksi\" before tuhat\n let result\n if (thousands === 1) {\n result = THOUSAND\n } else {\n result = buildSegment(thousands) + ' ' + THOUSAND\n }\n\n if (remainder > 0) {\n result += ' ' + buildSegment(remainder)\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Finnish words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const segmentWord = buildSegment(segment)\n\n if (scaleIndex === 0) {\n // Units segment\n parts.push(segmentWord)\n } else if (scaleIndex === 1) {\n // Thousands - omit \"yksi\" before tuhat\n if (segment === 1) {\n parts.push(THOUSAND)\n } else {\n parts.push(segmentWord + ' ' + THOUSAND)\n }\n } else {\n // Millions+ - keep \"yksi\" before scale words\n const scaleWord = SCALES[scaleIndex - 2]\n parts.push(segmentWord + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts decimal digits to Finnish words (per-digit).\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Finnish words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n const parts = []\n\n for (const digit of decimalPart) {\n const d = parseInt(digit, 10)\n if (d === 0) {\n parts.push(ZERO)\n } else {\n parts.push(ONES[d])\n }\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts a numeric value to Finnish words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Finnish words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'kaksikymmentäyksi'\n * toWords(1000) // 'tuhat'\n * toWords('3.14') // 'kolme pilkku yksi neljä'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Filipino language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Linker \"ng\" after vowels: \"isang daang\" (100)\n * - Linker \" na \" after consonants: \"siyam na daang\" (900)\n * - Special tens with linker: \"limampung anim\" (56)\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'isa', 'dalawa', 'tatlo', 'apat', 'lima', 'anim', 'pito', 'walo', 'siyam']\nconst TEENS = ['sampu', 'labinisa', 'labindalawa', 'labintatlo', 'labinapat', 'labinlima', 'labinanum', 'labimpito', 'labingwalo', 'labinsiyam']\nconst TENS = ['', '', 'dalawampu', 'tatlumpu', 'apatnapu', 'limampu', 'animnapu', 'pitumpu', 'walumpu', 'siyamnapu']\n\n// Scale words include linker (end with \"ng\")\nconst HUNDRED = 'daang'\nconst THOUSAND = 'libong'\n\nconst ZERO = 'zero'\nconst NEGATIVE = 'negatibo'\nconst DECIMAL_SEP = 'punto'\n\n// Short scale with linker\nconst SCALE_WORDS = ['', THOUSAND, 'milyong', 'bilyong', 'trilyong']\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nconst VOWELS = ['a', 'e', 'i', 'o', 'u']\n\nfunction addLinker (word) {\n const lastChar = word[word.length - 1]\n if (VOWELS.includes(lastChar)) {\n return word + 'ng'\n }\n return word + ' na'\n}\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds: \"isang daan\", \"dalawang daan\", \"siyam na daan\"\n if (hundredsDigit > 0) {\n const hundredPrefix = addLinker(ONES[hundredsDigit])\n parts.push(hundredPrefix + ' ' + HUNDRED)\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n // Single digit\n parts.push(ONES[ones])\n } else if (tensOnes < 20) {\n // Teens (10-19)\n parts.push(TEENS[ones])\n } else if (ones === 0) {\n // Even tens (20, 30, 40, etc.)\n parts.push(TENS[tensDigit])\n } else {\n // Tens + ones\n // limampu (50) gets special linker: \"limampung anim\" (56)\n if (tensDigit === 5) {\n parts.push(TENS[tensDigit] + 'ng ' + ONES[ones])\n } else {\n parts.push(TENS[tensDigit] + ' ' + ONES[ones])\n }\n }\n\n return parts.join(' ')\n}\n\n/**\n * Builds segment with linker added to last word (for use before scale words).\n */\nfunction buildSegmentWithLinker (n) {\n const segmentWord = buildSegment(n)\n if (!segmentWord) return ''\n\n // Find the last space to get the last word\n const lastSpaceIdx = segmentWord.lastIndexOf(' ')\n if (lastSpaceIdx === -1) {\n // Single word\n const lastChar = segmentWord[segmentWord.length - 1]\n if (lastChar === 'g' && segmentWord.endsWith('ng')) {\n return segmentWord // Already has linker\n }\n return addLinker(segmentWord)\n }\n\n // Multi-word: add linker to last word\n const prefix = segmentWord.slice(0, lastSpaceIdx + 1)\n const lastWord = segmentWord.slice(lastSpaceIdx + 1)\n\n if (lastWord.endsWith('ng')) {\n return segmentWord // Already has linker\n }\n return prefix + addLinker(lastWord)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for large numbers using BigInt division.\n * @param {bigint} n - Number >= 1000\n * @returns {string} Filipino words\n */\nfunction buildLargeNumberWords (n) {\n // Extract segments using BigInt division (faster than string slicing)\n const segmentValues = []\n let temp = n\n while (temp > 0n) {\n segmentValues.push(Number(temp % 1000n))\n temp = temp / 1000n\n }\n\n // Build result string directly\n let result = ''\n\n for (let i = segmentValues.length - 1; i >= 0; i--) {\n const segment = segmentValues[i]\n if (segment === 0) continue\n\n const scaleWord = SCALE_WORDS[i] || ''\n\n if (result) result += ' '\n\n if (i === 0) {\n result += buildSegment(segment)\n } else {\n // Add linker to segment before scale word\n const segmentWord = buildSegmentWithLinker(segment)\n result += segmentWord + ' ' + scaleWord\n }\n }\n\n return result\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Filipino words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Filipino words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * French language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * French-specific rules:\n * - Vigesimal patterns: 70 = soixante-dix, 80 = quatre-vingts, 90 = quatre-vingt-dix\n * - \"et\" conjunction: vingt et un (21), soixante et onze (71), but NOT quatre-vingt-un\n * - Pluralization: \"cents\" loses 's' when followed by more digits\n * - Long scale with -ard forms: milliard, billiard, trilliard\n * - Omit \"un\" before mille\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf']\nconst TEENS = ['dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf']\nconst TENS = ['', '', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante']\n\n// Scale words (even indices: million, billion, trillion, quadrillion)\nconst SCALES = ['million', 'billion', 'trillion', 'quadrillion']\nconst SCALES_ARD = ['milliard', 'billiard', 'trilliard', 'quadrilliard']\n\nconst THOUSAND = 'mille'\nconst HUNDRED = 'cent'\nconst ZERO = 'zéro'\nconst NEGATIVE = 'moins'\nconst DECIMAL_SEP = 'virgule'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n * Returns object with { word, endsWithCents, endsWithVingts } for pluralization handling.\n */\nfunction buildSegment (n) {\n if (n === 0) return { word: '', endsWithCents: false, endsWithVingts: false }\n\n const tensOnes = n % 100\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n let endsWithCents = false\n let endsWithVingts = false\n\n // Hundreds\n if (hundreds > 0) {\n if (hundreds === 1) {\n if (tensOnes === 0) {\n parts.push(HUNDRED)\n } else {\n parts.push(HUNDRED)\n }\n } else {\n if (tensOnes === 0) {\n // \"deux cents\", \"trois cents\" (with 's')\n parts.push(ONES[hundreds] + ' ' + HUNDRED + 's')\n endsWithCents = true\n } else {\n // \"deux cent\", \"trois cent\" (no 's' when followed by more)\n parts.push(ONES[hundreds] + ' ' + HUNDRED)\n }\n }\n }\n\n // Tens and ones - vigesimal pattern\n if (tensOnes === 0) {\n // Just hundreds, nothing more\n } else if (tensOnes < 10) {\n // Single digit\n parts.push(ONES[tensOnes])\n } else if (tensOnes < 17) {\n // 10-16: regular teens\n parts.push(TEENS[tensOnes - 10])\n } else if (tensOnes < 20) {\n // 17-19: dix-sept, dix-huit, dix-neuf\n parts.push(TEENS[tensOnes - 10])\n } else if (tensOnes < 70) {\n // 20-69: standard tens + ones\n const t = Math.floor(tensOnes / 10)\n const o = tensOnes % 10\n if (o === 0) {\n parts.push(TENS[t])\n } else if (o === 1) {\n // \"et un\" for 21, 31, 41, 51, 61\n parts.push(TENS[t] + ' et ' + ONES[1])\n } else {\n parts.push(TENS[t] + '-' + ONES[o])\n }\n } else if (tensOnes < 80) {\n // 70-79: soixante-dix, soixante et onze, soixante-douze...\n const remainder = tensOnes - 60\n if (remainder === 11) {\n // 71: soixante et onze\n parts.push('soixante et onze')\n } else {\n // 70, 72-79: soixante-dix, soixante-douze...\n parts.push('soixante-' + TEENS[remainder - 10])\n }\n } else if (tensOnes === 80) {\n // 80: quatre-vingts (with 's')\n parts.push('quatre-vingts')\n endsWithVingts = true\n } else if (tensOnes < 100) {\n // 81-99: quatre-vingt-un, quatre-vingt-dix...\n const remainder = tensOnes - 80\n if (remainder < 10) {\n // 81-89\n parts.push('quatre-vingt-' + ONES[remainder])\n } else {\n // 90-99\n parts.push('quatre-vingt-' + TEENS[remainder - 10])\n }\n }\n\n // Join parts with space (between hundreds and rest)\n return { word: parts.join(' '), endsWithCents, endsWithVingts }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Gets scale word for French long scale with -ard pattern.\n *\n * @param {number} scaleIndex - Scale level (1 = thousand, 2 = million, etc.)\n * @param {bigint} segment - Segment value for pluralization\n * @returns {string} Scale word\n */\nfunction getScaleWord (scaleIndex, segment) {\n if (scaleIndex === 1) return THOUSAND\n\n // Even indices (2, 4, 6, 8): million, billion, trillion, quadrillion\n // Odd indices > 1 (3, 5, 7, 9): milliard, billiard, trilliard, quadrilliard\n if (scaleIndex % 2 === 0) {\n const arrayIndex = (scaleIndex / 2) - 1\n const baseWord = SCALES[arrayIndex]\n if (!baseWord) return ''\n return segment > 1n ? baseWord + 's' : baseWord\n } else {\n const arrayIndex = ((scaleIndex - 1) / 2) - 1\n const ardWord = SCALES_ARD[arrayIndex]\n if (!ardWord) return THOUSAND\n return segment > 1n ? ardWord + 's' : ardWord\n }\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to French words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {boolean} withHyphen - Whether to use hyphen separators\n * @returns {string} French words\n */\nfunction integerToWords (n, withHyphen = false) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n const { word } = buildSegment(Number(n))\n return withHyphen ? word.replace(/ /g, '-') : word\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n let result\n if (thousands === 1) {\n // \"mille\" not \"un mille\"\n result = THOUSAND\n } else {\n // Check if segment ends with \"cents\" or \"vingts\" - need to strip 's' before mille\n const { word: thousandsWord, endsWithCents, endsWithVingts } = buildSegment(thousands)\n let adjustedWord = thousandsWord\n if (endsWithCents || endsWithVingts) {\n adjustedWord = thousandsWord.slice(0, -1) // Remove trailing 's'\n }\n result = adjustedWord + (withHyphen ? '-' : ' ') + THOUSAND\n }\n\n if (remainder > 0) {\n const { word: remainderWord } = buildSegment(remainder)\n result += (withHyphen ? '-' : ' ') + remainderWord\n }\n\n if (withHyphen) {\n result = result.replace(/ /g, '-')\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n, withHyphen)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @param {boolean} withHyphen - Whether to use hyphen separators\n * @returns {string} French words\n */\nfunction buildLargeNumberWords (n, withHyphen) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const scaleWord = scaleIndex > 0 ? getScaleWord(scaleIndex, BigInt(segment)) : ''\n const { word: segWords, endsWithCents, endsWithVingts } = buildSegment(segment)\n\n if (scaleIndex === 0) {\n // Units segment\n parts.push(segWords)\n } else if (scaleIndex === 1) {\n // Thousands: \"mille\" not \"un mille\"\n if (segment === 1) {\n parts.push(THOUSAND)\n } else {\n // Strip 's' from cents/vingts before mille\n let adjustedWord = segWords\n if (endsWithCents || endsWithVingts) {\n adjustedWord = segWords.slice(0, -1)\n }\n parts.push(adjustedWord)\n parts.push(scaleWord)\n }\n } else {\n // Million and above\n parts.push(segWords)\n parts.push(scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n const sep = withHyphen ? '-' : ' '\n let result = parts.join(sep)\n\n if (withHyphen) {\n result = result.replace(/ /g, '-')\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to French words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {boolean} withHyphen - Whether to use hyphen separators\n * @returns {string} French words for decimal part\n */\nfunction decimalPartToWords (decimalPart, withHyphen) {\n let result = ''\n const sep = withHyphen ? '-' : ' '\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += sep\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += sep\n result += integerToWords(BigInt(remainder), withHyphen)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to French words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.withHyphenSeparator=false] - Use hyphens between all words\n * @returns {string} The number in French words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'vingt et un'\n * toWords(80) // 'quatre-vingts'\n * toWords(1000000) // 'un million'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n const withHyphen = options.withHyphenSeparator || false\n\n let result = ''\n const sep = withHyphen ? '-' : ' '\n\n if (isNegative) {\n result = NEGATIVE + sep\n }\n\n result += integerToWords(integerPart, withHyphen)\n\n if (decimalPart) {\n result += sep + DECIMAL_SEP + sep + decimalPartToWords(decimalPart, withHyphen)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * French (Belgium) language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Belgian French differences from standard French:\n * - septante (70) instead of soixante-dix\n * - nonante (90) instead of quatre-vingt-dix\n * - Keeps quatre-vingts (80) like standard French\n * - Uses \"septante et un\" (71), \"nonante et un\" (91)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf']\nconst TEENS = ['dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf']\nconst TENS = ['', '', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante', 'septante', 'quatre-vingt', 'nonante']\n\n// Scale words (long scale with -ard forms)\nconst SCALES = ['million', 'billion', 'trillion', 'quadrillion']\nconst SCALES_ARD = ['milliard', 'billiard', 'trilliard', 'quadrilliard']\n\nconst THOUSAND = 'mille'\nconst HUNDRED = 'cent'\nconst ZERO = 'zéro'\nconst NEGATIVE = 'moins'\nconst DECIMAL_SEP = 'virgule'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return { word: '', endsWithCents: false, endsWithVingts: false }\n\n const tensOnes = n % 100\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n let endsWithCents = false\n let endsWithVingts = false\n\n // Hundreds\n if (hundreds > 0) {\n if (hundreds === 1) {\n parts.push(HUNDRED)\n } else {\n if (tensOnes === 0) {\n parts.push(ONES[hundreds] + ' ' + HUNDRED + 's')\n endsWithCents = true\n } else {\n parts.push(ONES[hundreds] + ' ' + HUNDRED)\n }\n }\n }\n\n // Tens and ones - Belgian pattern\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n parts.push(ONES[tensOnes])\n } else if (tensOnes < 17) {\n parts.push(TEENS[tensOnes - 10])\n } else if (tensOnes < 20) {\n parts.push(TEENS[tensOnes - 10])\n } else if (tensOnes < 70) {\n // 20-69: standard pattern\n const t = Math.floor(tensOnes / 10)\n const o = tensOnes % 10\n if (o === 0) {\n parts.push(TENS[t])\n } else if (o === 1) {\n parts.push(TENS[t] + ' et ' + ONES[1])\n } else {\n parts.push(TENS[t] + '-' + ONES[o])\n }\n } else if (tensOnes < 80) {\n // 70-79: septante pattern (Belgian)\n const o = tensOnes % 10\n if (o === 0) {\n parts.push('septante')\n } else if (o === 1) {\n parts.push('septante et ' + ONES[1])\n } else {\n parts.push('septante-' + ONES[o])\n }\n } else if (tensOnes === 80) {\n // 80: quatre-vingts (same as standard)\n parts.push('quatre-vingts')\n endsWithVingts = true\n } else if (tensOnes < 90) {\n // 81-89: quatre-vingt-X (same as standard)\n const remainder = tensOnes - 80\n parts.push('quatre-vingt-' + ONES[remainder])\n } else {\n // 90-99: nonante pattern (Belgian)\n const o = tensOnes % 10\n if (o === 0) {\n parts.push('nonante')\n } else if (o === 1) {\n parts.push('nonante et ' + ONES[1])\n } else {\n parts.push('nonante-' + ONES[o])\n }\n }\n\n return { word: parts.join(' '), endsWithCents, endsWithVingts }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nfunction getScaleWord (scaleIndex, segment) {\n if (scaleIndex === 1) return THOUSAND\n\n if (scaleIndex % 2 === 0) {\n const arrayIndex = (scaleIndex / 2) - 1\n const baseWord = SCALES[arrayIndex]\n if (!baseWord) return ''\n return segment > 1n ? baseWord + 's' : baseWord\n } else {\n const arrayIndex = ((scaleIndex - 1) / 2) - 1\n const ardWord = SCALES_ARD[arrayIndex]\n if (!ardWord) return THOUSAND\n return segment > 1n ? ardWord + 's' : ardWord\n }\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, withHyphen = false) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n const { word } = buildSegment(Number(n))\n return withHyphen ? word.replace(/ /g, '-') : word\n }\n\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n let result\n if (thousands === 1) {\n result = THOUSAND\n } else {\n const { word: thousandsWord, endsWithCents, endsWithVingts } = buildSegment(thousands)\n let adjustedWord = thousandsWord\n if (endsWithCents || endsWithVingts) {\n adjustedWord = thousandsWord.slice(0, -1)\n }\n result = adjustedWord + (withHyphen ? '-' : ' ') + THOUSAND\n }\n\n if (remainder > 0) {\n const { word: remainderWord } = buildSegment(remainder)\n result += (withHyphen ? '-' : ' ') + remainderWord\n }\n\n if (withHyphen) {\n result = result.replace(/ /g, '-')\n }\n\n return result\n }\n\n return buildLargeNumberWords(n, withHyphen)\n}\n\nfunction buildLargeNumberWords (n, withHyphen) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const scaleWord = scaleIndex > 0 ? getScaleWord(scaleIndex, BigInt(segment)) : ''\n const { word: segWords, endsWithCents, endsWithVingts } = buildSegment(segment)\n\n if (scaleIndex === 0) {\n parts.push(segWords)\n } else if (scaleIndex === 1) {\n if (segment === 1) {\n parts.push(THOUSAND)\n } else {\n let adjustedWord = segWords\n if (endsWithCents || endsWithVingts) {\n adjustedWord = segWords.slice(0, -1)\n }\n parts.push(adjustedWord)\n parts.push(scaleWord)\n }\n } else {\n parts.push(segWords)\n parts.push(scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n const sep = withHyphen ? '-' : ' '\n let result = parts.join(sep)\n\n if (withHyphen) {\n result = result.replace(/ /g, '-')\n }\n\n return result\n}\n\nfunction decimalPartToWords (decimalPart, withHyphen) {\n let result = ''\n const sep = withHyphen ? '-' : ' '\n\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += sep\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += sep\n result += integerToWords(BigInt(remainder), withHyphen)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Belgian French words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.withHyphenSeparator=false] - Use hyphens between words\n * @returns {string} The number in Belgian French words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n const withHyphen = options.withHyphenSeparator || false\n\n let result = ''\n const sep = withHyphen ? '-' : ' '\n\n if (isNegative) {\n result = NEGATIVE + sep\n }\n\n result += integerToWords(integerPart, withHyphen)\n\n if (decimalPart) {\n result += sep + DECIMAL_SEP + sep + decimalPartToWords(decimalPart, withHyphen)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Gujarati language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Indian numbering system (હજાર, લાખ, કરોડ)\n * - Gujarati script\n * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)\n * - Complete word forms for 0-99\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'શૂન્ય'\nconst NEGATIVE = 'ઋણ'\nconst DECIMAL_SEP = 'દશાંશ'\nconst HUNDRED = 'સો'\n\nconst BELOW_HUNDRED = [\n 'શૂન્ય', 'એક', 'બે', 'ત્રણ', 'ચાર', 'પાંચ', 'છ', 'સાત', 'આઠ', 'નવ',\n 'દસ', 'અગિયાર', 'બાર', 'તેર', 'ચૌદ', 'પંદર', 'સોળ', 'સત્તર', 'અઢાર', 'ઓગણીસ',\n 'વીસ', 'એકવીસ', 'બાવીસ', 'ત્રેવીસ', 'ચોવીસ', 'પચીસ', 'છવ્વીસ', 'સત્તાવીસ', 'અઠ્ઠાવીસ', 'ઓગણત્રીસ',\n 'ત્રીસ', 'એકત્રીસ', 'બત્રીસ', 'તેત્રીસ', 'ચોત્રીસ', 'પાંત્રીસ', 'છત્રીસ', 'સાડત્રીસ', 'અડત્રીસ', 'ઓગણચાલીસ',\n 'ચાલીસ', 'એકતાલીસ', 'બેતાળીસ', 'ત્રેતાળીસ', 'ચુંમાલીસ', 'પિસ્તાલીસ', 'છેતાળીસ', 'સુડતાળીસ', 'અડતાળીસ', 'ઓગણપચાસ',\n 'પચાસ', 'એકાવન', 'બાવન', 'ત્રેપન', 'ચોપન', 'પંચાવન', 'છપ્પન', 'સત્તાવન', 'અઠ્ઠાવન', 'ઓગણસાઠ',\n 'સાઠ', 'એકસઠ', 'બાસઠ', 'ત્રેસઠ', 'ચોસઠ', 'પાંસઠ', 'છાસઠ', 'સડસઠ', 'અડસઠ', 'અગણોસિત્તેર',\n 'સિત્તેર', 'એકોતેર', 'બોતેર', 'તોતેર', 'ચુમોતેર', 'પંચોતેર', 'છોતેર', 'સિત્યોતેર', 'ઇઠ્યોતેર', 'ઓગણાએંસી',\n 'એંસી', 'એક્યાસી', 'બ્યાસી', 'ત્યાસી', 'ચોર્યાસી', 'પંચાસી', 'છ્યાસી', 'સિત્યાસી', 'અઠ્યાસી', 'નેવ્યાસી',\n 'નેવું', 'એકાણું', 'બાણું', 'ત્રાણું', 'ચોરાણું', 'પંચાણું', 'છન્નું', 'સત્તાણું', 'અઠ્ઠાણું', 'નવ્વાણું'\n]\n\n// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.\nconst SCALE_WORDS = ['', 'હજાર', 'લાખ', 'કરોડ', 'અબજ', 'ખરબ', 'નીલ', 'પદ્મ', 'શંખ']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds words for a 0-999 segment.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED\n }\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Gujarati words.\n *\n * Uses BigInt modulo for segment extraction (faster than string slicing).\n * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Gujarati words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n segments.push(Number(n % 1000n))\n let temp = n / 1000n\n\n while (temp > 0n) {\n segments.push(Number(temp % 100n))\n temp = temp / 100n\n }\n\n // Build result string (process from most-significant to least)\n const words = []\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n words.push(buildSegment(segment))\n } else {\n words.push(BELOW_HUNDRED[segment])\n }\n\n if (i > 0 && SCALE_WORDS[i]) {\n words.push(SCALE_WORDS[i])\n }\n }\n\n return words.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : BELOW_HUNDRED[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Gujarati words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Gujarati words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Hausa language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Authentic Boko orthography with ɗ (hooked d) and ' (glottal stop)\n * - Teens with \"sha\" prefix (sha ɗaya = 11)\n * - Compound numbers with \"da\" connector (ashirin da ɗaya = 21)\n * - Arabic loanwords for tens (ashirin, talatin, arba'in, etc.)\n * - Reversed multiplier order: \"biyu ɗari\" (200), \"biyu dubu\" (2000)\n * - Implicit one before ɗari and dubu\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'ɗaya', 'biyu', 'uku', 'huɗu', 'biyar', 'shida', 'bakwai', 'takwas', 'tara']\nconst TEENS = ['goma', 'sha ɗaya', 'sha biyu', 'sha uku', 'sha huɗu', 'sha biyar', 'sha shida', 'sha bakwai', 'sha takwas', 'sha tara']\n// Arabic loanwords for tens\nconst TENS = ['', '', 'ashirin', 'talatin', \"arba'in\", 'hamsin', 'sittin', \"saba'in\", 'tamanin', \"tis'in\"]\n\nconst HUNDRED = 'ɗari'\nconst THOUSAND = 'dubu'\n\nconst ZERO = 'sifiri'\nconst NEGATIVE = 'babu'\nconst DECIMAL_SEP = 'digo'\n\n// Short scale\nconst SCALE_WORDS = ['', THOUSAND, 'miliyan', 'biliyan']\n\n// ============================================================================\n// Precomputed Lookup Table\n// ============================================================================\n\n/**\n * Build segment for 0-999 with Hausa patterns.\n * Hausa uses reversed order for hundreds: \"biyu ɗari\" (200)\n * And \"da\" connector for ones: \"ashirin da ɗaya\" (21)\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds: implicit one, or \"biyu ɗari\" (reversed order)\n if (hundredsDigit > 0) {\n if (hundredsDigit === 1) {\n parts.push(HUNDRED)\n } else {\n // Reversed: multiplier + hundredWord\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED)\n }\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n // Single digit - with \"da\" connector if after hundreds\n if (hundredsDigit > 0) {\n parts.push('da ' + ONES[ones])\n } else {\n parts.push(ONES[ones])\n }\n } else if (tensOnes < 20) {\n // Teens (10-19): \"sha X\"\n parts.push(TEENS[ones])\n } else if (ones === 0) {\n // Even tens (20, 30, 40, etc.)\n parts.push(TENS[tensDigit])\n } else {\n // Tens + ones with \"da\" connector\n parts.push(TENS[tensDigit] + ' da ' + ONES[ones])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n return buildLargeNumberWords(n)\n}\n\n/**\n * Checks if a word is a single digit (1-9).\n */\nfunction isSingleDigit (word) {\n return ONES.slice(1).includes(word)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Build raw parts (segment words and scale words)\n const rawParts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const scaleWord = SCALE_WORDS[scaleIndex] || ''\n\n if (scaleIndex === 0) {\n rawParts.push(buildSegment(segment))\n } else {\n rawParts.push(buildSegment(segment))\n rawParts.push(scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n // Filter out implicit \"ɗaya\" before ɗari or dubu\n const filtered = []\n for (let i = 0; i < rawParts.length; i++) {\n const part = rawParts[i]\n const nextPart = rawParts[i + 1]\n\n // Skip \"ɗaya\" before ɗari or dubu (implicit one)\n if (part === 'ɗaya' && nextPart && (nextPart === HUNDRED || nextPart === THOUSAND)) {\n continue\n }\n\n filtered.push(part)\n }\n\n // Join with correct separators\n const result = []\n for (let i = 0; i < filtered.length; i++) {\n const part = filtered[i]\n const prevPart = i > 0 ? filtered[i - 1] : null\n\n // Determine if we need \"da\" connector\n // Use \"da\" when current is a single digit following a scale word\n if (prevPart && isSingleDigit(part) &&\n (prevPart === THOUSAND || prevPart === HUNDRED ||\n SCALE_WORDS.includes(prevPart))) {\n result.push(' da ')\n } else if (i > 0) {\n result.push(' ')\n }\n\n result.push(part)\n }\n\n return result.join('')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Hausa words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Hausa words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Biblical Hebrew language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Gender agreement (masculine default, feminine via option)\n * - Dual forms for 2, 200, 2000\n * - Special 1-9 thousands construct state\n * - \"ו\" (ve) conjunction rules vary by position\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (arrays for indexed access - faster than object property lookup)\n// ============================================================================\n\n// Masculine forms (default in Biblical Hebrew) - index 0 unused\nconst ONES_MASC = ['', 'אחד', 'שניים', 'שלשה', 'ארבעה', 'חמשה', 'ששה', 'שבעה', 'שמונה', 'תשעה']\nconst TEENS_MASC = ['עשרה', 'אחד עשר', 'שנים עשר', 'שלשה עשר', 'ארבעה עשר', 'חמשה עשר', 'ששה עשר', 'שבעה עשר', 'שמונה עשר', 'תשעה עשר']\nconst THOUSANDS_MASC = ['', 'אלף', 'אלפיים', 'שלשה אלפים', 'ארבעה אלפים', 'חמשה אלפים', 'ששה אלפים', 'שבעה אלפים', 'שמונה אלפים', 'תשעה אלפים']\n\n// Feminine forms - index 0 unused\nconst ONES_FEM = ['', 'אחת', 'שתים', 'שלש', 'ארבע', 'חמש', 'שש', 'שבע', 'שמונה', 'תשע']\nconst TEENS_FEM = ['עשר', 'אחת עשרה', 'שתים עשרה', 'שלש עשרה', 'ארבע עשרה', 'חמש עשרה', 'שש עשרה', 'שבע עשרה', 'שמונה עשרה', 'תשע עשרה']\nconst THOUSANDS_FEM = ['', 'אלף', 'אלפיים', 'שלשת אלפים', 'ארבעת אלפים', 'חמשת אלפים', 'ששת אלפים', 'שבעת אלפים', 'שמונת אלפים', 'תשעת אלפים']\n\n// Shared vocabulary\nconst TENS = ['', '', 'עשרים', 'שלשים', 'ארבעים', 'חמישים', 'ששים', 'שבעים', 'שמונים', 'תשעים']\nconst HUNDREDS = ['', 'מאה', 'מאתיים', 'שלשה מאות', 'ארבעה מאות', 'חמשה מאות', 'ששה מאות', 'שבעה מאות', 'שמונה מאות', 'תשעה מאות']\nconst HUNDREDS_FEM = ['', 'מאה', 'מאתיים', 'שלש מאות', 'ארבע מאות', 'חמש מאות', 'שש מאות', 'שבע מאות', 'שמונה מאות', 'תשע מאות']\n\n// Scale words (index 1 = thousands, 2 = millions, etc.)\nconst SCALE = ['', 'אלף', 'מיליון', 'מיליארד', 'טריליון', 'קוודרליון', 'קווינטיליון']\nconst SCALE_PLURAL = ['', 'אלפים', 'מיליונים', 'מיליארדים', 'טריליונים', 'קוודרליונים', 'קווינטיליונים']\n\nconst ZERO = 'אפס'\nconst NEGATIVE = 'מינוס'\nconst DECIMAL_SEP = 'נקודה'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for scale segments (thousands, millions, etc.).\n * \"ו\" is added before tens and ones when following hundreds.\n */\nfunction buildScaleSegment (n, andWord, ONES, TEENS, HUNDREDS_ARR) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n let result = ''\n\n // Hundreds\n if (hundreds > 0) {\n result = HUNDREDS_ARR[hundreds]\n }\n\n // Tens and ones\n if (tens === 1) {\n // Teens (10-19)\n const teenWord = TEENS[ones]\n if (result) {\n result += ' ' + andWord + teenWord\n } else {\n result = teenWord\n }\n } else {\n // Tens (20-90)\n if (tens >= 2) {\n if (result) {\n result += ' ' + andWord + TENS[tens]\n } else {\n result = TENS[tens]\n }\n }\n\n // Ones\n if (ones > 0) {\n if (result) {\n result += ' ' + andWord + ONES[ones]\n } else {\n result = ONES[ones]\n }\n }\n }\n\n return result\n}\n\n/**\n * Builds segment word for units segment (no scale word).\n * \"ו\" is only added before the final ones digit.\n */\nfunction buildUnitsSegment (n, andWord, ONES, TEENS, HUNDREDS_ARR) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n let result = ''\n\n // Hundreds\n if (hundreds > 0) {\n result = HUNDREDS_ARR[hundreds]\n }\n\n // Tens (no conjunction)\n if (tens === 1) {\n // Teens (10-19)\n if (result) {\n result += ' ' + TEENS[ones]\n } else {\n result = TEENS[ones]\n }\n } else {\n if (tens >= 2) {\n if (result) {\n result += ' ' + TENS[tens]\n } else {\n result = TENS[tens]\n }\n }\n\n // Ones - conjunction only here\n if (ones > 0) {\n if (result) {\n result += ' ' + andWord + ONES[ones]\n } else {\n result = ONES[ones]\n }\n }\n }\n\n return result\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Biblical Hebrew words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {Object} options - Conversion options\n * @returns {string} Biblical Hebrew words\n */\nfunction integerToWords (n, options) {\n if (n === 0n) return ZERO\n\n const andWord = options.andWord ?? 'ו'\n const gender = options.gender || 'masculine'\n const isFeminine = gender === 'feminine'\n\n // Select vocabulary based on gender\n const ONES = isFeminine ? ONES_FEM : ONES_MASC\n const TEENS = isFeminine ? TEENS_FEM : TEENS_MASC\n const THOUSANDS_SPECIAL = isFeminine ? THOUSANDS_FEM : THOUSANDS_MASC\n const HUNDREDS_ARR = isFeminine ? HUNDREDS_FEM : HUNDREDS\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildUnitsSegment(Number(n), andWord, ONES, TEENS, HUNDREDS_ARR)\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n let temp = n\n while (temp > 0n) {\n segments.push(Number(temp % 1000n))\n temp = temp / 1000n\n }\n\n // Build result string directly\n let result = ''\n\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n // Units segment (no scale word)\n const segmentWord = buildUnitsSegment(segment, andWord, ONES, TEENS, HUNDREDS_ARR)\n if (result) {\n // Add \"ו\" before single-digit units when following scale words\n if (segment <= 9) {\n result += ' ' + andWord + segmentWord\n } else {\n result += ' ' + segmentWord\n }\n } else {\n result = segmentWord\n }\n } else if (i === 1) {\n // Thousands - special handling for 1-9\n if (segment <= 9) {\n if (result) result += ' '\n result += THOUSANDS_SPECIAL[segment]\n } else {\n const segmentWord = buildScaleSegment(segment, andWord, ONES, TEENS, HUNDREDS_ARR)\n if (result) result += ' '\n result += segmentWord + ' ' + SCALE[1]\n }\n } else {\n // Millions and above\n if (segment === 1) {\n if (result) result += ' '\n result += SCALE[i]\n } else {\n const segmentWord = buildScaleSegment(segment, andWord, ONES, TEENS, HUNDREDS_ARR)\n if (result) result += ' '\n result += segmentWord + ' ' + SCALE_PLURAL[i]\n }\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to Biblical Hebrew words (digit by digit).\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {Object} options - Conversion options\n * @returns {string} Biblical Hebrew words for decimal part\n */\nfunction decimalPartToWords (decimalPart, options) {\n const gender = options.gender || 'masculine'\n const ONES = gender === 'feminine' ? ONES_FEM : ONES_MASC\n\n let result = ''\n for (let i = 0; i < decimalPart.length; i++) {\n const d = parseInt(decimalPart[i], 10)\n if (result) result += ' '\n result += d === 0 ? ZERO : ONES[d]\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Biblical Hebrew words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @param {string} [options.andWord] - Custom conjunction word\n * @returns {string} The number in Biblical Hebrew words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Modern Hebrew language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Feminine grammatical forms (default in Modern Hebrew)\n * - Dual forms for 2, 200, 2000\n * - Special 1-9 thousands construct state\n * - \"ו\" (ve) conjunction rules vary by position\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (arrays for indexed access - faster than object property lookup)\n// ============================================================================\n\n// Feminine forms (default in Modern Hebrew) - index 0 unused\nconst ONES = ['', 'אחת', 'שתים', 'שלש', 'ארבע', 'חמש', 'שש', 'שבע', 'שמונה', 'תשע']\nconst TEENS = ['עשר', 'אחת עשרה', 'שתים עשרה', 'שלש עשרה', 'ארבע עשרה', 'חמש עשרה', 'שש עשרה', 'שבע עשרה', 'שמונה עשרה', 'תשע עשרה']\nconst TENS = ['', '', 'עשרים', 'שלשים', 'ארבעים', 'חמישים', 'ששים', 'שבעים', 'שמונים', 'תשעים']\nconst HUNDREDS = ['', 'מאה', 'מאתיים', 'שלש מאות', 'ארבע מאות', 'חמש מאות', 'שש מאות', 'שבע מאות', 'שמונה מאות', 'תשע מאות']\n\n// Special forms for 1-9 thousand (index 0 unused)\nconst THOUSANDS_SPECIAL = ['', 'אלף', 'אלפיים', 'שלשת אלפים', 'ארבעת אלפים', 'חמשת אלפים', 'ששת אלפים', 'שבעת אלפים', 'שמונת אלפים', 'תשעת אלפים']\n\n// Scale words (index 1 = thousands, 2 = millions, etc.)\nconst SCALE = ['', 'אלף', 'מיליון', 'מיליארד', 'טריליון', 'קוודרליון', 'קווינטיליון']\nconst SCALE_PLURAL = ['', 'אלפים', 'מיליונים', 'מיליארדים', 'טריליונים', 'קוודרליונים', 'קווינטיליונים']\n\nconst ZERO = 'אפס'\nconst NEGATIVE = 'מינוס'\nconst DECIMAL_SEP = 'נקודה'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for scale segments (thousands, millions, etc.).\n * \"ו\" is added before tens and ones when following hundreds.\n */\nfunction buildScaleSegment (n, andWord) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n let result = ''\n\n // Hundreds\n if (hundreds > 0) {\n result = HUNDREDS[hundreds]\n }\n\n // Tens and ones\n if (tens === 1) {\n // Teens (10-19)\n const teenWord = TEENS[ones]\n if (result) {\n result += ' ' + andWord + teenWord\n } else {\n result = teenWord\n }\n } else {\n // Tens (20-90)\n if (tens >= 2) {\n if (result) {\n result += ' ' + andWord + TENS[tens]\n } else {\n result = TENS[tens]\n }\n }\n\n // Ones\n if (ones > 0) {\n if (result) {\n result += ' ' + andWord + ONES[ones]\n } else {\n result = ONES[ones]\n }\n }\n }\n\n return result\n}\n\n/**\n * Builds segment word for units segment (no scale word).\n * \"ו\" is only added before the final ones digit.\n */\nfunction buildUnitsSegment (n, andWord) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n let result = ''\n\n // Hundreds\n if (hundreds > 0) {\n result = HUNDREDS[hundreds]\n }\n\n // Tens (no conjunction)\n if (tens === 1) {\n // Teens (10-19)\n if (result) {\n result += ' ' + TEENS[ones]\n } else {\n result = TEENS[ones]\n }\n } else {\n if (tens >= 2) {\n if (result) {\n result += ' ' + TENS[tens]\n } else {\n result = TENS[tens]\n }\n }\n\n // Ones - conjunction only here\n if (ones > 0) {\n if (result) {\n result += ' ' + andWord + ONES[ones]\n } else {\n result = ONES[ones]\n }\n }\n }\n\n return result\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Hebrew words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {Object} options - Conversion options\n * @returns {string} Hebrew words\n */\nfunction integerToWords (n, options) {\n if (n === 0n) return ZERO\n\n const andWord = options.andWord ?? 'ו'\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildUnitsSegment(Number(n), andWord)\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n let temp = n\n while (temp > 0n) {\n segments.push(Number(temp % 1000n))\n temp = temp / 1000n\n }\n\n // Build result string directly\n let result = ''\n\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n // Units segment (no scale word)\n const segmentWord = buildUnitsSegment(segment, andWord)\n if (result) {\n // Add \"ו\" before single-digit units when following scale words\n if (segment <= 9) {\n result += ' ' + andWord + segmentWord\n } else {\n result += ' ' + segmentWord\n }\n } else {\n result = segmentWord\n }\n } else if (i === 1) {\n // Thousands - special handling for 1-9\n if (segment <= 9) {\n if (result) result += ' '\n result += THOUSANDS_SPECIAL[segment]\n } else {\n const segmentWord = buildScaleSegment(segment, andWord)\n if (result) result += ' '\n result += segmentWord + ' ' + SCALE[1]\n }\n } else {\n // Millions and above\n if (segment === 1) {\n if (result) result += ' '\n result += SCALE[i]\n } else {\n const segmentWord = buildScaleSegment(segment, andWord)\n if (result) result += ' '\n result += segmentWord + ' ' + SCALE_PLURAL[i]\n }\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to Hebrew words (digit by digit).\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Hebrew words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n for (let i = 0; i < decimalPart.length; i++) {\n const d = parseInt(decimalPart[i], 10)\n if (result) result += ' '\n result += d === 0 ? ZERO : ONES[d]\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Modern Hebrew words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @param {string} [options.andWord] - Custom conjunction word\n * @returns {string} The number in Modern Hebrew words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Hindi language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Indian numbering system (हज़ार, लाख, करोड़)\n * - Devanagari script\n * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)\n * - Complete word forms for 0-99\n * - BigInt modulo for efficient segment extraction\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'शून्य'\nconst NEGATIVE = 'माइनस'\nconst DECIMAL_SEP = 'दशमलव'\nconst HUNDRED = 'सौ'\n\nconst BELOW_HUNDRED = [\n 'शून्य', 'एक', 'दो', 'तीन', 'चार', 'पाँच', 'छह', 'सात', 'आठ', 'नौ',\n 'दस', 'ग्यारह', 'बारह', 'तेरह', 'चौदह', 'पंद्रह', 'सोलह', 'सत्रह', 'अठारह', 'उन्नीस',\n 'बीस', 'इक्कीस', 'बाईस', 'तेईस', 'चौबीस', 'पच्चीस', 'छब्बीस', 'सत्ताईस', 'अट्ठाईस', 'उनतीस',\n 'तीस', 'इकतीस', 'बत्तीस', 'तैंतीस', 'चौंतीस', 'पैंतीस', 'छत्तीस', 'सैंतीस', 'अड़तीस', 'उनतालीस',\n 'चालीस', 'इकतालीस', 'बयालीस', 'तेतालीस', 'चवालीस', 'पैंतालीस', 'छियालीस', 'सैंतालीस', 'अड़तालीस', 'उनचास',\n 'पचास', 'इक्यावन', 'बावन', 'तिरपन', 'चौवन', 'पचपन', 'छप्पन', 'सत्तावन', 'अट्ठावन', 'उनसठ',\n 'साठ', 'इकसठ', 'बासठ', 'तिरसठ', 'चौंसठ', 'पैंसठ', 'छियासठ', 'सड़सठ', 'अड़सठ', 'उनहत्तर',\n 'सत्तर', 'इकहत्तर', 'बहत्तर', 'तिहत्तर', 'चौहत्तर', 'पचहत्तर', 'छिहत्तर', 'सतहत्तर', 'अठहत्तर', 'उन्यासी',\n 'अस्सी', 'इक्यासी', 'बयासी', 'तिरासी', 'चौरासी', 'पचासी', 'छियासी', 'सत्तासी', 'अट्ठासी', 'नवासी',\n 'नब्बे', 'इक्यानवे', 'बानवे', 'तिरानवे', 'चौरानवे', 'पचानवे', 'छियानवे', 'सत्तानवे', 'अट्ठानवे', 'निन्यानवे'\n]\n\n// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.\nconst SCALE_WORDS = ['', 'हज़ार', 'लाख', 'करोड़', 'अरब', 'खरब', 'नील', 'पद्म', 'शंख']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds words for a 0-999 segment.\n *\n * @param {number} n - Number 0-999\n * @returns {string} Hindi words for the segment\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED\n }\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Hindi words.\n *\n * Uses BigInt modulo for segment extraction (faster than string slicing).\n * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Hindi words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Extract segments using BigInt modulo\n // First segment is 3 digits (thousands), rest are 2 digits (lakhs, crores, etc.)\n // Segments stored least-significant first\n const segments = []\n\n // First segment: last 3 digits\n segments.push(Number(n % 1000n))\n let temp = n / 1000n\n\n // Remaining segments: 2 digits each (lakh = 100k, crore = 10M, etc.)\n while (temp > 0n) {\n segments.push(Number(temp % 100n))\n temp = temp / 100n\n }\n\n // Build result string (process from most-significant to least)\n const words = []\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n // First segment (units place) can be 0-999\n words.push(buildSegment(segment))\n } else {\n // Other segments are 0-99\n words.push(BELOW_HUNDRED[segment])\n }\n\n // Add scale word if not the units segment\n if (i > 0 && SCALE_WORDS[i]) {\n words.push(SCALE_WORDS[i])\n }\n }\n\n return words.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Hindi words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Hindi words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Croatian language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Three-form pluralization (one/few/many)\n * - Gender: thousands are feminine, millions+ are masculine\n * - Irregular hundreds (dvjesto, tristo, etc.)\n * - Long scale naming with -ard forms\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES_MASC = ['', 'jedan', 'dva', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']\nconst ONES_FEM = ['', 'jedna', 'dvije', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']\n\nconst TEENS = ['deset', 'jedanaest', 'dvanaest', 'trinaest', 'četrnaest', 'petnaest', 'šesnaest', 'sedamnaest', 'osamnaest', 'devetnaest']\nconst TENS = ['', '', 'dvadeset', 'trideset', 'četrdeset', 'pedeset', 'šezdeset', 'sedamdeset', 'osamdeset', 'devedeset']\n\n// Croatian has irregular hundreds\nconst HUNDREDS = ['', 'sto', 'dvjesto', 'tristo', 'četiristo', 'petsto', 'šesto', 'sedamsto', 'osamsto', 'devetsto']\n\nconst ZERO = 'nula'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'zarez'\n\n// Scale words: [singular, few, many]\n// Thousands (index 0) are feminine, rest are masculine\nconst SCALE_FORMS = [\n ['tisuća', 'tisuće', 'tisuća'],\n ['milijun', 'milijuna', 'milijuna'],\n ['milijarda', 'milijarde', 'milijarda'],\n ['bilijun', 'bilijuna', 'bilijuna'],\n ['bilijarda', 'bilijarde', 'bilijarda'],\n ['trilijun', 'trilijuna', 'trilijuna'],\n ['trilijarda', 'trilijarde', 'trilijarda'],\n ['kvadrilijun', 'kvadrilijuna', 'kvadrilijuna'],\n ['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda']\n]\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\nfunction pluralize (n, forms) {\n const num = typeof n === 'bigint' ? Number(n) : n\n const lastDigit = num % 10\n const lastTwoDigits = num % 100\n\n if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {\n return forms[2]\n }\n\n if (lastDigit === 1) return forms[0]\n if (lastDigit >= 2 && lastDigit <= 4) return forms[1]\n return forms[2]\n}\n\nfunction buildSegmentMasc (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n parts.push(HUNDREDS[hundredsDigit])\n }\n\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[onesDigit])\n } else if (onesDigit > 0) {\n parts.push(ONES_MASC[onesDigit])\n }\n\n return parts.join(' ')\n}\n\nfunction buildSegmentFem (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n parts.push(HUNDREDS[hundredsDigit])\n }\n\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[onesDigit])\n } else if (onesDigit > 0) {\n parts.push(ONES_FEM[onesDigit])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return options.gender === 'feminine' ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))\n }\n\n return buildLargeNumberWords(n, options)\n}\n\nfunction buildLargeNumberWords (n, options) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n parts.push(options.gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))\n } else {\n const scaleForms = SCALE_FORMS[scaleIndex - 1]\n const scaleWord = pluralize(segment, scaleForms)\n // Thousands (scaleIndex=1) are feminine, others masculine\n const isFeminine = scaleIndex === 1\n const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)\n parts.push(segmentWord + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart, options) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder), options)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Croatian words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @returns {string} The number in Croatian words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Hungarian language converter - Functional Implementation\n *\n * Self-contained converter with agglutinative word formation.\n *\n * Key features:\n * - Agglutinative structure (no spaces between compound parts)\n * - Special handling for \"egy\" (one) omission\n * - Pre-composed twenties (huszonegy through huszonkilenc)\n * - \"két\" instead of \"kettő\" in compound forms\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\n// Word map using BigInt keys for scale values\nconst WORDS = new Map([\n [1_000_000_000_000_000_000_000_000_000n, 'quadrilliárd'],\n [1_000_000_000_000_000_000_000_000n, 'quadrillió'],\n [1_000_000_000_000_000_000_000n, 'trilliárd'],\n [1_000_000_000_000_000_000n, 'trillió'],\n [1_000_000_000_000_000n, 'billiárd'],\n [1_000_000_000_000n, 'billió'],\n [1_000_000_000n, 'milliárd'],\n [1_000_000n, 'millió'],\n [1000n, 'ezer'],\n [100n, 'száz'],\n [90n, 'kilencven'],\n [80n, 'nyolcvan'],\n [70n, 'hetven'],\n [60n, 'hatvan'],\n [50n, 'ötven'],\n [40n, 'negyven'],\n [30n, 'harminc'],\n [29n, 'huszonkilenc'],\n [28n, 'huszonnyolc'],\n [27n, 'huszonhét'],\n [26n, 'huszonhat'],\n [25n, 'huszonöt'],\n [24n, 'huszonnégy'],\n [23n, 'huszonhárom'],\n [22n, 'huszonkettő'],\n [21n, 'huszonegy'],\n [20n, 'húsz'],\n [19n, 'tizenkilenc'],\n [18n, 'tizennyolc'],\n [17n, 'tizenhét'],\n [16n, 'tizenhat'],\n [15n, 'tizenöt'],\n [14n, 'tizennégy'],\n [13n, 'tizenhárom'],\n [12n, 'tizenkettő'],\n [11n, 'tizenegy'],\n [10n, 'tíz'],\n [9n, 'kilenc'],\n [8n, 'nyolc'],\n [7n, 'hét'],\n [6n, 'hat'],\n [5n, 'öt'],\n [4n, 'négy'],\n [3n, 'három'],\n [2n, 'kettő'],\n [1n, 'egy'],\n [0n, 'nulla']\n])\n\nconst ZERO = 'nulla'\nconst NEGATIVE = 'mínusz'\nconst DECIMAL_SEP = 'egész'\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction wordForScale (value) {\n return WORDS.get(value)\n}\n\nfunction tensToCardinal (n) {\n if (WORDS.has(n)) {\n return WORDS.get(n)\n }\n const tens = n / 10n\n const units = n % 10n\n return wordForScale(tens * 10n) + integerToWordsInner(units)\n}\n\nfunction hundredsToCardinal (n) {\n const hundreds = n / 100n\n let prefix = 'száz'\n if (hundreds !== 1n) {\n prefix = integerToWordsInner(hundreds, '') + prefix\n }\n const postfix = integerToWordsInner(n % 100n, '')\n return prefix + postfix\n}\n\nfunction thousandsToCardinal (n) {\n const thousands = n / 1000n\n let prefix = 'ezer'\n if (thousands !== 1n) {\n prefix = integerToWordsInner(thousands, '') + prefix\n }\n const postfix = integerToWordsInner(n % 1000n, '')\n const middle = (n <= 2000n || postfix === '') ? '' : '-'\n return prefix + middle + postfix\n}\n\n// Scale values for finding appropriate scale (avoids toString)\nconst SCALES = [\n 1_000_000_000_000_000_000_000_000_000n,\n 1_000_000_000_000_000_000_000_000n,\n 1_000_000_000_000_000_000_000n,\n 1_000_000_000_000_000_000n,\n 1_000_000_000_000_000n,\n 1_000_000_000_000n,\n 1_000_000_000n,\n 1_000_000n\n]\n\nfunction bigNumberToCardinal (n) {\n // Find appropriate scale using BigInt comparison (avoids toString)\n let exp = 1_000_000n\n for (const scale of SCALES) {\n if (n >= scale) {\n exp = scale\n break\n }\n }\n\n const prefix = integerToWordsInner(n / exp, '')\n const rest = integerToWordsInner(n % exp, '')\n const postfix = (rest === '') ? '' : ('-' + rest)\n return prefix + wordForScale(exp) + postfix\n}\n\nfunction integerToWordsInner (n, zeroWord = ZERO) {\n // Normalize to BigInt for consistent comparisons\n if (typeof n !== 'bigint') n = BigInt(n)\n\n if (n === 0n) {\n return zeroWord\n }\n if (zeroWord === '' && n === 2n) {\n return 'két'\n }\n if (n < 30n) {\n return wordForScale(n)\n }\n if (n < 100n) {\n return tensToCardinal(n)\n }\n if (n < 1000n) {\n return hundredsToCardinal(n)\n }\n if (n < 1_000_000n) {\n return thousandsToCardinal(n)\n }\n return bigNumberToCardinal(n)\n}\n\nfunction integerToWords (n) {\n return integerToWordsInner(n, ZERO)\n}\n\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Hungarian words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Hungarian words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Indonesian language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - \"Se-\" prefix for 100 (seratus) and 1000 (seribu)\n * - Regular patterns (puluh for tens, ratus for hundreds)\n * - Teens with \"belas\" suffix\n * - Indonesian uses \"satu juta\" (not \"sejuta\") for millions+\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'satu', 'dua', 'tiga', 'empat', 'lima', 'enam', 'tujuh', 'delapan', 'sembilan']\nconst TEENS = ['sepuluh', 'sebelas', 'dua belas', 'tiga belas', 'empat belas', 'lima belas', 'enam belas', 'tujuh belas', 'delapan belas', 'sembilan belas']\nconst TENS = ['', '', 'dua puluh', 'tiga puluh', 'empat puluh', 'lima puluh', 'enam puluh', 'tujuh puluh', 'delapan puluh', 'sembilan puluh']\n\nconst HUNDRED_WORD = 'ratus'\nconst THOUSAND_WORD = 'ribu'\nconst SCALE_WORDS = ['juta', 'miliar', 'triliun', 'kuadriliun', 'kuantiliun', 'sekstiliun', 'septiliun', 'oktiliun', 'noniliun', 'desiliun']\n\nconst ZERO = 'nol'\nconst NEGATIVE = 'min'\nconst DECIMAL_SEP = 'koma'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds: seratus (100) or N ratus (200-900)\n if (hundredsDigit > 0) {\n if (hundredsDigit === 1) {\n parts.push('se' + HUNDRED_WORD)\n } else {\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED_WORD)\n }\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n parts.push(ONES[tensOnes])\n } else if (tensOnes < 20) {\n parts.push(TEENS[tensOnes - 10])\n } else if (onesDigit === 0) {\n parts.push(TENS[tensDigit])\n } else {\n parts.push(TENS[tensDigit] + ' ' + ONES[onesDigit])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n let result\n if (thousands === 1) {\n result = 'se' + THOUSAND_WORD\n } else {\n result = buildSegment(thousands) + ' ' + THOUSAND_WORD\n }\n\n if (remainder > 0) {\n result += ' ' + buildSegment(remainder)\n }\n\n return result\n }\n\n return buildLargeNumberWords(n)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n parts.push(buildSegment(segment))\n } else if (scaleIndex === 1) {\n if (segment === 1) {\n parts.push('se' + THOUSAND_WORD)\n } else {\n parts.push(buildSegment(segment) + ' ' + THOUSAND_WORD)\n }\n } else {\n // Indonesian: \"satu juta\" not \"sejuta\"\n const scaleWord = SCALE_WORDS[scaleIndex - 2]\n parts.push(buildSegment(segment) + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Indonesian words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Indonesian words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Italian language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Italian-specific rules:\n * - Concatenation without spaces within segments (\"ventotto\" not \"venti otto\")\n * - Phonetic vowel elision: \"venti\" + \"otto\" → \"ventotto\"\n * - Accent on final \"tre\" in compounds: \"ventitré\"\n * - mille/mila alternation for thousands\n * - Scale words: milione/milioni, miliardo/miliardi, etc.\n * - \"e\" connector before simple final remainder\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Base vocabulary\nconst ONES = ['', 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto', 'nove']\nconst TEENS = ['dieci', 'undici', 'dodici', 'tredici', 'quattordici', 'quindici', 'sedici', 'diciassette', 'diciotto', 'diciannove']\nconst TENS = ['', '', 'venti', 'trenta', 'quaranta', 'cinquanta', 'sessanta', 'settanta', 'ottanta', 'novanta']\nconst HUNDREDS = ['', 'cento', 'duecento', 'trecento', 'quattrocento', 'cinquecento', 'seicento', 'settecento', 'ottocento', 'novecento']\n\n// Pre-elided tens stems (drop final vowel before 'uno'/'otto')\n// vent- (from venti), trent- (from trenta), etc.\nconst TENS_STEM = ['', '', 'vent', 'trent', 'quarant', 'cinquant', 'sessant', 'settant', 'ottant', 'novant']\n\nconst ZERO = 'zero'\nconst NEGATIVE = 'meno'\nconst DECIMAL_SEP = 'virgola'\n\n// Thousands\nconst THOUSAND_SINGULAR = 'mille'\nconst THOUSAND_PLURAL_SUFFIX = 'mila'\n\n// Scale word generation\nconst SCALE_PREFIXES = ['m', 'b', 'tr', 'quadr', 'quint', 'sest', 'sett', 'ott', 'nov', 'dec']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds the segment word for a number 0-999.\n * Handles Italian phonetic elision inline (no regex).\n *\n * Elision rules:\n * - Tens ending in vowel + uno/otto → drop tens vowel: ventuno, ventotto\n * - Hundreds cento + otto/ottanta → centotto, centottanta (drop 'o')\n * - Final 'tre' in compounds becomes 'tré': ventitré, trentatré\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n let result = ''\n\n // Hundreds\n if (hundreds > 0) {\n // Elision: *cento + otto/ottanta → *centotto/centottanta (drop final 'o')\n // Only applies when tens = 8 (ottanta) or tens = 0 and ones = 8 (otto)\n if (tens === 8 || (tens === 0 && ones === 8)) {\n // Remove final 'o' from hundreds: cento→cent, duecento→duecent, etc.\n result = HUNDREDS[hundreds].slice(0, -1)\n } else {\n result = HUNDREDS[hundreds]\n }\n }\n\n // Tens and ones\n if (tens === 0 && ones === 0) {\n // Nothing more (just hundreds)\n } else if (tens === 1) {\n // Teens: 10-19\n result += TEENS[ones]\n } else if (tens >= 2) {\n // 20-99: handle elision for uno (1) and otto (8)\n if (ones === 1 || ones === 8) {\n // Use stem form: vent + uno = ventuno, vent + otto = ventotto\n result += TENS_STEM[tens] + ONES[ones]\n } else if (ones === 3) {\n // Final tre becomes tré\n result += TENS[tens] + 'tré'\n } else if (ones > 0) {\n result += TENS[tens] + ONES[ones]\n } else {\n result += TENS[tens]\n }\n } else if (ones > 0) {\n // 1-9 (tens === 0)\n if (ones === 3 && hundreds > 0) {\n // centotré, duecentotré, etc.\n result += 'tré'\n } else {\n result += ONES[ones]\n }\n }\n\n return result\n}\n\n/**\n * Builds segment word with \"un\" for scale context (millions, billions).\n * Same as buildSegment but returns \"un\" for 1 instead of \"uno\".\n */\nfunction buildSegmentForScale (n) {\n if (n === 0) return ''\n if (n === 1) return 'un' // \"un milione\" not \"uno milione\"\n return buildSegment(n)\n}\n\n/**\n * Builds thousands word for 1-999 thousand.\n * Handles elision: tre + mila = tremila (no accent), otto + mila = ottomila\n */\nfunction buildThousands (n) {\n if (n === 0) return ''\n if (n === 1) return THOUSAND_SINGULAR // \"mille\"\n\n // Build segment and append \"mila\"\n // Note: elision of segment ending vowel + 'mila' not needed (mila starts with 'm')\n // But we need to handle cases like \"ottomila\" (no double-o issue since we build directly)\n return buildSegment(n) + THOUSAND_PLURAL_SUFFIX\n}\n\n// ============================================================================\n// Scale Word Functions\n// ============================================================================\n\n/**\n * Gets singular scale word for index.\n * @param {number} scaleIndex - 2=million, 3=billion, etc.\n */\nfunction getScaleWordSingular (scaleIndex) {\n if (scaleIndex < 2) return ''\n const prefixIndex = Math.floor((scaleIndex - 2) / 2)\n const isIardo = (scaleIndex - 2) % 2 === 1\n const prefix = SCALE_PREFIXES[prefixIndex]\n if (!prefix) return ''\n return prefix + (isIardo ? 'iliardo' : 'ilione')\n}\n\n/**\n * Gets plural scale word for index.\n * @param {number} scaleIndex - 2=million, 3=billion, etc.\n */\nfunction getScaleWordPlural (scaleIndex) {\n if (scaleIndex < 2) return ''\n const prefixIndex = Math.floor((scaleIndex - 2) / 2)\n const isIardo = (scaleIndex - 2) % 2 === 1\n const prefix = SCALE_PREFIXES[prefixIndex]\n if (!prefix) return ''\n return prefix + (isIardo ? 'iliardi' : 'ilioni')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Italian words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Italian words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n if (remainder === 0) {\n return buildThousands(thousands)\n }\n\n // Concatenate thousands + remainder\n return buildThousands(thousands) + buildSegment(remainder)\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Italian words\n */\nfunction buildLargeNumberWords (n) {\n const parts = []\n let remaining = n\n\n // Find the highest scale\n let maxScale = 2\n let testValue = 1_000_000n\n while (testValue * 1000n <= remaining) {\n testValue *= 1000n\n maxScale++\n }\n\n // Process from highest scale down\n for (let scaleIndex = maxScale; scaleIndex >= 0; scaleIndex--) {\n const divisor = 1000n ** BigInt(scaleIndex)\n const segment = remaining / divisor\n remaining = remaining % divisor\n\n if (segment === 0n) continue\n\n const segNum = Number(segment)\n\n if (scaleIndex >= 2) {\n // Millions and above: \"segment scaleWord\"\n const segmentWords = buildSegmentForScale(segNum)\n const scaleWord = segment === 1n\n ? getScaleWordSingular(scaleIndex)\n : getScaleWordPlural(scaleIndex)\n parts.push(segmentWords + ' ' + scaleWord)\n } else if (scaleIndex === 1) {\n // Thousands\n parts.push(buildThousands(segNum))\n } else {\n // Units (scaleIndex === 0): just the segment\n parts.push(buildSegment(segNum))\n }\n }\n\n return joinPartsWithConnector(parts)\n}\n\n/**\n * Joins parts with Italian connector rules.\n * Uses \"e\" before simple (non-compound) final segment.\n *\n * @param {string[]} parts - Parts to join\n * @returns {string} Joined string\n */\nfunction joinPartsWithConnector (parts) {\n const len = parts.length\n if (len === 0) return ''\n if (len === 1) return parts[0]\n\n // Check if last part is \"simple\" (no space = no scale word)\n const lastPart = parts[len - 1]\n if (lastPart.indexOf(' ') === -1) {\n // Join all but last with space, then add \"e\" connector\n let result = parts[0]\n for (let i = 1; i < len - 1; i++) {\n result += ' ' + parts[i]\n }\n return result + ' e ' + lastPart\n }\n\n // Join with spaces\n let result = parts[0]\n for (let i = 1; i < len; i++) {\n result += ' ' + parts[i]\n }\n return result\n}\n\n/**\n * Converts decimal digits to Italian words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Italian words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Italian words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Italian words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(28) // 'ventotto'\n * toWords(23) // 'ventitré'\n * toWords(1000) // 'mille'\n * toWords(2000) // 'duemila'\n * toWords(1000000) // 'un milione'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Japanese language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Japanese-specific rules:\n * - Myriad (万-based) grouping: 4 digits per segment instead of 3\n * - 一 omission: Omit \"一\" before 十, 百, 千 but NOT before 万 and higher scales\n * - Kanji numerals: 零一二三四五六七八九\n * - No spaces between characters\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Ones words (1-9), index 0 unused\nconst ONES = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九']\n\n// Scale words for powers of 10,000 (万-based system)\n// Index 0 = 万 (10^4), 1 = 億 (10^8), 2 = 兆 (10^12), etc.\nconst SCALES = [\n '万', // 10^4 (man)\n '億', // 10^8 (oku)\n '兆', // 10^12 (chō)\n '京', // 10^16 (kei)\n '垓', // 10^20 (gai)\n '秭', // 10^24 (jo/shi)\n '穣', // 10^28 (jō)\n '溝', // 10^32 (kō)\n '澗', // 10^36 (kan)\n '正', // 10^40 (sei)\n '載', // 10^44 (sai)\n '極', // 10^48 (goku)\n '恒河沙', // 10^52 (gōgasha)\n '阿僧祇', // 10^56 (asōgi)\n '那由他', // 10^60 (nayuta)\n '不可思議', // 10^64 (fukashigi)\n '無量大数' // 10^68 (muryōtaisū)\n]\n\nconst ZERO = '零'\nconst NEGATIVE = 'マイナス'\nconst DECIMAL_SEP = '点'\n\n// Internal scale words (within 4-digit segments)\nconst TEN = '十'\nconst HUNDRED = '百'\nconst THOUSAND = '千'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-9999 with 一 omission rules.\n * - Omit 一 before 十, 百, 千\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100) % 10\n const thousands = Math.floor(n / 1000)\n\n let result = ''\n\n // Thousands (千) - omit 一 when 1\n if (thousands > 0) {\n if (thousands === 1) {\n result += THOUSAND\n } else {\n result += ONES[thousands] + THOUSAND\n }\n }\n\n // Hundreds (百) - omit 一 when 1\n if (hundreds > 0) {\n if (hundreds === 1) {\n result += HUNDRED\n } else {\n result += ONES[hundreds] + HUNDRED\n }\n }\n\n // Tens (十) - omit 一 when 1\n if (tens > 0) {\n if (tens === 1) {\n result += TEN\n } else {\n result += ONES[tens] + TEN\n }\n }\n\n // Ones\n if (ones > 0) {\n result += ONES[ones]\n }\n\n return result\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Japanese words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Japanese kanji words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 10000\n if (n < 10000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 100,000,000 (万 range)\n if (n < 100_000_000n) {\n const man = Number(n / 10000n)\n const remainder = Number(n % 10000n)\n\n // For 万 and above, we need 一 before the scale word when segment is 1\n let result\n if (man === 1) {\n result = '一' + SCALES[0] // 一万\n } else {\n result = buildSegment(man) + SCALES[0]\n }\n\n if (remainder > 0) {\n result += buildSegment(remainder)\n }\n\n return result\n }\n\n // For numbers >= 100,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 100,000,000.\n * Uses BigInt modulo for 4-digit (myriad) segment extraction.\n *\n * @param {bigint} n - Number >= 100,000,000\n * @returns {string} Japanese kanji words\n */\nfunction buildLargeNumberWords (n) {\n // Extract segments using BigInt modulo (faster than string slicing)\n // Segments stored least-significant first (index 0 = units, 1 = 万, etc.)\n const segments = []\n let temp = n\n while (temp > 0n) {\n segments.push(Number(temp % 10000n))\n temp = temp / 10000n\n }\n\n // Build result string directly (process from most-significant to least)\n let result = ''\n\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i > 0) {\n // For scales >= 万, we need 一 before scale word when segment is 1\n if (segment === 1) {\n result += '一' + SCALES[i - 1]\n } else {\n result += buildSegment(segment) + SCALES[i - 1]\n }\n } else {\n // Units segment (no scale word)\n result += buildSegment(segment)\n }\n }\n\n return result || ZERO\n}\n\n/**\n * Converts decimal digits to Japanese words (digit by digit).\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Japanese kanji words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n for (let i = 0; i < decimalPart.length; i++) {\n const digit = parseInt(decimalPart[i], 10)\n if (digit === 0) {\n result += ZERO\n } else {\n result += ONES[digit]\n }\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Japanese words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Japanese kanji words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(42) // '四十二'\n * toWords(10000) // '一万'\n * toWords(100000000) // '一億'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += DECIMAL_SEP + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Georgian language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Georgian-specific rules:\n * - Vigesimal (base-20) system for 20-99\n * - 40 = ორმოცი (2×20), 60 = სამოცი (3×20), 80 = ოთხმოცი (4×20)\n * - 30/50/70/90 use \"და\" + \"ათი\": ოცდაათი (20+10), ორმოცდაათი (40+10)\n * - Compound numbers use \"და\" (da = \"and\") connector\n * - Hundreds: unit prefix + ას (ორასი = 200)\n * - Short scale for large numbers (მილიონი, მილიარდი, ტრილიონი)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Numbers 0-9 (primitives)\nconst ONES = ['ნული', 'ერთი', 'ორი', 'სამი', 'ოთხი', 'ხუთი', 'ექვსი', 'შვიდი', 'რვა', 'ცხრა']\n\n// Numbers 10-19\nconst TEENS = ['ათი', 'თერთმეტი', 'თორმეტი', 'ცამეტი', 'თოთხმეტი', 'თხუთმეტი', 'თექვსმეტი', 'ჩვიდმეტი', 'თვრამეტი', 'ცხრამეტი']\n\n// Vigesimal bases: 20, 40, 60, 80 (with და connector forms)\nconst VIGESIMAL = ['', 'ოცი', 'ორმოცი', 'სამოცი', 'ოთხმოცი']\nconst VIGESIMAL_DA = ['', 'ოცდა', 'ორმოცდა', 'სამოცდა', 'ოთხმოცდა']\n\n// Prefixes for hundreds (unit forms without final vowel)\nconst HUNDRED_PREFIXES = ['', '', 'ორ', 'სამ', 'ოთხ', 'ხუთ', 'ექვს', 'შვიდ', 'რვა', 'ცხრა']\n\nconst HUNDRED = 'ასი'\nconst HUNDRED_STEM = 'ას' // Without final vowel\nconst THOUSAND = 'ათასი'\nconst THOUSAND_STEM = 'ათას' // Without final vowel\n\n// Scale words (short scale) - indexed by segment position\nconst SCALES = ['', '', 'მილიონი', 'მილიარდი', 'ტრილიონი', 'კვადრილიონი', 'კვინტილიონი', 'სექსტილიონი']\n\nconst ZERO = 'ნული'\nconst NEGATIVE = 'მინუს'\nconst DECIMAL_SEP = 'მთელი'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-99 using vigesimal system.\n * @param {number} n - Number 0-99\n * @returns {string} Georgian word\n */\nfunction buildTens (n) {\n if (n < 10) return ONES[n]\n if (n < 20) return TEENS[n - 10]\n\n const vigesimalGroup = Math.floor(n / 20)\n const remainder = n % 20\n\n if (remainder === 0) {\n return VIGESIMAL[vigesimalGroup]\n }\n\n // Use და connector: ოცდა + remainder\n const base = VIGESIMAL_DA[vigesimalGroup]\n if (remainder < 10) {\n return base + ONES[remainder]\n }\n return base + TEENS[remainder - 10]\n}\n\n/**\n * Builds segment word for 0-999.\n * Returns object with full form and stem form (without final vowel).\n * @param {number} n - Number 0-999\n * @returns {{full: string, stem: string}} Georgian words\n */\nfunction buildSegment (n) {\n if (n === 0) return { full: '', stem: '' }\n if (n < 100) {\n const word = buildTens(n)\n // Remove final vowel for stem\n const lastChar = word.slice(-1)\n const stem = (lastChar === 'ი' || lastChar === 'ა') ? word.slice(0, -1) : word\n return { full: word, stem }\n }\n\n const hundreds = Math.floor(n / 100)\n const remainder = n % 100\n\n // Build hundreds: ასი (100), ორასი (200), etc.\n let hundredWord\n if (hundreds === 1) {\n hundredWord = remainder > 0 ? HUNDRED_STEM : HUNDRED\n } else {\n hundredWord = HUNDRED_PREFIXES[hundreds] + (remainder > 0 ? HUNDRED_STEM : HUNDRED)\n }\n\n if (remainder > 0) {\n const remainderWord = buildTens(remainder)\n const full = hundredWord + ' ' + remainderWord\n // Stem removes final vowel from remainder\n const lastChar = remainderWord.slice(-1)\n const remainderStem = (lastChar === 'ი' || lastChar === 'ა') ? remainderWord.slice(0, -1) : remainderWord\n return { full, stem: hundredWord + ' ' + remainderStem }\n }\n\n // Hundreds only - stem removes final ი\n return { full: hundredWord, stem: hundredWord.slice(0, -1) }\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Georgian words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Georgian words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n const { full } = buildSegment(Number(n))\n return full\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n let result\n if (thousands === 1) {\n // \"ათასი\" not \"ერთი ათასი\"\n result = remainder > 0 ? THOUSAND_STEM : THOUSAND\n } else {\n // Use stem form before ათასი\n const { stem: thousandsPart } = buildSegment(thousands)\n result = thousandsPart + ' ' + (remainder > 0 ? THOUSAND_STEM : THOUSAND)\n }\n\n if (remainder > 0) {\n const { full: remainderWord } = buildSegment(remainder)\n result += ' ' + remainderWord\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Georgian words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from left to right\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n // Units (no scale)\n const { full } = buildSegment(segment)\n parts.push(full)\n } else if (scaleIndex === 1) {\n // Thousands - check if there's a remainder\n const hasRemainder = segments.slice(i + 1).some(s => s !== 0)\n const thousandWord = hasRemainder ? THOUSAND_STEM : THOUSAND\n\n if (segment === 1) {\n parts.push(thousandWord)\n } else {\n const { stem } = buildSegment(segment)\n parts.push(stem + ' ' + thousandWord)\n }\n } else {\n // Million and above\n const scaleWord = SCALES[scaleIndex] || SCALES[SCALES.length - 1]\n if (segment === 1) {\n parts.push('ერთი ' + scaleWord)\n } else {\n const { full } = buildSegment(segment)\n parts.push(full + ' ' + scaleWord)\n }\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts decimal digits to Georgian words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Georgian words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Georgian words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Georgian words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'ოცდაერთი'\n * toWords(100) // 'ასი'\n * toWords(1000) // 'ათასი'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Kannada language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Indian numbering system (ಸಾವಿರ, ಲಕ್ಷ, ಕೋಟಿ)\n * - Kannada script\n * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)\n * - Complete word forms for 0-99\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'ಸೊನ್ನೆ'\nconst NEGATIVE = 'ಋಣಾತ್ಮಕ'\nconst DECIMAL_SEP = 'ದಶಮಾಂಶ'\nconst HUNDRED = 'ನೂರು'\n\nconst BELOW_HUNDRED = [\n 'ಸೊನ್ನೆ', 'ಒಂದು', 'ಎರಡು', 'ಮೂರು', 'ನಾಲ್ಕು', 'ಐದು', 'ಆರು', 'ಏಳು', 'ಎಂಟು', 'ಒಂಬತ್ತು',\n 'ಹತ್ತು', 'ಹನ್ನೊಂದು', 'ಹನ್ನೆರಡು', 'ಹದಿಮೂರು', 'ಹದಿನಾಲ್ಕು', 'ಹದಿನೈದು', 'ಹದಿನಾರು', 'ಹದಿನೇಳು', 'ಹದಿನೆಂಟು', 'ಹತ್ತೊಂಬತ್ತು',\n 'ಇಪ್ಪತ್ತು', 'ಇಪ್ಪತ್ತೊಂದು', 'ಇಪ್ಪತ್ತೆರಡು', 'ಇಪ್ಪತ್ತಮೂರು', 'ಇಪ್ಪತ್ತನಾಲ್ಕು', 'ಇಪ್ಪತ್ತೈದು', 'ಇಪ್ಪತ್ತಾರು', 'ಇಪ್ಪತ್ತೇಳು', 'ಇಪ್ಪತ್ತೆಂಟು', 'ಇಪ್ಪತ್ತೊಂಬತ್ತು',\n 'ಮೂವತ್ತು', 'ಮೂವತ್ತೊಂದು', 'ಮೂವತ್ತೆರಡು', 'ಮೂವತ್ತಮೂರು', 'ಮೂವತ್ತನಾಲ್ಕು', 'ಮೂವತ್ತೈದು', 'ಮೂವತ್ತಾರು', 'ಮೂವತ್ತೇಳು', 'ಮೂವತ್ತೆಂಟು', 'ಮೂವತ್ತೊಂಬತ್ತು',\n 'ನಲವತ್ತು', 'ನಲವತ್ತೊಂದು', 'ನಲವತ್ತೆರಡು', 'ನಲವತ್ತಮೂರು', 'ನಲವತ್ತನಾಲ್ಕು', 'ನಲವತ್ತೈದು', 'ನಲವತ್ತಾರು', 'ನಲವತ್ತೇಳು', 'ನಲವತ್ತೆಂಟು', 'ನಲವತ್ತೊಂಬತ್ತು',\n 'ಐವತ್ತು', 'ಐವತ್ತೊಂದು', 'ಐವತ್ತೆರಡು', 'ಐವತ್ತಮೂರು', 'ಐವತ್ತನಾಲ್ಕು', 'ಐವತ್ತೈದು', 'ಐವತ್ತಾರು', 'ಐವತ್ತೇಳು', 'ಐವತ್ತೆಂಟು', 'ಐವತ್ತೊಂಬತ್ತು',\n 'ಅರವತ್ತು', 'ಅರವತ್ತೊಂದು', 'ಅರವತ್ತೆರಡು', 'ಅರವತ್ತಮೂರು', 'ಅರವತ್ತನಾಲ್ಕು', 'ಅರವತ್ತೈದು', 'ಅರವತ್ತಾರು', 'ಅರವತ್ತೇಳು', 'ಅರವತ್ತೆಂಟು', 'ಅರವತ್ತೊಂಬತ್ತು',\n 'ಎಪ್ಪತ್ತು', 'ಎಪ್ಪತ್ತೊಂದು', 'ಎಪ್ಪತ್ತೆರಡು', 'ಎಪ್ಪತ್ತಮೂರು', 'ಎಪ್ಪತ್ತನಾಲ್ಕು', 'ಎಪ್ಪತ್ತೈದು', 'ಎಪ್ಪತ್ತಾರು', 'ಎಪ್ಪತ್ತೇಳು', 'ಎಪ್ಪತ್ತೆಂಟು', 'ಎಪ್ಪತ್ತೊಂಬತ್ತು',\n 'ಎಂಬತ್ತು', 'ಎಂಬತ್ತೊಂದು', 'ಎಂಬತ್ತೆರಡು', 'ಎಂಬತ್ತಮೂರು', 'ಎಂಬತ್ತನಾಲ್ಕು', 'ಎಂಬತ್ತೈದು', 'ಎಂಬತ್ತಾರು', 'ಎಂಬತ್ತೇಳು', 'ಎಂಬತ್ತೆಂಟು', 'ಎಂಬತ್ತೊಂಬತ್ತು',\n 'ತೊಂಬತ್ತು', 'ತೊಂಬತ್ತೊಂದು', 'ತೊಂಬತ್ತೆರಡು', 'ತೊಂಬತ್ತಮೂರು', 'ತೊಂಬತ್ತನಾಲ್ಕು', 'ತೊಂಬತ್ತೈದು', 'ತೊಂಬತ್ತಾರು', 'ತೊಂಬತ್ತೇಳು', 'ತೊಂಬತ್ತೆಂಟು', 'ತೊಂಬತ್ತೊಂಬತ್ತು'\n]\n\n// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.\nconst SCALE_WORDS = ['', 'ಸಾವಿರ', 'ಲಕ್ಷ', 'ಕೋಟಿ', 'ಅಬ್ಜ', 'ಖರ್ವ', 'ನೀಲ', 'ಪದ್ಮ', 'ಶಂಖ']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds words for a 0-999 segment.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED\n }\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Kannada words.\n *\n * Uses BigInt modulo for segment extraction (faster than string slicing).\n * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Kannada words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n segments.push(Number(n % 1000n))\n let temp = n / 1000n\n\n while (temp > 0n) {\n segments.push(Number(temp % 100n))\n temp = temp / 100n\n }\n\n // Build result string (process from most-significant to least)\n const words = []\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n words.push(buildSegment(segment))\n } else {\n words.push(BELOW_HUNDRED[segment])\n }\n\n if (i > 0 && SCALE_WORDS[i]) {\n words.push(SCALE_WORDS[i])\n }\n }\n\n return words.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : BELOW_HUNDRED[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Kannada words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Kannada words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Korean language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Myriad-based (만) grouping - 4 digits\n * - Implicit '일' (one) omission before scale words\n * - Space separation after 만+ scales\n * - Hangul numerals\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구']\n\nconst TEN = '십'\nconst HUNDRED = '백'\nconst THOUSAND = '천'\n\nconst ZERO = '영'\nconst NEGATIVE = '마이너스'\nconst DECIMAL_SEP = '점'\n\n// Myriad scale words (powers of 10,000)\n// 만 (10^4), 억 (10^8), 조 (10^12), 경 (10^16), etc.\nconst SCALES = ['만', '억', '조', '경', '해', '자', '양']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-9999 (4-digit myriad segment).\n * Korean omits \"일\" before 십, 백, 천.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100) % 10\n const thousands = Math.floor(n / 1000)\n\n let result = ''\n\n // Thousands\n if (thousands > 0) {\n if (thousands === 1) {\n result += THOUSAND\n } else {\n result += ONES[thousands] + THOUSAND\n }\n }\n\n // Hundreds\n if (hundreds > 0) {\n if (hundreds === 1) {\n result += HUNDRED\n } else {\n result += ONES[hundreds] + HUNDRED\n }\n }\n\n // Tens\n if (tens > 0) {\n if (tens === 1) {\n result += TEN\n } else {\n result += ONES[tens] + TEN\n }\n }\n\n // Ones\n if (ones > 0) {\n result += ONES[ones]\n }\n\n return result\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Korean words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Korean words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 10000\n if (n < 10000n) {\n return buildSegment(Number(n))\n }\n\n // For numbers >= 10000, use myriad decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 10000.\n * Uses myriad (만) grouping - 4 digits per segment.\n *\n * @param {bigint} n - Number >= 10000\n * @returns {string} Korean words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 4 digits from right to left\n const segments = []\n const segmentSize = 4\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n // Units segment (no scale word)\n parts.push({ word: buildSegment(segment), isScale: false })\n } else {\n // Segment with scale word\n const scaleWord = SCALES[scaleIndex - 1]\n\n // Korean omits segment when it's 1 before scale words\n if (segment === 1) {\n parts.push({ word: scaleWord, isScale: true })\n } else {\n parts.push({ word: buildSegment(segment), isScale: false })\n parts.push({ word: scaleWord, isScale: true })\n }\n }\n }\n\n scaleIndex--\n }\n\n // Join with Korean spacing rules\n return joinKoreanParts(parts)\n}\n\n/**\n * Joins parts with Korean spacing rules.\n * - Concatenate without spaces within segments\n * - Space after scale words before next number\n *\n * @param {Array} parts - Parts with isScale metadata\n * @returns {string} Joined string\n */\nfunction joinKoreanParts (parts) {\n if (parts.length === 0) return ZERO\n if (parts.length === 1) return parts[0].word\n\n const result = []\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n const prevPart = i > 0 ? parts[i - 1] : null\n\n // Add space after scale words before next number\n if (prevPart && prevPart.isScale && !part.isScale) {\n result.push(' ')\n }\n\n result.push(part.word)\n }\n\n return result.join('')\n}\n\n/**\n * Converts decimal digits to Korean words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Korean words for decimal part (space-separated)\n */\nfunction decimalPartToWords (decimalPart) {\n const parts = []\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n parts.push(ZERO)\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n parts.push(integerToWords(BigInt(remainder)))\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts a numeric value to Korean words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Korean words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // '이십일'\n * toWords(10000) // '만'\n * toWords(1000000) // '백만'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n const parts = []\n\n if (isNegative) {\n parts.push(NEGATIVE)\n }\n\n parts.push(integerToWords(integerPart))\n\n if (decimalPart) {\n parts.push(DECIMAL_SEP)\n parts.push(decimalPartToWords(decimalPart))\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Lithuanian language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Three-form pluralization (singular/plural/genitive)\n * - Gender agreement (masculine/feminine for numbers < 1000)\n * - Two-form hundreds (šimtas/šimtai)\n * - Long scale naming\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES_MASC = ['', 'vienas', 'du', 'trys', 'keturi', 'penki', 'šeši', 'septyni', 'aštuoni', 'devyni']\nconst ONES_FEM = ['', 'viena', 'dvi', 'trys', 'keturios', 'penkios', 'šešios', 'septynios', 'aštuonios', 'devynios']\n\nconst TEENS = ['dešimt', 'vienuolika', 'dvylika', 'trylika', 'keturiolika', 'penkiolika', 'šešiolika', 'septyniolika', 'aštuoniolika', 'devyniolika']\nconst TENS = ['', '', 'dvidešimt', 'trisdešimt', 'keturiasdešimt', 'penkiasdešimt', 'šešiasdešimt', 'septyniasdešimt', 'aštuoniasdešimt', 'devyniasdešimt']\n\n// Hundreds: šimtas (singular), šimtai (plural)\nconst HUNDRED_SINGULAR = 'šimtas'\nconst HUNDRED_PLURAL = 'šimtai'\n\nconst ZERO = 'nulis'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'kablelis'\n\n// Scale words: [singular, plural, genitive]\nconst SCALE_FORMS = [\n ['tūkstantis', 'tūkstančiai', 'tūkstančių'],\n ['milijonas', 'milijonai', 'milijonų'],\n ['milijardas', 'milijardai', 'milijardų'],\n ['trilijonas', 'trilijonai', 'trilijonų'],\n ['kvadrilijonas', 'kvadrilijonai', 'kvadrilijonų'],\n ['kvintilijonas', 'kvintilijonai', 'kvintilijonų'],\n ['sikstilijonas', 'sikstilijonai', 'sikstilijonų'],\n ['septilijonas', 'septilijonai', 'septilijonų'],\n ['oktilijonas', 'oktilijonai', 'oktilijonų']\n]\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999 (masculine form).\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds - Lithuanian always includes the numeral\n if (hundreds > 0) {\n parts.push(ONES_MASC[hundreds])\n parts.push(hundreds === 1 ? HUNDRED_SINGULAR : HUNDRED_PLURAL)\n }\n\n // Tens\n if (tens > 1) {\n parts.push(TENS[tens])\n }\n\n // Teens or ones\n if (tens === 1) {\n parts.push(TEENS[ones])\n } else if (ones > 0) {\n parts.push(ONES_MASC[ones])\n }\n\n return parts.join(' ')\n}\n\n/**\n * Builds segment word for 0-999 (feminine form - only differs in ones).\n */\nfunction buildSegmentFeminine (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds - always masculine\n if (hundreds > 0) {\n parts.push(ONES_MASC[hundreds])\n parts.push(hundreds === 1 ? HUNDRED_SINGULAR : HUNDRED_PLURAL)\n }\n\n // Tens\n if (tens > 1) {\n parts.push(TENS[tens])\n }\n\n // Teens or ones - feminine for ones only\n if (tens === 1) {\n parts.push(TEENS[ones])\n } else if (ones > 0) {\n parts.push(ONES_FEM[ones])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Pluralization\n// ============================================================================\n\n/**\n * Lithuanian pluralization rules.\n * - Singular: ends in 1 (except 11)\n * - Plural: ends in 2-9 (except 12-19)\n * - Genitive: 0, 10-19, or ends in 0\n *\n * @param {number} n - The segment value\n * @param {string[]} forms - [singular, plural, genitive]\n * @returns {string} The appropriate form\n */\nfunction pluralize (n, forms) {\n if (n === 0) return forms[2]\n\n const lastDigit = n % 10\n const lastTwoDigits = n % 100\n\n // 10-19 always use genitive\n if (lastTwoDigits >= 10 && lastTwoDigits <= 19) {\n return forms[2]\n }\n\n // Ends in 0 → genitive\n if (lastDigit === 0) {\n return forms[2]\n }\n\n // Ends in 1 → singular\n if (lastDigit === 1) {\n return forms[0]\n }\n\n // Ends in 2-9 → plural\n return forms[1]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Lithuanian words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {Object} options - Conversion options\n * @returns {string} Lithuanian words\n */\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n const num = Number(n)\n return options.gender === 'feminine' ? buildSegmentFeminine(num) : buildSegment(num)\n }\n\n // For numbers >= 1000, feminine only applies to final segment if < 1000\n // But the fixture shows feminine NOT applying for n >= 1000\n // So we use masculine for all segments when n >= 1000\n return buildLargeNumberWords(n, options)\n}\n\n/**\n * Builds words for numbers >= 1000.\n *\n * @param {bigint} n - Number >= 1000\n * @param {Object} options - Conversion options\n * @returns {string} Lithuanian words\n */\nfunction buildLargeNumberWords (n, options) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const segmentWord = buildSegment(segment)\n\n if (scaleIndex === 0) {\n // Units segment - use masculine (feminine doesn't apply when n >= 1000)\n parts.push(segmentWord)\n } else {\n // Segment with scale word\n const scaleForms = SCALE_FORMS[scaleIndex - 1]\n const scaleWord = pluralize(segment, scaleForms)\n parts.push(segmentWord + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts decimal digits to Lithuanian words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {Object} options - Conversion options\n * @returns {string} Lithuanian words for decimal part\n */\nfunction decimalPartToWords (decimalPart, options) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder), options)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Lithuanian words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Conversion options\n * @param {string} [options.gender='masculine'] - Gender for numbers < 1000\n * @returns {string} The number in Lithuanian words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(42) // 'keturiasdešimt du'\n * toWords(1, { gender: 'feminine' }) // 'viena'\n * toWords(1000000) // 'vienas milijonas'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Latvian language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Two-form pluralization (singular for 1 except 11, plural otherwise)\n * - Gender agreement (masculine/feminine for numbers < 1000)\n * - Special hundreds forms (simts/simti/simtu)\n * - Omit \"one\" before scale words\n * - Long scale naming\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES_MASC = ['', 'viens', 'divi', 'trīs', 'četri', 'pieci', 'seši', 'septiņi', 'astoņi', 'deviņi']\nconst ONES_FEM = ['', 'viena', 'divas', 'trīs', 'četras', 'piecas', 'sešas', 'septiņas', 'astoņas', 'deviņas']\n\nconst TEENS = ['desmit', 'vienpadsmit', 'divpadsmit', 'trīspadsmit', 'četrpadsmit', 'piecpadsmit', 'sešpadsmit', 'septiņpadsmit', 'astoņpadsmit', 'deviņpadsmit']\nconst TENS = ['', '', 'divdesmit', 'trīsdesmit', 'četrdesmit', 'piecdesmit', 'sešdesmit', 'septiņdesmit', 'astoņdesmit', 'deviņdesmit']\n\n// Hundreds: simts (100, 110-199), simti (200-999), simtu (101-109)\nconst HUNDRED_SINGULAR = 'simts'\nconst HUNDRED_PLURAL = 'simti'\nconst HUNDRED_GENITIVE = 'simtu'\n\nconst ZERO = 'nulle'\nconst NEGATIVE = 'mīnus'\nconst DECIMAL_SEP = 'komats'\n\n// Scale words: [singular, plural, genitive]\nconst SCALE_FORMS = [\n ['tūkstotis', 'tūkstoši', 'tūkstošu'],\n ['miljons', 'miljoni', 'miljonu'],\n ['miljards', 'miljardi', 'miljardu'],\n ['triljons', 'triljoni', 'triljonu'],\n ['kvadriljons', 'kvadriljoni', 'kvadriljonu'],\n ['kvintiljons', 'kvintiljoni', 'kvintiljonu'],\n ['sikstiljons', 'sikstiljoni', 'sikstiljonu'],\n ['septiljons', 'septiljoni', 'septiljonu'],\n ['oktiljons', 'oktiljoni', 'oktiljonu']\n]\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999 (masculine form).\n * Does NOT include special handling for segment=1 (omitOneBeforeScale).\n * That's handled at join time.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds - Latvian has special forms\n if (hundreds > 0) {\n if (hundreds === 1 && tens === 0 && ones > 0) {\n // 101-109: use genitive form \"simtu\"\n parts.push(HUNDRED_GENITIVE)\n } else if (hundreds > 1) {\n // 200-999: use plural \"simti\"\n parts.push(ONES_MASC[hundreds])\n parts.push(HUNDRED_PLURAL)\n } else {\n // 100, 110-199: use singular \"simts\"\n parts.push(HUNDRED_SINGULAR)\n }\n }\n\n // Tens\n if (tens > 1) {\n parts.push(TENS[tens])\n }\n\n // Teens or ones\n if (tens === 1) {\n parts.push(TEENS[ones])\n } else if (ones > 0) {\n parts.push(ONES_MASC[ones])\n }\n\n return parts.join(' ')\n}\n\n/**\n * Builds segment word for 0-999 (feminine form - only differs in ones).\n */\nfunction buildSegmentFeminine (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds - always masculine\n if (hundreds > 0) {\n if (hundreds === 1 && tens === 0 && ones > 0) {\n parts.push(HUNDRED_GENITIVE)\n } else if (hundreds > 1) {\n parts.push(ONES_MASC[hundreds])\n parts.push(HUNDRED_PLURAL)\n } else {\n parts.push(HUNDRED_SINGULAR)\n }\n }\n\n // Tens\n if (tens > 1) {\n parts.push(TENS[tens])\n }\n\n // Teens or ones - feminine for ones only\n if (tens === 1) {\n parts.push(TEENS[ones])\n } else if (ones > 0) {\n parts.push(ONES_FEM[ones])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Pluralization\n// ============================================================================\n\n/**\n * Latvian pluralization - simpler than Slavic.\n * Singular: ends in 1 (except 11)\n * Plural: everything else\n *\n * @param {number} n - The segment value\n * @param {string[]} forms - [singular, plural, genitive]\n * @returns {string} The appropriate form\n */\nfunction pluralize (n, forms) {\n if (n === 0) return forms[2]\n\n const lastDigit = n % 10\n const lastTwoDigits = n % 100\n\n if (lastDigit === 1 && lastTwoDigits !== 11) {\n return forms[0]\n }\n\n return forms[1]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Latvian words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {Object} options - Conversion options\n * @returns {string} Latvian words\n */\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n const num = Number(n)\n return options.gender === 'feminine' ? buildSegmentFeminine(num) : buildSegment(num)\n }\n\n // For numbers >= 1000, feminine only applies to final segment if < 1000\n // But we use masculine for all segments when n >= 1000\n return buildLargeNumberWords(n, options)\n}\n\n/**\n * Builds words for numbers >= 1000.\n *\n * @param {bigint} n - Number >= 1000\n * @param {Object} options - Conversion options\n * @returns {string} Latvian words\n */\nfunction buildLargeNumberWords (n, options) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const segmentWord = buildSegment(segment)\n\n if (scaleIndex === 0) {\n // Units segment - use masculine (feminine doesn't apply when n >= 1000)\n parts.push(segmentWord)\n } else {\n // Segment with scale word\n const scaleForms = SCALE_FORMS[scaleIndex - 1]\n const scaleWord = pluralize(segment, scaleForms)\n\n // Latvian omits \"one\" before scale words\n if (segment === 1) {\n parts.push(scaleWord)\n } else {\n parts.push(segmentWord + ' ' + scaleWord)\n }\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts decimal digits to Latvian words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {Object} options - Conversion options\n * @returns {string} Latvian words for decimal part\n */\nfunction decimalPartToWords (decimalPart, options) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder), options)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Latvian words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Conversion options\n * @param {string} [options.gender='masculine'] - Gender for numbers < 1000\n * @returns {string} The number in Latvian words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(42) // 'četrdesmit divi'\n * toWords(1, { gender: 'feminine' }) // 'viena'\n * toWords(1000) // 'tūkstotis'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Marathi language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Indian numbering system (हजार, लाख, कोटी)\n * - Devanagari script\n * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)\n * - Complete word forms for 0-99\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'शून्य'\nconst NEGATIVE = 'उणे'\nconst DECIMAL_SEP = 'दशांश'\nconst HUNDRED = 'शंभर'\n\nconst BELOW_HUNDRED = [\n 'शून्य', 'एक', 'दोन', 'तीन', 'चार', 'पाच', 'सहा', 'सात', 'आठ', 'नऊ',\n 'दहा', 'अकरा', 'बारा', 'तेरा', 'चौदा', 'पंधरा', 'सोळा', 'सतरा', 'अठरा', 'एकोणीस',\n 'वीस', 'एकवीस', 'बावीस', 'तेवीस', 'चोवीस', 'पंचवीस', 'सव्वीस', 'सत्तावीस', 'अठ्ठावीस', 'एकोणतीस',\n 'तीस', 'एकतीस', 'बत्तीस', 'तेहेतीस', 'चौतीस', 'पस्तीस', 'छत्तीस', 'सदतीस', 'अडतीस', 'एकोणचाळीस',\n 'चाळीस', 'एकेचाळीस', 'बेचाळीस', 'त्रेचाळीस', 'चव्वेचाळीस', 'पंचेचाळीस', 'सेहेचाळीस', 'सत्तेचाळीस', 'अठ्ठेचाळीस', 'एकोणपन्नास',\n 'पन्नास', 'एक्काव्वन', 'बावन्न', 'त्रेपन्न', 'चोपन्न', 'पंचाव्वन', 'छप्पन्न', 'सत्तावन्न', 'अठ्ठावन्न', 'एकोणसाठ',\n 'साठ', 'एकसष्ठ', 'बासष्ठ', 'त्रेसष्ठ', 'चौसष्ठ', 'पासष्ठ', 'सहासष्ठ', 'सदुसष्ठ', 'अडुसष्ठ', 'एकोणसत्तर',\n 'सत्तर', 'एकाहत्तर', 'बाहत्तर', 'त्र्याहत्तर', 'चौऱ्याहत्तर', 'पंच्याहत्तर', 'शहात्तर', 'सत्याहत्तर', 'अठ्ठ्याहत्तर', 'एकोणऐंशी',\n 'ऐंशी', 'एक्याऐंशी', 'ब्याऐंशी', 'त्र्याऐंशी', 'चौऱ्याऐंशी', 'पंच्याऐंशी', 'शहाऐंशी', 'सत्याऐंशी', 'अठ्ठ्याऐंशी', 'एकोणनव्वद',\n 'नव्वद', 'एक्याण्णव', 'ब्याण्णव', 'त्र्याण्णव', 'चौऱ्याण्णव', 'पंच्याण्णव', 'शहाण्णव', 'सत्याण्णव', 'अठ्ठ्याण्णव', 'नव्याण्णव'\n]\n\n// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.\nconst SCALE_WORDS = ['', 'हजार', 'लाख', 'कोटी', 'अब्ज', 'खर्व', 'निखर्व', 'महापद्म', 'शंकू']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds words for a 0-999 segment.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED\n }\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Marathi words.\n *\n * Uses BigInt modulo for segment extraction (faster than string slicing).\n * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Marathi words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n segments.push(Number(n % 1000n))\n let temp = n / 1000n\n\n while (temp > 0n) {\n segments.push(Number(temp % 100n))\n temp = temp / 100n\n }\n\n // Build result string (process from most-significant to least)\n const words = []\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n words.push(buildSegment(segment))\n } else {\n words.push(BELOW_HUNDRED[segment])\n }\n\n if (i > 0 && SCALE_WORDS[i]) {\n words.push(SCALE_WORDS[i])\n }\n }\n\n return words.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : BELOW_HUNDRED[d])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Marathi words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Marathi words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Malay (Bahasa Melayu) language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - \"Se-\" prefix for ALL singular scale units (seratus, seribu, sejuta, sebilion)\n * - Regular patterns (puluh for tens, ratus for hundreds)\n * - Teens with \"belas\" suffix\n * - Note: \"lapan\" (8) differs from Indonesian \"delapan\"\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['', 'satu', 'dua', 'tiga', 'empat', 'lima', 'enam', 'tujuh', 'lapan', 'sembilan']\nconst TEENS = ['sepuluh', 'sebelas', 'dua belas', 'tiga belas', 'empat belas', 'lima belas', 'enam belas', 'tujuh belas', 'lapan belas', 'sembilan belas']\nconst TENS = ['', '', 'dua puluh', 'tiga puluh', 'empat puluh', 'lima puluh', 'enam puluh', 'tujuh puluh', 'lapan puluh', 'sembilan puluh']\n\nconst HUNDRED_WORD = 'ratus'\nconst THOUSAND_WORD = 'ribu'\nconst SCALE_WORDS = ['juta', 'bilion', 'trilion']\n\nconst ZERO = 'sifar'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'perpuluhan'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds: seratus (100) or N ratus (200-900)\n if (hundredsDigit > 0) {\n if (hundredsDigit === 1) {\n parts.push('se' + HUNDRED_WORD)\n } else {\n parts.push(ONES[hundredsDigit] + ' ' + HUNDRED_WORD)\n }\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n parts.push(ONES[tensOnes])\n } else if (tensOnes < 20) {\n parts.push(TEENS[tensOnes - 10])\n } else if (onesDigit === 0) {\n parts.push(TENS[tensDigit])\n } else {\n parts.push(TENS[tensDigit] + ' ' + ONES[onesDigit])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n let result\n if (thousands === 1) {\n result = 'se' + THOUSAND_WORD\n } else {\n result = buildSegment(thousands) + ' ' + THOUSAND_WORD\n }\n\n if (remainder > 0) {\n result += ' ' + buildSegment(remainder)\n }\n\n return result\n }\n\n return buildLargeNumberWords(n)\n}\n\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n parts.push(buildSegment(segment))\n } else if (scaleIndex === 1) {\n if (segment === 1) {\n parts.push('se' + THOUSAND_WORD)\n } else {\n parts.push(buildSegment(segment) + ' ' + THOUSAND_WORD)\n }\n } else {\n // Malay: \"se-\" prefix for ALL scale words when segment is 1\n const scaleWord = SCALE_WORDS[scaleIndex - 2]\n if (segment === 1) {\n parts.push('se' + scaleWord)\n } else {\n parts.push(buildSegment(segment) + ' ' + scaleWord)\n }\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Malay words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Malay words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Norwegian Bokmål language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Hyphenated tens+ones: \"tjue-en\" (21)\n * - \"og\" conjunction after hundreds\n * - Comma separator after thousands before hundreds\n * - Short scale: million, milliard, billion, etc.\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'en', 'to', 'tre', 'fire', 'fem', 'seks', 'syv', 'åtte', 'ni']\n\nconst TEENS = ['ti', 'elleve', 'tolv', 'tretten', 'fjorten', 'femten', 'seksten', 'sytten', 'atten', 'nitten']\nconst TENS = ['', '', 'tjue', 'tretti', 'førti', 'femti', 'seksti', 'sytti', 'åtti', 'nitti']\n\nconst HUNDRED = 'hundre'\nconst THOUSAND = 'tusen'\n\nconst ZERO = 'null'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'komma'\n\n// Short scale: million, milliard, billion, etc.\nconst SCALES = ['million', 'milliard', 'billion', 'billiard', 'kvintillion', 'sekstillion', 'septillion', 'oktillion']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n * Returns object with word and hasHundred flag.\n */\nfunction buildSegment (n) {\n if (n === 0) return { word: '', hasHundred: false }\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n let hasHundred = false\n\n // Hundreds: \"en hundre\", \"to hundre\"\n if (hundreds > 0) {\n hasHundred = true\n parts.push(ONES[hundreds] + ' ' + HUNDRED)\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n // Single digit\n parts.push(ONES[ones])\n } else if (tensOnes < 20) {\n // Teens\n parts.push(TEENS[ones])\n } else if (ones === 0) {\n // Even tens\n parts.push(TENS[tens])\n } else {\n // Hyphenated: \"tjue-en\"\n parts.push(TENS[tens] + '-' + ONES[ones])\n }\n\n // Combine with \" og \" between hundreds and remainder\n if (parts.length === 2) {\n return { word: parts[0] + ' og ' + parts[1], hasHundred: true }\n }\n return { word: parts[0] || '', hasHundred }\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Norwegian Bokmål words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Norwegian words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n)).word\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n let result = buildSegment(thousands).word + ' ' + THOUSAND\n\n if (remainder > 0) {\n const remainderResult = buildSegment(remainder)\n // Comma before hundreds, \" og \" before small numbers\n if (remainderResult.hasHundred) {\n result += ', ' + remainderResult.word\n } else {\n result += ' og ' + remainderResult.word\n }\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Norwegian words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const segmentResult = buildSegment(segment)\n\n if (scaleIndex === 0) {\n // Units segment\n parts.push({ word: segmentResult.word, hasHundred: segmentResult.hasHundred, type: 'units' })\n } else if (scaleIndex === 1) {\n // Thousands\n parts.push({ word: segmentResult.word + ' ' + THOUSAND, hasHundred: false, type: 'thousand' })\n } else {\n // Millions+\n const scaleWord = SCALES[scaleIndex - 2]\n parts.push({ word: segmentResult.word + ' ' + scaleWord, hasHundred: false, type: 'million' })\n }\n }\n\n scaleIndex--\n }\n\n // Join parts with Norwegian rules\n return joinNorwegianParts(parts)\n}\n\n/**\n * Joins parts with Norwegian spacing and comma rules.\n *\n * @param {Array} parts - Parts with type metadata\n * @returns {string} Joined string\n */\nfunction joinNorwegianParts (parts) {\n if (parts.length === 0) return ZERO\n if (parts.length === 1) return parts[0].word\n\n const result = []\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n const nextPart = parts[i + 1]\n\n result.push(part.word)\n\n if (nextPart) {\n if (part.type === 'thousand') {\n // After thousands: comma if next has hundred, else \" og \"\n if (nextPart.hasHundred) {\n result.push(', ')\n } else {\n result.push(' og ')\n }\n } else if (part.type === 'million') {\n // After millions: \" og \" for units without hundred, space otherwise\n if (nextPart.type === 'units' && !nextPart.hasHundred) {\n result.push(' og ')\n } else {\n result.push(' ')\n }\n } else {\n result.push(' ')\n }\n }\n }\n\n return result.join('')\n}\n\n/**\n * Converts decimal digits to Norwegian words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Norwegian words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Norwegian Bokmål words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Norwegian words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'tjue-en'\n * toWords(101) // 'en hundre og en'\n * toWords(1000000) // 'en million'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Dutch language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Dutch-specific rules:\n * - Inverted tens-ones: eenentwintig (one-and-twenty)\n * - \"ën\" connector when ones ends in 'e' (twee, drie)\n * - Compound words without spaces\n * - Hundred pairing for 1100-9999 (elfhonderd style)\n * - \"één\" vs \"een\" (accentOne option)\n * - Optional \"en\" separator (includeOptionalAnd option)\n * - Long scale with -ard forms\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'een', 'twee', 'drie', 'vier', 'vijf', 'zes', 'zeven', 'acht', 'negen']\nconst TEENS = ['tien', 'elf', 'twaalf', 'dertien', 'veertien', 'vijftien', 'zestien', 'zeventien', 'achttien', 'negentien']\nconst TENS = ['', '', 'twintig', 'dertig', 'veertig', 'vijftig', 'zestig', 'zeventig', 'tachtig', 'negentig']\n\nconst HUNDRED = 'honderd'\n\n// Scale words (long scale with -ard forms)\nconst SCALES = ['duizend', 'miljoen', 'miljard', 'biljoen', 'biljard', 'triljoen', 'triljard', 'quadriljoen', 'quadriljard']\n\nconst ZERO = 'nul'\nconst NEGATIVE = 'min'\nconst DECIMAL_SEP = 'komma'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n * @param {number} n - Segment value\n * @param {boolean} withAnd - Include \"en\" for values < 13 after hundreds\n * @returns {string} Dutch word (compound, no spaces)\n */\nfunction buildSegment (n, withAnd) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n const tensOnes = n % 100\n\n let result = ''\n\n // Hundreds\n if (hundreds > 0) {\n if (hundreds === 1) {\n result = HUNDRED\n } else {\n result = ONES[hundreds] + HUNDRED\n }\n }\n\n // Tens and ones\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n // Single digit - add \"en\" if withAnd and after hundreds\n if (hundreds > 0 && withAnd) {\n result += 'en' + ONES[tensOnes]\n } else {\n result += ONES[tensOnes]\n }\n } else if (tensOnes < 20) {\n // Teens - add \"en\" if withAnd and after hundreds and < 13\n if (hundreds > 0 && withAnd && tensOnes < 13) {\n result += 'en' + TEENS[ones]\n } else {\n result += TEENS[ones]\n }\n } else {\n // 20-99: Dutch inverts with connector\n if (ones === 0) {\n result += TENS[tens]\n } else {\n // \"ën\" if ones ends in 'e' (twee, drie)\n const onesWord = ONES[ones]\n const connector = onesWord.endsWith('e') ? 'ën' : 'en'\n result += onesWord + connector + TENS[tens]\n }\n }\n\n return result\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Dutch words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {Object} options - Conversion options\n * @returns {string} Dutch words\n */\nfunction integerToWords (n, options) {\n if (n === 0n) return ZERO\n\n const { accentOne, includeOptionalAnd, noHundredPairing } = options\n\n // Apply één/een replacement\n const applyAccent = (word) => {\n if (accentOne) {\n return word.replace(/\\been\\b/g, 'één')\n }\n return word\n }\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return applyAccent(buildSegment(Number(n), includeOptionalAnd))\n }\n\n // Hundred pairing for 1100-9999\n if (!noHundredPairing && n >= 1100n && n < 10000n) {\n const high = Number(n / 100n)\n const low = Number(n % 100n)\n\n // Only use pairing when high is not a multiple of 10\n if (high % 10 !== 0) {\n let result = buildSegment(high, includeOptionalAnd) + HUNDRED\n if (low > 0) {\n const lowWord = buildSegment(low, includeOptionalAnd)\n if (includeOptionalAnd && low < 13) {\n result += ' en ' + lowWord\n } else {\n result += ' ' + lowWord\n }\n }\n return applyAccent(result)\n }\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n let result\n if (thousands === 1) {\n // \"duizend\" not \"eenduizend\"\n result = SCALES[0]\n } else {\n // Compound: \"vijfduizend\"\n result = buildSegment(thousands, includeOptionalAnd) + SCALES[0]\n }\n\n if (remainder > 0) {\n const remainderWord = buildSegment(remainder, includeOptionalAnd)\n if (includeOptionalAnd && remainder < 13) {\n result += ' en ' + remainderWord\n } else {\n result += ' ' + remainderWord\n }\n }\n\n return applyAccent(result)\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return applyAccent(buildLargeNumberWords(n, options))\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n * Uses BigInt division for faster segment extraction (4x faster than string slicing).\n *\n * @param {bigint} n - Number >= 1,000,000\n * @param {Object} options - Conversion options\n * @returns {string} Dutch words\n */\nfunction buildLargeNumberWords (n, options) {\n const { includeOptionalAnd } = options\n\n // Extract segments using BigInt division (faster than string slicing)\n // Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)\n const segmentValues = []\n let temp = n\n while (temp > 0n) {\n segmentValues.push(Number(temp % 1000n))\n temp = temp / 1000n\n }\n\n // Build result string directly (avoids object allocation and join)\n let result = ''\n let prevWasScale = false\n\n for (let i = segmentValues.length - 1; i >= 0; i--) {\n const segment = segmentValues[i]\n if (segment === 0) continue\n\n if (i === 0) {\n // Units segment\n const word = buildSegment(segment, includeOptionalAnd)\n if (result) {\n if (prevWasScale && includeOptionalAnd && segment < 13) {\n result += ' en ' + word\n } else {\n result += ' ' + word\n }\n } else {\n result = word\n }\n prevWasScale = false\n } else if (i === 1) {\n // Thousands - compound\n if (result) result += ' '\n if (segment === 1) {\n result += SCALES[0]\n } else {\n result += buildSegment(segment, includeOptionalAnd) + SCALES[0]\n }\n prevWasScale = true\n } else {\n // Million and above - space around scale\n const scaleWord = SCALES[i - 1]\n if (result) result += ' '\n if (segment === 1) {\n result += 'een ' + scaleWord\n } else {\n result += buildSegment(segment, includeOptionalAnd) + ' ' + scaleWord\n }\n prevWasScale = true\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to Dutch words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {Object} options - Conversion options\n * @returns {string} Dutch words for decimal part\n */\nfunction decimalPartToWords (decimalPart, options) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n const word = integerToWords(BigInt(remainder), { ...options, noHundredPairing: true })\n result += word\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Dutch words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.accentOne=true] - Use \"één\" instead of \"een\"\n * @param {boolean} [options.includeOptionalAnd=false] - Include \"en\" before small numbers\n * @param {boolean} [options.noHundredPairing=false] - Disable hundred pairing (1104→duizend honderdvier)\n * @returns {string} The number in Dutch words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'eenentwintig'\n * toWords(1) // 'één'\n * toWords(1, {accentOne: false}) // 'een'\n * toWords(1104) // 'elfhonderd vier'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n const opts = {\n accentOne: options.accentOne !== false, // default true\n includeOptionalAnd: options.includeOptionalAnd || false,\n noHundredPairing: options.noHundredPairing || false\n }\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, opts)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, opts)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Punjabi language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Indian numbering system (ਹਜ਼ਾਰ, ਲੱਖ, ਕਰੋੜ)\n * - Gurmukhi script\n * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)\n * - Complete word forms for 0-99\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'ਸਿਫ਼ਰ'\nconst NEGATIVE = 'ਮਾਇਨਸ'\nconst DECIMAL_SEP = 'ਦਸ਼ਮਲਵ'\nconst HUNDRED = 'ਸੌ'\n\nconst BELOW_HUNDRED = [\n 'ਸਿਫ਼ਰ', 'ਇੱਕ', 'ਦੋ', 'ਤਿੰਨ', 'ਚਾਰ', 'ਪੰਜ', 'ਛੇ', 'ਸੱਤ', 'ਅੱਠ', 'ਨੌਂ',\n 'ਦੱਸ', 'ਗਿਆਰਾਂ', 'ਬਾਰਾਂ', 'ਤੇਰਾਂ', 'ਚੌਦਾਂ', 'ਪੰਦਰਾਂ', 'ਸੋਲਾਂ', 'ਸਤਾਰਾਂ', 'ਅਠਾਰਾਂ', 'ਉੱਨੀ',\n 'ਵੀਹ', 'ਇੱਕੀ', 'ਬਾਈ', 'ਤੇਈ', 'ਚੌਬੀ', 'ਪੱਚੀ', 'ਛੱਬੀ', 'ਸਤਾਈ', 'ਅਠਾਈ', 'ਉਨੱਤੀ',\n 'ਤੀਹ', 'ਇਕੱਤੀ', 'ਬੱਤੀ', 'ਤੇਤੀ', 'ਚੌਂਤੀ', 'ਪੈਂਤੀ', 'ਛੱਤੀ', 'ਸੈਂਤੀ', 'ਅਠੱਤੀ', 'ਉਨਤਾਲੀ',\n 'ਚਾਲੀ', 'ਇਕਤਾਲੀ', 'ਬਿਆਲੀ', 'ਤਿਰਤਾਲੀ', 'ਚੁਵਾਲੀ', 'ਪੰਤਾਲੀ', 'ਛਿਆਲੀ', 'ਸੈਂਤਾਲੀ', 'ਅਠਤਾਲੀ', 'ਉਨੰਜਾ',\n 'ਪੰਜਾਹ', 'ਇਕਵੰਜਾ', 'ਬਵੰਜਾ', 'ਤਰਵੰਜਾ', 'ਚੁਰਵੰਜਾ', 'ਪੰਜਵੰਜਾ', 'ਛਪੰਜਾ', 'ਸੱਤਵੰਜਾ', 'ਅਠਵੰਜਾ', 'ਉਨਾਹਠ',\n 'ਸੱਠ', 'ਇਕਾਹਠ', 'ਬਾਹਠ', 'ਤਰਸਠ', 'ਚੌਂਸਠ', 'ਪੈਂਸਠ', 'ਛਿਆਸਠ', 'ਸੜਸਠ', 'ਅੜਸਠ', 'ਉਣਹੱਤਰ',\n 'ਸਤੱਰ', 'ਇਕਹੱਤਰ', 'ਬਹੱਤਰ', 'ਤਹੱਤਰ', 'ਚੌਹੱਤਰ', 'ਪੰਝਹੱਤਰ', 'ਛਿਹੱਤਰ', 'ਸਤੱਤਰ', 'ਅਠੱਤਰ', 'ਉਨਾਸੀ',\n 'ਅੱਸੀ', 'ਇਕਿਆਸੀ', 'ਬਿਆਸੀ', 'ਤਰਿਆਸੀ', 'ਚੌਰਿਆਸੀ', 'ਪਚਾਸੀ', 'ਛਿਆਸੀ', 'ਸੱਤਾਸੀ', 'ਅਠਾਸੀ', 'ਨਵਾਸੀ',\n 'ਨੱਬੇ', 'ਇਕਾਨਵੇਂ', 'ਬਾਨਵੇਂ', 'ਤਰਾਨਵੇਂ', 'ਚੁਰਾਨਵੇਂ', 'ਪੰਚਾਨਵੇਂ', 'ਛਿਆਨਵੇਂ', 'ਸਤਾਨਵੇਂ', 'ਅਠਾਨਵੇਂ', 'ਨਿਨਾਨਵੇਂ'\n]\n\n// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.\nconst SCALE_WORDS = ['', 'ਹਜ਼ਾਰ', 'ਲੱਖ', 'ਕਰੋੜ', 'ਅਰਬ', 'ਖਰਬ', 'ਨੀਲ', 'ਪਦਮ', 'ਸ਼ੰਖ']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds words for a 0-999 segment.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED\n }\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Punjabi words.\n *\n * Uses BigInt modulo for segment extraction (faster than string slicing).\n * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Punjabi words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n segments.push(Number(n % 1000n))\n let temp = n / 1000n\n\n while (temp > 0n) {\n segments.push(Number(temp % 100n))\n temp = temp / 100n\n }\n\n // Build result string (process from most-significant to least)\n const words = []\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n words.push(buildSegment(segment))\n } else {\n words.push(BELOW_HUNDRED[segment])\n }\n\n if (i > 0 && SCALE_WORDS[i]) {\n words.push(SCALE_WORDS[i])\n }\n }\n\n return words.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Punjabi words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Punjabi words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Polish language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Polish-specific rules:\n * - Three-form pluralization: 1 = singular, 2-4 = few, 5+ = many\n * - Gender agreement (masculine/feminine for numbers < 1000)\n * - Omit \"jeden\" before scale words (tysiąc, milion, etc.)\n * - Irregular hundreds: dwieście, trzysta, czterysta, pięćset...\n * - Long scale with -ard forms: miliard, biliard, tryliard\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES_MASC = ['', 'jeden', 'dwa', 'trzy', 'cztery', 'pięć', 'sześć', 'siedem', 'osiem', 'dziewięć']\nconst ONES_FEM = ['', 'jedna', 'dwie', 'trzy', 'cztery', 'pięć', 'sześć', 'siedem', 'osiem', 'dziewięć']\n\nconst TEENS = ['dziesięć', 'jedenaście', 'dwanaście', 'trzynaście', 'czternaście', 'piętnaście', 'szesnaście', 'siedemnaście', 'osiemnaście', 'dziewiętnaście']\n\nconst TENS = ['', '', 'dwadzieścia', 'trzydzieści', 'czterdzieści', 'pięćdziesiąt', 'sześćdziesiąt', 'siedemdziesiąt', 'osiemdziesiąt', 'dziewięćdziesiąt']\n\n// Irregular hundreds\nconst HUNDREDS = ['', 'sto', 'dwieście', 'trzysta', 'czterysta', 'pięćset', 'sześćset', 'siedemset', 'osiemset', 'dziewięćset']\n\n// Scale words: [singular, few (2-4), many (5+)]\nconst PLURAL_FORMS = {\n 1: ['tysiąc', 'tysiące', 'tysięcy'],\n 2: ['milion', 'miliony', 'milionów'],\n 3: ['miliard', 'miliardy', 'miliardów'],\n 4: ['bilion', 'biliony', 'bilionów'],\n 5: ['biliard', 'biliardy', 'biliardów'],\n 6: ['trylion', 'tryliony', 'trylionów'],\n 7: ['tryliard', 'tryliardy', 'tryliardów'],\n 8: ['kwadrylion', 'kwadryliony', 'kwadrylionów'],\n 9: ['kwaryliard', 'kwadryliardy', 'kwadryliardów'],\n 10: ['kwintylion', 'kwintyliony', 'kwintylionów']\n}\n\nconst ZERO = 'zero'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'przecinek'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999 (masculine form).\n * @param {number} n - Segment value\n * @returns {string} Polish word\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds\n if (hundreds > 0) {\n parts.push(HUNDREDS[hundreds])\n }\n\n // Tens and ones\n if (tens === 1) {\n // Teens (10-19)\n parts.push(TEENS[ones])\n } else {\n if (tens >= 2) {\n parts.push(TENS[tens])\n }\n if (ones > 0) {\n parts.push(ONES_MASC[ones])\n }\n }\n\n return parts.join(' ')\n}\n\n/**\n * Builds segment word for 0-999 (feminine form - only differs in ones).\n * @param {number} n - Segment value\n * @returns {string} Polish word\n */\nfunction buildSegmentFeminine (n) {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds\n if (hundreds > 0) {\n parts.push(HUNDREDS[hundreds])\n }\n\n // Tens and ones - feminine for ones only\n if (tens === 1) {\n parts.push(TEENS[ones])\n } else {\n if (tens >= 2) {\n parts.push(TENS[tens])\n }\n if (ones > 0) {\n parts.push(ONES_FEM[ones])\n }\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Polish pluralization: 1 = singular, 2-4 = few, else = many.\n * Special case: 11-19 always use many form.\n *\n * @param {bigint} n - Number to pluralize\n * @param {string[]} forms - [singular, few, many]\n * @returns {string} Correct plural form\n */\nfunction pluralize (n, forms) {\n if (n === 1n) {\n return forms[0]\n }\n\n const lastDigit = n % 10n\n const lastTwoDigits = n % 100n\n\n // Teens (11-19) always use many form\n // 2-4 use few form (but not 12-14)\n if (lastDigit >= 2n && lastDigit <= 4n && (lastTwoDigits < 10n || lastTwoDigits > 20n)) {\n return forms[1]\n }\n\n return forms[2]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Polish words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {Object} options - Conversion options\n * @returns {string} Polish words\n */\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return options.gender === 'feminine' ? buildSegmentFeminine(Number(n)) : buildSegment(Number(n))\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n const scaleWord = pluralize(BigInt(thousands), PLURAL_FORMS[1])\n\n let result\n if (thousands === 1) {\n // Omit \"jeden\" before tysiąc\n result = scaleWord\n } else {\n result = buildSegment(thousands) + ' ' + scaleWord\n }\n\n if (remainder > 0) {\n result += ' ' + buildSegment(remainder)\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n, options)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n * Uses BigInt division for faster segment extraction.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @param {Object} options - Conversion options\n * @returns {string} Polish words\n */\nfunction buildLargeNumberWords (n, options) {\n // Extract segments using BigInt division (faster than string slicing)\n // Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)\n const segmentValues = []\n let temp = n\n while (temp > 0n) {\n segmentValues.push(temp % 1000n)\n temp = temp / 1000n\n }\n\n // Build result string directly\n let result = ''\n\n for (let i = segmentValues.length - 1; i >= 0; i--) {\n const segment = segmentValues[i]\n if (segment === 0n) continue\n\n const segmentWord = buildSegment(Number(segment))\n\n if (result) result += ' '\n\n if (i === 0) {\n // Units segment\n result += segmentWord\n } else {\n // Scale word needed\n const forms = PLURAL_FORMS[i]\n if (forms) {\n const scaleWord = pluralize(segment, forms)\n\n if (segment === 1n) {\n // Omit \"jeden\" before scale words\n result += scaleWord\n } else {\n result += segmentWord + ' ' + scaleWord\n }\n }\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to Polish words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {Object} options - Conversion options\n * @returns {string} Polish words for decimal part\n */\nfunction decimalPartToWords (decimalPart, options) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder), options)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Polish words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Conversion options\n * @param {string} [options.gender='masculine'] - Gender for numbers < 1000\n * @returns {string} The number in Polish words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(1) // 'jeden'\n * toWords(1, { gender: 'feminine' }) // 'jedna'\n * toWords(1000) // 'tysiąc'\n * toWords(2000) // 'dwa tysiące'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Portuguese language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Portuguese-specific rules:\n * - \"e\" conjunction everywhere: vinte e um, cento e um, mil e um\n * - \"cem\" for exact 100, \"cento\" for 100+ remainder\n * - Irregular hundreds: duzentos, trezentos, quatrocentos, etc.\n * - Compound scale: milhão (10^6), mil milhões (10^9), bilião (10^12)\n * - Omit \"um\" before \"mil\"\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'um', 'dois', 'três', 'quatro', 'cinco', 'seis', 'sete', 'oito', 'nove']\nconst TEENS = ['dez', 'onze', 'doze', 'treze', 'catorze', 'quinze', 'dezasseis', 'dezassete', 'dezoito', 'dezanove']\nconst TENS = ['', '', 'vinte', 'trinta', 'quarenta', 'cinquenta', 'sessenta', 'setenta', 'oitenta', 'noventa']\n\n// Irregular hundreds\nconst HUNDREDS = ['', 'cento', 'duzentos', 'trezentos', 'quatrocentos', 'quinhentos', 'seiscentos', 'setecentos', 'oitocentos', 'novecentos']\n\nconst THOUSAND = 'mil'\nconst ZERO = 'zero'\nconst NEGATIVE = 'menos'\nconst DECIMAL_SEP = 'vírgula'\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999 with Portuguese \"e\" rules.\n * Returns the word and whether it's an exact hundred (for \"cem\" handling).\n */\nfunction buildSegment (n) {\n if (n === 0) return { word: '', isExactHundred: false }\n\n // Special case: exact 100 is \"cem\"\n if (n === 100) return { word: 'cem', isExactHundred: true }\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds\n if (hundreds > 0) {\n parts.push(HUNDREDS[hundreds])\n }\n\n // Tens and ones\n if (tens === 1) {\n // Teens (10-19)\n parts.push(TEENS[ones])\n } else if (tens >= 2) {\n if (ones > 0) {\n // Tens + ones with \"e\": \"vinte e um\"\n parts.push(TENS[tens] + ' e ' + ONES[ones])\n } else {\n parts.push(TENS[tens])\n }\n } else if (ones > 0) {\n parts.push(ONES[ones])\n }\n\n // Join hundreds with \"e\": \"cento e um\", \"duzentos e trinta e um\"\n const word = parts.join(' e ')\n\n return { word, isExactHundred: hundreds > 0 && tens === 0 && ones === 0, startsWithHundreds: n >= 100 }\n}\n\n// ============================================================================\n// Scale Word Lookup\n// ============================================================================\n\n// Precompute scale words for singular and plural forms\n// Index 1 = thousands, 2 = millions, 3 = billions (mil milhões), etc.\nconst SCALE_WORDS_SINGULAR = [\n '', // 0 unused\n THOUSAND, // 1: mil\n 'milhão', // 2: 10^6\n 'mil milhões', // 3: 10^9 (compound)\n 'bilião', // 4: 10^12\n 'mil biliões', // 5: 10^15 (compound)\n 'trilião', // 6: 10^18\n 'mil triliões', // 7: 10^21 (compound)\n 'quatrilião' // 8: 10^24\n]\n\nconst SCALE_WORDS_PLURAL = [\n '', // 0 unused\n THOUSAND, // 1: mil (same)\n 'milhões', // 2: 10^6\n 'mil milhões', // 3: 10^9 (compound, same)\n 'biliões', // 4: 10^12\n 'mil biliões', // 5: 10^15 (compound, same)\n 'triliões', // 6: 10^18\n 'mil triliões', // 7: 10^21 (compound, same)\n 'quatriliões' // 8: 10^24\n]\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Portuguese words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Portuguese words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n)).word\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n let result\n if (thousands === 1) {\n // \"mil\" not \"um mil\"\n result = THOUSAND\n } else {\n result = buildSegment(thousands).word + ' ' + THOUSAND\n }\n\n if (remainder > 0) {\n const remainderResult = buildSegment(remainder)\n // Insert \"e\" before remainder if it doesn't start with hundreds (< 100)\n if (!remainderResult.startsWithHundreds) {\n result += ' e ' + remainderResult.word\n } else {\n result += ' ' + remainderResult.word\n }\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n * Uses BigInt division for faster segment extraction.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Portuguese words\n */\nfunction buildLargeNumberWords (n) {\n // Extract segments using BigInt division\n // Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)\n const segments = []\n let temp = n\n while (temp > 0n) {\n segments.push(Number(temp % 1000n))\n temp = temp / 1000n\n }\n\n // Find the first non-zero segment index (lowest scale with value)\n let firstNonZeroIdx = 0\n for (let i = 0; i < segments.length; i++) {\n if (segments[i] !== 0) {\n firstNonZeroIdx = i\n break\n }\n }\n\n // Build result string directly\n let result = ''\n let prevWasScale = false\n\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n const segmentResult = buildSegment(segment)\n const isLastSegment = (i === firstNonZeroIdx)\n\n // Add \"e\" before final segment if previous was scale and this doesn't start with hundreds\n if (result && isLastSegment && prevWasScale && !segmentResult.startsWithHundreds) {\n result += ' e'\n }\n\n if (result) result += ' '\n\n if (i === 0) {\n // Units segment\n result += segmentResult.word\n prevWasScale = false\n } else if (i === 1) {\n // Thousands\n if (segment === 1) {\n result += THOUSAND\n } else {\n result += segmentResult.word + ' ' + THOUSAND\n }\n prevWasScale = true\n } else {\n // Million and above - use scale arrays\n const scaleWord = segment === 1 ? SCALE_WORDS_SINGULAR[i] : SCALE_WORDS_PLURAL[i]\n if (segment === 1) {\n result += 'um ' + scaleWord\n } else {\n result += segmentResult.word + ' ' + scaleWord\n }\n prevWasScale = true\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to Portuguese words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Portuguese words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Portuguese words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Portuguese words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'vinte e um'\n * toWords(100) // 'cem'\n * toWords(1000000) // 'um milhão'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Romanian language converter - Functional Implementation\n *\n * A performance-optimized number-to-words converter using precomputed lookup tables.\n *\n * Key features:\n * - Gender agreement (unu/una, doi/două)\n * - \"De\" preposition insertion for groups >= 20\n * - Complex scale word handling (mie/mii, milion/milioane)\n * - Feminine units for thousands\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES_MASC = ['', 'unu', 'doi', 'trei', 'patru', 'cinci', 'șase', 'șapte', 'opt', 'nouă']\nconst ONES_FEM = ['', 'una', 'două', 'trei', 'patru', 'cinci', 'șase', 'șapte', 'opt', 'nouă']\n\nconst TEENS = ['zece', 'unsprezece', 'douăsprezece', 'treisprezece', 'paisprezece', 'cincisprezece', 'șaisprezece', 'șaptesprezece', 'optsprezece', 'nouăsprezece']\nconst TEENS_MASC = ['zece', 'unsprezece', 'doisprezece', 'treisprezece', 'paisprezece', 'cincisprezece', 'șaisprezece', 'șaptesprezece', 'optsprezece', 'nouăsprezece']\n\nconst TWENTIES = ['', '', 'douăzeci', 'treizeci', 'patruzeci', 'cincizeci', 'șaizeci', 'șaptezeci', 'optzeci', 'nouăzeci']\n\nconst HUNDREDS = ['', 'o sută', 'două sute', 'trei sute', 'patru sute', 'cinci sute', 'șase sute', 'șapte sute', 'opt sute', 'nouă sute']\n\nconst ZERO = 'zero'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'virgulă'\n\n// Scale metadata: [singular, plural, article, feminine, needsDe]\n// - singular: form for 1\n// - plural: form for 2+\n// - article: 'o' for feminine, 'un' for masculine\n// - feminine: whether units should be feminine\n// - needsDe: whether \"de\" is inserted for segment >= 20\nconst SCALE_META = [\n { singular: 'mie', plural: 'mii', article: 'o', feminine: true, needsDe: true },\n { singular: 'milion', plural: 'milioane', article: 'un', feminine: false, needsDe: true },\n { singular: 'miliard', plural: 'miliarde', article: 'un', feminine: false, needsDe: true },\n { singular: 'trilion', plural: 'trilioane', article: 'un', feminine: false, needsDe: true },\n { singular: 'cvadrilion', plural: 'cvadrilioane', article: 'un', feminine: false, needsDe: true },\n { singular: 'cvintilion', plural: 'cvintilioane', article: 'un', feminine: false, needsDe: true },\n { singular: 'sextilion', plural: 'sextilioane', article: 'un', feminine: false, needsDe: true },\n { singular: 'septilion', plural: 'septilioane', article: 'un', feminine: false, needsDe: true },\n { singular: 'octilion', plural: 'octilioane', article: 'un', feminine: false, needsDe: true }\n]\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Spells number under 100.\n */\nfunction spellUnder100 (n, feminine = false, masculineTeens = false) {\n if (n === 0) return ''\n if (n < 10) {\n return feminine ? ONES_FEM[n] : ONES_MASC[n]\n }\n if (n < 20) {\n return masculineTeens ? TEENS_MASC[n - 10] : TEENS[n - 10]\n }\n const t = Math.floor(n / 10)\n const u = n % 10\n if (u === 0) {\n return TWENTIES[t]\n }\n const onesWord = feminine ? ONES_FEM[u] : ONES_MASC[u]\n return TWENTIES[t] + ' și ' + onesWord\n}\n\n/**\n * Spells number under 1000.\n */\nfunction spellUnder1000 (n, feminine = false, masculineTeens = false) {\n if (n === 0) return ''\n if (n < 100) return spellUnder100(n, feminine, masculineTeens)\n\n const h = Math.floor(n / 100)\n const r = n % 100\n const hundredWord = HUNDREDS[h]\n\n if (r === 0) return hundredWord\n return hundredWord + ' ' + spellUnder100(r, feminine, masculineTeens)\n}\n\n/**\n * Builds scale word with proper pluralization and \"de\" insertion.\n * Romanian always uses feminine forms (două, not doi) when counting scale words.\n */\nfunction buildScalePhrase (segment, scaleIndex) {\n const meta = SCALE_META[scaleIndex - 1]\n if (!meta) return spellUnder1000(segment, true)\n\n if (segment === 1) {\n return meta.article + ' ' + meta.singular\n }\n\n // Special case: 21 with scale words uses feminine \"una\"\n if (segment === 21 && meta.needsDe) {\n return 'douăzeci și una de ' + meta.plural\n }\n\n // Romanian always uses feminine when counting scale words (două milioane, not doi milioane)\n const words = spellUnder1000(segment, true)\n\n // \"de\" after >= 20\n const needsDe = meta.needsDe && segment >= 20\n const separator = needsDe ? ' de ' : ' '\n\n return words + separator + meta.plural\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Romanian words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {Object} options - Conversion options\n * @returns {string} Romanian words\n */\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n const feminine = options.gender === 'feminine'\n return spellUnder1000(Number(n), feminine)\n }\n\n return buildLargeNumberWords(n, options)\n}\n\n/**\n * Builds words for numbers >= 1000.\n * Uses BigInt division for faster segment extraction.\n *\n * @param {bigint} n - Number >= 1000\n * @param {Object} options - Conversion options\n * @returns {string} Romanian words\n */\nfunction buildLargeNumberWords (n, options) {\n // Extract segments using BigInt division (faster than string slicing)\n // Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)\n const segmentValues = []\n let temp = n\n while (temp > 0n) {\n segmentValues.push(Number(temp % 1000n))\n temp = temp / 1000n\n }\n\n // Build result string directly (avoid regex cleanup)\n let result = ''\n\n for (let i = segmentValues.length - 1; i >= 0; i--) {\n const segment = segmentValues[i]\n if (segment === 0) continue\n\n let segmentWords\n if (i === 0) {\n // Units segment - use gender from options\n const feminine = options.gender === 'feminine'\n segmentWords = spellUnder1000(segment, feminine)\n } else {\n // Scale segment\n segmentWords = buildScalePhrase(segment, i)\n }\n\n if (result && segmentWords) {\n result += ' ' + segmentWords\n } else if (segmentWords) {\n result = segmentWords\n }\n }\n\n return result\n}\n\n/**\n * Converts decimal digits to Romanian words.\n * Decimals always use masculine forms.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Romanian words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n let i = 0\n\n // Handle leading zeros\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number (masculine, with masculine teens)\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n const n = BigInt(remainder)\n if (n < 1000n) {\n result += spellUnder1000(Number(n), false, true)\n } else {\n result += integerToWords(n, { gender: 'masculine' })\n }\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Romanian words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Conversion options\n * @param {string} [options.gender='masculine'] - Gender for numbers\n * @returns {string} The number in Romanian words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'douăzeci și unu'\n * toWords(1, { gender: 'feminine' }) // 'una'\n * toWords(1000) // 'o mie'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Russian language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Three-form pluralization (one/few/many)\n * - Gender: thousands are feminine, millions+ are masculine\n * - Irregular hundreds (двести, триста, etc.)\n * - Long scale naming\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES_MASC = ['', 'один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']\nconst ONES_FEM = ['', 'одна', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']\n\nconst TEENS = ['десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать']\nconst TENS = ['', '', 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят', 'семьдесят', 'восемьдесят', 'девяносто']\n\n// Irregular hundreds\nconst HUNDREDS = ['', 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот', 'семьсот', 'восемьсот', 'девятьсот']\n\nconst ZERO = 'ноль'\nconst NEGATIVE = 'минус'\nconst DECIMAL_SEP = 'запятая'\n\n// Scale words: [singular, few, many]\n// Thousands (index 0) are feminine, rest are masculine\nconst SCALE_FORMS = [\n ['тысяча', 'тысячи', 'тысяч'],\n ['миллион', 'миллиона', 'миллионов'],\n ['миллиард', 'миллиарда', 'миллиардов'],\n ['триллион', 'триллиона', 'триллионов'],\n ['квадриллион', 'квадриллиона', 'квадриллионов'],\n ['квинтиллион', 'квинтиллиона', 'квинтиллионов'],\n ['секстиллион', 'секстиллиона', 'секстиллионов'],\n ['септиллион', 'септиллиона', 'септиллионов'],\n ['октиллион', 'октиллиона', 'октиллионов'],\n ['нониллион', 'нониллиона', 'нониллионов']\n]\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\nfunction pluralize (n, forms) {\n const num = typeof n === 'bigint' ? Number(n) : n\n const lastDigit = num % 10\n const lastTwoDigits = num % 100\n\n if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {\n return forms[2]\n }\n\n if (lastDigit === 1) return forms[0]\n if (lastDigit >= 2 && lastDigit <= 4) return forms[1]\n return forms[2]\n}\n\nfunction buildSegmentMasc (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n parts.push(HUNDREDS[hundredsDigit])\n }\n\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[onesDigit])\n } else if (onesDigit > 0) {\n parts.push(ONES_MASC[onesDigit])\n }\n\n return parts.join(' ')\n}\n\nfunction buildSegmentFem (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n parts.push(HUNDREDS[hundredsDigit])\n }\n\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[onesDigit])\n } else if (onesDigit > 0) {\n parts.push(ONES_FEM[onesDigit])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n const feminine = options.gender === 'feminine'\n\n if (n < 1000n) {\n return feminine ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))\n }\n\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n // Thousands are always feminine in Russian\n const thousandsWord = buildSegmentFem(thousands)\n const scaleWord = pluralize(thousands, SCALE_FORMS[0])\n\n let result = thousandsWord + ' ' + scaleWord\n\n if (remainder > 0) {\n result += ' ' + (feminine ? buildSegmentFem(remainder) : buildSegmentMasc(remainder))\n }\n\n return result\n }\n\n return buildLargeNumberWords(n, options)\n}\n\nfunction buildLargeNumberWords (n, options) {\n const feminine = options.gender === 'feminine'\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n parts.push(feminine ? buildSegmentFem(segment) : buildSegmentMasc(segment))\n } else {\n const scaleForms = SCALE_FORMS[scaleIndex - 1]\n const scaleWord = pluralize(segment, scaleForms)\n // Thousands (scaleIndex=1) are feminine, others masculine\n const isFeminine = scaleIndex === 1\n const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)\n parts.push(segmentWord + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart, options) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder), options)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Russian words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @returns {string} The number in Russian words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Serbian Cyrillic language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Three-form pluralization (one/few/many)\n * - Gender: thousands are feminine, millions+ are masculine\n * - Irregular hundreds\n * - Long scale naming with -ard forms\n * - Cyrillic script\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES_MASC = ['', 'један', 'два', 'три', 'четири', 'пет', 'шест', 'седам', 'осам', 'девет']\nconst ONES_FEM = ['', 'једна', 'две', 'три', 'четири', 'пет', 'шест', 'седам', 'осам', 'девет']\nconst TEENS = ['десет', 'једанаест', 'дванаест', 'тринаест', 'четрнаест', 'петнаест', 'шеснаест', 'седамнаест', 'осамнаест', 'деветнаест']\nconst TENS = ['', '', 'двадесет', 'тридесет', 'четрдесет', 'педесет', 'шездесет', 'седамдесет', 'осамдесет', 'деведесет']\nconst HUNDREDS = ['', 'сто', 'двеста', 'триста', 'четиристо', 'петсто', 'шесто', 'седамсто', 'осамсто', 'девестo']\n\nconst ZERO = 'нула'\nconst NEGATIVE = 'минус'\nconst DECIMAL_SEP = 'запета'\n\n// Scale words: [singular, few, many]\nconst SCALE_FORMS = [\n ['хиљада', 'хиљаде', 'хиљада'],\n ['милион', 'милиона', 'милиона'],\n ['милијарда', 'милијарде', 'милијарда'],\n ['билион', 'билиона', 'билиона'],\n ['билијарда', 'билијарде', 'билијарда'],\n ['трилион', 'трилиона', 'трилиона'],\n ['трилијарда', 'трилијарде', 'трилијарда'],\n ['квадрилион', 'квадрилиона', 'квадрилиона'],\n ['квадрилијарда', 'квадрилијарде', 'квадрилијарда']\n]\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\nfunction pluralize (n, forms) {\n const num = typeof n === 'bigint' ? Number(n) : n\n const lastDigit = num % 10\n const lastTwoDigits = num % 100\n\n if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {\n return forms[2]\n }\n\n if (lastDigit === 1) return forms[0]\n if (lastDigit >= 2 && lastDigit <= 4) return forms[1]\n return forms[2]\n}\n\nfunction buildSegmentMasc (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n parts.push(HUNDREDS[hundredsDigit])\n }\n\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[onesDigit])\n } else if (onesDigit > 0) {\n parts.push(ONES_MASC[onesDigit])\n }\n\n return parts.join(' ')\n}\n\nfunction buildSegmentFem (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n parts.push(HUNDREDS[hundredsDigit])\n }\n\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[onesDigit])\n } else if (onesDigit > 0) {\n parts.push(ONES_FEM[onesDigit])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return options.gender === 'feminine' ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))\n }\n\n return buildLargeNumberWords(n, options)\n}\n\nfunction buildLargeNumberWords (n, options) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n parts.push(options.gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))\n } else {\n const scaleForms = SCALE_FORMS[scaleIndex - 1]\n const scaleWord = pluralize(segment, scaleForms)\n // Thousands (scaleIndex=1) are feminine, others masculine\n const isFeminine = scaleIndex === 1\n const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)\n parts.push(segmentWord + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart, options) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder), options)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Serbian (Cyrillic) words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @returns {string} The number in Serbian Cyrillic words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Serbian Latin language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Three-form pluralization (one/few/many)\n * - Gender: thousands are feminine, millions+ are masculine\n * - Irregular hundreds (dvesta, trista, etc.)\n * - Long scale naming with -ard forms\n * - Latin script\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES_MASC = ['', 'jedan', 'dva', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']\nconst ONES_FEM = ['', 'jedna', 'dve', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']\nconst TEENS = ['deset', 'jedanaest', 'dvanaest', 'trinaest', 'četrnaest', 'petnaest', 'šesnaest', 'sedamnaest', 'osamnaest', 'devetnaest']\nconst TENS = ['', '', 'dvadeset', 'trideset', 'četrdeset', 'pedeset', 'šezdeset', 'sedamdeset', 'osamdeset', 'devedeset']\nconst HUNDREDS = ['', 'sto', 'dvesta', 'trista', 'četiristo', 'petsto', 'šesto', 'sedamsto', 'osamsto', 'devetsto']\n\nconst ZERO = 'nula'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'zapeta'\n\n// Scale words: [singular, few, many]\nconst SCALE_FORMS = [\n ['hiljada', 'hiljade', 'hiljada'],\n ['milion', 'miliona', 'miliona'],\n ['milijarda', 'milijarde', 'milijarda'],\n ['bilion', 'biliona', 'biliona'],\n ['bilijarda', 'bilijarde', 'bilijarda'],\n ['trilion', 'triliona', 'triliona'],\n ['trilijarda', 'trilijarde', 'trilijarda'],\n ['kvadrilion', 'kvadriliona', 'kvadriliona'],\n ['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda']\n]\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\nfunction pluralize (n, forms) {\n const num = typeof n === 'bigint' ? Number(n) : n\n const lastDigit = num % 10\n const lastTwoDigits = num % 100\n\n if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {\n return forms[2]\n }\n\n if (lastDigit === 1) return forms[0]\n if (lastDigit >= 2 && lastDigit <= 4) return forms[1]\n return forms[2]\n}\n\nfunction buildSegmentMasc (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n parts.push(HUNDREDS[hundredsDigit])\n }\n\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[onesDigit])\n } else if (onesDigit > 0) {\n parts.push(ONES_MASC[onesDigit])\n }\n\n return parts.join(' ')\n}\n\nfunction buildSegmentFem (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n parts.push(HUNDREDS[hundredsDigit])\n }\n\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[onesDigit])\n } else if (onesDigit > 0) {\n parts.push(ONES_FEM[onesDigit])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return options.gender === 'feminine' ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))\n }\n\n return buildLargeNumberWords(n, options)\n}\n\nfunction buildLargeNumberWords (n, options) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n parts.push(options.gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))\n } else {\n const scaleForms = SCALE_FORMS[scaleIndex - 1]\n const scaleWord = pluralize(segment, scaleForms)\n // Thousands (scaleIndex=1) are feminine, others masculine\n const isFeminine = scaleIndex === 1\n const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)\n parts.push(segmentWord + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart, options) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder), options)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Serbian (Latin) words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @returns {string} The number in Serbian Latin words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Swedish language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Hyphenation for tens-ones (tjugo-ett)\n * - \"och\" after hundreds before small numbers\n * - Omit \"ett\" before hundra and tusen\n * - Use \"en\" (not \"ett\") before million+ scales\n * - Long scale naming with -ard forms\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'ett', 'två', 'tre', 'fyra', 'fem', 'sex', 'sju', 'åtta', 'nio']\n\nconst TEENS = ['tio', 'elva', 'tolv', 'tretton', 'fjorton', 'femton', 'sexton', 'sjutton', 'arton', 'nitton']\nconst TENS = ['', '', 'tjugo', 'trettio', 'fyrtio', 'femtio', 'sextio', 'sjuttio', 'åttio', 'nittio']\n\nconst HUNDRED = 'hundra'\n\nconst ZERO = 'noll'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'komma'\n\n// Scale words (long scale with -ard forms)\nconst SCALES = ['tusen', 'miljon', 'miljard', 'biljon', 'biljard', 'triljon', 'triljard', 'kvadriljon']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n * Returns object with word and metadata for \"och\" logic.\n */\nfunction buildSegment (n) {\n if (n === 0) return { word: '', hasHundred: false, lessThan100: false }\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n let hasHundred = false\n\n // Hundreds - omit \"ett\" before hundra\n if (hundreds > 0) {\n hasHundred = true\n if (hundreds === 1) {\n parts.push(HUNDRED)\n } else {\n parts.push(ONES[hundreds] + ' ' + HUNDRED)\n }\n }\n\n // Tens and ones with hyphenation\n let tensOnesWord = ''\n if (tens === 1) {\n tensOnesWord = TEENS[ones]\n } else if (tens >= 2) {\n if (ones > 0) {\n tensOnesWord = TENS[tens] + '-' + ONES[ones]\n } else {\n tensOnesWord = TENS[tens]\n }\n } else if (ones > 0) {\n tensOnesWord = ONES[ones]\n }\n\n // Combine with \"och\" after hundreds if there's a remainder\n if (hasHundred && tensOnesWord) {\n return { word: parts[0] + ' och ' + tensOnesWord, hasHundred: true, lessThan100: false }\n } else if (hasHundred) {\n return { word: parts[0], hasHundred: true, lessThan100: false }\n } else {\n return { word: tensOnesWord, hasHundred: false, lessThan100: true }\n }\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Swedish words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Swedish words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n)).word\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n // Omit \"ett\" before tusen\n let result = thousands === 1 ? SCALES[0] : buildSegment(thousands).word + ' ' + SCALES[0]\n\n if (remainder > 0) {\n const remainderResult = buildSegment(remainder)\n // Insert \"och\" if remainder < 100 (doesn't have hundred)\n if (remainderResult.lessThan100) {\n result += ' och ' + remainderResult.word\n } else {\n result += ' ' + remainderResult.word\n }\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Swedish words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const segmentResult = buildSegment(segment)\n\n if (scaleIndex === 0) {\n // Units segment\n parts.push({\n word: segmentResult.word,\n hasHundred: segmentResult.hasHundred,\n isScale: false\n })\n } else {\n // Segment with scale word\n const scaleWord = SCALES[scaleIndex - 1]\n\n let segmentWord\n if (segment === 1) {\n // Omit \"ett\" before tusen, use \"en\" before million+\n if (scaleIndex === 1) {\n segmentWord = '' // Just \"tusen\"\n } else {\n segmentWord = 'en' // \"en miljon\"\n }\n } else {\n segmentWord = segmentResult.word\n }\n\n if (segmentWord) {\n parts.push({ word: segmentWord, hasHundred: false, isScale: false })\n }\n parts.push({ word: scaleWord, hasHundred: false, isScale: true })\n }\n }\n\n scaleIndex--\n }\n\n // Join with Swedish \"och\" rules\n return joinSwedishParts(parts)\n}\n\n/**\n * Joins parts with Swedish \"och\" rules.\n * Insert \"och\" before final segment if it follows a scale word and doesn't have \"hundra\".\n *\n * @param {Array} parts - Parts with metadata\n * @returns {string} Joined string\n */\nfunction joinSwedishParts (parts) {\n if (parts.length === 0) return ZERO\n if (parts.length === 1) return parts[0].word\n\n const result = []\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n const isLast = i === parts.length - 1\n\n if (isLast && parts.length > 1) {\n const prevPart = parts[i - 1]\n // Insert \"och\" if previous was scale and this doesn't have hundred\n if (prevPart.isScale && !part.hasHundred) {\n result.push('och')\n }\n }\n\n result.push(part.word)\n }\n\n return result.join(' ')\n}\n\n/**\n * Converts decimal digits to Swedish words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Swedish words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Swedish words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Swedish words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(42) // 'fyrtio-två'\n * toWords(101) // 'hundra och ett'\n * toWords(1000000) // 'en miljon'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Swahili language converter - Functional Implementation\n *\n * Self-contained converter with precomputed lookup tables.\n *\n * Key features:\n * - \"na\" connector for compound numbers\n * - Reversed hundreds: \"mia moja\" (one hundred)\n * - Scale words: elfu, milioni, bilioni\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['sifuri', 'moja', 'mbili', 'tatu', 'nne', 'tano', 'sita', 'saba', 'nane', 'tisa']\nconst TENS = { 10: 'kumi', 20: 'ishirini', 30: 'thelathini', 40: 'arobaini', 50: 'hamsini', 60: 'sitini', 70: 'sabini', 80: 'themanini', 90: 'tisini' }\n\nconst SCALE_WORDS = ['', 'elfu', 'milioni', 'bilioni', 'trilioni', 'kwadrilioni', 'kwintilioni']\n\nconst ZERO = 'sifuri'\nconst NEGATIVE = 'minus'\nconst DECIMAL_SEP = 'nukta'\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction wordsUnder100 (n) {\n if (n < 10) return ONES[n]\n if (n === 10) return TENS[10]\n if (n < 20) {\n // 11-19: 'kumi na <digit>'\n return TENS[10] + ' na ' + ONES[n - 10]\n }\n const tens = Math.trunc(n / 10) * 10\n const ones = n % 10\n if (ones === 0) return TENS[tens]\n return TENS[tens] + ' na ' + ONES[ones]\n}\n\nfunction wordsUnder1000 (n) {\n if (n < 100) return wordsUnder100(n)\n if (n === 100) return 'mia moja'\n const hundreds = Math.trunc(n / 100)\n const rest = n % 100\n const parts = []\n\n // Hundreds: 'mia <digit>'\n parts.push('mia ' + ONES[hundreds])\n if (rest > 0) {\n if (rest < 10) {\n parts.push('na ' + ONES[rest])\n } else {\n parts.push(wordsUnder100(rest))\n }\n }\n\n return parts.join(' ')\n}\n\nfunction extractSegments (n) {\n const segments = []\n let temp = n\n while (temp > 0n) {\n segments.push(Number(temp % 1000n))\n temp = temp / 1000n\n }\n return segments\n}\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // segments stored least-significant first: [ones, thousands, millions, ...]\n const segments = extractSegments(n)\n const parts = []\n\n // Iterate from highest scale to lowest\n for (let scaleIndex = segments.length - 1; scaleIndex >= 0; scaleIndex--) {\n const val = segments[scaleIndex]\n if (val === 0) continue\n\n if (scaleIndex === 0) {\n // Units segment\n if (val < 10 && parts.length > 0) {\n parts.push('na ' + ONES[val])\n } else if (val === 100 && parts.length > 0) {\n // In compound numbers (e.g., 1100 -> 'elfu moja mia'), use 'mia' not 'mia moja'\n parts.push('mia')\n } else {\n parts.push(wordsUnder1000(val))\n }\n } else {\n // Scale segments: 'elfu moja', 'milioni mbili'\n const unit = (val === 1) ? 'moja' : wordsUnder1000(val)\n parts.push(SCALE_WORDS[scaleIndex] + ' ' + unit)\n }\n }\n\n return parts.join(' ').trim()\n}\n\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Swahili words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Swahili words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Tamil language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Indian numbering system (ஆயிரம், லட்சம், கோடி)\n * - Tamil script\n * - 3-2-2 grouping pattern\n * - Complete word forms for 0-99\n * - Special hundred word transformations (connected vs standalone)\n * - Per-digit decimal reading\n * - BigInt modulo for efficient segment extraction\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'பூஜ்ஜியம்'\nconst NEGATIVE = 'மைனஸ்'\nconst DECIMAL_SEP = 'புள்ளி'\n\nconst BELOW_HUNDRED = [\n 'பூஜ்ஜியம்', 'ஒன்று', 'இரண்டு', 'மூன்று', 'நான்கு', 'ஐந்து', 'ஆறு', 'ஏழு', 'எட்டு', 'ஒன்பது',\n 'பத்து', 'பதினொன்று', 'பன்னிரண்டு', 'பதிமூன்று', 'பதினான்கு', 'பதினைந்து', 'பதினாறு', 'பதினேழு', 'பதினெட்டு', 'பத்தொன்பது',\n 'இருபது', 'இருபத்தொன்று', 'இருபத்திரண்டு', 'இருபத்திமூன்று', 'இருபத்திநான்கு', 'இருபத்தைந்து', 'இருபத்தாறு', 'இருபத்தேழு', 'இருபத்தெட்டு', 'இருபத்தொன்பது',\n 'முப்பது', 'முப்பத்தொன்று', 'முப்பத்திரண்டு', 'முப்பத்திமூன்று', 'முப்பத்திநான்கு', 'முப்பத்தைந்து', 'முப்பத்தாறு', 'முப்பத்தேழு', 'முப்பத்தெட்டு', 'முப்பத்தொன்பது',\n 'நாற்பது', 'நாற்பத்தொன்று', 'நாற்பத்திரண்டு', 'நாற்பத்திமூன்று', 'நாற்பத்திநான்கு', 'நாற்பத்தைந்து', 'நாற்பத்தாறு', 'நாற்பத்தேழு', 'நாற்பத்தெட்டு', 'நாற்பத்தொன்பது',\n 'ஐம்பது', 'ஐம்பத்தொன்று', 'ஐம்பத்திரண்டு', 'ஐம்பத்திமூன்று', 'ஐம்பத்திநான்கு', 'ஐம்பத்தைந்து', 'ஐம்பத்தாறு', 'ஐம்பத்தேழு', 'ஐம்பத்தெட்டு', 'ஐம்பத்தொன்பது',\n 'அறுபது', 'அறுபத்தொன்று', 'அறுபத்திரண்டு', 'அறுபத்திமூன்று', 'அறுபத்திநான்கு', 'அறுபத்தைந்து', 'அறுபத்தாறு', 'அறுபத்தேழு', 'அறுபத்தெட்டு', 'அறுபத்தொன்பது',\n 'எழுபது', 'எழுபத்தொன்று', 'எழுபத்திரண்டு', 'எழுபத்திமூன்று', 'எழுபத்திநான்கு', 'எழுபத்தைந்து', 'எழுபத்தாறு', 'எழுபத்தேழு', 'எழுபத்தெட்டு', 'எழுபத்தொன்பது',\n 'எண்பது', 'எண்பத்தொன்று', 'எண்பத்திரண்டு', 'எண்பத்திமூன்று', 'எண்பத்திநான்கு', 'எண்பத்தைந்து', 'எண்பத்தாறு', 'எண்பத்தேழு', 'எண்பத்தெட்டு', 'எண்பத்தொன்பது',\n 'தொண்ணூறு', 'தொண்ணூற்று ஒன்று', 'தொண்ணூற்று இரண்டு', 'தொண்ணூற்று மூன்று', 'தொண்ணூற்று நான்கு', 'தொண்ணூற்று ஐந்து', 'தொண்ணூற்று ஆறு', 'தொண்ணூற்று ஏழு', 'தொண்ணூற்று எட்டு', 'தொண்ணூற்று ஒன்பது'\n]\n\n// Standalone hundreds (when not followed by remainder)\nconst HUNDREDS = ['', 'நூறு', 'இருநூறு', 'முன்னூறு', 'நானூறு', 'ஐநூறு', 'அறுநூறு', 'எழுநூறு', 'எண்நூறு', 'தொள்ளாயிரம்']\n\n// Connected form of hundreds (when followed by remainder) - precomputed\nconst HUNDREDS_CONNECTED = ['', 'நூற்று', 'இருநூற்று', 'முன்னூற்று', 'நானூற்று', 'ஐநூற்று', 'அறுநூற்று', 'எழுநூற்று', 'எண்நூற்று', 'தொள்ளாயிரத்து']\n\n// Ones for decimal reading\nconst ONES = ['ஒன்று', 'இரண்டு', 'மூன்று', 'நான்கு', 'ஐந்து', 'ஆறு', 'ஏழு', 'எட்டு', 'ஒன்பது']\n\n// Scale words: index 0 = units, 1 = thousand, 2 = lakh, etc.\nconst SCALE_WORDS = ['', 'ஆயிரம்', 'லட்சம்', 'கோடி', 'அரபு', 'கராபு', 'நீல்', 'பத்ம', 'சங்கு']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds words for a 0-999 segment.\n *\n * @param {number} n - Number 0-999\n * @returns {string} Tamil words for the segment\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return HUNDREDS[hundreds]\n }\n\n // Use connected form when followed by remainder\n return HUNDREDS_CONNECTED[hundreds] + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Tamil words.\n *\n * Uses BigInt modulo for segment extraction (faster than string slicing).\n * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Tamil words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n segments.push(Number(n % 1000n))\n let temp = n / 1000n\n\n while (temp > 0n) {\n segments.push(Number(temp % 100n))\n temp = temp / 100n\n }\n\n // Build result string (process from most-significant to least)\n const words = []\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n words.push(buildSegment(segment))\n } else {\n // Use 'ஒரு' for 1 at scale positions\n const groupWords = (segment === 1) ? 'ஒரு' : BELOW_HUNDRED[segment]\n words.push(groupWords)\n }\n\n if (i > 0 && SCALE_WORDS[i]) {\n words.push(SCALE_WORDS[i])\n }\n }\n\n return words.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d - 1])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Tamil words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Tamil words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Telugu language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Indian numbering system (వెయ్యి, లక్ష, కోటి)\n * - Telugu script\n * - 3-2-2 grouping pattern\n * - Complete word forms for 0-99\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'సున్నా'\nconst NEGATIVE = 'మైనస్'\nconst DECIMAL_SEP = 'పాయింట్'\n\nconst BELOW_HUNDRED = [\n 'సున్నా', 'ఒకటి', 'రెండు', 'మూడు', 'నాలుగు', 'ఐదు', 'ఆరు', 'ఏడు', 'ఎనిమిది', 'తొమ్మిది',\n 'పది', 'పదకొండు', 'పన్నెండు', 'పదమూడు', 'పద్నాలుగు', 'పదిహేను', 'పదహారు', 'పదిహేడు', 'పద్దెనిమిది', 'పంతొమ్మిది',\n 'ఇరవై', 'ఇరవై ఒక్కటి', 'ఇరవై రెండు', 'ఇరవై మూడు', 'ఇరవై నాలుగు', 'ఇరవై ఐదు', 'ఇరవై ఆరు', 'ఇరవై ఏడు', 'ఇరవై ఎనిమిది', 'ఇరవై తొమ్మిది',\n 'ముప్పై', 'ముప్పై ఒకటి', 'ముప్పై రెండు', 'ముప్పై మూడు', 'ముప్పై నాలుగు', 'ముప్పై ఐదు', 'ముప్పై ఆరు', 'ముప్పై ఏడు', 'ముప్పై ఎనిమిది', 'ముప్పై తొమ్మిది',\n 'నలభై', 'నలభై ఒకటి', 'నలభై రెండు', 'నలభై మూడు', 'నలభై నాలుగు', 'నలభై ఐదు', 'నలభై ఆరు', 'నలభై ఏడు', 'నలభై ఎనిమిది', 'నలభై తొమ్మిది',\n 'యాభై', 'యాభై ఒకటి', 'యాభై రెండు', 'యాభై మూడు', 'యాభై నాలుగు', 'యాభై ఐదు', 'యాభై ఆరు', 'యాభై ఏడు', 'యాభై ఎనిమిది', 'యాభై తొమ్మిది',\n 'అరవై', 'అరవై ఒకటి', 'అరవై రెండు', 'అరవై మూడు', 'అరవై నాలుగు', 'అరవై ఐదు', 'అరవై ఆరు', 'అరవై ఏడు', 'అరవై ఎనిమిది', 'అరవై తొమ్మిది',\n 'డెబ్బై', 'డెబ్బై ఒకటి', 'డెబ్బై రెండు', 'డెబ్బై మూడు', 'డెబ్బై నాలుగు', 'డెబ్బై ఐదు', 'డెబ్బై ఆరు', 'డెబ్బై ఏడు', 'డెబ్బై ఎనిమిది', 'డెబ్బై తొమ్మిది',\n 'ఎనభై', 'ఎనభై ఒకటి', 'ఎనభై రెండు', 'ఎనభై మూడు', 'ఎనభై నాలుగు', 'ఎనభై ఐదు', 'ఎనభై ఆరు', 'ఎనభై ఏడు', 'ఎనభై ఎనిమిది', 'ఎనభై తొమ్మిది',\n 'తొంభై', 'తొంభై ఒకటి', 'తొంభై రెండు', 'తొంభై మూడు', 'తొంభై నాలుగు', 'తొంభై ఐదు', 'తొంభై ఆరు', 'తొంభై ఏడు', 'తొంభై ఎనిమిది', 'తొంభై తొమ్మిది'\n]\n\nconst HUNDREDS = ['', 'వంద', 'రెండు వందలు', 'మూడు వందలు', 'నాలుగు వందలు', 'ఐదు వందలు', 'ఆరు వందలు', 'ఏడు వందలు', 'ఎనిమిది వందలు', 'తొమ్మిది వందలు']\n\n// Ones for decimal reading\nconst ONES = ['ఒకటి', 'రెండు', 'మూడు', 'నాలుగు', 'ఐదు', 'ఆరు', 'ఏడు', 'ఎనిమిది', 'తొమ్మిది']\n\n// Scale words: index 0 = units, 1 = thousand, 2 = lakh, etc.\nconst SCALE_WORDS = ['', 'వెయ్యి', 'లక్ష', 'కోటి', 'అరబ్', 'ఖరబ్', 'నిల్', 'పడ్మ', 'శంకు']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds words for a 0-999 segment.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return HUNDREDS[hundreds]\n }\n return HUNDREDS[hundreds] + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Telugu words.\n *\n * Uses BigInt modulo for segment extraction (faster than string slicing).\n * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Telugu words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n segments.push(Number(n % 1000n))\n let temp = n / 1000n\n\n while (temp > 0n) {\n segments.push(Number(temp % 100n))\n temp = temp / 100n\n }\n\n // Build result string (process from most-significant to least)\n const words = []\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n words.push(buildSegment(segment))\n } else {\n // Use 'ఒక' for 1 at scale positions\n const groupWords = (segment === 1) ? 'ఒక' : BELOW_HUNDRED[segment]\n words.push(groupWords)\n }\n\n if (i > 0 && SCALE_WORDS[i]) {\n words.push(SCALE_WORDS[i])\n }\n }\n\n return words.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d - 1])\n }\n return digits.join(' ')\n}\n\n/**\n * Converts a numeric value to Telugu words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Telugu words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Thai language converter - Functional Implementation\n *\n * Self-contained converter with precomputed lookup tables.\n *\n * Key features:\n * - No word separators (continuous Thai script)\n * - Million-based grouping (ล้าน)\n * - Special handling for 1 as \"เอ็ด\" in compounds\n * - 20 is \"ยี่สิบ\" (not \"สองสิบ\")\n * - Per-digit decimal reading\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES = ['หนึ่ง', 'สอง', 'สาม', 'สี่', 'ห้า', 'หก', 'เจ็ด', 'แปด', 'เก้า']\n\nconst ZERO = 'ศูนย์'\nconst NEGATIVE = 'ลบ'\nconst DECIMAL_SEP = 'จุด'\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction convertBelowMillion (n) {\n if (n === 0) return ''\n\n let value = n\n const parts = []\n\n const hundredThousands = Math.trunc(value / 100000)\n value %= 100000\n const tenThousands = Math.trunc(value / 10000)\n value %= 10000\n const thousands = Math.trunc(value / 1000)\n value %= 1000\n const hundreds = Math.trunc(value / 100)\n value %= 100\n const tens = Math.trunc(value / 10)\n const ones = value % 10\n\n if (hundredThousands > 0) {\n parts.push(ONES[hundredThousands - 1] + 'แสน')\n }\n\n if (tenThousands > 0) {\n if (tenThousands === 1) {\n parts.push('หนึ่งหมื่น')\n } else {\n parts.push(ONES[tenThousands - 1] + 'หมื่น')\n }\n }\n\n if (thousands > 0) {\n parts.push(ONES[thousands - 1] + 'พัน')\n }\n\n if (hundreds > 0) {\n parts.push(ONES[hundreds - 1] + 'ร้อย')\n }\n\n if (tens > 0) {\n if (tens === 1) {\n parts.push('สิบ')\n } else if (tens === 2) {\n parts.push('ยี่สิบ')\n } else {\n parts.push(ONES[tens - 1] + 'สิบ')\n }\n }\n\n if (ones > 0) {\n const hasHigher = hundredThousands > 0 || tenThousands > 0 || thousands > 0 || hundreds > 0 || tens > 0\n if (ones === 1 && (tens > 0 || hasHigher)) {\n parts.push('เอ็ด')\n } else {\n parts.push(ONES[ones - 1])\n }\n }\n\n return parts.join('')\n}\n\nfunction splitMillionGroups (n) {\n const groups = []\n let remaining = n\n\n const million = 1_000_000n\n while (remaining > 0n) {\n const chunk = Number(remaining % million)\n groups.unshift(chunk)\n remaining = remaining / million\n }\n\n return groups\n}\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n const groups = splitMillionGroups(n)\n const parts = []\n\n for (let i = 0; i < groups.length; i++) {\n const groupValue = groups[i]\n if (groupValue === 0) continue\n\n parts.push(convertBelowMillion(groupValue))\n const remaining = groups.length - i - 1\n if (remaining > 0) {\n parts.push('ล้าน'.repeat(remaining))\n }\n }\n\n return parts.join('')\n}\n\nfunction decimalPartToWords (decimalPart) {\n // Per-digit decimal reading\n const digits = []\n for (const char of decimalPart) {\n const d = parseInt(char, 10)\n digits.push(d === 0 ? ZERO : ONES[d - 1])\n }\n return digits.join('')\n}\n\n/**\n * Converts a numeric value to Thai words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Thai words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += DECIMAL_SEP + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Turkish language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Omits 'bir' (one) before hundreds and thousands\n * - Optional dropSpaces for compound form\n * - Short scale naming\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\nconst ONES = ['', 'bir', 'iki', 'üç', 'dört', 'beş', 'altı', 'yedi', 'sekiz', 'dokuz']\n\nconst TEENS = ['on', 'on bir', 'on iki', 'on üç', 'on dört', 'on beş', 'on altı', 'on yedi', 'on sekiz', 'on dokuz']\nconst TENS = ['', '', 'yirmi', 'otuz', 'kırk', 'elli', 'altmış', 'yetmiş', 'seksen', 'doksan']\n\nconst HUNDRED = 'yüz'\nconst THOUSAND = 'bin'\n\nconst ZERO = 'sıfır'\nconst NEGATIVE = 'eksi'\nconst DECIMAL_SEP = 'virgül'\n\n// Short scale\nconst SCALES = ['milyon', 'milyar', 'trilyon', 'katrilyon', 'kentilyon']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n * Omits \"bir\" before \"yüz\" (hundred).\n */\nfunction buildSegment (n, separator = ' ') {\n if (n === 0) return ''\n\n const ones = n % 10\n const tens = Math.floor(n / 10) % 10\n const hundreds = Math.floor(n / 100)\n\n const parts = []\n\n // Hundreds - omit \"bir\" before yüz\n if (hundreds > 0) {\n if (hundreds === 1) {\n parts.push(HUNDRED)\n } else {\n parts.push(ONES[hundreds] + separator + HUNDRED)\n }\n }\n\n // Tens and ones\n const tensOnes = n % 100\n\n if (tensOnes === 0) {\n // Just hundreds\n } else if (tensOnes < 10) {\n parts.push(ONES[ones])\n } else if (tensOnes < 20) {\n parts.push(TEENS[ones].replace(' ', separator))\n } else if (ones === 0) {\n parts.push(TENS[tens])\n } else {\n parts.push(TENS[tens] + separator + ONES[ones])\n }\n\n return parts.join(separator)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Turkish words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @param {Object} options - Conversion options\n * @returns {string} Turkish words\n */\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n const sep = options.dropSpaces ? '' : ' '\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n), sep)\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n // Omit \"bir\" before bin (thousand)\n let result\n if (thousands === 1) {\n result = THOUSAND\n } else {\n result = buildSegment(thousands, sep) + sep + THOUSAND\n }\n\n if (remainder > 0) {\n result += sep + buildSegment(remainder, sep)\n }\n\n return result\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n, options)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @param {Object} options - Conversion options\n * @returns {string} Turkish words\n */\nfunction buildLargeNumberWords (n, options) {\n const sep = options.dropSpaces ? '' : ' '\n\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n const segmentWord = buildSegment(segment, sep)\n\n if (scaleIndex === 0) {\n // Units segment\n parts.push(segmentWord)\n } else if (scaleIndex === 1) {\n // Thousands - omit \"bir\" before bin\n if (segment === 1) {\n parts.push(THOUSAND)\n } else {\n parts.push(segmentWord + sep + THOUSAND)\n }\n } else {\n // Millions+ - \"bir\" is kept before scale words\n const scaleWord = SCALES[scaleIndex - 2]\n parts.push(segmentWord + sep + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(sep)\n}\n\n/**\n * Converts decimal digits to Turkish words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @param {Object} options - Conversion options\n * @returns {string} Turkish words for decimal part\n */\nfunction decimalPartToWords (decimalPart, options) {\n const sep = options.dropSpaces ? '' : ' '\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += sep\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += sep\n result += integerToWords(BigInt(remainder), options)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Turkish words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Conversion options\n * @param {boolean} [options.dropSpaces=false] - Remove spaces for compound form\n * @returns {string} The number in Turkish words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(21) // 'yirmi bir'\n * toWords(21, { dropSpaces: true }) // 'yirmibir'\n * toWords(1000) // 'bin'\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n const sep = options.dropSpaces ? '' : ' '\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + sep\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += sep + DECIMAL_SEP + sep + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Ukrainian language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Three-form pluralization (one/few/many)\n * - Gender: thousands are feminine, millions+ are masculine\n * - Irregular hundreds\n * - Long scale naming\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ONES_MASC = ['', 'один', 'два', 'три', 'чотири', 'п\\'ять', 'шiсть', 'сiм', 'вiсiм', 'дев\\'ять']\nconst ONES_FEM = ['', 'одна', 'двi', 'три', 'чотири', 'п\\'ять', 'шiсть', 'сiм', 'вiсiм', 'дев\\'ять']\n\nconst TEENS = ['десять', 'одинадцять', 'дванадцять', 'тринадцять', 'чотирнадцять', 'п\\'ятнадцять', 'шiстнадцять', 'сiмнадцять', 'вiсiмнадцять', 'дев\\'ятнадцять']\nconst TENS = ['', '', 'двадцять', 'тридцять', 'сорок', 'п\\'ятдесят', 'шiстдесят', 'сiмдесят', 'вiсiмдесят', 'дев\\'яносто']\n\n// Irregular hundreds\nconst HUNDREDS = ['', 'сто', 'двiстi', 'триста', 'чотириста', 'п\\'ятсот', 'шiстсот', 'сiмсот', 'вiсiмсот', 'дев\\'ятсот']\n\nconst ZERO = 'нуль'\nconst NEGATIVE = 'мiнус'\nconst DECIMAL_SEP = 'кома'\n\n// Scale words: [singular, few, many]\n// Thousands (index 0) are feminine, rest are masculine\nconst SCALE_FORMS = [\n ['тисяча', 'тисячi', 'тисяч'],\n ['мiльйон', 'мiльйони', 'мiльйонiв'],\n ['мiльярд', 'мiльярди', 'мiльярдiв'],\n ['трильйон', 'трильйони', 'трильйонiв'],\n ['квадрильйон', 'квадрильйони', 'квадрильйонiв'],\n ['квiнтильйон', 'квiнтильйони', 'квiнтильйонiв'],\n ['секстильйон', 'секстильйони', 'секстильйонiв'],\n ['септильйон', 'септильйони', 'септильйонiв'],\n ['октильйон', 'октильйони', 'октильйонiв']\n]\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\nfunction pluralize (n, forms) {\n const num = typeof n === 'bigint' ? Number(n) : n\n const lastDigit = num % 10\n const lastTwoDigits = num % 100\n\n if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {\n return forms[2]\n }\n\n if (lastDigit === 1) return forms[0]\n if (lastDigit >= 2 && lastDigit <= 4) return forms[1]\n return forms[2]\n}\n\nfunction buildSegmentMasc (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n parts.push(HUNDREDS[hundredsDigit])\n }\n\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[onesDigit])\n } else if (onesDigit > 0) {\n parts.push(ONES_MASC[onesDigit])\n }\n\n return parts.join(' ')\n}\n\nfunction buildSegmentFem (n) {\n if (n === 0) return ''\n\n const onesDigit = n % 10\n const tensDigit = Math.floor(n / 10) % 10\n const hundredsDigit = Math.floor(n / 100)\n\n const parts = []\n\n if (hundredsDigit > 0) {\n parts.push(HUNDREDS[hundredsDigit])\n }\n\n if (tensDigit > 1) {\n parts.push(TENS[tensDigit])\n }\n\n if (tensDigit === 1) {\n parts.push(TEENS[onesDigit])\n } else if (onesDigit > 0) {\n parts.push(ONES_FEM[onesDigit])\n }\n\n return parts.join(' ')\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return options.gender === 'feminine' ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))\n }\n\n return buildLargeNumberWords(n, options)\n}\n\nfunction buildLargeNumberWords (n, options) {\n const numStr = n.toString()\n const len = numStr.length\n\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n\n if (segment !== 0) {\n if (scaleIndex === 0) {\n parts.push(options.gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))\n } else {\n const scaleForms = SCALE_FORMS[scaleIndex - 1]\n const scaleWord = pluralize(segment, scaleForms)\n // Thousands (scaleIndex=1) are feminine, others masculine\n const isFeminine = scaleIndex === 1\n const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)\n parts.push(segmentWord + ' ' + scaleWord)\n }\n }\n\n scaleIndex--\n }\n\n return parts.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart, options) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder), options)\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Ukrainian words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender\n * @returns {string} The number in Ukrainian words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart, options)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Urdu language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key features:\n * - Indian numbering system (ہزار, لاکھ, کروڑ)\n * - Urdu script (right-to-left)\n * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)\n * - Complete word forms for 0-99\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\nconst ZERO = 'صفر'\nconst NEGATIVE = 'منفی'\nconst DECIMAL_SEP = 'اعشاریہ'\nconst HUNDRED = 'سو'\n\nconst BELOW_HUNDRED = [\n 'صفر', 'ایک', 'دو', 'تین', 'چار', 'پانچ', 'چھ', 'سات', 'آٹھ', 'نو',\n 'دس', 'گیارہ', 'بارہ', 'تیرہ', 'چودہ', 'پندرہ', 'سولہ', 'سترہ', 'اٹھارہ', 'انیس',\n 'بیس', 'اکیس', 'بائیس', 'تیئیس', 'چوبیس', 'پچیس', 'چھبیس', 'ستائیس', 'اٹھائیس', 'انتیس',\n 'تیس', 'اکتیس', 'بتیس', 'تینتیس', 'چونتیس', 'پینتیس', 'چھتیس', 'سینتیس', 'اڑتیس', 'انتالیس',\n 'چالیس', 'اکتالیس', 'بیالیس', 'تینتالیس', 'چوالیس', 'پینتالیس', 'چھالیس', 'سینتالیس', 'اڑتالیس', 'انچاس',\n 'پچاس', 'اکاون', 'باون', 'ترپن', 'چون', 'پچپن', 'چھپن', 'ستاون', 'اٹھاون', 'انسٹھ',\n 'ساٹھ', 'اکسٹھ', 'باسٹھ', 'ترسٹھ', 'چونسٹھ', 'پینسٹھ', 'چھیاسٹھ', 'سڑسٹھ', 'اڑسٹھ', 'انہتر',\n 'ستر', 'اکہتر', 'بہتر', 'تہتر', 'چوہتر', 'پچھتر', 'چھہتر', 'ستتر', 'اٹھہتر', 'اناسی',\n 'اسی', 'اکیاسی', 'بیاسی', 'تریاسی', 'چوراسی', 'پچاسی', 'چھیاسی', 'ستاسی', 'اٹھاسی', 'نواسی',\n 'نوے', 'اکانوے', 'بانوے', 'ترانوے', 'چورانوے', 'پچانوے', 'چھیانوے', 'ستانوے', 'اٹھانوے', 'ننانوے'\n]\n\n// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.\nconst SCALE_WORDS = ['', 'ہزار', 'لاکھ', 'کروڑ', 'ارب', 'کھرب', 'نیل', 'پدم', 'شنکھ']\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds words for a 0-999 segment.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n if (n < 100) return BELOW_HUNDRED[n]\n\n const hundreds = Math.trunc(n / 100)\n const remainder = n % 100\n\n if (remainder === 0) {\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED\n }\n return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Urdu words.\n *\n * Uses BigInt modulo for segment extraction (faster than string slicing).\n * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Urdu words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Extract segments using BigInt modulo\n const segments = []\n segments.push(Number(n % 1000n))\n let temp = n / 1000n\n\n while (temp > 0n) {\n segments.push(Number(temp % 100n))\n temp = temp / 100n\n }\n\n // Build result string (process from most-significant to least)\n const words = []\n for (let i = segments.length - 1; i >= 0; i--) {\n const segment = segments[i]\n if (segment === 0) continue\n\n if (i === 0) {\n words.push(buildSegment(segment))\n } else {\n words.push(BELOW_HUNDRED[segment])\n }\n\n if (i > 0 && SCALE_WORDS[i]) {\n words.push(SCALE_WORDS[i])\n }\n }\n\n return words.join(' ')\n}\n\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n let i = 0\n\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Urdu words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Urdu words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Vietnamese language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Vietnamese-specific rules:\n * - Special pronunciation: \"lăm\" for 5 in tens position, \"mốt\" for final 1\n * - \"Lẻ\" (odd/extra) marker when tens place is zero after hundreds/scales\n * - Short scale system with Vietnamese words (nghìn, triệu, tỷ)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Base vocabulary for building lookup tables\nconst ONES = ['không', 'một', 'hai', 'ba', 'bốn', 'năm', 'sáu', 'bảy', 'tám', 'chín']\n\n// Scale words indexed by scale level (0 = units, 1 = thousands, etc.)\nconst SCALES = [\n '', 'nghìn', 'triệu', 'tỷ', 'nghìn tỷ', 'trăm nghìn tỷ',\n 'Quintillion', 'Sextillion', 'Septillion', 'Octillion',\n 'Nonillion', 'Decillion', 'Undecillion', 'Duodecillion',\n 'Tredecillion', 'Quattuordecillion', 'Sexdecillion',\n 'Septendecillion', 'Octodecillion', 'Novemdecillion', 'Vigintillion'\n]\n\nconst HUNDRED = 'trăm'\nconst ZERO = 'không'\nconst NEGATIVE = 'âm'\nconst DECIMAL_SEP = 'phẩy'\nconst LE = 'lẻ' // \"odd/extra\" marker for gaps\n\n// Special forms\nconst MOT_FINAL = 'mốt' // 1 in tens position (21, 31, etc.)\nconst LAM = 'lăm' // 5 in tens position (25, 35, etc.)\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds word for 0-99 with special forms (mốt, lăm).\n */\nfunction buildBelowHundred (n) {\n if (n === 0) return ONES[0]\n if (n < 10) return ONES[n]\n\n // Teens: 10-19\n if (n < 20) {\n const ones = n - 10\n if (ones === 0) return 'mười'\n if (ones === 5) return 'mười lăm'\n return 'mười ' + ONES[ones]\n }\n\n // 20-99\n const ones = n % 10\n const tens = Math.floor(n / 10)\n const tensWord = ONES[tens] + ' mươi'\n\n if (ones === 0) return tensWord\n if (ones === 1) return tensWord + ' ' + MOT_FINAL\n if (ones === 5) return tensWord + ' ' + LAM\n return tensWord + ' ' + ONES[ones]\n}\n\n/**\n * Builds segment word for 0-999.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const hundreds = Math.floor(n / 100)\n const remainder = n % 100\n\n let result = ''\n\n if (hundreds > 0) {\n result = ONES[hundreds] + ' ' + HUNDRED\n }\n\n if (remainder > 0) {\n if (remainder < 10) {\n // Single digit after hundreds needs \"lẻ\"\n if (result) {\n result += ' ' + LE + ' '\n // Use \"năm\" not \"lăm\" after lẻ\n result += remainder === 5 ? 'năm' : ONES[remainder]\n } else {\n result = ONES[remainder]\n }\n } else {\n // 10-99 after hundreds\n if (result) result += ' '\n result += buildBelowHundred(remainder)\n }\n }\n\n return result\n}\n\n/**\n * Builds \"lẻ\" prefixed word for small remainders (1-99) after scale words.\n */\nfunction buildLeSegment (n) {\n if (n === 0) return ''\n if (n < 10) {\n // Use \"năm\" not \"lăm\" after lẻ\n return LE + ' ' + (n === 5 ? 'năm' : ONES[n])\n }\n return LE + ' ' + buildBelowHundred(n)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Vietnamese words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Vietnamese words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 100\n if (n < 100n) {\n return buildBelowHundred(Number(n))\n }\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n const thousandsWords = buildSegment(thousands) + ' ' + SCALES[1]\n\n if (remainder === 0) {\n return thousandsWords\n }\n\n // Check if remainder needs \"lẻ\" marker (< 100)\n if (remainder < 100) {\n return thousandsWords + ' ' + buildLeSegment(remainder)\n }\n\n return thousandsWords + ' ' + buildSegment(remainder)\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Vietnamese words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n if (segment !== 0) {\n const words = buildSegment(segment)\n if (words) {\n if (scaleIndex > 0) {\n parts.push(words + ' ' + SCALES[scaleIndex])\n } else {\n parts.push(words)\n }\n }\n }\n scaleIndex--\n }\n\n // Join with \"lẻ\" logic for small remainders\n const partsLen = parts.length\n if (partsLen === 0) return ZERO\n if (partsLen === 1) return parts[0]\n\n // Check if final segment needs \"lẻ\" marker (remainder <= 99 after scale word)\n const lastSegment = segments[segments.length - 1]\n if (lastSegment > 0 && lastSegment <= 99) {\n // Last segment is small (no hundreds), needs \"lẻ\" after scale word\n let result = parts[0]\n for (let i = 1; i < partsLen - 1; i++) {\n result += ' ' + parts[i]\n }\n return result + ' ' + buildLeSegment(lastSegment)\n }\n\n // Join with spaces\n let result = parts[0]\n for (let i = 1; i < partsLen; i++) {\n result += ' ' + parts[i]\n }\n return result\n}\n\n/**\n * Converts decimal digits to Vietnamese words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Vietnamese words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Vietnamese words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Vietnamese words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(42) // 'bốn mươi hai'\n * toWords(101) // 'một trăm lẻ một'\n * toWords(1000000) // 'một triệu'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n","/**\n * Yoruba language converter - Functional Implementation\n *\n * A number-to-words converter for Yoruba (yo), a Niger-Congo language\n * spoken by ~45 million people in Nigeria, Benin, and Togo.\n *\n * Yoruba uses a complex vigesimal (base-20) system with:\n * - Additive patterns: 1-4 added to decade (lé = \"plus\")\n * - Subtractive patterns: 5-9 subtracted from next decade (dín = \"minus\")\n * - Odd decades (30,50,70,90) formed by subtracting 10 from next even decade\n * - Even decades (20,40,60,80,100) are multiples of 20\n *\n * Examples:\n * - 21 = ọ̀kan lé lógún (1 + 20)\n * - 15 = àrùndínlógún (20 - 5)\n * - 50 = àádọ́ta (60 - 10)\n * - 45 = àrùndínláàádọ́ta (50 - 5)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Basic numbers 1-10\nconst ONES = [\n '',\n 'ọ̀kan', // 1\n 'èjì', // 2\n 'ẹ̀ta', // 3\n 'ẹ̀rin', // 4\n 'àrùn', // 5\n 'ẹ̀fà', // 6\n 'èje', // 7\n 'ẹ̀jọ', // 8\n 'ẹ̀sán', // 9\n 'ẹ̀wá' // 10\n]\n\n// Numbers 11-14 (additive: X + 10, using \"lá\")\nconst TEENS_ADD = [\n '',\n 'ọ̀kànlá', // 11 = 1 + 10\n 'èjìlá', // 12 = 2 + 10\n 'ẹ̀talá', // 13 = 3 + 10\n 'ẹ̀rinlá' // 14 = 4 + 10\n]\n\n// Numbers 15-19 (subtractive: 20 - X, using \"dín\")\nconst TEENS_SUB = [\n 'àrùndínlógún', // 15 = 20 - 5\n 'ẹ̀rìndínlógún', // 16 = 20 - 4\n 'ẹ̀tadínlógún', // 17 = 20 - 3\n 'èjìdínlógún', // 18 = 20 - 2\n 'ọ̀kàndínlógún' // 19 = 20 - 1\n]\n\n// Decades (base-20 structure)\n// Even decades are multiples of 20\n// Odd decades subtract 10 from next even decade\nconst DECADES = {\n 20: 'ogún', // 20 = 20 × 1\n 30: 'ọgbọ̀n', // 30 = 20 + 10 (special word)\n 40: 'ogójì', // 40 = 20 × 2\n 50: 'àádọ́ta', // 50 = 60 - 10\n 60: 'ogóta', // 60 = 20 × 3\n 70: 'àádọ́rin', // 70 = 80 - 10\n 80: 'ogórin', // 80 = 20 × 4\n 90: 'àádọ́rùn', // 90 = 100 - 10\n 100: 'ọgọ́rùn' // 100 = 20 × 5\n}\n\n// Prefixes for adding to decades (lé lógún, lé lọgbọ̀n, etc.)\nconst DECADE_ADD_SUFFIX = {\n 20: 'lógún',\n 30: 'lọgbọ̀n',\n 40: 'lógójì',\n 50: 'láàádọ́ta',\n 60: 'lógóta',\n 70: 'láàádọ́rin',\n 80: 'lógórin',\n 90: 'láàádọ́rùn',\n 100: 'lọ́gọ́rùn'\n}\n\n// Prefixes for subtracting from decades (dín lógójì, etc.)\nconst DECADE_SUB_SUFFIX = {\n 20: 'dínlógún',\n 30: 'dínlọgbọ̀n',\n 40: 'dínlógójì',\n 50: 'dínláàádọ́ta',\n 60: 'dínlógóta',\n 70: 'dínláàádọ́rin',\n 80: 'dínlógórin',\n 90: 'dínláàádọ́rùn',\n 100: 'dínlọ́gọ́rùn'\n}\n\n// Scale words\nconst HUNDRED = 'ọgọ́rùn'\nconst TWO_HUNDRED = 'igba' // 200 (special word, historically 200 cowries)\nconst FOUR_HUNDRED = 'irinwó' // 400 (20 × 20)\nconst THOUSAND = 'ẹgbẹ̀rún' // 1000\nconst TEN_THOUSAND = 'ẹgbàárùn' // 10,000 (special)\nconst TWENTY_THOUSAND = 'ọ̀kẹ́' // 20,000 (bag of cowries)\nconst MILLION = 'mílíọ̀nù' // million (loanword)\n\nconst ZERO = 'òdo'\nconst NEGATIVE = 'àìní'\nconst DECIMAL_SEP = 'àmì'\nconst AND = 'ó lé' // \"and\" / \"plus\" connector\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds word for numbers 0-99\n */\nfunction buildUnder100 (n) {\n if (n === 0) return ''\n if (n <= 10) return ONES[n]\n\n // 11-14: additive from 10\n if (n <= 14) return TEENS_ADD[n - 10]\n\n // 15-19: subtractive from 20\n if (n <= 19) return TEENS_SUB[n - 15]\n\n // Exact decades\n if (n % 10 === 0) return DECADES[n]\n\n const decade = Math.floor(n / 10) * 10\n const unit = n % 10\n\n // 1-4 are added to current decade (21 = 1 + 20, not 1 + 30)\n if (unit <= 4) {\n return ONES[unit] + ' lé ' + DECADE_ADD_SUFFIX[decade]\n }\n\n // 5-9 are subtracted from next decade\n const nextDecade = decade + 10\n const subtractAmount = 10 - unit\n return ONES[subtractAmount] + DECADE_SUB_SUFFIX[nextDecade]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts hundreds (100-999)\n */\nfunction convertHundreds (n) {\n if (n < 100) return buildUnder100(n)\n\n const hundreds = Math.floor(n / 100)\n const remainder = n % 100\n\n let result\n\n // Special cases for 200 and 400\n if (hundreds === 2 && remainder === 0) {\n return TWO_HUNDRED\n }\n if (hundreds === 4 && remainder === 0) {\n return FOUR_HUNDRED\n }\n\n // Build hundreds\n if (hundreds === 1) {\n result = HUNDRED\n } else if (hundreds === 2) {\n result = TWO_HUNDRED\n } else if (hundreds === 4) {\n result = FOUR_HUNDRED\n } else {\n // Other hundreds: X ọgọ́rùn\n result = ONES[hundreds] + ' ' + HUNDRED\n }\n\n if (remainder > 0) {\n result += ' ' + AND + ' ' + buildUnder100(remainder)\n }\n\n return result\n}\n\n/**\n * Converts a non-negative integer to Yoruba words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Yoruba words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 100\n if (n < 100n) {\n return buildUnder100(Number(n))\n }\n\n // Numbers < 1000\n if (n < 1000n) {\n return convertHundreds(Number(n))\n }\n\n // Build from segments\n const parts = []\n let remaining = n\n\n // Millions\n if (remaining >= 1_000_000n) {\n const millions = remaining / 1_000_000n\n remaining = remaining % 1_000_000n\n if (millions === 1n) {\n parts.push(MILLION + ' kan')\n } else {\n parts.push(MILLION + ' ' + integerToWords(millions))\n }\n }\n\n // Thousands\n if (remaining >= 1000n) {\n const thousands = remaining / 1000n\n remaining = remaining % 1000n\n\n if (thousands === 1n) {\n parts.push(THOUSAND + ' kan')\n } else if (thousands === 10n) {\n parts.push(TEN_THOUSAND)\n } else if (thousands === 20n) {\n parts.push(TWENTY_THOUSAND)\n } else if (thousands < 100n) {\n parts.push(THOUSAND + ' ' + buildUnder100(Number(thousands)))\n } else {\n parts.push(THOUSAND + ' ' + convertHundreds(Number(thousands)))\n }\n }\n\n // Hundreds and below\n if (remaining > 0n) {\n if (remaining < 100n) {\n if (parts.length > 0) {\n parts.push(AND + ' ' + buildUnder100(Number(remaining)))\n } else {\n parts.push(buildUnder100(Number(remaining)))\n }\n } else {\n if (parts.length > 0) {\n parts.push(AND + ' ' + convertHundreds(Number(remaining)))\n } else {\n parts.push(convertHundreds(Number(remaining)))\n }\n }\n }\n\n return parts.join(', ')\n}\n\n/**\n * Converts decimal digits to Yoruba words.\n *\n * @param {string} decimalPart - Decimal digits\n * @returns {string} Yoruba words for decimal\n */\nfunction decimalPartToWords (decimalPart) {\n const parts = []\n\n for (const digit of decimalPart) {\n const d = parseInt(digit, 10)\n parts.push(d === 0 ? ZERO : ONES[d])\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts a numeric value to Yoruba words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Yoruba words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\nexport { toWords }\n","/**\n * Simplified Chinese language converter - Functional Implementation\n *\n * Self-contained converter for Simplified Chinese.\n *\n * Key features:\n * - Myriad-based (万, 亿) grouping - 4 digits\n * - Formal (financial) vs common numerals\n * - Zero insertion for skipped positions\n * - No word separators (concatenated format)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\n// Common (everyday) numerals\nconst ONES_COMMON = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']\nconst TEN_COMMON = '十'\nconst HUNDRED_COMMON = '百'\nconst THOUSAND_COMMON = '千'\n\n// Formal (financial) numerals - harder to alter/forge\nconst ONES_FORMAL = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']\nconst TEN_FORMAL = '拾'\nconst HUNDRED_FORMAL = '佰'\nconst THOUSAND_FORMAL = '仟'\n\n// Scale words\nconst WAN_WORD = '万' // 10,000\nconst YI_WORD = '亿' // 100,000,000\n\nconst ZERO = '零'\nconst NEGATIVE = '负'\nconst DECIMAL_SEP = '点'\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Convert number below 万 (10,000) to words using direct string concatenation.\n */\nfunction convertBelowWan (value, ones, ten, hundred, thousand) {\n if (value === 0n) return ''\n\n let result = ''\n let needsZero = false\n\n // Thousands (千)\n const thousandsVal = value / 1000n\n const thousandsRemainder = value % 1000n\n if (thousandsVal > 0n) {\n result = ones[Number(thousandsVal)] + thousand\n needsZero = thousandsRemainder > 0n && thousandsRemainder < 100n\n }\n\n // Hundreds (百)\n const hundredsVal = thousandsRemainder / 100n\n const hundredsRemainder = thousandsRemainder % 100n\n if (hundredsVal > 0n) {\n if (needsZero) result += ZERO\n result += ones[Number(hundredsVal)] + hundred\n needsZero = hundredsRemainder > 0n && hundredsRemainder < 10n\n } else if (thousandsVal > 0n && hundredsRemainder > 0n) {\n needsZero = true\n }\n\n // Tens (十)\n const tensVal = hundredsRemainder / 10n\n const onesVal = hundredsRemainder % 10n\n if (tensVal > 0n) {\n if (needsZero) result += ZERO\n result += ones[Number(tensVal)] + ten\n needsZero = false\n } else if ((hundredsVal > 0n || thousandsVal > 0n) && onesVal > 0n) {\n needsZero = true\n }\n\n // Ones\n if (onesVal > 0n) {\n if (needsZero) result += ZERO\n result += ones[Number(onesVal)]\n }\n\n return result\n}\n\n/**\n * Convert number below 亿 (100 million) to words.\n */\nfunction convertBelowYi (value, ones, ten, hundred, thousand) {\n if (value === 0n) return ''\n\n if (value >= 10_000n) {\n const wanValue = value / 10_000n\n const wanRemainder = value % 10_000n\n\n let result = convertBelowWan(wanValue, ones, ten, hundred, thousand) + WAN_WORD\n\n if (wanRemainder > 0n) {\n const needsZero = (wanValue % 10n === 0n) || (wanRemainder < 1000n)\n if (needsZero) result += ZERO\n result += convertBelowWan(wanRemainder, ones, ten, hundred, thousand)\n }\n\n return result\n }\n\n return convertBelowWan(value, ones, ten, hundred, thousand)\n}\n\nfunction integerToWords (n, formal = true) {\n if (n === 0n) return ZERO\n\n const ones = formal ? ONES_FORMAL : ONES_COMMON\n const ten = formal ? TEN_FORMAL : TEN_COMMON\n const hundred = formal ? HUNDRED_FORMAL : HUNDRED_COMMON\n const thousand = formal ? THOUSAND_FORMAL : THOUSAND_COMMON\n\n // Handle numbers >= 亿 (100 million)\n if (n >= 100_000_000n) {\n const yiValue = n / 100_000_000n\n const yiRemainder = n % 100_000_000n\n\n let result = convertBelowYi(yiValue, ones, ten, hundred, thousand) + YI_WORD\n\n if (yiRemainder > 0n) {\n if (yiRemainder < 10_000_000n) result += ZERO\n result += convertBelowYi(yiRemainder, ones, ten, hundred, thousand)\n }\n\n return result\n }\n\n return convertBelowYi(n, ones, ten, hundred, thousand)\n}\n\n/**\n * Convert decimal digits to words using direct concatenation.\n */\nfunction decimalDigitsToWords (decimalString, ones) {\n let result = ''\n for (let i = 0; i < decimalString.length; i++) {\n result += ones[Number(decimalString[i])]\n }\n return result\n}\n\n/**\n * Converts a numeric value to Simplified Chinese words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.formal=true] - Use formal/financial numerals\n * @returns {string} The number in Simplified Chinese words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n const formal = options.formal !== false // Default to true\n\n let result = isNegative ? NEGATIVE : ''\n\n result += integerToWords(integerPart, formal)\n\n if (decimalPart) {\n const ones = formal ? ONES_FORMAL : ONES_COMMON\n result += DECIMAL_SEP + decimalDigitsToWords(decimalPart, ones)\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n","/**\n * Traditional Chinese language converter - Functional Implementation\n *\n * Self-contained converter for Traditional Chinese.\n *\n * Key features:\n * - Myriad-based (萬, 億) grouping - 4 digits\n * - Formal (financial) vs common numerals\n * - Zero insertion for skipped positions\n * - No word separators (concatenated format)\n *\n * Differences from Simplified:\n * - Different character forms (e.g., 負/负, 點/点, 億/亿, 萬/万)\n * - Some formal numerals differ (參/叁, 貳/贰, 陸/陆)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\nimport { validateOptions } from '../utils/validate-options.js'\n\n// ============================================================================\n// Vocabulary\n// ============================================================================\n\n// Common (everyday) numerals - Traditional forms\nconst ONES_COMMON = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']\nconst TEN_COMMON = '十'\nconst HUNDRED_COMMON = '百'\nconst THOUSAND_COMMON = '千'\n\n// Formal (financial) numerals - Traditional forms\nconst ONES_FORMAL = ['零', '壹', '貳', '參', '肆', '伍', '陸', '柒', '捌', '玖']\nconst TEN_FORMAL = '拾'\nconst HUNDRED_FORMAL = '佰'\nconst THOUSAND_FORMAL = '仟'\n\n// Scale words - Traditional forms\nconst WAN_WORD = '萬' // 10,000\nconst YI_WORD = '億' // 100,000,000\n\nconst ZERO = '零'\nconst NEGATIVE = '負'\nconst DECIMAL_SEP = '點'\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, formal = true) {\n if (n === 0n) return ZERO\n\n const ones = formal ? ONES_FORMAL : ONES_COMMON\n const ten = formal ? TEN_FORMAL : TEN_COMMON\n const hundred = formal ? HUNDRED_FORMAL : HUNDRED_COMMON\n const thousand = formal ? THOUSAND_FORMAL : THOUSAND_COMMON\n\n // Convert number below 萬 (10,000)\n function convertBelowWan (value) {\n if (value === 0n) return ''\n\n const parts = []\n let needsZero = false\n\n // Thousands (千)\n const thousandsVal = value / 1000n\n const thousandsRemainder = value % 1000n\n if (thousandsVal > 0n) {\n parts.push(ones[Number(thousandsVal)] + thousand)\n needsZero = thousandsRemainder > 0n && thousandsRemainder < 100n\n }\n\n // Hundreds (百)\n const hundredsVal = thousandsRemainder / 100n\n const hundredsRemainder = thousandsRemainder % 100n\n if (hundredsVal > 0n) {\n if (needsZero) {\n parts.push(ZERO)\n needsZero = false\n }\n parts.push(ones[Number(hundredsVal)] + hundred)\n needsZero = hundredsRemainder > 0n && hundredsRemainder < 10n\n } else if (thousandsVal > 0n && hundredsRemainder > 0n) {\n needsZero = true\n }\n\n // Tens (十)\n const tensVal = hundredsRemainder / 10n\n const onesVal = hundredsRemainder % 10n\n if (tensVal > 0n) {\n if (needsZero) {\n parts.push(ZERO)\n needsZero = false\n }\n parts.push(ones[Number(tensVal)] + ten)\n } else if ((hundredsVal > 0n || thousandsVal > 0n) && onesVal > 0n) {\n needsZero = true\n }\n\n // Ones\n if (onesVal > 0n) {\n if (needsZero) {\n parts.push(ZERO)\n }\n parts.push(ones[Number(onesVal)])\n }\n\n return parts.join('')\n }\n\n // Convert number below 億 (100 million)\n function convertBelowYi (value) {\n if (value === 0n) return ''\n\n const parts = []\n\n if (value >= 10_000n) {\n const wanValue = value / 10_000n\n const wanRemainder = value % 10_000n\n\n parts.push(convertBelowWan(wanValue) + WAN_WORD)\n\n if (wanRemainder > 0n) {\n const wanEndsWithZero = wanValue % 10n === 0n\n const remainderMissesThousands = wanRemainder < 1000n\n const needsZero = wanEndsWithZero || remainderMissesThousands\n if (needsZero) {\n parts.push(ZERO)\n }\n parts.push(convertBelowWan(wanRemainder))\n }\n } else {\n parts.push(convertBelowWan(value))\n }\n\n return parts.join('')\n }\n\n // Main conversion\n const parts = []\n\n if (n >= 100_000_000n) {\n const yiValue = n / 100_000_000n\n const yiRemainder = n % 100_000_000n\n\n const yiWords = convertBelowYi(yiValue)\n parts.push(yiWords + YI_WORD)\n\n if (yiRemainder > 0n) {\n const needsZero = yiRemainder < 10_000_000n\n if (needsZero) {\n parts.push(ZERO)\n }\n parts.push(convertBelowYi(yiRemainder))\n }\n } else {\n parts.push(convertBelowYi(n))\n }\n\n return parts.join('')\n}\n\nfunction decimalDigitsToWords (decimalString, formal = true) {\n const ones = formal ? ONES_FORMAL : ONES_COMMON\n const words = []\n for (const char of decimalString) {\n words.push(ones[Number(char)])\n }\n return words\n}\n\n/**\n * Converts a numeric value to Traditional Chinese words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @param {Object} [options] - Optional configuration\n * @param {boolean} [options.formal=true] - Use formal/financial numerals\n * @returns {string} The number in Traditional Chinese words\n */\nfunction toWords (value, options) {\n options = validateOptions(options)\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n const formal = options.formal !== false // Default to true\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE\n }\n\n result += integerToWords(integerPart, formal)\n\n if (decimalPart) {\n result += DECIMAL_SEP + decimalDigitsToWords(decimalPart, formal).join('')\n }\n\n return result\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericValue","value","type","isNegative","integerPart","Number","isFinite","Error","isSafeInteger","BigInt","parseNumericString","str","toString","includes","expandScientificNotation","numberToString","trimmed","trim","length","isNaN","normalizeString","TypeError","slice","dotIndex","indexOf","integerStr","decimalPart","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","repeat","ONES","TEENS","TENS","SCALE_WORDS","buildSegment","n","ones","tensDigit","Math","floor","hundredsDigit","parts","push","join","ZERO","validateOptions","options","undefined","proto","Object","getPrototypeOf","prototype","isPlainObject","HUNDREDS","SCALE_APPENDED","SCALE_PLURAL","DUAL","DUAL_APPENDED","ONES_MASC","ONES_FEM","segmentToWords","groupNumber","groupLevel","fullNumber","tensValue","trunc","result","hundredsWord","numValue","pow","log10","onesDigit","tensIndex","integerToWords","gender","temp","group","groups","numberToProcess","groupDescription","groupText","unshift","i","numStr","len","segments","remainderLen","pos","scaleIndex","segment","scaleWord","buildLargeNumberWords","BELOW_HUNDRED","hundreds","remainder","words","PLURAL_FORMS","tens","buildSegmentWithHundreds","pluralize","forms","lastDigit","lastTwoDigits","thousands","segmentValues","ONES_VIGESIMAL","THOUSAND","SCALES","tensOnes","segmentWord","word","numWord","part","nextPart","endsWith","joinDanishParts","EIN","SCALES_PLURAL","HUNDRED","buildSegmentForThousand","isScale","scaleLevel","segWords","prevPart","joinGermanParts","segmentResult","hasHundred","thousandsWord","remainderWord","firstNonZeroIdx","prevWasScale","TWENTIES_MASC","TWENTIES_FEM","HUNDREDS_MASC","HUNDREDS_FEM","feminine","getScaleWord","arrayIndex","baseWord","pluralWord","thousandMultiplier","VOWELS","addLinker","hundredPrefix","buildSegmentWithLinker","lastSpaceIdx","lastIndexOf","prefix","lastWord","SCALES_ARD","endsWithCents","endsWithVingts","t","o","ardWord","withHyphen","replace","adjustedWord","isSingleDigit","TEENS_MASC","THOUSANDS_MASC","TEENS_FEM","THOUSANDS_FEM","SCALE","buildScaleSegment","andWord","HUNDREDS_ARR","teenWord","buildUnitsSegment","THOUSANDS_SPECIAL","SCALE_FORMS","num","buildSegmentMasc","buildSegmentFem","WORDS","Map","wordForScale","get","integerToWordsInner","zeroWord","has","units","tensToCardinal","hundredsToCardinal","postfix","thousandsToCardinal","scale","rest","bigNumberToCardinal","HUNDRED_WORD","THOUSAND_WORD","TENS_STEM","SCALE_PREFIXES","buildSegmentForScale","buildThousands","getScaleWordSingular","prefixIndex","getScaleWordPlural","remaining","maxScale","testValue","divisor","segNum","segmentWords","lastPart","joinPartsWithConnector","VIGESIMAL","VIGESIMAL_DA","HUNDRED_PREFIXES","THOUSAND_STEM","buildTens","vigesimalGroup","base","full","stem","lastChar","hundredWord","thousandsPart","thousandWord","some","s","joinKoreanParts","HUNDRED_SINGULAR","HUNDRED_PLURAL","buildSegmentFeminine","HUNDRED_GENITIVE","remainderResult","joinNorwegianParts","withAnd","onesWord","connector","accentOne","includeOptionalAnd","noHundredPairing","applyAccent","high","low","lowWord","isExactHundred","startsWithHundreds","SCALE_WORDS_SINGULAR","SCALE_WORDS_PLURAL","TWENTIES","SCALE_META","singular","plural","article","needsDe","spellUnder100","masculineTeens","u","spellUnder1000","h","r","buildScalePhrase","meta","lessThan100","tensOnesWord","joinSwedishParts","wordsUnder100","wordsUnder1000","extractSegments","val","unit","HUNDREDS_CONNECTED","convertBelowMillion","hundredThousands","tenThousands","separator","sep","dropSpaces","buildBelowHundred","tensWord","buildLeSegment","LE","thousandsWords","partsLen","lastSegment","TEENS_ADD","TEENS_SUB","DECADES","DECADE_ADD_SUFFIX","DECADE_SUB_SUFFIX","TWO_HUNDRED","FOUR_HUNDRED","MILLION","AND","buildUnder100","decade","convertHundreds","millions","ONES_COMMON","ONES_FORMAL","convertBelowWan","ten","hundred","thousand","needsZero","thousandsVal","thousandsRemainder","hundredsVal","hundredsRemainder","tensVal","onesVal","convertBelowYi","wanValue","wanRemainder","NEGATIVE","char","d","decimalPartToWords","negativeWord","getDecimalSeparator","digit","withHyphenSeparator","rawParts","filtered","isFeminine","man","opts","million","chunk","splitMillionGroups","groupValue","formal","yiRemainder","decimalString","decimalDigitsToWords","yiWords"],"mappings":"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"}
|