Acciones

Diferencia entre revisiones de «Tema 4»

De Seminario de Lógica Computacional (2018)

Línea 13: Línea 13:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Definir el tipo boollist para representar las listas de
+
   Ejemplo. Definir el tipo boollist para representar las listas de
 
   booleanos con los constructores bool_nil y bool_cons tales que  
 
   booleanos con los constructores bool_nil y bool_cons tales que  
 
   + bool_nil es la lista vacía y
 
   + bool_nil es la lista vacía y
Línea 26: Línea 26:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Definir el tipo (list X) para representar las listas de
+
   Ejemplo. Definir el tipo (list X) para representar las listas de
 
   elementos de con los constructores nil y cons tales que  
 
   elementos de con los constructores nil y cons tales que  
 
   + nil es la lista vacía y
 
   + nil es la lista vacía y
Línea 241: Línea 241:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Definir el tipo (list' {X}) para representar las listas de
+
   Ejemplo. Definir el tipo (list' {X}) para representar las listas de
 
   elementos de con los constructores nil y cons tales que  
 
   elementos de con los constructores nil y cons tales que  
 
   + nil' es la lista vacía y
 
   + nil' es la lista vacía y
Línea 308: Línea 308:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Especificar que la siguiente definición es errónea
+
   Ejemplo. Especificar que la siguiente definición es errónea
 
       Fail Definition mynil := nil.
 
       Fail Definition mynil := nil.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 316: Línea 316:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Completar la definición anterior para obtener la lista
+
   Ejemplo. Completar la definición anterior para obtener la lista
 
   vacía de números naturales.
 
   vacía de números naturales.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 326: Línea 326:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejercicio. Definir las siguientes abreviaturas
+
   Ejemplo. Definir las siguientes abreviaturas
 
   + "x :: y"        para (cons x y)
 
   + "x :: y"        para (cons x y)
 
   + "[ ]"            para nil
 
   + "[ ]"            para nil
Línea 351: Línea 351:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que la lista vacía es el elemento neutro por la
+
   Ejercicio. Demostrar que la lista vacía es el elemento neutro por la
 
   derecha de la concatenación.
 
   derecha de la concatenación.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 357: Línea 357:
 
Theorem app_nil_r : forall (X:Type), forall l:list X,
 
Theorem app_nil_r : forall (X:Type), forall l:list X,
 
   l ++ [] = l.
 
   l ++ [] = l.
Proof. induction l.
+
Proof.
 +
  induction l.
 
   + reflexivity.
 
   + reflexivity.
 
   + simpl. rewrite IHl. reflexivity.
 
   + simpl. rewrite IHl. reflexivity.
Línea 363: Línea 364:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que la concatenación es asociativa.
+
   Ejercicio. Demostrar que la concatenación es asociativa.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
  
Línea 369: Línea 370:
 
   l ++ m ++ n = (l ++ m) ++ n.
 
   l ++ m ++ n = (l ++ m) ++ n.
 
Proof.
 
Proof.
  intros A l m n. induction l.  
+
  intros A l m n.
 +
  induction l.
 
   + reflexivity.
 
   + reflexivity.
 
   + simpl. rewrite IHl. reflexivity.
 
   + simpl. rewrite IHl. reflexivity.
Línea 375: Línea 377:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que la longitud de una concatenación es la suma de
+
   Ejercicio. Demostrar que la longitud de una concatenación es la suma de
 
   las longitudes de las listas (es decir, es un homomorfismo).
 
   las longitudes de las listas (es decir, es un homomorfismo).
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 382: Línea 384:
 
   length (l1 ++ l2) = length l1 + length l2.
 
   length (l1 ++ l2) = length l1 + length l2.
 
Proof.
 
Proof.
   intros X l1 l2. induction l1.
+
   intros X l1 l2.
 +
  induction l1.
 
   + reflexivity.
 
   + reflexivity.
 
   + simpl. rewrite IHl1. reflexivity.
 
   + simpl. rewrite IHl1. reflexivity.
 
Qed.
 
Qed.
(** [] *)
 
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       rev (l1 ++ l2) = rev l2 ++ rev l1.
 
       rev (l1 ++ l2) = rev l2 ++ rev l1.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 396: Línea 398:
 
   rev (l1 ++ l2) = rev l2 ++ rev l1.
 
   rev (l1 ++ l2) = rev l2 ++ rev l1.
 
Proof.
 
Proof.
    intros X l1 l2. induction l1.
+
  intros X l1 l2.
 +
  induction l1.
 
   + simpl. rewrite app_nil_r. reflexivity.
 
   + simpl. rewrite app_nil_r. reflexivity.
 
   + simpl. rewrite IHl1, app_assoc. reflexivity.
 
   + simpl. rewrite IHl1, app_assoc. reflexivity.
Línea 402: Línea 405:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       rev (rev l) = l.
 
       rev (rev l) = l.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 413: Línea 416:
 
   + simpl. rewrite rev_app_distr, IHl. reflexivity.
 
   + simpl. rewrite rev_app_distr, IHl. reflexivity.
 
Qed.
 
Qed.
(** [] *)
 
  
 
(* =====================================================================
 
(* =====================================================================
Línea 466: Línea 468:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
+
   Ejercicio. Definir la función
 
       combine {X Y : Type} (lx : list X) (ly : list Y) : list (X*Y)  
 
       combine {X Y : Type} (lx : list X) (ly : list Y) : list (X*Y)  
 
   tal que (combine lx ly) es la lista obtenida emparejando los
 
   tal que (combine lx ly) es la lista obtenida emparejando los
Línea 483: Línea 485:
 
       Check combine
 
       Check combine
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
 +
 
Check @combine.
 
Check @combine.
 +
(* ==> forall X Y : Type, list X -> list Y -> list (X * Y)*)
 +
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
 
   Ejercicio. Calcular el resultado de  
 
   Ejercicio. Calcular el resultado de  
Línea 490: Línea 495:
  
 
Compute (combine [1;2] [false;false;true;true]).
 
Compute (combine [1;2] [false;false;true;true]).
 +
(* ==> [(1, false); (2, false)] : list (nat * bool) *)
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 501: Línea 507:
 
Fixpoint split {X Y : Type} (l : list (X*Y)) : (list X) * (list Y) :=
 
Fixpoint split {X Y : Type} (l : list (X*Y)) : (list X) * (list Y) :=
 
  match l with
 
  match l with
  | [] => ([], [])
+
  | []         => ([], [])
 
  | (x, y) :: t => let s := split t in (x :: fst s, y :: snd s)
 
  | (x, y) :: t => let s := split t in (x :: fst s, y :: snd s)
 
end.
 
end.
Línea 528: Línea 534:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
+
   Ejercicio. Definir la función
 
       nth_error {X : Type} (l : list X) (n : nat) : option X :=
 
       nth_error {X : Type} (l : list X) (n : nat) : option X :=
 
   tal que (nth_error l n) es el n-ésimo elemento de l. Por ejemplo,  
 
   tal que (nth_error l n) es el n-ésimo elemento de l. Por ejemplo,  
Línea 559: Línea 565:
 
Definition hd_error {X : Type} (l : list X) : option X :=
 
Definition hd_error {X : Type} (l : list X) : option X :=
 
  match l with  
 
  match l with  
     | [] => None
+
     | []     => None
 
     | x :: _ => Some x
 
     | x :: _ => Some x
 
  end.
 
  end.
Línea 631: Línea 637:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
+
   Ejercicio. Definir la función
 
       countoddmembers' (l:list nat) : nat  
 
       countoddmembers' (l:list nat) : nat  
 
   tal que countoddmembers' l) es el número de elementos impares de
 
   tal que countoddmembers' l) es el número de elementos impares de
Línea 722: Línea 728:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
+
   Ejercicio. Definir la función
 
       map {X Y:Type} (f:X->Y) (l:list X) : (list Y)  
 
       map {X Y:Type} (f:X->Y) (l:list X) : (list Y)  
 
   tal que (map f l) es la lista obtenida aplicando f a todos los
 
   tal que (map f l) es la lista obtenida aplicando f a todos los
Línea 751: Línea 757:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       map f (rev l) = rev (map f l).
 
       map f (rev l) = rev (map f l).
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 757: Línea 763:
 
Lemma map_app_distr : forall (X Y : Type) (f : X -> Y) (l t : list X),
 
Lemma map_app_distr : forall (X Y : Type) (f : X -> Y) (l t : list X),
 
     map f (l ++ t) = map f l ++ map f t.
 
     map f (l ++ t) = map f l ++ map f t.
Proof. intros X Y f l t. induction l.
+
Proof.
      + reflexivity.
+
  intros X Y f l t.
      + simpl. rewrite IHl. reflexivity.
+
  induction l.
 +
  + reflexivity.
 +
  + simpl. rewrite IHl. reflexivity.
 
Qed.
 
Qed.
  
Línea 765: Línea 773:
 
   map f (rev l) = rev (map f l).
 
   map f (rev l) = rev (map f l).
 
Proof.
 
Proof.
intros X Y f l. induction l.
+
  intros X Y f l.
 +
  induction l.
 
   + reflexivity.
 
   + reflexivity.
 
   + simpl. rewrite map_app_distr, IHl. reflexivity.
 
   + simpl. rewrite map_app_distr, IHl. reflexivity.
Línea 916: Línea 925:
 
Theorem fold_length_correct : forall X (l : list X),
 
Theorem fold_length_correct : forall X (l : list X),
 
   fold_length l = length l.
 
   fold_length l = length l.
Proof. intros X l. unfold fold_length. induction l.
+
Proof.
      - reflexivity.
+
  intros X l.
      - simpl. rewrite IHl. reflexivity.
+
  unfold fold_length.
 +
  induction l.
 +
  - reflexivity.
 +
  - simpl. rewrite IHl. reflexivity.
 
Qed.
 
Qed.
  
Línea 938: Línea 950:
 
Theorem fold_map_correct : forall (X Y : Type) (f : X -> Y) (l : list X),
 
Theorem fold_map_correct : forall (X Y : Type) (f : X -> Y) (l : list X),
 
     fold_map f l = map f l.
 
     fold_map f l = map f l.
Proof. intros X Y f l. unfold fold_map. induction l.
+
Proof.
      - reflexivity.
+
  intros X Y f l.
      - simpl. rewrite IHl. reflexivity. Qed.
+
  unfold fold_map.
 +
  induction l.
 +
  - reflexivity.
 +
  - simpl. rewrite IHl. reflexivity.
 +
Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
Línea 964: Línea 980:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       prod_curry (prod_uncurry f) x y = f x y
 
       prod_curry (prod_uncurry f) x y = f x y
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 975: Línea 991:
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       prod_uncurry (prod_curry f) p = f p.
 
       prod_uncurry (prod_curry f) p = f p.
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 982: Línea 998:
 
                         (f : (X * Y) -> Z) (p : X * Y),
 
                         (f : (X * Y) -> Z) (p : X * Y),
 
   prod_uncurry (prod_curry f) p = f p.
 
   prod_uncurry (prod_curry f) p = f p.
Proof. intros. destruct p. reflexivity. Qed.
+
Proof.
 +
  intros.
 +
  destruct p.
 +
  reflexivity.
 +
Qed.
  
 
(* ---------------------------------------------------------------------
 
(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
+
   Ejercicio. Demostrar que
 
       forall X n l, length l = n -> @nth_error X l n = None
 
       forall X n l, length l = n -> @nth_error X l n = None
 
   ------------------------------------------------------------------ *)
 
   ------------------------------------------------------------------ *)
Línea 1113: Línea 1133:
  
 
Definition exp (n m : nat) : nat :=
 
Definition exp (n m : nat) : nat :=
   ( fun (X : Type) (f : X -> X) (x : X) => (m (X -> X) (n X) f) x.
+
   ( fun (X : Type) (f : X -> X) (x : X) => (m (X -> X) (n X) f) x).
  
 
Example exp_1 : exp two two = plus two two.
 
Example exp_1 : exp two two = plus two two.

Revisión del 14:37 3 may 2018

(* T4: Polimorfismo y funciones deo orden superior en Coq *)

Require Export T3_Listas.

(* =====================================================================
   § Polimorfismo
   ================================================================== *)

(* =====================================================================
   §§ Listas polimórficas  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir el tipo boollist para representar las listas de
   booleanos con los constructores bool_nil y bool_cons tales que 
   + bool_nil es la lista vacía y
   + (bool_cons x ys) es la lista obtenida añadiendo el booleano x a la
     lista ys.
   ------------------------------------------------------------------ *)

Inductive boollist : Type :=
  | bool_nil : boollist
  | bool_cons : bool -> boollist -> boollist.


(* ---------------------------------------------------------------------
   Ejemplo. Definir el tipo (list X) para representar las listas de
   elementos de con los constructores nil y cons tales que 
   + nil es la lista vacía y
   + (cons x ys) es la lista obtenida añadiendo el elemento x a la
     lista ys.
   ------------------------------------------------------------------ *)

Inductive list (X:Type) : Type :=
  | nil  : list X
  | cons : X -> list X -> list X.

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de list.
   ------------------------------------------------------------------ *)

Check list.
(* ===> list : Type -> Type *)

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de (nil nat).
   ------------------------------------------------------------------ *)

Check (nil nat).
(* ===> nil nat : list nat *)

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de (cons nat 3 (nil nat)).
   ------------------------------------------------------------------ *)

Check (cons nat 3 (nil nat)).
(* ===> cons nat 3 (nil nat) : list nat *)

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de nil.
   ------------------------------------------------------------------ *)

Check nil.
(* ===> nil : forall X : Type, list X *)

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de cons.
   ------------------------------------------------------------------ *)

Check cons.
(* ===> cons : forall X : Type, X -> list X -> list X *)

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de (cons nat 2 (cons nat 1 (nil nat))).
   ------------------------------------------------------------------ *)

Check (cons nat 2 (cons nat 1 (nil nat))).
(* ==> cons nat 2 (cons nat 1 (nil nat)) : list nat *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      repeat (X : Type) (x : X) (count : nat) : list X
   tal que (repeat X x n) es la lista obtenida repitiendo n veces el
   elemento x. Por ejemplo,
      repeat nat 4 2 = cons nat 4 (cons nat 4 (nil nat)).
      repeat bool false 1 = cons bool false (nil bool).
   ------------------------------------------------------------------ *)

Fixpoint repeat (X : Type) (x : X) (count : nat) : list X :=
  match count with
  | 0 => nil X
  | S count' => cons X x (repeat X x count')
  end.

Example test_repeat1 :
  repeat nat 4 2 = cons nat 4 (cons nat 4 (nil nat)).
Proof. reflexivity.  Qed.

Example test_repeat2 :
  repeat bool false 1 = cons bool false (nil bool).
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Se definen los siguientes tipos
      Inductive mumble : Type :=
        | a : mumble
        | b : mumble -> nat -> mumble
        | c : mumble.
      
      Inductive grumble (X:Type) : Type :=
        | d : mumble -> grumble X
        | e : X -> grumble X.
  
   Decidir cuáles de los siguientes expresiones son del tipo (grumble X)
   para algún X:
      - [d (b a 5)]
      - [d mumble (b a 5)]
      - [d bool (b a 5)]
      - [e bool true]
      - [e mumble (b c 0)]
      - [e bool (b c 0)]
      - [c]
   ------------------------------------------------------------------ *)

