packages feed

poly 0.3.1.0 → 0.3.2.0

raw patch · 21 files changed

+908/−471 lines, 21 filesdep +deepseqdep ~semiringsdep ~vectorPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: deepseq

Dependency ranges changed: semirings, vector

API changes (from Hackage documentation)

- Data.Poly: PolyOverFractional :: poly -> PolyOverFractional poly
- Data.Poly: [unPolyOverFractional] :: PolyOverFractional poly -> poly
- Data.Poly: newtype PolyOverFractional poly
- Data.Poly.Semiring: PolyOverFractional :: poly -> PolyOverFractional poly
- Data.Poly.Semiring: [unPolyOverFractional] :: PolyOverFractional poly -> poly
- Data.Poly.Semiring: newtype PolyOverFractional poly
+ Data.Poly: PolyOverField :: poly -> PolyOverField poly
+ Data.Poly: [unPolyOverField] :: PolyOverField poly -> poly
+ Data.Poly: gcdExt :: (Eq a, Field a, Vector v a, Eq (v a)) => Poly v a -> Poly v a -> (Poly v a, Poly v a)
+ Data.Poly: newtype PolyOverField poly
+ Data.Poly: pattern PolyOverFractional :: poly -> PolyOverField poly
+ Data.Poly: type PolyOverFractional = PolyOverField
+ Data.Poly: unPolyOverFractional :: PolyOverField poly -> poly
+ Data.Poly.Semiring: PolyOverField :: poly -> PolyOverField poly
+ Data.Poly.Semiring: [unPolyOverField] :: PolyOverField poly -> poly
+ Data.Poly.Semiring: gcdExt :: (Eq a, Field a, Vector v a, Eq (v a)) => Poly v a -> Poly v a -> (Poly v a, Poly v a)
+ Data.Poly.Semiring: integral :: (Eq a, Field a, Vector v a) => Poly v a -> Poly v a
+ Data.Poly.Semiring: newtype PolyOverField poly
+ Data.Poly.Semiring: pattern PolyOverFractional :: poly -> PolyOverField poly
+ Data.Poly.Semiring: type PolyOverFractional = PolyOverField
+ Data.Poly.Semiring: unPolyOverFractional :: PolyOverField poly -> poly
+ Data.Poly.Sparse: gcdExt :: (Eq a, Field a, Vector v (Word, a), Eq (v (Word, a))) => Poly v a -> Poly v a -> (Poly v a, Poly v a)
+ Data.Poly.Sparse.Semiring: gcdExt :: (Eq a, Field a, Vector v (Word, a), Eq (v (Word, a))) => Poly v a -> Poly v a -> (Poly v a, Poly v a)
+ Data.Poly.Sparse.Semiring: integral :: (Eq a, Field a, Vector v (Word, a)) => Poly v a -> Poly v a

Files

