Advertisement
chemoelectric

Original Scheme implementation for Continued fraction/Arithmetic bivariate

Mar 6th, 2023
2,289
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scheme 16.64 KB | None | 0 0
  1. (cond-expand
  2.   (r7rs)
  3.   (chicken (import r7rs)))
  4.  
  5. (define-library (continued-fraction)
  6.  
  7.   (export define-continued-fraction
  8.           make-continued-fraction
  9.           continued-fraction?
  10.           continued-fraction-ref
  11.           continued-fraction->stream
  12.           stream->continued-fraction
  13.           convergent-stream
  14.           convergent
  15.           number->continued-fraction
  16.  
  17.           *continued-fraction-max-terms*
  18.           continued-fraction->string
  19.  
  20.           *ng8-overflow-threshold*
  21.           apply-ng8
  22.           continued-fraction+
  23.           continued-fraction-
  24.           continued-fraction*
  25.           continued-fraction/
  26.  
  27.           golden-ratio
  28.           silver-ratio
  29.           sqrt2)
  30.  
  31.   (import (scheme base)
  32.           (scheme case-lambda)
  33.           (scheme inexact))
  34.   (import (srfi 41))
  35.  
  36.   (begin
  37.  
  38.     (define-record-type <continued-fraction>
  39.       (%%continued-fraction terminated? ;; No more terms?
  40.                             m           ;; No. of terms memoized.
  41.                             memo        ;; Memoized terms.
  42.                             gen)        ;; Term-generating thunk.
  43.       continued-fraction?
  44.       (terminated? cf-terminated? set-cf-terminated?!)
  45.       (m cf-m set-cf-m!)
  46.       (memo cf-memo set-cf-memo!)
  47.       (gen cf-gen set-cf-gen!))
  48.  
  49.     (define-syntax define-continued-fraction
  50.       (syntax-rules ()
  51.         ((_ cf gen)
  52.          ;; Make a continued fraction from a generator thunk.
  53.          (define cf (make-continued-fraction gen)))
  54.         ((_ (make-cf args ...) body body* ...)
  55.          ;; Make a procedure that makes a continued fraction from a
  56.          ;; procedure body that returns a generator thunk.
  57.          (define (make-cf args ...)
  58.            (make-continued-fraction
  59.             (lambda (args ...)
  60.               body body* ...))))))
  61.              
  62.     (define (make-continued-fraction gen)
  63.       (%%continued-fraction #f 0 (make-vector 32) gen))
  64.  
  65.     (define (%%cf-get-more-terms! cf needed)
  66.       (do () ((or (cf-terminated? cf) (= (cf-m cf) needed)))
  67.         (let ((term ((cf-gen cf))))
  68.           (if term
  69.               (begin
  70.                 (vector-set! (cf-memo cf) (cf-m cf) term)
  71.                 (set-cf-m! cf (+ (cf-m cf) 1)))
  72.               (set-cf-terminated?! cf #t)))))
  73.  
  74.     (define (%%cf-update! cf needed)
  75.       (cond ((cf-terminated? cf) (begin))
  76.             ((<= needed (cf-m cf)) (begin))
  77.             ((<= needed (vector-length (cf-memo cf)))
  78.              (%%cf-get-more-terms! cf needed))
  79.             (else ;; Increase the storage space for memoization.
  80.              (let* ((n1 (+ needed needed))
  81.                     (memo1 (make-vector n1)))
  82.                (vector-copy! memo1 0 (cf-memo cf) 0 (cf-m cf))
  83.                (set-cf-memo! cf memo1))
  84.              (%%cf-get-more-terms! cf needed))))
  85.  
  86.     (define (continued-fraction-ref cf i)
  87.       (%%cf-update! cf (+ i 1))
  88.       (and (< i (cf-m cf))
  89.            (vector-ref (cf-memo cf) i)))
  90.  
  91.     (define *continued-fraction-max-terms* (make-parameter 20))
  92.  
  93.     (define continued-fraction->string
  94.       (case-lambda
  95.         ((cf) (continued-fraction->string
  96.                cf (*continued-fraction-max-terms*)))
  97.         ((cf max-terms)
  98.          (let loop ((i 0)
  99.                     (s "["))
  100.            (let ((term (continued-fraction-ref cf i)))
  101.              (cond ((not term) (string-append s "]"))
  102.                    ((= i max-terms) (string-append s ",...]"))
  103.                    (else
  104.                     (let ((separator (case i
  105.                                        ((0) "")
  106.                                        ((1) ";")
  107.                                        (else ",")))
  108.                           (term-str (number->string term)))
  109.                       (loop (+ i 1) (string-append s separator
  110.                                                    term-str))))))))))
  111.  
  112.     ;;
  113.     ;; I do not demonstrate the "continued-fraction->stream" procedure
  114.     ;; in this program, but I wrote it and here it is.
  115.     ;;
  116.     (define (continued-fraction->stream cf)
  117.       (stream-take-while
  118.        (lambda (term) term)
  119.        (stream-of (continued-fraction-ref cf i)
  120.          (i in (stream-from 0)))))
  121.  
  122.     ;;
  123.     ;; I do not demonstrate the "stream->continued-fraction" procedure
  124.     ;; in this program, but I wrote it and here it is.
  125.     ;;
  126.     (define-continued-fraction (stream->continued-fraction lazylst)
  127.       (lambda ()
  128.         (stream-match lazylst
  129.           (() #f)
  130.           ((term . rest)
  131.            (set! lazylst rest)
  132.            term))))
  133.  
  134.     (define (convergent-stream cf)
  135.       ;; Return the convergents as a SRFI-41 stream.
  136.       (let ((cf^ (continued-fraction->stream cf))
  137.             (p0 0)
  138.             (p1 1)
  139.             (q0 1)
  140.             (q1 0))
  141.         (stream-of (let ((numer (+ p0 (* p1 term)))
  142.                          (denom (+ q0 (* q1 term))))
  143.                      (set! p0 p1)
  144.                      (set! p1 numer)
  145.                      (set! q0 q1)
  146.                      (set! q1 denom)
  147.                      (/ numer denom))
  148.           (term in cf^))))
  149.  
  150.     ;;
  151.     ;; I do not demonstrate the "convergent" procedure in this
  152.     ;; program, but I wrote it and here it is.
  153.     ;;
  154.     (define (convergent cf i)
  155.       ;; Return the ith convergent, where i starts at zero, and the
  156.       ;; continued fraction is (if necessary) extended infinitely with
  157.       ;; implicit infinities.
  158.       (when (negative? i)
  159.         (error "convergent: index negative" i))
  160.       (let loop ((j 0)
  161.                  (p0 0)
  162.                  (p1 1)
  163.                  (q0 1)
  164.                  (q1 0))
  165.         (if (= j (+ i 1)) (/ p1 q1)
  166.             (let ((term (continued-fraction-ref cf j)))
  167.               (if (not term)
  168.                   (if (zero? q1) +inf.0 (/ p1 q1))
  169.                   (let ((numer (+ p0 (* p1 term)))
  170.                         (denom (+ q0 (* q1 term))))
  171.                     (loop (+ j 1) p1 numer q1 denom)))))))
  172.  
  173.     (define-continued-fraction (number->continued-fraction num)
  174.       (lambda ()
  175.         (and (not (infinite? num))
  176.              (let ((num^ (exact num)))
  177.                (let ((n (numerator num^))
  178.                      (d (denominator num^)))
  179.                  (let-values (((q r) (floor/ n d)))
  180.                    (set! num (if (zero? r) +inf.0 (/ d r)))
  181.                    q))))))
  182.  
  183.     (define *ng8-overflow-threshold*
  184.       ;; Some very large positive number. Probably it should not be as
  185.       ;; large as this.
  186.       (make-parameter (expt 2 128)))
  187.  
  188.     (define (%%too-big? arg)
  189.       (let ((threshold (*ng8-overflow-threshold*)))
  190.         (let too? ((arg arg))
  191.           (cond ((null? arg) #f)
  192.                 ((pair? arg) (or (too? (car arg))
  193.                                  (too? (cdr arg))))
  194.                 (else (>= (abs arg) threshold))))))
  195.  
  196.     (define-continued-fraction (apply-ng8 ng8-as-list x y)
  197.       (let ((x (if (number? x) (number->continued-fraction x) x))
  198.             (y (if (number? y) (number->continued-fraction y) y))
  199.             (state-ref `(,@ng8-as-list 0 0 #f)))
  200.         (lambda ()
  201.           (define (loop state)
  202.             (let-values (((a12 a1 a2 a b12 b1 b2 b i j overflow?)
  203.                           (apply values state)))
  204.               (cond ((and (zero? b) (zero? b2))
  205.                      (if (and (zero? b1) (zero? b12))
  206.                          #f ;; b12 = b1 = b2 = b = 0
  207.                          (absorb-x-term state)))
  208.                     ((or (zero? b) (zero? b2))
  209.                      (absorb-y-term state))
  210.                     ((zero? b1)
  211.                      (absorb-x-term state))
  212.                     (else
  213.                      (let-values
  214.                          (((q r) (floor/ a b))
  215.                           ((q1 r1) (floor/ a1 b1))
  216.                           ((q2 r2) (floor/ a2 b2))
  217.                           ((q12 r12) (if (zero? b12)
  218.                                          (values +inf.0 0)
  219.                                          (floor/ a12 b12))))
  220.                        (let ((q1-diff (abs (- q1 q)))
  221.                              (q2-diff (abs (- q2 q))))
  222.                          (cond ((> q1-diff q2-diff)
  223.                                 (absorb-x-term state))
  224.                                ((< q1-diff q2-diff)
  225.                                 (absorb-y-term state))
  226.                                ((not (= q q12))
  227.                                 ;;
  228.                                 ;; Because q = q1 = q2, the following
  229.                                 ;; also are true:
  230.                                 ;;
  231.                                 ;;   a1/b1 - a/b = r1/b1 - r/b
  232.                                 ;;   a2/b2 - a/b = r2/b2 - r/b
  233.                                 ;;
  234.                                 ;; Rather than compare fractions, we
  235.                                 ;; will put the numerators over a
  236.                                 ;; common denominator of b*b1*b2, and
  237.                                 ;; then compare the new
  238.                                 ;; numerators. Doing this might keep
  239.                                 ;; the calculations entirely in
  240.                                 ;; fixnums.
  241.                                 ;;
  242.                                 (let ((n (* r b1 b2))
  243.                                       (n1 (* r1 b b2))
  244.                                       (n2 (* r2 b b1)))
  245.                                   (if (> (abs (- n1 n))
  246.                                          (abs (- n2 n)))
  247.                                       (absorb-x-term state)
  248.                                       (absorb-y-term state))))
  249.                                (else ;; q = q1 = q2 = q12
  250.                                 (set! state-ref
  251.                                   (list b12 b1 b2 b r12 r1 r2 r
  252.                                         i j overflow?))
  253.                                 q))))))))
  254.  
  255.           (define (absorb-x-term state)
  256.             (let-values (((a12 a1 a2 a b12 b1 b2 b i j overflow?)
  257.                           (apply values state)))
  258.               (let ((term (and (not overflow?)
  259.                                (continued-fraction-ref x i))))
  260.                 (if term
  261.                     (let ((ng (list (+ a2 (* a12 term))
  262.                                     (+ a (* a1 term)) a12 a1
  263.                                     (+ b2 (* b12 term))
  264.                                     (+ b (* b1 term)) b12 b1)))
  265.                       (if (not (%%too-big? ng))
  266.                           (loop `(,@ng ,(+ i 1) ,j ,overflow?))
  267.                           (loop (list a12 a1 a12 a1 b12 b1 b12 b1
  268.                                       i j #t))))
  269.                     (loop (list a12 a1 a12 a1 b12 b1 b12 b1
  270.                                 (+ i 1) j overflow?))))))
  271.  
  272.           (define (absorb-y-term state)
  273.             (let-values (((a12 a1 a2 a b12 b1 b2 b i j overflow?)
  274.                           (apply values state)))
  275.               (let ((term (and (not overflow?)
  276.                                (continued-fraction-ref y j))))
  277.                 (if term
  278.                     (let ((ng (list (+ a1 (* a12 term)) a12
  279.                                     (+ a (* a2 term)) a2
  280.                                     (+ b1 (* b12 term)) b12
  281.                                     (+ b (* b2 term)) b2)))
  282.                       (if (not (%%too-big? ng))
  283.                           (loop `(,@ng ,i ,(+ j 1) ,overflow?))
  284.                           (loop (list a12 a12 a2 a2 b12 b12 b2 b2
  285.                                       i j #t))))
  286.                     (loop (list a12 a12 a2 a2 b12 b12 b2 b2
  287.                                 i (+ j 1) overflow?))))))
  288.  
  289.           (loop state-ref))))
  290.  
  291.     (define continued-fraction+
  292.       ;; Not an efficient implementation!
  293.       (case-lambda
  294.         (() (number->continued-fraction 0))
  295.         ((x . rest)
  296.          (let loop ((z (if (number? x)
  297.                            (number->continued-fraction x)
  298.                            x))
  299.                     (lst rest))
  300.            (if (null? lst)
  301.                z
  302.                (loop (apply-ng8 '(0 1 1 0 0 0 0 1) z (car lst))
  303.                      (cdr lst)))))))
  304.  
  305.     (define continued-fraction-
  306.       ;; Not an efficient implementation!
  307.       (case-lambda
  308.         ((x) (apply-ng8 '(0 -1 0 0 0 0 0 1) x 0))
  309.         ((x . rest)
  310.          (let loop ((z x)
  311.                     (lst rest))
  312.            (if (null? lst)
  313.                z
  314.                (loop (apply-ng8 '(0 1 -1 0 0 0 0 1) z (car lst))
  315.                      (cdr lst)))))))
  316.  
  317.     (define continued-fraction*
  318.       ;; Not an efficient implementation!
  319.       (case-lambda
  320.         (() (number->continued-fraction 1))
  321.         ((x . rest)
  322.          (let loop ((z (if (number? x)
  323.                            (number->continued-fraction x)
  324.                            x))
  325.                     (lst rest))
  326.            (if (null? lst)
  327.                z
  328.                (loop (apply-ng8 '(1 0 0 0 0 0 0 1) z (car lst))
  329.                      (cdr lst)))))))
  330.  
  331.     (define continued-fraction/
  332.       ;; Not an efficient implementation!
  333.       (case-lambda
  334.         ((x) (apply-ng8 '(0 0 0 1 0 0 1 0) x x))
  335.         ((x . rest)
  336.          (let loop ((z (if (number? x)
  337.                            (number->continued-fraction x)
  338.                            x))
  339.                     (lst rest))
  340.            (if (null? lst)
  341.                z
  342.                (loop (apply-ng8 '(0 1 0 0 0 0 1 0) z (car lst))
  343.                      (cdr lst)))))))
  344.  
  345.     (define-continued-fraction golden-ratio
  346.       ;; The golden ratio, (1+sqrt(5))/2.
  347.       (lambda () 1))
  348.  
  349.     (define-continued-fraction silver-ratio
  350.       ;; The silver ratio, 1+sqrt(2).
  351.       (lambda () 2))
  352.  
  353.     (define-continued-fraction sqrt2
  354.       ;; The square root of two.
  355.       (let ((term 0))
  356.         (lambda ()
  357.           (set! term (min 2 (+ term 1)))
  358.           term)))
  359.  
  360.     )) ;; end library (continued-fraction)
  361.  
  362. (import (scheme base))
  363. (import (scheme write))
  364. (import (continued-fraction))
  365.  
  366. (import (scheme inexact))
  367. (import (srfi 41))
  368.  
  369. (newline)
  370.  
  371. (let* ((x (continued-fraction+ 13/11 1/2))
  372.        (convlst (stream->list (convergent-stream x))))
  373.   (display "13/11 + 1/2 => ")
  374.   (display (continued-fraction->string x))
  375.   (display "\nIts convergents:\n  ")
  376.   (display convlst)
  377.   (display "\n  ")
  378.   (display (map inexact convlst))
  379.   (newline))
  380.  
  381. (newline)
  382.  
  383. (let* ((x (continued-fraction+ 22/7 1/2))
  384.        (convlst (stream->list (convergent-stream x))))
  385.   (display "22/7 + 1/2 => ")
  386.   (display (continued-fraction->string x))
  387.   (display "\nIts convergents:\n  ")
  388.   (display convlst)
  389.   (display "\n  ")
  390.   (display (map inexact convlst))
  391.   (newline))
  392.  
  393. (newline)
  394.  
  395. (let* ((x (continued-fraction/ 22/7 4))
  396.        (convlst (stream->list (convergent-stream x))))
  397.   (display "(22/7)/4 => ")
  398.   (display (continued-fraction->string x))
  399.   (display "\nIts convergents:\n  ")
  400.   (display convlst)
  401.   (display "\n  ")
  402.   (display (map inexact convlst))
  403.   (newline))
  404.  
  405. (newline)
  406.  
  407. (let* ((x (continued-fraction/ sqrt2))
  408.        (convlst (stream->list (stream-take 7 (convergent-stream x)))))
  409.   (display "1/sqrt(2) => ")
  410.   (display (continued-fraction->string x))
  411.   (display "\nIts first 7 convergents:\n  ")
  412.   (display convlst)
  413.   (display "\n  ")
  414.   (display (map inexact convlst))
  415.   (newline))
  416.  
  417. (newline)
  418.  
  419. (let* ((x (continued-fraction+ 1/2 (continued-fraction/ sqrt2 4)))
  420.        (convlst (stream->list (stream-take 7 (convergent-stream x)))))
  421.   (display "(1 + 1/sqrt(2))/2 => ")
  422.   (display (continued-fraction->string x))
  423.   (display "\nIts first 7 convergents:\n  ")
  424.   (display convlst)
  425.   (display "\n  ")
  426.   (display (map inexact convlst))
  427.   (newline))
  428.  
  429. (newline)
  430.  
  431. (let* ((x (continued-fraction* sqrt2 sqrt2))
  432.        (convlst (stream->list (convergent-stream x))))
  433.   (display "sqrt(2)*sqrt(2) => ")
  434.   (display (continued-fraction->string x))
  435.   (display "\nIts convergents:\n  ")
  436.   (display convlst)
  437.   (display "\n  ")
  438.   (display (map inexact convlst))
  439.   (newline))
  440.  
  441. (newline)
  442.  
  443. (display "Obviously the last term of the previous continued fraction SHOULD be\n")
  444. (display "'infinity'. We could invent some heuristic to elide the term. For\n")
  445. (display "instance, stopping before any term that exceeds a threshold in size.\n")
  446. (display "In any case, the current implementation DOES terminate, with a very\n")
  447. (display "close answer.\n")
  448.  
  449. (newline)
  450.  
  451. (display "Indeed, the answer can be improved by changing a parameter:\n")
  452.  
  453. (newline)
  454.  
  455. (parameterize ((*ng8-overflow-threshold* (expt 2 1024)))
  456.   (let* ((x (continued-fraction* sqrt2 sqrt2))
  457.          (convlst (stream->list (convergent-stream x))))
  458.     (display "sqrt(2)*sqrt(2) => ")
  459.     (display (continued-fraction->string x))
  460.     (display "\nIts convergents:\n  ")
  461.     (display convlst)
  462.     (display "\n  ")
  463.     (display (map inexact convlst))
  464.     (newline)))
  465.  
  466. (newline)
  467.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement