--- /dev/null
+++ b/test/TestUtils.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances, TypeFamilies #-}
+module TestUtils where
+
+import Control.Applicative
+import Control.Monad
+import Data.List
+import Data.VectorSpace
+import Math.Polynomial
+import Math.Polynomial.Type
+import Test.QuickCheck
+import qualified Data.Vector as V
+
+instance Arbitrary Endianness where
+    arbitrary = elements [BE, LE]
+
+instance (Num a, Eq a, Arbitrary a) => Arbitrary (Poly a) where
+    arbitrary = polyCon <*> arbitrary <*> arbitrary
+        where
+            polyCon = elements
+                [ poly
+                , rawListPoly
+                , \e -> rawVectorPoly e . V.fromList
+                ]
+
+newtype SmallPoly a = SmallPoly (Poly a)
+    deriving (Eq, Show)
+instance (Num a, Eq a, Arbitrary a) => Arbitrary (SmallPoly a) where
+    arbitrary = SmallPoly <$> (polyCon <*> arbitrary <*> smallList)
+        where
+            polyCon = elements
+                [ poly
+                , rawListPoly
+                , \e -> rawVectorPoly e . V.fromList
+                ]
+            
+            smallList = do
+                let binom n k = product [k+1 .. n] `div` product [1 .. n-k]
+                n <- frequency [ (fromInteger (binom 10 k), return k) | k <- [0..10]]
+                replicateM (fromInteger n) arbitrary
+
+-- instance AdditiveGroup Rational where
+--     zeroV = 0
+--     (^+^) = (+)
+--     negateV = negate
+-- instance VectorSpace Rational where
+--     type Scalar Rational = Rational
+--     (*^) = (*)
+
+rev BE = LE
+rev LE = BE
+
+sep [] = []
+sep rts = uniq : sep (rts \\ uniq)
+    where uniq = nub rts
+
+-- arbitrary function mapping the real line to the unit interval
+onUnitInterval x = x - fromIntegral (floor x)
+
+onInterval a b x = a + w * onUnitInterval x
+    where
+        w = b - a
+
+distinct [] = True
+distinct (x:xs) = all (/= x) xs && distinct xs
+
+relErr 0 0 = 0
+relErr x y = abs (x - y) / max (abs x) (abs y)
--- /dev/null
+++ b/test/Tests/Bernstein.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE ExtendedDefaultRules #-}
+module Tests.Bernstein (bernsteinTests) where
+
+import Math.Polynomial
+import Math.Polynomial.Bernstein
+import Test.Framework (testGroup)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+import Test.QuickCheck
+import TestUtils
+
+default (Integer, Rational)
+
+bernsteinTests =
+    [ testGroup "bernstein"     bernstein_tests
+    , testGroup "evalBernstein" evalBernstein_tests
+    ]
+
+bernstein_tests =
+    [ testProperty "sum"        prop_bernstein_sum
+    , testProperty "recurrence" prop_bernstein_recurrence
+    , testProperty "symmetry"   prop_bernstein_symmetry
+    ]
+
+-- prop_bernstein_sum can be very time and space intensive if we let it
+-- look too far into the table.  We also don't really gain much if any 
+-- additional confidence in the correctness of the implementation by doing
+-- so, so here's a limit for 'n' in the "bernstein :: [[Poly Integer]]" tests.
+bernsteinLimit = 500
+
+prop_bernstein_sum (NonNegative n) =
+    polyIsOne (sumPolys (bernstein !! (n `mod` bernsteinLimit)))
+
+prop_bernstein_recurrence (Positive a) (Positive b) =
+    let a' = a `mod` bernsteinLimit
+        b' = b `mod` bernsteinLimit
+        n = 1 + max a' b'; n' = fromIntegral n
+        v =     min a' b'; v' = fromIntegral v
+     in addPoly (scalePoly ((n'-v')/n') (fmap toRational (bernstein !! n !!  v   )))
+                (scalePoly ((v'+ 1)/n') (fmap toRational (bernstein !! n !! (v+1))))
+        == fmap toRational (bernstein !! (n-1) !! v)
+            
+
+prop_bernstein_symmetry (Positive a) (Positive b) =
+    let a' = a `mod` bernsteinLimit
+        b' = b `mod` bernsteinLimit
+        n = max a' b'
+        v = min a' b'
+     in bernstein !! n !! (n-v)
+     == composePoly (bernstein !! n !! v) (poly LE [1, -1])
+            
+
+evalBernstein_tests = 
+    [ testProperty "sane"       prop_evalBernstein_sane
+    , testProperty "sign"       prop_evalBernstein_sign
+    , testProperty "local max"  prop_evalBernstein_local_max
+    , testProperty "symmetry"   prop_evalBernstein_symmetry
+    ]
+
+prop_evalBernstein_sane (NonNegative a) (NonNegative b) x =
+    let n = max a b
+        v = min a b
+     in n <= bernsteinLimit ==>
+            evalPoly (fmap toRational (bernstein !! n !! v)) x
+            == evalBernstein n v x
+
+prop_evalBernstein_sign (NonNegative a) (NonNegative b) x =
+    let n = max a b
+        v = min a b
+     in signum (evalBernstein n v x) == evalBernstein_sign n v x
+
+prop_evalBernstein_local_max  (NonNegative a) (NonNegative b) x =
+    let n = max a b
+        v = min a b
+        xMax = fromIntegral v / fromIntegral n
+     in n >= 0 ==>
+            evalBernstein n v (onUnitInterval x) <= evalBernstein n v xMax
+
+prop_evalBernstein_symmetry  (NonNegative a) (NonNegative b) x =
+    let n = max a b
+        v = min a b
+     in evalBernstein n (n-v) x
+     == evalBernstein n    v  (1-x)
+
+evalBernstein_sign 0 0 _ = 1
+evalBernstein_sign n v 0 = delta v 0
+evalBernstein_sign n v 1 = delta v n
+evalBernstein_sign n v x
+    | x > 0 && x < 1    = 1
+    | even n || x < 0   = if even v then 1 else -1
+    | otherwise         = if odd  v then 1 else -1 
+
+delta x y
+    | x == y    = 1
+    | otherwise = 0
--- /dev/null
+++ b/test/Tests/Chebyshev.hs
@@ -0,0 +1,142 @@
+{-# LANGUAGE ExtendedDefaultRules #-}
+module Tests.Chebyshev (chebyshevTests) where
+
+import Math.Polynomial
+import Math.Polynomial.Chebyshev
+import Test.Framework (testGroup)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+import Test.QuickCheck
+import TestUtils
+import Text.Show.Functions ()
+
+default (Integer, Rational)
+
+chebyshevTests =
+    [ testGroup "ts" ts_tests
+    , testGroup "us" us_tests
+    , testProperty "Pell's equation" prop_pell's_eqn
+    , testProperty "t n == ts !! n" $ \(NonNegative a) -> let n = a `mod` 1000 in t n == ts !! n
+    , testProperty "u n == us !! n" $ \(NonNegative a) -> let n = a `mod` 1000 in u n == us !! n
+    , testGroup "evalT" evalT_tests
+    , testGroup "evalU" evalU_tests
+    , testGroup "tRoots" tRoots_tests
+    , testGroup "tExtrema" tExtrema_tests
+    , testGroup "chebyshevFit" chebyshevFit_tests
+    , testGroup "evalChebyshevSeries" evalChebyshevSeries_tests
+    ]
+
+ts_tests =
+    [ testProperty "recurrence" prop_ts_recurrence
+    , testProperty "parity"     prop_ts_parity
+    ]
+
+prop_ts_recurrence (NonNegative 0) = ts !! 0 == one
+prop_ts_recurrence (NonNegative 1) = ts !! 1 == x
+prop_ts_recurrence (NonNegative a) =
+    let n = 1 + a `mod` 1200
+     in (multPoly (scalePoly 2 x) (ts !! n))
+     == addPoly (ts !! (n-1)) (ts !! (n+1))
+
+prop_ts_parity (NonNegative n)
+    | even n    = composePoly t (negatePoly x) == t
+    | otherwise = composePoly t (negatePoly x) == negatePoly t
+        where t = ts !! (n `mod` 1200)
+
+us_tests =
+    [ testProperty "recurrence" prop_us_recurrence
+    , testProperty "parity"     prop_us_parity
+    ]
+
+prop_us_recurrence (NonNegative 0) = us !! 0 == one
+prop_us_recurrence (NonNegative 1) = us !! 1 == scalePoly 2 x
+prop_us_recurrence (NonNegative a) =
+    let n = 1 + a `mod` 1200
+     in (multPoly (scalePoly 2 x) (us !! n))
+     == addPoly (us !! (n-1)) (us !! (n+1))
+
+prop_us_parity (NonNegative n)
+    | even n    = composePoly u (negatePoly x) == u
+    | otherwise = composePoly u (negatePoly x) == negatePoly u
+        where u = us !! (n `mod` 1200)
+
+prop_pell's_eqn (Positive a) =
+    let n = 1 +  a `mod` 400
+     in powPoly (ts !! n) 2
+     == addPoly one (multPoly (poly BE [1,0,-1]) (powPoly (us !! (n-1)) 2))
+
+evalT_tests =
+    [ testProperty "sane"           prop_evalT_sane
+    , testProperty "limits (Float)"  (prop_evalT_limits (1e-7  :: Float))
+    , testProperty "limits (Double)" (prop_evalT_limits (1e-15 :: Double))
+    , testProperty "endpoints"       prop_evalT_endpoints
+    ]
+
+prop_evalT_sane (NonNegative a) x =
+    let n = a `mod` 1000
+     in evalT n x == evalPoly (t n) x
+
+prop_evalT_limits eps (NonNegative n) x = abs (evalT (n `mod` 1000) (onInterval (-1) 1 x)) <= 1 + eps
+
+prop_evalT_endpoints (NonNegative a) True  =
+    let n = a `mod` 5000
+     in evalT n 1 == 1
+prop_evalT_endpoints (NonNegative a) False =
+    let n = a `mod` 5000
+     in evalT n (-1) == if even n then 1 else -1
+
+evalU_tests =
+    [ testProperty "sane"           prop_evalU_sane
+    ]
+
+prop_evalU_sane (NonNegative a) x =
+    let n = a `mod` 1000
+     in evalT n x == evalPoly (t n) x
+
+
+tRoots_tests =
+    [ testProperty "distinct (Float)"  (prop_tRoots_distinct (undefined :: Float))
+    , testProperty "distinct (Double)" (prop_tRoots_distinct (undefined :: Double))
+    -- I could test that the roots are roots, but numerically they aren't,
+    -- and with the insanely large derivatives at some of the roots, I don't
+    -- expect 'evalT n' to even be particularly close to zero at many of the
+    -- "roots".
+    ]
+
+prop_tRoots_distinct w (NonNegative n') = distinct (tRoots n `asTypeOf` [w])
+    where n = n' `mod` 1200
+
+tExtrema_tests =
+    [ testProperty "distinct (Float)"   (prop_tExtrema_distinct (undefined :: Float))
+    , testProperty "distinct (Double)"  (prop_tExtrema_distinct (undefined :: Double))
+    , testProperty "near 1 (Float)"     (prop_tExtrema_near_1 (\n -> n * 1e-7 :: Float))
+    , testProperty "near 1 (Double)"    (prop_tExtrema_near_1 (\n -> n * 1e-16 :: Double))
+    ]
+
+prop_tExtrema_distinct w (NonNegative n') = distinct (tExtrema n `asTypeOf` [w])
+    where n = n' `mod` 1200
+
+prop_tExtrema_near_1 eps (NonNegative n') = all (near1.abs.evalT n) (tExtrema n)
+    where 
+        n = n' `mod` 1200
+        near1 y = abs (y-1) <= eps (fromIntegral n)
+
+chebyshevFit_tests =
+    [ testProperty "sane (Double)"   (prop_chebyshevFit_sane (\n -> 1e-11 * n^2 :: Double))
+    , testProperty "sane (Float)"    (prop_chebyshevFit_sane (\n -> 1e-4  * n^2 :: Float))
+    ]
+
+prop_chebyshevFit_sane epsF (NonNegative n') f =
+    n' < 100 ==> all (<= eps) [relErr (f x) (f' x) | x <- tRoots n]
+    where 
+        eps = epsF (fromIntegral n)
+        n = n' `mod` 500
+        cs = chebyshevFit n f
+        f' = evalChebyshevSeries cs
+
+evalChebyshevSeries_tests =
+    [ testProperty "sane"   prop_evalChebyshevSeries_sane
+    ]
+
+prop_evalChebyshevSeries_sane cs x 
+    =  evalChebyshevSeries cs x 
+    == sum (zipWith (*) cs (evalTs x))
--- /dev/null
+++ b/test/Tests/Core.hs
@@ -0,0 +1,290 @@
+{-# LANGUAGE ExtendedDefaultRules #-}
+module Tests.Core (coreTests) where
+
+import Data.List
+import Data.VectorSpace
+import Math.Polynomial
+import Math.Polynomial.Lagrange
+import Test.Framework (testGroup)
+import Test.Framework.Providers.HUnit (testCase)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+import Test.HUnit
+import Test.QuickCheck
+import TestUtils
+
+-- Use exact math everywhere; we're not testing stability or speed,
+-- just mathematical soundness.
+default (Integer, Rational)
+
+coreTests = 
+    [ testGroup "constants"
+        [ testGroup "zero"
+            [ testCase "polyIsZero zero" (assert (polyIsZero zero))
+            , testCase "polyDegree zero == (-1)" (assert (polyDegree zero == (-1)))
+            , testProperty "(p == zero) == polyIsZero p" $ \p ->
+                (p == zero) == polyIsZero p
+            , testProperty "evalPoly zero x == 0" $ \x ->
+                evalPoly zero x == 0
+            ]
+        , testGroup "one"
+            [ testCase "polyIsOne one" (assert (polyIsOne one))
+            , testCase "polyDegree one == 0" (assert (polyDegree one == 0))
+            , testProperty "(p == one) == polyIsOne p" $ \p ->
+                (p == one) == polyIsOne p
+            , testProperty "evalPoly one x == 1" $ \x ->
+                evalPoly one x == 1
+            ]
+        , testGroup "x"
+            [ testProperty "evalPoly x t == t" $ \t ->
+                evalPoly x t == t
+            , testCase "polyDegree x == 1" (assert (polyDegree x == 1))
+            ]
+        , testGroup "constPoly"
+            [ testProperty "evalPoly (constPoly x) == const x" $ \a b -> 
+                evalPoly (constPoly a) b == const a b
+            , testProperty "polyDegree (constPoly x) == if x == 0 then -1 else 0" $ \x -> 
+                polyDegree (constPoly x) == if x == 0 then -1 else 0
+            ]
+        ]
+    , testGroup "constructors"
+        [ testProperty "polyCoeffs LE . poly LE" $ \cs ->
+            case stripPrefix (polyCoeffs LE (poly LE cs)) cs of
+                Just zs -> all (== 0) zs
+                Nothing -> False
+        , testProperty "polyCoeffs LE . poly BE" $ \cs ->
+            case stripPrefix (polyCoeffs LE (poly BE cs)) (reverse cs) of
+                Just zs -> all (== 0) zs
+                Nothing -> False
+        , testProperty "polyCoeffs BE . poly LE" $ \cs ->
+            case stripPrefix (reverse (polyCoeffs BE (poly LE cs))) cs of
+                Just zs -> all (== 0) zs
+                Nothing -> False
+        , testProperty "polyCoeffs BE . poly BE" $ \cs ->
+            case stripPrefix (reverse (polyCoeffs BE (poly BE cs))) (reverse cs) of
+                Just zs -> all (== 0) zs
+                Nothing -> False
+        ]
+    , testGroup "instances"
+        [ testGroup "Eq"
+            [ testProperty "reflexive"  $ \p     -> p == p
+            , testProperty "symmetric"  $ \p q   -> (p==q) == (q==p)
+            , testProperty "transitive" $ \p q r -> (p == q && q == r) ==> p == r
+            , testProperty "sane"       $ \cs ds end1 end2 -> 
+                let p = poly end1 cs; q = poly end1 ds
+                 in (p==q) == (polyCoeffs end2 p == polyCoeffs end2 q)
+            , testProperty "endianness-independent" $ \cs end ->
+                poly end cs == poly (rev end) (reverse cs)
+            ]
+        , testGroup "AdditiveGroup"
+            [ testGroup "zeroV"
+                [ testCase "polyIsZero zeroV" (assert (polyIsZero zeroV))
+                , testProperty "(p == zeroV) == polyIsZero p" $ \p ->
+                    (p == zeroV) == polyIsZero p
+                , testProperty "evalPoly zeroV x == zeroV" $ \x ->
+                    evalPoly zeroV x == zeroV
+                ]
+            , testGroup "^+^"
+                [ testProperty "left  unit"  $ \p     -> zeroV ^+^ p == p
+                , testProperty "right unit"  $ \p     -> p ^+^ zeroV == p
+                , testProperty "commutative" $ \p q   -> p ^+^ q == q ^+^ p
+                , testProperty "associative" $ \p q r ->
+                    p ^+^ (q ^+^ r) == (p ^+^ q) ^+^ r
+                , testProperty "sane" $ \p q x ->
+                    evalPoly (p ^+^ q) x ==
+                    evalPoly p x ^+^ evalPoly q x
+                ]
+            , testGroup "negateV"
+                [ testProperty "sane" $ \p -> p ^+^ negateV p == zeroV
+                ]
+            ]
+        , testGroup "VectorSpace"
+            [ testProperty "sane" $ \s p x ->
+                evalPoly (s *^ (p :: Poly Rational)) x == s *^ evalPoly p x
+            ]
+        ]
+    , testGroup "addPoly"
+        [ testProperty "left  unit"  $ \p     -> addPoly zero p == p
+        , testProperty "right unit"  $ \p     -> addPoly p zero == p
+        , testProperty "commutative" $ \p q   -> addPoly p q == addPoly q p
+        , testProperty "associative" $ \p q r ->
+            addPoly p (addPoly q r) == addPoly (addPoly p q) r
+        , testProperty "sane" $ \p q x ->
+            evalPoly (addPoly p q) x ==
+            evalPoly p x + evalPoly q x
+        , testProperty "degree" $ \p q -> 
+            let n = polyDegree p
+                m = polyDegree q
+                r = addPoly p q
+             in if n /= m
+                || polyIsZero p || polyIsZero q
+                || head (polyCoeffs BE p) + head (polyCoeffs BE q) /= 0
+                    then polyDegree r == max m n
+                    else polyDegree r <  max m n
+        ]
+    , testGroup "sumPoly"
+        [ testProperty "sane" $ \ps -> sumPolys ps == foldl' addPoly zero ps
+        ]
+    , testGroup "negatePoly"
+        [ testProperty "sane" $ \p -> polyIsZero (addPoly p (negatePoly p))
+        , testProperty "degree" $ \p -> polyDegree p == polyDegree (negatePoly p)
+        ]
+    , testGroup "composePoly"
+        [ testProperty "sane" $ \f g x -> 
+            polyDegree f * polyDegree g <= 250 ==>
+                    evalPoly (composePoly f g) x 
+                 == evalPoly f (evalPoly g x)
+        , testProperty "associative" $ \(SmallPoly f) (SmallPoly g) (SmallPoly h) -> 
+            polyDegree f * polyDegree g * polyDegree h <= 500 ==>
+                    composePoly f (composePoly g h)
+                 == composePoly (composePoly f g) h
+        , testProperty "left  cancel" $ \p k ->
+            composePoly p (constPoly k) == constPoly (evalPoly p k)
+        , testProperty "right cancel" $ \k p ->
+            composePoly (constPoly k) p == constPoly k
+        , testProperty "left  identity" $ \p ->
+            composePoly p x == p
+        , testProperty "right identity" $ \p ->
+            composePoly x p == p
+        , testProperty "degree" $ \(SmallPoly p) (SmallPoly q) -> 
+            polyDegree (composePoly p q)
+                <= (1 + polyDegree p) * (1 + polyDegree q)
+        ]
+    , testGroup "scalePoly"
+        [ testProperty "sane" $ \s p x ->
+            evalPoly (scalePoly s p) x == s * evalPoly p x
+        , testProperty "degree" $ \s p -> 
+            polyDegree (scalePoly s p) == if s == 0 then -1 else polyDegree p
+        ]
+    , testGroup "multPoly"
+        [ testProperty "left  cancel" $ \p     -> polyIsZero (multPoly zero p)
+        , testProperty "right cancel" $ \p     -> polyIsZero (multPoly p zero)
+        , testProperty "left  unit"   $ \p     -> multPoly one p == p
+        , testProperty "right unit"   $ \p     -> multPoly p one == p
+        , testProperty "commutative"  $ \p q   -> multPoly p q == multPoly q p
+        , testProperty "associative"  $ \p q r ->
+            multPoly p (multPoly q r) == multPoly (multPoly p q) r
+        , testProperty "distributive" $ \p q r ->
+            multPoly p (addPoly q r) == addPoly (multPoly p q) (multPoly p r)
+        , testProperty "sane" $ \p q x ->
+            evalPoly (multPoly p q) x ==
+            evalPoly p x * evalPoly q x
+        , testProperty "degree" $ \p q -> 
+            if polyIsZero p || polyIsZero q
+                then polyDegree (multPoly p q) == (-1)
+                else polyDegree (multPoly p q) == polyDegree p + polyDegree q
+        ]
+    , testGroup "powPoly"
+        [ testProperty "cancel"   $ \p -> polyIsOne (powPoly p 0)
+        , testProperty "unit"     $ \p -> powPoly p 1 == p
+        , testProperty "multiply" $ \(SmallPoly p) (NonNegative a) (NonNegative b) ->
+            let a' = a `mod` 8; b' = b `mod` 8
+             in multPoly (powPoly p a') (powPoly p b') == powPoly p (a' + b')
+        , testProperty "compose"  $ \(SmallPoly p) (NonNegative a) (NonNegative b) ->
+            let a' = a `mod` 6; b' = b `mod` 6
+             in powPoly (powPoly p b') a' == powPoly p (a' * b')
+        , testProperty "sane"     $ \(SmallPoly p) (NonNegative n) ->
+            let n' = n `mod` 16
+             in powPoly p n' == foldl' multPoly one (replicate n' p)
+        , testProperty "degree" $ \p (NonNegative n) ->
+            let n' = n `mod` 16
+             in polyDegree (powPoly p n') == max (-1) (n' * polyDegree p)
+        ]
+    , testGroup "quotRemPoly"
+        [ testProperty "sane" $ \a (SmallPoly b) -> 
+            not (polyIsZero b) ==> case quotRemPoly a b of
+                (q, r) -> polyDegree r < polyDegree b 
+                       && addPoly (multPoly q b) r == a
+        ]
+    , testGroup "quotPoly"
+        [ testProperty "sane" $ \a b -> 
+            not (polyIsZero b) ==> 
+            quotPoly a b == fst (quotRemPoly a b)
+        ]
+    , testGroup "remPoly"
+        [ testProperty "sane" $ \a b -> 
+            not (polyIsZero b) ==> 
+            remPoly a b == snd (quotRemPoly a b)
+        ]
+    , testGroup "evalPolyDeriv"
+        [ testProperty "zero"       $ \t   -> evalPolyDeriv zero          t == (0,0)
+        , testProperty "one"        $ \t   -> evalPolyDeriv one           t == (1,0)
+        , testProperty "constPoly"  $ \t k -> evalPolyDeriv (constPoly k) t == (k,0)
+        , testProperty "x"          $ \t   -> evalPolyDeriv x             t == (t,1)
+        , testProperty "chain rule" $ \p q x ->
+               snd (evalPolyDeriv (multPoly p q) x)
+            == snd (evalPolyDeriv p x) * evalPoly q x + snd (evalPolyDeriv q x) * evalPoly p x
+        , testProperty "sane" $ \p x ->
+            evalPolyDeriv p x == (evalPoly p x, evalPoly (polyDeriv p) x)
+        ]
+    , testGroup "evalPolyDerivs"
+        [ testProperty "sane" $ \p x ->
+            and $ zipWith (==) 
+                (evalPolyDerivs p x) 
+                [evalPoly p x | p <- iterate polyDeriv p]
+        ]
+    , testGroup "contractPoly"
+        [ testProperty "sane" $ \p a -> 
+            case contractPoly p a of
+                (q, r) -> addPoly (multPoly q (poly BE [1,-a])) (constPoly r) == p
+        , testProperty "root" $ \p a ->
+            case contractPoly p a of
+                (q, r) -> evalPoly (addPoly p (poly BE [-r])) a == 0
+        ]
+    , testGroup "monicPoly"
+        [ testProperty "sane" $ \p ->
+            if polyIsZero p 
+                then polyIsZero (monicPoly p)
+                else head (polyCoeffs BE (monicPoly p)) == 1
+        ]
+    , testGroup "gcdPoly"
+        [ testProperty "sane" $ \p q ->
+            (polyDegree p + polyDegree q <= 20) &&
+            not (all polyIsZero [p,q]) ==>
+                let g = gcdPoly p q
+                 in all polyIsZero [p `remPoly` g, q `remPoly` g]
+        , testProperty "monic" $ \p q ->
+            (polyDegree p + polyDegree q <= 20) &&
+            not (all polyIsZero [p,q]) ==>
+                head (polyCoeffs BE (gcdPoly p q)) == 1
+        , testProperty "right cancel" $ \p -> gcdPoly p one == one
+        , testProperty "left  cancel" $ \p -> gcdPoly one p == one
+        , testProperty "commutative" $ \p q -> 
+            (polyDegree p + polyDegree q <= 20) &&
+            not (all polyIsZero [p,q]) ==>
+                gcdPoly p q == gcdPoly q p
+        , testProperty "associative" $ \p q r -> 
+            (polyDegree p + polyDegree q + polyDegree r <= 20) &&
+            not (any (all polyIsZero) [[p,q], [p,r], [q,r]]) ==>
+                gcdPoly (gcdPoly p q) r == gcdPoly p (gcdPoly q r)
+        , testProperty "roots" $ \pScale (Ordered pRoots) qScale (Ordered qRoots) ->
+            (length pRoots + length qRoots <= 20) ==>
+            let p = scalePoly pScale (lagrange pRoots)
+                q = scalePoly qScale (lagrange qRoots)
+                r = lagrange (intersect pRoots qRoots)
+             in not (null pRoots && null qRoots)
+                ==> r == gcdPoly p q
+
+        ]
+    , testGroup "polyDeriv"
+        [ testCase "zero" $ do
+            assert (polyDeriv zero == zero)
+        , testCase "one" $ do
+            assert (polyDeriv one == zero)
+        , testProperty "constPoly" $ \k -> 
+            polyDeriv (constPoly k) == zero
+        , testCase "x" $ do
+            assert (polyDeriv x == one)
+        , testProperty "chain rule" $ \p q ->
+            (polyDegree p + polyDegree q <= 20) ==>
+            polyDeriv (multPoly p q) == addPoly (multPoly p (polyDeriv q)) (multPoly q (polyDeriv p))
+        ]
+    , testGroup "polyIntegral"
+        [ testProperty "sane" $ \p -> polyDeriv (polyIntegral p) == p
+        , testProperty "constant factor" $ \p -> evalPoly (polyIntegral p) 0 == 0
+        ]
+    , testGroup "separateRoots"
+        [ testProperty "sane" $ \(NonEmpty rts) ->
+            length rts < 10 ==>
+            separateRoots (lagrange rts) == map lagrange (sep rts)
+        ]
+    ]
--- /dev/null
+++ b/test/Tests/Hermite.hs
@@ -0,0 +1,119 @@
+{-# LANGUAGE ExtendedDefaultRules #-}
+module Tests.Hermite (hermiteTests) where
+
+import Data.VectorSpace
+import Math.Polynomial
+import Math.Polynomial.Hermite
+import Test.Framework (testGroup)
+import Test.Framework.Providers.HUnit (testCase)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+import Test.HUnit
+import Test.QuickCheck
+import TestUtils
+
+default (Integer, Rational)
+
+hermiteLimit = 500
+
+hermiteTests =
+    [ testGroup "probHermite"           probHermite_tests
+    , testGroup "physHermite"           physHermite_tests
+    , testGroup "evalProbHermite"       evalProbHermite_tests
+    , testGroup "evalPhysHermite"       evalPhysHermite_tests
+    , testGroup "evalProbHermiteDeriv"  evalProbHermiteDeriv_tests
+    , testGroup "evalPhysHermiteDeriv"  evalPhysHermiteDeriv_tests
+    ]
+
+probHermite_tests =
+    [ testCase     "known values"   case_probHermite_knownValues
+    , testProperty "recurrence"     prop_probHermite_recurrence 
+    ]
+
+probHermite_knownValues :: [(Int, Poly Integer)]
+probHermite_knownValues =
+    [ ( 0, one)
+    , ( 1, x)
+    , ( 2, poly BE [1, 0,  -1])
+    , ( 3, poly BE [1, 0,  -3, 0])
+    , ( 4, poly BE [1, 0,  -6, 0,  3])
+    , ( 5, poly BE [1, 0, -10, 0,  15, 0])
+    , ( 6, poly BE [1, 0, -15, 0,  45, 0,   -15])
+    , ( 7, poly BE [1, 0, -21, 0, 105, 0,  -105, 0])
+    , ( 8, poly BE [1, 0, -28, 0, 210, 0,  -420, 0,  105])
+    , ( 9, poly BE [1, 0, -36, 0, 378, 0, -1260, 0,  945, 0])
+    , (10, poly BE [1, 0, -45, 0, 630, 0, -3150, 0, 4725, 0, -945])
+    ]
+case_probHermite_knownValues = sequence_
+    [ assertEqual ("probHermite !! " ++ show n) (probHermite !! n) value
+    | (n, value) <- probHermite_knownValues
+    ]
+
+prop_probHermite_recurrence (Positive n)
+    =  probHermite !! (n'+1)
+    == multPoly x (probHermite !! n') ^-^ polyDeriv (probHermite !! n')
+    where n' = n `mod` hermiteLimit
+
+physHermite_tests =
+    [ testCase     "known values"   case_physHermite_knownValues
+    , testProperty "recurrence"     prop_physHermite_recurrence 
+    ]
+
+physHermite_knownValues :: [(Int, Poly Integer)]
+physHermite_knownValues =
+    [ ( 0, one)
+    , ( 1, poly BE [   2, 0])
+    , ( 2, poly BE [   4, 0,     -2])
+    , ( 3, poly BE [   8, 0,    -12, 0])
+    , ( 4, poly BE [  16, 0,    -48, 0,     12])
+    , ( 5, poly BE [  32, 0,   -160, 0,    120, 0])
+    , ( 6, poly BE [  64, 0,   -480, 0,    720, 0,    -120])
+    , ( 7, poly BE [ 128, 0,  -1344, 0,   3360, 0,   -1680, 0])
+    , ( 8, poly BE [ 256, 0,  -3584, 0,  13440, 0,  -13440, 0,   1680])
+    , ( 9, poly BE [ 512, 0,  -9216, 0,  48384, 0,  -80640, 0,  30240, 0])
+    , (10, poly BE [1024, 0, -23040, 0, 161280, 0, -403200, 0, 302400, 0, -30240])
+    ]
+case_physHermite_knownValues = sequence_
+    [ assertEqual ("physHermite !! " ++ show n) (physHermite !! n) value
+    | (n, value) <- physHermite_knownValues
+    ]
+
+prop_physHermite_recurrence (Positive n)
+    =  physHermite !! (n'+1)
+    == scalePoly 2 (multPoly x (physHermite !! n')) ^-^ polyDeriv (physHermite !! n')
+    where n' = n `mod` hermiteLimit
+
+evalProbHermite_tests =
+    [ testProperty "sane"   prop_evalProbHermite_sane
+    ]
+
+prop_evalProbHermite_sane n x
+    =  evalProbHermite n' x
+    == evalPoly (fmap fromIntegral (probHermite !! n')) x
+    where n' = n `mod` hermiteLimit
+
+evalPhysHermite_tests =
+    [ testProperty "sane"   prop_evalPhysHermite_sane
+    ]
+
+prop_evalPhysHermite_sane n x
+    =  evalPhysHermite n' x
+    == evalPoly (fmap fromIntegral (physHermite !! n')) x
+    where n' = n `mod` hermiteLimit
+
+evalProbHermiteDeriv_tests =
+    [ testProperty "sane"   prop_evalProbHermiteDeriv_sane
+    ]
+
+prop_evalProbHermiteDeriv_sane n x
+    =  evalProbHermiteDeriv n' x
+    == evalPolyDeriv (fmap fromIntegral (probHermite !! n')) x
+    where n' = n `mod` hermiteLimit
+
+evalPhysHermiteDeriv_tests =
+    [ testProperty "sane"   prop_evalPhysHermiteDeriv_sane
+    ]
+
+prop_evalPhysHermiteDeriv_sane n x
+    =  evalPhysHermiteDeriv n' x
+    == evalPolyDeriv (fmap fromIntegral (physHermite !! n')) x
+    where n' = n `mod` hermiteLimit
