-
Notifications
You must be signed in to change notification settings - Fork 20
/
read-bignum.lisp
45 lines (43 loc) · 1.94 KB
/
read-bignum.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
(defpackage :cp/read-bignum
(:use :cl)
(:export #:read-bignum))
(in-package :cp/read-bignum)
(declaim (ftype (function * (values integer &optional)) read-bignum))
(defun read-bignum (&optional (in *standard-input*))
(macrolet ((%read-byte ()
`(the (unsigned-byte 8)
#+swank (char-code (read-char in nil #\Nul))
#-swank (sb-impl::ansi-stream-read-byte in nil #.(char-code #\Nul) nil))))
(let* ((minusp nil)
(result (loop (let ((byte (%read-byte)))
(cond ((<= 48 byte 57)
(return (- byte 48)))
((zerop byte) ; #\Nul
;; (return-from read-fixnum 0)
(error "Read EOF or #\Nul."))
((= byte #.(char-code #\-))
(setq minusp t))))))
(mid-result 0)
(index-mod18 0))
(declare (fixnum mid-result)
((integer 0 19) index-mod18)
(integer result))
(loop
(when (= index-mod18 18)
(setq result (+ mid-result (* result #.(expt 10 18))))
(setq mid-result 0)
(setq index-mod18 0))
(let ((byte (%read-byte)))
(unless (<= 48 byte 57) (return))
(setq mid-result (+ (- byte 48) (* 10 (the (mod #.(expt 10 17)) mid-result))))
(incf index-mod18)))
(setq result (+ mid-result (* result (expt 10 index-mod18))))
(if minusp (- result) result))))
;; I leave test code as there's no unit test for read-bignum.
;; (dotimes (i 1000)
;; (let* ((num (* (if (zerop (random 2)) -1 1)
;; (random (expt 10 100))))
;; (str1 (make-string-input-stream (format nil "~D~%" num)))
;; (str2 (make-string-input-stream (format nil " ~D~%" num))))
;; (assert (= num (read-bignum str1)))
;; (assert (= num (read-bignum str2)))))