README.md view
@@ -76,10 +76,10 @@ cover main functionality of `Integral`, providing division with remainder and `gcd` / `lcm`:  ```haskell-> Data.Euclidean.gcd (X^2 + 7 * X + 6) (X^2 - 5 * X - 6) :: Data.Poly.UPoly Int+> Data.Euclidean.gcd (X^2 + 7 * X + 6) (X^2 - 5 * X - 6) :: UPoly Int 1 * X + 1 -> Data.Euclidean.quotRem (X^3 + 2) (X^2 - 1 :: Data.Poly.UPoly Double)+> Data.Euclidean.quotRem (X^3 + 2) (X^2 - 1 :: UPoly Double) (1.0 * X + 0.0,1.0 * X + 2.0) ``` 
bench/DenseBench.hs view
@@ -1,16 +1,24 @@-{-# LANGUAGE CPP        #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE CPP                        #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RankNTypes                 #-}+{-# LANGUAGE TypeApplications           #-}  module DenseBench   ( benchSuite   ) where  import Prelude hiding (quotRem, gcd)+import Control.DeepSeq import Gauge.Main import Data.Poly import qualified Data.Vector.Unboxed as U #if MIN_VERSION_semirings(0,4,2)+import Control.Exception+import Data.Bits+import Data.Coerce import Data.Euclidean+import Data.Semiring (Semiring(..), Ring, isZero)+import qualified Data.Semiring as S (negate) import qualified Data.Vector as V #endif @@ -22,9 +30,12 @@   , map benchDeriv    [100, 1000, 10000]   , map benchIntegral [100, 1000, 10000] #if MIN_VERSION_semirings(0,4,2)-  , map benchQuotRem  [10, 100]-  , map benchGcdFrac  [10, 100]-  , map benchGcd      [10, 100]+  , map benchQuotRem    [10, 100]+  , map benchGcd        [10, 100]+  , map benchGcdExtRat  [10, 20, 40]+  , map benchGcdFracRat [10, 20, 40]+  , map benchGcdExtM    [10, 100, 1000]+  , map benchGcdFracM   [10, 100, 1000] #endif   ] @@ -51,9 +62,18 @@ benchGcd :: Int -> Benchmark benchGcd k = bench ("gcd/" ++ show k) $ nf doGcd k -benchGcdFrac :: Int -> Benchmark-benchGcdFrac k = bench ("gcdFrac/" ++ show k) $ nf doGcdFrac k+benchGcdExtRat :: Int -> Benchmark+benchGcdExtRat k = bench ("gcdExt/Rational/" ++ show k) $ nf (doGcdExt @Rational) k +benchGcdFracRat :: Int -> Benchmark+benchGcdFracRat k = bench ("gcdFrac/Rational/" ++ show k) $ nf (doGcdFrac @Rational) k++benchGcdExtM :: Int -> Benchmark+benchGcdExtM k = bench ("gcdExt/Mod2/" ++ show k) $ nf (doGcdExt @Mod2) k++benchGcdFracM :: Int -> Benchmark+benchGcdFracM k = bench ("gcdFrac/Mod2/" ++ show k) $ nf (doGcdFrac @Mod2) k+ #endif  doBinOp :: (forall a. Num a => a -> a -> a) -> Int -> Int@@ -83,25 +103,68 @@  #if MIN_VERSION_semirings(0,4,2) +gen1 :: Num a => Int -> a+gen1 k = fromIntegral (truncate (pi * fromIntegral k :: Double) `mod` (k + 1))++gen2 :: Num a => Int -> a+gen2 k = fromIntegral (truncate (exp 1.0 * fromIntegral k :: Double) `mod` (k + 1))+ doQuotRem :: Int -> Double doQuotRem n = U.sum (unPoly qs) + U.sum (unPoly rs)   where-    xs = toPoly $ U.generate (2 * n) ((+ 1.0) . (* 2.0) . fromIntegral)-    ys = toPoly $ U.generate n       ((+ 2.0) . (* 3.0) . fromIntegral)+    xs = toPoly $ U.generate (2 * n) gen1+    ys = toPoly $ U.generate n       gen2     (qs, rs) = xs `quotRem` ys  doGcd :: Int -> Integer doGcd n = V.sum gs   where-    xs = toPoly $ V.generate n ((+ 1) . (* 2) . fromIntegral)-    ys = toPoly $ V.generate n ((+ 2) . (* 3) . fromIntegral)+    xs = toPoly $ V.generate n gen1+    ys = toPoly $ V.generate n gen2     gs = unPoly $ xs `gcd` ys -doGcdFrac :: Int -> Rational+doGcdExt :: (Eq a, Num a, Field a) => Int -> a+doGcdExt n = V.sum gs+  where+    xs = toPoly $ V.generate n gen1+    ys = toPoly $ V.generate n gen2+    gs = unPoly $ fst $ xs `gcdExt` ys++doGcdFrac :: (Eq a, Num a, Field a) => Int -> a doGcdFrac n = V.sum gs   where-    xs = PolyOverFractional $ toPoly $ V.generate n ((+ 1) . (* 2) . fromIntegral)-    ys = PolyOverFractional $ toPoly $ V.generate n ((+ 2) . (* 3) . fromIntegral)-    gs = unPoly $ unPolyOverFractional $ xs `gcd` ys+    xs = PolyOverField $ toPoly $ V.generate n gen1+    ys = PolyOverField $ toPoly $ V.generate n gen2+    gs = unPoly $ unPolyOverField $ xs `gcd` ys++-- | Inspired by 'semirings'.+newtype Mod2 = Mod2 { _getMod2 :: Bool }+  deriving (Eq, NFData)++instance Num Mod2 where+  (+) = coerce (xor @Bool)+  (*) = coerce (&&)+  negate = id+  abs    = id+  signum = id+  fromInteger = Mod2 . odd++instance Semiring Mod2 where+  plus  = coerce (xor @Bool)+  times = coerce (&&)+  fromNatural = Mod2 . odd++instance Ring Mod2 where+  negate = id++instance GcdDomain Mod2 where++instance Euclidean Mod2 where+  degree = const 0+  quotRem x y+    | isZero y  = throw DivideByZero+    | otherwise = (x, zero)++instance Field Mod2  #endif
bench/SparseBench.hs view
@@ -67,4 +67,3 @@ doIntegral xs = U.foldl' (\acc (_, x) -> acc + x) 0 zs   where     zs = unPoly $ integral xs-
changelog.md view
@@ -1,3 +1,10 @@+# 0.3.2.0++* Add `NFData` instance.+* Implement extended GCD.+* Rename `PolyOverFractional` to `PolyOverField`.+* Add `integral` with `Semiring`-based interface.+ # 0.3.1.0  * Implement Karatsuba multiplication.
poly.cabal view
@@ -1,5 +1,5 @@ name: poly-version: 0.3.1.0+version: 0.3.2.0 synopsis: Polynomials description:   Polynomials backed by `Vector`.@@ -30,14 +30,15 @@     Data.Poly.Sparse.Semiring   other-modules:     Data.Poly.Internal.Dense-    Data.Poly.Internal.Dense.Fractional+    Data.Poly.Internal.Dense.Field     Data.Poly.Internal.Dense.GcdDomain-    Data.Poly.Internal.PolyOverFractional+    Data.Poly.Internal.PolyOverField     Data.Poly.Internal.Sparse-    Data.Poly.Internal.Sparse.Fractional+    Data.Poly.Internal.Sparse.Field     Data.Poly.Internal.Sparse.GcdDomain   build-depends:     base >= 4.9 && < 5,+    deepseq >= 1.1 && < 1.5,     primitive >= 0.6,     semirings >= 0.2,     vector >= 0.12.0.2,@@ -68,6 +69,7 @@ benchmark poly-gauge   build-depends:     base >=4.9 && <5,+    deepseq >= 1.1 && < 1.5,     gauge >= 0.1,     poly,     semirings >= 0.2,
src/Data/Poly.hs view
@@ -7,10 +7,8 @@ -- Dense polynomials and a 'Num'-based interface. -- -{-# LANGUAGE CPP                        #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE PatternSynonyms            #-}+{-# LANGUAGE CPP             #-}+{-# LANGUAGE PatternSynonyms #-}  module Data.Poly   ( Poly@@ -27,14 +25,18 @@   , deriv   , integral #if MIN_VERSION_semirings(0,4,2)-  -- * Fractional coefficients-  , PolyOverFractional(..)+  -- * Polynomials over 'Field'+  , PolyOverField(..)+  , gcdExt+  , PolyOverFractional+  , pattern PolyOverFractional+  , unPolyOverFractional #endif   ) where  import Data.Poly.Internal.Dense #if MIN_VERSION_semirings(0,4,2)-import Data.Poly.Internal.Dense.Fractional ()+import Data.Poly.Internal.Dense.Field (gcdExt) import Data.Poly.Internal.Dense.GcdDomain ()-import Data.Poly.Internal.PolyOverFractional+import Data.Poly.Internal.PolyOverField #endif
src/Data/Poly/Internal/Dense.hs view
@@ -36,14 +36,19 @@   , pattern X'   , eval'   , deriv'+#if MIN_VERSION_semirings(0,5,0)+  , integral'+#endif   ) where -import Prelude hiding (quotRem, rem, gcd, lcm, (^))+import Prelude hiding (quotRem, quot, rem, gcd, lcm, (^))+import Control.DeepSeq (NFData) import Control.Monad import Control.Monad.Primitive import Control.Monad.ST+import Data.Bits import Data.List (foldl', intersperse)-import Data.Semiring (Semiring(..))+import Data.Semiring (Semiring(..), Ring()) import qualified Data.Semiring as Semiring import qualified Data.Vector as V import qualified Data.Vector.Generic as G@@ -54,6 +59,9 @@ import Data.Semigroup import Numeric.Natural #endif+#if MIN_VERSION_semirings(0,5,0)+import Data.Euclidean (Field, quot)+#endif  -- | Polynomials of one variable with coefficients from @a@, -- backed by a 'G.Vector' @v@ (boxed, unboxed, storable, etc.).@@ -76,7 +84,7 @@   -- ^ Convert 'Poly' to a vector of coefficients   -- (first element corresponds to a constant term).   }-  deriving (Eq, Ord)+  deriving (Eq, NFData, Ord)  instance (Eq a, Semiring a, G.Vector v a) => IsList (Poly v a) where   type Item (Poly v a) = a@@ -139,7 +147,7 @@   abs = id   signum = const 1   fromInteger n = case fromInteger n of-    0 -> Poly $ G.empty+    0 -> Poly G.empty     m -> Poly $ G.singleton m   Poly xs * Poly ys = toPoly $ karatsuba xs ys   {-# INLINE (+) #-}@@ -168,7 +176,7 @@   {-# INLINE fromNatural #-} #endif -instance (Eq a, Semiring.Ring a, G.Vector v a) => Semiring.Ring (Poly v a) where+instance (Eq a, Ring a, G.Vector v a) => Ring (Poly v a) where   negate (Poly xs) = Poly $ G.map Semiring.negate xs  dropWhileEnd@@ -176,7 +184,7 @@   => (a -> Bool)   -> v a   -> v a-dropWhileEnd p xs = G.basicUnsafeSlice 0 (go (G.basicLength xs)) xs+dropWhileEnd p xs = G.unsafeSlice 0 (go (G.length xs)) xs   where     go 0 = 0     go n = if p (G.unsafeIndex xs (n - 1)) then go (n - 1) else n@@ -187,12 +195,12 @@   => (a -> Bool)   -> G.Mutable v (PrimState m) a   -> m (G.Mutable v (PrimState m) a)-dropWhileEndM p xs = go (MG.basicLength xs)+dropWhileEndM p xs = go (MG.length xs)   where-    go 0 = pure $ MG.basicUnsafeSlice 0 0 xs+    go 0 = pure $ MG.unsafeSlice 0 0 xs     go n = do       x <- MG.unsafeRead xs (n - 1)-      if p x then go (n - 1) else pure (MG.basicUnsafeSlice 0 n xs)+      if p x then go (n - 1) else pure (MG.unsafeSlice 0 n xs) {-# INLINE dropWhileEndM #-}  plusPoly@@ -202,17 +210,17 @@   -> v a   -> v a plusPoly add xs ys = runST $ do-  let lenXs = G.basicLength xs-      lenYs = G.basicLength ys+  let lenXs = G.length xs+      lenYs = G.length ys       lenMn = lenXs `min` lenYs       lenMx = lenXs `max` lenYs -  zs <- MG.basicUnsafeNew lenMx+  zs <- MG.unsafeNew lenMx   forM_ [0 .. lenMn - 1] $ \i ->     MG.unsafeWrite zs i (add (G.unsafeIndex xs i) (G.unsafeIndex ys i))   G.unsafeCopy-    (MG.basicUnsafeSlice lenMn (lenMx - lenMn) zs)-    (G.basicUnsafeSlice  lenMn (lenMx - lenMn) (if lenXs <= lenYs then ys else xs))+    (MG.unsafeSlice lenMn (lenMx - lenMn) zs)+    (G.unsafeSlice  lenMn (lenMx - lenMn) (if lenXs <= lenYs then ys else xs))    G.unsafeFreeze zs {-# INLINE plusPoly #-}@@ -225,12 +233,12 @@   -> v a   -> v a minusPoly neg sub xs ys = runST $ do-  let lenXs = G.basicLength xs-      lenYs = G.basicLength ys+  let lenXs = G.length xs+      lenYs = G.length ys       lenMn = lenXs `min` lenYs       lenMx = lenXs `max` lenYs -  zs <- MG.basicUnsafeNew lenMx+  zs <- MG.unsafeNew lenMx   forM_ [0 .. lenMn - 1] $ \i ->     MG.unsafeWrite zs i (sub (G.unsafeIndex xs i) (G.unsafeIndex ys i)) @@ -238,8 +246,8 @@     then forM_ [lenXs .. lenYs - 1] $ \i ->       MG.unsafeWrite zs i (neg (G.unsafeIndex ys i))     else G.unsafeCopy-      (MG.basicUnsafeSlice lenYs (lenXs - lenYs) zs)-      (G.basicUnsafeSlice  lenYs (lenXs - lenYs) xs)+      (MG.unsafeSlice lenYs (lenXs - lenYs) zs)+      (G.unsafeSlice  lenYs (lenXs - lenYs) xs)    G.unsafeFreeze zs {-# INLINE minusPoly #-}@@ -256,31 +264,31 @@   | lenXs <= karatsubaThreshold || lenYs <= karatsubaThreshold   = convolution 0 (+) (*) xs ys   | otherwise = runST $ do-    zs <- MG.basicUnsafeNew lenZs+    zs <- MG.unsafeNew lenZs     forM_ [0 .. lenZs - 1] $ \k -> do-      let z0 = if k < G.basicLength zs0+      let z0 = if k < G.length zs0                then G.unsafeIndex zs0 k                else 0-          z11 = if k - m >= 0 && k - m < G.basicLength zs11+          z11 = if k - m >= 0 && k - m < G.length zs11                then G.unsafeIndex zs11 (k - m)                else 0-          z10 = if k - m >= 0 && k - m < G.basicLength zs0+          z10 = if k - m >= 0 && k - m < G.length zs0                then G.unsafeIndex zs0 (k - m)                else 0-          z12 = if k - m >= 0 && k - m < G.basicLength zs2+          z12 = if k - m >= 0 && k - m < G.length zs2                then G.unsafeIndex zs2 (k - m)                else 0-          z2 = if k - 2 * m >= 0 && k - 2 * m < G.basicLength zs2+          z2 = if k - 2 * m >= 0 && k - 2 * m < G.length zs2                then G.unsafeIndex zs2 (k - 2 * m)                else 0       MG.unsafeWrite zs k (z0 + (z11 - z10 - z12) + z2)     G.unsafeFreeze zs   where-    lenXs = G.basicLength xs-    lenYs = G.basicLength ys+    lenXs = G.length xs+    lenYs = G.length ys     lenZs = lenXs + lenYs - 1 -    m    = ((lenXs `min` lenYs) + 1) `quot` 2+    m    = ((lenXs `min` lenYs) + 1) `shiftR` 1      xs0  = G.slice 0 m xs     xs1  = G.slice m (lenXs - m) xs@@ -303,18 +311,15 @@   -> v a   -> v a convolution zer add mul xs ys-  | G.null xs || G.null ys = G.empty-  | otherwise = runST $ do-    let lenXs = G.basicLength xs-        lenYs = G.basicLength ys-        lenZs = lenXs + lenYs - 1-    zs <- MG.basicUnsafeNew lenZs-    forM_ [0 .. lenZs - 1] $ \k -> do-      let is = [max (k - lenYs + 1) 0 .. min k (lenXs - 1)]-          acc = foldl' add zer $ flip map is $ \i ->-            mul (G.unsafeIndex xs i) (G.unsafeIndex ys (k - i))-      MG.unsafeWrite zs k acc-    G.unsafeFreeze zs+  | lenXs == 0 || lenYs == 0 = G.empty+  | otherwise = G.generate lenZs $ \k -> foldl'+    (\acc i -> acc `add` mul (G.unsafeIndex xs i) (G.unsafeIndex ys (k - i)))+    zer+    [max (k - lenYs + 1) 0 .. min k (lenXs - 1)]+  where+    lenXs = G.length xs+    lenYs = G.length ys+    lenZs = lenXs + lenYs - 1 {-# INLINE convolution #-}  -- | Create a monomial from a power and a coefficient.@@ -338,8 +343,8 @@   -> v a   -> v a scaleInternal zer mul yp yc xs = runST $ do-  let lenXs = G.basicLength xs-  zs <- MG.basicUnsafeNew (fromIntegral yp + lenXs)+  let lenXs = G.length xs+  zs <- MG.unsafeNew (fromIntegral yp + lenXs)   forM_ [0 .. fromIntegral yp - 1] $ \k ->     MG.unsafeWrite zs k zer   forM_ [0 .. lenXs - 1] $ \k ->@@ -372,12 +377,12 @@ -- 1 * X^2 + 2 * X + 2 eval :: (Num a, G.Vector v a) => Poly v a -> a -> a eval (Poly cs) x = fst' $-  G.foldl' (\(acc :*: xn) cn -> (acc + cn * xn :*: x * xn)) (0 :*: 1) cs+  G.foldl' (\(acc :*: xn) cn -> acc + cn * xn :*: x * xn) (0 :*: 1) cs {-# INLINE eval #-}  eval' :: (Semiring a, G.Vector v a) => Poly v a -> a -> a eval' (Poly cs) x = fst' $-  G.foldl' (\(acc :*: xn) cn -> (acc `plus` cn `times` xn :*: x `times` xn)) (zero :*: one) cs+  G.foldl' (\(acc :*: xn) cn -> acc `plus` cn `times` xn :*: x `times` xn) (zero :*: one) cs {-# INLINE eval' #-}  -- | Take a derivative.@@ -416,14 +421,29 @@ integral (Poly xs)   | G.null xs = Poly G.empty   | otherwise = toPoly $ runST $ do-    zs <- MG.basicUnsafeNew (lenXs + 1)+    zs <- MG.unsafeNew (lenXs + 1)     MG.unsafeWrite zs 0 0     forM_ [0 .. lenXs - 1] $ \i ->       MG.unsafeWrite zs (i + 1) (G.unsafeIndex xs i * recip (fromIntegral i + 1))     G.unsafeFreeze zs     where-      lenXs = G.basicLength xs+      lenXs = G.length xs {-# INLINE integral #-}++#if MIN_VERSION_semirings(0,5,0)+integral' :: (Eq a, Field a, G.Vector v a) => Poly v a -> Poly v a+integral' (Poly xs)+  | G.null xs = Poly G.empty+  | otherwise = toPoly' $ runST $ do+    zs <- MG.unsafeNew (lenXs + 1)+    MG.unsafeWrite zs zero zero+    forM_ [0 .. lenXs - 1] $ \i ->+      MG.unsafeWrite zs (i + 1) (G.unsafeIndex xs i `quot` Semiring.fromIntegral (i + 1))+    G.unsafeFreeze zs+    where+      lenXs = G.length xs+{-# INLINE integral' #-}+#endif  -- | Create an identity polynomial. pattern X :: (Eq a, Num a, G.Vector v a, Eq (v a)) => Poly v a
+ src/Data/Poly/Internal/Dense/Field.hs view
@@ -0,0 +1,189 @@+-- |+-- Module:      Data.Poly.Internal.Dense.Field+-- Copyright:   (c) 2019 Andrew Lelechenko+-- Licence:     BSD3+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+--+-- GcdDomain for Field underlying+--++{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE CPP                        #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE PatternSynonyms            #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeFamilies               #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++#if MIN_VERSION_semirings(0,4,2)++module Data.Poly.Internal.Dense.Field+  ( fieldGcd+  , gcdExt+  ) where++import Prelude hiding (quotRem, quot, rem, gcd)+import Control.Exception+import Control.Monad+import Control.Monad.Primitive+import Control.Monad.ST+import Data.Euclidean+#if !MIN_VERSION_semirings(0,5,0)+import Data.Semiring (Ring)+#endif+import Data.Semiring (times, minus, zero, one)+import qualified Data.Vector.Generic as G+import qualified Data.Vector.Generic.Mutable as MG++import Data.Poly.Internal.Dense+import Data.Poly.Internal.Dense.GcdDomain ()++#if !MIN_VERSION_semirings(0,5,0)+type Field a = (Euclidean a, Ring a, Fractional a)+#endif++instance (Eq a, Eq (v a), Field a, G.Vector v a) => Euclidean (Poly v a) where+  degree (Poly xs) = fromIntegral (G.length xs)++  quotRem (Poly xs) (Poly ys) = (toPoly' qs, toPoly' rs)+    where+      (qs, rs) = quotientAndRemainder xs ys+  {-# INLINE quotRem #-}++  rem (Poly xs) (Poly ys) = toPoly' $ remainder xs ys+  {-# INLINE rem #-}++quotientAndRemainder+  :: (Field a, G.Vector v a)+  => v a+  -> v a+  -> (v a, v a)+quotientAndRemainder xs ys+  | G.null ys = throw DivideByZero+  | G.length xs < G.length ys = (G.empty, xs)+  | otherwise = runST $ do+    let lenXs = G.length xs+        lenYs = G.length ys+        lenQs = lenXs - lenYs + 1+    qs <- MG.unsafeNew lenQs+    rs <- MG.unsafeNew lenXs+    G.unsafeCopy rs xs+    forM_ [lenQs - 1, lenQs - 2 .. 0] $ \i -> do+      r <- MG.unsafeRead rs (lenYs - 1 + i)+      let q = r `quot` G.unsafeLast ys+      MG.unsafeWrite qs i q+      forM_ [0 .. lenYs - 1] $ \k -> do+        MG.unsafeModify rs (\c -> c `minus` q `times` G.unsafeIndex ys k) (i + k)+    let rs' = MG.unsafeSlice 0 lenYs rs+    (,) <$> G.unsafeFreeze qs <*> G.unsafeFreeze rs'+{-# INLINE quotientAndRemainder #-}++remainder+  :: (Field a, G.Vector v a)+  => v a+  -> v a+  -> v a+remainder xs ys+  | G.null ys = throw DivideByZero+  | otherwise = runST $ do+    rs <- G.thaw xs+    ys' <- G.unsafeThaw ys+    remainderM rs ys'+    G.unsafeFreeze $ MG.unsafeSlice 0 (G.length xs `min` G.length ys) rs+{-# INLINE remainder #-}++remainderM+  :: (PrimMonad m, Field a, G.Vector v a)+  => G.Mutable v (PrimState m) a+  -> G.Mutable v (PrimState m) a+  -> m ()+remainderM xs ys+  | MG.null ys = throw DivideByZero+  | MG.length xs < MG.length ys = pure ()+  | otherwise = do+    let lenXs = MG.length xs+        lenYs = MG.length ys+        lenQs = lenXs - lenYs + 1+    yLast <- MG.unsafeRead ys (lenYs - 1)+    forM_ [lenQs - 1, lenQs - 2 .. 0] $ \i -> do+      r <- MG.unsafeRead xs (lenYs - 1 + i)+      forM_ [0 .. lenYs - 1] $ \k -> do+        y <- MG.unsafeRead ys k+        -- do not move r / yLast outside the loop,+        -- because of numerical instability+        MG.unsafeModify xs (\c -> c `minus` r `times` y `quot` yLast) (i + k)+{-# INLINE remainderM #-}++fieldGcd+  :: (Eq a, Field a, G.Vector v a)+  => Poly v a+  -> Poly v a+  -> Poly v a+fieldGcd (Poly xs) (Poly ys) = toPoly' $ runST $ do+  xs' <- G.thaw xs+  ys' <- G.thaw ys+  gcdM xs' ys'+{-# INLINE fieldGcd #-}++gcdM+  :: (PrimMonad m, Eq a, Field a, G.Vector v a)+  => G.Mutable v (PrimState m) a+  -> G.Mutable v (PrimState m) a+  -> m (v a)+gcdM xs ys = do+  ys' <- dropWhileEndM (== zero) ys+  if MG.null ys' then G.unsafeFreeze xs else do+    remainderM xs ys'+    gcdM ys' xs+{-# INLINE gcdM #-}++-- | Execute the extended Euclidean algorithm.+-- For polynomials @a@ and @b@, compute their unique greatest common divisor @g@+-- and the unique coefficient polynomial @s@ satisfying @as + bt = g@,+-- such that either @g@ is monic, or @g = 0@ and @s@ is monic, or @g = s = 0@.+--+-- >>> gcdExt (X^2 + 1 :: UPoly Double) (X^3 + 3 * X :: UPoly Double)+-- (1.0, 0.5 * X^2 + (-0.0) * X + 1.0)+-- >>> gcdExt (X^3 + 3 * X :: UPoly Double) (3 * X^4 + 3 * X^2 :: UPoly Double)+-- (1.0 * X + 0.0,(-0.16666666666666666) * X^2 + (-0.0) * X + 0.3333333333333333)+gcdExt+  :: (Eq a, Field a, G.Vector v a, Eq (v a))+  => Poly v a+  -> Poly v a+  -> (Poly v a, Poly v a)+gcdExt xs ys = case scaleMonic gs of+  Just (c', gs') -> (gs', scale' zero c' ss)+  Nothing -> case scaleMonic ss of+    Just (_, ss') -> (zero, ss')+    Nothing -> (zero, zero)+  where+    (gs, ss) = go ys xs zero one+      where+        go r' r s' s+          | r' == zero = (r, s)+          | otherwise  = case r `quotRem` r' of+            (q, r'') -> go r'' r' (s `minus` q `times` s') s'+{-# INLINE gcdExt #-}++-- | Scale a non-zero polynomial such that its leading coefficient is one,+-- returning the reciprocal of the leading coefficient in the scaling.+--+-- >>> scaleMonic (X^3 + 3 * X :: UPoly Double)+-- Just (1.0, 1.0 * X^3 + 0.0 * X^2 + 3.0 * X + 0.0)+-- >>> scaleMonic (3 * X^4 + 3 * X^2 :: UPoly Double)+-- Just (0.3333333333333333, 1.0 * X^4 + 0.0 * X^3 + 1.0 * X^2 + 0.0 * X + 0.0)+scaleMonic+  :: (Eq a, Field a, G.Vector v a, Eq (v a))+  => Poly v a+  -> Maybe (a, Poly v a)+scaleMonic xs = case leading xs of+  Nothing -> Nothing+  Just (_, c) -> let c' = one `quot` c in Just (c', scale' zero c' xs)+{-# INLINE scaleMonic #-}++#else++module Data.Poly.Internal.Dense.Field () where++#endif
− src/Data/Poly/Internal/Dense/Fractional.hs
@@ -1,138 +0,0 @@--- |--- Module:      Data.Poly.Internal.Dense.Fractional--- Copyright:   (c) 2019 Andrew Lelechenko--- Licence:     BSD3--- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>------ GcdDomain for Fractional underlying-----{-# LANGUAGE CPP                        #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE PatternSynonyms            #-}-{-# LANGUAGE ScopedTypeVariables        #-}-{-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE ViewPatterns               #-}--{-# OPTIONS_GHC -fno-warn-orphans #-}--#if MIN_VERSION_semirings(0,4,2)--module Data.Poly.Internal.Dense.Fractional-  ( fractionalGcd-  ) where--import Prelude hiding (rem, gcd)-import Control.Exception-import Control.Monad-import Control.Monad.Primitive-import Control.Monad.ST-import Data.Euclidean-import qualified Data.Semiring as Semiring-import qualified Data.Vector.Generic as G-import qualified Data.Vector.Generic.Mutable as MG--import Data.Poly.Internal.Dense-import Data.Poly.Internal.Dense.GcdDomain ()--instance (Eq a, Eq (v a), Semiring.Ring a, GcdDomain a, Fractional a, G.Vector v a) => Euclidean (Poly v a) where-  degree (Poly xs) = fromIntegral (G.basicLength xs)--  quotRem (Poly xs) (Poly ys) = (toPoly qs, toPoly rs)-    where-      (qs, rs) = quotientAndRemainder xs ys-  {-# INLINE quotRem #-}--  rem (Poly xs) (Poly ys) = toPoly $ remainder xs ys-  {-# INLINE rem #-}--quotientAndRemainder-  :: (Fractional a, G.Vector v a)-  => v a-  -> v a-  -> (v a, v a)-quotientAndRemainder xs ys-  | G.null ys = throw DivideByZero-  | G.basicLength xs < G.basicLength ys = (G.empty, xs)-  | otherwise = runST $ do-    let lenXs = G.basicLength xs-        lenYs = G.basicLength ys-        lenQs = lenXs - lenYs + 1-    qs <- MG.basicUnsafeNew lenQs-    rs <- MG.basicUnsafeNew lenXs-    G.unsafeCopy rs xs-    forM_ [lenQs - 1, lenQs - 2 .. 0] $ \i -> do-      r <- MG.unsafeRead rs (lenYs - 1 + i)-      let q = r / G.unsafeLast ys-      MG.unsafeWrite qs i q-      forM_ [0 .. lenYs - 1] $ \k -> do-        MG.unsafeModify rs (\c -> c - q * G.unsafeIndex ys k) (i + k)-    let rs' = MG.basicUnsafeSlice 0 lenYs rs-    (,) <$> G.unsafeFreeze qs <*> G.unsafeFreeze rs'-{-# INLINE quotientAndRemainder #-}--remainder-  :: (Fractional a, G.Vector v a)-  => v a-  -> v a-  -> v a-remainder xs ys-  | G.null ys = throw DivideByZero-  | otherwise = runST $ do-    rs <- G.thaw xs-    ys' <- G.unsafeThaw ys-    remainderM rs ys'-    G.unsafeFreeze $ MG.basicUnsafeSlice 0 (G.basicLength xs `min` G.basicLength ys) rs-{-# INLINE remainder #-}--remainderM-  :: (PrimMonad m, Fractional a, G.Vector v a)-  => G.Mutable v (PrimState m) a-  -> G.Mutable v (PrimState m) a-  -> m ()-remainderM xs ys-  | MG.null ys = throw DivideByZero-  | MG.basicLength xs < MG.basicLength ys = pure ()-  | otherwise = do-    let lenXs = MG.basicLength xs-        lenYs = MG.basicLength ys-        lenQs = lenXs - lenYs + 1-    yLast <- MG.unsafeRead ys (lenYs - 1)-    forM_ [lenQs - 1, lenQs - 2 .. 0] $ \i -> do-      r <- MG.unsafeRead xs (lenYs - 1 + i)-      forM_ [0 .. lenYs - 1] $ \k -> do-        y <- MG.unsafeRead ys k-        -- do not move r / yLast outside the loop,-        -- because of numerical instability-        MG.unsafeModify xs (\c -> c - r * y / yLast) (i + k)-{-# INLINE remainderM #-}--fractionalGcd-  :: (Eq a, Fractional a, G.Vector v a)-  => Poly v a-  -> Poly v a-  -> Poly v a-fractionalGcd (Poly xs) (Poly ys) = toPoly $ runST $ do-  xs' <- G.thaw xs-  ys' <- G.thaw ys-  gcdM xs' ys'-{-# INLINE fractionalGcd #-}--gcdM-  :: (PrimMonad m, Eq a, Fractional a, G.Vector v a)-  => G.Mutable v (PrimState m) a-  -> G.Mutable v (PrimState m) a-  -> m (v a)-gcdM xs ys = do-  ys' <- dropWhileEndM (== 0) ys-  if MG.null ys' then G.unsafeFreeze xs else do-    remainderM xs ys'-    gcdM ys' xs-{-# INLINE gcdM #-}--#else--module Data.Poly.Internal.Dense.Fractional () where--#endif
src/Data/Poly/Internal/Dense/GcdDomain.hs view
@@ -9,11 +9,9 @@  {-# LANGUAGE CPP                        #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE PatternSynonyms            #-} {-# LANGUAGE ScopedTypeVariables        #-} {-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE ViewPatterns               #-}  {-# OPTIONS_GHC -fno-warn-orphans #-} @@ -28,18 +26,16 @@ import Control.Monad.Primitive import Control.Monad.ST import Data.Euclidean-import Data.Semiring (Semiring(..), isZero)-import qualified Data.Semiring as Semiring+import Data.Semiring (Semiring(..), Ring(), isZero, minus) import qualified Data.Vector.Generic as G import qualified Data.Vector.Generic.Mutable as MG  import Data.Poly.Internal.Dense --- | Consider using 'Data.Poly.Semiring.PolyOverFractional' wrapper,+-- | Consider using 'Data.Poly.Semiring.PolyOverField' wrapper, -- which provides a much faster implementation of--- 'Data.Euclidean.gcd' for 'Fractional'--- coefficients.-instance (Eq a, Semiring.Ring a, GcdDomain a, Eq (v a), G.Vector v a) => GcdDomain (Poly v a) where+-- 'Data.Euclidean.gcd' for polynomials over 'Field'.+instance (Eq a, Ring a, GcdDomain a, Eq (v a), G.Vector v a) => GcdDomain (Poly v a) where   divide (Poly xs) (Poly ys) =     toPoly' <$> quotient xs ys @@ -50,7 +46,7 @@   {-# INLINE gcd #-}  gcdNonEmpty-  :: (Eq a, Semiring.Ring a, GcdDomain a, G.Vector v a)+  :: (Eq a, Ring a, GcdDomain a, G.Vector v a)   => v a   -> v a   -> v a@@ -62,7 +58,7 @@     ys' <- G.thaw ys     zs' <- gcdM xs' ys' -    let lenZs = MG.basicLength zs'+    let lenZs = MG.length zs'         go acc 0 = pure acc         go acc n = do           t <- MG.unsafeRead zs' (n - 1)@@ -80,7 +76,7 @@     G.unsafeFreeze zs'  gcdM-  :: (PrimMonad m, Eq a, Semiring.Ring a, GcdDomain a, G.Vector v a)+  :: (PrimMonad m, Eq a, Ring a, GcdDomain a, G.Vector v a)   => G.Mutable v (PrimState m) a   -> G.Mutable v (PrimState m) a   -> m (G.Mutable v (PrimState m) a)@@ -88,8 +84,8 @@   | MG.null xs = pure ys   | MG.null ys = pure xs   | otherwise = do-  let lenXs = MG.basicLength xs-      lenYs = MG.basicLength ys+  let lenXs = MG.length xs+      lenYs = MG.length ys   xLast <- MG.unsafeRead xs (lenXs - 1)   yLast <- MG.unsafeRead ys (lenYs - 1)   let z = xLast `lcm` yLast@@ -105,7 +101,7 @@       x <- MG.unsafeRead xs i       MG.unsafeModify         ys-        (\y -> (y `times` zy) `plus` Semiring.negate (x `times` zx))+        (\y -> (y `times` zy) `minus` x `times` zx)         (i + lenYs - lenXs)     forM_ [0 .. lenYs - lenXs - 1] $       MG.unsafeModify ys (`times` zy)@@ -116,7 +112,7 @@       y <- MG.unsafeRead ys i       MG.unsafeModify         xs-        (\x -> (x `times` zx) `plus` Semiring.negate (y `times` zy))+        (\x -> (x `times` zx) `minus` y `times` zy)         (i + lenXs - lenYs)     forM_ [0 .. lenXs - lenYs - 1] $       MG.unsafeModify xs (`times` zx)@@ -128,7 +124,7 @@   :: (Eq a, Semiring a, PrimMonad m, G.Vector v a)   => G.Mutable v (PrimState m) a   -> m Bool-isZeroM xs = go (MG.basicLength xs)+isZeroM xs = go (MG.length xs)   where     go 0 = pure True     go n = do@@ -137,20 +133,20 @@ {-# INLINE isZeroM #-}  quotient-  :: (Eq a, Eq (v a), Semiring.Ring a, GcdDomain a, G.Vector v a)+  :: (Eq a, Eq (v a), Ring a, GcdDomain a, G.Vector v a)   => v a   -> v a   -> Maybe (v a) quotient xs ys   | G.null ys = throw DivideByZero   | G.null xs = Just xs-  | G.basicLength xs < G.basicLength ys = Nothing+  | G.length xs < G.length ys = Nothing   | otherwise = runST $ do-    let lenXs = G.basicLength xs-        lenYs = G.basicLength ys+    let lenXs = G.length xs+        lenYs = G.length ys         lenQs = lenXs - lenYs + 1-    qs <- MG.basicUnsafeNew lenQs-    rs <- MG.basicUnsafeNew lenXs+    qs <- MG.unsafeNew lenQs+    rs <- MG.unsafeNew lenXs     G.unsafeCopy rs xs      let go i@@ -168,7 +164,7 @@                 forM_ [0 .. lenYs - 1] $ \k -> do                   MG.unsafeModify                     rs-                    (\c -> c `plus` (Semiring.negate $ q `times` G.unsafeIndex ys k))+                    (\c -> c `minus` q `times` G.unsafeIndex ys k)                     (i + k)                 go (i - 1) 
+ src/Data/Poly/Internal/PolyOverField.hs view
@@ -0,0 +1,75 @@+-- |+-- Module:      Data.Poly.Internal.PolyOverField+-- Copyright:   (c) 2019 Andrew Lelechenko+-- Licence:     BSD3+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+--+-- Wrapper with a more efficient 'Euclidean' instance.+--++{-# LANGUAGE CPP                        #-}+{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE PatternSynonyms            #-}++#if MIN_VERSION_semirings(0,4,2)++module Data.Poly.Internal.PolyOverField+  ( PolyOverField(..)+  , PolyOverFractional+  , pattern PolyOverFractional+  , unPolyOverFractional+  ) where++import Prelude hiding (quotRem, quot, rem, gcd, lcm, (^))+import Control.DeepSeq (NFData)+import Data.Euclidean+import Data.Semiring+import qualified Data.Vector.Generic as G++import qualified Data.Poly.Internal.Dense as Dense+import qualified Data.Poly.Internal.Dense.Field as Dense (fieldGcd)++-- | Wrapper for polynomials over 'Field',+-- providing a faster 'GcdDomain' instance.+newtype PolyOverField poly = PolyOverField { unPolyOverField :: poly }+  deriving (Eq, NFData, Num, Ord, Ring, Semiring, Show)++-- |+type PolyOverFractional = PolyOverField+{-# DEPRECATED PolyOverFractional "Use 'PolyOverField'" #-}++-- |+pattern PolyOverFractional :: poly -> PolyOverField poly+pattern PolyOverFractional poly = PolyOverField poly++-- |+unPolyOverFractional :: PolyOverField poly -> poly+unPolyOverFractional = unPolyOverField+{-# DEPRECATED unPolyOverFractional "Use 'unPolyOverField'" #-}++#if !MIN_VERSION_semirings(0,5,0)+type Field a = (Euclidean a, Ring a, Fractional a)+#endif++instance (Eq a, Eq (v a), Field a, G.Vector v a) => GcdDomain (PolyOverField (Dense.Poly v a)) where+  gcd (PolyOverField x) (PolyOverField y) = PolyOverField (Dense.fieldGcd x y)+  {-# INLINE gcd #-}++instance (Eq a, Eq (v a), Field a, G.Vector v a) => Euclidean (PolyOverField (Dense.Poly v a)) where+  degree (PolyOverField x) =+    degree x+  quotRem (PolyOverField x) (PolyOverField y) =+    let (q, r) = quotRem x y in+      (PolyOverField q, PolyOverField r)+  {-# INLINE quotRem #-}+  rem (PolyOverField x) (PolyOverField y) =+    PolyOverField (rem x y)+  {-# INLINE rem #-}++#else++module Data.Poly.Internal.PolyOverField () where++#endif
− src/Data/Poly/Internal/PolyOverFractional.hs
@@ -1,55 +0,0 @@--- |--- Module:      Data.Poly.Internal.PolyOverFractional--- Copyright:   (c) 2019 Andrew Lelechenko--- Licence:     BSD3--- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>------ Wrapper with a more efficient 'Euclidean' instance.-----{-# LANGUAGE CPP                        #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE UndecidableInstances       #-}--#if MIN_VERSION_semirings(0,4,2)--module Data.Poly.Internal.PolyOverFractional-  ( PolyOverFractional(..)-  ) where--import Prelude hiding (quotRem, quot, rem, gcd, lcm, (^))-import Data.Euclidean-import Data.Semiring-import qualified Data.Semiring as Semiring-import qualified Data.Vector.Generic as G--import qualified Data.Poly.Internal.Dense as Dense-import qualified Data.Poly.Internal.Dense.Fractional as Dense (fractionalGcd)---- | Wrapper over polynomials,--- providing a faster 'GcdDomain' instance,--- when coefficients are 'Fractional'.-newtype PolyOverFractional poly = PolyOverFractional { unPolyOverFractional :: poly }-  deriving (Eq, Ord, Show, Num, Semiring, Semiring.Ring)--instance (Eq a, Eq (v a), Semiring.Ring a, GcdDomain a, Fractional a, G.Vector v a) => GcdDomain (PolyOverFractional (Dense.Poly v a)) where-  gcd (PolyOverFractional x) (PolyOverFractional y) = PolyOverFractional (Dense.fractionalGcd x y)-  {-# INLINE gcd #-}--instance (Eq a, Eq (v a), Semiring.Ring a, GcdDomain a, Fractional a, G.Vector v a) => Euclidean (PolyOverFractional (Dense.Poly v a)) where-  degree (PolyOverFractional x) =-    degree x-  quotRem (PolyOverFractional x) (PolyOverFractional y) =-    let (q, r) = quotRem x y in-      (PolyOverFractional q, PolyOverFractional r)-  {-# INLINE quotRem #-}-  rem (PolyOverFractional x) (PolyOverFractional y) =-    PolyOverFractional (rem x y)-  {-# INLINE rem #-}--#else--module Data.Poly.Internal.PolyOverFractional () where--#endif
src/Data/Poly/Internal/Sparse.hs view
@@ -7,14 +7,15 @@ -- Sparse polynomials of one variable. -- -{-# LANGUAGE CPP                  #-}-{-# LANGUAGE FlexibleContexts     #-}-{-# LANGUAGE PatternSynonyms      #-}-{-# LANGUAGE ScopedTypeVariables  #-}-{-# LANGUAGE StandaloneDeriving   #-}-{-# LANGUAGE TypeFamilies         #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ViewPatterns         #-}+{-# LANGUAGE CPP                        #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE PatternSynonyms            #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE StandaloneDeriving         #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE UndecidableInstances       #-}+{-# LANGUAGE ViewPatterns               #-}  module Data.Poly.Internal.Sparse   ( Poly(..)@@ -36,14 +37,20 @@   , pattern X'   , eval'   , deriv'+#if MIN_VERSION_semirings(0,5,0)+  , integral'+#endif   ) where +import Prelude hiding (quot)+import Control.DeepSeq (NFData) import Control.Monad import Control.Monad.Primitive import Control.Monad.ST+import Data.Bits import Data.List (intersperse) import Data.Ord-import Data.Semiring (Semiring(..))+import Data.Semiring (Semiring(..), Ring()) import qualified Data.Semiring as Semiring import qualified Data.Vector as V import qualified Data.Vector.Generic as G@@ -55,6 +62,9 @@ import Data.Semigroup import Numeric.Natural #endif+#if MIN_VERSION_semirings(0,5,0)+import Data.Euclidean (Field, quot)+#endif  -- | Polynomials of one variable with coefficients from @a@, -- backed by a 'G.Vector' @v@ (boxed, unboxed, storable, etc.).@@ -78,8 +88,9 @@   -- (first element corresponds to a constant term).   } -deriving instance Eq   (v (Word, a)) => Eq   (Poly v a)-deriving instance Ord  (v (Word, a)) => Ord  (Poly v a)+deriving instance Eq     (v (Word, a)) => Eq     (Poly v a)+deriving instance Ord    (v (Word, a)) => Ord    (Poly v a)+deriving instance NFData (v (Word, a)) => NFData (Poly v a)  instance (Eq a, Semiring a, G.Vector v (Word, a)) => IsList (Poly v a) where   type Item (Poly v a) = (Word, a)@@ -143,7 +154,7 @@   | otherwise = runST $ do     ws <- G.thaw vs     l' <- normalizeM p add ws-    G.unsafeFreeze $ MG.basicUnsafeSlice 0 l' ws+    G.unsafeFreeze $ MG.unsafeSlice 0 l' ws  normalizeM   :: (PrimMonad m, G.Vector v (Word, a))@@ -152,9 +163,9 @@   -> G.Mutable v (PrimState m) (Word, a)   -> m Int normalizeM p add ws = do-    let l = MG.basicLength ws+    let l = MG.length ws     let go i j acc@(accP, accC)-          | j >= l = do+          | j >= l =             if p accC               then do                 MG.write ws i acc@@ -181,7 +192,7 @@   abs = id   signum = const 1   fromInteger n = case fromInteger n of-    0 -> Poly $ G.empty+    0 -> Poly G.empty     m -> Poly $ G.singleton (0, m)   Poly xs * Poly ys = Poly $ convolution (/= 0) (+) (*) xs ys   {-# INLINE (+) #-}@@ -210,7 +221,7 @@   {-# INLINE fromNatural #-} #endif -instance (Eq a, Semiring.Ring a, G.Vector v (Word, a)) => Semiring.Ring (Poly v a) where+instance (Eq a, Ring a, G.Vector v (Word, a)) => Ring (Poly v a) where   negate (Poly xs) = Poly $ G.map (fmap Semiring.negate) xs  plusPoly@@ -221,9 +232,9 @@   -> v (Word, a)   -> v (Word, a) plusPoly p add xs ys = runST $ do-  zs <- MG.basicUnsafeNew (G.basicLength xs + G.basicLength ys)+  zs <- MG.unsafeNew (G.length xs + G.length ys)   lenZs <- plusPolyM p add xs ys zs-  G.unsafeFreeze $ MG.basicUnsafeSlice 0 lenZs zs+  G.unsafeFreeze $ MG.unsafeSlice 0 lenZs zs {-# INLINE plusPoly #-}  plusPolyM@@ -236,20 +247,20 @@   -> m Int plusPolyM p add xs ys zs = go 0 0 0   where-    lenXs = G.basicLength xs-    lenYs = G.basicLength ys+    lenXs = G.length xs+    lenYs = G.length ys      go ix iy iz       | ix == lenXs, iy == lenYs = pure iz       | ix == lenXs = do         G.unsafeCopy-          (MG.basicUnsafeSlice iz (lenYs - iy) zs)-          (G.basicUnsafeSlice iy (lenYs - iy) ys)+          (MG.unsafeSlice iz (lenYs - iy) zs)+          (G.unsafeSlice iy (lenYs - iy) ys)         pure $ iz + lenYs - iy       | iy == lenYs = do         G.unsafeCopy-          (MG.basicUnsafeSlice iz (lenXs - ix) zs)-          (G.basicUnsafeSlice ix (lenXs - ix) xs)+          (MG.unsafeSlice iz (lenXs - ix) zs)+          (G.unsafeSlice ix (lenXs - ix) xs)         pure $ iz + lenXs - ix       | (xp, xc) <- G.unsafeIndex xs ix       , (yp, yc) <- G.unsafeIndex ys iy@@ -278,7 +289,7 @@   -> v (Word, a)   -> v (Word, a) minusPoly p neg sub xs ys = runST $ do-  zs <- MG.basicUnsafeNew (lenXs + lenYs)+  zs <- MG.unsafeNew (lenXs + lenYs)   let go ix iy iz         | ix == lenXs, iy == lenYs = pure iz         | ix == lenXs = do@@ -288,8 +299,8 @@           pure $ iz + lenYs - iy         | iy == lenYs = do           G.unsafeCopy-            (MG.basicUnsafeSlice iz (lenXs - ix) zs)-            (G.basicUnsafeSlice ix (lenXs - ix) xs)+            (MG.unsafeSlice iz (lenXs - ix) zs)+            (G.unsafeSlice ix (lenXs - ix) xs)           pure $ iz + lenXs - ix         | (xp, xc) <- G.unsafeIndex xs ix         , (yp, yc) <- G.unsafeIndex ys iy@@ -308,10 +319,10 @@             MG.unsafeWrite zs iz (yp, neg yc)             go ix (iy + 1) (iz + 1)   lenZs <- go 0 0 0-  G.unsafeFreeze $ MG.basicUnsafeSlice 0 lenZs zs+  G.unsafeFreeze $ MG.unsafeSlice 0 lenZs zs   where-    lenXs = G.basicLength xs-    lenYs = G.basicLength ys+    lenXs = G.length xs+    lenYs = G.length ys {-# INLINE minusPoly #-}  scaleM@@ -324,7 +335,7 @@   -> m Int scaleM p mul xs (yp, yc) zs = go 0 0   where-    lenXs = G.basicLength xs+    lenXs = G.length xs      go ix iz       | ix == lenXs = pure iz@@ -347,9 +358,9 @@   -> Poly v a   -> Poly v a scaleInternal p mul yp yc (Poly xs) = runST $ do-  zs <- MG.basicUnsafeNew (G.basicLength xs)+  zs <- MG.unsafeNew (G.length xs)   len <- scaleM p (flip mul) xs (yp, yc) zs-  fmap Poly $ G.unsafeFreeze $ MG.basicUnsafeSlice 0 len zs+  fmap Poly $ G.unsafeFreeze $ MG.unsafeSlice 0 len zs {-# INLINE scaleInternal #-}  -- | Multiply a polynomial by a monomial, expressed as a power and a coefficient.@@ -372,29 +383,29 @@   -> v (Word, a)   -> v (Word, a) convolution p add mult xs ys-  | G.basicLength xs >= G.basicLength ys+  | G.length xs >= G.length ys   = go mult xs ys   | otherwise   = go (flip mult) ys xs   where     go :: (a -> a -> a) -> v (Word, a) -> v (Word, a) -> v (Word, a)     go mul long short = runST $ do-      let lenLong   = G.basicLength long-          lenShort  = G.basicLength short+      let lenLong   = G.length long+          lenShort  = G.length short           lenBuffer = lenLong * lenShort-      slices <- MG.basicUnsafeNew lenShort-      buffer <- MG.basicUnsafeNew lenBuffer+      slices <- MG.unsafeNew lenShort+      buffer <- MG.unsafeNew lenBuffer        forM_ [0 .. lenShort - 1] $ \iShort -> do         let (pShort, cShort) = G.unsafeIndex short iShort             from = iShort * lenLong-            bufferSlice = MG.basicUnsafeSlice from lenLong buffer+            bufferSlice = MG.unsafeSlice from lenLong buffer         len <- scaleM p mul long (pShort, cShort) bufferSlice         MG.unsafeWrite slices iShort (from, len)        slices' <- G.unsafeFreeze slices       buffer' <- G.unsafeFreeze buffer-      bufferNew <- MG.basicUnsafeNew lenBuffer+      bufferNew <- MG.unsafeNew lenBuffer       gogo slices' buffer' bufferNew      gogo@@ -404,29 +415,29 @@       -> G.Mutable v (PrimState m) (Word, a)       -> m (v (Word, a))     gogo slices buffer bufferNew-      | G.basicLength slices == 0+      | G.length slices == 0       = pure G.empty-      | G.basicLength slices == 1+      | G.length slices == 1       , (from, len) <- G.unsafeIndex slices 0-      = pure $ G.basicUnsafeSlice from len buffer+      = pure $ G.unsafeSlice from len buffer       | otherwise = do-        let nSlices = G.basicLength slices-        slicesNew <- MG.basicUnsafeNew ((nSlices + 1) `quot` 2)-        forM_ [0 .. (nSlices - 2) `quot` 2] $ \i -> do+        let nSlices = G.length slices+        slicesNew <- MG.unsafeNew ((nSlices + 1) `shiftR` 1)+        forM_ [0 .. (nSlices - 2) `shiftR` 1] $ \i -> do           let (from1, len1) = G.unsafeIndex slices (2 * i)               (from2, len2) = G.unsafeIndex slices (2 * i + 1)-              slice1 = G.basicUnsafeSlice from1 len1 buffer-              slice2 = G.basicUnsafeSlice from2 len2 buffer-              slice3 = MG.basicUnsafeSlice from1 (len1 + len2) bufferNew+              slice1 = G.unsafeSlice from1 len1 buffer+              slice2 = G.unsafeSlice from2 len2 buffer+              slice3 = MG.unsafeSlice from1 (len1 + len2) bufferNew           len3 <- plusPolyM p add slice1 slice2 slice3           MG.unsafeWrite slicesNew i (from1, len3)          when (odd nSlices) $ do           let (from, len) = G.unsafeIndex slices (nSlices - 1)-              slice1 = G.basicUnsafeSlice from len buffer-              slice3 = MG.basicUnsafeSlice from len bufferNew+              slice1 = G.unsafeSlice from len buffer+              slice3 = MG.unsafeSlice from len bufferNew           G.unsafeCopy slice3 slice1-          MG.unsafeWrite slicesNew (nSlices `quot` 2) (from, len)+          MG.unsafeWrite slicesNew (nSlices `shiftR` 1) (from, len)          slicesNew' <- G.unsafeFreeze slicesNew         buffer'    <- G.unsafeThaw   buffer@@ -509,8 +520,8 @@ derivPoly p mul xs   | G.null xs = G.empty   | otherwise = runST $ do-    let lenXs = G.basicLength xs-    zs <- MG.basicUnsafeNew lenXs+    let lenXs = G.length xs+    zs <- MG.unsafeNew lenXs     let go ix iz           | ix == lenXs = pure iz           | (xp, xc) <- G.unsafeIndex xs ix@@ -522,7 +533,7 @@             else               go (ix + 1) iz     lenZs <- go 0 0-    G.unsafeFreeze $ MG.basicUnsafeSlice 0 lenZs zs+    G.unsafeFreeze $ MG.unsafeSlice 0 lenZs zs {-# INLINE derivPoly #-}  -- | Compute an indefinite integral of a polynomial,@@ -535,6 +546,14 @@   = Poly   $ G.map (\(p, c) -> (p + 1, c / (fromIntegral p + 1))) xs {-# INLINE integral #-}++#if MIN_VERSION_semirings(0,5,0)+integral' :: (Eq a, Field a, G.Vector v (Word, a)) => Poly v a -> Poly v a+integral' (Poly xs)+  = Poly+  $ G.map (\(p, c) -> (p + 1, c `quot` Semiring.fromIntegral (p + 1))) xs+{-# INLINE integral' #-}+#endif  -- | Create an identity polynomial. pattern X :: (Eq a, Num a, G.Vector v (Word, a), Eq (v (Word, a))) => Poly v a
+ src/Data/Poly/Internal/Sparse/Field.hs view
@@ -0,0 +1,118 @@+-- |+-- Module:      Data.Poly.Internal.Sparse.Field+-- Copyright:   (c) 2019 Andrew Lelechenko+-- Licence:     BSD3+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+--+-- GcdDomain for Field underlying+--++{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE CPP                        #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE PatternSynonyms            #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE UndecidableInstances       #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++#if MIN_VERSION_semirings(0,4,2)++module Data.Poly.Internal.Sparse.Field+  ( gcdExt+  ) where++import Prelude hiding (quotRem, quot, rem, gcd)+import Control.Arrow+import Control.Exception+import Data.Euclidean+#if !MIN_VERSION_semirings(0,5,0)+import Data.Semiring (Ring)+#endif+import Data.Semiring (minus, plus, times, zero, one)+import qualified Data.Vector.Generic as G++import Data.Poly.Internal.Sparse+import Data.Poly.Internal.Sparse.GcdDomain ()++#if !MIN_VERSION_semirings(0,5,0)+type Field a = (Euclidean a, Ring a, Fractional a)+#endif++instance (Eq a, Eq (v (Word, a)), Field a, G.Vector v (Word, a)) => Euclidean (Poly v a) where+  degree (Poly xs)+    | G.null xs = 0+    | otherwise = 1 + fromIntegral (fst (G.last xs))++  quotRem = quotientRemainder++quotientRemainder+  :: (Eq a, Field a, G.Vector v (Word, a))+  => Poly v a+  -> Poly v a+  -> (Poly v a, Poly v a)+quotientRemainder ts ys = case leading ys of+  Nothing -> throw DivideByZero+  Just (yp, yc) -> go ts+    where+      go xs = case leading xs of+        Nothing -> (zero, zero)+        Just (xp, xc) -> case xp `compare` yp of+          LT -> (zero, xs)+          EQ -> (zs, xs')+          GT -> first (`plus` zs) $ go xs'+          where+            zs = Poly $ G.singleton (xp `minus` yp, xc `quot` yc)+            xs' = xs `minus` zs `times` ys++-- | Execute the extended Euclidean algorithm.+-- For polynomials @a@ and @b@, compute their unique greatest common divisor @g@+-- and the unique coefficient polynomial @s@ satisfying @as + bt = g@,+-- such that either @g@ is monic, or @g = 0@ and @s@ is monic, or @g = s = 0@.+--+-- >>> gcdExt (X^2 + 1 :: UPoly Double) (X^3 + 3 * X :: UPoly Double)+-- (1.0, 0.5 * X^2 + (-0.0) * X + 1.0)+-- >>> gcdExt (X^3 + 3 * X :: UPoly Double) (3 * X^4 + 3 * X^2 :: UPoly Double)+-- (1.0 * X + 0.0,(-0.16666666666666666) * X^2 + (-0.0) * X + 0.3333333333333333)+gcdExt+  :: (Eq a, Field a, G.Vector v (Word, a), Eq (v (Word, a)))+  => Poly v a+  -> Poly v a+  -> (Poly v a, Poly v a)+gcdExt xs ys = case scaleMonic gs of+  Just (c', gs') -> (gs', scale' zero c' ss)+  Nothing -> case scaleMonic ss of+    Just (_, ss') -> (zero, ss')+    Nothing -> (zero, zero)+  where+    (gs, ss) = go ys xs zero one+      where+        go r' r s' s+          | r' == zero = (r, s)+          | otherwise  = case r `quotRem` r' of+            (q, r'') -> go r'' r' (s `minus` q `times` s') s'+{-# INLINE gcdExt #-}++-- | Scale a non-zero polynomial such that its leading coefficient is one,+-- returning the reciprocal of the leading coefficient in the scaling.+--+-- >>> scaleMonic (X^3 + 3 * X :: UPoly Double)+-- Just (1.0, 1.0 * X^3 + 0.0 * X^2 + 3.0 * X + 0.0)+-- >>> scaleMonic (3 * X^4 + 3 * X^2 :: UPoly Double)+-- Just (0.3333333333333333, 1.0 * X^4 + 0.0 * X^3 + 1.0 * X^2 + 0.0 * X + 0.0)+scaleMonic+  :: (Eq a, Field a, G.Vector v (Word, a), Eq (v (Word, a)))+  => Poly v a+  -> Maybe (a, Poly v a)+scaleMonic xs = case leading xs of+  Nothing -> Nothing+  Just (_, c) -> let c' = one `quot` c in Just (c', scale' zero c' xs)+{-# INLINE scaleMonic #-}++#else++module Data.Poly.Internal.Sparse.Field () where++#endif
− src/Data/Poly/Internal/Sparse/Fractional.hs
@@ -1,78 +0,0 @@--- |--- Module:      Data.Poly.Internal.Sparse.Fractional--- Copyright:   (c) 2019 Andrew Lelechenko--- Licence:     BSD3--- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>------ GcdDomain for Fractional underlying-----{-# LANGUAGE CPP                        #-}-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE PatternSynonyms            #-}-{-# LANGUAGE ScopedTypeVariables        #-}-{-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE UndecidableInstances       #-}-{-# LANGUAGE ViewPatterns               #-}--{-# OPTIONS_GHC -fno-warn-orphans #-}--#if MIN_VERSION_semirings(0,4,2)--module Data.Poly.Internal.Sparse.Fractional-  ( fractionalGcd-  ) where--import Prelude hiding (quotRem, rem, gcd)-import Control.Arrow-import Control.Exception-import Data.Euclidean-import qualified Data.Semiring as Semiring-import qualified Data.Vector.Generic as G--import Data.Poly.Internal.Sparse-import Data.Poly.Internal.Sparse.GcdDomain ()--instance (Eq a, Eq (v (Word, a)), Semiring.Ring a, GcdDomain a, Fractional a, G.Vector v (Word, a)) => Euclidean (Poly v a) where-  degree (Poly xs)-    | G.null xs = 0-    | otherwise = 1 + fromIntegral (fst (G.last xs))--  quotRem = quotientRemainder--quotientRemainder-  :: (Eq a, Fractional a, G.Vector v (Word, a))-  => Poly v a-  -> Poly v a-  -> (Poly v a, Poly v a)-quotientRemainder ts ys = case leading ys of-  Nothing -> throw DivideByZero-  Just (yp, yc) -> go ts-    where-      go xs = case leading xs of-        Nothing -> (0, 0)-        Just (xp, xc) -> case xp `compare` yp of-          LT -> (0, xs)-          EQ -> (zs, xs')-          GT -> first (+ zs) $ go xs'-          where-            zs = Poly $ G.singleton (xp - yp, xc / yc)-            xs' = xs - zs * ys--fractionalGcd-  :: (Eq a, Fractional a, G.Vector v (Word, a))-  => Poly v a-  -> Poly v a-  -> Poly v a-fractionalGcd xs ys-  | G.null (unPoly ys) = xs-  | otherwise = fractionalGcd ys $ snd $ quotientRemainder xs ys-{-# INLINE fractionalGcd #-}--#else--module Data.Poly.Internal.Sparse.Fractional () where--#endif
src/Data/Poly/Internal/Sparse/GcdDomain.hs view
@@ -10,12 +10,10 @@ {-# LANGUAGE CPP                        #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE PatternSynonyms            #-} {-# LANGUAGE ScopedTypeVariables        #-} {-# LANGUAGE TypeFamilies               #-} {-# LANGUAGE UndecidableInstances       #-}-{-# LANGUAGE ViewPatterns               #-}  {-# OPTIONS_GHC -fno-warn-orphans #-} @@ -28,17 +26,15 @@ import Control.Exception import Data.Euclidean import Data.Maybe-import Data.Semiring (Semiring(..))-import qualified Data.Semiring as Semiring+import Data.Semiring (Semiring(..), Ring(), minus) import qualified Data.Vector.Generic as G  import Data.Poly.Internal.Sparse --- | Consider using 'Data.Poly.Sparse.Semiring.PolyOverFractional' wrapper,+-- | Consider using 'Data.Poly.Sparse.Semiring.PolyOverField' wrapper, -- which provides a much faster implementation of--- 'Data.Euclidean.gcd' for 'Fractional'--- coefficients.-instance (Eq a, Semiring.Ring a, GcdDomain a, Eq (v (Word, a)), G.Vector v (Word, a)) => GcdDomain (Poly v a) where+-- 'Data.Euclidean.gcd' for polynomials over 'Field'.+instance (Eq a, Ring a, GcdDomain a, Eq (v (Word, a)), G.Vector v (Word, a)) => GcdDomain (Poly v a) where   divide xs ys = case leading ys of     Nothing -> throw DivideByZero     Just (yp, yc) -> case leading xs of@@ -48,7 +44,7 @@         | otherwise -> do           zc <- divide xc yc           let z = Poly $ G.singleton (xp - yp, zc)-          rest <- divide (xs `plus` Semiring.negate z `times` ys) ys+          rest <- divide (xs `minus` z `times` ys) ys           pure $ rest `plus` z    gcd xs ys@@ -62,7 +58,7 @@         xy = monomial' 0 (gcd (cont xs) (cont ys))  gcdHelper-  :: (Eq a, Semiring.Ring a, GcdDomain a, G.Vector v (Word, a))+  :: (Eq a, Ring a, GcdDomain a, G.Vector v (Word, a))   => Poly v a   -> Poly v a   -> Poly v a@@ -71,9 +67,9 @@   Just (xp, xc) -> case leading ys of     Nothing -> xs     Just (yp, yc) -> case xp `compare` yp of-      LT -> gcdHelper xs (ys `times` monomial' 0 gy `plus` Semiring.negate (xs `times` monomial' (yp - xp) gx))-      EQ -> gcdHelper xs (ys `times` monomial' 0 gy `plus` Semiring.negate (xs `times` monomial' 0 gx))-      GT -> gcdHelper (xs `times` monomial' 0 gx `plus` Semiring.negate (ys `times` monomial' (xp - yp) gy)) ys+      LT -> gcdHelper xs (ys `times` monomial' 0 gy `minus` xs `times` monomial' (yp - xp) gx)+      EQ -> gcdHelper xs (ys `times` monomial' 0 gy `minus` xs `times` monomial' 0 gx)+      GT -> gcdHelper (xs `times` monomial' 0 gx `minus` ys `times` monomial' (xp - yp) gy) ys       where         g = lcm xc yc         gx = fromMaybe err $ divide g xc
src/Data/Poly/Semiring.hs view
@@ -7,8 +7,8 @@ -- Dense polynomials and a 'Semiring'-based interface. -- -{-# LANGUAGE CPP                 #-}-{-# LANGUAGE PatternSynonyms     #-}+{-# LANGUAGE CPP             #-}+{-# LANGUAGE PatternSynonyms #-}  module Data.Poly.Semiring   ( Poly@@ -23,9 +23,16 @@   , pattern X   , eval   , deriv+#if MIN_VERSION_semirings(0,5,0)+  , integral+#endif #if MIN_VERSION_semirings(0,4,2)-  -- * Fractional coefficients-  , PolyOverFractional(..)+  -- * Polynomials over 'Field'+  , PolyOverField(..)+  , gcdExt+  , PolyOverFractional+  , pattern PolyOverFractional+  , unPolyOverFractional #endif   ) where @@ -35,10 +42,13 @@ import Data.Poly.Internal.Dense (Poly(..), VPoly, UPoly, leading) import qualified Data.Poly.Internal.Dense as Dense #if MIN_VERSION_semirings(0,4,2)-import Data.Poly.Internal.Dense.Fractional ()+import Data.Poly.Internal.Dense.Field (gcdExt) import Data.Poly.Internal.Dense.GcdDomain ()-import Data.Poly.Internal.PolyOverFractional+import Data.Poly.Internal.PolyOverField #endif+#if MIN_VERSION_semirings(0,5,0)+import Data.Euclidean (Field)+#endif  -- | Make 'Poly' from a vector of coefficients -- (first element corresponds to a constant term).@@ -81,3 +91,13 @@ -- 3 * X^2 + 0 * X + 3 deriv :: (Eq a, Semiring a, G.Vector v a) => Poly v a -> Poly v a deriv = Dense.deriv'++#if MIN_VERSION_semirings(0,5,0)+-- | Compute an indefinite integral of a polynomial,+-- setting constant term to zero.+--+-- >>> integral (3 * X^2 + 3) :: UPoly Double+-- 1.0 * X^3 + 0.0 * X^2 + 3.0 * X + 0.0+integral :: (Eq a, Field a, G.Vector v a) => Poly v a -> Poly v a+integral = Dense.integral'+#endif
src/Data/Poly/Sparse.hs view
@@ -7,7 +7,8 @@ -- Sparse polynomials with 'Num' instance. -- -{-# LANGUAGE PatternSynonyms     #-}+{-# LANGUAGE CPP             #-}+{-# LANGUAGE PatternSynonyms #-}  module Data.Poly.Sparse   ( Poly@@ -23,8 +24,14 @@   , eval   , deriv   , integral+#if MIN_VERSION_semirings(0,4,2)+  -- * Polynomials over 'Field'+  , gcdExt+#endif   ) where  import Data.Poly.Internal.Sparse-import Data.Poly.Internal.Sparse.Fractional ()+#if MIN_VERSION_semirings(0,4,2)+import Data.Poly.Internal.Sparse.Field (gcdExt) import Data.Poly.Internal.Sparse.GcdDomain ()+#endif
src/Data/Poly/Sparse/Semiring.hs view
@@ -7,8 +7,9 @@ -- Sparse polynomials with 'Semiring' instance. -- -{-# LANGUAGE FlexibleContexts    #-}-{-# LANGUAGE PatternSynonyms     #-}+{-# LANGUAGE CPP              #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms  #-}  module Data.Poly.Sparse.Semiring   ( Poly@@ -23,6 +24,13 @@   , pattern X   , eval   , deriv+#if MIN_VERSION_semirings(0,5,0)+  , integral+#endif+#if MIN_VERSION_semirings(0,4,2)+  -- * Polynomials over 'Field'+  , gcdExt+#endif   ) where  import Data.Semiring (Semiring)@@ -30,8 +38,13 @@  import Data.Poly.Internal.Sparse (Poly(..), VPoly, UPoly, leading) import qualified Data.Poly.Internal.Sparse as Sparse-import Data.Poly.Internal.Sparse.Fractional ()+#if MIN_VERSION_semirings(0,4,2)+import Data.Poly.Internal.Sparse.Field (gcdExt) import Data.Poly.Internal.Sparse.GcdDomain ()+#endif+#if MIN_VERSION_semirings(0,5,0)+import Data.Euclidean (Field)+#endif  -- | Make 'Poly' from a list of (power, coefficient) pairs. -- (first element corresponds to a constant term).@@ -74,3 +87,13 @@ -- 3 * X^2 + 3 deriv :: (Eq a, Semiring a, G.Vector v (Word, a)) => Poly v a -> Poly v a deriv = Sparse.deriv'++#if MIN_VERSION_semirings(0,5,0)+-- | Compute an indefinite integral of a polynomial,+-- setting constant term to zero.+--+-- >>> integral (3 * X^2 + 3) :: UPoly Double+-- 1.0 * X^3 + 3.0 * X+integral :: (Eq a, Field a, G.Vector v (Word, a)) => Poly v a -> Poly v a+integral = Sparse.integral'+#endif
test/Dense.hs view
@@ -10,11 +10,12 @@   ( testSuite   ) where -import Prelude hiding (quotRem)+import Prelude hiding (gcd, quotRem, rem) #if MIN_VERSION_semirings(0,4,2) import Data.Euclidean #endif import Data.Int+import Data.Maybe import Data.Poly import qualified Data.Poly.Semiring as S import Data.Proxy@@ -23,7 +24,7 @@ import qualified Data.Vector.Generic as G import qualified Data.Vector.Unboxed as U import Test.Tasty-import Test.Tasty.QuickCheck hiding (scale)+import Test.Tasty.QuickCheck hiding (scale, numTests) import Test.QuickCheck.Classes  import Quaternion@@ -33,9 +34,9 @@   shrink = fmap (S.toPoly . G.fromList) . shrink . G.toList . unPoly  #if MIN_VERSION_semirings(0,4,2)-instance (Eq a, Semiring a, Arbitrary a, G.Vector v a) => Arbitrary (PolyOverFractional (Poly v a)) where-  arbitrary = PolyOverFractional . S.toPoly . G.fromList . (\xs -> take (length xs `mod` 10) xs) <$> arbitrary-  shrink = fmap (PolyOverFractional . S.toPoly . G.fromList) . shrink . G.toList . unPoly . unPolyOverFractional+instance (Eq a, Semiring a, Arbitrary a, G.Vector v a) => Arbitrary (PolyOverField (Poly v a)) where+  arbitrary = PolyOverField . S.toPoly . G.fromList . (\xs -> take (length xs `mod` 10) xs) <$> arbitrary+  shrink = fmap (PolyOverField . S.toPoly . G.fromList) . shrink . G.toList . unPoly . unPolyOverField #endif  newtype ShortPoly a = ShortPoly { unShortPoly :: a }@@ -57,14 +58,24 @@ testSuite = testGroup "Dense"     [ arithmeticTests     , otherTests-    , semiringTests+    , lawsTests     , evalTests     , derivTests #if MIN_VERSION_semirings(0,4,2)-    -- , euclideanTests+    , gcdExtTests #endif     ] +lawsTests :: TestTree+lawsTests = testGroup "Laws"+  [ semiringTests+  , ringTests+  , numTests+  , euclideanTests+  , isListTests+  , showTests+  ]+ semiringTests :: TestTree semiringTests   = testGroup "Semiring"@@ -74,26 +85,73 @@   , semiringLaws (Proxy :: Proxy (Poly U.Vector Int8))   , semiringLaws (Proxy :: Proxy (Poly V.Vector Integer))   , semiringLaws (Proxy :: Proxy (Poly U.Vector (Quaternion Int)))+  ]++ringTests :: TestTree+ringTests+  = testGroup "Ring"+  $ map (uncurry testProperty)+  $ concatMap lawsProperties+  [ #if MIN_VERSION_quickcheck_classes(0,6,1)-  , ringLaws (Proxy :: Proxy (Poly U.Vector ()))+    ringLaws (Proxy :: Proxy (Poly U.Vector ()))   , ringLaws (Proxy :: Proxy (Poly U.Vector Int8))   , ringLaws (Proxy :: Proxy (Poly V.Vector Integer))   , ringLaws (Proxy :: Proxy (Poly U.Vector (Quaternion Int))) #endif   ] -#if MIN_VERSION_semirings(0,4,2)--- euclideanTests :: TestTree--- euclideanTests---   = testGroup "Euclidean"---   $ map (uncurry testProperty)---   $ concatMap lawsProperties---   [ gcdDomainLaws (Proxy :: Proxy (ShortPoly (Poly V.Vector Integer)))---   , gcdDomainLaws (Proxy :: Proxy (PolyOverFractional (Poly V.Vector Rational)))---   , euclideanLaws (Proxy :: Proxy (ShortPoly (Poly V.Vector Rational)))---   ]+numTests :: TestTree+numTests+  = testGroup "Num"+  $ map (uncurry testProperty)+  $ concatMap lawsProperties+  [+#if MIN_VERSION_quickcheck_classes(0,6,3)+    numLaws (Proxy :: Proxy (Poly U.Vector Int8))+  , numLaws (Proxy :: Proxy (Poly V.Vector Integer))+  , numLaws (Proxy :: Proxy (Poly U.Vector (Quaternion Int))) #endif+  ] +euclideanTests :: TestTree+euclideanTests+  = testGroup "Euclidean"+  $ map (uncurry testProperty)+  $ concatMap lawsProperties+  [+#if MIN_VERSION_semirings(0,4,2) && MIN_VERSION_quickcheck_classes(0,6,3)+    gcdDomainLaws (Proxy :: Proxy (ShortPoly (Poly V.Vector Integer)))+  , gcdDomainLaws (Proxy :: Proxy (PolyOverField (Poly V.Vector Rational)))+  , euclideanLaws (Proxy :: Proxy (ShortPoly (Poly V.Vector Rational)))+#endif+  ]++isListTests :: TestTree+isListTests+  = testGroup "IsList"+  $ map (uncurry testProperty)+  $ concatMap lawsProperties+  [ isListLaws (Proxy :: Proxy (Poly U.Vector ()))+  , isListLaws (Proxy :: Proxy (Poly U.Vector Int8))+  , isListLaws (Proxy :: Proxy (Poly V.Vector Integer))+  , isListLaws (Proxy :: Proxy (Poly U.Vector (Quaternion Int)))+  ]++showTests :: TestTree+showTests+  = testGroup "Show"+  $ map (uncurry testProperty)+  $ concatMap lawsProperties+  [+#if MIN_VERSION_quickcheck_classes(0,6,0)+    showLaws (Proxy :: Proxy (Poly U.Vector ()))+  , showLaws (Proxy :: Proxy (Poly U.Vector Int8))+  , showLaws (Proxy :: Proxy (Poly V.Vector Integer))+  , showLaws (Proxy :: Proxy (Poly U.Vector (Quaternion Int)))+#endif+  ]+ arithmeticTests :: TestTree arithmeticTests = testGroup "Arithmetic"   [ testProperty "addition matches reference" $@@ -189,6 +247,8 @@ derivTests = testGroup "deriv"   [ testProperty "deriv = S.deriv" $     \(p :: Poly V.Vector Integer) -> deriv p === S.deriv p+  , testProperty "integral = S.integral" $+    \(p :: Poly V.Vector Rational) -> integral p === S.integral p   , testProperty "deriv . integral = id" $     \(p :: Poly V.Vector Rational) -> deriv (integral p) === p   , testProperty "deriv c = 0" $@@ -205,3 +265,22 @@   --     deriv (eval (toPoly $ fmap (monomial 0) $ unPoly p) q) ===   --       deriv q * eval (toPoly $ fmap (monomial 0) $ unPoly $ deriv p) q   ]++#if MIN_VERSION_semirings(0,4,2)+gcdExtTests :: TestTree+gcdExtTests = localOption (QuickCheckMaxSize 12) $ testGroup "gcdExt"+  [ testProperty "gcdExt == S.gcdExt" $+    \(a :: Poly V.Vector Rational) b ->+      gcdExt a b === S.gcdExt a b+  , testProperty "g == as (mod b) for gcdExt" $+    \(a :: Poly V.Vector Rational) b -> b /= 0 ==>+      uncurry ((. flip rem b) . (===) . flip rem b) ((* a) <$> gcdExt a b)+  , testProperty "fst . gcdExt == gcd (mod units)" $+    \(a :: Poly V.Vector Rational) b ->+      fst (gcdExt a b) `sameUpToUnits` gcd a b+  ]++sameUpToUnits :: (Eq a, GcdDomain a) => a -> a -> Bool+sameUpToUnits x y = x == y ||+  isJust (x `divide` y) && isJust (y `divide` x)+#endif
test/Sparse.hs view
@@ -11,13 +11,14 @@   ( testSuite   ) where -import Prelude hiding (quotRem)+import Prelude hiding (gcd, quotRem, rem) #if MIN_VERSION_semirings(0,4,2) import Data.Euclidean #endif import Data.Function import Data.Int import Data.List+import Data.Maybe import Data.Poly.Sparse import qualified Data.Poly.Sparse.Semiring as S import Data.Proxy@@ -26,7 +27,7 @@ import qualified Data.Vector.Generic as G import qualified Data.Vector.Unboxed as U import Test.Tasty-import Test.Tasty.QuickCheck hiding (scale)+import Test.Tasty.QuickCheck hiding (scale, numTests) import Test.QuickCheck.Classes  import Quaternion@@ -54,11 +55,24 @@ testSuite = testGroup "Sparse"     [ arithmeticTests     , otherTests-    , semiringTests+    , lawsTests     , evalTests     , derivTests+#if MIN_VERSION_semirings(0,4,2)+    , gcdExtTests+#endif     ] +lawsTests :: TestTree+lawsTests = testGroup "Laws"+  [ semiringTests+  , ringTests+  , numTests+  , euclideanTests+  , isListTests+  , showTests+  ]+ semiringTests :: TestTree semiringTests   = testGroup "Semiring"@@ -68,14 +82,72 @@   , semiringLaws (Proxy :: Proxy (Poly U.Vector Int8))   , semiringLaws (Proxy :: Proxy (Poly V.Vector Integer))   , semiringLaws (Proxy :: Proxy (Poly U.Vector (Quaternion Int)))+  ]++ringTests :: TestTree+ringTests+  = testGroup "Ring"+  $ map (uncurry testProperty)+  $ concatMap lawsProperties+  [ #if MIN_VERSION_quickcheck_classes(0,6,1)-  , ringLaws (Proxy :: Proxy (Poly U.Vector ()))+    ringLaws (Proxy :: Proxy (Poly U.Vector ()))   , ringLaws (Proxy :: Proxy (Poly U.Vector Int8))   , ringLaws (Proxy :: Proxy (Poly V.Vector Integer))   , ringLaws (Proxy :: Proxy (Poly U.Vector (Quaternion Int))) #endif   ] +numTests :: TestTree+numTests+  = testGroup "Num"+  $ map (uncurry testProperty)+  $ concatMap lawsProperties+  [+#if MIN_VERSION_quickcheck_classes(0,6,3)+    numLaws (Proxy :: Proxy (Poly U.Vector Int8))+  , numLaws (Proxy :: Proxy (Poly V.Vector Integer))+  , numLaws (Proxy :: Proxy (Poly U.Vector (Quaternion Int)))+#endif+  ]++euclideanTests :: TestTree+euclideanTests+  = testGroup "Euclidean"+  $ map (uncurry testProperty)+  $ concatMap lawsProperties+  [+#if MIN_VERSION_semirings(0,4,2) && MIN_VERSION_quickcheck_classes(0,6,3)+    gcdDomainLaws (Proxy :: Proxy (ShortPoly (Poly V.Vector Integer)))+  , euclideanLaws (Proxy :: Proxy (ShortPoly (Poly V.Vector Rational)))+#endif+  ]++isListTests :: TestTree+isListTests+  = testGroup "IsList"+  $ map (uncurry testProperty)+  $ concatMap lawsProperties+  [ isListLaws (Proxy :: Proxy (Poly U.Vector ()))+  , isListLaws (Proxy :: Proxy (Poly U.Vector Int8))+  , isListLaws (Proxy :: Proxy (Poly V.Vector Integer))+  , isListLaws (Proxy :: Proxy (Poly U.Vector (Quaternion Int)))+  ]++showTests :: TestTree+showTests+  = testGroup "Show"+  $ map (uncurry testProperty)+  $ concatMap lawsProperties+  [+#if MIN_VERSION_quickcheck_classes(0,6,0)+    showLaws (Proxy :: Proxy (Poly U.Vector ()))+  , showLaws (Proxy :: Proxy (Poly U.Vector Int8))+  , showLaws (Proxy :: Proxy (Poly V.Vector Integer))+  , showLaws (Proxy :: Proxy (Poly U.Vector (Quaternion Int)))+#endif+  ]+ arithmeticTests :: TestTree arithmeticTests = testGroup "Arithmetic"   [ testProperty "addition matches reference" $@@ -180,6 +252,8 @@ derivTests = testGroup "deriv"   [ testProperty "deriv = S.deriv" $     \(p :: Poly V.Vector Integer) -> deriv p === S.deriv p+  , testProperty "integral = S.integral" $+    \(p :: Poly V.Vector Rational) -> integral p === S.integral p   , testProperty "deriv . integral = id" $     \(p :: Poly V.Vector Rational) -> deriv (integral p) === p   , testProperty "deriv c = 0" $@@ -196,3 +270,22 @@   --     deriv (eval (toPoly $ fmap (fmap $ monomial 0) $ unPoly p) q) ===   --       deriv q * eval (toPoly $ fmap (fmap $ monomial 0) $ unPoly $ deriv p) q   ]++#if MIN_VERSION_semirings(0,4,2)+gcdExtTests :: TestTree+gcdExtTests = localOption (QuickCheckMaxSize 12) $ testGroup "gcdExt"+  [ testProperty "gcdExt == S.gcdExt" $+    \(a :: Poly V.Vector Rational) b ->+      gcdExt a b === S.gcdExt a b+  , testProperty "g == as (mod b) for gcdExt" $+    \(a :: Poly V.Vector Rational) b -> b /= 0 ==>+      uncurry ((. flip rem b) . (===) . flip rem b) ((* a) <$> gcdExt a b)+  , testProperty "fst . gcdExt == gcd (mod units)" $+    \(a :: Poly V.Vector Rational) b ->+      fst (gcdExt a b) `sameUpToUnits` gcd a b+  ]++sameUpToUnits :: (Eq a, GcdDomain a) => a -> a -> Bool+sameUpToUnits x y = x == y ||+  isJust (x `divide` y) && isJust (y `divide` x)+#endif