Module MumbleGrumble.

Inductive mumble : Type :=
  | a : mumble
  | b : mumble -> nat -> mumble
  | c : mumble.

Inductive grumble (X:Type) : Type :=
  | d : mumble -> grumble X
  | e : X -> grumble X.

End MumbleGrumble.

(* =====================================================================
   §§§ Inferencia de tipos
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      repeat' X x count : list X
   tal que (repeat' X x n) es la lista obtenida repitiendo n veces el
   elemento x. Por ejemplo,
      repeat' nat 4 2 = cons nat 4 (cons nat 4 (nil nat)).
      repeat' bool false 1 = cons bool false (nil bool).
   ------------------------------------------------------------------ *)

Fixpoint repeat' X x count : list X :=
  match count with
  | 0        => nil X
  | S count' => cons X x (repeat' X x count')
  end.

(* ---------------------------------------------------------------------
   Ejemplo. Calcular los tipos de repeat' y repeat.
   ------------------------------------------------------------------ *)

Check repeat'.
(* ===> forall X : Type, X -> nat -> list X *)
Check repeat.
(* ===> forall X : Type, X -> nat -> list X *)

(* =====================================================================
   §§§ Síntesis de los tipos de los argumentos  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      repeat'' X x count : list X
   tal que (repeat'' X x n) es la lista obtenida repitiendo n veces el
   elemento x, usando argumentos implícitos. Por ejemplo,
      repeat'' nat 4 2 = cons nat 4 (cons nat 4 (nil nat)).
      repeat'' bool false 1 = cons bool false (nil bool).
   ------------------------------------------------------------------ *)

Fixpoint repeat'' X x count : list X :=
  match count with
  | 0        => nil _
  | S count' => cons _ x (repeat'' _ x count')
  end.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la lista formada por los números naturales 1, 2 y 3.
   ------------------------------------------------------------------ *)

