oakscriptjs 0.10.1 → 0.11.1

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 (271) hide show
  1. package/dist/cjs/array/index.d.ts +8 -2
  2. package/dist/cjs/array/index.d.ts.map +1 -1
  3. package/dist/cjs/array/index.js +2 -1
  4. package/dist/cjs/array/index.js.map +2 -2
  5. package/dist/cjs/box/index.d.ts.map +1 -1
  6. package/dist/cjs/box/index.js +7 -1
  7. package/dist/cjs/box/index.js.map +2 -2
  8. package/dist/cjs/callsite/index.d.ts +73 -32
  9. package/dist/cjs/callsite/index.d.ts.map +1 -1
  10. package/dist/cjs/callsite/index.js +69 -65
  11. package/dist/cjs/callsite/index.js.map +2 -2
  12. package/dist/cjs/drawing/registry.d.ts +84 -0
  13. package/dist/cjs/drawing/registry.d.ts.map +1 -1
  14. package/dist/cjs/drawing/registry.js +153 -6
  15. package/dist/cjs/drawing/registry.js.map +2 -2
  16. package/dist/cjs/index.d.ts +8 -2
  17. package/dist/cjs/index.d.ts.map +1 -1
  18. package/dist/cjs/index.js +33 -1
  19. package/dist/cjs/index.js.map +2 -2
  20. package/dist/cjs/input.d.ts +9 -1
  21. package/dist/cjs/input.d.ts.map +1 -1
  22. package/dist/cjs/input.js +16 -0
  23. package/dist/cjs/input.js.map +2 -2
  24. package/dist/cjs/label/index.d.ts.map +1 -1
  25. package/dist/cjs/label/index.js +3 -0
  26. package/dist/cjs/label/index.js.map +2 -2
  27. package/dist/cjs/line/index.d.ts +10 -27
  28. package/dist/cjs/line/index.d.ts.map +1 -1
  29. package/dist/cjs/line/index.js +11 -22
  30. package/dist/cjs/line/index.js.map +2 -2
  31. package/dist/cjs/linefill/index.d.ts +3 -0
  32. package/dist/cjs/linefill/index.d.ts.map +1 -1
  33. package/dist/cjs/linefill/index.js +15 -1
  34. package/dist/cjs/linefill/index.js.map +3 -3
  35. package/dist/cjs/math/index.d.ts.map +1 -1
  36. package/dist/cjs/math/index.js +2 -0
  37. package/dist/cjs/math/index.js.map +2 -2
  38. package/dist/cjs/polyline/index.d.ts.map +1 -1
  39. package/dist/cjs/polyline/index.js +3 -0
  40. package/dist/cjs/polyline/index.js.map +2 -2
  41. package/dist/cjs/runtime/adapters/SimpleInputAdapter.js +1 -1
  42. package/dist/cjs/runtime/adapters/SimpleInputAdapter.js.map +2 -2
  43. package/dist/cjs/runtime/index.d.ts +1 -1
  44. package/dist/cjs/runtime/index.d.ts.map +1 -1
  45. package/dist/cjs/runtime/index.js +1 -0
  46. package/dist/cjs/runtime/index.js.map +2 -2
  47. package/dist/cjs/runtime/inputs.d.ts +9 -0
  48. package/dist/cjs/runtime/inputs.d.ts.map +1 -1
  49. package/dist/cjs/runtime/inputs.js +8 -0
  50. package/dist/cjs/runtime/inputs.js.map +2 -2
  51. package/dist/cjs/runtime/types.d.ts +1 -1
  52. package/dist/cjs/runtime/types.d.ts.map +1 -1
  53. package/dist/cjs/runtime/types.js.map +1 -1
  54. package/dist/cjs/script/index.d.ts +91 -21
  55. package/dist/cjs/script/index.d.ts.map +1 -1
  56. package/dist/cjs/script/index.js +207 -19
  57. package/dist/cjs/script/index.js.map +3 -3
  58. package/dist/cjs/security/other.d.ts +92 -0
  59. package/dist/cjs/security/other.d.ts.map +1 -0
  60. package/dist/cjs/security/other.js +164 -0
  61. package/dist/cjs/security/other.js.map +7 -0
  62. package/dist/cjs/session/bars.d.ts +24 -1
  63. package/dist/cjs/session/bars.d.ts.map +1 -1
  64. package/dist/cjs/session/bars.js +56 -7
  65. package/dist/cjs/session/bars.js.map +3 -3
  66. package/dist/cjs/session/calendar.d.ts +13 -2
  67. package/dist/cjs/session/calendar.d.ts.map +1 -1
  68. package/dist/cjs/session/calendar.js +39 -9
  69. package/dist/cjs/session/calendar.js.map +2 -2
  70. package/dist/cjs/session/context.d.ts +76 -0
  71. package/dist/cjs/session/context.d.ts.map +1 -0
  72. package/dist/cjs/session/context.js +114 -0
  73. package/dist/cjs/session/context.js.map +7 -0
  74. package/dist/cjs/str/floatingdecimal.d.ts +31 -0
  75. package/dist/cjs/str/floatingdecimal.d.ts.map +1 -0
  76. package/dist/cjs/str/floatingdecimal.js +298 -0
  77. package/dist/cjs/str/floatingdecimal.js.map +7 -0
  78. package/dist/cjs/str/index.d.ts +4 -1
  79. package/dist/cjs/str/index.d.ts.map +1 -1
  80. package/dist/cjs/str/index.js +2 -1
  81. package/dist/cjs/str/index.js.map +2 -2
  82. package/dist/cjs/str/numberformat.d.ts +7 -3
  83. package/dist/cjs/str/numberformat.d.ts.map +1 -1
  84. package/dist/cjs/str/numberformat.js +4 -3
  85. package/dist/cjs/str/numberformat.js.map +2 -2
  86. package/dist/cjs/ta/index.d.ts +67 -34
  87. package/dist/cjs/ta/index.d.ts.map +1 -1
  88. package/dist/cjs/ta/index.js +202 -145
  89. package/dist/cjs/ta/index.js.map +2 -2
  90. package/dist/cjs/ta/limits.d.ts +35 -0
  91. package/dist/cjs/ta/limits.d.ts.map +1 -0
  92. package/dist/cjs/ta/limits.js +64 -0
  93. package/dist/cjs/ta/limits.js.map +7 -0
  94. package/dist/cjs/ta/running-sum.d.ts +0 -16
  95. package/dist/cjs/ta/running-sum.d.ts.map +1 -1
  96. package/dist/cjs/ta/running-sum.js +2 -0
  97. package/dist/cjs/ta/running-sum.js.map +2 -2
  98. package/dist/cjs/ta-series.d.ts +8 -8
  99. package/dist/cjs/ta-series.d.ts.map +1 -1
  100. package/dist/cjs/ta-series.js +10 -8
  101. package/dist/cjs/ta-series.js.map +2 -2
  102. package/dist/cjs/table/index.d.ts +3 -1
  103. package/dist/cjs/table/index.d.ts.map +1 -1
  104. package/dist/cjs/table/index.js +1 -0
  105. package/dist/cjs/table/index.js.map +3 -3
  106. package/dist/cjs/time/index.d.ts +5 -2
  107. package/dist/cjs/time/index.d.ts.map +1 -1
  108. package/dist/cjs/time/index.js +2 -2
  109. package/dist/cjs/time/index.js.map +2 -2
  110. package/dist/cjs/time/session.d.ts +26 -1
  111. package/dist/cjs/time/session.d.ts.map +1 -1
  112. package/dist/cjs/time/session.js +94 -12
  113. package/dist/cjs/time/session.js.map +3 -3
  114. package/dist/cjs/time/timezone.d.ts +6 -1
  115. package/dist/cjs/time/timezone.d.ts.map +1 -1
  116. package/dist/cjs/time/timezone.js +2 -2
  117. package/dist/cjs/time/timezone.js.map +2 -2
  118. package/dist/cjs/types/metadata.d.ts +28 -4
  119. package/dist/cjs/types/metadata.d.ts.map +1 -1
  120. package/dist/cjs/types/metadata.js.map +1 -1
  121. package/dist/esm/array/index.d.ts +8 -2
  122. package/dist/esm/array/index.d.ts.map +1 -1
  123. package/dist/esm/array/index.js +12 -3
  124. package/dist/esm/array/index.js.map +1 -1
  125. package/dist/esm/box/index.d.ts.map +1 -1
  126. package/dist/esm/box/index.js +8 -2
  127. package/dist/esm/box/index.js.map +1 -1
  128. package/dist/esm/callsite/index.d.ts +73 -32
  129. package/dist/esm/callsite/index.d.ts.map +1 -1
  130. package/dist/esm/callsite/index.js +143 -106
  131. package/dist/esm/callsite/index.js.map +1 -1
  132. package/dist/esm/drawing/registry.d.ts +84 -0
  133. package/dist/esm/drawing/registry.d.ts.map +1 -1
  134. package/dist/esm/drawing/registry.js +260 -6
  135. package/dist/esm/drawing/registry.js.map +1 -1
  136. package/dist/esm/index.d.ts +8 -2
  137. package/dist/esm/index.d.ts.map +1 -1
  138. package/dist/esm/index.js +13 -2
  139. package/dist/esm/index.js.map +1 -1
  140. package/dist/esm/input.d.ts +9 -1
  141. package/dist/esm/input.d.ts.map +1 -1
  142. package/dist/esm/input.js +16 -0
  143. package/dist/esm/input.js.map +1 -1
  144. package/dist/esm/label/index.d.ts.map +1 -1
  145. package/dist/esm/label/index.js +4 -1
  146. package/dist/esm/label/index.js.map +1 -1
  147. package/dist/esm/line/index.d.ts +10 -27
  148. package/dist/esm/line/index.d.ts.map +1 -1
  149. package/dist/esm/line/index.js +22 -60
  150. package/dist/esm/line/index.js.map +1 -1
  151. package/dist/esm/linefill/index.d.ts +3 -0
  152. package/dist/esm/linefill/index.d.ts.map +1 -1
  153. package/dist/esm/linefill/index.js +20 -1
  154. package/dist/esm/linefill/index.js.map +1 -1
  155. package/dist/esm/math/index.d.ts.map +1 -1
  156. package/dist/esm/math/index.js +3 -0
  157. package/dist/esm/math/index.js.map +1 -1
  158. package/dist/esm/polyline/index.d.ts.map +1 -1
  159. package/dist/esm/polyline/index.js +5 -1
  160. package/dist/esm/polyline/index.js.map +1 -1
  161. package/dist/esm/runtime/adapters/SimpleInputAdapter.js +1 -1
  162. package/dist/esm/runtime/adapters/SimpleInputAdapter.js.map +1 -1
  163. package/dist/esm/runtime/index.d.ts +1 -1
  164. package/dist/esm/runtime/index.d.ts.map +1 -1
  165. package/dist/esm/runtime/index.js +1 -1
  166. package/dist/esm/runtime/index.js.map +1 -1
  167. package/dist/esm/runtime/inputs.d.ts +9 -0
  168. package/dist/esm/runtime/inputs.d.ts.map +1 -1
  169. package/dist/esm/runtime/inputs.js +15 -0
  170. package/dist/esm/runtime/inputs.js.map +1 -1
  171. package/dist/esm/runtime/types.d.ts +1 -1
  172. package/dist/esm/runtime/types.d.ts.map +1 -1
  173. package/dist/esm/script/index.d.ts +91 -21
  174. package/dist/esm/script/index.d.ts.map +1 -1
  175. package/dist/esm/script/index.js +259 -28
  176. package/dist/esm/script/index.js.map +1 -1
  177. package/dist/esm/security/other.d.ts +92 -0
  178. package/dist/esm/security/other.d.ts.map +1 -0
  179. package/dist/esm/security/other.js +211 -0
  180. package/dist/esm/security/other.js.map +1 -0
  181. package/dist/esm/session/bars.d.ts +24 -1
  182. package/dist/esm/session/bars.d.ts.map +1 -1
  183. package/dist/esm/session/bars.js +75 -6
  184. package/dist/esm/session/bars.js.map +1 -1
  185. package/dist/esm/session/calendar.d.ts +13 -2
  186. package/dist/esm/session/calendar.d.ts.map +1 -1
  187. package/dist/esm/session/calendar.js +54 -10
  188. package/dist/esm/session/calendar.js.map +1 -1
  189. package/dist/esm/session/context.d.ts +76 -0
  190. package/dist/esm/session/context.d.ts.map +1 -0
  191. package/dist/esm/session/context.js +124 -0
  192. package/dist/esm/session/context.js.map +1 -0
  193. package/dist/esm/str/floatingdecimal.d.ts +31 -0
  194. package/dist/esm/str/floatingdecimal.d.ts.map +1 -0
  195. package/dist/esm/str/floatingdecimal.js +272 -0
  196. package/dist/esm/str/floatingdecimal.js.map +1 -0
  197. package/dist/esm/str/index.d.ts +4 -1
  198. package/dist/esm/str/index.d.ts.map +1 -1
  199. package/dist/esm/str/index.js +9 -2
  200. package/dist/esm/str/index.js.map +1 -1
  201. package/dist/esm/str/numberformat.d.ts +7 -3
  202. package/dist/esm/str/numberformat.d.ts.map +1 -1
  203. package/dist/esm/str/numberformat.js +11 -5
  204. package/dist/esm/str/numberformat.js.map +1 -1
  205. package/dist/esm/ta/index.d.ts +67 -34
  206. package/dist/esm/ta/index.d.ts.map +1 -1
  207. package/dist/esm/ta/index.js +284 -240
  208. package/dist/esm/ta/index.js.map +1 -1
  209. package/dist/esm/ta/limits.d.ts +35 -0
  210. package/dist/esm/ta/limits.d.ts.map +1 -0
  211. package/dist/esm/ta/limits.js +67 -0
  212. package/dist/esm/ta/limits.js.map +1 -0
  213. package/dist/esm/ta/running-sum.d.ts +0 -16
  214. package/dist/esm/ta/running-sum.d.ts.map +1 -1
  215. package/dist/esm/ta/running-sum.js +2 -0
  216. package/dist/esm/ta/running-sum.js.map +1 -1
  217. package/dist/esm/ta-series.d.ts +8 -8
  218. package/dist/esm/ta-series.d.ts.map +1 -1
  219. package/dist/esm/ta-series.js +11 -8
  220. package/dist/esm/ta-series.js.map +1 -1
  221. package/dist/esm/table/index.d.ts +3 -1
  222. package/dist/esm/table/index.d.ts.map +1 -1
  223. package/dist/esm/table/index.js +6 -1
  224. package/dist/esm/table/index.js.map +1 -1
  225. package/dist/esm/time/index.d.ts +5 -2
  226. package/dist/esm/time/index.d.ts.map +1 -1
  227. package/dist/esm/time/index.js +6 -3
  228. package/dist/esm/time/index.js.map +1 -1
  229. package/dist/esm/time/session.d.ts +26 -1
  230. package/dist/esm/time/session.d.ts.map +1 -1
  231. package/dist/esm/time/session.js +141 -16
  232. package/dist/esm/time/session.js.map +1 -1
  233. package/dist/esm/time/timezone.d.ts +6 -1
  234. package/dist/esm/time/timezone.d.ts.map +1 -1
  235. package/dist/esm/time/timezone.js +8 -2
  236. package/dist/esm/time/timezone.js.map +1 -1
  237. package/dist/esm/types/metadata.d.ts +28 -4
  238. package/dist/esm/types/metadata.d.ts.map +1 -1
  239. package/package.json +1 -1
  240. package/src/array/index.ts +11 -3
  241. package/src/box/index.ts +8 -2
  242. package/src/callsite/index.ts +150 -100
  243. package/src/drawing/registry.ts +278 -5
  244. package/src/index.ts +44 -1
  245. package/src/input.ts +18 -1
  246. package/src/label/index.ts +4 -1
  247. package/src/line/index.ts +22 -64
  248. package/src/linefill/index.ts +20 -1
  249. package/src/math/index.ts +2 -0
  250. package/src/polyline/index.ts +3 -1
  251. package/src/runtime/adapters/SimpleInputAdapter.ts +1 -1
  252. package/src/runtime/index.ts +1 -0
  253. package/src/runtime/inputs.ts +16 -0
  254. package/src/runtime/types.ts +1 -1
  255. package/src/script/index.ts +355 -34
  256. package/src/security/other.ts +269 -0
  257. package/src/session/bars.ts +70 -6
  258. package/src/session/calendar.ts +60 -10
  259. package/src/session/context.ts +162 -0
  260. package/src/str/floatingdecimal.ts +268 -0
  261. package/src/str/index.ts +8 -2
  262. package/src/str/numberformat.ts +13 -6
  263. package/src/ta/index.ts +304 -248
  264. package/src/ta/limits.ts +74 -0
  265. package/src/ta/running-sum.ts +2 -0
  266. package/src/ta-series.ts +19 -16
  267. package/src/table/index.ts +4 -1
  268. package/src/time/index.ts +6 -3
  269. package/src/time/session.ts +137 -15
  270. package/src/time/timezone.ts +9 -2
  271. package/src/types/metadata.ts +28 -4
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var floatingdecimal_exports = {};
20
+ __export(floatingdecimal_exports, {
21
+ convertDouble: () => convertDouble,
22
+ javaFixedDigits: () => javaFixedDigits
23
+ });
24
+ module.exports = __toCommonJS(floatingdecimal_exports);
25
+ const EXP_SHIFT = 52;
26
+ const MAX_SMALL_BIN_EXP = 62;
27
+ const MIN_SMALL_BIN_EXP = -21;
28
+ const N_5_BITS = [0, 3, 5, 7, 10, 12, 14, 17, 19, 21, 24, 26, 28, 31, 33, 35, 38, 40, 42, 45, 47, 49, 52, 54, 56, 59, 61];
29
+ const INSIGNIFICANT_DIGITS = [
30
+ 0,
31
+ 0,
32
+ 0,
33
+ 0,
34
+ 1,
35
+ 1,
36
+ 1,
37
+ 2,
38
+ 2,
39
+ 2,
40
+ 3,
41
+ 3,
42
+ 3,
43
+ 3,
44
+ 4,
45
+ 4,
46
+ 4,
47
+ 5,
48
+ 5,
49
+ 5,
50
+ 6,
51
+ 6,
52
+ 6,
53
+ 6,
54
+ 7,
55
+ 7,
56
+ 7,
57
+ 8,
58
+ 8,
59
+ 8,
60
+ 9,
61
+ 9,
62
+ 9,
63
+ 9,
64
+ 10,
65
+ 10,
66
+ 10,
67
+ 11,
68
+ 11,
69
+ 11,
70
+ 12,
71
+ 12,
72
+ 12,
73
+ 12,
74
+ 13,
75
+ 13,
76
+ 13,
77
+ 14,
78
+ 14,
79
+ 14,
80
+ 15,
81
+ 15,
82
+ 15,
83
+ 15,
84
+ 16,
85
+ 16,
86
+ 16,
87
+ 17,
88
+ 17,
89
+ 17,
90
+ 18,
91
+ 18,
92
+ 18,
93
+ 19
94
+ ];
95
+ const LONG_5_POW_LENGTH = 27;
96
+ const view = new DataView(new ArrayBuffer(8));
97
+ const int32 = (v) => BigInt.asIntN(32, v);
98
+ const int64 = (v) => BigInt.asIntN(64, v);
99
+ function trailingZeros(v) {
100
+ let n = 0;
101
+ while ((v & 1n) === 0n) {
102
+ v >>= 1n;
103
+ n++;
104
+ }
105
+ return n;
106
+ }
107
+ function estimateDecExp(fractBits, binExp) {
108
+ view.setBigUint64(0, 1023n << 52n | fractBits & 0xfffffffffffffn);
109
+ const d2 = view.getFloat64(0);
110
+ const d = (d2 - 1.5) * 0.289529654 + 0.176091259 + binExp * 0.301029995663981;
111
+ const r = Math.trunc(d);
112
+ return d < 0 && r !== d ? r - 1 : r;
113
+ }
114
+ function convertDouble(x) {
115
+ view.setFloat64(0, Math.abs(x));
116
+ const bits = view.getBigUint64(0);
117
+ let fractBits = bits & 0xfffffffffffffn;
118
+ let binExp = Number(bits >> 52n & 0x7ffn);
119
+ if (binExp === 0 && fractBits === 0n) return { digits: "0", decExponent: 1, roundedUp: false, exact: false };
120
+ let nSignificantBits;
121
+ if (binExp === 0) {
122
+ const leadingZeros = 64 - fractBits.toString(2).length;
123
+ const shift = leadingZeros - (63 - EXP_SHIFT);
124
+ fractBits <<= BigInt(shift);
125
+ binExp = 1 - shift;
126
+ nSignificantBits = 64 - leadingZeros;
127
+ } else {
128
+ fractBits |= 1n << 52n;
129
+ nSignificantBits = EXP_SHIFT + 1;
130
+ }
131
+ binExp -= 1023;
132
+ const tailZeros = trailingZeros(fractBits);
133
+ const nFractBits = EXP_SHIFT + 1 - tailZeros;
134
+ const nTinyBits = Math.max(0, nFractBits - binExp - 1);
135
+ if (binExp <= MAX_SMALL_BIN_EXP && binExp >= MIN_SMALL_BIN_EXP && nTinyBits < LONG_5_POW_LENGTH && nFractBits + N_5_BITS[nTinyBits] < 64 && nTinyBits === 0) {
136
+ const p2 = binExp - nSignificantBits - 1;
137
+ const insignificant = binExp > nSignificantBits && p2 > 1 && p2 < INSIGNIFICANT_DIGITS.length ? INSIGNIFICANT_DIGITS[p2] : 0;
138
+ let lvalue = binExp >= EXP_SHIFT ? fractBits << BigInt(binExp - EXP_SHIFT) : fractBits >> BigInt(EXP_SHIFT - binExp);
139
+ if (insignificant !== 0) {
140
+ const pow10 = 10n ** BigInt(insignificant);
141
+ const residue = lvalue % pow10;
142
+ lvalue /= pow10;
143
+ if (residue >= pow10 >> 1n) lvalue++;
144
+ }
145
+ const s = lvalue.toString();
146
+ return { digits: s.replace(/0+$/, ""), decExponent: insignificant + s.length, roundedUp: false, exact: false };
147
+ }
148
+ let decExp = estimateDecExp(fractBits, binExp);
149
+ const B5 = Math.max(0, -decExp);
150
+ let B2 = B5 + nTinyBits + binExp;
151
+ const S5 = Math.max(0, decExp);
152
+ let S2 = S5 + nTinyBits;
153
+ const M5 = B5;
154
+ let M2 = B2 - nSignificantBits;
155
+ fractBits >>= BigInt(tailZeros);
156
+ B2 -= nFractBits - 1;
157
+ const common2factor = Math.min(B2, S2);
158
+ B2 -= common2factor;
159
+ S2 -= common2factor;
160
+ M2 -= common2factor;
161
+ if (nFractBits === 1) M2 -= 1;
162
+ if (M2 < 0) {
163
+ B2 -= M2;
164
+ S2 -= M2;
165
+ M2 = 0;
166
+ }
167
+ const digits = [];
168
+ let low;
169
+ let high;
170
+ let lowDigitDifference;
171
+ let exact;
172
+ const Bbits = nFractBits + B2 + (B5 < N_5_BITS.length ? N_5_BITS[B5] : B5 * 3);
173
+ const tenSbits = S2 + 1 + (S5 + 1 < N_5_BITS.length ? N_5_BITS[S5 + 1] : (S5 + 1) * 3);
174
+ const minTwo = decExp < -3 || decExp >= 8;
175
+ if (Bbits < 64 && tenSbits < 64) {
176
+ const wrap = Bbits < 32 && tenSbits < 32 ? int32 : int64;
177
+ let b = wrap(fractBits * 5n ** BigInt(B5) << BigInt(B2));
178
+ const s = wrap(5n ** BigInt(S5) << BigInt(S2));
179
+ let m = wrap(5n ** BigInt(M5) << BigInt(M2));
180
+ const tens = wrap(s * 10n);
181
+ let q = Number(b / s);
182
+ b = wrap(10n * (b % s));
183
+ m = wrap(m * 10n);
184
+ low = b < m;
185
+ high = wrap(b + m) > tens;
186
+ if (q === 0 && !high) decExp--;
187
+ else digits.push(q);
188
+ if (minTwo) high = low = false;
189
+ while (!low && !high) {
190
+ q = Number(b / s);
191
+ b = wrap(10n * (b % s));
192
+ m = wrap(m * 10n);
193
+ if (m > 0n) {
194
+ low = b < m;
195
+ high = wrap(b + m) > tens;
196
+ } else {
197
+ low = true;
198
+ high = true;
199
+ }
200
+ digits.push(q);
201
+ }
202
+ lowDigitDifference = wrap((b << 1n) - tens);
203
+ exact = b === 0n;
204
+ } else {
205
+ let b = fractBits * 5n ** BigInt(B5) << BigInt(B2);
206
+ const s = 5n ** BigInt(S5) << BigInt(S2);
207
+ let m = 5n ** BigInt(M5 + 1) << BigInt(M2 + 1);
208
+ const tens = s * 10n;
209
+ let q = Number(b / s);
210
+ b = b % s * 10n;
211
+ low = b < m;
212
+ high = b + m >= tens;
213
+ if (q === 0 && !high) decExp--;
214
+ else digits.push(q);
215
+ if (minTwo) high = low = false;
216
+ while (!low && !high) {
217
+ q = Number(b / s);
218
+ b = b % s * 10n;
219
+ m *= 10n;
220
+ low = b < m;
221
+ high = b + m >= tens;
222
+ digits.push(q);
223
+ }
224
+ lowDigitDifference = high && low ? (b << 1n) - tens : 0n;
225
+ exact = b === 0n;
226
+ }
227
+ let decExponent = decExp + 1;
228
+ let roundedUp = false;
229
+ const roundup = () => {
230
+ let i = digits.length - 1;
231
+ while (digits[i] === 9 && i > 0) digits[i--] = 0;
232
+ if (digits[i] === 9) {
233
+ decExponent += 1;
234
+ digits[0] = 1;
235
+ return;
236
+ }
237
+ digits[i] += 1;
238
+ roundedUp = true;
239
+ };
240
+ if (high) {
241
+ if (!low) roundup();
242
+ else if (lowDigitDifference === 0n) {
243
+ if (digits[digits.length - 1] % 2 !== 0) roundup();
244
+ } else if (lowDigitDifference > 0n) roundup();
245
+ }
246
+ const text = digits.join("").replace(/0+$/, "");
247
+ return { digits: text || "0", decExponent, roundedUp, exact };
248
+ }
249
+ function javaFixedDigits(x, maxFrac) {
250
+ if (x === 0) return { digits: "", intLen: 0 };
251
+ const c = convertDouble(x);
252
+ const digits = [...c.digits].map(Number);
253
+ let decimalAt = c.decExponent;
254
+ const n = digits.length;
255
+ if (decimalAt > 0 && decimalAt < 8) {
256
+ if (n <= decimalAt) digits.push(...Array(decimalAt - n + 1).fill(0));
257
+ } else if (decimalAt <= -3 || decimalAt >= 8) {
258
+ if (n === 1) digits.push(0);
259
+ }
260
+ let count = digits.length;
261
+ const shouldRoundUp = (keep2) => {
262
+ if (keep2 >= count) return false;
263
+ if (digits[keep2] > 5) return true;
264
+ if (digits[keep2] !== 5) return false;
265
+ if (keep2 === count - 1) {
266
+ if (c.exact) return keep2 > 0 && digits[keep2 - 1] % 2 !== 0;
267
+ return !c.roundedUp;
268
+ }
269
+ for (let i = keep2 + 1; i < count; i++) if (digits[i] !== 0) return true;
270
+ return false;
271
+ };
272
+ if (-decimalAt > maxFrac) return { digits: "", intLen: 0 };
273
+ if (-decimalAt === maxFrac) {
274
+ return shouldRoundUp(0) ? { digits: "1", intLen: decimalAt + 1 } : { digits: "", intLen: 0 };
275
+ }
276
+ while (count > 1 && digits[count - 1] === 0) count--;
277
+ let keep = maxFrac + decimalAt;
278
+ if (keep >= 0 && keep < count) {
279
+ if (shouldRoundUp(keep)) {
280
+ do {
281
+ keep--;
282
+ if (keep < 0) {
283
+ digits[0] = 1;
284
+ decimalAt++;
285
+ keep = 0;
286
+ break;
287
+ }
288
+ digits[keep] += 1;
289
+ } while (digits[keep] > 9);
290
+ keep++;
291
+ }
292
+ count = keep;
293
+ while (count > 1 && digits[count - 1] === 0) count--;
294
+ }
295
+ const text = digits.slice(0, count).join("").replace(/0+$/, "");
296
+ return { digits: text, intLen: text ? decimalAt : 0 };
297
+ }
298
+ //# sourceMappingURL=floatingdecimal.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/str/floatingdecimal.ts"],
4
+ "sourcesContent": ["/**\n * Decimal digits of a double as PineScript's plain `str.tostring` produces them.\n *\n * PineScript formats a number without a format with the digits of the classic binary to decimal conversion of the\n * JVM (the one `Double.toString` used before Java 19), then rounds them to 10 decimals half to even, with two\n * flags of the conversion deciding a tie on the last digit: digits that were rounded up are not rounded up again,\n * digits that were cut on a '5' are. These digits are not always the shortest ones: 78195689105219552 keeps all\n * its digits (the shortest form is 78195689105219550), and 1990736.000001111 has 16 digits where rounding the exact\n * value would give 1990736.0000011111 (#174).\n */\n\n/** Digits d1d2d3... of |x| = 0.d1d2d3... \u00D7 10^decExponent, with the flags of the conversion. */\nexport interface ConvertedDigits {\n digits: string;\n decExponent: number;\n /** The last digit was rounded up */\n roundedUp: boolean;\n /** The digits are the exact value of the double */\n exact: boolean;\n}\n\nconst EXP_SHIFT = 52;\nconst MAX_SMALL_BIN_EXP = 62;\nconst MIN_SMALL_BIN_EXP = -21;\n// approximately ceil(log2(5^i))\nconst N_5_BITS = [0, 3, 5, 7, 10, 12, 14, 17, 19, 21, 24, 26, 28, 31, 33, 35, 38, 40, 42, 45, 47, 49, 52, 54, 56, 59, 61];\n// number of decimal digits of 2^i minus one, for i < 64\nconst INSIGNIFICANT_DIGITS = [\n 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10,\n 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19,\n];\nconst LONG_5_POW_LENGTH = 27;\nconst view = new DataView(new ArrayBuffer(8));\n\nconst int32 = (v: bigint): bigint => BigInt.asIntN(32, v);\nconst int64 = (v: bigint): bigint => BigInt.asIntN(64, v);\n\nfunction trailingZeros(v: bigint): number {\n let n = 0;\n while ((v & 1n) === 0n) {\n v >>= 1n;\n n++;\n }\n return n;\n}\n\n/** floor(log10(x)) estimate of the conversion (it can be one too high). */\nfunction estimateDecExp(fractBits: bigint, binExp: number): number {\n view.setBigUint64(0, (1023n << 52n) | (fractBits & 0xfffffffffffffn));\n const d2 = view.getFloat64(0);\n const d = (d2 - 1.5) * 0.289529654 + 0.176091259 + binExp * 0.301029995663981;\n // the conversion truncates toward zero and corrects negative non-integers down\n const r = Math.trunc(d);\n return d < 0 && r !== d ? r - 1 : r;\n}\n\n/** Digits of a positive finite double, as the JVM converter gives them. */\nexport function convertDouble(x: number): ConvertedDigits {\n view.setFloat64(0, Math.abs(x));\n const bits = view.getBigUint64(0);\n let fractBits = bits & 0xfffffffffffffn;\n let binExp = Number((bits >> 52n) & 0x7ffn);\n if (binExp === 0 && fractBits === 0n) return { digits: '0', decExponent: 1, roundedUp: false, exact: false };\n let nSignificantBits: number;\n if (binExp === 0) {\n const leadingZeros = 64 - fractBits.toString(2).length;\n const shift = leadingZeros - (63 - EXP_SHIFT);\n fractBits <<= BigInt(shift);\n binExp = 1 - shift;\n nSignificantBits = 64 - leadingZeros;\n } else {\n fractBits |= 1n << 52n;\n nSignificantBits = EXP_SHIFT + 1;\n }\n binExp -= 1023;\n\n const tailZeros = trailingZeros(fractBits);\n const nFractBits = EXP_SHIFT + 1 - tailZeros;\n const nTinyBits = Math.max(0, nFractBits - binExp - 1);\n\n // an integer that fits in a long: its digits, the low ones rounded away when they are not significant\n if (\n binExp <= MAX_SMALL_BIN_EXP &&\n binExp >= MIN_SMALL_BIN_EXP &&\n nTinyBits < LONG_5_POW_LENGTH &&\n nFractBits + N_5_BITS[nTinyBits]! < 64 &&\n nTinyBits === 0\n ) {\n const p2 = binExp - nSignificantBits - 1;\n const insignificant = binExp > nSignificantBits && p2 > 1 && p2 < INSIGNIFICANT_DIGITS.length ? INSIGNIFICANT_DIGITS[p2]! : 0;\n let lvalue = binExp >= EXP_SHIFT ? fractBits << BigInt(binExp - EXP_SHIFT) : fractBits >> BigInt(EXP_SHIFT - binExp);\n if (insignificant !== 0) {\n const pow10 = 10n ** BigInt(insignificant);\n const residue = lvalue % pow10;\n lvalue /= pow10;\n if (residue >= pow10 >> 1n) lvalue++;\n }\n const s = lvalue.toString();\n return { digits: s.replace(/0+$/, ''), decExponent: insignificant + s.length, roundedUp: false, exact: false };\n }\n\n let decExp = estimateDecExp(fractBits, binExp);\n const B5 = Math.max(0, -decExp);\n let B2 = B5 + nTinyBits + binExp;\n const S5 = Math.max(0, decExp);\n let S2 = S5 + nTinyBits;\n const M5 = B5;\n let M2 = B2 - nSignificantBits;\n\n fractBits >>= BigInt(tailZeros);\n B2 -= nFractBits - 1;\n const common2factor = Math.min(B2, S2);\n B2 -= common2factor;\n S2 -= common2factor;\n M2 -= common2factor;\n // an exact power of two: the next smaller double is half as far\n if (nFractBits === 1) M2 -= 1;\n if (M2 < 0) {\n B2 -= M2;\n S2 -= M2;\n M2 = 0;\n }\n\n const digits: number[] = [];\n let low: boolean;\n let high: boolean;\n let lowDigitDifference: bigint;\n let exact: boolean;\n const Bbits = nFractBits + B2 + (B5 < N_5_BITS.length ? N_5_BITS[B5]! : B5 * 3);\n const tenSbits = S2 + 1 + (S5 + 1 < N_5_BITS.length ? N_5_BITS[S5 + 1]! : (S5 + 1) * 3);\n // two digits at least in the exponent form\n const minTwo = decExp < -3 || decExp >= 8;\n\n if (Bbits < 64 && tenSbits < 64) {\n // int or long arithmetic, with its overflows\n const wrap = Bbits < 32 && tenSbits < 32 ? int32 : int64;\n let b = wrap((fractBits * 5n ** BigInt(B5)) << BigInt(B2));\n const s = wrap(5n ** BigInt(S5) << BigInt(S2));\n let m = wrap(5n ** BigInt(M5) << BigInt(M2));\n const tens = wrap(s * 10n);\n let q = Number(b / s);\n b = wrap(10n * (b % s));\n m = wrap(m * 10n);\n low = b < m;\n high = wrap(b + m) > tens;\n if (q === 0 && !high) decExp--;\n else digits.push(q);\n if (minTwo) high = low = false;\n while (!low && !high) {\n q = Number(b / s);\n b = wrap(10n * (b % s));\n m = wrap(m * 10n);\n if (m > 0n) {\n low = b < m;\n high = wrap(b + m) > tens;\n } else {\n low = true;\n high = true;\n }\n digits.push(q);\n }\n lowDigitDifference = wrap((b << 1n) - tens);\n exact = b === 0n;\n } else {\n let b = (fractBits * 5n ** BigInt(B5)) << BigInt(B2);\n const s = 5n ** BigInt(S5) << BigInt(S2);\n let m = 5n ** BigInt(M5 + 1) << BigInt(M2 + 1);\n const tens = s * 10n;\n let q = Number(b / s);\n b = (b % s) * 10n;\n low = b < m;\n high = b + m >= tens;\n if (q === 0 && !high) decExp--;\n else digits.push(q);\n if (minTwo) high = low = false;\n while (!low && !high) {\n q = Number(b / s);\n b = (b % s) * 10n;\n m *= 10n;\n low = b < m;\n high = b + m >= tens;\n digits.push(q);\n }\n lowDigitDifference = high && low ? (b << 1n) - tens : 0n;\n exact = b === 0n;\n }\n\n let decExponent = decExp + 1;\n let roundedUp = false;\n const roundup = (): void => {\n let i = digits.length - 1;\n while (digits[i] === 9 && i > 0) digits[i--] = 0;\n if (digits[i] === 9) {\n decExponent += 1;\n digits[0] = 1;\n return;\n }\n digits[i]! += 1;\n roundedUp = true;\n };\n if (high) {\n if (!low) roundup();\n else if (lowDigitDifference === 0n) {\n if (digits[digits.length - 1]! % 2 !== 0) roundup();\n } else if (lowDigitDifference > 0n) roundup();\n }\n const text = digits.join('').replace(/0+$/, '');\n return { digits: text || '0', decExponent, roundedUp, exact };\n}\n\n/**\n * `|x|` with at most `maxFrac` decimals, as the JVM's decimal formatter rounds the converted digits (half to even;\n * a tie on the last converted digit is decided by the conversion flags). Returns the digits and the position of the\n * decimal point (value = 0.digits \u00D7 10^intLen); \"\" for zero.\n */\nexport function javaFixedDigits(x: number, maxFrac: number): { digits: string; intLen: number } {\n if (x === 0) return { digits: '', intLen: 0 };\n const c = convertDouble(x);\n const digits = [...c.digits].map(Number);\n let decimalAt = c.decExponent;\n // the formatter reads the conversion's text, which ends in \".0\" when no digit follows the point (\"5.0E-11\",\n // \"120.0\"): that zero is a digit for the tie test below (5e-11 to 10 decimals gives 0)\n const n = digits.length;\n if (decimalAt > 0 && decimalAt < 8) {\n if (n <= decimalAt) digits.push(...Array<number>(decimalAt - n + 1).fill(0));\n } else if (decimalAt <= -3 || decimalAt >= 8) {\n if (n === 1) digits.push(0);\n }\n let count = digits.length;\n\n const shouldRoundUp = (keep: number): boolean => {\n if (keep >= count) return false;\n if (digits[keep]! > 5) return true;\n if (digits[keep] !== 5) return false;\n if (keep === count - 1) {\n if (c.exact) return keep > 0 && digits[keep - 1]! % 2 !== 0;\n return !c.roundedUp;\n }\n for (let i = keep + 1; i < count; i++) if (digits[i] !== 0) return true;\n return false;\n };\n\n if (-decimalAt > maxFrac) return { digits: '', intLen: 0 };\n if (-decimalAt === maxFrac) {\n return shouldRoundUp(0) ? { digits: '1', intLen: decimalAt + 1 } : { digits: '', intLen: 0 };\n }\n while (count > 1 && digits[count - 1] === 0) count--;\n let keep = maxFrac + decimalAt;\n if (keep >= 0 && keep < count) {\n if (shouldRoundUp(keep)) {\n do {\n keep--;\n if (keep < 0) {\n digits[0] = 1;\n decimalAt++;\n keep = 0;\n break;\n }\n digits[keep]! += 1;\n } while (digits[keep]! > 9);\n keep++;\n }\n count = keep;\n while (count > 1 && digits[count - 1] === 0) count--;\n }\n const text = digits.slice(0, count).join('').replace(/0+$/, '');\n return { digits: text, intLen: text ? decimalAt : 0 };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["keep"]
7
+ }
@@ -37,7 +37,10 @@ export declare function length(str: simple_string): int;
37
37
  *
