arxo-codegen 0.4.0__py3-none-any.whl

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 (152) hide show
  1. arxo_codegen/__init__.py +5 -0
  2. arxo_codegen/__main__.py +7 -0
  3. arxo_codegen/active-outcomes.json +6837 -0
  4. arxo_codegen/check.py +158 -0
  5. arxo_codegen/check_active_canaries.py +262 -0
  6. arxo_codegen/check_api.py +76 -0
  7. arxo_codegen/check_conformance.py +414 -0
  8. arxo_codegen/check_matrix.py +131 -0
  9. arxo_codegen/check_packages.py +281 -0
  10. arxo_codegen/check_semantic_probes.py +293 -0
  11. arxo_codegen/check_xlsx.py +320 -0
  12. arxo_codegen/consumer_runner.py +316 -0
  13. arxo_codegen/core_support.py +203 -0
  14. arxo_codegen/coverage.py +341 -0
  15. arxo_codegen/deliver.py +180 -0
  16. arxo_codegen/examples/xlsx/pricing.lawir.json +409 -0
  17. arxo_codegen/examples/xlsx/pricing.xlsx.json +52 -0
  18. arxo_codegen/excel_plan.py +602 -0
  19. arxo_codegen/harness/verify/ci/gates/backends/check_backends.py +1248 -0
  20. arxo_codegen/harness/verify/ci/gates/backends/l1_fixtures.py +463 -0
  21. arxo_codegen/inert-nodes.json +62 -0
  22. arxo_codegen/inert.py +91 -0
  23. arxo_codegen/inputs.py +59 -0
  24. arxo_codegen/package.py +565 -0
  25. arxo_codegen/plan.py +4053 -0
  26. arxo_codegen/recertify_registry.py +100 -0
  27. arxo_codegen/runtime-cert.json +5259 -0
  28. arxo_codegen/runtime_cert.py +233 -0
  29. arxo_codegen/survey.py +272 -0
  30. arxo_codegen/target_runner.py +101 -0
  31. arxo_codegen/targets/bsl/cert.bsl +113 -0
  32. arxo_codegen/targets/bsl/emit.py +356 -0
  33. arxo_codegen/targets/bsl/json.bsl +243 -0
  34. arxo_codegen/targets/bsl/runner.bsl +210 -0
  35. arxo_codegen/targets/bsl/runtime.bsl +858 -0
  36. arxo_codegen/targets/cs/Cert.cs +90 -0
  37. arxo_codegen/targets/cs/Runner.cs +154 -0
  38. arxo_codegen/targets/cs/Runtime.cs +243 -0
  39. arxo_codegen/targets/cs/emit.py +223 -0
  40. arxo_codegen/targets/dart/cert.dart +99 -0
  41. arxo_codegen/targets/dart/emit.py +206 -0
  42. arxo_codegen/targets/dart/json.dart +166 -0
  43. arxo_codegen/targets/dart/runner.dart +154 -0
  44. arxo_codegen/targets/dart/runtime.dart +293 -0
  45. arxo_codegen/targets/erl/cert.erl +64 -0
  46. arxo_codegen/targets/erl/emit.py +216 -0
  47. arxo_codegen/targets/erl/rt.erl +238 -0
  48. arxo_codegen/targets/erl/runner.erl +130 -0
  49. arxo_codegen/targets/ex/cert.exs +62 -0
  50. arxo_codegen/targets/ex/emit.py +204 -0
  51. arxo_codegen/targets/ex/runner.exs +124 -0
  52. arxo_codegen/targets/ex/runtime.ex +227 -0
  53. arxo_codegen/targets/go/apiBase.go +483 -0
  54. arxo_codegen/targets/go/apicheck.go +97 -0
  55. arxo_codegen/targets/go/cert.go +218 -0
  56. arxo_codegen/targets/go/emit.py +1806 -0
  57. arxo_codegen/targets/go/evaluate.go +1826 -0
  58. arxo_codegen/targets/go/runner.go +38 -0
  59. arxo_codegen/targets/go/runtime.go +2612 -0
  60. arxo_codegen/targets/java/Cert.java +91 -0
  61. arxo_codegen/targets/java/Json.java +148 -0
  62. arxo_codegen/targets/java/Runner.java +151 -0
  63. arxo_codegen/targets/java/Runtime.java +243 -0
  64. arxo_codegen/targets/java/emit.py +198 -0
  65. arxo_codegen/targets/kt/Cert.kt +67 -0
  66. arxo_codegen/targets/kt/Json.kt +103 -0
  67. arxo_codegen/targets/kt/Runner.kt +126 -0
  68. arxo_codegen/targets/kt/Runtime.kt +196 -0
  69. arxo_codegen/targets/kt/emit.py +195 -0
  70. arxo_codegen/targets/lua/cert.lua +82 -0
  71. arxo_codegen/targets/lua/emit.py +250 -0
  72. arxo_codegen/targets/lua/json.lua +133 -0
  73. arxo_codegen/targets/lua/runner.lua +167 -0
  74. arxo_codegen/targets/lua/runtime.lua +541 -0
  75. arxo_codegen/targets/php/cert.php +84 -0
  76. arxo_codegen/targets/php/emit.py +229 -0
  77. arxo_codegen/targets/php/json.php +144 -0
  78. arxo_codegen/targets/php/runner.php +189 -0
  79. arxo_codegen/targets/php/runtime.php +660 -0
  80. arxo_codegen/targets/py/api_base.py +334 -0
  81. arxo_codegen/targets/py/apicheck.py +51 -0
  82. arxo_codegen/targets/py/cert.py +140 -0
  83. arxo_codegen/targets/py/emit.py +1140 -0
  84. arxo_codegen/targets/py/evaluate.py +980 -0
  85. arxo_codegen/targets/py/runner.py +38 -0
  86. arxo_codegen/targets/py/runtime.py +1634 -0
  87. arxo_codegen/targets/rb/cert.rb +73 -0
  88. arxo_codegen/targets/rb/emit.py +203 -0
  89. arxo_codegen/targets/rb/runner.rb +130 -0
  90. arxo_codegen/targets/rb/runtime.rb +255 -0
  91. arxo_codegen/targets/rs/Cargo.lock +142 -0
  92. arxo_codegen/targets/rs/Cargo.toml +16 -0
  93. arxo_codegen/targets/rs/cert.rs +106 -0
  94. arxo_codegen/targets/rs/emit.py +233 -0
  95. arxo_codegen/targets/rs/prefetch.rs +1 -0
  96. arxo_codegen/targets/rs/runner.rs +181 -0
  97. arxo_codegen/targets/rs/runtime.rs +448 -0
  98. arxo_codegen/targets/scala/Cert.scala +77 -0
  99. arxo_codegen/targets/scala/Json.scala +140 -0
  100. arxo_codegen/targets/scala/Runner.scala +121 -0
  101. arxo_codegen/targets/scala/Runtime.scala +269 -0
  102. arxo_codegen/targets/scala/emit.py +193 -0
  103. arxo_codegen/targets/swift/cert.swift +105 -0
  104. arxo_codegen/targets/swift/emit.py +231 -0
  105. arxo_codegen/targets/swift/json.swift +208 -0
  106. arxo_codegen/targets/swift/runner.swift +133 -0
  107. arxo_codegen/targets/swift/runtime.swift +528 -0
  108. arxo_codegen/targets/ts/apiBase.ts +250 -0
  109. arxo_codegen/targets/ts/apicheck.ts +37 -0
  110. arxo_codegen/targets/ts/cert.ts +96 -0
  111. arxo_codegen/targets/ts/emit.py +1214 -0
  112. arxo_codegen/targets/ts/evaluate.ts +894 -0
  113. arxo_codegen/targets/ts/package-lock.json +45 -0
  114. arxo_codegen/targets/ts/package.json +9 -0
  115. arxo_codegen/targets/ts/runner.ts +31 -0
  116. arxo_codegen/targets/ts/runtime.ts +1369 -0
  117. arxo_codegen/targets/xlsx/README.ru.md +176 -0
  118. arxo_codegen/targets/xlsx/emit.py +198 -0
  119. arxo_codegen/temporal.py +59 -0
  120. arxo_codegen/test_alias_types.py +171 -0
  121. arxo_codegen/test_api_contract.py +89 -0
  122. arxo_codegen/test_calendar_policy.py +48 -0
  123. arxo_codegen/test_check.py +49 -0
  124. arxo_codegen/test_check_active_canaries.py +96 -0
  125. arxo_codegen/test_check_conformance.py +132 -0
  126. arxo_codegen/test_check_semantic_probes.py +85 -0
  127. arxo_codegen/test_check_xlsx.py +52 -0
  128. arxo_codegen/test_consumer_runner.py +175 -0
  129. arxo_codegen/test_core_support.py +80 -0
  130. arxo_codegen/test_e0212_decision_time.py +63 -0
  131. arxo_codegen/test_head_aggregate.py +71 -0
  132. arxo_codegen/test_inert.py +143 -0
  133. arxo_codegen/test_instant_types.py +113 -0
  134. arxo_codegen/test_key_conflict_scope.py +92 -0
  135. arxo_codegen/test_key_conflict_targets.py +17 -0
  136. arxo_codegen/test_package_layout.py +163 -0
  137. arxo_codegen/test_plan_language_version.py +44 -0
  138. arxo_codegen/test_positions_refusal.py +43 -0
  139. arxo_codegen/test_recertify_registry.py +77 -0
  140. arxo_codegen/test_ruby_target.py +41 -0
  141. arxo_codegen/test_runtime_cert.py +83 -0
  142. arxo_codegen/test_secondary_key_conflict_scope.py +17 -0
  143. arxo_codegen/test_stage_r.py +108 -0
  144. arxo_codegen/test_standalone_inputs.py +71 -0
  145. arxo_codegen/test_support.py +150 -0
  146. arxo_codegen/test_survey_inputs.py +115 -0
  147. arxo_codegen-0.4.0.dist-info/LICENSE +202 -0
  148. arxo_codegen-0.4.0.dist-info/METADATA +1673 -0
  149. arxo_codegen-0.4.0.dist-info/RECORD +152 -0
  150. arxo_codegen-0.4.0.dist-info/WHEEL +5 -0
  151. arxo_codegen-0.4.0.dist-info/entry_points.txt +13 -0
  152. arxo_codegen-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,528 @@
1
+ // Рантайм L0 для Swift. Тот же контракт, что у runtime.rb: точная десятичная
2
+ // арифметика §58, деньги с валютой, названный режим округления, календарный
3
+ // день, член перечисления. Целых произвольной длины в стандартной библиотеке
4
+ // нет — свои: модуль по основанию 10^7 и знак. Foundation не подключается:
5
+ // её `Decimal` ограничен 38 цифрами, а сборка с ней втрое дольше.
6
+
7
+ /// Названный отказ рантайма: то, что срез не считает, а не сбой. Раннер
8
+ /// печатает его как JSON с кодом выхода 3; любая другая ошибка — крах.
9
+ struct Refused: Error {
10
+ let message: String
11
+ init(_ message: String) { self.message = message }
12
+ }
13
+
14
+ // --- целые произвольной длины ---------------------------------------------------
15
+
16
+ struct BigI {
17
+ static let base = 10_000_000
18
+ let neg: Bool
19
+ let m: [Int] // младшая конечность первой; ноль — пустой массив
20
+
21
+ init(neg: Bool, m: [Int]) {
22
+ var mm = m
23
+ while let last = mm.last, last == 0 { mm.removeLast() }
24
+ self.m = mm
25
+ self.neg = mm.isEmpty ? false : neg
26
+ }
27
+
28
+ init(_ n: Int) {
29
+ var x = n.magnitude
30
+ var mm: [Int] = []
31
+ while x > 0 {
32
+ mm.append(Int(x % UInt(BigI.base)))
33
+ x /= UInt(BigI.base)
34
+ }
35
+ self.init(neg: n < 0, m: mm)
36
+ }
37
+
38
+ /// Десятичная запись со знаком; иная запись — крах, а не отказ.
39
+ init(parsing s: String) {
40
+ var bytes = Array(s.utf8)
41
+ var neg = false
42
+ if bytes.first == UInt8(ascii: "-") {
43
+ neg = true
44
+ bytes.removeFirst()
45
+ }
46
+ precondition(!bytes.isEmpty && bytes.allSatisfy { $0 >= 48 && $0 <= 57 }, "целое: не десятичная запись: \(s)")
47
+ var mm: [Int] = []
48
+ var end = bytes.count
49
+ while end > 0 {
50
+ let start = Swift.max(0, end - 7)
51
+ var limb = 0
52
+ for k in start..<end { limb = limb * 10 + Int(bytes[k] - 48) }
53
+ mm.append(limb)
54
+ end = start
55
+ }
56
+ self.init(neg: neg, m: mm)
57
+ }
58
+
59
+ var isZero: Bool { m.isEmpty }
60
+ var magnitude: BigI { BigI(neg: false, m: m) }
61
+ var negated: BigI { BigI(neg: !neg, m: m) }
62
+
63
+ var description: String {
64
+ guard let top = m.last else { return "0" }
65
+ var s = neg ? "-" : ""
66
+ s += String(top)
67
+ for limb in m.dropLast().reversed() {
68
+ let d = String(limb)
69
+ s += String(repeating: "0", count: 7 - d.count) + d
70
+ }
71
+ return s
72
+ }
73
+
74
+ /// Малое целое: вызывающий обязан сперва проверить диапазон.
75
+ var small: Int {
76
+ var n = 0
77
+ for limb in m.reversed() { n = n * BigI.base + limb }
78
+ return neg ? -n : n
79
+ }
80
+
81
+ static func magCmp(_ a: [Int], _ b: [Int]) -> Int {
82
+ if a.count != b.count { return a.count < b.count ? -1 : 1 }
83
+ var i = a.count - 1
84
+ while i >= 0 {
85
+ if a[i] != b[i] { return a[i] < b[i] ? -1 : 1 }
86
+ i -= 1
87
+ }
88
+ return 0
89
+ }
90
+
91
+ static func magAdd(_ a: [Int], _ b: [Int]) -> [Int] {
92
+ var r: [Int] = []
93
+ var carry = 0
94
+ for i in 0..<Swift.max(a.count, b.count) {
95
+ let t = (i < a.count ? a[i] : 0) + (i < b.count ? b[i] : 0) + carry
96
+ r.append(t % base)
97
+ carry = t / base
98
+ }
99
+ if carry > 0 { r.append(carry) }
100
+ return r
101
+ }
102
+
103
+ /// |a| >= |b|.
104
+ static func magSub(_ a: [Int], _ b: [Int]) -> [Int] {
105
+ var r: [Int] = []
106
+ var borrow = 0
107
+ for i in 0..<a.count {
108
+ var t = a[i] - (i < b.count ? b[i] : 0) - borrow
109
+ if t < 0 {
110
+ t += base
111
+ borrow = 1
112
+ } else {
113
+ borrow = 0
114
+ }
115
+ r.append(t)
116
+ }
117
+ while let last = r.last, last == 0 { r.removeLast() }
118
+ return r
119
+ }
120
+
121
+ static func magMul(_ a: [Int], _ b: [Int]) -> [Int] {
122
+ if a.isEmpty || b.isEmpty { return [] }
123
+ var r = [Int](repeating: 0, count: a.count + b.count + 1)
124
+ for i in 0..<a.count {
125
+ var carry = 0
126
+ for j in 0..<b.count {
127
+ let t = r[i + j] + a[i] * b[j] + carry
128
+ r[i + j] = t % base
129
+ carry = t / base
130
+ }
131
+ var k = i + b.count
132
+ while carry > 0 {
133
+ let t = r[k] + carry
134
+ r[k] = t % base
135
+ carry = t / base
136
+ k += 1
137
+ }
138
+ }
139
+ while let last = r.last, last == 0 { r.removeLast() }
140
+ return r
141
+ }
142
+
143
+ static func magMulSmall(_ a: [Int], _ s: Int) -> [Int] {
144
+ var r: [Int] = []
145
+ var carry = 0
146
+ for limb in a {
147
+ let t = limb * s + carry
148
+ r.append(t % base)
149
+ carry = t / base
150
+ }
151
+ while carry > 0 {
152
+ r.append(carry % base)
153
+ carry /= base
154
+ }
155
+ while let last = r.last, last == 0 { r.removeLast() }
156
+ return r
157
+ }
158
+
159
+ /// Деление модулей в столбик: цифра частного по основанию 10^7 подбирается
160
+ /// двоичным поиском. Делитель ненулевой.
161
+ static func magDivMod(_ a: [Int], _ b: [Int]) -> ([Int], [Int]) {
162
+ if magCmp(a, b) < 0 { return ([], a) }
163
+ var q = [Int](repeating: 0, count: a.count)
164
+ var rem: [Int] = []
165
+ var i = a.count - 1
166
+ while i >= 0 {
167
+ rem.insert(a[i], at: 0)
168
+ while let last = rem.last, last == 0 { rem.removeLast() }
169
+ var lo = 0, hi = base - 1
170
+ while lo < hi {
171
+ let mid = (lo + hi + 1) / 2
172
+ if magCmp(magMulSmall(b, mid), rem) <= 0 { lo = mid } else { hi = mid - 1 }
173
+ }
174
+ if lo > 0 { rem = magSub(rem, magMulSmall(b, lo)) }
175
+ q[i] = lo
176
+ i -= 1
177
+ }
178
+ return (q, rem)
179
+ }
180
+
181
+ static func + (x: BigI, y: BigI) -> BigI {
182
+ if x.neg == y.neg { return BigI(neg: x.neg, m: magAdd(x.m, y.m)) }
183
+ let c = magCmp(x.m, y.m)
184
+ if c == 0 { return BigI(0) }
185
+ return c > 0 ? BigI(neg: x.neg, m: magSub(x.m, y.m)) : BigI(neg: y.neg, m: magSub(y.m, x.m))
186
+ }
187
+
188
+ static func - (x: BigI, y: BigI) -> BigI { x + y.negated }
189
+ static func * (x: BigI, y: BigI) -> BigI { BigI(neg: x.neg != y.neg, m: magMul(x.m, y.m)) }
190
+
191
+ static func cmp(_ x: BigI, _ y: BigI) -> Int {
192
+ if x.neg != y.neg { return x.neg ? -1 : 1 }
193
+ let c = magCmp(x.m, y.m)
194
+ return x.neg ? -c : c
195
+ }
196
+
197
+ /// Частное и остаток с усечением к нулю: знак остатка — знак делимого.
198
+ func divMod(_ y: BigI) -> (BigI, BigI) {
199
+ let (q, r) = BigI.magDivMod(m, y.m)
200
+ return (BigI(neg: neg != y.neg, m: q), BigI(neg: neg, m: r))
201
+ }
202
+
203
+ static func pow10(_ k: Int) -> BigI {
204
+ var r = BigI(1)
205
+ for _ in 0..<Swift.max(0, k) { r = BigI(neg: false, m: magMulSmall(r.m, 10)) }
206
+ return r
207
+ }
208
+
209
+ static func gcd(_ x: BigI, _ y: BigI) -> BigI {
210
+ var a = x.magnitude, b = y.magnitude
211
+ while !b.isZero { (a, b) = (b, a.divMod(b).1) }
212
+ return a
213
+ }
214
+ }
215
+
216
+ // --- значения --------------------------------------------------------------------
217
+
218
+ /// Значение ячейки: Text и сущность — `text`; календарный день §2.15 в
219
+ /// канонической записи ISO — `date` (порядок записи и есть порядок дня); член
220
+ /// перечисления §38 или ссылка без объявления — `sym` (равенство по
221
+ /// идентичности, порядок не определён, §57).
222
+ enum V {
223
+ case text(String)
224
+ case dec(BigI, Int)
225
+ case money(BigI, Int, String)
226
+ case date(String)
227
+ case sym(String)
228
+ }
229
+
230
+ enum RT {
231
+ static func dec(_ lit: String) -> V {
232
+ let parts = lit.split(separator: ".", maxSplits: 1, omittingEmptySubsequences: false)
233
+ let i = String(parts[0])
234
+ let f = parts.count > 1 ? String(parts[1]) : ""
235
+ let neg = i.hasPrefix("-")
236
+ let digits = (neg ? String(i.dropFirst()) : i) + f
237
+ return .dec(BigI(parsing: (neg ? "-" : "") + digits), f.count)
238
+ }
239
+
240
+ static func money(_ lit: String, _ cur: String) -> V {
241
+ guard case let .dec(u, s) = dec(lit) else { fatalError() }
242
+ return .money(u, s, cur)
243
+ }
244
+
245
+ static func date(_ lit: String) -> V { .date(lit) }
246
+ static func sym(_ id: String) -> V { .sym(id) }
247
+
248
+ /// Модуль, масштаб и валюта числа; nil — не число.
249
+ static func num(_ v: V) -> (u: BigI, s: Int, cur: String?)? {
250
+ switch v {
251
+ case let .dec(u, s): return (u, s, nil)
252
+ case let .money(u, s, cur): return (u, s, cur)
253
+ default: return nil
254
+ }
255
+ }
256
+
257
+ static func make(_ u: BigI, _ s: Int, _ cur: String?) -> V {
258
+ if let cur = cur { return .money(u, s, cur) }
259
+ return .dec(u, s)
260
+ }
261
+
262
+ // §48/§58: сложение, вычитание, min/max требуют совпадения видов; у денег — и валюты.
263
+ static func same(_ a: V, _ b: V, _ op: String) throws -> ((u: BigI, s: Int, cur: String?), (u: BigI, s: Int, cur: String?)) {
264
+ guard let x = num(a), let y = num(b), (x.cur == nil) == (y.cur == nil) else {
265
+ throw Refused("§58: виды операндов не совпадают: \(op)")
266
+ }
267
+ if x.cur != nil && x.cur != y.cur { throw Refused("§58: разные валюты: \(op)") }
268
+ return (x, y)
269
+ }
270
+
271
+ static func pair(_ a: (u: BigI, s: Int, cur: String?), _ b: (u: BigI, s: Int, cur: String?)) -> (BigI, BigI, Int) {
272
+ let s = Swift.max(a.s, b.s)
273
+ return (a.u * BigI.pow10(s - a.s), b.u * BigI.pow10(s - b.s), s)
274
+ }
275
+
276
+ // Целое значение: дробная часть нулевая (защита в глубину под статикой плана).
277
+ static func integral(_ v: V) -> BigI? {
278
+ guard case let .dec(u, s) = v else { return nil }
279
+ let (q, r) = u.divMod(BigI.pow10(s))
280
+ return r.isZero ? q : nil
281
+ }
282
+
283
+ static func add(_ a: V, _ b: V) throws -> V {
284
+ let (x, y) = try same(a, b, "add")
285
+ let (p, q, s) = pair(x, y)
286
+ return make(p + q, s, x.cur)
287
+ }
288
+
289
+ static func sub(_ a: V, _ b: V) throws -> V {
290
+ let (x, y) = try same(a, b, "sub")
291
+ let (p, q, s) = pair(x, y)
292
+ return make(p - q, s, x.cur)
293
+ }
294
+
295
+ // Правило скаляра §49: безразмерное масштабирует размерное; масштаб результата —
296
+ // сумма масштабов. Два размерных не перемножаются.
297
+ static func mul(_ a: V, _ b: V) throws -> V {
298
+ guard let x = num(a), let y = num(b) else { throw Refused("§58: виды операндов не совпадают: mul") }
299
+ if x.cur != nil && y.cur != nil { throw Refused("§58: произведение двух размерных величин не определено") }
300
+ return make(x.u * y.u, x.s + y.s, x.cur ?? y.cur)
301
+ }
302
+
303
+ // Точное десятичное частное num/den при 10-гладком знаменателе (§18), иначе nil.
304
+ static func exact(_ num0: BigI, _ den0: BigI) -> (BigI, Int)? {
305
+ var g = BigI.gcd(num0, den0)
306
+ if g.isZero { g = BigI(1) }
307
+ var num = num0.divMod(g).0
308
+ var den = den0.divMod(g).0
309
+ if den.neg {
310
+ num = num.negated
311
+ den = den.negated
312
+ }
313
+ var twos = 0, fives = 0
314
+ while true {
315
+ let (q, r) = den.divMod(BigI(2))
316
+ if !r.isZero { break }
317
+ den = q
318
+ twos += 1
319
+ }
320
+ while true {
321
+ let (q, r) = den.divMod(BigI(5))
322
+ if !r.isZero { break }
323
+ den = q
324
+ fives += 1
325
+ }
326
+ if BigI.cmp(den, BigI(1)) != 0 { return nil }
327
+ let k = Swift.max(twos, fives)
328
+ var f = BigI(1)
329
+ for _ in 0..<(k - twos) { f = f * BigI(2) }
330
+ for _ in 0..<(k - fives) { f = f * BigI(5) }
331
+ return (num * f, k)
332
+ }
333
+
334
+ // Таблица делимых §58 в срезе: Decimal/Decimal, Money/Decimal, Money/Money одной
335
+ // валюты. Integer/Integer даёт Rational — вне среза, отвергается планом статически.
336
+ static func div(_ a: V, _ b: V) throws -> V {
337
+ guard let x = num(a), let y = num(b) else { throw Refused("§58: деление вне таблицы делимых") }
338
+ if y.u.isZero { throw Refused("деление на ноль — evaluation error (§58)") }
339
+ var like: String? = nil
340
+ if x.cur != nil && y.cur != nil {
341
+ if x.cur != y.cur {
342
+ throw Refused("CURRENCY_MISMATCH: §58 делит Money на Money только при тождественной валюте")
343
+ }
344
+ } else if x.cur != nil {
345
+ like = x.cur
346
+ } else if y.cur != nil {
347
+ throw Refused("§58: деление вне таблицы делимых")
348
+ }
349
+ guard let q = exact(x.u * BigI.pow10(y.s), y.u * BigI.pow10(x.s)) else {
350
+ if like != nil { throw Refused("INEXACT_DIVISION: округление только явной политикой (§50/§18)") }
351
+ throw Refused("§58: частное не 10-гладкое — Rational вне среза")
352
+ }
353
+ return make(q.0, q.1, like)
354
+ }
355
+
356
+ // Евклидов остаток §58: 0 <= r < |b| при любых знаках операндов.
357
+ static func mod(_ a: V, _ b: V) throws -> V {
358
+ guard let x = integral(a), let y = integral(b) else { throw Refused("§58: mod определён только для Integer") }
359
+ if y.isZero { throw Refused("остаток от деления на ноль — evaluation error (§58)") }
360
+ var r = x.divMod(y.magnitude).1
361
+ if r.neg { r = r + y.magnitude }
362
+ return .dec(r, 0)
363
+ }
364
+
365
+ static func min(_ a: V, _ b: V) throws -> V {
366
+ let (x, y) = try same(a, b, "min")
367
+ let (p, q, _) = pair(x, y)
368
+ return BigI.cmp(p, q) <= 0 ? a : b
369
+ }
370
+
371
+ static func max(_ a: V, _ b: V) throws -> V {
372
+ let (x, y) = try same(a, b, "max")
373
+ let (p, q, _) = pair(x, y)
374
+ return BigI.cmp(p, q) >= 0 ? a : b
375
+ }
376
+
377
+ static func abs(_ v: V) throws -> V {
378
+ guard let x = num(v) else { throw Refused("§58: abs — не числовой вид") }
379
+ return make(x.u.magnitude, x.s, x.cur)
380
+ }
381
+
382
+ static let powMin = BigI(parsing: "-9223372036854775808")
383
+ static let powMax = BigI(parsing: "9223372036854775807")
384
+
385
+ // §58 (E-0075): только Integer pow Integer; порядок проверок — знак, предел,
386
+ // вычисление, диапазон — часть контракта. Умножение останавливается, как
387
+ // только модуль превысил 2^63: при |основание| >= 1 он дальше не убывает, а
388
+ // при нулевом основании не растёт, — исход тот же, что у полного вычисления.
389
+ static func pow(_ base: V, _ exp: V) throws -> V {
390
+ guard let b = integral(base), let e = integral(exp) else { throw Refused("§58: pow определён только для Integer") }
391
+ if e.neg { throw Refused("§58: pow — показатель отрицателен") }
392
+ if BigI.cmp(e, BigI(4096)) > 0 { throw Refused("RESOURCE_LIMIT: pow — показатель превышает 4096") }
393
+ var r = BigI(1)
394
+ for _ in 0..<e.small {
395
+ r = r * b
396
+ if BigI.magCmp(r.m, powMin.m) > 0 { break }
397
+ }
398
+ if BigI.cmp(r, powMin) < 0 || BigI.cmp(r, powMax) > 0 {
399
+ throw Refused("RESOURCE_LIMIT: pow — результат вне 64-битного диапазона")
400
+ }
401
+ return .dec(r, 0)
402
+ }
403
+
404
+ static func round(_ m: V, _ digits: V, _ mode: String) throws -> V {
405
+ if mode != "HALF_UP" { throw Refused("§50: режим округления не реализован: \(mode)") }
406
+ guard let x = num(m) else { throw Refused("§50: round — не числовой вид") }
407
+ guard case let .dec(du, ds) = digits else { throw Refused("§50: round — не числовой вид") }
408
+ let n = du.divMod(BigI.pow10(ds)).0.small
409
+ if x.s <= n { return make(x.u * BigI.pow10(n - x.s), n, x.cur) }
410
+ let drop = BigI.pow10(x.s - n)
411
+ let (q, r) = x.u.magnitude.divMod(drop)
412
+ let up = BigI.cmp(r + r, drop) >= 0 ? q + BigI(1) : q // HALF_UP — от нуля
413
+ return make(x.u.neg ? up.negated : up, n, x.cur)
414
+ }
415
+
416
+ static func order(_ op: String, _ c: Int) throws -> Bool {
417
+ switch op {
418
+ case "lt": return c < 0
419
+ case "le": return c <= 0
420
+ case "gt": return c > 0
421
+ case "ge": return c >= 0
422
+ case "eq": return c == 0
423
+ case "ne": return c != 0
424
+ default: throw Refused("сравнение вне среза рантайма: \(op)")
425
+ }
426
+ }
427
+
428
+ static func strOrder(_ a: String, _ b: String) -> Int {
429
+ // Порядок по байтам UTF-8: у записи ISO он и есть порядок дня.
430
+ let x = Array(a.utf8), y = Array(b.utf8)
431
+ for i in 0..<Swift.min(x.count, y.count) where x[i] != y[i] { return x[i] < y[i] ? -1 : 1 }
432
+ return x.count == y.count ? 0 : (x.count < y.count ? -1 : 1)
433
+ }
434
+
435
+ static func cmp(_ a: V, _ op: String, _ b: V) throws -> Bool {
436
+ if case .sym = a {} else if case .sym = b {} else {
437
+ return try cmpNoSym(a, op, b)
438
+ }
439
+ var same = false
440
+ if case let .sym(x) = a, case let .sym(y) = b { same = x == y }
441
+ if op == "eq" { return same }
442
+ if op == "ne" { return !same }
443
+ throw Refused("§57: вид enum не упорядочен")
444
+ }
445
+
446
+ static func cmpNoSym(_ a: V, _ op: String, _ b: V) throws -> Bool {
447
+ switch (a, b) {
448
+ case let (.date(x), .date(y)): return try order(op, strOrder(x, y))
449
+ case (.date, _), (_, .date):
450
+ if op == "eq" { return false }
451
+ if op == "ne" { return true }
452
+ throw Refused("§57: Date сравнивается только с Date")
453
+ default: break
454
+ }
455
+ switch (a, b) {
456
+ case (.text, _), (_, .text):
457
+ var same = false
458
+ if case let .text(x) = a, case let .text(y) = b { same = x == y }
459
+ if op == "eq" { return same }
460
+ if op == "ne" { return !same }
461
+ throw Refused("порядок на Text вне среза рантайма: \(op)")
462
+ default: break
463
+ }
464
+ let x = num(a)!, y = num(b)!
465
+ if (x.cur == nil) != (y.cur == nil) { throw Refused("§49: сравнение денег с безразмерным") }
466
+ if x.cur != y.cur { throw Refused("§49: разные валюты") }
467
+ let (p, q, _) = pair(x, y)
468
+ return try order(op, BigI.cmp(p, q))
469
+ }
470
+
471
+ static func eq(_ a: V, _ b: V) -> Bool {
472
+ switch (a, b) {
473
+ case let (.sym(x), .sym(y)): return x == y
474
+ case (.sym, _), (_, .sym): return false
475
+ case let (.date(x), .date(y)): return x == y
476
+ case (.date, _), (_, .date): return false
477
+ case let (.text(x), .text(y)): return x == y
478
+ case (.text, _), (_, .text): return false
479
+ default: break
480
+ }
481
+ let x = num(a)!, y = num(b)!
482
+ if (x.cur == nil) != (y.cur == nil) || x.cur != y.cur { return false }
483
+ let (p, q, _) = pair(x, y)
484
+ return BigI.cmp(p, q) == 0
485
+ }
486
+
487
+ static func norm(_ u0: BigI, _ s0: Int) -> String {
488
+ var u = u0, s = s0
489
+ while s > 0 {
490
+ let (q, r) = u.divMod(BigI(10))
491
+ if !r.isZero { break }
492
+ u = q
493
+ s -= 1
494
+ }
495
+ return "\(u.description)e-\(s)"
496
+ }
497
+
498
+ // Разделитель и экран внутри текста экранируются: иначе ["a|e:b","c"] и
499
+ // ["a","b|e:c"] склеивались в один ключ, и второй факт терялся.
500
+ static func esc(_ text: String) -> String {
501
+ var out = ""
502
+ for ch in text.unicodeScalars {
503
+ if ch == "\\" { out += "\\\\" } else if ch == "|" { out += "\\|" } else { out.unicodeScalars.append(ch) }
504
+ }
505
+ return out
506
+ }
507
+
508
+ static func key(_ row: [V]) -> String {
509
+ row.map { v -> String in
510
+ switch v {
511
+ case let .text(t): return "e:" + esc(t)
512
+ case let .date(d): return "t:" + d
513
+ case let .sym(id): return "c:" + esc(id)
514
+ case let .money(u, s, cur): return "m:" + cur + ":" + norm(u, s)
515
+ case let .dec(u, s): return "d:" + norm(u, s)
516
+ }
517
+ }.joined(separator: "|")
518
+ }
519
+
520
+ static var legal = ""
521
+
522
+ static func setLegalTime(_ t: String) { legal = t }
523
+
524
+ static func legalTime() throws -> String {
525
+ if legal.isEmpty { throw Refused("legalTime не подан делом") }
526
+ return legal
527
+ }
528
+ }