lol-0.0.1.0: src/Crypto/Lol/Cyclotomic/Tensor/RepaTensor/CRT.hs
{-# LANGUAGE ConstraintKinds, FlexibleContexts, GADTs, NoImplicitPrelude,
ScopedTypeVariables #-}
-- | Functions to support the chinese remainder transform on Repa arrays
module Crypto.Lol.Cyclotomic.Tensor.RepaTensor.CRT
( scalarCRT'
, fCRT, fCRTInv
, mulGCRT', divGCRT'
, gCRT, gInvCRT
) where
import Crypto.Lol.CRTrans
import Crypto.Lol.Cyclotomic.Tensor
import Crypto.Lol.Cyclotomic.Tensor.RepaTensor.GL
import Crypto.Lol.Cyclotomic.Tensor.RepaTensor.RTCommon as RT
import Crypto.Lol.LatticePrelude as LP
import Control.Applicative
import Data.Coerce
import Data.Singletons.Prelude
import Data.Type.Natural as N hiding (Z, one, zero)
-- | Embeds a scalar into the CRT-basis when such basis exists
scalarCRT' :: forall m r . (Fact m, CRTrans r, Unbox r)
=> Maybe (r -> Arr m r)
scalarCRT'
= let pps = proxy ppsFact (Proxy::Proxy m)
sz = Z :. totientPPs pps
in pure $ Arr . force . fromFunction sz . const
-- | Multiplies an array in the CRT basis by 'g', when the CRT basis exists
mulGCRT' :: forall m r . (Fact m, CRTrans r, Unbox r, Elt r)
=> Maybe (Arr m r -> Arr m r)
mulGCRT' = (coerce (\x -> force . RT.zipWith (*) x) `asTypeOf` asTypeOf) <$> gCRT
-- | Divides an array in the CRT basis by 'g', when the CRT basis exists.
divGCRT' :: (Fact m, CRTrans r, IntegralDomain r, ZeroTestable r,
Unbox r, Elt r) => Maybe (Arr m r -> Arr m r)
divGCRT' = (coerce (\x -> force . RT.zipWith (*) x) `asTypeOf` asTypeOf) <$> gInvCRT
-- | The CRT-basis representation of 'g'
gCRT :: (Fact m, CRTrans r, Unbox r, Elt r)
=> Maybe (Arr m r)
gCRT = fCRT <*> pure (fGPow $ scalarPow' LP.one)
-- EAC: This was defined using (a safe call to) fromJust
-- | The CRT-basis representation of 'g^{ -1 }'
gInvCRT:: (Fact m, CRTrans r, IntegralDomain r,
ZeroTestable r, Unbox r, Elt r)
=> Maybe (Arr m r)
gInvCRT = fCRT <*> fGInvPow (scalarPow' LP.one)
fCRT, fCRTInv ::
forall m r . (Fact m, CRTrans r, Unbox r, Elt r)
=> Maybe (Arr m r -> Arr m r)
-- | The chinese remainder transform on Repa arrays.
-- Exists if and only if crt exists for all prime powers
fCRT = evalM $ fTensor ppCRT
-- divide by mhat after doing crtInv'
-- | The inverse chinese remainder transform on Repa arrays.
-- Exists if and only if crt exists for all prime powers
fCRTInv = do -- in Maybe
(_, mhatInv) :: (CRTInfo r) <- proxyT crtInfoFact (Proxy :: Proxy m)
let totm = proxy totientFact (Proxy :: Proxy m)
divMhat = trans totm $ RT.map (*mhatInv)
evalM $ (divMhat .*) <$> fTensor ppCRTInv'
ppDFT, ppDFTInv', ppCRT, ppCRTInv' ::
forall pp r . (PPow pp, CRTrans r, Unbox r, Elt r)
=> TaggedT pp Maybe (Trans r)
ppDFT = case (sing :: SPrimePower pp) of
(SPP (STuple2 _ SZ)) -> return $ Id 1
spp@(SPP (STuple2 sp (SS se1))) ->
tagT $ do
let spp' = SPP (STuple2 sp se1)
pp'dft <- withWitnessT ppDFT spp'
pptwid <- withWitnessT (ppTwid False) spp
pdft <- withWitnessT pDFT sp
return $ (pp'dft @* Id (dim pdft)) .* pptwid .* (Id (dim pp'dft) @* pdft)
ppDFTInv' = case (sing :: SPrimePower pp) of
(SPP (STuple2 _ SZ)) -> return $ Id 1
spp@(SPP (STuple2 sp (SS se1))) ->
tagT $ do
let spp' = SPP (STuple2 sp se1)
pp'dftInv' <- withWitnessT ppDFTInv' spp'
pptwidInv <- withWitnessT (ppTwid True) spp
pdftInv' <- withWitnessT pDFTInv' sp
return $
(Id (dim pp'dftInv') @* pdftInv') .* pptwidInv .*
(pp'dftInv' @* Id (dim pdftInv'))
ppCRT = case (sing :: SPrimePower pp) of
(SPP (STuple2 _ SZ)) -> return $ Id 1
spp@(SPP (STuple2 sp (SS se1))) ->
tagT $ do
let spp' = SPP (STuple2 sp se1)
pp'dft <- withWitnessT ppDFT spp'
pptwid <- withWitnessT (ppTwidHat False) spp
pcrt <- withWitnessT pCRT sp
return $
(pp'dft @* Id (dim pcrt)) .* pptwid .*
-- save some work when p=2
(if dim pcrt > 1 then Id (dim pp'dft) @* pcrt else Id (dim pp'dft))
ppCRTInv' = case (sing :: SPrimePower pp) of
(SPP (STuple2 _ SZ)) -> return $ Id 1
spp@(SPP (STuple2 sp (SS se1))) ->
tagT $ do
let spp' = SPP (STuple2 sp se1)
pp'dftInv' <- withWitnessT ppDFTInv' spp'
pptwidInv <- withWitnessT (ppTwidHat True) spp
pcrtInv' <- withWitnessT pCRTInv' sp
return $ -- special case for p=2 (necessary for scaling!)
(if dim pcrtInv' > 1
then Id (dim pp'dftInv') @* pcrtInv' else Id (dim pp'dftInv')) .*
pptwidInv .* (pp'dftInv' @* Id (dim pcrtInv'))
-- DFT_p, CRT_p, (scaled) DFT_p^-1, etc.
pDFT, pDFTInv', pCRT, pCRTInv' ::
forall p r . (NatC p, CRTrans r, Unbox r, Elt r)
=> TaggedT p Maybe (Trans r)
pDFT = let pval = proxy valueNatC (Proxy::Proxy p)
in do (omegaPPow, _) <- crtInfoNatC
return $ trans pval $ mulMat $ force $
fromFunction (Z :. pval :. pval)
(\(Z:.i:.j) -> omegaPPow (i*j))
pDFTInv' = let pval = proxy valueNatC (Proxy::Proxy p)
in do (omegaPPow, _) <- crtInfoNatC
return $ trans pval $ mulMat $ force $
fromFunction (Z :. pval :. pval)
(\(Z:.i:.j) -> omegaPPow (-i*j))
pCRT = let pval = proxy valueNatC (Proxy::Proxy p)
in do (omegaPPow, _) <- crtInfoNatC
return $ trans (pval-1) $ mulMat $ force $
fromFunction (Z :. pval-1 :. pval-1)
(\(Z:.i:.j) -> omegaPPow ((i+1)*j))
-- crt_p * this = pI, for all values of p. For p=2 this isn't the
-- matrix we "want," but it doesn't matter because we don't use it in
-- ppCRTInv'
pCRTInv' =
let pval = proxy valueNatC (Proxy::Proxy p)
in do (omegaPPow, _) <- crtInfoNatC
return $ trans (pval-1) $ mulMat $ force $
fromFunction (Z :. pval-1 :. pval-1)
(\(Z:.i:.j) -> omegaPPow (negate i*(j+1)) -
omegaPPow (j+1))
-- twiddle factors for DFT_pp and CRT_pp decompositions
ppTwid, ppTwidHat ::
forall pp r . (PPow pp, CRTrans r, Unbox r, Elt r)
=> Bool -> TaggedT pp Maybe (Trans r)
ppTwid inv =
let pp@(p,e) = proxy ppPPow (Proxy :: Proxy pp)
ppval = valuePP pp
in do
(omegaPPPow, _) <- crtInfoPPow
return $ trans ppval $ mulDiag $ force $
fromFunction (Z :. ppval)
(\(Z:.i) -> let (iq,ir) = i `divMod` p
pow = (if inv then negate else id)
ir * digitRev (p,e-1) iq
in omegaPPPow pow)
ppTwidHat inv =
let pp@(p,e) = proxy ppPPow (Proxy :: Proxy pp)
pptot = totientPP pp
in do
(omegaPPPow, _) <- crtInfoPPow
return $ trans pptot $ mulDiag $ force $
fromFunction (Z :. pptot)
(\(Z:.i) -> let (iq,ir) = i `divMod` (p-1)
pow = (if inv then negate else id)
(ir+1) * digitRev (p,e-1) iq
in omegaPPPow pow)