------------------------------------------------------------------------
-- The Agda standard library
--
-- Vectors, basic types and operations
------------------------------------------------------------------------

{-# OPTIONS --cubical-compatible --safe #-}

module Data.Vec.Base where

open import Data.Bool.Base using (Bool; true; false; if_then_else_)
open import Data.Nat.Base
open import Data.Fin.Base using (Fin; zero; suc)
open import Data.List.Base as List using (List)
open import Data.Product.Base as Prod using (; ∃₂; _×_; _,_; proj₁; proj₂)
open import Data.These.Base as These using (These; this; that; these)
open import Function.Base using (const; _∘′_; id; _∘_)
open import Level using (Level)
open import Relation.Binary.PropositionalEquality.Core using (_≡_; refl; trans; cong)
open import Relation.Nullary.Decidable.Core using (does; T?)
open import Relation.Unary using (Pred; Decidable)

private
  variable
    a b c p : Level
    A : Set a
    B : Set b
    C : Set c
    m n : 

------------------------------------------------------------------------
-- Types

infixr 5 _∷_

data Vec (A : Set a) :   Set a where
  []  : Vec A zero
  _∷_ :  (x : A) (xs : Vec A n)  Vec A (suc n)

infix 4 _[_]=_

data _[_]=_ {A : Set a} : Vec A n  Fin n  A  Set a where
  here  :      {x}   {xs : Vec A n}  x  xs [ zero ]= x
  there :  {i} {x y} {xs : Vec A n}
    (xs[i]=x : xs [ i ]= x)  y  xs [ suc i ]= x

------------------------------------------------------------------------
-- Basic operations

length : Vec A n  
length {n = n} _ = n

head : Vec A (1 + n)  A
head (x  xs) = x

tail : Vec A (1 + n)  Vec A n
tail (x  xs) = xs

lookup : Vec A n  Fin n  A
lookup (x  xs) zero    = x
lookup (x  xs) (suc i) = lookup xs i

iterate : (A  A)  A   n  Vec A n
iterate s z zero    = []
iterate s z (suc n) = z  iterate s (s z) n

insertAt : Vec A n  Fin (suc n)  A  Vec A (suc n)
insertAt xs       zero     v = v  xs
insertAt (x  xs) (suc i)  v = x  insertAt xs i v

removeAt : Vec A (suc n)  Fin (suc n)  Vec A n
removeAt (x  xs)         zero    = xs
removeAt (x  xs@(_  _)) (suc i) = x  removeAt xs i

updateAt : Vec A n  Fin n  (A  A)  Vec A n
updateAt (x  xs) zero    f = f x  xs
updateAt (x  xs) (suc i) f = x    updateAt xs i f

-- xs [ i ]%= f  modifies the i-th element of xs according to f

infixl 6 _[_]%=_ _[_]≔_

_[_]%=_ : Vec A n  Fin n  (A  A)  Vec A n
xs [ i ]%= f = updateAt xs i f

-- xs [ i ]≔ y  overwrites the i-th element of xs with y

_[_]≔_ : Vec A n  Fin n  A  Vec A n
xs [ i ]≔ y = xs [ i ]%= const y

------------------------------------------------------------------------
-- Operations for transforming vectors

-- See README.Data.Vec.Relation.Binary.Equality.Cast for the reasoning
-- system of `cast`-ed equality.
cast : .(eq : m  n)  Vec A m  Vec A n
cast {n = zero}  eq []       = []
cast {n = suc _} eq (x  xs) = x  cast (cong pred eq) xs

map : (A  B)  Vec A n  Vec B n
map f []       = []
map f (x  xs) = f x  map f xs

-- Concatenation.

infixr 5 _++_

_++_ : Vec A m  Vec A n  Vec A (m + n)
[]       ++ ys = ys
(x  xs) ++ ys = x  (xs ++ ys)

concat : Vec (Vec A m) n  Vec A (n * m)
concat []         = []
concat (xs  xss) = xs ++ concat xss

-- Align, Restrict, and Zip.

alignWith : (These A B  C)  Vec A m  Vec B n  Vec C (m  n)
alignWith f []         bs       = map (f ∘′ that) bs
alignWith f as@(_  _) []       = map (f ∘′ this) as
alignWith f (a  as)   (b  bs) = f (these a b)  alignWith f as bs

restrictWith : (A  B  C)  Vec A m  Vec B n  Vec C (m  n)
restrictWith f []       bs       = []
restrictWith f (_  _)  []       = []
restrictWith f (a  as) (b  bs) = f a b  restrictWith f as bs

zipWith : (A  B  C)  Vec A n  Vec B n  Vec C n
zipWith f []       []       = []
zipWith f (x  xs) (y  ys) = f x y  zipWith f xs ys

unzipWith : (A  B × C)  Vec A n  Vec B n × Vec C n
unzipWith f []       = [] , []
unzipWith f (a  as) = Prod.zip _∷_ _∷_ (f a) (unzipWith f as)

align : Vec A m  Vec B n  Vec (These A B) (m  n)
align = alignWith id

restrict : Vec A m  Vec B n  Vec (A × B) (m  n)
restrict = restrictWith _,_

zip : Vec A n  Vec B n  Vec (A × B) n
zip = zipWith _,_

unzip : Vec (A × B) n  Vec A n × Vec B n
unzip = unzipWith id

-- Interleaving.

infixr 5 _⋎_

_⋎_ : Vec A m  Vec A n  Vec A (m +⋎ n)
[]        ys = ys
(x  xs)  ys = x  (ys  xs)

-- Pointwise application

infixl 4 _⊛_

_⊛_ : Vec (A  B) n  Vec A n  Vec B n
[]        []       = []
(f  fs)  (x  xs) = f x  (fs  xs)

-- Multiplication

module CartesianBind where
  infixl 1 _>>=_

  _>>=_ : Vec A m  (A  Vec B n)  Vec B (m * n)
  xs >>= f = concat (map f xs)

infixl 4 _⊛*_

_⊛*_ : Vec (A  B) m  Vec A n  Vec B (m * n)
fs ⊛* xs = fs CartesianBind.>>= λ f  map f xs

allPairs : Vec A m  Vec B n  Vec (A × B) (m * n)
allPairs xs ys = map _,_ xs ⊛* ys

-- Diagonal

diagonal : Vec (Vec A n) n  Vec A n
diagonal [] = []
diagonal (xs  xss) = head xs  diagonal (map tail xss)

module DiagonalBind where
  infixl 1 _>>=_

  _>>=_ : Vec A n  (A  Vec B n)  Vec B n
  xs >>= f = diagonal (map f xs)


------------------------------------------------------------------------
-- Operations for reducing vectors

-- Dependent folds

module _ (A : Set a) (B :   Set b) where

  FoldrOp =  {n}  A  B n  B (suc n)
  FoldlOp =  {n}  B n  A  B (suc n)

foldr :  (B :   Set b)  FoldrOp A B  B zero  Vec A n  B n
foldr B _⊕_ e []       = e
foldr B _⊕_ e (x  xs) = x  foldr B _⊕_ e xs

foldl :  (B :   Set b)  FoldlOp A B  B zero  Vec A n  B n
foldl B _⊕_ e []       = e
foldl B _⊕_ e (x  xs) = foldl (B  suc) _⊕_ (e  x) xs

-- Non-dependent folds

foldr′ : (A  B  B)  B  Vec A n  B
foldr′ _⊕_ = foldr _ _⊕_

foldl′ : (B  A  B)  B  Vec A n  B
foldl′ _⊕_ = foldl _ _⊕_

-- Non-empty folds

foldr₁ : (A  A  A)  Vec A (suc n)  A
foldr₁ _⊕_ (x  [])     = x
foldr₁ _⊕_ (x  y  ys) = x  foldr₁ _⊕_ (y  ys)

foldl₁ : (A  A  A)  Vec A (suc n)  A
foldl₁ _⊕_ (x  xs) = foldl _ _⊕_ x xs

-- Special folds

sum : Vec  n  
sum = foldr _ _+_ 0

count :  {P : Pred A p}  Decidable P  Vec A n  
count P? []       = zero
count P? (x  xs) with does (P? x)
... | true  = suc (count P? xs)
... | false = count P? xs

countᵇ : (A  Bool)  Vec A n  
countᵇ p = count (T?  p)

------------------------------------------------------------------------
-- Operations for building vectors

[_] : A  Vec A 1
[ x ] = x  []

replicate : (n : )  A  Vec A n
replicate zero    x = []
replicate (suc n) x = x  replicate n x

tabulate : (Fin n  A)  Vec A n
tabulate {n = zero}  f = []
tabulate {n = suc n} f = f zero  tabulate (f  suc)

allFin :  n  Vec (Fin n) n
allFin _ = tabulate id

------------------------------------------------------------------------
-- Operations for dividing vectors

splitAt :  m {n} (xs : Vec A (m + n)) 
          ∃₂ λ (ys : Vec A m) (zs : Vec A n)  xs  ys ++ zs
splitAt zero    xs                = [] , xs , refl
splitAt (suc m) (x  xs) =
  let ys , zs , eq = splitAt m xs in x  ys , zs , cong (x ∷_) eq

take :  m {n}  Vec A (m + n)  Vec A m
take m xs = proj₁ (splitAt m xs)

drop :  m {n}  Vec A (m + n)  Vec A n
drop m xs = proj₁ (proj₂ (splitAt m xs))

group :  n k (xs : Vec A (n * k)) 
         λ (xss : Vec (Vec A k) n)  xs  concat xss
group zero    k []                  = ([] , refl)
group (suc n) k xs  =
  let ys , zs , eq-split = splitAt k xs in
  let zss , eq-group     = group n k zs in
   (ys  zss) , trans eq-split (cong (ys ++_) eq-group)

split : Vec A n  Vec A  n /2⌉ × Vec A  n /2⌋
split []           = ([]     , [])
split (x  [])     = (x  [] , [])
split (x  y  xs) = Prod.map (x ∷_) (y ∷_) (split xs)

uncons : Vec A (suc n)  A × Vec A n
uncons (x  xs) = x , xs

------------------------------------------------------------------------
-- Operations involving ≤

-- Take the first 'm' elements of a vector.
truncate :  {m n}  m  n  Vec A n  Vec A m
truncate {m = zero} _ _    = []
truncate (s≤s le) (x  xs) = x  (truncate le xs)

-- Pad out a vector with extra elements.
padRight :  {m n}  m  n  A  Vec A m  Vec A n
padRight z≤n      a xs       = replicate _ a
padRight (s≤s le) a (x  xs) = x  padRight le a xs

------------------------------------------------------------------------
-- Operations for converting between lists

toList : Vec A n  List A
toList []       = List.[]
toList (x  xs) = List._∷_ x (toList xs)

fromList : (xs : List A)  Vec A (List.length xs)
fromList List.[]         = []
fromList (List._∷_ x xs) = x  fromList xs

------------------------------------------------------------------------
-- Operations for reversing vectors

-- snoc

infixl 5 _∷ʳ_

_∷ʳ_ : Vec A n  A  Vec A (suc n)
[]       ∷ʳ y = [ y ]
(x  xs) ∷ʳ y = x  (xs ∷ʳ y)

-- vanilla reverse

reverse : Vec A n  Vec A n
reverse = foldl (Vec _)  rev x  x  rev) []

