#lang scheme
(define-values (struct:chebyshev-series
construct-chebyshev-series
chebyshev-series?
chebyshev-series-ref
chebyshev-series-set!)
(make-struct-type 'chebyshev-series #f 4 0))
(define chebyshev-series-coefficients
(make-struct-field-accessor chebyshev-series-ref 0 'coefficients))
(define set-chebyshev-series-coefficients!
(make-struct-field-mutator chebyshev-series-set! 0 'coefficients))
(define chebyshev-series-order
(make-struct-field-accessor chebyshev-series-ref 1 'order))
(define chebyshev-series-lower
(make-struct-field-accessor chebyshev-series-ref 2 'lower))
(define set-chebyshev-series-lower!
(make-struct-field-mutator chebyshev-series-set! 2 'lower))
(define chebyshev-series-upper
(make-struct-field-accessor chebyshev-series-ref 3 'upper))
(define set-chebyshev-series-upper!
(make-struct-field-mutator chebyshev-series-set! 3 'upper))
(define make-chebyshev-series
(case-lambda
((order)
(construct-chebyshev-series (make-vector (add1 order) 0.0) order 0.0 0.0))
((coeffs-or-func order lower upper)
(unless (< lower upper)
(error 'make-chebyshev-series
"null interval [~a, ~a]" lower upper))
(cond ((vector? coeffs-or-func)
(unless (= (vector-length coeffs-or-func) (add1 order))
(error 'make-chebyshev-series
"expected vector of length ~a, given vector of length ~a"
(add1 order) (vector-length coeffs-or-func)))
(construct-chebyshev-series coeffs-or-func order lower upper))
((procedure? coeffs-or-func)
(construct-chebyshev-series
(compute-coefficients coeffs-or-func order lower upper)
order lower upper))
(else
(error 'make-chebyshev-series
"expected vector or procedure, given ~a" coeffs-or-func))))))
(define (compute-coefficients func order a b)
(let* ((bma (* 0.5 (- b a)))
(bpa (* 0.5 (+ b a)))
(fac (/ 2.0 (add1 order)))
(f (build-vector
(add1 order)
(lambda (i)
(let ((y (cos (* pi (/ (+ i 0.5) (add1 order))))))
(func (+ (* y bma) bpa)))))))
(build-vector
(add1 order)
(lambda (j)
(* fac (for/fold ((sum 0.0))
((k (in-naturals))
(x (in-vector f)))
(+ sum (* x (cos (/ (* pi j (+ k 0.5)) (add1 order)))))))))))
(define (make-chebyshev-series-order order)
(make-chebyshev-series order))
(define (chebyshev-series-init series func a b)
(when (>= a b)
(error 'make-chebyshev-series
"null interval [~a, ~a]" a b))
(let ((order (chebyshev-series-order series)))
(set-chebyshev-series-coefficients!
series (compute-coefficients func order a b))
(set-chebyshev-series-lower! series a)
(set-chebyshev-series-upper! series b)))
(define (chebyshev-eval series x)
(let* ((coefficients (chebyshev-series-coefficients series))
(order (chebyshev-series-order series))
(lower (chebyshev-series-lower series))
(upper (chebyshev-series-upper series))
(c0 (vector-ref coefficients 0))
(y (/ (- (* 2.0 x) lower upper) (- upper lower)))
(y2 (* 2.0 y)))
(let-values (((d dd)
(for/fold ((d 0.0)
(dd 0.0))
((c (in-vector coefficients order 0 -1)))
(values (+ (* y2 d) (- dd) c) d))))
(+ (* y d) (- dd) (* 0.5 c0)))))
(define (chebyshev-eval-n series n x)
(let* ((coefficients (chebyshev-series-coefficients series))
(order (chebyshev-series-order series))
(lower (chebyshev-series-lower series))
(upper (chebyshev-series-upper series))
(c0 (vector-ref coefficients 0))
(y (/ (- (* 2.0 x) lower upper) (- upper lower)))
(y2 (* 2.0 y)))
(let-values (((d dd)
(for/fold ((d 0.0)
(dd 0.0))
((c (in-vector coefficients (min n order) 0 -1)))
(values (+ (* y2 d) (- dd) c) d))))
(+ (* y d) (- dd) (* 0.5 c0)))))
(define (make-chebyshev-series-derivative series)
(let* ((series-coefficients (chebyshev-series-coefficients series))
(order (chebyshev-series-order series))
(lower (chebyshev-series-lower series))
(upper (chebyshev-series-upper series))
(n (add1 order))
(deriv-coefficients (make-vector n))
(con (/ 2.0 (- upper lower))))
(vector-set! deriv-coefficients (sub1 n) 0.0)
(when (> n 1)
(vector-set! deriv-coefficients (- n 2)
(* 2.0 (- n 1) (vector-ref series-coefficients (- n 1))))
(for ((i (in-range (- n 3) 0 -1)))
(vector-set! deriv-coefficients i
(+ (vector-ref deriv-coefficients (+ i 2))
(* 2.0 (+ i 1) (vector-ref series-coefficients (+ i 1))))))
(vector-set! deriv-coefficients 0
(+ (vector-ref deriv-coefficients 2)
(* 2.0 (vector-ref series-coefficients 1))))
(for ((i (in-range n)))
(vector-set! deriv-coefficients i
(* (vector-ref deriv-coefficients i) con))))
(make-chebyshev-series deriv-coefficients order lower upper)))
(define (make-chebyshev-series-integral series)
(let* ((series-coefficients (chebyshev-series-coefficients series))
(order (chebyshev-series-order series))
(lower (chebyshev-series-lower series))
(upper (chebyshev-series-upper series))
(n (add1 order))
(integ-coefficients (make-vector n))
(con (* 0.25 (- upper lower))))
(cond ((= n 1)
(vector-set! integ-coefficients 0 0.0))
((= n 2)
(vector-set! integ-coefficients 1
(* con (vector-ref series-coefficients 0)))
(vector-set! integ-coefficients 0
(* 2.0 (vector-ref integ-coefficients 1))))
(else
(let-values (((sum fac)
(for/fold ((sum 0.0)
(fac 1.0))
((i (in-range 1 (- n 2))))
(vector-set! integ-coefficients i
(* con (- (vector-ref series-coefficients (- i 1))
(/ (vector-ref series-coefficients (+ 1))
i))))
(values (+ sum (* fac (vector-ref integ-coefficients i)))
(- fac)))))
(vector-set! integ-coefficients (- n 1)
(* con (/ (vector-ref series-coefficients (- n 2))
(- n 1))))
(set! sum (+ sum (* fac (vector-ref integ-coefficients (- n 1)))))
(vector-set! integ-coefficients 0
(* 2.0 sum)))))
(make-chebyshev-series integ-coefficients order lower upper)))
(provide
(rename-out (chebyshev-eval unchecked-chebyshev-eval)
(chebyshev-eval-n unchecked-chebyshev-eval-n)))
(provide/contract
(chebyshev-series?
(-> any/c boolean?))
(chebyshev-series-coefficients
(-> chebyshev-series? (vectorof real?)))
(chebyshev-series-order
(-> chebyshev-series? exact-positive-integer?))
(chebyshev-series-lower
(-> chebyshev-series? real?))
(chebyshev-series-upper
(-> chebyshev-series? real?))
(make-chebyshev-series
(case-> (-> exact-positive-integer? chebyshev-series?)
(-> (or/c (vectorof real?) (-> real? real?)) exact-positive-integer?
real? real? chebyshev-series?)))
(make-chebyshev-series-order
(-> exact-positive-integer? chebyshev-series?))
(chebyshev-series-init
(-> chebyshev-series? (-> real? real?) real? real? void?))
(chebyshev-eval
(-> chebyshev-series? real? real?))
(chebyshev-eval-n
(-> chebyshev-series? exact-positive-integer? real? real?))
(make-chebyshev-series-derivative
(-> chebyshev-series? chebyshev-series?))
(make-chebyshev-series-integral
(-> chebyshev-series? chebyshev-series?)))