spec: math completeness — trig, quotient, gcd/lcm, radix number<->string
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Phase 15 implementation: - spec/primitives.sx: stdlib.math module — sin/cos/tan/asin/acos/atan/exp/log/expt/quotient/gcd/lcm/number->string/string->number (13 primitives) - JS platform: stdlib.math module; strict string->number parsing (rejects partial matches like "fg" in base 16) - OCaml: expt, quotient, gcd, lcm, number->string (radix), string->number (radix); atan updated to accept optional 2nd arg (atan2 form) - spec/tests/test-math.sx: 44 tests — trig/inverse trig, expt, quotient semantics, gcd/lcm, radix formatting/parsing, tower integration - JS: 2311/4801 (+2 net); OCaml: 4547/5629 (+1 net); zero regressions in math area Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -948,4 +948,90 @@
|
||||
:returns "boolean"
|
||||
:doc "True if a char is immediately available on the port.")
|
||||
|
||||
(define-module :stdlib.math)
|
||||
|
||||
(define-primitive
|
||||
"sin"
|
||||
:params ((x :as number))
|
||||
:returns "float"
|
||||
:doc "Sine of x (radians).")
|
||||
|
||||
(define-primitive
|
||||
"cos"
|
||||
:params ((x :as number))
|
||||
:returns "float"
|
||||
:doc "Cosine of x (radians).")
|
||||
|
||||
(define-primitive
|
||||
"tan"
|
||||
:params ((x :as number))
|
||||
:returns "float"
|
||||
:doc "Tangent of x (radians).")
|
||||
|
||||
(define-primitive
|
||||
"asin"
|
||||
:params ((x :as number))
|
||||
:returns "float"
|
||||
:doc "Arc sine of x; result in radians.")
|
||||
|
||||
(define-primitive
|
||||
"acos"
|
||||
:params ((x :as number))
|
||||
:returns "float"
|
||||
:doc "Arc cosine of x; result in radians.")
|
||||
|
||||
(define-primitive
|
||||
"atan"
|
||||
:params ((x :as number) &rest (y :as number))
|
||||
:returns "float"
|
||||
:doc "Arc tangent. (atan x) → radians in (-π/2, π/2). (atan y x) → atan2(y, x).")
|
||||
|
||||
(define-primitive
|
||||
"exp"
|
||||
:params ((x :as number))
|
||||
:returns "float"
|
||||
:doc "e raised to the power x.")
|
||||
|
||||
(define-primitive
|
||||
"log"
|
||||
:params ((x :as number))
|
||||
:returns "float"
|
||||
:doc "Natural logarithm of x.")
|
||||
|
||||
(define-primitive
|
||||
"expt"
|
||||
:params ((base :as number) (exp :as number))
|
||||
:returns "number"
|
||||
:doc "base raised to the power exp. Alias: pow.")
|
||||
|
||||
(define-primitive
|
||||
"quotient"
|
||||
:params ((a :as number) (b :as number))
|
||||
:returns "integer"
|
||||
:doc "Integer quotient: truncate(a / b) toward zero. Sign follows dividend.")
|
||||
|
||||
(define-primitive
|
||||
"gcd"
|
||||
:params ((a :as number) (b :as number))
|
||||
:returns "integer"
|
||||
:doc "Greatest common divisor of a and b.")
|
||||
|
||||
(define-primitive
|
||||
"lcm"
|
||||
:params ((a :as number) (b :as number))
|
||||
:returns "integer"
|
||||
:doc "Least common multiple of a and b.")
|
||||
|
||||
(define-primitive
|
||||
"number->string"
|
||||
:params ((n :as number) &rest (radix :as number))
|
||||
:returns "string"
|
||||
:doc "Convert number n to string. Optional radix (default 10). E.g. (number->string 255 16) → \"ff\".")
|
||||
|
||||
(define-primitive
|
||||
"string->number"
|
||||
:params ((s :as string) &rest (radix :as number))
|
||||
:returns "any"
|
||||
:doc "Parse string s as a number. Optional radix (default 10). Returns nil on failure.")
|
||||
|
||||
(define-module :stdlib.hash-table)
|
||||
|
||||
131
spec/tests/test-math.sx
Normal file
131
spec/tests/test-math.sx
Normal file
@@ -0,0 +1,131 @@
|
||||
|
||||
(deftest
|
||||
"math completeness"
|
||||
(deftest
|
||||
"trigonometry"
|
||||
(deftest
|
||||
"sin"
|
||||
(assert= 0 (round (sin 0)) "sin 0 = 0")
|
||||
(assert=
|
||||
1
|
||||
(round (sin (/ 3.14159 2)))
|
||||
"sin pi/2 = 1")
|
||||
(assert= 0 (round (sin 3.14159)) "sin pi = 0"))
|
||||
(deftest
|
||||
"cos"
|
||||
(assert= 1 (round (cos 0)) "cos 0 = 1")
|
||||
(assert=
|
||||
0
|
||||
(round (cos (/ 3.14159 2)))
|
||||
"cos pi/2 = 0")
|
||||
(assert= -1 (round (cos 3.14159)) "cos pi = -1"))
|
||||
(deftest
|
||||
"tan"
|
||||
(assert= 0 (round (tan 0)) "tan 0 = 0")
|
||||
(assert= 1 (round (tan 0.785398)) "tan pi/4 = 1"))
|
||||
(deftest
|
||||
"asin"
|
||||
(assert= 0 (round (asin 0)) "asin 0 = 0")
|
||||
(let
|
||||
(r (asin 1))
|
||||
(assert= true (and (> r 1.5) (< r 1.6)) "asin 1 ≈ pi/2")))
|
||||
(deftest
|
||||
"acos"
|
||||
(assert= 0 (round (acos 1)) "acos 1 = 0")
|
||||
(let
|
||||
(r (acos 0))
|
||||
(assert= true (and (> r 1.5) (< r 1.6)) "acos 0 ≈ pi/2")))
|
||||
(deftest
|
||||
"atan"
|
||||
(assert= 0 (round (atan 0)) "atan 0 = 0")
|
||||
(let
|
||||
(r (atan 1))
|
||||
(assert= true (and (> r 0.78) (< r 0.8)) "atan 1 ≈ pi/4"))
|
||||
(let
|
||||
(r (atan 1 1))
|
||||
(assert=
|
||||
true
|
||||
(and (> r 0.78) (< r 0.8))
|
||||
"atan 1 1 = atan2(1,1) ≈ pi/4"))
|
||||
(let
|
||||
(r (atan 1 0))
|
||||
(assert= true (and (> r 1.5) (< r 1.6)) "atan 1 0 ≈ pi/2")))
|
||||
(deftest
|
||||
"exp"
|
||||
(assert= 1 (round (exp 0)) "exp 0 = 1")
|
||||
(let
|
||||
(r (exp 1))
|
||||
(assert= true (and (> r 2.71) (< r 2.72)) "exp 1 ≈ e")))
|
||||
(deftest
|
||||
"log"
|
||||
(assert= 0 (round (log 1)) "log 1 = 0")
|
||||
(let
|
||||
(r (log 2.71828))
|
||||
(assert= true (and (> r 0.99) (< r 1.01)) "log e ≈ 1"))))
|
||||
(deftest
|
||||
"expt"
|
||||
(assert= 8 (expt 2 3) "2^3 = 8")
|
||||
(assert= 1 (expt 5 0) "5^0 = 1")
|
||||
(assert= 1000 (expt 10 3) "10^3 = 1000")
|
||||
(let
|
||||
(r (expt 2 0.5))
|
||||
(assert= true (and (> r 1.41) (< r 1.43)) "2^0.5 ≈ sqrt(2)")))
|
||||
(deftest
|
||||
"quotient"
|
||||
(assert= 3 (quotient 13 4) "13/4 = 3")
|
||||
(assert=
|
||||
-3
|
||||
(quotient -13 4)
|
||||
"-13/4 = -3 (truncate toward zero)")
|
||||
(assert=
|
||||
-3
|
||||
(quotient 13 -4)
|
||||
"13/-4 = -3 (truncate toward zero)")
|
||||
(assert= 3 (quotient -13 -4) "-13/-4 = 3")
|
||||
(assert= 0 (quotient 0 5) "0/5 = 0"))
|
||||
(deftest
|
||||
"gcd"
|
||||
(assert= 6 (gcd 12 18) "gcd 12 18 = 6")
|
||||
(assert= 1 (gcd 7 13) "gcd 7 13 = 1 (coprime)")
|
||||
(assert= 4 (gcd 8 12) "gcd 8 12 = 4")
|
||||
(assert= 5 (gcd 0 5) "gcd 0 5 = 5")
|
||||
(assert= 6 (gcd -12 18) "gcd handles negatives"))
|
||||
(deftest
|
||||
"lcm"
|
||||
(assert= 12 (lcm 4 6) "lcm 4 6 = 12")
|
||||
(assert= 36 (lcm 12 18) "lcm 12 18 = 36")
|
||||
(assert= 0 (lcm 0 5) "lcm 0 5 = 0")
|
||||
(assert= 15 (lcm 3 5) "lcm 3 5 = 15"))
|
||||
(deftest
|
||||
"number->string"
|
||||
(assert= "42" (number->string 42) "integer to string")
|
||||
(assert= "0" (number->string 0) "zero to string")
|
||||
(assert= "-7" (number->string -7) "negative to string")
|
||||
(assert= "ff" (number->string 255 16) "255 in hex")
|
||||
(assert= "1111" (number->string 15 2) "15 in binary")
|
||||
(assert= "377" (number->string 255 8) "255 in octal")
|
||||
(assert= "z" (number->string 35 36) "35 in base 36"))
|
||||
(deftest
|
||||
"string->number"
|
||||
(assert= 42 (string->number "42") "string to integer")
|
||||
(assert= -7 (string->number "-7") "negative string to integer")
|
||||
(assert= 255 (string->number "ff" 16) "hex string")
|
||||
(assert= 15 (string->number "1111" 2) "binary string")
|
||||
(assert= 255 (string->number "377" 8) "octal string")
|
||||
(assert= nil (string->number "not-a-number") "invalid returns nil")
|
||||
(assert= nil (string->number "fg" 16) "invalid hex returns nil"))
|
||||
(deftest
|
||||
"numeric tower integration"
|
||||
(assert=
|
||||
true
|
||||
(< (abs (- (sin (asin 0.5)) 0.5)) 0.0001)
|
||||
"sin(asin(x)) = x")
|
||||
(assert=
|
||||
true
|
||||
(< (abs (- (cos (acos 0.5)) 0.5)) 0.0001)
|
||||
"cos(acos(x)) = x")
|
||||
(assert= true (< (abs (- (exp (log 2)) 2)) 0.0001) "exp(log(x)) = x")
|
||||
(assert=
|
||||
(* 12 18)
|
||||
(* (gcd 12 18) (lcm 12 18))
|
||||
"gcd * lcm = a * b")))
|
||||
Reference in New Issue
Block a user