numbers.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  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. most-positive-fixnum
  80. most-negative-fixnum)
  81. (import (hoot primitives)
  82. (hoot values)
  83. (hoot eq)
  84. (hoot not)
  85. (hoot errors)
  86. (hoot match)
  87. (hoot bitwise))
  88. (define (1+ x) (%+ x 1))
  89. (define (1- x) (%- x 1))
  90. (define-syntax-rule (define-associative-eta-expansion f %f)
  91. (define f
  92. (case-lambda
  93. (() (%f))
  94. ((x) (%f x))
  95. ((x y) (%f x y))
  96. ((x y . z) (apply f (%f x y) z)))))
  97. (define-associative-eta-expansion * %*)
  98. (define-associative-eta-expansion + %+)
  99. (define-syntax-rule (define-sub/div-eta-expansion f %f zero)
  100. (begin
  101. (define %generic
  102. (case-lambda
  103. ((y) (%f zero y))
  104. ((x y) (%f x y))
  105. ((x y . z) (apply %generic (%f x y) z))))
  106. (define-syntax f
  107. (lambda (stx)
  108. (syntax-case stx ()
  109. ((_ . x) #'(%f . x))
  110. (f (identifier? #'f) #'%generic))))))
  111. (define-sub/div-eta-expansion - %- 0)
  112. (define-sub/div-eta-expansion / %/ 1)
  113. (define-syntax-rule (define-comparison-expansion f %f)
  114. (begin
  115. (define %generic
  116. (case-lambda
  117. ((a b) (%f a b))
  118. ((a b . c)
  119. (let lp ((res (%f a b)) (a b) (c c))
  120. (match c
  121. (() res)
  122. ((b . c)
  123. (lp (and (%f a b) res) b c)))))))
  124. (define-syntax f
  125. (lambda (stx)
  126. (syntax-case stx ()
  127. ((_ x y . z) #'(%f x y . z))
  128. (f (identifier? #'f) #'%generic))))))
  129. (define-comparison-expansion < %<)
  130. (define-comparison-expansion <= %<=)
  131. (define-comparison-expansion = %=)
  132. (define-comparison-expansion >= %>=)
  133. (define-comparison-expansion > %>)
  134. (define (number? x) (%number? x))
  135. (define (complex? x) (%complex? x))
  136. (define (real? x) (%real? x))
  137. (define (rational? x) (%rational? x))
  138. (define (integer? x) (%integer? x))
  139. (define (exact-integer? x) (%exact-integer? x))
  140. (define (exact? x) (%exact? x))
  141. (define (inexact? x) (%inexact? x))
  142. (define (abs x) (%abs x))
  143. (define (floor x) (%floor x))
  144. (define (ceiling x) (%ceiling x))
  145. (define (round x) (%floor (+ x 0.5)))
  146. (define (truncate x)
  147. (check-type x real? 'truncate)
  148. (if (exact? x)
  149. (if (integer? x)
  150. x
  151. (truncate-quotient (numerator x) (denominator x)))
  152. (%inline-wasm
  153. '(func (param $x f64) (result f64)
  154. (f64.trunc (local.get $x)))
  155. x)))
  156. ;; Unlike R7RS, these only operate on real numbers.
  157. (define (infinite? x)
  158. (or (= x +inf.0) (= x -inf.0)))
  159. (define (nan? x)
  160. (not (= x x)))
  161. (define (finite? x)
  162. (and (not (infinite? x)) (not (nan? x))))
  163. (define (inexact x) (%inexact x))
  164. (define (exact x)
  165. (cond
  166. ((exact? x) x)
  167. (else
  168. (check-type x finite? 'exact)
  169. (%inline-wasm
  170. '(func (param $x f64)
  171. (result (ref eq))
  172. (call $f64->exact (local.get $x)))
  173. x))))
  174. (define (quotient x y) (%quotient x y))
  175. (define (remainder x y) (%remainder x y))
  176. (define (modulo x y) (%modulo x y))
  177. (define (even? x) (zero? (logand x 1)))
  178. (define (odd? x) (not (even? x)))
  179. (define (numerator x)
  180. (cond
  181. ((exact-integer? x) x)
  182. ((exact? x)
  183. (%inline-wasm
  184. '(func (param $x (ref $fraction))
  185. (result (ref eq))
  186. (struct.get $fraction $num (local.get $x)))
  187. x))
  188. (else (inexact (numerator (exact x))))))
  189. (define (denominator x)
  190. (cond
  191. ((exact-integer? x) 1)
  192. ((exact? x)
  193. (%inline-wasm
  194. '(func (param $x (ref $fraction))
  195. (result (ref eq))
  196. (struct.get $fraction $denom (local.get $x)))
  197. x))
  198. (else (inexact (denominator (exact x))))))
  199. (define (exact-integer-sqrt n)
  200. ;; FIXME: There's a compiler bug that makes this procedure return
  201. ;; junk when this exact-integer? check is enabled.
  202. ;;
  203. (check-type n exact-integer? 'exact-integer-sqrt)
  204. (assert (>= n 0) 'exact-integer-sqrt)
  205. (let loop ((x n) (y (quotient (+ n 1) 2)))
  206. (if (< y x)
  207. (loop y (quotient (+ y (quotient n y)) 2))
  208. (values x (- n (* x x))))))
  209. (define (floor/ x y)
  210. (values (floor-quotient x y) (floor-remainder x y)))
  211. ;; Adapted from the SRFI-141 reference implementation
  212. (define (floor-quotient x y)
  213. (check-type x integer? 'floor-quotient)
  214. (check-type y integer? 'floor-quotient)
  215. (cond
  216. ((and (negative? x) (negative? y))
  217. (quotient (- x) (- y)))
  218. ((negative? x)
  219. (let ((x (- x)))
  220. (call-with-values (lambda () (truncate/ x y))
  221. (lambda (q r)
  222. (if (zero? r)
  223. (- q)
  224. (1- (- q)))))))
  225. ((negative? y)
  226. (let ((y (- y)))
  227. (call-with-values (lambda () (truncate/ x y))
  228. (lambda (q r)
  229. (if (zero? r)
  230. (- q)
  231. (1- (- q)))))))
  232. (else (quotient x y))))
  233. (define (floor-remainder x y) (modulo x y))
  234. (define (truncate/ x y)
  235. (values (truncate-quotient x y)
  236. (truncate-remainder x y)))
  237. (define (truncate-quotient x y) (quotient x y))
  238. (define (truncate-remainder x y) (remainder x y))
  239. (define (%binary-gcd x y)
  240. (check-type x integer? 'gcd)
  241. (check-type y integer? 'gcd)
  242. (let ((result
  243. (%inline-wasm
  244. '(func (param $x (ref eq)) (param $y (ref eq))
  245. (result (ref eq))
  246. (call $gcd (local.get $x) (local.get $y)))
  247. (exact x)
  248. (exact y))))
  249. (if (or (inexact? x) (inexact? y))
  250. (inexact result)
  251. result)))
  252. (define-syntax %gcd
  253. (syntax-rules ()
  254. ((_) 0)
  255. ((_ x) x)
  256. ((_ x y) (%binary-gcd x y))))
  257. (define (%binary-lcm x y)
  258. (check-type x integer? 'lcm)
  259. (check-type y integer? 'lcm)
  260. (let* ((exact-x (exact x))
  261. (exact-y (exact y))
  262. (result (if (and (eqv? exact-x 0) (eqv? exact-y 0))
  263. 0
  264. (quotient (abs (* exact-x exact-y))
  265. (gcd exact-x exact-y)))))
  266. (if (or (inexact? x) (inexact? y))
  267. (inexact result)
  268. result)))
  269. (define-syntax %lcm
  270. (syntax-rules ()
  271. ((_) 1)
  272. ((_ x) x)
  273. ((_ x y) (%binary-lcm x y))))
  274. (define-syntax-rule (define-associative-eta-expansion f %f)
  275. (define f
  276. (case-lambda
  277. (() (%f))
  278. ((x) (%f x))
  279. ((x y) (%f x y))
  280. ((x y . z) (apply f (%f x y) z)))))
  281. (define-associative-eta-expansion gcd %gcd)
  282. (define-associative-eta-expansion lcm %lcm)
  283. (define max
  284. (case-lambda
  285. ((x) x)
  286. ((x y) (if (> x y) x y))
  287. ((x y . z) (apply max (max x y) z))))
  288. (define min
  289. (case-lambda
  290. ((x) x)
  291. ((x y) (if (< x y) x y))
  292. ((x y . z) (apply min (min x y) z))))
  293. (define (negative? x) (< x 0))
  294. (define (positive? x) (> x 0))
  295. (define (zero? x) (= x 0))
  296. (define (sin x) (%sin x))
  297. (define (cos x) (%cos x))
  298. (define (tan x) (%tan x))
  299. (define (asin x) (%asin x))
  300. (define (acos x) (%acos x))
  301. (define atan
  302. (case-lambda
  303. ((x) (%atan x))
  304. ((x y) (%atan x y))))
  305. (define (sqrt x) (%sqrt x))
  306. (define* (log x #:optional y)
  307. (define (%log x)
  308. (%inline-wasm
  309. '(func (param $x (ref eq)) (result (ref eq))
  310. (call $log (local.get $x)))
  311. x))
  312. (if y
  313. (/ (%log x)
  314. (%log y))
  315. (%log x)))
  316. (define (exp x)
  317. (define (%exp x)
  318. (%inline-wasm
  319. '(func (param $x (ref eq)) (result (ref eq))
  320. (call $exp (local.get $x)))
  321. x))
  322. (%exp x))
  323. ;; Adapted from the comments for scm_rationalize in libguile's numbers.c
  324. (define (rationalize x y)
  325. (check-type x rational? 'rationalize)
  326. (check-type y rational? 'rationalize)
  327. (define (exact-rationalize x eps)
  328. (let ((n1 (if (negative? x) -1 1))
  329. (x (abs x))
  330. (eps (abs eps)))
  331. (let ((lo (- x eps))
  332. (hi (+ x eps)))
  333. (if (<= lo 0)
  334. 0
  335. (let loop ((nlo (numerator lo)) (dlo (denominator lo))
  336. (nhi (numerator hi)) (dhi (denominator hi))
  337. (n1 n1) (d1 0) (n2 0) (d2 1))
  338. (let-values (((qlo rlo) (floor/ nlo dlo))
  339. ((qhi rhi) (floor/ nhi dhi)))
  340. (let ((n0 (+ n2 (* n1 qlo)))
  341. (d0 (+ d2 (* d1 qlo))))
  342. (cond ((zero? rlo) (/ n0 d0))
  343. ((< qlo qhi) (/ (+ n0 n1) (+ d0 d1)))
  344. (else (loop dhi rhi dlo rlo n0 d0 n1 d1))))))))))
  345. (if (and (exact? x) (exact? y))
  346. (exact-rationalize x y)
  347. (inexact (exact-rationalize (exact x) (exact y)))))
  348. (define (square x) (* x x))
  349. (define (expt x y)
  350. (check-type x number? 'expt)
  351. (check-type y number? 'expt)
  352. (cond
  353. ((eqv? x 0)
  354. (cond ((zero? y) (if (exact? y) 1 1.0))
  355. ((positive? y) (if (exact? y) 0 0.0))
  356. (else +nan.0)))
  357. ((eqv? x 0.0)
  358. (cond ((zero? y) 1.0)
  359. ((positive? y) 0.0)
  360. (else +nan.0)))
  361. ((exact-integer? y)
  362. (if (< y 0)
  363. (/ 1 (expt x (abs y)))
  364. (let lp ((y y)
  365. (result 1))
  366. (if (= y 0)
  367. result
  368. (lp (1- y) (* x result))))))
  369. (else (exp (* y (log x))))))
  370. ;; (scheme complex)
  371. ;; Adapted from Guile's numbers.c
  372. (define (make-rectangular real imag)
  373. (check-type real real? 'make-rectangular)
  374. (check-type imag real? 'make-rectangular)
  375. (if (eq? imag 0)
  376. real
  377. (%inline-wasm
  378. '(func (param $real f64) (param $imag f64) (result (ref eq))
  379. (struct.new $complex
  380. (i32.const 0)
  381. (local.get $real)
  382. (local.get $imag)))
  383. (inexact real) (inexact imag))))
  384. (define (make-polar mag ang)
  385. (check-type mag real? 'make-polar)
  386. (check-type ang real? 'make-polar)
  387. (cond
  388. ((eq? mag 0) 0)
  389. ((eq? ang 0) mag)
  390. (else
  391. (%inline-wasm
  392. '(func (param $mag f64) (param $ang f64) (result (ref eq))
  393. (local $f0 f64) (local $f1 f64)
  394. (local.set $f0 (call $fcos (local.get $ang)))
  395. (local.set $f1 (call $fsin (local.get $ang)))
  396. ;; If sin/cos are NaN and magnitude is 0, return a complex
  397. ;; zero.
  398. (if (ref eq)
  399. (i32.and (call $f64-is-nan (local.get $f0))
  400. (call $f64-is-nan (local.get $f1))
  401. (f64.eq (local.get $mag) (f64.const 0.0)))
  402. (then (struct.new $complex
  403. (i32.const 0)
  404. (f64.const 0.0)
  405. (f64.const 0.0)))
  406. (else (struct.new $complex
  407. (i32.const 0)
  408. (f64.mul (local.get $mag) (local.get $f0))
  409. (f64.mul (local.get $mag) (local.get $f1))))))
  410. (inexact mag) (inexact ang)))))
  411. (define (magnitude z)
  412. (cond
  413. ((real? z) (abs z))
  414. (else
  415. (check-type z complex? 'magnitude)
  416. (let ((r (real-part z))
  417. (i (imag-part z)))
  418. (sqrt (+ (* r r) (* i i)))))))
  419. (define (angle z)
  420. (cond
  421. ((real? z)
  422. (if (negative? z)
  423. (atan 0.0 -1.0)
  424. 0.0))
  425. (else
  426. (check-type z complex? 'angle)
  427. (atan (imag-part z) (real-part z)))))
  428. (define (real-part z)
  429. (cond
  430. ((real? z) z)
  431. (else
  432. (check-type z complex? 'real-part)
  433. (%inline-wasm
  434. '(func (param $z (ref $complex)) (result f64)
  435. (struct.get $complex $real (local.get $z)))
  436. z))))
  437. (define (imag-part z)
  438. (cond
  439. ((real? z) 0.0)
  440. (else
  441. (check-type z complex? 'real-part)
  442. (%inline-wasm
  443. '(func (param $z (ref $complex)) (result f64)
  444. (struct.get $complex $imag (local.get $z)))
  445. z))))
  446. (define most-negative-fixnum (ash -1 29))
  447. (define most-positive-fixnum (- (ash 1 29) 1))
  448. )