numbers.scm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. ;;; Bytevectors
  2. ;;; Copyright (C) 2024 Igalia, S.L.
  3. ;;;
  4. ;;; Licensed under the Apache License, Version 2.0 (the "License");
  5. ;;; you may not use this file except in compliance with the License.
  6. ;;; You may obtain a copy of the License at
  7. ;;;
  8. ;;; http://www.apache.org/licenses/LICENSE-2.0
  9. ;;;
  10. ;;; Unless required by applicable law or agreed to in writing, software
  11. ;;; distributed under the License is distributed on an "AS IS" BASIS,
  12. ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. ;;; See the License for the specific language governing permissions and
  14. ;;; limitations under the License.
  15. ;;; Commentary:
  16. ;;;
  17. ;;; Bytevectors.
  18. ;;;
  19. ;;; Code:
  20. (library (hoot numbers)
  21. (export + * - /
  22. < <= = >= >
  23. 1+ 1-
  24. abs
  25. floor
  26. ceiling
  27. round
  28. truncate
  29. number?
  30. complex?
  31. real?
  32. rational?
  33. integer?
  34. exact-integer?
  35. exact?
  36. inexact?
  37. finite?
  38. infinite?
  39. nan?
  40. inexact
  41. exact
  42. quotient
  43. remainder
  44. modulo
  45. even?
  46. odd?
  47. numerator
  48. denominator
  49. exact-integer-sqrt
  50. floor/
  51. floor-quotient
  52. floor-remainder
  53. truncate/
  54. truncate-quotient
  55. truncate-remainder
  56. gcd lcm
  57. max min
  58. negative?
  59. positive?
  60. zero?
  61. sin
  62. cos
  63. tan
  64. asin
  65. acos
  66. atan
  67. sqrt
  68. log
  69. exp
  70. rationalize
  71. square
  72. expt
  73. make-rectangular
  74. make-polar
  75. magnitude
  76. angle
  77. real-part
  78. imag-part)
  79. (import (hoot primitives)
  80. (hoot values)
  81. (hoot eq)
  82. (hoot not)
  83. (hoot errors)
  84. (hoot match)
  85. (hoot bitwise))
  86. (define (1+ x) (%+ x 1))
  87. (define (1- x) (%- x 1))
  88. (define-syntax-rule (define-associative-eta-expansion f %f)
  89. (define f
  90. (case-lambda
  91. (() (%f))
  92. ((x) (%f x))
  93. ((x y) (%f x y))
  94. ((x y . z) (apply f (%f x y) z)))))
  95. (define-associative-eta-expansion * %*)
  96. (define-associative-eta-expansion + %+)
  97. (define-syntax-rule (define-sub/div-eta-expansion f %f zero)
  98. (begin
  99. (define %generic
  100. (case-lambda
  101. ((y) (%f zero y))
  102. ((x y) (%f x y))
  103. ((x y . z) (apply %generic (%f x y) z))))
  104. (define-syntax f
  105. (lambda (stx)
  106. (syntax-case stx ()
  107. ((_ . x) #'(%f . x))
  108. (f (identifier? #'f) #'%generic))))))
  109. (define-sub/div-eta-expansion - %- 0)
  110. (define-sub/div-eta-expansion / %/ 1)
  111. (define-syntax-rule (define-comparison-expansion f %f)
  112. (begin
  113. (define %generic
  114. (case-lambda
  115. ((a b) (%f a b))
  116. ((a b . c)
  117. (let lp ((res (%f a b)) (a b) (c c))
  118. (match c
  119. (() res)
  120. ((b . c)
  121. (lp (and (%f a b) res) b c)))))))
  122. (define-syntax f
  123. (lambda (stx)
  124. (syntax-case stx ()
  125. ((_ x y . z) #'(%f x y . z))
  126. (f (identifier? #'f) #'%generic))))))
  127. (define-comparison-expansion < %<)
  128. (define-comparison-expansion <= %<=)
  129. (define-comparison-expansion = %=)
  130. (define-comparison-expansion >= %>=)
  131. (define-comparison-expansion > %>)
  132. (define (number? x) (%number? x))
  133. (define (complex? x) (%complex? x))
  134. (define (real? x) (%real? x))
  135. (define (rational? x) (%rational? x))
  136. (define (integer? x) (%integer? x))
  137. (define (exact-integer? x) (%exact-integer? x))
  138. (define (exact? x) (%exact? x))
  139. (define (inexact? x) (%inexact? x))
  140. (define (abs x) (%abs x))
  141. (define (floor x) (%floor x))
  142. (define (ceiling x) (%ceiling x))
  143. (define (round x) (%floor (+ x 0.5)))
  144. (define (truncate x)
  145. (check-type x real? 'truncate)
  146. (if (exact? x)
  147. (if (integer? x)
  148. x
  149. (truncate-quotient (numerator x) (denominator x)))
  150. (%inline-wasm
  151. '(func (param $x f64) (result f64)
  152. (f64.trunc (local.get $x)))
  153. x)))
  154. ;; Unlike R7RS, these only operate on real numbers.
  155. (define (infinite? x)
  156. (or (= x +inf.0) (= x -inf.0)))
  157. (define (nan? x)
  158. (not (= x x)))
  159. (define (finite? x)
  160. (and (not (infinite? x)) (not (nan? x))))
  161. (define (inexact x) (%inexact x))
  162. (define (exact x)
  163. (cond
  164. ((exact? x) x)
  165. (else
  166. (check-type x finite? 'exact)
  167. (%inline-wasm
  168. '(func (param $x f64)
  169. (result (ref eq))
  170. (call $f64->exact (local.get $x)))
  171. x))))
  172. (define (quotient x y) (%quotient x y))
  173. (define (remainder x y) (%remainder x y))
  174. (define (modulo x y) (%modulo x y))
  175. (define (even? x) (zero? (logand x 1)))
  176. (define (odd? x) (not (even? x)))
  177. (define (numerator x)
  178. (cond
  179. ((exact-integer? x) x)
  180. ((exact? x)
  181. (%inline-wasm
  182. '(func (param $x (ref $fraction))
  183. (result (ref eq))
  184. (struct.get $fraction $num (local.get $x)))
  185. x))
  186. (else (inexact (numerator (exact x))))))
  187. (define (denominator x)
  188. (cond
  189. ((exact-integer? x) 1)
  190. ((exact? x)
  191. (%inline-wasm
  192. '(func (param $x (ref $fraction))
  193. (result (ref eq))
  194. (struct.get $fraction $denom (local.get $x)))
  195. x))
  196. (else (inexact (denominator (exact x))))))
  197. (define (exact-integer-sqrt n)
  198. ;; FIXME: There's a compiler bug that makes this procedure return
  199. ;; junk when this exact-integer? check is enabled.
  200. ;;
  201. (check-type n exact-integer? 'exact-integer-sqrt)
  202. (assert (>= n 0) 'exact-integer-sqrt)
  203. (let loop ((x n) (y (quotient (+ n 1) 2)))
  204. (if (< y x)
  205. (loop y (quotient (+ y (quotient n y)) 2))
  206. (values x (- n (* x x))))))
  207. (define (floor/ x y)
  208. (values (floor-quotient x y) (floor-remainder x y)))
  209. ;; Adapted from the SRFI-141 reference implementation
  210. (define (floor-quotient x y)
  211. (check-type x integer? 'floor-quotient)
  212. (check-type y integer? 'floor-quotient)
  213. (cond
  214. ((and (negative? x) (negative? y))
  215. (quotient (- x) (- y)))
  216. ((negative? x)
  217. (let ((x (- x)))
  218. (call-with-values (lambda () (truncate/ x y))
  219. (lambda (q r)
  220. (if (zero? r)
  221. (- q)
  222. (1- (- q)))))))
  223. ((negative? y)
  224. (let ((y (- y)))
  225. (call-with-values (lambda () (truncate/ x y))
  226. (lambda (q r)
  227. (if (zero? r)
  228. (- q)
  229. (1- (- q)))))))
  230. (else (quotient x y))))
  231. (define (floor-remainder x y) (modulo x y))
  232. (define (truncate/ x y)
  233. (values (truncate-quotient x y)
  234. (truncate-remainder x y)))
  235. (define (truncate-quotient x y) (quotient x y))
  236. (define (truncate-remainder x y) (remainder x y))
  237. (define (%binary-gcd x y)
  238. (check-type x integer? 'gcd)
  239. (check-type y integer? 'gcd)
  240. (let ((result
  241. (%inline-wasm
  242. '(func (param $x (ref eq)) (param $y (ref eq))
  243. (result (ref eq))
  244. (call $gcd (local.get $x) (local.get $y)))
  245. (exact x)
  246. (exact y))))
  247. (if (or (inexact? x) (inexact? y))
  248. (inexact result)
  249. result)))
  250. (define-syntax %gcd
  251. (syntax-rules ()
  252. ((_) 0)
  253. ((_ x) x)
  254. ((_ x y) (%binary-gcd x y))))
  255. (define (%binary-lcm x y)
  256. (check-type x integer? 'lcm)
  257. (check-type y integer? 'lcm)
  258. (let* ((exact-x (exact x))
  259. (exact-y (exact y))
  260. (result (if (and (eqv? exact-x 0) (eqv? exact-y 0))
  261. 0
  262. (quotient (abs (* exact-x exact-y))
  263. (gcd exact-x exact-y)))))
  264. (if (or (inexact? x) (inexact? y))
  265. (inexact result)
  266. result)))
  267. (define-syntax %lcm
  268. (syntax-rules ()
  269. ((_) 1)
  270. ((_ x) x)
  271. ((_ x y) (%binary-lcm x y))))
  272. (define-syntax-rule (define-associative-eta-expansion f %f)
  273. (define f
  274. (case-lambda
  275. (() (%f))
  276. ((x) (%f x))
  277. ((x y) (%f x y))
  278. ((x y . z) (apply f (%f x y) z)))))
  279. (define-associative-eta-expansion gcd %gcd)
  280. (define-associative-eta-expansion lcm %lcm)
  281. (define max
  282. (case-lambda
  283. ((x) x)
  284. ((x y) (if (> x y) x y))
  285. ((x y . z) (apply max (max x y) z))))
  286. (define min
  287. (case-lambda
  288. ((x) x)
  289. ((x y) (if (< x y) x y))
  290. ((x y . z) (apply min (min x y) z))))
  291. (define (negative? x) (< x 0))
  292. (define (positive? x) (> x 0))
  293. (define (zero? x) (= x 0))
  294. (define (sin x) (%sin x))
  295. (define (cos x) (%cos x))
  296. (define (tan x) (%tan x))
  297. (define (asin x) (%asin x))
  298. (define (acos x) (%acos x))
  299. (define atan
  300. (case-lambda
  301. ((x) (%atan x))
  302. ((x y) (%atan x y))))
  303. (define (sqrt x) (%sqrt x))
  304. (define* (log x #:optional y)
  305. (define (%log x)
  306. (%inline-wasm
  307. '(func (param $x (ref eq)) (result (ref eq))
  308. (call $log (local.get $x)))
  309. x))
  310. (if y
  311. (/ (%log x)
  312. (%log y))
  313. (%log x)))
  314. (define (exp x)
  315. (define (%exp x)
  316. (%inline-wasm
  317. '(func (param $x (ref eq)) (result (ref eq))
  318. (call $exp (local.get $x)))
  319. x))
  320. (%exp x))
  321. ;; Adapted from the comments for scm_rationalize in libguile's numbers.c
  322. (define (rationalize x y)
  323. (check-type x rational? 'rationalize)
  324. (check-type y rational? 'rationalize)
  325. (define (exact-rationalize x eps)
  326. (let ((n1 (if (negative? x) -1 1))
  327. (x (abs x))
  328. (eps (abs eps)))
  329. (let ((lo (- x eps))
  330. (hi (+ x eps)))
  331. (if (<= lo 0)
  332. 0
  333. (let loop ((nlo (numerator lo)) (dlo (denominator lo))
  334. (nhi (numerator hi)) (dhi (denominator hi))
  335. (n1 n1) (d1 0) (n2 0) (d2 1))
  336. (let-values (((qlo rlo) (floor/ nlo dlo))
  337. ((qhi rhi) (floor/ nhi dhi)))
  338. (let ((n0 (+ n2 (* n1 qlo)))
  339. (d0 (+ d2 (* d1 qlo))))
  340. (cond ((zero? rlo) (/ n0 d0))
  341. ((< qlo qhi) (/ (+ n0 n1) (+ d0 d1)))
  342. (else (loop dhi rhi dlo rlo n0 d0 n1 d1))))))))))
  343. (if (and (exact? x) (exact? y))
  344. (exact-rationalize x y)
  345. (inexact (exact-rationalize (exact x) (exact y)))))
  346. (define (square x) (* x x))
  347. (define (expt x y)
  348. (check-type x number? 'expt)
  349. (check-type y number? 'expt)
  350. (cond
  351. ((eqv? x 0)
  352. (cond ((zero? y) (if (exact? y) 1 1.0))
  353. ((positive? y) (if (exact? y) 0 0.0))
  354. (else +nan.0)))
  355. ((eqv? x 0.0)
  356. (cond ((zero? y) 1.0)
  357. ((positive? y) 0.0)
  358. (else +nan.0)))
  359. ((exact-integer? y)
  360. (if (< y 0)
  361. (/ 1 (expt x (abs y)))
  362. (let lp ((y y)
  363. (result 1))
  364. (if (= y 0)
  365. result
  366. (lp (1- y) (* x result))))))
  367. (else (exp (* y (log x))))))
  368. ;; (scheme complex)
  369. ;; Adapted from Guile's numbers.c
  370. (define (make-rectangular real imag)
  371. (check-type real real? 'make-rectangular)
  372. (check-type imag real? 'make-rectangular)
  373. (if (eq? imag 0)
  374. real
  375. (%inline-wasm
  376. '(func (param $real f64) (param $imag f64) (result (ref eq))
  377. (struct.new $complex
  378. (i32.const 0)
  379. (local.get $real)
  380. (local.get $imag)))
  381. (inexact real) (inexact imag))))
  382. (define (make-polar mag ang)
  383. (check-type mag real? 'make-polar)
  384. (check-type ang real? 'make-polar)
  385. (cond
  386. ((eq? mag 0) 0)
  387. ((eq? ang 0) mag)
  388. (else
  389. (%inline-wasm
  390. '(func (param $mag f64) (param $ang f64) (result (ref eq))
  391. (local $f0 f64) (local $f1 f64)
  392. (local.set $f0 (call $fcos (local.get $ang)))
  393. (local.set $f1 (call $fsin (local.get $ang)))
  394. ;; If sin/cos are NaN and magnitude is 0, return a complex
  395. ;; zero.
  396. (if (ref eq)
  397. (i32.and (call $f64-is-nan (local.get $f0))
  398. (call $f64-is-nan (local.get $f1))
  399. (f64.eq (local.get $mag) (f64.const 0.0)))
  400. (then (struct.new $complex
  401. (i32.const 0)
  402. (f64.const 0.0)
  403. (f64.const 0.0)))
  404. (else (struct.new $complex
  405. (i32.const 0)
  406. (f64.mul (local.get $mag) (local.get $f0))
  407. (f64.mul (local.get $mag) (local.get $f1))))))
  408. (inexact mag) (inexact ang)))))
  409. (define (magnitude z)
  410. (cond
  411. ((real? z) (abs z))
  412. (else
  413. (check-type z complex? 'magnitude)
  414. (let ((r (real-part z))
  415. (i (imag-part z)))
  416. (sqrt (+ (* r r) (* i i)))))))
  417. (define (angle z)
  418. (cond
  419. ((real? z)
  420. (if (negative? z)
  421. (atan 0.0 -1.0)
  422. 0.0))
  423. (else
  424. (check-type z complex? 'angle)
  425. (atan (imag-part z) (real-part z)))))
  426. (define (real-part z)
  427. (cond
  428. ((real? z) z)
  429. (else
  430. (check-type z complex? 'real-part)
  431. (%inline-wasm
  432. '(func (param $z (ref $complex)) (result f64)
  433. (struct.get $complex $real (local.get $z)))
  434. z))))
  435. (define (imag-part z)
  436. (cond
  437. ((real? z) 0.0)
  438. (else
  439. (check-type z complex? 'real-part)
  440. (%inline-wasm
  441. '(func (param $z (ref $complex)) (result f64)
  442. (struct.get $complex $imag (local.get $z)))
  443. z))))
  444. )