module powerofpi where
open import Data.Nat
open import Data.Vec hiding(_++_ ; take ; drop ; split ; [_] ; concat)
open import Data.List hiding(take ; drop ; concat; [_])
open import Relation.Binary.PropositionalEquality hiding ([_])
open ≡-Reasoning
-- Based on:
-- Oury and Swierstra: The power of Pi.
----------------------------------------------
---Part I: A simple example for a view--------
----------------------------------------------
--snocView is a datatype indexed by (List A)'s
--i.e. for every list of type List A, there is a corresponding snocView
--The snocView makes the last element in the List available for pattern-matching
data snocView {A : Set} : List A → Set where
-- ⟨⟩ is the snocView of the empty list
⟨⟩ : snocView []
-- snoc takes a list xs of type (List A) and an element x of type A and gives back a snocView of (xs ++ (x :: [])
_snoc_ : (xs : List A) → (x : A) → snocView ( xs ++ (x ∷ []))
--Having defined a view for List, we now need a function that constructs the snocView for us
--The function view takes a list and gives back a snocView of that list
view : {A : Set} → (xs : List A) → snocView xs
view [] = ⟨⟩
view (x ∷ xs) with view xs
view (x ∷ .[]) | ⟨⟩
= [] snoc x
view (x ∷ .(ys ++ (y ∷ []))) | ys snoc y
= (x ∷ ys) snoc y
--Let's see this for some examples
list1 : List ℕ
list1 = 1 ∷ 2 ∷ 3 ∷ 4 ∷ []
snoclist1 : snocView list1
snoclist1 = view list1
list2 : List ℕ
list2 = []
snoclist2 : snocView list2
snoclist2 = view list2
--We can use our newly defined view to pattern-match on the last element of a list
--We use this in the function rotateright
rotateright : {A : Set} → List A → List A
rotateright xs with view xs
rotateright .[] | ⟨⟩
= []
rotateright .(ys ++ (y ∷ [])) | ys snoc y
= y ∷ ys
--Let's test this function
rotatedlist1 : List ℕ
rotatedlist1 = rotateright (rotateright list1)
-------------------------------------------------------------------
-----Part II: A more involved example: Cryptol---------------------
-------------------------------------------------------------------
--We have a type for Bits (0 and 1 dont work...)
data Bit : Set where
O : Bit
I : Bit
--And a type for Bit-Words of constant length
Word : ℕ → Set
Word n = Vec Bit n
--Now for some auxillary functions on Vectors and matrices
--The function "take" takes the first n bits of a vector and cuts of the rest
take : ∀ { m}{A : Set} → (n : ℕ) → Vec A (n + m) → Vec A n
take zero xs = []
take (suc n) (x ∷ xs) = x ∷ take n xs
--The function "drop" drops the first n bits of a vector and keeps the rest
drop : ∀ {m}{A : Set} → (n : ℕ) → Vec A (n + m) → Vec A m
drop zero xs = xs
drop (suc n) (x ∷ xs) = drop n xs
--The function "split" takes a vector of length n*m and makes it into a matrix of dimensions n*m
split : {A : Set} → (n : ℕ) → (m : ℕ) → Vec A (m * n) → Vec (Vec A n) m
split n zero [] = []
split n (suc m) x = take n x ∷ split n m (drop n x)
--The function "_app_" appends to Vectors
_app_ : ∀ {m n}{A : Set} → Vec A m → Vec A n → Vec A (m + n)
[] app ys = ys
(x ∷ xs) app ys = x ∷ (xs app ys)
--The function "concat" takes a matrix of dimension n*m and returns a "flattened" vector of length n*m
concat : ∀ {A n m} → Vec (Vec A n) m → Vec A (m * n)
concat [] = []
concat (x ∷ x₁) = x app (concat x₁)
--Lemma 1: Take drop Lemma
takeDropLemma : ∀ {A : Set}{ n m} → (xs : Vec A (n + m)) → (take n xs) app (drop n xs) ≡ xs
takeDropLemma {A} {zero} {m} xs = refl
takeDropLemma {A} {suc n} {m}(x ∷ xs) = cong (λ l → x ∷ l) (takeDropLemma {n = n} {m = m} xs)
--Lemma 2: Split Concat Lemma
--We show in a Lemma that first splitting a Vector and then flattening it does not change anything in the vector
splitConcatLemma : ∀ {A n m} → (xs : Vec A (m * n)) → concat (split n m xs) ≡ xs
splitConcatLemma {A} {n} {zero} [] = refl
--splitConcatLemma {A} {zero} {suc m} xs = splitConcatLemma {m = m} xs
splitConcatLemma {A}{n = n} {suc m} xs =
begin
take n xs app concat (split n m (drop n xs))
≡⟨ cong (λ l → take n xs app l) (splitConcatLemma {n = n} {m = m}(drop n xs)) ⟩
take n xs app drop n xs
≡⟨ takeDropLemma {n = n} {m = m * n} xs ⟩
xs
∎
--Now to the SplitView datatype
data SplitView {A : Set} : {n : ℕ} → (m : ℕ) → Vec A (m * n) → Set where
[_] : ∀ {m n} → (xss : Vec (Vec A n) m) → SplitView m (concat xss)
--Now let's define the view function that takes a Matrix and gives back a SplitView
splitview : {A : Set} → (n : ℕ) → (m : ℕ) → (xs : Vec A (m * n)) → SplitView m xs
splitview n m xs with concat (split n m xs ) | [ split n m xs ] | splitConcatLemma {n = n} {m = m} xs
splitview n m xs | .xs | v | refl = v
--Now let's implement the function
swab : Word 16 → Word 16
swab xs with splitview 4 4 xs
swab ._ | [ a ∷ b ∷ c ∷ d ∷ [] ]
= concat (b ∷ a ∷ d ∷ c ∷ [])
--Now let's test the function
testword : Word 16
testword = O ∷ O ∷ O ∷ O ∷ I ∷ I ∷ I ∷ I ∷ O ∷ O ∷ I ∷ I ∷ I ∷ I ∷ O ∷ O ∷ []
testsplit : SplitView 4 testword
testsplit = splitview 4 4 testword
swabword : Word 16
swabword = swab testword