lol-0.2.0.0: Crypto/Lol/Cyclotomic/UCyc.hs
{-# LANGUAGE ConstraintKinds, DataKinds, FlexibleContexts,
FlexibleInstances, GADTs, InstanceSigs, MultiParamTypeClasses,
NoImplicitPrelude, PolyKinds, RankNTypes, RebindableSyntax,
ScopedTypeVariables, TypeFamilies, TypeOperators,
UndecidableInstances #-}
-- | A low-level implementation of cyclotomic rings that allows (and
-- requires) the programmer to control the underlying representation
-- of ring elements, i.e., powerful, decoding, or CRT basis.
--
-- __WARNING:__ as with all fixed-point arithmetic, the functions
-- associated with 'UCyc' 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.
module Crypto.Lol.Cyclotomic.UCyc
(
-- * Data types and constraints
UCyc, P, D, C, UCElt, NFElt
-- * Changing representation
, toPow, toDec, toCRT, fmapPow, fmapDec, unzipCyc, unzipUCElt
-- * Scalars
, scalarPow, scalarCRT -- scalarDec suppressed
-- * Basic operations
, mulG, divG, gSqNorm
-- * Error sampling
, tGaussian, errorRounded, errorCoset
-- * Inter-ring operations and values
, embedPow, embedDec, embedCRT
, twacePow, twaceDec, twaceCRT
, coeffsPow, coeffsDec, powBasis, crtSet
) where
import Crypto.Lol.Cyclotomic.Tensor hiding (embedCRT, embedDec, embedPow,
scalarCRT, scalarPow, -- scalarDec
twaceCRT)
import Crypto.Lol.CRTrans
import qualified Crypto.Lol.Cyclotomic.Tensor as T
import Crypto.Lol.LatticePrelude as LP
import Crypto.Lol.Types.FiniteField
import Crypto.Lol.Types.ZPP
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 Control.Applicative as A
import Control.Arrow
import Control.DeepSeq
import Control.Monad
import Control.Monad.Random
import Data.Foldable as F
import Data.Maybe
import Data.Traversable
import Test.QuickCheck
--import qualified Debug.Trace as DT
-- | Nullary index type representing the powerful basis.
data P
-- | Nullary index type representing the decoding basis.
data D
-- | Nullary index type representing a CRT basis.
data C
-- | Represents a cyclotomic ring such as @Z[zeta]@,
-- @Zq[zeta]@, and @Q(zeta)@ in an explicit representation: @t@ is the
-- 'Tensor' type for storing coefficient tensors; @m@ is the
-- cyclotomic index; @rep@ is the representation ('P', 'D', or 'C');
-- @r@ is the base ring of the coefficients (e.g., @Z@, @Zq@).
--
-- The 'Functor', 'Applicative', 'Foldable' and 'Traversable'
-- instances all work coefficient-wise (in the specified basis).
data UCyc t m rep r where
Pow :: !(t m r) -> UCyc t m P r
Dec :: !(t m r) -> UCyc t m D r
-- Invariant: for a given (t,m,r), exactly one of these two is ever
-- used: CRTr if crtFuncs exists, otherwise CRTe
CRTr :: !(t m r) -> UCyc t m C r
CRTe :: !(t m (CRTExt r)) -> UCyc t m C r
-- | Constraints needed for many operations involving the 'UCyc' CRT ('C')
-- representation.
type UCElt t r = (Tensor t, CRTEmbed r, CRTElt t r, CRTElt t (CRTExt r))
-- | Convenient synonym for 'deepseq'-able element type.
type NFElt r = (NFData r, NFData (CRTExt r))
-- | Embed a scalar from the base ring.
scalarPow :: (Tensor t, Fact m, Ring r, TElt t r) => r -> UCyc t m P r
scalarPow = Pow . T.scalarPow
{-# INLINABLE scalarPow #-}
{- CJP: suppressed
-- | Embed a scalar from the base ring.
scalarDec :: (Tensor t, Fact m, Ring r, TElt t r) => r -> UCyc t m D r
scalarDec = Dec . T.scalarDec
{-# INLINABLE scalarDec #-}
-}
-- | Embed a scalar from the base ring.
scalarCRT :: (Fact m, UCElt t r) => r -> UCyc t m C r
scalarCRT = fromMaybe
(CRTe . fromJust' "UCyc: no CRT over CRTExt" T.scalarCRT . toExt)
((CRTr .) <$> T.scalarCRT)
{-# INLINABLE scalarCRT #-}
-- Eq instances
instance (Eq r, Tensor t, Fact m, TElt t r) => Eq (UCyc t m P r) where
(Pow v1) == (Pow v2) = v1 == v2 \\ witness entailEqT v1
{-# INLINABLE (==) #-}
instance (Eq r, Tensor t, Fact m, TElt t r) => Eq (UCyc t m D r) where
(Dec v1) == (Dec v2) = v1 == v2 \\ witness entailEqT v1
{-# INLINABLE (==) #-}
instance (Eq r, Fact m, UCElt t r) => Eq (UCyc t m C r) where
(CRTr v1) == (CRTr v2) = v1 == v2 \\ witness entailEqT v1
-- compare in pow due to precision
u1 == u2 = toPow u1 == toPow u2
{-# INLINABLE (==) #-}
---------- Numeric Prelude instances ----------
-- ZeroTestable instances
instance (ZeroTestable r, Tensor t, Fact m, TElt t r)
=> ZeroTestable.C (UCyc t m P r) where
isZero (Pow v) = isZero v \\ witness entailZTT v
{-# INLINABLE isZero #-}
instance (ZeroTestable r, Tensor t, Fact m, TElt t r)
=> ZeroTestable.C (UCyc t m D r) where
isZero (Dec v) = isZero v \\ witness entailZTT v
{-# INLINABLE isZero #-}
instance (ZeroTestable r, Fact m, UCElt t r)
=> ZeroTestable.C (UCyc t m C r) where
isZero (CRTr v) = isZero v \\ witness entailZTT v
-- use powerful basis due to precision
isZero u = isZero $ toPow u
{-# INLINABLE isZero #-}
-- Additive instances
instance (Additive r, Tensor t, Fact m, TElt t r) => Additive.C (UCyc t m P r) where
zero = Pow $ T.scalarPow zero
(Pow v1) + (Pow v2) = Pow $ zipWithT (+) v1 v2
(Pow v1) - (Pow v2) = Pow $ zipWithT (-) v1 v2
negate (Pow v) = Pow $ fmapT negate v
{-# INLINABLE zero #-}
{-# INLINABLE (+) #-}
{-# INLINABLE (-) #-}
{-# INLINABLE negate #-}
instance (Additive r, Tensor t, Fact m, TElt t r) => Additive.C (UCyc t m D r) where
zero = Dec $ T.scalarPow zero -- scalarPow works because it's zero
(Dec v1) + (Dec v2) = Dec $ zipWithT (+) v1 v2
(Dec v1) - (Dec v2) = Dec $ zipWithT (-) v1 v2
negate (Dec v) = Dec $ fmapT negate v
{-# INLINABLE zero #-}
{-# INLINABLE (+) #-}
{-# INLINABLE (-) #-}
{-# INLINABLE negate #-}
instance (Fact m, UCElt t r) => Additive.C (UCyc t m C r) where
zero = scalarCRT zero
-- CJP: precision OK? Alternatively, switch to pow and back after
-- adding/subtracting. Expensive, but necessary given output type.
(CRTr v1) + (CRTr v2) = CRTr $ zipWithT (+) v1 v2
(CRTe v1) + (CRTe v2) = CRTe $ zipWithT (+) v1 v2
_ + _ = error "UCyc (+) internal error: mixed CRTr/CRTe"
(CRTr v1) - (CRTr v2) = CRTr $ zipWithT (-) v1 v2
(CRTe v1) - (CRTe v2) = CRTe $ zipWithT (-) v1 v2
_ - _ = error "UCyc (-) internal error: mixed CRTr/CRTe"
negate (CRTr v) = CRTr $ fmapT negate v
negate (CRTe v) = CRTe $ fmapT negate v
{-# INLINABLE zero #-}
{-# INLINABLE (+) #-}
{-# INLINABLE (-) #-}
{-# INLINABLE negate #-}
-- Ring instance: only for CRT
instance (Fact m, UCElt t r) => Ring.C (UCyc t m C r) where
one = scalarCRT one
fromInteger c = scalarCRT $ fromInteger c
-- CJP: precision OK? Alternatively, switch to pow (and back) after
-- multiplying. Expensive, but necessary given output type.
(CRTr v1) * (CRTr v2) = CRTr $ zipWithT (*) v1 v2
(CRTe v1) * (CRTe v2) = CRTe $ zipWithT (*) v1 v2
_ * _ = error "UCyc internal error: mixed CRTr/CRTe"
{-# INLINABLE one #-}
{-# INLINABLE fromInteger #-}
{-# INLINABLE (*) #-}
instance (Ring r, Tensor t, Fact m, TElt t r) => Module.C r (UCyc t m P r) where
r *> (Pow v) = Pow $ fmapT (r *) v
{-# INLINABLE (*>) #-}
instance (Ring r, Tensor t, Fact m, TElt t r) => Module.C r (UCyc t m D r) where
r *> (Dec v) = Dec $ fmapT (r *) v
{-# INLINABLE (*>) #-}
instance (Ring r, Fact m, UCElt t r) => Module.C r (UCyc t m C r) where
r *> (CRTr v) = CRTr $ fmapT (r *) v
r *> (CRTe v) = CRTe $ fmapT (toExt r *) v
{-# INLINABLE (*>) #-}
instance (GFCtx fp d, Fact m, UCElt t fp) => Module.C (GF fp d) (UCyc t m P fp) where
-- can use any r-basis to define module mult, but must be
-- consistent.
r *> (Pow v) = Pow $ r LP.*> v \\ witness entailModuleT (r,v)
instance (Reduce a b, Tensor t, Fact m, TElt t a, TElt t b)
=> Reduce (UCyc t m P a) (UCyc t m P b) where
reduce (Pow v) = Pow $ fmapT reduce v
{-# INLINABLE reduce #-}
instance (Reduce a b, Tensor t, Fact m, TElt t a, TElt t b)
=> Reduce (UCyc t m D a) (UCyc t m D b) where
reduce (Dec v) = Dec $ fmapT reduce v
{-# INLINABLE reduce #-}
-- CJP: no Reduce for C rep because we can't efficiently handle CRTe case
type instance LiftOf (UCyc t m P r) = UCyc t m P (LiftOf r)
type instance LiftOf (UCyc t m D r) = UCyc t m D (LiftOf r)
instance (Lift' r, Tensor t, Fact m, TElt t r, TElt t (LiftOf r))
=> Lift' (UCyc t m P r) where
lift (Pow v) = Pow $ fmapT lift v
{-# INLINABLE lift #-}
instance (Lift' r, Tensor t, Fact m, TElt t r, TElt t (LiftOf r))
=> Lift' (UCyc t m D r) where
lift (Dec v) = Dec $ fmapT lift v
{-# INLINABLE lift #-}
instance (Rescale a b, Tensor t, Fact m, TElt t a, TElt t b)
=> Rescale (UCyc t m P a) (UCyc t m P b) where
rescale (Pow v) = Pow $ fmapT rescale v
{-# INLINABLE rescale #-}
instance (Rescale a b, Tensor t, Fact m, TElt t a, TElt t b)
=> Rescale (UCyc t m D a) (UCyc t m D b) where
rescale (Dec v) = Dec $ fmapT rescale v
{-# INLINABLE rescale #-}
-- CJP: we don't instantiate RescaleCyc because it requires changing bases
-- CJP: we don't instantiate Gadget etc., because (1) their methods
-- wouldn't be efficient, and (2) their superclasses are not even
-- well-defined (e.g., Ring for P rep).
-- | Type-restricted (and potentially more efficient) map for
-- powerful-basis representation.
fmapPow :: (Tensor t, Fact m, TElt t a, TElt t b)
=> (a -> b) -> UCyc t m P a -> UCyc t m P b
fmapPow f (Pow v) = Pow $ fmapT f v
{-# INLINABLE fmapPow #-}
-- | Type-restricted (and potentially more efficient) map for
-- decoding-basis representation.
fmapDec :: (Tensor t, Fact m, TElt t a, TElt t b)
=> (a -> b) -> UCyc t m D a -> UCyc t m D b
fmapDec f (Dec v) = Dec $ fmapT f v
{-# INLINABLE fmapDec #-}
-- | Unzip for unrestricted types.
unzipCyc :: (Tensor t, Fact m)
=> UCyc t m rep (a,b) -> (UCyc t m rep a, UCyc t m rep b)
{-# INLINABLE unzipCyc #-}
unzipCyc (Pow v) = Pow *** Pow $ unzipT v
unzipCyc (Dec v) = Dec *** Dec $ unzipT v
unzipCyc (CRTr v) = CRTr *** CRTr $ unzipT v
unzipCyc (CRTe v) = CRTe *** CRTe $ unzipT v
-- | Type-restricted (and potentially more efficient) unzip.
unzipUCElt :: (Tensor t, Fact m, UCElt t (a,b), UCElt t a, UCElt t b)
=> UCyc t m rep (a,b) -> (UCyc t m rep a, UCyc t m rep b)
{-# INLINABLE unzipUCElt #-}
unzipUCElt (Pow v) = Pow *** Pow $ unzipTElt v
unzipUCElt (Dec v) = Dec *** Dec $ unzipTElt v
unzipUCElt (CRTr v) = CRTr *** CRTr $ unzipTElt v
unzipUCElt (CRTe v) = CRTe *** CRTe $ unzipTElt v
-- | Multiply by the special element @g@.
mulG :: (Tensor t, Fact m, UCElt t r) => UCyc t m rep r -> UCyc t m rep r
{-# INLINABLE mulG #-}
mulG (Pow v) = Pow $ mulGPow v
mulG (Dec v) = Dec $ mulGDec v
-- fromJust is safe here because we're already in CRTr
mulG (CRTr v) = CRTr $ fromJust' "UCyc.mulG CRTr" mulGCRT v
mulG (CRTe v) = CRTe $ fromJust' "UCyc.mulG CRTe" mulGCRT v
-- | Divide by the special element @g@.
divG :: (Tensor t, Fact m, UCElt t r) => UCyc t m rep r -> Maybe (UCyc t m rep r)
{-# INLINABLE divG #-}
divG (Pow v) = Pow <$> divGPow v
divG (Dec v) = Dec <$> divGDec v
-- fromJust is OK here because we're already in CRTr
divG (CRTr v) = Just $ CRTr $ fromJust' "UCyc.divG CRTr" divGCRT v
divG (CRTe v) = Just $ CRTe $ fromJust' "UCyc.divG CRTe" divGCRT v
-- | Yield the scaled squared norm of @g_m \cdot e@ under
-- the canonical embedding, namely,
-- @\hat{m}^{ -1 } \cdot || \sigma(g_m \cdot e) ||^2@ .
gSqNorm :: (Ring r, Tensor t, Fact m, TElt t r) => UCyc t m D r -> r
gSqNorm (Dec v) = gSqNormDec v
{-# INLINABLE gSqNorm #-}
-- | Sample from the "tweaked" Gaussian error distribution @t*D@ in
-- the decoding basis, where @D@ has scaled variance @v@. (Note: This
-- implementation uses Double precision to generate the Gaussian
-- sample, which may not be sufficient for rigorous proof-based
-- security.)
tGaussian :: (Tensor t, Fact m, OrdFloat q, Random q, TElt t q,
ToRational v, MonadRandom rnd)
=> v -> rnd (UCyc t m D q)
tGaussian = fmap Dec . tGaussianDec
{-# INLINABLE tGaussian #-}
-- | Generate an LWE error term from the "tweaked" Gaussian with given
-- scaled variance, deterministically rounded using the decoding
-- basis.
errorRounded :: forall v rnd t m z .
(ToInteger z, Tensor t, Fact m, TElt t z,
ToRational v, MonadRandom rnd)
=> v -> rnd (UCyc t m D z)
{-# INLINABLE errorRounded #-}
errorRounded svar =
Dec . fmapT (roundMult one) <$> (tGaussianDec svar :: rnd (t m Double))
-- | Generate an LWE error term from the "tweaked" Gaussian with
-- scaled variance @v * p^2@, deterministically rounded to the given
-- coset of @R_p@ using the decoding basis.
errorCoset :: forall t m zp z v rnd .
(Mod zp, z ~ ModRep zp, Lift zp z, Tensor t, Fact m,
TElt t zp, TElt t z, ToRational v, MonadRandom rnd)
=> v -> UCyc t m D zp -> rnd (UCyc t m D z)
{-# INLINABLE errorCoset #-}
errorCoset =
let pval = fromIntegral $ proxy modulus (Proxy::Proxy zp)
in \ svar c -> do err <- tGaussian (svar*pval*pval) :: rnd (UCyc t m D Double)
return $! roundCoset <$> c <*> err
----- inter-ring operations
-- | Embed into an extension ring, for the powerful basis.
embedPow :: (Additive r, Tensor t, m `Divides` m', TElt t r)
=> UCyc t m P r -> UCyc t m' P r
embedPow (Pow v) = Pow $ T.embedPow v
{-# INLINABLE embedPow #-}
-- | Embed into an extension ring, for the decoding basis.
embedDec :: (Additive r, Tensor t, m `Divides` m', TElt t r)
=> UCyc t m D r -> UCyc t m' D r
embedDec (Dec v) = Dec $ T.embedDec v
{-# INLINABLE embedDec #-}
-- | Embed into an extension ring, for a CRT basis. (The output is
-- an 'Either' because in some cases it is most efficient to preserve
-- the 'UCyc' internal invariant by producing output with respect to
-- the powerful basis.)
embedCRT :: forall t m m' r . (m `Divides` m', UCElt t r)
=> UCyc t m C r -> Either (UCyc t m' P r) (UCyc t m' C r)
{-# INLINABLE embedCRT #-}
embedCRT x@(CRTr v) = fromMaybe (Left $ embedPow $ toPow x)
(Right . CRTr <$> (T.embedCRT <*> pure v))
embedCRT x@(CRTe v) =
-- preserve invariant: CRTe iff CRTr is invalid for m'
fromMaybe (Right $ CRTe $ fromJust' "UCyc.embedCRT CRTe" T.embedCRT v)
(proxyT hasCRTFuncs (Proxy::Proxy (t m r)) A.*>
pure (Left $ embedPow $ toPow x))
-- | Twace into a subring, for the powerful basis.
twacePow :: (Ring r, Tensor t, m `Divides` m', TElt t r)
=> UCyc t m' P r -> UCyc t m P r
twacePow (Pow v) = Pow $ twacePowDec v
{-# INLINABLE twacePow #-}
-- | Twace into a subring, for the decoding basis.
twaceDec :: (Ring r, Tensor t, m `Divides` m', TElt t r)
=> UCyc t m' D r -> UCyc t m D r
twaceDec (Dec v) = Dec $ twacePowDec v
{-# INLINABLE twaceDec #-}
-- | Twace into a subring, for a CRT basis. (The output is an
-- 'Either' because in some cases it is most efficient to preserve the
-- 'UCyc' internal invariant by producing output with respect to the
-- powerful basis.)
twaceCRT :: forall t m m' r . (m `Divides` m', UCElt t r)
=> UCyc t m' C r -> Either (UCyc t m P r) (UCyc t m C r)
{-# INLINABLE twaceCRT #-}
twaceCRT x@(CRTr v) =
-- stay in CRTr only iff it's valid for target, else go to Pow
fromMaybe (Left $ twacePow $ toPow x) (Right . CRTr <$> (T.twaceCRT <*> pure v))
twaceCRT x@(CRTe v) =
-- stay in CRTe iff CRTr is invalid for target, else go to Pow
fromMaybe (Right $ CRTe $ fromJust' "UCyc.twace CRTe" T.twaceCRT v)
(proxyT hasCRTFuncs (Proxy::Proxy (t m r)) A.*>
Just (Left $ twacePow $ toPow x))
-- | Yield the @O_m@-coefficients of an @O_m'@-element, with respect to
-- the relative powerful @O_m@-basis.
coeffsPow :: (Ring r, Tensor t, m `Divides` m', TElt t r)
=> UCyc t m' P r -> [UCyc t m P r]
{-# INLINABLE coeffsPow #-}
coeffsPow (Pow v) = LP.map Pow $ coeffs v
-- | Yield the @O_m@-coefficients of an @O_m'@ element, with respect to
-- the relative decoding @O_m@-basis.
coeffsDec :: (Ring r, Tensor t, m `Divides` m', TElt t r)
=> UCyc t m' D r -> [UCyc t m D r]
{-# INLINABLE coeffsDec #-}
coeffsDec (Dec v) = LP.map Dec $ coeffs v
-- | The relative powerful basis of @O_m' / O_m@.
powBasis :: (Ring r, Tensor t, m `Divides` m', TElt t r)
=> Tagged m [UCyc t m' P r]
{-# INLINABLE powBasis #-}
powBasis = (Pow <$>) <$> powBasisPow
-- | The relative mod-@r@ CRT set of @O_m' / O_m@, represented with
-- respect to the powerful basis (which seems to be the best choice
-- for typical use cases).
crtSet :: forall t m m' r p mbar m'bar .
(m `Divides` m', ZPP r, p ~ CharOf (ZpOf r),
mbar ~ PFree p m, m'bar ~ PFree p m',
UCElt t r, TElt t (ZpOf r))
=> Tagged m [UCyc t m' P r]
{-# INLINABLE crtSet #-}
crtSet =
-- CJP: consider using traceEvent or traceMarker
--DT.trace ("UCyc.crtSet: m = " ++
-- show (proxy valueFact (Proxy::Proxy m)) ++ ", m'= " ++
-- show (proxy valueFact (Proxy::Proxy m'))) $
let (p,e) = proxy modulusZPP (Proxy::Proxy r)
pp = Proxy::Proxy p
pm = Proxy::Proxy m
pm' = Proxy::Proxy m'
in retag (fmap (embedPow .
(if e > 1 then toPow . (^(p^(e-1))) . toCRT else toPow) .
Dec . fmapT liftZp) <$>
(crtSetDec :: Tagged mbar [t m'bar (ZpOf r)]))
\\ pFreeDivides pp pm pm' \\ pSplitTheorems pp pm \\ pSplitTheorems pp pm'
--------- Conversion methods ------------------
-- | Convert to powerful-basis representation.
toPow :: (Fact m, UCElt t r) => UCyc t m rep r -> UCyc t m P r
{-# INLINABLE toPow #-}
toPow x@(Pow _) = x
toPow (Dec v) = Pow $ l v
toPow (CRTr v) = Pow $ fromJust' "UCyc.toPow CRTr" crtInv v
toPow (CRTe v) =
Pow $ fmapT fromExt $ fromJust' "UCyc.toPow CRTe" crtInv v
-- | Convert to decoding-basis representation.
toDec :: (Fact m, UCElt t r) => UCyc t m rep r -> UCyc t m D r
{-# INLINABLE toDec #-}
toDec (Pow v) = Dec $ lInv v
toDec x@(Dec _) = x
toDec x@(CRTr _) = toDec $ toPow x
toDec x@(CRTe _) = toDec $ toPow x
-- | Convert to a CRT-basis representation.
toCRT :: forall t m rep r . (Fact m, UCElt t r)
=> UCyc t m rep r -> UCyc t m C r
{-# INLINABLE toCRT #-}
toCRT = let crte = CRTe . fromJust' "UCyc.toCRT: no crt for Ext" crt
crtr = fmap (CRTr .) crt
fromPow :: t m r -> UCyc t m C r
fromPow v = fromMaybe (crte $ fmapT toExt v) (crtr <*> Just v)
in \x -> case x of
(CRTr _) -> x
(CRTe _) -> x
(Pow v) -> fromPow v
(Dec v) -> fromPow $ l v
---------- Category-theoretic instances ----------
-- CJP: no Applicative, Foldable, Traversable for C because types
-- (and math) don't work out for the CRTe case.
instance (Tensor t, Fact m) => Functor (UCyc t m P) where
-- Functor instance is implied by Applicative laws
{-# INLINABLE fmap #-}
fmap f x = pure f <*> x
instance (Tensor t, Fact m) => Functor (UCyc t m D) where
-- Functor instance is implied by Applicative laws
{-# INLINABLE fmap #-}
fmap f x = pure f <*> x
instance (Tensor t, Fact m) => Applicative (UCyc t m P) where
pure = Pow . pure \\ proxy entailIndexT (Proxy::Proxy (t m r))
(Pow f) <*> (Pow v) = Pow $ f <*> v \\ witness entailIndexT v
{-# INLINABLE pure #-}
{-# INLINABLE (<*>) #-}
instance (Tensor t, Fact m) => Applicative (UCyc t m D) where
pure = Dec . pure \\ proxy entailIndexT (Proxy::Proxy (t m r))
(Dec f) <*> (Dec v) = Dec $ f <*> v \\ witness entailIndexT v
{-# INLINABLE pure #-}
{-# INLINABLE (<*>) #-}
instance (Tensor t, Fact m) => Foldable (UCyc t m P) where
{-# INLINABLE foldr #-}
foldr f b (Pow v) = F.foldr f b v \\ witness entailIndexT v
instance (Tensor t, Fact m) => Foldable (UCyc t m D) where
{-# INLINABLE foldr #-}
foldr f b (Dec v) = F.foldr f b v \\ witness entailIndexT v
instance (Tensor t, Fact m) => Traversable (UCyc t m P) where
{-# INLINABLE traverse #-}
traverse f (Pow v) = Pow <$> traverse f v \\ witness entailIndexT v
instance (Tensor t, Fact m) => Traversable (UCyc t m D) where
{-# INLINABLE traverse #-}
traverse f (Dec v) = Dec <$> traverse f v \\ witness entailIndexT v
---------- Utility instances ----------
instance (Random r, Tensor t, Fact m, CRTElt t r)
=> Random (Either (UCyc t m P r) (UCyc t m C r)) where
-- create in CRTr basis if possible, otherwise in powerful
random = let cons = fromMaybe (Left . Pow)
(proxyT hasCRTFuncs (Proxy::Proxy (t m r))
>> Just (Right . CRTr))
in \g -> let (v,g') = random g \\ witness entailRandomT v
in (cons v, g')
randomR _ = error "randomR non-sensical for UCyc"
instance (Show r, Show (CRTExt r), Tensor t, Fact m, TElt t r, TElt t (CRTExt r))
=> Show (UCyc t m rep r) where
show (Pow v) = "UCyc Pow: " ++ show v \\ witness entailShowT v
show (Dec v) = "UCyc Dec: " ++ show v \\ witness entailShowT v
show (CRTr v) = "UCyc CRTr: " ++ show v \\ witness entailShowT v
show (CRTe v) = "UCyc CRTe: " ++ show v \\ witness entailShowT v
instance (Arbitrary (t m r)) => Arbitrary (UCyc t m P r) where
arbitrary = Pow <$> arbitrary
shrink = shrinkNothing
instance (Arbitrary (t m r)) => Arbitrary (UCyc t m D r) where
arbitrary = Dec <$> arbitrary
shrink = shrinkNothing
instance (Arbitrary (t m r)) => Arbitrary (UCyc t m C r) where
arbitrary = CRTr <$> arbitrary
shrink = shrinkNothing
instance (Tensor t, Fact m, NFElt r, TElt t r, TElt t (CRTExt r))
=> NFData (UCyc t m rep r) where
rnf (Pow x) = rnf x \\ witness entailNFDataT x
rnf (Dec x) = rnf x \\ witness entailNFDataT x
rnf (CRTr x) = rnf x \\ witness entailNFDataT x
rnf (CRTe x) = rnf x \\ witness entailNFDataT x