-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathinteger-root.lisp
71 lines (65 loc) · 2.33 KB
/
integer-root.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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
(defpackage :cp/integer-root
(:use :cl)
(:export #:iroot)
(:documentation
"Provides computation of the floor of the nth root of an integer."))
(in-package :cp/integer-root)
;; This value is set for SBCL x86-64
(defconstant +bit-width+ 62)
(deftype uint () '(unsigned-byte #.+bit-width+))
(declaim ((simple-array uint (*)) *supremums*))
(defparameter *supremums*
(make-array (+ 1 +bit-width+) :element-type 'uint))
(defun initialize ()
(let ((most-uint (ldb (byte +bit-width+ 0) -1)))
(loop for exp from 2 to +bit-width+
do (let ((ok 0)
(ng 1))
(loop while (<= (expt ng exp) most-uint)
do (setq ng (ash ng 1)))
(loop until (= (- ng ok) 1)
for mid = (ash (+ ng ok) -1)
when (<= (expt mid exp) most-uint)
do (setq ok mid)
else
do (setq ng mid))
(setf (aref *supremums* exp) ng)))))
(initialize)
(declaim (inline %power)
(ftype (function * (values uint &optional)) %power))
(defun %power (base exp)
"Is equivalent to CL:EXPT for this purpose."
(declare (uint base exp))
(let ((res 1))
(declare (uint res))
(loop when (oddp exp)
do (setq res (* res base))
do (setq exp (ash exp -1))
until (zerop exp)
do (setq base (* base base)))
res))
(declaim (ftype (function * (values uint &optional)) iroot))
(defun iroot (x index)
"Returns the greatest integer less than or equal to the non-negative INDEX-th
root of X."
(declare (optimize (speed 3))
(uint x)
((integer 1) index))
(locally (declare (optimize (safety 0)))
(cond ((zerop x) 0)
((> index +bit-width+) 1)
((>= index 3)
(let ((ok 0)
(ng (aref *supremums* index)))
(declare (uint ok ng))
(loop until (= (the uint (- ng ok)) 1)
for mid = (ldb (byte +bit-width+ 0)
(+ (ash ng -1) (ash ok -1)
(ash (+ (logand ng 1) (logand ok 1)) -1)))
when (<= (%power mid index) x)
do (setq ok mid)
else
do (setq ng mid))
ok))
((= index 2) (isqrt x))
(t x))))