polynomial-0.6: src/Math/Polynomial/Bernstein.hs
{-# LANGUAGE ParallelListComp #-}
module Math.Polynomial.Bernstein
( bernstein
, evalBernstein
, bernsteinFit
, evalBernsteinSeries
, deCasteljau
, splitBernsteinSeries
) where
import Math.Polynomial
import Data.List
-- |The Bernstein basis polynomials. The @n@th inner list is a basis for
-- the polynomials of order @n@ or lower. The @n@th basis consists of @n@
-- polynomials of order @n@ which sum to @1@, and have roots of varying
-- multiplicities at @0@ and @1@.
bernstein :: [[Poly Integer]]
bernstein =
[ [ scalePoly nCv p `multPoly` q
| q <- reverse qs
| p <- ps
| nCv <- bico
]
| ps <- tail $ inits [poly BE (1 : zs) | zs <- inits (repeat 0)]
| qs <- tail $ inits (iterate (multPoly (poly LE [1,-1])) one)
| bico <- ptri
]
where
-- pascal's triangle
ptri = [1] : [ 1 : zipWith (+) row (tail row) ++ [1] | row <- ptri]
-- |@evalBernstein n v x@ evaluates the @v@'th Bernstein polynomial of order @n@
-- at the point @x@.
evalBernstein :: (Integral a, Num b) => a -> a -> b -> b
evalBernstein n v t
| n < 0 || v > n = 0
| otherwise = fromInteger nCv * t^v * (1-t)^(n-v)
where
n' = toInteger n
v' = toInteger v
nCv = product [1..n'] `div` (product [1..v'] * product [1..n'-v'])
-- |@bernsteinFit n f@: Approximate a function @f@ as a linear combination of
-- Bernstein polynomials of order @n@. This approximation converges slowly
-- but uniformly to @f@ on the interval [0,1].
bernsteinFit :: (Fractional b, Integral a) => a -> (b -> b) -> [b]
bernsteinFit n f = [f (fromIntegral v / fromIntegral n) | v <- [0..n]]
-- |Evaluate a polynomial given as a list of @n@ coefficients for the @n@th
-- Bernstein basis. Roughly:
--
-- > evalBernsteinSeries cs = sum (zipWith scalePoly cs (bernstein !! (length cs - 1)))
evalBernsteinSeries :: Num a => [a] -> a -> a
evalBernsteinSeries [] = const 0
evalBernsteinSeries cs = head . last . deCasteljau cs
-- |de Casteljau's algorithm, returning the whole tableau. Used both for
-- evaluating and splitting polynomials in Bernstein form.
deCasteljau :: Num a => [a] -> a -> [[a]]
deCasteljau [] _ = []
deCasteljau cs t = cs : deCasteljau (zipWith (interp t) cs (tail cs)) t
where interp t x0 x1 = (1-t)*x0 + t*x1
-- |Given a polynomial in Bernstein form (that is, a list of coefficients
-- for a basis set from 'bernstein', such as is returned by 'bernsteinFit')
-- and a parameter value @x@, split the polynomial into two halves, mapping
-- @[0,x]@ and @[x,1]@ respectively onto @[0,1]@.
--
-- A typical use for this operation would be to split a Bezier curve
-- (inserting a new knot at @x@).
splitBernsteinSeries :: Num a => [a] -> a -> ([a], [a])
splitBernsteinSeries cs t = (map head betas, map last (reverse betas))
where
betas = deCasteljau cs t