(***************)
(* EXPRESSIONS *)
(***************)

(* This is an expression tree with one binary operation `+` and
 * embedded literals in ℕ
 *)
Inductive expression_tree : Set :=
  | Plus_ET : expression_tree -> expression_tree -> expression_tree
  | Lit_ET : nat -> expression_tree.(***************)
(* EXPRESSIONS *)
(***************)

(* This is an expression tree with one binary operation `+` and
 * embedded literals in ℕ
 *)
Inductive expression_tree : Set :=
  | Plus_ET : expression_tree -> expression_tree -> expression_tree
  | Lit_ET : nat -> expression_tree.(***************)
(* EXPRESSIONS *)
(***************)

(* This is an expression tree with one binary operation `+` and
 * embedded literals in ℕ
 *)
Inductive expression_tree : Set :=
  | Plus_ET : expression_tree -> expression_tree -> expression_tree
  | Lit_ET : nat -> expression_tree.(* This is an interpretation of the expression language, giving every
 * expression a denotation into ℕ
 *)
Fixpoint interp_expression_tree (e : expression_tree) : nat :=
  match e with
  | Plus_ET e1 e2 => interp_expression_tree e1 + interp_expression_tree e2
  | Lit_ET n => n
  end.(* An equivalent way to represent these expression trees is by
 * flattening them into lists. This is ok to do because plus is
 * associative, and the zero literal is a unit for plus
 *)
Inductive expression_list : Set :=
  | Plus_EL : nat -> expression_list -> expression_list
  | Zero_EL : expression_list.(* An equivalent way to represent these expression trees is by
 * flattening them into lists. This is ok to do because plus is
 * associative, and the zero literal is a unit for plus
 *)
Inductive expression_list : Set :=
  | Plus_EL : nat -> expression_list -> expression_list
  | Zero_EL : expression_list.(* An equivalent way to represent these expression trees is by
 * flattening them into lists. This is ok to do because plus is
 * associative, and the zero literal is a unit for plus
 *)
Inductive expression_list : Set :=
  | Plus_EL : nat -> expression_list -> expression_list
  | Zero_EL : expression_list.(* This "list-y" version of things also has an interpretation *)
Fixpoint interp_expression_list (e : expression_list) : nat :=
  match e with
  | Plus_EL n e' => n + interp_expression_list e'
  | Zero_EL => 0
  end.(* A helper we need for the next thing... *)
Fixpoint append_expression_list (e1 : expression_list) (e2 : expression_list) : expression_list :=
  match e1 with
  | Plus_EL n e1' => Plus_EL n (append_expression_list e1' e2)
  | Zero_EL => e2
  end.(* This is a constructive definition to support the claim that
 * expression trees can be compiled into expression lists. In fact, we
 * could prove a theorem than the denotation of `e` and `compile(e)`
 * are equal, and you will notice that in the proof you have to use
 * associativity and unit laws for plus.
 *)
Fixpoint compile_expression_tree (e : expression_tree) : expression_list :=
  match e with
  | Plus_ET e1 e2 => append_expression_list (compile_expression_tree e1) (compile_expression_tree e2)
  | Lit_ET n => Plus_EL n Zero_EL
  end.

(* Specifications: *)
Lemma interp_list': forall l1 l2, interp_expression_list (append_expression_list l1 l2) = interp_expression_list l1 + interp_expression_list l2.
Theorem compiletree_correct : forall (e : expression_tree), interp_expression_tree e = interp_expression_list (compile_expression_tree e).
Lemma interp_list': forall l1 l2, interp_expression_list (append_expression_list l1 l2) = interp_expression_list l1 + interp_expression_list l2.
Theorem compiletree_correct : forall (e : expression_tree), interp_expression_tree e = interp_expression_list (compile_expression_tree e).