Definition id {A : Type} (a : A) : A := a.

Definition compose {A B C : Type} (g : B -> C) (f : A -> B): A -> C :=
    fun (a : A) => g (f a).

Axiom extensional_equality : forall (A B : Type)
 (f : A -> B)
 (f': A -> B),
 (forall x, f x = f' x) ->
  f = f'.

Theorem compose_left_identity
    : forall (A B : Type) (f : A -> B)
    , compose id f = f.
Proof.
    intros.
    apply extensional_equality.
    intros.
    unfold compose.
    unfold id.
    reflexivity.
Qed.

Theorem compose_right_identity
    : forall (A B : Type) (f : A -> B)
    , compose f id = f.
Proof.
    intros.
    apply extensional_equality.
    intros.
    unfold compose.
    unfold id.
    reflexivity.
Qed.


(** * StlcProp: Properties of STLC *)

Require Import SF.SfLib.
Require Import SF.Maps.
Require Import SF.Types.
Require Import SF.Stlc.
Require Import SF.Smallstep.
Module STLCProp.
Import STLC.

(** In this chapter, we develop the fundamental theory of the Simply
    Typed Lambda Calculus -- in particular, the type safety
    theorem. *)

(* ###################################################################### *)
(** * Canonical Forms *)

(** As we saw for the simple calculus in the [Types] chapter, the
    first step in establishing basic properties of reduction and types
    is to identify the possible _canonical forms_ (i.e., well-typed
    closed values) belonging to each type.  For [Bool], these are the boolean
    values [ttrue] and [tfalse].  For arrow types, the canonical forms
    are lambda-abstractions.  *)

Lemma canonical_forms_bool : forall t,
  empty |- t \in TBool ->
  value t ->
  (t = ttrue) \/ (t = tfalse).
Proof.
  intros t HT HVal.
  inversion HVal; intros; subst; try inversion HT; auto.
Qed.

Lemma canonical_forms_fun : forall t T1 T2,
  empty |- t \in (TArrow T1 T2) ->
  value t ->
  exists x u, t = tabs x T1 u.
Proof.
  intros t T1 T2 HT HVal.
  inversion HVal; intros; subst; try inversion HT; subst; auto.
  exists x0. exists t0.  auto.
Qed.

(* ###################################################################### *)
(** * Progress *)

(** As before, the _progress_ theorem tells us that closed, well-typed
    terms are not stuck: either a well-typed term is a value, or it
    can take a reduction step.  The proof is a relatively
    straightforward extension of the progress proof we saw in the
    [Types] chapter.  We'll give the proof in English first, then the
    formal version. *)

Theorem progress : forall t T,
     empty |- t \in T ->
     value t \/ exists t', t ==> t'.

(** _Proof_: By induction on the derivation of [|- t \in T].

    - The last rule of the derivation cannot be [T_Var], since a
      variable is never well typed in an empty context.

    - The [T_True], [T_False], and [T_Abs] cases are trivial, since in
      each of these cases we can see by inspecting the rule that [t]
      is a value.

    - If the last rule of the derivation is [T_App], then [t] has the
      form [t1 t2] for som e[t1] and [t2], where we know that [t1] and
      [t2] are also well typed in the empty context; in particular,
      there exists a type [T2] such that [|- t1 \in T2 -> T] and [|-
      t2 \in T2].  By the induction hypothesis, either [t1] is a value
      or it can take a reduction step.

        - If [t1] is a value, then consider [t2], which by the other
          induction hypothesis must also either be a value or take a step.

            - Suppose [t2] is a value.  Since [t1] is a value with an
              arrow type, it must be a lambda abstraction; hence [t1
              t2] can take a step by [ST_AppAbs].

            - Otherwise, [t2] can take a step, and hence so can [t1
              t2] by [ST_App2].

        - If [t1] can take a step, then so can [t1 t2] by [ST_App1].

    - If the last rule of the derivation is [T_If], then [t = if t1
      then t2 else t3], where [t1] has type [Bool].  By the IH, [t1]
      either is a value or takes a step.

        - If [t1] is a value, then since it has type [Bool] it must be
          either [true] or [false].  If it is [true], then [t] steps
          to [t2]; otherwise it steps to [t3].

        - Otherwise, [t1] takes a step, and therefore so does [t] (by
          [ST_If]). *)

Proof with eauto.
  intros t T Ht.
  remember (@empty ty) as Gamma.
  induction Ht; subst Gamma...
  - (* T_Var *)
    (* contradictory: variables cannot be typed in an
       empty context *)
    inversion H.

  - (* T_App *)
    (* [t] = [t1 t2].  Proceed by cases on whether [t1] is a
       value or steps... *)
    right. destruct IHHt1...
    + (* t1 is a value *)
      destruct IHHt2...
      * (* t2 is also a value *)
        assert (exists x0 t0, t1 = tabs x0 T11 t0).
        eapply canonical_forms_fun; eauto.
        destruct H1 as [x0 [t0 Heq]]. subst.
        exists ([x0:=t2]t0)...

      * (* t2 steps *)
        inversion H0 as [t2' Hstp]. exists (tapp t1 t2')...

    + (* t1 steps *)
      inversion H as [t1' Hstp]. exists (tapp t1' t2)...

  - (* T_If *)
    right. destruct IHHt1...

    + (* t1 is a value *)
      destruct (canonical_forms_bool t1); subst; eauto.

    + (* t1 also steps *)
      inversion H as [t1' Hstp]. exists (tif t1' t2 t3)...
Qed.

(** **** Exercise: 3 stars, optional (progress_from_term_ind)  *)
(** Show that progress can also be proved by induction on terms
    instead of induction on typing derivations. *)

Theorem progress' : forall t T,
     empty |- t \in T ->
     value t \/ exists t', t ==> t'.
Proof.
  intros t.
  induction t; intros T Ht; auto.
  (* FILL IN HERE *) Admitted.
(** [] *)

(* ###################################################################### *)
(** * Preservation *)

(** The other half of the type soundness property is the preservation
    of types during reduction.  For this, we need to develop some
    technical machinery for reasoning about variables and
    substitution.  Working from top to bottom (from the high-level
    property we are actually interested in to the lowest-level
    technical lemmas that are needed by various cases of the more
    interesting proofs), the story goes like this:

      - The _preservation theorem_ is proved by induction on a typing
        derivation, pretty much as we did in the [Types] chapter.  The
        one case that is significantly different is the one for the
        [ST_AppAbs] rule, whose definition uses the substitution
        operation.  To see that this step preserves typing, we need to
        know that the substitution itself does.  So we prove a...

      - _substitution lemma_, stating that substituting a (closed)
        term [s] for a variable [x] in a term [t] preserves the type
        of [t].  The proof goes by induction on the form of [t] and
        requires looking at all the different cases in the definition
        of substitition.  This time, the tricky cases are the ones for
        variables and for function abstractions.  In both cases, we
        discover that we need to take a term [s] that has been shown
        to be well-typed in some context [Gamma] and consider the same
        term [s] in a slightly different context [Gamma'].  For this
        we prove a...

      - _context invariance_ lemma, showing that typing is preserved
        under "inessential changes" to the context [Gamma] -- in
        particular, changes that do not affect any of the free
        variables of the term.  And finally, for this, we need a
        careful definition of...

      - the _free variables_ of a term -- i.e., those variables
        mentioned in a term and not in the scope of an enclosing
        function abstraction binding a variable of the same name.

   To make Coq happy, we need to formalize the story in the opposite
   order... *)

(* ###################################################################### *)
(** ** Free Occurrences *)

(** A variable [x] _appears free in_ a term _t_ if [t] contains some
    occurrence of [x] that is not under an abstraction labeled [x].
    For example:
      - [y] appears free, but [x] does not, in [\x:T->U. x y]
      - both [x] and [y] appear free in [(\x:T->U. x y) x]
      - no variables appear free in [\x:T->U. \y:T. x y]

    Formally: *)

Inductive appears_free_in : id -> tm -> Prop :=
  | afi_var : forall x,
      appears_free_in x (tvar x)
  | afi_app1 : forall x t1 t2,
      appears_free_in x t1 -> appears_free_in x (tapp t1 t2)
  | afi_app2 : forall x t1 t2,
      appears_free_in x t2 -> appears_free_in x (tapp t1 t2)
  | afi_abs : forall x y T11 t12,
      y <> x  ->
      appears_free_in x t12 ->
      appears_free_in x (tabs y T11 t12)
  | afi_if1 : forall x t1 t2 t3,
      appears_free_in x t1 ->
      appears_free_in x (tif t1 t2 t3)
  | afi_if2 : forall x t1 t2 t3,
      appears_free_in x t2 ->
      appears_free_in x (tif t1 t2 t3)
  | afi_if3 : forall x t1 t2 t3,
      appears_free_in x t3 ->
      appears_free_in x (tif t1 t2 t3).

Hint Constructors appears_free_in.

(** A term in which no variables appear free is said to be _closed_. *)

Definition closed (t:tm) :=
  forall x, ~ appears_free_in x t.

(** **** Exercise: 1 star (afi)  *)
(** If the definition of [appears_free_in] is not crystal clear to
    you, it is a good idea to take a piece of paper and write out the
    rules in informal inference-rule notation.  (Although it is a
    rather low-level, technical definition, understanding it is
    crucial to understanding substitution and its properties, which
    are really the crux of the lambda-calculus.) *)
(** [] *)

(* ###################################################################### *)
(** ** Substitution *)

(** To prove that substitution preserves typing, we first need a
    technical lemma connecting free variables and typing contexts: If
    a variable [x] appears free in a term [t], and if we know [t] is
    well typed in context [Gamma], then it must be the case that
    [Gamma] assigns a type to [x]. *)

Lemma free_in_context : forall x t T Gamma,
   appears_free_in x t ->
   Gamma |- t \in T ->
   exists T', Gamma x = Some T'.

(** _Proof_: We show, by induction on the proof that [x] appears
      free in [t], that, for all contexts [Gamma], if [t] is well
      typed under [Gamma], then [Gamma] assigns some type to [x].

      - If the last rule used was [afi_var], then [t = x], and from
        the assumption that [t] is well typed under [Gamma] we have
        immediately that [Gamma] assigns a type to [x].

      - If the last rule used was [afi_app1], then [t = t1 t2] and [x]
        appears free in [t1].  Since [t] is well typed under [Gamma],
        we can see from the typing rules that [t1] must also be, and
        the IH then tells us that [Gamma] assigns [x] a type.

      - Almost all the other cases are similar: [x] appears free in a
        subterm of [t], and since [t] is well typed under [Gamma], we
        know the subterm of [t] in which [x] appears is well typed
        under [Gamma] as well, and the IH gives us exactly the
        conclusion we want.

      - The only remaining case is [afi_abs].  In this case [t =
        \y:T11.t12], and [x] appears free in [t12]; we also know that
        [x] is different from [y].  The difference from the previous
        cases is that whereas [t] is well typed under [Gamma], its
        body [t12] is well typed under [(Gamma, y:T11)], so the IH
        allows us to conclude that [x] is assigned some type by the
        extended context [(Gamma, y:T11)].  To conclude that [Gamma]
        assigns a type to [x], we appeal to lemma [update_neq], noting
        that [x] and [y] are different variables. *)

Proof.
  intros x t T Gamma H H0. generalize dependent Gamma.
  generalize dependent T.
  induction H;
         intros; try solve [inversion H0; eauto].
  - (* afi_abs *)
    inversion H1; subst.
    apply IHappears_free_in in H7.
    rewrite update_neq in H7; assumption.
Qed.

(** Next, we'll need the fact that any term [t] which is well typed in
    the empty context is closed (it has no free variables). *)

(** **** Exercise: 2 stars, optional (typable_empty__closed)  *)
Corollary typable_empty__closed : forall t T,
    empty |- t \in T  ->
    closed t.
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(** Sometimes, when we have a proof [Gamma |- t : T], we will need to
    replace [Gamma] by a different context [Gamma'].  When is it safe
    to do this?  Intuitively, it must at least be the case that
    [Gamma'] assigns the same types as [Gamma] to all the variables
    that appear free in [t]. In fact, this is the only condition that
    is needed. *)

Lemma context_invariance : forall Gamma Gamma' t T,
     Gamma |- t \in T  ->
     (forall x, appears_free_in x t -> Gamma x = Gamma' x) ->
     Gamma' |- t \in T.

(** _Proof_: By induction on the derivation of
    [Gamma |- t \in T].

      - If the last rule in the derivation was [T_Var], then [t = x]
        and [Gamma x = T].  By assumption, [Gamma' x = T] as well, and
        hence [Gamma' |- t \in T] by [T_Var].

      - If the last rule was [T_Abs], then [t = \y:T11. t12], with [T
        = T11 -> T12] and [Gamma, y:T11 |- t12 \in T12].  The
        induction hypothesis is that, for any context [Gamma''], if
        [Gamma, y:T11] and [Gamma''] assign the same types to all the
        free variables in [t12], then [t12] has type [T12] under
        [Gamma''].  Let [Gamma'] be a context which agrees with
        [Gamma] on the free variables in [t]; we must show [Gamma' |-
        \y:T11. t12 \in T11 -> T12].

        By [T_Abs], it suffices to show that [Gamma', y:T11 |- t12 \in
        T12].  By the IH (setting [Gamma'' = Gamma', y:T11]), it
        suffices to show that [Gamma, y:T11] and [Gamma', y:T11] agree
        on all the variables that appear free in [t12].

        Any variable occurring free in [t12] must be either [y] or
        some other variable.  [Gamma, y:T11] and [Gamma', y:T11]
        clearly agree on [y].  Otherwise, note that any variable other
        than [y] that occurs free in [t12] also occurs free in [t =
        \y:T11. t12], and by assumption [Gamma] and [Gamma'] agree on
        all such variables; hence so do [Gamma, y:T11] and [Gamma',
        y:T11].

      - If the last rule was [T_App], then [t = t1 t2], with [Gamma |-
        t1 \in T2 -> T] and [Gamma |- t2 \in T2].  One induction
        hypothesis states that for all contexts [Gamma'], if [Gamma']
        agrees with [Gamma] on the free variables in [t1], then [t1]
        has type [T2 -> T] under [Gamma']; there is a similar IH for
        [t2].  We must show that [t1 t2] also has type [T] under
        [Gamma'], given the assumption that [Gamma'] agrees with
        [Gamma] on all the free variables in [t1 t2].  By [T_App], it
        suffices to show that [t1] and [t2] each have the same type
        under [Gamma'] as under [Gamma].  But all free variables in
        [t1] are also free in [t1 t2], and similarly for [t2]; hence
        the desired result follows from the induction hypotheses. *)

Proof with eauto.
  intros.
  generalize dependent Gamma'.
  induction H; intros; auto.
  - (* T_Var *)
    apply T_Var. rewrite <- H0...
  - (* T_Abs *)
    apply T_Abs.
    apply IHhas_type. intros x1 Hafi.
    (* the only tricky step... the [Gamma'] we use to
       instantiate is [update Gamma x T11] *)
    unfold update. unfold t_update. destruct (beq_id x0 x1) eqn: Hx0x1...
    rewrite beq_id_false_iff in Hx0x1. auto.
  - (* T_App *)
    apply T_App with T11...
Qed.

(** Now we come to the conceptual heart of the proof that reduction
    preserves types -- namely, the observation that _substitution_
    preserves types.

    Formally, the so-called _Substitution Lemma_ says this: Suppose we
    have a term [t] with a free variable [x], and suppose we've been
    able to assign a type [T] to [t] under the assumption that [x] has
    some type [U].  Also, suppose that we have some other term [v] and
    that we've shown that [v] has type [U].  Then, since [v] satisfies
    the assumption we made about [x] when typing [t], we should be
    able to substitute [v] for each of the occurrences of [x] in [t]
    and obtain a new term that still has type [T]. *)

(** _Lemma_: If [Gamma,x:U |- t \in T] and [|- v \in U], then [Gamma |-
    [x:=v]t \in T]. *)

Lemma substitution_preserves_typing : forall Gamma x U t v T,
     update Gamma x U |- t \in T ->
     empty |- v \in U   ->
     Gamma |- [x:=v]t \in T.

(** One technical subtlety in the statement of the lemma is that
    we assign [v] the type [U] in the _empty_ context -- in other
    words, we assume [v] is closed.  This assumption considerably
    simplifies the [T_Abs] case of the proof (compared to assuming
    [Gamma |- v \in U], which would be the other reasonable assumption
    at this point) because the context invariance lemma then tells us
    that [v] has type [U] in any context at all -- we don't have to
    worry about free variables in [v] clashing with the variable being
    introduced into the context by [T_Abs].

    The substitution lemma can be viewed as a kind of "commutation"
    property.  Intuitively, it says that substitution and typing can
    be done in either order: we can either assign types to the terms
    [t] and [v] separately (under suitable contexts) and then combine
    them using substitution, or we can substitute first and then
    assign a type to [ [x:=v] t ] -- the result is the same either
    way.

    _Proof_: We show, by induction on [t], that for all [T] and
    [Gamma], if [Gamma,x:U |- t \in T] and [|- v \in U], then [Gamma
    |- [x:=v]t \in T].

      - If [t] is a variable there are two cases to consider,
        depending on whether [t] is [x] or some other variable.

          - If [t = x], then from the fact that [Gamma, x:U |- x \in
            T] we conclude that [U = T].  We must show that [[x:=v]x =
            v] has type [T] under [Gamma], given the assumption that
            [v] has type [U = T] under the empty context.  This
            follows from context invariance: if a closed term has type
            [T] in the empty context, it has that type in any context.

          - If [t] is some variable [y] that is not equal to [x], then
            we need only note that [y] has the same type under [Gamma,
            x:U] as under [Gamma].

      - If [t] is an abstraction [\y:T11. t12], then the IH tells us,
        for all [Gamma'] and [T'], that if [Gamma',x:U |- t12 \in T']
        and [|- v \in U], then [Gamma' |- [x:=v]t12 \in T'].

        The substitution in the conclusion behaves differently
        depending on whether [x] and [y] are the same variable.

        First, suppose [x = y].  Then, by the definition of
        substitution, [[x:=v]t = t], so we just need to show [Gamma |-
        t \in T].  But we know [Gamma,x:U |- t : T], and, since [y]
        does not appear free in [\y:T11. t12], the context invariance
        lemma yields [Gamma |- t \in T].

        Second, suppose [x <> y].  We know [Gamma,x:U,y:T11 |- t12 \in
        T12] by inversion of the typing relation, from which
        [Gamma,y:T11,x:U |- t12 \in T12] follows by the context
        invariance lemma, so the IH applies, giving us [Gamma,y:T11 |-
        [x:=v]t12 \in T12].  By [T_Abs], [Gamma |- \y:T11. [x:=v]t12
        \in T11->T12], and by the definition of substitution (noting
        that [x <> y]), [Gamma |- \y:T11. [x:=v]t12 \in T11->T12] as
        required.

      - If [t] is an application [t1 t2], the result follows
        straightforwardly from the definition of substitution and the
        induction hypotheses.

      - The remaining cases are similar to the application case.

    One more technical note: This proof is a rare case where an
    induction on terms, rather than typing derivations, yields a
    simpler argument.  The reason for this is that the assumption
    [update Gamma x U |- t \in T] is not completely generic, in the
    sense that one of the "slots" in the typing relation -- namely the
    context -- is not just a variable, and this means that Coq's
    native induction tactic does not give us the induction hypothesis
    that we want.  It is possible to work around this, but the needed
    generalization is a little tricky.  The term [t], on the other
    hand, _is_ completely generic. *)

Proof with eauto.
  intros Gamma x U t v T Ht Ht'.
  generalize dependent Gamma. generalize dependent T.
  induction t; intros T Gamma H;
    (* in each case, we'll want to get at the derivation of H *)
    inversion H; subst; simpl...
  - (* tvar *)
    rename i into y. destruct (beq_idP x y) as [Hxy|Hxy].
    + (* x=y *)
      subst.
      rewrite update_eq in H2.
      inversion H2; subst. clear H2.
                  eapply context_invariance... intros x Hcontra.
      destruct (free_in_context _ _ T empty Hcontra) as [T' HT']...
      inversion HT'.
    + (* x<>y *)
      apply T_Var. rewrite update_neq in H2...
  - (* tabs *)
    rename i into y. apply T_Abs.
    destruct (beq_idP x y) as [Hxy | Hxy].
    + (* x=y *)
      subst.
      eapply context_invariance...
      intros x Hafi. unfold update, t_update.
      destruct (beq_id y x) eqn: Hyx...
    + (* x<>y *)
      apply IHt. eapply context_invariance...
      intros z Hafi. unfold update, t_update.
      destruct (beq_idP y z) as [Hyz | Hyz]; subst; trivial.
      rewrite <- beq_id_false_iff in Hxy.
      rewrite Hxy...
Qed.

(* ###################################################################### *)
(** ** Main Theorem *)

(** We now have the tools we need to prove preservation: if a closed
    term [t] has type [T] and takes a step to [t'], then [t']
    is also a closed term with type [T].  In other words, the small-step
    reduction relation preserves types. *)

Theorem preservation : forall t t' T,
     empty |- t \in T  ->
     t ==> t'  ->
     empty |- t' \in T.

(** _Proof_: By induction on the derivation of [|- t \in T].

    - We can immediately rule out [T_Var], [T_Abs], [T_True], and
      [T_False] as the final rules in the derivation, since in each of
      these cases [t] cannot take a step.

    - If the last rule in the derivation was [T_App], then [t = t1
      t2].  There are three cases to consider, one for each rule that
      could have been used to show that [t1 t2] takes a step to [t'].

        - If [t1 t2] takes a step by [ST_App1], with [t1] stepping to
          [t1'], then by the IH [t1'] has the same type as [t1], and
          hence [t1' t2] has the same type as [t1 t2].

        - The [ST_App2] case is similar.

        - If [t1 t2] takes a step by [ST_AppAbs], then [t1 =
          \x:T11.t12] and [t1 t2] steps to [[x:=t2]t12]; the
          desired result now follows from the fact that substitution
          preserves types.

    - If the last rule in the derivation was [T_If], then [t = if t1
      then t2 else t3], and there are again three cases depending on
      how [t] steps.

        - If [t] steps to [t2] or [t3], the result is immediate, since
          [t2] and [t3] have the same type as [t].

        - Otherwise, [t] steps by [ST_If], and the desired conclusion
          follows directly from the induction hypothesis. *)

Proof with eauto.
  remember (@empty ty) as Gamma.
  intros t t' T HT. generalize dependent t'.
  induction HT;
       intros t' HE; subst Gamma; subst;
       try solve [inversion HE; subst; auto].
  - (* T_App *)
    inversion HE; subst...
    (* Most of the cases are immediate by induction,
       and [eauto] takes care of them *)
    + (* ST_AppAbs *)
      apply substitution_preserves_typing with T11...
      inversion HT1...
Qed.

(** **** Exercise: 2 stars, recommended (subject_expansion_stlc)  *)
(** An exercise in the [Types] chapter asked about the subject
    expansion property for the simple language of arithmetic and
    boolean expressions.  Does this property hold for STLC?  That is,
    is it always the case that, if [t ==> t'] and [has_type t' T],
    then [empty |- t \in T]?  If so, prove it.  If not, give a
    counter-example not involving conditionals.

(* FILL IN HERE *)
[]
*)

(* ###################################################################### *)
(** * Type Soundness *)

(** **** Exercise: 2 stars, optional (type_soundness)  *)
(** Put progress and preservation together and show that a well-typed
    term can _never_ reach a stuck state.  *)

Definition stuck (t:tm) : Prop :=
  (normal_form step) t /\ ~ value t.

Corollary soundness : forall t t' T,
  empty |- t \in T ->
  t ==>* t' ->
  ~(stuck t').
Proof.
  intros t t' T Hhas_type Hmulti. unfold stuck.
  intros [Hnf Hnot_val]. unfold normal_form in Hnf.
  induction Hmulti.
  (* FILL IN HERE *) Admitted.
(** [] *)

(* ###################################################################### *)
(** * Uniqueness of Types *)

(** **** Exercise: 3 stars (types_unique)  *)
(** Another nice property of the STLC is that types are unique: a
    given term (in a given context) has at most one type. *)
(** Formalize this statement and prove it. *)

(* FILL IN HERE *)
(** [] *)

(* ###################################################################### *)
(** * Additional Exercises *)

(** **** Exercise: 1 star (progress_preservation_statement)  *)
(** Without peeking at their statements above, write down the progress
    and preservation theorems for the simply typed lambda-calculus. *)
(** [] *)

(** **** Exercise: 2 stars (stlc_variation1)  *)
(** Suppose we add a new term [zap] with the following reduction rule

                         ---------                  (ST_Zap)
                         t ==> zap

and the following typing rule:

                      ----------------               (T_Zap)
                      Gamma |- zap : T

    Which of the following properties of the STLC remain true in
    the presence of these rules?  For each property, write either
    "remains true" or "becomes false." If a property becomes
    false, give a counterexample.

      - Determinism of [step]

      - Progress

      - Preservation

[]
*)

(** **** Exercise: 2 stars (stlc_variation2)  *)
(** Suppose instead that we add a new term [foo] with the following
    reduction rules:

                       -----------------                (ST_Foo1)
                       (\x:A. x) ==> foo

                         ------------                   (ST_Foo2)
                         foo ==> true

    Which of the following properties of the STLC remain true in
    the presence of this rule?  For each one, write either
    "remains true" or else "becomes false." If a property becomes
    false, give a counterexample.

      - Determinism of [step]

      - Progress

      - Preservation

[]
*)

(** **** Exercise: 2 stars (stlc_variation3)  *)
(** Suppose instead that we remove the rule [ST_App1] from the [step]
    relation. Which of the following properties of the STLC remain
    true in the presence of this rule?  For each one, write either
    "remains true" or else "becomes false." If a property becomes
    false, give a counterexample.

      - Determinism of [step]

      - Progress

      - Preservation

[]
*)

(** **** Exercise: 2 stars, optional (stlc_variation4)  *)
(** Suppose instead that we add the following new rule to the
    reduction relation:

            ----------------------------------        (ST_FunnyIfTrue)
            (if true then t1 else t2) ==> true

    Which of the following properties of the STLC remain true in
    the presence of this rule?  For each one, write either
    "remains true" or else "becomes false." If a property becomes
    false, give a counterexample.

      - Determinism of [step]

      - Progress

      - Preservation

*)

(** **** Exercise: 2 stars, optional (stlc_variation5)  *)
(** Suppose instead that we add the following new rule to the typing
    relation:

                 Gamma |- t1 \in Bool->Bool->Bool
                     Gamma |- t2 \in Bool
                 ------------------------------          (T_FunnyApp)
                    Gamma |- t1 t2 \in Bool

    Which of the following properties of the STLC remain true in
    the presence of this rule?  For each one, write either
    "remains true" or else "becomes false." If a property becomes
    false, give a counterexample.

      - Determinism of [step]

      - Progress

      - Preservation

*)

(** **** Exercise: 2 stars, optional (stlc_variation6)  *)
(** Suppose instead that we add the following new rule to the typing
    relation:

                     Gamma |- t1 \in Bool
                     Gamma |- t2 \in Bool
                    ---------------------               (T_FunnyApp')
                    Gamma |- t1 t2 \in Bool

    Which of the following properties of the STLC remain true in
    the presence of this rule?  For each one, write either
    "remains true" or else "becomes false." If a property becomes
    false, give a counterexample.

      - Determinism of [step]

      - Progress

      - Preservation

*)

(** **** Exercise: 2 stars, optional (stlc_variation7)  *)
(** Suppose we add the following new rule to the typing relation
    of the STLC:

                         ------------------- (T_FunnyAbs)
                         |- \x:Bool.t \in Bool

    Which of the following properties of the STLC remain true in
    the presence of this rule?  For each one, write either
    "remains true" or else "becomes false." If a property becomes
    false, give a counterexample.

      - Determinism of [step]

      - Progress

      - Preservation

[]
*)

End STLCProp.

(* ###################################################################### *)
(* ###################################################################### *)
(** ** Exercise: STLC with Arithmetic *)

(** To see how the STLC might function as the core of a real
    programming language, let's extend it with a concrete base
    type of numbers and some constants and primitive
    operators. *)

Module STLCArith.

(** To types, we add a base type of natural numbers (and remove
    booleans, for brevity). *)

Inductive ty : Type :=
  | TArrow : ty -> ty -> ty
  | TNat   : ty.

(** To terms, we add natural number constants, along with
    successor, predecessor, multiplication, and zero-testing. *)

Inductive tm : Type :=
  | tvar : id -> tm
  | tapp : tm -> tm -> tm
  | tabs : id -> ty -> tm -> tm
  | tnat  : nat -> tm
  | tsucc : tm -> tm
  | tpred : tm -> tm
  | tmult : tm -> tm -> tm
  | tif0  : tm -> tm -> tm -> tm.

(** **** Exercise: 4 stars (stlc_arith)  *)
(** Finish formalizing the definition and properties of the STLC extended
    with arithmetic.  Specifically:

    - Copy the whole development of STLC that we went through above (from
      the definition of values through the Type Soundness theorem), and
      paste it into the file at this point.

    - Extend the definitions of the [subst] operation and the [step]
      relation to include appropriate clauses for the arithmetic operators.

    - Extend the proofs of all the properties (up to [soundness]) of
      the original STLC to deal with the new syntactic forms.  Make
      sure Coq accepts the whole file. *)

(* FILL IN HERE *)
(** [] *)

End STLCArith.

(** $Date: 2016-05-26 16:17:19 -0400 (Thu, 26 May 2016) $ *)


Require Import init.

Require Import set.

(* begin show *)
Set Universe Polymorphism.
(* end show *)

(** Note: I am learning category theory while writing this.  Apologies if
anything here is incorrect/not specified in the best way.
*)

(* begin show *)
Class Category := {
    cat_U : Type;
    cat_morphism : cat_U → cat_U → Type;
    cat_compose : ∀ {A B C},
        cat_morphism B C → cat_morphism A B → cat_morphism A C;
    cat_id : ∀ A, cat_morphism A A;
    cat_assoc : ∀ {A B C D}
        (h : cat_morphism C D) (g : cat_morphism B C) (f : cat_morphism A B),
        cat_compose h (cat_compose g f) = cat_compose (cat_compose h g) f;
    cat_lid : ∀ {A B} (f : cat_morphism A B), cat_compose (cat_id B) f = f;
    cat_rid : ∀ {A B} (f : cat_morphism A B), cat_compose f (cat_id A) = f;
}.
(* end show *)

Arguments cat_U : clear implicits.
Arguments cat_morphism : clear implicits.
Arguments cat_compose Category {A B C} f g.
Arguments cat_id : clear implicits.

Infix "∘" := (cat_compose _).
Notation "𝟙" := (cat_id _ _).

Definition cat_domain `{C0 : Category} {A B} (f : cat_morphism C0 A B) := A.
Definition cat_codomain `{C0 : Category} {A B} (f : cat_morphism C0 A B) := B.

Definition isomorphism `{C0 : Category} {A B} (f : cat_morphism C0 A B)
    := ∃ g, f ∘ g = 𝟙 ∧ g ∘ f = 𝟙.

Definition cat_inverse `{C0 : Category} {A B}
    (f : cat_morphism C0 A B) (H : isomorphism f) := ex_val H.

Definition isomorphic `{C0 : Category} A B
    := ∃ f : cat_morphism C0 A B, isomorphism f.

Notation "A ≅ B" := (isomorphic A B) (at level 70, no associativity).

(* begin show *)
Local Program Instance dual_category `(C0 : Category) : Category := {
    cat_U := cat_U C0;
    cat_morphism A B := cat_morphism C0 B A;
    cat_compose {A B C} f g := cat_compose C0 g f;
    cat_id A := cat_id C0 A;
}.
(* end show *)
Next Obligation.
    symmetry.
    apply cat_assoc.
Qed.
Next Obligation.
    apply cat_rid.
Qed.
Next Obligation.
    apply cat_lid.
Qed.

(* begin show *)
Local Program Instance product_category `(C1 : Category) `(C2 : Category) : Category
:= {
    cat_U := prod_type (cat_U C1) (cat_U C2);
    cat_morphism A B
        := prod_type (cat_morphism C1 (fst A) (fst B)) (cat_morphism C2 (snd A) (snd B));
    cat_compose {A B C} f g := (fst f ∘ fst g, snd f ∘ snd g);
    cat_id A := (𝟙, 𝟙);
}.
(* end show *)
Next Obligation.
    do 2 rewrite cat_assoc.
    reflexivity.
Qed.
Next Obligation.
    do 2 rewrite cat_lid.
    destruct f; reflexivity.
Qed.
Next Obligation.
    do 2 rewrite cat_rid.
    destruct f; reflexivity.
Qed.

Class SubCategory `(C0 : Category) := {
    subcat_S : cat_U C0 → Prop;
    subcat_morphism : ∀ {A B}, cat_morphism C0 A B → Prop;
    subcat_compose : ∀ {A B C} (f : cat_morphism C0 B C) (g : cat_morphism C0 A B),
        subcat_morphism f → subcat_morphism g → subcat_morphism (f ∘ g);
    subcat_id : ∀ A, subcat_morphism (cat_id C0 A);
}.

(* begin show *)
Local Program Instance subcategory `(SubCategory) : Category := {
    cat_U := set_type subcat_S;
    cat_morphism A B := set_type (subcat_morphism (A:=[A|]) (B:=[B|]));
    cat_compose {A B C} f g := [_|subcat_compose [f|] [g|] [|f] [|g]];
    cat_id A := [_|subcat_id [A|]];
}.
(* end show *)
Next Obligation.
    apply set_type_eq; cbn.
    apply cat_assoc.
Qed.
Next Obligation.
    apply set_type_eq; cbn.
    apply cat_lid.
Qed.
Next Obligation.
    apply set_type_eq; cbn.
    apply cat_rid.
Qed.
Global Remove Hints dual_category product_category subcategory : typeclass_instances.

Definition full_subcategory `(SubCategory) := ∀ A B,
    subcat_morphism (A:=A) (B:=B) = all.

(* begin hide *)
Section Category.

Context `{C0 : Category}.

Local Arguments cat_morphism {Category}.

(* end hide *)
Theorem lcompose : ∀ {A B C} {f g : cat_morphism A B} (h : cat_morphism B C),
    f = g → h ∘ f = h ∘ g.
Proof.
    intros A B C f g h eq.
    rewrite eq.
    reflexivity.
Qed.
Theorem rcompose : ∀ {A B C} {f g : cat_morphism B C} (h : cat_morphism A B),
    f = g → f ∘ h = g ∘ h.
Proof.
    intros A B C f g h eq.
    rewrite eq.
    reflexivity.
Qed.
Theorem lrcompose : ∀ {A B C} {f g : cat_morphism B C} {h i : cat_morphism A B},
    f = g → h = i → f ∘ h = g ∘ i.
Proof.
    intros A B C f g h i eq1 eq2.
    rewrite eq1, eq2.
    reflexivity.
Qed.

Theorem id_isomorphism : ∀ A, isomorphism (cat_id _ A).
Proof.
    intros A.
    exists 𝟙.
    split; apply cat_lid.
Qed.

Theorem compose_isomorphism : ∀ {A B C}
    (f : cat_morphism B C) (g : cat_morphism A B),
    isomorphism f → isomorphism g → isomorphism (f ∘ g).
Proof.
    intros A B C f g [f' [f1 f2]] [g' [g1 g2]].
    exists (g' ∘ f').
    split.
    -   rewrite <- cat_assoc.
        rewrite (cat_assoc g).
        rewrite g1.
        rewrite cat_lid.
        exact f1.
    -   rewrite <- cat_assoc.
        rewrite (cat_assoc f').
        rewrite f2.
        rewrite cat_lid.
        exact g2.
Qed.

Theorem cat_inverse_unique : ∀ {A B} (f : cat_morphism A B) g1 g2,
    f ∘ g1 = 𝟙 → g1 ∘ f = 𝟙 → f ∘ g2 = 𝟙 → g2 ∘ f = 𝟙 → g1 = g2.
Proof.
    intros A B f g1 g2 fg1 g1f fg2 g2f.
    apply lcompose with g2 in fg1.
    rewrite cat_assoc in fg1.
    rewrite g2f in fg1.
    rewrite cat_lid, cat_rid in fg1.
    exact fg1.
Qed.

Theorem isomorphic_refl : ∀ A, A ≅ A.
Proof.
    intros A.
    exists 𝟙, 𝟙.
    rewrite cat_lid.
    split; reflexivity.
Qed.
Theorem isomorphic_sym : ∀ A B, A ≅ B → B ≅ A.
Proof.
    intros A B [f [g [eq1 eq2]]].
    exists g, f.
    split; assumption.
Qed.
Theorem isomorphic_trans : ∀ {A B C}, A ≅ B → B ≅ C → A ≅ C.
Proof.
    intros A B C [f1 [g1 [eq11 eq12]]] [f2 [g2 [eq21 eq22]]].
    exists (f2 ∘ f1).
    exists (g1 ∘ g2).
    split.
    -   rewrite <- cat_assoc.
        rewrite (cat_assoc f1).
        rewrite eq11.
        rewrite cat_lid.
        exact eq21.
    -   rewrite <- cat_assoc.
        rewrite (cat_assoc g2).
        rewrite eq22.
        rewrite cat_lid.
        exact eq12.
Qed.
Theorem isomorphic_trans2 : ∀ {A B C}, B ≅ C → A ≅ B → A ≅ C.
Proof.
    intros A B C eq1 eq2.
    exact (isomorphic_trans eq2 eq1).
Qed.

Theorem dual_isomorphism : ∀ {A B} (f : cat_morphism A B),
    isomorphism (C0 := C0) f ↔ isomorphism (C0:=dual_category C0) f.
Proof.
    intros A B f.
    split.
    -   intros [g [g_eq1 g_eq2]].
        exists g.
        cbn in *.
        split; assumption.
    -   intros [g [g_eq1 g_eq2]].
        exists g.
        cbn in *.
        split; assumption.
Qed.

(* begin hide *)
End Category.

(* end hide *)
Definition convert_type {A B : Type} (H : A = B) (x : A) : B.
    rewrite H in x.
    exact x.
Defined.

Theorem cat_eq : ∀ C1 C2,
    ∀ H : @cat_U C1 = @cat_U C2,
    ∀ H' : (∀ A B, cat_morphism C1 A B =
                   cat_morphism C2 (convert_type H A) (convert_type H B)),
    (∀ A B C (f : cat_morphism C1 B C) (g : cat_morphism C1 A B),
        convert_type (H' _ _) (f ∘ g) =
        (convert_type (H' _ _) f) ∘ (convert_type (H' _ _) g)) →
    (∀ A, convert_type (H' A A) (cat_id C1 A) = cat_id C2 (convert_type H A)) →
    C1 = C2.
Proof.
    intros [U1 morphism1 compose1 id1 assoc1 lid1 rid1]
           [U2 morphism2 compose2 id2 assoc2 lid2 rid2] H H' eq1 eq2.
    cbn in *.
    destruct H.
    assert (morphism1 = morphism2) as eq.
    {
        apply functional_ext.
        intros A.
        apply functional_ext.
        apply H'.
    }
    subst morphism2; cbn in *.
    pose (H'2 A B := Logic.eq_refl (morphism1 A B)).
    rewrite (proof_irrelevance H' H'2) in eq1, eq2.
    clear H'.
    cbn in *.
    assert (compose1 = compose2) as eq.
    {
        apply functional_ext; intros A.
        apply functional_ext; intros B.
        apply functional_ext; intros C.
        apply functional_ext; intros f.
        apply functional_ext; intros g.
        apply eq1.
    }
    subst compose2; clear eq1.
    assert (id1 = id2) as eq.
    {
        apply functional_ext; intros A.
        apply eq2.
    }
    subst id2; clear eq2.
    rewrite (proof_irrelevance assoc2 assoc1).
    rewrite (proof_irrelevance lid2 lid1).
    rewrite (proof_irrelevance rid2 rid1).
    reflexivity.
Qed.

Theorem cat_dual_dual : ∀ C, C = dual_category (dual_category C).
Proof.
    intros C.
    assert (@cat_U C = @cat_U (dual_category (dual_category C))) as H
        by reflexivity.
    pose (H2 := Logic.eq_refl (cat_U C)).
    assert (∀ A B, cat_morphism _ A B =
                   cat_morphism _ (convert_type H A) (convert_type H B)) as H'.
    {
        intros A B.
        rewrite (proof_irrelevance H H2).
        cbn.
        reflexivity.
    }
    apply (cat_eq _ _ H H').
    all: pose proof (proof_irrelevance H H2) as H_eq.
    all: subst H.
    all: unfold H2 in *; cbn in *.
    all: clear H2.
    all: pose (H'2 A B := Logic.eq_refl (cat_morphism C A B)).
    all: rewrite (proof_irrelevance H' H'2).
    all: cbn.
    all: reflexivity.
Qed.

Unset Universe Polymorphism.


Require Import ZArith Arith Bool Omega.

Open Scope Z_scope.

(* The key to this corollary is that if s=Zsqrt_plain x, then
  s*s < (s+1) * (s+1), as stated by the companion theorem
  Zsqrt_interval, and the squaring operation is monotonic only
  for positive values. *)

Theorem div_Zsqrt :
 forall m n p:Z, 0 < m < n ->
  n=m*p-> 0 < m <= Z.sqrt n \/ 0 < p <= Z.sqrt n.
Proof.
 intros m n p Hint Heq.
 elim (Z_lt_le_dec (Z.sqrt n) m);
 elim (Z_lt_le_dec (Z.sqrt n) p).
 -  intros Hltm Hltp.
    assert (Hlem : (Z.sqrt n)+1 <= m) by  omega.
    assert (Hlep : (Z.sqrt n)+1 <= p) by  omega.
    elim (Zlt_irrefl n).
    apply Zlt_le_trans with (((Z.sqrt n)+1)*((Z.sqrt n)+1)).
    assert (Hposn : 0 <= n) by  omega.
    generalize (Z.sqrt_spec n Hposn);cbv zeta;intro H23.
    intuition.
    pattern n at 3; rewrite Heq.
    apply Zmult_le_compat; try omega.
    generalize (Z.sqrt_nonneg n).
    omega.
    generalize (Z.sqrt_nonneg n).
    omega.
 - intros Hple _; right; split; auto.
   apply Zmult_lt_0_reg_r with m; try tauto.
   rewrite Zmult_comm; omega.
 -  intros _ Hmle; left; split; tauto.
 -  intros _ Hmle; left; split; tauto.
Qed.

Definition divides_bool (p t:Z) : bool :=
 match t mod p with
   0 => true
 | _ => false
 end.

Fixpoint test_odds (n:nat) (p t:Z) {struct n} : bool :=
 match n with
 | 0%nat => negb (divides_bool 2 t)
 | S n' =>
   if test_odds n' (p - 2) t then negb (divides_bool p t) else false
 end.

Definition prime_test (n:nat) : bool :=
 match n with
 | 0%nat => false
 | 1%nat => false
 | S (S n) =>
  let x := (Z_of_nat (S (S n))) in
  let s := (Z.sqrt x) in
  let (half_s, even_bit) :=
    match s with
    | Zpos(xI h) => (Zpos h, 0)
    | Zpos(xO h) => (Zpos h, 1)
    | Zpos xH => (0, 0)
    | _ => (0, 1)
    end  in
  test_odds (Zabs_nat half_s) (s + even_bit) x
 end.

Time Eval lazy beta iota delta zeta in (prime_test 2333).

(* Time Eval compute in (prime_test 2333).

  This command takes a much longer time.  The reason is that Z.sqrt_plain
  calls a strongly specified function, which builds a proof term that is
  large, but is discarded later.  Lazy computation avoid the useless
  work. *)

(* we use the same axiom as in the book, it is corrected in another exercise.
 *)

Axiom verif_divide :
  (forall m p:nat, 0 < m -> 0 < p ->
   (exists q:nat, m = q*p) ->(Z_of_nat m mod Z_of_nat p = 0)%Z )%nat.

(* This axiom is actually a lemma used in the same other exercise. *)

Axiom Z_to_nat_and_back :
 forall x:Z, (0 <= x)%Z -> (Z_of_nat (Zabs_nat x))=x.

Theorem test_odds_correct2 :
  forall n x:nat,
    (1 < x)%nat ->
  forall p:Z,
    test_odds n p (Z_of_nat x) = true ->
    ~(exists y:nat, x = y*2)%nat.
Proof.
 intros n; elim n.
 - unfold test_odds, divides_bool; intros x H1ltx _ Heq Hex.
   assert (Heq' : Z_of_nat x mod Z_of_nat 2 = 0).
  +  apply verif_divide; auto with zarith.
  +  simpl (Z_of_nat 2) in Heq'; rewrite Heq' in Heq; simpl in Heq; discriminate.

 - clear n; intros n IHn x H1ltx p; simpl.
   case_eq (test_odds n (p - 2) (Z_of_nat x)).
   +  intros Htest' _ ; apply (IHn x H1ltx (p -2)); auto.
   +  intros; discriminate.
Qed.

Theorem Z_of_nat_le :
  forall x y, Z_of_nat x <= Z_of_nat y -> (x <= y)%nat.
Proof.
 intros; omega.
Qed.

Theorem test_odds_correct :
  forall (n x:nat)(p:Z),
   p = 2*(Z_of_nat n)+1 ->
   (1 < x)%nat -> test_odds n p (Z_of_nat x) = true ->
   forall q:nat, (1 < q <= 2*n+1)%nat -> ~(exists y:nat, x = q*y)%nat.
Proof.
 induction n.
 -  intros x p Hp1 H1ltx Hn q Hint.
    elimtype False;  omega.
  - intros x p Hp H1ltx; simpl (test_odds (S n) p (Z_of_nat x));
    intros Htest q (H1ltq, Hqle).
    case_eq (test_odds n (p -2) (Z_of_nat x)).
    + intros Htest'true.
      rewrite Htest'true in Htest.
      unfold divides_bool in Htest.
      elim (le_lt_or_eq q (2*S n + 1)%nat Hqle).
      *  intros Hqlt.
         assert (Hqle': (q <= (2* S n))%nat) by  omega.
         elim (le_lt_or_eq q (2 * S n)%nat Hqle').
         replace (2*S n)%nat with (2*n +2)%nat.
         intros Hqlt'.
         assert (Hqle'' : (q <= 2*n +1)%nat) by omega.
         apply (IHn x (p - 2)); auto with zarith arith.
         rewrite Hp; rewrite inj_S; unfold Zsucc; ring.
         ring.
         intros Hq (y, Hdiv); elim (test_odds_correct2 n x H1ltx (p - 2)); auto.
         exists (S n * y)%nat; rewrite Hdiv; rewrite Hq; ring.

      * intros Hq Hex; assert (Hp' : p = Z_of_nat q).
        rewrite Hp; rewrite Hq; rewrite inj_plus; rewrite inj_mult; auto.
        rewrite Hp' in Htest; rewrite (verif_divide x q) in Htest.
        simpl in Htest; discriminate.
        omega.
        omega.
        elim Hex; intros y Hdiv; exists y; rewrite Hdiv; ring.
    + intros Htest'; rewrite Htest' in Htest; simpl in Htest; discriminate.
Qed.

Axiom divisor_smaller :
  (forall m p:nat, 0 < m -> forall q:nat, m = q*p -> q <= m)%nat.

Theorem lt_Zpos : forall p:positive, 0 < Zpos p.
Proof.
 intros p; elim p.
 -  intros; rewrite Zpos_xI; omega.
 -  intros; rewrite Zpos_xO; omega.
 -  auto with zarith.
Qed.

Theorem Zneg_lt : forall p:positive, Zneg p < 0.
Proof.
 intros p; elim p.
 -  intros; rewrite Zneg_xI; omega.
 -  intros; rewrite Zneg_xO; omega.
 -  auto with zarith.
Qed.

Theorem prime_test_correct :
 forall n:nat, prime_test n = true ->
 ~(exists k:nat, k <> 1 /\ k <> n /\ (exists q:nat, n = q*k))%nat.
Proof.
 intros n; case_eq n.
 -  simpl;  intros Heq Hd; discriminate.
 -  intros n0; case_eq n0.
   +  simpl; intros Heq1 Heq2 Hd; discriminate.
   + unfold prime_test; intros n1 Heqn0 Heqn.
     assert (H1ltn : (1 < n)%nat).
     *  rewrite Heqn; auto with arith.
     * rewrite <- Heqn.
       lazy beta zeta delta [prime_test].
       case_eq (Z.sqrt (Z_of_nat n)).
       intros Hsqrt_eq.
       elim (Zlt_asym 1 (Z_of_nat n)).
       omega.
       lapply (Z.sqrt_spec (Z_of_nat n)).
       rewrite Hsqrt_eq; simpl.
       omega.
       omega.
       intros p Hsqrt_eq Htest_eq (k, (Hn1, (Hnn, (q,Heq)))).
       assert (H0ltn:(0 < n)%nat) by  omega.
       assert (Hkltn:(k < n)%nat).
       assert (Heq' : n=(k*q)%nat).
       rewrite Heq; ring.
       generalize (divisor_smaller n q H0ltn k Heq').
       omega.
       assert (Hex: exists k':nat, (1 < (Z_of_nat k') <= (Z.sqrt (Z_of_nat n))) /\
               (exists q':nat, n=(k'*q')%nat)).
       elim (div_Zsqrt (Z_of_nat k) (Z_of_nat n) (Z_of_nat q)).
       intros Hint1; exists k;split.
       omega.
       exists q; rewrite Heq; ring.
       intros Hint2; exists q; split.
       split.
       elim (Zle_or_lt (Z_of_nat q) 1); auto.
       intros hqle1;  assert (Hq1: q = 1%nat).
       omega.
       rewrite Hq1 in Heq; simpl in Heq; elim Hnn; rewrite Heq; ring.
       tauto.
       exists k; auto.
       split.
       case_eq k.
       intros Hk0; rewrite Hk0 in Heq; rewrite Heq in H1ltn;
       rewrite mult_0_r in H1ltn; omega.
       intros; unfold Zlt; simpl; auto.
       omega.
       rewrite Zmult_comm; rewrite <- inj_mult; rewrite Heq;auto.
       elim Hex; intros k' ((H1ltk', Hk'ltsqrt), Hex'); clear Hex.
       case_eq p.
       intros p' Hp; rewrite Hp in Htest_eq.
       elim (test_odds_correct (Zabs_nat (Zpos p'))
           n (Zpos p)) with k'.
       rewrite Z_to_nat_and_back.
       rewrite Hp.
       auto with zarith.
       auto with zarith.
       auto.
       repeat rewrite Zminus_0_r in Htest_eq.
       rewrite Hp; auto.
       split.
       omega.
       apply Z_of_nat_le.
       rewrite inj_plus.
       rewrite inj_mult.
       rewrite Z_to_nat_and_back.
       simpl (Z_of_nat 2).
       simpl (Z_of_nat 1).
       rewrite <- Zpos_xI.
       rewrite <- Hp.
       rewrite <- Hsqrt_eq; auto.
       auto with zarith.
       auto.
       intros p' Hp; rewrite Hp in Htest_eq.
       elim (test_odds_correct (Zabs_nat (Zpos p')) n (Zpos p + 1))
       with k'.
       rewrite Z_to_nat_and_back.
       rewrite Hp; rewrite Zpos_xO; ring.
       generalize (lt_Zpos p'); intros; omega.
       auto.
       rewrite <- Hp in Htest_eq; auto.
       split; try omega.
       apply Z_of_nat_le.
       rewrite inj_plus.
       rewrite inj_mult.
       rewrite Z_to_nat_and_back.
       simpl (Z_of_nat 2); simpl (Z_of_nat 1).
       rewrite <- Zpos_xO.
       rewrite <- Hp; omega.
       auto with zarith.
       auto.
       intros Hp; rewrite Hp in Hsqrt_eq.
       rewrite Hsqrt_eq in Hk'ltsqrt.
       omega.
       intros p Hsqrt_eq.
       elim (Zle_not_lt 0 (Z.sqrt (Z_of_nat n))).
       apply Z.sqrt_nonneg.
       rewrite Hsqrt_eq.
       apply Zneg_lt.
Qed.


Require Export GeoCoq.Elements.OriginalProofs.lemma_NCdistinct.
Require Export GeoCoq.Elements.OriginalProofs.lemma_NChelper.

Section Euclid.

Context `{Ax1:euclidean_neutral_ruler_compass}.

Lemma lemma_samesidecollinear :
   forall A B C P Q,
   OS P Q A B -> Col A B C -> neq A C ->
   OS P Q A C.
Proof.
intros.
let Tf:=fresh in
assert (Tf:exists p q r, (Col A B p /\ Col A B q /\ BetS P p r /\ BetS Q q r /\ nCol A B P /\ nCol A B Q)) by (conclude_def OS );destruct Tf as [p[q[r]]];spliter.
assert (neq A B) by (forward_using lemma_NCdistinct).
assert (eq A A) by (conclude cn_equalityreflexive).
assert (Col A B A) by (conclude_def Col ).
assert (nCol A C P) by (conclude lemma_NChelper).
assert (nCol A C Q) by (conclude lemma_NChelper).
assert (Col B A p) by (forward_using lemma_collinearorder).
assert (Col B A C) by (forward_using lemma_collinearorder).
assert (neq B A) by (conclude lemma_inequalitysymmetric).
assert (Col A C p) by (conclude lemma_collinear4).
assert (Col B A q) by (forward_using lemma_collinearorder).
assert (Col A C q) by (conclude lemma_collinear4).
assert (OS P Q A C) by (conclude_def OS ).
close.
Qed.

End Euclid.


Require Import Coq.Arith.EqNat.
Require Import Coq.Arith.Compare_dec.
Require Import Coq.Lists.List.
Require Import Coq.Bool.Bool.
Require Import Coq.micromega.Psatz.

Import ListNotations.

Definition tree_id := (nat * list nat) % type.

Notation root := (0, []).

Module TreeId.
  Definition tick (p:tree_id) :=
  let (n, l) := p in (S n, l).

  Definition child (p:tree_id) : tree_id :=
  let (n, l) := p in (0, n :: l).

  Definition add (p:tree_id) m :=
  let (n, l) := p in (n + m, l).
End TreeId.

Module Interval.
  Section Spec.

  Import TreeId.

  Require Import Coq.QArith.QArith.

  Definition interval := (Q * Q) % type.

  Definition max (x y:Q) :=
  if Qlt_le_dec x y then y else x.

  Definition min (x y:Q) :=
  if Qlt_le_dec x y then x else y.

  Definition merge (i j:interval) : interval :=
  let (l1, h1) := i in
  let (l2, h2) := j in
  (min l1 l2, max h1 h2).

  Open Scope Q_scope.

  Definition iroot : interval := (0, 1).

  Definition halve (i:interval) :=
  let (l, h) := i in
  (h - l) * (1# 2).

  Definition itick (i:interval) :=
  let (l, h) := i in
  (l, h - halve i).

  Definition ichild (i:interval) :=
  let (l, h) := i in
  (h - halve i, h).

  Fixpoint iadd (i:interval) (n:nat) :=
  match n with
  | 0 % nat => i
  | S n => iadd (itick i) n
  end.

  Fixpoint itree (l:list nat) :=
  match l with
  | nil => iroot
  | n :: l => iadd (ichild (itree l)) n
  end.

  Definition from_tree (t:tree_id) :=
  let (n, l) := t in itree (n:: l).

  Lemma tick_add_0:
    forall i,
    itick i = iadd i 1.
  Proof.
    intros.
    auto.
  Qed.

  Lemma itick_iadd:
    forall n i,
    iadd (itick i) n = itick (iadd i n).
  Proof.
    induction n; intros. {
      simpl; auto.
    }
    simpl.
    rewrite IHn.
    auto.
  Qed.

  Lemma itick_iadd_1:
    forall n i,
    itick (iadd i n) = iadd i (S n).
  Proof.
    intros.
    simpl.
    rewrite itick_iadd.
    auto.
  Qed.

  Lemma tick_1:
    forall (t:tree_id),
    itick (from_tree t) = from_tree (tick t).
  Proof.
    intros.
    destruct t.
    simpl.
    rewrite itick_iadd.
    auto.
  Qed.

  Lemma iadd_iadd:
    forall n (i:interval) m,
    iadd (iadd i n) m = iadd i (n + m).
  Proof.
    induction n; intros. {
      simpl; auto.
    }
    simpl.
    rewrite IHn.
    auto.
  Qed.

  Lemma add_1:
    forall (t:tree_id) n,
    iadd (from_tree t) n = from_tree (add t n).
  Proof.
    intros.
    destruct t.
    simpl.
    rewrite iadd_iadd.
    auto.
  Qed.

  Lemma ichild_child:
    forall t,
    from_tree (child t) = ichild (from_tree t).
  Proof.
    intros.
    destruct t.
    simpl.
    auto.
  Qed.

  Goal iroot = (0, 1).
    auto.
  Qed.

  Goal itick iroot = (0, 1 # 2).
    simpl.
    compute.
    auto.
  Qed.

  Goal ichild iroot = (1 # 2, 1).
    auto.
  Qed.

  Goal itick (ichild iroot) = (1#2, 3#4).
    simpl.
    compute.
    auto.
  Qed.

  Goal ichild (ichild iroot) = (3#4, 1).
    compute.
    auto.
  Qed.

  Goal itree [2%nat] = itick (itick (ichild iroot)).
    compute.
    auto.
  Qed.

  Inductive Overlap: interval -> interval -> Prop :=
  | overlap_def:
    forall l1 h1 l2 h2,
    l1 <= h2 ->
    l2 < h1 ->
    Overlap (l1, h1) (l2, h2).

  Lemma overlap_dec i1 i2 :
    { Overlap i1 i2 } + { ~ Overlap i1 i2 }.
  Proof.
    destruct i1 as (l1,h1), i2 as (l2,h2).
    destruct (Qlt_le_dec h2 l1). {
      right.
      unfold not; intros.
      inversion H.
      lra.
    }
    destruct (Qlt_le_dec l2 h1). {
      auto using overlap_def.
    }
    right.
    unfold not; intros N.
    inversion N.
    lra.
  Defined.

  Inductive Follows: interval -> interval -> Prop :=
  | follows_def:
    forall l1 h1 l2 h2,
    h1 == l2 ->
    Follows (l1, h1) (l2, h2).

  Inductive Contiguous i1 i2 : Prop :=
  | contiguous_l:
    Follows i1 i2 ->
    Contiguous i1 i2
  | contiguous_2:
    Follows i2 i1 ->
    Contiguous i1 i2.

  Inductive Prec : interval -> interval -> Prop :=
  | prec_def:
    forall l1 h1 l2 h2,
    h1 < l2 ->
    Prec (l1, h1) (l2, h2).

  Inductive CanMerge i1 i2 : Prop :=
  | can_merge_overlap:
    Overlap i1 i2 ->
    CanMerge i1 i2
  | can_merge_contiguous:
    Contiguous i1 i2 ->
    CanMerge i1 i2.

End Spec.
End Interval.

Module Event.
  Import Interval.
  Definition event := (interval * nat) % type.

  Definition union (e1 e2:event) :=
  let (i1, n1) := e1 in
  let (i2, n2) := e2 in
  (merge i1 i2, Nat.max n1 n2).

  Inductive Lt : event -> event -> Prop :=
  | lt_def:
    forall i1 i2 n1 n2,
    Overlap i1 i2 ->
    n1 < n2->
    Lt (i1, n1) (i2, n2).

  Lemma lt_trans:
    forall e1 e2 e3,
    Overlap (fst e1) (fst e3) ->
    Lt e1 e2 ->
    Lt e2 e3 ->
    Lt e1 e3.
  Proof.
    intros (i1, n1) (i2, n2) (i3, n3); intros.
    inversion H0; inversion H1; subst; clear H0 H1; simpl in *; subst.
    auto using lt_def with *.
  Qed.

  Inductive CanUnion: event -> event -> Prop :=
  | can_union_def:
    forall i1 i2 n1 n2,
    CanMerge i1 i2 ->
    CanUnion (i1,n1) (i2,n2).

End Event.

Module Stamp.
  Import Event.
  Definition stamp := (event * list event) % type.

  Definition Le l1 l2 :=
    forall x y,
    List.In x l1 ->
    List.In y l2 ->
    Event.Le x y.

End Stamp.

Module Others.

  Inductive Observes : list nat -> list nat -> Prop :=
  | observes_def:
    forall x y l,
    x > y ->
    Observes (x::l) (y::l).

  Inductive Spawn: tree_id -> tree_id -> Prop :=
  | spawn_def:
    forall n l,
    Spawn (S n, l) (0, n::l).

  Inductive ParentOf : tree_id -> tree_id -> Prop :=
  | parent_of_def:
    forall a b c l,
    a > c ->
    ParentOf (a, l) (b, c :: l).

  Lemma spawn_to_parent_of:
    forall x y,
    Spawn x y ->
    ParentOf x y.
  Proof.
    intros.
    inversion H; subst; clear H.
    apply parent_of_def.
    auto.
  Qed.

Definition parent_of (l:list nat) (r:list nat) :=
match r with
| _ :: r => if (list_eq_dec PeanoNat.Nat.eq_dec l r) then true else false
| _ => false
end.

(*
Goal parent_of [0;0] [1;0;0]  = true.
  compute.
  trivial.
Qed.
*)
Definition lt_sibling (l r:list nat) :=
match l, r with
| x :: l, y :: r =>
  if (lt_dec x y) then
    if (list_eq_dec PeanoNat.Nat.eq_dec l r) then true else false
  else false
| _,_ => false
end.

Definition gt_sibling (l r:list nat) := lt_sibling r l.

Definition contains (i j:list nat) :=
let fix ancestor_gt_sibling i j :=
  match i with
  | _ :: i => gt_sibling i j || ancestor_gt_sibling i j
  | _ => false
  end
in
if parent_of i j then true else ancestor_gt_sibling i j.
(*
Goal contains [3;1;0] [0;0]  = true.
  compute; auto.
Qed.

Goal contains [3;1;0] [2;0]  = false.
  compute;auto.
Qed.

Goal contains [3;3;0] [2;0]  = true.
  compute;auto.
Qed.
*)
Section AsCG.
  Require Import SafeJoins.
  Import SJ_Notations.
  Require Import Tid.

  Inductive MapsTo : tree_id -> tid -> list op -> Prop :=
  | maps_to_tick_root:
    forall x y,
    MapsTo (tick root) x [F x y]
  | maps_to_child_root:
    forall x y,
    MapsTo (child root) y [F x y]
  | maps_to_fork_parent:
    forall x y t k,
    MapsTo t x k ->
    MapsTo (tick t) x (F x y::k)
  | maps_to_fork_child:
    forall x y t k,
    MapsTo t x k ->
    MapsTo (child t) y (F x y::k)
  | maps_to_fork_neq:
    forall x y z k t,
    z <> x ->
    MapsTo t z k ->
    MapsTo t z (F x y::k)
  | maps_to_join:
    forall x y z t k,
    MapsTo t z k ->
    MapsTo t z (J x y::k).

  Lemma maps_to_inv_0:
    forall t x y,
    x <> y ->
    MapsTo t x [F x y] ->
    t = tick root.
  Proof.
    intros.
    inversion H0; subst; clear H0; auto; intuition.
    inversion H4.
  Qed.

  Lemma maps_to_inv_1:
    forall t x y,
    x <> y ->
    MapsTo t y [F x y] ->
    t = child root.
  Proof.
    intros.
    inversion H0; subst; clear H0; subst; simpl; intuition.
    + inversion H4.
    + inversion H7.
  Qed.

  Lemma maps_to_inv_2:
    forall t x o,
    MapsTo t x [o] ->
    exists y,
    (o = F x y /\ t = tick root)
    \/
    (o = F y x /\ t = child root).
  Proof.
    intros.
    inversion H; subst; clear H; eauto; try inversion H3.
    inversion H5.
  Qed.

  Lemma trace_inv_0:
    forall o,
    Trace [o] ->
    exists x y, F x y = o.
  Proof.
    intros.
    inversion H; subst. {
      eauto.
    }
    inversion H; subst.
    inversion H3.
  Qed.

  Lemma maps_to_inv_3:
    forall k t x y,
    x <> y ->
    k <> nil ->
    Trace (F x y :: k) ->
    MapsTo t x (F x y :: k) ->
    exists s, t = tick s /\ MapsTo s x k.
  Proof.
    induction k; intros; auto. {
      intuition.
    }
    destruct k. {
      inversion H1; subst; clear H1.
      inversion H2; subst; clear H2; intuition.
      apply maps_to_inv_2 in H5.
      destruct H5 as (z, [(?,He)|(?,He)]); inversion He; subst. {
        eauto using maps_to_tick_root.
      }
      eauto using maps_to_child_root.
    }
    inversion H1; subst; clear H1.
    inversion H2; subst; clear H2; eauto; intuition.
  Qed.

  Lemma maps_to_to_in:
    forall t x k,
    MapsTo t x k ->
    Graph.In (Edge k) x.
  Proof.
    intros.
    induction H;
    eauto using
    Graph.in_left, Graph.in_right,
    edge_fork_eq, Graph.in_impl, edge_cons.
  Qed.

  Lemma maps_to_fun:
    forall k t s x,
    Trace k ->
    MapsTo t x k ->
    MapsTo s x k ->
    t = s.
  Proof.
    induction k; intros. {
      inversion H0.
    }
    destruct a as ([], a, b). {
      inversion H; subst.
      inversion H0; subst; clear H0.
      + apply maps_to_inv_0 in H1; auto.
      + apply maps_to_inv_1 in H1; auto.
      + destruct k. {
          inversion H8.
        }
        apply maps_to_inv_3 in H1; auto with *.
        destruct H1 as (s', (?, Hm)).
        subst.
        apply IHk with (t:=t0) in Hm; subst; auto.
      + inversion H1; subst; clear H1; intuition.
        * inversion H8.
        * assert (t0 = t) by eauto; subst; auto.
        * contradiction H6.
          eauto using maps_to_to_in.
      + inversion H1; subst; clear H1; intuition.
        * inversion H11.
        * contradiction H6.
          eauto using maps_to_to_in.
        * eauto.
    }
    inversion H0; subst; clear H0.
    inversion H1; subst; clear H1.
    inversion H; subst; eauto.
  Qed.

  Lemma maps_to_inv_4:
    forall x y s t k,
    Trace (F x y :: k) ->
    MapsTo s x (F x y :: k) ->
    MapsTo t x k ->
    s = tick t.
  Proof.
    intros.
    destruct k. {
      inversion H1.
    }
    inversion H; subst.
    apply maps_to_inv_3 in H0; auto with *.
    destruct H0 as (t', (He, Hm)); subst.
    assert (He: t = t') by eauto using maps_to_fun.
    subst; auto.
  Qed.

  Lemma maps_to_inv_5:
    forall x y z t,
    x <> y ->
    MapsTo t z [F x y] ->
    (t = tick root /\ z = x) \/
    (t = child root /\ z = y).
  Proof.
    intros.
    inversion H0; subst; auto; try inversion H4.
    inversion H7.
  Qed.

  Lemma maps_to_inv_6:
    forall x y z,
    MapsTo (tick root) x [F y z] ->
    y = x.
  Proof.
    intros.
    inversion H; subst; clear H; auto. {
      inversion H3.
    }
    inversion H6.
  Qed.

  Lemma maps_to_inv_7:
    forall x y z,
     MapsTo (child root) x [F y z] ->
     x = z.
   Proof.
    intros.
    inversion H; subst; clear H; auto. {
      inversion H3.
    }
    inversion H6.
   Qed.

  Lemma child_neq_tick:
    forall x y,
    child x <> tick y.
  Proof.
    intros.
    unfold not; intros.
    destruct x, y.
    simpl in *.
    inversion H.
  Qed.

  Lemma maps_to_absurd_0:
    forall k n m l x y,
    n <> m ->
    MapsTo (n, l) x k ->
    ~ MapsTo (m, l) y k.
  Proof.
    induction k; intros. {
      inversion H0.
    }
    unfold not; intros.
    inversion H0; subst; clear H0.
    - inversion H1; subst; clear H1.
      + contradiction H; auto.
      + inversion H4.
      + inversion H4.
      + inversion H7.
    - inversion H1; subst; clear H1.
      + contradiction H; auto.
      + inversion H4.
      + inversion H4.
      + inversion H7.
    - destruct t; subst.
      inversion H2; subst; clear H2.
      inversion H1; subst; clear H1.
      + inversion H5.
      + inversion H5.
      + destruct t.
        simpl in *.
        destruct m. {
          inversion H0.
        }
        inversion H0; subst; clear H0.
        assert (n0 <> m). {
          unfold not; intros; subst.
          contradiction H; auto.
        }
        eapply IHk in H4; eauto.
      + destruct t.
        simpl in *.
        destruct m. {
          inversion H0; subst; clear H0.
        }
        apply child_neq_tick in H; contradiction.
      + destruct t0.
        simpl in *.
        destruct n. {

        }
  Qed.

  Lemma maps_to_fun_2:
    forall k t x y,
    Trace k ->
    MapsTo t x k ->
    MapsTo t y k ->
    x = y.
  Proof.
    induction k; intros. {
      inversion H0.
    }
    inversion H; subst. {
      inversion H0; subst; clear H0.
      - apply maps_to_inv_6 in H1; subst; auto.
      - apply maps_to_inv_7 in H1; subst; auto.
      - inversion H1; subst; clear H1.
        + inversion H8.
        + inversion H8.
        + auto.
        + apply child_neq_tick in H0; contradiction.
        + apply IHk with (x:=x0) (y:=y) in H6.
    }
  Qed.

  Inductive In t k : Prop :=
  | in_def:
    forall x,
    MapsTo t x k ->
    In t k.

  Inductive TreeId t x : list op -> Prop :=
  | tree_id_eq:
    forall k,
    MapsTo t x k ->
    TreeId t x k
  | tree_id_cons:
    forall k e,
    TreeId t x k ->
    TreeId t x (e::k).

  Inductive ForkTree : tid * tid -> tree_id * tree_id -> list op -> Prop :=
  | fork_tree_eq:
    forall x y t1 t2 k,
    MapsTo t1 x (F x y::k) ->
    MapsTo t2 y (F x y::k) ->
    ForkTree (x,y) (t1, t2) (F x y::k)
  | fork_tree_cons:
    forall e1 e2 e3 k,
    ForkTree e1 e2 k ->
    ForkTree e1 e2 (e3::k).

  Lemma parent_of_child:
    forall t,
    ParentOf (tick t) (child t).
  Proof.
    intros (n, l).
    unfold tick, child; simpl.
    auto using parent_of_def, observes_def with *.
  Qed.

  Lemma fork_tree_to_parent_of:
    forall k x y s t,
    Trace k ->
    ForkTree (x, y) (s, t) k ->
    ParentOf s t.
  Proof.
    induction k; intros. {
      inversion H0.
    }
    inversion H0; subst; clear H0. {
      inversion H; subst.
      inversion H8; subst; clear H8; intuition.
      + apply maps_to_inv_0 in H4; auto; subst.
        auto using parent_of_child.
      + eapply maps_to_inv_4 in H7; eauto; subst.
        auto using parent_of_child.
      + contradiction H5.
        eauto using maps_to_to_in.
    }
    inversion H; subst; clear H; eauto.
  Qed.

  Let absurd_cons:
    forall {A} l (x:A), x :: l <> l.
  Proof.
    induction l; intros. {
      auto with *.
    }
    unfold not; intros N.
    inversion N.
    apply IHl in H1.
    contradiction.
  Qed.

  Let absurd_cons_cons:
    forall {A} l (x y:A), y :: x :: l <> l.
  Proof.
    induction l; intros. {
      auto with *.
    }
    unfold not; intros N.
    inversion N.
    apply IHl in H1.
    contradiction.
  Qed.

  Lemma observes_length:
    forall x y,
    Observes x y ->
    length x = length y.
  Proof.
    induction x; intros. {
      inversion H.
    }
    inversion H; subst.
    simpl; auto.
  Qed.

  Lemma observes_irrefl:
    forall x,
    ~ Observes x x.
  Proof.
    intros.
    unfold not; intros.
    inversion H; subst; clear H.
    intuition.
  Qed.

  Lemma observes_asymm:
    forall x y,
    Observes x y ->
    ~ Observes y x.
  Proof.
    intros.
    unfold not; intros.
    inversion H; subst; clear H.
    inversion H0; subst; clear H0.
    intuition.
  Qed.

  Lemma parent_of_asymm:
    forall x y,
    ParentOf x y ->
    ~ ParentOf y x.
  Proof.
    intros.
    unfold not; intros.
    inversion H; subst; clear H.
    inversion H0; clear H0.
    rewrite <- H3 in *.
    rewrite H3 in *.
    apply absurd_cons_cons in H5.
    contradiction.
  Qed.

  Lemma parent_of_irrefl:
    forall x,
    ~ ParentOf x x.
  Proof.
    intros.
    unfold not; intros.
    inversion H.
    apply absurd_cons in H3.
    contradiction.
  Qed.

  Lemma parent_of_tick_snd:
    forall x y,
    ParentOf x y ->
    ParentOf x (tick y).
  Proof.
    intros.
    inversion H; subst; clear H; simpl.
    apply parent_of_def; simpl; auto.
  Qed.

  Lemma parent_of_absurd_root:
    forall x,
    ~ ParentOf x root.
  Proof.
    intros.
    unfold not; intros.
    inversion H; subst.
  Qed.

  Lemma parent_of_absurd_tick_root:
    forall x,
    ~ ParentOf x (tick root).
  Proof.
    intros.
    unfold not; intros.
    inversion H; subst.
  Qed.

  Lemma spawn_absurd_tick_root:
    forall x,
    ~ Spawn x (tick root).
  Proof.
    unfold tick, not; intros.
    inversion H.
  Qed.

  Require Import Omega.

  Lemma parent_of_child_root:
    forall x,
    ParentOf x (child root) ->
    exists n, x = tick (n, []).
  Proof.
    intros.
    unfold tick.
    simpl.
    inversion H; subst; clear H.
    destruct a. {
      omega.
    }
    eauto.
  Qed.

  Let fork_tree_eq_0:
    forall x y,
    ForkTree (x, y) (tick root, child root) [F x y].
  Proof.
    intros.
    auto using fork_tree_eq, maps_to_tick_root, maps_to_child_root.
  Qed.

  Lemma parent_of_simpl_left:
    forall x y,
    ParentOf x (tick y) ->
    ParentOf x y.
  Proof.
    intros.
    destruct y as (n, l); simpl in *.
    inversion H; subst; clear H.
    auto using parent_of_def.
  Qed.

  Lemma fork_tree_eq_fork:
    forall x y k t,
    x <> y ->
    ~ Graph.In (Edge k) y ->
    MapsTo t x k ->
    ForkTree (x, y) (tick t, child t) (F x y :: k).
  Proof.
    intros.
    auto using fork_tree_eq, maps_to_fork_parent, maps_to_fork_child.
  Qed.

  Lemma spawn_irrefl:
    forall x,
    ~ Spawn x x.
  Proof.
    unfold not; intros.
    destruct x.
    inversion H.
  Qed.

  Lemma spawn_inv_tick_child:
    forall x y,
    Spawn (tick x) (child y) ->
    x = y.
  Proof.
    intros.
    destruct x, y.
    unfold child, tick in *.
    inversion H; subst.
    auto.
  Qed.

  Lemma spawn_absurd_tick_tick:
    forall x y,
    ~ Spawn (tick x) (tick y).
  Proof.
    intros; destruct x, y.
    simpl.
    unfold not; intros.
    inversion H.
  Qed.

  Lemma maps_to_inv_root:
    forall k x,
    ~ MapsTo root x k.
  Proof.
    induction k; unfold not; intros. {
      inversion H.
    }
    inversion H; subst; clear H.
    - destruct t.
      simpl in *.
      inversion H0.
    - destruct t.
      simpl in *.
      inversion H0.
    - apply IHk in H5.
      contradiction.
    - apply IHk in H3; contradiction.
  Qed.

  Lemma tick_absurd_0:
    forall x,
    tick x <> x.
  Proof.
    intros.
    destruct x.
    unfold not; intros.
    simpl in *.
    inversion H.
    omega.
  Qed.

  Ltac simpl_tree :=
  repeat match goal with
  | [ H : ParentOf ?x ?x |- _ ] =>
    apply parent_of_irrefl in H; contradiction
  | [ H : Spawn ?x ?x |- _ ] =>
    apply spawn_irrefl in H; contradiction
  | [ H: Spawn (child ?x) (tick ?x) |- _ ] =>
    inversion H
  | [ H: Spawn (tick ?x) (child ?x) |- _ ] =>
    clear H
  | [ H: ParentOf _ (tick root) |- _ ] =>
    apply parent_of_absurd_tick_root in H; contradiction
  | [ H: Spawn (tick _) (tick _) |- _ ] =>
    apply spawn_absurd_tick_tick in H; contradiction
  | [ H: Spawn (tick ?x) (child ?y) |- _ ] =>
    apply spawn_inv_tick_child in H; rewrite H in *; clear H
  | [ H: Spawn _ (tick root) |- _ ] =>
    apply spawn_absurd_tick_root in H; contradiction
  | [ H: MapsTo root _ _ |- _ ] =>
    apply maps_to_inv_root in H; contradiction
  | [ H: MapsTo _ _ nil |- _ ] =>
    inversion H
  | [ H: MapsTo ?t ?x ?k, H2: MapsTo ?s ?x ?k |- _ ] =>
    assert (t = s) by eauto using maps_to_fun; subst; clear H2
  end.

  Lemma parent_of_to_fork_tree:
    forall k x y s t,
    Trace k ->
    MapsTo t x k ->
    MapsTo s y k ->
    Spawn t s ->
    ForkTree (x, y) (t, s) k.
  Proof.
    induction k; intros; simpl_tree.
    inversion H; subst; clear H. {
      inversion H0; subst; clear H0.
      + apply maps_to_inv_5 in H1; auto.
        destruct H1 as [(?,?)|(?,?)]; subst; simpl_tree.
        auto using fork_tree_eq_fork.
      + apply maps_to_inv_5 in H1; auto.
        destruct H1 as [(?,?)|(?,?)]; subst; simpl_tree.
      + inversion H1; subst; clear H1; simpl_tree. {
          auto using fork_tree_eq_fork.
        }
        apply fork_tree_cons.
        inversion H11; subst; clear H11; simpl_tree.
        - inversion H8; subst; clear H8; simpl_tree. {

          }
        - destruct t0; simpl in *.
          unfold root, child in *.
          simpl in *.
          inversion H2; subst.
        inversion H2; subst; clear H2.
        destruct t0; simpl in *.
        inversion H0; subst; clear H0.
        inversion H11; subst; clear H11.
        - destruct t.
          simpl in *.
          inversion H; subst; clear H.
        - destruct t.
          simpl in *.
          subst.
          apply
        destruct x, t0; simpl in *; subst; simpl in *; simpl_tree.
        apply fork_tree_cons.
        apply IHk; auto using spawn_def.
        unfold tick.
        simpl.
    }
    inversion H0; subst.
    - inversion H0.
    - inversion H0; subst; clear H0.
      + apply maps_to_inv_5 in H1; auto.
        destruct H1 as [(?,?)|(?,?)]; subst; simpl_tree; auto.
      + apply maps_to_inv_5 in H1; auto.
        destruct H1 as [(?,?)|(?,?)]; subst; simpl_tree.
      + inversion H1; subst; clear H1; simpl_tree.
        * auto using fork_tree_eq_fork.
        * apply fork_tree_cons.
  Qed.

  Inductive JoinTree : tree_id -> tree_id -> list op -> Prop :=
  | join_tree_eq:
    forall x y t1 t2 k,
    MapsTo t1 x k ->
    MapsTo t2 y k ->
    JoinTree t1 t2 (J x y::k)
  | join_tree_cons:
    forall t1 t2 k e,
    JoinTree t1 t2 k ->
    JoinTree t1 t2 (e :: k).

  Lemma fork_spec_1 k es (Hs: Safe k es):
    forall x y t s,
    MapsTo t x k ->
    MapsTo s y k ->
    ForkTree t s k ->
    List.In (F x y) k.
  Proof.
    induction k; intros. {
      inversion H; subst; clear H.
    }
    destruct a as ([], a,b). {
      destruct (tid_eq_dec x a). {
        subst.
        destruct (tid_eq_dec y b). {
          subst.
          auto using in_eq.
        }
        inversion Hs; subst; clear Hs.
        inversion H4; subst; clear H4.
      }
    }
  Qed.

  Inductive ForkEdge x y k : Prop :=
  | fork_edge_def:
    forall t s,
    ForkTree t s k ->
    TreeId t x k ->
    TreeId s y k ->
    ForkEdge x y k.

  Lemma fork_spec_1:
    forall k x y,
    ForkEdge x y k ->
    List.In (F x y) k.
  Proof.
    induction k; intros. {
      inversion H; subst; clear H.
      inversion H1; inversion H.
    }
    inversion H; subst; clear H. {
      inversion H0; subst; clear H0. {

      }
    }
  Qed.

End AsCG.


(* File: Lists.v *)
(* Title: Lists - Data Structures in Coq *)
(* Author: Peter Urbak <peteru@dragonwasrobot.com> *)
(* Version: 2012-09-24 *)

Require Export "Basics".

Module NatList.

(* Pairs of Numbers *)

Inductive natprod : Type :=
  pair : nat -> nat -> natprod.

Definition fst (p : natprod) : nat :=
  match p with
    | pair x y => x
  end.

Definition snd (p : natprod) : nat :=
  match p with
    | pair x y => y
  end.

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

Eval simpl in (fst (3,4)).

Definition fst' (p : natprod) : nat :=
  match p with
    (x,y) => x
  end.

Definition snd' (p : natprod) : nat :=
  match p with
    (x,y) => y
  end.

Definition swap_pair (p : natprod) : natprod :=
  match p with
    | (x,y) => (y,x)
  end.

Theorem surjective_pairing' : forall (n m : nat),
  (n,m) = (fst (n,m), snd (n,m)).
Proof.
  intros n m.
  unfold fst.
  unfold snd.
  reflexivity.
Qed.

Theorem surjective_pairing : forall (p : natprod),
  p = (fst p, snd p).
Proof.
  intros p.
  destruct p as (n, m).
  unfold fst.
  unfold snd.
  reflexivity.
Qed.

(* Exercise: 1 star (snd_fst_is_swap) *)

Theorem snd_fst_is_swap : forall (p : natprod),
  (snd p, fst p) = swap_pair p.
Proof.
  intros p.
  destruct p as (n, m).
  unfold swap_pair.
  unfold snd.
  unfold fst.
  reflexivity.
Qed.

(* Exercise: 1 star, optional (fst_swap_is_snd) *)

Theorem fst_swap_is_snd : forall (p : natprod),
  fst (swap_pair p) = snd p.
Proof.
  intros p.
  destruct p as (n, m).
  unfold swap_pair.
  unfold fst.
  unfold snd.
  reflexivity.
Qed.

(* Lists of Numbers *)

Inductive natlist : Type :=
  | nil : natlist
  | cons : nat -> natlist -> natlist.

Definition l_123 := cons 1 (cons 2 (cons 3 nil)).

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

Definition l_123' := 1 :: (2 :: (3 :: nil)).
Definition l_123'' := 1 :: 2 :: 3 :: nil.
Definition l_123''' := [1,2,3].

(* Added the ' on repeat to avoid wrong syntax highlighting in emacs. *)
Fixpoint repeat' (n count : nat) : natlist :=
  match count with
    | O => nil
    | S count' => n :: (repeat' n count')
  end.

Fixpoint length (l : natlist) : nat :=
  match l with
    | nil => O
    | h :: t => S (length t)
  end.

Fixpoint app (l1 l2 : natlist) : natlist :=
  match l1 with
    | nil => l2
    | h :: t => h :: (app t l2)
  end.

Notation "x ++ y" := (app x y) (at level 60, right associativity).

Example test_app1 : [1,2,3] ++ [4,5] = [1,2,3,4,5].
Proof. unfold app. reflexivity. Qed.

Example test_app2 : nil ++ [4,5] = [4,5].
Proof. unfold app. reflexivity. Qed.

Example test_app3 : [1,2,3] ++ nil = [1,2,3].
Proof. unfold app. reflexivity. Qed.

Definition hd (default : nat) (l : natlist) : nat :=
  match l with
    | nil => default
    | h :: t => h
  end.

Definition tail (l : natlist) : natlist :=
  match l with
    | nil => nil
    | h :: t => t
  end.

Example test_hd1 : hd 0 [1,2,3] = 1.
Proof. unfold hd. reflexivity. Qed.

Example test_hd2 : hd 0 [] = 0.
Proof. unfold hd. reflexivity. Qed.

Example test_tail : tail [1,2,3] = [2,3].
Proof. unfold tail. reflexivity. Qed.

(* Exercise: 2 stars, recommended (list_funs) *)

Fixpoint nonzeros (l : natlist) : natlist :=
  match l with
    | nil => nil
    | O :: t => (nonzeros t)
    | h :: t => h :: (nonzeros t)
  end.

Example test_nonzeros : nonzeros [0,1,0,2,3,0,0] = [1,2,3].
Proof. unfold nonzeros. reflexivity. Qed.

Fixpoint oddmembers (l : natlist) : natlist :=
  match l with
    | nil => nil
    | h :: t => match (evenb h) with
                  | true => (oddmembers t)
                  | false => h :: (oddmembers t)
                end
  end.

Example test_oddmembers : oddmembers [0,1,0,2,3,0,0] = [1,3].
Proof. unfold oddmembers. unfold evenb. reflexivity. Qed.

Fixpoint countoddmembers (l : natlist) : nat :=
  match l with
    | nil => O
    | h :: t => match (evenb h) with
                  | true => (countoddmembers t)
                  | false => S (countoddmembers t)
                end
  end.

Example test_countoddmembers1: countoddmembers [1,0,3,1,4,5] = 4.
Proof. unfold countoddmembers. unfold evenb. reflexivity. Qed.

Example test_countoddmembers2: countoddmembers [0,2,4] = 0.
Proof. unfold countoddmembers. unfold evenb. reflexivity. Qed.

Example test_countoddmembers3: countoddmembers nil = 0.
Proof. unfold countoddmembers. unfold evenb. reflexivity. Qed.

(* Exercise: 3 stars (alternate) *)

Fixpoint alternate (l1 l2 : natlist) : natlist :=
  match l1, l2 with
    | nil, l2 => l2
    | l1, nil => l1
    | h1 :: t1, h2 :: t2 => h1 :: h2 :: (alternate t1 t2)
  end.

Lemma unfold_alternate_inductive_case : forall (h1 h2 : nat) (t1 t2 : natlist),
  alternate (h1 :: t1) (h2 :: t2) = h1 :: h2 :: (alternate t1 t2).
Proof.
  intros h1 h2 t1 t2.
  unfold alternate.
  fold alternate.
  reflexivity.
Qed.

Lemma alternate_nil_l_case : forall (h1 : nat) (t1 : natlist),
  alternate [] (h1 :: t1) = (h1 :: t1).
Proof.
  intros h1 t1.
  unfold alternate.
  reflexivity.
Qed.

Lemma alternate_nil_r_case : forall (h1 : nat) (t1 : natlist),
  alternate (h1 :: t1) [] = (h1 :: t1).
Proof.
  intros h1 t1.
  unfold alternate.
  reflexivity.
Qed.

Example test_alternate1 : alternate [1,2,3] [4,5,6] = [1,4,2,5,3,6].
Proof. unfold alternate. reflexivity. Qed.
Example test_alternate2: alternate [1] [4,5,6] = [1,4,5,6].
Proof. unfold alternate. reflexivity. Qed.
Example test_alternate3: alternate [1,2,3] [4] = [1,4,2,3].
Proof. unfold alternate. reflexivity. Qed.
Example test_alternate4: alternate [] [20,30] = [20,30].
Proof. unfold alternate. reflexivity. Qed.

(* Bags via Lists *)

Definition bag := natlist.

(* Exercise: 3 stars (bag_functions) *)

Fixpoint count (v : nat) (s : bag) : nat :=
  match s with
    | nil => O
    | h :: t => match (beq_nat h v) with
                  | true => S (count v t)
                  | false => (count v t)
                end
    end.

Lemma unfold_count_inductive_case : forall (v h : nat) (t : bag),
  count v (h :: t) = match (beq_nat h v) with
                       | true => S (count v t)
                       | false => (count v t)
                     end.
Proof.
  intros v h t.
  unfold count.
  fold count.
  reflexivity.
Qed.

Example test_count1: count 1 [1,2,3,1,4,1] = 3.
Proof. unfold count. unfold beq_nat. reflexivity. Qed.
Example test_count2: count 6 [1,2,3,1,4,1] = 0.
Proof. unfold count. unfold beq_nat. reflexivity. Qed.

Definition sum : bag -> bag -> bag :=
  alternate.

Lemma sum_eq_alternate : sum = alternate.
Proof. unfold sum. reflexivity. Qed.

Example test_sum1: count 1 (sum [1,2,3] [1,4,1]) = 3.
Proof. unfold sum. unfold alternate. unfold count.
unfold beq_nat. reflexivity. Qed.

Definition add (v : nat) (s : bag) : bag :=
  app [v] s.

Example test_add1 : count 1 (add 1 [1,4,1]) = 3.
Proof. unfold add. unfold app. unfold count.
unfold beq_nat. reflexivity. Qed.
Example test_add2 : count 5 (add 1 [1,4,1]) = 0.
Proof. unfold add. unfold app. unfold count.
unfold beq_nat. reflexivity. Qed.

Definition member (v : nat) (s : bag) : bool :=
  blt_nat O (count v s).

Example test_member1 : member 1 [1,4,1] = true.
Proof. unfold member. unfold count. unfold beq_nat.
unfold blt_nat. unfold ble_nat. unfold beq_nat.
unfold negb. unfold andb. reflexivity. Qed.

Example test_member1' : member 1 [1,4,1] = true.
Proof. reflexivity. Qed.

Example test_member2 : member 2 [1,4,1] = false.
Proof. unfold member. unfold count. unfold beq_nat.
unfold blt_nat. unfold ble_nat. unfold beq_nat.
unfold negb. unfold andb. reflexivity. Qed.

Example test_member2' : member 2 [1,4,1] = false.
Proof. reflexivity. Qed.

(* Exercise: 3 stars, optional (bag_more_functions) *)

Fixpoint remove_one (v : nat) (s : bag) : bag :=
  match s with
    | nil => nil
    | h :: t => match (beq_nat v h) with
                  | true => t
                  | false => h :: (remove_one v t)
                end
  end.

Example test_remove_one1: count 5 (remove_one 5 [2,1,5,4,1]) = 0.
Proof. unfold remove_one. unfold beq_nat. unfold count. unfold beq_nat.
reflexivity. Qed.

Example test_remove_one2: count 5 (remove_one 5 [2,1,4,1]) = 0.
Proof. unfold remove_one. unfold beq_nat. unfold count. unfold beq_nat.
reflexivity. Qed.

Example test_remove_one3: count 4 (remove_one 5 [2,1,4,5,1,4]) = 2.
Proof. unfold remove_one. unfold beq_nat. unfold count. unfold beq_nat.
reflexivity. Qed.

Example test_remove_one4:
  count 5 (remove_one 5 [2,1,5,4,5,1,4]) = 1.
Proof. unfold remove_one. unfold beq_nat. unfold count. unfold beq_nat.
reflexivity. Qed.

Fixpoint remove_all (v : nat) (s : bag) : bag :=
  match s with
    | nil => nil
    | h :: t => match (beq_nat v h) with
                  | true => (remove_all v t)
                  | false => h :: (remove_all v t)
                end
  end.

Example test_remove_all1: count 5 (remove_all 5 [2,1,5,4,1]) = 0.
Proof. unfold remove_all. unfold beq_nat. unfold count. unfold beq_nat.
reflexivity. Qed.

Example test_remove_all2: count 5 (remove_all 5 [2,1,4,1]) = 0.
Proof. unfold remove_all. unfold beq_nat. unfold count. unfold beq_nat.
reflexivity. Qed.

Example test_remove_all3: count 4 (remove_all 5 [2,1,4,5,1,4]) = 2.
Proof. unfold remove_all. unfold beq_nat. unfold count. unfold beq_nat.
reflexivity. Qed.

Example test_remove_all4: count 5 (remove_all 5 [2,1,5,4,5,1,4,5,1,4]) = 0.
Proof. reflexivity. Qed.
(* From now on I won't do all the unfolding for Examples unless I feel it is
   important for the particular example. *)

Fixpoint subset (s1:bag) (s2:bag) : bool :=
  match s1 with
    | nil => true
    | h :: t => match member h s2 with
                  | true => (subset t (remove_one h s2))
                  | false => false
                end
    end.

Example test_subset1: subset [1,2] [2,1,4,1] = true.
Proof. reflexivity. Qed.

Example test_subset2: subset [1,2,2] [2,1,4,1] = false.
Proof.  reflexivity. Qed.

(* Exercise: 3 stars, recommended (bag_theorem) *)

(* Hmm, will simply show that count increment when another of the element type
   being looked for is added to the list. *)

Theorem bag_theorem : forall (v : nat) (s : bag),
  count v (add v s) = S (count v s).
Proof.
  intros v s.
  unfold add.
  unfold app.
  unfold count.
  fold count.
  rewrite <- beq_nat_refl.
  reflexivity.
Qed.

(* Reasoning about Lists *)

Theorem nil_app : forall (l : natlist),
  [] ++ l = l.
Proof.
  intro l.
  unfold app.
  reflexivity.
Qed.

Theorem tl_length_pred : forall (l : natlist),
  pred (length l) = length (tail l).
Proof.
  intro l.
  destruct l as [ | l' ].

  Case "l = nil".
  unfold tail.
  unfold length.
  unfold pred.
  reflexivity.

  Case "l = cons n l'".
  unfold tail.
  unfold pred.
  unfold length.
  reflexivity.
Qed.

(* Induction on Lists *)

Theorem app_ass : forall (l1 l2 l3 : natlist),
  (l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
Proof.
  intros l1 l2 l3.
  induction l1 as [ | n l1' ].

  Case "l1 = nil".
  unfold app.
  fold app.
  reflexivity.

  Case "l1 = cons n l1'".
  unfold app.
  fold app.
  rewrite -> IHl1'.
  reflexivity.
Qed.

Theorem app_length : forall (l1 l2 : natlist),
  length (l1 ++ l2) = (length l1) + (length l2).
Proof.
  intros l1 l2.
  induction l1 as [ | n l1' ].

  Case "l1 = nil".
  unfold app.
  unfold length.
  fold length.
  unfold plus.
  reflexivity.

  Case "l1 = cons n l1'".
  unfold app.
  fold app.
  unfold length.
  fold length.
  rewrite -> IHl1'.
  rewrite -> plus_Sn_m.
  reflexivity.
Qed.

Fixpoint snoc (l : natlist) (v : nat) : natlist :=
  match l with
    | nil => [v]
    | h :: t => h :: (snoc t v)
  end.

Fixpoint rev (l : natlist) : natlist :=
  match l with
    | nil => nil
    | h :: t => snoc (rev t) h
  end.

Example test_rev1: rev [1,2,3] = [3,2,1].
Proof. reflexivity. Qed.

Example test_rev2: rev nil = nil.
Proof. reflexivity. Qed.

Theorem length_snoc : forall (n : nat), forall (l : natlist),
  length (snoc l n) = S (length l).
Proof.
  intros n l.
  induction l as [ | n' l' ].

  Case "l = nil".
  unfold snoc.
  unfold length.
  reflexivity.

  Case "l = cons n' l'".
  unfold snoc.
  fold snoc.
  unfold length.
  fold length.
  rewrite -> IHl'.
  reflexivity.
Qed.

Theorem rev_length : forall (l : natlist),
  length (rev l) = length l.
Proof.
  intro l.
  induction l as [ | n l' ].

  Case "l = nil".
  unfold rev.
  unfold length.
  reflexivity.

  Case "l = cons n l'".
  unfold rev.
  fold rev.
  unfold length.
  fold length.
  rewrite -> length_snoc.
  rewrite -> IHl'.
  reflexivity.
Qed.

(* List Exercises, Part 1 *)

(* Exercise: 3 stars, recommended (list_exercises) *)

Theorem app_nil_end : forall (l : natlist),
  l ++ [] = l.
Proof.
  intro l.
  induction l as [ | n l' ].

  Case "l = nil".
  unfold app.
  reflexivity.

  Case "l = cons n l'".
  unfold app.
  fold app.
  rewrite -> IHl'.
  reflexivity.
Qed.

Lemma unfold_app_inductive_case : forall (h : nat) (t l : natlist),
  app (h :: t) l = h :: (app t l).
Proof.
  intros h t l.
  unfold app.
  fold app.
  reflexivity.
Qed.

Lemma unfold_snoc_inductive_case : forall (h n : nat) (t : natlist),
  snoc (h :: t) n = h :: (snoc t n).
Proof.
  intros h n l.
  unfold snoc.
  fold snoc.
  reflexivity.
Qed.

Lemma unfold_rev_inductive_case : forall (h : nat) (t : natlist),
  rev (h :: t) = snoc (rev t) h.
Proof.
  intros h t.
  unfold rev.
  fold rev.
  reflexivity.
Qed.

Lemma rev_snoc : forall (n : nat) (l : natlist),
  rev (snoc l n) = n :: (rev l).
Proof.
  intros n l.
  induction l as [ | n' l' ].
  unfold rev.
  unfold snoc.
  reflexivity.

  unfold snoc.
  fold snoc.
  unfold rev.
  fold rev.
  rewrite -> IHl'.
  unfold snoc.
  fold snoc.
  reflexivity.
Qed.

Theorem rev_involutive : forall (l : natlist),
  rev (rev l) = l.
Proof.
  intro l.
  induction l as [ | n l' ].

  Case "l = nil".
  unfold rev.
  reflexivity.

  Case "l = cons n l'".
  rewrite -> unfold_rev_inductive_case.
  rewrite -> rev_snoc.
  rewrite -> IHl'.
  reflexivity.
Qed.

Theorem snoc_append : forall (n : nat) (l : natlist),
  snoc l n = l ++ [n].
Proof.
  intros n l.
  induction l as [ | n' l' ].

  Case "l = nil".
  unfold app.
  unfold snoc.
  reflexivity.

  Case "l = cons n' l'".
  rewrite -> unfold_snoc_inductive_case.
  rewrite -> unfold_app_inductive_case.
  rewrite -> IHl'.
  reflexivity.
Qed.

Theorem distr_rev : forall (l1 l2 : natlist),
  rev (l1 ++ l2) = (rev l2) ++ (rev l1).
Proof.
  intros l1 l2.
  induction l1 as [ | n l' ].

  Case "l1 = nil".
  unfold rev.
  fold rev.
  rewrite -> app_nil_end.
  unfold app.
  reflexivity.

  Case "l2 = cons n l'".
  rewrite -> unfold_app_inductive_case.
  rewrite -> unfold_rev_inductive_case.
  rewrite -> snoc_append.
  rewrite -> IHl'.
  rewrite -> unfold_rev_inductive_case.
  rewrite -> snoc_append.
  rewrite -> app_ass.
  reflexivity.
Qed.

Theorem app_ass4 : forall (l1 l2 l3 l4 : natlist),
  l1 ++ (l2 ++ (l3 ++ l4)) = ((l1 ++ l2) ++ l3) ++ l4.
Proof.
  intros l1 l2 l3 l4.
  rewrite -> app_ass.
  rewrite -> app_ass.
  reflexivity.
Qed.

Lemma nonzeros_length : forall (l1 l2 : natlist),
  nonzeros (l1 ++ l2) = (nonzeros l1) ++ (nonzeros l2).
Proof.
  intros l1 l2.
  induction l1 as [ | n l1'].

  Case "l1 = nil".
  unfold app at 1.
  unfold nonzeros at 2.
  unfold app at 1.
  reflexivity.

  Case "l1 = cons n l1'".
  rewrite -> unfold_app_inductive_case.
  unfold nonzeros.
  fold nonzeros.
  rewrite -> IHl1'.
  case n.

  SCase "n = O".
  reflexivity.

  SCase "n = S n'".
  intros n'.
  reflexivity.
Qed.

(* List Exercises, Part 2 *)

(* Exercise: 2 stars, recommended (list_design) *)

Theorem cons_snoc : forall (h n : nat) (t : natlist),
  (cons h t) ++ [n] = [h] ++ (snoc t n).
Proof.
  intros h n t.
  rewrite -> unfold_app_inductive_case.
  rewrite -> unfold_app_inductive_case.
  rewrite -> snoc_append.
  unfold app at 2.
  reflexivity.
Qed.

(* Exercise: 2 stars, optional (bag_proofs) *)

Theorem count_member_nonzero : forall (s : bag),
  ble_nat 1 (count 1 (1 :: s)) = true.
Proof.
  intro s.
  induction s as [ | n s' ].

  Case "s = nil".
  unfold count.
  unfold beq_nat.
  unfold ble_nat.
  reflexivity.

  Case "s = cons n s'".
  unfold count.
  fold count.
  unfold beq_nat.
  fold beq_nat.
  case (beq_nat n 1).

  SCase "(beq_nat n 1) = true".
  unfold ble_nat.
  reflexivity.

  SCase "(beq_nat n 1) = false".
  unfold ble_nat.
  reflexivity.
Qed.

Theorem ble_nat_refl : forall n,
  ble_nat n n = true.
Proof.
  intro n.
  induction n as [ | n' ].

  Case "n = O".
  unfold ble_nat.
  reflexivity.

  Case "n = S n'".
  unfold ble_nat.
  fold ble_nat.
  rewrite -> IHn'.
  reflexivity.
Qed.

Theorem ble_n_Sn : forall n,
  ble_nat n (S n) = true.
Proof.
  intros n.
  induction n as [ | n' ].

  Case "n = O".
  unfold ble_nat.
  reflexivity.

  Case "n = S n'".
  unfold ble_nat.
  fold ble_nat.
  rewrite -> IHn'.
  reflexivity.
Qed.

Theorem remove_decreases_count : forall (s : bag),
  ble_nat (count O (remove_one O s)) (count O s) = true.
Proof.
  intro s.
  induction s as [ | n s' ].

  Case "s = nil".
  unfold remove_one.
  unfold count.
  unfold ble_nat.
  reflexivity.

  Case "s = cons n s'".
  unfold remove_one.
  fold remove_one.
  case (beq_nat 0 n).

  SCase "(beq_nat 0 n) = true".
  unfold count.
  fold count.
  case (beq_nat n 0).

  SSCase "(beq_nat n 0) = true".
  rewrite -> ble_n_Sn.
  reflexivity.

  SSCase "(beq_nat n 0) = false".
  rewrite -> ble_nat_refl.
  reflexivity.

  SCase "(beq_nat 0 n) = false".
  unfold count.
  fold count.
  case (beq_nat n 0).

  SSCase "(beq_nat n 0) = true".
  unfold count.
  fold count.
  unfold ble_nat.
  fold ble_nat.
  rewrite -> IHs'.
  reflexivity.

  SSCase "(beq_nat n 0) = false".
  rewrite -> IHs'.
  reflexivity.
Qed.
(* Hmm, a bit long, maybe it can be done simpler without all the subcases. *)

(* Exercise: 3 stars, optional (bag_count_sum) *)

Theorem bag_count_sum : forall (n : nat) (s1 s2 : bag),
  (count n s1) + (count n s2) = count n (alternate s1 s2).
Proof.
  intros n s1 s2.
  induction s1 as [ | n1' s1' ].

  Case "s1 = []".
  induction s2 as [ | n2' s2' ].

  SCase "s2 = []".
  simpl.
  reflexivity.

  SCase "s2 = n2' :: s2'".
  simpl.
  reflexivity.

  Case "s1 = n1' :: s1'".
  induction s2 as [ | n2' s2' ].

  SCase "s2 = []".
  simpl.
  rewrite -> plus_O_r.
  reflexivity.

  SCase "s2 = n2' :: s2'".
  Admitted. (* to be continued *)

(* Exercise: 4 stars, optional (rev_injective) *)

Theorem rev_injective : forall (l1 l2 : natlist), rev l1 = rev l2 -> l1 = l2.
Proof.
  intros l1 l2.
  intro H.
  rewrite <- rev_involutive.
  rewrite <- H.
  rewrite -> rev_involutive.
  reflexivity.
Qed.

(* Options *)

Inductive natoption : Type :=
  | Some : nat -> natoption
  | None : natoption.

Fixpoint index (n : nat) (l : natlist) : natoption :=
  match l with
    | nil => None
    | a :: l' => match beq_nat n O with
                   | true => Some a
                   | false => index (pred n) l'
                 end
    end.

Example test_index1 : index 0 [4,5,6,7] = Some 4.
Proof. reflexivity. Qed.
Example test_index2 : index 3 [4,5,6,7] = Some 7.
Proof. reflexivity. Qed.
Example test_index3 : index 10 [4,5,6,7] = None.
Proof. reflexivity. Qed.

Fixpoint index' (n : nat) (l : natlist) : natoption :=
  match l with
    | nil => None
    | a :: l' => if beq_nat n O then Some a else index (pred n) l'
  end.

Definition option_elim (d : nat) (o : natoption) : nat :=
  match o with
    | Some n' => n'
    | None => d
  end.

(* Exercise: 2 stars (hd_opt) *)

Definition hd_opt (l : natlist) : natoption :=
  match l with
    | nil => None
    | h :: t => Some h
  end.

Example test_hd_opt1 : hd_opt [] = None.
Proof. reflexivity. Qed.
Example test_hd_opt2 : hd_opt [1] = Some 1.
Proof. reflexivity. Qed.
Example test_hd_opt3 : hd_opt [5,6] = Some 5.
Proof. reflexivity. Qed.

(* Exercise: 2 stars, optional (option_elim_hd) *)

Theorem option_elim_hd : forall (l : natlist) (default : nat),
  hd default l = option_elim default (hd_opt l).
Proof.
  intros l default.
  induction l as [ | n l' ].

  Case "l = nil".
  unfold hd_opt.
  unfold option_elim.
  unfold hd.
  reflexivity.

  Case "l = cons n l'".
  unfold hd_opt.
  unfold option_elim.
  unfold hd.
  reflexivity.
Qed.

(* Exercise: 2 stars, recommended (beq_natlist) *)

Fixpoint beq_natlist (l1 l2 : natlist) : bool :=
  match l1, l2 with
    | nil, nil => true
    | (h1 :: t1), (h2 :: t2) => if beq_nat h1 h2 then (beq_natlist t1 t2) else false
    | _, _ => false
    end.

Example test_beq_natlist1 : (beq_natlist nil nil = true).
Proof. reflexivity. Qed.
Example test_beq_natlist2 : beq_natlist [1,2,3] [1,2,3] = true.
Proof. reflexivity. Qed.
Example test_beq_natlist3 : beq_natlist [1,2,3] [1,2,4] = false.
Proof. reflexivity. Qed.

Theorem beq_natlist_refl : forall (l : natlist),
  true = beq_natlist l l.
Proof.
  intro l.
  induction l as [ | n l' ].

  Case "l = nil".
  unfold beq_natlist.
  reflexivity.

  Case "l = cons n l'".
  unfold beq_natlist.
  fold beq_natlist.
  rewrite <- beq_nat_refl.
  rewrite <- IHl'.
  reflexivity.
Qed.

(* Extended Exercise: Dictionaries *)

Module Dictionary.

Inductive dictionary : Type :=
  | empty : dictionary
  | record : nat -> nat -> dictionary -> dictionary.

Definition insert (key value : nat) (d : dictionary) : dictionary :=
  (record key value d).

Fixpoint find (key : nat) (d : dictionary) : natoption :=
  match d with
    | empty => None
    | record k v d' => if (beq_nat k key) then (Some v) else (find key d')
  end.

(* Exercise: 1 star (dictionary_invariant1) *)

Theorem dictionary_invariant1 : forall (d : dictionary) (k v : nat),
  (find k (insert k v d)) = Some v.
Proof.
  intros d k v.
  induction d as [ | k' v' d' ].

  Case "d = empty".
  unfold insert.
  unfold find.
  rewrite <- beq_nat_refl.
  reflexivity.

  Case "d = (record k' v' d')".
  unfold insert.
  unfold find.
  fold find.
  rewrite <- beq_nat_refl.
  reflexivity.
Qed.

(* Exercise: 1 star (dictionary_invariant2) *)

(* took the liberity of writing 'beq_nat n m' instead of 'beq_nat m n' so I
   didn't have to prove beq_nat n m = beq_nat m n. *)
Theorem dictionary_invariant2 : forall (d : dictionary) (m n o : nat),
  (beq_nat n m) = false -> (find m d) = (find m (insert n o d)).
Proof.
  intros d m n o.
  intro H.
  induction d as [ | n' o' d' ].

  Case "d = empty".
  unfold insert.
  unfold find.
  rewrite -> H.
  reflexivity.

  Case "d = (record n' o' d')".
  unfold insert.
  unfold find.
  fold find.
  rewrite -> H.
  reflexivity.
Qed.

End Dictionary.

End NatList.

(* end-of-Lists.v *)