/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import init.data.rbtree.basic

universes u v w w'

def rbmap_lt {α : Type u} {β : Type v} (lt : α → α → Prop) (a b : α × β) : Prop :=
lt a.1 b.1

def rbmap (α : Type u) (β : Type v) (lt : α → α → Prop) : Type (max u v) :=
rbtree (α × β) (rbmap_lt lt)

def mk_rbmap (α : Type u) (β : Type v) (lt : α → α → Prop) : rbmap α β lt :=
mk_rbtree (α × β) (rbmap_lt lt)

namespace rbmap
variables {α : Type u} {β : Type v} {δ : Type w} {lt : α → α → Prop}

def empty (m : rbmap α β lt) : bool :=
m.empty

def to_list (m : rbmap α β lt) : list (α × β) :=
m.to_list

def min (m : rbmap α β lt) : option (α × β) :=
m.min

def max (m : rbmap α β lt) : option (α × β) :=
m.max

def fold (f : α → β → δ → δ) (m : rbmap α β lt) (d : δ) : δ :=
m.fold (λ e, f e.1 e.2) d

def rev_fold (f : α → β → δ → δ) (m : rbmap α β lt) (d : δ) : δ :=
m.rev_fold (λ e, f e.1 e.2) d

def mfold {m : Type w → Type w'} [monad m] (f : α → β → δ → m δ) (mp : rbmap α β lt) (d : δ) : m δ :=
mp.mfold (λ e, f e.1 e.2) d

def mfor {m : Type w → Type w'} [monad m] (f : α → β → m δ) (mp : rbmap α β lt) : m punit :=
mp.mfold (λ a b _, f a b >> return ⟨⟩) ⟨⟩

/-
We don't assume β is inhabited when in membership predicate `mem` and
function find_entry to make this module more convenient to use.
If we had assumed β to be inhabited we could use the following simpler
definition: (k, default β) ∈ m
-/

protected def mem (k : α) (m : rbmap α β lt) : Prop :=
match m.val with
| rbnode.leaf             := false
| rbnode.red_node _ e _   := rbtree.mem (k, e.2) m
| rbnode.black_node _ e _ := rbtree.mem (k, e.2) m

instance : has_mem α (rbmap α β lt) :=
⟨rbmap.mem⟩

instance [has_repr α] [has_repr β] : has_repr (rbmap α β lt) :=
⟨λ t, "rbmap_of " ++ repr t.to_list⟩

def rbmap_lt_dec [h : decidable_rel lt] : decidable_rel (@rbmap_lt α β lt) :=
λ a b, h a.1 b.1

variable [decidable_rel lt]

def insert (m : rbmap α β lt) (k : α) (v : β) : rbmap α β lt :=
@rbtree.insert _ _ rbmap_lt_dec m (k, v)

def find_entry (m : rbmap α β lt) (k : α) : option (α × β) :=
match m.val with
| rbnode.leaf             := none
| rbnode.red_node _ e _   := @rbtree.find _ _ rbmap_lt_dec m (k, e.2)
| rbnode.black_node _ e _ := @rbtree.find _ _ rbmap_lt_dec m (k, e.2)

def to_value : option (α × β) → option β
| none     := none
| (some e) := some e.2

def find (m : rbmap α β lt) (k : α) : option β :=
to_value (m.find_entry k)

def contains (m : rbmap α β lt) (k : α) : bool :=
(find_entry m k).is_some

def from_list (l : list (α × β)) (lt : α → α → Prop) [decidable_rel lt] : rbmap α β lt :=
l.foldl (λ m p, insert m p.1 p.2)  (mk_rbmap α β lt)

end rbmap

def rbmap_of {α : Type u} {β : Type v} (l : list (α × β)) (lt : α → α → Prop) [decidable_rel lt] : rbmap α β lt :=
rbmap.from_list l lt