Definition list123 :=
  cons nat 1 (cons nat 2 (cons nat 3 (nil nat))).

Definition list123' :=
  cons _ 1 (cons _ 2 (cons _ 3 (nil _))).

(* =====================================================================
   §§§ Argumentos implícitos  
   ================================================================== *)


(* ---------------------------------------------------------------------
   Ejemplo. Especificar las siguientes funciones y sus argumentos
   explícitos e implícitos:
   + nil
   + constructor
   + repeat
   ------------------------------------------------------------------ *)

Arguments nil {X}.
Arguments cons {X} _ _.
Arguments repeat {X} x count.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la lista formada por los números naturales 1, 2 y 3.
   ------------------------------------------------------------------ *)

Definition list123'' := cons 1 (cons 2 (cons 3 nil)).

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      repeat''' {X : Type} (x : X) (count : nat) : list X
   tal que (repeat'' X x n) es la lista obtenida repitiendo n veces el
   elemento x, usando argumentos implícitos. Por ejemplo,
      repeat'' nat 4 2 = cons nat 4 (cons nat 4 (nil nat)).
      repeat'' bool false 1 = cons bool false (nil bool).
   ------------------------------------------------------------------ *)

Fixpoint repeat''' {X : Type} (x : X) (count : nat) : list X :=
  match count with
  | 0        => nil
  | S count' => cons x (repeat''' x count')
  end.

Example test_repeat'''1 :
  repeat''' 4 2 = cons 4 (cons 4 nil).
Proof. reflexivity.  Qed.

Example test_repeat'''2 :
  repeat false 1 = cons false nil.
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Definir el tipo (list' {X}) para representar las listas de
   elementos de con los constructores nil y cons tales que 
   + nil' es la lista vacía y
   + (cons' x ys) es la lista obtenida añadiendo el elemento x a la
     lista ys.
   ------------------------------------------------------------------ *)

Inductive list' {X:Type} : Type :=
  | nil'  : list'
  | cons' : X -> list' -> list'.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      app {X : Type} (l1 l2 : list X) : (list X)
   tal que (app xs ys) es la concatenación de xs e ys.
   ------------------------------------------------------------------ *)

Fixpoint app {X : Type} (l1 l2 : list X) : (list X) :=
  match l1 with
  | nil      => l2
  | cons h t => cons h (app t l2)
  end.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      rev {X:Type} (l:list X) : list X
   tal que (rev xs) es la inversa de xs. Por ejemplo,
      rev (cons 1 (cons 2 nil)) = (cons 2 (cons 1 nil)).
      rev (cons true nil) = cons true nil.
   ------------------------------------------------------------------ *)

Fixpoint rev {X:Type} (l:list X) : list X :=
  match l with
  | nil      => nil
  | cons h t => app (rev t) (cons h nil)
  end.

Example test_rev1 :
  rev (cons 1 (cons 2 nil)) = (cons 2 (cons 1 nil)).
Proof. reflexivity.  Qed.

Example test_rev2:
  rev (cons true nil) = cons true nil.
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      length {X : Type} (l : list X) : nat 
   tal que (length xs) es el número de elementos de xs. Por ejemplo,
      length (cons 1 (cons 2 (cons 3 nil))) = 3.
   ------------------------------------------------------------------ *)

Fixpoint length {X : Type} (l : list X) : nat :=
  match l with
  | nil       => 0
  | cons _ l' => S (length l')
  end.

Example test_length1:
  length (cons 1 (cons 2 (cons 3 nil))) = 3.
Proof. reflexivity.  Qed.

(* =====================================================================
   §§§ Explicitación de argumentos  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Especificar que la siguiente definición es errónea
      Fail Definition mynil := nil.
   ------------------------------------------------------------------ *)

Fail Definition mynil := nil.
(* ==> Error: Cannot infer the implicit parameter X of nil. *)

(* ---------------------------------------------------------------------
   Ejemplo. Completar la definición anterior para obtener la lista
   vacía de números naturales.
   ------------------------------------------------------------------ *)

Definition mynil : list nat := nil.

(* Alternativamente *)
Definition mynil' := @nil nat.

(* ---------------------------------------------------------------------
   Ejemplo. Definir las siguientes abreviaturas
   + "x :: y"         para (cons x y)
   + "[ ]"            para nil
   + "[ x ; .. ; y ]" para (cons x .. (cons y []) ..).
   + "x ++ y"         para (app x y)
   ------------------------------------------------------------------ *)

Notation "x :: y" := (cons x y)
                     (at level 60, right associativity).
Notation "[ ]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y []) ..).
Notation "x ++ y" := (app x y)
                     (at level 60, right associativity).

(* ---------------------------------------------------------------------
   Ejemplo. Definir la lista cuyos elementos son 1, 2 y 3.
   ------------------------------------------------------------------ *)

Definition list123''' := [1; 2; 3].

(* =====================================================================
   §§§ Ejercicios  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que la lista vacía es el elemento neutro por la
   derecha de la concatenación.
   ------------------------------------------------------------------ *)

Theorem app_nil_r : forall (X:Type), forall l:list X,
  l ++ [] = l.
Proof.
  induction l.
  + reflexivity.
  + simpl. rewrite IHl. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que la concatenación es asociativa.
   ------------------------------------------------------------------ *)

Theorem app_assoc : forall A (l m n:list A),
  l ++ m ++ n = (l ++ m) ++ n.
Proof.
  intros A l m n.
  induction l.
  + reflexivity.
  + simpl. rewrite IHl. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que la longitud de una concatenación es la suma de
   las longitudes de las listas (es decir, es un homomorfismo).
   ------------------------------------------------------------------ *)

