(* =================================================================== *)

(* Some type definitions *)

Inductive rgb: Set :=
  | Rf : rgb
  | Gf : rgb
  | Bf : rgb
.

Inductive color: Set :=
  | Red : color
  | Orange : color
  | Yellow : color
  | Green : color
  | Blue : color
  | Indigo : color
  | Violet : color
.

Definition R: color := Red.
Definition O: color.
apply Orange.
Defined.

Inductive tuple4 : Set :=
  | Mk4rgb : forall x1: rgb, forall x2: rgb, forall x3: rgb, forall x4: rgb, tuple4
  | Mk4color : forall x1 x2 x3 x4: color, tuple4
  | Mk4t4 : forall x1 x2 x3 x4: tuple4, tuple4
.

(* =================================================================== *)

(* Interactive definitions of 4-tuples *)

Definition t1: tuple4.
apply Mk4rgb.
  apply Gf.
  apply Rf.
  apply Gf.
  apply Bf.
Defined.

Definition t2: tuple4.
apply Mk4rgb.
  apply Gf.
  apply Gf.
  apply Gf.
  apply Gf.
Defined.

Definition t3: tuple4.
apply Mk4color.
  apply Violet.
  apply Red.
  apply Yellow.
  apply Orange.
Defined.


(* In order to define a tuple depending on a variable,
   we use the "section" mechanism, which opens a new environment *)
Section a_tuple_with_variable.

Variable x2: rgb.
Variable x4: rgb.

Definition t4: tuple4.
apply Mk4rgb.
  apply Gf.
  apply x2.
  apply Rf.
  apply x4.
Defined.

Definition tuple_of_tuple: tuple4.
apply Mk4t4.
  apply t1.
  apply t2.
  apply t3.
  apply t4.
Defined.

End a_tuple_with_variable.

(* =================================================================== *)

(* Definitions by cases *)

Section def_by_cases_on_enum.

Variable r : rgb.

(* INTERACTIVE definition by cases on each possible value of r *)
(* The relevant tactic is called "case" or "destruct" *)

(* First version of the script *)
Definition color_of_r__version1: color.
destruct r.
  (* providing a color for Rf *)
  apply Red.

  (* providing a color for Gf *)
  apply Green.

  (* providing a color for Bf *)
  apply Blue.
Defined.


(* In the previous version, r was visible in each subgoal, 
   but it is not needed *)

(* Second version of the script *)
Definition color_of_r__version2: color.
destruct r.
  (* providing a color for Rf *)
  (* Forget about r *)
  clear r. 
  apply Red.

  (* providing a color for Gf *)
  (* Forget about r *)
  clear r. apply Green.

  (* Forget about r and immediately (";") providing a color for Bf *)
  clear r; apply Blue.
Defined.

(* In the next version, r is cleared immediately on EACH subgoal *)
(* Third version of the script *)
Definition color_of_r__version3: color.
destruct r; clear r.
  apply Red.
  apply Green.
  apply Blue.
Defined.


(* Instead of an interactive definition,
   we can write a DIRECT definition *)

Definition color_of_r__version4 : color :=
  match r with
  | Rf => Red
  | Gf => Green
  | Bf => Blue
  end.

Eval compute in color_of_r__version3.

(* EXERCISE *)
(* Define a value permut_r: rgb such that 

  +---------+--------------+
  |       r | Rf | Gf | Bf |
  +---------+--------------+
  |permut_r | Gf | Bf | Rf |
  +---------+--------------+

*)

Definition permut_r: rgb.
Admitted.

(* Anything of type rgb can be analyzed,
   for example we can assign a color to each possible
   value of permut_r *)

Definition color_of_permut_r: color.
clear r.
destruct permut_r.
  apply Green.
  apply Blue.
  apply Red.
Defined.

(* This applies to a constant as well!! *)

Definition color_of_Bf := 
  match Bf with
  | Rf => Red
  | Gf => Green
  | Bf => Blue
  end.

Print color_of_Bf.
Compute color_of_Bf.

(* Definition of a Set by destruct on r *)

Definition Set_of_r : Set :=
  match r with
  | Rf => rgb
  | Gf => color
  | Bf => tuple4
  end.

End def_by_cases_on_enum.


(* Parameterized definitions *)

(* We want to say that color_of provides a color
   for all possible values of r; 
   the type of color_of is then just: forall (r: rgb), color.
   what we get is just a function.
*)

Definition color_of : forall (r: rgb), color :=
  fun (r: rgb) => 
  match r with
  | Rf => Red
  | Gf => Green
  | Bf => Blue
  end.

(* A function can be applied to an argument of the expected type *)

Definition cb := color_of Bf.

(* Similarly, we want can define a Set for each r: rgb *)

Definition Set_of : forall (r: rgb), Set :=
  fun (r: rgb) => 
  match r with
  | Rf => rgb
  | Gf => color
  | Bf => tuple4
  end.

(* And we can then define a Set_of r for each r! *)

Definition funny : forall (r: rgb), Set_of r :=
  fun (r: rgb) => 
  match r with
  | Rf => Gf
  | Gf => Yellow
  | Bf => t1
  end.


(*
   In an interactive definition, we use "intro".
*)

Definition interactive_funny: forall (r: rgb), Set_of r.
intro r. (* variants: "intro a", etc. *)
destruct r.
  red. apply Gf.
  red. apply Yellow.
  red. apply t1.
Defined.

Eval compute in (funny Bf).
Print t1.

(* ========================================================= *)
(* Additional material to be presented now         *)
(* ========================================================= *)

(*
   Important abbreviation: when B does not depend on x,
   "forall (x: A), B"  can be written "A -> B"
   Coq displays things in this way whenever possible,
   as can be seen on the interactive definition of color_of.
*)

Definition interactive_color_of : forall (r: rgb), color.
intro r. 
destruct r.
  apply Red.
  apply Green.
  apply Blue.
Defined.


(*
   Consistently, A -> B -> C   means
   "forall (a:A), forall (b:B), C"  which reads
   "forall (a:A), (forall (b:B), C)" , i.e.
   "forall (a:A), (B -> C)" , i.e.
   "A -> (B -> C)"
*)

Definition t4_as_function : forall (x2 x4: rgb), tuple4.
intros x2 x4.
apply (Mk4rgb Gf x2 Rf x4).
Defined.

(* ========================================================================
  ** ADVANCED MATERIAL **
  Here we play with the style used earlier for the function called "funny".
  You can have a look, but you can also skip to the next exercises 
*)

(* 
   Remark that, in interactive_color_of, we can give a type of the
   result which artificially depends on r.
   We first define a stupid function providing the type of the result.
*)

Definition always_color : forall r: rgb, Set := fun r => color.

Definition artificial_interactive_color_of : forall (r: rgb), always_color r.
(* The goal is unchanged, with a forall *)
intro r.
(* now we reduce and destruct *)
red. destruct r.
  apply Red.
  apply Green.
  apply Blue.
Defined.

(* 
  ** End of ADVANCED MATERIAL **
  ======================================================================== 
*)
 


(* ========================================================= *)
(* Exercises to prepare before lecture 03                    *)
(* ========================================================= *)


(* Let us first define a new type with its constructors *)

Inductive day : Set :=
  | monday : day
  | tuesday : day
  | wednesday : day
  | thursday : day
  | friday : day
  | saturday : day
  | sunday : day.

Definition next_weekday : forall d:day, day :=
  fun d: day =>
  match d with
  | monday => tuesday
  | tuesday => wednesday
  | wednesday => thursday
  | thursday => friday
  | friday => monday
  | saturday => monday
  | sunday => monday
  end.


(* Verification *)
(* What is the expected answer of the following *)

Eval compute in next_weekday (next_weekday saturday).

(* Exercise: define interactively a function which computes 
   the next weekday of the next weekday of a day d, by case on d 
*)

Definition next_next_weekday : forall d: day, day.
(* If you don't like the arrow, 
   you can use the trick in the "advanced material above *)
Admitted.

(* Now don't forget to check on some examples using Eval compute *)

(* ================================ *)
(* Next exercise *)

(* We define an inductive type with exactly two constructors *)

Inductive bool : Set :=
  | true : bool
  | false : bool.


Definition negb : forall b:bool, bool := 
  fun b: bool =>
  match b with
  | true => false
  | false => true
  end.
 
Definition andb : forall b1 b2: bool, bool := 
  fun b1 => fun b2 =>
  (* the type of b1 and b2 was guessed from the previous forall *)
  match b1 with 
  | true => b2 
  | false => false
  end.

(* Define andb using the interactive technique *)
Definition andb_interactive : forall b1 b2: bool, bool.

(* Define the Boolean disjunction using the interactive technique
   or directly, as for andb *)
Definition orb (b1:bool) (b2:bool) : bool := 
  (* FILL IN HERE *) 

(* Now don't forget to check on some examples using Eval compute *)


Definition andb3 (b1:bool) (b2:bool) (b3:bool) : bool :=
  (* FILL IN HERE *) 

(*
Expected results:
                  (andb3 true true true) --> true.
                  (andb3 false true true) --> false.
                  (andb3 true false true) --> false.
                  (andb3 true true false) --> false.
Where "A --> B" means that 
Eval compute in A 
returns B
*)
