numbers.scm 14 KB

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