; nearly square divisor, meet in the middle
(define (take n xs)
(let loop ((n n) (xs xs) (ys '()))
(if (or (zero? n) (null? xs))
(reverse ys)
(loop (- n 1) (cdr xs)
(cons (car xs) ys)))))
(define (drop n xs)
(let loop ((n n) (xs xs))
(if (or (zero? n) (null? xs)) xs
(loop (- n 1) (cdr xs)))))
(define (isqrt n)
(if (not (and (positive? n) (integer? n)))
(error 'isqrt "must be positive integer")
(let loop ((x n))
(let ((y (quotient (+ x (quotient n x)) 2)))
(if (< y x) (loop y) x)))))
(define (primes n) ; list of primes not exceeding n
(let* ((len (quotient (- n 1) 2)) (bits (make-vector len #t)))
(let loop ((i 0) (p 3) (ps (list 2)))
(cond ((< n (* p p))
(do ((i i (+ i 1)) (p p (+ p 2))
(ps ps (if (vector-ref bits i) (cons p ps) ps)))
((= i len) (reverse ps))))
((vector-ref bits i)
(do ((j (+ (* 2 i i) (* 6 i) 3) (+ j p)))
((<= len j) (loop (+ i 1) (+ p 2) (cons p ps)))
(vector-set! bits j #f)))
(else (loop (+ i 1) (+ p 2) ps))))))
(define (factors n) ; 2,3,5-wheel
(let ((wheel (vector 1 2 2 4 2 4 2 4 6 2 6)))
(let loop ((n n) (f 2) (fs (list)) (w 0))
(if (< n (* f f)) (reverse (cons n fs))
(if (zero? (modulo n f))
(loop (/ n f) f (cons f fs) w)
(loop n (+ f (vector-ref wheel w)) fs
(if (= w 10) 3 (+ w 1))))))))
(define (merge-uniq xs ys)
(let loop ((xs xs) (ys ys) (zs (list)))
(cond ((and (null? xs) (null? ys)) (reverse zs))
((null? xs) (loop xs (cdr ys) (cons (car ys) zs)))
((null? ys) (loop (cdr xs) ys (cons (car xs) zs)))
((< (car xs) (car ys)) (loop (cdr xs) ys (cons (car xs) zs)))
((< (car ys) (car xs)) (loop xs (cdr ys) (cons (car ys) zs)))
(else (loop xs (cdr ys) zs)))))
(define (divisors n)
(let loop ((fs (factors n)) (divs (list 1)))
(if (null? fs) divs
(loop (cdr fs) (merge-uniq (map (lambda (d) (* d (car fs))) divs) divs)))))
(define (nsd n)
(let* ((limit (isqrt n))
(fs (factors n))
(mid (quotient (length fs) 2))
(los (divisors (apply * (take mid fs))))
(his (reverse (divisors (apply * (drop mid fs))))))
(let loop ((i 0) (best 0) (los los) (his his))
(if (or (null? los) (null? his)) best
(let ((t (* (car los) (car his))))
(cond ((< limit t) (loop (+ i 1) best los (cdr his)))
((< best t) (loop (+ i 1) t (cdr los) his))
(else (loop (+ i 1) best (cdr los) his))))))))
(define (primorial-nsd n)
(let ((p 1))
(do ((ps (primes n) (cdr ps))
(i 1 (+ i 1)))
((null? ps))
(set! p (* p (car ps)))
(display i) (display ": ")
(display (nsd p)) (newline))))
(primorial-nsd 100)