• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

msakai / toysolver / 513

24 Nov 2024 08:49AM UTC coverage: 68.675% (-1.0%) from 69.681%
513

push

github

web-flow
Merge 496ed4263 into 46e1509c4

5 of 121 new or added lines in 7 files covered. (4.13%)

105 existing lines in 14 files now uncovered.

9745 of 14190 relevant lines covered (68.68%)

0.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.23
/src/ToySolver/Data/Polynomial/Base.hs
1
{-# LANGUAGE BangPatterns #-}
2
{-# LANGUAGE DeriveDataTypeable #-}
3
{-# LANGUAGE FlexibleInstances #-}
4
{-# LANGUAGE FunctionalDependencies #-}
5
{-# LANGUAGE MultiParamTypeClasses #-}
6
{-# LANGUAGE ScopedTypeVariables #-}
7
{-# LANGUAGE TypeFamilies #-}
8
{-# LANGUAGE TypeOperators #-}
9
{-# OPTIONS_GHC -Wall #-}
10
{-# OPTIONS_HADDOCK show-extensions #-}
11
-----------------------------------------------------------------------------
12
-- |
13
-- Module      :  ToySolver.Data.Polynomial.Base
14
-- Copyright   :  (c) Masahiro Sakai 2012-2013
15
-- License     :  BSD-style
16
--
17
-- Maintainer  :  masahiro.sakai@gmail.com
18
-- Stability   :  provisional
19
-- Portability :  non-portable
20
--
21
-- Polynomials
22
--
23
-- References:
24
--
25
-- * Monomial order <http://en.wikipedia.org/wiki/Monomial_order>
26
--
27
-- * Polynomial class for Ruby <http://www.math.kobe-u.ac.jp/~kodama/tips-RubyPoly.html>
28
--
29
-- * constructive-algebra package <http://hackage.haskell.org/package/constructive-algebra>
30
--
31
-----------------------------------------------------------------------------
32
module ToySolver.Data.Polynomial.Base
33
  (
34
  -- * Polynomial type
35
    Polynomial
36

37
  -- * Conversion
38
  , Var (..)
39
  , constant
40
  , terms
41
  , fromTerms
42
  , coeffMap
43
  , fromCoeffMap
44
  , fromTerm
45

46
  -- * Query
47
  , Degree (..)
48
  , Vars (..)
49
  , lt
50
  , lc
51
  , lm
52
  , coeff
53
  , lookupCoeff
54
  , isPrimitive
55

56
  -- * Operations
57
  , Factor (..)
58
  , SQFree (..)
59
  , ContPP (..)
60
  , deriv
61
  , integral
62
  , eval
63
  , subst
64
  , mapCoeff
65
  , toMonic
66
  , toUPolynomialOf
67
  , divModMP
68
  , reduce
69

70
  -- * Univariate polynomials
71
  , UPolynomial
72
  , X (..)
73
  , UTerm
74
  , UMonomial
75
  , div
76
  , mod
77
  , divMod
78
  , divides
79
  , gcd
80
  , lcm
81
  , exgcd
82
  , pdivMod
83
  , pdiv
84
  , pmod
85
  , gcd'
86
  , isRootOf
87
  , isSquareFree
88
  , nat
89
  , eisensteinsCriterion
90

91
  -- * Term
92
  , Term
93
  , tdeg
94
  , tscale
95
  , tmult
96
  , tdivides
97
  , tdiv
98
  , tderiv
99
  , tintegral
100

101
  -- * Monic monomial
102
  , Monomial
103
  , mone
104
  , mfromIndices
105
  , mfromIndicesMap
106
  , mindices
107
  , mindicesMap
108
  , mmult
109
  , mpow
110
  , mdivides
111
  , mdiv
112
  , mderiv
113
  , mintegral
114
  , mlcm
115
  , mgcd
116
  , mcoprime
117

118
  -- * Monomial order
119
  , MonomialOrder
120
  , lex
121
  , revlex
122
  , grlex
123
  , grevlex
124

125
  -- * Pretty Printing
126
  , PrintOptions (..)
127
  , prettyPrint
128
  , PrettyCoeff (..)
129
  , PrettyVar (..)
130
  ) where
131

132
import Prelude hiding (lex, div, mod, divMod, gcd, lcm)
133
import qualified Prelude
134
import Control.DeepSeq
135
import Control.Exception (assert)
136
import Control.Monad
137
import Data.Default.Class
138
import Data.Data
139
import qualified Data.FiniteField as FF
140
import Data.Function
141
import Data.Hashable
142
import Data.List
143
import Data.Numbers.Primes (primeFactors)
144
import Data.Ratio
145
import Data.String (IsString (..))
146
import Data.Map.Strict (Map)
147
import qualified Data.Map.Strict as Map
148
import Data.Set (Set)
149
import qualified Data.Set as Set
150
import Data.IntMap.Strict (IntMap)
151
import qualified Data.IntMap.Strict as IntMap
152
import Data.VectorSpace
153
import qualified Text.PrettyPrint.HughesPJClass as PP
154
import Text.PrettyPrint.HughesPJClass (Doc, PrettyLevel, Pretty (..), maybeParens)
155

156
infixl 7  `div`, `mod`
157

158
{--------------------------------------------------------------------
159
  Classes
160
--------------------------------------------------------------------}
161

162
class Vars a v => Var a v | a -> v where
163
  var :: v -> a
164

165
class Ord v => Vars a v | a -> v where
166
  vars :: a -> Set v
167

168
-- | total degree of a given polynomial
169
class Degree t where
170
  deg :: t -> Integer
171

172
{--------------------------------------------------------------------
173
  Polynomial type
174
--------------------------------------------------------------------}
175

176
-- | Polynomial over commutative ring r
177
newtype Polynomial r v = Polynomial{ coeffMap :: Map (Monomial v) r }
1✔
178
  deriving (Eq, Ord, Typeable)
×
179

180
instance (Eq k, Num k, Ord v) => Num (Polynomial k v) where
1✔
181
  (+)      = plus
1✔
182
  (*)      = mult
1✔
183
  negate   = neg
1✔
184
  abs x    = x -- OK?
×
185
  signum _ = 1 -- OK?
×
186
  fromInteger x = constant (fromInteger x)
1✔
187

188
instance (Eq k, Num k, Ord v, IsString v) => IsString (Polynomial k v) where
189
  fromString s = var (fromString s)
×
190

191
instance (Eq k, Num k, Ord v) => AdditiveGroup (Polynomial k v) where
×
192
  (^+^)   = plus
1✔
193
  zeroV   = zero
1✔
194
  negateV = neg
×
195

196
instance (Eq k, Num k, Ord v) => VectorSpace (Polynomial k v) where
197
  type Scalar (Polynomial k v) = k
198
  k *^ p = scale k p
×
199

200
instance (Show v, Show k) => Show (Polynomial k v) where
×
201
  showsPrec d p  = showParen (d > 10) $
×
202
    showString "fromTerms " . shows (terms p)
×
203

204
instance (NFData k, NFData v) => NFData (Polynomial k v) where
205
  rnf (Polynomial m) = rnf m
×
206

207
instance (Hashable k, Hashable v) => Hashable (Polynomial k v) where
×
208
  hashWithSalt = hashUsing (Map.toList . coeffMap)
×
209

210
instance (Eq k, Num k, Ord v) => Var (Polynomial k v) v where
211
  var x = fromTerm (1, var x)
1✔
212

213
instance (Ord v) => Vars (Polynomial k v) v where
214
  vars p = Set.unions $ [vars mm | (_, mm) <- terms p]
×
215

216
instance Degree (Polynomial k v) where
217
  deg p
1✔
218
    | isZero p  = -1
1✔
219
    | otherwise = maximum [deg mm | (_,mm) <- terms p]
×
220

221
normalize :: (Eq k, Num k) => Polynomial k v -> Polynomial k v
222
normalize (Polynomial m) = Polynomial (Map.filter (0/=) m)
1✔
223

224
asConstant :: Num k => Polynomial k v -> Maybe k
225
asConstant p =
1✔
226
  case terms p of
1✔
227
    [] -> Just 0
1✔
228
    [(c,xs)] | Map.null (mindicesMap xs) -> Just c
1✔
229
    _ -> Nothing
1✔
230

231
scale :: (Eq k, Num k) => k -> Polynomial k v -> Polynomial k v
232
scale 0 _ = zero
1✔
233
scale 1 p = p
1✔
234
scale a (Polynomial m) = Polynomial (Map.mapMaybe f m)
1✔
235
  where
236
    f b = if c == 0 then Nothing else Just c
×
237
      where c = a * b
1✔
238

239
zero :: Polynomial k v
240
zero = Polynomial $ Map.empty
1✔
241

242
plus :: (Eq k, Num k, Ord v) => Polynomial k v -> Polynomial k v -> Polynomial k v
243
plus (Polynomial m1) (Polynomial m2) = Polynomial $ Map.mergeWithKey f id id m1 m2
1✔
244
  where
245
    f _ a b = if c == 0 then Nothing else Just c
1✔
246
      where
247
        c = a + b
1✔
248

249
neg :: (Num k) => Polynomial k v -> Polynomial k v
250
neg (Polynomial m) = Polynomial $ Map.map negate m
1✔
251

252
mult :: (Eq k, Num k, Ord v) => Polynomial k v -> Polynomial k v -> Polynomial k v
253
mult a b
1✔
254
  | Just c <- asConstant a = scale c b
1✔
255
  | Just c <- asConstant b = scale c a
1✔
256
mult (Polynomial m1) (Polynomial m2) = fromCoeffMap $ Map.fromListWith (+)
1✔
257
      [ (xs1 `mmult` xs2, c1*c2)
1✔
258
      | (xs1,c1) <- Map.toList m1, (xs2,c2) <- Map.toList m2
1✔
259
      ]
260

261
isZero :: Polynomial k v -> Bool
262
isZero (Polynomial m) = Map.null m
1✔
263

264
-- | construct a polynomial from a constant
265
constant :: (Eq k, Num k) => k -> Polynomial k v
266
constant c = fromTerm (c, mone)
1✔
267

268
-- | construct a polynomial from a list of terms
269
fromTerms :: (Eq k, Num k, Ord v) => [Term k v] -> Polynomial k v
270
fromTerms = fromCoeffMap . Map.fromListWith (+) . map (\(c,xs) -> (xs,c))
1✔
271

272
fromCoeffMap :: (Eq k, Num k) => Map (Monomial v) k -> Polynomial k v
273
fromCoeffMap m = normalize $ Polynomial m
1✔
274

275
-- | construct a polynomial from a singlet term
276
fromTerm :: (Eq k, Num k) => Term k v -> Polynomial k v
277
fromTerm (0,_) = zero
1✔
278
fromTerm (c,xs) = Polynomial $ Map.singleton xs c
1✔
279

280
-- | list of terms
281
terms :: Polynomial k v -> [Term k v]
282
terms (Polynomial m) = [(c,xs) | (xs,c) <- Map.toList m]
1✔
283

284
-- | leading term with respect to a given monomial order
285
lt :: (Num k) => MonomialOrder v -> Polynomial k v -> Term k v
286
lt cmp p =
1✔
287
  case terms p of
1✔
288
    [] -> (0, mone) -- should be error?
×
289
    ms -> maximumBy (cmp `on` snd) ms
1✔
290

291
-- | leading coefficient with respect to a given monomial order
292
lc :: (Num k) => MonomialOrder v -> Polynomial k v -> k
293
lc cmp = fst . lt cmp
1✔
294

295
-- | leading monomial with respect to a given monomial order
296
lm :: (Num k) => MonomialOrder v -> Polynomial k v -> Monomial v
297
lm cmp = snd . lt cmp
1✔
298

299
-- | Look up a coefficient for a given monomial.
300
-- If no such term exists, it returns @0@.
301
coeff :: (Num k, Ord v) => Monomial v -> Polynomial k v -> k
302
coeff xs (Polynomial m) = Map.findWithDefault 0 xs m
1✔
303

304
-- | Look up a coefficient for a given monomial.
305
-- If no such term exists, it returns @Nothing@.
306
lookupCoeff :: Ord v => Monomial v -> Polynomial k v -> Maybe k
307
lookupCoeff xs (Polynomial m) = Map.lookup xs m
×
308

309
contI :: (Integral r, Ord v) => Polynomial r v -> r
310
contI 0 = 1
1✔
311
contI p = foldl1' Prelude.gcd [abs c | (c,_) <- terms p]
1✔
312

313
ppI :: (Integral r, Ord v) => Polynomial r v -> Polynomial r v
314
ppI p = mapCoeff f p
1✔
315
  where
316
    c = contI p
1✔
317
    f x = assert (x `Prelude.mod` c == 0) $ x `Prelude.div` c
×
318

319
class ContPP k where
320
  type PPCoeff k
321

322
  -- | Content of a polynomial
323
  cont :: (Ord v) => Polynomial k v -> k
324
  -- constructive-algebra-0.3.0 では cont 0 は error になる
325

326
  -- | Primitive part of a polynomial
327
  pp :: (Ord v) => Polynomial k v -> Polynomial (PPCoeff k) v
328

329
instance ContPP Integer where
330
  type PPCoeff Integer = Integer
331
  cont = contI
1✔
332
  pp   = ppI
1✔
333

334
instance Integral r => ContPP (Ratio r) where
335
  {-# SPECIALIZE instance ContPP (Ratio Integer) #-}
336

337
  type PPCoeff (Ratio r) = r
338

339
  cont 0 = 1
×
340
  cont p = foldl1' Prelude.gcd ns % foldl' Prelude.lcm 1 ds
1✔
341
    where
342
      ns = [abs (numerator c) | (c,_) <- terms p]
1✔
343
      ds = [denominator c     | (c,_) <- terms p]
1✔
344

345
  pp p = mapCoeff (numerator . (/ c)) p
1✔
346
    where
347
      c = cont p
1✔
348

349
-- | a polynomial is called primitive if the greatest common divisor of its coefficients is 1.
350
isPrimitive :: (Eq k, Num k, ContPP k, Ord v) => Polynomial k v -> Bool
351
isPrimitive p = isZero p || cont p == 1
×
352

353
-- | Formal derivative of polynomials
354
deriv :: (Eq k, Num k, Ord v) => Polynomial k v -> v -> Polynomial k v
355
deriv p x = sumV [fromTerm (tderiv m x) | m <- terms p]
1✔
356

357
-- | Formal integral of polynomials
358
integral :: (Eq k, Fractional k, Ord v) => Polynomial k v -> v -> Polynomial k v
359
integral p x = sumV [fromTerm (tintegral m x) | m <- terms p]
1✔
360

361
-- | Evaluation
362
eval :: (Num k) => (v -> k) -> Polynomial k v -> k
363
eval env p = sum [c * product [(env x) ^ e | (x,e) <- mindices xs] | (c,xs) <- terms p]
1✔
364

365
-- | Substitution or bind
366
subst
367
  :: (Eq k, Num k, Ord v2)
368
  => Polynomial k v1 -> (v1 -> Polynomial k v2) -> Polynomial k v2
369
subst p s =
1✔
370
  sumV [constant c * product [(s x)^e | (x,e) <- mindices xs] | (c, xs) <- terms p]
1✔
371

372
-- | Transform polynomial coefficients.
373
mapCoeff :: (Eq k1, Num k1) => (k -> k1) -> Polynomial k v -> Polynomial k1 v
374
mapCoeff f (Polynomial m) = Polynomial $ Map.mapMaybe g m
1✔
375
  where
376
    g x = if y == 0 then Nothing else Just y
1✔
377
      where
378
        y = f x
1✔
379

380
-- | Transform a polynomial into a monic polynomial with respect to the given monomial order.
381
toMonic :: (Eq r, Fractional r) => MonomialOrder v -> Polynomial r v -> Polynomial r v
382
toMonic cmp p
1✔
383
  | c == 0 || c == 1 = p
1✔
384
  | otherwise = mapCoeff (/c) p
×
385
  where
386
    c = lc cmp p
1✔
387

388
-- | Convert /K[x,x1,x2,…]/ into /K[x1,x2,…][x]/.
389
toUPolynomialOf :: (Ord k, Num k, Ord v) => Polynomial k v -> v -> UPolynomial (Polynomial k v)
390
toUPolynomialOf p v = fromTerms $ do
1✔
391
  (c,mm) <- terms p
1✔
392
  let m = mindicesMap mm
1✔
393
  return ( fromTerms [(c, mfromIndicesMap (Map.delete v m))]
1✔
394
         , var X `mpow` Map.findWithDefault 0 v m
1✔
395
         )
396

397
-- | Multivariate division algorithm
398
--
399
-- @divModMP cmp f [g1,g2,..]@ returns a pair @([q1,q2,…],r)@ such that
400
--
401
--   * @f = g1*q1 + g2*q2 + .. + r@ and
402
--
403
--   * @g1, g2, ..@ do not divide @r@.
404
divModMP
405
  :: forall k v. (Eq k, Fractional k, Ord v)
406
  => MonomialOrder v -> Polynomial k v -> [Polynomial k v] -> ([Polynomial k v], Polynomial k v)
407
divModMP cmp p fs = go IntMap.empty (terms' p)
1✔
408
  where
409
    terms' :: Polynomial k v -> [Term k v]
410
    terms' g = sortBy (flip cmp `on` snd) (terms g)
1✔
411

412
    merge :: [Term k v] -> [Term k v] -> [Term k v]
413
    merge [] ys = ys
1✔
414
    merge xs [] = xs
1✔
415
    merge xxs@(x:xs) yys@(y:ys) =
416
      case cmp (snd x) (snd y) of
1✔
417
        GT -> x : merge xs yys
1✔
418
        LT -> y : merge xxs ys
1✔
419
        EQ ->
420
          case fst x + fst y of
1✔
421
            0 -> merge xs ys
1✔
UNCOV
422
            c -> (c, snd x) : merge xs ys
×
423

424
    ls = zip [0..] [(lt cmp f, terms' f) | f <- fs]
1✔
425

426
    go :: IntMap (Polynomial k v) -> [Term k v] -> ([Polynomial k v], Polynomial k v)
427
    go qs g =
1✔
428
      case xs of
1✔
429
        [] -> ([IntMap.findWithDefault 0 i qs | i <- [0 .. length fs - 1]], fromTerms g)
1✔
430
        (i, b, g') : _ -> go (IntMap.insertWith (+) i b qs) g'
1✔
431
      where
432
        xs = do
1✔
433
          (i,(a,f)) <- ls
1✔
434
          h <- g
1✔
435
          guard $ a `tdivides` h
1✔
436
          let b = tdiv h a
1✔
437
          return (i, fromTerm b, merge g [(tscale (-1) b `tmult` m) | m <- f])
1✔
438

439
-- | Multivariate division algorithm
440
--
441
-- @reduce cmp f gs = snd (divModMP cmp f gs)@
442
reduce
443
  :: (Eq k, Fractional k, Ord v)
444
  => MonomialOrder v -> Polynomial k v -> [Polynomial k v] -> Polynomial k v
445
reduce cmp p fs = go p
1✔
446
  where
447
    ls = [(lt cmp f, f) | f <- fs]
1✔
448
    go g = if null xs then g else go (head xs)
1✔
449
      where
450
        ms = sortBy (flip cmp `on` snd) (terms g)
1✔
451
        xs = do
1✔
452
          (a,f) <- ls
1✔
453
          h <- ms
1✔
454
          guard $ a `tdivides` h
1✔
455
          return (g - fromTerm (tdiv h a) * f)
1✔
456

457
-- | Prime factorization of polynomials
458
class Factor a where
459
  -- | factor a polynomial @p@ into @p1 ^ n1 * p2 ^ n2 * ..@ and
460
  -- return a list @[(p1,n1), (p2,n2), ..]@.
461
  factor :: a -> [(a, Integer)]
462

463
-- | Square-free factorization of polynomials
464
class SQFree a where
465
  -- | factor a polynomial @p@ into @p1 ^ n1 * p2 ^ n2 * ..@ and
466
  -- return a list @[(p1,n1), (p2,n2), ..]@.
467
  sqfree :: a -> [(a, Integer)]
468

469
-- | Eisenstein's irreducibility criterion.
470
--
471
-- Given a polynomial with integer coefficients @P[x] = an x^n + .. + a1*x + a0@,
472
-- it is irreducible over rational numbers if there exists a prime number @p@
473
-- satisfying the following conditions:
474
--
475
-- * @p@ divides ai for @i /= n@,
476
--
477
-- * @p@ does not divides @an@, and
478
--
479
-- * @p^2@ does not divides @a0@.
480
--
481
eisensteinsCriterion
482
  :: (ContPP k, PPCoeff k ~ Integer)
483
  => UPolynomial k -> Bool
484
eisensteinsCriterion p
1✔
485
  | deg p <= 1 = True
×
486
  | otherwise  = eisensteinsCriterion' (pp p)
×
487

488
eisensteinsCriterion' :: UPolynomial Integer -> Bool
489
eisensteinsCriterion' p = or [criterion prime | prime <- map head $ group $ primeFactors c]
1✔
490
  where
491
    Just ((_,an), ts) = Map.maxViewWithKey (coeffMap p)
1✔
492
    a0 = coeff mone p
1✔
493
    c = foldl' Prelude.gcd 0 [ai | (_,ai) <- Map.toList ts]
1✔
494
    criterion prime = an `Prelude.mod` prime /= 0 && a0 `Prelude.mod` (prime*prime) /= 0
1✔
495

496
{--------------------------------------------------------------------
497
  Pretty printing
498
--------------------------------------------------------------------}
499

500
-- | Options for pretty printing polynomials
501
--
502
-- The default value can be obtained by 'def'.
503
data PrintOptions k v
504
  = PrintOptions
505
  { pOptPrintVar        :: PrettyLevel -> Rational -> v -> Doc
1✔
506
  , pOptPrintCoeff      :: PrettyLevel -> Rational -> k -> Doc
1✔
507
  , pOptIsNegativeCoeff :: k -> Bool
1✔
508
  , pOptMonomialOrder   :: MonomialOrder v
1✔
509
  }
510

511
instance (PrettyCoeff k, PrettyVar v, Ord v) => Default (PrintOptions k v) where
512
  def =
1✔
513
    PrintOptions
1✔
514
    { pOptPrintVar        = pPrintVar
1✔
515
    , pOptPrintCoeff      = pPrintCoeff
1✔
516
    , pOptIsNegativeCoeff = isNegativeCoeff
1✔
517
    , pOptMonomialOrder   = grlex
1✔
518
    }
519

520
instance (Ord k, Num k, Ord v, PrettyCoeff k, PrettyVar v) => Pretty (Polynomial k v) where
×
521
  pPrintPrec = prettyPrint def
1✔
522

523
prettyPrint
524
  :: (Ord k, Num k)
525
  => PrintOptions k v
526
  -> PrettyLevel -> Rational -> Polynomial k v -> Doc
527
prettyPrint opt lv prec p =
1✔
528
    case sortBy (flip (pOptMonomialOrder opt) `on` snd) $ terms p of
1✔
529
      [] -> PP.int 0
×
530
      [t] -> pLeadingTerm prec t
1✔
531
      t:ts ->
532
        maybeParens (prec > addPrec) $
1✔
533
          PP.hcat (pLeadingTerm addPrec t : map pTrailingTerm ts)
1✔
534
    where
535
      pLeadingTerm prec' (c,xs) =
1✔
536
        if pOptIsNegativeCoeff opt c
1✔
537
        then maybeParens (prec' > addPrec) $
1✔
538
               PP.char '-' <> prettyPrintTerm opt lv (addPrec+1) (-c,xs)
×
539
        else prettyPrintTerm opt lv prec' (c,xs)
×
540

541
      pTrailingTerm (c,xs) =
1✔
542
        if pOptIsNegativeCoeff opt c
1✔
543
        then PP.space <> PP.char '-' <> PP.space <> prettyPrintTerm opt lv (addPrec+1) (-c,xs)
×
544
        else PP.space <> PP.char '+' <> PP.space <> prettyPrintTerm opt lv (addPrec+1) (c,xs)
×
545

546
prettyPrintTerm
547
  :: (Ord k, Num k)
548
  => PrintOptions k v
549
  -> PrettyLevel -> Rational -> Term k v -> Doc
550
prettyPrintTerm opt lv prec (c,xs)
1✔
551
  | len == 0  = pOptPrintCoeff opt lv (appPrec+1) c
×
552
    -- intentionally specify (appPrec+1) to parenthesize any composite expression
553
  | len == 1 && c == 1 = pPow prec $ head (mindices xs)
1✔
554
  | otherwise =
×
555
      maybeParens (prec > mulPrec) $
1✔
556
        PP.hcat $ intersperse (PP.char '*') fs
1✔
557
    where
558
      len = Map.size $ mindicesMap xs
1✔
559
      fs  = [pOptPrintCoeff opt lv (appPrec+1) c | c /= 1] ++ [pPow (mulPrec+1) p | p <- mindices xs]
×
560
      -- intentionally specify (appPrec+1) to parenthesize any composite expression
561

562
      pPow prec' (x,1) = pOptPrintVar opt lv prec' x
×
563
      pPow prec' (x,n) =
564
        maybeParens (prec' > expPrec) $
1✔
565
          pOptPrintVar opt lv (expPrec+1) x <> PP.char '^' <> PP.integer n
×
566

567
class PrettyCoeff a where
568
  pPrintCoeff :: PrettyLevel -> Rational -> a -> Doc
569
  isNegativeCoeff :: a -> Bool
570
  isNegativeCoeff _ = False
1✔
571

572
instance PrettyCoeff Integer where
573
  pPrintCoeff = pPrintPrec
1✔
574
  isNegativeCoeff = (0>)
1✔
575

576
instance (PrettyCoeff a, Integral a) => PrettyCoeff (Ratio a) where
577
  pPrintCoeff lv p r
1✔
578
    | denominator r == 1 = pPrintCoeff lv p (numerator r)
×
579
    | otherwise =
×
580
        maybeParens (p > ratPrec) $
1✔
581
          pPrintCoeff lv (ratPrec+1) (numerator r) <>
×
582
          PP.char '/' <>
1✔
583
          pPrintCoeff lv (ratPrec+1) (denominator r)
×
584
  isNegativeCoeff x = isNegativeCoeff (numerator x)
1✔
585

586
instance PrettyCoeff (FF.PrimeField a) where
587
  pPrintCoeff lv p a = pPrintCoeff lv p (FF.toInteger a)
×
588
  isNegativeCoeff _  = False
×
589

590
instance (Num c, Ord c, PrettyCoeff c, Ord v, PrettyVar v) => PrettyCoeff (Polynomial c v) where
1✔
591
  pPrintCoeff = pPrintPrec
1✔
592

593
class PrettyVar a where
594
  pPrintVar :: PrettyLevel -> Rational -> a -> Doc
595

596
instance PrettyVar Int where
597
  pPrintVar _ _ n = PP.char 'x' <> PP.int n
1✔
598

599
instance PrettyVar X where
600
  pPrintVar _ _ X = PP.char 'x'
1✔
601

602
addPrec, mulPrec, ratPrec, expPrec, appPrec :: Rational
603
addPrec = 6 -- Precedence of '+'
1✔
604
mulPrec = 7 -- Precedence of '*'
1✔
605
ratPrec = 7 -- Precedence of '/'
1✔
606
expPrec = 8 -- Precedence of '^'
1✔
607
appPrec = 10 -- Precedence of function application
1✔
608

609
{--------------------------------------------------------------------
610
  Univariate polynomials
611
--------------------------------------------------------------------}
612

613
-- | Univariate polynomials over commutative ring r
614
type UPolynomial r = Polynomial r X
615

616
-- | Variable "x"
617
data X = X
618
  deriving (Eq, Ord, Bounded, Enum, Show, Read, Typeable, Data)
×
619

620
instance NFData X where
621
   rnf a = a `seq` ()
×
622

623
instance Hashable X where
×
624
  hashWithSalt = hashUsing fromEnum
×
625

626
-- | Natural ordering /x^0 < x^1 < x^2 ../ is the unique monomial order for
627
-- univariate polynomials.
628
nat :: MonomialOrder X
629
nat = compare `on` deg
1✔
630

631
-- | division of univariate polynomials
632
div :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> UPolynomial k
633
div f1 f2 = fst (divMod f1 f2)
1✔
634

635
-- | division of univariate polynomials
636
mod :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> UPolynomial k
637
mod f1 f2 = snd (divMod f1 f2)
1✔
638

639
-- | division of univariate polynomials
640
divMod :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> (UPolynomial k, UPolynomial k)
641
divMod f g
1✔
642
  | isZero g  = error "ToySolver.Data.Polynomial.divMod: division by zero"
×
643
  | otherwise = go 0 f
×
644
  where
645
    lt_g = lt nat g
1✔
646
    go !q !r
1✔
647
      | deg r < deg g = (q,r)
1✔
648
      | otherwise     = go (q + t) (r - t * g)
×
649
        where
650
          lt_r = lt nat r
1✔
651
          t    = fromTerm $ lt_r `tdiv` lt_g
1✔
652

653
divides :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> Bool
654
divides f1 f2 = f2 `mod` f1 == 0
1✔
655

656
-- | GCD of univariate polynomials
657
gcd :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> UPolynomial k
658
gcd f1 0  = toMonic nat f1
1✔
659
gcd f1 f2 = gcd f2 (f1 `mod` f2)
1✔
660

661
-- | LCM of univariate polynomials
662
lcm :: (Eq k, Fractional k) => UPolynomial k -> UPolynomial k -> UPolynomial k
663
lcm _ 0 = 0
1✔
664
lcm 0 _ = 0
1✔
665
lcm f1 f2 = toMonic nat $ (f1 `mod` (gcd f1 f2)) * f2
×
666

667
-- | Extended GCD algorithm
668
exgcd
669
  :: (Eq k, Fractional k)
670
  => UPolynomial k
671
  -> UPolynomial k
672
  -> (UPolynomial k, UPolynomial k, UPolynomial k)
673
exgcd f1 f2 = f $ go f1 f2 1 0 0 1
1✔
674
  where
675
    go !r0 !r1 !s0 !s1 !t0 !t1
1✔
676
      | r1 == 0   = (r0, s0, t0)
1✔
677
      | otherwise = go r1 r2 s1 s2 t1 t2
×
678
      where
679
        (q, r2) = r0 `divMod` r1
1✔
680
        s2 = s0 - q*s1
1✔
681
        t2 = t0 - q*t1
1✔
682
    f (g,u,v)
1✔
683
      | lc_g == 0 = (g, u, v)
×
684
      | otherwise = (mapCoeff (/lc_g) g, mapCoeff (/lc_g) u, mapCoeff (/lc_g) v)
×
685
      where
686
        lc_g = lc nat g
1✔
687

688
-- | pseudo division
689
pdivMod :: (Eq r, Num r) => UPolynomial r -> UPolynomial r -> (r, UPolynomial r, UPolynomial r)
690
pdivMod _ 0 = error "ToySolver.Data.Polynomial.pdivMod: division by 0"
×
691
pdivMod f g
692
  | deg f < deg g = (1, 0, f)
1✔
693
  | otherwise     = go (deg f - deg g + 1) f 0
×
694
  where
695
    (lc_g, lm_g) = lt nat g
1✔
696
    b = lc_g ^ (deg f - deg_g + 1)
1✔
697
    deg_g = deg g
1✔
698
    go !n !f1 !q
1✔
699
      | deg_g > deg f1 = (b, q, scale (lc_g ^ n) f1)
1✔
700
      | otherwise      = go (n - 1) (scale lc_g f1 - s * g) (q + scale (lc_g ^ (n-1)) s)
×
701
          where
702
            (lc_f1, lm_f1) = lt nat f1
1✔
703
            s = fromTerm (lc_f1, lm_f1 `mdiv` lm_g)
1✔
704

705
-- | pseudo quotient
706
pdiv :: (Eq r, Num r) => UPolynomial r -> UPolynomial r -> UPolynomial r
707
pdiv f g =
1✔
708
  case f `pdivMod` g of
1✔
709
    (_, q, _) -> q
1✔
710

711
-- | pseudo reminder
712
pmod :: (Eq r, Num r) => UPolynomial r -> UPolynomial r -> UPolynomial r
713
pmod _ 0 = error "ToySolver.Data.Polynomial.pmod: division by 0"
×
714
pmod f g
715
  | deg f < deg g = f
1✔
716
  | otherwise     = go (deg f - deg g + 1) f
×
717
  where
718
    (lc_g, lm_g) = lt nat g
1✔
719
    deg_g = deg g
1✔
720
    go !n !f1
1✔
721
      | deg_g > deg f1 = scale (lc_g ^ n) f1
1✔
722
      | otherwise      = go (n - 1) (scale lc_g f1 - s * g)
×
723
          where
724
            (lc_f1, lm_f1) = lt nat f1
1✔
725
            s = fromTerm (lc_f1, lm_f1 `mdiv` lm_g)
1✔
726

727
-- | GCD of univariate polynomials
728
gcd' :: (Integral r) => UPolynomial r -> UPolynomial r -> UPolynomial r
729
gcd' f1 0  = ppI f1
1✔
730
gcd' f1 f2 = gcd' f2 (f1 `pmod` f2)
1✔
731

732
-- | Is the number a root of the polynomial?
733
isRootOf :: (Eq k, Num k) => k -> UPolynomial k -> Bool
734
isRootOf x p = eval (\_ -> x) p == 0
1✔
735

736
-- | Is the polynomial square free?
737
isSquareFree :: (Eq k, Fractional k) => UPolynomial k -> Bool
738
isSquareFree p = gcd p (deriv p X) == 1
1✔
739

740
{--------------------------------------------------------------------
741
  Term
742
--------------------------------------------------------------------}
743

744
type Term k v = (k, Monomial v)
745

746
type UTerm k = Term k X
747

748
tdeg :: Term k v -> Integer
749
tdeg (_,xs) = deg xs
×
750

751
tscale :: (Num k) => k -> Term k v -> Term k v
752
tscale a (c, xs) = (a*c, xs)
1✔
753

754
tmult :: (Num k, Ord v) => Term k v -> Term k v -> Term k v
755
tmult (c1,xs1) (c2,xs2) = (c1*c2, xs1 `mmult` xs2)
1✔
756

757
tdivides :: (Ord v) => Term k v -> Term k v -> Bool
758
tdivides (_,xs1) (_,xs2) = xs1 `mdivides` xs2
1✔
759

760
tdiv :: (Fractional k, Ord v) => Term k v -> Term k v -> Term k v
761
tdiv (c1,xs1) (c2,xs2) = (c1 / c2, xs1 `mdiv` xs2)
1✔
762

763
tderiv :: (Num k, Ord v) => Term k v -> v -> Term k v
764
tderiv (c,xs) x =
1✔
765
  case mderiv xs x of
1✔
766
    (s,ys) -> (c * fromIntegral s, ys)
1✔
767

768
tintegral :: (Fractional k, Ord v) => Term k v -> v -> Term k v
769
tintegral (c,xs) x =
1✔
770
  case mintegral xs x of
1✔
771
    (s,ys) -> (c * fromRational s, ys)
1✔
772

773
{--------------------------------------------------------------------
774
  Monic Monomial
775
--------------------------------------------------------------------}
776

777
-- 本当は変数の型に応じて type family で表現を変えたい
778

779
-- | Monic monomials
780
newtype Monomial v = Monomial{ mindicesMap :: Map v Integer }
1✔
781
  deriving (Eq, Ord, Typeable)
×
782

783
type UMonomial = Monomial X
784

785
instance (Show v) => Show (Monomial v) where
×
786
  showsPrec d m  = showParen (d > 10) $
×
787
    showString "mfromIndices " . shows (mindices m)
×
788

789
instance (Ord v, IsString v) => IsString (Monomial v) where
790
  fromString s = var (fromString s)
×
791

792
instance (NFData v) => NFData (Monomial v) where
793
  rnf (Monomial m) = rnf m
×
794

795
instance Degree (Monomial v) where
796
  deg (Monomial m) = sum $ Map.elems m
1✔
797

798
instance Ord v => Var (Monomial v) v where
799
  var x = Monomial $ Map.singleton x 1
1✔
800

801
instance Ord v => Vars (Monomial v) v where
802
  vars mm = Map.keysSet (mindicesMap mm)
×
803

804
instance Hashable v => Hashable (Monomial v) where
×
805
  hashWithSalt = hashUsing (Map.toList . mindicesMap)
×
806

807
mone :: Monomial v
808
mone = Monomial $ Map.empty
1✔
809

810
mfromIndices :: Ord v => [(v, Integer)] -> Monomial v
811
mfromIndices xs
1✔
812
  | any (\(_,e) -> 0>e) xs = error "ToySolver.Data.Polynomial.mfromIndices: negative exponent"
×
813
  | otherwise = Monomial $ Map.fromListWith (+) [(x,e) | (x,e) <- xs, e > 0]
×
814

815
mfromIndicesMap :: Ord v => Map v Integer -> Monomial v
816
mfromIndicesMap m
1✔
817
  | any (\(_,e) -> 0>e) (Map.toList m) = error "ToySolver.Data.Polynomial.mfromIndicesMap: negative exponent"
×
818
  | otherwise = mfromIndicesMap' m
×
819

820
mfromIndicesMap' :: Map v Integer -> Monomial v
821
mfromIndicesMap' m = Monomial $ Map.filter (>0) m
1✔
822

823
mindices :: Monomial v -> [(v, Integer)]
824
mindices = Map.toAscList . mindicesMap
1✔
825

826
mmult :: Ord v => Monomial v -> Monomial v -> Monomial v
827
mmult (Monomial xs1) (Monomial xs2) = mfromIndicesMap' $ Map.unionWith (+) xs1 xs2
1✔
828

829
mpow :: Ord v => Monomial v -> Integer -> Monomial v
830
mpow _ 0 = mone
1✔
831
mpow m 1 = m
1✔
832
mpow (Monomial xs) e
833
  | 0 > e     = error "ToySolver.Data.Polynomial.mpow: negative exponent"
×
834
  | otherwise = Monomial $ Map.map (e*) xs
×
835

836
mdivides :: Ord v => Monomial v -> Monomial v -> Bool
837
mdivides (Monomial xs1) (Monomial xs2) = Map.isSubmapOfBy (<=) xs1 xs2
1✔
838

839
mdiv :: Ord v => Monomial v -> Monomial v -> Monomial v
840
mdiv (Monomial xs1) (Monomial xs2) = Monomial $ Map.differenceWith f xs1 xs2
1✔
841
  where
842
    f m n
1✔
843
      | m <= n    = Nothing
1✔
844
      | otherwise = Just (m - n)
×
845

846
mderiv :: Ord v => Monomial v -> v -> (Integer, Monomial v)
847
mderiv (Monomial xs) x
1✔
848
  | n==0      = (0, mone)
×
849
  | otherwise = (n, Monomial $ Map.update f x xs)
×
850
  where
851
    n = Map.findWithDefault 0 x xs
1✔
852
    f m
1✔
853
      | m <= 1    = Nothing
1✔
854
      | otherwise = Just $! m - 1
×
855

856
mintegral :: Ord v => Monomial v -> v -> (Rational, Monomial v)
857
mintegral (Monomial xs) x =
1✔
858
  (1 % fromIntegral (n + 1), Monomial $ Map.insert x (n+1) xs)
1✔
859
  where
860
    n = Map.findWithDefault 0 x xs
1✔
861

862
mlcm :: Ord v => Monomial v -> Monomial v -> Monomial v
863
mlcm (Monomial m1) (Monomial m2) = Monomial $ Map.unionWith max m1 m2
1✔
864

865
mgcd :: Ord v => Monomial v -> Monomial v -> Monomial v
866
mgcd (Monomial m1) (Monomial m2) = Monomial $ Map.intersectionWith min m1 m2
1✔
867

868
mcoprime :: Ord v => Monomial v -> Monomial v -> Bool
869
mcoprime m1 m2 = mgcd m1 m2 == mone
1✔
870

871
{--------------------------------------------------------------------
872
  Monomial Order
873
--------------------------------------------------------------------}
874

875
type MonomialOrder v = Monomial v -> Monomial v -> Ordering
876

877
-- | Lexicographic order
878
lex :: Ord v => MonomialOrder v
879
lex = go `on` mindices
1✔
880
  where
881
    go [] [] = EQ
1✔
882
    go [] _  = LT -- = compare 0 n2
1✔
883
    go _ []  = GT -- = compare n1 0
1✔
884
    go ((x1,n1):xs1) ((x2,n2):xs2) =
885
      case compare x1 x2 of
1✔
886
        LT -> GT -- = compare n1 0
1✔
887
        GT -> LT -- = compare 0 n2
1✔
888
        EQ -> compare n1 n2 `mappend` go xs1 xs2
1✔
889

890
-- | Reverse lexicographic order.
891
--
892
-- Note that revlex is /NOT/ a monomial order.
893
revlex :: Ord v => Monomial v -> Monomial v -> Ordering
894
revlex = go `on` (Map.toDescList . mindicesMap)
1✔
895
  where
896
    go [] [] = EQ
1✔
897
    go [] _  = GT -- = cmp 0 n2
×
898
    go _ []  = LT -- = cmp n1 0
×
899
    go ((x1,n1):xs1) ((x2,n2):xs2) =
900
      case compare x1 x2 of
1✔
901
        LT -> GT -- = cmp 0 n2
1✔
902
        GT -> LT -- = cmp n1 0
1✔
903
        EQ -> cmp n1 n2 `mappend` go xs1 xs2
1✔
904
    cmp n1 n2 = compare n2 n1
1✔
905

906
-- | Graded lexicographic order
907
grlex :: Ord v => MonomialOrder v
908
grlex = (compare `on` deg) `mappend` lex
1✔
909

910
-- | Graded reverse lexicographic order
911
grevlex :: Ord v => MonomialOrder v
912
grevlex = (compare `on` deg) `mappend` revlex
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc