content: {-|
  Module      : Crypto.Lol.Tests.CycTests
  Description : Tests for the 'Cyc' interface.
  Copyright   : (c) Eric Crockett, 2011-2017
                    Chris Peikert, 2011-2017
  License     : GPL-3
  Maintainer  : ecrockett0@email.com
  Stability   : experimental
  Portability : POSIX
  
  Tests for the 'Cyc' interface.
  -}
  
  {-# LANGUAGE DataKinds             #-}
  {-# LANGUAGE FlexibleContexts      #-}
  {-# LANGUAGE NoImplicitPrelude     #-}
  {-# LANGUAGE PartialTypeSignatures #-}
  {-# LANGUAGE PolyKinds             #-}
  {-# LANGUAGE ScopedTypeVariables   #-}
  {-# LANGUAGE TypeFamilies          #-}
  {-# LANGUAGE TypeOperators         #-}
  
  {-# OPTIONS_GHC -fno-warn-partial-type-signatures #-}
  
  module Crypto.Lol.Tests.CycTests (cycTests1, cycTests2) where
  
  import Control.Applicative
  import Control.Monad       (join, liftM2)
  
  import Crypto.Lol
  
  import Crypto.Lol.Utils.ShowType
  import Crypto.Lol.Utils.Tests    (Gen, Test, testGroup, testWithGen,
                                    testWithoutGen)
  
  -- | Tests for single-index 'Cyc' operations. There must be a CRT basis for \(O_m\) over @r@.
  cycTests1 :: forall t m r . (Fact m, _) => Gen (Cyc t m r) -> Test
  cycTests1 cycGen =
    let ptmr = Proxy :: Proxy '(t,m,r) in
    testGroup (showType ptmr) $ ($ cycGen) <$> [
     testWithGen "mulGPow" prop_mulgPow,
     testWithGen "mulGDec" prop_mulgDec,
     testWithGen "mulGCRT" prop_mulgCRT]
  
  -- | Tests for inter-ring 'Cyc' operations. There must be a CRT basis for \(O_{m'}\) over @r@.
  cycTests2 :: forall t m m' r . _ => Proxy '(t,m,m',r) -> Gen (Cyc t m' r) -> Test
  cycTests2 _ cycGen =
    let ptmmr = Proxy::Proxy '(t,m,m',r)
    in testGroup (showType ptmmr) [
        testWithoutGen "crtSet" (prop_crtSet_pairs ptmmr),
        testWithGen "coeffsPow" (prop_coeffsPow ptmmr) cycGen]
  
  prop_mulgPow :: _ => Cyc t m r -> Bool
  prop_mulgPow x =
    let y = advisePow x
    in y == fromJust' "prop_mulgPow failed divisibility!" (divG $ mulG y)
  
  prop_mulgDec :: _ => Cyc t m r -> Bool
  prop_mulgDec x =
    let y = adviseDec x in
    y == fromJust' "prop_mulgDec failed divisibility!" (divG $ mulG y)
  
  prop_mulgCRT :: _ => Cyc t m r -> Bool
  prop_mulgCRT x =
    let y = adviseCRT x in
    y == fromJust' "prop_mulgCRT failed divisibility!" (divG $ mulG y)
  
  prop_coeffsPow :: forall t m m' r . _ => Proxy '(t,m,m',r) -> Cyc t m' r -> Bool
  prop_coeffsPow _ x =
    let xs = map embed (coeffsPow x :: [Cyc t m r])
        bs = proxy powBasis (Proxy::Proxy m)
    in sum (zipWith (*) xs bs) == x
  
  -- verifies that CRT set elements satisfy c_i * c_j = delta_ij * c_i
  -- necessary (but not sufficient) condition
  prop_crtSet_pairs :: forall (t :: Factored -> * -> *) m (m' :: Factored) (r :: *) . _
                    => Proxy '(t,m,m',r) -> Bool
  prop_crtSet_pairs _ =
    let crtset = proxy crtSet (Proxy::Proxy m) :: [Cyc t m' r]
        pairs = join (liftM2 (,)) crtset
    in all (\(a,b) -> if a == b then a*b == a else a*b == zero) pairs
  