module Vector.Properties where

open import Level using (Level)
open import Function
open import Data.Unit using ()
open import Data.Product
open import Data.Sum
open import Data.Bool using (Bool; T; false; true)
open import Data.Maybe
open import Data.Nat using ()
open import Data.Fin
open import Data.Fin.Properties
open import Data.Vec.Functional
open import Data.Vec.Functional.Properties
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary

open import Vector.Base

open ≡-Reasoning

private variable
   : Level
  A : Set 
  m n : 

tail++same : (xs : Vector A (ℕ.suc m)) (ys : Vector A n)  tail (xs ++ ys)  (tail xs ++ ys)
tail++same {m = m} _ _ i with splitAt m i
... | inj₁ _ = refl
... | inj₂ _ = refl

head++tail≡id : (xs : Vector A (ℕ.suc n))  (head xs  tail xs)  xs
head++tail≡id xs zero = refl
head++tail≡id xs (suc i) = refl

++-split :  x (xs : Vector A m) (ys : Vector A n)
   ((x  xs) ++ ys)  (x  xs ++ ys)
++-split x xs ys zero = refl
++-split {m = m} x xs ys (suc i) with splitAt m i
... | inj₁ _ = refl
... | inj₂ _ = refl

swapCons :  x (xs : Vector A n) (i j : Fin n) 
  swapV (x  xs) (suc i) (suc j)  (x  swapV xs i j)
swapCons _ _ _ _ zero    = refl
swapCons _ _ _ _ (suc _) = refl

[]≔-++ :  (xs : Vector A m) (ys : Vector A n) p v
   (xs ++ ys) [ p ↑ˡ n ]≔ v  xs [ p ]≔ v ++ ys
[]≔-++ {m = m} {n} xs ys p v i with i  p ↑ˡ n
... | yes refl rewrite updateAt-updates i {const v} (xs ++ ys) | splitAt-↑ˡ m p n =
  sym (updateAt-updates p _)
... | no   i≢p rewrite updateAt-minimal _ _ {const v} (xs ++ ys) i≢p with splitAt m i in splitEq
...  | inj₁ j = sym (updateAt-minimal j p xs  where refl  i≢p (sym (splitAt⁻¹-↑ˡ splitEq))))
...  | inj₂ _ = refl

cong-updateAt :  {xs ys : Vector A n} p f  xs  ys 
  updateAt xs p f  updateAt ys p f
cong-updateAt zero f xs≡ys zero = cong f (xs≡ys zero)
cong-updateAt (suc p) f xs≡ys zero = xs≡ys zero
cong-updateAt zero f xs≡ys (suc i) = xs≡ys (suc i)
cong-updateAt (suc p) f xs≡ys (suc i) = cong-updateAt p f (xs≡ys  suc) i

swapV-++ :  (xs : Vector A m) (ys : Vector A n) p q
   swapV (xs ++ ys) (p ↑ˡ n) (q ↑ˡ n)  swapV xs p q ++ ys
swapV-++ {m = m} {n} xs ys p q i = begin
  (xs++ysP [ q↑n ]≔ xs++ys p↑n) i ≡⟨ cong-updateAt q↑n _ xs++ysP-split i 
  ((xs [ p ]≔ xs q ++ ys) [ q↑n ]≔ xs++ys p↑n) i ≡⟨ xs++ysQ-split i 
  ((xs [ p ]≔ xs q ++ ys) [ q ↑ˡ n ]≔ xs p) i ≡⟨ []≔-++ (xs [ p ]≔ xs q) _ _ _ i 
  (xs [ p ]≔ xs q [ q ]≔ xs p ++ ys) i 
  where
  xs++ys = xs ++ ys
  p↑n = p ↑ˡ n
  q↑n = q ↑ˡ n

  xs++ysP = xs++ys [ p↑n ]≔ xs++ys q↑n

  xs++≡q : xs++ys q↑n  xs q
  xs++≡q = lookup-++ˡ xs ys _

  xs++≡p : xs++ys p↑n  xs p
  xs++≡p = lookup-++ˡ xs ys _

  xs++ysP-split : xs++ysP  xs [ p ]≔ xs q ++ ys
  xs++ysP-split i = trans ([]≔-++ xs ys _ _ i)
    (cong  x  (xs [ p ]≔ x ++ ys) i) xs++≡q)

  xs++ysQ-split : (xs [ p ]≔ xs q ++ ys) [ q↑n ]≔ xs++ys p↑n
     (xs [ p ]≔ xs q ++ ys) [ q↑n ]≔ xs p
  xs++ysQ-split i = cong  x  ((xs [ p ]≔ xs q ++ ys) [ q↑n ]≔ x) i) xs++≡p

swap-exchange :  (xs : Vector A n) i j  swapV xs i j  swapV xs j i
swap-exchange xs i j k with i  j | k  i | k  j
... | yes refl | _ | _  = refl
... | no i≢j | yes refl | _ = begin
 _ ≡⟨ updateAt-minimal _ _ _ i≢j 
 _ ≡⟨ updateAt-updates i _ 
 _ ≡˘⟨ updateAt-updates i _ 
 _ 
... | no i≢j | no k≢i | yes refl = begin
  _ ≡⟨ updateAt-updates j _ 
  _ ≡˘⟨ updateAt-updates j _ 
  _ ≡˘⟨ updateAt-minimal _ _ _ (i≢j  sym) 
  _ 
