module Vec.Updates.Base where

open import Level
open import Function
open import Data.Empty
open import Data.Bool
open import Data.Maybe as Maybe
open import Data.Maybe.Properties
open import Data.Product
open import Data.Nat hiding (less-than-or-equal)
open import Data.Fin as Fin
open import Data.Vec as Vec hiding (_[_]≔_)
import Data.Vec.Properties as Vec
open import Data.Vec.Functional using (Vector; fromVec)
open import Data.Vec.Functional.Properties using (updateAt-updates; updateAt-minimal)
open import Relation.Binary.PropositionalEquality
open import Vec.Relation.FirstOrNot
open import Relation.Nullary.Decidable
open import Relation.Nullary
open import Relation.Binary

open import Vector

private variable
   ℓ1 : Level
  A : Set 
  a : A
  m n p q : 
  b : Bool
  xs : Vec A n

open ≡-Reasoning

evalFromListUpdates : (xs : Vector A n) {indices : Vec (Fin n) m} (values : Vec A m) (i : Fin n)
  (firstOrNot : FirstOrNot (i ≡_) indices b)  A
evalFromListUpdates xs values i (there _ firstOrNot) =
  evalFromListUpdates xs (tail values) i firstOrNot
evalFromListUpdates {b = false} xs values i firstOrNot = xs i
evalFromListUpdates {b = true} xs (x  values) i (here _ _) = x

vecUpdates : (xs : Vector A n) (indices : Vec (Fin n) m) (values : Vec A m)  Vector A n
vecUpdates xs [] [] = xs
vecUpdates xs (ind  indices) (val  values) = vecUpdates xs indices values [ ind ]≔ val

vecUpdates≡listUpdates :  (xs : Vector A n) {indices : Vec (Fin n) m} (values : Vec A m) i
  (firstOrNot : FirstOrNot (i ≡_) indices b)
   vecUpdates xs indices values i  evalFromListUpdates xs values i firstOrNot
vecUpdates≡listUpdates xs [] i notHere = refl
vecUpdates≡listUpdates xs {.(i  _)} (_  values) i (here refl _) = updateAt-updates i _
vecUpdates≡listUpdates xs {ind  inds} (val  values) i (there ¬p firstOrNot) = begin
  (vecUpdates xs inds values [ ind ]≔ val) i ≡⟨ updateAt-minimal _ _ _ ¬p 
  vecUpdates xs inds values i                ≡⟨ vecUpdates≡listUpdates xs values i firstOrNot 
  evalFromListUpdates xs (val  values) i (there ¬p firstOrNot) 

evalFromPosition : (values : Vec A n) (default : A) (pos : Maybe $ Fin n)  A
evalFromPosition = maybe′  fromVec

firstOrNot→pos : (xs : Vector A n) {indices : Vec (Fin n) m} (values : Vec A m) (i : Fin n)
  (firstOrNot : FirstOrNot (i ≡_) indices b)  A
firstOrNot→pos xs values i = evalFromPosition values (xs i)  firstOrNotPositionMaybe

infixr 5 _∷_

data VecWithType {A : Set } (P : A  Set ℓ1) : Vec A n  Set ( Level.⊔ ℓ1) where
  []  : VecWithType P []
  _∷_ : P a  VecWithType P xs  VecWithType P (a  xs)

VecIndBool :  (indices : Vec A n) vBool k  Set _
VecIndBool indices vBool k = VecWithType  (ind , b)  Reflects (k  ind) b) $ Vec.zip indices vBool

evalFromVReflect : (xs : Vector A n) {vBool : Vec Bool m}
  (values : Vec A m) (i : Fin n) (firstOrNot : FirstOrNot T vBool b)  A
evalFromVReflect xs values i firstOrNot = evalFromPosition values
  (xs i) (firstOrNotPositionMaybe firstOrNot)

evalFromVReflect¬≡ :   (xs : Vector A n) {vBool : Vec Bool m}
  (values : Vec A m) (i : Fin n) (firstOrNot : FirstOrNot T vBool b) val 
  evalFromVReflect xs (val  values) i (there id firstOrNot)
   evalFromVReflect xs values i firstOrNot
evalFromVReflect¬≡ {b = false} xs values i firstOrNot val = refl
evalFromVReflect¬≡ {b = true} xs values i firstOrNot val = refl

vecUpdates≡reflectBool :  (xs : Vector A n) {indices : Vec (Fin n) m} (values : Vec A m) i
  {vBool : Vec Bool m} (vType : VecWithType  (ind , b)  Reflects (i  ind) b) (Vec.zip indices vBool))
  (firstOrNot : FirstOrNot T vBool b)
   vecUpdates xs indices values i  evalFromVReflect xs values i firstOrNot
