lol-0.7.0.0: Crypto/Lol/Cyclotomic/Cyc.hs
{-|
Module : Crypto.Lol.Cyclotomic.Cyc
Description : An implementation of cyclotomic rings that hides and
automatically manages the internal representations of
ring elements.
Copyright : (c) Eric Crockett, 2011-2018
Chris Peikert, 2011-2018
License : GPL-3
Maintainer : ecrockett0@gmail.com
Stability : experimental
Portability : POSIX
\( \def\Z{\mathbb{Z}} \)
\( \def\F{\mathbb{F}} \)
\( \def\Q{\mathbb{Q}} \)
\( \def\Tw{\text{Tw}} \)
\( \def\Tr{\text{Tr}} \)
\( \def\O{\mathcal{O}} \)
An implementation of cyclotomic rings that hides the
internal representations of ring elements (e.g., the choice of
basis), and also offers more efficient storage and operations on
subring elements (including elements from the base ring itself).
For an implementation that allows (and requires) the programmer to
control the underlying representation, see
"Crypto.Lol.Cyclotomic.CycRep".
__WARNING:__ as with all fixed-point arithmetic, the functions
associated with 'Cyc' may result in overflow (and thereby
incorrect answers and potential security flaws) if the input
arguments are too close to the bounds imposed by the base type.
The acceptable range of inputs for each function is determined by
the internal linear transforms and other operations it performs.
-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Crypto.Lol.Cyclotomic.Cyc
(
-- * Data type
Cyc
-- * Constructors/deconstructors
, UnCyc(..)
) where
import qualified Algebra.Additive as Additive (C)
import qualified Algebra.Module as Module (C)
import qualified Algebra.Ring as Ring (C)
import qualified Algebra.ZeroTestable as ZeroTestable (C)
import Crypto.Lol.CRTrans
import Crypto.Lol.Cyclotomic.CycRep hiding (coeffsDec,
coeffsPow, crtSet,
powBasis)
import qualified Crypto.Lol.Cyclotomic.CycRep as R
import Crypto.Lol.Cyclotomic.Language hiding (Dec, Pow)
import qualified Crypto.Lol.Cyclotomic.Language as L
import Crypto.Lol.Cyclotomic.Tensor (TensorCRT, TensorCRTSet,
TensorGSqNorm,
TensorGaussian,
TensorPowDec)
import Crypto.Lol.Gadget
import Crypto.Lol.Prelude as LP
import Crypto.Lol.Reflects
import Crypto.Lol.Types (RRq, ZqBasic)
import Crypto.Lol.Types.FiniteField
import Crypto.Lol.Types.IFunctor
import Crypto.Lol.Types.Proto
import Control.Applicative hiding ((*>))
import Control.Arrow
import Control.DeepSeq
import Control.Monad.Random hiding (lift)
import Data.Constraint ((\\))
import Data.Foldable (Foldable)
import Data.Traversable
import Language.Haskell.TH
-- | Underlying GADT for a cyclotomic ring in one of several
-- representations.
data CycG t m r where
Pow :: !(CycRep t P m r) -> CycG t m r
Dec :: !(CycRep t D m r) -> CycG t m r
CRT :: !(CycRepEC t m r) -> CycG t m r
-- super-optimized storage of scalars
Scalar :: !r -> CycG t m r
-- optimized storage of subring elements
Sub :: (l `Divides` m) => !(CycG t l r) -> CycG t m r
-- CJP: someday try to merge the above two?
-- | A cyclotomic ring such as \( \Z[\zeta_m] \), \( \Z_q[\zeta_m] \),
-- or \( \Q[\zeta_m] \): @t@ is the 'Tensor' type for storing
-- coefficient tensors; @m@ is the cyclotomic index; @r@ is the base
-- ring of the coefficients (e.g., \(\ \Q \), \( \Z \), \( \Z_q \)).
data family Cyc (t :: Factored -> * -> *) (m :: Factored) r
type instance LiftOf (CycG t m r) = CycG t m (LiftOf r)
type instance LiftOf (Cyc t m r) = Cyc t m (LiftOf r)
-- could also do an Int instance
newtype instance Cyc t m Double = CycDbl { unCycDbl :: CycG t m Double }
newtype instance Cyc t m Int64 = CycI64 { unCycI64 :: CycG t m Int64 }
newtype instance Cyc t m (ZqBasic q z) = CycZqB { unCycZqB :: CycG t m (ZqBasic q z) }
-- | cyclotomic over a product base ring, represented as a product of
-- cyclotomics over the individual rings
data instance Cyc t m (a,b) = CycPair !(Cyc t m a) !(Cyc t m b)
-- | cyclotomic ring of integers with unbounded precision, limited to
-- powerful- or decoding-basis representation.
data instance Cyc t m Integer
= PowIgr !(CycRep t P m Integer)
| DecIgr !(CycRep t D m Integer)
-- | additive group \( K/qR \), limited to powerful- or decoding-basis
-- representation
data instance Cyc t m (RRq q r)
= PowRRq !(CycRep t P m (RRq q r))
| DecRRq !(CycRep t D m (RRq q r))
---------- Constructors / destructors ----------
-- | Convenience wrapper.
cycPC :: Either (CycRep t P m r) (CycRep t C m r) -> CycG t m r
cycPC = either Pow (CRT . Right)
{-# INLINABLE cycPC #-}
-- | Convenience wrapper.
cycPE :: Either (CycRep t P m r) (CycRep t E m r) -> CycG t m r
cycPE = either Pow (CRT . Left)
{-# INLINABLE cycPE #-}
-- | Unwrap a 'CycG' as a 'CycRep' in powerful-basis representation.
unCycGPow :: (Fact m, CRTElt t r) => CycG t m r -> CycRep t P m r
{-# INLINABLE unCycGPow #-}
unCycGPow c = let (Pow u) = toPow' c in u
-- | Unwrap a 'CycG' as a 'CycRep' in decoding-basis representation.
unCycGDec :: (Fact m, CRTElt t r) => CycG t m r -> CycRep t D m r
{-# INLINABLE unCycGDec #-}
unCycGDec c = let (Dec u) = toDec' c in u
{-
-- | Unwrap a 'CycG' as a 'CycRep' in a CRT-basis representation.
uncycCRT :: (Fact m, CRTElt t r) => CycG t m r -> CycRepEC t m r
{-# INLINABLE uncycCRT #-}
uncycCRT c = let (CRT u) = toCRT' c in u
-}
-- | Go between 'Cyc' and 'CycRep', in a desired representation.
class UnCyc t r where
cycPow :: (Fact m) => CycRep t P m r -> Cyc t m r
cycDec :: (Fact m) => CycRep t D m r -> Cyc t m r
unCycPow :: (Fact m) => Cyc t m r -> CycRep t P m r
unCycDec :: (Fact m) => Cyc t m r -> CycRep t D m r
instance CRTElt t Double => UnCyc t Double where
cycPow = CycDbl . Pow
cycDec = CycDbl . Dec
unCycPow = unCycGPow . unCycDbl
unCycDec = unCycGDec . unCycDbl
instance CRTElt t Int64 => UnCyc t Int64 where
cycPow = CycI64 . Pow
cycDec = CycI64 . Dec
unCycPow = unCycGPow . unCycI64
unCycDec = unCycGDec . unCycI64
instance CRTElt t (ZqBasic q z) => UnCyc t (ZqBasic q z) where
cycPow = CycZqB . Pow
cycDec = CycZqB . Dec
unCycPow = unCycGPow . unCycZqB
unCycDec = unCycGDec . unCycZqB
-- CJP TODO: one for Integer?; would require converting between Pow and
-- Dec reps in pure Haskell
instance TensorPowDec t (RRq q r) => UnCyc t (RRq q r) where
cycPow = PowRRq
cycDec = DecRRq
unCycPow (PowRRq v) = v
unCycPow (DecRRq v) = toPow v
unCycDec (DecRRq v) = v
unCycDec (PowRRq v) = toDec v
instance (UnCyc t a, UnCyc t b, IFunctor t, IFElt t a, IFElt t b, IFElt t (a,b))
=> UnCyc t (a,b) where
cycPow = uncurry CycPair . ((cycPow . fmapI fst) &&& (cycPow . fmapI snd))
cycDec = uncurry CycPair . ((cycDec . fmapI fst) &&& (cycDec . fmapI snd))
unCycPow (CycPair a b) = zipWithI (,) (unCycPow a) (unCycPow b)
unCycDec (CycPair a b) = zipWithI (,) (unCycDec a) (unCycDec b)
---------- Category-theoretic instances ----------
instance (Fact m, CRTElt t r, Foldable (t m))
=> FoldableCyc (CycG t m) r where
foldrCyc (Just L.Pow) f acc (Pow u) = foldr f acc u
foldrCyc (Just L.Dec) f acc (Dec u) = foldr f acc u
foldrCyc Nothing f acc (Pow u) = foldr f acc u
foldrCyc Nothing f acc (Dec u) = foldr f acc u
foldrCyc b@(Just L.Pow) f acc c = foldrCyc b f acc $ toPow' c
foldrCyc b@(Just L.Dec) f acc c = foldrCyc b f acc $ toDec' c
foldrCyc Nothing f acc c = foldrCyc Nothing f acc $ toPow' c
instance (FoldableCyc (CycG t m) Double) => FoldableCyc (Cyc t m) Double where
foldrCyc bas f acc = foldrCyc bas f acc . unCycDbl
instance (FoldableCyc (CycG t m) Int64) => FoldableCyc (Cyc t m) Int64 where
foldrCyc bas f acc = foldrCyc bas f acc . unCycI64
instance (FoldableCyc (CycG t m) (ZqBasic q z))
=> FoldableCyc (Cyc t m) (ZqBasic q z) where
foldrCyc bas f acc = foldrCyc bas f acc . unCycZqB
-- No instance for CycPair -- is one possible?
instance (Foldable (t m)) => FoldableCyc (Cyc t m) Integer where
foldrCyc (Just L.Pow) f acc (PowIgr u) = foldr f acc u
foldrCyc (Just L.Dec) f acc (DecIgr u) = foldr f acc u
foldrCyc Nothing f acc (PowIgr u) = foldr f acc u
foldrCyc Nothing f acc (DecIgr u) = foldr f acc u
foldrCyc _ _ _ _ =
error "Cyc.foldrCyc over Integer: mismatched bases, implementation TODO."
instance (Fact m, TensorPowDec t (RRq q r), Foldable (t m))
=> FoldableCyc (Cyc t m) (RRq q r) where
foldrCyc (Just L.Pow) f acc = foldr f acc . unCycPow
foldrCyc (Just L.Dec) f acc = foldr f acc . unCycDec
foldrCyc Nothing f acc = foldrCyc (Just L.Pow) f acc
--- FunctorCyc
instance (Fact m, CRTElt t a, IFunctor t, IFElt t a, IFElt t b)
=> FunctorCyc (CycG t m) a b where
fmapCyc (Just L.Pow) f = Pow . fmapI f . unCycGPow
fmapCyc (Just L.Dec) f = Dec . fmapI f . unCycGDec
fmapCyc Nothing f = fmapCyc (Just L.Pow) f
instance (Functor (t m)) => FunctorCyc (Cyc t m) Integer Integer where
fmapCyc (Just L.Pow) f (PowIgr u) = PowIgr $ fmap f u
fmapCyc (Just L.Dec) f (DecIgr u) = DecIgr $ fmap f u
fmapCyc Nothing f (PowIgr u) = PowIgr $ fmap f u
fmapCyc Nothing f (DecIgr u) = DecIgr $ fmap f u
fmapCyc _ _ _ =
error "Cyc.fmapCyc Integer->Integer: mismatched bases, implementation TODO"
-- CJP: the rest of the FunctorCyc instances are autogen'd at end of
-- file, to avoid scoping problems
---------- Algebraic instances ----------
instance (Fact m, ZeroTestable r, CRTElt t r,
forall m' . Fact m' => ZeroTestable.C (t m' r))
=> ZeroTestable.C (CycG t m r) where
isZero x = case x of
(Pow u) -> isZero u
(Dec u) -> isZero u
(CRT (Right u)) -> isZero u
c@(CRT _) -> isZero $ toPow' c
(Scalar c) -> isZero c
(Sub c) -> isZero c
deriving instance ZeroTestable (CycG t m Double) => ZeroTestable.C (Cyc t m Double)
deriving instance ZeroTestable (CycG t m Int64) => ZeroTestable.C (Cyc t m Int64)
deriving instance ZeroTestable (CycG t m (ZqBasic q z)) => ZeroTestable.C (Cyc t m (ZqBasic q z))
instance (ZeroTestable (Cyc t m a), ZeroTestable (Cyc t m b))
=> ZeroTestable.C (Cyc t m (a,b)) where
isZero (CycPair a b) = isZero a && isZero b
instance ZeroTestable (t m Integer) => ZeroTestable.C (Cyc t m Integer) where
isZero (PowIgr v) = isZero v
isZero (DecIgr v) = isZero v
instance ZeroTestable (t m (RRq q r)) => ZeroTestable.C (Cyc t m (RRq q r)) where
isZero (PowRRq c) = isZero c
isZero (DecRRq c) = isZero c
-----
instance (Eq r, Fact m, CRTElt t r,
forall m' . Fact m' => Eq (t m' r)) => Eq (CycG t m r) where
{-# INLINABLE (==) #-}
-- same representations
(Scalar c1) == (Scalar c2) = c1 == c2
(Pow u1) == (Pow u2) = u1 == u2
(Dec u1) == (Dec u2) = u1 == u2
(CRT (Right u1)) == (CRT (Right u2)) = u1 == u2
-- compare Subs in compositum
-- EAC: would like to convert c2 to basis of c1 *before* embedding
(Sub (c1 :: CycG t l1 r)) == (Sub (c2 :: CycG t l2 r)) =
(embed' c1 :: CycG t (FLCM l1 l2) r) == embed' c2
\\ lcmDivides @l1 @l2
-- some other relatively efficient comparisons
(Scalar c1) == (Pow u2) = scalarPow c1 == u2
(Pow u1) == (Scalar c2) = u1 == scalarPow c2
-- otherwise: compare in powerful basis
c1 == c2 = toPow' c1 == toPow' c2
deriving instance Eq (CycG t m Int64) => Eq (Cyc t m Int64)
deriving instance Eq (CycG t m (ZqBasic q z)) => Eq (Cyc t m (ZqBasic q z))
instance (Eq (Cyc t m a), Eq (Cyc t m b)) => Eq (Cyc t m (a,b)) where
(CycPair a b) == (CycPair a' b') = a == a' && b == b'
-- no Eq for Double or RRq due to precision, nor for Integer because
-- we can't change representations
-----
instance (Fact m, CRTElt t r, ZeroTestable r) => Additive.C (CycG t m r) where
{-# INLINABLE zero #-}
zero = Scalar zero
{-# INLINABLE (+) #-}
-- optimized addition of zero
(Scalar c1) + c2 | isZero c1 = c2
c1 + (Scalar c2) | isZero c2 = c1
-- SAME CONSTRUCTORS
(Scalar c1) + (Scalar c2) = Scalar (c1+c2)
(Pow u1) + (Pow u2) = Pow $ u1 + u2
(Dec u1) + (Dec u2) = Dec $ u1 + u2
(CRT u1) + (CRT u2) = CRT $ u1 + u2
-- Sub plus Sub: work in compositum
-- EAC: would like to convert c2 to basis of c1 before embedding
(Sub (c1 :: CycG t m1 r)) + (Sub (c2 :: CycG t m2 r)) =
Sub ((embed' c1 :: CycG t (FLCM m1 m2) r) + embed' c2)
\\ lcm2Divides @m1 @m2 @m
-- SCALAR PLUS SOMETHING ELSE
(Scalar c) + (Pow u) = Pow $ scalarPow c + u
(Scalar c) + (Dec u) = Pow $ scalarPow c + toPow u -- workaround scalarDec
(Scalar c) + (CRT u) = CRT $ scalarCRT c + u
(Scalar c1) + (Sub c2) = Sub $ Scalar c1 + c2 -- must re-wrap Scalar!
(Pow u) + (Scalar c) = Pow $ u + scalarPow c
(Dec u) + (Scalar c) = Pow $ toPow u + scalarPow c -- workaround scalarDec
(CRT u) + (Scalar c) = CRT $ u + scalarCRT c
(Sub c1) + (Scalar c2) = Sub $ c1 + Scalar c2
-- SUB PLUS NON-SUB, NON-SCALAR: work in full ring
-- EAC: would like to convert sub to basis of other before embedding
(Sub c1) + c2 = embed' c1 + c2
c1 + (Sub c2) = c1 + embed' c2
-- mixed Dec and Pow: use linear-time conversions
(Dec u1) + (Pow u2) = Pow $ toPow u1 + u2
(Pow u1) + (Dec u2) = Pow $ u1 + toPow u2
-- one CRT: convert other to CRT
(CRT u1) + (Pow u2) = CRT $ u1 + toCRT u2
(CRT u1) + (Dec u2) = CRT $ u1 + toCRT u2
(Pow u1) + (CRT u2) = CRT $ toCRT u1 + u2
(Dec u1) + (CRT u2) = CRT $ toCRT u1 + u2
{-# INLINABLE negate #-}
negate (Pow u) = Pow $ negate u
negate (Dec u) = Dec $ negate u
negate (CRT u) = CRT $ negate u
negate (Scalar c) = Scalar (negate c)
negate (Sub c) = Sub $ negate c
deriving instance Additive (CycG t m Double) => Additive.C (Cyc t m Double)
deriving instance Additive (CycG t m Int64) => Additive.C (Cyc t m Int64)
deriving instance Additive (CycG t m (ZqBasic q z)) => Additive.C (Cyc t m (ZqBasic q z))
instance (Additive (Cyc t m a), Additive (Cyc t m b))
=> Additive.C (Cyc t m (a,b)) where
zero = CycPair zero zero
(CycPair a b) + (CycPair a' b') = CycPair (a+a') (b+b')
negate (CycPair a b) = CycPair (negate a) (negate b)
instance (Additive (RRq q r), TensorPowDec t (RRq q r), IFunctor t, Fact m)
=> Additive.C (Cyc t m (RRq q r)) where
zero = PowRRq zero
(PowRRq u1) + (PowRRq u2) = PowRRq $ u1 + u2
(DecRRq u1) + (DecRRq u2) = DecRRq $ u1 + u2
(PowRRq u1) + (DecRRq u2) = PowRRq $ u1 + toPow u2
(DecRRq u1) + (PowRRq u2) = PowRRq $ toPow u1 + u2
negate (PowRRq u) = PowRRq $ negate u
negate (DecRRq u) = DecRRq $ negate u
-----
instance (Fact m, CRTElt t r, ZeroTestable r) => Ring.C (CycG t m r) where
{-# INLINABLE one #-}
one = Scalar one
{-# INLINABLE fromInteger #-}
fromInteger = Scalar . fromInteger
{-# INLINABLE (*) #-}
-- optimized mul-by-zero
v1@(Scalar c1) * _ | isZero c1 = v1
_ * v2@(Scalar c2) | isZero c2 = v2
-- both CRT: if over C, then convert result to pow for precision reasons
(CRT u1) * (CRT u2) = either (Pow . toPow) (CRT . Right) $ u1*u2
-- at least one Scalar
(Scalar c1) * (Scalar c2) = Scalar $ c1*c2
(Scalar c) * (Pow u) = Pow $ c *> u
(Scalar c) * (Dec u) = Dec $ c *> u
(Scalar c) * (CRT u) = CRT $ c *> u
(Scalar c1) * (Sub c2) = Sub $ Scalar c1 * c2
(Pow u) * (Scalar c) = Pow $ c *> u
(Dec u) * (Scalar c) = Dec $ c *> u
(CRT u) * (Scalar c) = CRT $ c *> u
(Sub c1) * (Scalar c2) = Sub $ c1 * Scalar c2
-- TWO SUBS: work in a CRT rep for compositum
(Sub (c1 :: CycG t m1 r)) * (Sub (c2 :: CycG t m2 r)) =
-- re-wrap c1, c2 as Subs of the composition, and force them to CRT
Sub ((toCRT' $ Sub c1 :: CycG t (FLCM m1 m2) r) * toCRT' (Sub c2))
\\ lcm2Divides @m1 @m2 @m
-- ELSE: work in appropriate CRT rep
c1 * c2 = toCRT' c1 * toCRT' c2
deriving instance Ring (CycG t m Double) => Ring.C (Cyc t m Double)
deriving instance Ring (CycG t m Int64) => Ring.C (Cyc t m Int64)
deriving instance Ring (CycG t m (ZqBasic q z)) => Ring.C (Cyc t m (ZqBasic q z))
instance (Ring (Cyc t m a), Ring (Cyc t m b)) => Ring.C (Cyc t m (a,b)) where
one = CycPair one one
fromInteger z = CycPair (fromInteger z) (fromInteger z)
(CycPair a b) * (CycPair a' b') = CycPair (a*a') (b*b')
-- no instance for RRq because it's not a ring
-----
instance (Fact m, CRTElt t r, ZeroTestable r) => Module.C r (CycG t m r) where
r *> (Scalar c) = Scalar $ r * c
r *> (Pow v) = Pow $ r *> v
r *> (Dec v) = Dec $ r *> v
r *> (Sub c) = Sub $ r *> c
r *> x = r *> toPow' x
deriving instance Module Int64 (CycG t m Int64) => Module.C Int64 (Cyc t m Int64)
deriving instance Module Double (CycG t m Double) => Module.C Double (Cyc t m Double)
deriving instance (Module (ZqBasic q z) (CycG t m (ZqBasic q z)),
Ring (ZqBasic q z)) -- satisfy superclass
=> Module.C (ZqBasic q z) (Cyc t m (ZqBasic q z))
instance (Module a (Cyc t m a), Module b (Cyc t m b))
=> Module.C (a,b) (Cyc t m (a,b)) where
(a,b) *> (CycPair ca cb) = CycPair (a *> ca) (b *> cb)
-- no instance for RRq because it's not, mathematically
-- Module over finite field
-- | \(R_p\) is an \(\F_{p^d}\)-module when \(d\) divides
-- \(\varphi(m)\), by applying \(d\)-dimensional \(\F_p\)-linear
-- transform on \(d\)-dim chunks of powerful basis coeffs.
instance (GFCtx fp d, Fact m, CRTElt t fp, Module (GF fp d) (t m fp))
=> Module.C (GF fp d) (CycG t m fp) where
-- CJP: optimize for Scalar if we can: r *> (Scalar c) is the tensor
-- that has the coeffs of (r*c), followed by zeros. (This assumes
-- that the powerful basis has 1 as its first element, and that
-- we're using pow to define the module mult.)
-- Can use any r-basis to define module mult, but must be
-- consistent. We use powerful basis.
r *> (Pow v) = Pow $ r *> v
r *> x = r *> toPow' x
deriving instance (Ring (GF (ZqBasic q z) d),
Module (GF (ZqBasic q z) d) (CycG t m (ZqBasic q z)))
=> Module.C (GF (ZqBasic q z) d) (Cyc t m (ZqBasic q z))
---------- Cyclotomic classes ----------
instance (Fact m, CRTElt t r, ZeroTestable r, IntegralDomain r)
=> Cyclotomic (CycG t m r) where
{-# SPECIALIZE instance (Fact m, CRTElt t Int64) => Cyclotomic (CycG t m Int64) #-}
{-# SPECIALIZE instance (Fact m, CRTElt t Double) => Cyclotomic (CycG t m Double) #-}
{-# SPECIALIZE instance (Fact m, CRTElt t (ZqBasic q Int64), Reflects q Int64) => Cyclotomic (CycG t m (ZqBasic q Int64)) #-}
mulG (Pow u) = Pow $ R.mulGPow u
mulG (Dec u) = Dec $ R.mulGDec u
mulG (CRT (Left u)) = Pow $ R.mulGPow $ toPow u -- go to Pow for precision
mulG (CRT (Right u)) = CRT $ Right $ R.mulGCRTC u
mulG c@(Scalar _) = mulG $ toCRT' c
mulG (Sub c) = mulG $ embed' c -- must go to full ring
divG (Pow u) = Pow <$> R.divGPow u
divG (Dec u) = Dec <$> R.divGDec u
divG (CRT (Left u)) = Pow <$> R.divGPow (toPow u) -- go to Pow for precision
divG (CRT (Right u)) = Just $ (CRT . Right) $ R.divGCRTC u
divG c@(Scalar _) = divG $ toCRT' c
divG (Sub c) = divG $ embed' c -- must go to full ring
advisePow = toPow'
adviseDec = toDec'
adviseCRT = toCRT'
deriving instance Cyclotomic (CycG t m Double) => Cyclotomic (Cyc t m Double)
deriving instance Cyclotomic (CycG t m Int64) => Cyclotomic (Cyc t m Int64)
deriving instance Cyclotomic (CycG t m (ZqBasic q z)) => Cyclotomic (Cyc t m (ZqBasic q z))
instance (Cyclotomic (Cyc t m a), Cyclotomic (Cyc t m b))
=> Cyclotomic (Cyc t m (a,b)) where
{-# SPECIALIZE instance (Fact m, CRTElt t (ZqBasic q Int64), Reflects q Int64, Cyclotomic (Cyc t m b)) => Cyclotomic (Cyc t m (ZqBasic q Int64, b)) #-}
mulG (CycPair a b) = CycPair (mulG a) (mulG b)
divG (CycPair a b) = CycPair <$> divG a <*> divG b
advisePow (CycPair a b) = CycPair (advisePow a) (advisePow b)
adviseDec (CycPair a b) = CycPair (adviseDec a) (adviseDec b)
adviseCRT (CycPair a b) = CycPair (adviseCRT a) (adviseCRT b)
-----
instance (Fact m, TensorGSqNorm t r, CRTElt t r)
=> GSqNormCyc (CycG t m) r where
gSqNorm (Dec c) = gSqNormDec c
gSqNorm c = gSqNorm $ toDec' c
instance (Fact m, TensorGSqNorm t Double, CRTElt t Double) -- copied
=> GSqNormCyc (Cyc t m) Double where
gSqNorm = gSqNorm . unCycDbl
instance (Fact m, TensorGSqNorm t Int64, CRTElt t Int64) -- copied
=> GSqNormCyc (Cyc t m) Int64 where
gSqNorm = gSqNorm . unCycI64
----
-- | uses 'Double' for the intermediate Gaussian sample
instance (Fact m, TensorGaussian t Double, FunctorCyc (Cyc t m) Double Int64)
=> RoundedGaussianCyc (Cyc t m Int64) where
roundedGaussian :: forall v rnd . (ToRational v, MonadRandom rnd)
=> v -> rnd (Cyc t m Int64)
{-# INLINABLE roundedGaussian #-}
roundedGaussian svar =
fmapCyc (Just L.Dec) (roundMult one) <$>
(L.tweakedGaussian svar :: rnd (Cyc t m Double))
-----
instance (Lift' r, FunctorCyc (Cyc t m) r (LiftOf r)) => LiftCyc (Cyc t m r) where
{-# SPECIALIZE instance (Fact m, Reflects q Int64, UnCyc t (ZqBasic q Int64), UnCyc t Int64, IFunctor t, IFElt t (ZqBasic q Int64), IFElt t Int64) => LiftCyc (Cyc t m (ZqBasic q Int64)) #-}
{-# INLINABLE liftCyc #-}
liftCyc = flip fmapCyc lift
-----
instance (Fact m, TensorGaussian t q) => GaussianCyc (CycG t m q) where
tweakedGaussian = fmap Dec . R.tweakedGaussian
instance (Fact m, TensorGaussian t Double) => GaussianCyc (Cyc t m Double) where
tweakedGaussian = fmap CycDbl . L.tweakedGaussian
-- CJP: no GaussianCyc for Int64, Integer, ZqBasic, pairs, or RRq
-- | uses 'Double' precision for the intermediate Gaussian samples
instance (TensorGaussian t Double, IFElt t Double, IFunctor t, Fact m, Mod zp,
Lift zp (ModRep zp), CRTElt t zp, IFElt t (LiftOf zp))
=> CosetGaussianCyc (CycG t m zp) where
{-# INLINABLE cosetGaussian #-}
cosetGaussian v = (Dec <$>) . R.cosetGaussian v . unCycGDec
-- | uses 'Double' for the intermediate Gaussian samples
instance (CosetGaussianCyc (CycG t m (ZqBasic q Int64)))
=> CosetGaussianCyc (Cyc t m (ZqBasic q Int64)) where
cosetGaussian v = fmap CycI64 . L.cosetGaussian v . unCycZqB
-- CJP: no CosetGaussianCyc for Double, Int64, Integer, or pairs
-----
instance (CRTElt t r, ZeroTestable r, IntegralDomain r) -- ZT, ID for superclass
=> ExtensionCyc (CycG t) r where
{-# SPECIALIZE instance (CRTElt t Int64) => ExtensionCyc (CycG t) Int64 #-}
{-# SPECIALIZE instance (CRTElt t Double) => ExtensionCyc (CycG t) Double #-}
{-# SPECIALIZE instance (CRTElt t (ZqBasic q Int64), Reflects q Int64) => ExtensionCyc (CycG t) (ZqBasic q Int64) #-}
-- lazily embed
embed :: forall m m' . (m `Divides` m') => CycG t m r -> CycG t m' r
embed (Scalar c) = Scalar c -- keep as scalar
embed (Sub (c :: CycG t l r)) = Sub c -- keep as subring element
\\ transDivides @l @m @m'
embed c = Sub c
twace :: forall m m' . (m `Divides` m') => CycG t m' r -> CycG t m r
twace (Pow u) = Pow $ twacePow u
twace (Dec u) = Dec $ twaceDec u
twace (CRT u) = either (cycPE . twaceCRTE) (cycPC . twaceCRTC) u
twace (Scalar u) = Scalar u
twace (Sub (c :: CycG t l r)) = Sub (twace c :: CycG t (FGCD l m) r)
\\ gcdDivides @l @m
powBasis :: forall m m' . (m `Divides` m') => Tagged m [CycG t m' r]
powBasis = tag $ Pow <$> R.powBasis @m
coeffsCyc L.Pow c' = Pow <$> R.coeffsPow (unCycGPow c')
coeffsCyc L.Dec c' = Dec <$> R.coeffsDec (unCycGDec c')
instance CRTElt t Double => ExtensionCyc (Cyc t) Double where
embed = CycDbl . embed . unCycDbl
twace = CycDbl . twace . unCycDbl
powBasis = (CycDbl <$>) <$> powBasis
coeffsCyc b = fmap CycDbl . coeffsCyc b . unCycDbl
instance CRTElt t Int64 => ExtensionCyc (Cyc t) Int64 where
embed = CycI64 . embed . unCycI64
twace = CycI64 . twace . unCycI64
powBasis = (CycI64 <$>) <$> powBasis
coeffsCyc b = fmap CycI64 . coeffsCyc b . unCycI64
instance (ExtensionCyc (CycG t) (ZqBasic q z))
=> ExtensionCyc (Cyc t) (ZqBasic q z) where
embed = CycZqB . embed . unCycZqB
twace = CycZqB . twace . unCycZqB
powBasis = (CycZqB <$>) <$> powBasis
coeffsCyc b = fmap CycZqB . coeffsCyc b . unCycZqB
instance (ExtensionCyc (Cyc t) a, ExtensionCyc (Cyc t) b)
=> ExtensionCyc (Cyc t) (a,b) where
{-# SPECIALIZE instance (ExtensionCyc (Cyc t) (ZqBasic q Int64), ExtensionCyc (Cyc t) b) => ExtensionCyc (Cyc t) (ZqBasic q Int64, b) #-}
embed (CycPair a b) = CycPair (embed a) (embed b)
twace (CycPair a b) = CycPair (twace a) (twace b)
powBasis = zipWith CycPair <$> powBasis <*> powBasis
coeffsCyc bas (CycPair a b) =
zipWith CycPair (coeffsCyc bas a) (coeffsCyc bas b)
instance (TensorPowDec t (RRq q r)) => ExtensionCyc (Cyc t) (RRq q r) where
embed (PowRRq u) = PowRRq $ embedPow u
embed (DecRRq u) = PowRRq $ embedPow $ toPow u
twace (PowRRq u) = PowRRq $ twacePow u
twace (DecRRq u) = DecRRq $ twaceDec u
powBasis :: forall m m' . (m `Divides` m') => Tagged m [Cyc t m' (RRq q r)]
powBasis = tag $ PowRRq <$> R.powBasis @m
coeffsCyc L.Pow (PowRRq c) = PowRRq <$> R.coeffsPow c
coeffsCyc L.Dec (DecRRq c) = DecRRq <$> R.coeffsDec c
coeffsCyc L.Pow (DecRRq c) = PowRRq <$> R.coeffsPow (toPow c)
coeffsCyc L.Dec (PowRRq c) = DecRRq <$> R.coeffsDec (toDec c)
-- | Force to a non-'Sub' constructor (for internal use only).
embed' :: forall t r l m . (l `Divides` m, CRTElt t r)
=> CycG t l r -> CycG t m r
{-# INLINE embed' #-}
embed' (Pow u) = Pow $ embedPow u
embed' (Dec u) = Pow $ embedPow $ toPow u
embed' (CRT u) = either (cycPE . embedCRTE) (cycPC . embedCRTC) u
embed' (Scalar c) = Scalar c
embed' (Sub (c :: CycG t k r)) = embed' c \\ transDivides @k @l @m
-----
instance (PPow pp, Prime (PrimePP pp), zpp ~ ZqBasic pp z, ToInteger z,
CRTElt t zpp, TensorCRTSet t (ZqBasic (PrimePP pp) z),
ExtensionCyc (CycG t) zpp)
=> CRTSetCyc (CycG t) (ZqBasic pp z) where
crtSet :: forall m m' . (m `Divides` m') => Tagged m [CycG t m' zpp]
crtSet = tag $ Pow <$> R.crtSet @m
{-# INLINABLE crtSet #-}
instance (CRTSetCyc (CycG t) (ZqBasic q z))
=> CRTSetCyc (Cyc t) (ZqBasic q z) where
crtSet = (CycZqB <$>) <$> crtSet
{-# INLINABLE crtSet #-}
-- CJP TODO?: instance CRTSetCyc (Cyc t) (a,b)
---------- Promoted lattice operations ----------
-- | Rescales relative to the powerful basis. This instance is
-- provided for convenience, but usage of 'RescaleCyc' is preferred to
-- explicitly specify which basis by which to rescale.
instance (RescaleCyc (Cyc t m) a b, Fact m,
Additive (Cyc t m a), Additive (Cyc t m b)) -- superclasses
=> Rescale (Cyc t m a) (Cyc t m b) where
rescale = rescaleCyc L.Pow
{-# INLINABLE rescale #-}
-- CJP: can we avoid incoherent instances by changing instance heads
-- and using overlapping instances with isomorphism constraints?
instance (Fact m, Rescale a b, CRTElt t a, TensorPowDec t b)
=> RescaleCyc (CycG t m) a b where
{-# SPECIALIZE instance (Fact m, Reflects q1 Int64, Reflects q2 Int64, CRTElt t (ZqBasic q1 Int64), TensorPowDec t (ZqBasic q2 Int64)) => RescaleCyc (CycG t m) (ZqBasic q1 Int64) (ZqBasic q2 Int64) #-}
-- Optimized for subring constructors, for powerful basis.
-- Analogs for decoding basis are not quite correct, because (* -1)
-- doesn't commute with 'rescale' due to tiebreakers!
rescaleCyc L.Pow (Scalar c) = Scalar $ rescale c
rescaleCyc L.Pow (Sub c) = Sub $ rescalePow c
rescaleCyc L.Pow c = Pow $ fmapI rescale $ unCycGPow c
rescaleCyc L.Dec c = Dec $ fmapI rescale $ unCycGDec c
{-# INLINABLE rescaleCyc #-}
-- | identity rescale (more specific)
instance {-# OVERLAPPING #-} RescaleCyc (CycG t m) a a where
-- No-op rescale
rescaleCyc _ = id
{-# INLINABLE rescaleCyc #-}
-- | rescale from one modulus to another
instance (RescaleCyc (CycG t m) (ZqBasic q z) (ZqBasic p z))
=> RescaleCyc (Cyc t m) (ZqBasic q z) (ZqBasic p z) where
rescaleCyc b = CycZqB . rescaleCyc b . unCycZqB
{-# INLINABLE rescaleCyc #-}
-- | rescale from one modulus to another
instance (Fact m, Rescale (RRq q r) (RRq p r),
TensorPowDec t (RRq q r), TensorPowDec t (RRq p r))
=> RescaleCyc (Cyc t m) (RRq q r) (RRq p r) where
rescaleCyc L.Pow (PowRRq u) = PowRRq $ rescale u
rescaleCyc L.Pow (DecRRq u) = PowRRq $ rescale $ toPow u
rescaleCyc L.Dec (DecRRq u) = DecRRq $ rescale u
rescaleCyc L.Dec (PowRRq u) = DecRRq $ rescale $ toDec u
{-# INLINABLE rescaleCyc #-}
-- | no-op rescale for Cyc over pairs
instance RescaleCyc (Cyc t m) (a,b) (a,b) where
rescaleCyc = const id
{-# INLINABLE rescaleCyc #-}
-- | rescale up by one additional modulus
instance (Fact m, Reflects q z, Reduce z b, ZeroTestable z,
CRTElt t (ZqBasic q z), Module.C b (Cyc t m b))
=> RescaleCyc (Cyc t m) b (ZqBasic q z, b) where
rescaleCyc = let q :: z = value @q
-- same method works for any basis
in \_ b -> CycPair zero $ (reduce q :: b) *> b
{-# INLINABLE rescaleCyc #-}
-- | specialized (faster) rescale-down by a single \(\Z_q\)
instance (ToInteger z, Reflects q z, Reduce z b, Field b,
FunctorCyc (Cyc t m) (ZqBasic q z) z,
FunctorCyc (Cyc t m) z b,
Additive (Cyc t m b), Module b (Cyc t m b))
=> RescaleCyc (Cyc t m) (ZqBasic q z, b) b where
rescaleCyc bas (CycPair a b) =
let q :: z = value @q
x = liftCyc (Just bas) a
in recip (reduce q :: b) *> (b - reduceCyc x)
{-# INLINABLE rescaleCyc #-}
-- CJP: do we really need these? Just have client call rescaleCyc
-- multiple times?
-- | convenient rescale-down by multiple components at once
instance (RescaleCyc (Cyc t m) (b,c) c, RescaleCyc (Cyc t m) (a,(b,c)) (b,c))
=> RescaleCyc (Cyc t m) (a,(b,c)) c where
rescaleCyc bas a = rescaleCyc bas (rescaleCyc bas a :: Cyc t m (b,c))
{-# INLINABLE rescaleCyc #-}
-- | convenient rescale-down by multiple components at once
instance (RescaleCyc (Cyc t m) (b,(c,d)) d,
RescaleCyc (Cyc t m) (a,(b,(c,d))) (b,(c,d)))
=> RescaleCyc (Cyc t m) (a,(b,(c,d))) d where
rescaleCyc bas a = rescaleCyc bas (rescaleCyc bas a :: Cyc t m (b,(c,d)))
{-# INLINABLE rescaleCyc #-}
-- | convenient rescale-down by multiple components at once
instance (RescaleCyc (Cyc t m) (b,(c,(d,e))) e,
RescaleCyc (Cyc t m) (a,(b,(c,(d,e)))) (b,(c,(d,e))))
=> RescaleCyc (Cyc t m) (a,(b,(c,(d,e)))) e where
rescaleCyc bas a = rescaleCyc bas (rescaleCyc bas a :: Cyc t m (b,(c,(d,e))))
{-# INLINABLE rescaleCyc #-}
-----
-- | promoted from base ring
instance (Gadget gad (ZqBasic q z),
-- satisfy Gadget's Ring superclass; remove if it goes away
Fact m, CRTElt t (ZqBasic q z),
ZeroTestable (ZqBasic q z), IntegralDomain (ZqBasic q z))
=> Gadget gad (CycG t m (ZqBasic q z)) where
gadget = Scalar <$> gadget @gad
{-# INLINABLE gadget #-}
-- CJP: default 'encode' works because mul-by-Scalar is fast
-- can't auto-derive because of ambiguity of gadget
instance Gadget gad (CycG t m (ZqBasic q z))
=> Gadget gad (Cyc t m (ZqBasic q z)) where
gadget = CycZqB <$> gadget @gad
{-# INLINABLE gadget #-}
instance (Gadget gad (Cyc t m a), Gadget gad (Cyc t m b))
=> Gadget gad (Cyc t m (a,b)) where
gadget = (++) (flip CycPair zero <$> gadget @gad)
( CycPair zero <$> gadget @gad)
{-# INLINABLE gadget #-}
-----
instance (Fact m, Reduce a b, CRTElt t a, TensorPowDec t b)
=> Reduce (CycG t m a) (CycG t m b) where
reduce (Pow u) = Pow $ reduce u
reduce (Dec u) = Dec $ reduce u
reduce (CRT u) = Pow $ reduce $ either toPow toPow u
reduce (Scalar c) = Scalar $ reduce c
reduce (Sub (c :: CycG t l a)) = Sub (reduce c :: CycG t l b)
{-# INLINABLE reduce #-}
instance (Reduce (CycG t m Int64) (CycG t m (ZqBasic q Int64)))
=> Reduce (Cyc t m Int64) (Cyc t m (ZqBasic q Int64)) where
reduce = CycZqB . reduce . unCycI64
{-# INLINABLE reduce #-}
instance (Reflects q Int64, Functor (t m))
=> Reduce (Cyc t m Integer) (Cyc t m (ZqBasic q Int64)) where
reduce (PowIgr u) = CycZqB $ Pow $ fmap reduce u
reduce (DecIgr u) = CycZqB $ Dec $ fmap reduce u
{-# INLINABLE reduce #-}
instance (Reflects q Double, FunctorCyc (Cyc t m) Double (RRq q Double))
=> Reduce (Cyc t m Double) (Cyc t m (RRq q Double)) where
reduce = fmapCyc Nothing reduce
{-# INLINABLE reduce #-}
instance (Reduce (Cyc t m z) (Cyc t m a), Reduce (Cyc t m z) (Cyc t m b))
=> Reduce (Cyc t m z) (Cyc t m (a,b)) where
reduce z = CycPair (reduce z) (reduce z)
{-# INLINABLE reduce #-}
-- | promoted from base ring, using the powerful basis for best geometry
instance (Decompose gad (ZqBasic q z), CRTElt t (ZqBasic q z), Fact m,
-- for satisfying Decompose's Gadget superclass
ZeroTestable (ZqBasic q z), IntegralDomain (ZqBasic q z),
-- for satisfying Decompose's Reduce superclass w/o using m
CRTElt t z, ZeroTestable z)
=> Decompose gad (CycG t m (ZqBasic q z)) where
type DecompOf (CycG t m (ZqBasic q z)) = CycG t m z
-- faster implementations: decompose directly in subring, which is
-- correct because we decompose in powerful basis
decompose (Scalar c) = Scalar <$> decompose @gad c
decompose (Sub c) = Sub <$> decompose @gad c
-- traverse: Traversable (CycRep t P m) and Applicative ZipList
decompose (Pow u) = getZipList $ Pow <$> traverse (ZipList . decompose @gad) u
decompose c = decompose @gad $ toPow' c
{-# INLINABLE decompose #-}
-- specific to Int64 because we need to know constructor for lift type
instance (Decompose gad (CycG t m (ZqBasic q Int64)),
-- for satisfying Decompose's Reduce superclass
Reduce (Cyc t m Int64) (Cyc t m (ZqBasic q Int64)))
=> Decompose gad (Cyc t m (ZqBasic q Int64)) where
type DecompOf (Cyc t m (ZqBasic q Int64)) = Cyc t m Int64
decompose (CycZqB c) = CycI64 <$> decompose @gad c
{-# INLINABLE decompose #-}
instance (Decompose gad (Cyc t m a), Decompose gad (Cyc t m b),
DecompOf (Cyc t m a) ~ DecompOf (Cyc t m b),
-- for satisfying Decompose's Reduce superclass
Reduce (DecompOf (Cyc t m a)) (Cyc t m (a, b)))
=> Decompose gad (Cyc t m (a,b)) where
type DecompOf (Cyc t m (a,b)) = DecompOf (Cyc t m a)
decompose (CycPair a b) = (++) (decompose @gad a) (decompose @gad b)
{-# INLINABLE decompose #-}
-----
-- | promoted from base ring, using the decoding basis for best geometry
instance (Correct gad (ZqBasic q z), CRTElt t (ZqBasic q z), Fact m,
-- satisfy Gadget superclass
ZeroTestable (ZqBasic q z), IntegralDomain (ZqBasic q z),
Traversable (CycRep t D m))
=> Correct gad (CycG t m (ZqBasic q z)) where
-- sequence: Monad [] and Traversable (CycRep t D m)
-- sequenceA: Applicative (CycRep t D m) and Traversable (TaggedT gad [])
correct bs = Dec *** (Dec <$>) $
second sequence $ fmap fst &&& fmap snd $ correct @gad <$>
sequenceA (unCycGDec <$> bs)
{-# INLINABLE correct #-}
-- specific to Int64 due to LiftOf. Can't auto-derive because of
-- ambiguity of 'correct'
instance Correct gad (CycG t m (ZqBasic q Int64))
=> Correct gad (Cyc t m (ZqBasic q Int64)) where
correct = (CycZqB *** fmap CycI64) . correct @gad . fmap unCycZqB
{-# INLINABLE correct #-}
-- correct = coerce $
-- (correct @gad :: [CycG t m (ZqBasic q Int64)]
-- -> (CycG t m (ZqBasic q Int64), [CycG t m Int64]))
-- TODO: instance Correct gad (Cyc t m (a,b)) where
-- seems hard; see Correct instance for pairs in Gadget.hs
---------- Change of representation (internal use only) ----------
toPow', toDec', toCRT' :: (Fact m, CRTElt t r) => CycG t m r -> CycG t m r
{-# INLINABLE toPow' #-}
{-# INLINABLE toDec' #-}
{-# INLINABLE toCRT' #-}
-- | Force to powerful-basis representation (for internal use only).
toPow' c@(Pow _) = c
toPow' (Dec u) = Pow $ toPow u
toPow' (CRT u) = Pow $ either toPow toPow u
toPow' (Scalar c) = Pow $ scalarPow c
toPow' (Sub c) = toPow' $ embed' c
-- | Force to decoding-basis representation (for internal use only).
toDec' (Pow u) = Dec $ toDec u
toDec' c@(Dec _) = c
toDec' (CRT u) = Dec $ either toDec toDec u
toDec' (Scalar c) = Dec $ toDec $ scalarPow c
toDec' (Sub c) = toDec' $ embed' c
-- | Force to a CRT representation (for internal use only).
toCRT' (Pow u) = CRT $ toCRT u
toCRT' (Dec u) = CRT $ toCRT u
toCRT' c@(CRT _) = c
toCRT' (Scalar c) = CRT $ scalarCRT c
-- CJP: the following is the fastest algorithm for when both source
-- and target have the same CRTr/CRTe choice. It is not the fastest
-- when the choices are different (it will do an unnecessary CRT if
-- input is non-CRT), but this is an unusual case. Note: both calls
-- to toCRT' are necessary in general, because embed' may not preserve
-- CRT representation!
toCRT' (Sub c) = toCRT' $ embed' $ toCRT' c
---------- Utility instances ----------
instance (Random (t m r), Fact m, TensorCRT t Maybe r)
=> Random (CycG t m r) where
random g = let (u,g') = random g
in (either Pow (CRT . Right) u, g')
{-# INLINABLE random #-}
randomR _ = error "randomR non-sensical for CycG"
deriving instance Random (CycG t m Double) => Random (Cyc t m Double)
deriving instance Random (CycG t m Int64) => Random (Cyc t m Int64)
deriving instance Random (CycG t m (ZqBasic q z)) => Random (Cyc t m (ZqBasic q z))
instance (Random (Cyc t m a), Random (Cyc t m b)) => Random (Cyc t m (a,b)) where
{-# SPECIALIZE instance (Fact m, Random (t m (ZqBasic q Int64)), TensorCRT t Maybe (ZqBasic q Int64), Random (Cyc t m b)) => Random (Cyc t m (ZqBasic q Int64, b)) #-}
random g = let (a,g') = random g
(b,g'') = random g'
in (CycPair a b, g'')
randomR _ = error "randomR non-sensical for Cyc"
{-# INLINABLE random #-}
instance (Random (t m Integer), Fact m) => Random (Cyc t m Integer) where
random g = let (u,g') = random g in (PowIgr u, g')
randomR = error "randomR nonsensical for Cyc over Integer"
{-# INLINABLE random #-}
instance (Random (t m (RRq q r)))
=> Random (Cyc t m (RRq q r)) where
random g = let (u,g') = random g in (PowRRq u, g')
randomR = error "randomR nonsensical for Cyc over (RRq q r)"
{-# INLINABLE random #-}
-----
instance (Show r, Fact m, r' ~ CRTExt r, -- workaround TF in q'd constraint
forall m' . Fact m' => (Show (t m' r), Show (t m' r')))
=> Show (CycG t m r) where
show (Pow x) = "Cyc.Pow " ++ show x
show (Dec x) = "Cyc.Dec " ++ show x
show (CRT (Left x)) = "Cyc.CRT " ++ show x
show (CRT (Right x)) = "Cyc.CRT " ++ show x
show (Scalar x) = "Cyc.Scalar " ++ show x
show (Sub x) = "Cyc.Sub " ++ show x
deriving instance Show (CycG t m Double) => Show (Cyc t m Double)
deriving instance Show (CycG t m Int64) => Show (Cyc t m Int64)
deriving instance Show (CycG t m (ZqBasic q z)) => Show (Cyc t m (ZqBasic q z))
deriving instance (Show (Cyc t m a), Show (Cyc t m b))
=> Show (Cyc t m (a,b))
deriving instance (Show (t m Integer)) => Show (Cyc t m Integer)
deriving instance (Show (t m (RRq q r))) => Show (Cyc t m (RRq q r))
-----
instance (NFData r, Fact m, r' ~ CRTExt r,
forall m' . Fact m' => (NFData (t m' r), NFData (t m' r')))
=> NFData (CycG t m r) where
rnf (Pow u) = rnf u
rnf (Dec u) = rnf u
rnf (CRT u) = rnf u
rnf (Scalar u) = rnf u
rnf (Sub c) = rnf c
deriving instance NFData (CycG t m Double) => NFData (Cyc t m Double)
deriving instance NFData (CycG t m Int64) => NFData (Cyc t m Int64)
deriving instance NFData (CycG t m (ZqBasic q z)) => NFData (Cyc t m (ZqBasic q z))
instance (NFData (Cyc t m a), NFData (Cyc t m b)) => NFData (Cyc t m (a,b)) where
rnf (CycPair a b) = rnf a `seq` rnf b
instance (Fact m, forall m' . Fact m' => NFData (t m' Integer))
=> NFData (Cyc t m Integer) where
rnf (PowIgr u) = rnf u
rnf (DecIgr u) = rnf u
instance (Fact m, forall m' . Fact m' => NFData (t m' (RRq q r)))
=> NFData (Cyc t m (RRq q r)) where
rnf (PowRRq u) = rnf u
rnf (DecRRq u) = rnf u
---------- Protoable instances of Cyc/CycG ----------
instance (Fact m, CRTElt t r, Protoable (CycRep t D m r))
=> Protoable (CycG t m r) where
type ProtoType (CycG t m r) = ProtoType (CycRep t D m r)
toProto (Dec uc) = toProto uc
toProto x = toProto $ toDec' x
fromProto x = Dec <$> fromProto x
instance (Fact m, CRTElt t Double, Protoable (CycG t m Double))
=> Protoable (Cyc t m Double) where
type ProtoType (Cyc t m Double) = ProtoType (CycG t m Double)
toProto = toProto . unCycDbl
fromProto x = CycDbl <$> fromProto x
instance (Fact m, CRTElt t Int64, Protoable (CycG t m Int64))
=> Protoable (Cyc t m Int64) where
type ProtoType (Cyc t m Int64) = ProtoType (CycG t m Int64)
toProto = toProto . unCycI64
fromProto x = CycI64 <$> fromProto x
instance (Fact m, CRTElt t Double, Protoable (CycG t m (ZqBasic q z)))
=> Protoable (Cyc t m (ZqBasic q z)) where
type ProtoType (Cyc t m (ZqBasic q z)) = ProtoType (CycG t m (ZqBasic q z))
toProto = toProto . unCycZqB
fromProto x = CycZqB <$> fromProto x
instance (Fact m, CRTElt t Double, TensorPowDec t (RRq q Double),
Protoable (CycRep t D m (RRq q Double)))
=> Protoable (Cyc t m (RRq q Double)) where
type ProtoType (Cyc t m (RRq q Double)) = ProtoType (CycG t m (RRq q Double))
toProto (PowRRq x) = toProto $ toDec x
toProto (DecRRq x) = toProto x
fromProto x = DecRRq <$> fromProto x
---------- TH instances of FunctorCyc ----------
-- CJP: the TH needs to appear before/after everything in the module
-- so as not to screw up scoping
let types = [ [t| Int64 |]
, [t| Double |]
, [t| ZqBasic $(varT (mkName "q")) $(varT (mkName "z")) |]
, [t| RRq $(varT (mkName "q")) $(varT (mkName "r")) |]
, [t| ( $(varT (mkName "a")) , $(varT (mkName "b"))) |] -- pair
]
-- Instances that rely on IFunctor (in practice, Storable base
-- types), and go between any two IFElt types.
mkIFunctorCyc y z =
[d|
instance (Fact m, UnCyc t $y, UnCyc t $z,
IFunctor t, IFElt t $y, IFElt t $z)
=> FunctorCyc (Cyc t m) $y $z where
fmapCyc (Just L.Pow) f = cycPow . fmapI f . unCycPow
fmapCyc (Just L.Dec) f = cycDec . fmapI f . unCycDec
fmapCyc Nothing f = fmapCyc (Just L.Pow) f
|]
-- Instances that map to Integer, hence need to use fmap.
mkFunctorCyc y =
[d|
instance (Fact m, Functor (t m), UnCyc t $y)
=> FunctorCyc (Cyc t m) $y Integer where
fmapCyc (Just L.Pow) f = PowIgr . fmap f . unCycPow
fmapCyc (Just L.Dec) f = DecIgr . fmap f . unCycDec
fmapCyc Nothing f = fmapCyc (Just L.Pow) f
|]
-- CJP TODO: if/when we get a way to convert Integer between Pow
-- and Dec, we can also have instances that go *from* Integer
in fmap concat $ sequence $
(mkIFunctorCyc <$> types <*> types) ++ (mkFunctorCyc <$> types)