module Crypto.Lol.Cyclotomic.Tensor
( Tensor(..), CRTElt
, hasCRTFuncs
, scalarCRT, mulGCRT, divGCRT, crt, crtInv, twaceCRT, embedCRT
, Matrix, indexM, twCRTs
, zmsToIndexFact
, indexInfo
, extIndicesPowDec, extIndicesCRT, extIndicesCoeffs
, baseIndicesPow, baseIndicesDec, baseIndicesCRT
, digitRev
)
where
import Crypto.Lol.CRTrans
import Crypto.Lol.LatticePrelude as LP hiding (lift, (*>))
import Crypto.Lol.Types.FiniteField
import Control.Applicative
import Control.DeepSeq
import Control.Monad.Random
import Data.Constraint
import Data.Singletons.Prelude hiding ((:-))
import Data.Traversable
import Data.Tuple (swap)
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
type CRTElt t r = (ZeroTestable r, IntegralDomain r, CRTrans r, TElt t r)
class (TElt t Double, TElt t (Complex Double))
=> Tensor (t :: Factored -> * -> *) where
type TElt t r :: Constraint
entailIndexT :: Tagged (t m r)
(Fact m :- (Applicative (t m), Traversable (t m)))
entailEqT :: Tagged (t m r)
((Eq r, Fact m, TElt t r) :- Eq (t m r))
entailZTT :: Tagged (t m r)
((ZeroTestable r, Fact m, TElt t r) :- ZeroTestable (t m r))
entailNFDataT :: Tagged (t m r)
((NFData r, Fact m, TElt t r) :- NFData (t m r))
entailRandomT :: Tagged (t m r)
((Random r, Fact m, TElt t r) :- Random (t m r))
entailShowT :: Tagged (t m r)
((Show r, Fact m, TElt t r) :- (Show (t m r)))
entailModuleT :: Tagged (GF fp d, t m fp)
((GFCtx fp d, Fact m, TElt t fp) :- Module (GF fp d) (t m fp))
scalarPow :: (Additive r, Fact m, TElt t r) => r -> t m r
l, lInv :: (Additive r, Fact m, TElt t r) => t m r -> t m r
mulGPow, mulGDec :: (Ring r, Fact m, TElt t r) => t m r -> t m r
divGPow, divGDec :: (ZeroTestable r, IntegralDomain r, Fact m, TElt t r)
=> t m r -> Maybe (t m r)
crtFuncs :: (Fact m, CRTElt t r) =>
Maybe ( r -> t m r,
t m r -> t m r,
t m r -> t m r,
t m r -> t m r,
t m r -> t m r)
tGaussianDec :: (OrdFloat q, Random q, TElt t q,
ToRational v, Fact m, MonadRandom rnd)
=> v -> rnd (t m q)
gSqNormDec :: (Ring r, Fact m, TElt t r) => t m r -> r
twacePowDec :: (Ring r, m `Divides` m', TElt t r) => t m' r -> t m r
embedPow, embedDec :: (Additive r, m `Divides` m', TElt t r)
=> t m r -> t m' r
crtExtFuncs :: (m `Divides` m', CRTElt t r) =>
Maybe (t m' r -> t m r,
t m r -> t m' r)
coeffs :: (Ring r, m `Divides` m', TElt t r) => t m' r -> [t m r]
powBasisPow :: (Ring r, TElt t r, m `Divides` m') => Tagged m [t m' r]
crtSetDec :: (m `Divides` m', PrimeField fp, Coprime (PToF (CharOf fp)) m',
TElt t fp)
=> Tagged m [t m' fp]
fmapT :: (Fact m, TElt t a, TElt t b) => (a -> b) -> t m a -> t m b
fmapTM :: (Monad mon, Fact m, TElt t a, TElt t b)
=> (a -> mon b) -> t m a -> mon (t m b)
zipWithT :: (Fact m, TElt t a, TElt t b, TElt t c)
=> (a -> b -> c) -> t m a -> t m b -> t m c
unzipTElt :: (Fact m, TElt t (a,b), TElt t a, TElt t b)
=> t m (a,b) -> (t m a, t m b)
unzipT :: (Fact m) => t m (a,b) -> (t m a, t m b)
hasCRTFuncs :: forall t m r . (Tensor t, Fact m, CRTElt t r)
=> TaggedT (t m r) Maybe ()
hasCRTFuncs = tagT $ do
(_ :: r -> t m r,_,_,_,_) <- crtFuncs
return ()
scalarCRT :: (Tensor t, Fact m, CRTElt t r) => Maybe (r -> t m r)
scalarCRT = (\(f,_,_,_,_) -> f) <$> crtFuncs
mulGCRT, divGCRT, crt, crtInv ::
(Tensor t, Fact m, CRTElt t r) => Maybe (t m r -> t m r)
mulGCRT = (\(_,f,_,_,_) -> f) <$> crtFuncs
divGCRT = (\(_,_,f,_,_) -> f) <$> crtFuncs
crt = (\(_,_,_,f,_) -> f) <$> crtFuncs
crtInv = (\(_,_,_,_,f) -> f) <$> crtFuncs
twaceCRT :: forall t r m m' . (Tensor t, m `Divides` m', CRTElt t r)
=> Maybe (t m' r -> t m r)
twaceCRT = proxyT hasCRTFuncs (Proxy::Proxy (t m' r)) *>
proxyT hasCRTFuncs (Proxy::Proxy (t m r)) *>
(fst <$> crtExtFuncs)
embedCRT :: forall t r m m' . (Tensor t, m `Divides` m', CRTElt t r)
=> Maybe (t m r -> t m' r)
embedCRT = proxyT hasCRTFuncs (Proxy::Proxy (t m' r)) *>
proxyT hasCRTFuncs (Proxy::Proxy (t m r)) *>
(snd <$> crtExtFuncs)
fMatrix :: forall m r mon . (Fact m, Monad mon, Ring r)
=> (forall pp . (PPow pp) => TaggedT pp mon (MatrixC r))
-> TaggedT m mon (Matrix r)
fMatrix mat = tagT $ go $ sUnF (sing :: SFactored m)
where go :: Sing (pplist :: [PrimePower]) -> mon (Matrix r)
go spps = case spps of
SNil -> return MNil
(SCons spp rest) -> do
rest' <- go rest
mat' <- withWitnessT mat spp
return $ MKron rest' mat'
data MatrixC r =
MC (Int -> Int -> r)
Int Int
data Matrix r = MNil | MKron (Matrix r) (MatrixC r)
indexM :: Ring r => Matrix r -> Int -> Int -> r
indexM MNil 0 0 = LP.one
indexM (MKron m (MC mc r c)) i j =
let (iq,ir) = i `divMod` r
(jq,jr) = j `divMod` c
in indexM m iq jq * mc ir jr
twCRTs :: (Fact m, CRTrans r) => TaggedT m Maybe (Matrix r)
twCRTs = fMatrix twCRTsPPow
twCRTsPPow :: (PPow pp, CRTrans r) => TaggedT pp Maybe (MatrixC r)
twCRTsPPow = do
phi <- pureT totientPPow
iToZms <- pureT indexToZmsPPow
jToPow <- pureT indexToPowPPow
(wPow, _) <- crtInfoPPow
gEmb <- gEmbPPow
return $ MC (\j i -> let i' = iToZms i
in wPow (jToPow j * negate i') * gEmb i') phi phi
digitRev :: PP -> Int -> Int
digitRev (_,0) 0 = 0
digitRev (p,e) j
| e >= 1 = let (q,r) = j `divMod` p
in r * (p^(e1)) + digitRev (p,e1) q
indexToPowPPow, indexToZmsPPow :: PPow pp => Tagged pp (Int -> Int)
indexToPowPPow = indexToPow <$> ppPPow
indexToZmsPPow = indexToZms <$> ppPPow
zmsToIndexFact :: Fact m => Tagged m (Int -> Int)
zmsToIndexFact = zmsToIndex <$> ppsFact
indexToPow :: PP -> Int -> Int
indexToPow (p,e) j = let (jq,jr) = j `divMod` (p1)
in p^(e1)*jr + digitRev (p,e1) jq
indexToZms :: PP -> Int -> Int
indexToZms (p,_) i = let (i1,i0) = i `divMod` (p1)
in p*i1 + i0 + 1
zmsToIndex :: [PP] -> Int -> Int
zmsToIndex [] _ = 0
zmsToIndex (pp:rest) i = zmsToIndexPP pp (i `mod` valuePP pp)
+ (totientPP pp) * zmsToIndex rest i
zmsToIndexPP :: PP -> Int -> Int
zmsToIndexPP (p,_) i = let (i1,i0) = i `divMod` p
in (p1)*i1 + i0 1
toIndexPair :: [(Int,Int)] -> Int -> (Int,Int)
fromIndexPair :: [(Int,Int)] -> (Int,Int) -> Int
toIndexPair [] 0 = (0,0)
toIndexPair ((phi,phi'):rest) i' =
let (i'q,i'r) = i' `divMod` phi'
(i'rq,i'rr) = i'r `divMod` phi
(i'q1,i'q0) = toIndexPair rest i'q
in (i'rq + i'q1*(phi' `div` phi), i'rr + i'q0*phi)
fromIndexPair [] (0,0) = 0
fromIndexPair ((phi,phi'):rest) (i1,i0) =
let (i0q,i0r) = i0 `divMod` phi
(i1q,i1r) = i1 `divMod` (phi' `div` phi)
i = fromIndexPair rest (i1q,i0q)
in (i0r + i1r*phi) + i*phi'
indexInfo :: forall m m' . (m `Divides` m')
=> Tagged '(m, m') ([(Int,Int,Int)], Int, Int, [(Int,Int)])
indexInfo = let pps = proxy ppsFact (Proxy::Proxy m)
pps' = proxy ppsFact (Proxy::Proxy m')
mpps = mergePPs pps pps'
phi = totientPPs pps
phi' = totientPPs pps'
tots = totients mpps
in tag (mpps, phi, phi', tots)
extIndicesPowDec :: (m `Divides` m') => Tagged '(m, m') (U.Vector Int)
extIndicesPowDec = do
(_, phi, _, tots) <- indexInfo
return $ U.generate phi (fromIndexPair tots . (0,))
extIndicesCRT :: forall m m' . (m `Divides` m')
=> Tagged '(m, m') (U.Vector Int)
extIndicesCRT = do
(_, phi, phi', tots) <- indexInfo
return $ U.generate phi'
(fromIndexPair tots . swap . (`divMod` (phi' `div` phi)))
baseWrapper :: forall m m' a . (m `Divides` m', U.Unbox a)
=> ([(Int,Int,Int)] -> Int -> a)
-> Tagged '(m, m') (U.Vector a)
baseWrapper f = do
(mpps, _, phi', _) <- indexInfo
return $ U.generate phi' (f mpps)
baseIndicesPow :: forall m m' . (m `Divides` m')
=> Tagged '(m, m') (U.Vector (Int,Int))
baseIndicesDec :: forall m m' . (m `Divides` m')
=> Tagged '(m, m') (U.Vector (Maybe (Int,Bool)))
baseIndicesCRT :: forall m m' . (m `Divides` m')
=> Tagged '(m, m') (U.Vector Int)
baseIndicesPow = baseWrapper (toIndexPair . totients)
baseIndicesDec = baseWrapper baseIndexDec
baseIndicesCRT =
baseWrapper (\pps -> snd . toIndexPair (totients pps))
extIndicesCoeffs :: forall m m' . (m `Divides` m')
=> Tagged '(m, m') (V.Vector (U.Vector Int))
extIndicesCoeffs = do
(_, phi, phi', tots) <- indexInfo
return $ V.generate (phi' `div` phi)
(\i1 -> U.generate phi (\i0 -> fromIndexPair tots (i1,i0)))
baseIndexDec :: [(Int,Int,Int)] -> Int -> Maybe (Int, Bool)
baseIndexDec [] 0 = Just (0,False)
baseIndexDec ((p,e,e'):rest) i'
= let (i'q, i'r) = i' `divMod` totientPP (p,e')
phi = totientPP (p,e)
curr
| p>2 && e==0 && e' > 0 = case i'r of
0 -> Just (0,False)
1 -> Just (0,True)
_ -> Nothing
| otherwise = if i'r < phi then Just (i'r,False) else Nothing
in do
(i,b) <- curr
(j,b') <- baseIndexDec rest i'q
return (i + phi*j, b /= b')
mergePPs :: [PP] -> [PP] -> [(Int,Int,Int)]
mergePPs [] pps = LP.map (\(p,e) -> (p,0,e)) pps
mergePPs allpps@((p,e):pps) ((p',e'):pps')
| p == p' && e <= e' = (p, e, e') : mergePPs pps pps'
| p > p' = (p', 0, e') : mergePPs allpps pps'
totients :: [(Int, Int, Int)] -> [(Int,Int)]
totients = LP.map (\(p,e,e') -> (totientPP (p,e), totientPP (p,e')))