-- reverse-append

infix 5 _ʳ++_

_ʳ++_ : Vec A m  Vec A n  Vec A (m + n)
xs ʳ++ ys = foldl (Vec _  (_+ _))  rev x  x  rev) ys xs

-- init and last

initLast :  (xs : Vec A (1 + n))  ∃₂ λ ys y  xs  ys ∷ʳ y
initLast {n = zero}  (x  []) = [] , x , refl
initLast {n = suc n} (x  xs) =
  let ys , y , eq = initLast xs in
  x  ys , y , cong (x ∷_) eq

init : Vec A (1 + n)  Vec A n
init xs = proj₁ (initLast xs)

last : Vec A (1 + n)  A
last xs = proj₁ (proj₂ (initLast xs))

------------------------------------------------------------------------
-- Other operations

transpose : Vec (Vec A n) m  Vec (Vec A m) n
transpose {n = n} []          = replicate n []
transpose {n = n} (as  ass) = ((replicate n _∷_)  as)  transpose ass

------------------------------------------------------------------------
-- DEPRECATED
------------------------------------------------------------------------
-- Please use the new names as continuing support for the old names is
-- not guaranteed.

-- Version 2.0

remove = removeAt
{-# WARNING_ON_USAGE remove
"Warning: remove was deprecated in v2.0.
Please use removeAt instead."
#-}
insert = insertAt
{-# WARNING_ON_USAGE insert
"Warning: insert was deprecated in v2.0.
Please use insertAt instead."
#-}