/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
prelude
import init.logic init.nat
open decidable list

notation h :: t  := cons h t
notation `[` l:(foldr `, ` (h t, cons h t) nil `]`) := l

universe variables u v

instance (A : Type u) : inhabited (list A) :=
⟨list.nil⟩

variables {A : Type u} {B : Type v}

namespace list
protected def append : list A → list A → list A
| []       l := l
| (h :: s) t := h :: (append s t)

instance : has_append (list A) :=
⟨list.append⟩

protected def mem : A → list A → Prop
| a []       := false
| a (b :: l) := a = b ∨ mem a l

instance : has_mem A list :=
⟨list.mem⟩

instance decidable_mem [decidable_eq A] (a : A) : ∀ (l : list A), decidable (a ∈ l)
| []     := is_false not_false
| (b::l) :=
  if h₁ : a = b then is_true (or.inl h₁)
  else match decidable_mem l with
  | is_true  h₂ := is_true (or.inr h₂)
  | is_false h₂ := is_false (not_or h₁ h₂)
  end

def concat : list A → A → list A
| []     a := [a]
| (b::l) a := b :: concat l a

instance : has_emptyc (list A) :=
⟨list.nil⟩

protected def insert [decidable_eq A] (a : A) (l : list A) : list A :=
if a ∈ l then l else concat l a

instance [decidable_eq A] : has_insert A list :=
⟨list.insert⟩

protected def union [decidable_eq A] : list A → list A → list A
| l₁ []      := l₁
| l₁ (a::l₂) := union (insert a l₁) l₂

instance [decidable_eq A] : has_union (list A) :=
⟨list.union⟩

protected def inter [decidable_eq A] : list A → list A → list A
| []      l₂ := []
| (a::l₁) l₂ := if a ∈ l₂ then a :: inter l₁ l₂ else inter l₁ l₂

instance [decidable_eq A] : has_inter (list A) :=
⟨list.inter⟩

def length : list A → nat
| []       := 0
| (a :: l) := length l + 1

open option nat

def nth : list A → nat → option A
| []       n     := none
| (a :: l) 0     := some a
| (a :: l) (n+1) := nth l n

def head [inhabited A] : list A → A
| []       := default A
| (a :: l) := a

def tail : list A → list A
| []       := []
| (a :: l) := l

def reverse : list A → list A
| []       := []
| (a :: l) := concat (reverse l) a

def map (f : A → B) : list A → list B
| []       := []
| (a :: l) := f a :: map l

def for : list A → (A → B) → list B :=
flip map

def join : list (list A) → list A
| []        := []
| (l :: ls) := append l (join ls)

def filter (p : A → Prop) [h : decidable_pred p] : list A → list A
| []     := []
| (a::l) := if p a then a :: filter l else filter l

def dropn : ℕ → list A → list A
| 0 a := a
| (succ n) [] := []
| (succ n) (x::r) := dropn n r

end list
