{-# OPTIONS --safe #-}

module Data.List.Properties where

open import Prelude

mapl-id : (xs : List A)  mapl id xs  xs
mapl-id [] = refl
mapl-id (x  xs) = cong (x ∷_) (mapl-id xs)

foldr-universal :  (h : List B  A) f e
                 (h []  e)
                 (∀ x xs  h (x  xs)  f x (h xs))
                  xs  h xs  foldr f e xs
foldr-universal h f e base step [] = base
foldr-universal h f e base step (x  xs) =
  step x xs  cong (f x) (foldr-universal h f e base step xs)

foldr-fusion :  (f : C  A) {_⊕_ : B  C  C} {_⊗_ : B  A  A} e
               (∀ x y  f (x  y)  x  f y)
                xs  f (foldr _⊕_ e xs)  foldr _⊗_ (f e) xs
foldr-fusion h {f} {g} e fuse =
  foldr-universal (h  foldr f e) g (h e) refl  x xs  fuse x (foldr f e xs))

foldr-++ : (f : A  B  B) (b : B) (xs ys : List A)
          foldr f b (xs ++ ys)  foldr f (foldr f b ys) xs
foldr-++ f b xs ys = foldr-fusion (foldr f b) ys  _ _  refl) xs

foldl-by-r : (B  A  B)  B  List A  B
foldl-by-r f b xs = foldr  x k xs  k (f xs x)) id xs b

foldl-is-foldr : (f : B  A  B) (z : B) (xs : List A) 
                 foldl f z xs  foldl-by-r f z xs
foldl-is-foldr f z xs =
  cong (_$ z) (foldr-universal (flip (foldl f))  x k xs  k (f xs x)) id refl  x xs  refl) xs) 

foldl-fusion :  (f : C  A) {_⊕_ : C  B  C}  {_⊗_ : A  B  A} e 
                 (∀ x y  f (x  y)  f x  y) 
                  xs  f (foldl _⊕_ e xs)  foldl _⊗_ (f e) xs
foldl-fusion h {f} {g} e fuse [] = refl
foldl-fusion h {f} {g} e fuse (x  xs) =
  foldl-fusion h (f e x) fuse xs  cong (flip (foldl g) xs) (fuse e x)

module _ {A : Type a} {B : Type b} where
  foldl-++ : (f : B  A  B) (b : B) (xs ys : List A)
           foldl f b (xs ++ ys)  foldl f (foldl f b xs) ys
  foldl-++ f b xs ys =
    foldl f b (xs ++ ys) ≡⟨ foldl-is-foldr f b (xs ++ ys) 
    foldl-by-r f b (xs ++ ys) ≡⟨ cong (_$ b) (foldr-++  x k b  k (f b x)) id xs ys) 
    foldr  x k b  k (f b x)) (foldr  x k b  k (f b x)) id ys) xs b ≡˘⟨ cong  e  foldr  x k b  k (f b x)) e xs b) (funExt λ b  foldl-is-foldr f b ys) 
    foldr  x k b  k (f b x)) (flip (foldl f) ys) xs b ≡˘⟨ cong′ {A = B  B} (_$ b) (foldr-universal  xs b  foldl f (foldl f b xs) ys)  x k b  k (f b x)) (flip (foldl f) ys) refl  _ _  refl) xs ) 
    foldl f (foldl f b xs) ys 

open import Data.Fin

_!_ : (xs : List A)  Fin (length xs)  A
(x  xs) ! fz = x
(x  xs) ! fs i = xs ! i

Any All : (A  Type p)  List A  Type p
Any P xs = Σ[ i  Fin (length xs) ] × P (xs ! i)
All P xs =  (i : Fin (length xs))  P (xs ! i)


here :  (x : A) xs {P : A  Type p}  P x  Any P (x  xs)
here _ _ Px = (fz , Px)

there :  {P : A  Type p} (x : A) {xs}  Any P xs  Any P (x  xs)
there _ (i , Pxs) = fs i , Pxs

module _ {A : Type a} {P : A  Type p} (P? :  x  Dec (P x)) where
  Any? :  xs  Dec (Any P xs)
  Any? [] = no  ())
  Any? (x  xs) with P? x | Any? xs
  ... | yes Px | Pxs = yes (fz , Px)
  ... | no ¬Px | yes (i , Pxs) = yes (fs i , Pxs)
  ... | no ¬Px | no ¬Pxs = no λ { (fz , Pxs)  ¬Px Pxs ; (fs x , Pxs)  ¬Pxs (x , Pxs)}

  All? :  xs  Dec (All P xs)
  All? [] = yes λ ()
  All? (x  xs) with P? x | All? xs
  ... | no ¬Px | _ = no λ z  ¬Px (z fz)
  ... | yes Px | no ¬Pxs = no λ z  ¬Pxs  i  z (fs i))
  ... | yes Px | yes Pxs = yes λ { fz  Px ; (fs i)  Pxs i }