{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-duplicate-exports #-}
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
module Crypto.Lol.PosBinDefs
(
Pos(..), Sing(SO, SS), SPos, PosC, posType, posDec
, reifyPos, reifyPosI, posToInt, intToPos
, addPos, sAddPos, AddPos, subPos, sSubPos, SubPos
, OSym0, SSym0, SSym1, AddPosSym0, AddPosSym1, SubPosSym0, SubPosSym1
, Bin(..), Sing(SB1, SD0, SD1), SBin, BinC, binType, binDec
, reifyBin, reifyBinI, binToInt, intToBin
, B1Sym0, D0Sym0, D0Sym1, D1Sym0, D1Sym1
, intDec, primes, prime
)
where
import Data.Singletons.Prelude
import Data.Singletons.TH
import Language.Haskell.TH
import Algebra.ToInteger as ToInteger
import NumericPrelude
singletons [d|
data Pos = O
| S Pos
deriving (Show, Eq)
instance Ord Pos where
compare O O = EQ
compare O (S _) = LT
compare (S _) O = GT
compare (S a) (S b) = compare a b
addPos :: Pos -> Pos -> Pos
addPos O b = S b
addPos (S a) b = S $ addPos a b
subPos :: Pos -> Pos -> Pos
subPos (S a) O = a
subPos (S a) (S b) = subPos a b
subPos O _ = error "Invalid call to subPos: a <= b"
|]
{-# INLINABLE posToInt #-}
posToInt :: ToInteger.C z => Pos -> z
posToInt O = one
posToInt (S a) = one + posToInt a
singletons [d|
data Bin = B1
| D0 Bin
| D1 Bin
deriving (Show, Eq)
instance Ord Bin where
compare B1 B1 = EQ
compare B1 (D0 _) = LT
compare B1 (D1 _) = LT
compare (D0 _) B1 = GT
compare (D1 _) B1 = GT
compare (D0 a) (D0 b) = compare a b
compare (D1 a) (D1 b) = compare a b
compare (D0 a) (D1 b) = case compare a b of
EQ -> LT
LT -> LT
GT -> GT
compare (D1 a) (D0 b) = case compare a b of
EQ -> GT
LT -> LT
GT -> GT
|]
intToPos :: ToInteger.C z => z -> Pos
intToPos 1 = O
intToPos x | x > 0 = S $ intToPos $ x-1
intToPos _ = error "cannot convert non-positive value to a Pos"
{-# INLINABLE binToInt #-}
binToInt :: ToInteger.C z => Bin -> z
binToInt B1 = one
binToInt (D0 a) = 2 * binToInt a
binToInt (D1 a) = 1 + 2 * binToInt a
intToBin :: ToInteger.C z => z -> Bin
intToBin 1 = B1
intToBin x | x > 0 =
if even x
then D0 $ intToBin $ x `div` 2
else D1 $ intToBin $ x `div` 2
type PosC (p :: Pos) = SingI p
type BinC (b :: Bin) = SingI b
reifyPos :: Int -> (forall p . SPos p -> a) -> a
reifyPos x _ | x < 1 = error $ "reifyPos: non-positive x = " ++ show x
reifyPos 1 k = k SO
reifyPos n k = reifyPos (n-1) (k . SS)
reifyPosI :: Int -> (forall p proxy . PosC p => proxy p -> a) -> a
reifyPosI n k = reifyPos n (\(p::SPos p) -> withSingI p $ k (Proxy::Proxy p))
reifyBin :: Int -> (forall b . SBin b -> a) -> a
reifyBin x _ | x < 1 = error $ "reifyBin: non-positive x = " ++ show x
reifyBin 1 k = k SB1
reifyBin x k | even x = reifyBin (x `div` 2) (k . SD0)
reifyBin x k = reifyBin (x `div` 2) (k . SD1)
reifyBinI :: Int -> (forall b proxy . BinC b => proxy b -> a) -> a
reifyBinI n k = reifyBin n (\(b::SBin b) -> withSingI b $ k (Proxy::Proxy b))
posType :: Int -> TypeQ
posType n
| n <= 0 = fail $ "posType: non-positive argument n = " ++ show n
| n == 1 = conT 'O
| otherwise = conT 'S `appT` posType (n-1)
binType :: Int -> TypeQ
binType n
| n <= 0 = fail $ "binType: non-positive argument n = " ++ show n
| otherwise = case n `quotRem` 2 of
(0,1) -> conT 'B1
(q,0) -> conT 'D0 `appT` binType q
(q,1) -> conT 'D1 `appT` binType q
_ -> fail "internal error in PosBinTH.bin"
posDec, binDec :: Int -> DecQ
posDec = intDec "P" posType
binDec = intDec "B" binType
intDec :: String
-> (Int -> TypeQ)
-> Int
-> DecQ
intDec pfx f n = tySynD (mkName $ pfx ++ show n) [] (f n)
primes :: [Int]
primes = 2 : 3 : 5 : primes'
where
isPrime (p:ps) n = p*p > n || n `rem` p /= 0 && isPrime ps n
primes' = 7 : filter (isPrime primes') (scanl (+) 11 $ cycle [2,4,2,4,6,2,6,4])
prime :: Int -> Bool
prime = go primes
where go (p:ps) n = case compare p n of
LT -> go ps n
EQ -> True
GT -> False