Lemma app_length : forall (X:Type) (l1 l2 : list X),
  length (l1 ++ l2) = length l1 + length l2.
Proof.
  intros X l1 l2.
  induction l1.
  + reflexivity.
  + simpl. rewrite IHl1. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      rev (l1 ++ l2) = rev l2 ++ rev l1.
   ------------------------------------------------------------------ *)

Theorem rev_app_distr: forall X (l1 l2 : list X),
  rev (l1 ++ l2) = rev l2 ++ rev l1.
Proof.
  intros X l1 l2.
  induction l1.
  + simpl. rewrite app_nil_r. reflexivity.
  + simpl. rewrite IHl1, app_assoc. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      rev (rev l) = l.
   ------------------------------------------------------------------ *)

Theorem rev_involutive : forall X : Type, forall l : list X,
  rev (rev l) = l.
Proof.
  induction l.
  + reflexivity.
  + simpl. rewrite rev_app_distr, IHl. reflexivity.
Qed.

(* =====================================================================
   §§ Polimorfismo de pares  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir el tipo prod (X Y) con el constructor pair tal que 
   (pair x y) es el par cuyas componentes son x e y.
   ------------------------------------------------------------------ *)

Inductive prod (X Y : Type) : Type :=
| pair : X -> Y -> prod X Y.

Arguments pair {X} {Y} _ _.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la abreviatura
      "( x , y )" para (pair x y).
   ------------------------------------------------------------------ *)

Notation "( x , y )" := (pair x y).

(* ---------------------------------------------------------------------
   Ejemplo. Definir la abreviatura
      "X * Y" para (prod X Y) 
   ------------------------------------------------------------------ *)

Notation "X * Y" := (prod X Y) : type_scope.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      fst {X Y : Type} (p : X * Y) : X
   tal que (fst p) es la primera componente del par p.
   ------------------------------------------------------------------ *)

Definition fst {X Y : Type} (p : X * Y) : X :=
  match p with
  | (x, y) => x
  end.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      snd {X Y : Type} (p : X * Y) 
   tal que (snd p) es la segunda componente del par p.
   ------------------------------------------------------------------ *)

Definition snd {X Y : Type} (p : X * Y) : Y :=
  match p with
  | (x, y) => y
  end.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      combine {X Y : Type} (lx : list X) (ly : list Y) : list (X*Y) 
   tal que (combine lx ly) es la lista obtenida emparejando los
   elementos de lx y ly (como zip de Haskell).
   ------------------------------------------------------------------ *)

Fixpoint combine {X Y : Type} (lx : list X) (ly : list Y) : list (X*Y) :=
  match lx, ly with
  | []     , _       => []
  | _      , []      => []
  | x :: tx, y :: ty => (x, y) :: (combine tx ty)
  end.

(* ---------------------------------------------------------------------
   Ejercicio. Calcular el resultado de 
      Check combine
   ------------------------------------------------------------------ *)

Check @combine.
(* ==> forall X Y : Type, list X -> list Y -> list (X * Y)*)

(* ---------------------------------------------------------------------
   Ejercicio. Calcular el resultado de 
      Compute (combine [1;2] [false;false;true;true]).
   ------------------------------------------------------------------ *)

Compute (combine [1;2] [false;false;true;true]).
(* ==> [(1, false); (2, false)] : list (nat * bool) *)

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      split {X Y : Type} (l : list (X*Y)) : (list X) * (list Y)
   tal que (split l) es el par de lista (lx,ly) cuyo emparejamiento es
   l. (La función split es como unzip de Haskell). Por ejemplo,
      split [(1,false);(2,false)] = ([1;2],[false;false]).
   ------------------------------------------------------------------ *)

Fixpoint split {X Y : Type} (l : list (X*Y)) : (list X) * (list Y) :=
 match l with
 | []          => ([], [])
 | (x, y) :: t => let s := split t in (x :: fst s, y :: snd s)
end.

Example test_split:
  split [(1,false);(2,false)] = ([1;2],[false;false]).
Proof. reflexivity. Qed.

(* =====================================================================
   §§ Resultados opcionales polimórficos  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir el tipo (option X) con los constructores Some y None
   tales que 
   + (Some x) es un valor de tipo X.
   + None es el valor nulo.
   ------------------------------------------------------------------ *)

Inductive option (X:Type) : Type :=
  | Some : X -> option X
  | None : option X.

Arguments Some {X} _.
Arguments None {X}.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      nth_error {X : Type} (l : list X) (n : nat) : option X :=
   tal que (nth_error l n) es el n-ésimo elemento de l. Por ejemplo, 
      nth_error [4;5;6;7] 0 = Some 4.
      nth_error [[1];[2]] 1 = Some [2].
      nth_error [true] 2    = None.
   ------------------------------------------------------------------ *)

Fixpoint nth_error {X : Type} (l : list X) (n : nat) : option X :=
  match l with
  | []      => None
  | a :: l' => if beq_nat n O then Some a else nth_error l' (pred n)
  end.

Example test_nth_error1 : nth_error [4;5;6;7] 0 = Some 4.
Proof. reflexivity. Qed.
Example test_nth_error2 : nth_error [[1];[2]] 1 = Some [2].
Proof. reflexivity. Qed.
Example test_nth_error3 : nth_error [true] 2 = None.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      hd_error {X : Type} (l : list X) : option X
   tal que (hd_error l) es el primer elemento de l. Por ejemplo,
      hd_error [1;2]     = Some 1.
      hd_error [[1];[2]] = Some [1].
   ------------------------------------------------------------------ *)

Definition hd_error {X : Type} (l : list X) : option X :=
 match l with 
    | []     => None
    | x :: _ => Some x
 end.

Check @hd_error.

Example test_hd_error1 : hd_error [1;2] = Some 1.
 Proof. reflexivity. Qed.
