n2words 2.0.0 → 3.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.
Files changed (327) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +86 -188
  3. package/dist/languages/am-Latn.js +3 -0
  4. package/dist/languages/am-Latn.js.map +1 -0
  5. package/dist/languages/am.js +3 -0
  6. package/dist/languages/am.js.map +1 -0
  7. package/dist/languages/ar.js +3 -0
  8. package/dist/languages/ar.js.map +1 -0
  9. package/dist/languages/az.js +3 -0
  10. package/dist/languages/az.js.map +1 -0
  11. package/dist/languages/bn.js +3 -0
  12. package/dist/languages/bn.js.map +1 -0
  13. package/dist/languages/cs.js +3 -0
  14. package/dist/languages/cs.js.map +1 -0
  15. package/dist/languages/da.js +3 -0
  16. package/dist/languages/da.js.map +1 -0
  17. package/dist/languages/de.js +3 -0
  18. package/dist/languages/de.js.map +1 -0
  19. package/dist/languages/el.js +3 -0
  20. package/dist/languages/el.js.map +1 -0
  21. package/dist/languages/en.js +3 -0
  22. package/dist/languages/en.js.map +1 -0
  23. package/dist/languages/es.js +3 -0
  24. package/dist/languages/es.js.map +1 -0
  25. package/dist/languages/fa.js +3 -0
  26. package/dist/languages/fa.js.map +1 -0
  27. package/dist/languages/fi.js +3 -0
  28. package/dist/languages/fi.js.map +1 -0
  29. package/dist/languages/fil.js +3 -0
  30. package/dist/languages/fil.js.map +1 -0
  31. package/dist/languages/fr-BE.js +3 -0
  32. package/dist/languages/fr-BE.js.map +1 -0
  33. package/dist/languages/fr.js +3 -0
  34. package/dist/languages/fr.js.map +1 -0
  35. package/dist/languages/gu.js +3 -0
  36. package/dist/languages/gu.js.map +1 -0
  37. package/dist/languages/ha.js +3 -0
  38. package/dist/languages/ha.js.map +1 -0
  39. package/dist/languages/hbo.js +3 -0
  40. package/dist/languages/hbo.js.map +1 -0
  41. package/dist/languages/he.js +3 -0
  42. package/dist/languages/he.js.map +1 -0
  43. package/dist/languages/hi.js +3 -0
  44. package/dist/languages/hi.js.map +1 -0
  45. package/dist/languages/hr.js +3 -0
  46. package/dist/languages/hr.js.map +1 -0
  47. package/dist/languages/hu.js +3 -0
  48. package/dist/languages/hu.js.map +1 -0
  49. package/dist/languages/id.js +3 -0
  50. package/dist/languages/id.js.map +1 -0
  51. package/dist/languages/it.js +3 -0
  52. package/dist/languages/it.js.map +1 -0
  53. package/dist/languages/ja.js +3 -0
  54. package/dist/languages/ja.js.map +1 -0
  55. package/dist/languages/kn.js +3 -0
  56. package/dist/languages/kn.js.map +1 -0
  57. package/dist/languages/ko.js +3 -0
  58. package/dist/languages/ko.js.map +1 -0
  59. package/dist/languages/lt.js +3 -0
  60. package/dist/languages/lt.js.map +1 -0
  61. package/dist/languages/lv.js +3 -0
  62. package/dist/languages/lv.js.map +1 -0
  63. package/dist/languages/mr.js +3 -0
  64. package/dist/languages/mr.js.map +1 -0
  65. package/dist/languages/ms.js +3 -0
  66. package/dist/languages/ms.js.map +1 -0
  67. package/dist/languages/nb.js +3 -0
  68. package/dist/languages/nb.js.map +1 -0
  69. package/dist/languages/nl.js +3 -0
  70. package/dist/languages/nl.js.map +1 -0
  71. package/dist/languages/pa.js +3 -0
  72. package/dist/languages/pa.js.map +1 -0
  73. package/dist/languages/pl.js +3 -0
  74. package/dist/languages/pl.js.map +1 -0
  75. package/dist/languages/pt.js +3 -0
  76. package/dist/languages/pt.js.map +1 -0
  77. package/dist/languages/ro.js +3 -0
  78. package/dist/languages/ro.js.map +1 -0
  79. package/dist/languages/ru.js +3 -0
  80. package/dist/languages/ru.js.map +1 -0
  81. package/dist/languages/sr-Cyrl.js +3 -0
  82. package/dist/languages/sr-Cyrl.js.map +1 -0
  83. package/dist/languages/sr-Latn.js +3 -0
  84. package/dist/languages/sr-Latn.js.map +1 -0
  85. package/dist/languages/sv.js +3 -0
  86. package/dist/languages/sv.js.map +1 -0
  87. package/dist/languages/sw.js +3 -0
  88. package/dist/languages/sw.js.map +1 -0
  89. package/dist/languages/ta.js +3 -0
  90. package/dist/languages/ta.js.map +1 -0
  91. package/dist/languages/te.js +3 -0
  92. package/dist/languages/te.js.map +1 -0
  93. package/dist/languages/th.js +3 -0
  94. package/dist/languages/th.js.map +1 -0
  95. package/dist/languages/tr.js +3 -0
  96. package/dist/languages/tr.js.map +1 -0
  97. package/dist/languages/uk.js +3 -0
  98. package/dist/languages/uk.js.map +1 -0
  99. package/dist/languages/ur.js +3 -0
  100. package/dist/languages/ur.js.map +1 -0
  101. package/dist/languages/vi.js +3 -0
  102. package/dist/languages/vi.js.map +1 -0
  103. package/dist/languages/zh-Hans.js +3 -0
  104. package/dist/languages/zh-Hans.js.map +1 -0
  105. package/dist/languages/zh-Hant.js +3 -0
  106. package/dist/languages/zh-Hant.js.map +1 -0
  107. package/dist/n2words.js +2 -2
  108. package/dist/n2words.js.map +1 -1
  109. package/lib/languages/am-Latn.d.ts +7 -0
  110. package/lib/languages/am-Latn.js +164 -0
  111. package/lib/languages/am.d.ts +7 -0
  112. package/lib/languages/am.js +164 -0
  113. package/lib/languages/ar.d.ts +14 -27
  114. package/lib/languages/ar.js +175 -129
  115. package/lib/languages/az.d.ts +4 -9
  116. package/lib/languages/az.js +171 -37
  117. package/lib/languages/bn.d.ts +4 -8
  118. package/lib/languages/bn.js +138 -124
  119. package/lib/languages/cs.d.ts +15 -85
  120. package/lib/languages/cs.js +310 -114
  121. package/lib/languages/da.d.ts +11 -12
  122. package/lib/languages/da.js +276 -101
  123. package/lib/languages/de.d.ts +14 -11
  124. package/lib/languages/de.js +317 -86
  125. package/lib/languages/el.d.ts +11 -11
  126. package/lib/languages/el.js +231 -78
  127. package/lib/languages/en.d.ts +14 -13
  128. package/lib/languages/en.js +242 -72
  129. package/lib/languages/es.d.ts +18 -12
  130. package/lib/languages/es.js +317 -103
  131. package/lib/languages/fa.d.ts +4 -44
  132. package/lib/languages/fa.js +112 -122
  133. package/lib/languages/fi.d.ts +14 -0
  134. package/lib/languages/fi.js +245 -0
  135. package/lib/languages/fil.d.ts +4 -13
  136. package/lib/languages/fil.js +207 -106
  137. package/lib/languages/fr-BE.d.ts +8 -8
  138. package/lib/languages/fr-BE.js +294 -19
  139. package/lib/languages/fr.d.ts +18 -12
  140. package/lib/languages/fr.js +352 -89
  141. package/lib/languages/gu.d.ts +4 -8
  142. package/lib/languages/gu.js +130 -125
  143. package/lib/languages/ha.d.ts +7 -0
  144. package/lib/languages/ha.js +230 -0
  145. package/lib/languages/hbo.d.ts +10 -110
  146. package/lib/languages/hbo.js +263 -214
  147. package/lib/languages/he.d.ts +10 -77
  148. package/lib/languages/he.js +242 -172
  149. package/lib/languages/hi.d.ts +4 -8
  150. package/lib/languages/hi.js +138 -124
  151. package/lib/languages/hr.d.ts +8 -77
  152. package/lib/languages/hr.js +194 -89
  153. package/lib/languages/hu.d.ts +4 -19
  154. package/lib/languages/hu.js +198 -119
  155. package/lib/languages/id.d.ts +4 -34
  156. package/lib/languages/id.js +171 -129
  157. package/lib/languages/it.d.ts +16 -34
  158. package/lib/languages/it.js +339 -94
  159. package/lib/languages/ja.d.ts +14 -14
  160. package/lib/languages/ja.js +233 -111
  161. package/lib/languages/kn.d.ts +4 -8
  162. package/lib/languages/kn.js +130 -35
  163. package/lib/languages/ko.d.ts +11 -11
  164. package/lib/languages/ko.js +257 -49
  165. package/lib/languages/lt.d.ts +15 -67
  166. package/lib/languages/lt.js +296 -122
  167. package/lib/languages/lv.d.ts +15 -67
  168. package/lib/languages/lv.js +297 -106
  169. package/lib/languages/mr.d.ts +4 -8
  170. package/lib/languages/mr.js +130 -125
  171. package/lib/languages/ms.d.ts +4 -28
  172. package/lib/languages/ms.js +171 -116
  173. package/lib/languages/nb.d.ts +11 -9
  174. package/lib/languages/nb.js +282 -87
  175. package/lib/languages/nl.d.ts +23 -13
  176. package/lib/languages/nl.js +317 -133
  177. package/lib/languages/pa.d.ts +4 -8
  178. package/lib/languages/pa.js +156 -124
  179. package/lib/languages/pl.d.ts +19 -77
  180. package/lib/languages/pl.js +307 -87
  181. package/lib/languages/pt.d.ts +14 -26
  182. package/lib/languages/pt.js +286 -92
  183. package/lib/languages/ro.d.ts +15 -155
  184. package/lib/languages/ro.js +219 -235
  185. package/lib/languages/ru.d.ts +8 -82
  186. package/lib/languages/ru.js +222 -78
  187. package/lib/languages/sr-Cyrl.d.ts +8 -77
  188. package/lib/languages/sr-Cyrl.js +191 -89
  189. package/lib/languages/sr-Latn.d.ts +8 -77
  190. package/lib/languages/sr-Latn.js +191 -89
  191. package/lib/languages/sv.d.ts +11 -11
  192. package/lib/languages/sv.js +288 -74
  193. package/lib/languages/sw.d.ts +4 -36
  194. package/lib/languages/sw.js +133 -106
  195. package/lib/languages/ta.d.ts +4 -17
  196. package/lib/languages/ta.js +129 -201
  197. package/lib/languages/te.d.ts +4 -19
  198. package/lib/languages/te.js +141 -196
  199. package/lib/languages/th.d.ts +4 -14
  200. package/lib/languages/th.js +135 -91
  201. package/lib/languages/tr.d.ts +15 -9
  202. package/lib/languages/tr.js +256 -49
  203. package/lib/languages/uk.d.ts +8 -82
  204. package/lib/languages/uk.js +200 -78
  205. package/lib/languages/ur.d.ts +4 -8
  206. package/lib/languages/ur.js +156 -124
  207. package/lib/languages/vi.d.ts +14 -69
  208. package/lib/languages/vi.js +294 -125
  209. package/lib/languages/zh-Hans.d.ts +8 -18
  210. package/lib/languages/zh-Hans.js +163 -92
  211. package/lib/languages/zh-Hant.d.ts +8 -18
  212. package/lib/languages/zh-Hant.js +181 -90
  213. package/lib/n2words.d.ts +53 -209
  214. package/lib/n2words.js +111 -530
  215. package/lib/utils/is-plain-object.d.ts +13 -0
  216. package/lib/utils/is-plain-object.js +17 -0
  217. package/lib/utils/parse-numeric.d.ts +17 -0
  218. package/lib/utils/parse-numeric.js +108 -0
  219. package/lib/utils/validate-options.d.ts +8 -0
  220. package/lib/utils/validate-options.js +16 -0
  221. package/package.json +26 -14
  222. package/dist/ArabicConverter.js +0 -3
  223. package/dist/ArabicConverter.js.map +0 -1
  224. package/dist/AzerbaijaniConverter.js +0 -3
  225. package/dist/AzerbaijaniConverter.js.map +0 -1
  226. package/dist/BanglaConverter.js +0 -3
  227. package/dist/BanglaConverter.js.map +0 -1
  228. package/dist/BiblicalHebrewConverter.js +0 -3
  229. package/dist/BiblicalHebrewConverter.js.map +0 -1
  230. package/dist/CroatianConverter.js +0 -3
  231. package/dist/CroatianConverter.js.map +0 -1
  232. package/dist/CzechConverter.js +0 -3
  233. package/dist/CzechConverter.js.map +0 -1
  234. package/dist/DanishConverter.js +0 -3
  235. package/dist/DanishConverter.js.map +0 -1
  236. package/dist/DutchConverter.js +0 -3
  237. package/dist/DutchConverter.js.map +0 -1
  238. package/dist/EnglishConverter.js +0 -3
  239. package/dist/EnglishConverter.js.map +0 -1
  240. package/dist/FilipinoConverter.js +0 -3
  241. package/dist/FilipinoConverter.js.map +0 -1
  242. package/dist/FrenchBelgiumConverter.js +0 -3
  243. package/dist/FrenchBelgiumConverter.js.map +0 -1
  244. package/dist/FrenchConverter.js +0 -3
  245. package/dist/FrenchConverter.js.map +0 -1
  246. package/dist/GermanConverter.js +0 -3
  247. package/dist/GermanConverter.js.map +0 -1
  248. package/dist/GreekConverter.js +0 -3
  249. package/dist/GreekConverter.js.map +0 -1
  250. package/dist/GujaratiConverter.js +0 -3
  251. package/dist/GujaratiConverter.js.map +0 -1
  252. package/dist/HebrewConverter.js +0 -3
  253. package/dist/HebrewConverter.js.map +0 -1
  254. package/dist/HindiConverter.js +0 -3
  255. package/dist/HindiConverter.js.map +0 -1
  256. package/dist/HungarianConverter.js +0 -3
  257. package/dist/HungarianConverter.js.map +0 -1
  258. package/dist/IndonesianConverter.js +0 -3
  259. package/dist/IndonesianConverter.js.map +0 -1
  260. package/dist/ItalianConverter.js +0 -3
  261. package/dist/ItalianConverter.js.map +0 -1
  262. package/dist/JapaneseConverter.js +0 -3
  263. package/dist/JapaneseConverter.js.map +0 -1
  264. package/dist/KannadaConverter.js +0 -3
  265. package/dist/KannadaConverter.js.map +0 -1
  266. package/dist/KoreanConverter.js +0 -3
  267. package/dist/KoreanConverter.js.map +0 -1
  268. package/dist/LatvianConverter.js +0 -3
  269. package/dist/LatvianConverter.js.map +0 -1
  270. package/dist/LithuanianConverter.js +0 -3
  271. package/dist/LithuanianConverter.js.map +0 -1
  272. package/dist/MalayConverter.js +0 -3
  273. package/dist/MalayConverter.js.map +0 -1
  274. package/dist/MarathiConverter.js +0 -3
  275. package/dist/MarathiConverter.js.map +0 -1
  276. package/dist/NorwegianBokmalConverter.js +0 -3
  277. package/dist/NorwegianBokmalConverter.js.map +0 -1
  278. package/dist/PersianConverter.js +0 -3
  279. package/dist/PersianConverter.js.map +0 -1
  280. package/dist/PolishConverter.js +0 -3
  281. package/dist/PolishConverter.js.map +0 -1
  282. package/dist/PortugueseConverter.js +0 -3
  283. package/dist/PortugueseConverter.js.map +0 -1
  284. package/dist/PunjabiConverter.js +0 -3
  285. package/dist/PunjabiConverter.js.map +0 -1
  286. package/dist/RomanianConverter.js +0 -3
  287. package/dist/RomanianConverter.js.map +0 -1
  288. package/dist/RussianConverter.js +0 -3
  289. package/dist/RussianConverter.js.map +0 -1
  290. package/dist/SerbianCyrillicConverter.js +0 -3
  291. package/dist/SerbianCyrillicConverter.js.map +0 -1
  292. package/dist/SerbianLatinConverter.js +0 -3
  293. package/dist/SerbianLatinConverter.js.map +0 -1
  294. package/dist/SimplifiedChineseConverter.js +0 -3
  295. package/dist/SimplifiedChineseConverter.js.map +0 -1
  296. package/dist/SpanishConverter.js +0 -3
  297. package/dist/SpanishConverter.js.map +0 -1
  298. package/dist/SwahiliConverter.js +0 -3
  299. package/dist/SwahiliConverter.js.map +0 -1
  300. package/dist/SwedishConverter.js +0 -3
  301. package/dist/SwedishConverter.js.map +0 -1
  302. package/dist/TamilConverter.js +0 -3
  303. package/dist/TamilConverter.js.map +0 -1
  304. package/dist/TeluguConverter.js +0 -3
  305. package/dist/TeluguConverter.js.map +0 -1
  306. package/dist/ThaiConverter.js +0 -3
  307. package/dist/ThaiConverter.js.map +0 -1
  308. package/dist/TraditionalChineseConverter.js +0 -3
  309. package/dist/TraditionalChineseConverter.js.map +0 -1
  310. package/dist/TurkishConverter.js +0 -3
  311. package/dist/TurkishConverter.js.map +0 -1
  312. package/dist/UkrainianConverter.js +0 -3
  313. package/dist/UkrainianConverter.js.map +0 -1
  314. package/dist/UrduConverter.js +0 -3
  315. package/dist/UrduConverter.js.map +0 -1
  316. package/dist/VietnameseConverter.js +0 -3
  317. package/dist/VietnameseConverter.js.map +0 -1
  318. package/lib/classes/abstract-language.d.ts +0 -178
  319. package/lib/classes/abstract-language.js +0 -268
  320. package/lib/classes/greedy-scale-language.d.ts +0 -109
  321. package/lib/classes/greedy-scale-language.js +0 -201
  322. package/lib/classes/slavic-language.d.ts +0 -148
  323. package/lib/classes/slavic-language.js +0 -281
  324. package/lib/classes/south-asian-language.d.ts +0 -70
  325. package/lib/classes/south-asian-language.js +0 -154
  326. package/lib/classes/turkic-language.d.ts +0 -26
  327. package/lib/classes/turkic-language.js +0 -59
@@ -1 +1 @@
1
- {"version":3,"file":"n2words.js","sources":["../lib/classes/abstract-language.js","../lib/classes/greedy-scale-language.js","../lib/classes/turkic-language.js","../lib/classes/south-asian-language.js","../lib/classes/slavic-language.js","../lib/languages/fr.js","../lib/languages/pt.js","../lib/n2words.js","../lib/languages/ar.js","../lib/languages/az.js","../lib/languages/bn.js","../lib/languages/hbo.js","../lib/languages/hr.js","../lib/languages/cs.js","../lib/languages/da.js","../lib/languages/nl.js","../lib/languages/en.js","../lib/languages/fil.js","../lib/languages/fr-BE.js","../lib/languages/de.js","../lib/languages/el.js","../lib/languages/gu.js","../lib/languages/he.js","../lib/languages/hi.js","../lib/languages/hu.js","../lib/languages/id.js","../lib/languages/it.js","../lib/languages/ja.js","../lib/languages/kn.js","../lib/languages/ko.js","../lib/languages/lv.js","../lib/languages/lt.js","../lib/languages/ms.js","../lib/languages/mr.js","../lib/languages/nb.js","../lib/languages/fa.js","../lib/languages/pl.js","../lib/languages/pa.js","../lib/languages/ro.js","../lib/languages/ru.js","../lib/languages/sr-Cyrl.js","../lib/languages/sr-Latn.js","../lib/languages/zh-Hans.js","../lib/languages/es.js","../lib/languages/sw.js","../lib/languages/sv.js","../lib/languages/ta.js","../lib/languages/te.js","../lib/languages/th.js","../lib/languages/zh-Hant.js","../lib/languages/tr.js","../lib/languages/uk.js","../lib/languages/ur.js","../lib/languages/vi.js"],"sourcesContent":["/**\n * Abstract base class for language converters.\n *\n * This class provides the framework for converting numbers to words in any language.\n * It handles the common conversion flow while delegating language-specific logic to subclasses.\n *\n * ## Responsibilities\n *\n * - Receives pre-validated and normalized input from the public API (n2words.js)\n * - Handles negative number prefixing via `negativeWord`\n * - Converts decimals via `decimalDigitsToWords()`, preserving leading zeros\n * - Delegates integer conversion to `integerToWords()` (implemented by subclasses)\n *\n * ## Required Subclass Implementation\n *\n * Subclasses MUST provide:\n * - `integerToWords(integerPart)` - Core conversion logic (abstract method)\n * - `negativeWord` - Word preceding negative numbers (e.g., \"minus\"))\n * - `zeroWord` - Word for the digit 0 (e.g., \"zero\")\n * - `decimalSeparatorWord` - Word between whole and decimal parts (e.g., \"point\")\n *\n * ## Optional Overrides\n *\n * Subclasses MAY override:\n * - `wordSeparator` - Character(s) between words (default: space, empty for CJK languages)\n * - `usePerDigitDecimals` - Enable per-digit decimal mode (default: false)\n * - `decimalIntegerToWords()` - Custom decimal conversion (e.g., Romanian masculine forms)\n * - `decimalDigitsToWords()` - Complete decimal conversion override\n * - `toWords()` - Override to capture integerPart for context-dependent rules (e.g., Czech)\n *\n * ## Input Contract\n *\n * Input validation and normalization happen at the public API boundary (n2words.js).\n * This class assumes it receives clean, pre-processed data via `toWords()`.\n *\n * @abstract\n */\nexport class AbstractLanguage {\n // ============================================================================\n // Private Fields\n // ============================================================================\n\n /**\n * Private storage for options.\n * @type {Object}\n */\n #options = {}\n\n // ============================================================================\n // Required Properties (subclasses must define)\n // ============================================================================\n\n /**\n * Word that precedes negative numbers (e.g., \"minus\", \"negative\", \"moins\").\n * @type {string}\n */\n negativeWord = ''\n\n /**\n * Word that separates integer and decimal parts (e.g., \"point\", \"virgule\", \"comma\").\n * @type {string}\n */\n decimalSeparatorWord = ''\n\n /**\n * Word representation for the digit 0 (e.g., \"zero\", \"zéro\", \"null\").\n * Used for zero values and leading zeros in decimals.\n * @type {string}\n */\n zeroWord = ''\n\n // ============================================================================\n // Optional Properties (subclasses may override)\n // ============================================================================\n\n /**\n * Character(s) used to separate words in the output.\n *\n * Defaults to a single space. Set to empty string for languages without\n * word separators (e.g., Japanese, Thai, Chinese).\n *\n * @type {string}\n */\n wordSeparator = ' '\n\n /**\n * Whether to convert decimal digits individually rather than grouped.\n *\n * - `false` (default): Leading zeros preserved, remaining digits grouped as a number\n * - Example: \"05\" → [\"zero\", \"five\"], \"14\" → [\"fourteen\"]\n * - Used by: English, Spanish, French, German, etc.\n *\n * - `true`: Each digit converted separately\n * - Example: \"05\" → [\"zero\", \"five\"], \"14\" → [\"one\", \"four\"]\n * - Used by: Japanese, Thai, Tamil, Telugu, Greek, Hebrew, Filipino\n *\n * @type {boolean}\n */\n usePerDigitDecimals = false\n\n // ============================================================================\n // Public Accessors\n // ============================================================================\n\n /**\n * Read-only access to options set via `setOptions()`.\n * @type {Object}\n */\n get options () {\n return this.#options\n }\n\n // ============================================================================\n // Public Methods\n // ============================================================================\n\n /**\n * Converts pre-normalized numeric components to words.\n *\n * This is the main entry point called by the public API (makeConverter in n2words.js).\n * It assembles the final word representation from the provided components.\n *\n * **Caller contract (enforced by makeConverter):**\n * - `integerPart` is a non-negative BigInt (>= 0n)\n * - `decimalPart` is a string of digits only (no sign, no decimal point)\n * - `isNegative` reflects the original input sign\n *\n * **Conversion flow:**\n * 1. Prepend negative word if applicable\n * 2. Convert integer part via `integerToWords()`\n * 3. If decimals present: append separator and decimal words\n * 4. Join all parts with `wordSeparator`\n *\n * Subclasses needing access to the integer part during decimal conversion\n * (e.g., for context-dependent separator words) should override this\n * method to cache the value before calling super.toWords().\n *\n * @public\n * @param {boolean} isNegative Whether the original number was negative\n * @param {bigint} integerPart The integer part (always non-negative)\n * @param {string} [decimalPart] - Decimal digits if present (e.g., \"14\" for 3.14)\n * @returns {string} The localized cardinal string\n */\n toWords (isNegative, integerPart, decimalPart) {\n const words = []\n\n if (isNegative) words.push(this.negativeWord)\n\n words.push(this.integerToWords(integerPart))\n\n if (decimalPart) {\n words.push(this.decimalSeparatorWord)\n words.push(...this.decimalDigitsToWords(decimalPart))\n }\n\n return words.join(this.wordSeparator)\n }\n\n // ============================================================================\n // Abstract Methods (subclasses must implement)\n // ============================================================================\n\n /**\n * Converts a BigInt integer part to its cardinal word representation.\n *\n * This is the core template method that subclasses MUST implement to provide\n * language-specific number conversion logic.\n *\n * @abstract\n * @param {bigint} integerPart The integer part to convert (always >= 0n)\n * @returns {string} The cardinal representation in the target language\n * @throws {Error} If not implemented by subclass\n */\n integerToWords (integerPart) {\n throw new Error('integerToWords() must be implemented by subclass')\n }\n\n // ============================================================================\n // Protected Methods (subclasses may override or call)\n // ============================================================================\n\n /**\n * Sets options by merging language defaults with user-provided options.\n *\n * Merges defaults first, then user options (later keys override earlier ones).\n * Stores the result in the private `#options` field, accessible via the\n * read-only `options` getter.\n *\n * @protected\n * @param {Object} [defaults={}] - Default option values for the language\n * @param {Object} [userOptions={}] - Runtime options supplied by the caller\n *\n * @example\n * constructor(options = {}) {\n * super()\n * this.setOptions({ gender: 'masculine' }, options)\n * }\n */\n setOptions (defaults = {}, userOptions = {}) {\n this.#options = {\n ...defaults,\n ...userOptions\n }\n }\n\n /**\n * Converts an integer to words in decimal context.\n *\n * By default, delegates to `integerToWords()`. Override this method\n * when decimal conversion requires different behavior than integer conversion.\n *\n * Called with:\n * - Single digits (0-9) when `usePerDigitDecimals = true`\n * - Grouped numbers when `usePerDigitDecimals = false`\n *\n * **Use cases for overriding:**\n * - Romanian: Decimals always use masculine forms regardless of gender option\n * - Languages with different plural/gender rules for decimal vs integer parts\n *\n * @protected\n * @param {bigint} integerPart The integer to convert (single digit or grouped)\n * @returns {string} The word representation for use in decimal context\n */\n decimalIntegerToWords (integerPart) {\n return this.integerToWords(integerPart)\n }\n\n /**\n * Converts decimal fractional digits into an array of words.\n *\n * Leading zeros are always preserved individually. The remaining digits\n * are converted based on `usePerDigitDecimals`:\n *\n * - `false` (default): Remaining digits grouped as a single number\n * - \"05\" → [\"zero\", \"five\"], \"14\" → [\"fourteen\"]\n *\n * - `true`: Each remaining digit converted separately\n * - \"05\" → [\"zero\", \"five\"], \"14\" → [\"one\", \"four\"]\n *\n * @protected\n * @param {string} decimalPart Decimal digits as string (e.g., \"05\" for 3.05)\n * @returns {string[]} Array of word tokens for the fractional part\n */\n decimalDigitsToWords (decimalPart) {\n const words = []\n\n // Always preserve leading zeros individually\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n words.push(this.zeroWord)\n i++\n }\n\n const remainder = decimalPart.slice(i)\n if (!remainder) return words\n\n // Convert remainder: per-digit or as single number\n if (this.usePerDigitDecimals) {\n for (const char of remainder) {\n words.push(this.decimalIntegerToWords(BigInt(char)))\n }\n } else {\n words.push(this.decimalIntegerToWords(BigInt(remainder)))\n }\n\n return words\n }\n}\n","import { AbstractLanguage } from './abstract-language.js'\n\n/**\n * Greedy scale language converter implementing the \"highest-matching scale word\" algorithm.\n *\n * Responsibilities:\n * - Decompose an integer into a sequence of word-sets using greedy matching.\n * - Provide helpers to reduce and post-process matched word-sets.\n * - Inherits decimal handling from AbstractLanguage (supports grouped and per-digit\n * modes via the `usePerDigitDecimals` class property).\n *\n * Subclass requirements:\n * - Define `scaleWords` (ordered descending) as `[bigint, string]` tuples.\n * - Implement 'combineWordSets(preceding, following)' to combine adjacent word-sets\n * per language grammar.\n *\n * Scale words specification:\n * - `scaleWords` is an Array of 2-tuples: `[bigint, string]` where the first element\n * is the numeric scale value and the second is the word for that value.\n * - Scale words MUST be ordered from largest to smallest (descending) for the algorithm\n * to function correctly.\n *\n * Terminology:\n * - **Scale**: A magnitude value (100n, 1000n, 1000000n)\n * - **Word-set**: An object `{ word: bigint }` representing a partial result\n * - **Scale word**: The word for a scale value (\"hundred\", \"thousand\")\n *\n * @abstract\n * @extends AbstractLanguage\n */\n\nexport class GreedyScaleLanguage extends AbstractLanguage {\n // ============================================================================\n // Required Properties (subclasses must define)\n // ============================================================================\n\n /**\n * Array of scale words mapping numeric values to their word representations.\n *\n * Each element is a 2-tuple: `[bigint, string]` where the first element is the\n * numeric scale value and the second is the word for that value. The array MUST be\n * ordered from largest to smallest (descending) for the greedy algorithm to work correctly.\n *\n * @type {Array<[bigint, string]>}\n * @example\n * // English scale words (descending order):\n * // [[1000000000n, 'billion'], [1000000n, 'million'], [1000n, 'thousand'], [100n, 'hundred'], ..., [1n, 'one']]\n */\n scaleWords\n\n // ============================================================================\n // Public Methods\n // ============================================================================\n\n /**\n * Returns the word for an exact scale value.\n *\n * @param {bigint} scale The scale value to look up (prefer BigInt for exact matching).\n * @returns {string|undefined} The word for the provided scale, or `undefined`.\n */\n wordForScale (scale) {\n const match = this.scaleWords.find((pair) => pair[0] === scale)\n return match?.[1]\n }\n\n // ============================================================================\n // Protected Methods (subclasses may call or override)\n // ============================================================================\n\n /**\n * Decomposes an integer into a sequence of word-sets.\n *\n * This internal helper returns a nested structure that represents quantities and\n * their matching scale words (e.g. `[{ 'one': 1n }, { 'hundred': 100n }, ...]`).\n * The result is designed to be reduced by `reduceWordSets()` using language-specific `combineWordSets()`.\n *\n * For quantities > 1, the multiplier is recursively decomposed. For quantity = 1,\n * the implicit \"one\" is represented with `{ 'one': 1n }` and typically omitted during combineWordSets().\n *\n * @protected\n * @param {bigint} integerPart The integer to decompose.\n * @returns {Array<Object|Array>} An array of word-set objects and possibly nested arrays.\n */\n decomposeInteger (integerPart) {\n const wordSets = []\n let remaining = integerPart\n\n do {\n const match = this.scaleWords.find((pair) => remaining >= pair[0])\n if (!match) break\n\n const multiplier = remaining === 0n ? 1n : remaining / match[0]\n\n if (multiplier === 1n) {\n wordSets.push({ [this.wordForScale(1n)]: 1n })\n } else {\n wordSets.push(this.decomposeInteger(multiplier))\n }\n\n wordSets.push({ [match[1]]: match[0] })\n\n remaining = remaining === 0n ? 0n : remaining % match[0]\n } while (remaining > 0n)\n\n return wordSets\n }\n\n /**\n * Reduces a nested array of word-sets into a single word-set object.\n *\n * This method repeatedly applies the subclass `combineWordSets()` operation until a single\n * object remains. It normalizes nested arrays by recursively reducing them.\n *\n * @protected\n * @param {Array<Object|Array>} wordSets Array of word-set objects and nested arrays.\n * @returns {Object} Reduced word-set where the single object key is the language string\n * and its value is the numeric BigInt result for that string.\n */\n reduceWordSets (wordSets) {\n while (wordSets.length > 1) {\n const [first, second, ...rest] = wordSets\n\n if (!Array.isArray(first) && !Array.isArray(second)) {\n const combined = this.combineWordSets(first, second)\n wordSets = rest.length > 0 ? [combined, rest] : [combined]\n continue\n }\n\n const normalized = wordSets.map((element) => {\n if (!Array.isArray(element)) return element\n return element.length === 1 ? element[0] : this.reduceWordSets(element)\n })\n\n wordSets = normalized\n }\n\n return wordSets[0]\n }\n\n // ============================================================================\n // Abstract Methods (subclasses must implement)\n // ============================================================================\n\n /**\n * Combines two adjacent word-sets into a single word-set.\n *\n * This is the core language-specific method that must be implemented by subclasses\n * to define how adjacent word-sets are combined according to the language's grammar.\n * For example, English combines \"twenty\" + \"three\" → \"twenty-three\", while\n * French might combine \"quatre-vingts\" + \"dix\" → \"quatre-vingt-dix\".\n *\n * @abstract\n * @protected\n * @param {Object} preceding Preceding word-set as `{ word: bigint }`.\n * @param {Object} following Following word-set as `{ word: bigint }`.\n * @returns {Object} Combined word-set with merged text and resulting numeric value.\n *\n * @example\n * // English implementation might handle:\n * // combineWordSets({ 'twenty': 20n }, { 'three': 3n }) → { 'twenty-three': 23n }\n * // combineWordSets({ 'one': 1n }, { 'hundred': 100n }) → { 'one hundred': 100n }\n */\n combineWordSets (preceding, following) {\n throw new Error('combineWordSets() must be implemented by subclass')\n }\n\n // ============================================================================\n // Optional Methods (subclasses may override)\n // ============================================================================\n\n /**\n * Final string post-processing hook.\n *\n * Subclasses may override to apply language-specific whitespace, punctuation or\n * orthographic corrections.\n *\n * @protected\n * @param {string} output Language string produced by the conversion flow.\n * @returns {string} Final formatted string.\n */\n finalizeWords (output) {\n return output.trimEnd()\n }\n\n /**\n * Converts an integer to its language-specific cardinal words.\n *\n * This method orchestrates decomposition, reduction, and final formatting. It does\n * not handle decimals or sign; those concerns are implemented in\n * `AbstractLanguage.toWords` which calls this method for the integer part.\n *\n * @param {bigint} integerPart The integer to convert.\n * @returns {string} The cardinal representation for the integer in the language.\n */\n integerToWords (integerPart) {\n const wordSets = this.decomposeInteger(integerPart)\n const reduced = this.reduceWordSets(wordSets)\n const result = Object.keys(reduced)[0]\n return this.finalizeWords(result)\n }\n}\n","import { GreedyScaleLanguage } from './greedy-scale-language.js'\n\n/**\n * Base class for Turkic languages with shared grammar patterns.\n *\n * This class provides a reusable implementation for Turkic languages that share:\n * - Space-separated number combinations\n * - Implicit 'bir' (one) before hundreds and thousands\n * - Simple multiplication/addition logic\n * - Consistent magnitude handling\n * - Inherits decimal handling from AbstractLanguage via GreedyScaleLanguage\n * (supports both grouped and per-digit modes via the `usePerDigitDecimals` class property).\n *\n * Used by: Turkish (TR), Azerbaijani (AZ)\n *\n * Subclasses MUST define (from GreedyScaleLanguage requirements):\n * - `scaleWords` array of [value, word] tuples as a class property (ordered descending by value).\n * Optionally, language-specific class properties (e.g., `negativeWord`, `zeroWord`, `decimalSeparatorWord`, `wordSeparator`).\n *\n * TurkicLanguage provides a default `combineWordSets()` implementation; subclasses may override\n * if specialized combine logic is needed (unlikely for Turkic languages).\n *\n * @abstract\n * @extends GreedyScaleLanguage\n */\nexport class TurkicLanguage extends GreedyScaleLanguage {\n // ============================================================================\n // Inherited Implementation (overrides GreedyScaleLanguage)\n // ============================================================================\n\n /**\n * Combines two adjacent word-sets according to Turkic grammar rules.\n *\n * Shared Turkic patterns:\n * - Implicit 'bir' (one) before hundreds and thousands\n * - Space separator (wordSeparator property) for all combinations\n * - Multiplies when following > preceding (crossing magnitude boundary)\n * - Adds otherwise (combining same-magnitude components)\n *\n * @param {Object} preceding Preceding word-set as { word: bigint }.\n * @param {Object} following Following word-set as { word: bigint }.\n * @returns {Object} Combined word-set with merged word and resulting number.\n */\n combineWordSets (preceding, following) {\n const [[precedingWord, precedingValue]] = Object.entries(preceding)\n const [[followingWord, followingValue]] = Object.entries(following)\n\n // Implicit 'bir' (one) before certain magnitudes:\n // Omit '1' before hundreds (100n) and thousands (1000n) to form natural combinations\n if (precedingValue === 1n && (followingValue <= 100n || followingValue === 1000n)) {\n return following // Return just the magnitude word (e.g., \"yüz\", not \"bir yüz\")\n }\n\n // Combine numbers with space separator (wordSeparator from GreedyScaleLanguage):\n // Multiply when crossing magnitude boundary, add otherwise\n const resultNumber = followingValue > precedingValue ? precedingValue * followingValue : precedingValue + followingValue\n return { [`${precedingWord}${this.wordSeparator}${followingWord}`]: resultNumber }\n }\n}\n","import { AbstractLanguage } from './abstract-language.js'\n\n/**\n * Base class for South Asian languages with shared grouping patterns.\n *\n * This class provides a reusable implementation for South Asian languages that share:\n * - Indian-style number grouping: last 3 digits, then 2-2 (1,23,45,67,89)\n * - Lakh (100,000), Crore (10,000,000), Arab (1,000,000,000) scale words\n * - Standard negative and decimal handling (inherits AbstractLanguage decimal logic,\n * including `usePerDigitDecimals` support when set by subclasses)\n *\n * Used by: Hindi (hi), Bengali (bn), Urdu (ur), Punjabi (pa), Marathi (mr), Gujarati (gu), Kannada (kn)\n *\n * Subclasses MUST define language-specific vocabulary via class properties:\n * - `belowHundredWords` array with digit and teen words (0-99)\n * - `hundredWord` string used inside `segmentToWords`\n * - `scaleWords` array with grouping words (hazaar, lakh, crore, etc.) indexed by grouping level\n * - `negativeWord`, `decimalSeparatorWord`, `zeroWord`, `wordSeparator`\n *\n * @abstract\n * @extends AbstractLanguage\n */\nexport class SouthAsianLanguage extends AbstractLanguage {\n // ============================================================================\n // Required Properties (subclasses must define)\n // ============================================================================\n\n /**\n * Array of words for numbers 0-99 (digits and teens).\n * Index directly: belowHundredWords[0] through belowHundredWords[99].\n * @type {Array<string>}\n */\n belowHundredWords\n\n /**\n * Word for \"hundred\" in the language (e.g., 'सौ' in Hindi, 'শত' in Bengali).\n * Used to construct hundreds (e.g., \"1 hundred\", \"2 hundred\").\n * @type {string}\n */\n hundredWord\n\n /**\n * Array of scale words for Indian-style grouping (hazaar, lakh, crore, arab, etc.).\n * Index 0 contains empty string (ones place has no scale word).\n * Index 1 is for thousands, Index 2 for lakhs, Index 3 for crores, etc.\n * @type {Array<string>}\n */\n scaleWords\n\n // ============================================================================\n // Protected Methods (subclasses may call or override)\n // ============================================================================\n\n /**\n * Splits a number into Indian numbering system segments.\n *\n * The Indian system segments differently than Western (3-3-3) systems:\n * - First segment (rightmost): Up to 3 digits (ones, tens, hundreds)\n * - Subsequent segments: Exactly 2 digits each (thousands, lakhs, crores, etc.)\n *\n * This creates the familiar Indian comma pattern: 1,23,45,67,890\n *\n * @protected\n * @param {bigint} integerPart The integer to split into segments.\n * @returns {Array<number>} Array of segments from most significant to least significant.\n *\n * @example\n * // splitToSegments(1234567n) → [12, 34, 567]\n * // Reads as: 12 lakhs, 34 thousands, 567 units\n * // splitToSegments(98765432n) → [9, 87, 65, 432]\n * // Reads as: 9 crores, 87 lakhs, 65 thousands, 432 units\n */\n splitToSegments (integerPart) {\n const numStr = integerPart.toString()\n\n if (numStr.length <= 3) {\n return [Number(numStr)]\n }\n\n const segments = []\n const last3 = numStr.slice(-3)\n segments.unshift(Number(last3))\n\n let remaining = numStr.slice(0, -3)\n while (remaining.length > 0) {\n const segment = remaining.slice(-2)\n segments.unshift(Number(segment))\n remaining = remaining.slice(0, -2)\n }\n\n return segments\n }\n\n /**\n * Converts a segment (0-999) to words.\n *\n * @protected\n * @param {number} segmentValue Value between 0 and 999.\n * @returns {string} Language-specific word representation.\n */\n segmentToWords (segmentValue) {\n if (segmentValue === 0) return ''\n if (segmentValue < 100) return this.belowHundredWords[segmentValue]\n\n const hundreds = Math.trunc(segmentValue / 100)\n const remainder = segmentValue % 100\n const parts = []\n\n if (hundreds === 1) {\n parts.push(this.belowHundredWords[1] + ' ' + this.hundredWord)\n } else {\n parts.push(this.belowHundredWords[hundreds] + ' ' + this.hundredWord)\n }\n\n if (remainder > 0) {\n parts.push(this.belowHundredWords[remainder])\n }\n\n return parts.join(' ')\n }\n\n // ============================================================================\n // Public Methods\n // ============================================================================\n\n /**\n * Converts integer to cardinal words using South Asian grouping.\n *\n * @param {bigint} integerPart Number to convert.\n * @returns {string} Cardinal representation.\n */\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n\n const segments = this.splitToSegments(integerPart)\n const segmentCount = segments.length\n const words = []\n\n for (let i = 0; i < segmentCount; i++) {\n const segmentValue = segments[i]\n if (segmentValue === 0) continue\n\n const scaleIndex = segmentCount - i - 1\n words.push(this.segmentToWords(segmentValue))\n if (scaleIndex > 0 && this.scaleWords[scaleIndex]) {\n words.push(this.scaleWords[scaleIndex])\n }\n }\n\n return words.join(' ').trim()\n }\n}\n","import { AbstractLanguage } from './abstract-language.js'\n\n/**\n * Base class for Slavic and related languages with complex pluralization.\n *\n * This class provides a reusable implementation for languages that share:\n * - Three-form pluralization (singular/few/many)\n * - Gender-aware number forms (masculine/feminine for 1-9)\n * - Hundreds, tens, ones decomposition pattern\n * - Segment-based large number handling (thousands, millions, etc.)\n * - Inherits decimal handling from AbstractLanguage (supports both grouped and\n * per-digit modes via the `usePerDigitDecimals` class property).\n *\n * Used by: Russian (ru), Czech (cs), Polish (pl), Ukrainian (uk), Serbian (sr-Latn),\n * Croatian (hr), Lithuanian (lt), Latvian (lv), Hebrew (he), and Biblical Hebrew (hbo).\n *\n * Subclasses MUST define these properties with language-specific vocabulary:\n * - `onesWords` - Object mapping 1-9 to masculine forms (or default forms)\n * - `onesFeminineWords` - Object mapping 1-9 to feminine forms (if gender distinction exists)\n * - `teensWords` - Object mapping 0-9 to teen numbers (10-19)\n * - `twentiesWords` - Object mapping 2-9 to tens (20-90)\n * - `hundredsWords` - Object mapping 1-9 to hundreds (100-900) or special hundreds handling\n * - `pluralForms` - Object mapping segment indices to [singular, few, many] plural forms\n *\n * Optional properties:\n * - `scaleGenders` - Object mapping segment indices to boolean (true = feminine scale word)\n * If not defined, defaults to thousands (index 1) being feminine, others masculine.\n *\n * @abstract\n * @extends AbstractLanguage\n */\nexport class SlavicLanguage extends AbstractLanguage {\n // ============================================================================\n // Required Properties (subclasses must define)\n // ============================================================================\n\n /**\n * Masculine forms for digits 1-9 (or default forms if no gender distinction).\n *\n * @type {Object.<number, string>}\n */\n onesWords = {}\n\n /**\n * Feminine forms for digits 1-9 (if language has gender distinction).\n *\n * @type {Object.<number, string>}\n */\n onesFeminineWords = {}\n\n /**\n * Words for teen numbers (10-19).\n *\n * @type {Object.<number, string>}\n */\n teensWords = {}\n\n /**\n * Words for multiples of ten (20, 30, 40, etc.).\n *\n * @type {Object.<number, string>}\n */\n twentiesWords = {}\n\n /**\n * Words for hundreds (100, 200, 300, etc.) or special hundreds handling.\n *\n * @type {Object.<number, string>}\n */\n hundredsWords = {}\n\n /**\n * Plural forms for scale words (thousands, millions, billions, etc.).\n * Maps segment indices to [singular, few, many] forms.\n *\n * @type {Object.<number, string[]>}\n */\n pluralForms = {}\n\n // ============================================================================\n // Optional Properties (subclasses may override)\n // ============================================================================\n\n /**\n * Gender of each scale word.\n * Maps segment indices to boolean: true = feminine, false = masculine.\n * Default is empty (all masculine). Languages with feminine thousands\n * (Russian, Ukrainian, Serbian, Croatian) should set `{ 1: true }`.\n *\n * @type {Object.<number, boolean>}\n */\n scaleGenders = {}\n\n /**\n * Whether to omit \"one\" before scale words (e.g., \"thousand\" instead of \"one thousand\").\n * When true, 1000 becomes \"tysiąc\" (Polish) instead of \"jeden tysiąc\".\n * Used by Polish, Czech, and similar languages.\n *\n * @type {boolean}\n */\n omitOneBeforeScale = false\n\n // ============================================================================\n // Constructor\n // ============================================================================\n\n /**\n * Constructs a SlavicLanguage instance with optional configuration.\n *\n * @param {Object} [options] Configuration options.\n * @param {('masculine'|'feminine')} [options.gender='masculine'] Grammatical gender for number forms.\n */\n constructor (options = {}) {\n super()\n\n this.setOptions({\n gender: 'masculine'\n }, options)\n }\n\n // ============================================================================\n // Public Methods\n // ============================================================================\n\n /**\n * Converts an integer to its word representation.\n *\n * This method implements the Slavic number construction algorithm:\n * 1. Split number into 3-digit segments from right to left (567, 234, 1 for 1,234,567)\n * 2. For each segment (processing left to right): convert hundreds, tens, ones\n * 3. Apply gender rules: feminine forms for thousands segment or when feminine=true\n * 4. Add appropriate pluralized scale word (thousand/million/billion/etc.)\n * 5. Join all parts with spaces\n *\n * @param {bigint} integerPart The integer to convert (non-negative).\n * @returns {string} The number in words.\n */\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n\n const words = []\n const segments = this.splitToSegments(integerPart.toString(), 3)\n let segmentIndex = segments.length\n\n for (const segmentValue of segments) {\n segmentIndex = segmentIndex - 1\n\n if (segmentValue === 0n) {\n continue\n }\n\n const [onesDigit, tensDigit, hundredsDigit] = this.extractDigits(segmentValue)\n\n if (hundredsDigit > 0n) {\n words.push(this.hundredsWords[hundredsDigit])\n }\n\n if (tensDigit > 1n) {\n words.push(this.twentiesWords[tensDigit])\n }\n\n // Handle teens (10-19) or ones (1-9)\n if (tensDigit === 1n) {\n // Teens: use teensWords array directly\n words.push(this.teensWords[onesDigit])\n } else if (onesDigit > 0n) {\n // Skip \"one\" before scale words if omitOneBeforeScale is set\n // e.g., Polish says \"tysiąc\" not \"jeden tysiąc\" for 1000\n const shouldOmitOne = this.omitOneBeforeScale && segmentIndex > 0 && segmentValue === 1n\n\n if (!shouldOmitOne) {\n // Determine if feminine forms should be used:\n // 1. Check scaleGenders for this segment index (e.g., thousands = index 1)\n // 2. Also use feminine if user requested gender='feminine' for the ones segment\n const isScaleFeminine = this.scaleGenders[segmentIndex] === true\n const isFeminine = isScaleFeminine || (this.options.gender === 'feminine' && segmentIndex === 0)\n const onesArray = isFeminine ? this.onesFeminineWords : this.onesWords\n words.push(onesArray[onesDigit])\n }\n }\n\n // Add power word (thousand, million, etc.) with proper pluralization\n if (segmentIndex > 0) {\n words.push(this.pluralize(segmentValue, this.pluralForms[segmentIndex]))\n }\n }\n\n return words.join(' ')\n }\n\n // ============================================================================\n // Protected Methods (subclasses may call or override)\n // ============================================================================\n\n /**\n * Splits a number string into segments of specified size from right to left.\n *\n * Example: splitToSegments('1234567', 3) => [1n, 234n, 567n]\n * This represents: 1 million + 234 thousand + 567 ones\n *\n * @param {string} numberString The number as a string.\n * @param {number} segmentSize Segment size (typically 3 for thousands grouping).\n * @returns {bigint[]} Array of BigInt segments from highest to lowest scale.\n */\n splitToSegments (numberString, segmentSize) {\n const segments = []\n const stringLength = numberString.length\n\n if (stringLength > segmentSize) {\n const remainderLength = stringLength % segmentSize\n\n if (remainderLength > 0) {\n segments.push(BigInt(numberString.slice(0, remainderLength)))\n }\n\n for (let i = remainderLength; i < stringLength; i += segmentSize) {\n segments.push(BigInt(numberString.slice(i, i + segmentSize)))\n }\n } else {\n segments.push(BigInt(numberString))\n }\n\n return segments\n }\n\n /**\n * Extracts individual digits from a number (units, tens, hundreds).\n *\n * Returns digits in reverse order: [ones, tens, hundreds]\n * Example: 456 => [6n, 5n, 4n]\n *\n * @param {bigint} value The number to extract digits from (0-999).\n * @returns {bigint[]} Array of [ones, tens, hundreds] as BigInts.\n */\n extractDigits (value) {\n // Direct BigInt arithmetic is faster than string manipulation\n const onesPlace = value % 10n\n const tensPlace = (value / 10n) % 10n\n const hundredsPlace = value / 100n\n return [onesPlace, tensPlace, hundredsPlace]\n }\n\n /**\n * Selects the correct plural form based on Slavic pluralization rules.\n *\n * Slavic languages typically use three forms:\n * - Form 0 (singular): numbers ending in 1, except 11 (1, 21, 31, 101...)\n * - Form 1 (few): numbers ending in 2-4, except 12-14 (2-4, 22-24, 32-34...)\n * - Form 2 (many): all other numbers (0, 5-20, 25-30, 100, 111-119...)\n *\n * Examples using Russian тысяча (thousand):\n * - 1, 21, 31... ⇒ тысяча (form 0, singular)\n * - 2-4, 22-24, 32-34... ⇒ тысячи (form 1, few)\n * - 0, 5-20, 25-30, 100... ⇒ тысяч (form 2, many)\n *\n * @param {bigint} number The number to check.\n * @param {string[]} pluralForms Array of [singular, few, many] forms.\n * @returns {string} The appropriate form for the number.\n */\n pluralize (number, pluralForms) {\n const remainder100 = number % 100n\n const remainder10 = number % 10n\n\n // Check if in 11-19 range (special case)\n if (remainder100 >= 10n && remainder100 <= 20n) {\n return pluralForms[2] // Always use \"many\" form for 11-20\n }\n\n if (remainder10 === 1n) {\n return pluralForms[0] // Singular\n }\n\n if (remainder10 >= 2n && remainder10 <= 4n) {\n return pluralForms[1] // Few (2-4)\n }\n\n return pluralForms[2] // Many\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * French language converter.\n *\n * Supports:\n * - Pluralization of \"cent\" (hundred) and other words\n * - \"et\" (and) before odd numbers in tens place\n * - Optional hyphenation for compound numbers\n */\nexport class French extends GreedyScaleLanguage {\n negativeWord = 'moins'\n decimalSeparatorWord = 'virgule'\n zeroWord = 'zéro'\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000_000n, 'quadrilliard'],\n [1_000_000_000_000_000_000_000_000n, 'quadrillion'],\n [1_000_000_000_000_000_000_000n, 'trilliard'],\n [1_000_000_000_000_000_000n, 'trillion'],\n [1_000_000_000_000_000n, 'billiard'],\n [1_000_000_000_000n, 'billion'],\n [1_000_000_000n, 'milliard'],\n [1_000_000n, 'million'],\n [1000n, 'mille'],\n [100n, 'cent'],\n [80n, 'quatre-vingts'],\n [60n, 'soixante'],\n [50n, 'cinquante'],\n [40n, 'quarante'],\n [30n, 'trente'],\n [20n, 'vingt'],\n [19n, 'dix-neuf'],\n [18n, 'dix-huit'],\n [17n, 'dix-sept'],\n [16n, 'seize'],\n [15n, 'quinze'],\n [14n, 'quatorze'],\n [13n, 'treize'],\n [12n, 'douze'],\n [11n, 'onze'],\n [10n, 'dix'],\n [9n, 'neuf'],\n [8n, 'huit'],\n [7n, 'sept'],\n [6n, 'six'],\n [5n, 'cinq'],\n [4n, 'quatre'],\n [3n, 'trois'],\n [2n, 'deux'],\n [1n, 'un'],\n [0n, 'zéro']\n ]\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n withHyphenSeparator: false\n }, options)\n\n if (options.withHyphenSeparator) {\n this.wordSeparator = '-'\n }\n }\n\n /** Combines two word-sets with French pluralization and hyphenation rules. */\n combineWordSets (preceding, following) {\n let precedingWord = Object.keys(preceding)[0]\n let followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0]\n const followingValue = Object.values(following)[0]\n\n if (precedingValue === 1n) {\n if (followingValue < 1_000_000n) {\n return following\n }\n } else {\n if (\n ((precedingValue - 80n) % 100n === 0n || (precedingValue % 100n === 0n && precedingValue < 1000n)) &&\n followingValue < 1_000_000n &&\n precedingWord.at(-1) === 's'\n ) {\n precedingWord = precedingWord.slice(0, -1)\n }\n\n if (\n precedingValue < 1000n && followingValue !== 1000n &&\n followingWord.at(-1) !== 's' &&\n followingValue % 100n === 0n\n ) {\n followingWord += 's'\n }\n }\n\n if (followingValue < precedingValue && precedingValue < 100n) {\n if (followingValue % 10n === 1n && precedingValue !== 80n) {\n return { [`${precedingWord}${this.wordSeparator}et${this.wordSeparator}${followingWord}`]: precedingValue + followingValue }\n }\n return { [`${precedingWord}-${followingWord}`]: precedingValue + followingValue }\n }\n\n if (followingValue > precedingValue) return { [`${precedingWord}${this.wordSeparator}${followingWord}`]: precedingValue * followingValue }\n return { [`${precedingWord}${this.wordSeparator}${followingWord}`]: precedingValue + followingValue }\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * (European) Portuguese language converter.\n *\n * Supports:\n * - Gender-aware hundreds (duzentos, trezentos)\n * - \"e\" (and) conjunction for number combinations\n * - Post-processing to normalize word flow\n */\nexport class Portuguese extends GreedyScaleLanguage {\n negativeWord = 'menos'\n decimalSeparatorWord = 'vírgula'\n zeroWord = 'zero'\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000n, 'quatrilião'],\n [1_000_000_000_000_000_000n, 'trilião'],\n [1_000_000_000_000n, 'bilião'],\n [1_000_000n, 'milião'],\n [1000n, 'mil'],\n [100n, 'cem'],\n [90n, 'noventa'],\n [80n, 'oitenta'],\n [70n, 'setenta'],\n [60n, 'sessenta'],\n [50n, 'cinquenta'],\n [40n, 'quarenta'],\n [30n, 'trinta'],\n [20n, 'vinte'],\n [19n, 'dezanove'],\n [18n, 'dezoito'],\n [17n, 'dezassete'],\n [16n, 'dezasseis'],\n [15n, 'quinze'],\n [14n, 'catorze'],\n [13n, 'treze'],\n [12n, 'doze'],\n [11n, 'onze'],\n [10n, 'dez'],\n [9n, 'nove'],\n [8n, 'oito'],\n [7n, 'sete'],\n [6n, 'seis'],\n [5n, 'cinco'],\n [4n, 'quatro'],\n [3n, 'três'],\n [2n, 'dois'],\n [1n, 'um'],\n [0n, 'zero']\n ]\n\n hundredsWords = {\n 1: 'cento',\n 2: 'duzentos',\n 3: 'trezentos',\n 4: 'quatrocentos',\n 5: 'quinhentos',\n 6: 'seiscentos',\n 7: 'setecentos',\n 8: 'oitocentos',\n 9: 'novecentos'\n }\n\n // Pre-compiled regex patterns for postClean - avoid recompilation\n static POSTCLEAN_REGEX = / e (.*entos?) (?=.*e)/g\n\n finalizeWords (words) {\n return words.replaceAll(Portuguese.POSTCLEAN_REGEX, ' $1 ')\n }\n\n /** Combines two word-sets according to Portuguese grammar rules. */\n combineWordSets (preceding, following) {\n // Extract words and numeric values\n let precedingWord = Object.keys(preceding)[0]\n let followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0] // BigInt\n const followingValue = Object.values(following)[0] // BigInt\n\n // Implicit \"um\": omit before millions (\"um milhão\" → \"milhão\")\n if (precedingValue === 1n) {\n if (followingValue < 1_000_000n) return { [followingWord]: followingValue }\n precedingWord = 'um'\n } else if (precedingValue === 100n && followingValue % 1000n !== 0n) {\n // Special handling: \"cem\" (100) becomes \"cento\" when followed by non-thousands\n precedingWord = 'cento'\n }\n\n if (followingValue < precedingValue) {\n return { [`${precedingWord} e ${followingWord}`]: precedingValue + followingValue }\n }\n\n // Handle \"milião\" -> \"milhão\" conversion\n if (followingWord === 'milião') followingWord = 'milhão'\n\n // Pluralization logic for large numbers\n if (precedingValue > 1n) {\n if (followingValue % 1_000_000_000n === 0n) {\n followingWord = followingWord.replace('bilião', 'biliões')\n } else if (followingValue % 1_000_000n === 0n) {\n followingWord = followingWord.replace('milhão', 'milhões')\n }\n }\n\n if (followingValue === 100n) {\n precedingWord = this.hundredsWords[precedingValue]\n return { [`${precedingWord}`]: precedingValue * followingValue }\n }\n\n return { [`${precedingWord} ${followingWord}`]: precedingValue * followingValue }\n }\n}\n","/**\n * n2words - Convert numbers to words in multiple languages\n *\n * This file is the main entry point for the n2words library.\n * It exports converter functions for all supported languages.\n *\n * ## For Contributors\n *\n * When adding a new language, this file must be updated in THREE sections:\n * 1. Language Imports - Add import statement (alphabetically sorted)\n * 2. Language Converters - Create converter with makeConverter() (alphabetically sorted)\n * 3. Exports - Add to export list (alphabetically sorted)\n *\n * Use the scaffolding tool to automate this process:\n * npm run lang:add <language-code>\n *\n * ## Public API Structure\n *\n * Each language exports a converter function:\n * - Name: `{LanguageName}Converter` (e.g., EnglishConverter, SpanishConverter)\n * - Signature: `(value: NumericValue, options?: Options) => string`\n * - Input: number, bigint, or string (numeric strings only)\n * - Output: Words representing the number in the target language\n *\n * Languages without options use signature: `(value: NumericValue) => string`\n * Languages with options define a typedef (e.g., ArabicOptions) and use: `(value: NumericValue, options?: ArabicOptions) => string`\n *\n * @module n2words\n */\n\n// ============================================================================\n// Language Imports\n// ============================================================================\n\nimport { Arabic } from './languages/ar.js'\nimport { Azerbaijani } from './languages/az.js'\nimport { Bangla } from './languages/bn.js'\nimport { Czech } from './languages/cs.js'\nimport { Danish } from './languages/da.js'\nimport { German } from './languages/de.js'\nimport { Greek } from './languages/el.js'\nimport { English } from './languages/en.js'\nimport { Spanish } from './languages/es.js'\nimport { Persian } from './languages/fa.js'\nimport { Filipino } from './languages/fil.js'\nimport { French } from './languages/fr.js'\nimport { FrenchBelgium } from './languages/fr-BE.js'\nimport { Gujarati } from './languages/gu.js'\nimport { Hebrew } from './languages/he.js'\nimport { BiblicalHebrew } from './languages/hbo.js'\nimport { Hindi } from './languages/hi.js'\nimport { Croatian } from './languages/hr.js'\nimport { Hungarian } from './languages/hu.js'\nimport { Indonesian } from './languages/id.js'\nimport { Italian } from './languages/it.js'\nimport { Japanese } from './languages/ja.js'\nimport { Kannada } from './languages/kn.js'\nimport { Korean } from './languages/ko.js'\nimport { Lithuanian } from './languages/lt.js'\nimport { Latvian } from './languages/lv.js'\nimport { Marathi } from './languages/mr.js'\nimport { Malay } from './languages/ms.js'\nimport { Dutch } from './languages/nl.js'\nimport { NorwegianBokmal } from './languages/nb.js'\nimport { Punjabi } from './languages/pa.js'\nimport { Polish } from './languages/pl.js'\nimport { Portuguese } from './languages/pt.js'\nimport { Romanian } from './languages/ro.js'\nimport { Russian } from './languages/ru.js'\nimport { SerbianCyrillic } from './languages/sr-Cyrl.js'\nimport { SerbianLatin } from './languages/sr-Latn.js'\nimport { Swedish } from './languages/sv.js'\nimport { Swahili } from './languages/sw.js'\nimport { Tamil } from './languages/ta.js'\nimport { Telugu } from './languages/te.js'\nimport { Thai } from './languages/th.js'\nimport { Turkish } from './languages/tr.js'\nimport { Ukrainian } from './languages/uk.js'\nimport { Urdu } from './languages/ur.js'\nimport { Vietnamese } from './languages/vi.js'\nimport { SimplifiedChinese } from './languages/zh-Hans.js'\nimport { TraditionalChinese } from './languages/zh-Hant.js'\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n//\n// This section defines TypeScript-compatible JSDoc types for:\n// - NumericValue: The input types accepted by all converters\n// - {Language}Options: Optional configuration for languages that support it\n//\n// Keep options typedefs alphabetically sorted for maintainability.\n// ============================================================================\n\n/**\n * Numeric value that can be converted to words.\n * Accepts number, bigint, or numeric string representations.\n * @typedef {number | bigint | string} NumericValue\n */\n\n/**\n * @typedef {Object} ArabicOptions\n * @property {string} [negativeWord] Word for negative numbers\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} BiblicalHebrewOptions\n * @property {string} [andWord='ו'] Conjunction character (typically 'ו' for and)\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} CroatianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} CzechOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} DanishOptions\n * @property {boolean} [ordFlag=false] Enable ordinal number conversion\n */\n\n/**\n * @typedef {Object} DutchOptions\n * @property {boolean} [includeOptionalAnd=false] Include optional \"en\" separator\n * @property {boolean} [noHundredPairing=false] Disable hundred-pairing (e.g., \"twelve hundred\" becomes \"one thousand two hundred\")\n * @property {boolean} [accentOne=true] Use accented \"één\" for one\n */\n\n/**\n * @typedef {Object} FrenchOptions\n * @property {boolean} [withHyphenSeparator=false] Use hyphens (true) instead of spaces (false) in compounds\n */\n\n/**\n * @typedef {Object} FrenchBelgiumOptions\n * @property {boolean} [withHyphenSeparator=false] Use hyphens (true) instead of spaces (false) in compounds\n */\n\n/**\n * @typedef {Object} HebrewOptions\n * @property {string} [andWord='ו'] Conjunction character (typically 'ו' for and)\n */\n\n/**\n * @typedef {Object} LatvianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} LithuanianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} PolishOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} RomanianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} RussianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SerbianCyrillicOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SerbianLatinOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} SimplifiedChineseOptions\n * @property {boolean} [formal=true] Use formal/financial numerals (壹贰叁) vs. common numerals (一二三)\n */\n\n/**\n * @typedef {Object} SpanishOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n/**\n * @typedef {Object} TraditionalChineseOptions\n * @property {boolean} [formal=true] Use formal/financial numerals (壹貳參) vs. common numerals (一二三)\n */\n\n/**\n * @typedef {Object} TurkishOptions\n * @property {boolean} [dropSpaces=false] Remove spaces between words if true\n */\n\n/**\n * @typedef {Object} UkrainianOptions\n * @property {('masculine'|'feminine')} [gender='masculine'] Grammatical gender for number forms\n */\n\n// ============================================================================\n// Converter Factory\n// ============================================================================\n//\n// makeConverter() is a factory function that creates converter functions from\n// language classes. It provides a consistent functional API for all languages:\n//\n// const result = EnglishConverter(42) // \"forty-two\"\n// const result = ArabicConverter(1, {gender: 'feminine'}) // \"واحدة\"\n//\n// This design:\n// - Hides class instantiation details from public API\n// - Ensures each conversion uses a fresh instance\n// - Maintains immutability (no shared state between conversions)\n// ============================================================================\n\n/**\n * Creates a converter function for a language class.\n *\n * This factory handles all input validation and normalization at the public API\n * boundary, then delegates to the language class with pre-processed data.\n *\n * @template {Object} [TOptions={}]\n * @param {new (options?: TOptions) => { toWords: (isNegative: boolean, integerPart: bigint, decimalPart?: string) => string }} LanguageClass - Language class constructor\n * @returns {(value: NumericValue, options?: TOptions) => string} Converter function\n */\nfunction makeConverter (LanguageClass) {\n /**\n * @param {NumericValue} value\n * @param {TOptions} [options]\n * @returns {string}\n */\n return function convertToWords (value, options) {\n if (options !== undefined && !isPlainObject(options)) {\n throw new TypeError('options must be a plain object if provided')\n }\n\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n return new LanguageClass(options).toWords(isNegative, integerPart, decimalPart)\n }\n}\n\n// ============================================================================\n// Input Parsing Utilities\n// ============================================================================\n\n/**\n * @typedef {Object} ParsedNumericValue\n * @property {boolean} isNegative - Whether the value is negative\n * @property {bigint} integerPart - The absolute integer part\n * @property {string} [decimalPart] - The decimal digits (without the point)\n */\n\n/**\n * Parses and validates a numeric value into its components.\n * Handles number, string, and bigint inputs uniformly.\n *\n * @param {NumericValue} value The value to parse\n * @returns {ParsedNumericValue} The parsed components\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n */\nfunction parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case - no decimals, no scientific notation\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Reject invalid types early\n if (type !== 'number' && type !== 'string') {\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n }\n\n // Convert to normalized string\n const str = type === 'number'\n ? numberToString(value)\n : stringToNormalizedForm(value)\n\n return parseNumericString(str)\n}\n\n/**\n * Converts a JavaScript number to a decimal string.\n * Handles special cases: Infinity, NaN, and scientific notation.\n *\n * @param {number} value The number to convert\n * @returns {string} Decimal string representation\n * @throws {Error} If value is not finite\n */\nfunction numberToString (value) {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n\n const str = value.toString()\n\n // Expand scientific notation (used for values >= 1e21 or < 1e-6)\n if (str.includes('e') || str.includes('E')) {\n return expandScientificNotation(str)\n }\n\n return str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n *\n * @param {string} value The string to validate\n * @returns {string} Trimmed and validated string\n * @throws {Error} If string is empty or not a valid number format\n */\nfunction stringToNormalizedForm (value) {\n const trimmed = value.trim()\n\n if (trimmed.length === 0) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n\n // Validate by attempting conversion (handles edge cases like \"1e21\", \"-.5\", etc.)\n if (Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n\n // Expand scientific notation if present\n if (trimmed.includes('e') || trimmed.includes('E')) {\n return expandScientificNotation(trimmed)\n }\n\n return trimmed\n}\n\n/**\n * Parses a normalized numeric string into its components.\n *\n * @param {string} str A normalized decimal string (no scientific notation)\n * @returns {ParsedNumericValue} The parsed components\n */\nfunction parseNumericString (str) {\n // Extract sign\n const isNegative = str[0] === '-'\n if (isNegative) {\n str = str.slice(1)\n }\n\n // Split into integer and decimal parts\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\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form.\n * JavaScript uses scientific notation for values >= 1e21 or < 1e-6.\n *\n * @param {string} str String possibly in scientific notation (e.g., \"1e+21\")\n * @returns {string} Decimal form (e.g., \"1000000000000000000000\")\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n // Extract digits and determine original decimal position\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\n // Calculate new decimal position after applying exponent\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n // Pure integer: pad with trailing zeros\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n\n if (newDotPosition <= 0) {\n // Pure decimal: pad with leading zeros after decimal point\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n\n // Mixed: insert decimal point at new position\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n\n/**\n * Checks if a value is a plain object (not null, array, or other object types).\n *\n * @param {*} value Value to check\n * @returns {boolean} True if value is a plain object\n */\nfunction isPlainObject (value) {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n// ============================================================================\n// Language Converters\n// ============================================================================\n//\n// Each converter is created using makeConverter() with explicit type annotations.\n//\n// Pattern for languages WITHOUT options:\n// const LanguageConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Language))\n//\n// Pattern for languages WITH options:\n// const LanguageConverter = /** @type {(value: NumericValue, options?: LanguageOptions) => string} */ (makeConverter(Language))\n//\n// IMPORTANT: Keep converters alphabetically sorted by converter name.\n// ============================================================================\n\nconst ArabicConverter = /** @type {(value: NumericValue, options?: ArabicOptions) => string} */ (makeConverter(Arabic))\nconst AzerbaijaniConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Azerbaijani))\nconst BanglaConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Bangla))\nconst BiblicalHebrewConverter = /** @type {(value: NumericValue, options?: BiblicalHebrewOptions) => string} */ (makeConverter(BiblicalHebrew))\nconst CroatianConverter = /** @type {(value: NumericValue, options?: CroatianOptions) => string} */ (makeConverter(Croatian))\nconst CzechConverter = /** @type {(value: NumericValue, options?: CzechOptions) => string} */ (makeConverter(Czech))\nconst DanishConverter = /** @type {(value: NumericValue, options?: DanishOptions) => string} */ (makeConverter(Danish))\nconst DutchConverter = /** @type {(value: NumericValue, options?: DutchOptions) => string} */ (makeConverter(Dutch))\nconst EnglishConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(English))\nconst FilipinoConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Filipino))\nconst FrenchConverter = /** @type {(value: NumericValue, options?: FrenchOptions) => string} */ (makeConverter(French))\nconst FrenchBelgiumConverter = /** @type {(value: NumericValue, options?: FrenchBelgiumOptions) => string} */ (makeConverter(FrenchBelgium))\nconst GermanConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(German))\nconst GreekConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Greek))\nconst GujaratiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Gujarati))\nconst HebrewConverter = /** @type {(value: NumericValue, options?: HebrewOptions) => string} */ (makeConverter(Hebrew))\nconst HindiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Hindi))\nconst HungarianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Hungarian))\nconst IndonesianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Indonesian))\nconst ItalianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Italian))\nconst JapaneseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Japanese))\nconst KannadaConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Kannada))\nconst KoreanConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Korean))\nconst LatvianConverter = /** @type {(value: NumericValue, options?: LatvianOptions) => string} */ (makeConverter(Latvian))\nconst LithuanianConverter = /** @type {(value: NumericValue, options?: LithuanianOptions) => string} */ (makeConverter(Lithuanian))\nconst MalayConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Malay))\nconst MarathiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Marathi))\nconst NorwegianBokmalConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(NorwegianBokmal))\nconst PersianConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Persian))\nconst PolishConverter = /** @type {(value: NumericValue, options?: PolishOptions) => string} */ (makeConverter(Polish))\nconst PortugueseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Portuguese))\nconst PunjabiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Punjabi))\nconst RomanianConverter = /** @type {(value: NumericValue, options?: RomanianOptions) => string} */ (makeConverter(Romanian))\nconst RussianConverter = /** @type {(value: NumericValue, options?: RussianOptions) => string} */ (makeConverter(Russian))\nconst SerbianCyrillicConverter = /** @type {(value: NumericValue, options?: SerbianCyrillicOptions) => string} */ (makeConverter(SerbianCyrillic))\nconst SerbianLatinConverter = /** @type {(value: NumericValue, options?: SerbianLatinOptions) => string} */ (makeConverter(SerbianLatin))\nconst SimplifiedChineseConverter = /** @type {(value: NumericValue, options?: SimplifiedChineseOptions) => string} */ (makeConverter(SimplifiedChinese))\nconst SpanishConverter = /** @type {(value: NumericValue, options?: SpanishOptions) => string} */ (makeConverter(Spanish))\nconst SwahiliConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Swahili))\nconst SwedishConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Swedish))\nconst TamilConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Tamil))\nconst TeluguConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Telugu))\nconst ThaiConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Thai))\nconst TraditionalChineseConverter = /** @type {(value: NumericValue, options?: TraditionalChineseOptions) => string} */ (makeConverter(TraditionalChinese))\nconst TurkishConverter = /** @type {(value: NumericValue, options?: TurkishOptions) => string} */ (makeConverter(Turkish))\nconst UkrainianConverter = /** @type {(value: NumericValue, options?: UkrainianOptions) => string} */ (makeConverter(Ukrainian))\nconst UrduConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Urdu))\nconst VietnameseConverter = /** @type {(value: NumericValue) => string} */ (makeConverter(Vietnamese))\n\n// ============================================================================\n// Exports\n// ============================================================================\n//\n// All converter functions are exported for public use.\n//\n// IMPORTANT: Keep exports alphabetically sorted for maintainability.\n// ============================================================================\n\nexport {\n ArabicConverter,\n AzerbaijaniConverter,\n BanglaConverter,\n BiblicalHebrewConverter,\n CroatianConverter,\n CzechConverter,\n DanishConverter,\n DutchConverter,\n EnglishConverter,\n FilipinoConverter,\n FrenchConverter,\n FrenchBelgiumConverter,\n GermanConverter,\n GreekConverter,\n GujaratiConverter,\n HebrewConverter,\n HindiConverter,\n HungarianConverter,\n IndonesianConverter,\n ItalianConverter,\n JapaneseConverter,\n KannadaConverter,\n KoreanConverter,\n LatvianConverter,\n LithuanianConverter,\n MalayConverter,\n MarathiConverter,\n NorwegianBokmalConverter,\n PersianConverter,\n PolishConverter,\n PortugueseConverter,\n PunjabiConverter,\n RomanianConverter,\n RussianConverter,\n SerbianCyrillicConverter,\n SerbianLatinConverter,\n SimplifiedChineseConverter,\n SpanishConverter,\n SwahiliConverter,\n SwedishConverter,\n TamilConverter,\n TeluguConverter,\n ThaiConverter,\n TraditionalChineseConverter,\n TurkishConverter,\n UkrainianConverter,\n UrduConverter,\n VietnameseConverter\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Arabic language converter.\n *\n * Supports:\n * - Gender agreement (masculine/feminine forms)\n * - Complex pluralization (singular/dual/plural)\n * - Traditional Arabic number naming conventions\n * - Right-to-left text orientation\n */\nexport class Arabic extends AbstractLanguage {\n negativeWord = 'ناقص'\n decimalSeparatorWord = 'فاصلة'\n zeroWord = 'صفر'\n\n tensWords = ['عشرون', 'ثلاثون', 'أربعون', 'خمسون', 'ستون', 'سبعون', 'ثمانون', 'تسعون']\n hundredsWords = ['', 'مائة', 'مئتان', 'ثلاثمائة', 'أربعمائة', 'خمسمائة', 'ستمائة', 'سبعمائة', 'ثمانمائة', 'تسعمائة']\n\n // Magnitude words with three forms: singular, appended (tanween), plural\n scaleWords = ['مائة', 'ألف', 'مليون', 'مليار', 'تريليون', 'كوادريليون', 'كوينتليون', 'سكستيليون']\n scaleAppendedWords = ['', 'ألفاً', 'مليوناً', 'ملياراً', 'تريليوناً', 'كوادريليوناً', 'كوينتليوناً', 'سكستيليوناً']\n scalePluralWords = ['', 'آلاف', 'ملايين', 'مليارات', 'تريليونات', 'كوادريليونات', 'كوينتليونات', 'سكستيليونات']\n\n // Dual forms (Arabic has singular, dual, plural)\n dualWords = ['مئتان', 'ألفان', 'مليونان', 'ملياران', 'تريليونان', 'كوادريليونان', 'كوينتليونان', 'سكستيليونان']\n dualAppendedWords = ['مئتا', 'ألفا', 'مليونا', 'مليارا', 'تريليونا', 'كوادريليونا', 'كوينتليونا', 'سكستيليونا']\n\n // Gender-specific number words (1-19)\n ones = {\n masculine: [\n 'واحد', 'اثنان', 'ثلاثة', 'أربعة', 'خمسة', 'ستة', 'سبعة', 'ثمانية', 'تسعة', 'عشرة',\n 'أحد عشر', 'اثنا عشر', 'ثلاثة عشر', 'أربعة عشر', 'خمسة عشر', 'ستة عشر', 'سبعة عشر', 'ثمانية عشر', 'تسعة عشر'\n ],\n feminine: [\n 'واحدة', 'اثنتان', 'ثلاث', 'أربع', 'خمس', 'ست', 'سبع', 'ثمان', 'تسع', 'عشر',\n 'إحدى عشرة', 'اثنتا عشرة', 'ثلاث عشرة', 'أربع عشرة', 'خمس عشرة', 'ست عشرة', 'سبع عشرة', 'ثماني عشرة', 'تسع عشرة'\n ]\n }\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n gender: 'masculine'\n }, options)\n\n // Allow custom negativeWord via options\n if (options.negativeWord !== undefined) {\n this.negativeWord = options.negativeWord\n }\n }\n\n /** Selects masculine or feminine number forms based on options. */\n get selectedOnes () {\n return this.ones[this.options.gender === 'feminine' ? 'feminine' : 'masculine']\n }\n\n /** Converts a 3-digit segment (0-999) to Arabic words with gender/plural rules. */\n segmentToWords (groupNumber, groupLevel, fullNumber) {\n const tens = groupNumber % 100\n const hundredsRaw = groupNumber / 100\n const hundreds = Math.trunc(hundredsRaw)\n let returnValue = ''\n\n // Process hundreds\n if (hundreds > 0) {\n if (tens === 0 && hundreds === 2) {\n returnValue = this.dualWords[0]\n } else {\n const hundredsWord = this.hundredsWords[hundreds]\n if (hundredsWord) {\n returnValue = hundredsWord\n if (tens !== 0) {\n returnValue += ' و'\n }\n }\n }\n }\n\n // Process tens and ones\n if (tens > 0) {\n if (tens < 20) { // 1 -> 19\n if (tens === 2 && hundreds === 0 && groupLevel > 0) {\n // Cache expensive log10 calculation\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 returnValue = groupNumber === 2 ? this.dualWords[groupLevel] : this.dualAppendedWords[groupLevel]\n } else {\n returnValue = this.dualWords[groupLevel]\n }\n } else if (tens === 1 && groupLevel > 0) {\n returnValue += this.scaleWords[groupLevel]\n } else {\n returnValue += this.selectedOnes[tens - 1]\n }\n } else { // 20 -> 99\n const ones = tens % 10\n const tensIndex = Math.trunc(tens / 10) - 2\n\n if (ones > 0) {\n returnValue += this.selectedOnes[ones - 1]\n returnValue += ' و'\n }\n returnValue += this.tensWords[tensIndex]\n }\n }\n\n return returnValue\n }\n\n /** Converts integer part to Arabic words by processing 3-digit groups. */\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n let temp = integerPart\n let group = 0\n let result = ''\n\n // Process each group of 3 digits (right to left)\n while (temp > 0n) {\n const numberToProcess = Number(temp % 1000n)\n temp = temp / 1000n\n\n if (numberToProcess > 0) {\n const groupDescription = this.segmentToWords(numberToProcess, group, integerPart)\n\n if (groupDescription) {\n // Add group name for thousands, millions, etc.\n if (group > 0) {\n if (result) {\n result = ' و' + result\n }\n\n if (numberToProcess > 2) {\n const remainder = numberToProcess % 100\n if (remainder === 1) {\n result = this.scaleWords[group] + ' ' + result\n } else if (numberToProcess >= 3 && numberToProcess <= 10) {\n result = this.scalePluralWords[group] + ' ' + result\n } else {\n result = (result ? this.scaleAppendedWords[group] : this.scaleWords[group]) + ' ' + result\n }\n }\n }\n\n // Add group description (prepend for RTL)\n result = groupDescription + ' ' + result\n }\n }\n\n group++\n }\n\n return result.replace(/\\s+/g, ' ').trim()\n }\n}\n","import { TurkicLanguage } from '../classes/turkic-language.js'\n\n/**\n * Azerbaijani language converter.\n *\n * Supports:\n * - Implicit \"bir\" (one) omission before hundreds and thousands\n * - Turkic language number patterns\n * - Large numbers up to quintillion\n */\nexport class Azerbaijani extends TurkicLanguage {\n negativeWord = 'mənfi'\n decimalSeparatorWord = 'nöqtə'\n zeroWord = 'sıfır'\n\n scaleWords = [[1_000_000_000_000_000_000n, 'kentilyon'],\n [1_000_000_000_000_000n, 'katrilyon'],\n [1_000_000_000_000n, 'trilyon'],\n [1_000_000_000n, 'milyar'],\n [1_000_000n, 'milyon'],\n [1000n, 'min'],\n [100n, 'yüz'],\n [90n, 'doxsan'],\n [80n, 'səksən'],\n [70n, 'yetmiş'],\n [60n, 'altmış'],\n [50n, 'əlli'],\n [40n, 'qırx'],\n [30n, 'otuz'],\n [20n, 'iyirmi'],\n [10n, 'on'],\n [9n, 'doqquz'],\n [8n, 'səkkiz'],\n [7n, 'yeddi'],\n [6n, 'altı'],\n [5n, 'beş'],\n [4n, 'dörd'],\n [3n, 'üç'],\n [2n, 'iki'],\n [1n, 'bir'],\n [0n, 'sıfır']]\n}\n","import { SouthAsianLanguage } from '../classes/south-asian-language.js'\n\n/**\n * Bangla language converter.\n *\n * Supports:\n * - Indian numbering system (হাজার, লাখ, কোটি)\n * - Bangla script (Bengali)\n * - Complete word forms for 0-99\n */\nexport class Bangla extends SouthAsianLanguage {\n negativeWord = 'মাইনাস'\n decimalSeparatorWord = 'দশমিক'\n zeroWord = 'শূন্য'\n hundredWord = 'শত'\n\n belowHundredWords = [\n 'শূন্য',\n 'এক',\n 'দুই',\n 'তিন',\n 'চার',\n 'পাঁচ',\n 'ছয়',\n 'সাত',\n 'আট',\n 'নয়',\n 'দশ',\n 'এগারো',\n 'বারো',\n 'তেরো',\n 'চৌদ্দ',\n 'পনেরো',\n 'ষোল',\n 'সতেরো',\n 'আঠারো',\n 'উনিশ',\n 'বিশ',\n 'একুশ',\n 'বাইশ',\n 'তেইশ',\n 'চব্বিশ',\n 'পঁচিশ',\n 'ছাব্বিশ',\n 'সাতাশ',\n 'আঠাশ',\n 'উনত্রিশ',\n 'ত্রিশ',\n 'একত্রিশ',\n 'বত্রিশ',\n 'তেত্রিশ',\n 'চৌত্রিশ',\n 'পঁয়ত্রিশ',\n 'ছত্রিশ',\n 'সাঁইত্রিশ',\n 'আটত্রিশ',\n 'উনচল্লিশ',\n 'চল্লিশ',\n 'একচল্লিশ',\n 'বেয়াল্লিশ',\n 'তেতাল্লিশ',\n 'চুয়াল্লিশ',\n 'পঁয়তাল্লিশ',\n 'ছেচল্লিশ',\n 'সাতচল্লিশ',\n 'আটচল্লিশ',\n 'উনপঞ্চাশ',\n 'পঞ্চাশ',\n 'একান্ন',\n 'বাহান্ন',\n 'তিপ্পান্ন',\n 'চুয়ান্ন',\n 'পঞ্চান্ন',\n 'ছাপ্পান্ন',\n 'সাতান্ন',\n 'আটান্ন',\n 'উনষাট',\n 'ষাট',\n 'একষট্টি',\n 'বাষট্টি',\n 'তেষট্টি',\n 'চৌষট্টি',\n 'পঁয়ষট্টি',\n 'ছেষট্টি',\n 'সাতষট্টি',\n 'আটষট্টি',\n 'ঊনসত্তর',\n 'সত্তর',\n 'একাত্তর',\n 'বাহাত্তর',\n 'তেহাত্তর',\n 'চুয়াত্তর',\n 'পঁচাত্তর',\n 'ছিয়াত্তর',\n 'সাতাত্তর',\n 'আটাত্তর',\n 'উনআশি',\n 'আশি',\n 'একাশি',\n 'বিরাশি',\n 'তিরাশি',\n 'চুরাশি',\n 'পঁচাশি',\n 'ছিয়াশি',\n 'সাতাশি',\n 'আটাশি',\n 'উননব্বই',\n 'নব্বই',\n 'একানব্বই',\n 'বিরানব্বই',\n 'তিরানব্বই',\n 'চুরানব্বই',\n 'পঁচানব্বই',\n 'ছিয়ানব্বই',\n 'সাতানব্বই',\n 'আটানব্বই',\n 'নিরানব্বই'\n ]\n\n scaleWords = [\n '',\n 'হাজার',\n 'লাখ',\n 'কোটি',\n 'আরব',\n 'খরব',\n 'নীল',\n 'পদ্ম',\n 'শঙ্খ'\n ]\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Biblical Hebrew language converter.\n *\n * Supports:\n * - Three-form pluralization (one/few/many)\n * - Gender agreement (masculine/feminine)\n * - Optional \"ו\" (and) conjunction control\n */\nexport class BiblicalHebrew extends SlavicLanguage {\n negativeWord = 'מינוס'\n decimalSeparatorWord = 'נקודה'\n zeroWord = 'אפס'\n usePerDigitDecimals = true\n\n // Biblical Hebrew (masculine forms as default - historically accurate)\n onesWords = {\n 1: 'אחד',\n 2: 'שניים',\n 3: 'שלשה',\n 4: 'ארבעה',\n 5: 'חמשה',\n 6: 'ששה',\n 7: 'שבעה',\n 8: 'שמונה',\n 9: 'תשעה'\n }\n\n teensWords = {\n 0: 'עשרה',\n 1: 'אחד עשר',\n 2: 'שנים עשר',\n 3: 'שלשה עשר',\n 4: 'ארבעה עשר',\n 5: 'חמשה עשר',\n 6: 'ששה עשר',\n 7: 'שבעה עשר',\n 8: 'שמונה עשר',\n 9: 'תשעה עשר'\n }\n\n twentiesWords = {\n 2: 'עשרים',\n 3: 'שלשים',\n 4: 'ארבעים',\n 5: 'חמישים',\n 6: 'ששים',\n 7: 'שבעים',\n 8: 'שמונים',\n 9: 'תשעים'\n }\n\n hundredsWords = {\n 1: 'מאה',\n 2: 'מאתיים',\n 3: 'מאות'\n }\n\n pluralForms = {\n 1: 'אלף',\n 2: 'אלפיים',\n 3: 'שלשה אלפים',\n 4: 'ארבעה אלפים',\n 5: 'חמשה אלפים',\n 6: 'ששה אלפים',\n 7: 'שבעה אלפים',\n 8: 'שמונה אלפים',\n 9: 'תשעה אלפים'\n }\n\n // Feminine forms for thousands (when feminine=true is specified)\n femininePluralForms = {\n 1: 'אלף',\n 2: 'אלפיים',\n 3: 'שלשת אלפים',\n 4: 'ארבעת אלפים',\n 5: 'חמשת אלפים',\n 6: 'ששת אלפים',\n 7: 'שבעת אלפים',\n 8: 'שמונת אלפים',\n 9: 'תשעת אלפים'\n }\n\n scale = {\n 1: 'אלף', // thousand (singular)\n 2: 'מיליון', // million\n 3: 'מיליארד', // billion\n 4: 'טריליון', // trillion\n 5: 'קוודרליון', // quadrillion\n 6: 'קווינטיליון' // quintillion\n }\n\n scalePlural = {\n 1: 'אלפים', // thousands\n 2: 'מיליונים', // millions\n 3: 'מיליארדים', // billions\n 4: 'טריליונים', // trillions\n 5: 'קוודרליונים', // quadrillions\n 6: 'קווינטיליונים' // quintillions\n }\n\n // Feminine forms (for when gender='feminine' is specified)\n onesFeminineWords = {\n 1: 'אחת',\n 2: 'שתים',\n 3: 'שלש',\n 4: 'ארבע',\n 5: 'חמש',\n 6: 'שש',\n 7: 'שבע',\n 8: 'שמונה',\n 9: 'תשע'\n }\n\n teensFeminineWords = {\n 0: 'עשר',\n 1: 'אחת עשרה',\n 2: 'שתים עשרה',\n 3: 'שלש עשרה',\n 4: 'ארבע עשרה',\n 5: 'חמש עשרה',\n 6: 'שש עשרה',\n 7: 'שבע עשרה',\n 8: 'שמונה עשרה',\n 9: 'תשע עשרה'\n }\n\n constructor (options = {}) {\n super(options)\n\n this.setOptions({\n andWord: 'ו',\n gender: 'masculine'\n }, options)\n\n if (this.options.gender === 'feminine') {\n this.onesWords = this.onesFeminineWords\n this.teensWords = this.teensFeminineWords\n this.pluralForms = this.femininePluralForms\n }\n }\n\n /** Converts integer part with Biblical Hebrew three-form pluralization. */\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n const words = []\n const segments = this.splitToSegments(integerPart.toString(), 3)\n let index = segments.length\n for (const x of segments) {\n index = index - 1\n if (x === 0n) {\n continue\n }\n\n const [n1, n2, n3] = this.extractDigits(x)\n\n if (index > 0) {\n // For thousands and above, handle the full chunk value\n const chunkWords = []\n let hasHundreds = false\n\n // Process hundreds in this chunk\n if (n3 > 0n) {\n hasHundreds = true\n if (n3 <= 2n) {\n chunkWords.push(this.hundredsWords[n3])\n } else {\n chunkWords.push(this.onesWords[n3] + ' ' + this.hundredsWords[3])\n }\n }\n\n // Process tens in this chunk\n if (n2 > 1n) {\n // Add conjunction if there were hundreds before\n const tensWord = this.twentiesWords[n2]\n chunkWords.push(hasHundreds ? this.options.andWord + tensWord : tensWord)\n }\n\n // Process ones in this chunk\n if (n2 === 1n) {\n // Add conjunction if there were hundreds before\n const onesWord = this.teensWords[n1]\n chunkWords.push(hasHundreds ? this.options.andWord + onesWord : onesWord)\n } else if (n1 > 0n) {\n // For \"one million\", \"one billion\", etc., don't add the word \"one\"\n if (x === 1n && index > 1) {\n // Skip adding \"one\" for millions/billions/etc.\n } else if (x <= 9n && chunkWords.length === 0 && index === 1) {\n // Use special forms for 1-9 thousand\n chunkWords.push(this.pluralForms[n1])\n } else {\n const onesWord = this.onesWords[n1]\n // Add conjunction if there were hundreds or tens before\n chunkWords.push((hasHundreds || n2 > 0n) ? this.options.andWord + onesWord : onesWord)\n }\n }\n\n // Add scale word (thousand, million, billion, etc.)\n if (x > 9n || index > 1) {\n // For numbers > 9 or higher scales, use appropriate scale word\n if (x === 1n) {\n // Singular form (e.g., \"one thousand\", \"one million\")\n chunkWords.push(this.scale[index])\n } else if (x === 2n && index === 1) {\n // Special dual form for \"two thousand\" (already in thousands[2])\n return [this.pluralForms[2], ...words].join(' ')\n } else if (x === 2n) {\n // For two million, two billion, etc. - use plural form\n chunkWords.push(this.scalePlural[index])\n } else if (index === 1) {\n // For thousands (10+), always use singular \"אלף\"\n chunkWords.push(this.scale[index])\n } else {\n // For millions/billions/etc. use plural form\n chunkWords.push(this.scalePlural[index])\n }\n }\n\n words.push(chunkWords.join(' '))\n continue\n }\n\n if (n3 > 0n) {\n if (n3 <= 2n) {\n words.push(this.hundredsWords[n3])\n } else {\n words.push(this.onesWords[n3] + ' ' + this.hundredsWords[3])\n }\n }\n\n if (n2 > 1n) {\n words.push(this.twentiesWords[n2])\n }\n\n if (n2 === 1n) {\n words.push(this.teensWords[n1])\n } else if (n1 > 0n) {\n words.push(this.onesWords[n1])\n }\n }\n\n if (words.length > 1) {\n words[words.length - 1] = this.options.andWord + words.at(-1)\n }\n\n return words.join(' ')\n }\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Croatian language converter.\n *\n * Supports:\n * - Three-form pluralization (one/few/many)\n * - Gender agreement (jedan/jedna, dva/dvije)\n * - Croatian-specific declension endings\n */\nexport class Croatian extends SlavicLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'zarez'\n zeroWord = 'nula'\n\n onesWords = {\n 1: 'jedan',\n 2: 'dva',\n 3: 'tri',\n 4: 'četiri',\n 5: 'pet',\n 6: 'šest',\n 7: 'sedam',\n 8: 'osam',\n 9: 'devet'\n }\n\n onesFeminineWords = {\n 1: 'jedna',\n 2: 'dvije',\n 3: 'tri',\n 4: 'četiri',\n 5: 'pet',\n 6: 'šest',\n 7: 'sedam',\n 8: 'osam',\n 9: 'devet'\n }\n\n teensWords = {\n 0: 'deset',\n 1: 'jedanaest',\n 2: 'dvanaest',\n 3: 'trinaest',\n 4: 'četrnaest',\n 5: 'petnaest',\n 6: 'šesnaest',\n 7: 'sedamnaest',\n 8: 'osamnaest',\n 9: 'devetnaest'\n }\n\n twentiesWords = {\n 2: 'dvadeset',\n 3: 'trideset',\n 4: 'četrdeset',\n 5: 'pedeset',\n 6: 'šezdeset',\n 7: 'sedamdeset',\n 8: 'osamdeset',\n 9: 'devedeset'\n }\n\n hundredsWords = {\n 1: 'sto',\n 2: 'dvjesto',\n 3: 'tristo',\n 4: 'četiristo',\n 5: 'petsto',\n 6: 'šesto',\n 7: 'sedamsto',\n 8: 'osamsto',\n 9: 'devetsto'\n }\n\n pluralForms = {\n 1: ['tisuća', 'tisuće', 'tisuća'], // 10 ^ 3\n 2: ['milijun', 'milijuna', 'milijuna'], // 10 ^ 6\n 3: ['milijarda', 'milijarde', 'milijarda'], // 10 ^ 9\n 4: ['bilijun', 'bilijuna', 'bilijuna'], // 10 ^ 12\n 5: ['bilijarda', 'bilijarde', 'bilijarda'], // 10 ^ 15\n 6: ['trilijun', 'trilijuna', 'trilijuna'], // 10 ^ 18\n 7: ['trilijarda', 'trilijarde', 'trilijarda'], // 10 ^ 21\n 8: ['kvadrilijun', 'kvadrilijuna', 'kvadrilijuna'], // 10 ^ 24\n 9: ['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda'], // 10 ^ 27\n 10: ['kvintilijun', 'kvintilijuna', 'kvintilijuna'] // 10 ^ 30\n }\n\n /**\n * Maps segment indices to whether they are grammatically feminine.\n * In Croatian, thousands (index 1) are feminine, others are masculine.\n * @type {Object.<number, boolean>}\n */\n scaleGenders = {\n 1: true // thousands are feminine (others default to false)\n }\n\n /** Selects Croatian plural form: 1 = singular, 2-4 = few, else = many. */\n pluralize (n, forms) {\n const lastDigit = n % 10n\n const lastTwoDigits = n % 100n\n\n if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit === 1n) {\n return forms[0]\n }\n\n if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit > 1n && lastDigit < 5n) {\n return forms[1]\n }\n\n return forms[2]\n }\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Czech language converter.\n *\n * Supports:\n * - Three-form pluralization (one/few/many)\n * - Dynamic decimal separator (celá/celé/celých)\n * - Gender agreement in number words\n */\nexport class Czech extends SlavicLanguage {\n negativeWord = 'mínus'\n zeroWord = 'nula'\n\n onesWords = {\n 1: 'jedna',\n 2: 'dva',\n 3: 'tři',\n 4: 'čtyři',\n 5: 'pět',\n 6: 'šest',\n 7: 'sedm',\n 8: 'osm',\n 9: 'devět'\n }\n\n onesFeminineWords = {\n 1: 'jedna',\n 2: 'dvě',\n 3: 'tři',\n 4: 'čtyři',\n 5: 'pět',\n 6: 'šest',\n 7: 'sedm',\n 8: 'osm',\n 9: 'devět'\n }\n\n teensWords = {\n 0: 'deset',\n 1: 'jedenáct',\n 2: 'dvanáct',\n 3: 'třináct',\n 4: 'čtrnáct',\n 5: 'patnáct',\n 6: 'šestnáct',\n 7: 'sedmnáct',\n 8: 'osmnáct',\n 9: 'devatenáct'\n }\n\n twentiesWords = {\n 2: 'dvacet',\n 3: 'třicet',\n 4: 'čtyřicet',\n 5: 'padesát',\n 6: 'šedesát',\n 7: 'sedmdesát',\n 8: 'osmdesát',\n 9: 'devadesát'\n }\n\n hundredsWords = {\n 1: 'sto',\n 2: 'dvě stě',\n 3: 'tři sta',\n 4: 'čtyři sta',\n 5: 'pět set',\n 6: 'šest set',\n 7: 'sedm set',\n 8: 'osm set',\n 9: 'devět set'\n }\n\n pluralForms = {\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 10: ['quintillion', 'quintilliony', 'quintillionů'] // 10^ 30\n }\n\n /**\n * Czech omits \"one\" before scale words.\n * e.g., 1000 is \"tisíc\" not \"jedna tisíc\"\n */\n omitOneBeforeScale = true\n\n /**\n * Cached integer part for decimal separator word selection.\n * Set by toWords() before calling super.toWords().\n * @private\n */\n #integerPart = 0n\n\n /** Returns decimal separator word based on integer part (celá/celé/celých). */\n get decimalSeparatorWord () {\n if (this.#integerPart === 0n || this.#integerPart === 1n) {\n return 'celá'\n } else if (this.#integerPart >= 2n && this.#integerPart <= 4n) {\n return 'celé'\n } else {\n return 'celých'\n }\n }\n\n constructor (options = {}) {\n super(options)\n\n // Remove the inherited decimalSeparatorWord property so our getter works\n delete this.decimalSeparatorWord\n }\n\n /**\n * Override toWords to cache integer part before decimal separator is accessed.\n */\n toWords (isNegative, integerPart, decimalPart) {\n this.#integerPart = integerPart\n return super.toWords(isNegative, integerPart, decimalPart)\n }\n\n /** Selects Czech plural form: 1 = singular, 2-4 = few, else = many. */\n pluralize (n, forms) {\n if (n === 1n) {\n return forms[0]\n }\n\n // Check if n is in the \"few\" form range (2-4, 22-24, 32-34, etc., excluding teens)\n const lastDigit = n % 10n\n const lastTwoDigits = n % 100n\n\n if (lastDigit > 1n && lastDigit < 5n && (lastTwoDigits < 10n || lastTwoDigits > 20n)) {\n return forms[1]\n }\n\n return forms[2]\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Danish language converter.\n *\n * Supports:\n * - Vigesimal (base-20) number system\n * - Units-before-tens ordering (e.g., \"tre-og-tyve\" = 23)\n * - Optional ordinal numbers via ordFlag option\n */\nexport class Danish extends GreedyScaleLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'komma'\n zeroWord = 'nul'\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000_000n, 'quadrillarder'],\n [1_000_000_000_000_000_000_000_000n, 'quadrillioner'],\n [1_000_000_000_000_000_000_000n, 'trillarder'],\n [1_000_000_000_000_000_000n, 'trillioner'],\n [1_000_000_000_000_000n, 'billarder'],\n [1_000_000_000_000n, 'billioner'],\n [1_000_000_000n, 'millarder'],\n [1_000_000n, 'millioner'],\n [1000n, 'tusind'],\n [100n, 'hundrede'],\n [90n, 'halvfems'],\n [80n, 'firs'],\n [70n, 'halvfjerds'],\n [60n, 'treds'],\n [50n, 'halvtreds'],\n [40n, 'fyrre'],\n [30n, 'tredive'],\n [20n, 'tyve'],\n [19n, 'nitten'],\n [18n, 'atten'],\n [17n, 'sytten'],\n [16n, 'seksten'],\n [15n, 'femten'],\n [14n, 'fjorten'],\n [13n, 'tretten'],\n [12n, 'tolv'],\n [11n, 'elleve'],\n [10n, 'ti'],\n [9n, 'ni'],\n [8n, 'otte'],\n [7n, 'syv'],\n [6n, 'seks'],\n [5n, 'fem'],\n [4n, 'fire'],\n [3n, 'tre'],\n [2n, 'to'],\n [1n, 'et'],\n [0n, 'nul']\n ]\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n ordFlag: false\n }, options)\n }\n\n /** Combines two word-sets with Danish vigesimal and reversal rules. */\n combineWordSets (preceding, following) {\n let precedingWord = Object.keys(preceding)[0]\n let followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0] // BigInt (e.g., 1n, 100n, 1000n)\n const followingValue = Object.values(following)[0] // BigInt (e.g., magnitude level like 100n, 1000n)\n\n // Prepend \"et\" to hundreds and thousands (not \"en\") for proper Danish form\n if (followingValue === 100n || followingValue === 1000n) {\n following = { [`et${followingWord}`]: followingValue }\n }\n\n // Implicit '1' handling: omit '1' before most magnitudes (except millions/ordinals)\n if (precedingValue === 1n) {\n if (followingValue < 1_000_000n || this.options.ordFlag) {\n return following // Just the magnitude word (e.g., \"hundrede\" not \"en hundrede\")\n }\n precedingWord = 'en' // Explicit \"en\" (one) for millions and above\n }\n\n // Multiplication across magnitude boundaries\n if (followingValue > precedingValue) {\n // Space for million+ (e.g., \"en million\", \"to millioner\")\n if (followingValue >= 1_000_000n) {\n precedingWord += ' '\n }\n return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }\n }\n\n // Addition with separator rules:\n // \"og\" (and) for hundreds + smaller numbers\n if (precedingValue >= 100n && precedingValue < 1000n) {\n precedingWord += ' og '\n } else if (precedingValue >= 1000n && precedingValue <= 100_000n) {\n // Special \"e og\" for thousands (e.g., \"tusinde og tyve\")\n precedingWord += 'e og '\n }\n\n // Units-before-tens reversal (Danish vigesimal pattern):\n // For small units (< 10) with tens (10-99), swap order: \"tre og tyve\" (25)\n if (followingValue < 10n && precedingValue > 10n && precedingValue < 100n) {\n if (followingValue === 1n) {\n followingWord = 'en' // Convert 1 to \"en\" for vigesimal context\n }\n // Swap positions: units go after \"og\", tens go before\n const temporary = followingWord\n followingWord = precedingWord\n precedingWord = temporary + 'og'\n } else if (precedingValue >= 1_000_000n) {\n // Space for large magnitudes (millions+)\n precedingWord += ' '\n }\n\n return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Dutch language converter.\n *\n * Supports:\n * - Optional \"en\" (and) separator for tens\n * - Optional comma before hundreds\n * - Compound word formation without hyphenation\n */\nexport class Dutch extends GreedyScaleLanguage {\n negativeWord = 'min'\n decimalSeparatorWord = 'komma'\n zeroWord = 'nul'\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000_000n, 'quadriljard'],\n [1_000_000_000_000_000_000_000_000n, 'quadriljoen'],\n [1_000_000_000_000_000_000_000n, 'triljard'],\n [1_000_000_000_000_000_000n, 'triljoen'],\n [1_000_000_000_000_000n, 'biljard'],\n [1_000_000_000_000n, 'biljoen'],\n [1_000_000_000n, 'miljard'],\n [1_000_000n, 'miljoen'],\n [1000n, 'duizend'],\n [100n, 'honderd'],\n [90n, 'negentig'],\n [80n, 'tachtig'],\n [70n, 'zeventig'],\n [60n, 'zestig'],\n [50n, 'vijftig'],\n [40n, 'veertig'],\n [30n, 'dertig'],\n [20n, 'twintig'],\n [19n, 'negentien'],\n [18n, 'achttien'],\n [17n, 'zeventien'],\n [16n, 'zestien'],\n [15n, 'vijftien'],\n [14n, 'veertien'],\n [13n, 'dertien'],\n [12n, 'twaalf'],\n [11n, 'elf'],\n [10n, 'tien'],\n [9n, 'negen'],\n [8n, 'acht'],\n [7n, 'zeven'],\n [6n, 'zes'],\n [5n, 'vijf'],\n [4n, 'vier'],\n [3n, 'drie'],\n [2n, 'twee'],\n [1n, 'één'],\n [0n, 'nul']\n ]\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n includeOptionalAnd: false,\n noHundredPairing: false,\n accentOne: true\n }, options)\n\n if (!this.options.accentOne) {\n this.scaleWords[this.scaleWords.length - 2][1] = 'een'\n }\n }\n\n /** Combines two word-sets according to Dutch grammar rules. */\n combineWordSets (preceding, following) {\n let precedingWord = Object.keys(preceding)[0]\n let followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0] // BigInt\n const followingValue = Object.values(following)[0] // BigInt\n\n // Implicit \"een\": omit before large multipliers (\"miljoen\" not \"een miljoen\")\n if (precedingValue === 1n) {\n if (followingValue < 1_000_000n) {\n return following\n }\n precedingWord = this.options.accentOne ? 'één' : 'een'\n }\n\n // Handle multiplication and spacing\n if (followingValue > precedingValue) {\n let hadSpace = false\n // Large scale words (millions+): add space before multiplier\n if (followingValue >= 1_000_000n) {\n precedingWord += ' '\n hadSpace = true\n } else if (followingValue > 100n) {\n // Hundreds and above: add space after multiplier\n followingWord += ' '\n hadSpace = true\n }\n // Convert 'één' to 'een' in compounds (when no space or accentOne disabled)\n if (!hadSpace || !this.options.accentOne) {\n precedingWord = precedingWord.replace(/één/g, 'een')\n followingWord = followingWord.replace(/één/g, 'een')\n }\n return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }\n }\n\n // Track if we're adding a space (which keeps words separate)\n let hasSpace = false\n\n if (followingValue < 10n && precedingValue > 10n && precedingValue < 100n) {\n const temporary = followingWord\n followingWord = precedingWord\n const andTxt = temporary.endsWith('e') ? 'ën' : 'en'\n precedingWord = `${temporary}${andTxt}`\n } else if (followingValue < 13n && precedingValue < 1000n && this.options.includeOptionalAnd) {\n precedingWord = `${precedingWord}en`\n } else if (followingValue < 13n && precedingValue >= 1000n && this.options.includeOptionalAnd) {\n followingWord = ` en ${followingWord}`\n hasSpace = true\n } else if (precedingValue >= 1_000_000n) {\n precedingWord += ' '\n hasSpace = true\n } else if (precedingValue === 1000n) {\n precedingWord += ' '\n hasSpace = true\n }\n\n // Convert 'één' to 'een' in direct compounds (no space)\n // Keep 'één' only if there's a space AND accentOne=true\n if (!hasSpace) {\n precedingWord = precedingWord.replace(/één/g, 'een')\n followingWord = followingWord.replace(/één/g, 'een')\n } else if (!this.options.accentOne) {\n precedingWord = precedingWord.replace(/één/g, 'een')\n followingWord = followingWord.replace(/één/g, 'een')\n }\n\n return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }\n }\n\n integerToWords (integerPart) {\n if (integerPart >= 1100n && integerPart < 10_000n && !this.options.noHundredPairing) {\n const high = integerPart / 100n\n const low = integerPart % 100n\n if (high % 10n !== 0n) {\n let result = super.integerToWords(high) + 'honderd'\n if (low) {\n result +=\n (this.options.includeOptionalAnd ? ' en ' : ' ') + super.integerToWords(low)\n }\n return result\n }\n }\n return super.integerToWords(integerPart)\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * English language converter.\n *\n * Supports:\n * - Hyphenated compounds (e.g., \"twenty-three\")\n * - \"and\" after hundreds (e.g., \"one hundred and one\")\n * - Numbers up to octillions\n */\nexport class English extends GreedyScaleLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'point'\n zeroWord = 'zero'\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000_000n, 'octillion'],\n [1_000_000_000_000_000_000_000_000n, 'septillion'],\n [1_000_000_000_000_000_000_000n, 'sextillion'],\n [1_000_000_000_000_000_000n, 'quintillion'],\n [1_000_000_000_000_000n, 'quadrillion'],\n [1_000_000_000_000n, 'trillion'],\n [1_000_000_000n, 'billion'],\n [1_000_000n, 'million'],\n [1000n, 'thousand'],\n [100n, 'hundred'],\n [90n, 'ninety'],\n [80n, 'eighty'],\n [70n, 'seventy'],\n [60n, 'sixty'],\n [50n, 'fifty'],\n [40n, 'forty'],\n [30n, 'thirty'],\n [20n, 'twenty'],\n [19n, 'nineteen'],\n [18n, 'eighteen'],\n [17n, 'seventeen'],\n [16n, 'sixteen'],\n [15n, 'fifteen'],\n [14n, 'fourteen'],\n [13n, 'thirteen'],\n [12n, 'twelve'],\n [11n, 'eleven'],\n [10n, 'ten'],\n [9n, 'nine'],\n [8n, 'eight'],\n [7n, 'seven'],\n [6n, 'six'],\n [5n, 'five'],\n [4n, 'four'],\n [3n, 'three'],\n [2n, 'two'],\n [1n, 'one'],\n [0n, 'zero']\n ]\n\n /** Combines two word-sets with English hyphenation and \"and\" rules. */\n combineWordSets (preceding, following) {\n const precedingWord = Object.keys(preceding)[0]\n const precedingValue = Object.values(preceding)[0]\n const followingWord = Object.keys(following)[0]\n const followingValue = Object.values(following)[0]\n\n // Rule 1: Implicit \"one\" - omit when multiplying (\"one hundred\" → \"hundred\")\n if (precedingValue === 1n && followingValue < 100n) {\n return { [followingWord]: followingValue }\n }\n\n // Rule 2: Hyphenate compounds under 100 (\"twenty-three\")\n if (precedingValue < 100n && precedingValue > followingValue) {\n return { [`${precedingWord}-${followingWord}`]: precedingValue + followingValue }\n }\n\n // Rule 3: Add \"and\" before units after hundreds (\"one hundred and one\")\n if (precedingValue >= 100n && followingValue < 100n) {\n return { [`${precedingWord} and ${followingWord}`]: precedingValue + followingValue }\n }\n\n // Rule 4: Multiply when following > preceding (\"one thousand\")\n if (followingValue > precedingValue) {\n return { [`${precedingWord} ${followingWord}`]: precedingValue * followingValue }\n }\n\n return { [`${precedingWord} ${followingWord}`]: precedingValue + followingValue }\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Filipino language converter.\n *\n * Supports:\n * - \"ng\" connectors between words\n * - Implicit \"one\" omission\n * - Special linkers for certain tens (e.g., \"limampung\")\n */\nexport class Filipino extends GreedyScaleLanguage {\n negativeWord = 'negatibo'\n decimalSeparatorWord = 'punto'\n zeroWord = 'zero'\n usePerDigitDecimals = true // Read decimals digit-by-digit\n\n scaleWords = [\n [1000000000000n, 'trilyong'],\n [1000000000n, 'milyong'],\n [1000000n, 'milyong'],\n [1000n, 'libong'],\n [100n, 'daang'],\n\n // Tens\n [90n, 'siyamnapu'],\n [80n, 'walumpu'],\n [70n, 'pitumpu'],\n [60n, 'animnapu'],\n [50n, 'limampu'],\n [40n, 'apatnapu'],\n [30n, 'tatlumpu'],\n [20n, 'dalawampu'],\n\n // Teens (must come before 10 to be matched first)\n [19n, 'labinsiyam'],\n [18n, 'labingwalo'],\n [17n, 'labimpito'],\n [16n, 'labinanum'],\n [15n, 'labinlima'],\n [14n, 'labinapat'],\n [13n, 'labintatlo'],\n [12n, 'labindalawa'],\n [11n, 'labinisa'],\n [10n, 'sampu'],\n\n // Ones\n [9n, 'siyam'],\n [8n, 'walo'],\n [7n, 'pito'],\n [6n, 'anim'],\n [5n, 'lima'],\n [4n, 'apat'],\n [3n, 'tatlo'],\n [2n, 'dalawa'],\n [1n, 'isa'],\n [0n, 'zero']\n ]\n\n /** Converts integer part with explicit zero handling. */\n integerToWords (integerPart) {\n // Handle zero explicitly\n if (integerPart === 0n) {\n return this.zeroWord\n }\n return super.integerToWords(integerPart)\n }\n\n /** Combines two word-sets with Filipino connector and linker rules. */\n combineWordSets (preceding, following) {\n const precedingWord = Object.keys(preceding)[0]\n const followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0]\n const followingValue = Object.values(following)[0]\n\n // Don't merge zero with anything - just return the non-zero value\n if (precedingValue === 0n) {\n return following\n }\n if (followingValue === 0n) {\n return preceding\n }\n\n // Implicit \"one\" - omit when adding with values < 100\n if (precedingValue === 1n && followingValue < 100n) {\n return following\n }\n\n // Multiply when following is a scale word AND following > preceding\n // Use \"ng\" connector for Filipino, but consonant-ending words use \" na \"\n if (followingValue > precedingValue && followingValue >= 100n) {\n // Words ending in consonants (not vowels) use \" na \" instead of \"ng\"\n const vowels = ['a', 'e', 'i', 'o', 'u']\n const lastChar = precedingWord[precedingWord.length - 1]\n if (!vowels.includes(lastChar)) {\n return {\n [`${precedingWord} na ${followingWord}`]: precedingValue * followingValue\n }\n }\n // Vowel-ending words add \"ng\"\n return {\n [`${precedingWord}ng ${followingWord}`]: precedingValue * followingValue\n }\n }\n\n // Special Filipino rule: certain tens words take \"-ng\" linker when followed by ones\n // Only limampu (50) confirmed to use this pattern\n if (precedingValue >= 10n && precedingValue < 100n && followingValue >= 1n && followingValue < 10n) {\n const tensWithNg = ['limampu']\n if (tensWithNg.includes(precedingWord)) {\n return {\n [`${precedingWord}ng ${followingWord}`]: precedingValue + followingValue\n }\n }\n }\n\n // Default: space for addition\n return {\n [`${precedingWord} ${followingWord}`]: precedingValue + followingValue\n }\n }\n}\n","import { French } from './fr.js'\n\n/**\n * French (Belgium) language converter.\n *\n * Supports:\n * - Belgian regional variants: \"septante\" (70) and \"nonante\" (90)\n * - Simplified tens naming (no complex arithmetic)\n * - Inherits all other French grammar rules\n */\nexport class FrenchBelgium extends French {\n constructor (options = {}) {\n super(options)\n\n // Insert 90n ('nonante') after 80n and 70n ('septante') after 60n\n const tuples = [...this.scaleWords]\n // Find index of 80n and insert 90n after it\n const idx80 = tuples.findIndex(tuple => tuple[0] === 80n)\n if (idx80 !== -1) tuples.splice(idx80, 0, [90n, 'nonante'])\n // Find index of 60n and insert 70n after it\n const idx60 = tuples.findIndex(tuple => tuple[0] === 60n)\n if (idx60 !== -1) tuples.splice(idx60, 0, [70n, 'septante'])\n this.scaleWords = tuples\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * German language converter.\n *\n * Supports:\n * - Compound formation (no separators between words)\n * - Three forms of 1 (eins/ein/eine)\n * - Units-before-tens ordering (einundzwanzig = one and twenty)\n */\nexport class German extends GreedyScaleLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'komma'\n zeroWord = 'null'\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000_000n, 'Quadrilliarde'],\n [1_000_000_000_000_000_000_000_000n, 'Quadrillion'],\n [1_000_000_000_000_000_000_000n, 'Trilliarde'],\n [1_000_000_000_000_000_000n, 'Trillion'],\n [1_000_000_000_000_000n, 'Billiarde'],\n [1_000_000_000_000n, 'Billion'],\n [1_000_000_000n, 'Milliarde'],\n [1_000_000n, 'Million'],\n [1000n, 'tausend'],\n [100n, 'hundert'],\n [90n, 'neunzig'],\n [80n, 'achtzig'],\n [70n, 'siebzig'],\n [60n, 'sechzig'],\n [50n, 'fünfzig'],\n [40n, 'vierzig'],\n [30n, 'dreißig'],\n [20n, 'zwanzig'],\n [19n, 'neunzehn'],\n [18n, 'achtzehn'],\n [17n, 'siebzehn'],\n [16n, 'sechzehn'],\n [15n, 'fünfzehn'],\n [14n, 'vierzehn'],\n [13n, 'dreizehn'],\n [12n, 'zwölf'],\n [11n, 'elf'],\n [10n, 'zehn'],\n [9n, 'neun'],\n [8n, 'acht'],\n [7n, 'sieben'],\n [6n, 'sechs'],\n [5n, 'fünf'],\n [4n, 'vier'],\n [3n, 'drei'],\n [2n, 'zwei'],\n [1n, 'eins'],\n [0n, 'null']\n ]\n\n /** Combines two word-sets with German compound formation and reversal rules. */\n combineWordSets (preceding, following) {\n let precedingWord = Object.keys(preceding)[0]\n let followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0]\n const followingValue = Object.values(following)[0]\n\n // Handle form of 1: \"eins\" → \"ein(e)\" in certain contexts\n if (precedingValue === 1n) {\n if (followingValue === 100n || followingValue === 1000n) {\n return { [`ein${followingWord}`]: followingValue }\n }\n if (followingValue < 1_000_000n) {\n return following\n }\n precedingWord = 'eine'\n }\n\n if (followingValue > precedingValue) {\n // Multiply: apply pluralization rules for millions\n if (followingValue >= 1_000_000n) {\n if (precedingValue > 1n) {\n followingWord += followingWord.at(-1) === 'e' ? 'n' : 'en'\n }\n precedingWord += ' '\n }\n return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }\n }\n\n // Add: handle special case of tens + units\n if (followingValue < 10n && precedingValue > 10n && precedingValue < 100n) {\n // German reverses tens and units (einundzwanzig = one and twenty)\n if (followingValue === 1n) {\n followingWord = 'ein'\n }\n const temp = followingWord\n followingWord = precedingWord\n precedingWord = `${temp}und`\n } else if (precedingValue >= 1_000_000n) {\n precedingWord += ' '\n }\n\n return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Greek language converter.\n *\n * Supports:\n * - Space-separated number composition\n * - Implicit \"one\" (ένα) omission before scale words\n * - Digit-by-digit decimal reading\n */\nexport class Greek extends GreedyScaleLanguage {\n negativeWord = 'μείον'\n decimalSeparatorWord = 'κόμμα'\n zeroWord = 'μηδέν'\n usePerDigitDecimals = true\n\n scaleWords = [\n // Large numbers (limited set for now)\n [1_000_000_000n, 'δισεκατομμύριο'],\n [1_000_000n, 'εκατομμύριο'],\n [1000n, 'χίλια'],\n\n // Hundreds\n [900n, 'εννιακόσια'],\n [800n, 'οκτακόσια'],\n [700n, 'επτακόσια'],\n [600n, 'εξακόσια'],\n [500n, 'πεντακόσια'],\n [400n, 'τετρακόσια'],\n [300n, 'τριακόσια'],\n [200n, 'διακόσια'],\n [100n, 'εκατό'],\n\n // Tens\n [90n, 'ενενήντα'],\n [80n, 'ογδόντα'],\n [70n, 'εβδομήντα'],\n [60n, 'εξήντα'],\n [50n, 'πενήντα'],\n [40n, 'σαράντα'],\n [30n, 'τριάντα'],\n [20n, 'είκοσι'],\n [19n, 'δεκαεννέα'],\n [18n, 'δεκαοκτώ'],\n [17n, 'δεκαεπτά'],\n [16n, 'δεκαέξι'],\n [15n, 'δεκαπέντε'],\n [14n, 'δεκατέσσερα'],\n [13n, 'δεκατρία'],\n [12n, 'δώδεκα'],\n [11n, 'έντεκα'],\n [10n, 'δέκα'],\n\n // Singles\n [9n, 'εννέα'],\n [8n, 'οκτώ'],\n [7n, 'επτά'],\n [6n, 'έξι'],\n [5n, 'πέντε'],\n [4n, 'τέσσερα'],\n [3n, 'τρία'],\n [2n, 'δύο'],\n [1n, 'ένα'],\n [0n, 'μηδέν']\n ]\n\n /** Combines two word-sets with Greek space-separation rules. */\n combineWordSets (preceding, following) {\n const precedingWord = Object.keys(preceding)[0]\n const precedingValue = Object.values(preceding)[0]\n const followingWord = Object.keys(following)[0]\n const followingValue = Object.values(following)[0]\n\n // Implicit one: omit \"ένα\" before any following value (> 0)\n if (precedingValue === 1n) {\n return following\n }\n\n // No special handling needed for trailing 'ένα';\n // nested merge will first collapse {1, 'ένα'} -> 'ένα'.\n\n // Multiplication: larger following scale multiplied by preceding number\n if (followingValue > precedingValue) {\n return { [`${precedingWord} ${followingWord}`]: precedingValue * followingValue }\n }\n\n // Addition: smaller numbers added together\n return { [`${precedingWord} ${followingWord}`]: precedingValue + followingValue }\n }\n}\n","import { SouthAsianLanguage } from '../classes/south-asian-language.js'\n\n/**\n * Gujarati language converter.\n *\n * Supports:\n * - Indian numbering system (હજાર, લાખ, કરોડ)\n * - Gujarati script\n * - Complete word forms for 0-99\n */\nexport class Gujarati extends SouthAsianLanguage {\n negativeWord = 'ઋણ'\n decimalSeparatorWord = 'દશાંશ'\n zeroWord = 'શૂન્ય'\n hundredWord = 'સો'\n usePerDigitDecimals = true\n\n belowHundredWords = [\n 'શૂન્ય',\n 'એક',\n 'બે',\n 'ત્રણ',\n 'ચાર',\n 'પાંચ',\n 'છ',\n 'સાત',\n 'આઠ',\n 'નવ',\n 'દસ',\n 'અગિયાર',\n 'બાર',\n 'તેર',\n 'ચૌદ',\n 'પંદર',\n 'સોળ',\n 'સત્તર',\n 'અઢાર',\n 'ઓગણીસ',\n 'વીસ',\n 'એકવીસ',\n 'બાવીસ',\n 'ત્રેવીસ',\n 'ચોવીસ',\n 'પચીસ',\n 'છવ્વીસ',\n 'સત્તાવીસ',\n 'અઠ્ઠાવીસ',\n 'ઓગણત્રીસ',\n 'ત્રીસ',\n 'એકત્રીસ',\n 'બત્રીસ',\n 'તેત્રીસ',\n 'ચોત્રીસ',\n 'પાંત્રીસ',\n 'છત્રીસ',\n 'સાડત્રીસ',\n 'અડત્રીસ',\n 'ઓગણચાલીસ',\n 'ચાલીસ',\n 'એકતાલીસ',\n 'બેતાળીસ',\n 'ત્રેતાળીસ',\n 'ચુંમાલીસ',\n 'પિસ્તાલીસ',\n 'છેતાળીસ',\n 'સુડતાળીસ',\n 'અડતાળીસ',\n 'ઓગણપચાસ',\n 'પચાસ',\n 'એકાવન',\n 'બાવન',\n 'ત્રેપન',\n 'ચોપન',\n 'પંચાવન',\n 'છપ્પન',\n 'સત્તાવન',\n 'અઠ્ઠાવન',\n 'ઓગણસાઠ',\n 'સાઠ',\n 'એકસઠ',\n 'બાસઠ',\n 'ત્રેસઠ',\n 'ચોસઠ',\n 'પાંસઠ',\n 'છાસઠ',\n 'સડસઠ',\n 'અડસઠ',\n 'અગણોસિત્તેર',\n 'સિત્તેર',\n 'એકોતેર',\n 'બોતેર',\n 'તોતેર',\n 'ચુમોતેર',\n 'પંચોતેર',\n 'છોતેર',\n 'સિત્યોતેર',\n 'ઇઠ્યોતેર',\n 'ઓગણાએંસી',\n 'એંસી',\n 'એક્યાસી',\n 'બ્યાસી',\n 'ત્યાસી',\n 'ચોર્યાસી',\n 'પંચાસી',\n 'છ્યાસી',\n 'સિત્યાસી',\n 'અઠ્યાસી',\n 'નેવ્યાસી',\n 'નેવું',\n 'એકાણું',\n 'બાણું',\n 'ત્રાણું',\n 'ચોરાણું',\n 'પંચાણું',\n 'છન્નું',\n 'સત્તાણું',\n 'અઠ્ઠાણું',\n 'નવ્વાણું'\n ]\n\n scaleWords = [\n '',\n 'હજાર',\n 'લાખ',\n 'કરોડ',\n 'અબજ',\n 'ખરબ',\n 'નીલ',\n 'પદ્મ',\n 'શંખ'\n ]\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Modern Hebrew language converter.\n *\n * Supports:\n * - Right-to-left text orientation\n * - Feminine grammatical forms (default in Modern Hebrew)\n * - Three-form pluralization (singular/few/many)\n * - Optional \"ve\" (ו) conjunction via options\n */\nexport class Hebrew extends SlavicLanguage {\n negativeWord = 'מינוס'\n decimalSeparatorWord = 'נקודה'\n zeroWord = 'אפס'\n usePerDigitDecimals = true\n\n // Feminine forms (default in Modern Hebrew)\n onesWords = {\n 1: 'אחת',\n 2: 'שתים',\n 3: 'שלש',\n 4: 'ארבע',\n 5: 'חמש',\n 6: 'שש',\n 7: 'שבע',\n 8: 'שמונה',\n 9: 'תשע'\n }\n\n teensWords = {\n 0: 'עשר',\n 1: 'אחת עשרה',\n 2: 'שתים עשרה',\n 3: 'שלש עשרה',\n 4: 'ארבע עשרה',\n 5: 'חמש עשרה',\n 6: 'שש עשרה',\n 7: 'שבע עשרה',\n 8: 'שמונה עשרה',\n 9: 'תשע עשרה'\n }\n\n twentiesWords = {\n 2: 'עשרים',\n 3: 'שלשים',\n 4: 'ארבעים',\n 5: 'חמישים',\n 6: 'ששים',\n 7: 'שבעים',\n 8: 'שמונים',\n 9: 'תשעים'\n }\n\n hundredsWords = {\n 1: 'מאה',\n 2: 'מאתיים',\n 3: 'מאות'\n }\n\n pluralForms = {\n 1: 'אלף',\n 2: 'אלפיים',\n 3: 'שלשת אלפים',\n 4: 'ארבעת אלפים',\n 5: 'חמשת אלפים',\n 6: 'ששת אלפים',\n 7: 'שבעת אלפים',\n 8: 'שמונת אלפים',\n 9: 'תשעת אלפים'\n }\n\n scale = {\n 1: 'אלף', // thousand (singular)\n 2: 'מיליון', // million\n 3: 'מיליארד', // billion\n 4: 'טריליון', // trillion\n 5: 'קוודרליון', // quadrillion\n 6: 'קווינטיליון' // quintillion\n }\n\n scalePlural = {\n 1: 'אלפים', // thousands\n 2: 'מיליונים', // millions\n 3: 'מיליארדים', // billions\n 4: 'טריליונים', // trillions\n 5: 'קוודרליונים', // quadrillions\n 6: 'קווינטיליונים' // quintillions\n }\n\n constructor (options = {}) {\n super(options)\n\n this.setOptions({\n andWord: 'ו'\n }, options)\n }\n\n /** Converts integer part to Hebrew words with special handling for thousands. */\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n const words = []\n const segments = this.splitToSegments(integerPart.toString(), 3)\n let index = segments.length\n for (const x of segments) {\n index = index - 1\n if (x === 0n) {\n continue\n }\n\n const [n1, n2, n3] = this.extractDigits(x)\n\n if (index > 0) {\n // For thousands and above, handle the full chunk value\n const chunkWords = []\n let hasHundreds = false\n\n // Process hundreds in this chunk\n if (n3 > 0n) {\n hasHundreds = true\n if (n3 <= 2n) {\n chunkWords.push(this.hundredsWords[n3])\n } else {\n chunkWords.push(this.onesWords[n3] + ' ' + this.hundredsWords[3])\n }\n }\n\n // Process tens in this chunk\n if (n2 > 1n) {\n // Add conjunction if there were hundreds before\n const tensWord = this.twentiesWords[n2]\n chunkWords.push(hasHundreds ? this.options.andWord + tensWord : tensWord)\n }\n\n // Process ones in this chunk\n if (n2 === 1n) {\n // Add conjunction if there were hundreds before\n const onesWord = this.teensWords[n1]\n chunkWords.push(hasHundreds ? this.options.andWord + onesWord : onesWord)\n } else if (n1 > 0n) {\n // For \"one million\", \"one billion\", etc., don't add the word \"one\"\n if (x === 1n && index > 1) {\n // Skip adding \"one\" for millions/billions/etc.\n } else if (x <= 9n && chunkWords.length === 0 && index === 1) {\n // Use special forms for 1-9 thousand\n chunkWords.push(this.pluralForms[n1])\n } else {\n const onesWord = this.onesWords[n1]\n // Add conjunction if there were hundreds or tens before\n chunkWords.push((hasHundreds || n2 > 0n) ? this.options.andWord + onesWord : onesWord)\n }\n }\n\n // Add scale word (thousand, million, billion, etc.)\n if (x > 9n || index > 1) {\n // For numbers > 9 or higher scales, use appropriate scale word\n if (x === 1n) {\n // Singular form (e.g., \"one thousand\", \"one million\")\n chunkWords.push(this.scale[index])\n } else if (x === 2n && index === 1) {\n // Special dual form for \"two thousand\" (already in thousands[2])\n return [this.pluralForms[2], ...words].join(' ')\n } else if (x === 2n) {\n // For two million, two billion, etc. - use plural form\n chunkWords.push(this.scalePlural[index])\n } else if (index === 1) {\n // For thousands (10+), always use singular \"אלף\"\n chunkWords.push(this.scale[index])\n } else {\n // For millions/billions/etc. use plural form\n chunkWords.push(this.scalePlural[index])\n }\n }\n\n words.push(chunkWords.join(' '))\n continue\n }\n\n if (n3 > 0n) {\n if (n3 <= 2n) {\n words.push(this.hundredsWords[n3])\n } else {\n words.push(this.onesWords[n3] + ' ' + this.hundredsWords[3])\n }\n }\n\n if (n2 > 1n) {\n words.push(this.twentiesWords[n2])\n }\n\n if (n2 === 1n) {\n words.push(this.teensWords[n1])\n } else if (n1 > 0n) {\n words.push(this.onesWords[n1])\n }\n }\n\n if (words.length > 1) {\n words[words.length - 1] = this.options.andWord + words.at(-1)\n }\n\n return words.join(' ')\n }\n}\n","import { SouthAsianLanguage } from '../classes/south-asian-language.js'\n\n/**\n * Hindi language converter.\n *\n * Supports:\n * - Indian numbering system (हज़ार, लाख, करोड़)\n * - Devanagari script\n * - Complete word forms for 0-99\n */\nexport class Hindi extends SouthAsianLanguage {\n negativeWord = 'माइनस'\n decimalSeparatorWord = 'दशमलव'\n zeroWord = 'शून्य'\n hundredWord = 'सौ'\n\n belowHundredWords = [\n 'शून्य',\n 'एक',\n 'दो',\n 'तीन',\n 'चार',\n 'पाँच',\n 'छह',\n 'सात',\n 'आठ',\n 'नौ',\n 'दस',\n 'ग्यारह',\n 'बारह',\n 'तेरह',\n 'चौदह',\n 'पंद्रह',\n 'सोलह',\n 'सत्रह',\n 'अठारह',\n 'उन्नीस',\n 'बीस',\n 'इक्कीस',\n 'बाईस',\n 'तेईस',\n 'चौबीस',\n 'पच्चीस',\n 'छब्बीस',\n 'सत्ताईस',\n 'अट्ठाईस',\n 'उनतीस',\n 'तीस',\n 'इकतीस',\n 'बत्तीस',\n 'तैंतीस',\n 'चौंतीस',\n 'पैंतीस',\n 'छत्तीस',\n 'सैंतीस',\n 'अड़तीस',\n 'उनतालीस',\n 'चालीस',\n 'इकतालीस',\n 'बयालीस',\n 'तेतालीस',\n 'चवालीस',\n 'पैंतालीस',\n 'छियालीस',\n 'सैंतालीस',\n 'अड़तालीस',\n 'उनचास',\n 'पचास',\n 'इक्यावन',\n 'बावन',\n 'तिरपन',\n 'चौवन',\n 'पचपन',\n 'छप्पन',\n 'सत्तावन',\n 'अट्ठावन',\n 'उनसठ',\n 'साठ',\n 'इकसठ',\n 'बासठ',\n 'तिरसठ',\n 'चौंसठ',\n 'पैंसठ',\n 'छियासठ',\n 'सड़सठ',\n 'अड़सठ',\n 'उनहत्तर',\n 'सत्तर',\n 'इकहत्तर',\n 'बहत्तर',\n 'तिहत्तर',\n 'चौहत्तर',\n 'पचहत्तर',\n 'छिहत्तर',\n 'सतहत्तर',\n 'अठहत्तर',\n 'उन्यासी',\n 'अस्सी',\n 'इक्यासी',\n 'बयासी',\n 'तिरासी',\n 'चौरासी',\n 'पचासी',\n 'छियासी',\n 'सत्तासी',\n 'अट्ठासी',\n 'नवासी',\n 'नब्बे',\n 'इक्यानवे',\n 'बानवे',\n 'तिरानवे',\n 'चौरानवे',\n 'पचानवे',\n 'छियानवे',\n 'सत्तानवे',\n 'अट्ठानवे',\n 'निन्यानवे'\n ]\n\n scaleWords = [\n '',\n 'हज़ार',\n 'लाख',\n 'करोड़',\n 'अरब',\n 'खरब',\n 'नील',\n 'पद्म',\n 'शंख'\n ]\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Hungarian language converter.\n *\n * Supports:\n * - Agglutinative structure (no spaces between compound parts)\n * - Special handling for \"egy\" (one) omission\n * - Pre-composed twenties (huszonegy through huszonkilenc)\n */\nexport class Hungarian extends GreedyScaleLanguage {\n negativeWord = 'mínusz'\n decimalSeparatorWord = 'egész'\n zeroWord = 'nulla'\n\n scaleWords = [\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\n /** Converts tens (30-99) with agglutinative composition. */\n tensToCardinal (number) {\n // Expecting `number` as bigint when called from integerToWords\n if (this.wordForScale(number)) {\n return this.wordForScale(number)\n } else {\n const tens = number / 10n\n const units = number % 10n\n return this.wordForScale(tens * 10n) + this.integerToWords(units)\n }\n }\n\n /** Converts hundreds (100-999) with \"száz\" composition. */\n hundredsToCardinal (number) {\n const hundreds = number / 100n\n let prefix = 'száz'\n if (hundreds !== 1n) {\n prefix = this.integerToWords(hundreds, '') + prefix\n }\n const postfix = this.integerToWords(number % 100n, '')\n return prefix + postfix\n }\n\n /** Converts thousands (1000-999999) with \"ezer\" composition. */\n thousandsToCardinal (number) {\n const thousands = number / 1000n\n let prefix = 'ezer'\n if (thousands !== 1n) {\n prefix = this.integerToWords(thousands, '') + prefix\n }\n const postfix = this.integerToWords(number % 1000n, '')\n const middle = (number <= 2000n || postfix === '') ? '' : '-'\n return prefix + middle + postfix\n }\n\n /** Converts large numbers (millions and above) with scale words. */\n bigNumberToCardinal (number) {\n const numberLength = number.toString().length\n const digits = (numberLength % 3 === 0) ? numberLength - 2 : numberLength\n const exp = 10 ** (Math.floor(digits / 3) * 3)\n const prefix = this.integerToWords(number / BigInt(exp), '')\n const rest = this.integerToWords(number % BigInt(exp), '')\n const postfix = (rest === '') ? '' : ('-' + rest)\n return prefix + this.wordForScale(BigInt(exp)) + postfix\n }\n\n /** Converts integer part using Hungarian agglutinative rules. */\n integerToWords (integerPart, zeroWord = this.zeroWord) {\n let words = ''\n\n // Normalize to BigInt for consistent comparisons\n if (typeof integerPart !== 'bigint') integerPart = BigInt(integerPart)\n\n if (integerPart === 0n) {\n words = zeroWord\n } else if (zeroWord === '' && integerPart === 2n) {\n words = 'két'\n } else if (integerPart < 30n) {\n words = this.wordForScale(integerPart)\n } else if (integerPart < 100n) {\n words = this.tensToCardinal(integerPart)\n } else if (integerPart < 1000n) {\n words = this.hundredsToCardinal(integerPart)\n } else if (integerPart < 1_000_000n) {\n words = this.thousandsToCardinal(integerPart)\n } else {\n words = this.bigNumberToCardinal(integerPart)\n }\n\n return words\n }\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Indonesian language converter.\n *\n * Supports:\n * - \"Se-\" prefix for one (seratus, seribu, sejuta)\n * - Regular patterns (puluh for tens, ratus for hundreds)\n * - Space-separated number components\n */\nexport class Indonesian extends AbstractLanguage {\n negativeWord = 'min'\n decimalSeparatorWord = 'koma'\n zeroWord = 'nol'\n\n /**\n * Word forms for digits 0-9.\n * @type {Object.<number, string[]>}\n */\n digitWords = {\n 0: [],\n 1: ['satu'],\n 2: ['dua'],\n 3: ['tiga'],\n 4: ['empat'],\n 5: ['lima'],\n 6: ['enam'],\n 7: ['tujuh'],\n 8: ['delapan'],\n 9: ['sembilan']\n }\n\n /**\n * Scale magnitude words keyed by exponent (10^n).\n * @type {Object.<number, string>}\n */\n scaleWords = {\n 3: 'ribu', // 10^3\n 6: 'juta', // 10^6\n 9: 'miliar', // 10^9\n 12: 'triliun',\n 15: 'kuadriliun',\n 18: 'kuantiliun',\n 21: 'sekstiliun',\n 24: 'septiliun',\n 27: 'oktiliun',\n 30: 'noniliun',\n 33: 'desiliun'\n }\n\n /** Splits number into groups of 3 digits. */\n splitBy3 (number) {\n // Split to groups of 3 numbers: 1234567 -> [['1'], ['234'], ['567']]\n const blocks = []\n const stringNumber = number.toString()\n const length = stringNumber.length\n let firstBlock\n\n if (length < 3) {\n blocks.push([stringNumber])\n } else {\n const firstBlockLength = length % 3\n\n if (firstBlockLength > 0) {\n firstBlock = [stringNumber.slice(0, firstBlockLength)]\n blocks.push(firstBlock)\n }\n\n for (let index = firstBlockLength; index < length; index += 3) {\n const nextBlock = [stringNumber.slice(index, index + 3)]\n blocks.push(nextBlock)\n }\n }\n return blocks\n }\n\n /** Converts digit blocks to Indonesian words. */\n spell (blocks) {\n let wordBlocks = []\n let spelling\n const firstBlock = blocks[0]\n if (firstBlock[0].length === 1) {\n spelling = firstBlock[0] === '0' ? ['nol'] : this.digitWords[Math.trunc(firstBlock[0])]\n } else if (firstBlock[0].length === 2) {\n spelling = this.getTens(firstBlock[0])\n } else {\n spelling = [...this.getHundreds(firstBlock[0][0]), ...this.getTens(firstBlock[0].slice(1, 3))]\n }\n wordBlocks = [...wordBlocks, [firstBlock[0], spelling]]\n for (let index = 1; index < blocks.length; index++) {\n let block = blocks[index]\n spelling = [...this.getHundreds(block[0][0]), ...this.getTens(block[0].slice(1, 3))]\n block = [...block, spelling]\n wordBlocks = [...wordBlocks, block]\n }\n return wordBlocks\n }\n\n /** Converts hundreds digit with \"seratus\" or \"ratus\" suffix. */\n getHundreds (number) {\n if (number === '1') {\n return ['seratus']\n } else if (number === '0') {\n return []\n } else {\n return [...this.digitWords[Math.trunc(number)], 'ratus']\n }\n }\n\n /** Converts tens and units digits with \"puluh\" or \"belas\" suffix. */\n getTens (number) {\n if (number[0] === '1') {\n if (number[1] === '0') {\n return ['sepuluh']\n } else if (number[1] === '1') {\n return ['sebelas']\n }\n return [...this.digitWords[Math.trunc(number[1])], 'belas']\n }\n\n if (number[0] === '0') {\n return this.digitWords[Math.trunc(number[1])]\n }\n\n return [...this.digitWords[Math.trunc(number[0])], 'puluh', ...this.digitWords[Math.trunc(number[1])]]\n }\n\n /** Joins word blocks with magnitude scale words. */\n join (wordBlocks) {\n let wordList = []\n const length = wordBlocks.length - 1\n const firstBlock = [wordBlocks[0]]\n let start = 0\n if (length === 1 && firstBlock[0][0] === '1') {\n wordList.push('seribu')\n start = 1\n }\n for (let index = start; index < length + 1; index++) {\n wordList = [...wordList, ...wordBlocks[index][1]]\n if (wordBlocks[index][1].length === 0) {\n continue\n }\n if (index === length) {\n break\n }\n wordList = [...wordList, this.scaleWords[(length - index) * 3]]\n }\n return wordList.join(' ')\n }\n\n /** Converts integer part using Indonesian group-based conversion. */\n integerToWords (integerPart) {\n return this.join(\n this.spell(\n this.splitBy3(integerPart)\n )\n ).trim()\n }\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Italian language converter.\n *\n * Supports:\n * - Phonetic contractions (vowel elision: \"ventotto\" not \"ventiotto\")\n * - Accentuation rules for \"tre\" in compounds (\"ventitré\")\n * - Custom word construction for irregular patterns\n */\nexport class Italian extends AbstractLanguage {\n negativeWord = 'meno'\n decimalSeparatorWord = 'virgola'\n zeroWord = 'zero'\n\n onesWords = [\n this.zeroWord, 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto',\n 'nove', 'dieci', 'undici', 'dodici', 'tredici', 'quattordici', 'quindici',\n 'sedici', 'diciassette', 'diciotto', 'diciannove'\n ]\n\n tensWords = { 2: 'venti', 3: 'trenta', 4: 'quaranta', 6: 'sessanta' }\n\n exponentPrefixes = [this.zeroWord, 'm', 'b', 'tr', 'quadr', 'quint', 'sest', 'sett', 'ott', 'nov', 'dec']\n\n /** Adds accent to final \"tre\" in compound words (ventitré). */\n accentuate (string) {\n const splittedString = string.split(' ')\n\n const result = splittedString.map(word => {\n return word.slice(-3) === 'tre' && word.length > 3 ? word.replaceAll('tré', 'tre').slice(0, -3) + 'tré' : word.replaceAll('tré', 'tre')\n })\n return result.join(' ')\n }\n\n /** Omits word if it represents zero. */\n omitIfZero (numberToString) {\n return numberToString === this.zeroWord ? '' : numberToString\n }\n\n /** Removes duplicate vowels for phonetic contractions. */\n phoneticContraction (string) {\n return string.replaceAll('oo', 'o').replaceAll('ao', 'o').replaceAll('io', 'o').replaceAll('au', 'u').replaceAll('iu', 'u')\n }\n\n /** Converts tens (20-99) with phonetic contractions. */\n tensToCardinal (number) {\n const tens = Math.floor(number / 10)\n const units = number % 10\n const prefix = Object.prototype.hasOwnProperty.call(this.tensWords, tens) ? this.tensWords[tens] : this.onesWords[tens].slice(0, -1) + 'anta'\n const postfix = this.omitIfZero(this.onesWords[units])\n return this.phoneticContraction(prefix + postfix)\n }\n\n /** Converts hundreds (100-999) with \"cento\" composition. */\n hundredsToCardinal (number) {\n const hundreds = Math.floor(number / 100)\n let prefix = 'cento'\n if (hundreds !== 1) {\n prefix = this.onesWords[hundreds] + prefix\n }\n const postfix = this.omitIfZero(this.integerToWords(number % 100))\n return this.phoneticContraction(prefix + postfix)\n }\n\n /** Converts thousands (1000-999999) with \"mille/mila\" composition. */\n thousandsToCardinal (number) {\n const thousands = Math.floor(number / 1000)\n const prefix = thousands === 1 ? 'mille' : this.integerToWords(thousands) + 'mila'\n const postfix = this.omitIfZero(this.integerToWords(number % 1000))\n return prefix + postfix\n }\n\n /** Converts exponent length to Italian scale word (milione, miliardo, etc.). */\n exponentLengthToString (exponentLength) {\n const prefix = this.exponentPrefixes[Math.floor(exponentLength / 6)]\n return exponentLength % 6 === 0 ? prefix + 'ilione' : prefix + 'iliardo'\n }\n\n /** Converts large numbers (millions and above) with exponent-based naming. */\n bigNumberToCardinal (number) {\n const digits = [...number.toString()]\n\n let preDigits = digits.length % 3\n if (preDigits === 0) {\n preDigits = 3\n }\n\n const multiplier = digits.slice(0, preDigits) // first `preDigits` elements\n const exponent = digits.slice(preDigits) // without the first `preDigits` elements\n\n let prefix, postfix\n let infix = this.exponentLengthToString(exponent.length)\n\n if (multiplier.join('') === '1') {\n prefix = 'un '\n } else {\n prefix = this.integerToWords(Math.trunc(Number(multiplier.join(''))))\n infix = ' ' + infix.slice(0, -1) + 'i' // without last element\n }\n\n const isSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value))\n if (isSetsEqual(new Set(exponent), new Set(['0']))) {\n postfix = ''\n } else {\n postfix = this.integerToWords(Math.trunc(exponent.join('')))\n\n infix += (postfix.includes(' e ') ? ', ' : ' e ')\n }\n\n return prefix + infix + postfix\n }\n\n /** Converts integer part using Italian custom algorithm with accentuation. */\n integerToWords (integerPart) {\n let words = ''\n\n if (integerPart < 20) {\n words = this.onesWords[integerPart]\n } else if (integerPart < 100) {\n words = this.tensToCardinal(Number(integerPart))\n } else if (integerPart < 1000) {\n words = this.hundredsToCardinal(Number(integerPart))\n } else if (integerPart < 1_000_000) {\n words = this.thousandsToCardinal(Number(integerPart))\n } else {\n words = this.bigNumberToCardinal(integerPart)\n }\n\n return this.accentuate(words)\n }\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Japanese language converter.\n *\n * Supports:\n * - Kanji numerals (一, 二, 三, etc.)\n * - Grouping by 万 (10,000) instead of 1,000\n * - Special 一 (one) omission rules (十, 百, 千 but 一万, 一億)\n */\nexport class Japanese extends AbstractLanguage {\n negativeWord = 'マイナス'\n decimalSeparatorWord = '点'\n zeroWord = '零'\n wordSeparator = '' // Japanese doesn't use spaces between characters\n usePerDigitDecimals = true // Enable digit-by-digit decimal conversion\n\n // Ones words used for group conversion (1-9)\n onesWords = ['一', '二', '三', '四', '五', '六', '七', '八', '九']\n\n // Scale words for grouping by 10^4\n scaleWords = [\n '', // 10^0 (ones)\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\n /** Converts a segment of up to 4 digits to Japanese kanji with 一 omission rules. */\n segmentToWords (num) {\n if (num === 0n) return ''\n\n const thousands = num / 1000n\n const hundreds = (num % 1000n) / 100n\n const tens = (num % 100n) / 10n\n const ones = num % 10n\n\n let result = ''\n\n // Thousands (千)\n if (thousands > 0n) {\n // Always omit 一 before 千 when thousands === 1\n if (thousands === 1n) {\n result += '千'\n } else {\n result += this.onesWords[Number(thousands) - 1] + '千'\n }\n }\n\n // Hundreds (百)\n if (hundreds > 0n) {\n // Always omit 一 before 百 when hundreds === 1\n if (hundreds === 1n) {\n result += '百'\n } else {\n result += this.onesWords[Number(hundreds) - 1] + '百'\n }\n }\n\n // Tens (十)\n if (tens > 0n) {\n // Always omit 一 before 十 when tens === 1\n if (tens === 1n) {\n result += '十'\n } else {\n result += this.onesWords[Number(tens) - 1] + '十'\n }\n }\n\n // Ones\n if (ones > 0n) {\n result += this.onesWords[Number(ones) - 1]\n }\n\n return result\n }\n\n /** Converts integer part using Japanese 万-based grouping system. */\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n\n let temp = integerPart\n let scaleIndex = 0\n const groups = []\n\n // Split into groups of 4 digits (万-based system)\n while (temp > 0n) {\n const group = temp % 10000n\n if (group > 0n) {\n groups.push({ value: group, scale: scaleIndex })\n }\n temp = temp / 10000n\n scaleIndex++\n }\n\n // Reverse to process from highest to lowest\n groups.reverse()\n\n let result = ''\n\n for (let i = 0; i < groups.length; i++) {\n const { value, scale } = groups[i]\n\n const groupStr = this.segmentToWords(value)\n\n // For scales >= 1 (万 and above), always add the scale word\n if (scale >= 1) {\n // Special case: if group is 1 and scale >= 1, we need 一 before the scale\n if (value === 1n) {\n result += '一' + this.scaleWords[scale]\n } else {\n result += groupStr + this.scaleWords[scale]\n }\n } else {\n result += groupStr\n }\n }\n\n return result\n }\n}\n","import { SouthAsianLanguage } from '../classes/south-asian-language.js'\n\n/**\n * Kannada language converter.\n *\n * Supports:\n * - Indian numbering system (ಸಾವಿರ, ಲಕ್ಷ, ಕೋಟಿ)\n * - Kannada script\n * - Complete word forms for 0-99\n */\nexport class Kannada extends SouthAsianLanguage {\n negativeWord = 'ಋಣಾತ್ಮಕ'\n decimalSeparatorWord = 'ದಶಮಾಂಶ'\n zeroWord = 'ಸೊನ್ನೆ'\n hundredWord = 'ನೂರು'\n usePerDigitDecimals = true\n\n belowHundredWords = [\n 'ಸೊನ್ನೆ', 'ಒಂದು', 'ಎರಡು', 'ಮೂರು', 'ನಾಲ್ಕು', 'ಐದು', 'ಆರು', 'ಏಳು', 'ಎಂಟು', 'ಒಂಬತ್ತು',\n 'ಹತ್ತು', 'ಹನ್ನೊಂದು', 'ಹನ್ನೆರಡು', 'ಹದಿಮೂರು', 'ಹದಿನಾಲ್ಕು', 'ಹದಿನೈದು', 'ಹದಿನಾರು', 'ಹದಿನೇಳು', 'ಹದಿನೆಂಟು', 'ಹತ್ತೊಂಬತ್ತು',\n 'ಇಪ್ಪತ್ತು', 'ಇಪ್ಪತ್ತೊಂದು', 'ಇಪ್ಪತ್ತೆರಡು', 'ಇಪ್ಪತ್ತಮೂರು', 'ಇಪ್ಪತ್ತನಾಲ್ಕು', 'ಇಪ್ಪತ್ತೈದು', 'ಇಪ್ಪತ್ತಾರು', 'ಇಪ್ಪತ್ತೇಳು', 'ಇಪ್ಪತ್ತೆಂಟು', 'ಇಪ್ಪತ್ತೊಂಬತ್ತು',\n 'ಮೂವತ್ತು', 'ಮೂವತ್ತೊಂದು', 'ಮೂವತ್ತೆರಡು', 'ಮೂವತ್ತಮೂರು', 'ಮೂವತ್ತನಾಲ್ಕು', 'ಮೂವತ್ತೈದು', 'ಮೂವತ್ತಾರು', 'ಮೂವತ್ತೇಳು', 'ಮೂವತ್ತೆಂಟು', 'ಮೂವತ್ತೊಂಬತ್ತು',\n 'ನಲವತ್ತು', 'ನಲವತ್ತೊಂದು', 'ನಲವತ್ತೆರಡು', 'ನಲವತ್ತಮೂರು', 'ನಲವತ್ತನಾಲ್ಕು', 'ನಲವತ್ತೈದು', 'ನಲವತ್ತಾರು', 'ನಲವತ್ತೇಳು', 'ನಲವತ್ತೆಂಟು', 'ನಲವತ್ತೊಂಬತ್ತು',\n 'ಐವತ್ತು', 'ಐವತ್ತೊಂದು', 'ಐವತ್ತೆರಡು', 'ಐವತ್ತಮೂರು', 'ಐವತ್ತನಾಲ್ಕು', 'ಐವತ್ತೈದು', 'ಐವತ್ತಾರು', 'ಐವತ್ತೇಳು', 'ಐವತ್ತೆಂಟು', 'ಐವತ್ತೊಂಬತ್ತು',\n 'ಅರವತ್ತು', 'ಅರವತ್ತೊಂದು', 'ಅರವತ್ತೆರಡು', 'ಅರವತ್ತಮೂರು', 'ಅರವತ್ತನಾಲ್ಕು', 'ಅರವತ್ತೈದು', 'ಅರವತ್ತಾರು', 'ಅರವತ್ತೇಳು', 'ಅರವತ್ತೆಂಟು', 'ಅರವತ್ತೊಂಬತ್ತು',\n 'ಎಪ್ಪತ್ತು', 'ಎಪ್ಪತ್ತೊಂದು', 'ಎಪ್ಪತ್ತೆರಡು', 'ಎಪ್ಪತ್ತಮೂರು', 'ಎಪ್ಪತ್ತನಾಲ್ಕು', 'ಎಪ್ಪತ್ತೈದು', 'ಎಪ್ಪತ್ತಾರು', 'ಎಪ್ಪತ್ತೇಳು', 'ಎಪ್ಪತ್ತೆಂಟು', 'ಎಪ್ಪತ್ತೊಂಬತ್ತು',\n 'ಎಂಬತ್ತು', 'ಎಂಬತ್ತೊಂದು', 'ಎಂಬತ್ತೆರಡು', 'ಎಂಬತ್ತಮೂರು', 'ಎಂಬತ್ತನಾಲ್ಕು', 'ಎಂಬತ್ತೈದು', 'ಎಂಬತ್ತಾರು', 'ಎಂಬತ್ತೇಳು', 'ಎಂಬತ್ತೆಂಟು', 'ಎಂಬತ್ತೊಂಬತ್ತು',\n 'ತೊಂಬತ್ತು', 'ತೊಂಬತ್ತೊಂದು', 'ತೊಂಬತ್ತೆರಡು', 'ತೊಂಬತ್ತಮೂರು', 'ತೊಂಬತ್ತನಾಲ್ಕು', 'ತೊಂಬತ್ತೈದು', 'ತೊಂಬತ್ತಾರು', 'ತೊಂಬತ್ತೇಳು', 'ತೊಂಬತ್ತೆಂಟು', 'ತೊಂಬತ್ತೊಂಬತ್ತು'\n ]\n\n scaleWords = [\n '', // units\n 'ಸಾವಿರ', // thousand (1,000)\n 'ಲಕ್ಷ', // lakh (100,000)\n 'ಕೋಟಿ', // crore (10,000,000)\n 'ಅಬ್ಜ', // arab (1,000,000,000)\n 'ಖರ್ವ', // kharab (100,000,000,000)\n 'ನೀಲ', // neel (10,000,000,000,000)\n 'ಪದ್ಮ', // padma (1,000,000,000,000,000)\n 'ಶಂಖ' // shankh (100,000,000,000,000,000)\n ]\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Korean language converter.\n *\n * Supports:\n * - Hangul numerals (일, 이, 삼, etc.)\n * - Grouping by 만 (10,000) system\n * - Implicit '일' (one) omission before multipliers\n */\nexport class Korean extends GreedyScaleLanguage {\n negativeWord = '마이너스'\n decimalSeparatorWord = '점'\n zeroWord = '영'\n\n scaleWords = [\n [10_000_000_000_000_000_000_000_000_000n, '양'],\n [1_000_000_000_000_000_000_000_000n, '자'],\n [100_000_000_000_000_000_000n, '해'],\n [10_000_000_000_000_000n, '경'],\n [1_000_000_000_000n, '조'],\n [100_000_000n, '억'],\n [10_000n, '만'],\n [1000n, '천'],\n [100n, '백'],\n [10n, '십'],\n [9n, '구'],\n [8n, '팔'],\n [7n, '칠'],\n [6n, '육'],\n [5n, '오'],\n [4n, '사'],\n [3n, '삼'],\n [2n, '이'],\n [1n, '일'],\n [0n, '영']\n ]\n\n /** Combines two word-sets according to Korean grammar rules. */\n combineWordSets (preceding, following) {\n const precedingWord = Object.keys(preceding)[0]\n const followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0] // BigInt\n const followingValue = Object.values(following)[0] // BigInt\n\n // Implicit \"일\": omit 1 before multipliers up to 만 (10,000)\n if (precedingValue === 1n && followingValue <= 10_000n) return following\n // Concatenate (no space) for small numbers less than 만\n if (precedingValue < 10_000n && precedingValue > followingValue) return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }\n // Space-separate for large numbers (>= 만) when adding\n if (precedingValue >= 10_000n && precedingValue > followingValue) return { [`${precedingWord} ${followingWord}`]: precedingValue + followingValue }\n // Multiply for all scale combinations\n return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }\n }\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Latvian language converter.\n *\n * Supports:\n * - Three-form pluralization (one/few/many)\n * - Latvian diacritical marks (ī, ā, ē, ū)\n * - Compound number formation (divdesmit, trīsdesmit)\n */\nexport class Latvian extends SlavicLanguage {\n negativeWord = 'mīnus'\n decimalSeparatorWord = 'komats'\n zeroWord = 'nulle'\n\n onesWords = {\n 1: 'viens',\n 2: 'divi',\n 3: 'trīs',\n 4: 'četri',\n 5: 'pieci',\n 6: 'seši',\n 7: 'septiņi',\n 8: 'astoņi',\n 9: 'deviņi'\n }\n\n onesFeminineWords = {\n 1: 'viena',\n 2: 'divas',\n 3: 'trīs',\n 4: 'četras',\n 5: 'piecas',\n 6: 'sešas',\n 7: 'septiņas',\n 8: 'astoņas',\n 9: 'deviņas'\n }\n\n teensWords = {\n 0: 'desmit',\n 1: 'vienpadsmit',\n 2: 'divpadsmit',\n 3: 'trīspadsmit',\n 4: 'četrpadsmit',\n 5: 'piecpadsmit',\n 6: 'sešpadsmit',\n 7: 'septiņpadsmit',\n 8: 'astoņpadsmit',\n 9: 'deviņpadsmit'\n }\n\n twentiesWords = {\n 2: 'divdesmit',\n 3: 'trīsdesmit',\n 4: 'četrdesmit',\n 5: 'piecdesmit',\n 6: 'sešdesmit',\n 7: 'septiņdesmit',\n 8: 'astoņdesmit',\n 9: 'deviņdesmit'\n }\n\n hundredsWords = ['simts', 'simti', 'simtu']\n\n pluralForms = {\n 1: ['tūkstotis', 'tūkstoši', 'tūkstošu'],\n 2: ['miljons', 'miljoni', 'miljonu'],\n 3: ['miljards', 'miljardi', 'miljardu'],\n 4: ['triljons', 'triljoni', 'triljonu'],\n 5: ['kvadriljons', 'kvadriljoni', 'kvadriljonu'],\n 6: ['kvintiljons', 'kvintiljoni', 'kvintiljonu'],\n 7: ['sikstiljons', 'sikstiljoni', 'sikstiljonu'],\n 8: ['septiljons', 'septiljoni', 'septiljonu'],\n 9: ['oktiljons', 'oktiljoni', 'oktiljonu'],\n 10: ['nontiljons', 'nontiljoni', 'nontiljonu']\n }\n\n pluralize (n, forms) {\n if (n === 0n) {\n return forms[2]\n }\n\n const lastDigit = n % 10n\n const lastTwoDigits = n % 100n\n\n if (lastDigit === 1n && lastTwoDigits !== 11n) {\n return forms[0]\n }\n\n return forms[1]\n }\n\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n const words = []\n const segments = this.splitToSegments(integerPart.toString(), 3)\n let index = segments.length\n for (const x of segments) {\n index = index - 1\n if (x === 0n) {\n continue\n }\n const [n1, n2, n3] = this.extractDigits(x)\n if (n3 > 0n) {\n if (n3 === 1n && n2 === 0n && n1 > 0n) {\n words.push(this.hundredsWords[2])\n } else if (n3 > 1n) {\n words.push(this.onesWords[n3], this.hundredsWords[1])\n } else {\n words.push(this.hundredsWords[0])\n }\n }\n if (n2 > 1n) {\n words.push(this.twentiesWords[n2])\n }\n if (n2 === 1n) {\n words.push(this.teensWords[n1])\n } else if (n1 > 0n && !(index > 0 && x === 1n)) {\n words.push(this.onesWords[n1])\n }\n if (index > 0) {\n words.push(this.pluralize(x, this.pluralForms[index]))\n }\n }\n return words.join(' ')\n }\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Lithuanian language converter.\n *\n * Supports:\n * - Three-form pluralization (one/few/many)\n * - Gender agreement (vienas/viena, du/dvi)\n * - Baltic declension patterns\n */\nexport class Lithuanian extends SlavicLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'kablelis'\n zeroWord = 'nulis'\n\n onesWords = {\n 1: 'vienas',\n 2: 'du',\n 3: 'trys',\n 4: 'keturi',\n 5: 'penki',\n 6: 'šeši',\n 7: 'septyni',\n 8: 'aštuoni',\n 9: 'devyni'\n }\n\n onesFeminineWords = {\n 1: 'viena',\n 2: 'dvi',\n 3: 'trys',\n 4: 'keturios',\n 5: 'penkios',\n 6: 'šešios',\n 7: 'septynios',\n 8: 'aštuonios',\n 9: 'devynios'\n }\n\n teensWords = {\n 0: 'dešimt',\n 1: 'vienuolika',\n 2: 'dvylika',\n 3: 'trylika',\n 4: 'keturiolika',\n 5: 'penkiolika',\n 6: 'šešiolika',\n 7: 'septyniolika',\n 8: 'aštuoniolika',\n 9: 'devyniolika'\n }\n\n twentiesWords = {\n 2: 'dvidešimt',\n 3: 'trisdešimt',\n 4: 'keturiasdešimt',\n 5: 'penkiasdešimt',\n 6: 'šešiasdešimt',\n 7: 'septyniasdešimt',\n 8: 'aštuoniasdešimt',\n 9: 'devyniasdešimt'\n }\n\n hundredsWords = ['šimtas', 'šimtai']\n\n pluralForms = {\n 1: ['tūkstantis', 'tūkstančiai', 'tūkstančių'],\n 2: ['milijonas', 'milijonai', 'milijonų'],\n 3: ['milijardas', 'milijardai', 'milijardų'],\n 4: ['trilijonas', 'trilijonai', 'trilijonų'],\n 5: ['kvadrilijonas', 'kvadrilijonai', 'kvadrilijonų'],\n 6: ['kvintilijonas', 'kvintilijonai', 'kvintilijonų'],\n 7: ['sikstilijonas', 'sikstilijonai', 'sikstilijonų'],\n 8: ['septilijonas', 'septilijonai', 'septilijonų'],\n 9: ['oktilijonas', 'oktilijonai', 'oktilijonų'],\n 10: ['naintilijonas', 'naintilijonai', 'naintilijonų']\n }\n\n pluralize (n, forms) {\n if (n === 0n) {\n return forms[2]\n }\n\n const [n1, n2] = this.extractDigits(n)\n\n if (n2 === 1n || n1 === 0n) {\n return forms[2]\n }\n\n if (n1 === 1n) {\n return forms[0]\n }\n\n return forms[1]\n }\n\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n const words = []\n const segments = this.splitToSegments(integerPart.toString(), 3)\n let index = segments.length\n for (const x of segments) {\n index = index - 1\n if (x === 0n) {\n continue\n }\n const [n1, n2, n3] = this.extractDigits(x)\n if (n3 > 0n) {\n words.push(this.onesWords[n3])\n if (n3 > 1n) {\n words.push(this.hundredsWords[1])\n } else {\n words.push(this.hundredsWords[0])\n }\n }\n if (n2 > 1n) {\n words.push(this.twentiesWords[n2])\n }\n if (n2 === 1n) {\n words.push(this.teensWords[n1])\n } else if (n1 > 0n) {\n if ((index === 1 || (this.options.gender === 'feminine' && index === 0)) && integerPart < 1000n) {\n words.push(this.onesFeminineWords[n1])\n } else {\n words.push(this.onesWords[n1])\n }\n }\n if (index > 0) {\n words.push(this.pluralize(x, this.pluralForms[index]))\n }\n }\n return words.join(' ')\n }\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Malay (Bahasa Melayu) language converter.\n *\n * Supports:\n * - \"Se-\" prefix for singular units (seratus, seribu, sejuta)\n * - Regular patterns (puluh for tens, ratus for hundreds)\n * - Space-separated number components\n */\nexport class Malay extends AbstractLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'perpuluhan'\n zeroWord = 'sifar'\n\n /**\n * Word forms for digits 0-9.\n * @type {Object.<number, string[]>}\n */\n digitWords = {\n 0: [],\n 1: ['satu'],\n 2: ['dua'],\n 3: ['tiga'],\n 4: ['empat'],\n 5: ['lima'],\n 6: ['enam'],\n 7: ['tujuh'],\n 8: ['lapan'],\n 9: ['sembilan']\n }\n\n /**\n * Scale magnitude words keyed by exponent (10^n).\n * @type {Object.<number, string>}\n */\n scaleWords = {\n 3: 'ribu',\n 6: 'juta',\n 9: 'bilion',\n 12: 'trilion'\n }\n\n splitBy3 (number) {\n const blocks = []\n const stringNumber = number.toString()\n const length = stringNumber.length\n let firstBlock\n\n if (length < 3) {\n blocks.push([stringNumber])\n } else {\n const firstBlockLength = length % 3\n\n if (firstBlockLength > 0) {\n firstBlock = [stringNumber.slice(0, firstBlockLength)]\n blocks.push(firstBlock)\n }\n\n for (let index = firstBlockLength; index < length; index += 3) {\n const nextBlock = [stringNumber.slice(index, index + 3)]\n blocks.push(nextBlock)\n }\n }\n return blocks\n }\n\n spell (blocks) {\n let wordBlocks = []\n let spelling\n const firstBlock = blocks[0]\n if (firstBlock[0].length === 1) {\n spelling = firstBlock[0] === '0' ? ['sifar'] : this.digitWords[Math.trunc(firstBlock[0])]\n } else if (firstBlock[0].length === 2) {\n spelling = this.getTens(firstBlock[0])\n } else {\n spelling = [...this.getHundreds(firstBlock[0][0]), ...this.getTens(firstBlock[0].slice(1, 3))]\n }\n wordBlocks = [...wordBlocks, [firstBlock[0], spelling]]\n for (let index = 1; index < blocks.length; index++) {\n let block = blocks[index]\n spelling = [...this.getHundreds(block[0][0]), ...this.getTens(block[0].slice(1, 3))]\n block = [...block, spelling]\n wordBlocks = [...wordBlocks, block]\n }\n return wordBlocks\n }\n\n getHundreds (number) { // 'ratus'\n if (number === '1') {\n return ['seratus']\n } else if (number === '0') {\n return []\n } else {\n return [...this.digitWords[Math.trunc(number)], 'ratus']\n }\n }\n\n getTens (number) { // 'puluh' and 'belas'\n if (number[0] === '1') {\n if (number[1] === '0') {\n return ['sepuluh']\n } else if (number[1] === '1') {\n return ['sebelas']\n }\n return [...this.digitWords[Math.trunc(number[1])], 'belas']\n }\n\n if (number[0] === '0') {\n return this.digitWords[Math.trunc(number[1])]\n }\n\n return [...this.digitWords[Math.trunc(number[0])], 'puluh', ...this.digitWords[Math.trunc(number[1])]]\n }\n\n join (wordBlocks) {\n const wordList = []\n const length = wordBlocks.length - 1\n\n for (let index = 0; index <= length; index++) {\n const words = wordBlocks[index][1]\n const isLast = index === length\n const scaleWord = isLast ? null : this.scaleWords[(length - index) * 3]\n\n if (!isLast && words.length === 1 && words[0] === 'satu') {\n // Use se- prefix for singular scale units: seribu, sejuta, sebilion, setrilion\n wordList.push('se' + scaleWord)\n continue\n }\n\n // Add current block words\n for (const w of words) wordList.push(w)\n\n // Append scale word if applicable and current block contributed words\n if (!isLast && words.length > 0) {\n wordList.push(scaleWord)\n }\n }\n\n return wordList.join(' ')\n }\n\n integerToWords (integerPart) {\n return this.join(\n this.spell(\n this.splitBy3(integerPart)\n )\n ).trim()\n }\n}\n","import { SouthAsianLanguage } from '../classes/south-asian-language.js'\n\n/**\n * Marathi language converter.\n *\n * Supports:\n * - Indian numbering system (हजार, लाख, कोटी)\n * - Devanagari script\n * - Complete word forms for 0-99\n */\nexport class Marathi extends SouthAsianLanguage {\n negativeWord = 'उणे'\n decimalSeparatorWord = 'दशांश'\n zeroWord = 'शून्य'\n hundredWord = 'शंभर'\n usePerDigitDecimals = true\n\n belowHundredWords = [\n 'शून्य',\n 'एक',\n 'दोन',\n 'तीन',\n 'चार',\n 'पाच',\n 'सहा',\n 'सात',\n 'आठ',\n 'नऊ',\n 'दहा',\n 'अकरा',\n 'बारा',\n 'तेरा',\n 'चौदा',\n 'पंधरा',\n 'सोळा',\n 'सतरा',\n 'अठरा',\n 'एकोणीस',\n 'वीस',\n 'एकवीस',\n 'बावीस',\n 'तेवीस',\n 'चोवीस',\n 'पंचवीस',\n 'सव्वीस',\n 'सत्तावीस',\n 'अठ्ठावीस',\n 'एकोणतीस',\n 'तीस',\n 'एकतीस',\n 'बत्तीस',\n 'तेहेतीस',\n 'चौतीस',\n 'पस्तीस',\n 'छत्तीस',\n 'सदतीस',\n 'अडतीस',\n 'एकोणचाळीस',\n 'चाळीस',\n 'एकेचाळीस',\n 'बेचाळीस',\n 'त्रेचाळीस',\n 'चव्वेचाळीस',\n 'पंचेचाळीस',\n 'सेहेचाळीस',\n 'सत्तेचाळीस',\n 'अठ्ठेचाळीस',\n 'एकोणपन्नास',\n 'पन्नास',\n 'एक्काव्वन',\n 'बावन्न',\n 'त्रेपन्न',\n 'चोपन्न',\n 'पंचाव्वन',\n 'छप्पन्न',\n 'सत्तावन्न',\n 'अठ्ठावन्न',\n 'एकोणसाठ',\n 'साठ',\n 'एकसष्ठ',\n 'बासष्ठ',\n 'त्रेसष्ठ',\n 'चौसष्ठ',\n 'पासष्ठ',\n 'सहासष्ठ',\n 'सदुसष्ठ',\n 'अडुसष्ठ',\n 'एकोणसत्तर',\n 'सत्तर',\n 'एकाहत्तर',\n 'बाहत्तर',\n 'त्र्याहत्तर',\n 'चौऱ्याहत्तर',\n 'पंच्याहत्तर',\n 'शहात्तर',\n 'सत्याहत्तर',\n 'अठ्ठ्याहत्तर',\n 'एकोणऐंशी',\n 'ऐंशी',\n 'एक्याऐंशी',\n 'ब्याऐंशी',\n 'त्र्याऐंशी',\n 'चौऱ्याऐंशी',\n 'पंच्याऐंशी',\n 'शहाऐंशी',\n 'सत्याऐंशी',\n 'अठ्ठ्याऐंशी',\n 'एकोणनव्वद',\n 'नव्वद',\n 'एक्याण्णव',\n 'ब्याण्णव',\n 'त्र्याण्णव',\n 'चौऱ्याण्णव',\n 'पंच्याण्णव',\n 'शहाण्णव',\n 'सत्याण्णव',\n 'अठ्ठ्याण्णव',\n 'नव्याण्णव'\n ]\n\n scaleWords = [\n '',\n 'हजार',\n 'लाख',\n 'कोटी',\n 'अब्ज',\n 'खर्व',\n 'निखर्व',\n 'महापद्म',\n 'शंकू'\n ]\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Norwegian Bokmål language converter.\n *\n * Supports:\n * - Hyphenation for compound numbers (tjue-en)\n * - \"og\" (and) for hundreds combinations\n * - Implicit '1' omission before tens and magnitudes\n */\nexport class NorwegianBokmal extends GreedyScaleLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'komma'\n zeroWord = 'null'\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000_000n, 'oktillion'],\n [1_000_000_000_000_000_000_000_000n, 'septillion'],\n [1_000_000_000_000_000_000_000n, 'sekstillion'],\n [1_000_000_000_000_000_000n, 'kvintillion'],\n [1_000_000_000_000_000n, 'kvadrillion'],\n [1_000_000_000_000n, 'billion'],\n [1_000_000_000n, 'milliard'],\n [1_000_000n, 'million'],\n [1000n, 'tusen'],\n [100n, 'hundre'],\n [90n, 'nitti'],\n [80n, 'åtti'],\n [70n, 'sytti'],\n [60n, 'seksti'],\n [50n, 'femti'],\n [40n, 'førti'],\n [30n, 'tretti'],\n [20n, 'tjue'],\n [19n, 'nitten'],\n [18n, 'atten'],\n [17n, 'sytten'],\n [16n, 'seksten'],\n [15n, 'femten'],\n [14n, 'fjorten'],\n [13n, 'tretten'],\n [12n, 'tolv'],\n [11n, 'elleve'],\n [10n, 'ti'],\n [9n, 'ni'],\n [8n, 'åtte'],\n [7n, 'syv'],\n [6n, 'seks'],\n [5n, 'fem'],\n [4n, 'fire'],\n [3n, 'tre'],\n [2n, 'to'],\n [1n, 'en'],\n [0n, 'null']\n ]\n\n /**\n * Merges two adjacent word-sets according to Norwegian grammar rules.\n *\n * Norwegian-specific rules:\n * - Implicit \"en\": `combineWordSets({ 'en': 1n }, { 'hundre': 100n })` → `{ 'hundre': 100n }`\n * - Hyphenation for compound tens: `combineWordSets({ 'tjue': 20n }, { 'en': 1n })` → `{ 'tjue-en': 21n }`\n * - \"og\" (and) after hundreds: `combineWordSets({ 'hundre': 100n }, { 'en': 1n })` → `{ 'hundre og en': 101n }`\n * - Space-separated for large numbers (thousands+)\n * - Comma separator for non-magnitude additions (e.g., \\\"tusen, en\\\")\n *\n * @param {Object} preceding The preceding word-set as `{ word: BigInt }`.\n * @param {Object} following The following word-set as `{ word: BigInt }`.\n * @returns {Object} Merged pair with combined word and resulting numeric value.\n *\n * @example\n * combineWordSets({ 'en': 1n }, { 'hundre': 100n }); // { 'hundre': 100n }\n * combineWordSets({ 'tjue': 20n }, { 'tre': 3n }); // { 'tjue-tre': 23n }\n */\n // Norwegian merge rules mirror the former Scandinavian base logic\n combineWordSets (preceding, following) {\n const precedingWord = Object.keys(preceding)[0]\n const followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0]\n const followingValue = Object.values(following)[0]\n\n if (precedingValue === 1n && followingValue < 100n) {\n return following\n }\n\n if (precedingValue < 100n && precedingValue > followingValue) {\n return { [`${precedingWord}-${followingWord}`]: precedingValue + followingValue }\n }\n\n if (precedingValue >= 100n && followingValue < 100n) {\n return { [`${precedingWord} og ${followingWord}`]: precedingValue + followingValue }\n }\n\n if (followingValue > precedingValue) {\n return { [`${precedingWord} ${followingWord}`]: precedingValue * followingValue }\n }\n\n return { [`${precedingWord}, ${followingWord}`]: precedingValue + followingValue }\n }\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Persian language converter.\n *\n * Supports:\n * - \"و\" (and) conjunction for compound numbers\n * - Recursive conversion for larger numbers\n */\nexport class Persian extends AbstractLanguage {\n negativeWord = 'منفى'\n decimalSeparatorWord = 'ممیّز'\n zeroWord = 'صفر'\n\n /**\n * Words for digits 1-9.\n * @type {Object.<number, string>}\n */\n onesWords = {\n 1: 'یک',\n 2: 'دو',\n 3: 'سه',\n 4: 'چهار',\n 5: 'پنج',\n 6: 'شش',\n 7: 'هفت',\n 8: 'هشت',\n 9: 'نه'\n }\n\n /**\n * Words for teen numbers (10-19).\n * @type {Object.<number, string>}\n */\n teensWords = {\n 10: 'ده',\n 11: 'یازده',\n 12: 'دوازده',\n 13: 'سیزده',\n 14: 'چهارده',\n 15: 'پانزده',\n 16: 'شانزده',\n 17: 'هفده',\n 18: 'هجده',\n 19: 'نوزده'\n }\n\n /**\n * Words for multiples of ten (20-90).\n * @type {Object.<number, string>}\n */\n tensWords = {\n 20: 'بیست',\n 30: 'سی',\n 40: 'چهل',\n 50: 'پنجاه',\n 60: 'شصت',\n 70: 'هفتاد',\n 80: 'هشتاد',\n 90: 'نود'\n }\n\n /**\n * Words for hundreds (100-900).\n * @type {Object.<number, string>}\n */\n hundredsWords = {\n 100: 'صد',\n 200: 'دویست',\n 300: 'سيصد',\n 400: 'چهار صد',\n 500: 'پانصد',\n 600: 'ششصد',\n 700: 'هفتصد',\n 800: 'هشتصد',\n 900: 'نهصد'\n }\n\n /**\n * Scale magnitude words.\n * @type {Object.<number, string>}\n */\n scaleWords = {\n 1000: 'هزار',\n 1_000_000: 'میلیون'\n }\n\n /** Converts integer part using categorized word tables. */\n integerToWords (integerPart) {\n // Zero\n if (integerPart === 0n) {\n return this.zeroWord\n }\n\n // 1-9\n if (integerPart <= 9n) {\n return this.onesWords[integerPart]\n }\n\n // 10-19\n if (integerPart <= 19n) {\n return this.teensWords[integerPart]\n }\n\n // 20-99\n if (integerPart < 100n) {\n const ones = integerPart % 10n\n const tens = integerPart - ones\n if (ones === 0n) {\n return this.tensWords[tens]\n }\n return `${this.tensWords[tens]} و ${this.onesWords[ones]}`\n }\n\n // 100-999\n if (integerPart < 1000n) {\n const hundreds = 100n * (integerPart / 100n)\n const remainder = integerPart - hundreds\n if (remainder === 0n) {\n return this.hundredsWords[hundreds]\n }\n return `${this.hundredsWords[hundreds]} و ${this.integerToWords(remainder)}`\n }\n\n // 1000-999999\n if (integerPart < 1_000_000n) {\n const thousandMultiplier = integerPart / 1000n\n // Persian omits \"one\" before thousand: 1000 is just \"هزار\", not \"یک هزار\"\n const thousandPrefix = thousandMultiplier === 1n\n ? ''\n : this.integerToWords(thousandMultiplier) + ' '\n const remainder = integerPart % 1000n\n const suffix = remainder === 0n ? '' : ' ' + this.integerToWords(remainder)\n return `${thousandPrefix}${this.scaleWords[1000]}${suffix}`\n }\n\n // 1000000+\n const millionMultiplier = integerPart / 1_000_000n\n const millionPrefix = this.integerToWords(millionMultiplier) + ' ' + this.scaleWords[1_000_000]\n const remainder = integerPart % 1_000_000n\n const suffix = remainder === 0n ? '' : ' و ' + this.integerToWords(remainder)\n return `${millionPrefix}${suffix}`\n }\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Polish language converter.\n *\n * Supports:\n * - Three-form pluralization (one/few/many)\n * - Polish-specific declension patterns\n * - Distinctive Polish phonology and orthography\n */\nexport class Polish extends SlavicLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'przecinek'\n zeroWord = 'zero'\n\n onesWords = {\n 1: 'jeden',\n 2: 'dwa',\n 3: 'trzy',\n 4: 'cztery',\n 5: 'pięć',\n 6: 'sześć',\n 7: 'siedem',\n 8: 'osiem',\n 9: 'dziewięć'\n }\n\n onesFeminineWords = {\n 1: 'jedna',\n 2: 'dwie',\n 3: 'trzy',\n 4: 'cztery',\n 5: 'pięć',\n 6: 'sześć',\n 7: 'siedem',\n 8: 'osiem',\n 9: 'dziewięć'\n }\n\n teensWords = {\n 0: 'dziesięć',\n 1: 'jedenaście',\n 2: 'dwanaście',\n 3: 'trzynaście',\n 4: 'czternaście',\n 5: 'piętnaście',\n 6: 'szesnaście',\n 7: 'siedemnaście',\n 8: 'osiemnaście',\n 9: 'dziewiętnaście'\n }\n\n twentiesWords = {\n 2: 'dwadzieścia',\n 3: 'trzydzieści',\n 4: 'czterdzieści',\n 5: 'pięćdziesiąt',\n 6: 'sześćdziesiąt',\n 7: 'siedemdziesiąt',\n 8: 'osiemdziesiąt',\n 9: 'dziewięćdziesiąt'\n }\n\n hundredsWords = {\n 1: 'sto',\n 2: 'dwieście',\n 3: 'trzysta',\n 4: 'czterysta',\n 5: 'pięćset',\n 6: 'sześćset',\n 7: 'siedemset',\n 8: 'osiemset',\n 9: 'dziewięćset'\n }\n\n pluralForms = {\n 1: ['tysiąc', 'tysiące', 'tysięcy'], // 10^ 3\n 2: ['milion', 'miliony', 'milionów'], // 10^ 6\n 3: ['miliard', 'miliardy', 'miliardów'], // 10^ 9\n 4: ['bilion', 'biliony', 'bilionów'], // 10^ 12\n 5: ['biliard', 'biliardy', 'biliardów'], // 10^ 15\n 6: ['trylion', 'tryliony', 'trylionów'], // 10^ 18\n 7: ['tryliard', 'tryliardy', 'tryliardów'], // 10^ 21\n 8: ['kwadrylion', 'kwadryliony', 'kwadrylionów'], // 10^ 24\n 9: ['kwaryliard', 'kwadryliardy', 'kwadryliardów'], // 10^ 27\n 10: ['kwintylion', 'kwintyliony', 'kwintylionów'] // 10^ 30\n }\n\n /**\n * Polish omits \"one\" before scale words.\n * e.g., 1000 is \"tysiąc\" not \"jeden tysiąc\"\n */\n omitOneBeforeScale = true\n\n /** Implements Polish-specific three-form pluralization rules. */\n 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 if (lastDigit < 5n && lastDigit > 1n && (lastTwoDigits < 10n || lastTwoDigits > 20n)) {\n return forms[1]\n }\n\n return forms[2]\n }\n}\n","import { SouthAsianLanguage } from '../classes/south-asian-language.js'\n\n/**\n * Punjabi language converter.\n *\n * Supports:\n * - Indian numbering system (ਹਜ਼ਾਰ, ਲੱਖ, ਕਰੋੜ)\n * - Gurmukhi script\n * - Complete word forms for 0-99\n */\nexport class Punjabi extends SouthAsianLanguage {\n negativeWord = 'ਮਾਇਨਸ'\n decimalSeparatorWord = 'ਦਸ਼ਮਲਵ'\n zeroWord = 'ਸਿਫ਼ਰ'\n hundredWord = 'ਸੌ'\n\n belowHundredWords = [\n 'ਸਿਫ਼ਰ',\n 'ਇੱਕ',\n 'ਦੋ',\n 'ਤਿੰਨ',\n 'ਚਾਰ',\n 'ਪੰਜ',\n 'ਛੇ',\n 'ਸੱਤ',\n 'ਅੱਠ',\n 'ਨੌਂ',\n 'ਦੱਸ',\n 'ਗਿਆਰਾਂ',\n 'ਬਾਰਾਂ',\n 'ਤੇਰਾਂ',\n 'ਚੌਦਾਂ',\n 'ਪੰਦਰਾਂ',\n 'ਸੋਲਾਂ',\n 'ਸਤਾਰਾਂ',\n 'ਅਠਾਰਾਂ',\n 'ਉੱਨੀ',\n 'ਵੀਹ',\n 'ਇੱਕੀ',\n 'ਬਾਈ',\n 'ਤੇਈ',\n 'ਚੌਬੀ',\n 'ਪੱਚੀ',\n 'ਛੱਬੀ',\n 'ਸਤਾਈ',\n 'ਅਠਾਈ',\n 'ਉਨੱਤੀ',\n 'ਤੀਹ',\n 'ਇਕੱਤੀ',\n 'ਬੱਤੀ',\n 'ਤੇਤੀ',\n 'ਚੌਂਤੀ',\n 'ਪੈਂਤੀ',\n 'ਛੱਤੀ',\n 'ਸੈਂਤੀ',\n 'ਅਠੱਤੀ',\n 'ਉਨਤਾਲੀ',\n 'ਚਾਲੀ',\n 'ਇਕਤਾਲੀ',\n 'ਬਿਆਲੀ',\n 'ਤਿਰਤਾਲੀ',\n 'ਚੁਵਾਲੀ',\n 'ਪੰਤਾਲੀ',\n 'ਛਿਆਲੀ',\n 'ਸੈਂਤਾਲੀ',\n 'ਅਠਤਾਲੀ',\n 'ਉਨੰਜਾ',\n 'ਪੰਜਾਹ',\n 'ਇਕਵੰਜਾ',\n 'ਬਵੰਜਾ',\n 'ਤਰਵੰਜਾ',\n 'ਚੁਰਵੰਜਾ',\n 'ਪੰਜਵੰਜਾ',\n 'ਛਪੰਜਾ',\n 'ਸੱਤਵੰਜਾ',\n 'ਅਠਵੰਜਾ',\n 'ਉਨਾਹਠ',\n 'ਸੱਠ',\n 'ਇਕਾਹਠ',\n 'ਬਾਹਠ',\n 'ਤਰਸਠ',\n 'ਚੌਂਸਠ',\n 'ਪੈਂਸਠ',\n 'ਛਿਆਸਠ',\n 'ਸੜਸਠ',\n 'ਅੜਸਠ',\n 'ਉਣਹੱਤਰ',\n 'ਸਤੱਰ',\n 'ਇਕਹੱਤਰ',\n 'ਬਹੱਤਰ',\n 'ਤਹੱਤਰ',\n 'ਚੌਹੱਤਰ',\n 'ਪੰਝਹੱਤਰ',\n 'ਛਿਹੱਤਰ',\n 'ਸਤੱਤਰ',\n 'ਅਠੱਤਰ',\n 'ਉਨਾਸੀ',\n 'ਅੱਸੀ',\n 'ਇਕਿਆਸੀ',\n 'ਬਿਆਸੀ',\n 'ਤਰਿਆਸੀ',\n 'ਚੌਰਿਆਸੀ',\n 'ਪਚਾਸੀ',\n 'ਛਿਆਸੀ',\n 'ਸੱਤਾਸੀ',\n 'ਅਠਾਸੀ',\n 'ਨਵਾਸੀ',\n 'ਨੱਬੇ',\n 'ਇਕਾਨਵੇਂ',\n 'ਬਾਨਵੇਂ',\n 'ਤਰਾਨਵੇਂ',\n 'ਚੁਰਾਨਵੇਂ',\n 'ਪੰਚਾਨਵੇਂ',\n 'ਛਿਆਨਵੇਂ',\n 'ਸਤਾਨਵੇਂ',\n 'ਅਠਾਨਵੇਂ',\n 'ਨਿਨਾਨਵੇਂ'\n ]\n\n scaleWords = [\n '',\n 'ਹਜ਼ਾਰ',\n 'ਲੱਖ',\n 'ਕਰੋੜ',\n 'ਅਰਬ',\n 'ਖਰਬ',\n 'ਨੀਲ',\n 'ਪਦਮ',\n 'ਸ਼ੰਖ'\n ]\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Romanian language converter.\n *\n * Supports:\n * - Gender agreement (unu/una, doi/două)\n * - Complex pluralization (singular/plural forms)\n * - \"De\" preposition insertion for groups >= 20\n */\nexport class Romanian extends AbstractLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'virgulă'\n zeroWord = 'zero'\n\n onesWords = {\n 1: 'unu',\n 2: 'doi',\n 3: 'trei',\n 4: 'patru',\n 5: 'cinci',\n 6: 'șase',\n 7: 'șapte',\n 8: 'opt',\n 9: 'nouă'\n }\n\n onesFeminineWords = {\n 1: 'una',\n 2: 'două',\n 3: 'trei',\n 4: 'patru',\n 5: 'cinci',\n 6: 'șase',\n 7: 'șapte',\n 8: 'opt',\n 9: 'nouă'\n }\n\n teensWords = {\n 0: 'zece',\n 1: 'unsprezece',\n 2: 'douăsprezece',\n 3: 'treisprezece',\n 4: 'paisprezece',\n 5: 'cincisprezece',\n 6: 'șaisprezece',\n 7: 'șaptesprezece',\n 8: 'optsprezece',\n 9: 'nouăsprezece'\n }\n\n teensMasculineWords = {\n 0: 'zece',\n 1: 'unsprezece',\n 2: 'doisprezece',\n 3: 'treisprezece',\n 4: 'paisprezece',\n 5: 'cincisprezece',\n 6: 'șaisprezece',\n 7: 'șaptesprezece',\n 8: 'optsprezece',\n 9: 'nouăsprezece'\n }\n\n twentiesWords = {\n 2: 'douăzeci',\n 3: 'treizeci',\n 4: 'patruzeci',\n 5: 'cincizeci',\n 6: 'șaizeci',\n 7: 'șaptezeci',\n 8: 'optzeci',\n 9: 'nouăzeci'\n }\n\n hundredsWords = {\n 1: 'o sută',\n 2: 'două sute',\n 3: 'trei sute',\n 4: 'patru sute',\n 5: 'cinci sute',\n 6: 'șase sute',\n 7: 'șapte sute',\n 8: 'opt sute',\n 9: 'nouă sute'\n }\n\n /**\n * Romanian big units.\n * For each power group we keep: singular, plural, feminineUnits?, needsDe?\n * - 10^3: mie/mii (feminine units in segment; \"de\" for segment >= 20)\n * - 10^6: milion/milioane (\"de\" for segment >= 20)\n * - 10^9: miliard/miliarde (\"de\" for segment >= 20)\n */\n scaleMetadata = {\n 1: { singular: 'mie', plural: 'mii', feminine: true, needsDe: true }, // 10^3\n 2: { singular: 'milion', plural: 'milioane', feminine: false, needsDe: true }, // 10^6\n 3: { singular: 'miliard', plural: 'miliarde', feminine: false, needsDe: true }, // 10^9\n 4: { singular: 'trilion', plural: 'trilioane', feminine: false, needsDe: true }, // 10^12\n 5: { singular: 'cvadrilion', plural: 'cvadrilioane', feminine: false, needsDe: true }, // 10^15\n 6: { singular: 'cvintilion', plural: 'cvintilioane', feminine: false, needsDe: true }, // 10^18\n 7: { singular: 'sextilion', plural: 'sextilioane', feminine: false, needsDe: true }, // 10^21\n 8: { singular: 'septilion', plural: 'septilioane', feminine: false, needsDe: true }, // 10^24\n 9: { singular: 'octilion', plural: 'octilioane', feminine: false, needsDe: true }, // 10^27\n 10: { singular: 'decilion', plural: 'decilioane', feminine: false, needsDe: true } // 10^33\n }\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n gender: 'masculine'\n }, options)\n }\n\n /** Split numeric string into BigInt segments of specified size from left to right. */\n splitToSegments (n, x) {\n const results = []\n const l = n.length\n let result\n\n if (l > x) {\n const start = l % x\n if (start > 0) {\n result = n.slice(0, start)\n results.push(BigInt(result))\n }\n for (let index = start; index < l; index += x) {\n result = n.slice(index, index + x)\n results.push(BigInt(result))\n }\n } else {\n results.push(BigInt(n))\n }\n return results\n }\n\n extractDigits (value) {\n const stringValue = value.toString().padStart(3, '0').slice(-3)\n const a = [...stringValue].toReversed()\n return a.map(BigInt)\n }\n\n /** Romanian pluralization & \"de\" rule for big units. */\n romanianPluralize (segment, form) {\n const n = Number(segment)\n\n if (n === 1) {\n // article differs for feminine \"mie\" (o mie) vs the rest (un milion/miliard…)\n const article = form.feminine ? 'o' : 'un'\n return `${article} ${form.singular}`\n }\n\n // For 21 with big units, use feminine \"una\" with plural nouns\n if (n === 21 && form.needsDe) {\n return `douăzeci și una de ${form.plural}`\n }\n\n // spell the segment itself (use feminine units for big numbers)\n const words = this.spellUnder1000(n, true)\n\n // \"de\" after >= 20 (covers 20, 21, 100, 101, 999, etc.)\n const needsDe = form.needsDe && n >= 20\n const de = needsDe ? ' de ' : ' '\n\n return `${words}${de}${form.plural}`\n }\n\n spellUnder100 (n, feminineUnits = false, masculineTeens = false) {\n if (n < 10) {\n return (feminineUnits ? this.onesFeminineWords : this.onesWords)[n]\n }\n if (n < 20) {\n return (masculineTeens ? this.teensMasculineWords : this.teensWords)[n - 10]\n }\n const t = Math.floor(n / 10)\n const u = n % 10\n return u\n ? `${this.twentiesWords[t]} și ${(feminineUnits ? this.onesFeminineWords : this.onesWords)[u]}`\n : this.twentiesWords[t]\n }\n\n spellUnder1000 (n, feminineUnits = false, masculineTeens = false) {\n if (n < 100) return this.spellUnder100(n, feminineUnits, masculineTeens)\n const h = Math.floor(n / 100)\n const r = n % 100\n const hundredWords = this.hundredsWords[h]\n if (!r) return hundredWords\n // Standard readable form: \"o sută unu\" (for units) or \"o sută cincizeci\" (for tens)\n const separator = ' '\n return `${hundredWords}${separator}${this.spellUnder100(r, feminineUnits, masculineTeens)}`\n }\n\n /** Decimals always use masculine forms regardless of gender option. */\n decimalIntegerToWords (integerPart) {\n if (integerPart === 0n) return this.zeroWord\n // Use spellUnder1000 with masculine forms (feminineUnits=false, masculineTeens=true)\n if (integerPart < 1000n) return this.spellUnder1000(Number(integerPart), false, true)\n // For larger decimals, process segments with masculine forms\n const words = []\n const segments = this.splitToSegments(integerPart.toString(), 3)\n let index = segments.length\n for (const x of segments) {\n index = index - 1\n if (x === 0n) continue\n const [n1, n2, n3] = this.extractDigits(x)\n if (n3 > 0n) words.push(this.hundredsWords[Number(n3)])\n if (n2 > 1n) words.push(this.twentiesWords[Number(n2)])\n if (n2 === 1n) {\n words.push(this.teensMasculineWords[Number(n1)])\n } else if (n1 > 0n) {\n if (n2 > 1n) words.push('și')\n words.push(this.onesWords[Number(n1)])\n }\n if (index > 0) {\n const form = this.scaleMetadata[index]\n if (form) {\n words.push(this.romanianPluralize(x, form))\n } else {\n words.push(this.spellUnder1000(Number(x), false))\n }\n }\n }\n return words.join(' ').replaceAll(/\\s+/g, ' ').trim()\n }\n\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n const words = []\n const segments = this.splitToSegments(integerPart.toString(), 3)\n let index = segments.length\n for (const x of segments) {\n let onesMap = []\n index = index - 1\n if (x === 0n) continue\n const [n1, n2, n3] = this.extractDigits(x) // units, tens, hundreds (as BigInt)\n // hundreds (only for the last group, not for thousands)\n if (n3 > 0n && index === 0) {\n words.push(this.hundredsWords[Number(n3)])\n }\n // tens & teens (only for the last group, not for thousands)\n if (index === 0) {\n if (n2 > 1n) {\n words.push(this.twentiesWords[Number(n2)])\n }\n if (n2 === 1n) {\n words.push(this.teensWords[Number(n1)])\n } else if (n1 > 0n) {\n // pick masculine/feminine units (only for the last group, not for thousands)\n const feminineUnits = this.options.gender === 'feminine' && index === 0\n onesMap = feminineUnits ? this.onesFeminineWords : this.onesWords\n // if there is a twenty/treizeci/etc AND ones > 0 → add \"și\"\n if (n2 > 1n) words.push('și')\n words.push(onesMap[Number(n1)])\n }\n }\n // big unit name (mie/mii, milion/milioane, …)\n if (index > 0) {\n const form = this.scaleMetadata[index]\n if (form) {\n words.push(this.romanianPluralize(x, form))\n } else {\n // For very large numbers beyond our defined units, just spell out the number\n words.push(this.spellUnder1000(Number(x), true))\n }\n }\n }\n return words.join(' ').replaceAll(/\\s+/g, ' ').trim()\n }\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Russian language converter.\n *\n * Supports:\n * - Gender agreement (masculine/feminine forms)\n * - Three-form pluralization (one/few/many)\n * - Large numbers up to nonillions\n */\nexport class Russian extends SlavicLanguage {\n negativeWord = 'минус'\n decimalSeparatorWord = 'запятая'\n zeroWord = 'ноль'\n\n onesWords = {\n 1: 'один',\n 2: 'два',\n 3: 'три',\n 4: 'четыре',\n 5: 'пять',\n 6: 'шесть',\n 7: 'семь',\n 8: 'восемь',\n 9: 'девять'\n }\n\n onesFeminineWords = {\n 1: 'одна',\n 2: 'две',\n 3: 'три',\n 4: 'четыре',\n 5: 'пять',\n 6: 'шесть',\n 7: 'семь',\n 8: 'восемь',\n 9: 'девять'\n }\n\n teensWords = {\n 0: 'десять',\n 1: 'одиннадцать',\n 2: 'двенадцать',\n 3: 'тринадцать',\n 4: 'четырнадцать',\n 5: 'пятнадцать',\n 6: 'шестнадцать',\n 7: 'семнадцать',\n 8: 'восемнадцать',\n 9: 'девятнадцать'\n }\n\n twentiesWords = {\n 2: 'двадцать',\n 3: 'тридцать',\n 4: 'сорок',\n 5: 'пятьдесят',\n 6: 'шестьдесят',\n 7: 'семьдесят',\n 8: 'восемьдесят',\n 9: 'девяносто'\n }\n\n hundredsWords = {\n 1: 'сто',\n 2: 'двести',\n 3: 'триста',\n 4: 'четыреста',\n 5: 'пятьсот',\n 6: 'шестьсот',\n 7: 'семьсот',\n 8: 'восемьсот',\n 9: 'девятьсот'\n }\n\n pluralForms = {\n 1: ['тысяча', 'тысячи', 'тысяч'], // 10^ 3\n 2: ['миллион', 'миллиона', 'миллионов'], // 10^ 6\n 3: ['миллиард', 'миллиарда', 'миллиардов'], // 10^ 9\n 4: ['триллион', 'триллиона', 'триллионов'], // 10^ 12\n 5: ['квадриллион', 'квадриллиона', 'квадриллионов'], // 10^ 15\n 6: ['квинтиллион', 'квинтиллиона', 'квинтиллионов'], // 10^ 18\n 7: ['секстиллион', 'секстиллиона', 'секстиллионов'], // 10^ 21\n 8: ['септиллион', 'септиллиона', 'септиллионов'], // 10^ 24\n 9: ['октиллион', 'октиллиона', 'октиллионов'], // 10^ 27\n 10: ['нониллион', 'нониллиона', 'нониллионов'] // 10^ 30\n }\n\n /**\n * Russian thousands (тысяча) are feminine, requiring одна/две forms.\n * Other scales (million, billion, etc.) are masculine.\n */\n scaleGenders = {\n 1: true // thousands are feminine\n }\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Serbian language converter (Cyrillic script).\n *\n * Supports:\n * - Three-form pluralization (one/few/many)\n * - Gender agreement (један/једна, два/две)\n * - Cyrillic script representation\n */\nexport class SerbianCyrillic extends SlavicLanguage {\n negativeWord = 'минус'\n decimalSeparatorWord = 'запета'\n zeroWord = 'нула'\n\n onesWords = {\n 1: 'један',\n 2: 'два',\n 3: 'три',\n 4: 'четири',\n 5: 'пет',\n 6: 'шест',\n 7: 'седам',\n 8: 'осам',\n 9: 'девет'\n }\n\n onesFeminineWords = {\n 1: 'једна',\n 2: 'две',\n 3: 'три',\n 4: 'четири',\n 5: 'пет',\n 6: 'шест',\n 7: 'седам',\n 8: 'осам',\n 9: 'девет'\n }\n\n teensWords = {\n 0: 'десет',\n 1: 'једанаест',\n 2: 'дванаест',\n 3: 'тринаест',\n 4: 'четрнаест',\n 5: 'петнаест',\n 6: 'шеснаест',\n 7: 'седамнаест',\n 8: 'осамнаест',\n 9: 'деветнаест'\n }\n\n twentiesWords = {\n 2: 'двадесет',\n 3: 'тридесет',\n 4: 'четрдесет',\n 5: 'педесет',\n 6: 'шездесет',\n 7: 'седамдесет',\n 8: 'осамдесет',\n 9: 'деведесет'\n }\n\n hundredsWords = {\n 1: 'сто',\n 2: 'двеста',\n 3: 'триста',\n 4: 'четиристо',\n 5: 'петсто',\n 6: 'шесто',\n 7: 'седамсто',\n 8: 'осамсто',\n 9: 'девестo'\n }\n\n pluralForms = {\n 1: ['хиљада', 'хиљаде', 'хиљада'], // 10 ^ 3\n 2: ['милион', 'милиона', 'милиона'], // 10 ^ 6\n 3: ['милијарда', 'милијарде', 'милијарда'], // 10 ^ 9\n 4: ['билион', 'билиона', 'билиона'], // 10 ^ 12\n 5: ['билијарда', 'билијарде', 'билијарда'], // 10 ^ 15\n 6: ['трилион', 'трилиона', 'трилиона'], // 10 ^ 18\n 7: ['трилијарда', 'трилијарде', 'трилијарда'], // 10 ^ 21\n 8: ['квадрилион', 'квадрилиона', 'квадрилиона'], // 10 ^ 24\n 9: ['квадрилијарда', 'квадрилијарде', 'квадрилијарда'], // 10 ^ 27\n 10: ['квинтилион', 'квинтилиона', 'квинтилиона'] // 10 ^ 30\n }\n\n /**\n * Maps segment indices to whether they are grammatically feminine.\n * In Serbian, thousands (index 1) are feminine, others are masculine.\n * @type {Object.<number, boolean>}\n */\n scaleGenders = {\n 1: true // thousands are feminine (others default to false)\n }\n\n /** Selects Serbian plural form: 1 = singular, 2-4 = few, else = many. */\n pluralize (n, forms) {\n const lastDigit = n % 10n\n const lastTwoDigits = n % 100n\n\n if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit === 1n) {\n return forms[0]\n }\n\n if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit > 1n && lastDigit < 5n) {\n return forms[1]\n }\n\n return forms[2]\n }\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Serbian Latin language converter.\n *\n * Supports:\n * - Three-form pluralization (one/few/many)\n * - Gender agreement (jedan/jedna, dva/dve)\n * - Latin script representation\n */\nexport class SerbianLatin extends SlavicLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'zapeta'\n zeroWord = 'nula'\n\n onesWords = {\n 1: 'jedan',\n 2: 'dva',\n 3: 'tri',\n 4: 'četiri',\n 5: 'pet',\n 6: 'šest',\n 7: 'sedam',\n 8: 'osam',\n 9: 'devet'\n }\n\n onesFeminineWords = {\n 1: 'jedna',\n 2: 'dve',\n 3: 'tri',\n 4: 'četiri',\n 5: 'pet',\n 6: 'šest',\n 7: 'sedam',\n 8: 'osam',\n 9: 'devet'\n }\n\n teensWords = {\n 0: 'deset',\n 1: 'jedanaest',\n 2: 'dvanaest',\n 3: 'trinaest',\n 4: 'četrnaest',\n 5: 'petnaest',\n 6: 'šesnaest',\n 7: 'sedamnaest',\n 8: 'osamnaest',\n 9: 'devetnaest'\n }\n\n twentiesWords = {\n 2: 'dvadeset',\n 3: 'trideset',\n 4: 'četrdeset',\n 5: 'pedeset',\n 6: 'šezdeset',\n 7: 'sedamdeset',\n 8: 'osamdeset',\n 9: 'devedeset'\n }\n\n hundredsWords = {\n 1: 'sto',\n 2: 'dvesta',\n 3: 'trista',\n 4: 'četiristo',\n 5: 'petsto',\n 6: 'šesto',\n 7: 'sedamsto',\n 8: 'osamsto',\n 9: 'devetsto'\n }\n\n pluralForms = {\n 1: ['hiljada', 'hiljade', 'hiljada'], // 10 ^ 3\n 2: ['milion', 'miliona', 'miliona'], // 10 ^ 6\n 3: ['milijarda', 'milijarde', 'milijarda'], // 10 ^ 9\n 4: ['bilion', 'biliona', 'biliona'], // 10 ^ 12\n 5: ['bilijarda', 'bilijarde', 'bilijarda'], // 10 ^ 15\n 6: ['trilion', 'triliona', 'triliona'], // 10 ^ 18\n 7: ['trilijarda', 'trilijarde', 'trilijarda'], // 10 ^ 21\n 8: ['kvadrilion', 'kvadriliona', 'kvadriliona'], // 10 ^ 24\n 9: ['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda'], // 10 ^ 27\n 10: ['kvintilion', 'kvintiliona', 'kvintiliona'] // 10 ^ 30\n }\n\n /**\n * Maps segment indices to whether they are grammatically feminine.\n * In Serbian, thousands (index 1) are feminine, others are masculine.\n * @type {Object.<number, boolean>}\n */\n scaleGenders = {\n 1: true // thousands are feminine (others default to false)\n }\n\n /** Selects Serbian plural form: 1 = singular, 2-4 = few, else = many. */\n pluralize (n, forms) {\n const lastDigit = n % 10n\n const lastTwoDigits = n % 100n\n\n if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit === 1n) {\n return forms[0]\n }\n\n if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit > 1n && lastDigit < 5n) {\n return forms[1]\n }\n\n return forms[2]\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Simplified Chinese language converter.\n *\n * Supports:\n * - Simplified Chinese characters (简体中文)\n * - No word separators (concatenated format)\n * - Optional formal (financial) vs common numerals\n */\nexport class SimplifiedChinese extends GreedyScaleLanguage {\n negativeWord = '负'\n decimalSeparatorWord = '点'\n zeroWord = '零'\n wordSeparator = ''\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n formal: true\n }, options)\n\n if (this.options.formal) {\n this.scaleWords = [\n [1_000_000_000_000n, '万'],\n [100_000_000n, '亿'],\n [10_000n, '万'],\n [1000n, '仟'],\n [100n, '佰'],\n [10n, '拾'],\n [9n, '玖'],\n [8n, '捌'],\n [7n, '柒'],\n [6n, '陆'],\n [5n, '伍'],\n [4n, '肆'],\n [3n, '叁'],\n [2n, '贰'],\n [1n, '壹'],\n [0n, '零']\n ]\n } else {\n this.scaleWords = [\n [1_000_000_000_000n, '万'],\n [100_000_000n, '亿'],\n [10_000n, '万'],\n [1000n, '千'],\n [100n, '百'],\n [10n, '十'],\n [9n, '九'],\n [8n, '八'],\n [7n, '七'],\n [6n, '六'],\n [5n, '五'],\n [4n, '四'],\n [3n, '三'],\n [2n, '二'],\n [1n, '一'],\n [0n, '零']\n ]\n }\n }\n\n /** Combines two word-sets according to Chinese grammar rules. */\n combineWordSets (preceding, following) {\n const precedingWord = Object.keys(preceding)[0]\n const precedingValue = Object.values(preceding)[0]\n const followingWord = Object.keys(following)[0]\n const followingValue = Object.values(following)[0]\n\n // Implicit one: omit 1 before single digits (< 10)\n if (precedingValue === 1n && followingValue < 10n) {\n return following\n }\n\n // Multiply when following > preceding (scale words like 仁, 万, 亿)\n if (followingValue > precedingValue) {\n return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }\n }\n\n // Insert \"零\" (zero) when position skip levels (e.g., 1003 = 千零三)\n // zeroDigit() checks if gap exists between preceding and following magnitude\n if (this.zeroDigit(precedingValue) > this.digit(followingValue)) {\n return { [`${precedingWord}${this.zeroWord}${followingWord}`]: precedingValue + followingValue }\n }\n\n // Default: concatenate without zero insertion\n return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }\n }\n\n /** Returns the number of digits in a number. */\n digit (number_) {\n return number_.toString().length\n }\n\n /** Counts the number of zero digits in a number. */\n zeroDigit (number_) {\n return [...number_.toString()].filter(c => c === '0').length\n }\n\n /** Converts decimal digits to words by reading each digit individually. */\n decimalDigitsToWords (decimalString) {\n const words = []\n for (let i = 0; i < decimalString.length; i++) {\n const digitValue = BigInt(decimalString[i])\n words.push(this.integerToWords(digitValue))\n }\n return words\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Spanish language converter.\n *\n * Supports:\n * - Gender agreement (masculine/feminine via genderStem option)\n * - \"y\" conjunction between tens and units\n * - Special hundred forms (cien/ciento)\n */\nexport class Spanish extends GreedyScaleLanguage {\n negativeWord = 'menos'\n decimalSeparatorWord = 'punto'\n zeroWord = 'cero'\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000n, 'cuatrillón'],\n [1_000_000_000_000_000_000n, 'trillón'],\n [1_000_000_000_000n, 'billón'],\n [1_000_000n, 'millón'],\n [1000n, 'mil'],\n [100n, 'cien'],\n [90n, 'noventa'],\n [80n, 'ochenta'],\n [70n, 'setenta'],\n [60n, 'sesenta'],\n [50n, 'cincuenta'],\n [40n, 'cuarenta'],\n [30n, 'treinta'],\n [29n, 'veintinueve'],\n [28n, 'veintiocho'],\n [27n, 'veintisiete'],\n [26n, 'veintiséis'],\n [25n, 'veinticinco'],\n [24n, 'veinticuatro'],\n [23n, 'veintitrés'],\n [22n, 'veintidós'],\n [21n, 'veintiuno'],\n [20n, 'veinte'],\n [19n, 'diecinueve'],\n [18n, 'dieciocho'],\n [17n, 'diecisiete'],\n [16n, 'dieciseis'],\n [15n, 'quince'],\n [14n, 'catorce'],\n [13n, 'trece'],\n [12n, 'doce'],\n [11n, 'once'],\n [10n, 'diez'],\n [9n, 'nueve'],\n [8n, 'ocho'],\n [7n, 'siete'],\n [6n, 'seis'],\n [5n, 'cinco'],\n [4n, 'cuatro'],\n [3n, 'tres'],\n [2n, 'dos'],\n [1n, 'uno'],\n [0n, 'cero']\n ]\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n gender: 'masculine'\n }, options)\n\n // Apply gender agreement to scale words if feminine\n if (this.options.gender === 'feminine') {\n this.scaleWords = this.scaleWords.map(([value, word]) => {\n if (word === 'veintiuno') return [value, 'veintiuna']\n if (word === 'uno') return [value, 'una']\n return [value, word]\n })\n }\n }\n\n /** Combines two word-sets with Spanish gender and conjunction rules. */\n combineWordSets (preceding, following) {\n let precedingWord = Object.keys(preceding)[0]\n let followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0]\n const followingValue = Object.values(following)[0]\n const genderStem = this.options.gender === 'feminine' ? 'a' : 'o'\n\n if (precedingValue === 1n) {\n if (followingValue < 1_000_000n) return following\n precedingWord = 'un'\n } else if (precedingValue === 100n && followingValue % 1000n !== 0n) {\n precedingWord += 't' + genderStem\n }\n\n if (followingValue < precedingValue) {\n if (precedingValue < 100n) {\n return { [`${precedingWord} y ${followingWord}`]: precedingValue + followingValue }\n }\n return { [`${precedingWord} ${followingWord}`]: precedingValue + followingValue }\n }\n\n if (followingValue % 1_000_000n === 0n && precedingValue > 1n) {\n followingWord = followingWord.slice(0, -3) + 'lones'\n }\n\n if (followingValue === 100n) {\n if (precedingValue === 5n) {\n precedingWord = 'quinien'\n followingWord = ''\n } else if (precedingValue === 7n) {\n precedingWord = 'sete'\n } else if (precedingValue === 9n) {\n precedingWord = 'nove'\n }\n followingWord += 't' + genderStem + 's'\n } else {\n followingWord = ' ' + followingWord\n }\n\n return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }\n }\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Swahili language converter.\n *\n * Supports:\n * - Swahili number words (moja, mbili, tatu)\n * - Space-separated components\n * - Simple decimal point notation\n */\nexport class Swahili extends AbstractLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'nukta'\n zeroWord = 'sifuri'\n\n onesWords = {\n 0: 'sifuri',\n 1: 'moja',\n 2: 'mbili',\n 3: 'tatu',\n 4: 'nne',\n 5: 'tano',\n 6: 'sita',\n 7: 'saba',\n 8: 'nane',\n 9: 'tisa'\n }\n\n tensWords = {\n 10: 'kumi',\n 20: 'ishirini',\n 30: 'thelathini',\n 40: 'arobaini',\n 50: 'hamsini',\n 60: 'sitini',\n 70: 'sabini',\n 80: 'themanini',\n 90: 'tisini'\n }\n\n scaleWords = [\n '',\n 'elfu',\n 'milioni',\n 'bilioni',\n 'trilioni'\n ]\n\n wordsUnder100 (n) {\n if (n < 10) return this.onesWords[n]\n if (n === 10) return this.tensWords[10]\n if (n < 20) {\n // 11-19: 'kumi na <digit>'\n return this.tensWords[10] + ' na ' + this.onesWords[n - 10]\n }\n const tens = Math.trunc(n / 10) * 10\n const ones = n % 10\n if (ones === 0) return this.tensWords[tens]\n return this.tensWords[tens] + ' na ' + this.onesWords[ones]\n }\n\n wordsUnder1000 (n) {\n if (n < 100) return this.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 ' + this.onesWords[hundreds])\n if (rest > 0) {\n if (rest < 10) {\n parts.push('na ' + this.onesWords[rest])\n } else {\n parts.push(this.wordsUnder100(rest))\n }\n }\n\n return parts.join(' ')\n }\n\n splitBy3 (number) {\n const s = number.toString()\n if (s.length <= 3) return [Number(s)]\n const groups = []\n const last3 = s.slice(-3)\n groups.unshift(Number(last3))\n let remaining = s.slice(0, -3)\n while (remaining.length > 0) {\n const group = remaining.slice(-3)\n groups.unshift(Number(group))\n remaining = remaining.slice(0, -3)\n }\n return groups\n }\n\n integerToWords (integerPart) {\n if (integerPart === 0n) return this.zeroWord\n\n const groups = this.splitBy3(integerPart)\n const parts = []\n\n for (let i = 0; i < groups.length; i++) {\n const val = groups[i]\n if (val === 0) continue\n const scaleIndex = groups.length - i - 1\n // scale word\n if (scaleIndex === 0) {\n if (val < 10 && parts.length > 0) {\n parts.push('na ' + this.onesWords[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(this.wordsUnder1000(val))\n }\n } else {\n // e.g., 'elfu moja', 'milioni mbili'\n const unit = (val === 1) ? 'moja' : this.wordsUnder1000(val)\n parts.push(this.scaleWords[scaleIndex] + ' ' + unit)\n }\n }\n\n return parts.join(' ').trim()\n }\n}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Swedish language converter.\n *\n * Supports:\n * - Hyphenation for compound tens (tjugo-tre)\n * - \"och\" (and) after hundreds\n * - Space-separated larger composites\n */\nexport class Swedish extends GreedyScaleLanguage {\n negativeWord = 'minus'\n decimalSeparatorWord = 'komma'\n zeroWord = 'noll'\n wordSeparator = ' '\n\n scaleWords = [\n [1_000_000_000_000_000_000_000_000_000n, 'kvadriljon'],\n [1_000_000_000_000_000_000_000_000n, 'triljon'],\n [1_000_000_000_000_000_000_000n, 'biljon'],\n [1_000_000_000_000_000_000n, 'miljard'],\n [1_000_000_000_000n, 'biljon'],\n [1_000_000_000n, 'miljard'],\n [1_000_000n, 'miljon'],\n [1000n, 'tusen'],\n [100n, 'hundra'],\n [90n, 'nittio'],\n [80n, 'åttio'],\n [70n, 'sjuttio'],\n [60n, 'sextio'],\n [50n, 'femtio'],\n [40n, 'fyrtio'],\n [30n, 'trettio'],\n [20n, 'tjugo'],\n [19n, 'nitton'],\n [18n, 'arton'],\n [17n, 'sjutton'],\n [16n, 'sexton'],\n [15n, 'femton'],\n [14n, 'fjorton'],\n [13n, 'tretton'],\n [12n, 'tolv'],\n [11n, 'elva'],\n [10n, 'tio'],\n [9n, 'nio'],\n [8n, 'åtta'],\n [7n, 'sju'],\n [6n, 'sex'],\n [5n, 'fem'],\n [4n, 'fyra'],\n [3n, 'tre'],\n [2n, 'två'],\n [1n, 'ett'],\n [0n, 'noll']\n ]\n\n /** Combines two word-sets according to Swedish grammar rules. */\n combineWordSets (preceding, following) {\n const precedingWord = Object.keys(preceding)[0]\n const followingWord = Object.keys(following)[0]\n const precedingValue = Object.values(preceding)[0]\n const followingValue = Object.values(following)[0]\n\n if (precedingValue === 1n && followingValue < 100n) {\n return following\n }\n\n // Omit 'ett' before 'hundra' and 'tusen'\n if (precedingValue === 1n && (followingValue === 100n || followingValue === 1000n)) {\n return following\n }\n\n if (precedingValue < 100n && precedingValue > followingValue) {\n return { [`${precedingWord}-${followingWord}`]: precedingValue + followingValue }\n }\n\n if (precedingValue >= 100n && followingValue < 100n) {\n return { [`${precedingWord} och ${followingWord}`]: precedingValue + followingValue }\n }\n\n if (followingValue > precedingValue) {\n if (precedingValue === 1n && followingValue >= 1_000_000n) {\n return { [`en ${followingWord}`]: precedingValue * followingValue }\n }\n return { [`${precedingWord} ${followingWord}`]: precedingValue * followingValue }\n }\n\n return { [`${precedingWord} ${followingWord}`]: precedingValue + followingValue }\n }\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Tamil language converter.\n *\n * Supports:\n * - Tamil numbering system\n * - Tamil script\n * - Complete word forms for 0-99\n */\nexport class Tamil extends AbstractLanguage {\n negativeWord = 'மைனஸ்'\n decimalSeparatorWord = 'புள்ளி'\n zeroWord = 'பூஜ்ஜியம்'\n usePerDigitDecimals = true // Enable digit-by-digit decimal conversion\n\n belowHundredWords = [\n 'பூஜ்ஜியம்',\n 'ஒன்று',\n 'இரண்டு',\n 'மூன்று',\n 'நான்கு',\n 'ஐந்து',\n 'ஆறு',\n 'ஏழு',\n 'எட்டு',\n 'ஒன்பது',\n 'பத்து',\n 'பதினொன்று',\n 'பன்னிரண்டு',\n 'பதிமூன்று',\n 'பதினான்கு',\n 'பதினைந்து',\n 'பதினாறு',\n 'பதினேழு',\n 'பதினெட்டு',\n 'பத்தொன்பது',\n 'இருபது',\n 'இருபத்தொன்று',\n 'இருபத்திரண்டு',\n 'இருபத்திமூன்று',\n 'இருபத்திநான்கு',\n 'இருபத்தைந்து',\n 'இருபத்தாறு',\n 'இருபத்தேழு',\n 'இருபத்தெட்டு',\n 'இருபத்தொன்பது',\n 'முப்பது',\n 'முப்பத்தொன்று',\n 'முப்பத்திரண்டு',\n 'முப்பத்திமூன்று',\n 'முப்பத்திநான்கு',\n 'முப்பத்தைந்து',\n 'முப்பத்தாறு',\n 'முப்பத்தேழு',\n 'முப்பத்தெட்டு',\n 'முப்பத்தொன்பது',\n 'நாற்பது',\n 'நாற்பத்தொன்று',\n 'நாற்பத்திரண்டு',\n 'நாற்பத்திமூன்று',\n 'நாற்பத்திநான்கு',\n 'நாற்பத்தைந்து',\n 'நாற்பத்தாறு',\n 'நாற்பத்தேழு',\n 'நாற்பத்தெட்டு',\n 'நாற்பத்தொன்பது',\n 'ஐம்பது',\n 'ஐம்பத்தொன்று',\n 'ஐம்பத்திரண்டு',\n 'ஐம்பத்திமூன்று',\n 'ஐம்பத்திநான்கு',\n 'ஐம்பத்தைந்து',\n 'ஐம்பத்தாறு',\n 'ஐம்பத்தேழு',\n 'ஐம்பத்தெட்டு',\n 'ஐம்பத்தொன்பது',\n 'அறுபது',\n 'அறுபத்தொன்று',\n 'அறுபத்திரண்டு',\n 'அறுபத்திமூன்று',\n 'அறுபத்திநான்கு',\n 'அறுபத்தைந்து',\n 'அறுபத்தாறு',\n 'அறுபத்தேழு',\n 'அறுபத்தெட்டு',\n 'அறுபத்தொன்பது',\n 'எழுபது',\n 'எழுபத்தொன்று',\n 'எழுபத்திரண்டு',\n 'எழுபத்திமூன்று',\n 'எழுபத்திநான்கு',\n 'எழுபத்தைந்து',\n 'எழுபத்தாறு',\n 'எழுபத்தேழு',\n 'எழுபத்தெட்டு',\n 'எழுபத்தொன்பது',\n 'எண்பது',\n 'எண்பத்தொன்று',\n 'எண்பத்திரண்டு',\n 'எண்பத்திமூன்று',\n 'எண்பத்திநான்கு',\n 'எண்பத்தைந்து',\n 'எண்பத்தாறு',\n 'எண்பத்தேழு',\n 'எண்பத்தெட்டு',\n 'எண்பத்தொன்பது',\n 'தொண்ணூறு',\n 'தொண்ணூற்று ஒன்று',\n 'தொண்ணூற்று இரண்டு',\n 'தொண்ணூற்று மூன்று',\n 'தொண்ணூற்று நான்கு',\n 'தொண்ணூற்று ஐந்து',\n 'தொண்ணூற்று ஆறு',\n 'தொண்ணூற்று ஏழு',\n 'தொண்ணூற்று எட்டு',\n 'தொண்ணூற்று ஒன்பது'\n ]\n\n hundredsWords = [\n '',\n 'நூறு',\n 'இருநூறு',\n 'முன்னூறு',\n 'நானூறு',\n 'ஐநூறு',\n 'அறுநூறு',\n 'எழுநூறு',\n 'எண்நூறு',\n 'தொள்ளாயிரம்'\n ]\n\n // Ones words map 1–9 for decimal reading\n onesWords = [\n 'ஒன்று',\n 'இரண்டு',\n 'மூன்று',\n 'நான்கு',\n 'ஐந்து',\n 'ஆறு',\n 'ஏழு',\n 'எட்டு',\n 'ஒன்பது'\n ]\n\n scaleWords = [\n '',\n 'ஆயிரம்',\n 'லட்சம்',\n 'கோடி',\n 'அரபு',\n 'கராபு',\n 'நீல்',\n 'பத்ம',\n 'சங்கு'\n ]\n\n /** Convert numbers below 100 to Tamil words. */\n convertBelowHundred (number) {\n return this.belowHundredWords[number]\n }\n\n convertBelowThousand (number) {\n if (number === 0) return ''\n if (number < 100) return this.convertBelowHundred(number)\n\n const hundreds = Math.trunc(number / 100)\n const remainder = number % 100\n let hundredPart = this.hundredsWords[hundreds]\n\n if (remainder > 0) {\n if (hundredPart.endsWith('ம்')) {\n hundredPart = hundredPart.replace(/ம்$/, 'த்து')\n } else if (hundredPart.endsWith('று')) {\n hundredPart = hundredPart.slice(0, -2) + 'ற்று'\n }\n }\n\n const parts = [hundredPart]\n if (remainder > 0) {\n parts.push(this.convertBelowHundred(remainder))\n }\n\n return parts.join(' ').trim()\n }\n\n splitIndian (number) {\n const numStr = number.toString()\n if (numStr.length <= 3) return [Number(numStr)]\n\n const groups = []\n const last3 = numStr.slice(-3)\n groups.unshift(Number(last3))\n\n let remaining = numStr.slice(0, -3)\n while (remaining.length > 0) {\n const group = remaining.slice(-2)\n groups.unshift(Number(group))\n remaining = remaining.slice(0, -2)\n }\n\n return groups\n }\n\n integerToWords (integerPart) {\n if (integerPart === 0n) return this.zeroWord\n\n const groups = this.splitIndian(integerPart)\n const groupCount = groups.length\n const words = []\n\n for (let i = 0; i < groupCount; i++) {\n const groupValue = groups[i]\n if (groupValue === 0) continue\n\n const scaleIndex = groupCount - i - 1\n const groupWords = (groupValue === 1 && scaleIndex > 0) ? 'ஒரு' : this.convertBelowThousand(groupValue)\n words.push(groupWords)\n if (scaleIndex > 0 && this.scaleWords[scaleIndex]) {\n words.push(this.scaleWords[scaleIndex])\n }\n }\n\n return words.join(' ').trim()\n }\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Telugu language converter.\n *\n * Supports:\n * - Telugu numbering system\n * - Telugu script\n * - Complete word forms for 0-99\n */\nexport class Telugu extends AbstractLanguage {\n negativeWord = 'మైనస్'\n decimalSeparatorWord = 'పాయింట్'\n zeroWord = 'సున్నా'\n usePerDigitDecimals = true // Enable digit-by-digit decimal conversion\n\n belowHundredWords = [\n 'సున్నా',\n 'ఒకటి',\n 'రెండు',\n 'మూడు',\n 'నాలుగు',\n 'ఐదు',\n 'ఆరు',\n 'ఏడు',\n 'ఎనిమిది',\n 'తొమ్మిది',\n 'పది',\n 'పదకొండు',\n 'పన్నెండు',\n 'పదమూడు',\n 'పద్నాలుగు',\n 'పదిహేను',\n 'పదహారు',\n 'పదిహేడు',\n 'పద్దెనిమిది',\n 'పంతొమ్మిది',\n 'ఇరవై',\n 'ఇరవై ఒక్కటి',\n 'ఇరవై రెండు',\n 'ఇరవై మూడు',\n 'ఇరవై నాలుగు',\n 'ఇరవై ఐదు',\n 'ఇరవై ఆరు',\n 'ఇరవై ఏడు',\n 'ఇరవై ఎనిమిది',\n 'ఇరవై తొమ్మిది',\n 'ముప్పై',\n 'ముప్పై ఒకటి',\n 'ముప్పై రెండు',\n 'ముప్పై మూడు',\n 'ముప్పై నాలుగు',\n 'ముప్పై ఐదు',\n 'ముప్పై ఆరు',\n 'ముప్పై ఏడు',\n 'ముప్పై ఎనిమిది',\n 'ముప్పై తొమ్మిది',\n 'నలభై',\n 'నలభై ఒకటి',\n 'నలభై రెండు',\n 'నలభై మూడు',\n 'నలభై నాలుగు',\n 'నలభై ఐదు',\n 'నలభై ఆరు',\n 'నలభై ఏడు',\n 'నలభై ఎనిమిది',\n 'నలభై తొమ్మిది',\n 'యాభై',\n 'యాభై ఒకటి',\n 'యాభై రెండు',\n 'యాభై మూడు',\n 'యాభై నాలుగు',\n 'యాభై ఐదు',\n 'యాభై ఆరు',\n 'యాభై ఏడు',\n 'యాభై ఎనిమిది',\n 'యాభై తొమ్మిది',\n 'అరవై',\n 'అరవై ఒకటి',\n 'అరవై రెండు',\n 'అరవై మూడు',\n 'అరవై నాలుగు',\n 'అరవై ఐదు',\n 'అరవై ఆరు',\n 'అరవై ఏడు',\n 'అరవై ఎనిమిది',\n 'అరవై తొమ్మిది',\n 'డెబ్బై',\n 'డెబ్బై ఒకటి',\n 'డెబ్బై రెండు',\n 'డెబ్బై మూడు',\n 'డెబ్బై నాలుగు',\n 'డెబ్బై ఐదు',\n 'డెబ్బై ఆరు',\n 'డెబ్బై ఏడు',\n 'డెబ్బై ఎనిమిది',\n 'డెబ్బై తొమ్మిది',\n 'ఎనభై',\n 'ఎనభై ఒకటి',\n 'ఎనభై రెండు',\n 'ఎనభై మూడు',\n 'ఎనభై నాలుగు',\n 'ఎనభై ఐదు',\n 'ఎనభై ఆరు',\n 'ఎనభై ఏడు',\n 'ఎనభై ఎనిమిది',\n 'ఎనభై తొమ్మిది',\n 'తొంభై',\n 'తొంభై ఒకటి',\n 'తొంభై రెండు',\n 'తొంభై మూడు',\n 'తొంభై నాలుగు',\n 'తొంభై ఐదు',\n 'తొంభై ఆరు',\n 'తొంభై ఏడు',\n 'తొంభై ఎనిమిది',\n 'తొంభై తొమ్మిది'\n ]\n\n hundredsWords = [\n '',\n 'వంద',\n 'రెండు వందలు',\n 'మూడు వందలు',\n 'నాలుగు వందలు',\n 'ఐదు వందలు',\n 'ఆరు వందలు',\n 'ఏడు వందలు',\n 'ఎనిమిది వందలు',\n 'తొమ్మిది వందలు'\n ]\n\n // Ones words map 1–9 for decimal reading\n onesWords = [\n 'ఒకటి',\n 'రెండు',\n 'మూడు',\n 'నాలుగు',\n 'ఐదు',\n 'ఆరు',\n 'ఏడు',\n 'ఎనిమిది',\n 'తొమ్మిది'\n ]\n\n scaleWords = [\n '',\n 'వెయ్యి',\n 'లక్ష',\n 'కోటి',\n 'అరబ్',\n 'ఖరబ్',\n 'నిల్',\n 'పడ్మ',\n 'శంకు'\n ]\n\n /** Convert numbers below 100 to Telugu words. */\n convertBelowHundred (number) {\n return this.belowHundredWords[number]\n }\n\n /** Convert numbers below 1000 to Telugu words. */\n convertBelowThousand (number) {\n if (number === 0) return ''\n if (number < 100) return this.convertBelowHundred(number)\n\n const hundreds = Math.trunc(number / 100)\n const remainder = number % 100\n const parts = [this.hundredsWords[hundreds]]\n\n if (remainder > 0) {\n parts.push(this.convertBelowHundred(remainder))\n }\n\n return parts.join(' ').trim()\n }\n\n /** Split a number using Indian-style grouping (3-2-2 digits from right). */\n splitIndian (number) {\n const numStr = number.toString()\n if (numStr.length <= 3) return [Number(numStr)]\n\n const groups = []\n const last3 = numStr.slice(-3)\n groups.unshift(Number(last3))\n\n let remaining = numStr.slice(0, -3)\n while (remaining.length > 0) {\n const group = remaining.slice(-2)\n groups.unshift(Number(group))\n remaining = remaining.slice(0, -2)\n }\n\n return groups\n }\n\n integerToWords (integerPart) {\n if (integerPart === 0n) return this.zeroWord\n\n const groups = this.splitIndian(integerPart)\n const groupCount = groups.length\n const words = []\n\n for (let i = 0; i < groupCount; i++) {\n const groupValue = groups[i]\n if (groupValue === 0) continue\n\n const scaleIndex = groupCount - i - 1\n const groupWords = (groupValue === 1 && scaleIndex > 0) ? 'ఒక' : this.convertBelowThousand(groupValue)\n words.push(groupWords)\n if (scaleIndex > 0 && this.scaleWords[scaleIndex]) {\n words.push(this.scaleWords[scaleIndex])\n }\n }\n\n return words.join(' ').trim()\n }\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Thai language converter.\n *\n * Supports:\n * - Thai numerals (ศูนย์, หนึ่ง, สอง, สาม)\n * - No word separators (continuous Thai script)\n * - Digit-by-digit decimal reading\n */\nexport class Thai extends AbstractLanguage {\n negativeWord = 'ลบ'\n decimalSeparatorWord = 'จุด'\n zeroWord = 'ศูนย์'\n wordSeparator = ''\n usePerDigitDecimals = true // Enable digit-by-digit decimal conversion\n\n // Ones words map 1–9\n onesWords = ['หนึ่ง', 'สอง', 'สาม', 'สี่', 'ห้า', 'หก', 'เจ็ด', 'แปด', 'เก้า']\n\n /** Converts numbers below one million to Thai words. */\n convertBelowMillion (number) {\n if (number === 0) return ''\n\n let value = number\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(this.onesWords[hundredThousands - 1] + 'แสน')\n }\n\n if (tenThousands > 0) {\n if (tenThousands === 1) {\n parts.push('หนึ่งหมื่น')\n } else {\n parts.push(this.onesWords[tenThousands - 1] + 'หมื่น')\n }\n }\n\n if (thousands > 0) {\n parts.push(this.onesWords[thousands - 1] + 'พัน')\n }\n\n if (hundreds > 0) {\n parts.push(this.onesWords[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(this.onesWords[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(this.onesWords[ones - 1])\n }\n }\n\n return parts.join('')\n }\n\n /** Split a number into million-sized groups for processing. */\n splitMillionGroups (number) {\n const groups = []\n let remaining = number\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\n integerToWords (integerPart) {\n if (integerPart === 0n) {\n return this.zeroWord\n }\n\n const groups = this.splitMillionGroups(integerPart)\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(this.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}\n","import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'\n\n/**\n * Traditional Chinese language converter.\n *\n * Supports:\n * - Traditional Chinese characters (繁體中文)\n * - No word separators (concatenated format)\n * - Optional formal (financial) vs common numerals\n */\nexport class TraditionalChinese extends GreedyScaleLanguage {\n negativeWord = '負'\n decimalSeparatorWord = '點'\n zeroWord = '零'\n wordSeparator = ''\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n formal: true\n }, options)\n\n if (this.options.formal) {\n this.scaleWords = [\n [1_000_000_000_000n, '萬'],\n [100_000_000n, '億'],\n [10_000n, '萬'],\n [1000n, '仟'],\n [100n, '佰'],\n [10n, '拾'],\n [9n, '玖'],\n [8n, '捌'],\n [7n, '柒'],\n [6n, '陸'],\n [5n, '伍'],\n [4n, '肆'],\n [3n, '參'],\n [2n, '貳'],\n [1n, '壹'],\n [0n, '零']\n ]\n } else {\n this.scaleWords = [\n [1_000_000_000_000n, '萬'],\n [100_000_000n, '億'],\n [10_000n, '萬'],\n [1000n, '千'],\n [100n, '百'],\n [10n, '十'],\n [9n, '九'],\n [8n, '八'],\n [7n, '七'],\n [6n, '六'],\n [5n, '五'],\n [4n, '四'],\n [3n, '三'],\n [2n, '二'],\n [1n, '一'],\n [0n, '零']\n ]\n }\n }\n\n /** Combines two word-sets with Traditional Chinese grammar rules and zero insertion. */\n combineWordSets (preceding, following) {\n const precedingWord = Object.keys(preceding)[0]\n const precedingValue = Object.values(preceding)[0]\n const followingWord = Object.keys(following)[0]\n const followingValue = Object.values(following)[0]\n\n // Implicit one: omit 1 before single digits (< 10)\n if (precedingValue === 1n && followingValue < 10n) {\n return following\n }\n\n // Multiply when following > preceding (scale words like 千, 萬, 億)\n if (followingValue > precedingValue) {\n return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }\n }\n\n // Insert \"零\" (zero) when position skip levels (e.g., 1003 = 千零三)\n // zeroDigit() checks if gap exists between preceding and following magnitude\n if (this.zeroDigit(precedingValue) > this.digit(followingValue)) {\n return { [`${precedingWord}${this.zeroWord}${followingWord}`]: precedingValue + followingValue }\n }\n\n // Default: concatenate without zero insertion\n return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }\n }\n\n /** Returns the number of digits in a number. */\n digit (number_) {\n return number_.toString().length\n }\n\n /** Counts the number of zero digits in a number. */\n zeroDigit (number_) {\n return [...number_.toString()].filter(c => c === '0').length\n }\n\n /** Converts decimal digits to words by reading each digit individually. */\n decimalDigitsToWords (decimalString) {\n const words = []\n for (let i = 0; i < decimalString.length; i++) {\n const digitValue = BigInt(decimalString[i])\n words.push(this.integerToWords(digitValue))\n }\n return words\n }\n}\n","import { TurkicLanguage } from '../classes/turkic-language.js'\n\n/**\n * Turkish language converter.\n *\n * Supports:\n * - Omits 'bir' (one) before hundreds and thousands\n * - Optional space removal via dropSpaces option\n */\nexport class Turkish extends TurkicLanguage {\n negativeWord = 'eksi'\n decimalSeparatorWord = 'virgül'\n zeroWord = 'sıfır'\n wordSeparator = ' '\n\n scaleWords = [\n [1_000_000_000_000_000_000n, 'kentilyon'],\n [1_000_000_000_000_000n, 'katrilyon'],\n [1_000_000_000_000n, 'trilyon'],\n [1_000_000_000n, 'milyar'],\n [1_000_000n, 'milyon'],\n [1000n, 'bin'],\n [100n, 'yüz'],\n [90n, 'doksan'],\n [80n, 'seksen'],\n [70n, 'yetmiş'],\n [60n, 'altmış'],\n [50n, 'elli'],\n [40n, 'kırk'],\n [30n, 'otuz'],\n [20n, 'yirmi'],\n [10n, 'on'],\n [9n, 'dokuz'],\n [8n, 'sekiz'],\n [7n, 'yedi'],\n [6n, 'altı'],\n [5n, 'beş'],\n [4n, 'dört'],\n [3n, 'üç'],\n [2n, 'iki'],\n [1n, 'bir'],\n [0n, 'sıfır']\n ]\n\n constructor (options = {}) {\n super()\n\n this.setOptions({\n dropSpaces: false\n }, options)\n\n if (this.options.dropSpaces) {\n this.wordSeparator = ''\n }\n }\n}\n","import { SlavicLanguage } from '../classes/slavic-language.js'\n\n/**\n * Ukrainian language converter.\n *\n * Supports:\n * - Three-form pluralization (one/few/many)\n * - Gender agreement (один/одна, два/дві)\n * - Ukrainian orthography and phonology\n */\nexport class Ukrainian extends SlavicLanguage {\n negativeWord = 'мiнус'\n decimalSeparatorWord = 'кома'\n zeroWord = 'нуль'\n\n onesWords = {\n 1: 'один',\n 2: 'два',\n 3: 'три',\n 4: 'чотири',\n 5: 'п\\'ять',\n 6: 'шiсть',\n 7: 'сiм',\n 8: 'вiсiм',\n 9: 'дев\\'ять'\n }\n\n onesFeminineWords = {\n 1: 'одна',\n 2: 'двi',\n 3: 'три',\n 4: 'чотири',\n 5: 'п\\'ять',\n 6: 'шiсть',\n 7: 'сiм',\n 8: 'вiсiм',\n 9: 'дев\\'ять'\n }\n\n teensWords = {\n 0: 'десять',\n 1: 'одинадцять',\n 2: 'дванадцять',\n 3: 'тринадцять',\n 4: 'чотирнадцять',\n 5: 'п\\'ятнадцять',\n 6: 'шiстнадцять',\n 7: 'сiмнадцять',\n 8: 'вiсiмнадцять',\n 9: 'дев\\'ятнадцять'\n }\n\n twentiesWords = {\n 2: 'двадцять',\n 3: 'тридцять',\n 4: 'сорок',\n 5: 'п\\'ятдесят',\n 6: 'шiстдесят',\n 7: 'сiмдесят',\n 8: 'вiсiмдесят',\n 9: 'дев\\'яносто'\n }\n\n hundredsWords = {\n 1: 'сто',\n 2: 'двiстi',\n 3: 'триста',\n 4: 'чотириста',\n 5: 'п\\'ятсот',\n 6: 'шiстсот',\n 7: 'сiмсот',\n 8: 'вiсiмсот',\n 9: 'дев\\'ятсот'\n }\n\n pluralForms = {\n 1: ['тисяча', 'тисячi', 'тисяч'], // 10^ 3\n 2: ['мiльйон', 'мiльйони', 'мiльйонiв'], // 10^ 6\n 3: ['мiльярд', 'мiльярди', 'мiльярдiв'], // 10^ 9\n 4: ['трильйон', 'трильйони', 'трильйонiв'], // 10^ 12\n 5: ['квадрильйон', 'квадрильйони', 'квадрильйонiв'], // 10^ 15\n 6: ['квiнтильйон', 'квiнтильйони', 'квiнтильйонiв'], // 10^ 18\n 7: ['секстильйон', 'секстильйони', 'секстильйонiв'], // 10^ 21\n 8: ['септильйон', 'септильйони', 'септильйонiв'], // 10^ 24\n 9: ['октильйон', 'октильйони', 'октильйонiв'], // 10^ 27\n 10: ['нонiльйон', 'нонiльйони', 'нонiльйонiв'] // 10^ 30\n }\n\n /**\n * Ukrainian thousands (тисяча) are feminine, requiring одна/двi forms.\n * Other scales (million, billion, etc.) are masculine.\n */\n scaleGenders = {\n 1: true // thousands are feminine\n }\n}\n","import { SouthAsianLanguage } from '../classes/south-asian-language.js'\n\n/**\n * Urdu language converter.\n *\n * Supports:\n * - Indian numbering system (ہزار, لاکھ, کروڑ)\n * - Urdu script (right-to-left)\n * - Complete word forms for 0-99\n */\nexport class Urdu extends SouthAsianLanguage {\n negativeWord = 'منفی'\n decimalSeparatorWord = 'اعشاریہ'\n zeroWord = 'صفر'\n hundredWord = 'سو'\n\n belowHundredWords = [\n 'صفر',\n 'ایک',\n 'دو',\n 'تین',\n 'چار',\n 'پانچ',\n 'چھ',\n 'سات',\n 'آٹھ',\n 'نو',\n 'دس',\n 'گیارہ',\n 'بارہ',\n 'تیرہ',\n 'چودہ',\n 'پندرہ',\n 'سولہ',\n 'سترہ',\n 'اٹھارہ',\n 'انیس',\n 'بیس',\n 'اکیس',\n 'بائیس',\n 'تیئیس',\n 'چوبیس',\n 'پچیس',\n 'چھبیس',\n 'ستائیس',\n 'اٹھائیس',\n 'انتیس',\n 'تیس',\n 'اکتیس',\n 'بتیس',\n 'تینتیس',\n 'چونتیس',\n 'پینتیس',\n 'چھتیس',\n 'سینتیس',\n 'اڑتیس',\n 'انتالیس',\n 'چالیس',\n 'اکتالیس',\n 'بیالیس',\n 'تینتالیس',\n 'چوالیس',\n 'پینتالیس',\n 'چھالیس',\n 'سینتالیس',\n 'اڑتالیس',\n 'انچاس',\n 'پچاس',\n 'اکاون',\n 'باون',\n 'ترپن',\n 'چون',\n 'پچپن',\n 'چھپن',\n 'ستاون',\n 'اٹھاون',\n 'انسٹھ',\n 'ساٹھ',\n 'اکسٹھ',\n 'باسٹھ',\n 'ترسٹھ',\n 'چونسٹھ',\n 'پینسٹھ',\n 'چھیاسٹھ',\n 'سڑسٹھ',\n 'اڑسٹھ',\n 'انہتر',\n 'ستر',\n 'اکہتر',\n 'بہتر',\n 'تہتر',\n 'چوہتر',\n 'پچھتر',\n 'چھہتر',\n 'ستتر',\n 'اٹھہتر',\n 'اناسی',\n 'اسی',\n 'اکیاسی',\n 'بیاسی',\n 'تریاسی',\n 'چوراسی',\n 'پچاسی',\n 'چھیاسی',\n 'ستاسی',\n 'اٹھاسی',\n 'نواسی',\n 'نوے',\n 'اکانوے',\n 'بانوے',\n 'ترانوے',\n 'چورانوے',\n 'پچانوے',\n 'چھیانوے',\n 'ستانوے',\n 'اٹھانوے',\n 'ننانوے'\n ]\n\n scaleWords = [\n '',\n 'ہزار',\n 'لاکھ',\n 'کروڑ',\n 'ارب',\n 'کھرب',\n 'نیل',\n 'پدم',\n 'شنکھ'\n ]\n}\n","import { AbstractLanguage } from '../classes/abstract-language.js'\n\n/**\n * Vietnamese language converter.\n *\n * Supports:\n * - Special pronunciation rules (lăm for 5, mốt for final 1)\n * - \"Lẻ\" (odd/extra) when tens place is zero\n * - Vietnamese diacritical marks\n */\nexport class Vietnamese extends AbstractLanguage {\n negativeWord = 'âm'\n decimalSeparatorWord = 'phẩy'\n zeroWord = 'không'\n\n belowTwentyWords = {\n 0: 'không',\n 1: 'một',\n 2: 'hai',\n 3: 'ba',\n 4: 'bốn',\n 5: 'năm',\n 6: 'sáu',\n 7: 'bảy',\n 8: 'tám',\n 9: 'chín',\n 10: 'mười',\n 11: 'mười một',\n 12: 'mười hai',\n 13: 'mười ba',\n 14: 'mười bốn',\n 15: 'mười lăm',\n 16: 'mười sáu',\n 17: 'mười bảy',\n 18: 'mười tám',\n 19: 'mười chín'\n }\n\n twentiesWords = {\n 20: 'hai mươi',\n 30: 'ba mươi',\n 40: 'bốn mươi',\n 50: 'năm mươi',\n 60: 'sáu mươi',\n 70: 'bảy mươi',\n 80: 'tám mươi',\n 90: 'chín mươi'\n }\n\n scaleWords = {\n 1: 'nghìn', // 10^1\n 2: 'triệu', // 10^2\n 3: 'tỷ', // 10^3\n 4: 'nghìn tỷ',\n 5: 'trăm nghìn tỷ',\n 6: 'Quintillion',\n 7: 'Sextillion',\n 8: 'Septillion',\n 9: 'Octillion',\n 10: 'Nonillion',\n 11: 'Decillion',\n 12: 'Undecillion',\n 13: 'Duodecillion',\n 14: 'Tredecillion',\n 15: 'Quattuordecillion',\n 16: 'Sexdecillion',\n 17: 'Septendecillion',\n 18: 'Octodecillion',\n 19: 'Novemdecillion',\n 20: 'Vigintillion'\n }\n\n /** Convert numbers less than 100 to Vietnamese words. */\n convertLess100 (number) {\n const unitsPart = number % 10\n const tensPart = number - unitsPart\n const tensPartText = this.twentiesWords[tensPart]\n if (unitsPart === 0) {\n return tensPartText\n }\n const unitsPartText = this.belowTwentyWords[unitsPart]\n let suffix = unitsPartText\n if (unitsPart === 1) {\n suffix = 'mốt'\n }\n if (unitsPart === 5) {\n suffix = 'lăm'\n }\n return tensPartText + ' ' + suffix\n }\n\n /** Convert numbers less than 1000 to Vietnamese words. */\n convertLess1000 (number) {\n const words = []\n const tensUnitsPart = number % 100\n const hundredsPart = number - tensUnitsPart\n if (hundredsPart > 0) {\n words.push(this.belowTwentyWords[hundredsPart / 100], 'trăm')\n }\n if (tensUnitsPart > 0 && tensUnitsPart < 10) {\n if (words.length > 0) {\n words.push('lẻ')\n }\n if (tensUnitsPart === 5) {\n words.push('năm')\n } else {\n words.push(this.belowTwentyWords[tensUnitsPart])\n }\n }\n if (tensUnitsPart >= 10) {\n words.push(this.integerToWords(tensUnitsPart))\n }\n return words.join(' ')\n }\n\n /** Convert numbers greater than 1000 to Vietnamese words. */\n convertMore1000 (number) {\n const words = []\n let division = number / 1000n\n let power = 1\n while (division >= 1000n) {\n division = division / 1000n\n power = power + 1\n }\n const r = number - (division * BigInt(Math.pow(1000, power)))\n words.push(this.integerToWords(division), this.scaleWords[power])\n if (r > 0n) {\n if (r <= 99n) {\n words.push('lẻ')\n }\n words.push(this.integerToWords(r))\n }\n return words.join(' ')\n }\n\n integerToWords (integerPart) {\n if (integerPart < 20n) {\n return this.belowTwentyWords[Number(integerPart)]\n } else {\n if (integerPart < 100n) {\n return this.convertLess100(Number(integerPart))\n } else {\n return (integerPart < 1000n ? this.convertLess1000(Number(integerPart)) : this.convertMore1000(integerPart))\n }\n }\n }\n}\n"],"names":["AbstractLanguage","options","negativeWord","decimalSeparatorWord","zeroWord","wordSeparator","usePerDigitDecimals","this","toWords","isNegative","integerPart","decimalPart","words","push","integerToWords","decimalDigitsToWords","join","Error","setOptions","defaults","userOptions","decimalIntegerToWords","i","length","remainder","slice","char","BigInt","GreedyScaleLanguage","scaleWords","wordForScale","scale","match","find","pair","decomposeInteger","wordSets","remaining","multiplier","reduceWordSets","first","second","rest","Array","isArray","combined","combineWordSets","normalized","map","element","preceding","following","finalizeWords","output","trimEnd","reduced","result","Object","keys","TurkicLanguage","precedingWord","precedingValue","entries","followingWord","followingValue","resultNumber","SouthAsianLanguage","belowHundredWords","hundredWord","splitToSegments","numStr","toString","Number","segments","last3","unshift","segment","segmentToWords","segmentValue","hundreds","Math","trunc","parts","segmentCount","scaleIndex","trim","SlavicLanguage","onesWords","onesFeminineWords","teensWords","twentiesWords","hundredsWords","pluralForms","scaleGenders","omitOneBeforeScale","constructor","super","gender","segmentIndex","onesDigit","tensDigit","hundredsDigit","extractDigits","onesArray","pluralize","numberString","segmentSize","stringLength","remainderLength","value","number","remainder100","remainder10","French","withHyphenSeparator","values","at","Portuguese","static","replaceAll","POSTCLEAN_REGEX","replace","makeConverter","LanguageClass","undefined","proto","getPrototypeOf","prototype","isPlainObject","TypeError","type","str","isFinite","includes","expandScientificNotation","numberToString","trimmed","isNaN","stringToNormalizedForm","dotIndex","indexOf","integerStr","parseNumericString","parseNumericValue","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","repeat","ArabicConverter","tensWords","scaleAppendedWords","scalePluralWords","dualWords","dualAppendedWords","ones","masculine","feminine","selectedOnes","groupNumber","groupLevel","fullNumber","tens","hundredsRaw","returnValue","hundredsWord","numValue","pow","log10","tensIndex","temp","group","numberToProcess","groupDescription","AzerbaijaniConverter","BanglaConverter","BiblicalHebrewConverter","femininePluralForms","scalePlural","teensFeminineWords","andWord","index","x","n1","n2","n3","chunkWords","hasHundreds","tensWord","onesWord","CroatianConverter","n","forms","lastDigit","lastTwoDigits","CzechConverter","DanishConverter","ordFlag","temporary","DutchConverter","includeOptionalAnd","noHundredPairing","accentOne","hadSpace","hasSpace","andTxt","endsWith","high","low","EnglishConverter","FilipinoConverter","vowels","lastChar","FrenchConverter","FrenchBelgiumConverter","tuples","idx80","findIndex","tuple","splice","idx60","GermanConverter","GreekConverter","GujaratiConverter","HebrewConverter","HindiConverter","HungarianConverter","tensToCardinal","units","hundredsToCardinal","prefix","thousandsToCardinal","thousands","postfix","bigNumberToCardinal","numberLength","floor","IndonesianConverter","digitWords","splitBy3","blocks","stringNumber","firstBlock","firstBlockLength","nextBlock","spell","spelling","wordBlocks","getTens","getHundreds","block","wordList","start","ItalianConverter","exponentPrefixes","accentuate","string","word","omitIfZero","phoneticContraction","hasOwnProperty","call","exponentLengthToString","exponentLength","preDigits","exponent","infix","isSetsEqual","a","b","Set","size","every","has","JapaneseConverter","num","groups","reverse","groupStr","KannadaConverter","KoreanConverter","LatvianConverter","LithuanianConverter","MalayConverter","isLast","scaleWord","w","MarathiConverter","NorwegianBokmalConverter","PersianConverter","thousandMultiplier","thousandPrefix","suffix","millionMultiplier","PolishConverter","PortugueseConverter","PunjabiConverter","RomanianConverter","teensMasculineWords","scaleMetadata","singular","plural","needsDe","results","l","padStart","toReversed","romanianPluralize","form","spellUnder1000","spellUnder100","feminineUnits","masculineTeens","t","u","h","r","hundredWords","onesMap","RussianConverter","SerbianCyrillicConverter","SerbianLatinConverter","SimplifiedChineseConverter","formal","zeroDigit","digit","number_","filter","c","decimalString","digitValue","SpanishConverter","genderStem","SwahiliConverter","wordsUnder100","wordsUnder1000","s","val","unit","SwedishConverter","TamilConverter","convertBelowHundred","convertBelowThousand","hundredPart","splitIndian","groupCount","groupValue","groupWords","TeluguConverter","ThaiConverter","convertBelowMillion","hundredThousands","tenThousands","splitMillionGroups","million","chunk","TraditionalChineseConverter","TurkishConverter","dropSpaces","UkrainianConverter","UrduConverter","VietnameseConverter","belowTwentyWords","convertLess100","unitsPart","tensPart","tensPartText","convertLess1000","tensUnitsPart","hundredsPart","convertMore1000","division","power"],"mappings":"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ACN,CAAC,IAAK,QACN,CAAC,IAAK,QACN,CAAC,IAAK,QACN,CAAC,IAAK,UACN,CAAC,IAAK,MACN,CAAC,GAAI,UACL,CAAC,GAAI,UACL,CAAC,GAAI,SACL,CAAC,GAAI,QACL,CAAC,GAAI,OACL,CAAC,GAAI,QACL,CAAC,GAAI,MACL,CAAC,GAAI,OACL,CAAC,GAAI,OACL,CAAC,GAAI,YF2YHsJ,EAAkEzD,EGzajE,cAAqBxD,EAC1BhE,aAAe,SACfC,qBAAuB,QACvBC,SAAW,QACXgE,YAAc,KAEdD,kBAAoB,CAClB,QACA,KACA,MACA,MACA,MACA,OACA,MACA,MACA,KACA,MACA,KACA,QACA,OACA,OACA,QACA,QACA,MACA,QACA,QACA,OACA,MACA,OACA,OACA,OACA,SACA,QACA,UACA,QACA,OACA,UACA,QACA,UACA,SACA,UACA,UACA,YACA,SACA,YACA,UACA,WACA,SACA,WACA,aACA,YACA,aACA,cACA,WACA,YACA,WACA,WACA,SACA,SACA,UACA,YACA,WACA,WACA,YACA,UACA,SACA,QACA,MACA,UACA,UACA,UACA,UACA,YACA,UACA,WACA,UACA,UACA,QACA,UACA,WACA,WACA,YACA,WACA,YACA,WACA,UACA,QACA,MACA,QACA,SACA,SACA,SACA,SACA,UACA,SACA,QACA,UACA,QACA,WACA,YACA,YACA,YACA,YACA,aACA,YACA,WACA,aAGFtC,WAAa,CACX,GACA,QACA,MACA,OACA,MACA,MACA,MACA,OACA,UHoTEuJ,EAA2G1D,EI1a1G,cAA6BpC,EAClCpF,aAAe,QACfC,qBAAuB,QACvBC,SAAW,MACXE,qBAAsB,EAGtBiF,UAAY,CACV,EAAG,MACH,EAAG,QACH,EAAG,OACH,EAAG,QACH,EAAG,OACH,EAAG,MACH,EAAG,OACH,EAAG,QACH,EAAG,QAGLE,WAAa,CACX,EAAG,OACH,EAAG,UACH,EAAG,WACH,EAAG,WACH,EAAG,YACH,EAAG,WACH,EAAG,UACH,EAAG,WACH,EAAG,YACH,EAAG,YAGLC,cAAgB,CACd,EAAG,QACH,E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,OACL,CAAC,GAAI,SACL,CAAC,GAAI,WACL,CAAC,GAAI,QACL,CAAC,GAAI,OACL,CAAC,GAAI,OACL,CAAC,GAAI,UAIPiB,eAAAA,CAAiBI,EAAWC,GAC1B,MAAMS,EAAgBH,OAAOC,KAAKR,GAAW,GACvCW,EAAiBJ,OAAO0D,OAAOjE,GAAW,GAC1Ca,EAAgBN,OAAOC,KAAKP,GAAW,GACvCa,EAAiBP,OAAO0D,OAAOhE,GAAW,GAGhD,OAAuB,KAAnBU,EACKV,EAOLa,EAAiBH,EACZ,CAAE,CAAC,GAAGD,KAAiBG,KAAkBF,EAAiBG,GAI5D,CAAE,CAAC,GAAGJ,KAAiBG,KAAkBF,EAAiBG,EACnE,IbuWImK,EAAoEzG,EcrbnE,cAAuBxD,EAC5BhE,aAAe,KACfC,qBAAuB,QACvBC,SAAW,QACXgE,YAAc,KACd9D,qBAAsB,EAEtB6D,kBAAoB,CAClB,QACA,KACA,KACA,OACA,MACA,OACA,IACA,MACA,KACA,KACA,KACA,SACA,MACA,MACA,MACA,OACA,MACA,QACA,OACA,QACA,MACA,QACA,QACA,UACA,QACA,OACA,SACA,WACA,WACA,WACA,QACA,UACA,SACA,UACA,UACA,WACA,SACA,WACA,UACA,WACA,QACA,UACA,UACA,YACA,WACA,YACA,UACA,WACA,UACA,UACA,OACA,QACA,OACA,SACA,OACA,SACA,QACA,UACA,UACA,SACA,MACA,OACA,OACA,SACA,OACA,QACA,OACA,OACA,OACA,cACA,UACA,SACA,QACA,QACA,UACA,UACA,QACA,YACA,WACA,WACA,OACA,UACA,SACA,SACA,WACA,SACA,SACA,WACA,UACA,WACA,QACA,SACA,QACA,UACA,UACA,UACA,SACA,WACA,WACA,YAGFtC,WAAa,CACX,GACA,OACA,MACA,OACA,MACA,MACA,MACA,OACA,Sd+TEuM,EAA2F1G,Eerb1F,cAAqBpC,EAC1BpF,aAAe,QACfC,qBAAuB,QACvBC,SAAW,MACXE,qBAAsB,EAGtBiF,UAAY,CACV,EAAG,MACH,EAAG,OACH,EAAG,MACH,EAAG,OACH,EAAG,MACH,EAA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,QACA,SACA,SACA,UACA,UACA,QACA,MACA,QACA,SACA,SACA,SACA,SACA,SACA,SACA,SACA,UACA,QACA,UACA,SACA,UACA,SACA,WACA,UACA,WACA,WACA,QACA,OACA,UACA,OACA,QACA,OACA,OACA,QACA,UACA,UACA,OACA,MACA,OACA,OACA,QACA,QACA,QACA,SACA,QACA,QACA,UACA,QACA,UACA,SACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,QACA,UACA,QACA,SACA,SACA,QACA,SACA,UACA,UACA,QACA,QACA,WACA,QACA,UACA,UACA,SACA,UACA,WACA,WACA,aAGFtC,WAAa,CACX,GACA,QACA,MACA,QACA,MACA,MACA,MACA,OACA,ShBkUEyM,EAAqE5G,EiBxbpE,cAAwB9F,EAC7B1B,aAAe,SACfC,qBAAuB,QACvBC,SAAW,QAEXyB,WAAa,CACX,CAAC,8BAAwC,gBACzC,CAAC,2BAAoC,cACrC,CAAC,wBAAgC,aACjC,CAAC,qBAA4B,WAC7B,CAAC,kBAAwB,YACzB,CAAC,eAAoB,UACrB,CAAC,YAAgB,YACjB,CAAC,SAAY,UACb,CAAC,MAAO,QACR,CAAC,KAAM,QACP,CAAC,IAAK,aACN,CAAC,IAAK,YACN,CAAC,IAAK,UACN,CAAC,IAAK,UACN,CAAC,IAAK,SACN,CAAC,IAAK,WACN,CAAC,IAAK,WACN,CAAC,IAAK,gBACN,CAAC,IAAK,eACN,CAAC,IAAK,aACN,CAAC,IAAK,aACN,CAAC,IAAK,YACN,CAAC,IAAK,cACN,CAAC,IAAK,eACN,CAAC,IAAK,eACN,CAAC,IAAK,aACN,CAAC,IAAK,QACN,CAAC,IAAK,eACN,CAAC,IAAK,cACN,CAAC,IAAK,YACN,CAAC,IAAK,YACN,CAAC,IAAK,WACN,CAAC,IAAK,aACN,CAAC,IAAK,cACN,CAAC,IAAK,cACN,CAAC,IAAK,YACN,CAAC,IAAK,OACN,CAAC,GAAI,UACL,CAAC,GAAI,SACL,CAAC,GAAI,OACL,CAAC,GAAI,OACL,CAAC,GAAI,MACL,CAAC,GAAI,QACL,CAAC,GAAI,SAC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Gf,MAAMC,EAAYF,EAAI,IAChBG,EAAgBH,EAAI,KAE1B,OAAIE,EAAY,IAAMA,EAAY,KAAOC,EAAgB,KAAOA,EAAgB,KACvEF,EAAM,GAGRA,EAAM,EACf,I7BmWIuG,EAAsEjL,EAAcL,GACpFuL,EAAmElL,E8BtclE,cAAsBxD,EAC3BhE,aAAe,QACfC,qBAAuB,SACvBC,SAAW,QACXgE,YAAc,KAEdD,kBAAoB,CAClB,QACA,MACA,KACA,OACA,MACA,MACA,KACA,MACA,MACA,MACA,MACA,SACA,QACA,QACA,QACA,SACA,QACA,SACA,SACA,OACA,MACA,OACA,MACA,MACA,OACA,OACA,OACA,OACA,OACA,QACA,MACA,QACA,OACA,OACA,QACA,QACA,OACA,QACA,QACA,SACA,OACA,SACA,QACA,UACA,SACA,SACA,QACA,UACA,SACA,QACA,QACA,SACA,QACA,SACA,UACA,UACA,QACA,UACA,SACA,QACA,MACA,QACA,OACA,OACA,QACA,QACA,QACA,OACA,OACA,SACA,OACA,SACA,QACA,QACA,SACA,UACA,SACA,QACA,QACA,QACA,OACA,SACA,QACA,SACA,UACA,QACA,QACA,SACA,QACA,QACA,OACA,UACA,SACA,UACA,WACA,WACA,UACA,UACA,UACA,YAGFtC,WAAa,CACX,GACA,QACA,MACA,OACA,MACA,MACA,MACA,MACA,U9BiVEgR,EAA+FnL,E+Bvc9F,cAAuB1H,EAC5BE,aAAe,QACfC,qBAAuB,UACvBC,SAAW,OAEXmF,UAAY,CACV,EAAG,MACH,EAAG,MACH,EAAG,OACH,EAAG,QACH,EAAG,QACH,EAAG,OACH,EAAG,QACH,EAAG,MACH,EAAG,QAGLC,kBAAoB,CAClB,EAAG,MACH,EAAG,OACH,EAAG,OACH,EAAG,QACH,EAAG,QACH,EAAG,OACH,EAAG,QACH,EAAG,MACH,EAAG,QAGLC,WAAa,CACX,EAAG,OACH,EAAG,aACH,EAAG,eACH,EAAG,eACH,EAAG,cACH,EAAG,gBACH,EAAG,cACH,EAAG,gBACH,EAAG,cACH,EAAG,gBAGLqN,oBAAsB,CACpB,EAAG,OACH,EAAG,aACH,EAAG,cACH,EAAG,eACH,EAAG,cACH,EAAG,gBACH,EAAG,cACH,EAAG,gBACH,EAAG,cACH,EAAG,gBAGLpN,cAAgB,CACd,EAAG,WACH,EAAG,WACH,EAAG,YACH,EAAG,YACH,EAAG,UACH,EAAG,YACH,EAAG,UACH,EAAG,YAGLC,cAAgB,CACd,EAAG,SACH,EAAG,YACH,EAAG,YACH,EAAG,aACH,EAAG,aACH,EAAG,YACH,EAAG,aACH,EAAG,WACH,EAAG,aAULoN,cAAgB,CACd,EAAG,CAAEC,SAAU,MAAOC,OAAQ,MAAOhJ,UAAU,EAAMiJ,SAAS,GAC9D,EAAG,CAAEF,SAAU,SAAUC,OAAQ,WAAYhJ,UAAU,EAAOiJ,SAAS,GACvE,EAAG,CAAEF,SAAU,UAAWC,OAAQ,WAAYhJ,UAAU,EAAOiJ,SAAS,GACxE,EAAG,CAAEF,SAAU,UAAWC,OAAQ,YAAahJ,UAAU,EAAOiJ,SAAS,GACzE,EAAG,CAAEF,SAAU,aAAcC,OAAQ,eAAgBhJ,UAAU,EAAOiJ,SAAS,GAC/E,EAAG,CAAEF,SAAU,aAAcC,OAAQ,eAAgBhJ,UAAU,EAAOiJ,SAAS,GAC/E,EAAG,CAAEF,SAAU,YAAaC,OAAQ,cAAehJ,UAAU,EAAOiJ,SAAS,GAC7E,EAAG,CAAEF,SAAU,YAAaC,OAAQ,cAAehJ,UAAU,EAAOiJ,SAAS,GAC7E,EAAG,CAAEF,SAAU,WAAYC,OAAQ,aAAchJ,UAAU,EAAOiJ,SAAS,GAC3E,GAAI,CAAEF,SAAU,WAAYC,OAAQ,aAAchJ,UAAU,EAAOiJ,SAAS,IAG9EnN,WAAAA,CAAa9F,EAAU,IACrB+F,QAEAzF,KAAKW,WAAW,CACd+E,OAAQ,aACPhG,EACL,CAGAoE,eAAAA,CAAiB8H,EAAGT,GAClB,MAAMyH,EAAU,GACVC,EAAIjH,EAAE5K,OACZ,IAAIiC,EAEJ,GAAI4P,EAAI1H,EAAG,CACT,MAAMsE,EAAQoD,EAAI1H,EACdsE,EAAQ,IACVxM,EAAS2I,EAAE1K,MAAM,EAAGuO,GACpBmD,EAAQtS,KAAKc,OAAO6B,KAEtB,IAAK,IAAIiI,EAAQuE,EAAOvE,EAAQ2H,EAAG3H,GAASC,EAC1ClI,EAAS2I,EAAE1K,MAAMgK,EAAOA,EAAQC,GAChCyH,EAAQtS,KAAKc,OAAO6B,GAExB,MACE2P,EAAQtS,KAAKc,OAAOwK,IAEtB,OAAOgH,CACT,CAEA7M,aAAAA,CAAeO,GAGb,MADU,IADUA,EAAMtC,WAAW8O,SAAS,EAAG,KAAK5R,WAC3B6R,aAClBtQ,IAAIrB,OACf,CAGA4R,iBAAAA,CAAmB3O,EAAS4O,GAC1B,MAAMrH,EAAI3H,OAAOI,GAEjB,OAAU,IAANuH,EAGK,GADSqH,EAAKvJ,SAAW,IAAM,QACjBuJ,EAAKR,WAIlB,KAAN7G,GAAYqH,EAAKN,QACZ,sBAAsBM,EAAKP,SAU7B,GANO1S,KAAKkT,eAAetH,GAAG,KAGrBqH,EAAKN,SAAW/G,GAAK,GAChB,OAAS,MAEPqH,EAAKP,QAC9B,CAEAS,aAAAA,CAAevH,EAAGwH,GAAgB,EAAOC,GAAiB,GACxD,GAAIzH,EAAI,GACN,OAAQwH,EAAgBpT,KAAKiF,kBAAoBjF,KAAKgF,WAAW4G,GAEnE,GAAIA,EAAI,GACN,OAAQyH,EAAiBrT,KAAKuS,oBAAsBvS,KAAKkF,YAAY0G,EAAI,IAE3E,MAAM0H,EAAI7O,KAAKgK,MAAM7C,EAAI,IACnB2H,EAAI3H,EAAI,GACd,OAAO2H,EACH,GAAGvT,KAAKmF,cAAcmO,UAAUF,EAAgBpT,KAAKiF,kBAAoBjF,KAAKgF,WAAWuO,KACzFvT,KAAKmF,cAAcmO,EACzB,CAEAJ,cAAAA,CAAgBtH,EAAGwH,GAAgB,EAAOC,GAAiB,GACzD,GAAIzH,EAAI,IAAK,OAAO5L,KAAKmT,cAAcvH,EAAGwH,EAAeC,GACzD,MAAMG,EAAI/O,KAAKgK,MAAM7C,EAAI,KACnB6H,EAAI7H,EAAI,IACR8H,EAAe1T,KAAKoF,cAAcoO,GACxC,OAAKC,EAGE,GAAGC,KAA2B1T,KAAKmT,cAAcM,EAAGL,EAAeC,KAH3DK,CAIjB,CAGA5S,qBAAAA,CAAuBX,GACrB,GAAoB,KAAhBA,EAAoB,OAAOH,KAAKH,SAEpC,GAAIM,EAAc,MAAO,OAAOH,KAAKkT,eAAejP,OAAO9D,IAAc,GAAO,GAEhF,MAAME,EAAQ,GACR6D,EAAWlE,KAAK8D,gBAAgB3D,EAAY6D,WAAY,GAC9D,IAAIkH,EAAQhH,EAASlD,OACrB,IAAK,MAAMmK,KAAKjH,EAAU,CAExB,GADAgH,GAAgB,EACN,KAANC,EAAU,SACd,MAAOC,EAAIC,EAAIC,GAAMtL,KAAK+F,cAAcoF,GASxC,GARIG,EAAK,IAAIjL,EAAMC,KAAKN,KAAKoF,cAAcnB,OAAOqH,KAC9CD,EAAK,IAAIhL,EAAMC,KAAKN,KAAKmF,cAAclB,OAAOoH,KACvC,KAAPA,EACFhL,EAAMC,KAAKN,KAAKuS,oBAAoBtO,OAAOmH,KAClCA,EAAK,KACVC,EAAK,IAAIhL,EAAMC,KAAK,MACxBD,EAAMC,KAAKN,KAAKgF,UAAUf,OAAOmH,MAE/BF,EAAQ,EAAG,CACb,MAAM+H,EAAOjT,KAAKwS,cAActH,GAC5B+H,EACF5S,EAAMC,KAAKN,KAAKgT,kBAAkB7H,EAAG8H,IAErC5S,EAAMC,KAAKN,KAAKkT,eAAejP,OAAOkH,IAAI,GAE9C,CACF,CACA,OAAO9K,EAAMI,KAAK,KAAKuG,WAAW,OAAQ,KAAKlC,MACjD,CAEAvE,cAAAA,CAAgBJ,GACd,GAAoB,KAAhBA,EACF,OAAOH,KAAKH,SAEd,MAAMQ,EAAQ,GACR6D,EAAWlE,KAAK8D,gBAAgB3D,EAAY6D,WAAY,GAC9D,IAAIkH,EAAQhH,EAASlD,OACrB,IAAK,MAAMmK,KAAKjH,EAAU,CACxB,IAAIyP,EAAU,GAEd,GADAzI,GAAgB,EACN,KAANC,EAAU,SACd,MAAOC,EAAIC,EAAIC,GAAMtL,KAAK+F,cAAcoF,GAsBxC,GApBIG,EAAK,IAAgB,IAAVJ,GACb7K,EAAMC,KAAKN,KAAKoF,cAAcnB,OAAOqH,KAGzB,IAAVJ,IACEG,EAAK,IACPhL,EAAMC,KAAKN,KAAKmF,cAAclB,OAAOoH,KAE5B,KAAPA,EACFhL,EAAMC,KAAKN,KAAKkF,WAAWjB,OAAOmH,KACzBA,EAAK,KAGduI,EAD8C,aAAxB3T,KAAKN,QAAQgG,QAAmC,IAAVwF,EAClClL,KAAKiF,kBAAoBjF,KAAKgF,UAEpDqG,EAAK,IAAIhL,EAAMC,KAAK,MACxBD,EAAMC,KAAKqT,EAAQ1P,OAAOmH,OAI1BF,EAAQ,EAAG,CACb,MAAM+H,EAAOjT,KAAKwS,cAActH,GAC5B+H,EACF5S,EAAMC,KAAKN,KAAKgT,kBAAkB7H,EAAG8H,IAGrC5S,EAAMC,KAAKN,KAAKkT,eAAejP,OAAOkH,IAAI,GAE9C,CACF,CACA,OAAO9K,EAAMI,KAAK,KAAKuG,WAAW,OAAQ,KAAKlC,MACjD,I/BmMI8O,EAA6FzM,EgCxc5F,cAAsBpC,EAC3BpF,aAAe,QACfC,qBAAuB,UACvBC,SAAW,OAEXmF,UAAY,CACV,EAAG,OACH,EAAG,MACH,EAAG,MACH,EAAG,SACH,EAAG,OACH,EAAG,QACH,EAAG,OACH,EAAG,SACH,EAAG,UAGLC,kBAAoB,CAClB,EAAG,OACH,EAAG,MACH,EAAG,MACH,EAAG,SACH,EAAG,OACH,EAAG,QACH,EAAG,OACH,EAAG,SACH,EAAG,UAGLC,WAAa,CACX,EAAG,SACH,EAAG,cACH,EAAG,aACH,EAAG,aACH,EAAG,eACH,EAAG,aACH,EAAG,cACH,EAAG,aACH,EAAG,eACH,EAAG,gBAGLC,cAAgB,CACd,EAAG,WACH,EAAG,WACH,EAAG,QACH,EAAG,YACH,EAAG,aACH,EAAG,YACH,EAAG,cACH,EAAG,aAGLC,cAAgB,CACd,EAAG,MACH,EAAG,SACH,EAAG,SACH,EAAG,YACH,EAAG,UACH,EAAG,WACH,EAAG,UACH,EAAG,YACH,EAAG,aAGLC,YAAc,CACZ,EAAG,CAAC,SAAU,SAAU,SACxB,EAAG,CAAC,UAAW,WAAY,aAC3B,EAAG,CAAC,WAAY,YAAa,cAC7B,EAAG,CAAC,WAAY,YAAa,cAC7B,EAAG,CAAC,cAAe,eAAgB,iBACnC,EAAG,CAAC,cAAe,eAAgB,iBACnC,EAAG,CAAC,cAAe,eAAgB,iBACnC,EAAG,CAAC,aAAc,cAAe,gBACjC,EAAG,CAAC,YAAa,aAAc,eAC/B,GAAI,CAAC,YAAa,aAAc,gBAOlCC,aAAe,CACb,GAAG,KhCsXDuO,EAA6G1M,EiCzc5G,cAA8BpC,EACnCpF,aAAe,QACfC,qBAAuB,SACvBC,SAAW,OAEXmF,UAAY,CACV,EAAG,QACH,EAAG,MACH,EAAG,MACH,EAAG,SACH,EAAG,MACH,EAAG,OACH,EAAG,QACH,EAAG,OACH,EAAG,SAGLC,kBAAoB,CAClB,EAAG,QACH,EAAG,MACH,EAAG,MACH,EAAG,SACH,EAAG,MACH,EAAG,OACH,EAAG,QACH,EAAG,OACH,EAAG,SAGLC,WAAa,CACX,EAAG,QACH,EAAG,YACH,EAAG,WACH,EAAG,WACH,EAAG,YACH,EAAG,WACH,EAAG,WACH,EAAG,aACH,EAAG,YACH,EAAG,cAGLC,cAAgB,CACd,EAAG,WACH,EAAG,WACH,EAAG,YACH,EAAG,UACH,EAAG,WACH,EAAG,aACH,EAAG,YACH,EAAG,aAGLC,cAAgB,CACd,EAAG,MACH,EAAG,SACH,EAAG,SACH,EAAG,YACH,EAAG,SACH,EAAG,QACH,EAAG,WACH,EAAG,UACH,EAAG,WAGLC,YAAc,CACZ,EAAG,CAAC,SAAU,SAAU,UACxB,EAAG,CAAC,SAAU,UAAW,WACzB,EAAG,CAAC,YAAa,YAAa,aAC9B,EAAG,CAAC,SAAU,UAAW,WACzB,EAAG,CAAC,YAAa,YAAa,aAC9B,EAAG,CAAC,UAAW,WAAY,YAC3B,EAAG,CAAC,aAAc,aAAc,cAChC,EAAG,CAAC,aAAc,cAAe,eACjC,EAAG,CAAC,gBAAiB,gBAAiB,iBACtC,GAAI,CAAC,aAAc,cAAe,gBAQpCC,aAAe,CACb,GAAG,GAILW,SAAAA,CAAW2F,EAAGC,GACZ,MAAMC,EAAYF,EAAI,IAChBG,EAAgBH,EAAI,KAE1B,OAAKG,EAAgB,KAAOA,EAAgB,MAAsB,KAAdD,EAC3CD,EAAM,IAGVE,EAAgB,KAAOA,EAAgB,MAAQD,EAAY,IAAMA,EAAY,GACzED,EAAM,GAGRA,EAAM,EACf,IjCqWIiI,EAAuG3M,EkC1ctG,cAA2BpC,EAChCpF,aAAe,QACfC,qBAAuB,SACvBC,SAAW,OAEXmF,UAAY,CACV,EAAG,QACH,EAAG,MACH,EAAG,MACH,EAAG,SACH,EAAG,MACH,EAAG,OACH,EAAG,QACH,EAAG,OACH,EAAG,SAGLC,kBAAoB,CAClB,EAAG,QACH,EAAG,MACH,EAAG,MACH,EAAG,SACH,EAAG,MACH,EAAG,OACH,EAAG,QACH,EAAG,OACH,EAAG,SAGLC,WAAa,CACX,EAAG,QACH,EAAG,YACH,EAAG,WACH,EAAG,WACH,EAAG,YACH,EAAG,WACH,EAAG,WACH,EAAG,aACH,EAAG,YACH,EAAG,cAGLC,cAAgB,CACd,EAAG,WACH,EAAG,WACH,EAAG,YACH,EAAG,UACH,EAAG,WACH,EAAG,aACH,EAAG,YACH,EAAG,aAGLC,cAAgB,CACd,EAAG,MACH,EAAG,SACH,EAAG,SACH,EAAG,YACH,EAAG,SACH,EAAG,QACH,EAAG,WACH,EAAG,UACH,EAAG,YAGLC,YAAc,CACZ,EAAG,CAAC,UAAW,UAAW,WAC1B,EAAG,CAAC,SAAU,UAAW,WACzB,EAAG,CAAC,YAAa,YAAa,aAC9B,EAAG,CAAC,SAAU,UAAW,WACzB,EAAG,CAAC,YAAa,YAAa,aAC9B,EAAG,CAAC,UAAW,WAAY,YAC3B,EAAG,CAAC,aAAc,aAAc,cAChC,EAAG,CAAC,aAAc,cAAe,eACjC,EAAG,CAAC,gBAAiB,gBAAiB,iBACtC,GAAI,CAAC,aAAc,cAAe,gBAQpCC,aAAe,CACb,GAAG,GAILW,SAAAA,CAAW2F,EAAGC,GACZ,MAAMC,EAAYF,EAAI,IAChBG,EAAgBH,EAAI,KAE1B,OAAKG,EAAgB,KAAOA,EAAgB,MAAsB,KAAdD,EAC3CD,EAAM,IAGVE,EAAgB,KAAOA,EAAgB,MAAQD,EAAY,IAAMA,EAAY,GACzED,EAAM,GAGRA,EAAM,EACf,IlCsWIkI,EAAiH5M,EmC3chH,cAAgC9F,EACrC1B,aAAe,IACfC,qBAAuB,IACvBC,SAAW,IACXC,cAAgB,GAEhB0F,WAAAA,CAAa9F,EAAU,IACrB+F,QAEAzF,KAAKW,WAAW,CACdqT,QAAQ,GACPtU,GAECM,KAAKN,QAAQsU,OACfhU,KAAKsB,WAAa,CAChB,CAAC,eAAoB,KACrB,CAAC,WAAc,KACf,CAAC,OAAS,KACV,CAAC,MAAO,KACR,CAAC,KAAM,KACP,CAAC,IAAK,KACN,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,MAGPtB,KAAKsB,WAAa,CAChB,CAAC,eAAoB,KACrB,CAAC,WAAc,KACf,CAAC,OAAS,KACV,CAAC,MAAO,KACR,CAAC,KAAM,KACP,CAAC,IAAK,KACN,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KACL,CAAC,GAAI,KAGX,CAGAiB,eAAAA,CAAiBI,EAAWC,GAC1B,MAAMS,EAAgBH,OAAOC,KAAKR,GAAW,GACvCW,EAAiBJ,OAAO0D,OAAOjE,GAAW,GAC1Ca,EAAgBN,OAAOC,KAAKP,GAAW,GACvCa,EAAiBP,OAAO0D,OAAOhE,GAAW,GAGhD,OAAuB,KAAnBU,GAAyBG,EAAiB,IACrCb,EAILa,EAAiBH,EACZ,CAAE,CAAC,GAAGD,IAAgBG,KAAkBF,EAAiBG,GAK9DzD,KAAKiU,UAAU3Q,GAAkBtD,KAAKkU,MAAMzQ,GACvC,CAAE,CAAC,GAAGJ,IAAgBrD,KAAKH,WAAW2D,KAAkBF,EAAiBG,GAI3E,CAAE,CAAC,GAAGJ,IAAgBG,KAAkBF,EAAiBG,EAClE,CAGAyQ,KAAAA,CAAOC,GACL,OAAOA,EAAQnQ,WAAWhD,MAC5B,CAGAiT,SAAAA,CAAWE,GACT,MAAO,IAAIA,EAAQnQ,YAAYoQ,OAAOC,GAAW,MAANA,GAAWrT,MACxD,CAGAR,oBAAAA,CAAsB8T,GACpB,MAAMjU,EAAQ,GACd,IAAK,IAAIU,EAAI,EAAGA,EAAIuT,EAActT,OAAQD,IAAK,CAC7C,MAAMwT,EAAanT,OAAOkT,EAAcvT,IACxCV,EAAMC,KAAKN,KAAKO,eAAegU,GACjC,CACA,OAAOlU,CACT,InCyWImU,EAA6FrN,EoC5c5F,cAAsB9F,EAC3B1B,aAAe,QACfC,qBAAuB,QACvBC,SAAW,OAEXyB,WAAa,CACX,CAAC,2BAAoC,cACrC,CAAC,qBAA4B,WAC7B,CAAC,eAAoB,UACrB,CAAC,SAAY,UACb,CAAC,MAAO,OACR,CAAC,KAAM,QACP,CAAC,IAAK,WACN,CAAC,IAAK,WACN,CAAC,IAAK,WACN,CAAC,IAAK,WACN,CAAC,IAAK,aACN,CAAC,IAAK,YACN,CAAC,IAAK,WACN,CAAC,IAAK,eACN,CAAC,IAAK,cACN,CAAC,IAAK,eACN,CAAC,IAAK,cACN,CAAC,IAAK,eACN,CAAC,IAAK,gBACN,CAAC,IAAK,cACN,CAAC,IAAK,aACN,CAAC,IAAK,aACN,CAAC,IAAK,UACN,CAAC,IAAK,cACN,CAAC,IAAK,aACN,CAAC,IAAK,cACN,CAAC,IAAK,aACN,CAAC,IAAK,UACN,CAAC,IAAK,WACN,CAAC,IAAK,SACN,CAAC,IAAK,QACN,CAAC,IAAK,QACN,CAAC,IAAK,QACN,CAAC,GAAI,SACL,CAAC,GAAI,QACL,CAAC,GAAI,SACL,CAAC,GAAI,QACL,CAAC,GAAI,SACL,CAAC,GAAI,UACL,CAAC,GAAI,QACL,CAAC,GAAI,OACL,CAAC,GAAI,OACL,CAAC,GAAI,SAGPkE,WAAAA,CAAa9F,EAAU,IACrB+F,QAEAzF,KAAKW,WAAW,CACd+E,OAAQ,aACPhG,GAGyB,aAAxBM,KAAKN,QAAQgG,SACf1F,KAAKsB,WAAatB,KAAKsB,WAAWmB,IAAI,EAAE6D,EAAOwJ,KAChC,cAATA,EAA6B,CAACxJ,EAAO,aAC5B,QAATwJ,EAAuB,CAACxJ,EAAO,OAC5B,CAACA,EAAOwJ,IAGrB,CAGAvN,eAAAA,CAAiBI,EAAWC,GAC1B,IAAIS,EAAgBH,OAAOC,KAAKR,GAAW,GACvCa,EAAgBN,OAAOC,KAAKP,GAAW,GAC3C,MAAMU,EAAiBJ,OAAO0D,OAAOjE,GAAW,GAC1Cc,EAAiBP,OAAO0D,OAAOhE,GAAW,GAC1C6R,EAAqC,aAAxBzU,KAAKN,QAAQgG,OAAwB,IAAM,IAE9D,GAAuB,KAAnBpC,EAAuB,CACzB,GAAIG,EAAiB,SAAY,OAAOb,EACxCS,EAAgB,IAClB,MAA8B,OAAnBC,GAA2BG,EAAiB,OAAU,KAC/DJ,GAAiB,IAAMoR,GAGzB,OAAIhR,EAAiBH,EACfA,EAAiB,KACZ,CAAE,CAAC,GAAGD,OAAmBG,KAAkBF,EAAiBG,GAE9D,CAAE,CAAC,GAAGJ,KAAiBG,KAAkBF,EAAiBG,IAG/DA,EAAiB,UAAe,IAAMH,EAAiB,KACzDE,EAAgBA,EAActC,MAAM,GAAG,GAAM,SAGxB,OAAnBuC,GACqB,KAAnBH,GACFD,EAAgB,UAChBG,EAAgB,IACY,KAAnBF,EACTD,EAAgB,OACY,KAAnBC,IACTD,EAAgB,QAElBG,GAAiB,IAAMiR,EAAa,KAEpCjR,EAAgB,IAAMA,EAGjB,CAAE,CAAC,GAAGH,IAAgBG,KAAkBF,EAAiBG,GAClE,IpCgWIiR,EAAmEvN,EqC7clE,cAAsB1H,EAC3BE,aAAe,QACfC,qBAAuB,QACvBC,SAAW,SAEXmF,UAAY,CACV,EAAG,SACH,EAAG,OACH,EAAG,QACH,EAAG,OACH,EAAG,MACH,EAAG,OACH,EAAG,OACH,EAAG,OACH,EAAG,OACH,EAAG,QAGLmE,UAAY,CACV,GAAI,OACJ,GAAI,WACJ,GAAI,aACJ,GAAI,WACJ,GAAI,UACJ,GAAI,SACJ,GAAI,SACJ,GAAI,YACJ,GAAI,UAGN7H,WAAa,CACX,GACA,OACA,UACA,UACA,YAGFqT,aAAAA,CAAe/I,GACb,GAAIA,EAAI,GAAI,OAAO5L,KAAKgF,UAAU4G,GAClC,GAAU,KAANA,EAAU,OAAO5L,KAAKmJ,UAAU,IACpC,GAAIyC,EAAI,GAEN,OAAO5L,KAAKmJ,UAAU,IAAM,OAASnJ,KAAKgF,UAAU4G,EAAI,IAE1D,MAAM7B,EAA4B,GAArBtF,KAAKC,MAAMkH,EAAI,IACtBpC,EAAOoC,EAAI,GACjB,OAAa,IAATpC,EAAmBxJ,KAAKmJ,UAAUY,GAC/B/J,KAAKmJ,UAAUY,GAAQ,OAAS/J,KAAKgF,UAAUwE,EACxD,CAEAoL,cAAAA,CAAgBhJ,GACd,GAAIA,EAAI,IAAK,OAAO5L,KAAK2U,cAAc/I,GACvC,GAAU,MAANA,EAAW,MAAO,WACtB,MAAMpH,EAAWC,KAAKC,MAAMkH,EAAI,KAC1BzJ,EAAOyJ,EAAI,IACXjH,EAAQ,GAYd,OATAA,EAAMrE,KAAK,OAASN,KAAKgF,UAAUR,IAC/BrC,EAAO,IACLA,EAAO,GACTwC,EAAMrE,KAAK,MAAQN,KAAKgF,UAAU7C,IAElCwC,EAAMrE,KAAKN,KAAK2U,cAAcxS,KAI3BwC,EAAMlE,KAAK,IACpB,CAEAmO,QAAAA,CAAUrI,GACR,MAAMsO,EAAItO,EAAOvC,WACjB,GAAI6Q,EAAE7T,QAAU,EAAG,MAAO,CAACiD,OAAO4Q,IAClC,MAAM5D,EAAS,GACT9M,EAAQ0Q,EAAE3T,UAChB+P,EAAO7M,QAAQH,OAAOE,IACtB,IAAIrC,EAAY+S,EAAE3T,MAAM,GAAG,GAC3B,KAAOY,EAAUd,OAAS,GAAG,CAC3B,MAAMwJ,EAAQ1I,EAAUZ,UACxB+P,EAAO7M,QAAQH,OAAOuG,IACtB1I,EAAYA,EAAUZ,MAAM,GAAG,EACjC,CACA,OAAO+P,CACT,CAEA1Q,cAAAA,CAAgBJ,GACd,GAAoB,KAAhBA,EAAoB,OAAOH,KAAKH,SAEpC,MAAMoR,EAASjR,KAAK4O,SAASzO,GACvBwE,EAAQ,GAEd,IAAK,IAAI5D,EAAI,EAAGA,EAAIkQ,EAAOjQ,OAAQD,IAAK,CACtC,MAAM+T,EAAM7D,EAAOlQ,GACnB,GAAY,IAAR+T,EAAW,SACf,MAAMjQ,EAAaoM,EAAOjQ,OAASD,EAAI,EAEvC,GAAmB,IAAf8D,EACEiQ,EAAM,IAAMnQ,EAAM3D,OAAS,EAC7B2D,EAAMrE,KAAK,MAAQN,KAAKgF,UAAU8P,IACjB,MAARA,GAAenQ,EAAM3D,OAAS,EAEvC2D,EAAMrE,KAAK,OAEXqE,EAAMrE,KAAKN,KAAK4U,eAAeE,QAE5B,CAEL,MAAMC,EAAgB,IAARD,EAAa,OAAS9U,KAAK4U,eAAeE,GACxDnQ,EAAMrE,KAAKN,KAAKsB,WAAWuD,GAAc,IAAMkQ,EACjD,CACF,CAEA,OAAOpQ,EAAMlE,KAAK,KAAKqE,MACzB,IrC4VIkQ,EAAmE7N,EsC9clE,cAAsB9F,EAC3B1B,aAAe,QACfC,qBAAuB,QACvBC,SAAW,OACXC,cAAgB,IAEhBwB,WAAa,CACX,CAAC,8BAAwC,cACzC,CAAC,2BAAoC,WACrC,CAAC,wBAAgC,UACjC,CAAC,qBAA4B,WAC7B,CAAC,eAAoB,UACrB,CAAC,YAAgB,WACjB,CAAC,SAAY,UACb,CAAC,MAAO,SACR,CAAC,KAAM,UACP,CAAC,IAAK,UACN,CAAC,IAAK,SACN,CAAC,IAAK,WACN,CAAC,IAAK,UACN,CAAC,IAAK,UACN,CAAC,IAAK,UACN,CAAC,IAAK,WACN,CAAC,IAAK,SACN,CAAC,IAAK,UACN,CAAC,IAAK,SACN,CAAC,IAAK,WACN,CAAC,IAAK,UACN,CAAC,IAAK,UACN,CAAC,IAAK,WACN,CAAC,IAAK,WACN,CAAC,IAAK,QACN,CAAC,IAAK,QACN,CAAC,IAAK,OACN,CAAC,GAAI,OACL,CAAC,GAAI,QACL,CAAC,GAAI,OACL,CAAC,GAAI,OACL,CAAC,GAAI,OACL,CAAC,GAAI,QACL,CAAC,GAAI,OACL,CAAC,GAAI,OACL,CAAC,GAAI,OACL,CAAC,GAAI,SAIPiB,eAAAA,CAAiBI,EAAWC,GAC1B,MAAMS,EAAgBH,OAAOC,KAAKR,GAAW,GACvCa,EAAgBN,OAAOC,KAAKP,GAAW,GACvCU,EAAiBJ,OAAO0D,OAAOjE,GAAW,GAC1Cc,EAAiBP,OAAO0D,OAAOhE,GAAW,GAEhD,OAAuB,KAAnBU,GAAyBG,EAAiB,KACrCb,EAIc,KAAnBU,GAA6C,OAAnBG,GAA8C,QAAnBA,EAIrDH,EAAiB,MAAQA,EAAiBG,EACrC,CAAE,CAAC,GAAGJ,KAAiBG,KAAkBF,EAAiBG,GAG/DH,GAAkB,MAAQG,EAAiB,KACtC,CAAE,CAAC,GAAGJ,SAAqBG,KAAkBF,EAAiBG,GAGnEA,EAAiBH,EACI,KAAnBA,GAAyBG,GAAkB,SACtC,CAAE,CAAC,MAAMD,KAAkBF,EAAiBG,GAE9C,CAAE,CAAC,GAAGJ,KAAiBG,KAAkBF,EAAiBG,GAG5D,CAAE,CAAC,GAAGJ,KAAiBG,KAAkBF,EAAiBG,GAlBxDb,CAmBX,ItCiYIqS,EAAiE9N,EuC/chE,cAAoB1H,EACzBE,aAAe,QACfC,qBAAuB,SACvBC,SAAW,YACXE,qBAAsB,EAEtB6D,kBAAoB,CAClB,YACA,QACA,SACA,SACA,SACA,QACA,MACA,MACA,QACA,SACA,QACA,YACA,aACA,YACA,YACA,YACA,UACA,UACA,YACA,aACA,SACA,eACA,gBACA,iBACA,iBACA,eACA,aACA,aACA,eACA,gBACA,UACA,gBACA,iBACA,kBACA,kBACA,gBACA,cACA,cACA,gBACA,iBACA,UACA,gBACA,iBACA,kBACA,kBACA,gBACA,cACA,cACA,gBACA,iBACA,SACA,eACA,gBACA,iBACA,iBACA,eACA,aACA,aACA,eACA,gBACA,SACA,eACA,gBACA,iBACA,iBACA,eACA,aACA,aACA,eACA,gBACA,SACA,eACA,gBACA,iBACA,iBACA,eACA,aACA,aACA,eACA,gBACA,SACA,eACA,gBACA,iBACA,iBACA,eACA,aACA,aACA,eACA,gBACA,WACA,mBACA,oBACA,oBACA,oBACA,mBACA,iBACA,iBACA,mBACA,qBAGFwB,cAAgB,CACd,GACA,OACA,UACA,WACA,SACA,QACA,UACA,UACA,UACA,eAIFJ,UAAY,CACV,QACA,SACA,SACA,SACA,QACA,MACA,MACA,QACA,UAGF1D,WAAa,CACX,GACA,SACA,SACA,OACA,OACA,QACA,OACA,OACA,SAIF4T,mBAAAA,CAAqB3O,GACnB,OAAOvG,KAAK4D,kBAAkB2C,EAChC,CAEA4O,oBAAAA,CAAsB5O,GACpB,GAAe,IAAXA,EAAc,MAAO,GACzB,GAAIA,EAAS,IAAK,OAAOvG,KAAKkV,oBAAoB3O,GAElD,MAAM/B,EAAWC,KAAKC,MAAM6B,EAAS,KAC/BtF,EAAYsF,EAAS,IAC3B,IAAI6O,EAAcpV,KAAKoF,cAAcZ,GAEjCvD,EAAY,IACVmU,EAAYzI,SAAS,MACvByI,EAAcA,EAAYlO,QAAQ,MAAO,QAChCkO,EAAYzI,SAAS,QAC9ByI,EAAcA,EAAYlU,MAAM,GAAG,GAAM,SAI7C,MAAMyD,EAAQ,CAACyQ,GAKf,OAJInU,EAAY,GACd0D,EAAMrE,KAAKN,KAAKkV,oBAAoBjU,IAG/B0D,EAAMlE,KAAK,KAAKqE,MACzB,CAEAuQ,WAAAA,CAAa9O,GAC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Ae,OACfC,qBAAuB,UACvBC,SAAW,MACXgE,YAAc,KAEdD,kBAAoB,CAClB,MACA,MACA,KACA,MACA,MACA,OACA,KACA,MACA,MACA,KACA,KACA,QACA,OACA,OACA,OACA,QACA,OACA,OACA,SACA,OACA,MACA,OACA,QACA,QACA,QACA,OACA,QACA,SACA,UACA,QACA,MACA,QACA,OACA,SACA,SACA,SACA,QACA,SACA,QACA,UACA,QACA,UACA,SACA,WACA,SACA,WACA,SACA,WACA,UACA,QACA,OACA,QACA,OACA,OACA,MACA,OACA,OACA,QACA,SACA,QACA,OACA,QACA,QACA,QACA,SACA,SACA,UACA,QACA,QACA,QACA,MACA,QACA,OACA,OACA,QACA,QACA,QACA,OACA,SACA,QACA,MACA,SACA,QACA,SACA,SACA,QACA,SACA,QACA,SACA,QACA,MACA,SACA,QACA,SACA,UACA,SACA,UACA,SACA,UACA,UAGFtC,WAAa,CACX,GACA,OACA,OACA,OACA,MACA,OACA,MACA,MACA,U7CgWEgV,GAAsEnP,E8CtdrE,cAAyB1H,EAC9BE,aAAe,KACfC,qBAAuB,OACvBC,SAAW,QAEX0W,iBAAmB,CACjB,EAAG,QACH,EAAG,MACH,EAAG,MACH,EAAG,KACH,EAAG,MACH,EAAG,MACH,EAAG,MACH,EAAG,MACH,EAAG,MACH,EAAG,OACH,GAAI,OACJ,GAAI,WACJ,GAAI,WACJ,GAAI,UACJ,GAAI,WACJ,GAAI,WACJ,GAAI,WACJ,GAAI,WACJ,GAAI,WACJ,GAAI,aAGNpR,cAAgB,CACd,GAAI,WACJ,GAAI,UACJ,GAAI,WACJ,GAAI,WACJ,GAAI,WACJ,GAAI,WACJ,GAAI,WACJ,GAAI,aAGN7D,WAAa,CACX,EAAG,QACH,EAAG,QACH,EAAG,KACH,EAAG,WACH,EAAG,gBACH,EAAG,cACH,EAAG,aACH,EAAG,aACH,EAAG,YACH,GAAI,YACJ,GAAI,YACJ,GAAI,cACJ,GAAI,eACJ,GAAI,eACJ,GAAI,oBACJ,GAAI,eACJ,GAAI,kBACJ,GAAI,gBACJ,GAAI,iBACJ,GAAI,gBAINkV,cAAAA,CAAgBjQ,GACd,MAAMkQ,EAAYlQ,EAAS,GACrBmQ,EAAWnQ,EAASkQ,EACpBE,EAAe3W,KAAKmF,cAAcuR,GACxC,GAAkB,IAAdD,EACF,OAAOE,EAGT,IAAI1E,EADkBjS,KAAKuW,iBAAiBE,GAQ5C,OANkB,IAAdA,IACFxE,EAAS,OAEO,IAAdwE,IACFxE,EAAS,OAEJ0E,EAAe,IAAM1E,CAC9B,CAGA2E,eAAAA,CAAiBrQ,GACf,MAAMlG,EAAQ,GACRwW,EAAgBtQ,EAAS,IACzBuQ,EAAevQ,EAASsQ,EAiB9B,OAhBIC,EAAe,GACjBzW,EAAMC,KAAKN,KAAKuW,iBAAiBO,EAAe,KAAM,QAEpDD,EAAgB,GAAKA,EAAgB,KACnCxW,EAAMW,OAAS,GACjBX,EAAMC,KAAK,MAES,IAAlBuW,EACFxW,EAAMC,KAAK,OAEXD,EAAMC,KAAKN,KAAKuW,iBAAiBM,KAGjCA,GAAiB,IACnBxW,EAAMC,KAAKN,KAAKO,eAAesW,IAE1BxW,EAAMI,KAAK,IACpB,CAGAsW,eAAAA,CAAiBxQ,GACf,MAAMlG,EAAQ,GACd,IAAI2W,EAAWzQ,EAAS,MACpB0Q,EAAQ,EACZ,KAAOD,GAAY,OACjBA,GAAsB,MACtBC,GAAgB,EAElB,MAAMxD,EAAIlN,EAAUyQ,EAAW5V,OAAOqD,KAAK2F,IAAI,IAAM6M,IAQrD,OAPA5W,EAAMC,KAAKN,KAAKO,eAAeyW,GAAWhX,KAAKsB,WAAW2V,IACtDxD,EAAI,KACFA,GAAK,KACPpT,EAAMC,KAAK,MAEbD,EAAMC,KAAKN,KAAKO,eAAekT,KAE1BpT,EAAMI,KAAK,IACpB,CAEAF,cAAAA,CAAgBJ,GACd,OAAIA,EAAc,IACTH,KAAKuW,iBAAiBtS,OAAO9D,IAEhCA,EAAc,KACTH,KAAKwW,eAAevS,OAAO9D,IAE1BA,EAAc,MAAQH,KAAK4W,gBAAgB3S,OAAO9D,IAAgBH,KAAK+W,gBAAgB5W,EAGrG"}
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/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/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 converter with precomputed lookup tables.\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\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return SEGMENTS[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(SEGMENTS[segment])\n } else {\n parts.push(SEGMENTS[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 converter with precomputed lookup tables.\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\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return SEGMENTS[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(SEGMENTS[segment])\n } else {\n parts.push(SEGMENTS[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 converter with precomputed lookup tables.\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\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return SEGMENTS[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(SEGMENTS[segment])\n } else if (scaleIndex === 1 && segment === 1) {\n // Omit \"bir\" before thousand\n parts.push(scaleWord)\n } else {\n parts.push(SEGMENTS[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 converter for South Asian numbering.\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 Splitting (inlined for performance)\n// ============================================================================\n\nfunction groupByThreeThenTwos (n) {\n const numStr = n.toString()\n if (numStr.length <= 3) return [Number(numStr)]\n\n const segments = []\n segments.unshift(Number(numStr.slice(-3)))\n\n let remaining = numStr.slice(0, -3)\n while (remaining.length > 0) {\n segments.unshift(Number(remaining.slice(-2)))\n remaining = remaining.slice(0, -2)\n }\n\n return segments\n}\n\nfunction segmentToWords (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\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n const segments = groupByThreeThenTwos(n)\n const segmentCount = segments.length\n const words = []\n\n for (let i = 0; i < segmentCount; i++) {\n const segmentValue = segments[i]\n if (segmentValue === 0) continue\n\n const scaleIndex = segmentCount - i - 1\n words.push(segmentToWords(segmentValue))\n if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {\n words.push(SCALE_WORDS[scaleIndex])\n }\n }\n\n return words.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 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 * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Czech-specific rules (handled in precomputation):\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// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Builds segment word for 0-999 (masculine, default form).\n * Only used during table construction.\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// Precompute all 1000 segment words (0-999) - masculine form\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// Precompute all 1000 segment words with hundreds (irregular hundreds forms)\nconst SEGMENTS_WITH_HUNDREDS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_WITH_HUNDREDS[i] = buildSegmentWithHundreds(i)\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 (direct lookup)\n if (n < 1000n) {\n return SEGMENTS[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 = SEGMENTS[Number(thousands)] + ' ' + scaleWord\n }\n\n if (remainder > 0) {\n // Use form with irregular hundreds (for \"dvě stě\" etc.)\n result += ' ' + SEGMENTS_WITH_HUNDREDS[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 += SEGMENTS_WITH_HUNDREDS[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 += SEGMENTS[Number(segment)] + ' ' + scaleWord\n }\n } else {\n // Fallback for very large scales without defined forms\n result += SEGMENTS[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 * A performance-optimized number-to-words converter using precomputed lookup tables.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999)\nconst SEGMENTS = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\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 SEGMENTS[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 = SEGMENTS[thousands] + THOUSAND\n\n if (remainder > 0) {\n // Add 'e' suffix and \" og \" for remainder: \"firetusinde og ...\"\n result += 'e og ' + SEGMENTS[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 = SEGMENTS[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 * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * German-specific rules (handled in precomputation):\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 */\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// Precomputed Lookup Tables (built once at module load)\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 * Only used during table construction.\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: \"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 */\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.floor(n / 10) % 10\n const hundreds = Math.floor(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 // After hundreds, ones 1 stays \"eins\" when not followed by scale\n // But we're going to tausend, so this path won't hit for n=1\n result += ONES[ones]\n } else if (ones > 0) {\n result += ONES[ones]\n }\n\n return result\n}\n\n// Precompute all 1000 segment words (0-999) - standalone form\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// Precompute all 1000 segment words for thousand context\nconst SEGMENTS_THOUSAND = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_THOUSAND[i] = buildSegmentForThousand(i)\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 (direct lookup)\n if (n < 1000n) {\n return SEGMENTS[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: \"eintausendzweihundert\" (no spaces)\n let result = SEGMENTS_THOUSAND[thousands] + SCALES[0]\n\n if (remainder > 0) {\n result += SEGMENTS[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: SEGMENTS[segment], isScale: false, scaleLevel: 0 })\n } else if (scaleIndex === 1) {\n // Thousands: compound without space\n const segWords = SEGMENTS_THOUSAND[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 = SEGMENTS[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 * A performance-optimized number-to-words converter using precomputed lookup tables.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999)\nconst SEGMENTS = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\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 SEGMENTS[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 = SEGMENTS[thousands] + ' ' + THOUSAND\n }\n\n if (remainder > 0) {\n result += ' ' + SEGMENTS[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 = SEGMENTS[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 v2\n *\n * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * English-specific rules (handled in precomputation):\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 */\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// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Builds segment word for 0-999.\n * Returns object with word and whether it contains \"hundred\" (for \"and\" logic).\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 let result = ''\n let hasHundred = false\n\n // Hundreds\n if (hundreds > 0) {\n result = ONES[hundreds] + ' ' + HUNDRED\n hasHundred = true\n }\n\n // Tens and ones\n let tensOnes = ''\n if (tens === 1) {\n tensOnes = TEENS[ones]\n } else if (tens >= 2) {\n if (ones > 0) {\n tensOnes = TENS[tens] + '-' + ONES[ones]\n } else {\n tensOnes = TENS[tens]\n }\n } else if (ones > 0) {\n tensOnes = ONES[ones]\n }\n\n // Combine with \"and\" after hundreds\n if (result && tensOnes) {\n return { word: result + ' and ' + tensOnes, hasHundred: true }\n }\n\n return { word: result || tensOnes, hasHundred }\n}\n\n// Precompute all 1000 segment words (0-999)\nconst SEGMENTS = new Array(1000)\nconst SEGMENTS_HAS_HUNDRED = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n const result = buildSegment(i)\n SEGMENTS[i] = result.word\n SEGMENTS_HAS_HUNDRED[i] = result.hasHundred\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 (direct lookup)\n if (n < 1000n) {\n return SEGMENTS[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 let result = SEGMENTS[thousands] + ' ' + SCALES[0]\n\n if (remainder > 0) {\n const remainderWord = SEGMENTS[remainder]\n // Insert \"and\" if remainder doesn't have hundred\n if (!SEGMENTS_HAS_HUNDRED[remainder]) {\n result += ' and ' + remainderWord\n } else {\n result += ' ' + remainderWord\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} English words\n */\nfunction buildLargeNumberWords (n) {\n // Extract segments using BigInt division (faster than string slicing)\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 directly (avoids intermediate object allocations)\n // Process from most-significant (end) to least-significant (start)\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 segmentWord = SEGMENTS[segment]\n const hasHundred = SEGMENTS_HAS_HUNDRED[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 += segmentWord\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 * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Spanish-specific rules (handled in precomputation):\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999) for masculine\nconst SEGMENTS_MASC = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_MASC[i] = buildSegment(i, false)\n}\n\n// Precompute all 1000 segment words (0-999) for feminine\nconst SEGMENTS_FEM = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_FEM[i] = buildSegment(i, true)\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 const segments = feminine ? SEGMENTS_FEM : SEGMENTS_MASC\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return segments[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 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 = SEGMENTS_MASC[thousands]\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 += ' ' + segments[remainder]\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 const segments = feminine ? SEGMENTS_FEM : SEGMENTS_MASC\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 += segments[Number(segment)]\n } else if (i === 1) {\n // Thousands: omit \"uno\" before mil\n if (segment === 1n) {\n result += THOUSAND\n } else {\n result += SEGMENTS_MASC[Number(segment)] + ' ' + 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 += SEGMENTS_MASC[Number(segment)] + ' ' + 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 += SEGMENTS_MASC[Number(segment)] + ' ' + 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 * A performance-optimized number-to-words converter using precomputed lookup tables.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999)\nconst SEGMENTS = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\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 SEGMENTS[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 = SEGMENTS[thousands] + ' ' + THOUSAND\n }\n\n if (remainder > 0) {\n result += ' ' + SEGMENTS[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 = SEGMENTS[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 converter with precomputed lookup tables.\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// 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 // 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\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n/**\n * Builds segment with linker added to last word (for use before scale words).\n */\nfunction buildSegmentWithLinker (n) {\n const segmentWord = SEGMENTS[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// Precompute segments with linker for scale word usage\nconst SEGMENTS_WITH_LINKER = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_WITH_LINKER[i] = buildSegmentWithLinker(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return SEGMENTS[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 += SEGMENTS[segment]\n } else {\n // Add linker to segment before scale word\n const segmentWord = SEGMENTS_WITH_LINKER[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 * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * French-specific rules (handled in precomputation):\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999)\nconst SEGMENTS = new Array(1000)\nconst SEGMENTS_ENDS_CENTS = new Array(1000)\nconst SEGMENTS_ENDS_VINGTS = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n const result = buildSegment(i)\n SEGMENTS[i] = result.word\n SEGMENTS_ENDS_CENTS[i] = result.endsWithCents\n SEGMENTS_ENDS_VINGTS[i] = result.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 (direct lookup)\n if (n < 1000n) {\n const word = SEGMENTS[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 let thousandsWord = SEGMENTS[thousands]\n if (SEGMENTS_ENDS_CENTS[thousands] || SEGMENTS_ENDS_VINGTS[thousands]) {\n thousandsWord = thousandsWord.slice(0, -1) // Remove trailing 's'\n }\n result = thousandsWord + (withHyphen ? '-' : ' ') + THOUSAND\n }\n\n if (remainder > 0) {\n result += (withHyphen ? '-' : ' ') + SEGMENTS[remainder]\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\n if (scaleIndex === 0) {\n // Units segment\n parts.push(SEGMENTS[segment])\n } else if (scaleIndex === 1) {\n // Thousands: \"mille\" not \"un mille\"\n if (segment === 1) {\n parts.push(THOUSAND)\n } else {\n let segWords = SEGMENTS[segment]\n // Strip 's' from cents/vingts before mille\n if (SEGMENTS_ENDS_CENTS[segment] || SEGMENTS_ENDS_VINGTS[segment]) {\n segWords = segWords.slice(0, -1)\n }\n parts.push(segWords)\n parts.push(scaleWord)\n }\n } else {\n // Million and above\n parts.push(SEGMENTS[segment])\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 converter with precomputed lookup tables.\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// Precomputed Lookup Tables\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// Precompute all 1000 segment words\nconst SEGMENTS = new Array(1000)\nconst SEGMENTS_ENDS_CENTS = new Array(1000)\nconst SEGMENTS_ENDS_VINGTS = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n const result = buildSegment(i)\n SEGMENTS[i] = result.word\n SEGMENTS_ENDS_CENTS[i] = result.endsWithCents\n SEGMENTS_ENDS_VINGTS[i] = result.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 = SEGMENTS[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 let thousandsWord = SEGMENTS[thousands]\n if (SEGMENTS_ENDS_CENTS[thousands] || SEGMENTS_ENDS_VINGTS[thousands]) {\n thousandsWord = thousandsWord.slice(0, -1)\n }\n result = thousandsWord + (withHyphen ? '-' : ' ') + THOUSAND\n }\n\n if (remainder > 0) {\n result += (withHyphen ? '-' : ' ') + SEGMENTS[remainder]\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\n if (scaleIndex === 0) {\n parts.push(SEGMENTS[segment])\n } else if (scaleIndex === 1) {\n if (segment === 1) {\n parts.push(THOUSAND)\n } else {\n let segWords = SEGMENTS[segment]\n if (SEGMENTS_ENDS_CENTS[segment] || SEGMENTS_ENDS_VINGTS[segment]) {\n segWords = segWords.slice(0, -1)\n }\n parts.push(segWords)\n parts.push(scaleWord)\n }\n } else {\n parts.push(SEGMENTS[segment])\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 converter for South Asian numbering.\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 Splitting (inlined for performance)\n// ============================================================================\n\nfunction groupByThreeThenTwos (n) {\n const numStr = n.toString()\n if (numStr.length <= 3) return [Number(numStr)]\n\n const segments = []\n segments.unshift(Number(numStr.slice(-3)))\n\n let remaining = numStr.slice(0, -3)\n while (remaining.length > 0) {\n segments.unshift(Number(remaining.slice(-2)))\n remaining = remaining.slice(0, -2)\n }\n\n return segments\n}\n\nfunction segmentToWords (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\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n const segments = groupByThreeThenTwos(n)\n const segmentCount = segments.length\n const words = []\n\n for (let i = 0; i < segmentCount; i++) {\n const segmentValue = segments[i]\n if (segmentValue === 0) continue\n\n const scaleIndex = segmentCount - i - 1\n words.push(segmentToWords(segmentValue))\n if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {\n words.push(SCALE_WORDS[scaleIndex])\n }\n }\n\n return words.join(' ').trim()\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 converter with precomputed lookup tables.\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\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return SEGMENTS[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(SEGMENTS[segment])\n } else {\n rawParts.push(SEGMENTS[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 converter with segment-based decomposition.\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 * Optimization: Precomputed segment lookup tables for both genders.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words for masculine (default) with default conjunction\nconst SCALE_SEGMENTS_MASC = new Array(1000)\nconst UNITS_SEGMENTS_MASC = new Array(1000)\nconst SCALE_SEGMENTS_FEM = new Array(1000)\nconst UNITS_SEGMENTS_FEM = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n SCALE_SEGMENTS_MASC[i] = buildScaleSegment(i, 'ו', ONES_MASC, TEENS_MASC, HUNDREDS)\n UNITS_SEGMENTS_MASC[i] = buildUnitsSegment(i, 'ו', ONES_MASC, TEENS_MASC, HUNDREDS)\n SCALE_SEGMENTS_FEM[i] = buildScaleSegment(i, 'ו', ONES_FEM, TEENS_FEM, HUNDREDS_FEM)\n UNITS_SEGMENTS_FEM[i] = buildUnitsSegment(i, 'ו', ONES_FEM, TEENS_FEM, HUNDREDS_FEM)\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 const usePrecomputed = andWord === 'ו'\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 const SCALE_SEGS = isFeminine ? SCALE_SEGMENTS_FEM : SCALE_SEGMENTS_MASC\n const UNITS_SEGS = isFeminine ? UNITS_SEGMENTS_FEM : UNITS_SEGMENTS_MASC\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return usePrecomputed ? UNITS_SEGS[Number(n)] : 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 = usePrecomputed ? UNITS_SEGS[segment] : 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 = usePrecomputed ? SCALE_SEGS[segment] : 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 = usePrecomputed ? SCALE_SEGS[segment] : 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 converter with segment-based decomposition.\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 * Optimization: Precomputed segment lookup tables for 0-999.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words with default conjunction\nconst SCALE_SEGMENTS = new Array(1000)\nconst UNITS_SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SCALE_SEGMENTS[i] = buildScaleSegment(i, 'ו')\n UNITS_SEGMENTS[i] = buildUnitsSegment(i, 'ו')\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 const usePrecomputed = andWord === 'ו'\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return usePrecomputed ? UNITS_SEGMENTS[Number(n)] : 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 = usePrecomputed ? UNITS_SEGMENTS[segment] : 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 = usePrecomputed ? SCALE_SEGMENTS[segment] : 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 = usePrecomputed ? SCALE_SEGMENTS[segment] : 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 converter for South Asian numbering.\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 */\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 Splitting (inlined for performance)\n// ============================================================================\n\nfunction groupByThreeThenTwos (n) {\n const numStr = n.toString()\n if (numStr.length <= 3) return [Number(numStr)]\n\n const segments = []\n segments.unshift(Number(numStr.slice(-3)))\n\n let remaining = numStr.slice(0, -3)\n while (remaining.length > 0) {\n segments.unshift(Number(remaining.slice(-2)))\n remaining = remaining.slice(0, -2)\n }\n\n return segments\n}\n\nfunction segmentToWords (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\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n const segments = groupByThreeThenTwos(n)\n const segmentCount = segments.length\n const words = []\n\n for (let i = 0; i < segmentCount; i++) {\n const segmentValue = segments[i]\n if (segmentValue === 0) continue\n\n const scaleIndex = segmentCount - i - 1\n words.push(segmentToWords(segmentValue))\n if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {\n words.push(SCALE_WORDS[scaleIndex])\n }\n }\n\n return words.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 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 converter using shared Slavic utilities.\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// Slavic Utilities (inlined for performance)\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 buildAllSegments (onesMasc, onesFem, teens, tens, hundreds) {\n const masc = new Array(1000)\n const fem = new Array(1000)\n\n for (let i = 0; i < 1000; i++) {\n masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)\n fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)\n }\n\n return { masc, fem }\n}\n\nfunction buildSegment (n, ones, teens, tens, hundreds) {\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[onesDigit])\n }\n\n return parts.join(' ')\n}\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// Precomputed Lookup Tables\n// ============================================================================\n\nconst { masc: SEGMENTS_MASC, fem: SEGMENTS_FEM } = buildAllSegments(ONES_MASC, ONES_FEM, TEENS, TENS, HUNDREDS)\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n return segments[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 const segmentWords = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n parts.push(segmentWords[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 segmentWords = isFeminine ? SEGMENTS_FEM : SEGMENTS_MASC\n parts.push(segmentWords[segment] + ' ' + 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 converter with precomputed lookup tables.\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// Precomputed Lookup Tables\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\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return SEGMENTS[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 = SEGMENTS[thousands] + ' ' + THOUSAND_WORD\n }\n\n if (remainder > 0) {\n result += ' ' + SEGMENTS[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(SEGMENTS[segment])\n } else if (scaleIndex === 1) {\n if (segment === 1) {\n parts.push('se' + THOUSAND_WORD)\n } else {\n parts.push(SEGMENTS[segment] + ' ' + THOUSAND_WORD)\n }\n } else {\n // Indonesian: \"satu juta\" not \"sejuta\"\n const scaleWord = SCALE_WORDS[scaleIndex - 2]\n parts.push(SEGMENTS[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 v2\n *\n * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all 1000 segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Italian-specific rules (handled in precomputation):\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 for building lookup tables\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\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// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Applies Italian phonetic vowel elision rules.\n * Only used during table construction.\n */\nfunction applyPhoneticRules (str) {\n return str\n .replace(/io/g, 'o')\n .replace(/ao/g, 'o')\n .replace(/oo/g, 'o')\n .replace(/iu/g, 'u')\n .replace(/au/g, 'u')\n}\n\n/**\n * Adds accent to final \"tre\" in a word.\n * Only used during table construction.\n */\nfunction accentuateTre (word) {\n if (word.length > 3 && word.slice(-3) === 'tre') {\n return word.slice(0, -3) + 'tré'\n }\n return word\n}\n\n/**\n * Builds the segment word for a number 0-999.\n * Only used during table construction.\n */\nfunction buildSegmentWord (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 result = HUNDREDS[hundreds]\n }\n\n // Tens and ones\n if (tens === 0 && ones === 0) {\n // Nothing more\n } else if (tens === 1) {\n // Teens: 10-19\n result += TEENS[ones]\n } else if (tens >= 2) {\n // 20-99\n result += TENS[tens]\n if (ones > 0) {\n result += ONES[ones]\n }\n } else if (ones > 0) {\n // 1-9 (tens === 0)\n result += ONES[ones]\n }\n\n // Apply phonetic rules and accent\n return accentuateTre(applyPhoneticRules(result))\n}\n\n/**\n * Builds segment word with \"un\" for scale context (millions, billions).\n * Only used during table construction.\n */\nfunction buildSegmentWordForScale (n) {\n if (n === 0) return ''\n if (n === 1) return 'un' // \"un milione\" not \"uno milione\"\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 if (hundreds > 0) {\n result = HUNDREDS[hundreds]\n }\n\n if (tens === 0 && ones === 0) {\n // Nothing more\n } else if (tens === 1) {\n result += TEENS[ones]\n } else if (tens >= 2) {\n result += TENS[tens]\n if (ones > 0) {\n result += ONES[ones]\n }\n } else if (ones > 0) {\n // 1-9 with tens === 0\n // \"un\" only for exactly 1, others normal\n result += ONES[ones]\n }\n\n return accentuateTre(applyPhoneticRules(result))\n}\n\n// Precompute all 1000 segment words (0-999)\n// SEGMENTS[n] gives the Italian word for n within a segment\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegmentWord(i)\n}\n\n// Precompute segment words for scale context (uses \"un\" for 1)\nconst SEGMENTS_SCALE = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_SCALE[i] = buildSegmentWordForScale(i)\n}\n\n// Precompute thousands words (1-999) + \"mila\" suffix\n// THOUSANDS[n] gives the word for n thousand (e.g., THOUSANDS[2] = \"duemila\")\nconst THOUSANDS = new Array(1000)\nTHOUSANDS[0] = ''\nTHOUSANDS[1] = THOUSAND_SINGULAR // \"mille\"\nfor (let i = 2; i < 1000; i++) {\n THOUSANDS[i] = applyPhoneticRules(SEGMENTS[i] + 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 (direct lookup)\n if (n < 1000n) {\n return SEGMENTS[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 THOUSANDS[thousands]\n }\n\n // Concatenate thousands + remainder\n return THOUSANDS[thousands] + SEGMENTS[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 = SEGMENTS_SCALE[segNum]\n const scaleWord = segment === 1n\n ? getScaleWordSingular(scaleIndex)\n : getScaleWordPlural(scaleIndex)\n parts.push(segmentWords + ' ' + scaleWord)\n } else if (scaleIndex === 1) {\n // Thousands: use precomputed table\n parts.push(THOUSANDS[segNum])\n } else {\n // Units (scaleIndex === 0): just the segment\n parts.push(SEGMENTS[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 * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-9999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Japanese-specific rules (handled in precomputation):\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// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Builds segment word for 0-9999 with 一 omission rules.\n * - Omit 一 before 十, 百, 千\n * Only used during table construction.\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// Precompute all 10000 segment words (0-9999)\n// SEGMENTS[n] gives the Japanese word for n within a segment\nconst SEGMENTS = new Array(10000)\nfor (let i = 0; i < 10000; i++) {\n SEGMENTS[i] = buildSegment(i)\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 (direct lookup)\n if (n < 10000n) {\n return SEGMENTS[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 = SEGMENTS[man] + SCALES[0]\n }\n\n if (remainder > 0) {\n result += SEGMENTS[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 += SEGMENTS[segment] + SCALES[i - 1]\n }\n } else {\n // Units segment (no scale word)\n result += SEGMENTS[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 * Kannada language converter - Functional Implementation\n *\n * Self-contained converter for South Asian numbering.\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 Splitting (inlined for performance)\n// ============================================================================\n\nfunction groupByThreeThenTwos (n) {\n const numStr = n.toString()\n if (numStr.length <= 3) return [Number(numStr)]\n\n const segments = []\n segments.unshift(Number(numStr.slice(-3)))\n\n let remaining = numStr.slice(0, -3)\n while (remaining.length > 0) {\n segments.unshift(Number(remaining.slice(-2)))\n remaining = remaining.slice(0, -2)\n }\n\n return segments\n}\n\nfunction segmentToWords (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\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n const segments = groupByThreeThenTwos(n)\n const segmentCount = segments.length\n const words = []\n\n for (let i = 0; i < segmentCount; i++) {\n const segmentValue = segments[i]\n if (segmentValue === 0) continue\n\n const scaleIndex = segmentCount - i - 1\n words.push(segmentToWords(segmentValue))\n if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {\n words.push(SCALE_WORDS[scaleIndex])\n }\n }\n\n return words.join(' ').trim()\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 * A performance-optimized number-to-words converter using precomputed lookup tables.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 10000 segment words (0-9999) for myriad grouping\nconst SEGMENTS = new Array(10000)\n\nfor (let i = 0; i < 10000; i++) {\n SEGMENTS[i] = buildSegment(i)\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 (direct lookup)\n if (n < 10000n) {\n return SEGMENTS[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: SEGMENTS[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: SEGMENTS[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 * A performance-optimized number-to-words converter using precomputed lookup tables.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999)\nconst SEGMENTS_MASC = new Array(1000)\nconst SEGMENTS_FEM = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_MASC[i] = buildSegment(i)\n SEGMENTS_FEM[i] = buildSegmentFeminine(i)\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 (direct lookup)\n if (n < 1000n) {\n const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n return segments[Number(n)]\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 = SEGMENTS_MASC[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 * A performance-optimized number-to-words converter using precomputed lookup tables.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999)\nconst SEGMENTS_MASC = new Array(1000)\nconst SEGMENTS_FEM = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_MASC[i] = buildSegment(i)\n SEGMENTS_FEM[i] = buildSegmentFeminine(i)\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 (direct lookup)\n if (n < 1000n) {\n const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n return segments[Number(n)]\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 = SEGMENTS_MASC[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 converter for South Asian numbering.\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 Splitting (inlined for performance)\n// ============================================================================\n\nfunction groupByThreeThenTwos (n) {\n const numStr = n.toString()\n if (numStr.length <= 3) return [Number(numStr)]\n\n const segments = []\n segments.unshift(Number(numStr.slice(-3)))\n\n let remaining = numStr.slice(0, -3)\n while (remaining.length > 0) {\n segments.unshift(Number(remaining.slice(-2)))\n remaining = remaining.slice(0, -2)\n }\n\n return segments\n}\n\nfunction segmentToWords (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\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n const segments = groupByThreeThenTwos(n)\n const segmentCount = segments.length\n const words = []\n\n for (let i = 0; i < segmentCount; i++) {\n const segmentValue = segments[i]\n if (segmentValue === 0) continue\n\n const scaleIndex = segmentCount - i - 1\n words.push(segmentToWords(segmentValue))\n if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {\n words.push(SCALE_WORDS[scaleIndex])\n }\n }\n\n return words.join(' ').trim()\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 converter with precomputed lookup tables.\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// Precomputed Lookup Tables\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\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n return SEGMENTS[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 = SEGMENTS[thousands] + ' ' + THOUSAND_WORD\n }\n\n if (remainder > 0) {\n result += ' ' + SEGMENTS[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(SEGMENTS[segment])\n } else if (scaleIndex === 1) {\n if (segment === 1) {\n parts.push('se' + THOUSAND_WORD)\n } else {\n parts.push(SEGMENTS[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(SEGMENTS[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 * A performance-optimized number-to-words converter using precomputed lookup tables.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999)\nconst SEGMENTS = new Array(1000)\nconst SEGMENTS_HAS_HUNDRED = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n const result = buildSegment(i)\n SEGMENTS[i] = result.word\n SEGMENTS_HAS_HUNDRED[i] = result.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 (direct lookup)\n if (n < 1000n) {\n return SEGMENTS[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 let result = SEGMENTS[thousands] + ' ' + THOUSAND\n\n if (remainder > 0) {\n // Comma before hundreds, \" og \" before small numbers\n if (SEGMENTS_HAS_HUNDRED[remainder]) {\n result += ', ' + SEGMENTS[remainder]\n } else {\n result += ' og ' + SEGMENTS[remainder]\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 segmentWord = SEGMENTS[segment]\n const hasHundred = SEGMENTS_HAS_HUNDRED[segment]\n\n if (scaleIndex === 0) {\n // Units segment\n parts.push({ word: segmentWord, hasHundred, type: 'units' })\n } else if (scaleIndex === 1) {\n // Thousands\n parts.push({ word: segmentWord + ' ' + THOUSAND, hasHundred: false, type: 'thousand' })\n } else {\n // Millions+\n const scaleWord = SCALES[scaleIndex - 2]\n parts.push({ word: segmentWord + ' ' + 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 * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Dutch-specific rules (handled in precomputation):\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999) - standard form\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i, false)\n}\n\n// Precompute all 1000 segment words (0-999) - with optional \"en\"\nconst SEGMENTS_WITH_AND = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_WITH_AND[i] = buildSegment(i, true)\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 const segments = includeOptionalAnd ? SEGMENTS_WITH_AND : SEGMENTS\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 (direct lookup)\n if (n < 1000n) {\n return applyAccent(segments[Number(n)])\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 = segments[high] + HUNDRED\n if (low > 0) {\n const lowWord = segments[low]\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 = segments[thousands] + SCALES[0]\n }\n\n if (remainder > 0) {\n const remainderWord = segments[remainder]\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 const segmentLookup = includeOptionalAnd ? SEGMENTS_WITH_AND : SEGMENTS\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 = segmentLookup[segment]\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 += segmentLookup[segment] + 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 += segmentLookup[segment] + ' ' + 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 converter for South Asian numbering.\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// Conversion Functions\n// ============================================================================\n\n/**\n * Converts 0-999 to Punjabi words.\n */\nfunction segmentToWords (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 * Converts a non-negative integer to Punjabi words.\n * Uses recursive approach for Indian 3-2-2 grouping pattern.\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 segmentToWords(Number(n))\n }\n\n return buildLargeNumberWords(n, 0)\n}\n\n/**\n * Recursively builds words for numbers >= 1000.\n * Indian grouping: first 3 digits, then 2-digit groups.\n *\n * @param {bigint} n - Number to convert\n * @param {number} scale - Current scale index (0=units, 1=thousands, etc.)\n * @returns {string} Punjabi words\n */\nfunction buildLargeNumberWords (n, scale) {\n if (n === 0n) return ''\n\n // Determine divisor: 1000 for first split, 100 for rest\n const divisor = scale === 0 ? 1000n : 100n\n const segment = Number(n % divisor)\n const rest = n / divisor\n\n // Build higher segments first (recursive)\n let result = ''\n if (rest > 0n) {\n result = buildLargeNumberWords(rest, scale + 1)\n }\n\n // Add current segment\n if (segment > 0) {\n if (result) result += ' '\n\n if (scale === 0) {\n // Units segment (0-999)\n result += segmentToWords(segment)\n } else {\n // Scale segments (0-99)\n result += BELOW_HUNDRED[segment] + ' ' + SCALE_WORDS[scale]\n }\n }\n\n return result\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 * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Polish-specific rules (handled in precomputation):\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999)\nconst SEGMENTS_MASC = new Array(1000)\nconst SEGMENTS_FEM = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS_MASC[i] = buildSegment(i)\n SEGMENTS_FEM[i] = buildSegmentFeminine(i)\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 (direct lookup)\n if (n < 1000n) {\n const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n return segments[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 = SEGMENTS_MASC[thousands] + ' ' + scaleWord\n }\n\n if (remainder > 0) {\n result += ' ' + SEGMENTS_MASC[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 = SEGMENTS_MASC[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 * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Portuguese-specific rules (handled in precomputation):\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// Precomputed Lookup Tables (built once at module load)\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 }\n}\n\n// Precompute all 1000 segment words (0-999)\nconst SEGMENTS = new Array(1000)\nconst SEGMENTS_STARTS_WITH_HUNDREDS = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n const result = buildSegment(i)\n SEGMENTS[i] = result.word\n // Precompute whether segment starts with hundreds (100-999)\n SEGMENTS_STARTS_WITH_HUNDREDS[i] = i >= 100\n}\n\n// ============================================================================\n// Scale Word Lookup (precomputed for common scales)\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 (direct lookup)\n if (n < 1000n) {\n return SEGMENTS[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 let result\n if (thousands === 1) {\n // \"mil\" not \"um mil\"\n result = THOUSAND\n } else {\n result = SEGMENTS[thousands] + ' ' + THOUSAND\n }\n\n if (remainder > 0) {\n // Insert \"e\" before remainder if it doesn't start with hundreds (< 100)\n if (!SEGMENTS_STARTS_WITH_HUNDREDS[remainder]) {\n result += ' e ' + SEGMENTS[remainder]\n } else {\n result += ' ' + SEGMENTS[remainder]\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 segmentWord = SEGMENTS[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 && !SEGMENTS_STARTS_WITH_HUNDREDS[segment]) {\n result += ' e'\n }\n\n if (result) result += ' '\n\n if (i === 0) {\n // Units segment\n result += segmentWord\n prevWasScale = false\n } else if (i === 1) {\n // Thousands\n if (segment === 1) {\n result += THOUSAND\n } else {\n result += segmentWord + ' ' + THOUSAND\n }\n prevWasScale = true\n } else {\n // Million and above - use precomputed 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 += segmentWord + ' ' + 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 converter using shared Slavic utilities.\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// Slavic Utilities (inlined for performance)\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 buildAllSegments (onesMasc, onesFem, teens, tens, hundreds) {\n const masc = new Array(1000)\n const fem = new Array(1000)\n\n for (let i = 0; i < 1000; i++) {\n masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)\n fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)\n }\n\n return { masc, fem }\n}\n\nfunction buildSegment (n, ones, teens, tens, hundreds) {\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[onesDigit])\n }\n\n return parts.join(' ')\n}\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// Precomputed Lookup Tables\n// ============================================================================\n\nconst { masc: SEGMENTS_MASC, fem: SEGMENTS_FEM } = buildAllSegments(ONES_MASC, ONES_FEM, TEENS, TENS, HUNDREDS)\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 const segments = feminine ? SEGMENTS_FEM : SEGMENTS_MASC\n return segments[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 = SEGMENTS_FEM[thousands]\n const scaleWord = pluralize(thousands, SCALE_FORMS[0])\n\n let result = thousandsWord + ' ' + scaleWord\n\n if (remainder > 0) {\n const segments = feminine ? SEGMENTS_FEM : SEGMENTS_MASC\n result += ' ' + segments[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 const segmentWords = feminine ? SEGMENTS_FEM : SEGMENTS_MASC\n parts.push(segmentWords[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 segmentWords = isFeminine ? SEGMENTS_FEM : SEGMENTS_MASC\n parts.push(segmentWords[segment] + ' ' + 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 converter using shared Slavic utilities.\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// Slavic Utilities (inlined for performance)\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 buildAllSegments (onesMasc, onesFem, teens, tens, hundreds) {\n const masc = new Array(1000)\n const fem = new Array(1000)\n\n for (let i = 0; i < 1000; i++) {\n masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)\n fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)\n }\n\n return { masc, fem }\n}\n\nfunction buildSegment (n, ones, teens, tens, hundreds) {\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[onesDigit])\n }\n\n return parts.join(' ')\n}\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// Precomputed Lookup Tables\n// ============================================================================\n\nconst { masc: SEGMENTS_MASC, fem: SEGMENTS_FEM } = buildAllSegments(ONES_MASC, ONES_FEM, TEENS, TENS, HUNDREDS)\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n return segments[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 const segmentWords = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n parts.push(segmentWords[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 segmentWords = isFeminine ? SEGMENTS_FEM : SEGMENTS_MASC\n parts.push(segmentWords[segment] + ' ' + 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 converter using shared Slavic utilities.\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// Slavic Utilities (inlined for performance)\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 buildAllSegments (onesMasc, onesFem, teens, tens, hundreds) {\n const masc = new Array(1000)\n const fem = new Array(1000)\n\n for (let i = 0; i < 1000; i++) {\n masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)\n fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)\n }\n\n return { masc, fem }\n}\n\nfunction buildSegment (n, ones, teens, tens, hundreds) {\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[onesDigit])\n }\n\n return parts.join(' ')\n}\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// Precomputed Lookup Tables\n// ============================================================================\n\nconst { masc: SEGMENTS_MASC, fem: SEGMENTS_FEM } = buildAllSegments(ONES_MASC, ONES_FEM, TEENS, TENS, HUNDREDS)\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n return segments[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 const segmentWords = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n parts.push(segmentWords[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 segmentWords = isFeminine ? SEGMENTS_FEM : SEGMENTS_MASC\n parts.push(segmentWords[segment] + ' ' + 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 * A performance-optimized number-to-words converter using precomputed lookup tables.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999)\nconst SEGMENTS = new Array(1000)\nconst SEGMENTS_HAS_HUNDRED = new Array(1000)\nconst SEGMENTS_LESS_THAN_100 = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n const result = buildSegment(i)\n SEGMENTS[i] = result.word\n SEGMENTS_HAS_HUNDRED[i] = result.hasHundred\n SEGMENTS_LESS_THAN_100[i] = result.lessThan100\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 (direct lookup)\n if (n < 1000n) {\n return SEGMENTS[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 \"ett\" before tusen\n let result = thousands === 1 ? SCALES[0] : SEGMENTS[thousands] + ' ' + SCALES[0]\n\n if (remainder > 0) {\n const remainderWord = SEGMENTS[remainder]\n // Insert \"och\" if remainder < 100 (doesn't have hundred)\n if (SEGMENTS_LESS_THAN_100[remainder]) {\n result += ' och ' + remainderWord\n } else {\n result += ' ' + remainderWord\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 if (scaleIndex === 0) {\n // Units segment\n parts.push({\n word: SEGMENTS[segment],\n hasHundred: SEGMENTS_HAS_HUNDRED[segment],\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 = SEGMENTS[segment]\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 converter for South Asian numbering.\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\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\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 Splitting (inlined for performance)\n// ============================================================================\n\nfunction groupByThreeThenTwos (n) {\n const numStr = n.toString()\n\n if (numStr.length <= 3) {\n return [Number(numStr)]\n }\n\n const segments = []\n const last3 = numStr.slice(-3)\n segments.unshift(Number(last3))\n\n let remaining = numStr.slice(0, -3)\n while (remaining.length > 0) {\n const segment = remaining.slice(-2)\n segments.unshift(Number(segment))\n remaining = remaining.slice(0, -2)\n }\n\n return segments\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction convertBelowThousand (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 precomputed connected form\n return HUNDREDS_CONNECTED[hundreds] + ' ' + BELOW_HUNDRED[remainder]\n}\n\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n const groups = groupByThreeThenTwos(n)\n const groupCount = groups.length\n const words = []\n\n for (let i = 0; i < groupCount; i++) {\n const groupValue = groups[i]\n if (groupValue === 0) continue\n\n const scaleIndex = groupCount - i - 1\n const groupWords = (groupValue === 1 && scaleIndex > 0) ? 'ஒரு' : convertBelowThousand(groupValue)\n words.push(groupWords)\n if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {\n words.push(SCALE_WORDS[scaleIndex])\n }\n }\n\n return words.join(' ').trim()\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 converter for South Asian numbering.\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\n// ============================================================================\n// Vocabulary (continued)\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// Conversion Functions\n// ============================================================================\n\n/**\n * Converts 0-999 to Telugu words.\n */\nfunction convertBelowThousand (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 * Converts a non-negative integer to Telugu words.\n * Uses recursive approach for Indian 3-2-2 grouping pattern.\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 convertBelowThousand(Number(n))\n }\n\n return buildLargeNumberWords(n, 0)\n}\n\n/**\n * Recursively builds words for numbers >= 1000.\n * Indian grouping: first 3 digits, then 2-digit groups.\n *\n * @param {bigint} n - Number to convert\n * @param {number} scale - Current scale index (0=units, 1=thousands, etc.)\n * @returns {string} Telugu words\n */\nfunction buildLargeNumberWords (n, scale) {\n if (n === 0n) return ''\n\n // Determine divisor: 1000 for first split, 100 for rest\n const divisor = scale === 0 ? 1000n : 100n\n const segment = Number(n % divisor)\n const rest = n / divisor\n\n // Build higher segments first (recursive)\n let result = ''\n if (rest > 0n) {\n result = buildLargeNumberWords(rest, scale + 1)\n }\n\n // Add current segment\n if (segment > 0) {\n if (result) result += ' '\n\n if (scale === 0) {\n // Units segment (0-999)\n result += convertBelowThousand(segment)\n } else {\n // Scale segments (0-99)\n const groupWords = (segment === 1) ? 'ఒక' : BELOW_HUNDRED[segment]\n result += groupWords + ' ' + SCALE_WORDS[scale]\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 - 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 * A performance-optimized number-to-words converter using precomputed lookup tables.\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// Precomputed Lookup Tables (built once at module load)\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// Precompute all 1000 segment words (0-999) with space separator\nconst SEGMENTS = new Array(1000)\nconst SEGMENTS_NO_SPACE = new Array(1000)\n\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i, ' ')\n SEGMENTS_NO_SPACE[i] = buildSegment(i, '')\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 const segments = options.dropSpaces ? SEGMENTS_NO_SPACE : SEGMENTS\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return segments[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 \"bir\" before bin (thousand)\n let result\n if (thousands === 1) {\n result = THOUSAND\n } else {\n result = segments[thousands] + sep + THOUSAND\n }\n\n if (remainder > 0) {\n result += sep + segments[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 *\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 const segmentsArr = options.dropSpaces ? SEGMENTS_NO_SPACE : SEGMENTS\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 = segmentsArr[segment]\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 converter using shared Slavic utilities.\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// Slavic Utilities (inlined for performance)\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 buildAllSegments (onesMasc, onesFem, teens, tens, hundreds) {\n const masc = new Array(1000)\n const fem = new Array(1000)\n\n for (let i = 0; i < 1000; i++) {\n masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)\n fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)\n }\n\n return { masc, fem }\n}\n\nfunction buildSegment (n, ones, teens, tens, hundreds) {\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[onesDigit])\n }\n\n return parts.join(' ')\n}\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// Precomputed Lookup Tables\n// ============================================================================\n\nconst { masc: SEGMENTS_MASC, fem: SEGMENTS_FEM } = buildAllSegments(ONES_MASC, ONES_FEM, TEENS, TENS, HUNDREDS)\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\nfunction integerToWords (n, options = {}) {\n if (n === 0n) return ZERO\n\n if (n < 1000n) {\n const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n return segments[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 const segmentWords = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC\n parts.push(segmentWords[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 segmentWords = isFeminine ? SEGMENTS_FEM : SEGMENTS_MASC\n parts.push(segmentWords[segment] + ' ' + 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 converter for South Asian numbering.\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// Conversion Functions\n// ============================================================================\n\n/**\n * Converts 0-999 to Urdu words.\n */\nfunction segmentToWords (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 * Converts a non-negative integer to Urdu words.\n * Uses recursive approach for Indian 3-2-2 grouping pattern.\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 segmentToWords(Number(n))\n }\n\n return buildLargeNumberWords(n, 0)\n}\n\n/**\n * Recursively builds words for numbers >= 1000.\n * Indian grouping: first 3 digits, then 2-digit groups.\n *\n * @param {bigint} n - Number to convert\n * @param {number} scale - Current scale index (0=units, 1=thousands, etc.)\n * @returns {string} Urdu words\n */\nfunction buildLargeNumberWords (n, scale) {\n if (n === 0n) return ''\n\n // Determine divisor: 1000 for first split, 100 for rest\n const divisor = scale === 0 ? 1000n : 100n\n const segment = Number(n % divisor)\n const rest = n / divisor\n\n // Build higher segments first (recursive)\n let result = ''\n if (rest > 0n) {\n result = buildLargeNumberWords(rest, scale + 1)\n }\n\n // Add current segment\n if (segment > 0) {\n if (result) result += ' '\n\n if (scale === 0) {\n // Units segment (0-999)\n result += segmentToWords(segment)\n } else {\n // Scale segments (0-99)\n result += BELOW_HUNDRED[segment] + ' ' + SCALE_WORDS[scale]\n }\n }\n\n return result\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 v2\n *\n * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Vietnamese-specific rules (handled in precomputation):\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// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Builds word for 0-99 with special forms (mốt, lăm).\n * Only used during table construction.\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 * Only used during table construction.\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 += BELOW_100[remainder]\n }\n }\n\n return result\n}\n\n// Precompute all 100 below-hundred words (0-99)\n// BELOW_100[n] gives the Vietnamese word for n\nconst BELOW_100 = new Array(100)\nfor (let i = 0; i < 100; i++) {\n BELOW_100[i] = buildBelowHundred(i)\n}\n\n// Precompute all 1000 segment words (0-999)\n// SEGMENTS[n] gives the Vietnamese word for n within a segment\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// Precompute \"lẻ\" prefixed versions for small remainders after scale words\n// LE_SEGMENTS[n] gives \"lẻ X\" for n in range 1-99\nconst LE_SEGMENTS = new Array(100)\nLE_SEGMENTS[0] = ''\nfor (let i = 1; i < 10; i++) {\n // Use \"năm\" not \"lăm\" after lẻ\n LE_SEGMENTS[i] = LE + ' ' + (i === 5 ? 'năm' : ONES[i])\n}\nfor (let i = 10; i < 100; i++) {\n LE_SEGMENTS[i] = LE + ' ' + BELOW_100[i]\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 (direct lookup)\n if (n < 100n) {\n return BELOW_100[Number(n)]\n }\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return SEGMENTS[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 = SEGMENTS[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 + ' ' + LE_SEGMENTS[remainder]\n }\n\n return thousandsWords + ' ' + SEGMENTS[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 = SEGMENTS[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 + ' ' + LE_SEGMENTS[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 * 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","SEGMENTS","Array","i","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","numStr","len","segments","remainderLen","pos","scaleIndex","segment","scaleWord","buildLargeNumberWords","BELOW_HUNDRED","hundreds","remainder","remaining","groupByThreeThenTwos","segmentCount","words","segmentValue","PLURAL_FORMS","tens","buildSegmentWithHundreds","SEGMENTS_WITH_HUNDREDS","pluralize","forms","lastDigit","lastTwoDigits","thousands","segmentValues","ONES_VIGESIMAL","THOUSAND","SCALES","tensOnes","segmentWord","word","numWord","part","nextPart","endsWith","joinDanishParts","EIN","SCALES_PLURAL","HUNDRED","buildSegmentForThousand","SEGMENTS_THOUSAND","isScale","scaleLevel","segWords","prevPart","joinGermanParts","hasHundred","SEGMENTS_HAS_HUNDRED","remainderWord","firstNonZeroIdx","prevWasScale","TWENTIES_MASC","TWENTIES_FEM","HUNDREDS_MASC","HUNDREDS_FEM","feminine","SEGMENTS_MASC","SEGMENTS_FEM","getScaleWord","arrayIndex","baseWord","pluralWord","thousandsWord","thousandMultiplier","VOWELS","addLinker","hundredPrefix","buildSegmentWithLinker","lastSpaceIdx","lastIndexOf","prefix","lastWord","SEGMENTS_WITH_LINKER","SCALES_ARD","endsWithCents","endsWithVingts","t","o","SEGMENTS_ENDS_CENTS","SEGMENTS_ENDS_VINGTS","ardWord","withHyphen","replace","isSingleDigit","TEENS_MASC","THOUSANDS_MASC","TEENS_FEM","THOUSANDS_FEM","SCALE","buildScaleSegment","andWord","HUNDREDS_ARR","teenWord","buildUnitsSegment","SCALE_SEGMENTS_MASC","UNITS_SEGMENTS_MASC","SCALE_SEGMENTS_FEM","UNITS_SEGMENTS_FEM","THOUSANDS_SPECIAL","SCALE_SEGMENTS","UNITS_SEGMENTS","num","teens","SCALE_FORMS","masc","fem","onesMasc","onesFem","buildAllSegments","WORDS","Map","wordForScale","get","integerToWordsInner","zeroWord","has","units","tensToCardinal","hundredsToCardinal","postfix","thousandsToCardinal","scale","rest","bigNumberToCardinal","HUNDRED_WORD","THOUSAND_WORD","SCALE_PREFIXES","applyPhoneticRules","accentuateTre","buildSegmentWord","buildSegmentWordForScale","SEGMENTS_SCALE","THOUSANDS","getScaleWordSingular","prefixIndex","getScaleWordPlural","maxScale","testValue","divisor","segNum","segmentWords","lastPart","joinPartsWithConnector","joinKoreanParts","HUNDRED_SINGULAR","HUNDRED_PLURAL","buildSegmentFeminine","HUNDRED_GENITIVE","joinNorwegianParts","withAnd","onesWord","connector","SEGMENTS_WITH_AND","accentOne","includeOptionalAnd","noHundredPairing","applyAccent","high","low","lowWord","segmentLookup","isExactHundred","SEGMENTS_STARTS_WITH_HUNDREDS","SCALE_WORDS_SINGULAR","SCALE_WORDS_PLURAL","TWENTIES","SCALE_META","singular","plural","article","needsDe","spellUnder100","masculineTeens","u","spellUnder1000","h","r","hundredWord","buildScalePhrase","meta","lessThan100","tensOnesWord","SEGMENTS_LESS_THAN_100","joinSwedishParts","wordsUnder100","wordsUnder1000","extractSegments","val","unit","HUNDREDS_CONNECTED","convertBelowThousand","convertBelowMillion","hundredThousands","tenThousands","separator","SEGMENTS_NO_SPACE","sep","dropSpaces","segmentsArr","buildBelowHundred","tensWord","BELOW_100","LE_SEGMENTS","LE","thousandsWords","partsLen","lastSegment","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","usePrecomputed","SCALE_SEGS","UNITS_SEGS","man","opts","last3","groupCount","groupValue","groupWords","million","chunk","splitMillionGroups","formal","yiRemainder","decimalString","decimalDigitsToWords","yiWords"],"mappings":";8OAaO,SAASA,EAAmBC,GACjC,MAAMC,SAAcD,EAGpB,GAAa,WAATC,EACF,OAAOD,EAAQ,GACX,CAAEE,YAAY,EAAMC,aAAcH,GAClC,CAAEE,YAAY,EAAOC,YAAaH,GAIxC,GAAa,WAATC,EAAmB,CACrB,IAAKG,OAAOC,SAASL,GACnB,MAAM,IAAIM,MAAM,8DAElB,OAAIF,OAAOG,cAAcP,GAChBA,EAAQ,EACX,CAAEE,YAAY,EAAMC,YAAaK,QAAQR,IACzC,CAAEE,YAAY,EAAOC,YAAaK,OAAOR,IAExCS,EAgBX,SAAyBT,GACvB,MAAMU,EAAMV,EAAMW,WAClB,OAAQD,EAAIE,SAAS,MAAQF,EAAIE,SAAS,KACtCC,EAAyBH,GACzBA,CACN,CArB8BI,CAAed,GAC3C,CAGA,GAAa,WAATC,EACF,OAAOQ,EAqBX,SAA0BT,GACxB,MAAMe,EAAUf,EAAMgB,OACtB,GAAuB,IAAnBD,EAAQE,QAAgBb,OAAOc,MAAMd,OAAOW,IAC9C,MAAM,IAAIT,MAAM,2BAA2BN,MAE7C,OAAQe,EAAQH,SAAS,MAAQG,EAAQH,SAAS,KAC9CC,EAAyBE,GACzBA,CACN,CA7B8BI,CAAgBnB,IAG5C,MAAM,IAAIoB,UACR,oEAAoEnB,IAExE,CA4BA,SAASQ,EAAoBC,GAC3B,MAAMR,EAAwB,MAAXQ,EAAI,GACnBR,IAAYQ,EAAMA,EAAIW,MAAM,IAEhC,MAAMC,EAAWZ,EAAIa,QAAQ,KAC7B,IAAiB,IAAbD,EACF,MAAO,CAAEpB,aAAYC,YAAaK,OAAOE,IAG3C,MAAMc,EAAad,EAAIW,MAAM,EAAGC,IAAa,IACvCG,EAAcf,EAAIW,MAAMC,EAAW,GACzC,MAAO,CAAEpB,aAAYC,YAAaK,OAAOgB,GAAaC,cACxD,CAKA,SAASZ,EAA0BH,GACjC,MAAOgB,EAAUC,GAAUjB,EAAIkB,cAAcC,MAAM,KAC7CC,EAAMC,SAASJ,EAAQ,IAEvBL,EAAWI,EAASH,QAAQ,KAC5BS,OAASV,EACXI,EACAA,EAASL,MAAM,EAAGC,GAAYI,EAASL,MAAMC,EAAW,GAEtDW,IAD6B,IAAbX,EAAkBI,EAAST,OAASK,GACnBQ,EAEvC,OAAIG,GAAkBD,EAAOf,OACpBe,EAAS,IAAIE,OAAOD,EAAiBD,EAAOf,QAEjDgB,GAAkB,EACb,KAAO,IAAIC,QAAQD,GAAkBD,EAEvCA,EAAOX,MAAM,EAAGY,GAAkB,IAAMD,EAAOX,MAAMY,EAC9D,CCvFA,MAAME,EAAO,CAAC,GAAI,MAAO,MAAO,MAAO,MAAO,OAAQ,OAAQ,MAAO,OAAQ,OACvEC,EAAQ,CAAC,MAAO,UAAW,UAAW,UAAW,UAAW,WAAY,WAAY,UAAW,WAAY,WAC3GC,EAAO,CAAC,GAAI,GAAI,KAAM,MAAO,MAAO,MAAO,MAAO,KAAM,OAAQ,OAUhEC,EAAc,CAAC,GAPJ,IAOkB,OAAQ,QAM3C,SAASC,EAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAkBd,OAfID,EAAgB,GAClBC,EAAMC,KAAKZ,EAAKU,GAALV,OAGK,IAAdO,EACFI,EAAMC,KAAKX,EAAMK,KAEbC,EAAY,GACdI,EAAMC,KAAKV,EAAKK,IAEdD,EAAO,GACTK,EAAMC,KAAKZ,EAAKM,KAIbK,EAAME,KAAK,IACpB,CAEA,MAAMC,EAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,EAASE,GAAKZ,EAAaY,GChD7B,MAAMhB,EAAO,CAAC,GAAI,MAAO,QAAS,OAAQ,OAAQ,QAAS,UAAW,QAAS,SAAU,UACnFC,EAAQ,CAAC,OAAQ,WAAY,aAAc,YAAa,YAAa,aAAc,eAAgB,aAAc,cAAe,eAChIC,EAAO,CAAC,GAAI,GAAI,OAAQ,SAAU,OAAQ,QAAS,QAAS,OAAQ,UAAW,UAK/Ee,EAAO,OAKPd,EAAc,CAAC,GAPJ,MAOkB,UAAW,YAM9C,SAASC,EAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAkBd,OAfID,EAAgB,GAClBC,EAAMC,KAAKZ,EAAKU,GAALV,SAGK,IAAdO,EACFI,EAAMC,KAAKX,EAAMK,KAEbC,EAAY,GACdI,EAAMC,KAAKV,EAAKK,IAEdD,EAAO,GACTK,EAAMC,KAAKZ,EAAKM,KAIbK,EAAME,KAAK,IACpB,CAEA,MAAMC,EAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,EAASE,GAAKZ,EAAaY,GC3DtB,SAASE,EAAiBC,GAC/B,QAAgBC,IAAZD,EAAuB,MAAO,CAAA,EAClC,GCCK,SAAwBtD,GAC7B,GAAc,OAAVA,GAAmC,iBAAVA,EAAoB,OAAO,EACxD,MAAMwD,EAAQC,OAAOC,eAAe1D,GACpC,OAAiB,OAAVwD,GAAkBA,IAAUC,OAAOE,SAC5C,CDLMC,CAAcN,GAAU,OAAOA,EACnC,MAAM,IAAIlC,UACR,mEAAmEkC,EAEvE,CEIA,MAAMjB,EAAO,CAAC,QAAS,SAAU,SAAU,QAAS,OAAQ,QAAS,SAAU,SACzEwB,EAAW,CAAC,GAAI,OAAQ,QAAS,WAAY,WAAY,UAAW,SAAU,UAAW,WAAY,WAGrGvB,EAAc,CAAC,OAAQ,MAAO,QAAS,QAAS,UAAW,aAAc,YAAa,aACtFwB,EAAiB,CAAC,GAAI,QAAS,UAAW,UAAW,YAAa,eAAgB,cAAe,eACjGC,EAAe,CAAC,GAAI,OAAQ,SAAU,UAAW,YAAa,eAAgB,cAAe,eAG7FC,EAAO,CAAC,QAAS,QAAS,UAAW,UAAW,YAAa,eAAgB,cAAe,eAC5FC,EAAgB,CAAC,OAAQ,OAAQ,SAAU,SAAU,WAAY,cAAe,aAAc,cAG9FC,EAAY,CAAC,OAAQ,QAAS,QAAS,QAAS,OAAQ,MAAO,OAAQ,SAAU,OAAQ,OAAQ,UAAW,WAAY,YAAa,YAAa,WAAY,UAAW,WAAY,aAAc,YACnMC,EAAW,CAAC,QAAS,SAAU,OAAQ,OAAQ,MAAO,KAAM,MAAO,OAAQ,MAAO,MAAO,YAAa,aAAc,YAAa,YAAa,WAAY,UAAW,WAAY,aAAc,YAgBrM,SAASC,EAAgBC,EAAaC,EAAYC,EAAY9B,GAC5D,MAAM+B,EAAYH,EAAc,IAC1BxB,EAAgBF,KAAK8B,MAAMJ,EAAc,KAC/C,IAAIK,EAAS,GAGb,GAAI7B,EAAgB,EAClB,GAAkB,IAAd2B,GAAqC,IAAlB3B,EACrB6B,EAASV,EAAK,OACT,CACL,MAAMW,EAAed,EAAShB,GAC1B8B,IACFD,EAASC,EACS,IAAdH,IACFE,GAAU,MAGhB,CAIF,GAAIF,EAAY,EACd,GAAIA,EAAY,GACd,GAAkB,IAAdA,GAAqC,IAAlB3B,GAAuByB,EAAa,EAAG,CAC5D,MAAMM,EAAWxE,OAAOmE,GAClBM,EAAMlC,KAAK8B,MAAM9B,KAAKmC,MAAMF,IAC9BC,EAAM,GAAM,GAAKN,IAAe/D,OAAO,EAAImC,KAAKkC,IAAI,GAAIA,IAC1DH,GAA2B,IAAhBL,EAAoBL,EAAKM,GAAcL,EAAcK,GAEhEI,GAAUV,EAAKM,EAEnB,MACEI,GADuB,IAAdF,GAAmBF,EAAa,EAC/BhC,EAAYgC,GAEZ7B,EAAK+B,EAAY,OAExB,CACL,MAAMO,EAAYP,EAAY,GACxBQ,EAAYrC,KAAK8B,MAAMD,EAAY,IAAM,EAE3CO,EAAY,IACdL,GAAUjC,EAAKsC,EAAY,GAAjBtC,MAEZiC,GAAUrC,EAAK2C,EACjB,CAGF,OAAON,CACT,CAEA,SAASO,EAAgBzC,EAAGc,GAC1B,GAAU,KAANd,EAAU,MAjEH,MAmEX,MACMC,EAAkB,cADTa,EAAQ4B,QAAU,aACIf,EAAWD,EAEhD,IAAIiB,EAAO3C,EACP4C,EAAQ,EACZ,MAAMC,EAAS,GAEf,KAAOF,EAAO,IAAI,CAChB,MAAMG,EAAkBlF,OAAO+E,EAAO,OAGtC,GAFAA,GAAc,MAEVG,EAAkB,EAAG,CACvB,MAAMC,EAAmBnB,EAAekB,EAAiBF,EAAO5C,EAAGC,GAEnE,GAAI8C,EAAkB,CACpB,IAAIC,EAAYD,EAGZH,EAAQ,GAAKE,EAAkB,IAG/BE,GADgB,GADAF,EAAkB,IAErB,IAAMhD,EAAY8C,GACtBE,GAAmB,GAAKA,GAAmB,GACvC,IAAMvB,EAAaqB,GAEnB,KAAOC,EAAOpE,OAAS,EAAI6C,EAAesB,GAAS9C,EAAY8C,KAIhFC,EAAOI,QAAQD,EACjB,CACF,CAEAJ,GACF,CAIA,GAAsB,IAAlBC,EAAOpE,OAAc,OAAOoE,EAAO,GAGvC,IAAIX,EAASW,EAAO,GACpB,IAAK,IAAIlC,EAAI,EAAGA,EAAIkC,EAAOpE,OAAQkC,IACjCuB,GAAU,KAAYW,EAAOlC,GAE/B,OAAOuB,CACT,CCnIA,MAAMvC,EAAO,CAAC,GAAI,MAAO,MAAO,KAAM,OAAQ,MAAO,OAAQ,QAAS,SAAU,UAC1EC,EAAQ,CAAC,KAAM,SAAU,SAAU,QAAS,UAAW,SAAU,UAAW,WAAY,YAAa,aACrGC,EAAO,CAAC,GAAI,GAAI,SAAU,OAAQ,OAAQ,OAAQ,SAAU,SAAU,SAAU,UAKhFe,EAAO,QAKPd,EAAc,CAAC,GAPJ,MAOkB,SAAU,SAAU,UAAW,YAAa,aAM/E,SAASC,EAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAqBd,OAnBID,EAAgB,GAEhBC,EAAMC,KADc,IAAlBF,EAxBQ,MA2BCV,EAAKU,GAALV,QAIG,IAAdO,EACFI,EAAMC,KAAKX,EAAMK,KAEbC,EAAY,GACdI,EAAMC,KAAKV,EAAKK,IAEdD,EAAO,GACTK,EAAMC,KAAKZ,EAAKM,KAIbK,EAAME,KAAK,IACpB,CAEA,MAAMC,EAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,EAASE,GAAKZ,EAAaY,GAO7B,SAAS8B,EAAgBzC,GACvB,OAAU,KAANA,EAAiBY,EAEjBZ,EAAI,MACCS,EAAS7C,OAAOoC,IAM3B,SAAgCA,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMC,EAAY3D,EAAYyD,IAAe,GAG3CjD,EAAMC,KADW,IAAfgD,EACS9C,EAAS+C,GACI,IAAfD,GAAgC,IAAZC,EAElBC,EAEAhD,EAAS+C,GAAW,IAAMC,EAEzC,CAEAF,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CA5CSkD,CAAsB1D,EAC/B,CCjEA,MAAMY,EAAO,QAKP+C,EAAgB,CACpB,QAAS,KAAM,MAAO,MAAO,MAAO,OAAQ,MAAO,MAAO,KAAM,MAChE,KAAM,QAAS,OAAQ,OAAQ,QAAS,QAAS,MAAO,QAAS,QAAS,OAC1E,MAAO,OAAQ,OAAQ,OAAQ,SAAU,QAAS,UAAW,QAAS,OAAQ,UAC9E,QAAS,UAAW,SAAU,UAAW,UAAW,YAAa,SAAU,YAAa,UAAW,WACnG,SAAU,WAAY,aAAc,YAAa,aAAc,cAAe,WAAY,YAAa,WAAY,WACnH,SAAU,SAAU,UAAW,YAAa,WAAY,WAAY,YAAa,UAAW,SAAU,QACtG,MAAO,UAAW,UAAW,UAAW,UAAW,YAAa,UAAW,WAAY,UAAW,UAClG,QAAS,UAAW,WAAY,WAAY,YAAa,WAAY,YAAa,WAAY,UAAW,QACzG,MAAO,QAAS,SAAU,SAAU,SAAU,SAAU,UAAW,SAAU,QAAS,UACtF,QAAS,WAAY,YAAa,YAAa,YAAa,YAAa,aAAc,YAAa,WAAY,aAI5G7D,EAAc,CAAC,GAAI,QAAS,MAAO,OAAQ,MAAO,MAAO,MAAO,OAAQ,QAsB9E,SAAS8B,EAAgB5B,GACvB,GAAU,IAANA,EAAS,MAAO,GACpB,GAAIA,EAAI,IAAK,OAAO2D,EAAc3D,GAElC,MAAM4D,EAAWzD,KAAK8B,MAAMjC,EAAI,KAC1B6D,EAAY7D,EAAI,IAEtB,OAAkB,IAAd6D,EACKF,EAAcC,GAAdD,MAEFA,EAAcC,GAAdD,OAAgDA,EAAcE,EACvE,CAMA,SAASpB,EAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,EAErB,MAAMwC,EApCR,SAA+BpD,GAC7B,MAAMkD,EAASlD,EAAE7B,WACjB,GAAI+E,EAAOzE,QAAU,EAAG,MAAO,CAACb,OAAOsF,IAEvC,MAAME,EAAW,GACjBA,EAASH,QAAQrF,OAAOsF,EAAOrE,OAAM,KAErC,IAAIiF,EAAYZ,EAAOrE,MAAM,GAAG,GAChC,KAAOiF,EAAUrF,OAAS,GACxB2E,EAASH,QAAQrF,OAAOkG,EAAUjF,OAAM,KACxCiF,EAAYA,EAAUjF,MAAM,GAAG,GAGjC,OAAOuE,CACT,CAsBmBW,CAAqB/D,GAChCgE,EAAeZ,EAAS3E,OACxBwF,EAAQ,GAEd,IAAK,IAAItD,EAAI,EAAGA,EAAIqD,EAAcrD,IAAK,CACrC,MAAMuD,EAAed,EAASzC,GAC9B,GAAqB,IAAjBuD,EAAoB,SAExB,MAAMX,EAAaS,EAAerD,EAAI,EACtCsD,EAAM1D,KAAKqB,EAAesC,IACtBX,EAAa,GAAKzD,EAAYyD,IAChCU,EAAM1D,KAAKT,EAAYyD,GAE3B,CAEA,OAAOU,EAAMzD,KAAK,KAAKhC,MACzB,CCvEA,MAAMmB,EAAO,CAAC,GAAI,QAAS,MAAO,MAAO,QAAS,MAAO,OAAQ,OAAQ,MAAO,SAG1EC,EAAQ,CAAC,QAAS,WAAY,UAAW,UAAW,UAAW,UAAW,WAAY,WAAY,UAAW,cAG7GC,EAAO,CAAC,GAAI,GAAI,SAAU,SAAU,WAAY,UAAW,UAAW,YAAa,WAAY,aAG/FwB,EAAW,CAAC,GAAI,MAAO,UAAW,UAAW,YAAa,UAAW,WAAY,WAAY,UAAW,aAGxG8C,EAAe,CACnB,EAAG,CAAC,QAAS,SAAU,SACvB,EAAG,CAAC,SAAU,UAAW,WACzB,EAAG,CAAC,WAAY,WAAY,WAC5B,EAAG,CAAC,SAAU,UAAW,WACzB,EAAG,CAAC,WAAY,WAAY,WAC5B,EAAG,CAAC,UAAW,WAAY,YAC3B,EAAG,CAAC,YAAa,YAAa,YAC9B,EAAG,CAAC,aAAc,cAAe,eACjC,EAAG,CAAC,eAAgB,eAAgB,gBAGhCvD,EAAO,OAWb,SAASb,EAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAoBd,OAjBIsD,EAAW,GACbtD,EAAMC,KAAKc,EAASuC,IAIT,IAATQ,EAEF9D,EAAMC,KAAKX,EAAMK,IACRmE,GAAQ,GACjB9D,EAAMC,KAAKV,EAAKuE,IACZnE,EAAO,GACTK,EAAMC,KAAKZ,EAAKM,KAETA,EAAO,GAChBK,EAAMC,KAAKZ,EAAKM,IAGXK,EAAME,KAAK,IACpB,CAMA,SAAS6D,EAA0BrE,GACjC,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAmBd,OAhBIsD,EAAW,GACbtD,EAAMC,KAAKc,EAASuC,IAIT,IAATQ,EACF9D,EAAMC,KAAKX,EAAMK,IACRmE,GAAQ,GACjB9D,EAAMC,KAAKV,EAAKuE,IACZnE,EAAO,GACTK,EAAMC,KAAKZ,EAAKM,KAETA,EAAO,GAChBK,EAAMC,KAAKZ,EAAKM,IAGXK,EAAME,KAAK,IACpB,CAGA,MAAMC,EAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,EAASE,GAAKZ,EAAaY,GAI7B,MAAM2D,EAAyB,IAAI5D,MAAM,KACzC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxB2D,EAAuB3D,GAAK0D,EAAyB1D,GAevD,SAAS4D,EAAWvE,EAAGwE,GACrB,GAAU,KAANxE,EAAU,OAAOwE,EAAM,GAE3B,MAAMC,EAAYzE,EAAI,IAChB0E,EAAgB1E,EAAI,KAG1B,OAAIyE,GAAa,IAAMA,GAAa,KAAOC,EAAgB,KAAOA,EAAgB,KACzEF,EAAM,GAGRA,EAAM,EACf,CA0BA,SAAS/B,EAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,EAGrB,GAAIZ,EAAI,MACN,OAAOS,EAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY3E,EAAI,MAChB6D,EAAYjG,OAAOoC,EAAI,OAEvByD,EAAYc,EAAUI,EAAWR,EAAa,IAEpD,IAAIjC,EAaJ,OAVEA,EAFgB,KAAdyC,EAEOlB,EAEAhD,EAAS7C,OAAO+G,IAAc,IAAMlB,EAG3CI,EAAY,IAEd3B,GAAU,IAAMoC,EAAuBT,IAGlC3B,CACT,CAGA,OAUF,SAAgClC,GAG9B,MAAM4E,EAAgB,GACtB,IAAIjC,EAAO3C,EACX,KAAO2C,EAAO,IACZiC,EAAcrE,KAAKoC,EAAO,OAC1BA,GAAc,MAIhB,IAAIT,EAAS,GAEb,IAAK,IAAIvB,EAAIiE,EAAcnG,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAClD,MAAM6C,EAAUoB,EAAcjE,GAC9B,GAAgB,KAAZ6C,EAIJ,GAFItB,IAAQA,GAAU,KAEZ,IAANvB,EAEFuB,GAAUoC,EAAuB1G,OAAO4F,QACnC,CAEL,MAAMgB,EAAQL,EAAaxD,GAC3B,GAAI6D,EAAO,CACT,MAAMf,EAAYc,EAAUf,EAASgB,GAInCtC,GAFc,KAAZsB,EAEQC,EAGAhD,EAAS7C,OAAO4F,IAAY,IAAMC,CAEhD,MAEEvB,GAAUzB,EAAS7C,OAAO4F,GAE9B,CACF,CAEA,OAAOtB,CACT,CArDSwB,CAAsB1D,EAC/B,CCtMA,MAAML,GAAO,CAAC,GAAI,KAAM,KAAM,MAAO,OAAQ,MAAO,OAAQ,MAAO,OAAQ,MAErEkF,GAAiB,CAAC,GAAI,KAAM,KAAM,MAAO,OAAQ,MAAO,OAAQ,MAAO,OAAQ,MAE/EjF,GAAQ,CAAC,KAAM,SAAU,OAAQ,UAAW,UAAW,SAAU,UAAW,SAAU,QAAS,UAG/FC,GAAO,CAAC,GAAI,GAAI,OAAQ,UAAW,QAAS,YAAa,QAAS,aAAc,OAAQ,YAGxFiF,GAAW,SAEXlE,GAAO,MAKPmE,GAAS,CAAC,YAAa,YAAa,YAAa,YAAa,aAAc,aAAc,gBAAiB,iBASjH,SAAShF,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAGVsD,EAAW,GACbtD,EAAMC,KAAKZ,GAAKiE,GA5BJ,YAgCd,MAAMoB,EAAWhF,EAAI,IAmBrB,OAjBiB,IAAbgF,GAIF1E,EAAMC,KAFGyE,EAAW,GAETrF,GAAKM,GACP+E,EAAW,GAETpF,GAAMK,GACC,IAATA,EAEEJ,GAAKuE,GAGLS,GAAe5E,GAAQ,KAAOJ,GAAKuE,IAI3B,IAAjB9D,EAAM7B,OACD6B,EAAM,GAAK,OAASA,EAAM,GAE5BA,EAAM,IAAM,EACrB,CAGA,MAAMG,GAAW,IAAIC,MAAM,KAE3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,GAa7B,SAAS8B,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAG7B,IAAIkC,EAASzB,GAASkE,GAAaG,GAOnC,OALIjB,EAAY,IAEd3B,GAAU,QAAUzB,GAASoD,IAGxB3B,CACT,CAGA,OASF,SAAgClC,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAepB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMyB,EAAcxE,GAAS+C,GAE7B,GAAmB,IAAfD,EAEFjD,EAAMC,KAAK,CAAE2E,KAAMD,EAAaxH,KAAM,eACjC,GAAmB,IAAf8F,EAETjD,EAAMC,KAAK,CAAE2E,KAAMD,EAAcH,GAAUrH,KAAM,iBAC5C,CAGL,IAAI0H,EAAUF,EAEE,IAAZzB,IACF2B,EAAU,MAEZ7E,EAAMC,KAAK,CAAE2E,KAAMC,EAAU,IANXJ,GAAOxB,EAAa,GAMQ9F,KAAM,WACtD,CACF,CAEA8F,GACF,CAGA,OAWF,SAA0BjD,GACxB,GAAqB,IAAjBA,EAAM7B,OAAc,OAAOmC,GAC/B,GAAqB,IAAjBN,EAAM7B,OAAc,OAAO6B,EAAM,GAAG4E,KAExC,MAAMhD,EAAS,GAEf,IAAK,IAAIvB,EAAI,EAAGA,EAAIL,EAAM7B,OAAQkC,IAAK,CACrC,MAAMyE,EAAO9E,EAAMK,GACb0E,EAAW/E,EAAMK,EAAI,GAET,aAAdyE,EAAK3H,MAAuB4H,GAA8B,UAAlBA,EAAS5H,MAEnDyE,EAAO3B,KAAK6E,EAAKF,KAAO,QAAUG,EAASH,MAC3CvE,KACuB,YAAdyE,EAAK3H,MACVyE,EAAOzD,OAAS,GAClByD,EAAO3B,KAAK,KAEd2B,EAAO3B,KAAK6E,EAAKF,MACbG,GACFnD,EAAO3B,KAAK,OAGV2B,EAAOzD,OAAS,IAAMyD,EAAOA,EAAOzD,OAAS,GAAG6G,SAAS,MAC3DpD,EAAO3B,KAAK,KAEd2B,EAAO3B,KAAK6E,EAAKF,MAErB,CAEA,OAAOhD,EAAO1B,KAAK,GACrB,CA1CS+E,CAAgBjF,EACzB,CA9DSoD,CAAsB1D,EAC/B,CCxGA,MAAML,GAAO,CAAC,GAAI,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QAAS,SAAU,OAAQ,QAG/E6F,GAAM,MAGN5F,GAAQ,CAAC,OAAQ,MAAO,QAAS,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,YAGzGC,GAAO,CAAC,GAAI,GAAI,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,WAG7FkF,GAAS,CAAC,UAAW,UAAW,YAAa,UAAW,YAAa,WAAY,aAAc,cAAe,iBAG9GU,GAAgB,CAAC,UAAW,YAAa,aAAc,YAAa,aAAc,aAAc,cAAe,gBAAiB,kBAEhIC,GAAU,UACV9E,GAAO,OAab,SAASb,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAEhC,IAAIkC,EAAS,GAwBb,OArBI0B,EAAW,IACb1B,IAAwB,IAAb0B,EAAiB4B,GAAM7F,GAAKiE,IAAa8B,IAIzC,IAATtB,EAEFlC,GAAUtC,GAAMK,GACPmE,GAAQ,GAAKnE,EAAO,EAG7BiC,IAAoB,IAATjC,EAAauF,GAAM7F,GAAKM,IAAS,MAAQJ,GAAKuE,GAChDA,GAAQ,EAEjBlC,GAAUrC,GAAKuE,GACNnE,EAAO,IAGhBiC,GAAUvC,GAAKM,IAGViC,CACT,CAMA,SAASyD,GAAyB3F,GAChC,GAAU,IAANA,EAAS,MAAO,GACpB,GAAU,IAANA,EAAS,OAAOwF,GAEpB,MAAMvF,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAEhC,IAAIkC,EAAS,GAoBb,OAlBI0B,EAAW,IACb1B,IAAwB,IAAb0B,EAAiB4B,GAAM7F,GAAKiE,IAAa8B,IAGzC,IAATtB,EACFlC,GAAUtC,GAAMK,GACPmE,GAAQ,GAAKnE,EAAO,EAC7BiC,IAAoB,IAATjC,EAAauF,GAAM7F,GAAKM,IAAS,MAAQJ,GAAKuE,GAChDA,GAAQ,EACjBlC,GAAUrC,GAAKuE,IACNnE,EAAO,GAAK2D,EAAW,GAIvB3D,EAAO,KADhBiC,GAAUvC,GAAKM,IAKViC,CACT,CAGA,MAAMzB,GAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,GAI7B,MAAMiF,GAAoB,IAAIlF,MAAM,KACpC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiF,GAAkBjF,GAAKgF,GAAwBhF,GAajD,SAAS8B,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAG7B,IAAIkC,EAAS0D,GAAkBjB,GAAaI,GAAO,GAMnD,OAJIlB,EAAY,IACd3B,GAAUzB,GAASoD,IAGd3B,CACT,CAGA,OASF,SAAgClC,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EACF,GAAmB,IAAfD,EAEFjD,EAAMC,KAAK,CAAE0D,MAAOxD,GAAS+C,GAAUqC,SAAS,EAAOC,WAAY,SAC9D,GAAmB,IAAfvC,EAGTjD,EAAMC,KAAK,CAAE0D,MADI2B,GAAkBpC,GACJuB,GAAO,GAAIc,SAAS,EAAOC,WAAY,QACjE,CAEL,IAAIC,EAEFA,EADc,IAAZvC,EACS,OAEA/C,GAAS+C,GAEtB,MAAMC,EAAwB,IAAZD,EAAgBuB,GAAOxB,EAAa,GAAKkC,GAAclC,EAAa,GACtFjD,EAAMC,KAAK,CAAE0D,MAAO8B,EAAUF,SAAS,EAAOC,WAAYvC,IAC1DjD,EAAMC,KAAK,CAAE0D,MAAOR,EAAWoC,SAAS,EAAMC,WAAYvC,GAC5D,CAGFA,GACF,CAGA,OAUF,SAA0BjD,GACxB,GAAqB,IAAjBA,EAAM7B,OAAc,OAAOmC,GAE/B,IAAIsB,EAAS,GAEb,IAAK,IAAIvB,EAAI,EAAGA,EAAIL,EAAM7B,OAAQkC,IAAK,CACrC,MAAMyE,EAAO9E,EAAMK,GACbqF,EAAWrF,EAAI,EAAIL,EAAMK,EAAI,GAAK,KAKpCA,EAAI,IACayE,EAAKS,SAAYG,GAAYA,EAASH,WAEvD3D,GAAU,KAIdA,GAAUkD,EAAKnB,KACjB,CAEA,OAAO/B,CACT,CAjCS+D,CAAgB3F,EACzB,CA9DSoD,CAAsB1D,EAC/B,CCzJA,MAAML,GAAO,CAAC,GAAI,MAAO,MAAO,OAAQ,UAAW,QAAS,MAAO,OAAQ,OAAQ,SAE7EC,GAAQ,CAAC,OAAQ,SAAU,SAAU,WAAY,cAAe,YAAa,UAAW,WAAY,WAAY,aAEhHC,GAAO,CAAC,GAAI,GAAI,SAAU,UAAW,UAAW,UAAW,SAAU,YAAa,UAAW,YAG7FwB,GAAW,CAAC,GAAI,QAAS,WAAY,YAAa,aAAc,aAAc,WAAY,YAAa,YAAa,cAEpHyD,GAAW,QAEXlE,GAAO,QAKPmE,GAAS,CAAC,cAAe,iBAAkB,mBASjD,SAAShF,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAGVsD,EAAW,GACbtD,EAAMC,KAAKc,GAASuC,IAItB,MAAMoB,EAAWhF,EAAI,IAcrB,OAZiB,IAAbgF,GAGF1E,EAAMC,KADGyE,EAAW,GACTrF,GAAKM,GACP+E,EAAW,GACTpF,GAAMK,GACC,IAATA,EACEJ,GAAKuE,GAELvE,GAAKuE,GAAQ,IAAMzE,GAAKM,IAG9BK,EAAME,KAAK,IACpB,CAGA,MAAMC,GAAW,IAAIC,MAAM,KAE3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,GC1D7B,MAAMhB,GAAO,CAAC,GAAI,MAAO,MAAO,QAAS,OAAQ,OAAQ,MAAO,QAAS,QAAS,QAC5EC,GAAQ,CAAC,MAAO,SAAU,SAAU,WAAY,WAAY,UAAW,UAAW,YAAa,WAAY,YAC3GC,GAAO,CAAC,GAAI,GAAI,SAAU,SAAU,QAAS,QAAS,QAAS,UAAW,SAAU,UAEpFkF,GAAS,CAAC,WAAY,UAAW,UAAW,WAAY,cAAe,cAAe,aAAc,aAAc,aAGlHnE,GAAO,OAYb,SAASb,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,CAAEkF,KAAM,GAAIgB,YAAY,GAE5C,MAAMjG,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAEhC,IAAIkC,EAAS,GACTgE,GAAa,EAGbtC,EAAW,IACb1B,EAASvC,GAAKiE,GAALjE,WACTuG,GAAa,GAIf,IAAIlB,EAAW,GAcf,OAba,IAATZ,EACFY,EAAWpF,GAAMK,GACRmE,GAAQ,EAEfY,EADE/E,EAAO,EACEJ,GAAKuE,GAAQ,IAAMzE,GAAKM,GAExBJ,GAAKuE,GAETnE,EAAO,IAChB+E,EAAWrF,GAAKM,IAIdiC,GAAU8C,EACL,CAAEE,KAAMhD,EAAS,QAAU8C,EAAUkB,YAAY,GAGnD,CAAEhB,KAAMhD,GAAU8C,EAAUkB,aACrC,CAGA,MAAMzF,GAAW,IAAIC,MAAM,KACrByF,GAAuB,IAAIzF,MAAM,KAEvC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IAAK,CAC7B,MAAMuB,EAASnC,GAAaY,GAC5BF,GAASE,GAAKuB,EAAOgD,KACrBiB,GAAqBxF,GAAKuB,EAAOgE,UACnC,CAYA,SAASzD,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAE7B,IAAIkC,EAASzB,GAASkE,GAAa,IAAMI,GAAO,GAEhD,GAAIlB,EAAY,EAAG,CACjB,MAAMuC,EAAgB3F,GAASoD,GAK7B3B,GAHGiE,GAAqBtC,GAGd,IAAMuC,EAFN,QAAUA,CAIxB,CAEA,OAAOlE,CACT,CAGA,OAUF,SAAgClC,GAG9B,MAAMoD,EAAW,GACjB,IAAIT,EAAO3C,EACX,KAAO2C,EAAO,IACZS,EAAS7C,KAAK3C,OAAO+E,EAAO,QAC5BA,GAAc,MAIhB,IAAI0D,GAAkB,EACtB,IAAK,IAAI1F,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IACnC,GAAoB,IAAhByC,EAASzC,GAAU,CACrB0F,EAAkB1F,EAClB,KACF,CAKF,IAAIuB,EAAS,GACToE,GAAe,EAEnB,IAAK,IAAI3F,EAAIyC,EAAS3E,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAC7C,MAAM6C,EAAUJ,EAASzC,GACT,IAAZ6C,IAOAtB,GAHmBvB,IAAM0F,GAGEC,IAJZH,GAAqB3C,KAKtCtB,GAAU,QAIRA,IAAQA,GAAU,KACtBA,GAXoBzB,GAAS+C,GAczB7C,EAAI,GACNuB,GAAU,IAAM6C,GAAOpE,EAAI,GAC3B2F,GAAe,GAEfA,GAAe,EAEnB,CAEA,OAAOpE,CACT,CA7DSwB,CAAsB1D,EAC/B,CCtGA,MAAM0B,GAAY,CAAC,GAAI,MAAO,MAAO,OAAQ,SAAU,QAAS,OAAQ,QAAS,OAAQ,SACnFC,GAAW,CAAC,GAAI,MAAO,MAAO,OAAQ,SAAU,QAAS,OAAQ,QAAS,OAAQ,SAElF/B,GAAQ,CAAC,OAAQ,OAAQ,OAAQ,QAAS,UAAW,SAAU,YAAa,aAAc,YAAa,cAGvG2G,GAAgB,CAAC,SAAU,YAAa,YAAa,aAAc,eAAgB,cAAe,aAAc,cAAe,aAAc,eAC7IC,GAAe,CAAC,SAAU,YAAa,YAAa,aAAc,eAAgB,cAAe,aAAc,cAAe,aAAc,eAE5I3G,GAAO,CAAC,GAAI,GAAI,GAAI,UAAW,WAAY,YAAa,UAAW,UAAW,UAAW,WAGzF4G,GAAgB,CAAC,GAAI,SAAU,aAAc,cAAe,gBAAiB,aAAc,cAAe,cAAe,cAAe,eACxIC,GAAe,CAAC,GAAI,SAAU,aAAc,cAAe,gBAAiB,aAAc,cAAe,cAAe,cAAe,eAGvI3B,GAAS,CAAC,SAAU,SAAU,UAAW,cACzCU,GAAgB,CAAC,WAAY,WAAY,YAAa,gBAEtDX,GAAW,MACXlE,GAAO,OAcb,SAASb,GAAcC,EAAG2G,GACxB,GAAU,IAAN3G,EAAS,MAAO,GAGpB,GAAU,MAANA,EAAW,MAAO,OAEtB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAC1BgF,EAAWhF,EAAI,IAEfM,EAAQ,GAgCd,OA7BIsD,EAAW,GAEbtD,EAAMC,MADcoG,EAAWD,GAAeD,IACvB7C,IAIR,IAAboB,GAKF1E,EAAMC,KAHGyE,EAAW,IAEJ2B,EAAWhF,GAAWD,IACnBsD,GACVA,EAAW,GAETpF,GAAMK,GACR+E,EAAW,IAEA2B,EAAWH,GAAeD,IACvBtG,GAGV,IAATA,EACSJ,GAAKuE,GAGLvE,GAAKuE,GAAQ,OADRuC,EAAWhF,GAAWD,IACEzB,IAIrCK,EAAME,KAAK,IACpB,CAGA,MAAMoG,GAAgB,IAAIlG,MAAM,KAChC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiG,GAAcjG,GAAKZ,GAAaY,GAAG,GAIrC,MAAMkG,GAAe,IAAInG,MAAM,KAC/B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBkG,GAAalG,GAAKZ,GAAaY,GAAG,GAcpC,SAASmG,GAAcvD,EAAYC,GACjC,GAAmB,IAAfD,EAAkB,OAAOuB,GAI7B,GAAIvB,EAAa,GAAM,EAAG,CACxB,MAAMwD,EAAcxD,EAAa,EAAK,EAChCyD,EAAWjC,GAAOgC,GACxB,OAAKC,EACExD,EAAU,GAAKiC,GAAcsB,GAAcC,EAD5B,EAExB,CAAO,CAEL,MACMC,EAAaxB,IADElC,EAAa,GAAK,EAAK,GAE5C,OAAK0D,EACEnC,GAAW,IAAMmC,EADAnC,EAE1B,CACF,CAaA,SAASrC,GAAgBzC,EAAG2G,GAC1B,GAAU,KAAN3G,EAAU,OAAOY,GAErB,MAAMwC,EAAWuD,EAAWE,GAAeD,GAG3C,GAAI5G,EAAI,MACN,OAAOoD,EAASxF,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAE7B,IAAIkC,EACJ,GAAkB,IAAdyC,EAEFzC,EAAS4C,OACJ,CAEL,MAAMoC,EAAgBN,GAAcjC,GAGlCzC,EADoB,QAAlBgF,GAA6C,QAAlBA,EACpBpC,GAEAoC,EAAgB,IAAMpC,EAEnC,CAMA,OAJIjB,EAAY,IACd3B,GAAU,IAAMkB,EAASS,IAGpB3B,CACT,CAGA,OAWF,SAAgClC,EAAG2G,GAGjC,MAAM/B,EAAgB,GACtB,IAAIjC,EAAO3C,EACX,KAAO2C,EAAO,IACZiC,EAAcrE,KAAKoC,EAAO,OAC1BA,GAAc,MAGhB,MAAMS,EAAWuD,EAAWE,GAAeD,GAG3C,IAAI1E,EAAS,GAEb,IAAK,IAAIvB,EAAIiE,EAAcnG,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAClD,MAAM6C,EAAUoB,EAAcjE,GAC9B,GAAgB,KAAZ6C,EAAgB,SAEpB,MAAMC,EAAY9C,EAAI,EAAImG,GAAanG,EAAG6C,GAAW,GAEjDtB,IAAQA,GAAU,KAIpBA,GAFQ,IAANvB,EAEQyC,EAASxF,OAAO4F,IACX,IAAN7C,EAEO,KAAZ6C,EACQsB,GAEA8B,GAAchJ,OAAO4F,IAAY,IAAMC,EAE1C9C,EAAI,GAAM,EAGH,KAAZ6C,EACQC,EAEAmD,GAAchJ,OAAO4F,IAAY,IAAMC,EAInC,KAAZD,EAEQ,MAAQC,EAGRmD,GAAchJ,OAAO4F,IAAY,IAAMC,CAGvD,CAEA,OAAOvB,CACT,CAjESwB,CAAsB1D,EAAG2G,EAClC,CCrLA,MAAMhH,GAAO,CAAE,EAAG,KAAM,EAAG,KAAM,EAAG,KAAM,EAAG,OAAQ,EAAG,MAAO,EAAG,KAAM,EAAG,MAAO,EAAG,MAAO,EAAG,MACzFC,GAAQ,CAAE,GAAI,KAAM,GAAI,QAAS,GAAI,SAAU,GAAI,QAAS,GAAI,SAAU,GAAI,SAAU,GAAI,SAAU,GAAI,OAAQ,GAAI,OAAQ,GAAI,SAClIC,GAAO,CAAE,GAAI,OAAQ,GAAI,KAAM,GAAI,MAAO,GAAI,QAAS,GAAI,MAAO,GAAI,QAAS,GAAI,QAAS,GAAI,OAChGwB,GAAW,CAAE,IAAK,KAAM,IAAK,QAAS,IAAK,OAAQ,IAAK,UAAW,IAAK,QAAS,IAAK,OAAQ,IAAK,QAAS,IAAK,QAAS,IAAK,QAarI,SAASoB,GAAgBzC,GACvB,GAAU,KAANA,EAAU,MATH,MAYX,GAAIA,GAAK,GACP,OAAOL,GAAK/B,OAAOoC,IAIrB,GAAIA,GAAK,IACP,OAAOJ,GAAMhC,OAAOoC,IAItB,GAAIA,EAAI,KAAM,CACZ,MAAMC,EAAOD,EAAI,IACXoE,EAAOpE,EAAIC,EACjB,OAAa,KAATA,EACKJ,GAAKjC,OAAOwG,IAEd,GAAGvE,GAAKjC,OAAOwG,SAAYzE,GAAK/B,OAAOqC,KAChD,CAGA,GAAID,EAAI,MAAO,CACb,MAAM4D,EAAmB5D,EAAI,KAAZ,KACX6D,EAAY7D,EAAI4D,EACtB,OAAkB,KAAdC,EACKxC,GAASzD,OAAOgG,IAElB,GAAGvC,GAASzD,OAAOgG,SAAgBnB,GAAeoB,IAC3D,CAGA,GAAI7D,EAAI,SAAY,CAClB,MAAMmH,EAAqBnH,EAAI,MAKzB6D,EAAY7D,EAAI,MAEtB,MAAO,GALuC,KAAvBmH,EACnB,GACA1E,GAAe0E,GAAsB,UAEZ,KAAdtD,EAAmB,GAAK,IAAMpB,GAAeoB,IAE9D,CAGA,MAEMA,EAAY7D,EAAI,SAEtB,MAAO,GAHeyC,GADIzC,EAAI,UACRyC,YAEO,KAAdoB,EAAmB,GAAK,MAAQpB,GAAeoB,IAEhE,CCjEA,MAAMlE,GAAO,CAAC,GAAI,OAAQ,QAAS,QAAS,QAAS,QAAS,QAAS,YAAa,YAAa,YAE3FC,GAAQ,CAAC,WAAY,aAAc,cAAe,cAAe,cAAe,cAAe,cAAe,kBAAmB,kBAAmB,kBAGpJC,GAAO,CAAC,GAAI,GAAI,gBAAiB,gBAAiB,gBAAiB,gBAAiB,gBAAiB,oBAAqB,oBAAqB,oBAE/I6F,GAAU,OACVZ,GAAW,QAEXlE,GAAO,QAKPmE,GAAS,CAAC,WAAY,WAAY,WAAY,aAUpD,SAAShF,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAGVsD,EAAW,GAEXtD,EAAMC,KADS,IAAbqD,EACS8B,GAEA/F,GAAKiE,GAAY,IAAM8B,IAKtC,MAAMV,EAAWhF,EAAI,IAerB,OAbiB,IAAbgF,GAGF1E,EAAMC,KADGyE,EAAW,GACTrF,GAAKM,GACP+E,EAAW,GACTpF,GAAMK,GACC,IAATA,EACEJ,GAAKuE,GAGLvE,GAAKuE,GAAQzE,GAAKM,IAGxBK,EAAME,KAAK,IACpB,CAGA,MAAMC,GAAW,IAAIC,MAAM,KAE3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,GCnE7B,MAAMhB,GAAO,CAAC,GAAI,MAAO,SAAU,QAAS,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,SAC9EC,GAAQ,CAAC,QAAS,WAAY,cAAe,aAAc,YAAa,YAAa,YAAa,YAAa,aAAc,cAC7HC,GAAO,CAAC,GAAI,GAAI,YAAa,WAAY,WAAY,UAAW,WAAY,UAAW,UAAW,aAMlGe,GAAO,OAKPd,GAAc,CAAC,GAPJ,SAOkB,UAAW,UAAW,YAMnDsH,GAAS,CAAC,IAAK,IAAK,IAAK,IAAK,KAEpC,SAASC,GAAWnC,GAElB,OAAIkC,GAAOhJ,SADM8G,EAAKA,EAAKzG,OAAS,IAE3ByG,EAAO,KAETA,EAAO,KAChB,CAMA,SAASnF,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAGd,GAAID,EAAgB,EAAG,CACrB,MAAMiH,EAAgBD,GAAU1H,GAAKU,IACrCC,EAAMC,KAAK+G,WACb,CAGA,MAAMtC,EAAWhF,EAAI,IAuBrB,OArBiB,IAAbgF,GAIF1E,EAAMC,KAFGyE,EAAW,GAETrF,GAAKM,GACP+E,EAAW,GAETpF,GAAMK,GACC,IAATA,EAEEJ,GAAKK,GAIE,IAAdA,EACSL,GAAKK,GAAa,MAAQP,GAAKM,GAE/BJ,GAAKK,GAAa,IAAMP,GAAKM,IAIrCK,EAAME,KAAK,IACpB,CAEA,MAAMC,GAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,GAM7B,SAAS4G,GAAwBvH,GAC/B,MAAMiF,EAAcxE,GAAST,GAC7B,IAAKiF,EAAa,MAAO,GAGzB,MAAMuC,EAAevC,EAAYwC,YAAY,KAC7C,IAAqB,IAAjBD,EAGF,MAAiB,MADAvC,EAAYA,EAAYxG,OAAS,IAC1BwG,EAAYK,SAAS,MACpCL,EAEFoC,GAAUpC,GAInB,MAAMyC,EAASzC,EAAYpG,MAAM,EAAG2I,EAAe,GAC7CG,EAAW1C,EAAYpG,MAAM2I,EAAe,GAElD,OAAIG,EAASrC,SAAS,MACbL,EAEFyC,EAASL,GAAUM,EAC5B,CAGA,MAAMC,GAAuB,IAAIlH,MAAM,KACvC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiH,GAAqBjH,GAAK4G,GAAuB5G,GCzGnD,MAAMhB,GAAO,CAAC,GAAI,KAAM,OAAQ,QAAS,SAAU,OAAQ,MAAO,OAAQ,OAAQ,QAC5EC,GAAQ,CAAC,MAAO,OAAQ,QAAS,SAAU,WAAY,SAAU,QAAS,WAAY,WAAY,YAClGC,GAAO,CAAC,GAAI,GAAI,QAAS,SAAU,WAAY,YAAa,YAG5DkF,GAAS,CAAC,UAAW,UAAW,WAAY,eAC5C8C,GAAa,CAAC,WAAY,WAAY,YAAa,gBAEnD/C,GAAW,QACXY,GAAU,OACV9E,GAAO,OAYb,SAASb,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,CAAEkF,KAAM,GAAI4C,eAAe,EAAOC,gBAAgB,GAEtE,MAAM/C,EAAWhF,EAAI,IACf4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GACd,IAAIwH,GAAgB,EAChBC,GAAiB,EAuBrB,GApBInE,EAAW,IACI,IAAbA,EAEAtD,EAAMC,KAAKmF,IAKI,IAAbV,GAEF1E,EAAMC,KAAKZ,GAAKiE,GAAY,IAAM8B,GAAU,KAC5CoC,GAAgB,GAGhBxH,EAAMC,KAAKZ,GAAKiE,GAAY,IAAM8B,KAMvB,IAAbV,QAEG,GAAIA,EAAW,GAEpB1E,EAAMC,KAAKZ,GAAKqF,SACX,GAAIA,EAAW,GAEpB1E,EAAMC,KAAKX,GAAMoF,EAAW,UACvB,GAAIA,EAAW,GAEpB1E,EAAMC,KAAKX,GAAMoF,EAAW,UACvB,GAAIA,EAAW,GAAI,CAExB,MAAMgD,EAAI7H,KAAKC,MAAM4E,EAAW,IAC1BiD,EAAIjD,EAAW,GAEnB1E,EAAMC,KADE,IAAN0H,EACSpI,GAAKmI,GACD,IAANC,EAEEpI,GAAKmI,GAAK,OAASrI,GAAK,GAExBE,GAAKmI,GAAK,IAAMrI,GAAKsI,GAEpC,MAAO,GAAIjD,EAAW,GAAI,CAExB,MAAMnB,EAAYmB,EAAW,GAG3B1E,EAAMC,KAFU,KAAdsD,EAES,mBAGA,YAAcjE,GAAMiE,EAAY,IAE/C,MAAO,GAAiB,KAAbmB,EAET1E,EAAMC,KAAK,iBACXwH,GAAiB,OACZ,GAAI/C,EAAW,IAAK,CAEzB,MAAMnB,EAAYmB,EAAW,GAG3B1E,EAAMC,KAFJsD,EAAY,GAEH,gBAAkBlE,GAAKkE,GAGvB,gBAAkBjE,GAAMiE,EAAY,IAEnD,CAGA,MAAO,CAAEqB,KAAM5E,EAAME,KAAK,KAAMsH,gBAAeC,iBACjD,CAGA,MAAMtH,GAAW,IAAIC,MAAM,KACrBwH,GAAsB,IAAIxH,MAAM,KAChCyH,GAAuB,IAAIzH,MAAM,KAEvC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IAAK,CAC7B,MAAMuB,EAASnC,GAAaY,GAC5BF,GAASE,GAAKuB,EAAOgD,KACrBgD,GAAoBvH,GAAKuB,EAAO4F,cAChCK,GAAqBxH,GAAKuB,EAAO6F,cACnC,CAaA,SAASjB,GAAcvD,EAAYC,GACjC,GAAmB,IAAfD,EAAkB,OAAOuB,GAI7B,GAAIvB,EAAa,GAAM,EAAG,CACxB,MACMyD,EAAWjC,GADGxB,EAAa,EAAK,GAEtC,OAAKyD,EACExD,EAAU,GAAKwD,EAAW,IAAMA,EADjB,EAExB,CAAO,CACL,MACMoB,EAAUP,IADKtE,EAAa,GAAK,EAAK,GAE5C,OAAK6E,EACE5E,EAAU,GAAK4E,EAAU,IAAMA,EADjBtD,EAEvB,CACF,CAaA,SAASrC,GAAgBzC,EAAGqI,GAAa,GACvC,GAAU,KAANrI,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MAAO,CACb,MAAMkF,EAAOzE,GAAS7C,OAAOoC,IAC7B,OAAOqI,EAAanD,EAAKoD,QAAQ,KAAM,KAAOpD,CAChD,CAGA,GAAIlF,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAE7B,IAAIkC,EACJ,GAAkB,IAAdyC,EAEFzC,EAAS4C,OACJ,CAEL,IAAIoC,EAAgBzG,GAASkE,IACzBuD,GAAoBvD,IAAcwD,GAAqBxD,MACzDuC,EAAgBA,EAAcrI,MAAM,GAAG,IAEzCqD,EAASgF,GAAiBmB,EAAa,IAAM,KAAOvD,EACtD,CAUA,OARIjB,EAAY,IACd3B,IAAWmG,EAAa,IAAM,KAAO5H,GAASoD,IAG5CwE,IACFnG,EAASA,EAAOoG,QAAQ,KAAM,MAGzBpG,CACT,CAGA,OAUF,SAAgClC,EAAGqI,GACjC,MAAMnF,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMC,EAAYF,EAAa,EAAIuD,GAAavD,EAAYvF,OAAOwF,IAAY,GAE/E,GAAmB,IAAfD,EAEFjD,EAAMC,KAAKE,GAAS+C,SACf,GAAmB,IAAfD,EAET,GAAgB,IAAZC,EACFlD,EAAMC,KAAKuE,QACN,CACL,IAAIiB,EAAWtF,GAAS+C,IAEpB0E,GAAoB1E,IAAY2E,GAAqB3E,MACvDuC,EAAWA,EAASlH,MAAM,GAAG,IAE/ByB,EAAMC,KAAKwF,GACXzF,EAAMC,KAAKkD,EACb,MAGAnD,EAAMC,KAAKE,GAAS+C,IACpBlD,EAAMC,KAAKkD,EAEf,CAEAF,GACF,CAGA,IAAIrB,EAAS5B,EAAME,KADP6H,EAAa,IAAM,KAO/B,OAJIA,IACFnG,EAASA,EAAOoG,QAAQ,KAAM,MAGzBpG,CACT,CAzESwB,CAAsB1D,EAAGqI,EAClC,CC3MA,MAAM1I,GAAO,CAAC,GAAI,KAAM,OAAQ,QAAS,SAAU,OAAQ,MAAO,OAAQ,OAAQ,QAC5EC,GAAQ,CAAC,MAAO,OAAQ,QAAS,SAAU,WAAY,SAAU,QAAS,WAAY,WAAY,YAClGC,GAAO,CAAC,GAAI,GAAI,QAAS,SAAU,WAAY,YAAa,WAAY,WAAY,eAAgB,WAGpGkF,GAAS,CAAC,UAAW,UAAW,WAAY,eAC5C8C,GAAa,CAAC,WAAY,WAAY,YAAa,gBAEnD/C,GAAW,QACXY,GAAU,OACV9E,GAAO,OAQb,SAASb,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,CAAEkF,KAAM,GAAI4C,eAAe,EAAOC,gBAAgB,GAEtE,MAAM/C,EAAWhF,EAAI,IACf4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GACd,IAAIwH,GAAgB,EAChBC,GAAiB,EAiBrB,GAdInE,EAAW,IACI,IAAbA,EACFtD,EAAMC,KAAKmF,IAEM,IAAbV,GACF1E,EAAMC,KAAKZ,GAAKiE,GAAY,IAAM8B,GAAU,KAC5CoC,GAAgB,GAEhBxH,EAAMC,KAAKZ,GAAKiE,GAAY,IAAM8B,KAMvB,IAAbV,QAEG,GAAIA,EAAW,GACpB1E,EAAMC,KAAKZ,GAAKqF,SACX,GAAIA,EAAW,GACpB1E,EAAMC,KAAKX,GAAMoF,EAAW,UACvB,GAAIA,EAAW,GACpB1E,EAAMC,KAAKX,GAAMoF,EAAW,UACvB,GAAIA,EAAW,GAAI,CAExB,MAAMgD,EAAI7H,KAAKC,MAAM4E,EAAW,IAC1BiD,EAAIjD,EAAW,GAEnB1E,EAAMC,KADE,IAAN0H,EACSpI,GAAKmI,GACD,IAANC,EACEpI,GAAKmI,GAAK,OAASrI,GAAK,GAExBE,GAAKmI,GAAK,IAAMrI,GAAKsI,GAEpC,MAAO,GAAIjD,EAAW,GAAI,CAExB,MAAMiD,EAAIjD,EAAW,GAEnB1E,EAAMC,KADE,IAAN0H,EACS,WACI,IAANA,EACE,eAAiBtI,GAAK,GAEtB,YAAcA,GAAKsI,GAElC,MAAO,GAAiB,KAAbjD,EAET1E,EAAMC,KAAK,iBACXwH,GAAiB,OACZ,GAAI/C,EAAW,GAGpB1E,EAAMC,KAAK,gBAAkBZ,GADXqF,EAAW,SAExB,CAEL,MAAMiD,EAAIjD,EAAW,GAEnB1E,EAAMC,KADE,IAAN0H,EACS,UACI,IAANA,EACE,cAAgBtI,GAAK,GAErB,WAAaA,GAAKsI,GAEjC,CAEA,MAAO,CAAE/C,KAAM5E,EAAME,KAAK,KAAMsH,gBAAeC,iBACjD,CAGA,MAAMtH,GAAW,IAAIC,MAAM,KACrBwH,GAAsB,IAAIxH,MAAM,KAChCyH,GAAuB,IAAIzH,MAAM,KAEvC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IAAK,CAC7B,MAAMuB,EAASnC,GAAaY,GAC5BF,GAASE,GAAKuB,EAAOgD,KACrBgD,GAAoBvH,GAAKuB,EAAO4F,cAChCK,GAAqBxH,GAAKuB,EAAO6F,cACnC,CAMA,SAASjB,GAAcvD,EAAYC,GACjC,GAAmB,IAAfD,EAAkB,OAAOuB,GAE7B,GAAIvB,EAAa,GAAM,EAAG,CACxB,MACMyD,EAAWjC,GADGxB,EAAa,EAAK,GAEtC,OAAKyD,EACExD,EAAU,GAAKwD,EAAW,IAAMA,EADjB,EAExB,CAAO,CACL,MACMoB,EAAUP,IADKtE,EAAa,GAAK,EAAK,GAE5C,OAAK6E,EACE5E,EAAU,GAAK4E,EAAU,IAAMA,EADjBtD,EAEvB,CACF,CAMA,SAASrC,GAAgBzC,EAAGqI,GAAa,GACvC,GAAU,KAANrI,EAAU,OAAOY,GAErB,GAAIZ,EAAI,MAAO,CACb,MAAMkF,EAAOzE,GAAS7C,OAAOoC,IAC7B,OAAOqI,EAAanD,EAAKoD,QAAQ,KAAM,KAAOpD,CAChD,CAEA,GAAIlF,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAE7B,IAAIkC,EACJ,GAAkB,IAAdyC,EACFzC,EAAS4C,OACJ,CACL,IAAIoC,EAAgBzG,GAASkE,IACzBuD,GAAoBvD,IAAcwD,GAAqBxD,MACzDuC,EAAgBA,EAAcrI,MAAM,GAAG,IAEzCqD,EAASgF,GAAiBmB,EAAa,IAAM,KAAOvD,EACtD,CAUA,OARIjB,EAAY,IACd3B,IAAWmG,EAAa,IAAM,KAAO5H,GAASoD,IAG5CwE,IACFnG,EAASA,EAAOoG,QAAQ,KAAM,MAGzBpG,CACT,CAEA,OAGF,SAAgClC,EAAGqI,GACjC,MAAMnF,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMC,EAAYF,EAAa,EAAIuD,GAAavD,EAAYvF,OAAOwF,IAAY,GAE/E,GAAmB,IAAfD,EACFjD,EAAMC,KAAKE,GAAS+C,SACf,GAAmB,IAAfD,EACT,GAAgB,IAAZC,EACFlD,EAAMC,KAAKuE,QACN,CACL,IAAIiB,EAAWtF,GAAS+C,IACpB0E,GAAoB1E,IAAY2E,GAAqB3E,MACvDuC,EAAWA,EAASlH,MAAM,GAAG,IAE/ByB,EAAMC,KAAKwF,GACXzF,EAAMC,KAAKkD,EACb,MAEAnD,EAAMC,KAAKE,GAAS+C,IACpBlD,EAAMC,KAAKkD,EAEf,CAEAF,GACF,CAGA,IAAIrB,EAAS5B,EAAME,KADP6H,EAAa,IAAM,KAO/B,OAJIA,IACFnG,EAASA,EAAOoG,QAAQ,KAAM,MAGzBpG,CACT,CA5DSwB,CAAsB1D,EAAGqI,EAClC,CCtKA,MAAMzH,GAAO,QAKP+C,GAAgB,CACpB,QAAS,KAAM,KAAM,OAAQ,MAAO,OAAQ,IAAK,MAAO,KAAM,KAC9D,KAAM,SAAU,MAAO,MAAO,MAAO,OAAQ,MAAO,QAAS,OAAQ,QACrE,MAAO,QAAS,QAAS,UAAW,QAAS,OAAQ,SAAU,WAAY,WAAY,WACvF,QAAS,UAAW,SAAU,UAAW,UAAW,WAAY,SAAU,WAAY,UAAW,WACjG,QAAS,UAAW,UAAW,YAAa,WAAY,YAAa,UAAW,WAAY,UAAW,UACvG,OAAQ,QAAS,OAAQ,SAAU,OAAQ,SAAU,QAAS,UAAW,UAAW,SACpF,MAAO,OAAQ,OAAQ,SAAU,OAAQ,QAAS,OAAQ,OAAQ,OAAQ,cAC1E,UAAW,SAAU,QAAS,QAAS,UAAW,UAAW,QAAS,YAAa,WAAY,WAC/F,OAAQ,UAAW,SAAU,SAAU,WAAY,SAAU,SAAU,WAAY,UAAW,WAC9F,QAAS,SAAU,QAAS,UAAW,UAAW,UAAW,SAAU,WAAY,WAAY,YAI3F7D,GAAc,CAAC,GAAI,OAAQ,MAAO,OAAQ,MAAO,MAAO,MAAO,OAAQ,OAsB7E,SAAS8B,GAAgB5B,GACvB,GAAU,IAANA,EAAS,MAAO,GACpB,GAAIA,EAAI,IAAK,OAAO2D,GAAc3D,GAElC,MAAM4D,EAAWzD,KAAK8B,MAAMjC,EAAI,KAC1B6D,EAAY7D,EAAI,IAEtB,OAAkB,IAAd6D,EACKF,GAAcC,GAAdD,MAEFA,GAAcC,GAAdD,OAAgDA,GAAcE,EACvE,CClDA,MAAMlE,GAAO,CAAC,GAAI,OAAQ,OAAQ,MAAO,OAAQ,QAAS,QAAS,SAAU,SAAU,QACjFC,GAAQ,CAAC,OAAQ,WAAY,WAAY,UAAW,WAAY,YAAa,YAAa,aAAc,aAAc,YAEtHC,GAAO,CAAC,GAAI,GAAI,UAAW,UAAW,UAAW,SAAU,SAAU,UAAW,UAAW,UAE3F6F,GAAU,OACVZ,GAAW,OAEXlE,GAAO,SAKPd,GAAc,CAAC,GAAIgF,GAAU,UAAW,WAW9C,SAAS/E,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAGVD,EAAgB,GAEhBC,EAAMC,KADc,IAAlBF,EACSqF,GAGA/F,GAAKU,GAAiB,IAAMqF,IAK3C,MAAMV,EAAWhF,EAAI,IAsBrB,OApBiB,IAAbgF,GAKA1E,EAAMC,KAHCyE,EAAW,GAEhB3E,EAAgB,EACP,MAAQV,GAAKM,GAEbN,GAAKM,GAET+E,EAAW,GAETpF,GAAMK,GACC,IAATA,EAEEJ,GAAKK,GAGLL,GAAKK,GAAa,OAASP,GAAKM,IAGtCK,EAAME,KAAK,IACpB,CAEA,MAAMC,GAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,GAoB7B,SAAS4H,GAAerD,GACtB,OAAOvF,GAAKd,MAAM,GAAGT,SAAS8G,EAChC,CC3FA,MAAMxD,GAAY,CAAC,GAAI,MAAO,QAAS,OAAQ,QAAS,OAAQ,MAAO,OAAQ,QAAS,QAClF8G,GAAa,CAAC,OAAQ,UAAW,WAAY,WAAY,YAAa,WAAY,UAAW,WAAY,YAAa,YACtHC,GAAiB,CAAC,GAAI,MAAO,SAAU,aAAc,cAAe,aAAc,YAAa,aAAc,cAAe,cAG5H9G,GAAW,CAAC,GAAI,MAAO,OAAQ,MAAO,OAAQ,MAAO,KAAM,MAAO,QAAS,OAC3E+G,GAAY,CAAC,MAAO,WAAY,YAAa,WAAY,YAAa,WAAY,UAAW,WAAY,aAAc,YACvHC,GAAgB,CAAC,GAAI,MAAO,SAAU,aAAc,cAAe,aAAc,YAAa,aAAc,cAAe,cAG3H9I,GAAO,CAAC,GAAI,GAAI,QAAS,QAAS,SAAU,SAAU,OAAQ,QAAS,SAAU,SACjFwB,GAAW,CAAC,GAAI,MAAO,SAAU,YAAa,aAAc,YAAa,WAAY,YAAa,aAAc,aAChHqF,GAAe,CAAC,GAAI,MAAO,SAAU,WAAY,YAAa,WAAY,UAAW,WAAY,aAAc,YAG/GkC,GAAQ,CAAC,GAAI,MAAO,SAAU,UAAW,UAAW,YAAa,eACjErH,GAAe,CAAC,GAAI,QAAS,WAAY,YAAa,YAAa,cAAe,iBAcxF,SAASsH,GAAmB7I,EAAG8I,EAASnJ,EAAMC,EAAOmJ,GACnD,GAAU,IAAN/I,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAEhC,IAAIkC,EAAS,GAQb,GALI0B,EAAW,IACb1B,EAAS6G,EAAanF,IAIX,IAATQ,EAAY,CAEd,MAAM4E,EAAWpJ,EAAMK,GACnBiC,EACFA,GAAU,IAAM4G,EAAUE,EAE1B9G,EAAS8G,CAEb,MAEM5E,GAAQ,IACNlC,EACFA,GAAU,IAAM4G,EAAUjJ,GAAKuE,GAE/BlC,EAASrC,GAAKuE,IAKdnE,EAAO,IACLiC,EACFA,GAAU,IAAM4G,EAAUnJ,EAAKM,GAE/BiC,EAASvC,EAAKM,IAKpB,OAAOiC,CACT,CAMA,SAAS+G,GAAmBjJ,EAAG8I,EAASnJ,EAAMC,EAAOmJ,GACnD,GAAU,IAAN/I,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAEhC,IAAIkC,EAAS,GAkCb,OA/BI0B,EAAW,IACb1B,EAAS6G,EAAanF,IAIX,IAATQ,EAEElC,EACFA,GAAU,IAAMtC,EAAMK,GAEtBiC,EAAStC,EAAMK,IAGbmE,GAAQ,IACNlC,EACFA,GAAU,IAAMrC,GAAKuE,GAErBlC,EAASrC,GAAKuE,IAKdnE,EAAO,IACLiC,EACFA,GAAU,IAAM4G,EAAUnJ,EAAKM,GAE/BiC,EAASvC,EAAKM,KAKbiC,CACT,CAGA,MAAMgH,GAAsB,IAAIxI,MAAM,KAChCyI,GAAsB,IAAIzI,MAAM,KAChC0I,GAAqB,IAAI1I,MAAM,KAC/B2I,GAAqB,IAAI3I,MAAM,KAErC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBuI,GAAoBvI,GAAKkI,GAAkBlI,EAAG,IAAKe,GAAW8G,GAAYnH,IAC1E8H,GAAoBxI,GAAKsI,GAAkBtI,EAAG,IAAKe,GAAW8G,GAAYnH,IAC1E+H,GAAmBzI,GAAKkI,GAAkBlI,EAAG,IAAKgB,GAAU+G,GAAWhC,IACvE2C,GAAmB1I,GAAKsI,GAAkBtI,EAAG,IAAKgB,GAAU+G,GAAWhC,ICtIzE,MAAM/G,GAAO,CAAC,GAAI,MAAO,OAAQ,MAAO,OAAQ,MAAO,KAAM,MAAO,QAAS,OACvEC,GAAQ,CAAC,MAAO,WAAY,YAAa,WAAY,YAAa,WAAY,UAAW,WAAY,aAAc,YACnHC,GAAO,CAAC,GAAI,GAAI,QAAS,QAAS,SAAU,SAAU,OAAQ,QAAS,SAAU,SACjFwB,GAAW,CAAC,GAAI,MAAO,SAAU,WAAY,YAAa,WAAY,UAAW,WAAY,aAAc,YAG3GiI,GAAoB,CAAC,GAAI,MAAO,SAAU,aAAc,cAAe,aAAc,YAAa,aAAc,cAAe,cAG/HV,GAAQ,CAAC,GAAI,MAAO,SAAU,UAAW,UAAW,YAAa,eACjErH,GAAe,CAAC,GAAI,QAAS,WAAY,YAAa,YAAa,cAAe,iBAcxF,SAASsH,GAAmB7I,EAAG8I,GAC7B,GAAU,IAAN9I,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAEhC,IAAIkC,EAAS,GAQb,GALI0B,EAAW,IACb1B,EAASb,GAASuC,IAIP,IAATQ,EAAY,CAEd,MAAM4E,EAAWpJ,GAAMK,GACnBiC,EACFA,GAAU,IAAM4G,EAAUE,EAE1B9G,EAAS8G,CAEb,MAEM5E,GAAQ,IACNlC,EACFA,GAAU,IAAM4G,EAAUjJ,GAAKuE,GAE/BlC,EAASrC,GAAKuE,IAKdnE,EAAO,IACLiC,EACFA,GAAU,IAAM4G,EAAUnJ,GAAKM,GAE/BiC,EAASvC,GAAKM,IAKpB,OAAOiC,CACT,CAMA,SAAS+G,GAAmBjJ,EAAG8I,GAC7B,GAAU,IAAN9I,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAEhC,IAAIkC,EAAS,GAkCb,OA/BI0B,EAAW,IACb1B,EAASb,GAASuC,IAIP,IAATQ,EAEElC,EACFA,GAAU,IAAMtC,GAAMK,GAEtBiC,EAAStC,GAAMK,IAGbmE,GAAQ,IACNlC,EACFA,GAAU,IAAMrC,GAAKuE,GAErBlC,EAASrC,GAAKuE,IAKdnE,EAAO,IACLiC,EACFA,GAAU,IAAM4G,EAAUnJ,GAAKM,GAE/BiC,EAASvC,GAAKM,KAKbiC,CACT,CAGA,MAAMqH,GAAiB,IAAI7I,MAAM,KAC3B8I,GAAiB,IAAI9I,MAAM,KACjC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxB4I,GAAe5I,GAAKkI,GAAkBlI,EAAG,KACzC6I,GAAe7I,GAAKsI,GAAkBtI,EAAG,KChI3C,MAAMC,GAAO,QAKP+C,GAAgB,CACpB,QAAS,KAAM,KAAM,MAAO,MAAO,OAAQ,KAAM,MAAO,KAAM,KAC9D,KAAM,SAAU,OAAQ,OAAQ,OAAQ,SAAU,OAAQ,QAAS,QAAS,SAC5E,MAAO,SAAU,OAAQ,OAAQ,QAAS,SAAU,SAAU,UAAW,UAAW,QACpF,MAAO,QAAS,SAAU,SAAU,SAAU,SAAU,SAAU,SAAU,SAAU,UACtF,QAAS,UAAW,SAAU,UAAW,SAAU,WAAY,UAAW,WAAY,WAAY,QAClG,OAAQ,UAAW,OAAQ,QAAS,OAAQ,OAAQ,QAAS,UAAW,UAAW,OACnF,MAAO,OAAQ,OAAQ,QAAS,QAAS,QAAS,SAAU,QAAS,QAAS,UAC9E,QAAS,UAAW,SAAU,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAChG,QAAS,UAAW,QAAS,SAAU,SAAU,QAAS,SAAU,UAAW,UAAW,QAC1F,QAAS,WAAY,QAAS,UAAW,UAAW,SAAU,UAAW,WAAY,WAAY,aAI7F7D,GAAc,CAAC,GAAI,QAAS,MAAO,QAAS,MAAO,MAAO,MAAO,OAAQ,OAsB/E,SAAS8B,GAAgB5B,GACvB,GAAU,IAANA,EAAS,MAAO,GACpB,GAAIA,EAAI,IAAK,OAAO2D,GAAc3D,GAElC,MAAM4D,EAAWzD,KAAK8B,MAAMjC,EAAI,KAC1B6D,EAAY7D,EAAI,IAEtB,OAAkB,IAAd6D,EACKF,GAAcC,GAAdD,MAEFA,GAAcC,GAAdD,OAAgDA,GAAcE,EACvE,CAMA,SAASpB,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAErB,MAAMwC,EApCR,SAA+BpD,GAC7B,MAAMkD,EAASlD,EAAE7B,WACjB,GAAI+E,EAAOzE,QAAU,EAAG,MAAO,CAACb,OAAOsF,IAEvC,MAAME,EAAW,GACjBA,EAASH,QAAQrF,OAAOsF,EAAOrE,OAAM,KAErC,IAAIiF,EAAYZ,EAAOrE,MAAM,GAAG,GAChC,KAAOiF,EAAUrF,OAAS,GACxB2E,EAASH,QAAQrF,OAAOkG,EAAUjF,OAAM,KACxCiF,EAAYA,EAAUjF,MAAM,GAAG,GAGjC,OAAOuE,CACT,CAsBmBW,CAAqB/D,GAChCgE,EAAeZ,EAAS3E,OACxBwF,EAAQ,GAEd,IAAK,IAAItD,EAAI,EAAGA,EAAIqD,EAAcrD,IAAK,CACrC,MAAMuD,EAAed,EAASzC,GAC9B,GAAqB,IAAjBuD,EAAoB,SAExB,MAAMX,EAAaS,EAAerD,EAAI,EACtCsD,EAAM1D,KAAKqB,GAAesC,IACtBX,EAAa,GAAKzD,GAAYyD,IAChCU,EAAM1D,KAAKT,GAAYyD,GAE3B,CAEA,OAAOU,EAAMzD,KAAK,KAAKhC,MACzB,CC5EA,SAAS+F,GAAWvE,EAAGwE,GACrB,MAAMiF,EAAmB,iBAANzJ,EAAiBpC,OAAOoC,GAAKA,EAC1CyE,EAAYgF,EAAM,GAClB/E,EAAgB+E,EAAM,IAE5B,OAAI/E,GAAiB,IAAMA,GAAiB,GACnCF,EAAM,GAGG,IAAdC,EAAwBD,EAAM,GAC9BC,GAAa,GAAKA,GAAa,EAAUD,EAAM,GAC5CA,EAAM,EACf,CAcA,SAASzE,GAAcC,EAAGC,EAAMyJ,EAAOtF,EAAMR,GAC3C,GAAU,IAAN5D,EAAS,MAAO,GAEpB,MAAMuC,EAAYvC,EAAI,GAChBE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAgBd,OAdID,EAAgB,GAClBC,EAAMC,KAAKqD,EAASvD,IAGlBH,EAAY,GACdI,EAAMC,KAAK6D,EAAKlE,IAGA,IAAdA,EACFI,EAAMC,KAAKmJ,EAAMnH,IACRA,EAAY,GACrBjC,EAAMC,KAAKN,EAAKsC,IAGXjC,EAAME,KAAK,IACpB,CAMA,MASMI,GAAO,OAMP+I,GAAc,CAClB,CAAC,SAAU,SAAU,UACrB,CAAC,UAAW,WAAY,YACxB,CAAC,YAAa,YAAa,aAC3B,CAAC,UAAW,WAAY,YACxB,CAAC,YAAa,YAAa,aAC3B,CAAC,WAAY,YAAa,aAC1B,CAAC,aAAc,aAAc,cAC7B,CAAC,cAAe,eAAgB,gBAChC,CAAC,gBAAiB,gBAAiB,mBAO7BC,KAAMhD,GAAeiD,IAAKhD,IAzElC,SAA2BiD,EAAUC,EAASL,EAAOtF,EAAMR,GACzD,MAAMgG,EAAO,IAAIlJ,MAAM,KACjBmJ,EAAM,IAAInJ,MAAM,KAEtB,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiJ,EAAKjJ,GAAKZ,GAAaY,EAAGmJ,EAAUJ,EAAOtF,EAAMR,GACjDiG,EAAIlJ,GAAKZ,GAAaY,EAAGoJ,EAASL,EAAOtF,EAAMR,GAGjD,MAAO,CAAEgG,OAAMC,MACjB,CA+DmDG,CA/BjC,CAAC,GAAI,QAAS,MAAO,MAAO,SAAU,MAAO,OAAQ,QAAS,OAAQ,SACvE,CAAC,GAAI,QAAS,QAAS,MAAO,SAAU,MAAO,OAAQ,QAAS,OAAQ,SAE3E,CAAC,QAAS,YAAa,WAAY,WAAY,YAAa,WAAY,WAAY,aAAc,YAAa,cAChH,CAAC,GAAI,GAAI,WAAY,WAAY,YAAa,UAAW,WAAY,aAAc,YAAa,aAG5F,CAAC,GAAI,MAAO,UAAW,SAAU,YAAa,SAAU,QAAS,WAAY,UAAW,aA8BzG,SAASvH,GAAgBzC,EAAGc,EAAU,IACpC,OAAU,KAANd,EAAiBY,GAEjBZ,EAAI,OAC8B,aAAnBc,EAAQ4B,OAAwBmE,GAAeD,IAChDhJ,OAAOoC,IAM3B,SAAgCA,EAAGc,GACjC,MAAMoC,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EACF,GAAmB,IAAfD,EAEFjD,EAAMC,MADkC,aAAnBO,EAAQ4B,OAAwBmE,GAAeD,IAC5CpD,QACnB,CACL,MACMC,EAAYc,GAAUf,EADTmG,GAAYpG,EAAa,IAK5CjD,EAAMC,MAF4B,IAAfgD,EACesD,GAAeD,IACzBpD,GAAW,IAAMC,EAC3C,CAGFF,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CA7CSkD,CAAsB1D,EAAGc,EAClC,CCtGA,MAAMmJ,GAAQ,IAAIC,IAAI,CACpB,CAAC,8BAAwC,gBACzC,CAAC,2BAAoC,cACrC,CAAC,wBAAgC,aACjC,CAAC,qBAA4B,WAC7B,CAAC,kBAAwB,YACzB,CAAC,eAAoB,UACrB,CAAC,YAAgB,YACjB,CAAC,SAAY,UACb,CAAC,MAAO,QACR,CAAC,KAAM,QACP,CAAC,IAAK,aACN,CAAC,IAAK,YACN,CAAC,IAAK,UACN,CAAC,IAAK,UACN,CAAC,IAAK,SACN,CAAC,IAAK,WACN,CAAC,IAAK,WACN,CAAC,IAAK,gBACN,CAAC,IAAK,eACN,CAAC,IAAK,aACN,CAAC,IAAK,aACN,CAAC,IAAK,YACN,CAAC,IAAK,cACN,CAAC,IAAK,eACN,CAAC,IAAK,eACN,CAAC,IAAK,aACN,CAAC,IAAK,QACN,CAAC,IAAK,eACN,CAAC,IAAK,cACN,CAAC,IAAK,YACN,CAAC,IAAK,YACN,CAAC,IAAK,WACN,CAAC,IAAK,aACN,CAAC,IAAK,cACN,CAAC,IAAK,cACN,CAAC,IAAK,YACN,CAAC,IAAK,OACN,CAAC,GAAI,UACL,CAAC,GAAI,SACL,CAAC,GAAI,OACL,CAAC,GAAI,OACL,CAAC,GAAI,MACL,CAAC,GAAI,QACL,CAAC,GAAI,SACL,CAAC,GAAI,SACL,CAAC,GAAI,OACL,CAAC,GAAI,WAGDtJ,GAAO,QAQb,SAASuJ,GAAc3M,GACrB,OAAOyM,GAAMG,IAAI5M,EACnB,CAiCA,MAAMuH,GAAS,CACb,8BACA,2BACA,wBACA,qBACA,kBACA,eACA,YACA,UAmBF,SAASsF,GAAqBrK,EAAGsK,EAAW1J,IAI1C,MAFiB,iBAANZ,IAAgBA,EAAIhC,OAAOgC,IAE5B,KAANA,EACKsK,EAEQ,KAAbA,GAAyB,KAANtK,EACd,MAELA,EAAI,IACCmK,GAAanK,GAElBA,EAAI,KAvEV,SAAyBA,GACvB,GAAIiK,GAAMM,IAAIvK,GACZ,OAAOiK,GAAMG,IAAIpK,GAEnB,MACMwK,EAAQxK,EAAI,IAClB,OAAOmK,GAFMnK,EAAI,IAEU,KAAOqK,GAAoBG,EACxD,CAiEWC,CAAezK,GAEpBA,EAAI,MAjEV,SAA6BA,GAC3B,MAAM4D,EAAW5D,EAAI,KACrB,IAAI0H,EAAS,OAKb,OAJiB,KAAb9D,IACF8D,EAAS2C,GAAoBzG,EAAU,IAAM8D,GAGxCA,EADS2C,GAAoBrK,EAAI,KAAM,GAEhD,CA0DW0K,CAAmB1K,GAExBA,EAAI,SA1DV,SAA8BA,GAC5B,MAAM2E,EAAY3E,EAAI,MACtB,IAAI0H,EAAS,OACK,KAAd/C,IACF+C,EAAS2C,GAAoB1F,EAAW,IAAM+C,GAEhD,MAAMiD,EAAUN,GAAoBrK,EAAI,MAAO,IAE/C,OAAO0H,GADS1H,GAAK,OAAqB,KAAZ2K,EAAkB,GAAK,KAC5BA,CAC3B,CAkDWC,CAAoB5K,GApC/B,SAA8BA,GAE5B,IAAIV,EAAM,SACV,IAAK,MAAMuL,KAAS9F,GAClB,GAAI/E,GAAK6K,EAAO,CACdvL,EAAMuL,EACN,KACF,CAGF,MAAMnD,EAAS2C,GAAoBrK,EAAIV,EAAK,IACtCwL,EAAOT,GAAoBrK,EAAIV,EAAK,IACpCqL,EAAoB,KAATG,EAAe,GAAM,IAAMA,EAC5C,OAAOpD,EAASyC,GAAa7K,GAAOqL,CACtC,CAwBSI,CAAoB/K,EAC7B,CAEA,SAASyC,GAAgBzC,GACvB,OAAOqK,GAAoBrK,EAAGY,GAChC,CCpJA,MAAMjB,GAAO,CAAC,GAAI,OAAQ,MAAO,OAAQ,QAAS,OAAQ,OAAQ,QAAS,UAAW,YAChFC,GAAQ,CAAC,UAAW,UAAW,YAAa,aAAc,cAAe,aAAc,aAAc,cAAe,gBAAiB,kBACrIC,GAAO,CAAC,GAAI,GAAI,YAAa,aAAc,cAAe,aAAc,aAAc,cAAe,gBAAiB,kBAEtHmL,GAAe,QACfC,GAAgB,OAChBnL,GAAc,CAAC,OAAQ,SAAU,UAAW,aAAc,aAAc,aAAc,YAAa,WAAY,WAAY,YAUjI,SAASC,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMuC,EAAYvC,EAAI,GAChBE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAGVD,EAAgB,GAEhBC,EAAMC,KADc,IAAlBF,EACS,KAAO2K,GAEPrL,GAAKU,GAAiB,IAAM2K,IAK3C,MAAMhG,EAAWhF,EAAI,IAcrB,OAZiB,IAAbgF,GAGF1E,EAAMC,KADGyE,EAAW,GACTrF,GAAKqF,GACPA,EAAW,GACTpF,GAAMoF,EAAW,IACL,IAAdzC,EACE1C,GAAKK,GAELL,GAAKK,GAAa,IAAMP,GAAK4C,IAGnCjC,EAAME,KAAK,IACpB,CAEA,MAAMC,GAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,GAO7B,SAAS8B,GAAgBzC,GACvB,GAAU,KAANA,EAAU,MAtDH,MAwDX,GAAIA,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAGzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAE7B,IAAIkC,EAWJ,OATEA,EADgB,IAAdyC,EACO,KAAOsG,GAEPxK,GAASkE,GAAa,IAAMsG,GAGnCpH,EAAY,IACd3B,GAAU,IAAMzB,GAASoD,IAGpB3B,CACT,CAEA,OAGF,SAAgClC,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAET,IAAZ6C,GAEAlD,EAAMC,KADW,IAAfgD,EACS9C,GAAS+C,GACI,IAAfD,EACO,IAAZC,EACS,KAAOyH,GAEPxK,GAAS+C,GAAW,IAAMyH,GAK5BxK,GAAS+C,GAAW,IADb1D,GAAYyD,EAAa,IAK/CA,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CA/CSkD,CAAsB1D,EAC/B,CChFA,MAAML,GAAO,CAAC,GAAI,MAAO,MAAO,MAAO,UAAW,SAAU,MAAO,QAAS,OAAQ,QAC9EC,GAAQ,CAAC,QAAS,SAAU,SAAU,UAAW,cAAe,WAAY,SAAU,cAAe,WAAY,cACjHC,GAAO,CAAC,GAAI,GAAI,QAAS,SAAU,WAAY,YAAa,WAAY,WAAY,UAAW,WAC/FwB,GAAW,CAAC,GAAI,QAAS,WAAY,WAAY,eAAgB,cAAe,WAAY,aAAc,YAAa,aAEvHT,GAAO,OASPsK,GAAiB,CAAC,IAAK,IAAK,KAAM,QAAS,QAAS,OAAQ,OAAQ,MAAO,MAAO,OAUxF,SAASC,GAAoBjN,GAC3B,OAAOA,EACJoK,QAAQ,MAAO,KACfA,QAAQ,MAAO,KACfA,QAAQ,MAAO,KACfA,QAAQ,MAAO,KACfA,QAAQ,MAAO,IACpB,CAMA,SAAS8C,GAAelG,GACtB,OAAIA,EAAKzG,OAAS,GAAwB,QAAnByG,EAAKrG,OAAM,GACzBqG,EAAKrG,MAAM,GAAG,GAAM,MAEtBqG,CACT,CAMA,SAASmG,GAAkBrL,GACzB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAEhC,IAAIkC,EAAS,GAyBb,OAtBI0B,EAAW,IACb1B,EAASb,GAASuC,IAIP,IAATQ,GAAuB,IAATnE,IAEE,IAATmE,EAETlC,GAAUtC,GAAMK,GACPmE,GAAQ,GAEjBlC,GAAUrC,GAAKuE,GACXnE,EAAO,IACTiC,GAAUvC,GAAKM,KAERA,EAAO,IAEhBiC,GAAUvC,GAAKM,KAIVmL,GAAcD,GAAmBjJ,GAC1C,CAMA,SAASoJ,GAA0BtL,GACjC,GAAU,IAANA,EAAS,MAAO,GACpB,GAAU,IAANA,EAAS,MAAO,KAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAEhC,IAAIkC,EAAS,GAqBb,OAnBI0B,EAAW,IACb1B,EAASb,GAASuC,IAGP,IAATQ,GAAuB,IAATnE,IAEE,IAATmE,EACTlC,GAAUtC,GAAMK,GACPmE,GAAQ,GACjBlC,GAAUrC,GAAKuE,GACXnE,EAAO,IACTiC,GAAUvC,GAAKM,KAERA,EAAO,IAGhBiC,GAAUvC,GAAKM,KAGVmL,GAAcD,GAAmBjJ,GAC1C,CAIA,MAAMzB,GAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAK0K,GAAiB1K,GAIjC,MAAM4K,GAAiB,IAAI7K,MAAM,KACjC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxB4K,GAAe5K,GAAK2K,GAAyB3K,GAK/C,MAAM6K,GAAY,IAAI9K,MAAM,KAC5B8K,GAAU,GAAK,GACfA,GAAU,GA9HgB,QA+H1B,IAAK,IAAI7K,EAAI,EAAGA,EAAI,IAAMA,IACxB6K,GAAU7K,GAAKwK,GAAmB1K,GAASE,GA/Hd,QA0I/B,SAAS8K,GAAsBlI,GAC7B,GAAIA,EAAa,EAAG,MAAO,GAC3B,MAAMmI,EAAcvL,KAAKC,OAAOmD,EAAa,GAAK,GAE5CmE,EAASwD,GAAeQ,GAC9B,OAAKhE,EACEA,IAHUnE,EAAa,GAAK,GAAM,EAGd,UAAY,UADnB,EAEtB,CAMA,SAASoI,GAAoBpI,GAC3B,GAAIA,EAAa,EAAG,MAAO,GAC3B,MAAMmI,EAAcvL,KAAKC,OAAOmD,EAAa,GAAK,GAE5CmE,EAASwD,GAAeQ,GAC9B,OAAKhE,EACEA,IAHUnE,EAAa,GAAK,GAAM,EAGd,UAAY,UADnB,EAEtB,CAYA,SAASd,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAE7B,OAAkB,IAAd6D,EACK2H,GAAU7G,GAIZ6G,GAAU7G,GAAalE,GAASoD,EACzC,CAGA,OASF,SAAgC7D,GAC9B,MAAMM,EAAQ,GACd,IAAIwD,EAAY9D,EAGZ4L,EAAW,EACXC,EAAY,SAChB,KAAmB,MAAZA,GAAqB/H,GAC1B+H,GAAa,MACbD,IAIF,IAAK,IAAIrI,EAAaqI,EAAUrI,GAAc,EAAGA,IAAc,CAC7D,MAAMuI,EAAU,OAAS9N,OAAOuF,GAC1BC,EAAUM,EAAYgI,EAG5B,GAFAhI,GAAwBgI,EAER,KAAZtI,EAAgB,SAEpB,MAAMuI,EAASnO,OAAO4F,GAEtB,GAAID,GAAc,EAAG,CAEnB,MAAMyI,EAAeT,GAAeQ,GAC9BtI,EAAwB,KAAZD,EACdiI,GAAqBlI,GACrBoI,GAAmBpI,GACvBjD,EAAMC,KAAKyL,EAAe,IAAMvI,EAClC,MAEEnD,EAAMC,KAFkB,IAAfgD,EAEEiI,GAAUO,GAGVtL,GAASsL,GAExB,CAEA,OAUF,SAAiCzL,GAC/B,MAAM6C,EAAM7C,EAAM7B,OAClB,GAAY,IAAR0E,EAAW,MAAO,GACtB,GAAY,IAARA,EAAW,OAAO7C,EAAM,GAG5B,MAAM2L,EAAW3L,EAAM6C,EAAM,GAC7B,IAA8B,IAA1B8I,EAASlN,QAAQ,KAAa,CAEhC,IAAImD,EAAS5B,EAAM,GACnB,IAAK,IAAIK,EAAI,EAAGA,EAAIwC,EAAM,EAAGxC,IAC3BuB,GAAU,IAAM5B,EAAMK,GAExB,OAAOuB,EAAS,MAAQ+J,CAC1B,CAGA,IAAI/J,EAAS5B,EAAM,GACnB,IAAK,IAAIK,EAAI,EAAGA,EAAIwC,EAAKxC,IACvBuB,GAAU,IAAM5B,EAAMK,GAExB,OAAOuB,CACT,CAhCSgK,CAAuB5L,EAChC,CAhDSoD,CAAsB1D,EAC/B,CC9MA,MAAML,GAAO,CAAC,GAAI,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,KAIpDoF,GAAS,CACb,IACA,IACA,IACA,IACA,IACA,IACA,IACA,IACA,IACA,IACA,IACA,IACA,MACA,MACA,MACA,OACA,QAqBF,SAAShF,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAAO,GACjC2E,EAAYxE,KAAKC,MAAMJ,EAAI,KAEjC,IAAIkC,EAAS,GAkCb,OA/BIyC,EAAY,IAEZzC,GADgB,IAAdyC,EAvBS,IA0BDhF,GAAKgF,GA1BJ,KA+BXf,EAAW,IAEX1B,GADe,IAAb0B,EAjCQ,IAoCAjE,GAAKiE,GApCL,KAyCVQ,EAAO,IAEPlC,GADW,IAATkC,EA3CI,IA8CIzE,GAAKyE,GA9CT,KAmDNnE,EAAO,IACTiC,GAAUvC,GAAKM,IAGViC,CACT,CAIA,MAAMzB,GAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAOA,IACzBF,GAASE,GAAKZ,GAAaY,GC/F7B,MAAMC,GAAO,SAGP8E,GAAU,OAEV/B,GAAgB,CACpB,SAAU,OAAQ,OAAQ,OAAQ,SAAU,MAAO,MAAO,MAAO,OAAQ,UACzE,QAAS,WAAY,WAAY,UAAW,YAAa,UAAW,UAAW,UAAW,WAAY,cACtG,WAAY,cAAe,cAAe,cAAe,gBAAiB,aAAc,aAAc,aAAc,cAAe,iBACnI,UAAW,aAAc,aAAc,aAAc,eAAgB,YAAa,YAAa,YAAa,aAAc,gBAC1H,UAAW,aAAc,aAAc,aAAc,eAAgB,YAAa,YAAa,YAAa,aAAc,gBAC1H,SAAU,YAAa,YAAa,YAAa,cAAe,WAAY,WAAY,WAAY,YAAa,eACjH,UAAW,aAAc,aAAc,aAAc,eAAgB,YAAa,YAAa,YAAa,aAAc,gBAC1H,WAAY,cAAe,cAAe,cAAe,gBAAiB,aAAc,aAAc,aAAc,cAAe,iBACnI,UAAW,aAAc,aAAc,aAAc,eAAgB,YAAa,YAAa,YAAa,aAAc,gBAC1H,WAAY,cAAe,cAAe,cAAe,gBAAiB,aAAc,aAAc,aAAc,cAAe,kBAI/H7D,GAAc,CAAC,GAAI,QAAS,OAAQ,OAAQ,OAAQ,OAAQ,MAAO,OAAQ,OAsBjF,SAAS8B,GAAgB5B,GACvB,GAAU,IAANA,EAAS,MAAO,GACpB,GAAIA,EAAI,IAAK,OAAO2D,GAAc3D,GAElC,MAAM4D,EAAWzD,KAAK8B,MAAMjC,EAAI,KAC1B6D,EAAY7D,EAAI,IAEtB,OAAkB,IAAd6D,EACKF,GAAcC,GAAY,IAAM8B,GAElC/B,GAAcC,GAAY,IAAM8B,GAAU,IAAM/B,GAAcE,EACvE,CCrDA,MAAMlE,GAAO,CAAC,GAAI,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,KAYpDoF,GAAS,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,KAU9C,SAAShF,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAAO,GACjC2E,EAAYxE,KAAKC,MAAMJ,EAAI,KAEjC,IAAIkC,EAAS,GAkCb,OA/BIyC,EAAY,IAEZzC,GADgB,IAAdyC,EA9BS,IAiCDhF,GAAKgF,GAjCJ,KAsCXf,EAAW,IAEX1B,GADe,IAAb0B,EAxCQ,IA2CAjE,GAAKiE,GA3CL,KAgDVQ,EAAO,IAEPlC,GADW,IAATkC,EAlDI,IAqDIzE,GAAKyE,GArDT,KA0DNnE,EAAO,IACTiC,GAAUvC,GAAKM,IAGViC,CACT,CAGA,MAAMzB,GAAW,IAAIC,MAAM,KAE3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAOA,IACzBF,GAASE,GAAKZ,GAAaY,GAa7B,SAAS8B,GAAgBzC,GACvB,OAAU,KAANA,EA/EO,IAkFPA,EAAI,OACCS,GAAS7C,OAAOoC,IAc3B,SAAgCA,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EACF,GAAmB,IAAfD,EAEFjD,EAAMC,KAAK,CAAE2E,KAAMzE,GAAS+C,GAAUqC,SAAS,QAC1C,CAEL,MAAMpC,EAAYsB,GAAOxB,EAAa,GAGtB,IAAZC,GAGFlD,EAAMC,KAAK,CAAE2E,KAAMzE,GAAS+C,GAAUqC,SAAS,IAF/CvF,EAAMC,KAAK,CAAE2E,KAAMzB,EAAWoC,SAAS,GAK3C,CAGFtC,GACF,CAGA,OAWF,SAA0BjD,GACxB,GAAqB,IAAjBA,EAAM7B,OAAc,MA7Jb,IA8JX,GAAqB,IAAjB6B,EAAM7B,OAAc,OAAO6B,EAAM,GAAG4E,KAExC,MAAMhD,EAAS,GAEf,IAAK,IAAIvB,EAAI,EAAGA,EAAIL,EAAM7B,OAAQkC,IAAK,CACrC,MAAMyE,EAAO9E,EAAMK,GACbqF,EAAWrF,EAAI,EAAIL,EAAMK,EAAI,GAAK,KAGpCqF,GAAYA,EAASH,UAAYT,EAAKS,SACxC3D,EAAO3B,KAAK,KAGd2B,EAAO3B,KAAK6E,EAAKF,KACnB,CAEA,OAAOhD,EAAO1B,KAAK,GACrB,CA9BS2L,CAAgB7L,EACzB,CA3DSoD,CAAsB1D,EAC/B,CC7FA,MAAM0B,GAAY,CAAC,GAAI,SAAU,KAAM,OAAQ,SAAU,QAAS,OAAQ,UAAW,UAAW,UAC1FC,GAAW,CAAC,GAAI,QAAS,MAAO,OAAQ,WAAY,UAAW,SAAU,YAAa,YAAa,YAEnG/B,GAAQ,CAAC,SAAU,aAAc,UAAW,UAAW,cAAe,aAAc,YAAa,eAAgB,eAAgB,eACjIC,GAAO,CAAC,GAAI,GAAI,YAAa,aAAc,iBAAkB,gBAAiB,eAAgB,kBAAmB,kBAAmB,kBAGpIuM,GAAmB,SACnBC,GAAiB,SAEjBzL,GAAO,QAKP+I,GAAc,CAClB,CAAC,aAAc,cAAe,cAC9B,CAAC,YAAa,YAAa,YAC3B,CAAC,aAAc,aAAc,aAC7B,CAAC,aAAc,aAAc,aAC7B,CAAC,gBAAiB,gBAAiB,gBACnC,CAAC,gBAAiB,gBAAiB,gBACnC,CAAC,gBAAiB,gBAAiB,gBACnC,CAAC,eAAgB,eAAgB,eACjC,CAAC,cAAe,cAAe,eAUjC,SAAS5J,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAoBd,OAjBIsD,EAAW,IACbtD,EAAMC,KAAKmB,GAAUkC,IACrBtD,EAAMC,KAAkB,IAAbqD,EAAiBwI,GAAmBC,KAI7CjI,EAAO,GACT9D,EAAMC,KAAKV,GAAKuE,IAIL,IAATA,EACF9D,EAAMC,KAAKX,GAAMK,IACRA,EAAO,GAChBK,EAAMC,KAAKmB,GAAUzB,IAGhBK,EAAME,KAAK,IACpB,CAKA,SAAS8L,GAAsBtM,GAC7B,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAoBd,OAjBIsD,EAAW,IACbtD,EAAMC,KAAKmB,GAAUkC,IACrBtD,EAAMC,KAAkB,IAAbqD,EAAiBwI,GAAmBC,KAI7CjI,EAAO,GACT9D,EAAMC,KAAKV,GAAKuE,IAIL,IAATA,EACF9D,EAAMC,KAAKX,GAAMK,IACRA,EAAO,GAChBK,EAAMC,KAAKoB,GAAS1B,IAGfK,EAAME,KAAK,IACpB,CAGA,MAAMoG,GAAgB,IAAIlG,MAAM,KAC1BmG,GAAe,IAAInG,MAAM,KAE/B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiG,GAAcjG,GAAKZ,GAAaY,GAChCkG,GAAalG,GAAK2L,GAAqB3L,GAiBzC,SAAS4D,GAAWvE,EAAGwE,GACrB,GAAU,IAANxE,EAAS,OAAOwE,EAAM,GAE1B,MAAMC,EAAYzE,EAAI,GAChB0E,EAAgB1E,EAAI,IAG1B,OAAI0E,GAAiB,IAAMA,GAAiB,IAK1B,IAAdD,EAJKD,EAAM,GASG,IAAdC,EACKD,EAAM,GAIRA,EAAM,EACf,CAaA,SAAS/B,GAAgBzC,EAAGc,EAAU,IACpC,OAAU,KAANd,EAAiBY,GAGjBZ,EAAI,OAC8B,aAAnBc,EAAQ4B,OAAwBmE,GAAeD,IAChDhJ,OAAOoC,IAgB3B,SAAgCA,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMyB,EAAc2B,GAAcpD,GAElC,GAAmB,IAAfD,EAEFjD,EAAMC,KAAK0E,OACN,CAEL,MACMxB,EAAYc,GAAUf,EADTmG,GAAYpG,EAAa,IAE5CjD,EAAMC,KAAK0E,EAAc,IAAMxB,EACjC,CACF,CAEAF,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CAtDSkD,CAAsB1D,EAC/B,CCxKA,MAAM0B,GAAY,CAAC,GAAI,QAAS,OAAQ,OAAQ,QAAS,QAAS,OAAQ,UAAW,SAAU,UACzFC,GAAW,CAAC,GAAI,QAAS,QAAS,OAAQ,SAAU,SAAU,QAAS,WAAY,UAAW,WAE9F/B,GAAQ,CAAC,SAAU,cAAe,aAAc,cAAe,cAAe,cAAe,aAAc,gBAAiB,eAAgB,gBAC5IC,GAAO,CAAC,GAAI,GAAI,YAAa,aAAc,aAAc,aAAc,YAAa,eAAgB,cAAe,eAGnHuM,GAAmB,QACnBC,GAAiB,QACjBE,GAAmB,QAEnB3L,GAAO,QAKP+I,GAAc,CAClB,CAAC,YAAa,WAAY,YAC1B,CAAC,UAAW,UAAW,WACvB,CAAC,WAAY,WAAY,YACzB,CAAC,WAAY,WAAY,YACzB,CAAC,cAAe,cAAe,eAC/B,CAAC,cAAe,cAAe,eAC/B,CAAC,cAAe,cAAe,eAC/B,CAAC,aAAc,aAAc,cAC7B,CAAC,YAAa,YAAa,cAY7B,SAAS5J,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GA6Bd,OA1BIsD,EAAW,IACI,IAAbA,GAA2B,IAATQ,GAAcnE,EAAO,EAEzCK,EAAMC,KAAKgM,IACF3I,EAAW,GAEpBtD,EAAMC,KAAKmB,GAAUkC,IACrBtD,EAAMC,KAAK8L,KAGX/L,EAAMC,KAAK6L,KAKXhI,EAAO,GACT9D,EAAMC,KAAKV,GAAKuE,IAIL,IAATA,EACF9D,EAAMC,KAAKX,GAAMK,IACRA,EAAO,GAChBK,EAAMC,KAAKmB,GAAUzB,IAGhBK,EAAME,KAAK,IACpB,CAKA,SAAS8L,GAAsBtM,GAC7B,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GA0Bd,OAvBIsD,EAAW,IACI,IAAbA,GAA2B,IAATQ,GAAcnE,EAAO,EACzCK,EAAMC,KAAKgM,IACF3I,EAAW,GACpBtD,EAAMC,KAAKmB,GAAUkC,IACrBtD,EAAMC,KAAK8L,KAEX/L,EAAMC,KAAK6L,KAKXhI,EAAO,GACT9D,EAAMC,KAAKV,GAAKuE,IAIL,IAATA,EACF9D,EAAMC,KAAKX,GAAMK,IACRA,EAAO,GAChBK,EAAMC,KAAKoB,GAAS1B,IAGfK,EAAME,KAAK,IACpB,CAGA,MAAMoG,GAAgB,IAAIlG,MAAM,KAC1BmG,GAAe,IAAInG,MAAM,KAE/B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiG,GAAcjG,GAAKZ,GAAaY,GAChCkG,GAAalG,GAAK2L,GAAqB3L,GAgBzC,SAAS4D,GAAWvE,EAAGwE,GACrB,OAAU,IAANxE,EAAgBwE,EAAM,GAKR,GAHAxE,EAAI,IAGmB,IAFnBA,EAAI,IAGjBwE,EAAM,GAGRA,EAAM,EACf,CAaA,SAAS/B,GAAgBzC,EAAGc,EAAU,IACpC,OAAU,KAANd,EAAiBY,GAGjBZ,EAAI,OAC8B,aAAnBc,EAAQ4B,OAAwBmE,GAAeD,IAChDhJ,OAAOoC,IAe3B,SAAgCA,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMyB,EAAc2B,GAAcpD,GAElC,GAAmB,IAAfD,EAEFjD,EAAMC,KAAK0E,OACN,CAEL,MACMxB,EAAYc,GAAUf,EADTmG,GAAYpG,EAAa,IAK1CjD,EAAMC,KADQ,IAAZiD,EACSC,EAEAwB,EAAc,IAAMxB,EAEnC,CACF,CAEAF,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CA5DSkD,CAAsB1D,EAC/B,CC9KA,MAAMY,GAAO,QAGP8E,GAAU,OAEV/B,GAAgB,CACpB,QAAS,KAAM,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,KAAM,KAC/D,MAAO,OAAQ,OAAQ,OAAQ,OAAQ,QAAS,OAAQ,OAAQ,OAAQ,SACxE,MAAO,QAAS,QAAS,QAAS,QAAS,SAAU,SAAU,WAAY,WAAY,UACvF,MAAO,QAAS,SAAU,UAAW,QAAS,SAAU,SAAU,QAAS,QAAS,YACpF,QAAS,WAAY,UAAW,YAAa,aAAc,YAAa,YAAa,aAAc,aAAc,aACjH,SAAU,YAAa,SAAU,WAAY,SAAU,WAAY,UAAW,YAAa,YAAa,UACxG,MAAO,SAAU,SAAU,WAAY,SAAU,SAAU,UAAW,UAAW,UAAW,YAC5F,QAAS,WAAY,UAAW,cAAe,cAAe,cAAe,UAAW,aAAc,eAAgB,WACtH,OAAQ,YAAa,WAAY,aAAc,aAAc,aAAc,UAAW,YAAa,cAAe,YAClH,QAAS,YAAa,WAAY,aAAc,aAAc,aAAc,UAAW,YAAa,cAAe,aAI/G7D,GAAc,CAAC,GAAI,OAAQ,MAAO,OAAQ,OAAQ,OAAQ,SAAU,UAAW,QAsBrF,SAAS8B,GAAgB5B,GACvB,GAAU,IAANA,EAAS,MAAO,GACpB,GAAIA,EAAI,IAAK,OAAO2D,GAAc3D,GAElC,MAAM4D,EAAWzD,KAAK8B,MAAMjC,EAAI,KAC1B6D,EAAY7D,EAAI,IAEtB,OAAkB,IAAd6D,EACKF,GAAcC,GAAY,IAAM8B,GAElC/B,GAAcC,GAAY,IAAM8B,GAAU,IAAM/B,GAAcE,EACvE,CCrDA,MAAMlE,GAAO,CAAC,GAAI,OAAQ,MAAO,OAAQ,QAAS,OAAQ,OAAQ,QAAS,QAAS,YAC9EC,GAAQ,CAAC,UAAW,UAAW,YAAa,aAAc,cAAe,aAAc,aAAc,cAAe,cAAe,kBACnIC,GAAO,CAAC,GAAI,GAAI,YAAa,aAAc,cAAe,aAAc,aAAc,cAAe,cAAe,kBAEpHmL,GAAe,QACfC,GAAgB,OAChBnL,GAAc,CAAC,OAAQ,SAAU,WAEjCc,GAAO,QAQb,SAASb,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMuC,EAAYvC,EAAI,GAChBE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAGVD,EAAgB,GAEhBC,EAAMC,KADc,IAAlBF,EACS,KAAO2K,GAEPrL,GAAKU,GAAiB,IAAM2K,IAK3C,MAAMhG,EAAWhF,EAAI,IAcrB,OAZiB,IAAbgF,GAGF1E,EAAMC,KADGyE,EAAW,GACTrF,GAAKqF,GACPA,EAAW,GACTpF,GAAMoF,EAAW,IACL,IAAdzC,EACE1C,GAAKK,GAELL,GAAKK,GAAa,IAAMP,GAAK4C,IAGnCjC,EAAME,KAAK,IACpB,CAEA,MAAMC,GAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,GAO7B,SAAS8B,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAErB,GAAIZ,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAGzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAE7B,IAAIkC,EAWJ,OATEA,EADgB,IAAdyC,EACO,KAAOsG,GAEPxK,GAASkE,GAAa,IAAMsG,GAGnCpH,EAAY,IACd3B,GAAU,IAAMzB,GAASoD,IAGpB3B,CACT,CAEA,OAGF,SAAgClC,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EACF,GAAmB,IAAfD,EACFjD,EAAMC,KAAKE,GAAS+C,SACf,GAAmB,IAAfD,EAEPjD,EAAMC,KADQ,IAAZiD,EACS,KAAOyH,GAEPxK,GAAS+C,GAAW,IAAMyH,QAElC,CAEL,MAAMxH,EAAY3D,GAAYyD,EAAa,GAEzCjD,EAAMC,KADQ,IAAZiD,EACS,KAAOC,EAEPhD,GAAS+C,GAAW,IAAMC,EAEzC,CAGFF,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CAnDSkD,CAAsB1D,EAC/B,CCvFA,MAAML,GAAO,CAAC,GAAI,KAAM,KAAM,MAAO,OAAQ,MAAO,OAAQ,MAAO,OAAQ,MAErEC,GAAQ,CAAC,KAAM,SAAU,OAAQ,UAAW,UAAW,SAAU,UAAW,SAAU,QAAS,UAC/FC,GAAO,CAAC,GAAI,GAAI,OAAQ,SAAU,QAAS,QAAS,SAAU,QAAS,OAAQ,SAG/EiF,GAAW,QAEXlE,GAAO,OAKPmE,GAAS,CAAC,UAAW,WAAY,UAAW,WAAY,cAAe,cAAe,aAAc,aAU1G,SAAShF,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,CAAEkF,KAAM,GAAIgB,YAAY,GAE5C,MAAMjG,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GACd,IAAI4F,GAAa,EAGbtC,EAAW,IACbsC,GAAa,EACb5F,EAAMC,KAAKZ,GAAKiE,GAALjE,YAIb,MAAMqF,EAAWhF,EAAI,IAmBrB,OAjBiB,IAAbgF,GAIF1E,EAAMC,KAFGyE,EAAW,GAETrF,GAAKM,GACP+E,EAAW,GAETpF,GAAMK,GACC,IAATA,EAEEJ,GAAKuE,GAGLvE,GAAKuE,GAAQ,IAAMzE,GAAKM,IAIhB,IAAjBK,EAAM7B,OACD,CAAEyG,KAAM5E,EAAM,GAAK,OAASA,EAAM,GAAI4F,YAAY,GAEpD,CAAEhB,KAAM5E,EAAM,IAAM,GAAI4F,aACjC,CAGA,MAAMzF,GAAW,IAAIC,MAAM,KACrByF,GAAuB,IAAIzF,MAAM,KAEvC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IAAK,CAC7B,MAAMuB,EAASnC,GAAaY,GAC5BF,GAASE,GAAKuB,EAAOgD,KACrBiB,GAAqBxF,GAAKuB,EAAOgE,UACnC,CAYA,SAASzD,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAE7B,IAAIkC,EAASzB,GAASkE,GAAa,IAAMG,GAWzC,OATIjB,EAAY,IAGZ3B,GADEiE,GAAqBtC,GACb,KAAOpD,GAASoD,GAEhB,OAASpD,GAASoD,IAIzB3B,CACT,CAGA,OASF,SAAgClC,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMyB,EAAcxE,GAAS+C,GAK3BlD,EAAMC,KAFW,IAAfgD,EAES,CAAE2B,KAAMD,EAAaiB,WAJfC,GAAqB3C,GAIM/F,KAAM,SAC1B,IAAf8F,EAEE,CAAE2B,KAAMD,EAAc,IAAMH,GAAUoB,YAAY,EAAOzI,KAAM,YAI/D,CAAEyH,KAAMD,EAAc,IADfF,GAAOxB,EAAa,GACY2C,YAAY,EAAOzI,KAAM,WAE/E,CAEA8F,GACF,CAGA,OASF,SAA6BjD,GAC3B,GAAqB,IAAjBA,EAAM7B,OAAc,OAAOmC,GAC/B,GAAqB,IAAjBN,EAAM7B,OAAc,OAAO6B,EAAM,GAAG4E,KAExC,MAAMhD,EAAS,GAEf,IAAK,IAAIvB,EAAI,EAAGA,EAAIL,EAAM7B,OAAQkC,IAAK,CACrC,MAAMyE,EAAO9E,EAAMK,GACb0E,EAAW/E,EAAMK,EAAI,GAE3BuB,EAAO3B,KAAK6E,EAAKF,MAEbG,GAIEnD,EAAO3B,KAHO,aAAd6E,EAAK3H,KAEH4H,EAASa,WACC,KAEA,OAES,YAAdd,EAAK3H,KAEQ,UAAlB4H,EAAS5H,MAAqB4H,EAASa,WAG7B,IAFA,OAKF,IAGlB,CAEA,OAAOhE,EAAO1B,KAAK,GACrB,CA3CSgM,CAAmBlM,EAC5B,CAzDSoD,CAAsB1D,EAC/B,CC1GA,MAAML,GAAO,CAAC,GAAI,MAAO,OAAQ,OAAQ,OAAQ,OAAQ,MAAO,QAAS,OAAQ,SAC3EC,GAAQ,CAAC,OAAQ,MAAO,SAAU,UAAW,WAAY,WAAY,UAAW,YAAa,WAAY,aACzGC,GAAO,CAAC,GAAI,GAAI,UAAW,SAAU,UAAW,UAAW,SAAU,WAAY,UAAW,YAE5F6F,GAAU,UAGVX,GAAS,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,WAAY,WAAY,cAAe,eAgB9G,SAAShF,GAAcC,EAAGyM,GACxB,GAAU,IAANzM,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAC1BgF,EAAWhF,EAAI,IAErB,IAAIkC,EAAS,GAYb,GATI0B,EAAW,IAEX1B,EADe,IAAb0B,EACO8B,GAEA/F,GAAKiE,GAAY8B,IAKb,IAAbV,QAEG,GAAIA,EAAW,GAGlB9C,GADE0B,EAAW,GAAK6I,EACR,KAAO9M,GAAKqF,GAEZrF,GAAKqF,QAEZ,GAAIA,EAAW,GAGlB9C,GADE0B,EAAW,GAAK6I,GAAWzH,EAAW,GAC9B,KAAOpF,GAAMK,GAEbL,GAAMK,QAIlB,GAAa,IAATA,EACFiC,GAAUrC,GAAKuE,OACV,CAEL,MAAMsI,EAAW/M,GAAKM,GAChB0M,EAAYD,EAASpH,SAAS,KAAO,KAAO,KAClDpD,GAAUwK,EAAWC,EAAY9M,GAAKuE,EACxC,CAGF,OAAOlC,CACT,CAGA,MAAMzB,GAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,GAAG,GAIhC,MAAMiM,GAAoB,IAAIlM,MAAM,KACpC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiM,GAAkBjM,GAAKZ,GAAaY,GAAG,GAczC,SAAS8B,GAAgBzC,EAAGc,GAC1B,GAAU,KAANd,EAAU,MAzFH,MA2FX,MAAM6M,UAAEA,EAASC,mBAAEA,EAAkBC,iBAAEA,GAAqBjM,EACtDsC,EAAW0J,EAAqBF,GAAoBnM,GAGpDuM,EAAe9H,GACf2H,EACK3H,EAAKoD,QAAQ,WAAY,OAE3BpD,EAIT,GAAIlF,EAAI,MACN,OAAOgN,EAAY5J,EAASxF,OAAOoC,KAIrC,IAAK+M,GAAoB/M,GAAK,OAASA,EAAI,OAAQ,CACjD,MAAMiN,EAAOrP,OAAOoC,EAAI,MAClBkN,EAAMtP,OAAOoC,EAAI,MAGvB,GAAIiN,EAAO,IAAO,EAAG,CACnB,IAAI/K,EAASkB,EAAS6J,GAAQvH,GAC9B,GAAIwH,EAAM,EAAG,CACX,MAAMC,EAAU/J,EAAS8J,GAEvBhL,GADE4K,GAAsBI,EAAM,GACpB,OAASC,EAET,IAAMA,CAEpB,CACA,OAAOH,EAAY9K,EACrB,CACF,CAGA,GAAIlC,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAE7B,IAAIkC,EASJ,GANEA,EAFgB,IAAdyC,EAEOI,GAAO,GAGP3B,EAASuB,GAAaI,GAAO,GAGpClB,EAAY,EAAG,CACjB,MAAMuC,EAAgBhD,EAASS,GAE7B3B,GADE4K,GAAsBjJ,EAAY,GAC1B,OAASuC,EAET,IAAMA,CAEpB,CAEA,OAAO4G,EAAY9K,EACrB,CAGA,OAAO8K,EAWT,SAAgChN,EAAGc,GACjC,MAAMgM,mBAAEA,GAAuBhM,EACzBsM,EAAgBN,EAAqBF,GAAoBnM,GAIzDmE,EAAgB,GACtB,IAAIjC,EAAO3C,EACX,KAAO2C,EAAO,IACZiC,EAAcrE,KAAK3C,OAAO+E,EAAO,QACjCA,GAAc,MAIhB,IAAIT,EAAS,GACToE,GAAe,EAEnB,IAAK,IAAI3F,EAAIiE,EAAcnG,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAClD,MAAM6C,EAAUoB,EAAcjE,GAC9B,GAAgB,IAAZ6C,EAEJ,GAAU,IAAN7C,EAAS,CAEX,MAAMuE,EAAOkI,EAAc5J,GACvBtB,EAEAA,GADEoE,GAAgBwG,GAAsBtJ,EAAU,GACxC,OAAS0B,EAET,IAAMA,EAGlBhD,EAASgD,EAEXoB,GAAe,CACjB,MAAO,GAAU,IAAN3F,EAELuB,IAAQA,GAAU,KAEpBA,GADc,IAAZsB,EACQuB,GAAO,GAEPqI,EAAc5J,GAAWuB,GAAO,GAE5CuB,GAAe,MACV,CAEL,MAAM7C,EAAYsB,GAAOpE,EAAI,GACzBuB,IAAQA,GAAU,KAEpBA,GADc,IAAZsB,EACQ,OAASC,EAET2J,EAAc5J,GAAW,IAAMC,EAE3C6C,GAAe,CACjB,CACF,CAEA,OAAOpE,CACT,CApEqBwB,CAAsB1D,EAAGc,GAC9C,CC5KA,MAAMF,GAAO,QAKP+C,GAAgB,CACpB,QAAS,MAAO,KAAM,OAAQ,MAAO,MAAO,KAAM,MAAO,MAAO,MAChE,MAAO,SAAU,QAAS,QAAS,QAAS,SAAU,QAAS,SAAU,SAAU,OACnF,MAAO,OAAQ,MAAO,MAAO,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QACrE,MAAO,QAAS,OAAQ,OAAQ,QAAS,QAAS,OAAQ,QAAS,QAAS,SAC5E,OAAQ,SAAU,QAAS,UAAW,SAAU,SAAU,QAAS,UAAW,SAAU,QACxF,QAAS,SAAU,QAAS,SAAU,UAAW,UAAW,QAAS,UAAW,SAAU,QAC1F,MAAO,QAAS,OAAQ,OAAQ,QAAS,QAAS,QAAS,OAAQ,OAAQ,SAC3E,OAAQ,SAAU,QAAS,QAAS,SAAU,UAAW,SAAU,QAAS,QAAS,QACrF,OAAQ,SAAU,QAAS,SAAU,UAAW,QAAS,QAAS,SAAU,QAAS,QACrF,OAAQ,UAAW,SAAU,UAAW,WAAY,WAAY,UAAW,UAAW,UAAW,YAI7F7D,GAAc,CAAC,GAAI,QAAS,MAAO,OAAQ,MAAO,MAAO,MAAO,MAAO,QAS7E,SAAS8B,GAAgB5B,GACvB,GAAU,IAANA,EAAS,MAAO,GACpB,GAAIA,EAAI,IAAK,OAAO2D,GAAc3D,GAElC,MAAM4D,EAAWzD,KAAK8B,MAAMjC,EAAI,KAC1B6D,EAAY7D,EAAI,IAEtB,OAAkB,IAAd6D,EACKF,GAAcC,GAAdD,MAEFA,GAAcC,GAAdD,OAAgDA,GAAcE,EACvE,CASA,SAASpB,GAAgBzC,GACvB,OAAU,KAANA,EAAiBY,GAGjBZ,EAAI,MACC4B,GAAehE,OAAOoC,IAGxB0D,GAAsB1D,EAAG,EAClC,CAUA,SAAS0D,GAAuB1D,EAAG6K,GACjC,GAAU,KAAN7K,EAAU,MAAO,GAGrB,MAAM8L,EAAoB,IAAVjB,EAAc,MAAQ,KAChCrH,EAAU5F,OAAOoC,EAAI8L,GACrBhB,EAAO9K,EAAI8L,EAGjB,IAAI5J,EAAS,GAkBb,OAjBI4I,EAAO,KACT5I,EAASwB,GAAsBoH,EAAMD,EAAQ,IAI3CrH,EAAU,IACRtB,IAAQA,GAAU,KAIpBA,GAFY,IAAV2I,EAEQjJ,GAAe4B,GAGfG,GAAcH,GAAW,IAAM1D,GAAY+K,IAIlD3I,CACT,CCzFA,MAAMR,GAAY,CAAC,GAAI,QAAS,MAAO,OAAQ,SAAU,OAAQ,QAAS,SAAU,QAAS,YACvFC,GAAW,CAAC,GAAI,QAAS,OAAQ,OAAQ,SAAU,OAAQ,QAAS,SAAU,QAAS,YAEvF/B,GAAQ,CAAC,WAAY,aAAc,YAAa,aAAc,cAAe,aAAc,aAAc,eAAgB,cAAe,kBAExIC,GAAO,CAAC,GAAI,GAAI,cAAe,cAAe,eAAgB,eAAgB,gBAAiB,iBAAkB,gBAAiB,oBAGlIwB,GAAW,CAAC,GAAI,MAAO,WAAY,UAAW,YAAa,UAAW,WAAY,YAAa,WAAY,eAG3G8C,GAAe,CACnB,EAAG,CAAC,SAAU,UAAW,WACzB,EAAG,CAAC,SAAU,UAAW,YACzB,EAAG,CAAC,UAAW,WAAY,aAC3B,EAAG,CAAC,SAAU,UAAW,YACzB,EAAG,CAAC,UAAW,WAAY,aAC3B,EAAG,CAAC,UAAW,WAAY,aAC3B,EAAG,CAAC,WAAY,YAAa,cAC7B,EAAG,CAAC,aAAc,cAAe,gBACjC,EAAG,CAAC,aAAc,eAAgB,iBAClC,GAAI,CAAC,aAAc,cAAe,iBAG9BvD,GAAO,OAab,SAASb,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAoBd,OAjBIsD,EAAW,GACbtD,EAAMC,KAAKc,GAASuC,IAIT,IAATQ,EAEF9D,EAAMC,KAAKX,GAAMK,KAEbmE,GAAQ,GACV9D,EAAMC,KAAKV,GAAKuE,IAEdnE,EAAO,GACTK,EAAMC,KAAKmB,GAAUzB,KAIlBK,EAAME,KAAK,IACpB,CAOA,SAAS8L,GAAsBtM,GAC7B,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAmBd,OAhBIsD,EAAW,GACbtD,EAAMC,KAAKc,GAASuC,IAIT,IAATQ,EACF9D,EAAMC,KAAKX,GAAMK,KAEbmE,GAAQ,GACV9D,EAAMC,KAAKV,GAAKuE,IAEdnE,EAAO,GACTK,EAAMC,KAAKoB,GAAS1B,KAIjBK,EAAME,KAAK,IACpB,CAGA,MAAMoG,GAAgB,IAAIlG,MAAM,KAC1BmG,GAAe,IAAInG,MAAM,KAC/B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiG,GAAcjG,GAAKZ,GAAaY,GAChCkG,GAAalG,GAAK2L,GAAqB3L,GAezC,SAAS4D,GAAWvE,EAAGwE,GACrB,GAAU,KAANxE,EACF,OAAOwE,EAAM,GAGf,MAAMC,EAAYzE,EAAI,IAChB0E,EAAgB1E,EAAI,KAI1B,OAAIyE,GAAa,IAAMA,GAAa,KAAOC,EAAgB,KAAOA,EAAgB,KACzEF,EAAM,GAGRA,EAAM,EACf,CAaA,SAAS/B,GAAgBzC,EAAGc,EAAU,IACpC,GAAU,KAANd,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MAEN,OADoC,aAAnBc,EAAQ4B,OAAwBmE,GAAeD,IAChDhJ,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAEvByD,EAAYc,GAAUvG,OAAO2G,GAAYR,GAAa,IAE5D,IAAIjC,EAYJ,OATEA,EAFgB,IAAdyC,EAEOlB,EAEAmD,GAAcjC,GAAa,IAAMlB,EAGxCI,EAAY,IACd3B,GAAU,IAAM0E,GAAc/C,IAGzB3B,CACT,CAGA,OAWF,SAAgClC,GAG9B,MAAM4E,EAAgB,GACtB,IAAIjC,EAAO3C,EACX,KAAO2C,EAAO,IACZiC,EAAcrE,KAAKoC,EAAO,OAC1BA,GAAc,MAIhB,IAAIT,EAAS,GAEb,IAAK,IAAIvB,EAAIiE,EAAcnG,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAClD,MAAM6C,EAAUoB,EAAcjE,GAC9B,GAAgB,KAAZ6C,EAAgB,SAEpB,MAAMyB,EAAc2B,GAAchJ,OAAO4F,IAIzC,GAFItB,IAAQA,GAAU,KAEZ,IAANvB,EAEFuB,GAAU+C,MACL,CAEL,MAAMT,EAAQL,GAAaxD,GAC3B,GAAI6D,EAAO,CACT,MAAMf,EAAYc,GAAUf,EAASgB,GAInCtC,GAFc,KAAZsB,EAEQC,EAEAwB,EAAc,IAAMxB,CAElC,CACF,CACF,CAEA,OAAOvB,CACT,CApDSwB,CAAsB1D,EAC/B,CCvLA,MAAML,GAAO,CAAC,GAAI,KAAM,OAAQ,OAAQ,SAAU,QAAS,OAAQ,OAAQ,OAAQ,QAC7EC,GAAQ,CAAC,MAAO,OAAQ,OAAQ,QAAS,UAAW,SAAU,YAAa,YAAa,UAAW,YACnGC,GAAO,CAAC,GAAI,GAAI,QAAS,SAAU,WAAY,YAAa,WAAY,UAAW,UAAW,WAG9FwB,GAAW,CAAC,GAAI,QAAS,WAAY,YAAa,eAAgB,aAAc,aAAc,aAAc,aAAc,cAE1HyD,GAAW,MACXlE,GAAO,OAYb,SAASb,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,CAAEkF,KAAM,GAAImI,gBAAgB,GAGhD,GAAU,MAANrN,EAAW,MAAO,CAAEkF,KAAM,MAAOmI,gBAAgB,GAErD,MAAMpN,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAyBd,OAtBIsD,EAAW,GACbtD,EAAMC,KAAKc,GAASuC,IAIT,IAATQ,EAEF9D,EAAMC,KAAKX,GAAMK,IACRmE,GAAQ,EAGf9D,EAAMC,KAFJN,EAAO,EAEEJ,GAAKuE,GAAQ,MAAQzE,GAAKM,GAE1BJ,GAAKuE,IAETnE,EAAO,GAChBK,EAAMC,KAAKZ,GAAKM,IAMX,CAAEiF,KAFI5E,EAAME,KAAK,OAET6M,eAAgBzJ,EAAW,GAAc,IAATQ,GAAuB,IAATnE,EAC/D,CAGA,MAAMQ,GAAW,IAAIC,MAAM,KACrB4M,GAAgC,IAAI5M,MAAM,KAEhD,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IAAK,CAC7B,MAAMuB,EAASnC,GAAaY,GAC5BF,GAASE,GAAKuB,EAAOgD,KAErBoI,GAA8B3M,GAAKA,GAAK,GAC1C,CAQA,MAAM4M,GAAuB,CAC3B,GACAzI,GACA,SACA,cACA,SACA,cACA,UACA,eACA,cAGI0I,GAAqB,CACzB,GACA1I,GACA,UACA,cACA,UACA,cACA,WACA,eACA,eAaF,SAASrC,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAE7B,IAAIkC,EAiBJ,OAdEA,EAFgB,IAAdyC,EAEOG,GAEArE,GAASkE,GAAa,IAAMG,GAGnCjB,EAAY,IAKZ3B,GAHGoL,GAA8BzJ,GAGvB,IAAMpD,GAASoD,GAFf,MAAQpD,GAASoD,IAMxB3B,CACT,CAGA,OAUF,SAAgClC,GAG9B,MAAMoD,EAAW,GACjB,IAAIT,EAAO3C,EACX,KAAO2C,EAAO,IACZS,EAAS7C,KAAK3C,OAAO+E,EAAO,QAC5BA,GAAc,MAIhB,IAAI0D,EAAkB,EACtB,IAAK,IAAI1F,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IACnC,GAAoB,IAAhByC,EAASzC,GAAU,CACrB0F,EAAkB1F,EAClB,KACF,CAIF,IAAIuB,EAAS,GACToE,GAAe,EAEnB,IAAK,IAAI3F,EAAIyC,EAAS3E,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAC7C,MAAM6C,EAAUJ,EAASzC,GACzB,GAAgB,IAAZ6C,EAAe,SAEnB,MAAMyB,EAAcxE,GAAS+C,GAU7B,GANItB,GAHmBvB,IAAM0F,GAGEC,IAAiBgH,GAA8B9J,KAC5EtB,GAAU,MAGRA,IAAQA,GAAU,KAEZ,IAANvB,EAEFuB,GAAU+C,EACVqB,GAAe,OACV,GAAU,IAAN3F,EAGPuB,GADc,IAAZsB,EACQsB,GAEAG,EAAc,IAAMH,GAEhCwB,GAAe,MACV,CAEL,MAAM7C,EAAwB,IAAZD,EAAgB+J,GAAqB5M,GAAK6M,GAAmB7M,GAE7EuB,GADc,IAAZsB,EACQ,MAAQC,EAERwB,EAAc,IAAMxB,EAEhC6C,GAAe,CACjB,CACF,CAEA,OAAOpE,CACT,CAxESwB,CAAsB1D,EAC/B,CCpJA,MAAM0B,GAAY,CAAC,GAAI,MAAO,MAAO,OAAQ,QAAS,QAAS,OAAQ,QAAS,MAAO,QACjFC,GAAW,CAAC,GAAI,MAAO,OAAQ,OAAQ,QAAS,QAAS,OAAQ,QAAS,MAAO,QAEjF/B,GAAQ,CAAC,OAAQ,aAAc,eAAgB,eAAgB,cAAe,gBAAiB,cAAe,gBAAiB,cAAe,gBAC9I4I,GAAa,CAAC,OAAQ,aAAc,cAAe,eAAgB,cAAe,gBAAiB,cAAe,gBAAiB,cAAe,gBAElJiF,GAAW,CAAC,GAAI,GAAI,WAAY,WAAY,YAAa,YAAa,UAAW,YAAa,UAAW,YAEzGpM,GAAW,CAAC,GAAI,SAAU,YAAa,YAAa,aAAc,aAAc,YAAa,aAAc,WAAY,aAEvHT,GAAO,OAUP8M,GAAa,CACjB,CAAEC,SAAU,MAAOC,OAAQ,MAAOC,QAAS,IAAKlH,UAAU,EAAMmH,SAAS,GACzE,CAAEH,SAAU,SAAUC,OAAQ,WAAYC,QAAS,KAAMlH,UAAU,EAAOmH,SAAS,GACnF,CAAEH,SAAU,UAAWC,OAAQ,WAAYC,QAAS,KAAMlH,UAAU,EAAOmH,SAAS,GACpF,CAAEH,SAAU,UAAWC,OAAQ,YAAaC,QAAS,KAAMlH,UAAU,EAAOmH,SAAS,GACrF,CAAEH,SAAU,aAAcC,OAAQ,eAAgBC,QAAS,KAAMlH,UAAU,EAAOmH,SAAS,GAC3F,CAAEH,SAAU,aAAcC,OAAQ,eAAgBC,QAAS,KAAMlH,UAAU,EAAOmH,SAAS,GAC3F,CAAEH,SAAU,YAAaC,OAAQ,cAAeC,QAAS,KAAMlH,UAAU,EAAOmH,SAAS,GACzF,CAAEH,SAAU,YAAaC,OAAQ,cAAeC,QAAS,KAAMlH,UAAU,EAAOmH,SAAS,GACzF,CAAEH,SAAU,WAAYC,OAAQ,aAAcC,QAAS,KAAMlH,UAAU,EAAOmH,SAAS,IAUzF,SAASC,GAAe/N,EAAG2G,GAAW,EAAOqH,GAAiB,GAC5D,GAAU,IAANhO,EAAS,MAAO,GACpB,GAAIA,EAAI,GACN,OAAO2G,EAAWhF,GAAS3B,GAAK0B,GAAU1B,GAE5C,GAAIA,EAAI,GACN,OAAOgO,EAAiBxF,GAAWxI,EAAI,IAAMJ,GAAMI,EAAI,IAEzD,MAAMgI,EAAI7H,KAAKC,MAAMJ,EAAI,IACnBiO,EAAIjO,EAAI,GACd,OAAU,IAANiO,EACKR,GAASzF,GAGXyF,GAASzF,GAAK,QADJrB,EAAWhF,GAASsM,GAAKvM,GAAUuM,GAEtD,CAKA,SAASC,GAAgBlO,EAAG2G,GAAW,EAAOqH,GAAiB,GAC7D,GAAU,IAANhO,EAAS,MAAO,GACpB,GAAIA,EAAI,IAAK,OAAO+N,GAAc/N,EAAG2G,EAAUqH,GAE/C,MAAMG,EAAIhO,KAAKC,MAAMJ,EAAI,KACnBoO,EAAIpO,EAAI,IACRqO,EAAchN,GAAS8M,GAE7B,OAAU,IAANC,EAAgBC,EACbA,EAAc,IAAMN,GAAcK,EAAGzH,EAAUqH,EACxD,CAMA,SAASM,GAAkB9K,EAASD,GAClC,MAAMgL,EAAOb,GAAWnK,EAAa,GACrC,OAAKgL,EAEW,IAAZ/K,EACK+K,EAAKV,QAAU,IAAMU,EAAKZ,SAInB,KAAZnK,GAAkB+K,EAAKT,QAClB,sBAAwBS,EAAKX,OAIxBM,GAAe1K,GAAS,IAGtB+K,EAAKT,SAAWtK,GAAW,GACf,OAAS,KAEV+K,EAAKX,OAlBdM,GAAe1K,GAAS,EAmB5C,CAaA,SAASf,GAAgBzC,EAAGc,EAAU,IACpC,GAAU,KAANd,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MAAO,CACb,MAAM2G,EAA8B,aAAnB7F,EAAQ4B,OACzB,OAAOwL,GAAetQ,OAAOoC,GAAI2G,EACnC,CAEA,OAWF,SAAgC3G,EAAGc,GAGjC,MAAM8D,EAAgB,GACtB,IAAIjC,EAAO3C,EACX,KAAO2C,EAAO,IACZiC,EAAcrE,KAAK3C,OAAO+E,EAAO,QACjCA,GAAc,MAIhB,IAAIT,EAAS,GAEb,IAAK,IAAIvB,EAAIiE,EAAcnG,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAClD,MAAM6C,EAAUoB,EAAcjE,GAC9B,GAAgB,IAAZ6C,EAAe,SAEnB,IAAIwI,EAIFA,EAHQ,IAANrL,EAGauN,GAAe1K,EADM,aAAnB1C,EAAQ4B,QAIV4L,GAAiB9K,EAAS7C,GAGvCuB,GAAU8J,EACZ9J,GAAU,IAAM8J,EACPA,IACT9J,EAAS8J,EAEb,CAEA,OAAO9J,CACT,CA9CSwB,CAAsB1D,EAAGc,EAClC,CCvHA,SAASyD,GAAWvE,EAAGwE,GACrB,MAAMiF,EAAmB,iBAANzJ,EAAiBpC,OAAOoC,GAAKA,EAC1CyE,EAAYgF,EAAM,GAClB/E,EAAgB+E,EAAM,IAE5B,OAAI/E,GAAiB,IAAMA,GAAiB,GACnCF,EAAM,GAGG,IAAdC,EAAwBD,EAAM,GAC9BC,GAAa,GAAKA,GAAa,EAAUD,EAAM,GAC5CA,EAAM,EACf,CAcA,SAASzE,GAAcC,EAAGC,EAAMyJ,EAAOtF,EAAMR,GAC3C,GAAU,IAAN5D,EAAS,MAAO,GAEpB,MAAMuC,EAAYvC,EAAI,GAChBE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAgBd,OAdID,EAAgB,GAClBC,EAAMC,KAAKqD,EAASvD,IAGlBH,EAAY,GACdI,EAAMC,KAAK6D,EAAKlE,IAGA,IAAdA,EACFI,EAAMC,KAAKmJ,EAAMnH,IACRA,EAAY,GACrBjC,EAAMC,KAAKN,EAAKsC,IAGXjC,EAAME,KAAK,IACpB,CAMA,MASMI,GAAO,OAMP+I,GAAc,CAClB,CAAC,SAAU,SAAU,SACrB,CAAC,UAAW,WAAY,aACxB,CAAC,WAAY,YAAa,cAC1B,CAAC,WAAY,YAAa,cAC1B,CAAC,cAAe,eAAgB,iBAChC,CAAC,cAAe,eAAgB,iBAChC,CAAC,cAAe,eAAgB,iBAChC,CAAC,aAAc,cAAe,gBAC9B,CAAC,YAAa,aAAc,eAC5B,CAAC,YAAa,aAAc,iBAOtBC,KAAMhD,GAAeiD,IAAKhD,IA1ElC,SAA2BiD,EAAUC,EAASL,EAAOtF,EAAMR,GACzD,MAAMgG,EAAO,IAAIlJ,MAAM,KACjBmJ,EAAM,IAAInJ,MAAM,KAEtB,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiJ,EAAKjJ,GAAKZ,GAAaY,EAAGmJ,EAAUJ,EAAOtF,EAAMR,GACjDiG,EAAIlJ,GAAKZ,GAAaY,EAAGoJ,EAASL,EAAOtF,EAAMR,GAGjD,MAAO,CAAEgG,OAAMC,MACjB,CAgEmDG,CAhCjC,CAAC,GAAI,OAAQ,MAAO,MAAO,SAAU,OAAQ,QAAS,OAAQ,SAAU,UACzE,CAAC,GAAI,OAAQ,MAAO,MAAO,SAAU,OAAQ,QAAS,OAAQ,SAAU,UAE3E,CAAC,SAAU,cAAe,aAAc,aAAc,eAAgB,aAAc,cAAe,aAAc,eAAgB,gBAClI,CAAC,GAAI,GAAI,WAAY,WAAY,QAAS,YAAa,aAAc,YAAa,cAAe,aAG7F,CAAC,GAAI,MAAO,SAAU,SAAU,YAAa,UAAW,WAAY,UAAW,YAAa,cA+B7G,SAASvH,GAAgBzC,EAAGc,EAAU,IACpC,GAAU,KAANd,EAAU,OAAOY,GAErB,MAAM+F,EAA8B,aAAnB7F,EAAQ4B,OAEzB,GAAI1C,EAAI,MAEN,OADiB2G,EAAWE,GAAeD,IAC3BhJ,OAAOoC,IAGzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAM7B,IAAIkC,EAHkB2E,GAAalC,GAGN,IAFXJ,GAAUI,EAAWgF,GAAY,IASnD,OALI9F,EAAY,IAEd3B,GAAU,KADOyE,EAAWE,GAAeD,IAClB/C,IAGpB3B,CACT,CAEA,OAGF,SAAgClC,EAAGc,GACjC,MAAM6F,EAA8B,aAAnB7F,EAAQ4B,OACnBQ,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EACF,GAAmB,IAAfD,EAEFjD,EAAMC,MADeoG,EAAWE,GAAeD,IACvBpD,QACnB,CACL,MACMC,EAAYc,GAAUf,EADTmG,GAAYpG,EAAa,IAK5CjD,EAAMC,MAF4B,IAAfgD,EACesD,GAAeD,IACzBpD,GAAW,IAAMC,EAC3C,CAGFF,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CA9CSkD,CAAsB1D,EAAGc,EAClC,CC1HA,SAASyD,GAAWvE,EAAGwE,GACrB,MAAMiF,EAAmB,iBAANzJ,EAAiBpC,OAAOoC,GAAKA,EAC1CyE,EAAYgF,EAAM,GAClB/E,EAAgB+E,EAAM,IAE5B,OAAI/E,GAAiB,IAAMA,GAAiB,GACnCF,EAAM,GAGG,IAAdC,EAAwBD,EAAM,GAC9BC,GAAa,GAAKA,GAAa,EAAUD,EAAM,GAC5CA,EAAM,EACf,CAcA,SAASzE,GAAcC,EAAGC,EAAMyJ,EAAOtF,EAAMR,GAC3C,GAAU,IAAN5D,EAAS,MAAO,GAEpB,MAAMuC,EAAYvC,EAAI,GAChBE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAgBd,OAdID,EAAgB,GAClBC,EAAMC,KAAKqD,EAASvD,IAGlBH,EAAY,GACdI,EAAMC,KAAK6D,EAAKlE,IAGA,IAAdA,EACFI,EAAMC,KAAKmJ,EAAMnH,IACRA,EAAY,GACrBjC,EAAMC,KAAKN,EAAKsC,IAGXjC,EAAME,KAAK,IACpB,CAMA,MAMMI,GAAO,OAKP+I,GAAc,CAClB,CAAC,SAAU,SAAU,UACrB,CAAC,SAAU,UAAW,WACtB,CAAC,YAAa,YAAa,aAC3B,CAAC,SAAU,UAAW,WACtB,CAAC,YAAa,YAAa,aAC3B,CAAC,UAAW,WAAY,YACxB,CAAC,aAAc,aAAc,cAC7B,CAAC,aAAc,cAAe,eAC9B,CAAC,gBAAiB,gBAAiB,mBAO7BC,KAAMhD,GAAeiD,IAAKhD,IArElC,SAA2BiD,EAAUC,EAASL,EAAOtF,EAAMR,GACzD,MAAMgG,EAAO,IAAIlJ,MAAM,KACjBmJ,EAAM,IAAInJ,MAAM,KAEtB,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiJ,EAAKjJ,GAAKZ,GAAaY,EAAGmJ,EAAUJ,EAAOtF,EAAMR,GACjDiG,EAAIlJ,GAAKZ,GAAaY,EAAGoJ,EAASL,EAAOtF,EAAMR,GAGjD,MAAO,CAAEgG,OAAMC,MACjB,CA2DmDG,CA3BjC,CAAC,GAAI,QAAS,MAAO,MAAO,SAAU,MAAO,OAAQ,QAAS,OAAQ,SACvE,CAAC,GAAI,QAAS,MAAO,MAAO,SAAU,MAAO,OAAQ,QAAS,OAAQ,SACzE,CAAC,QAAS,YAAa,WAAY,WAAY,YAAa,WAAY,WAAY,aAAc,YAAa,cAChH,CAAC,GAAI,GAAI,WAAY,WAAY,YAAa,UAAW,WAAY,aAAc,YAAa,aAC5F,CAAC,GAAI,MAAO,SAAU,SAAU,YAAa,SAAU,QAAS,WAAY,UAAW,YA6BxG,SAASvH,GAAgBzC,EAAGc,EAAU,IACpC,OAAU,KAANd,EAAiBY,GAEjBZ,EAAI,OAC8B,aAAnBc,EAAQ4B,OAAwBmE,GAAeD,IAChDhJ,OAAOoC,IAM3B,SAAgCA,EAAGc,GACjC,MAAMoC,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EACF,GAAmB,IAAfD,EAEFjD,EAAMC,MADkC,aAAnBO,EAAQ4B,OAAwBmE,GAAeD,IAC5CpD,QACnB,CACL,MACMC,EAAYc,GAAUf,EADTmG,GAAYpG,EAAa,IAK5CjD,EAAMC,MAF4B,IAAfgD,EACesD,GAAeD,IACzBpD,GAAW,IAAMC,EAC3C,CAGFF,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CA7CSkD,CAAsB1D,EAAGc,EAClC,CClGA,SAASyD,GAAWvE,EAAGwE,GACrB,MAAMiF,EAAmB,iBAANzJ,EAAiBpC,OAAOoC,GAAKA,EAC1CyE,EAAYgF,EAAM,GAClB/E,EAAgB+E,EAAM,IAE5B,OAAI/E,GAAiB,IAAMA,GAAiB,GACnCF,EAAM,GAGG,IAAdC,EAAwBD,EAAM,GAC9BC,GAAa,GAAKA,GAAa,EAAUD,EAAM,GAC5CA,EAAM,EACf,CAcA,SAASzE,GAAcC,EAAGC,EAAMyJ,EAAOtF,EAAMR,GAC3C,GAAU,IAAN5D,EAAS,MAAO,GAEpB,MAAMuC,EAAYvC,EAAI,GAChBE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAgBd,OAdID,EAAgB,GAClBC,EAAMC,KAAKqD,EAASvD,IAGlBH,EAAY,GACdI,EAAMC,KAAK6D,EAAKlE,IAGA,IAAdA,EACFI,EAAMC,KAAKmJ,EAAMnH,IACRA,EAAY,GACrBjC,EAAMC,KAAKN,EAAKsC,IAGXjC,EAAME,KAAK,IACpB,CAMA,MAMMI,GAAO,OAKP+I,GAAc,CAClB,CAAC,UAAW,UAAW,WACvB,CAAC,SAAU,UAAW,WACtB,CAAC,YAAa,YAAa,aAC3B,CAAC,SAAU,UAAW,WACtB,CAAC,YAAa,YAAa,aAC3B,CAAC,UAAW,WAAY,YACxB,CAAC,aAAc,aAAc,cAC7B,CAAC,aAAc,cAAe,eAC9B,CAAC,gBAAiB,gBAAiB,mBAO7BC,KAAMhD,GAAeiD,IAAKhD,IArElC,SAA2BiD,EAAUC,EAASL,EAAOtF,EAAMR,GACzD,MAAMgG,EAAO,IAAIlJ,MAAM,KACjBmJ,EAAM,IAAInJ,MAAM,KAEtB,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiJ,EAAKjJ,GAAKZ,GAAaY,EAAGmJ,EAAUJ,EAAOtF,EAAMR,GACjDiG,EAAIlJ,GAAKZ,GAAaY,EAAGoJ,EAASL,EAAOtF,EAAMR,GAGjD,MAAO,CAAEgG,OAAMC,MACjB,CA2DmDG,CA3BjC,CAAC,GAAI,QAAS,MAAO,MAAO,SAAU,MAAO,OAAQ,QAAS,OAAQ,SACvE,CAAC,GAAI,QAAS,MAAO,MAAO,SAAU,MAAO,OAAQ,QAAS,OAAQ,SACzE,CAAC,QAAS,YAAa,WAAY,WAAY,YAAa,WAAY,WAAY,aAAc,YAAa,cAChH,CAAC,GAAI,GAAI,WAAY,WAAY,YAAa,UAAW,WAAY,aAAc,YAAa,aAC5F,CAAC,GAAI,MAAO,SAAU,SAAU,YAAa,SAAU,QAAS,WAAY,UAAW,aA6BxG,SAASvH,GAAgBzC,EAAGc,EAAU,IACpC,OAAU,KAANd,EAAiBY,GAEjBZ,EAAI,OAC8B,aAAnBc,EAAQ4B,OAAwBmE,GAAeD,IAChDhJ,OAAOoC,IAM3B,SAAgCA,EAAGc,GACjC,MAAMoC,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EACF,GAAmB,IAAfD,EAEFjD,EAAMC,MADkC,aAAnBO,EAAQ4B,OAAwBmE,GAAeD,IAC5CpD,QACnB,CACL,MACMC,EAAYc,GAAUf,EADTmG,GAAYpG,EAAa,IAK5CjD,EAAMC,MAF4B,IAAfgD,EACesD,GAAeD,IACzBpD,GAAW,IAAMC,EAC3C,CAGFF,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CA7CSkD,CAAsB1D,EAAGc,EAClC,CCnGA,MAAMnB,GAAO,CAAC,GAAI,MAAO,MAAO,MAAO,OAAQ,MAAO,MAAO,MAAO,OAAQ,OAEtEC,GAAQ,CAAC,MAAO,OAAQ,OAAQ,UAAW,UAAW,SAAU,SAAU,UAAW,QAAS,UAC9FC,GAAO,CAAC,GAAI,GAAI,QAAS,UAAW,SAAU,SAAU,SAAU,UAAW,QAAS,UAEtF6F,GAAU,SAEV9E,GAAO,OAKPmE,GAAS,CAAC,QAAS,SAAU,UAAW,SAAU,UAAW,UAAW,WAAY,cAU1F,SAAShF,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,CAAEkF,KAAM,GAAIgB,YAAY,EAAOsI,aAAa,GAEhE,MAAMvO,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GACd,IAAI4F,GAAa,EAGbtC,EAAW,IACbsC,GAAa,EAEX5F,EAAMC,KADS,IAAbqD,EACS8B,GAEA/F,GAAKiE,GAAY,IAAM8B,KAKtC,IAAI+I,EAAe,GAcnB,OAba,IAATrK,EACFqK,EAAe7O,GAAMK,GACZmE,GAAQ,EAEfqK,EADExO,EAAO,EACMJ,GAAKuE,GAAQ,IAAMzE,GAAKM,GAExBJ,GAAKuE,GAEbnE,EAAO,IAChBwO,EAAe9O,GAAKM,IAIlBiG,GAAcuI,EACT,CAAEvJ,KAAM5E,EAAM,GAAK,QAAUmO,EAAcvI,YAAY,EAAMsI,aAAa,GACxEtI,EACF,CAAEhB,KAAM5E,EAAM,GAAI4F,YAAY,EAAMsI,aAAa,GAEjD,CAAEtJ,KAAMuJ,EAAcvI,YAAY,EAAOsI,aAAa,EAEjE,CAGA,MAAM/N,GAAW,IAAIC,MAAM,KACrByF,GAAuB,IAAIzF,MAAM,KACjCgO,GAAyB,IAAIhO,MAAM,KAEzC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IAAK,CAC7B,MAAMuB,EAASnC,GAAaY,GAC5BF,GAASE,GAAKuB,EAAOgD,KACrBiB,GAAqBxF,GAAKuB,EAAOgE,WACjCwI,GAAuB/N,GAAKuB,EAAOsM,WACrC,CAYA,SAAS/L,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAG7B,IAAIkC,EAAuB,IAAdyC,EAAkBI,GAAO,GAAKtE,GAASkE,GAAa,IAAMI,GAAO,GAE9E,GAAIlB,EAAY,EAAG,CACjB,MAAMuC,EAAgB3F,GAASoD,GAG7B3B,GADEwM,GAAuB7K,GACf,QAAUuC,EAEV,IAAMA,CAEpB,CAEA,OAAOlE,CACT,CAGA,OASF,SAAgClC,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EACF,GAAmB,IAAfD,EAEFjD,EAAMC,KAAK,CACT2E,KAAMzE,GAAS+C,GACf0C,WAAYC,GAAqB3C,GACjCqC,SAAS,QAEN,CAEL,MAAMpC,EAAYsB,GAAOxB,EAAa,GAEtC,IAAI0B,EAIAA,EAHY,IAAZzB,EAEiB,IAAfD,EACY,GAEA,KAGF9C,GAAS+C,GAGrByB,GACF3E,EAAMC,KAAK,CAAE2E,KAAMD,EAAaiB,YAAY,EAAOL,SAAS,IAE9DvF,EAAMC,KAAK,CAAE2E,KAAMzB,EAAWyC,YAAY,EAAOL,SAAS,GAC5D,CAGFtC,GACF,CAGA,OAUF,SAA2BjD,GACzB,GAAqB,IAAjBA,EAAM7B,OAAc,OAAOmC,GAC/B,GAAqB,IAAjBN,EAAM7B,OAAc,OAAO6B,EAAM,GAAG4E,KAExC,MAAMhD,EAAS,GAEf,IAAK,IAAIvB,EAAI,EAAGA,EAAIL,EAAM7B,OAAQkC,IAAK,CACrC,MAAMyE,EAAO9E,EAAMK,GACJA,IAAML,EAAM7B,OAAS,GAEtB6B,EAAM7B,OAAS,GACV6B,EAAMK,EAAI,GAEdkF,UAAYT,EAAKc,YAC5BhE,EAAO3B,KAAK,OAIhB2B,EAAO3B,KAAK6E,EAAKF,KACnB,CAEA,OAAOhD,EAAO1B,KAAK,IACrB,CAhCSmO,CAAiBrO,EAC1B,CAvESoD,CAAsB1D,EAC/B,CCzHA,MAAML,GAAO,CAAC,SAAU,OAAQ,QAAS,OAAQ,MAAO,OAAQ,OAAQ,OAAQ,OAAQ,QAClFE,GAAO,CAAE,GAAI,OAAQ,GAAI,WAAY,GAAI,aAAc,GAAI,WAAY,GAAI,UAAW,GAAI,SAAU,GAAI,SAAU,GAAI,YAAa,GAAI,UAEvIC,GAAc,CAAC,GAAI,OAAQ,UAAW,UAAW,WAAY,cAAe,eAE5Ec,GAAO,SAQb,SAASgO,GAAe5O,GACtB,GAAIA,EAAI,GAAI,OAAOL,GAAKK,GACxB,GAAU,KAANA,EAAU,OAAOH,GAAK,IAC1B,GAAIG,EAAI,GAEN,OAAOH,GAAK,IAAM,OAASF,GAAKK,EAAI,IAEtC,MAAMoE,EAA4B,GAArBjE,KAAK8B,MAAMjC,EAAI,IACtBC,EAAOD,EAAI,GACjB,OAAa,IAATC,EAAmBJ,GAAKuE,GACrBvE,GAAKuE,GAAQ,OAASzE,GAAKM,EACpC,CAEA,SAAS4O,GAAgB7O,GACvB,GAAIA,EAAI,IAAK,OAAO4O,GAAc5O,GAClC,GAAU,MAANA,EAAW,MAAO,WACtB,MAAM4D,EAAWzD,KAAK8B,MAAMjC,EAAI,KAC1B8K,EAAO9K,EAAI,IACXM,EAAQ,GAYd,OATAA,EAAMC,KAAK,OAASZ,GAAKiE,IACrBkH,EAAO,GAEPxK,EAAMC,KADJuK,EAAO,GACE,MAAQnL,GAAKmL,GAEb8D,GAAc9D,IAItBxK,EAAME,KAAK,IACpB,CAYA,SAASiC,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,MAAMwC,EAdR,SAA0BpD,GACxB,MAAMoD,EAAW,GACjB,IAAIT,EAAO3C,EACX,KAAO2C,EAAO,IACZS,EAAS7C,KAAK3C,OAAO+E,EAAO,QAC5BA,GAAc,MAEhB,OAAOS,CACT,CAMmB0L,CAAgB9O,GAC3BM,EAAQ,GAGd,IAAK,IAAIiD,EAAaH,EAAS3E,OAAS,EAAG8E,GAAc,EAAGA,IAAc,CACxE,MAAMwL,EAAM3L,EAASG,GACrB,GAAY,IAARwL,EAEJ,GAAmB,IAAfxL,EAGAjD,EAAMC,KADJwO,EAAM,IAAMzO,EAAM7B,OAAS,EAClB,MAAQkB,GAAKoP,GACP,MAARA,GAAezO,EAAM7B,OAAS,EAE5B,MAEAoQ,GAAeE,QAEvB,CAEL,MAAMC,EAAgB,IAARD,EAAa,OAASF,GAAeE,GACnDzO,EAAMC,KAAKT,GAAYyD,GAAc,IAAMyL,EAC7C,CACF,CAEA,OAAO1O,EAAME,KAAK,KAAKhC,MACzB,CCnFA,MAAMoC,GAAO,YAIP+C,GAAgB,CACpB,YAAa,QAAS,SAAU,SAAU,SAAU,QAAS,MAAO,MAAO,QAAS,SACpF,QAAS,YAAa,aAAc,YAAa,YAAa,YAAa,UAAW,UAAW,YAAa,aAC9G,SAAU,eAAgB,gBAAiB,iBAAkB,iBAAkB,eAAgB,aAAc,aAAc,eAAgB,gBAC3I,UAAW,gBAAiB,iBAAkB,kBAAmB,kBAAmB,gBAAiB,cAAe,cAAe,gBAAiB,iBACpJ,UAAW,gBAAiB,iBAAkB,kBAAmB,kBAAmB,gBAAiB,cAAe,cAAe,gBAAiB,iBACpJ,SAAU,eAAgB,gBAAiB,iBAAkB,iBAAkB,eAAgB,aAAc,aAAc,eAAgB,gBAC3I,SAAU,eAAgB,gBAAiB,iBAAkB,iBAAkB,eAAgB,aAAc,aAAc,eAAgB,gBAC3I,SAAU,eAAgB,gBAAiB,iBAAkB,iBAAkB,eAAgB,aAAc,aAAc,eAAgB,gBAC3I,SAAU,eAAgB,gBAAiB,iBAAkB,iBAAkB,eAAgB,aAAc,aAAc,eAAgB,gBAC3I,WAAY,mBAAoB,oBAAqB,oBAAqB,oBAAqB,mBAAoB,iBAAkB,iBAAkB,mBAAoB,qBAIvKtC,GAAW,CAAC,GAAI,OAAQ,UAAW,WAAY,SAAU,QAAS,UAAW,UAAW,UAAW,eAGnG4N,GAAqB,CAAC,GAAI,SAAU,YAAa,aAAc,WAAY,UAAW,YAAa,YAAa,YAAa,iBAG7HtP,GAAO,CAAC,QAAS,SAAU,SAAU,SAAU,QAAS,MAAO,MAAO,QAAS,UAG/EG,GAAc,CAAC,GAAI,SAAU,SAAU,OAAQ,OAAQ,QAAS,OAAQ,OAAQ,SA+BtF,SAASoP,GAAsBlP,GAC7B,GAAU,IAANA,EAAS,MAAO,GACpB,GAAIA,EAAI,IAAK,OAAO2D,GAAc3D,GAElC,MAAM4D,EAAWzD,KAAK8B,MAAMjC,EAAI,KAC1B6D,EAAY7D,EAAI,IAEtB,OAAkB,IAAd6D,EACKxC,GAASuC,GAIXqL,GAAmBrL,GAAY,IAAMD,GAAcE,EAC5D,CCxEA,MAAMjD,GAAO,SAIP+C,GAAgB,CACpB,SAAU,OAAQ,QAAS,OAAQ,SAAU,MAAO,MAAO,MAAO,UAAW,WAC7E,MAAO,UAAW,WAAY,SAAU,YAAa,UAAW,SAAU,UAAW,cAAe,aACpG,OAAQ,cAAe,aAAc,YAAa,cAAe,WAAY,WAAY,WAAY,eAAgB,gBACrH,SAAU,cAAe,eAAgB,cAAe,gBAAiB,aAAc,aAAc,aAAc,iBAAkB,kBACrI,OAAQ,YAAa,aAAc,YAAa,cAAe,WAAY,WAAY,WAAY,eAAgB,gBACnH,OAAQ,YAAa,aAAc,YAAa,cAAe,WAAY,WAAY,WAAY,eAAgB,gBACnH,OAAQ,YAAa,aAAc,YAAa,cAAe,WAAY,WAAY,WAAY,eAAgB,gBACnH,SAAU,cAAe,eAAgB,cAAe,gBAAiB,aAAc,aAAc,aAAc,iBAAkB,kBACrI,OAAQ,YAAa,aAAc,YAAa,cAAe,WAAY,WAAY,WAAY,eAAgB,gBACnH,QAAS,aAAc,cAAe,aAAc,eAAgB,YAAa,YAAa,YAAa,gBAAiB,kBAOxHtC,GAAW,CAAC,GAAI,MAAO,cAAe,aAAc,eAAgB,YAAa,YAAa,YAAa,gBAAiB,kBAG5H1B,GAAO,CAAC,OAAQ,QAAS,OAAQ,SAAU,MAAO,MAAO,MAAO,UAAW,YAG3EG,GAAc,CAAC,GAAI,SAAU,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QASnF,SAASoP,GAAsBlP,GAC7B,GAAU,IAANA,EAAS,MAAO,GACpB,GAAIA,EAAI,IAAK,OAAO2D,GAAc3D,GAElC,MAAM4D,EAAWzD,KAAK8B,MAAMjC,EAAI,KAC1B6D,EAAY7D,EAAI,IAEtB,OAAkB,IAAd6D,EACKxC,GAASuC,GAEXvC,GAASuC,GAAY,IAAMD,GAAcE,EAClD,CASA,SAASpB,GAAgBzC,GACvB,OAAU,KAANA,EAAiBY,GAGjBZ,EAAI,MACCkP,GAAqBtR,OAAOoC,IAG9B0D,GAAsB1D,EAAG,EAClC,CAUA,SAAS0D,GAAuB1D,EAAG6K,GACjC,GAAU,KAAN7K,EAAU,MAAO,GAGrB,MAAM8L,EAAoB,IAAVjB,EAAc,MAAQ,KAChCrH,EAAU5F,OAAOoC,EAAI8L,GACrBhB,EAAO9K,EAAI8L,EAGjB,IAAI5J,EAAS,GAmBb,OAlBI4I,EAAO,KACT5I,EAASwB,GAAsBoH,EAAMD,EAAQ,IAI3CrH,EAAU,IACRtB,IAAQA,GAAU,KAIpBA,GAFY,IAAV2I,EAEQqE,GAAqB1L,IAGC,IAAZA,EAAiB,KAAOG,GAAcH,IACnC,IAAM1D,GAAY+K,IAItC3I,CACT,CCxGA,MAAMvC,GAAO,CAAC,QAAS,MAAO,MAAO,MAAO,MAAO,KAAM,OAAQ,MAAO,QAElEiB,GAAO,QAQb,SAASuO,GAAqBnP,GAC5B,GAAU,IAANA,EAAS,MAAO,GAEpB,IAAIxC,EAAQwC,EACZ,MAAMM,EAAQ,GAER8O,EAAmBjP,KAAK8B,MAAMzE,EAAQ,KAC5CA,GAAS,IACT,MAAM6R,EAAelP,KAAK8B,MAAMzE,EAAQ,KACxCA,GAAS,IACT,MAAMmH,EAAYxE,KAAK8B,MAAMzE,EAAQ,KACrCA,GAAS,IACT,MAAMoG,EAAWzD,KAAK8B,MAAMzE,EAAQ,KACpCA,GAAS,IACT,MAAM4G,EAAOjE,KAAK8B,MAAMzE,EAAQ,IAC1ByC,EAAOzC,EAAQ,GAyCrB,OAvCI4R,EAAmB,GACrB9O,EAAMC,KAAKZ,GAAKyP,EAAmB,GAAK,OAGtCC,EAAe,GAEf/O,EAAMC,KADa,IAAjB8O,EACS,aAEA1P,GAAK0P,EAAe,GAAK,SAIpC1K,EAAY,GACdrE,EAAMC,KAAKZ,GAAKgF,EAAY,GAAK,OAG/Bf,EAAW,GACbtD,EAAMC,KAAKZ,GAAKiE,EAAW,GAAK,QAG9BQ,EAAO,GAEP9D,EAAMC,KADK,IAAT6D,EACS,MACO,IAATA,EACE,SAEAzE,GAAKyE,EAAO,GAAK,OAI5BnE,EAAO,GAGPK,EAAMC,KADK,IAATN,IAAemE,EAAO,GADRgL,EAAmB,GAAKC,EAAe,GAAK1K,EAAY,GAAKf,EAAW,GAAKQ,EAAO,GAEzF,OAEAzE,GAAKM,EAAO,IAIpBK,EAAME,KAAK,GACpB,CCpEA,MAAMb,GAAO,CAAC,GAAI,MAAO,MAAO,KAAM,OAAQ,MAAO,OAAQ,OAAQ,QAAS,SAExEC,GAAQ,CAAC,KAAM,SAAU,SAAU,QAAS,UAAW,SAAU,UAAW,UAAW,WAAY,YACnGC,GAAO,CAAC,GAAI,GAAI,QAAS,OAAQ,OAAQ,OAAQ,SAAU,SAAU,SAAU,UAG/EiF,GAAW,MAEXlE,GAAO,QAKPmE,GAAS,CAAC,SAAU,SAAU,UAAW,YAAa,aAU5D,SAAShF,GAAcC,EAAGsP,EAAY,KACpC,GAAU,IAANtP,EAAS,MAAO,GAEpB,MAAMC,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IAAM,GAC5B4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAE1BM,EAAQ,GAGVsD,EAAW,GAEXtD,EAAMC,KADS,IAAbqD,EA7BQ,MAgCCjE,GAAKiE,GAAY0L,EAhClB,OAqCd,MAAMtK,EAAWhF,EAAI,IAcrB,OAZiB,IAAbgF,GAGF1E,EAAMC,KADGyE,EAAW,GACTrF,GAAKM,GACP+E,EAAW,GACTpF,GAAMK,GAAMqI,QAAQ,IAAKgH,GAClB,IAATrP,EACEJ,GAAKuE,GAELvE,GAAKuE,GAAQkL,EAAY3P,GAAKM,IAGpCK,EAAME,KAAK8O,EACpB,CAGA,MAAM7O,GAAW,IAAIC,MAAM,KACrB6O,GAAoB,IAAI7O,MAAM,KAEpC,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,EAAG,KAC9B4O,GAAkB5O,GAAKZ,GAAaY,EAAG,IAczC,SAAS8B,GAAgBzC,EAAGc,EAAU,IACpC,GAAU,KAANd,EAAU,OAAOY,GAErB,MAAM4O,EAAM1O,EAAQ2O,WAAa,GAAK,IAChCrM,EAAWtC,EAAQ2O,WAAaF,GAAoB9O,GAG1D,GAAIT,EAAI,MACN,OAAOoD,EAASxF,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAG7B,IAAIkC,EAWJ,OATEA,EADgB,IAAdyC,EACOG,GAEA1B,EAASuB,GAAa6K,EAAM1K,GAGnCjB,EAAY,IACd3B,GAAUsN,EAAMpM,EAASS,IAGpB3B,CACT,CAGA,OAUF,SAAgClC,EAAGc,GACjC,MAAM0O,EAAM1O,EAAQ2O,WAAa,GAAK,IAChCC,EAAc5O,EAAQ2O,WAAaF,GAAoB9O,GAEvDyC,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMyB,EAAcyK,EAAYlM,GAI9BlD,EAAMC,KAFW,IAAfgD,EAES0B,EACa,IAAf1B,EAEO,IAAZC,EACSsB,GAEAG,EAAcuK,EAAM1K,GAKtBG,EAAcuK,EADPzK,GAAOxB,EAAa,GAG1C,CAEAA,GACF,CAEA,OAAOjD,EAAME,KAAKgP,EACpB,CA/DS9L,CAAsB1D,EAAGc,EAClC,CC/GA,SAASyD,GAAWvE,EAAGwE,GACrB,MAAMiF,EAAmB,iBAANzJ,EAAiBpC,OAAOoC,GAAKA,EAC1CyE,EAAYgF,EAAM,GAClB/E,EAAgB+E,EAAM,IAE5B,OAAI/E,GAAiB,IAAMA,GAAiB,GACnCF,EAAM,GAGG,IAAdC,EAAwBD,EAAM,GAC9BC,GAAa,GAAKA,GAAa,EAAUD,EAAM,GAC5CA,EAAM,EACf,CAcA,SAASzE,GAAcC,EAAGC,EAAMyJ,EAAOtF,EAAMR,GAC3C,GAAU,IAAN5D,EAAS,MAAO,GAEpB,MAAMuC,EAAYvC,EAAI,GAChBE,EAAYC,KAAKC,MAAMJ,EAAI,IAAM,GACjCK,EAAgBF,KAAKC,MAAMJ,EAAI,KAE/BM,EAAQ,GAgBd,OAdID,EAAgB,GAClBC,EAAMC,KAAKqD,EAASvD,IAGlBH,EAAY,GACdI,EAAMC,KAAK6D,EAAKlE,IAGA,IAAdA,EACFI,EAAMC,KAAKmJ,EAAMnH,IACRA,EAAY,GACrBjC,EAAMC,KAAKN,EAAKsC,IAGXjC,EAAME,KAAK,IACpB,CAMA,MASMI,GAAO,OAMP+I,GAAc,CAClB,CAAC,SAAU,SAAU,SACrB,CAAC,UAAW,WAAY,aACxB,CAAC,UAAW,WAAY,aACxB,CAAC,WAAY,YAAa,cAC1B,CAAC,cAAe,eAAgB,iBAChC,CAAC,cAAe,eAAgB,iBAChC,CAAC,cAAe,eAAgB,iBAChC,CAAC,aAAc,cAAe,gBAC9B,CAAC,YAAa,aAAc,iBAOtBC,KAAMhD,GAAeiD,IAAKhD,IAzElC,SAA2BiD,EAAUC,EAASL,EAAOtF,EAAMR,GACzD,MAAMgG,EAAO,IAAIlJ,MAAM,KACjBmJ,EAAM,IAAInJ,MAAM,KAEtB,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBiJ,EAAKjJ,GAAKZ,GAAaY,EAAGmJ,EAAUJ,EAAOtF,EAAMR,GACjDiG,EAAIlJ,GAAKZ,GAAaY,EAAGoJ,EAASL,EAAOtF,EAAMR,GAGjD,MAAO,CAAEgG,OAAMC,MACjB,CA+DmDG,CA/BjC,CAAC,GAAI,OAAQ,MAAO,MAAO,SAAU,QAAU,QAAS,MAAO,QAAS,WACzE,CAAC,GAAI,OAAQ,MAAO,MAAO,SAAU,QAAU,QAAS,MAAO,QAAS,WAE3E,CAAC,SAAU,aAAc,aAAc,aAAc,eAAgB,cAAgB,cAAe,aAAc,eAAgB,iBACnI,CAAC,GAAI,GAAI,WAAY,WAAY,QAAS,YAAc,YAAa,WAAY,aAAc,cAG3F,CAAC,GAAI,MAAO,SAAU,SAAU,YAAa,UAAY,UAAW,SAAU,WAAY,cA8B3G,SAASvH,GAAgBzC,EAAGc,EAAU,IACpC,OAAU,KAANd,EAAiBY,GAEjBZ,EAAI,OAC8B,aAAnBc,EAAQ4B,OAAwBmE,GAAeD,IAChDhJ,OAAOoC,IAM3B,SAAgCA,EAAGc,GACjC,MAAMoC,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EACF,GAAmB,IAAfD,EAEFjD,EAAMC,MADkC,aAAnBO,EAAQ4B,OAAwBmE,GAAeD,IAC5CpD,QACnB,CACL,MACMC,EAAYc,GAAUf,EADTmG,GAAYpG,EAAa,IAK5CjD,EAAMC,MAF4B,IAAfgD,EACesD,GAAeD,IACzBpD,GAAW,IAAMC,EAC3C,CAGFF,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CA7CSkD,CAAsB1D,EAAGc,EAClC,CCvGA,MAKM6C,GAAgB,CACpB,MAAO,MAAO,KAAM,MAAO,MAAO,OAAQ,KAAM,MAAO,MAAO,KAC9D,KAAM,QAAS,OAAQ,OAAQ,OAAQ,QAAS,OAAQ,OAAQ,SAAU,OAC1E,MAAO,OAAQ,QAAS,QAAS,QAAS,OAAQ,QAAS,SAAU,UAAW,QAChF,MAAO,QAAS,OAAQ,SAAU,SAAU,SAAU,QAAS,SAAU,QAAS,UAClF,QAAS,UAAW,SAAU,WAAY,SAAU,WAAY,SAAU,WAAY,UAAW,QACjG,OAAQ,QAAS,OAAQ,OAAQ,MAAO,OAAQ,OAAQ,QAAS,SAAU,QAC3E,OAAQ,QAAS,QAAS,QAAS,SAAU,SAAU,UAAW,QAAS,QAAS,QACpF,MAAO,QAAS,OAAQ,OAAQ,QAAS,QAAS,QAAS,OAAQ,SAAU,QAC7E,MAAO,SAAU,QAAS,SAAU,SAAU,QAAS,SAAU,QAAS,SAAU,QACpF,MAAO,SAAU,QAAS,SAAU,UAAW,SAAU,UAAW,SAAU,UAAW,UAIrF7D,GAAc,CAAC,GAAI,OAAQ,OAAQ,OAAQ,MAAO,OAAQ,MAAO,MAAO,QAS9E,SAAS8B,GAAgB5B,GACvB,GAAU,IAANA,EAAS,MAAO,GACpB,GAAIA,EAAI,IAAK,OAAO2D,GAAc3D,GAElC,MAAM4D,EAAWzD,KAAK8B,MAAMjC,EAAI,KAC1B6D,EAAY7D,EAAI,IAEtB,OAAkB,IAAd6D,EACKF,GAAcC,GAAdD,MAEFA,GAAcC,GAAdD,OAAgDA,GAAcE,EACvE,CASA,SAASpB,GAAgBzC,GACvB,OAAU,KAANA,EAjDO,MAoDPA,EAAI,MACC4B,GAAehE,OAAOoC,IAGxB0D,GAAsB1D,EAAG,EAClC,CAUA,SAAS0D,GAAuB1D,EAAG6K,GACjC,GAAU,KAAN7K,EAAU,MAAO,GAGrB,MAAM8L,EAAoB,IAAVjB,EAAc,MAAQ,KAChCrH,EAAU5F,OAAOoC,EAAI8L,GACrBhB,EAAO9K,EAAI8L,EAGjB,IAAI5J,EAAS,GAkBb,OAjBI4I,EAAO,KACT5I,EAASwB,GAAsBoH,EAAMD,EAAQ,IAI3CrH,EAAU,IACRtB,IAAQA,GAAU,KAIpBA,GAFY,IAAV2I,EAEQjJ,GAAe4B,GAGfG,GAAcH,GAAW,IAAM1D,GAAY+K,IAIlD3I,CACT,CC3FA,MAAMvC,GAAO,CAAC,QAAS,MAAO,MAAO,KAAM,MAAO,MAAO,MAAO,MAAO,MAAO,QAGxEoF,GAAS,CACb,GAAI,QAAS,QAAS,KAAM,WAAY,gBACxC,cAAe,aAAc,aAAc,YAC3C,YAAa,YAAa,cAAe,eACzC,eAAgB,oBAAqB,eACrC,kBAAmB,gBAAiB,iBAAkB,gBAIlDnE,GAAO,QAiBb,SAAS+O,GAAmB3P,GAC1B,GAAU,IAANA,EAAS,OAAOL,GAAK,GACzB,GAAIK,EAAI,GAAI,OAAOL,GAAKK,GAGxB,GAAIA,EAAI,GAAI,CACV,MAAMC,EAAOD,EAAI,GACjB,OAAa,IAATC,EAAmB,OACV,IAATA,EAAmB,WAChB,QAAUN,GAAKM,EACxB,CAGA,MAAMA,EAAOD,EAAI,GACXoE,EAAOjE,KAAKC,MAAMJ,EAAI,IACtB4P,EAAWjQ,GAAKyE,GAAQ,QAE9B,OAAa,IAATnE,EAAmB2P,EACV,IAAT3P,EAAmB2P,SACV,IAAT3P,EAAmB2P,SAChBA,EAAW,IAAMjQ,GAAKM,EAC/B,CAMA,SAASF,GAAcC,GACrB,GAAU,IAANA,EAAS,MAAO,GAEpB,MAAM4D,EAAWzD,KAAKC,MAAMJ,EAAI,KAC1B6D,EAAY7D,EAAI,IAEtB,IAAIkC,EAAS,GAuBb,OArBI0B,EAAW,IACb1B,EAASvC,GAAKiE,GAALjE,SAGPkE,EAAY,IACVA,EAAY,GAEV3B,GACFA,GAAU,OAEVA,GAAwB,IAAd2B,EAAkB,MAAQlE,GAAKkE,IAEzC3B,EAASvC,GAAKkE,IAIZ3B,IAAQA,GAAU,KACtBA,GAAU2N,GAAUhM,KAIjB3B,CACT,CAIA,MAAM2N,GAAY,IAAInP,MAAM,KAC5B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAKA,IACvBkP,GAAUlP,GAAKgP,GAAkBhP,GAKnC,MAAMF,GAAW,IAAIC,MAAM,KAC3B,IAAK,IAAIC,EAAI,EAAGA,EAAI,IAAMA,IACxBF,GAASE,GAAKZ,GAAaY,GAK7B,MAAMmP,GAAc,IAAIpP,MAAM,KAC9BoP,GAAY,GAAK,GACjB,IAAK,IAAInP,EAAI,EAAGA,EAAI,GAAIA,IAEtBmP,GAAYnP,GAAKoP,OAAkB,IAANpP,EAAU,MAAQhB,GAAKgB,IAEtD,IAAK,IAAIA,EAAI,GAAIA,EAAI,IAAKA,IACxBmP,GAAYnP,GAAKoP,MAAWF,GAAUlP,GAaxC,SAAS8B,GAAgBzC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,KACN,OAAO6P,GAAUjS,OAAOoC,IAI1B,GAAIA,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAEvBgQ,EAAiBvP,GAASkE,GAAa,IAAMI,GAAO,GAE1D,OAAkB,IAAdlB,EACKmM,EAILnM,EAAY,IACPmM,EAAiB,IAAMF,GAAYjM,GAGrCmM,EAAiB,IAAMvP,GAASoD,EACzC,CAGA,OASF,SAAgC7D,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GACzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMS,EAAQxD,GAAS+C,GACnBS,GAEA3D,EAAMC,KADJgD,EAAa,EACJU,EAAQ,IAAMc,GAAOxB,GAErBU,EAGjB,CACAV,GACF,CAGA,MAAM0M,EAAW3P,EAAM7B,OACvB,GAAiB,IAAbwR,EAAgB,OAAOrP,GAC3B,GAAiB,IAAbqP,EAAgB,OAAO3P,EAAM,GAGjC,MAAM4P,EAAc9M,EAASA,EAAS3E,OAAS,GAC/C,GAAIyR,EAAc,GAAKA,GAAe,GAAI,CAExC,IAAIhO,EAAS5B,EAAM,GACnB,IAAK,IAAIK,EAAI,EAAGA,EAAIsP,EAAW,EAAGtP,IAChCuB,GAAU,IAAM5B,EAAMK,GAExB,OAAOuB,EAAS,IAAM4N,GAAYI,EACpC,CAGA,IAAIhO,EAAS5B,EAAM,GACnB,IAAK,IAAIK,EAAI,EAAGA,EAAIsP,EAAUtP,IAC5BuB,GAAU,IAAM5B,EAAMK,GAExB,OAAOuB,CACT,CArESwB,CAAsB1D,EAC/B,CChKA,MAAMmQ,GAAc,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,KAM5DC,GAAc,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,KAS5DxP,GAAO,IAWb,SAASyP,GAAiB7S,EAAOyC,EAAMqQ,EAAKC,EAASC,GACnD,GAAc,KAAVhT,EAAc,MAAO,GAEzB,IAAI0E,EAAS,GACTuO,GAAY,EAGhB,MAAMC,EAAelT,EAAQ,MACvBmT,EAAqBnT,EAAQ,MAC/BkT,EAAe,KACjBxO,EAASjC,EAAKrC,OAAO8S,IAAiBF,EACtCC,EAAYE,EAAqB,IAAMA,EAAqB,MAI9D,MAAMC,EAAcD,EAAqB,KACnCE,EAAoBF,EAAqB,KAC3CC,EAAc,IACZH,IAAWvO,GAAUtB,IACzBsB,GAAUjC,EAAKrC,OAAOgT,IAAgBL,EACtCE,EAAYI,EAAoB,IAAMA,EAAoB,KACjDH,EAAe,IAAMG,EAAoB,KAClDJ,GAAY,GAId,MAAMK,EAAUD,EAAoB,IAC9BE,EAAUF,EAAoB,IAepC,OAdIC,EAAU,IACRL,IAAWvO,GAAUtB,IACzBsB,GAAUjC,EAAKrC,OAAOkT,IAAYR,EAClCG,GAAY,IACFG,EAAc,IAAMF,EAAe,KAAOK,EAAU,KAC9DN,GAAY,GAIVM,EAAU,KACRN,IAAWvO,GAAUtB,IACzBsB,GAAUjC,EAAKrC,OAAOmT,KAGjB7O,CACT,CAKA,SAAS8O,GAAgBxT,EAAOyC,EAAMqQ,EAAKC,EAASC,GAClD,GAAc,KAAVhT,EAAc,MAAO,GAEzB,GAAIA,GAAS,OAAS,CACpB,MAAMyT,EAAWzT,EAAQ,OACnB0T,EAAe1T,EAAQ,OAE7B,IAAI0E,EAASmO,GAAgBY,EAAUhR,EAAMqQ,EAAKC,EAASC,GArE9C,IA6Eb,OANIU,EAAe,MACED,EAAW,KAAQ,IAAQC,EAAe,SAC9ChP,GAAUtB,IACzBsB,GAAUmO,GAAgBa,EAAcjR,EAAMqQ,EAAKC,EAASC,IAGvDtO,CACT,CAEA,OAAOmO,GAAgB7S,EAAOyC,EAAMqQ,EAAKC,EAASC,EACpD,CCzFA,MAAML,GAAc,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,KAM5DC,GAAc,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,KAS5DxP,GAAO,SrDsGb,SAAkBpD,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,SAGXjP,GA1EU,MADalC,EA2EErC,GA3Hd,KAmDPqC,EAAI,MACCS,EAAS7C,OAAOoC,IAM3B,SAAgCA,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAET,IAAZ6C,GAIAlD,EAAMC,KADW,IAAfgD,EACS9C,EAAS+C,GAET/C,EAAS+C,GAAW,KALf1D,EAAYyD,IAAe,KAS/CA,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CAzCSkD,CAAsB1D,GAsEzBf,IACFiD,GAAU,QA5Bd,SAA6BjD,GAE3B,MAAMO,EAAS,GACf,IAAK,MAAM4R,KAAQnS,EAAa,CAC9B,MAAMoS,EAAI9R,SAAS6R,EAAM,IACzB5R,EAAOe,KAAW,IAAN8Q,EAvGH,KAuGoB1R,EAAK0R,GACpC,CACA,OAAO7R,EAAOgB,KAAK,IACrB,CAoBwC8Q,CAAmBrS,IAGlDiD,EAjFT,IAAyBlC,CAkFzB,WChBA,SAAkBxC,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,aAGXjP,GA1EU,MADalC,EA2EErC,GA1EJiD,EAEjBZ,EAAI,MACCS,EAAS7C,OAAOoC,IAM3B,SAAgCA,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAapB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAET,IAAZ6C,GAIAlD,EAAMC,KADW,IAAfgD,EACS9C,EAAS+C,GAET/C,EAAS+C,GAAW,KALf1D,EAAYyD,IAAe,KAS/CA,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CAzCSkD,CAAsB1D,GAsEzBf,IACFiD,GAAU,UA5Bd,SAA6BjD,GAE3B,MAAMO,EAAS,GACf,IAAK,MAAM4R,KAAQnS,EAAa,CAC9B,MAAMoS,EAAI9R,SAAS6R,EAAM,IACzB5R,EAAOe,KAAW,IAAN8Q,EAAUzQ,EAAOjB,EAAK0R,GACpC,CACA,OAAO7R,EAAOgB,KAAK,IACrB,CAoBwC8Q,CAAmBrS,IAGlDiD,EAjFT,IAAyBlC,CAkFzB,OGuBA,SAAkBxC,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAE7D8C,EAAQ,GAad,OAXI5C,GACF4C,EAAMC,KAAKO,EAAQyQ,cAvJN,QA0JfjR,EAAMC,KAAKkC,EAAe9E,EAAamD,IAEnC7B,IACFqB,EAAMC,KA5JU,SA6JhBD,EAAMC,KA5CV,SAA6BtB,EAAa6B,GACxC,MAAMR,EAAQ,GACd,IAAIK,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IAC3CL,EAAMC,KAxHG,OAyHTI,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAKpC,OAJIkD,GACFvD,EAAMC,KAAKkC,EAAezE,OAAO6F,GAAY/C,IAGxCR,EAAME,KAAK,IACpB,CA6Be8Q,CAAmBrS,EAAa6B,KAGtCR,EAAME,KAAK,IACpB,OC7CA,SAAkBhD,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,EAAe9E,GAErBsB,IACFiD,GAAU,UArCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,EACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,EAAezE,OAAO6F,KAG3B3B,CACT,CAoBwCoP,CAAmBrS,IAGlDiD,CACT,OC/CA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,WAGXjP,GAAUO,EAAe9E,GAErBsB,IACFiD,GAAU,UArCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,EACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,EAAezE,OAAO6F,KAG3B3B,CACT,CAoBwCoP,CAAmBrS,IAGlDiD,CACT,OCiLA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAQb,GANIxE,IACFwE,EAASiP,UAGXjP,GAAUO,EAAe9E,GAErBsB,EAAa,CACf,MAAMqQ,EAnKV,SAA8B3R,GAC5B,OAAoB,KAAhBA,GAAsC,KAAhBA,EACjB,OACEA,GAAe,IAAMA,GAAe,GACtC,OAEA,QAEX,CA2JsB6T,CAAoB7T,GACtCuE,GAAU,IAAMoN,EAAY,IAnDhC,SAA6BrQ,GAC3B,IAAIiD,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,EACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,EAAezE,OAAO6F,KAG3B3B,CACT,CAgCsCoP,CAAmBrS,EACvD,CAEA,OAAOiD,CACT,OC5DA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,UA9Cd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CA2BwCoP,CAAmBrS,IAGlDiD,CACT,OCqBA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,UAjDd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CA8BwCoP,CAAmBrS,IAGlDiD,CACT,OCzGA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAzIF,SAAyBlC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAG7B,IAAIkC,EAWJ,OATEA,EADgB,IAAdyC,EACOG,GAEArE,GAASkE,GAAa,IAAMG,GAGnCjB,EAAY,IACd3B,GAAU,IAAMzB,GAASoD,IAGpB3B,CACT,CAGA,OASF,SAAgClC,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMyB,EAAcxE,GAAS+C,GAE7B,GAAmB,IAAfD,EAEFjD,EAAMC,KAAK0E,QACN,GAAmB,IAAf1B,EAGPjD,EAAMC,KADQ,IAAZiD,EACSsB,GAEAG,EAAc,IAAMH,QAE5B,CAEL,MAAMrB,EAAYsB,GAAOxB,EAAa,GAEpCjD,EAAMC,KADQ,IAAZiD,EACSC,EAEAwB,EAAc,IAAMxB,EAEnC,CACF,CAEAF,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CA/DSkD,CAAsB1D,EAC/B,CA2GYyC,CAAe9E,GAErBsB,IACFiD,GAAU,UAxCd,SAA6BjD,GAC3B,MAAMqB,EAAQ,GAEd,IAAK,MAAMmR,KAASxS,EAAa,CAC/B,MAAMoS,EAAI9R,SAASkS,EAAO,IAExBnR,EAAMC,KADE,IAAN8Q,EACSzQ,GAEAjB,GAAK0R,GAEpB,CAEA,OAAO/Q,EAAME,KAAK,IACpB,CA2BwC8Q,CAAmBrS,IAGlDiD,CACT,OCHA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,UAjDd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CA8BwCoP,CAAmBrS,IAGlDiD,CACT,OC6DA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAC7DmJ,EAA8B,aAAnB7F,EAAQ4B,OAEzB,IAAIR,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,EAAagJ,GAElC1H,IACFiD,GAAU,UArDd,SAA6BjD,EAAa0H,GACxC,IAAIzE,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,GAAY8C,IAGvCzE,CACT,CAkCwCoP,CAAmBrS,EAAa0H,IAG/DzE,CACT,OCzNA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,SAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,UArCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAnES,MAoETvB,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CAoBwCoP,CAAmBrS,IAGlDiD,CACT,OC+FA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,WAGXjP,GArIF,SAAyBlC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAGrB,GAAIZ,EAAI,MACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,SAAY,CAClB,MAAM2E,EAAY/G,OAAOoC,EAAI,OACvB6D,EAAYjG,OAAOoC,EAAI,OAG7B,IAAIkC,EAWJ,OATEA,EADgB,IAAdyC,EACOG,GAEArE,GAASkE,GAAa,IAAMG,GAGnCjB,EAAY,IACd3B,GAAU,IAAMzB,GAASoD,IAGpB3B,CACT,CAGA,OASF,SAAgClC,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAGb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMhD,EAAQ,GACd,IAAIiD,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMyB,EAAcxE,GAAS+C,GAI3BlD,EAAMC,KAFW,IAAfgD,EAES0B,EACa,IAAf1B,EAEO,IAAZC,EACSsB,GAEAG,EAAc,IAAMH,GAKtBG,EAAc,IADPF,GAAOxB,EAAa,GAG1C,CAEAA,GACF,CAEA,OAAOjD,EAAME,KAAK,IACpB,CA3DSkD,CAAsB1D,EAC/B,CAuGYyC,CAAe9E,GAErBsB,IACFiD,GAAU,WAxCd,SAA6BjD,GAC3B,MAAMqB,EAAQ,GAEd,IAAK,MAAMmR,KAASxS,EAAa,CAC/B,MAAMoS,EAAI9R,SAASkS,EAAO,IAExBnR,EAAMC,KADE,IAAN8Q,EACSzQ,GAEAjB,GAAK0R,GAEpB,CAEA,OAAO/Q,EAAME,KAAK,IACpB,CA2BwC8Q,CAAmBrS,IAGlDiD,CACT,QCvCA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,aAGXjP,GAvEU,MADalC,EAwEErC,GAvEJiD,GAEjBZ,EAAI,MACCS,GAAS7C,OAAOoC,IAW3B,SAAgCA,GAE9B,MAAM4E,EAAgB,GACtB,IAAIjC,EAAO3C,EACX,KAAO2C,EAAO,IACZiC,EAAcrE,KAAK3C,OAAO+E,EAAO,QACjCA,GAAc,MAIhB,IAAIT,EAAS,GAEb,IAAK,IAAIvB,EAAIiE,EAAcnG,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAClD,MAAM6C,EAAUoB,EAAcjE,GACd,IAAZ6C,IAIAtB,IAAQA,GAAU,KAGpBA,GADQ,IAANvB,EACQF,GAAS+C,GAGCoE,GAAqBpE,GACjB,KATR1D,GAAYa,IAAM,IAWtC,CAEA,OAAOuB,CACT,CAtCSwB,CAAsB1D,GAmEzBf,IACFiD,GAAU,UA5Bd,SAA6BjD,GAE3B,MAAMO,EAAS,GACf,IAAK,MAAM4R,KAAQnS,EAAa,CAC9B,MAAMoS,EAAI9R,SAAS6R,EAAM,IACzB5R,EAAOe,KAAW,IAAN8Q,EAAUzQ,GAAOjB,GAAK0R,GACpC,CACA,OAAO7R,EAAOgB,KAAK,IACrB,CAoBwC8Q,CAAmBrS,IAGlDiD,EA9ET,IAAyBlC,CA+EzB,OCgIA,SAAkBxC,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAC7D6K,EAAavH,EAAQ4Q,sBAAuB,EAElD,IAAIxP,EAAS,GACb,MAAMsN,EAAMnH,EAAa,IAAM,IAY/B,OAVI3K,IACFwE,EA7Ta,QA6TOsN,GAGtBtN,GAAUO,GAAe9E,EAAa0K,GAElCpJ,IACFiD,GAAUsN,EAlUM,UAkUcA,EAvDlC,SAA6BvQ,EAAaoJ,GACxC,IAAInG,EAAS,GACb,MAAMsN,EAAMnH,EAAa,IAAM,IAG/B,IAAI1H,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAUsN,GACtBtN,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAUsN,GACtBtN,GAAUO,GAAezE,OAAO6F,GAAYwE,IAGvCnG,CACT,CAmCwCoP,CAAmBrS,EAAaoJ,IAG/DnG,CACT,SCxFA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAC7D6K,EAAavH,EAAQ4Q,sBAAuB,EAElD,IAAIxP,EAAS,GACb,MAAMsN,EAAMnH,EAAa,IAAM,IAY/B,OAVI3K,IACFwE,EA7Pa,QA6POsN,GAGtBtN,GAAUO,GAAe9E,EAAa0K,GAElCpJ,IACFiD,GAAUsN,EAlQM,UAkQcA,EA3ClC,SAA6BvQ,EAAaoJ,GACxC,IAAInG,EAAS,GACb,MAAMsN,EAAMnH,EAAa,IAAM,IAE/B,IAAI1H,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAUsN,GACtBtN,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAUsN,GACtBtN,GAAUO,GAAezE,OAAO6F,GAAYwE,IAGvCnG,CACT,CAyBwCoP,CAAmBrS,EAAaoJ,IAG/DnG,CACT,OCnLA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,OAGXjP,GA9CF,SAAyBlC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAErB,MAAMwC,EApCR,SAA+BpD,GAC7B,MAAMkD,EAASlD,EAAE7B,WACjB,GAAI+E,EAAOzE,QAAU,EAAG,MAAO,CAACb,OAAOsF,IAEvC,MAAME,EAAW,GACjBA,EAASH,QAAQrF,OAAOsF,EAAOrE,OAAM,KAErC,IAAIiF,EAAYZ,EAAOrE,MAAM,GAAG,GAChC,KAAOiF,EAAUrF,OAAS,GACxB2E,EAASH,QAAQrF,OAAOkG,EAAUjF,OAAM,KACxCiF,EAAYA,EAAUjF,MAAM,GAAG,GAGjC,OAAOuE,CACT,CAsBmBW,CAAqB/D,GAChCgE,EAAeZ,EAAS3E,OACxBwF,EAAQ,GAEd,IAAK,IAAItD,EAAI,EAAGA,EAAIqD,EAAcrD,IAAK,CACrC,MAAMuD,EAAed,EAASzC,GAC9B,GAAqB,IAAjBuD,EAAoB,SAExB,MAAMX,EAAaS,EAAerD,EAAI,EACtCsD,EAAM1D,KAAKqB,GAAesC,IACtBX,EAAa,GAAKzD,GAAYyD,IAChCU,EAAM1D,KAAKT,GAAYyD,GAE3B,CAEA,OAAOU,EAAMzD,KAAK,KAAKhC,MACzB,CA2BYiE,CAAe9E,GAErBsB,IACFiD,GAAU,UA5Bd,SAA6BjD,GAE3B,MAAMO,EAAS,GACf,IAAK,MAAM4R,KAAQnS,EAAa,CAC9B,MAAMoS,EAAI9R,SAAS6R,EAAM,IACzB5R,EAAOe,KAAW,IAAN8Q,EAAUzQ,GAAO+C,GAAc0N,GAC7C,CACA,OAAO7R,EAAOgB,KAAK,IACrB,CAoBwC8Q,CAAmBrS,IAGlDiD,CACT,OC6EA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,SAGXjP,GApHU,MADalC,EAqHErC,GApHJiD,GAEjBZ,EAAI,MACCS,GAAS7C,OAAOoC,IAa3B,SAAgCA,GAC9B,MAAMkD,EAASlD,EAAE7B,WACXgF,EAAMD,EAAOzE,OAEb2E,EAAW,GAGXC,EAAeF,EAFD,EAGpB,IAAIG,EAAM,EAKV,IAJID,EAAe,IACjBD,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAM,EAAGwE,KACrCC,EAAMD,GAEDC,EAAMH,GACXC,EAAS7C,KAAK3C,OAAOsF,EAAOrE,MAAMyE,EAAKA,EATrB,KAUlBA,GAVkB,EAcpB,MAAMqO,EAAW,GACjB,IAAIpO,EAAaH,EAAS3E,OAAS,EAEnC,IAAK,IAAIkC,EAAI,EAAGA,EAAIyC,EAAS3E,OAAQkC,IAAK,CACxC,MAAM6C,EAAUJ,EAASzC,GAEzB,GAAgB,IAAZ6C,EAAe,CACjB,MAAMC,EAAY3D,GAAYyD,IAAe,GAE1B,IAAfA,EACFoO,EAASpR,KAAKE,GAAS+C,KAEvBmO,EAASpR,KAAKE,GAAS+C,IACvBmO,EAASpR,KAAKkD,GAElB,CAEAF,GACF,CAGA,MAAMqO,EAAW,GACjB,IAAK,IAAIjR,EAAI,EAAGA,EAAIgR,EAASlT,OAAQkC,IAAK,CACxC,MAAMyE,EAAOuM,EAAShR,GAChB0E,EAAWsM,EAAShR,EAAI,IAGjB,SAATyE,IAAmBC,GAAaA,IAAaK,IAAWL,IAAaP,KAIzE8M,EAASrR,KAAK6E,EAChB,CAGA,MAAMlD,EAAS,GACf,IAAK,IAAIvB,EAAI,EAAGA,EAAIiR,EAASnT,OAAQkC,IAAK,CACxC,MAAMyE,EAAOwM,EAASjR,GAChBqF,EAAWrF,EAAI,EAAIiR,EAASjR,EAAI,GAAK,KAIvCqF,GAAYuC,GAAcnD,KACzBY,IAAalB,IAAYkB,IAAaN,IACtC5F,GAAY1B,SAAS4H,IACxB9D,EAAO3B,KAAK,QACHI,EAAI,GACbuB,EAAO3B,KAAK,KAGd2B,EAAO3B,KAAK6E,EACd,CAEA,OAAOlD,EAAO1B,KAAK,GACrB,CAnFSkD,CAAsB1D,GAgHzBf,IACFiD,GAAU,SA5Bd,SAA6BjD,GAE3B,MAAMO,EAAS,GACf,IAAK,MAAM4R,KAAQnS,EAAa,CAC9B,MAAMoS,EAAI9R,SAAS6R,EAAM,IACzB5R,EAAOe,KAAW,IAAN8Q,EAAUzQ,GAAOjB,GAAK0R,GACpC,CACA,OAAO7R,EAAOgB,KAAK,IACrB,CAoBwC8Q,CAAmBrS,IAGlDiD,EA3HT,IAAyBlC,CA4HzB,QCqDA,SAAkBxC,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAnHF,SAAyBlC,EAAGc,GAC1B,GAAU,KAANd,EAAU,MAnIH,MAqIX,MAAM8I,EAAUhI,EAAQgI,SAAW,IAE7B+I,EAAwB,cADf/Q,EAAQ4B,QAAU,aAE3BoP,EAA6B,MAAZhJ,EAGjBnJ,EAAOkS,EAAalQ,GAAWD,GAC/B9B,EAAQiS,EAAanJ,GAAYF,GACjCc,EAAoBuI,EAAalJ,GAAgBF,GACjDM,EAAe8I,EAAanL,GAAerF,GAC3C0Q,EAAaF,EAAazI,GAAqBF,GAC/C8I,EAAaH,EAAaxI,GAAqBF,GAGrD,GAAInJ,EAAI,MACN,OAAO8R,EAAiBE,EAAWpU,OAAOoC,IAAMiJ,GAAkBrL,OAAOoC,GAAI8I,EAASnJ,EAAMC,EAAOmJ,GAIrG,MAAM3F,EAAW,GACjB,IAAIT,EAAO3C,EACX,KAAO2C,EAAO,IACZS,EAAS7C,KAAK3C,OAAO+E,EAAO,QAC5BA,GAAc,MAIhB,IAAIT,EAAS,GAEb,IAAK,IAAIvB,EAAIyC,EAAS3E,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAC7C,MAAM6C,EAAUJ,EAASzC,GACzB,GAAgB,IAAZ6C,EAEJ,GAAU,IAAN7C,EAAS,CAEX,MAAMsE,EAAc6M,EAAiBE,EAAWxO,GAAWyF,GAAkBzF,EAASsF,EAASnJ,EAAMC,EAAOmJ,GACxG7G,EAGAA,GADEsB,GAAW,EACH,IAAMsF,EAAU7D,EAEhB,IAAMA,EAGlB/C,EAAS+C,CAEb,MAAiB,IAANtE,EAEL6C,GAAW,GACTtB,IAAQA,GAAU,KACtBA,GAAUoH,EAAkB9F,KAGxBtB,IAAQA,GAAU,KACtBA,IAFoB4P,EAAiBC,EAAWvO,GAAWqF,GAAkBrF,EAASsF,EAASnJ,EAAMC,EAAOmJ,IAEpF,IAAMH,GAAM,IAItB,IAAZpF,GACEtB,IAAQA,GAAU,KACtBA,GAAU0G,GAAMjI,KAGZuB,IAAQA,GAAU,KACtBA,IAFoB4P,EAAiBC,EAAWvO,GAAWqF,GAAkBrF,EAASsF,EAASnJ,EAAMC,EAAOmJ,IAEpF,IAAMxH,GAAaZ,GAGjD,CAEA,OAAOuB,CACT,CA0CYO,CAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,UApCd,SAA6BjD,EAAa6B,GACxC,MACMnB,EAAkB,cADTmB,EAAQ4B,QAAU,aACIf,GAAWD,GAEhD,IAAIQ,EAAS,GACb,IAAK,IAAIvB,EAAI,EAAGA,EAAI1B,EAAYR,OAAQkC,IAAK,CAC3C,MAAM0Q,EAAI9R,SAASN,EAAY0B,GAAI,IAC/BuB,IAAQA,GAAU,KACtBA,GAAgB,IAANmP,EA5ND,MA4NkB1R,EAAK0R,EAClC,CAEA,OAAOnP,CACT,CAwBwCoP,CAAmBrS,EAAa6B,IAG/DoB,CACT,OCzCA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAtGF,SAAyBlC,EAAGc,GAC1B,GAAU,KAANd,EAAU,MA9HH,MAgIX,MAAM8I,EAAUhI,EAAQgI,SAAW,IAC7BgJ,EAA6B,MAAZhJ,EAGvB,GAAI9I,EAAI,MACN,OAAO8R,EAAiBtI,GAAe5L,OAAOoC,IAAMiJ,GAAkBrL,OAAOoC,GAAI8I,GAInF,MAAM1F,EAAW,GACjB,IAAIT,EAAO3C,EACX,KAAO2C,EAAO,IACZS,EAAS7C,KAAK3C,OAAO+E,EAAO,QAC5BA,GAAc,MAIhB,IAAIT,EAAS,GAEb,IAAK,IAAIvB,EAAIyC,EAAS3E,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAC7C,MAAM6C,EAAUJ,EAASzC,GACzB,GAAgB,IAAZ6C,EAEJ,GAAU,IAAN7C,EAAS,CAEX,MAAMsE,EAAc6M,EAAiBtI,GAAehG,GAAWyF,GAAkBzF,EAASsF,GACtF5G,EAGAA,GADEsB,GAAW,EACH,IAAMsF,EAAU7D,EAEhB,IAAMA,EAGlB/C,EAAS+C,CAEb,MAAiB,IAANtE,EAEL6C,GAAW,GACTtB,IAAQA,GAAU,KACtBA,GAAUoH,GAAkB9F,KAGxBtB,IAAQA,GAAU,KACtBA,IAFoB4P,EAAiBvI,GAAe/F,GAAWqF,GAAkBrF,EAASsF,IAElE,IAAMF,GAAM,IAItB,IAAZpF,GACEtB,IAAQA,GAAU,KACtBA,GAAU0G,GAAMjI,KAGZuB,IAAQA,GAAU,KACtBA,IAFoB4P,EAAiBvI,GAAe/F,GAAWqF,GAAkBrF,EAASsF,IAElE,IAAMvH,GAAaZ,GAGjD,CAEA,OAAOuB,CACT,CAuCYO,CAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,UAlCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAEb,IAAK,IAAIvB,EAAI,EAAGA,EAAI1B,EAAYR,OAAQkC,IAAK,CAC3C,MAAM0Q,EAAI9R,SAASN,EAAY0B,GAAI,IAC/BuB,IAAQA,GAAU,KACtBA,GAAgB,IAANmP,EA1MD,MA0MkB1R,GAAK0R,EAClC,CAEA,OAAOnP,CACT,CAwBwCoP,CAAmBrS,IAGlDiD,CACT,OCnJA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,UArCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CAoBwCoP,CAAmBrS,IAGlDiD,CACT,OCwDA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,UAxCd,SAA6BjD,EAAa6B,GACxC,IAAIoB,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,GAAY/C,IAGvCoB,CACT,CAuBwCoP,CAAmBrS,EAAa6B,IAG/DoB,CACT,OClBA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,WAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,UArCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CAoBwCoP,CAAmBrS,IAGlDiD,CACT,OC/BA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,QAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,SArCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAETvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GArIS,MAsITvB,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CAoBwCoP,CAAmBrS,IAGlDiD,CACT,OCgKA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,SAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,YAnDd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CAgCwCoP,CAAmBrS,IAGlDiD,CACT,OCtIA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAlMa,QAqMfA,GAtHF,SAAyBlC,GACvB,GAAU,KAANA,EAAU,MAjFH,IAoFX,GAAIA,EAAI,OACN,OAAOS,GAAS7C,OAAOoC,IAIzB,GAAIA,EAAI,WAAc,CACpB,MAAMiS,EAAMrU,OAAOoC,EAAI,QACjB6D,EAAYjG,OAAOoC,EAAI,QAG7B,IAAIkC,EAWJ,OATEA,EADU,IAAR+P,EACO,IAAMlN,GAAO,GAEbtE,GAASwR,GAAOlN,GAAO,GAG9BlB,EAAY,IACd3B,GAAUzB,GAASoD,IAGd3B,CACT,CAGA,OAUF,SAAgClC,GAG9B,MAAMoD,EAAW,GACjB,IAAIT,EAAO3C,EACX,KAAO2C,EAAO,IACZS,EAAS7C,KAAK3C,OAAO+E,EAAO,SAC5BA,GAAc,OAIhB,IAAIT,EAAS,GAEb,IAAK,IAAIvB,EAAIyC,EAAS3E,OAAS,EAAGkC,GAAK,EAAGA,IAAK,CAC7C,MAAM6C,EAAUJ,EAASzC,GACT,IAAZ6C,IAKAtB,GAHAvB,EAAI,EAEU,IAAZ6C,EACQ,IAAMuB,GAAOpE,EAAI,GAEjBF,GAAS+C,GAAWuB,GAAOpE,EAAI,GAIjCF,GAAS+C,GAEvB,CAEA,OAAOtB,GArJI,GAsJb,CAzCSwB,CAAsB1D,EAC/B,CAwFYyC,CAAe9E,GAErBsB,IACFiD,GAvMgB,IA4JpB,SAA6BjD,GAC3B,IAAIiD,EAAS,GAEb,IAAK,IAAIvB,EAAI,EAAGA,EAAI1B,EAAYR,OAAQkC,IAAK,CAC3C,MAAM8Q,EAAQlS,SAASN,EAAY0B,GAAI,IAErCuB,GADY,IAAVuP,EAnKK,IAsKG9R,GAAK8R,EAEnB,CAEA,OAAOvP,CACT,CA8B4BoP,CAAmBrS,IAGtCiD,CACT,OC1IA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,YAGXjP,GA9CF,SAAyBlC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAErB,MAAMwC,EApCR,SAA+BpD,GAC7B,MAAMkD,EAASlD,EAAE7B,WACjB,GAAI+E,EAAOzE,QAAU,EAAG,MAAO,CAACb,OAAOsF,IAEvC,MAAME,EAAW,GACjBA,EAASH,QAAQrF,OAAOsF,EAAOrE,OAAM,KAErC,IAAIiF,EAAYZ,EAAOrE,MAAM,GAAG,GAChC,KAAOiF,EAAUrF,OAAS,GACxB2E,EAASH,QAAQrF,OAAOkG,EAAUjF,OAAM,KACxCiF,EAAYA,EAAUjF,MAAM,GAAG,GAGjC,OAAOuE,CACT,CAsBmBW,CAAqB/D,GAChCgE,EAAeZ,EAAS3E,OACxBwF,EAAQ,GAEd,IAAK,IAAItD,EAAI,EAAGA,EAAIqD,EAAcrD,IAAK,CACrC,MAAMuD,EAAed,EAASzC,GAC9B,GAAqB,IAAjBuD,EAAoB,SAExB,MAAMX,EAAaS,EAAerD,EAAI,EACtCsD,EAAM1D,KAAKqB,GAAesC,IACtBX,EAAa,GAAKzD,GAAYyD,IAChCU,EAAM1D,KAAKT,GAAYyD,GAE3B,CAEA,OAAOU,EAAMzD,KAAK,KAAKhC,MACzB,CA2BYiE,CAAe9E,GAErBsB,IACFiD,GAAU,WA5Bd,SAA6BjD,GAE3B,MAAMO,EAAS,GACf,IAAK,MAAM4R,KAAQnS,EAAa,CAC9B,MAAMoS,EAAI9R,SAAS6R,EAAM,IACzB5R,EAAOe,KAAW,IAAN8Q,EAAUzQ,GAAO+C,GAAc0N,GAC7C,CACA,OAAO7R,EAAOgB,KAAK,IACrB,CAoBwC8Q,CAAmBrS,IAGlDiD,CACT,OC6GA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAE7D8C,EAAQ,GAad,OAXI5C,GACF4C,EAAMC,KA5NO,QA+NfD,EAAMC,KAAKkC,GAAe9E,IAEtBsB,IACFqB,EAAMC,KAjOU,KAkOhBD,EAAMC,KA7CV,SAA6BtB,GAC3B,MAAMqB,EAAQ,GAGd,IAAIK,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IAC3CL,EAAMC,KA7LG,KA8LTI,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAKpC,OAJIkD,GACFvD,EAAMC,KAAKkC,GAAezE,OAAO6F,KAG5BvD,EAAME,KAAK,IACpB,CA4Be8Q,CAAmBrS,KAGzBqB,EAAME,KAAK,IACpB,OC8BA,SAAkBhD,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,aAjDd,SAA6BjD,EAAa6B,GACxC,IAAIoB,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,GAAY/C,IAGvCoB,CACT,CA8BwCoP,CAAmBrS,EAAa6B,IAG/DoB,CACT,OCNA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,WAjDd,SAA6BjD,EAAa6B,GACxC,IAAIoB,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,GAAY/C,IAGvCoB,CACT,CA8BwCoP,CAAmBrS,EAAa6B,IAG/DoB,CACT,OCxMA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,QAGXjP,GA9CF,SAAyBlC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAErB,MAAMwC,EApCR,SAA+BpD,GAC7B,MAAMkD,EAASlD,EAAE7B,WACjB,GAAI+E,EAAOzE,QAAU,EAAG,MAAO,CAACb,OAAOsF,IAEvC,MAAME,EAAW,GACjBA,EAASH,QAAQrF,OAAOsF,EAAOrE,OAAM,KAErC,IAAIiF,EAAYZ,EAAOrE,MAAM,GAAG,GAChC,KAAOiF,EAAUrF,OAAS,GACxB2E,EAASH,QAAQrF,OAAOkG,EAAUjF,OAAM,KACxCiF,EAAYA,EAAUjF,MAAM,GAAG,GAGjC,OAAOuE,CACT,CAsBmBW,CAAqB/D,GAChCgE,EAAeZ,EAAS3E,OACxBwF,EAAQ,GAEd,IAAK,IAAItD,EAAI,EAAGA,EAAIqD,EAAcrD,IAAK,CACrC,MAAMuD,EAAed,EAASzC,GAC9B,GAAqB,IAAjBuD,EAAoB,SAExB,MAAMX,EAAaS,EAAerD,EAAI,EACtCsD,EAAM1D,KAAKqB,GAAesC,IACtBX,EAAa,GAAKzD,GAAYyD,IAChCU,EAAM1D,KAAKT,GAAYyD,GAE3B,CAEA,OAAOU,EAAMzD,KAAK,KAAKhC,MACzB,CA2BYiE,CAAe9E,GAErBsB,IACFiD,GAAU,UA5Bd,SAA6BjD,GAE3B,MAAMO,EAAS,GACf,IAAK,MAAM4R,KAAQnS,EAAa,CAC9B,MAAMoS,EAAI9R,SAAS6R,EAAM,IACzB5R,EAAOe,KAAW,IAAN8Q,EAAUzQ,GAAO+C,GAAc0N,GAC7C,CACA,OAAO7R,EAAOgB,KAAK,IACrB,CAoBwC8Q,CAAmBrS,IAGlDiD,CACT,OCoDA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,eArCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAETvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CAoBwCoP,CAAmBrS,IAGlDiD,CACT,OC0EA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,UA9Cd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CA2BwCoP,CAAmBrS,IAGlDiD,CACT,OCqBA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAE7D0U,EAAO,CACXrF,WAAiC,IAAtB/L,EAAQ+L,UACnBC,mBAAoBhM,EAAQgM,qBAAsB,EAClDC,iBAAkBjM,EAAQiM,mBAAoB,GAGhD,IAAI7K,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,QAGXjP,GAAUO,GAAe9E,EAAauU,GAElCjT,IACFiD,GAAU,UA9Dd,SAA6BjD,EAAa6B,GACxC,IAAIoB,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GA9OS,MA+OTvB,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAOpC,OANIkD,IACE3B,IAAQA,GAAU,KAEtBA,GADaO,GAAezE,OAAO6F,GAAY,IAAK/C,EAASiM,kBAAkB,KAI1E7K,CACT,CA0CwCoP,CAAmBrS,EAAaiT,IAG/DhQ,CACT,OChMA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,WArCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CAoBwCoP,CAAmBrS,IAGlDiD,CACT,OCsJA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,cArDd,SAA6BjD,EAAa6B,GACxC,IAAIoB,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,GAAY/C,IAGvCoB,CACT,CAkCwCoP,CAAmBrS,EAAa6B,IAG/DoB,CACT,OCxCA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,YAjDd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CA8BwCoP,CAAmBrS,IAGlDiD,CACT,OClEA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,YAtDd,SAA6BjD,GAC3B,IAAIiD,EAAS,GACTvB,EAAI,EAGR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GACpC,GAAIkD,EAAW,CACT3B,IAAQA,GAAU,KACtB,MAAMlC,EAAIhC,OAAO6F,GAEf3B,GADElC,EAAI,MACIkO,GAAetQ,OAAOoC,IAAI,GAAO,GAEjCyC,GAAezC,EAAG,CAAE0C,OAAQ,aAE1C,CAEA,OAAOR,CACT,CA8BwCoP,CAAmBrS,IAGlDiD,CACT,OClCA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,YAxCd,SAA6BjD,EAAa6B,GACxC,IAAIoB,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,GAAY/C,IAGvCoB,CACT,CAuBwCoP,CAAmBrS,EAAa6B,IAG/DoB,CACT,WC1CA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,WAxCd,SAA6BjD,EAAa6B,GACxC,IAAIoB,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,GAAY/C,IAGvCoB,CACT,CAuBwCoP,CAAmBrS,EAAa6B,IAG/DoB,CACT,WCjBA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,WAxCd,SAA6BjD,EAAa6B,GACxC,IAAIoB,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,GAAY/C,IAGvCoB,CACT,CAuBwCoP,CAAmBrS,EAAa6B,IAG/DoB,CACT,OCyEA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,UA9Cd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CA2BwCoP,CAAmBrS,IAGlDiD,CACT,OCvKA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,UArCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CAoBwCoP,CAAmBrS,IAGlDiD,CACT,OCfA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GA/CF,SAAyBlC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAErB,MAAMiC,EA3CR,SAA+B7C,GAC7B,MAAMkD,EAASlD,EAAE7B,WAEjB,GAAI+E,EAAOzE,QAAU,EACnB,MAAO,CAACb,OAAOsF,IAGjB,MAAME,EAAW,GACX+O,EAAQjP,EAAOrE,UACrBuE,EAASH,QAAQrF,OAAOuU,IAExB,IAAIrO,EAAYZ,EAAOrE,MAAM,GAAG,GAChC,KAAOiF,EAAUrF,OAAS,GAAG,CAC3B,MAAM+E,EAAUM,EAAUjF,UAC1BuE,EAASH,QAAQrF,OAAO4F,IACxBM,EAAYA,EAAUjF,MAAM,GAAG,EACjC,CAEA,OAAOuE,CACT,CAwBiBW,CAAqB/D,GAC9BoS,EAAavP,EAAOpE,OACpBwF,EAAQ,GAEd,IAAK,IAAItD,EAAI,EAAGA,EAAIyR,EAAYzR,IAAK,CACnC,MAAM0R,EAAaxP,EAAOlC,GAC1B,GAAmB,IAAf0R,EAAkB,SAEtB,MAAM9O,EAAa6O,EAAazR,EAAI,EAC9B2R,EAA6B,IAAfD,GAAoB9O,EAAa,EAAK,MAAQ2L,GAAqBmD,GACvFpO,EAAM1D,KAAK+R,GACP/O,EAAa,GAAKzD,GAAYyD,IAChCU,EAAM1D,KAAKT,GAAYyD,GAE3B,CAEA,OAAOU,EAAMzD,KAAK,KAAKhC,MACzB,CA2BYiE,CAAe9E,GAErBsB,IACFiD,GAAU,WA5Bd,SAA6BjD,GAE3B,MAAMO,EAAS,GACf,IAAK,MAAM4R,KAAQnS,EAAa,CAC9B,MAAMoS,EAAI9R,SAAS6R,EAAM,IACzB5R,EAAOe,KAAW,IAAN8Q,EAAUzQ,GAAOjB,GAAK0R,EAAI,GACxC,CACA,OAAO7R,EAAOgB,KAAK,IACrB,CAoBwC8Q,CAAmBrS,IAGlDiD,CACT,OCNA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,YA5Bd,SAA6BjD,GAE3B,MAAMO,EAAS,GACf,IAAK,MAAM4R,KAAQnS,EAAa,CAC9B,MAAMoS,EAAI9R,SAAS6R,EAAM,IACzB5R,EAAOe,KAAW,IAAN8Q,EAAUzQ,GAAOjB,GAAK0R,EAAI,GACxC,CACA,OAAO7R,EAAOgB,KAAK,IACrB,CAoBwC8Q,CAAmBrS,IAGlDiD,CACT,OCnBA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EA1Ha,MA6HfA,GA7CF,SAAyBlC,GACvB,GAAU,KAANA,EAAU,OAAOY,GAErB,MAAMiC,EAjBR,SAA6B7C,GAC3B,MAAM6C,EAAS,GACf,IAAIiB,EAAY9D,EAEhB,MAAMuS,EAAU,SAChB,KAAOzO,EAAY,IAAI,CACrB,MAAM0O,EAAQ5U,OAAOkG,EAAYyO,GACjC1P,EAAOI,QAAQuP,GACf1O,GAAwByO,CAC1B,CAEA,OAAO1P,CACT,CAKiB4P,CAAmBzS,GAC5BM,EAAQ,GAEd,IAAK,IAAIK,EAAI,EAAGA,EAAIkC,EAAOpE,OAAQkC,IAAK,CACtC,MAAM0R,EAAaxP,EAAOlC,GAC1B,GAAmB,IAAf0R,EAAkB,SAEtB/R,EAAMC,KAAK4O,GAAoBkD,IAC/B,MAAMvO,EAAYjB,EAAOpE,OAASkC,EAAI,EAClCmD,EAAY,GACdxD,EAAMC,KAAK,OAAOb,OAAOoE,GAE7B,CAEA,OAAOxD,EAAME,KAAK,GACpB,CA2BYiC,CAAe9E,GAErBsB,IACFiD,GA/HgB,MAmGpB,SAA6BjD,GAE3B,MAAMO,EAAS,GACf,IAAK,MAAM4R,KAAQnS,EAAa,CAC9B,MAAMoS,EAAI9R,SAAS6R,EAAM,IACzB5R,EAAOe,KAAW,IAAN8Q,EAAUzQ,GAAOjB,GAAK0R,EAAI,GACxC,CACA,OAAO7R,EAAOgB,KAAK,GACrB,CAoB4B8Q,CAAmBrS,IAGtCiD,CACT,OCoFA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAE7DgS,EAAM1O,EAAQ2O,WAAa,GAAK,IACtC,IAAIvN,EAAS,GAYb,OAVIxE,IACFwE,EA3Na,OA2NOsN,GAGtBtN,GAAUO,GAAe9E,EAAamD,GAElC7B,IACFiD,GAAUsN,EAhOM,SAgOcA,EAnDlC,SAA6BvQ,EAAa6B,GACxC,MAAM0O,EAAM1O,EAAQ2O,WAAa,GAAK,IACtC,IAAIvN,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAUsN,GACtBtN,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAUsN,GACtBtN,GAAUO,GAAezE,OAAO6F,GAAY/C,IAGvCoB,CACT,CA+BwCoP,CAAmBrS,EAAa6B,IAG/DoB,CACT,OC9DA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,UAGXjP,GAAUO,GAAe9E,EAAamD,GAElC7B,IACFiD,GAAU,SAxCd,SAA6BjD,EAAa6B,GACxC,IAAIoB,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,GAAY/C,IAGvCoB,CACT,CAuBwCoP,CAAmBrS,EAAa6B,IAG/DoB,CACT,OCvEA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,SAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,YArCd,SAA6BjD,GAC3B,IAAIiD,EAAS,GACTvB,EAAI,EAER,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAvGS,MAwGTvB,IAGF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CAoBwCoP,CAAmBrS,IAGlDiD,CACT,OCyIA,SAAkB1E,GAChB,MAAME,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAEnE,IAAI0E,EAAS,GAYb,OAVIxE,IACFwE,EAASiP,OAGXjP,GAAUO,GAAe9E,GAErBsB,IACFiD,GAAU,SAjDd,SAA6BjD,GAC3B,IAAIiD,EAAS,GAGTvB,EAAI,EACR,KAAOA,EAAI1B,EAAYR,QAA6B,MAAnBQ,EAAY0B,IACvCuB,IAAQA,GAAU,KACtBA,GAAUtB,GACVD,IAIF,MAAMkD,EAAY5E,EAAYJ,MAAM8B,GAMpC,OALIkD,IACE3B,IAAQA,GAAU,KACtBA,GAAUO,GAAezE,OAAO6F,KAG3B3B,CACT,CA8BwCoP,CAAmBrS,IAGlDiD,CACT,WCrJA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAC7DkV,GAA4B,IAAnB5R,EAAQ4R,OAEvB,IAAIxQ,EAASxE,EAjIE,IAiIsB,GASrC,OAPAwE,GApDF,SAAyBlC,EAAG0S,GAAS,GACnC,GAAU,KAAN1S,EAAU,OAAOY,GAErB,MAAMX,EAAOyS,EAAStC,GAAcD,GAC9BG,EAAMoC,EA5FK,IANA,IAmGXnC,EAAUmC,EA5FK,IANA,IAmGflC,EAAWkC,EA5FK,IANA,IAqGtB,GAAI1S,GAAK,WAAc,CACrB,MACM2S,EAAc3S,EAAI,WAExB,IAAIkC,EAAS8O,GAHGhR,EAAI,WAGiBC,EAAMqQ,EAAKC,EAASC,GA/F7C,IAsGZ,OALImC,EAAc,KACZA,EAAc,YAAazQ,GAAUtB,IACzCsB,GAAU8O,GAAe2B,EAAa1S,EAAMqQ,EAAKC,EAASC,IAGrDtO,CACT,CAEA,OAAO8O,GAAehR,EAAGC,EAAMqQ,EAAKC,EAASC,EAC/C,CA4BY/N,CAAe9E,EAAa+U,GAElCzT,IAEFiD,GAtIgB,IA2GpB,SAA+B0Q,EAAe3S,GAC5C,IAAIiC,EAAS,GACb,IAAK,IAAIvB,EAAI,EAAGA,EAAIiS,EAAcnU,OAAQkC,IACxCuB,GAAUjC,EAAKrC,OAAOgV,EAAcjS,KAEtC,OAAOuB,CACT,CAqB4B2Q,CAAqB5T,EADhCyT,EAAStC,GAAcD,KAI/BjO,CACT,WCEA,SAAkB1E,EAAOsD,GACvBA,EAAUD,EAAgBC,GAC1B,MAAMpD,WAAEA,EAAUC,YAAEA,EAAWsB,YAAEA,GAAgB1B,EAAkBC,GAC7DkV,GAA4B,IAAnB5R,EAAQ4R,OAEvB,IAAIxQ,EAAS,GAYb,OAVIxE,IACFwE,EAjJa,KAoJfA,GA7IF,SAAyBlC,EAAG0S,GAAS,GACnC,GAAU,KAAN1S,EAAU,OAAOY,GAErB,MAAMX,EAAOyS,EAAStC,GAAcD,GAC9BG,EAAMoC,EApBK,IANA,IA2BXnC,EAAUmC,EApBK,IANA,IA2BflC,EAAWkC,EApBK,IANA,IA6BtB,SAASrC,EAAiB7S,GACxB,GAAc,KAAVA,EAAc,MAAO,GAEzB,MAAM8C,EAAQ,GACd,IAAImQ,GAAY,EAGhB,MAAMC,EAAelT,EAAQ,MACvBmT,EAAqBnT,EAAQ,MAC/BkT,EAAe,KACjBpQ,EAAMC,KAAKN,EAAKrC,OAAO8S,IAAiBF,GACxCC,EAAYE,EAAqB,IAAMA,EAAqB,MAI9D,MAAMC,EAAcD,EAAqB,KACnCE,EAAoBF,EAAqB,KAC3CC,EAAc,IACZH,IACFnQ,EAAMC,KAAKK,IACX6P,GAAY,GAEdnQ,EAAMC,KAAKN,EAAKrC,OAAOgT,IAAgBL,GACvCE,EAAYI,EAAoB,IAAMA,EAAoB,KACjDH,EAAe,IAAMG,EAAoB,KAClDJ,GAAY,GAId,MAAMK,EAAUD,EAAoB,IAC9BE,EAAUF,EAAoB,IAmBpC,OAlBIC,EAAU,IACRL,IACFnQ,EAAMC,KAAKK,IACX6P,GAAY,GAEdnQ,EAAMC,KAAKN,EAAKrC,OAAOkT,IAAYR,KACzBM,EAAc,IAAMF,EAAe,KAAOK,EAAU,KAC9DN,GAAY,GAIVM,EAAU,KACRN,GACFnQ,EAAMC,KAAKK,IAEbN,EAAMC,KAAKN,EAAKrC,OAAOmT,MAGlBzQ,EAAME,KAAK,GACpB,CAGA,SAASwQ,EAAgBxT,GACvB,GAAc,KAAVA,EAAc,MAAO,GAEzB,MAAM8C,EAAQ,GAEd,GAAI9C,GAAS,OAAS,CACpB,MAAMyT,EAAWzT,EAAQ,OACnB0T,EAAe1T,EAAQ,OAE7B8C,EAAMC,KAAK8P,EAAgBY,GAlFhB,KAoFPC,EAAe,MACOD,EAAW,KAAQ,IACVC,EAAe,QAG9C5Q,EAAMC,KAAKK,IAEbN,EAAMC,KAAK8P,EAAgBa,IAE/B,MACE5Q,EAAMC,KAAK8P,EAAgB7S,IAG7B,OAAO8C,EAAME,KAAK,GACpB,CAGA,MAAMF,EAAQ,GAEd,GAAIN,GAAK,WAAc,CACrB,MACM2S,EAAc3S,EAAI,WAElB8S,EAAU9B,EAHAhR,EAAI,YAIpBM,EAAMC,KAAKuS,EA3GC,KA6GRH,EAAc,KACEA,EAAc,WAE9BrS,EAAMC,KAAKK,IAEbN,EAAMC,KAAKyQ,EAAe2B,IAE9B,MACErS,EAAMC,KAAKyQ,EAAehR,IAG5B,OAAOM,EAAME,KAAK,GACpB,CA8BYiC,CAAe9E,EAAa+U,GAElCzT,IACFiD,GAtJgB,IAuHpB,SAA+B0Q,EAAeF,GAAS,GACrD,MAAMzS,EAAOyS,EAAStC,GAAcD,GAC9BlM,EAAQ,GACd,IAAK,MAAMmN,KAAQwB,EACjB3O,EAAM1D,KAAKN,EAAKrC,OAAOwT,KAEzB,OAAOnN,CACT,CAwB4B4O,CAAqB5T,EAAayT,GAAQlS,KAAK,KAGlE0B,CACT"}