{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-missing-safe-haskell-mode -Wno-unsafe #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Sponge
( sponge,
Block,
W,
WBits,
State,
B,
)
where
import Control.Applicative (Applicative (..))
import Control.Arrow (Arrow (..))
import Control.Category (Category (..))
import Data.Bit (Bit (..), Vector)
import Data.Bitraversable (Bitraversable (..))
import Data.Bits (Bits (..), FiniteBits (..))
import Data.Bool (Bool (..))
import Data.Foldable (Foldable (..))
import Data.Functor (Functor (..), (<$>))
import Data.Maybe (Maybe)
import Data.Proxy (Proxy (..))
import Data.Semigroup (Semigroup (..))
import Data.Tuple (uncurry)
import Data.Type.Nat (Nat (..))
import Data.Type.Nat qualified as Nat
import Data.Type.Nat.LE qualified as Nat
import Data.Vec.Lazy (Vec (..))
import Data.Vec.Lazy qualified as Vec
import Data.Vector.Generic qualified as V
import Yaya.Fold (Corecursive (..), Projectable (..), Steppable (..))
import Yaya.Zoo (Stream)
import Prelude (Num (..), fromIntegral, undefined, ($))
instance (Nat.SNatI n, Bits a) => Bits (Vec n a) where
(.&.) = Vec.zipWith (.&.)
(.|.) = Vec.zipWith (.|.)
xor = Vec.zipWith xor
complement = fmap complement
bitSizeMaybe _ = (Nat.reflectToNum (Proxy @n) *) <$> bitSizeMaybe @a undefined
{-# INLINEABLE bitSizeMaybe #-}
instance (Nat.SNatI n, FiniteBits a) => FiniteBits (Vec n a) where
finiteBitSize _ = Nat.reflectToNum (Proxy @n) * finiteBitSize @a undefined
type family Exp (n :: Nat) (m :: Nat) :: Nat where
Exp 'Z ('S _) = 'Z
Exp ('S _) 'Z = 'S 'Z
Exp n ('S 'Z) = n Exp n ('S m) = Nat.Mult n (Exp n m)
type Block = Nat.FromGHC 5
type W l = Exp (Nat.FromGHC 2) l
type WBits (l :: Nat) = Vec (W l) Bool
type State l = Vec Block (Vec Block (WBits l))
type B l = Nat.Mult Block (Nat.Mult Block (W l))
restructureVec ::
(Nat.SNatI x, Nat.SNatI y, Nat.SNatI (W l), Nat.SNatI (Nat.Mult y (W l))) =>
Proxy l ->
Vec (Nat.Mult x (Nat.Mult y (W l))) Bool ->
Vec x (Vec y (WBits l))
restructureVec Proxy = fmap Vec.chunks . Vec.chunks
unstructureVec ::
Proxy l ->
Vec x (Vec y (WBits l)) ->
Vec (Nat.Mult x (Nat.Mult y (W l))) Bool
unstructureVec Proxy = Vec.concat . fmap Vec.concat
data ComposedVec w n a = ComposedVec {getVec :: (Vec n a), getWritten :: w}
takeFromStream :: Nat.SNatI n => Stream a -> Vec n a
takeFromStream stream =
getVec $
Nat.induction1
(ComposedVec VNil stream)
(\(ComposedVec v s) -> uncurry ComposedVec . first (::: v) $ project s)
takeEnough ::
Nat.SNatI n => Stream (Vec ('S r) Bool) -> Vec ('S n) (Vec ('S r) Bool)
takeEnough = takeFromStream
sponge ::
forall l' l r c n.
( Nat.SNatI n,
Nat.SNatI (W l),
Bits (WBits l),
Nat.SNatI r,
Nat.SNatI c,
Nat.SNatI (Nat.Mult Block (W l)),
B l ~ Nat.Plus ('S r) ('S c),
Nat.LE ('S r) (B l),
Nat.LE ('S l') (Nat.Mult ('S n) ('S r))
) =>
Proxy l ->
(State l -> State l) ->
(Proxy ('S r) -> Nat -> Vector Bit) ->
Proxy ('S r) ->
Proxy ('S c) ->
Proxy ('S n) ->
Vector Bit ->
Maybe (Vec ('S l') Bool)
sponge l f pad r Proxy Proxy =
fmap (Vec.take . Vec.concat . takeEnough @n . squeeze . absorb)
. cut
. uncurry (<>)
. (id &&& pad r . fromIntegral . V.length)
where
cut :: Vector Bit -> Maybe [Vec ('S r) Bool]
cut p =
if V.null p
then pure []
else
fmap (uncurry (:))
. bitraverse
(Vec.fromList . V.toList . V.map unBit)
cut
$ V.splitAt (Nat.reflectToNum r) p
absorb :: [Vec ('S r) Bool] -> State l
absorb =
foldr
( \p_i s ->
f (s `xor` restructureVec l (p_i Vec.++ pure @(Vec ('S c)) False))
)
. pure
$ pure zeroBits
squeeze :: State l -> Stream (Vec ('S r) Bool)
squeeze =
embed
. ( Vec.take @('S r) . unstructureVec l
&&& ana ((Vec.take @('S r) . unstructureVec l &&& id) . f)
)