------------------------------------------------------------------------------- |-- Module : Data.SBV.BitVectors.Polynomials-- Copyright : (c) Levent Erkok-- License : BSD3-- Maintainer : erkokl@gmail.com-- Stability : experimental-- Portability : portable---- Implementation of polynomial arithmetic-----------------------------------------------------------------------------{-# LANGUAGE TypeSynonymInstances #-}{-# LANGUAGE FlexibleContexts #-}{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE PatternGuards #-}moduleData.SBV.BitVectors.Polynomial(Polynomial(..))whereimportData.Bits(Bits(..))importData.List(genericTake)importData.Maybe(fromJust)importData.Word(Word8,Word16,Word32,Word64)importData.SBV.BitVectors.DataimportData.SBV.BitVectors.ModelimportData.SBV.BitVectors.SplittableimportData.SBV.Utils.Boolean-- | Implements polynomial addition, multiplication, division, and modulus operations-- over GF(2^n). NB. Similar to 'bvQuotRem', division by @0@ is interpreted as follows:---- @x `pDivMod` 0 = (0, x)@---- for all @x@ (including @0@)---- Minimal complete definiton: 'pMult', 'pDivMod', 'showPoly'classBitsa=>Polynomialawhere-- | Given bit-positions to be set, create a polynomial-- For instance---- @polynomial [0, 1, 3] :: SWord8@-- -- will evaluate to @11@, since it sets the bits @0@, @1@, and @3@. Mathematicans would write this polynomial-- as @x^3 + x + 1@. And in fact, 'showPoly' will show it like that.polynomial::[Int]->a-- | Add two polynomials in GF(2^n)pAdd::a->a->a-- | Multiply two polynomials in GF(2^n), and reduce it by the irreducible specified by-- the polynomial as specified by coefficients of the third argument. Note that the third-- argument is specifically left in this form as it is usally in GF(2^(n+1)), which is not available in our-- formalism. (That is, we would need SWord9 for SWord8 multiplication, etc.) Also note that we do not-- support symbolic irreducibles, which is a minor shortcoming. (Most GF's will come with fixed irreducibles,-- so this should not be a problem in practice.)---- Passing [] for the third argument will multiply the polynomials and then ignore the higher bits that won't-- fit into the resulting size.pMult::(a,a,[Int])->a-- | Divide two polynomials in GF(2^n), see above note for division by 0pDiv::a->a->a-- | Compute modulus of two polynomials in GF(2^n), see above note for modulus by 0pMod::a->a->a-- | Division and modulus packed togetherpDivMod::a->a->(a,a)-- | Display a polynomial like a mathematician would (over the monomial @x@)showPoly::a->String-- defaults.. Minumum complete definition: pMult, pDivMod, showPolypolynomial=foldr(flipsetBit)0pAdd=xorpDivxy=fst(pDivModxy)pModxy=snd(pDivModxy)instancePolynomialWord8where{showPoly=sp;pMult=liftpolyMult;pDivMod=liftCpolyDivMod}instancePolynomialWord16where{showPoly=sp;pMult=liftpolyMult;pDivMod=liftCpolyDivMod}instancePolynomialWord32where{showPoly=sp;pMult=liftpolyMult;pDivMod=liftCpolyDivMod}instancePolynomialWord64where{showPoly=sp;pMult=liftpolyMult;pDivMod=liftCpolyDivMod}instancePolynomialSWord8where{showPoly=liftSsp;pMult=polyMult;pDivMod=polyDivMod}instancePolynomialSWord16where{showPoly=liftSsp;pMult=polyMult;pDivMod=polyDivMod}instancePolynomialSWord32where{showPoly=liftSsp;pMult=polyMult;pDivMod=polyDivMod}instancePolynomialSWord64where{showPoly=liftSsp;pMult=polyMult;pDivMod=polyDivMod}lift::SymWorda=>((SBVa,SBVa,[Int])->SBVa)->(a,a,[Int])->aliftf(x,y,z)=fromJust$unliteral$f(literalx,literaly,z)liftC::SymWorda=>(SBVa->SBVa->(SBVa,SBVa))->a->a->(a,a)liftCfxy=let(a,b)=f(literalx)(literaly)in(fromJust(unliterala),fromJust(unliteralb))liftS::SymWorda=>(a->String)->SBVa->StringliftSfs|Justx<-unliterals=fx|True=shows-- | Pretty print as a polynomialsp::Bitsa=>a->Stringspa|nullcs="0"++t|True=foldr(\xy->shx++" + "++y)(sh(lastcs))(initcs)++twheret=" :: GF(2^"++shown++")"n=bitSizeais=[n-1,n-2..0]cs=mapfst$filtersnd$zipis(map(testBita)is)sh0="1"sh1="x"shi="x^"++showi-- | Add two polynomialsaddPoly::[SBool]->[SBool]->[SBool]addPolyxs[]=xsaddPoly[]ys=ysaddPoly(x:xs)(y:ys)=x<+>y:addPolyxsysites::SBool->[SBool]->[SBool]->[SBool]itessxsys|Justt<-unliterals=iftthenxselseys|True=goxsyswherego[][]=[]go[](b:bs)=itesfalseb:go[]bsgo(a:as)[]=itesafalse:goas[]go(a:as)(b:bs)=itesab:goasbs-- | Multiply two polynomials and reduce by the third (concrete) irreducible, given by its coefficients.-- See the remarks for the 'pMult' function for this design choicepolyMult::(Bitsa,SymWorda,FromBits(SBVa))=>(SBVa,SBVa,[Int])->SBVapolyMult(x,y,red)=fromBitsLE$genericTakesz$r++repeatfalsewhere(_,r)=mdpmsrsms=genericTake(2*sz)$mul(blastLEx)(blastLEy)[]++repeatfalsers=genericTake(2*sz)$[ifi`elem`redthentrueelsefalse|i<-[0..foldrmax0red]]++repeatfalsesz=sizeOfxmul_[]ps=psmulas(b:bs)ps=mul(false:as)bs(itesb(as`addPoly`ps)ps)polyDivMod::(Bitsa,SymWorda,FromBits(SBVa))=>SBVa->SBVa->(SBVa,SBVa)polyDivModxy=ite(y.==0)(0,x)(adjustd,adjustr)whereadjustxs=fromBitsLE$genericTakesz$xs++repeatfalsesz=sizeOfx(d,r)=mdp(blastLEx)(blastLEy)-- conservative over-approximation of the degreedegree::[SBool]->Intdegreexs=walk(lengthxs-1)$reversexswherewalkn[]=nwalkn(b:bs)|Justt<-unliteralb=iftthennelsewalk(n-1)bs|True=n-- over-estimatemdp::[SBool]->[SBool]->([SBool],[SBool])mdpxsys=go(lengthys-1)(reverseys)wheredegTop=degreexsgo_[]=error"SBV.Polynomial.mdp: Impossible happened; exhausted ys before hitting 0"gon(b:bs)|n==0=(reverseqs,rs)|True=let(rqs,rrs)=go(n-1)bsin(itesb(reverseqs)rqs,itesbrsrrs)wheredegQuot=degTop-nys'=replicatedegQuotfalse++ys(qs,rs)=divx(degQuot+1)degTopxsys'-- return the element at index i; if not enough elements, return false-- N.B. equivalent to '(xs ++ repeat false) !! i', but more efficientidx::[SBool]->Int->SBoolidx[]_=falseidx(x:_)0=xidx(_:xs)i=idxxs(i-1)divx::Int->Int->[SBool]->[SBool]->([SBool],[SBool])divxn_xs_|n<=0=([],xs)divxnixsys'=(q:qs,rs)whereq=xs`idx`ixs'=itesq(xs`addPoly`ys')xs(qs,rs)=divx(n-1)(i-1)xs'(tailys')