Sources/OrgCore/Compute/Calc.swift
879 lines · 37464 bytes
62 symbols in this file
CalcUnsupportedFailureValueFloatFormatModesevaluatedigitspower10scaleRoundingmakeFloatpartsrationalfractiongcdexactdoublefromDoubleaddnegatesubtractisNaNmultiplydivideisIntegerintegerPowerpowercompareformatformatDateparseDateNodeParserskippeekparsetakeorandnotcomparisonsumquotientproductunarypowerNodeprimarynumberEvaluatorevaltruthmodidivfloorOfdeviationsrootelementsoneradiansanglecallexactRoot
1import Foundation
2
3/// The part of Emacs Calc that table formulas use, with Calc's number semantics: exact
4/// integers, decimal floats rounded to `calc-internal-prec` digits after every operation, and
5/// Calc's display formats. Anything outside this domain throws `Calc.Unsupported`, and the
6/// caller hands the work to Emacs.
7public enum Calc {
8 public struct Unsupported: Error, Equatable, CustomStringConvertible {
9 public let reason: String
10 public var description: String { reason }
11 init(_ reason: String) { self.reason = reason }
12 }
13
14 /// An error Calc reports, which a table shows as `#ERROR`.
15 public struct Failure: Error, Equatable, CustomStringConvertible {
16 public let reason: String
17 public var description: String { reason }
18 init(_ reason: String) { self.reason = reason }
19 }
20
21 public indirect enum Value: Equatable, Sendable {
22 case int(Int)
23 /// Mantissa and exponent, normalized: at most `precision` digits, no trailing zeros.
24 case float(Int, Int)
25 case vector([Value])
26 /// A date form: the day number, with the time of day as a fraction.
27 case date(Value)
28 case nan
29 /// An exact fraction, reduced, the denominator above 1 (`calc-prefer-frac`).
30 case frac(Int, Int)
31 }
32
33 public enum FloatFormat: Equatable, Sendable {
34 case float(Int), fix(Int), sci(Int), eng(Int)
35 }
36
37 public struct Modes: Equatable, Sendable {
38 public var precision = 12
39 public var format = FloatFormat.float(8)
40 public var degrees = true
41 /// `calc-prefer-frac`: dividing integers gives a fraction.
42 public var preferFractions = false
43 public init() {}
44 }
45
46 /// `calc-eval` of `expression`, as the string Calc would return; with `numeric`, as
47 /// `calc-eval` with `num`, a result that isn't a number is an error.
48 public static func evaluate(_ expression: String, modes: Modes = Modes(), numeric: Bool = false) throws -> String {
49 guard modes.precision == 12 else { throw Unsupported("calc precision") }
50 var parser = Parser(expression)
51 let tree = try parser.parse()
52 let value = try Evaluator(modes: modes).eval(tree)
53 if numeric {
54 switch value {
55 case .int, .float: break
56 default: throw Failure("Number expected")
57 }
58 }
59 return try format(value, modes.format)
60 }
61
62 // MARK: - Numbers
63
64 static let precision = 12
65
66 static func digits(_ x: Int128) -> Int {
67 var n = x.magnitude
68 var count = 1
69 while n >= 10 {
70 n /= 10
71 count += 1
72 }
73 return count
74 }
75
76 static func power10(_ n: Int) throws -> Int128 {
77 guard n >= 0, n <= 37 else { throw Unsupported("number size") }
78 var result: Int128 = 1
79 for _ in 0..<n { result *= 10 }
80 return result
81 }
82
83 /// `math-scale-rounding`: `x` times 10^n, rounding half away from zero when n < 0.
84 static func scaleRounding(_ x: Int128, _ n: Int) throws -> Int128 {
85 if n >= 0 {
86 let (result, overflow) = x.multipliedReportingOverflow(by: try power10(n))
87 if overflow { throw Unsupported("number size") }
88 return result
89 }
90 if x < 0 { return -(try scaleRounding(-x, n)) }
91 if -n > 38 { return 0 }
92 let truncated = -n - 1 > 37 ? 0 : x / (try power10(-n - 1))
93 return (truncated + 5) / 10
94 }
95
96 /// `math-make-float`.
97 static func makeFloat(_ mantissa: Int128, _ exponent: Int) throws -> Value {
98 var m = mantissa
99 var e = exponent
100 if m == 0 { return .float(0, 0) }
101 let excess = digits(m) - precision
102 if excess > 0 {
103 m = try scaleRounding(m, -excess)
104 e += excess
105 }
106 if m == 0 { return .float(0, 0) }
107 while m % 10 == 0 {
108 m /= 10
109 e += 1
110 }
111 return .float(Int(m), e)
112 }
113
114 static func parts(_ v: Value) throws -> (Int128, Int) {
115 switch v {
116 case .int(let i): return (Int128(i), 0)
117 case .float(let m, let e): return (Int128(m), e)
118 case .frac(let n, let d): return try parts(try divide(.int(n), .int(d)))
119 case .vector: throw Unsupported("vector arithmetic")
120 case .date, .nan: throw Unsupported("date arithmetic")
121 }
122 }
123
124 /// An integer or a fraction as numerator and denominator.
125 static func rational(_ v: Value) -> (Int, Int)? {
126 switch v {
127 case .int(let i): (i, 1)
128 case .frac(let n, let d): (n, d)
129 default: nil
130 }
131 }
132
133 /// `math-make-frac`: reduced, as an integer when it is one.
134 static func fraction(_ n: Int, _ d: Int) throws -> Value {
135 guard d != 0 else { throw Unsupported("division by zero") }
136 guard n != .min, d != .min else { throw Unsupported("integer size") }
137 func gcd(_ a: Int, _ b: Int) -> Int { b == 0 ? abs(a) : gcd(b, a % b) }
138 let g = gcd(n, d)
139 var (num, den) = (n / g, d / g)
140 if den < 0 { (num, den) = (-num, -den) }
141 return den == 1 ? .int(num) : .frac(num, den)
142 }
143
144 /// Two rationals, one of them a fraction, combined exactly; nil otherwise.
145 static func exact(_ a: Value, _ b: Value, _ op: (Int, Int, Int, Int) -> (Int, Int, Bool)) throws -> Value? {
146 var fraction = false
147 if case .frac = a { fraction = true }
148 if case .frac = b { fraction = true }
149 guard fraction, let (n1, d1) = rational(a), let (n2, d2) = rational(b) else { return nil }
150 let (n, d, overflow) = op(n1, d1, n2, d2)
151 if overflow { throw Unsupported("integer size") }
152 return try self.fraction(n, d)
153 }
154
155 static func double(_ v: Value) throws -> Double {
156 switch v {
157 case .int(let i): return Double(i)
158 case .float(let m, let e): return Double(m) * pow(10, Double(e))
159 case .frac(let n, let d): return Double(n) / Double(d)
160 case .vector: throw Unsupported("vector arithmetic")
161 case .date, .nan: throw Unsupported("date arithmetic")
162 }
163 }
164
165 static func fromDouble(_ d: Double) throws -> Value {
166 guard d.isFinite else { throw Unsupported("infinite result") }
167 if d == 0 { return .float(0, 0) }
168 // Seventeen significant digits are exact for a Double; rounding to 12 follows.
169 let text = String(format: "%.16e", d)
170 let pieces = text.split(separator: "e")
171 let digitsText = pieces[0].replacingOccurrences(of: ".", with: "")
172 guard let mantissa = Int128(digitsText), let exponent = Int(pieces[1]) else { throw Unsupported("number") }
173 return try makeFloat(mantissa, exponent - 16)
174 }
175
176 static func add(_ a: Value, _ b: Value) throws -> Value {
177 switch (a, b) {
178 case (.nan, .vector), (.vector, .nan): throw Unsupported("vector arithmetic")
179 case (.nan, _), (_, .nan): return .nan
180 case (.date, .date): throw Unsupported("date arithmetic")
181 case (.date(let d), _): return .date(try add(d, b))
182 case (_, .date(let d)): return .date(try add(a, d))
183 default: break
184 }
185 if case .int(let x) = a, case .int(let y) = b {
186 let (sum, overflow) = x.addingReportingOverflow(y)
187 if overflow { throw Unsupported("integer size") }
188 return .int(sum)
189 }
190 if let sum = try exact(a, b, { n1, d1, n2, d2 in
191 let (p, o1) = n1.multipliedReportingOverflow(by: d2)
192 let (q, o2) = n2.multipliedReportingOverflow(by: d1)
193 let (n, o3) = p.addingReportingOverflow(q)
194 let (d, o4) = d1.multipliedReportingOverflow(by: d2)
195 return (n, d, o1 || o2 || o3 || o4)
196 }) { return sum }
197 let (m1, e1) = try parts(a)
198 let (m2, e2) = try parts(b)
199 if m1 == 0 { return try makeFloat(m2, e2) }
200 if m2 == 0 { return try makeFloat(m1, e1) }
201 let ediff = e1 - e2
202 if ediff >= 0 {
203 if ediff >= 2 * precision { return try makeFloat(m1, e1) }
204 return try makeFloat(try scaleRounding(m1, ediff) + m2, e2)
205 }
206 if -ediff >= 2 * precision { return try makeFloat(m2, e2) }
207 return try makeFloat(m1 + (try scaleRounding(m2, -ediff)), e1)
208 }
209
210 static func negate(_ a: Value) throws -> Value {
211 switch a {
212 case .int(let i):
213 guard i != .min else { throw Unsupported("integer size") }
214 return .int(-i)
215 case .float(let m, let e): return .float(-m, e)
216 case .frac(let n, let d): return .frac(-n, d)
217 case .vector: throw Unsupported("vector arithmetic")
218 case .date: throw Unsupported("date arithmetic")
219 case .nan: return .nan
220 }
221 }
222
223 /// `a - b`: a date less a date is the days between them.
224 static func subtract(_ a: Value, _ b: Value) throws -> Value {
225 if case .date(let x) = a, case .date(let y) = b { return try add(x, try negate(y)) }
226 if case .date(let x) = a, b != .nan { return .date(try add(x, try negate(b))) }
227 return try add(a, try negate(b))
228 }
229
230 static func isNaN(_ a: Value, _ b: Value) -> Bool {
231 if case .vector = a { return false }
232 if case .vector = b { return false }
233 return a == .nan || b == .nan
234 }
235
236 static func multiply(_ a: Value, _ b: Value) throws -> Value {
237 if isNaN(a, b) { return .nan }
238 if case .int(let x) = a, case .int(let y) = b {
239 let (product, overflow) = x.multipliedReportingOverflow(by: y)
240 if overflow { throw Unsupported("integer size") }
241 return .int(product)
242 }
243 if let product = try exact(a, b, { n1, d1, n2, d2 in
244 let (n, o1) = n1.multipliedReportingOverflow(by: n2)
245 let (d, o2) = d1.multipliedReportingOverflow(by: d2)
246 return (n, d, o1 || o2)
247 }) { return product }
248 let (m1, e1) = try parts(a)
249 let (m2, e2) = try parts(b)
250 let (product, overflow) = m1.multipliedReportingOverflow(by: m2)
251 if overflow { throw Unsupported("number size") }
252 return try makeFloat(product, e1 + e2)
253 }
254
255 static func divide(_ a: Value, _ b: Value, fractions: Bool = false) throws -> Value {
256 if isNaN(a, b) { return .nan }
257 if fractions || { if case .frac = a { return true }; if case .frac = b { return true }; return false }(),
258 let (n1, d1) = rational(a), let (n2, d2) = rational(b) {
259 guard n2 != 0 else { throw Unsupported("division by zero") }
260 let (n, o1) = n1.multipliedReportingOverflow(by: d2)
261 let (d, o2) = d1.multipliedReportingOverflow(by: n2)
262 if o1 || o2 { throw Unsupported("integer size") }
263 return try fraction(n, d)
264 }
265 if case .int(let x) = a, case .int(let y) = b {
266 guard y != 0 else { throw Unsupported("division by zero") }
267 if x % y == 0 { return .int(x / y) }
268 }
269 let (m1, e1) = try parts(a)
270 let (m2, e2) = try parts(b)
271 guard m2 != 0 else { throw Unsupported("division by zero") }
272 // `math-div-float`: scale so the truncated quotient has one digit more than needed.
273 let ldiff = max(precision + 1 - (digits(m1) - digits(m2)), 0)
274 let quotient = (try scaleRounding(m1, ldiff)) / m2
275 return try makeFloat(quotient, e1 - ldiff - e2)
276 }
277
278 static func isInteger(_ v: Value) -> Bool {
279 if case .int = v { return true }
280 return false
281 }
282
283 /// `math-ipow`.
284 static func integerPower(_ a: Value, _ n: Int, fractions: Bool = false) throws -> Value {
285 if n < 0 { return try integerPower(try divide(.int(1), a, fractions: fractions), -n) }
286 if n == 0 { return .int(1) }
287 if n == 1 { return a }
288 let square = try multiply(a, a)
289 if n % 2 == 0 { return try integerPower(square, n / 2) }
290 return try multiply(a, try integerPower(square, n / 2))
291 }
292
293 static func power(_ a: Value, _ b: Value, fractions: Bool = false) throws -> Value {
294 if isNaN(a, b) { return .nan }
295 if case .int(let n) = b { return try integerPower(a, n, fractions: fractions) }
296 let base = try double(a)
297 guard base >= 0 else { throw Unsupported("complex result") }
298 return try fromDouble(pow(base, try double(b)))
299 }
300
301 static func compare(_ a: Value, _ b: Value) throws -> Int {
302 if case .date(let x) = a, case .date(let y) = b { return try compare(x, y) }
303 if a == .nan || b == .nan { throw Unsupported("comparison with nan") }
304 let difference = try add(a, try negate(b))
305 let (m, _) = try parts(difference)
306 return m == 0 ? 0 : (m < 0 ? -1 : 1)
307 }
308
309 // MARK: - Display
310
311 /// `math-format-number`.
312 static func format(_ value: Value, _ floatFormat: FloatFormat) throws -> String {
313 switch value {
314 case .int(let i): return String(i)
315 case .frac(let n, let d): return "\(n):\(d)"
316 case .vector: throw Unsupported("vector result")
317 case .nan: return "nan"
318 case .date(let d): return "<" + (try formatDate(d)) + ">"
319 case .float(let m, _) where m < 0:
320 guard case .float(let m, let e) = value else { fatalError() }
321 return "-" + (try format(.float(-m, e), floatFormat))
322 case .float(let m, var exp):
323 var mant = Int128(m)
324 var figs: Int
325 let kind: String
326 switch floatFormat {
327 case .float(let n): (figs, kind) = (n, "float")
328 case .fix(let n): (figs, kind) = (n, "fix")
329 case .sci(let n): (figs, kind) = (n, "sci")
330 case .eng(let n): (figs, kind) = (n, "eng")
331 }
332 if kind == "fix", figs < 0 || exp + digits(mant) > -figs {
333 if figs < 0 { figs = -figs }
334 mant = try scaleRounding(mant, exp + figs)
335 var str = String(mant)
336 if str.count <= figs { str = String(repeating: "0", count: figs + 1 - str.count) + str }
337 if figs > 0 {
338 return String(str.dropLast(figs)) + "." + String(str.suffix(figs))
339 }
340 return str + "."
341 }
342 if figs < 0 { figs += precision }
343 if figs > 0 {
344 let adj = figs - digits(mant)
345 if adj < 0 {
346 mant = try scaleRounding(mant, adj)
347 exp -= adj
348 }
349 }
350 var str = String(mant)
351 let len = str.count
352 let dpos = exp + len
353 // `calc-display-sci-high` 0, `calc-display-sci-low` -3.
354 if kind == "float", dpos <= precision, dpos >= -1 {
355 if dpos == 0 {
356 str = "0." + str
357 } else if exp <= 0, dpos > 0 {
358 str = String(str.prefix(dpos)) + "." + String(str.dropFirst(dpos))
359 } else if exp > 0 {
360 str += String(repeating: "0", count: exp) + "."
361 } else {
362 str = "0." + String(repeating: "0", count: -dpos) + str
363 }
364 return str
365 }
366 let eadj = exp + len
367 let scale = kind == "eng" ? 1 + ((eadj + 300002) % 3) : 1
368 if scale > str.count { str += String(repeating: "0", count: scale - str.count) }
369 if scale < str.count { str = String(str.prefix(scale)) + "." + String(str.dropFirst(scale)) }
370 return str + "e" + String(eadj - scale)
371 }
372 }
373
374 static let weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
375
376 /// `calc-date-format` as Org sets it: `YYYY-MM-DD Www`, then ` hh:mm` for a time of day.
377 static func formatDate(_ value: Value) throws -> String {
378 let days = try double(value)
379 let day = Int(days.rounded(.down))
380 let date = Days.date(day)
381 var text = String(format: "%04d-%02d-%02d ", date.year, date.month, date.day) + weekdays[Days.weekday(day)]
382 if case .float = value, days != Double(day) {
383 let seconds = Int(((days - Double(day)) * 86400).rounded())
384 text += String(format: " %02d:%02d", seconds / 3600, seconds / 60 % 60)
385 }
386 return text
387 }
388
389 /// A date form `<YYYY-MM-DD Www>` or `<YYYY-MM-DD Www hh:mm>`, from the text between
390 /// the brackets.
391 static func parseDate(_ text: String) throws -> Value {
392 guard let m = text.firstMatch(of: /^([0-9]{4})-([0-9]{2})-([0-9]{2})(?: +[A-Za-z]+)?(?: +([0-9]{1,2}):([0-9]{2})(?::([0-9]{2}))?)?$/) else {
393 throw Unsupported("date syntax")
394 }
395 let day = Days.absolute(year: Int(m.1)!, month: Int(m.2)!, day: Int(m.3)!)
396 guard let hour = m.4.flatMap({ Int($0) }), let minute = m.5.flatMap({ Int($0) }) else { return .date(.int(day)) }
397 let seconds = hour * 3600 + minute * 60 + (m.6.flatMap { Int($0) } ?? 0)
398 if seconds == 0 { return .date(.int(day)) }
399 return .date(try add(.int(day), try divide(.int(seconds), .int(86400))))
400 }
401
402 // MARK: - Parsing
403
404 indirect enum Node {
405 case number(Value)
406 case vector([Node])
407 case unary(Character, Node)
408 case binary(Character, Node, Node)
409 case call(String, [Node])
410 }
411
412 struct Parser {
413 let chars: [Character]
414 var i = 0
415
416 init(_ s: String) { chars = Array(s) }
417
418 mutating func skip() {
419 while i < chars.count, chars[i] == " " || chars[i] == "\t" { i += 1 }
420 }
421
422 mutating func peek() -> Character? {
423 skip()
424 return i < chars.count ? chars[i] : nil
425 }
426
427 mutating func parse() throws -> Node {
428 let node = try or()
429 guard peek() == nil else { throw Unsupported("formula syntax") }
430 return node
431 }
432
433 mutating func take(_ token: String) -> Bool {
434 guard peek() != nil else { return false }
435 let t = Array(token)
436 guard i + t.count <= chars.count, Array(chars[i..<(i + t.count)]) == t else { return false }
437 i += t.count
438 return true
439 }
440
441 // `||` < `&&` < `!` < comparisons < sums.
442 mutating func or() throws -> Node {
443 var left = try and()
444 while take("||") { left = .binary("|", left, try and()) }
445 return left
446 }
447
448 mutating func and() throws -> Node {
449 var left = try not()
450 while take("&&") { left = .binary("&", left, try not()) }
451 return left
452 }
453
454 mutating func not() throws -> Node {
455 if peek() == "!", i + 1 >= chars.count || chars[i + 1] != "=" {
456 i += 1
457 return .unary("!", try not())
458 }
459 return try comparison()
460 }
461
462 mutating func comparison() throws -> Node {
463 let left = try sum()
464 for (token, op) in [("==", "="), ("!=", "≠"), ("<=", "≤"), (">=", "≥"), ("<", "<"), (">", ">")] where take(token) {
465 let right = try sum()
466 if let next = peek(), "=!<>".contains(next) { throw Unsupported("chained comparison") }
467 return .binary(Character(op), left, right)
468 }
469 return left
470 }
471
472 // `+ -` < `/` < `*` < unary minus < `^`, as Calc's operator table orders them.
473 mutating func sum() throws -> Node {
474 var left = try quotient()
475 while let c = peek(), c == "+" || c == "-" {
476 i += 1
477 left = .binary(c, left, try quotient())
478 }
479 return left
480 }
481
482 mutating func quotient() throws -> Node {
483 var left = try product()
484 while let c = peek(), c == "/" || c == "%" || c == "\\" {
485 i += 1
486 left = .binary(c, left, try product())
487 }
488 return left
489 }
490
491 mutating func product() throws -> Node {
492 var left = try unary()
493 while peek() == "*" {
494 i += 1
495 left = .binary("*", left, try unary())
496 }
497 return left
498 }
499
500 mutating func unary() throws -> Node {
501 if let c = peek(), c == "-" || c == "+" {
502 i += 1
503 let operand = try unary()
504 return c == "-" ? .unary("-", operand) : operand
505 }
506 return try powerNode()
507 }
508
509 mutating func powerNode() throws -> Node {
510 let base = try primary()
511 if peek() == "^" {
512 i += 1
513 return .binary("^", base, try unary())
514 }
515 return base
516 }
517
518 mutating func primary() throws -> Node {
519 guard let c = peek() else { throw Unsupported("formula syntax") }
520 if c == "(" {
521 i += 1
522 let inner = try or()
523 guard peek() == ")" else { throw Unsupported("formula syntax") }
524 i += 1
525 return inner
526 }
527 if c == "[" {
528 i += 1
529 var items: [Node] = []
530 if peek() != "]" {
531 items.append(try or())
532 while peek() == "," {
533 i += 1
534 items.append(try or())
535 }
536 }
537 guard peek() == "]" else { throw Unsupported("formula syntax") }
538 i += 1
539 return .vector(items)
540 }
541 if c.isASCII, c.isNumber || c == "." { return .number(try number()) }
542 if c == "<" {
543 guard let close = chars[i...].firstIndex(of: ">") else { throw Unsupported("formula syntax") }
544 let text = String(chars[(i + 1)..<close])
545 i = close + 1
546 return .number(try parseDate(text))
547 }
548 if c.isLetter {
549 var name = ""
550 while i < chars.count, chars[i].isLetter || chars[i].isNumber || chars[i] == "_" {
551 name.append(chars[i])
552 i += 1
553 }
554 guard peek() == "(" else {
555 if name == "nan" { return .number(.nan) }
556 throw Unsupported("variable \(name)")
557 }
558 i += 1
559 var args: [Node] = []
560 if peek() != ")" {
561 args.append(try or())
562 while peek() == "," {
563 i += 1
564 args.append(try or())
565 }
566 }
567 guard peek() == ")" else { throw Unsupported("formula syntax") }
568 i += 1
569 return .call(name, args)
570 }
571 throw Unsupported("formula syntax")
572 }
573
574 /// Calc's number syntax: `7`, `007`, `1.5`, `.5`, `5.`, `1e3`, `1.5e-3`.
575 mutating func number() throws -> Value {
576 var whole = ""
577 while i < chars.count, chars[i].isASCII, chars[i].isNumber {
578 whole.append(chars[i])
579 i += 1
580 }
581 var fraction: String?
582 if i < chars.count, chars[i] == "." {
583 i += 1
584 var f = ""
585 while i < chars.count, chars[i].isASCII, chars[i].isNumber {
586 f.append(chars[i])
587 i += 1
588 }
589 fraction = f
590 }
591 var exponent: Int?
592 if i < chars.count, chars[i] == "e" || chars[i] == "E" {
593 var j = i + 1
594 var e = ""
595 if j < chars.count, chars[j] == "-" || chars[j] == "+" {
596 e.append(chars[j])
597 j += 1
598 }
599 var digitsSeen = false
600 while j < chars.count, chars[j].isASCII, chars[j].isNumber {
601 e.append(chars[j])
602 j += 1
603 digitsSeen = true
604 }
605 if digitsSeen {
606 exponent = Int(e)
607 i = j
608 }
609 }
610 guard !whole.isEmpty || !(fraction ?? "").isEmpty else { throw Unsupported("formula syntax") }
611 if fraction == nil, exponent == nil {
612 guard let value = Int(whole) else { throw Unsupported("integer size") }
613 return .int(value)
614 }
615 let digitsText = whole + (fraction ?? "")
616 guard let mantissa = Int128(digitsText.isEmpty ? "0" : digitsText) else { throw Unsupported("number size") }
617 return try makeFloat(mantissa, (exponent ?? 0) - (fraction?.count ?? 0))
618 }
619 }
620
621 // MARK: - Evaluation
622
623 struct Evaluator {
624 let modes: Modes
625
626 func eval(_ node: Node) throws -> Value {
627 switch node {
628 case .number(let v): return v
629 case .vector(let items): return .vector(try items.map(eval))
630 case .unary("!", let operand): return .int(try truth(try eval(operand)) ? 0 : 1)
631 case .unary(_, let operand): return try negate(try eval(operand))
632 case .binary(let op, let l, let r):
633 let a = try eval(l)
634 let b = try eval(r)
635 switch op {
636 case "+": return try add(a, b)
637 case "-": return try subtract(a, b)
638 case "*": return try multiply(a, b)
639 case "/": return try divide(a, b, fractions: modes.preferFractions)
640 case "%": return try mod(a, b)
641 case "\\": return try idiv(a, b)
642 case "^": return try power(a, b, fractions: modes.preferFractions)
643 case "|": return .int(try truth(a) || truth(b) ? 1 : 0)
644 case "&": return .int(try truth(a) && truth(b) ? 1 : 0)
645 default:
646 let order = try compare(a, b)
647 let holds = switch op {
648 case "=": order == 0
649 case "≠": order != 0
650 case "<": order < 0
651 case ">": order > 0
652 case "≤": order <= 0
653 default: order >= 0
654 }
655 return .int(holds ? 1 : 0)
656 }
657 case .call(let name, let args):
658 return try call(name, try args.map(eval))
659 }
660 }
661
662 /// A condition's value: a number, nonzero for true.
663 func truth(_ v: Value) throws -> Bool {
664 switch v {
665 case .int(let i): return i != 0
666 case .float(let m, _): return m != 0
667 default: throw Unsupported("condition")
668 }
669 }
670
671 /// `math-mod`: the remainder with the divisor's sign.
672 func mod(_ a: Value, _ b: Value) throws -> Value {
673 if case .int(let x) = a, case .int(let y) = b {
674 guard y != 0 else { throw Unsupported("division by zero") }
675 let r = x % y
676 return .int(r != 0 && (r < 0) != (y < 0) ? r + y : r)
677 }
678 let q = try floorOf(try divide(a, b))
679 return try subtract(a, try multiply(b, q))
680 }
681
682 /// `calcFunc-idiv`: the quotient rounded down.
683 func idiv(_ a: Value, _ b: Value) throws -> Value {
684 if case .int(let x) = a, case .int(let y) = b {
685 guard y != 0 else { throw Unsupported("division by zero") }
686 let q = x / y
687 return .int(q * y != x && (x < 0) != (y < 0) ? q - 1 : q)
688 }
689 return try floorOf(try divide(a, b))
690 }
691
692 func floorOf(_ v: Value) throws -> Value {
693 if case .int = v { return v }
694 guard let i = Int(exactly: try double(v).rounded(.down)) else { throw Unsupported("integer size") }
695 return .int(i)
696 }
697
698 /// The sum of squared deviations from the mean, and the count.
699 func deviations(_ args: [Value]) throws -> (Value, Int) {
700 let items = try elements(args)
701 guard !items.isEmpty else { throw Unsupported("empty vector") }
702 let mean = try divide(try items.reduce(Value.int(0)) { try add($0, $1) }, .int(items.count), fractions: modes.preferFractions)
703 let squares = try items.reduce(Value.int(0)) { sum, x in
704 let d = try subtract(x, mean)
705 return try add(sum, try multiply(d, d))
706 }
707 return (squares, items.count)
708 }
709
710 func root(_ x: Value) throws -> Value {
711 try call("sqrt", [x])
712 }
713
714 func elements(_ args: [Value]) throws -> [Value] {
715 guard args.count == 1 else { throw Unsupported("arguments") }
716 if case .vector(let items) = args[0] { return items }
717 return [args[0]]
718 }
719
720 func one(_ args: [Value]) throws -> Value {
721 guard args.count == 1 else { throw Unsupported("arguments") }
722 if case .vector = args[0] { throw Unsupported("vector argument") }
723 return args[0]
724 }
725
726 func radians(_ x: Double) -> Double { modes.degrees ? x * .pi / 180 : x }
727 func angle(_ x: Double) -> Double { modes.degrees ? x * 180 / .pi : x }
728
729 func call(_ name: String, _ args: [Value]) throws -> Value {
730 switch name {
731 case "vsum":
732 return try elements(args).reduce(Value.int(0)) { try add($0, $1) }
733 case "vprod":
734 return try elements(args).reduce(Value.int(1)) { try multiply($0, $1) }
735 case "vcount":
736 return .int(try elements(args).count)
737 case "vmean":
738 let items = try elements(args)
739 guard !items.isEmpty else { throw Unsupported("empty vector") }
740 return try divide(try items.reduce(Value.int(0)) { try add($0, $1) }, .int(items.count), fractions: modes.preferFractions)
741 case "vmax", "vmin":
742 let items = try elements(args)
743 guard var best = items.first else { throw Unsupported("empty vector") }
744 for item in items.dropFirst() {
745 let order = try compare(item, best)
746 if name == "vmax" ? order > 0 : order < 0 { best = item }
747 }
748 return best
749 case "vmedian":
750 var items = try elements(args)
751 guard !items.isEmpty else { throw Unsupported("empty vector") }
752 try items.sort { try compare($0, $1) < 0 }
753 if items.count % 2 == 1 { return items[items.count / 2] }
754 return try divide(try add(items[items.count / 2 - 1], items[items.count / 2]), .int(2))
755 case "if":
756 guard args.count == 3 else { throw Unsupported("arguments") }
757 return try truth(args[0]) ? args[1] : args[2]
758 case "vvar", "vpvar", "vsdev", "vpsdev":
759 let (squares, n) = try deviations(args)
760 let population = name == "vpvar" || name == "vpsdev"
761 if !population, n < 2 { throw Unsupported("variance of one value") }
762 let variance = try divide(squares, .int(population ? n : n - 1), fractions: modes.preferFractions)
763 return name.hasSuffix("sdev") ? try root(variance) : variance
764 case "mod":
765 guard args.count == 2 else { throw Unsupported("arguments") }
766 return try mod(args[0], args[1])
767 case "idiv":
768 guard args.count == 2 else { throw Unsupported("arguments") }
769 return try idiv(args[0], args[1])
770 case "fact":
771 guard case .int(let n) = try one(args), n >= 0 else { throw Unsupported("arguments") }
772 var result = Value.int(1)
773 for k in stride(from: 2, through: n, by: 1) { result = try multiply(result, .int(k)) }
774 return result
775 case "max", "min":
776 guard !args.isEmpty else { throw Unsupported("arguments") }
777 var best = args[0]
778 for item in args.dropFirst() {
779 let order = try compare(item, best)
780 if name == "max" ? order > 0 : order < 0 { best = item }
781 }
782 return best
783 case "abs":
784 let x = try one(args)
785 return try compare(x, .int(0)) < 0 ? try negate(x) : x
786 case "floor", "ceil", "trunc":
787 var x = try one(args)
788 if case .frac(let n, let d) = x { x = try divide(.int(n), .int(d)) }
789 if case .int = x { return x }
790 let d = try double(x)
791 let r = name == "floor" ? d.rounded(.down) : name == "ceil" ? d.rounded(.up) : d.rounded(.towardZero)
792 guard let i = Int(exactly: r) else { throw Unsupported("integer size") }
793 return .int(i)
794 case "round":
795 guard args.count == 1 || args.count == 2 else { throw Unsupported("arguments") }
796 var x = args[0]
797 if case .frac(let n, let d) = x { x = try divide(.int(n), .int(d)) }
798 if args.count == 2 {
799 guard case .int(let places) = args[1] else { throw Unsupported("arguments") }
800 guard case .float(let m, let e) = x else { return x }
801 return try makeFloat(try scaleRounding(Int128(m), e + places), -places)
802 }
803 guard case .float(let m, let e) = x else { return x }
804 guard let i = Int(exactly: try scaleRounding(Int128(m), e)) else { throw Unsupported("integer size") }
805 return .int(i)
806 case "sqrt":
807 let x = try one(args)
808 func exactRoot(_ i: Int) -> Int? {
809 guard i >= 0 else { return nil }
810 let root = Int(Double(i).squareRoot().rounded())
811 return root.multipliedReportingOverflow(by: root) == (i, false) ? root : nil
812 }
813 if case .int(let i) = x {
814 guard i >= 0 else { throw Unsupported("complex result") }
815 if let root = exactRoot(i) { return .int(root) }
816 }
817 if case .frac(let n, let d) = x, let rn = exactRoot(n), let rd = exactRoot(d) { return try fraction(rn, rd) }
818 let d = try double(x)
819 guard d >= 0 else { throw Unsupported("complex result") }
820 return try fromDouble(d.squareRoot())
821 case "exp":
822 let x = try one(args)
823 if x == .int(0) { return .int(1) }
824 return try fromDouble(Foundation.exp(try double(x)))
825 case "ln":
826 let x = try one(args)
827 if x == .int(1) { return .int(0) }
828 let d = try double(x)
829 guard d > 0 else { throw Unsupported("logarithm domain") }
830 return try fromDouble(Foundation.log(d))
831 case "log10":
832 let x = try one(args)
833 if case .int(let i) = x, i > 0 {
834 var n = i
835 var k = 0
836 while n % 10 == 0 {
837 n /= 10
838 k += 1
839 }
840 if n == 1 { return .int(k) }
841 }
842 let d = try double(x)
843 guard d > 0 else { throw Unsupported("logarithm domain") }
844 return try fromDouble(Foundation.log10(d))
845 case "sin", "cos", "tan":
846 let x = try one(args)
847 if modes.degrees, case .int(let i) = x, i % 90 == 0 {
848 let quarter = ((i / 90) % 4 + 4) % 4
849 switch name {
850 case "sin": return .int([0, 1, 0, -1][quarter])
851 case "cos": return .int([1, 0, -1, 0][quarter])
852 default:
853 guard quarter % 2 == 0 else { throw Unsupported("infinite result") }
854 return .int(0)
855 }
856 }
857 if !modes.degrees, x == .int(0) { return .int(name == "cos" ? 1 : 0) }
858 let r = radians(try double(x))
859 return try fromDouble(name == "sin" ? Foundation.sin(r) : name == "cos" ? Foundation.cos(r) : Foundation.tan(r))
860 case "arcsin", "arccos", "arctan":
861 let x = try one(args)
862 if modes.degrees, case .int(let i) = x {
863 switch (name, i) {
864 case ("arcsin", -1), ("arcsin", 0), ("arcsin", 1): return .int(90 * i)
865 case ("arccos", -1), ("arccos", 0), ("arccos", 1): return .int(90 - 90 * i)
866 case ("arctan", -1), ("arctan", 0), ("arctan", 1): return .int(45 * i)
867 default: break
868 }
869 }
870 let d = try double(x)
871 if name != "arctan", abs(d) > 1 { throw Unsupported("complex result") }
872 let r = name == "arcsin" ? Foundation.asin(d) : name == "arccos" ? Foundation.acos(d) : Foundation.atan(d)
873 return try fromDouble(angle(r))
874 default:
875 throw Unsupported("function \(name)")
876 }
877 }
878 }
879}