... | no i≢j | no k≢i | no k≢j = begin
  _ ≡⟨ updateAt-minimal _ _ _ k≢j 
  _ ≡⟨ updateAt-minimal _ _ _ k≢i 
  _ ≡˘⟨ updateAt-minimal _ _ _ k≢j 
  _ ≡˘⟨ updateAt-minimal _ _ _ k≢i 
  _ 

swap-injective :  {xs ys : Vector A n} {i j}  swapV xs i j  swapV ys i j  xs  ys
swap-injective {n = n} {xs} {ys} {i} {j} same k with k  i | k  j | i  j
... | yes refl | yes refl | _ = begin
  _ ≡˘⟨ updateAt-updates i _ 
  _ ≡⟨ same k 
  _ ≡⟨ updateAt-updates i _ 
  _ 
... | yes refl | no k≢j | yes refl = begin
  _ ≡˘⟨ updateAt-updates i _ 
  _ ≡˘⟨ updateAt-minimal _ _ _ k≢j 
  _ ≡⟨ same k 
  _ ≡⟨ updateAt-minimal _ _ _ k≢j 
  _ ≡⟨ updateAt-updates i _ 
  _ 
... | yes refl | no k≢j | no i≢j = begin
  _ ≡˘⟨ updateAt-updates j _ 
  _ ≡⟨ same j 
  _ ≡⟨ updateAt-updates j _ 
  _ 
... | no k≢i | yes refl | _ = begin
  _ ≡˘⟨ updateAt-updates i _ 
  _ ≡˘⟨ updateAt-minimal _ _ _ (k≢i  sym) 
  _ ≡⟨ same i 
  _ ≡⟨ updateAt-minimal _ _ _ (k≢i  sym) 
  _ ≡⟨ updateAt-updates i _ 
  _ 
... | no k≢i | no k≢j | _ = begin
  _ ≡˘⟨ updateAt-minimal _ _ _ k≢i 
  _ ≡˘⟨ updateAt-minimal _ _ _ k≢j 
  _ ≡⟨ same k 
  _ ≡⟨ updateAt-minimal _ _ _ k≢j 
  _ ≡⟨ updateAt-minimal _ _ _ k≢i 
  _ 


swap-involute :  (xs : Vector A n) i j  swapV (swapV xs i j) i j  xs
swap-involute xs i j k with k  i | k  j | i  j
... | yes refl | yes refl | _ = begin
  _ ≡⟨ updateAt-updates i _ 
  _ ≡⟨ updateAt-updates i _ 
  _ 
... | yes refl | no k≢j | _ = begin
  _ ≡⟨ updateAt-minimal _ _ _ k≢j 
  _ ≡⟨ updateAt-updates i _ 
  _ ≡⟨ updateAt-updates j _ 
  _ 
... | no k≢i | yes refl | no i≢j = begin
  _ ≡⟨ updateAt-updates j _ 
  _ ≡⟨ updateAt-minimal _ _ _ i≢j 
  _ ≡⟨ updateAt-updates i _ 
  _ 
... | no k≢i | yes refl | yes refl = begin
  _ ≡⟨ updateAt-updates j _ 
  _ ≡⟨ updateAt-updates i _ 
  _ 
... | no k≢i | no k≢j | _ = begin
  _ ≡⟨ updateAt-minimal _ _ _ k≢j 
  _ ≡⟨ updateAt-minimal _ _ _ k≢i 
  _ ≡⟨ updateAt-minimal _ _ _ k≢j 
  _ ≡⟨ updateAt-minimal _ _ _ k≢i 
  _ 

swap-same-left :  {xs ys : Vector A n} {i j}  swapV xs i j  ys  xs  swapV ys i j
swap-same-left {n = n} {xs} {ys} {i} {j} same =
  swap-injective λ j  trans (same _) (sym (swap-involute ys _ _ _))

swap-diff :  (xs : Vector A n) k {i j}  k  i  k  j  swapV xs i j k  xs k
swap-diff xs k {i} {j} k≢i k≢j = begin
  _ ≡⟨ updateAt-minimal _ _ _ k≢j 
  _ ≡⟨ updateAt-minimal _ _ _ k≢i 
  _ 


firstHasProperty : {A : Set } (xs : Vector A n) (f : A  Bool) 
  maybe  (_ , a)  T (f a))  (findFirst xs f)
firstHasProperty {n = ℕ.zero} xs f = _
firstHasProperty {n = ℕ.suc n} xs f with f (xs zero) in eqF0
... | true rewrite eqF0 = _
... | false rewrite eqF0 with findFirst (tail xs) f in eqF | firstHasProperty (tail xs) f
... | just _ | prop = prop
... | nothing | _ rewrite eqF = _

appendLastFromℕ :  (xs : Vector A n) a  appendLast xs a (fromℕ _)  a
appendLastFromℕ {n = ℕ.zero} xs a = refl
appendLastFromℕ {n = ℕ.suc n} xs a = appendLastFromℕ (tail xs) a

appendLastInj :  (xs : Vector A n) a i  appendLast xs a (inject₁ i)  xs i
appendLastInj xs a zero = refl
appendLastInj xs a (suc i) = appendLastInj (tail xs) a i

appendLastLower :  (xs : Vector A n) a i (n≢i : n  toℕ i)  appendLast xs a i  xs (lower₁ i n≢i)
appendLastLower {n = ℕ.zero} xs a zero n≢i = contradiction refl n≢i
appendLastLower {n = ℕ.suc n} xs a zero n≢i = refl
appendLastLower {n = ℕ.suc n} xs a (suc i) n≢i = appendLastLower (tail xs) a i  n≡i  n≢i (cong ℕ.suc n≡i))