38
38
  * @remarks
39
39
  * As in PineScript:
40
- * - without format, numbers show up to 10 decimals ("0.3333333333"); 1e21 and more use "1E21"
40
+ * - without format, numbers show up to 10 decimals ("0.3333333333"): the digits of the JVM's binary to decimal
41
+ * conversion, rounded half to even, a tie on the last converted digit decided by the conversion
42
+ * (82325.10000000005 gives "82325.1", with the format "#.##########" "82325.1000000001"; 1791662400001 / 900000
43
+ * gives "1990736.000001111"; 78195689105219552 keeps its 17 digits); 1e21 and more use "1E21"
41
44
  * - patterns round half away from zero on the shortest decimal form: 1.005 with "#.##" gives "1.01";
42
45
  * at most 16 decimals from 1e-3 up (0.0012345678901234567 with 20 '#' decimals gives "0.0012345678901235")
43
46
  * - "pos;neg" patterns: a negative number takes the text of the negative subpattern ("#.##;(#.##)": "(3.5)")
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/str/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/str/index.ts"],"names":[],"mappings":"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"}
@@ -49,6 +49,7 @@ function length(str) {
49
49
  return str.length;
50
50
  }
51
51
  function tostring(value, format2, mintick, pricescale) {
52
+ if (Array.isArray(value)) return "[" + value.map((v) => tostring(v, format2, mintick, pricescale)).join(", ") + "]";
52
53
  if (typeof value !== "number") return String(value);
53
54
  if (Number.isNaN(value)) return format2 === "percent" ? "NaN%" : "NaN";
54
55
  if (!Number.isFinite(value)) {
@@ -59,7 +60,7 @@ function tostring(value, format2, mintick, pricescale) {
59
60
  if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {
60
61
  return String(value).replace("e+", "E").replace(/\.0+E/, "E");
61
62
  }
62
- return (0, import_numberformat.formatNumber)(value, "#.##########", "halfUpShortest");
63
+ return (0, import_numberformat.formatNumber)(value, "#.##########", "halfEvenConverted");
63
64
  }
64
65
  switch (format2) {
65
66
  case "percent":
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/str/index.ts"],
4
- "sourcesContent": ["/**\n * String namespace\n * Mirrors PineScript's str.* functions for string manipulation and analysis.\n *\n * @remarks\n * All string functions in this namespace follow PineScript v6 API specifications.\n * String operations are case-sensitive unless otherwise specified.\n *\n * @version 6\n */\n\nimport { formatNumber, tickScale } from './numberformat.js';\nimport { formatMessage } from './messageformat.js';\nimport { formatDate } from './dateformat.js';\nimport type { int, bool, float, simple_int, simple_string } from '../types/index.js';\n\n/**\n * Returns the length of a string.\n *\n * @param str - The string to measure\n * @returns The number of characters in the string\n *\n * @example\n * ```typescript\n * str.length(\"Hello\") // Returns: 5\n * str.length(\"\") // Returns: 0\n * ```\n */\nexport function length(str: simple_string): int {\n return str.length;\n}\n\n/**\n * Converts a value to its string representation (PineScript `str.tostring`).\n *\n * @param value - The value to convert (number, boolean, string, etc.)\n * @param format - Optional number format: a pattern (\"#.##\", \"#,###.00\", \"0.00%\", \"0.00E0\", \"#.##;(#.##)\") or\n * `format.percent` (\"percent\"), `format.volume` (\"volume\"), `format.mintick` (\"mintick\"),\n * `format.price` / `format.inherit`\n * @param mintick - Tick size for `format.mintick` (PineScript `syminfo.mintick`, implicit there). Not part of the\n * PineScript signature: the core function cannot see the chart, so pass it, as for `math.round_to_mintick`\n * @param pricescale - Price scale of the symbol for `format.mintick` (PineScript `syminfo.pricescale`), needed for a\n * symbol priced in fractions (32 for a 1/32 tick). Default: 10^(decimals of `mintick`)\n * @returns String representation of the value\n *\n * @remarks\n * As in PineScript:\n * - without format, numbers show up to 10 decimals (\"0.3333333333\"); 1e21 and more use \"1E21\"\n * - patterns round half away from zero on the shortest decimal form: 1.005 with \"#.##\" gives \"1.01\";\n * at most 16 decimals from 1e-3 up (0.0012345678901234567 with 20 '#' decimals gives \"0.0012345678901235\")\n * - \"pos;neg\" patterns: a negative number takes the text of the negative subpattern (\"#.##;(#.##)\": \"(3.5)\")\n * - \"#\" is an optional digit (\"1.5\" with \"#.##\"), \"0\" a required digit (\"1.50\" with \"0.00\"),\n * \",\" groups thousands, \"%\" multiplies by 100, 'text' is literal text\n * - `format.percent` shows 2 decimals and \"%\" without multiplying; `format.volume` uses K, M, B, T\n * - `format.price` and `format.inherit` give \"price1\" / \"inherit1\" in PineScript: they are patterns\n * without digit characters, and oakscriptjs does the same\n * - `format.mintick` shows floor(log10(pricescale)) decimals, zeros included (\"1.00\", \"0.40\" for tick 0.01), and\n * rounds in two steps, with mintick = minmove / pricescale: the double quotient `|x| / (1 / pricescale)` half away\n * from zero (140.355 / 0.01 = 14035.499999999998 gives \"140.35\", where `math.round_to_mintick` gives 140.36),\n * then `floor(units / minmove + 0.5)` ticks (tick 0.02: 0.005 gives \"0.02\", -0.01 gives \"0.00\"). With a\n * pricescale that is no power of 10 the tick value is then rounded half even to those decimals (1/32: 25.25 gives\n * \"25.2\", -0.02 gives \"-0.0\"). It throws without `mintick`\n * - na gives \"NaN\" (\"NaN%\" with `format.percent`); an infinity gives \"NaN\" too (\"NaN%\" for `format.percent`,\n * \"NaNT\" for `format.volume`), except `format.mintick`, which gives \"Infinity\" / \"-Infinity\"\n *\n * @example\n * ```typescript\n * str.tostring(123) // \"123\"\n * str.tostring(1.5, \"#.##\") // \"1.5\"\n * str.tostring(0.5, \"#.00\") // \".50\"\n * str.tostring(12345.678, format.volume) // \"12.346K\"\n * ```\n */\nexport function tostring(value: any, format?: simple_string, mintick?: float, pricescale?: float): string {\n if (typeof value !== 'number') return String(value);\n if (Number.isNaN(value)) return format === 'percent' ? 'NaN%' : 'NaN';\n if (!Number.isFinite(value)) {\n // the chart formatter has no infinity: NaN with the format's suffix\n if (format === 'mintick') return String(value);\n return format === 'percent' ? 'NaN%' : format === 'volume' ? 'NaNT' : 'NaN';\n }\n if (format === undefined || format === '') {\n if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {\n return String(value).replace('e+', 'E').replace(/\\.0+E/, 'E');\n }\n return formatNumber(value, '#.##########', 'halfUpShortest');\n }\n switch (format) {\n case 'percent':\n return formatNumber(value, '0.00', 'halfUpShortest') + '%';\n case 'volume': {\n const abs = Math.abs(value);\n for (const [size, unit] of [[1e12, 'T'], [1e9, 'B'], [1e6, 'M'], [1e3, 'K']] as const) {\n if (abs >= size) return formatNumber(value / size, '#.###', 'halfUpShortest') + unit;\n }\n return formatNumber(value, '#', 'halfUpShortest');\n }\n case 'mintick': {\n if (mintick === undefined) {\n throw new Error('str.tostring(x, format.mintick) needs the tick size: pass it as str.tostring(x, format.mintick, syminfo.mintick)');\n }\n const [minmove, scale] = tickScale(mintick, pricescale);\n const decimals = Math.floor(Math.log10(scale) + 1e-9);\n const mask = decimals > 0 ? '0.' + '0'.repeat(decimals) : '0';\n if (!(minmove >= 1)) return formatNumber(value, mask, 'halfUpShortest');\n // 1. the double quotient |x| / (1 / pricescale), half away from zero (not math.round_to_mintick's exact decimal)\n const scaled = Math.abs(value) / (1 / scale);\n let units = Math.trunc(scaled);\n if (scaled - units >= 0.5) units++;\n // 2. to the tick, a half going up (minmove 2: -0.01 gives \"0.00\", 0.01 gives \"0.02\")\n const ticks = Math.floor((value < 0 ? -units : units) / minmove + 0.5);\n // 3. the tick value at the scale's decimals; a negative one keeps its sign when it prints as zero (\"-0.0\")\n const text = formatNumber((Math.abs(ticks) * minmove) / scale, mask, 'halfEvenExact');\n return ticks < 0 ? '-' + text : text;\n }\n default:\n return formatNumber(value, format, 'halfUpShortest');\n }\n}\n\n/**\n * Converts a string to a number (float).\n *\n * @param str - The string to convert\n * @returns The numeric value, or null (na) if conversion fails\n *\n * @remarks\n * As in PineScript:\n * - The whole text must be a number: a sign, digits and one optional \".\" (\"0.5\", \".5\", \"5.\", \"-12\"); \"12abc\" is na\n * - Scientific notation (\"1e3\"), \"Infinity\", \"NaN\", hexadecimal, \"1,000\" give na\n * - The characters U+0000 to U+0020 (space, tab, line feed...) around the number are ignored; other spaces\n * (no-break space U+00A0, U+3000) give na\n *\n * @example\n * ```typescript\n * str.tonumber(\"123\") // Returns: 123\n * str.tonumber(\"45.67\") // Returns: 45.67\n * str.tonumber(\"abc\") // Returns: null\n * str.tonumber(\"12abc\") // Returns: null\n * str.tonumber(\"1e3\") // Returns: null\n * ```\n */\nexport function tonumber(str: simple_string): float | null {\n // remove the characters U+0000..U+0020 at both ends\n let start = 0;\n let end = str.length;\n while (start < end && str.charCodeAt(start) <= 0x20) start++;\n while (end > start && str.charCodeAt(end - 1) <= 0x20) end--;\n const text = str.slice(start, end);\n if (!/^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)$/.test(text)) return null;\n return parseFloat(text);\n}\n\n/**\n * Extracts a substring from a string.\n *\n * @param str - The source string\n * @param begin - Starting index (0-based, inclusive)\n * @param end - Ending index (0-based, exclusive). If omitted, extracts to end of string\n * @returns The extracted substring\n *\n * @remarks\n * - An na `end` (NaN / null) is a runtime error in PineScript: it throws\n * - If begin > end, indices are swapped (JavaScript behavior)\n * - Negative indices are treated as 0\n * - Indices beyond string length return empty string\n *\n * @example\n * ```typescript\n * str.substring(\"hello world\", 0, 5) // Returns: \"hello\"\n * str.substring(\"hello world\", 6) // Returns: \"world\"\n * str.substring(\"test\", 1, 3) // Returns: \"es\"\n * ```\n */\nexport function substring(str: simple_string, begin: simple_int, end?: simple_int): string {\n if (end === null || Number.isNaN(end)) throw new Error('str.substring: the end position is na');\n return str.substring(begin, end);\n}\n\n/**\n * Converts all characters in a string to uppercase.\n *\n * @param str - The string to convert\n * @returns The uppercase string\n *\n * @example\n * ```typescript\n * str.upper(\"hello\") // Returns: \"HELLO\"\n * str.upper(\"Hello World\") // Returns: \"HELLO WORLD\"\n * str.upper(\"test123\") // Returns: \"TEST123\"\n * ```\n */\nexport function upper(str: simple_string): string {\n return str.toUpperCase();\n}\n\n/**\n * Converts all characters in a string to lowercase.\n *\n * @param str - The string to convert\n * @returns The lowercase string\n *\n * @example\n * ```typescript\n * str.lower(\"HELLO\") // Returns: \"hello\"\n * str.lower(\"Hello World\") // Returns: \"hello world\"\n * str.lower(\"TEST123\") // Returns: \"test123\"\n * ```\n */\nexport function lower(str: simple_string): string {\n return str.toLowerCase();\n}\n\n/**\n * Checks if a string contains a substring.\n *\n * @param source - The string to search in\n * @param str - The substring to search for\n * @returns True if source contains str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string is contained in any string\n *\n * @example\n * ```typescript\n * str.contains(\"hello world\", \"world\") // Returns: true\n * str.contains(\"hello world\", \"World\") // Returns: false (case-sensitive)\n * str.contains(\"test\", \"xyz\") // Returns: false\n * ```\n */\nexport function contains(source: simple_string, str: simple_string): bool {\n return source.includes(str);\n}\n\n/**\n * Returns the position of the first occurrence of a substring.\n *\n * @param source - The string to search in\n * @param str - The substring to search for\n * @returns The 0-based index of the first occurrence, or na (NaN) if not found\n *\n * @remarks\n * - Search is case-sensitive\n * - Returns 0 for empty search string\n * - Returns na (NaN) if substring is not found, as in PineScript\n *\n * @example\n * ```typescript\n * str.pos(\"hello world\", \"world\") // Returns: 6\n * str.pos(\"hello world\", \"o\") // Returns: 4 (first occurrence)\n * str.pos(\"hello\", \"xyz\") // Returns: NaN (na)\n * ```\n */\nexport function pos(source: simple_string, str: simple_string): int {\n const index = source.indexOf(str);\n return index === -1 ? NaN : index;\n}\n\n/**\n * Replaces the Nth occurrence of a substring (PineScript `str.replace`).\n *\n * @param source - The source string\n * @param target - The substring to replace\n * @param replacement - The string to replace with\n * @param occurrence - 0-based occurrence to replace (default 0, the first one)\n * @returns The string with one occurrence replaced\n *\n * @remarks\n * PineScript rules:\n * - only the Nth occurrence is replaced; an na occurrence is 0 (`str.replace(\"aaa\", \"a\", \"-\", na)` is \"-aa\")\n * - occurrences overlap: \"aa\" occurs at 0 and 1 in \"aaa\" (`str.replace(\"aaa\", \"aa\", \"-\", 1)` is \"a-\")\n * - an empty target inserts the replacement at character N, at most at the end\n * (`str.replace(\"abc\", \"\", \"-\", 2)` is \"ab-c\")\n * - fewer than N + 1 occurrences (or a negative N) leave the source unchanged\n *\n * @example\n * ```typescript\n * str.replace(\"hello world hello\", \"hello\", \"hi\") // Returns: \"hi world hello\"\n * str.replace(\"hello world hello\", \"hello\", \"hi\", 1) // Returns: \"hello world hi\"\n * str.replace(\"test\", \"xyz\", \"abc\") // Returns: \"test\"\n * ```\n */\nexport function replace(source: simple_string, target: simple_string, replacement: simple_string, occurrence: simple_int = 0): string {\n const n = occurrence === null || Number.isNaN(occurrence) ? 0 : Math.trunc(occurrence);\n if (n < 0) return source;\n let index: number;\n if (target === '') {\n index = Math.min(n, source.length);\n } else {\n index = -1;\n for (let k = 0; k <= n; k++) {\n index = source.indexOf(target, index + 1);\n if (index === -1) return source;\n }\n }\n return source.slice(0, index) + replacement + source.slice(index + target.length);\n}\n\n/**\n * Replaces all occurrences of a target string with a replacement string.\n *\n * @param source - The source string\n * @param target - The substring to find\n * @param replacement - The replacement string\n * @returns String with all occurrences replaced\n *\n * @remarks\n * - Replaces all occurrences of the target string\n * - Case sensitive\n * - If target is empty string, returns original string\n * - This matches PineScript's `str.replace_all()` behavior\n *\n * @example\n * ```typescript\n * str.replace_all(\"hello world hello\", \"hello\", \"hi\") // Returns: \"hi world hi\"\n * str.replace_all(\"a-b-c\", \"-\", \"_\") // Returns: \"a_b_c\"\n * str.replace_all(\"test\", \"x\", \"y\") // Returns: \"test\" (no match)\n * ```\n */\nexport function replace_all(source: simple_string, target: simple_string, replacement: simple_string): string {\n return source.replaceAll(target, replacement);\n}\n\n/**\n * Splits a string into an array of substrings using a separator.\n *\n * @param str - The string to split\n * @param separator - The separator string\n * @returns Array of substrings\n *\n * @remarks\n * - Empty separator splits into individual characters; an empty string gives [\"\"] (also with \"\")\n * - If separator is not found, returns array with original string\n * - Consecutive separators create empty strings in result\n *\n * @example\n * ```typescript\n * str.split(\"a,b,c\", \",\") // Returns: [\"a\", \"b\", \"c\"]\n * str.split(\"hello\", \"\") // Returns: [\"h\", \"e\", \"l\", \"l\", \"o\"]\n * str.split(\"a,,b\", \",\") // Returns: [\"a\", \"\", \"b\"]\n * ```\n */\nexport function split(str: simple_string, separator: simple_string): string[] {\n // JavaScript gives [] for \"\".split(\"\"); PineScript gives [\"\"]\n return str === '' ? [''] : str.split(separator);\n}\n\n/**\n * Concatenates multiple strings into one.\n *\n * @param strings - Variable number of strings to concatenate\n * @returns The concatenated string\n *\n * @example\n * ```typescript\n * str.concat(\"hello\", \" \", \"world\") // Returns: \"hello world\"\n * str.concat(\"a\", \"b\", \"c\") // Returns: \"abc\"\n * str.concat(\"test\") // Returns: \"test\"\n * ```\n */\nexport function concat(...strings: simple_string[]): string {\n return strings.join('');\n}\n\n/**\n * Formats a string with placeholders (PineScript `str.format`, Java MessageFormat syntax).\n *\n * @param formatStr - Pattern with `{0}`, `{1,number,#.##}`, `{0,number,percent}`, `{0,date,yyyy-MM-dd}`...\n * @param args - Values for the placeholders\n * @returns Formatted string\n *\n * @remarks\n * As in PineScript: numbers in `{n}` use\n * \"#,##0.###\" (1234.5678 gives \"1,234.568\"); number patterns round half to even on the exact\n * value (16 decimals at most from 1e-3 up); an array gives \"[a, b]\"; `'...'` is literal text and `''` a quote;\n * a missing argument leaves `{n}` in the text.\n * Dates are formatted in UTC here; PineScript uses the exchange time zone.\n *\n * @example\n * ```typescript\n * str.format('Close: {0,number,#.##}', 101.456) // \"Close: 101.46\"\n * str.format('{0} and {1}', 'a', 2) // \"a and 2\"\n * ```\n */\nexport function format(formatStr: simple_string, ...args: any[]): string {\n return formatMessage(formatStr, args, 'Etc/UTC');\n}\n\n/**\n * Checks if a string starts with a specific prefix.\n *\n * @param source - The string to check\n * @param str - The prefix to look for\n * @returns True if source starts with str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string prefix always returns true\n *\n * @example\n * ```typescript\n * str.startswith(\"hello world\", \"hello\") // Returns: true\n * str.startswith(\"hello world\", \"Hello\") // Returns: false (case-sensitive)\n * str.startswith(\"test\", \"testing\") // Returns: false\n * ```\n */\nexport function startswith(source: simple_string, str: simple_string): bool {\n return source.startsWith(str);\n}\n\n/**\n * Checks if a string ends with a specific suffix.\n *\n * @param source - The string to check\n * @param str - The suffix to look for\n * @returns True if source ends with str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string suffix always returns true\n *\n * @example\n * ```typescript\n * str.endswith(\"hello world\", \"world\") // Returns: true\n * str.endswith(\"hello world\", \"World\") // Returns: false (case-sensitive)\n * str.endswith(\"testing\", \"test\") // Returns: false\n * ```\n */\nexport function endswith(source: simple_string, str: simple_string): bool {\n return source.endsWith(str);\n}\n\n/**\n * Returns the character at a specific position in a string.\n *\n * @param str - The source string\n * @param pos - The 0-based position\n * @returns The character at the position, or empty string if out of bounds\n *\n * @remarks\n * - Returns empty string for negative or out-of-bounds positions\n * - Unicode characters may span multiple code units\n *\n * @example\n * ```typescript\n * str.charAt(\"hello\", 0) // Returns: \"h\"\n * str.charAt(\"hello\", 4) // Returns: \"o\"\n * str.charAt(\"hello\", 10) // Returns: \"\"\n * ```\n */\nexport function charAt(str: simple_string, pos: simple_int): string {\n return str.charAt(pos);\n}\n\n/**\n * Removes whitespace from both ends of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Removes spaces, tabs, newlines, and other whitespace characters\n * - Preserves internal whitespace\n *\n * @example\n * ```typescript\n * str.trim(\" hello \") // Returns: \"hello\"\n * str.trim(\" hello world \") // Returns: \"hello world\"\n * str.trim(\"\\thello\\n\") // Returns: \"hello\"\n * ```\n */\nexport function trim(str: simple_string): string {\n return str.trim();\n}\n\n/**\n * Removes whitespace from the left (start) of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Only removes leading whitespace\n * - Preserves trailing and internal whitespace\n *\n * @example\n * ```typescript\n * str.trimLeft(\" hello \") // Returns: \"hello \"\n * str.trimLeft(\" hello\") // Returns: \"hello\"\n * str.trimLeft(\"\\thello\") // Returns: \"hello\"\n * ```\n */\nexport function trimLeft(str: simple_string): string {\n return str.trimStart();\n}\n\n/**\n * Removes whitespace from the right (end) of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Only removes trailing whitespace\n * - Preserves leading and internal whitespace\n *\n * @example\n * ```typescript\n * str.trimRight(\" hello \") // Returns: \" hello\"\n * str.trimRight(\"hello \") // Returns: \"hello\"\n * str.trimRight(\"hello\\t\") // Returns: \"hello\"\n * ```\n */\nexport function trimRight(str: simple_string): string {\n return str.trimEnd();\n}\n\n/**\n * Returns the first substring of `source` that matches the `regex` (PineScript `str.match`).\n *\n * @param source - Source string\n * @param regex - Regular expression\n * @returns The first match (searched anywhere in the string), or na (null) when nothing matches or the match is empty\n *\n * @remarks\n * - As PineScript: \"Hello World!\" with \"o\" gives \"o\", with \"[a-z]+\" gives \"ello\"; no match gives na\n * - Uses JavaScript regex syntax\n *\n * @example\n * ```typescript\n * str.match(\"abc123def\", \"[0-9]+\") // Returns: \"123\"\n * str.match(\"abc\", \"[0-9]+\") // Returns: null (na)\n * ```\n */\nexport function match(source: simple_string, regex: simple_string): simple_string | null {\n const found = new RegExp(regex).exec(source);\n return found && found[0] !== '' ? found[0] : null;\n}\n\n/**\n * Formats a timestamp as a string according to the specified format.\n *\n * @param time - Unix timestamp in milliseconds\n * @param format - Format string using PineScript format specifiers\n * @returns Formatted date/time string\n *\n * @remarks\n * Supported format specifiers (case-sensitive):\n * - `yyyy` - 4-digit year (e.g., \"2024\")\n * - `yy` - 2-digit year (e.g., \"24\")\n * - `MMMM` - Full month name (e.g., \"January\")\n * - `MMM` - Abbreviated month name (e.g., \"Jan\")\n * - `MM` - 2-digit month (01-12)\n * - `M` - Month (1-12)\n * - `dd` - 2-digit day (01-31)\n * - `d` - Day (1-31)\n * - `HH` - 2-digit hour (00-23)\n * - `H` - Hour (0-23)\n * - `hh` - 2-digit hour (01-12)\n * - `h` - Hour (1-12)\n * - `mm` - 2-digit minute (00-59)\n * - `m` - Minute (0-59)\n * - `ss` - 2-digit second (00-59)\n * - `s` - Second (0-59)\n * - `a` - AM/PM\n *\n * @example\n * ```typescript\n * const timestamp = 1609459200000; // Jan 1, 2021 00:00:00 UTC\n * str.format_time(timestamp, \"dd MMM yyyy\") // Returns: \"01 Jan 2021\"\n * str.format_time(timestamp, \"yyyy-MM-dd HH:mm:ss\") // Returns: \"2021-01-01 00:00:00\"\n * str.format_time(timestamp, \"MMM d, yyyy\") // Returns: \"Jan 1, 2021\"\n * str.format_time(timestamp, \"hh:mm a\") // Returns: \"12:00 AM\"\n * ```\n */\n/**\n * Constructs a new string containing the source string repeated N times with an optional separator.\n *\n * @param source - The string to repeat\n * @param count - Number of times to repeat\n * @param separator - String to inject between repeated instances. Optional, defaults to empty string.\n * @returns The repeated string, or null (na) if source is na or count is 0 / negative / na, as in PineScript\n *\n * @example\n * ```typescript\n * str.repeat(\"ab\", 3) // Returns: \"ababab\"\n * str.repeat(\"?\", 3, \",\") // Returns: \"?,?,?\"\n * str.repeat(\"hello\", 0) // Returns: null (na)\n * str.repeat(\"x\", 1) // Returns: \"x\"\n * ```\n */\nexport function repeat(source: simple_string, count: simple_int, separator: simple_string = ''): string | null {\n if (source == null) return null;\n if (!(count > 0)) return null; // 0, negative or na: na\n return Array(count).fill(source).join(separator);\n}\n\n/**\n * Formats a UNIX time (ms) with a date pattern (PineScript `str.format_time`).\n *\n * @param time - UNIX time in milliseconds\n * @param format - Java SimpleDateFormat pattern, e.g. \"yyyy-MM-dd HH:mm:ss\", \"dd MMM yyyy\", \"hh:mm a\"\n * @param timezone - UTC/GMT offset (\"UTC-5\") or IANA name (\"America/New_York\"). PineScript's default is\n * the exchange time zone, which is not known here: the default is UTC\n * @returns Formatted date string\n *\n * @remarks\n * Letters: y, M, d, E, u, D, w,\n * H, k, K, h, a, m, s, S, Z, z; 'text' is literal text.\n */\nexport function format_time(time: simple_int, format: simple_string = \"yyyy-MM-dd'T'HH:mm:ssZ\", timezone: simple_string = 'Etc/UTC'): string {\n return formatDate(time, format, timezone);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * String namespace\n * Mirrors PineScript's str.* functions for string manipulation and analysis.\n *\n * @remarks\n * All string functions in this namespace follow PineScript v6 API specifications.\n * String operations are case-sensitive unless otherwise specified.\n *\n * @version 6\n */\n\nimport { formatNumber, tickScale } from './numberformat.js';\nimport { formatMessage } from './messageformat.js';\nimport { formatDate } from './dateformat.js';\nimport type { int, bool, float, simple_int, simple_string } from '../types/index.js';\n\n/**\n * Returns the length of a string.\n *\n * @param str - The string to measure\n * @returns The number of characters in the string\n *\n * @example\n * ```typescript\n * str.length(\"Hello\") // Returns: 5\n * str.length(\"\") // Returns: 0\n * ```\n */\nexport function length(str: simple_string): int {\n return str.length;\n}\n\n/**\n * Converts a value to its string representation (PineScript `str.tostring`).\n *\n * @param value - The value to convert (number, boolean, string, etc.)\n * @param format - Optional number format: a pattern (\"#.##\", \"#,###.00\", \"0.00%\", \"0.00E0\", \"#.##;(#.##)\") or\n * `format.percent` (\"percent\"), `format.volume` (\"volume\"), `format.mintick` (\"mintick\"),\n * `format.price` / `format.inherit`\n * @param mintick - Tick size for `format.mintick` (PineScript `syminfo.mintick`, implicit there). Not part of the\n * PineScript signature: the core function cannot see the chart, so pass it, as for `math.round_to_mintick`\n * @param pricescale - Price scale of the symbol for `format.mintick` (PineScript `syminfo.pricescale`), needed for a\n * symbol priced in fractions (32 for a 1/32 tick). Default: 10^(decimals of `mintick`)\n * @returns String representation of the value\n *\n * @remarks\n * As in PineScript:\n * - without format, numbers show up to 10 decimals (\"0.3333333333\"): the digits of the JVM's binary to decimal\n * conversion, rounded half to even, a tie on the last converted digit decided by the conversion\n * (82325.10000000005 gives \"82325.1\", with the format \"#.##########\" \"82325.1000000001\"; 1791662400001 / 900000\n * gives \"1990736.000001111\"; 78195689105219552 keeps its 17 digits); 1e21 and more use \"1E21\"\n * - patterns round half away from zero on the shortest decimal form: 1.005 with \"#.##\" gives \"1.01\";\n * at most 16 decimals from 1e-3 up (0.0012345678901234567 with 20 '#' decimals gives \"0.0012345678901235\")\n * - \"pos;neg\" patterns: a negative number takes the text of the negative subpattern (\"#.##;(#.##)\": \"(3.5)\")\n * - \"#\" is an optional digit (\"1.5\" with \"#.##\"), \"0\" a required digit (\"1.50\" with \"0.00\"),\n * \",\" groups thousands, \"%\" multiplies by 100, 'text' is literal text\n * - `format.percent` shows 2 decimals and \"%\" without multiplying; `format.volume` uses K, M, B, T\n * - `format.price` and `format.inherit` give \"price1\" / \"inherit1\" in PineScript: they are patterns\n * without digit characters, and oakscriptjs does the same\n * - `format.mintick` shows floor(log10(pricescale)) decimals, zeros included (\"1.00\", \"0.40\" for tick 0.01), and\n * rounds in two steps, with mintick = minmove / pricescale: the double quotient `|x| / (1 / pricescale)` half away\n * from zero (140.355 / 0.01 = 14035.499999999998 gives \"140.35\", where `math.round_to_mintick` gives 140.36),\n * then `floor(units / minmove + 0.5)` ticks (tick 0.02: 0.005 gives \"0.02\", -0.01 gives \"0.00\"). With a\n * pricescale that is no power of 10 the tick value is then rounded half even to those decimals (1/32: 25.25 gives\n * \"25.2\", -0.02 gives \"-0.0\"). It throws without `mintick`\n * - na gives \"NaN\" (\"NaN%\" with `format.percent`); an infinity gives \"NaN\" too (\"NaN%\" for `format.percent`,\n * \"NaNT\" for `format.volume`), except `format.mintick`, which gives \"Infinity\" / \"-Infinity\"\n *\n * @example\n * ```typescript\n * str.tostring(123) // \"123\"\n * str.tostring(1.5, \"#.##\") // \"1.5\"\n * str.tostring(0.5, \"#.00\") // \".50\"\n * str.tostring(12345.678, format.volume) // \"12.346K\"\n * ```\n */\nexport function tostring(value: any, format?: simple_string, mintick?: float, pricescale?: float): string {\n // an array: \"[a, b]\", each element as a single value\n if (Array.isArray(value)) return '[' + value.map((v) => tostring(v, format, mintick, pricescale)).join(', ') + ']';\n if (typeof value !== 'number') return String(value);\n if (Number.isNaN(value)) return format === 'percent' ? 'NaN%' : 'NaN';\n if (!Number.isFinite(value)) {\n // the chart formatter has no infinity: NaN with the format's suffix\n if (format === 'mintick') return String(value);\n return format === 'percent' ? 'NaN%' : format === 'volume' ? 'NaNT' : 'NaN';\n }\n if (format === undefined || format === '') {\n if (Math.abs(value) >= 1e21 && Number.isFinite(value)) {\n return String(value).replace('e+', 'E').replace(/\\.0+E/, 'E');\n }\n // without a format: the digits of the JVM conversion, 10 decimals half to even (#171, #174)\n return formatNumber(value, '#.##########', 'halfEvenConverted');\n }\n switch (format) {\n case 'percent':\n return formatNumber(value, '0.00', 'halfUpShortest') + '%';\n case 'volume': {\n const abs = Math.abs(value);\n for (const [size, unit] of [[1e12, 'T'], [1e9, 'B'], [1e6, 'M'], [1e3, 'K']] as const) {\n if (abs >= size) return formatNumber(value / size, '#.###', 'halfUpShortest') + unit;\n }\n return formatNumber(value, '#', 'halfUpShortest');\n }\n case 'mintick': {\n if (mintick === undefined) {\n throw new Error('str.tostring(x, format.mintick) needs the tick size: pass it as str.tostring(x, format.mintick, syminfo.mintick)');\n }\n const [minmove, scale] = tickScale(mintick, pricescale);\n const decimals = Math.floor(Math.log10(scale) + 1e-9);\n const mask = decimals > 0 ? '0.' + '0'.repeat(decimals) : '0';\n if (!(minmove >= 1)) return formatNumber(value, mask, 'halfUpShortest');\n // 1. the double quotient |x| / (1 / pricescale), half away from zero (not math.round_to_mintick's exact decimal)\n const scaled = Math.abs(value) / (1 / scale);\n let units = Math.trunc(scaled);\n if (scaled - units >= 0.5) units++;\n // 2. to the tick, a half going up (minmove 2: -0.01 gives \"0.00\", 0.01 gives \"0.02\")\n const ticks = Math.floor((value < 0 ? -units : units) / minmove + 0.5);\n // 3. the tick value at the scale's decimals; a negative one keeps its sign when it prints as zero (\"-0.0\")\n const text = formatNumber((Math.abs(ticks) * minmove) / scale, mask, 'halfEvenExact');\n return ticks < 0 ? '-' + text : text;\n }\n default:\n return formatNumber(value, format, 'halfUpShortest');\n }\n}\n\n/**\n * Converts a string to a number (float).\n *\n * @param str - The string to convert\n * @returns The numeric value, or null (na) if conversion fails\n *\n * @remarks\n * As in PineScript:\n * - The whole text must be a number: a sign, digits and one optional \".\" (\"0.5\", \".5\", \"5.\", \"-12\"); \"12abc\" is na\n * - Scientific notation (\"1e3\"), \"Infinity\", \"NaN\", hexadecimal, \"1,000\" give na\n * - The characters U+0000 to U+0020 (space, tab, line feed...) around the number are ignored; other spaces\n * (no-break space U+00A0, U+3000) give na\n *\n * @example\n * ```typescript\n * str.tonumber(\"123\") // Returns: 123\n * str.tonumber(\"45.67\") // Returns: 45.67\n * str.tonumber(\"abc\") // Returns: null\n * str.tonumber(\"12abc\") // Returns: null\n * str.tonumber(\"1e3\") // Returns: null\n * ```\n */\nexport function tonumber(str: simple_string): float | null {\n // remove the characters U+0000..U+0020 at both ends\n let start = 0;\n let end = str.length;\n while (start < end && str.charCodeAt(start) <= 0x20) start++;\n while (end > start && str.charCodeAt(end - 1) <= 0x20) end--;\n const text = str.slice(start, end);\n if (!/^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)$/.test(text)) return null;\n return parseFloat(text);\n}\n\n/**\n * Extracts a substring from a string.\n *\n * @param str - The source string\n * @param begin - Starting index (0-based, inclusive)\n * @param end - Ending index (0-based, exclusive). If omitted, extracts to end of string\n * @returns The extracted substring\n *\n * @remarks\n * - An na `end` (NaN / null) is a runtime error in PineScript: it throws\n * - If begin > end, indices are swapped (JavaScript behavior)\n * - Negative indices are treated as 0\n * - Indices beyond string length return empty string\n *\n * @example\n * ```typescript\n * str.substring(\"hello world\", 0, 5) // Returns: \"hello\"\n * str.substring(\"hello world\", 6) // Returns: \"world\"\n * str.substring(\"test\", 1, 3) // Returns: \"es\"\n * ```\n */\nexport function substring(str: simple_string, begin: simple_int, end?: simple_int): string {\n if (end === null || Number.isNaN(end)) throw new Error('str.substring: the end position is na');\n return str.substring(begin, end);\n}\n\n/**\n * Converts all characters in a string to uppercase.\n *\n * @param str - The string to convert\n * @returns The uppercase string\n *\n * @example\n * ```typescript\n * str.upper(\"hello\") // Returns: \"HELLO\"\n * str.upper(\"Hello World\") // Returns: \"HELLO WORLD\"\n * str.upper(\"test123\") // Returns: \"TEST123\"\n * ```\n */\nexport function upper(str: simple_string): string {\n return str.toUpperCase();\n}\n\n/**\n * Converts all characters in a string to lowercase.\n *\n * @param str - The string to convert\n * @returns The lowercase string\n *\n * @example\n * ```typescript\n * str.lower(\"HELLO\") // Returns: \"hello\"\n * str.lower(\"Hello World\") // Returns: \"hello world\"\n * str.lower(\"TEST123\") // Returns: \"test123\"\n * ```\n */\nexport function lower(str: simple_string): string {\n return str.toLowerCase();\n}\n\n/**\n * Checks if a string contains a substring.\n *\n * @param source - The string to search in\n * @param str - The substring to search for\n * @returns True if source contains str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string is contained in any string\n *\n * @example\n * ```typescript\n * str.contains(\"hello world\", \"world\") // Returns: true\n * str.contains(\"hello world\", \"World\") // Returns: false (case-sensitive)\n * str.contains(\"test\", \"xyz\") // Returns: false\n * ```\n */\nexport function contains(source: simple_string, str: simple_string): bool {\n return source.includes(str);\n}\n\n/**\n * Returns the position of the first occurrence of a substring.\n *\n * @param source - The string to search in\n * @param str - The substring to search for\n * @returns The 0-based index of the first occurrence, or na (NaN) if not found\n *\n * @remarks\n * - Search is case-sensitive\n * - Returns 0 for empty search string\n * - Returns na (NaN) if substring is not found, as in PineScript\n *\n * @example\n * ```typescript\n * str.pos(\"hello world\", \"world\") // Returns: 6\n * str.pos(\"hello world\", \"o\") // Returns: 4 (first occurrence)\n * str.pos(\"hello\", \"xyz\") // Returns: NaN (na)\n * ```\n */\nexport function pos(source: simple_string, str: simple_string): int {\n const index = source.indexOf(str);\n return index === -1 ? NaN : index;\n}\n\n/**\n * Replaces the Nth occurrence of a substring (PineScript `str.replace`).\n *\n * @param source - The source string\n * @param target - The substring to replace\n * @param replacement - The string to replace with\n * @param occurrence - 0-based occurrence to replace (default 0, the first one)\n * @returns The string with one occurrence replaced\n *\n * @remarks\n * PineScript rules:\n * - only the Nth occurrence is replaced; an na occurrence is 0 (`str.replace(\"aaa\", \"a\", \"-\", na)` is \"-aa\")\n * - occurrences overlap: \"aa\" occurs at 0 and 1 in \"aaa\" (`str.replace(\"aaa\", \"aa\", \"-\", 1)` is \"a-\")\n * - an empty target inserts the replacement at character N, at most at the end\n * (`str.replace(\"abc\", \"\", \"-\", 2)` is \"ab-c\")\n * - fewer than N + 1 occurrences (or a negative N) leave the source unchanged\n *\n * @example\n * ```typescript\n * str.replace(\"hello world hello\", \"hello\", \"hi\") // Returns: \"hi world hello\"\n * str.replace(\"hello world hello\", \"hello\", \"hi\", 1) // Returns: \"hello world hi\"\n * str.replace(\"test\", \"xyz\", \"abc\") // Returns: \"test\"\n * ```\n */\nexport function replace(source: simple_string, target: simple_string, replacement: simple_string, occurrence: simple_int = 0): string {\n const n = occurrence === null || Number.isNaN(occurrence) ? 0 : Math.trunc(occurrence);\n if (n < 0) return source;\n let index: number;\n if (target === '') {\n index = Math.min(n, source.length);\n } else {\n index = -1;\n for (let k = 0; k <= n; k++) {\n index = source.indexOf(target, index + 1);\n if (index === -1) return source;\n }\n }\n return source.slice(0, index) + replacement + source.slice(index + target.length);\n}\n\n/**\n * Replaces all occurrences of a target string with a replacement string.\n *\n * @param source - The source string\n * @param target - The substring to find\n * @param replacement - The replacement string\n * @returns String with all occurrences replaced\n *\n * @remarks\n * - Replaces all occurrences of the target string\n * - Case sensitive\n * - If target is empty string, returns original string\n * - This matches PineScript's `str.replace_all()` behavior\n *\n * @example\n * ```typescript\n * str.replace_all(\"hello world hello\", \"hello\", \"hi\") // Returns: \"hi world hi\"\n * str.replace_all(\"a-b-c\", \"-\", \"_\") // Returns: \"a_b_c\"\n * str.replace_all(\"test\", \"x\", \"y\") // Returns: \"test\" (no match)\n * ```\n */\nexport function replace_all(source: simple_string, target: simple_string, replacement: simple_string): string {\n return source.replaceAll(target, replacement);\n}\n\n/**\n * Splits a string into an array of substrings using a separator.\n *\n * @param str - The string to split\n * @param separator - The separator string\n * @returns Array of substrings\n *\n * @remarks\n * - Empty separator splits into individual characters; an empty string gives [\"\"] (also with \"\")\n * - If separator is not found, returns array with original string\n * - Consecutive separators create empty strings in result\n *\n * @example\n * ```typescript\n * str.split(\"a,b,c\", \",\") // Returns: [\"a\", \"b\", \"c\"]\n * str.split(\"hello\", \"\") // Returns: [\"h\", \"e\", \"l\", \"l\", \"o\"]\n * str.split(\"a,,b\", \",\") // Returns: [\"a\", \"\", \"b\"]\n * ```\n */\nexport function split(str: simple_string, separator: simple_string): string[] {\n // JavaScript gives [] for \"\".split(\"\"); PineScript gives [\"\"]\n return str === '' ? [''] : str.split(separator);\n}\n\n/**\n * Concatenates multiple strings into one.\n *\n * @param strings - Variable number of strings to concatenate\n * @returns The concatenated string\n *\n * @example\n * ```typescript\n * str.concat(\"hello\", \" \", \"world\") // Returns: \"hello world\"\n * str.concat(\"a\", \"b\", \"c\") // Returns: \"abc\"\n * str.concat(\"test\") // Returns: \"test\"\n * ```\n */\nexport function concat(...strings: simple_string[]): string {\n return strings.join('');\n}\n\n/**\n * Formats a string with placeholders (PineScript `str.format`, Java MessageFormat syntax).\n *\n * @param formatStr - Pattern with `{0}`, `{1,number,#.##}`, `{0,number,percent}`, `{0,date,yyyy-MM-dd}`...\n * @param args - Values for the placeholders\n * @returns Formatted string\n *\n * @remarks\n * As in PineScript: numbers in `{n}` use\n * \"#,##0.###\" (1234.5678 gives \"1,234.568\"); number patterns round half to even on the exact\n * value (16 decimals at most from 1e-3 up); an array gives \"[a, b]\"; `'...'` is literal text and `''` a quote;\n * a missing argument leaves `{n}` in the text.\n * Dates are formatted in UTC here; PineScript uses the exchange time zone.\n *\n * @example\n * ```typescript\n * str.format('Close: {0,number,#.##}', 101.456) // \"Close: 101.46\"\n * str.format('{0} and {1}', 'a', 2) // \"a and 2\"\n * ```\n */\nexport function format(formatStr: simple_string, ...args: any[]): string {\n return formatMessage(formatStr, args, 'Etc/UTC');\n}\n\n/**\n * Checks if a string starts with a specific prefix.\n *\n * @param source - The string to check\n * @param str - The prefix to look for\n * @returns True if source starts with str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string prefix always returns true\n *\n * @example\n * ```typescript\n * str.startswith(\"hello world\", \"hello\") // Returns: true\n * str.startswith(\"hello world\", \"Hello\") // Returns: false (case-sensitive)\n * str.startswith(\"test\", \"testing\") // Returns: false\n * ```\n */\nexport function startswith(source: simple_string, str: simple_string): bool {\n return source.startsWith(str);\n}\n\n/**\n * Checks if a string ends with a specific suffix.\n *\n * @param source - The string to check\n * @param str - The suffix to look for\n * @returns True if source ends with str, false otherwise\n *\n * @remarks\n * - Search is case-sensitive\n * - Empty string suffix always returns true\n *\n * @example\n * ```typescript\n * str.endswith(\"hello world\", \"world\") // Returns: true\n * str.endswith(\"hello world\", \"World\") // Returns: false (case-sensitive)\n * str.endswith(\"testing\", \"test\") // Returns: false\n * ```\n */\nexport function endswith(source: simple_string, str: simple_string): bool {\n return source.endsWith(str);\n}\n\n/**\n * Returns the character at a specific position in a string.\n *\n * @param str - The source string\n * @param pos - The 0-based position\n * @returns The character at the position, or empty string if out of bounds\n *\n * @remarks\n * - Returns empty string for negative or out-of-bounds positions\n * - Unicode characters may span multiple code units\n *\n * @example\n * ```typescript\n * str.charAt(\"hello\", 0) // Returns: \"h\"\n * str.charAt(\"hello\", 4) // Returns: \"o\"\n * str.charAt(\"hello\", 10) // Returns: \"\"\n * ```\n */\nexport function charAt(str: simple_string, pos: simple_int): string {\n return str.charAt(pos);\n}\n\n/**\n * Removes whitespace from both ends of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Removes spaces, tabs, newlines, and other whitespace characters\n * - Preserves internal whitespace\n *\n * @example\n * ```typescript\n * str.trim(\" hello \") // Returns: \"hello\"\n * str.trim(\" hello world \") // Returns: \"hello world\"\n * str.trim(\"\\thello\\n\") // Returns: \"hello\"\n * ```\n */\nexport function trim(str: simple_string): string {\n return str.trim();\n}\n\n/**\n * Removes whitespace from the left (start) of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Only removes leading whitespace\n * - Preserves trailing and internal whitespace\n *\n * @example\n * ```typescript\n * str.trimLeft(\" hello \") // Returns: \"hello \"\n * str.trimLeft(\" hello\") // Returns: \"hello\"\n * str.trimLeft(\"\\thello\") // Returns: \"hello\"\n * ```\n */\nexport function trimLeft(str: simple_string): string {\n return str.trimStart();\n}\n\n/**\n * Removes whitespace from the right (end) of a string.\n *\n * @param str - The string to trim\n * @returns The trimmed string\n *\n * @remarks\n * - Only removes trailing whitespace\n * - Preserves leading and internal whitespace\n *\n * @example\n * ```typescript\n * str.trimRight(\" hello \") // Returns: \" hello\"\n * str.trimRight(\"hello \") // Returns: \"hello\"\n * str.trimRight(\"hello\\t\") // Returns: \"hello\"\n * ```\n */\nexport function trimRight(str: simple_string): string {\n return str.trimEnd();\n}\n\n/**\n * Returns the first substring of `source` that matches the `regex` (PineScript `str.match`).\n *\n * @param source - Source string\n * @param regex - Regular expression\n * @returns The first match (searched anywhere in the string), or na (null) when nothing matches or the match is empty\n *\n * @remarks\n * - As PineScript: \"Hello World!\" with \"o\" gives \"o\", with \"[a-z]+\" gives \"ello\"; no match gives na\n * - Uses JavaScript regex syntax\n *\n * @example\n * ```typescript\n * str.match(\"abc123def\", \"[0-9]+\") // Returns: \"123\"\n * str.match(\"abc\", \"[0-9]+\") // Returns: null (na)\n * ```\n */\nexport function match(source: simple_string, regex: simple_string): simple_string | null {\n const found = new RegExp(regex).exec(source);\n return found && found[0] !== '' ? found[0] : null;\n}\n\n/**\n * Formats a timestamp as a string according to the specified format.\n *\n * @param time - Unix timestamp in milliseconds\n * @param format - Format string using PineScript format specifiers\n * @returns Formatted date/time string\n *\n * @remarks\n * Supported format specifiers (case-sensitive):\n * - `yyyy` - 4-digit year (e.g., \"2024\")\n * - `yy` - 2-digit year (e.g., \"24\")\n * - `MMMM` - Full month name (e.g., \"January\")\n * - `MMM` - Abbreviated month name (e.g., \"Jan\")\n * - `MM` - 2-digit month (01-12)\n * - `M` - Month (1-12)\n * - `dd` - 2-digit day (01-31)\n * - `d` - Day (1-31)\n * - `HH` - 2-digit hour (00-23)\n * - `H` - Hour (0-23)\n * - `hh` - 2-digit hour (01-12)\n * - `h` - Hour (1-12)\n * - `mm` - 2-digit minute (00-59)\n * - `m` - Minute (0-59)\n * - `ss` - 2-digit second (00-59)\n * - `s` - Second (0-59)\n * - `a` - AM/PM\n *\n * @example\n * ```typescript\n * const timestamp = 1609459200000; // Jan 1, 2021 00:00:00 UTC\n * str.format_time(timestamp, \"dd MMM yyyy\") // Returns: \"01 Jan 2021\"\n * str.format_time(timestamp, \"yyyy-MM-dd HH:mm:ss\") // Returns: \"2021-01-01 00:00:00\"\n * str.format_time(timestamp, \"MMM d, yyyy\") // Returns: \"Jan 1, 2021\"\n * str.format_time(timestamp, \"hh:mm a\") // Returns: \"12:00 AM\"\n * ```\n */\n/**\n * Constructs a new string containing the source string repeated N times with an optional separator.\n *\n * @param source - The string to repeat\n * @param count - Number of times to repeat\n * @param separator - String to inject between repeated instances. Optional, defaults to empty string.\n * @returns The repeated string, or null (na) if source is na or count is 0 / negative / na, as in PineScript\n *\n * @example\n * ```typescript\n * str.repeat(\"ab\", 3) // Returns: \"ababab\"\n * str.repeat(\"?\", 3, \",\") // Returns: \"?,?,?\"\n * str.repeat(\"hello\", 0) // Returns: null (na)\n * str.repeat(\"x\", 1) // Returns: \"x\"\n * ```\n */\nexport function repeat(source: simple_string, count: simple_int, separator: simple_string = ''): string | null {\n if (source == null) return null;\n if (!(count > 0)) return null; // 0, negative or na: na\n return Array(count).fill(source).join(separator);\n}\n\n/**\n * Formats a UNIX time (ms) with a date pattern (PineScript `str.format_time`).\n *\n * @param time - UNIX time in milliseconds\n * @param format - Java SimpleDateFormat pattern, e.g. \"yyyy-MM-dd HH:mm:ss\", \"dd MMM yyyy\", \"hh:mm a\"\n * @param timezone - UTC/GMT offset (\"UTC-5\") or IANA name (\"America/New_York\"). PineScript's default is\n * the exchange time zone, which is not known here: the default is UTC\n * @returns Formatted date string\n *\n * @remarks\n * Letters: y, M, d, E, u, D, w,\n * H, k, K, h, a, m, s, S, Z, z; 'text' is literal text.\n */\nexport function format_time(time: simple_int, format: simple_string = \"yyyy-MM-dd'T'HH:mm:ssZ\", timezone: simple_string = 'Etc/UTC'): string {\n return formatDate(time, format, timezone);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["format", "pos"]
7
7
  }
@@ -4,10 +4,14 @@
4
4
  * The pattern syntax is Java's DecimalFormat: `0` required digit, `#` optional digit, `,` grouping,
5
5
  * `.` decimal point, `E0` exponent, `%` multiplies by 100, `'...'` quoted literal text.
6
6
  * PineScript rules:
7
- * - `str.tostring` rounds half away from zero on the shortest decimal form of the number
7
+ * - `str.tostring` with a format rounds half away from zero on the shortest decimal form of the number
8
8
  * (`1.005` with "#.##" gives "1.01", `2.5` with "#" gives "3")
9
+ * - `str.tostring` without a format rounds the digits of the JVM's binary to decimal conversion to 10 decimals,
10
+ * half to even, the conversion flags deciding a tie (see `floatingdecimal.ts`): 12.34500000005 gives "12.345"
11
+ * ("12.3450000001" with the format "#.##########"), 1791662400001 / 900000 gives "1990736.000001111"
9
12
  * - `str.format` rounds half to even on the exact binary value (`0.25` with "#.#" gives "0.2")
10
- * - a negative number that rounds to zero loses its sign; "#.00" writes `.50` (no leading zero),
13
+ * - a negative number that rounds to zero loses its sign, except with "%" ("#.#%": -0.0004 gives "-0%");
14
+ * "#.00" writes `.50` (no leading zero),
11
15
  * but "#.##" writes `0.5` (a pattern without any '0' shows one integer digit)
12
16
  * - with an exponent, the value is first rounded to the fraction digits ("0.00E0": 0.125 gives "1.30E-1")
13
17
  * - a pattern without digit characters (`format.price` is "price") formats like "#" after its text,
@@ -17,7 +21,7 @@
17
21
  * - at most 16 decimals when |x| >= 1e-3 (the shortest digits of a double never have more there):
18
22
  * 0.1 with 20 '#' decimals gives "0.1" in both rounding modes
19
23
  */
20
- export type Rounding = 'halfUpShortest' | 'halfEvenExact';
24
+ export type Rounding = 'halfUpShortest' | 'halfUpExact' | 'halfEvenExact' | 'halfEvenConverted';
21
25
  /** Formats a number with a DecimalFormat pattern. */
22
26
  export declare function formatNumber(value: number, pattern: string, mode: Rounding): string;
23
27
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"numberformat.d.ts","sourceRoot":"","sources":["../../../src/str/numberformat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,QAAQ,GAAG,gBAAgB,GAAG,eAAe,CAAC;AA8K1D,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CA6BnF;AASD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAMhF"}
1
+ {"version":3,"file":"numberformat.d.ts","sourceRoot":"","sources":["../../../src/str/numberformat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,MAAM,MAAM,QAAQ,GAAG,gBAAgB,GAAG,aAAa,GAAG,eAAe,GAAG,mBAAmB,CAAC;AA8KhG,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,CA8BnF;AASD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAMhF"}
@@ -22,6 +22,7 @@ __export(numberformat_exports, {
22
22
  tickScale: () => tickScale
23
23
  });
24
24
  module.exports = __toCommonJS(numberformat_exports);
25
+ var import_floatingdecimal = require("./floatingdecimal.js");
25
26
  const patterns = /* @__PURE__ */ new Map();
26
27
  function subpatternSplit(pattern) {
27
28
  let quoted = false;
@@ -129,7 +130,7 @@ function roundDigits(d, frac, mode) {
129
130
  if (keep < 0) return { digits: "", intLen: 0 };
130
131
  const first = d.digits.charCodeAt(keep) - 48;
131
132
  let up;
132
- if (mode === "halfUpShortest") up = first >= 5;
133
+ if (mode !== "halfEvenExact") up = first >= 5;
133
134
  else if (first !== 5) up = first > 5;
134
135
  else if (d.digits.length > keep + 1) up = true;
135
136
  else up = keep > 0 && (d.digits.charCodeAt(keep - 1) - 48) % 2 === 1;
@@ -179,11 +180,11 @@ function formatNumber(value, pattern, mode) {
179
180
  body = layout(mant, p) + "E" + (exponent < 0 ? "-" : "") + e;
180
181
  } else {
181
182
  const frac = !p.hasDigits ? 0 : Math.abs(x) >= 1e-3 ? Math.min(p.maxFrac, 16) : p.maxFrac;
182
- const rounded = roundDigits(toDigits(x), frac, mode);
183
+ const rounded = mode === "halfEvenConverted" ? (0, import_floatingdecimal.javaFixedDigits)(x, frac) : roundDigits(toDigits(x), frac, mode);
183
184
  isZero = rounded.digits === "";
184
185
  body = p.hasDigits ? layout(rounded, p) : layout(rounded, { ...p, minInt: 0, maxFrac: 0, minFrac: 0 });
185
186
  }
186
- return affixes(p, x < 0 && (!isZero || !p.hasDigits), body);
187
+ return affixes(p, x < 0 && (!isZero || !p.hasDigits || p.percent), body);
187
188
  }
188
189
  function affixes(p, negative, body) {
189
190
  if (!negative) return p.prefix + body + p.suffix;