vecUpdates≡reflectBool xs {[]} [] i vType notHere = refl
vecUpdates≡reflectBool xs {ind  indices} (val  values) _ (ofʸ refl  vType) (here _ vBool) = updateAt-updates ind _
vecUpdates≡reflectBool xs {ind  indices} (val  values) _ (ofʸ refl  vType) (there ¬p firstOrNot) with ()¬p _
vecUpdates≡reflectBool xs {ind  indices} (val  values) i (ofⁿ ¬i≡ind  vType) (there ¬p firstOrNot) = begin
  (vecUpdates xs indices values [ ind ]≔ val) i ≡⟨ updateAt-minimal i _ _ ¬i≡ind 
  vecUpdates xs indices values i                ≡⟨ vecUpdates≡reflectBool xs values i vType firstOrNot 
  evalFromVReflect xs values i firstOrNot      ≡˘⟨ evalFromVReflect¬≡ xs values i firstOrNot val 
  evalFromVReflect xs (val  values) i (there ¬p firstOrNot) 

vecUpdates≡reflectBool-lema₂ :  (xs : Vector A n) (values : Vec A m) (vBool : Vec Bool m) 
  firstOrNotPositionMaybe (proj₂ (firstOrNotFromDec T? vBool))  firstTrue vBool
vecUpdates≡reflectBool-lema₂ xs [] [] = refl
vecUpdates≡reflectBool-lema₂ xs (x  values) (true  vBool) = refl
vecUpdates≡reflectBool-lema₂ xs (x  values) (false  vBool)
  rewrite sym $ vecUpdates≡reflectBool-lema₂ xs values vBool
  with firstOrNotFromDec T? vBool in eq
... | true , _ = refl
... | false , _ = refl

vecUpdates≡reflectBool-lemma :  (xs : Vector A n) (values : Vec A m) i (vBool : Vec Bool m) 
  evalFromPosition values (xs i) (firstOrNotPositionMaybe $ proj₂ (firstOrNotFromDec T? vBool)) 
  evalFromPosition values (xs i) (firstTrue vBool)
vecUpdates≡reflectBool-lemma xs values i vBool rewrite vecUpdates≡reflectBool-lema₂ xs values vBool = refl

vecUpdates≡reflectBool-theo :  (xs : Vector A n) {indices : Vec (Fin n) m} (values : Vec A m) i
  {vBool : Vec Bool m} (vType : VecIndBool indices vBool i)
   vecUpdates xs indices values i  evalFromPosition values (xs i) (firstTrue vBool)
vecUpdates≡reflectBool-theo xs {indices} values i {vBool} vType = begin
  vecUpdates xs indices values i ≡⟨ vecUpdates≡reflectBool xs values i vType (proj₂ (firstOrNotFromDec T? vBool)) 
  evalFromVReflect xs values i (proj₂ (firstOrNotFromDec T? vBool)) ≡⟨ vecUpdates≡reflectBool-lemma xs values i vBool 
  evalFromPosition values (xs i) (firstTrue vBool) 

vBoolFromIndices :  (indices : Vec (Fin n) m) (i : Fin n)  Σ[ vBool  Vec Bool m ]
  VecWithType  (ind , b)  Reflects (i  ind) b) (Vec.zip indices vBool)
vBoolFromIndices [] i = [] , []
vBoolFromIndices (ind  indices) i = _ , proof (i Fin.≟ ind)  proj₂ (vBoolFromIndices indices i)

vecUpdates≡reflectBool-theo2 :  (xs : Vector A n) (indices : Vec (Fin n) m) (values : Vec A m) i
   vecUpdates xs indices values i  evalFromPosition values (xs i) (firstTrue (vBoolFromIndices indices i .proj₁))
vecUpdates≡reflectBool-theo2 xs indices values i = vecUpdates≡reflectBool-theo xs values i (proj₂ (vBoolFromIndices indices i))

vecUpdates≡reflectBool-lemma3 :  (indices : Vec (Fin n) m) i
  {vBool : Vec Bool m} (vType : VecIndBool indices vBool i)
   vBoolFromIndices indices i .proj₁  vBool
vecUpdates≡reflectBool-lemma3 [] i {[]} vType = refl
vecUpdates≡reflectBool-lemma3 (ind  indices) i {false  vBool} (ofⁿ ¬a  vType) =
  cong₂ _∷_ (dec-false (_ Fin.≟ _) ¬a) (vecUpdates≡reflectBool-lemma3 indices _ vType)
vecUpdates≡reflectBool-lemma3 (ind  indices) _ {true  vBool} (ofʸ refl  vType)
   = cong₂ _∷_ (dec-true (ind Fin.≟ ind) refl) (vecUpdates≡reflectBool-lemma3 indices _ vType)

vecUpdates≡reflectBool-theo3 :  (xs : Vector A n) (indices : Vec (Fin n) m) (values : Vec A m) i
  {vBool : Vec Bool m} (vType : VecIndBool indices vBool i)
   evalFromPosition values (xs i) (firstTrue (vBoolFromIndices indices i .proj₁))
   evalFromPosition values (xs i) (firstTrue vBool)
vecUpdates≡reflectBool-theo3 xs indices values i vType
  rewrite vecUpdates≡reflectBool-lemma3 indices i vType = refl