{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Crypto.Lol.Types.ZmStar
( order, partitionCosets
) where
import Crypto.Lol.Factored
import Crypto.Lol.Prelude as LP hiding (null)
import Crypto.Lol.Reflects
import Crypto.Lol.Types.Unsafe.ZqBasic hiding (ZqB)
import Data.List as L (foldl', transpose)
import Data.Map.Strict (Map, elems, empty, insertWith)
import Data.Set as S (Set, difference, findMin, fromList, map, null)
order :: forall m . (Reflects m Int) => Int -> Int
order p =
let mval = value @m
in if gcd p mval /= 1
then error "p and m not coprime"
else 1 + length (takeWhile (/= one) $
tail $ iterate (* fromIntegral p) (one :: ZqBasic m Int))
cosets :: forall zm . (Mod zm, ModRep zm ~ Int, Ord zm, Ring zm)
=> Int -> [Set zm]
cosets p =
let mval = modulus @zm
in if gcd p mval /= 1
then error "p and m not coprime"
else let zmstar = fromList $ LP.map fromIntegral $ filter ((==) 1 . gcd mval) [1..mval]
zp = fromIntegral p
coset x = fromList $ x : takeWhile (/=x) (iterate (*zp) $ zp*x)
genCosets s | null s = []
genCosets s = let c = coset (findMin s)
in c : genCosets (difference s c)
in genCosets zmstar
partitionCosets :: forall m m' . (m `Divides` m') => Int -> [[Int]]
partitionCosets p =
let m'cosets = cosets p
partition =
L.foldl' (\cmap x -> insertWith (++) (S.map (reduce . lift) x) [x] cmap)
(empty :: Map (Set (ZqBasic m Int)) [Set (ZqBasic m' Int)])
m'cosets
part' = transpose $ elems partition
in LP.map (LP.map (lift . findMin)) part'