#lang scheme/base
(require "math.ss"
         (lib "contract.ss"))
(define-struct chebyshev-series
  (coefficients
   order
   (lower #:mutable)
   (upper #:mutable)))
(provide
 (rename-out (make-chebyshev-series-order unchecked-make-chebyshev-series-order)
             (chebyshev-series-init unchecked-chebyshev-series-init)
             (chebyshev-eval unchecked-chebyshev-eval)
             (chebyshev-eval-n unchecked-chebyshev-eval-n)))
(provide/contract
 (struct chebyshev-series ((coefficients (vectorof real?))
                           (order natural-number/c)
                           (lower real?)
                           (upper real?)))
 (make-chebyshev-series-order
  (-> natural-number/c chebyshev-series?))
 (chebyshev-series-init
  (-> chebyshev-series? procedure? real? real? void?))
 (chebyshev-eval
  (-> chebyshev-series? real? real?))
 (chebyshev-eval-n
  (-> chebyshev-series? natural-number/c real? real?)))
(define (make-chebyshev-series-order order)
  (make-chebyshev-series
   (make-vector (+ order 1))
   order
   0.0 0.0))
(define (chebyshev-series-init cs func a b)
  (when (>= a b)
    (error 'chebyshev-series-init
           "null function interval [a,b]"))
  (set-chebyshev-series-lower! cs a)
  (set-chebyshev-series-upper! cs b)
  (let* ((order (chebyshev-series-order cs))
         (coefficients (chebyshev-series-coefficients cs))
         (bma (* 0.5 (- b a)))
         (bpa (* 0.5 (+ b a)))
         (fac (/ 2.0 (+ order 1.0)))
         (f (make-vector (+ order 1))))
    (do ((k 0 (+ k 1)))
        ((> k order) (void))
      (let ((y (cos (* pi (/ (+ k 0.5) (+ order 1))))))
        (vector-set! f k
                     (func (+ (* y bma) bpa)))))
    (do ((j 0 (+ j 1)))
        ((> j order) (void))
      (let ((sum 0.0))
        (do ((k 0 (+ k 1)))
          ((> k order) (void))
          (set! sum (+ sum (* (vector-ref f k)
                              (cos (/ (* pi j (+ k 0.5))
                                      (+ order 1)))))))
        (vector-set! coefficients j (* fac sum))))))
(define (chebyshev-eval cs x)
  (let* ((coefs (chebyshev-series-coefficients cs))
         (order (chebyshev-series-order cs))
         (lower (chebyshev-series-lower cs))
         (upper (chebyshev-series-upper cs))
         (d 0.0)
         (dd 0.0)
         (y (/ (- (* 2.0 x) lower upper) (- upper lower)))
         (y2 (* 2.0 y)))
    (do ((j order (- j 1)))
        ((= j 0) (void))
      (let ((temp d))
        (set! d (+ (* y2 d) (- dd) (vector-ref coefs j)))
        (set! dd temp)))
    (+ (* y d) (- dd) (* 0.5 (vector-ref coefs 0)))))
(define (chebyshev-eval-n cs n x)
  (let* ((coefs (chebyshev-series-coefficients cs))
         (order (chebyshev-series-order cs))
         (lower (chebyshev-series-lower cs))
         (upper (chebyshev-series-upper cs))
         (d 0.0)
         (dd 0.0)
         (y (/ (- (* 2.0 x) lower upper) (- upper lower)))
         (y2 (* 2.0 y)))
    (do ((j (min n order) (- j 1)))
        ((= j 0) (void))
      (let ((temp d))
        (set! d (+ (* y2 d) (- dd) (vector-ref coefs j)))
        (set! dd temp)))
    (+ (* y d) (- dd) (* 0.5 (vector-ref coefs 0)))))