Example test_hd_error2 : hd_error  [[1];[2]]  = Some [1].
 Proof. reflexivity. Qed.

(* =====================================================================
   § Funciones como datos
   ================================================================== *)

(* =====================================================================
   § Funciones de orden superior 
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función 
      doit3times {X:Type} (f:X->X) (n:X) : X 
   tal que (doit3times f) aplica 3 veces la función f. Por ejemplo,
      doit3times minustwo 9 = 3.
      doit3times negb true  = false.
   ------------------------------------------------------------------ *)

Definition doit3times {X:Type} (f:X->X) (n:X) : X :=
  f (f (f n)).

Check @doit3times.
(* ===> doit3times : forall X : Type, (X -> X) -> X -> X *)

Example test_doit3times: doit3times minustwo 9 = 3.
Proof. reflexivity.  Qed.
Example test_doit3times': doit3times negb true = false.
Proof. reflexivity.  Qed.

(* =====================================================================
   §§ Filtrado  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      filter {X:Type} (test: X->bool) (l:list X) : (list X)
   tal que (filter p l) es la lista de los elementos de l que verifican
   p. Por ejemplo,
      filter evenb [1;2;3;4] = [2;4].
   ------------------------------------------------------------------ *)


Fixpoint filter {X:Type} (test: X->bool) (l:list X)
                : (list X) :=
  match l with
  | []     => []
  | h :: t => if test h then h :: (filter test t)
                       else       filter test t
  end.

Example test_filter1: filter evenb [1;2;3;4] = [2;4].
Proof. reflexivity.  Qed.

Definition length_is_1 {X : Type} (l : list X) : bool :=
  beq_nat (length l) 1.

Example test_filter2:
    filter length_is_1
           [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ]
  = [ [3]; [4]; [8] ].
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      countoddmembers' (l:list nat) : nat 
   tal que countoddmembers' l) es el número de elementos impares de
   l. Por ejemplo,
      countoddmembers' [1;0;3;1;4;5] = 4.
      countoddmembers' [0;2;4]       = 0.
      countoddmembers' nil           = 0.
   ------------------------------------------------------------------ *)

Definition countoddmembers' (l:list nat) : nat :=
  length (filter oddb l).

Example test_countoddmembers'1:   countoddmembers' [1;0;3;1;4;5] = 4.
Proof. reflexivity.  Qed.
Example test_countoddmembers'2:   countoddmembers' [0;2;4] = 0.
Proof. reflexivity.  Qed.
Example test_countoddmembers'3:   countoddmembers' nil = 0.
Proof. reflexivity.  Qed.

(* =====================================================================
   §§ Funciones anónimas  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
      doit3times (fun n => n * n) 2 = 256.
   ------------------------------------------------------------------ *)

Example test_anon_fun':
  doit3times (fun n => n * n) 2 = 256.
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Calcular
      filter (fun l => beq_nat (length l) 1)
             [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ]
   ------------------------------------------------------------------ *)

Example test_filter2':
    filter (fun l => beq_nat (length l) 1)
           [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ]
  = [ [3]; [4]; [8] ].
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      filter_even_gt7 (l : list nat) : list nat
   tal que (filter_even_gt7 l) es la lista de los elemntos de l que son
   pares y mayores que 7. Por ejemplo,
      filter_even_gt7 [1;2;6;9;10;3;12;8] = [10;12;8].
      filter_even_gt7 [5;2;6;19;129]      = [].
   ------------------------------------------------------------------ *)

Definition filter_even_gt7 (l : list nat) : list nat :=
  filter (fun x => evenb x && leb 7 x) l.

Example test_filter_even_gt7_1 :
  filter_even_gt7 [1;2;6;9;10;3;12;8] = [10;12;8].
 Proof. reflexivity. Qed.

Example test_filter_even_gt7_2 :
  filter_even_gt7 [5;2;6;19;129] = [].
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      partition : forall X : Type,
                  (X -> bool) -> list X -> list X * list X
   tal que (patition p l) es el par de lista (lx,ly) tal que lx es la
   lista de los elementos de l que cumplen p y ly la de las que no lo
   cumplen. Por ejemplo,
      partition oddb [1;2;3;4;5]         = ([1;3;5], [2;4]).
      partition (fun x => false) [5;9;0] = ([], [5;9;0]).
   ------------------------------------------------------------------ *)

Definition partition {X : Type}
                     (test : X -> bool)
                     (l : list X)
                   : list X * list X :=
  (filter test l, filter (fun x => negb (test x)) l).

Example test_partition1: partition oddb [1;2;3;4;5] = ([1;3;5], [2;4]).
Proof. reflexivity. Qed.
Example test_partition2: partition (fun x => false) [5;9;0] = ([], [5;9;0]).
Proof. reflexivity. Qed.

(* =====================================================================
   §§ Aplicación a todos los elementos (map)
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      map {X Y:Type} (f:X->Y) (l:list X) : (list Y) 
   tal que (map f l) es la lista obtenida aplicando f a todos los
   elementos de l. Por ejemplo,
      map (fun x => plus 3 x) [2;0;2] = [5;3;5].
      map oddb [2;1;2;5] = [false;true;false;true].
      map (fun n => [evenb n;oddb n]) [2;1;2;5]
        = [[true;false];[false;true];[true;false];[false;true]].
   ------------------------------------------------------------------ *)

Fixpoint map {X Y:Type} (f:X->Y) (l:list X) : (list Y) :=
  match l with
  | []     => []
  | h :: t => (f h) :: (map f t)
  end.

Example test_map1: map (fun x => plus 3 x) [2;0;2] = [5;3;5].
Proof. reflexivity.  Qed.

Example test_map2:
  map oddb [2;1;2;5] = [false;true;false;true].
Proof. reflexivity.  Qed.

Example test_map3:
    map (fun n => [evenb n;oddb n]) [2;1;2;5]
  = [[true;false];[false;true];[true;false];[false;true]].
Proof. reflexivity.  Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      map f (rev l) = rev (map f l).
   ------------------------------------------------------------------ *)

Lemma map_app_distr : forall (X Y : Type) (f : X -> Y) (l t : list X),
    map f (l ++ t) = map f l ++ map f t.
Proof.
  intros X Y f l t.
  induction l.
  + reflexivity.
  + simpl. rewrite IHl. reflexivity.
Qed.

Theorem map_rev : forall (X Y : Type) (f : X -> Y) (l : list X),
  map f (rev l) = rev (map f l).
Proof.
  intros X Y f l.
  induction l.
  + reflexivity.
  + simpl. rewrite map_app_distr, IHl. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      flat_map {X Y:Type} (f:X -> list Y) (l:list X) : (list Y)
   tal que (flat_map f l) es la concatenación de las listas obtenidas
   aplicando f a l. Por ejemplo,
      flat_map (fun n => [n;n;n]) [1;5;4] = [1; 1; 1; 5; 5; 5; 4; 4; 4].
   ------------------------------------------------------------------ *)

Fixpoint flat_map {X Y:Type} (f:X -> list Y) (l:list X) 
                   : (list Y) :=
   match l with
  | [] => []
  | x :: t => f x ++ flat_map f t
  end.

Example test_flat_map1:
  flat_map (fun n => [n;n;n]) [1;5;4]
  = [1; 1; 1; 5; 5; 5; 4; 4; 4].
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      option_map {X Y : Type} (f : X -> Y) (xo : option X) : option Y
   tal que (option_map f xo) es la aplicación de f a xo.
   ------------------------------------------------------------------ *)

Definition option_map {X Y : Type} (f : X -> Y) (xo : option X)
                      : option Y :=
  match xo with
    | None   => None
    | Some x => Some (f x)
  end.

(* =====================================================================
   §§ Plegados (fold)  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      fold {X Y:Type} (f: X->Y->Y) (l:list X) (b:Y) : Y
   tal que (fold f l b) es el plegado de l con la operación f a partir
   del elemento b. Por ejemplo,
      fold mult [1;2;3;4] 1                 = 24.
      fold andb [true;true;false;true] true = false.
      fold app  [[1];[];[2;3];[4]] []       = [1;2;3;4].
   ------------------------------------------------------------------ *)

Fixpoint fold {X Y:Type} (f: X->Y->Y) (l:list X) (b:Y)
                         : Y :=
  match l with
  | nil    => b
  | h :: t => f h (fold f t b)
  end.

Check (fold andb).
(* ===> fold andb : list bool -> bool -> bool *)

Example fold_example1 :
  fold mult [1;2;3;4] 1 = 24.
Proof. reflexivity. Qed.

Example fold_example2 :
  fold andb [true;true;false;true] true = false.
Proof. reflexivity. Qed.

Example fold_example3 :
  fold app  [[1];[];[2;3];[4]] [] = [1;2;3;4].
Proof. reflexivity. Qed.

(* =====================================================================
   §§ Funciones que construyen funciones  
   ================================================================== *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      constfun {X: Type} (x: X) : nat->X
   tal que (constfun x) es la función que a todos los naturales le
   asigna el x. Por ejemplo, si se define 
      Definition ftrue := constfun true.
   entonces,
      ftrue 0         = true.
      (constfun 5) 99 = 5.
   ------------------------------------------------------------------ *)

Definition constfun {X: Type} (x: X) : nat->X :=
  fun (k:nat) => x.

Definition ftrue := constfun true.

Example constfun_example1 : ftrue 0 = true.
Proof. reflexivity. Qed.

Example constfun_example2 : (constfun 5) 99 = 5.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Calcular el tipo de plus.
   ------------------------------------------------------------------ *)

Check plus.
(* ==> nat -> nat -> nat *)

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      plus3 : nat -> nat
   tal que (plus3 x) es tres más x. Por ejemplo,
      plus3 4               = 7.
      doit3times plus3 0    = 9.
      doit3times (plus 3) 0 = 9.
   ------------------------------------------------------------------ *)

Definition plus3 := plus 3.

Example test_plus3 :    plus3 4 = 7.
Proof. reflexivity.  Qed.
Example test_plus3' :   doit3times plus3 0 = 9.
Proof. reflexivity.  Qed.
Example test_plus3'' :  doit3times (plus 3) 0 = 9.
Proof. reflexivity.  Qed.

(* =====================================================================
   § Ejercicios 
   ================================================================== *)

Module Exercises.

(* ---------------------------------------------------------------------
   Ejercicio. Definir, usando fold, la función
      fold_length {X : Type} (l : list X) : nat
   tal que (fold_length l) es la longitud de l. Por ejemplo,
      fold_length [4;7;0] = 3.
   ------------------------------------------------------------------ *)
  
Definition fold_length {X : Type} (l : list X) : nat :=
  fold (fun _ n => S n) l 0.

Example test_fold_length1 : fold_length [4;7;0] = 3.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que
      fold_length l = length l.
   ------------------------------------------------------------------ *)

Theorem fold_length_correct : forall X (l : list X),
  fold_length l = length l.
Proof.
  intros X l.
  unfold fold_length.
  induction l.
  - reflexivity.
  - simpl. rewrite IHl. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir, usando fold, la función
      fold_map {X Y:Type} (f : X -> Y) (l : list X) : list Y
   tal que (fold_map f l) es la lista obtenida aplicando f a los
   elementos de l.
   ------------------------------------------------------------------ *)

Definition fold_map {X Y:Type} (f : X -> Y) (l : list X) : list Y :=
   fold (fun x t => (f x) :: t)  l [].


(* ---------------------------------------------------------------------
   Ejemplo. Demostrar que fold_map es equivalente a map.
   ------------------------------------------------------------------ *)

Theorem fold_map_correct : forall (X Y : Type) (f : X -> Y) (l : list X),
     fold_map f l = map f l.
Proof.
  intros X Y f l.
  unfold fold_map.
  induction l.
  - reflexivity.
  - simpl. rewrite IHl. reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejemplo. Definir la función
      prod_curry {X Y Z : Type} (f : X * Y -> Z) (x : X) (y : Y) : Z
   tal que (prod_curry f x y) es la versión curryficada de f.
   ------------------------------------------------------------------ *)

Definition prod_curry {X Y Z : Type}
  (f : X * Y -> Z) (x : X) (y : Y) : Z := f (x, y).

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      prod_uncurry {X Y Z : Type} (f : X -> Y -> Z) (p : X * Y) : Z
   tal que (prod_uncurry f p) es la versión incurryficada de f.
   ------------------------------------------------------------------ *)

Definition prod_uncurry {X Y Z : Type}
  (f : X -> Y -> Z) (p : X * Y) : Z := f (fst p) (snd p).

Check @prod_curry.
Check @prod_uncurry.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      prod_curry (prod_uncurry f) x y = f x y
   ------------------------------------------------------------------ *)

Theorem uncurry_curry : forall (X Y Z : Type)
                        (f : X -> Y -> Z)
                        x y,
  prod_curry (prod_uncurry f) x y = f x y.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      prod_uncurry (prod_curry f) p = f p.
   ------------------------------------------------------------------ *)

Theorem curry_uncurry : forall (X Y Z : Type)
                        (f : (X * Y) -> Z) (p : X * Y),
  prod_uncurry (prod_curry f) p = f p.
Proof.
  intros.
  destruct p.
  reflexivity.
Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Demostrar que
      forall X n l, length l = n -> @nth_error X l n = None
   ------------------------------------------------------------------ *)

(* ---------------------------------------------------------------------
   Ejercicio. En los siguientes ejercicios se trabajará con la
   definición de Church de los números naturales: el número natural n es
   la función que toma como argumento una función f y devuelve como
   valor la aplicación de n veces la función f. 
   ------------------------------------------------------------------ *)

Module Church.

(* ---------------------------------------------------------------------
   Ejercicio. Definir el tipo nat para los números naturales de Church. 
   ------------------------------------------------------------------ *)
  
Definition nat := forall X : Type, (X -> X) -> X -> X.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      one : nat
   tal que one es el número uno de Church.
   ------------------------------------------------------------------ *)

Definition one : nat :=
  fun (X : Type) (f : X -> X) (x : X) => f x.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      two : nat
   tal que two es el número dos de Church.
   ------------------------------------------------------------------ *)

Definition two : nat :=
  fun (X : Type) (f : X -> X) (x : X) => f (f x).

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      zero : nat
   tal que zero es el número cero de Church.
   ------------------------------------------------------------------ *)

Definition zero : nat :=
  fun (X : Type) (f : X -> X) (x : X) => x.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      three : nat
   tal que three es el número tres de Church.
   ------------------------------------------------------------------ *)

Definition three : nat := @doit3times.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      succ (n : nat) : nat
   tal que (succ n) es el siguiente del número n de Church. Por ejemplo, 
      succ zero = one.
      succ one  = two.
      succ two  = three.
   ------------------------------------------------------------------ *)

Definition succ (n : nat) : nat :=
   fun (X : Type) (f : X -> X) (x : X) => f (n X f x).

Example succ_1 : succ zero = one.
Proof. reflexivity. Qed.

Example succ_2 : succ one = two.
Proof. reflexivity. Qed.

Example succ_3 : succ two = three.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      plus (n m : nat) : nat
   tal que (plus n m) es la suma de n y m. Por ejemplo,
      plus zero one             = one.
      plus two three            = plus three two.
      plus (plus two two) three = plus one (plus three three).
   ------------------------------------------------------------------ *)

Definition plus (n m : nat) : nat :=
  fun (X : Type) (f : X -> X) (x : X) => m X f (n X f x).

Example plus_1 : plus zero one = one.
Proof. reflexivity. Qed.

Example plus_2 : plus two three = plus three two.
Proof. reflexivity. Qed.

Example plus_3 :
  plus (plus two two) three = plus one (plus three three).
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      mult (n m : nat) : nat
   tal que (mult n m) es el producto de n y m. Por ejemplo,
      mult one one = one.
      mult zero (plus three three) = zero.
      mult two three = plus three three.
   ------------------------------------------------------------------ *)

Definition mult (n m : nat) : nat :=
   fun (X : Type) (f : X -> X) (x : X) => n X (m X f) x.

Example mult_1 : mult one one = one.
Proof. reflexivity. Qed.

Example mult_2 : mult zero (plus three three) = zero.
Proof. reflexivity. Qed.

Example mult_3 : mult two three = plus three three.
Proof. reflexivity. Qed.

(* ---------------------------------------------------------------------
   Ejercicio. Definir la función
      exp (n m : nat) : nat
   tal que (exp n m) es la potencia m-ésima de n. Por ejemplo, 
      exp two two = plus two two.
      exp three two = plus (mult two (mult two two)) one.
      exp three zero = one.
   ------------------------------------------------------------------ *)

Definition exp (n m : nat) : nat :=
  ( fun (X : Type) (f : X -> X) (x : X) => (m (X -> X) (n X) f) x).

Example exp_1 : exp two two = plus two two.
Proof. reflexivity. Qed.

Example exp_2 : exp three two = plus (mult two (mult two two)) one.
Proof. reflexivity. Qed.

Example exp_3 : exp three zero = one.
Proof. reflexivity. Qed.

End Church.

End Exercises.