Inductive ty : Type :=
  | TBool  : ty
  | TArrow : ty -> ty -> ty.Inductive ty : Type :=
  | TBool  : ty
  | TArrow : ty -> ty -> ty.Inductive ty : Type :=
  | TBool  : ty
  | TArrow : ty -> ty -> ty.Instance eq_dec_ty (t1 t2 : ty) : Dec (t1 = t2).Inductive id : Type :=
  | Id : string -> id.Inductive id : Type :=
  | Id : string -> id.Instance eq_dec_id (x y : id) : Dec (x = y).Definition beq_id x y :=
  match x,y with
    | Id n1, Id n2 => if string_dec n1 n2 then true else false
  end.Inductive trm : Type :=
  | tvar : id -> trm
  | tapp : trm -> trm -> trm
  | tabs : id -> ty -> trm -> trm
  | ttrue : trm
  | tfalse : trm
  | tif : trm -> trm -> trm -> trm.Inductive trm : Type :=
  | tvar : id -> trm
  | tapp : trm -> trm -> trm
  | tabs : id -> ty -> trm -> trm
  | ttrue : trm
  | tfalse : trm
  | tif : trm -> trm -> trm -> trm.Inductive trm : Type :=
  | tvar : id -> trm
  | tapp : trm -> trm -> trm
  | tabs : id -> ty -> trm -> trm
  | ttrue : trm
  | tfalse : trm
  | tif : trm -> trm -> trm -> trm.Inductive trm : Type :=
  | tvar : id -> trm
  | tapp : trm -> trm -> trm
  | tabs : id -> ty -> trm -> trm
  | ttrue : trm
  | tfalse : trm
  | tif : trm -> trm -> trm -> trm.Inductive trm : Type :=
  | tvar : id -> trm
  | tapp : trm -> trm -> trm
  | tabs : id -> ty -> trm -> trm
  | ttrue : trm
  | tfalse : trm
  | tif : trm -> trm -> trm -> trm.Inductive trm : Type :=
  | tvar : id -> trm
  | tapp : trm -> trm -> trm
  | tabs : id -> ty -> trm -> trm
  | ttrue : trm
  | tfalse : trm
  | tif : trm -> trm -> trm -> trm.Inductive trm : Type :=
  | tvar : id -> trm
  | tapp : trm -> trm -> trm
  | tabs : id -> ty -> trm -> trm
  | ttrue : trm
  | tfalse : trm
  | tif : trm -> trm -> trm -> trm.Instance eq_dec_tm (t1 t2 : trm) : Dec (t1 = t2).(* Leo: TODO: lowercase x - name clash *)
Inductive value : trm -> Prop :=
  | v_abs : forall X T t,
      value (tabs X T t)
  | v_true :
      value ttrue
  | v_false :
      value tfalse.(* Leo: TODO: lowercase x - name clash *)
Inductive value : trm -> Prop :=
  | v_abs : forall X T t,
      value (tabs X T t)
  | v_true :
      value ttrue
  | v_false :
      value tfalse.(* Leo: TODO: lowercase x - name clash *)
Inductive value : trm -> Prop :=
  | v_abs : forall X T t,
      value (tabs X T t)
  | v_true :
      value ttrue
  | v_false :
      value tfalse.(* Leo: TODO: lowercase x - name clash *)
Inductive value : trm -> Prop :=
  | v_abs : forall X T t,
      value (tabs X T t)
  | v_true :
      value ttrue
  | v_false :
      value tfalse.Instance dec_value t : Dec (value t).Fixpoint subst (x:id) (s:trm) (t:trm) : trm :=
  match t with
  | tvar x' =>
      if beq_id x x' then s else t
  | tabs x' T t1 =>
      tabs x' T (if beq_id x x' then t1 else ([x:=s] t1))
  | tapp t1 t2 =>
      tapp ([x:=s] t1) ([x:=s] t2)
  | ttrue =>
      ttrue
  | tfalse =>
      tfalse
  | tif t1 t2 t3 =>
      tif ([x:=s] t1) ([x:=s] t2) ([x:=s] t3)
  end

where "'[' x ':=' s ']' t" := (subst x s t).Fixpoint subst (x:id) (s:trm) (t:trm) : trm :=
  match t with
  | tvar x' =>
      if beq_id x x' then s else t
  | tabs x' T t1 =>
      tabs x' T (if beq_id x x' then t1 else ([x:=s] t1))
  | tapp t1 t2 =>
      tapp ([x:=s] t1) ([x:=s] t2)
  | ttrue =>
      ttrue
  | tfalse =>
      tfalse
  | tif t1 t2 t3 =>
      tif ([x:=s] t1) ([x:=s] t2) ([x:=s] t3)
  end

where "'[' x ':=' s ']' t" := (subst x s t).Inductive step : trm -> trm -> Prop :=
  | ST_AppAbs : forall x T t12 v2,
         value v2 ->
         (tapp (tabs x T t12) v2) ===> [x:=v2]t12
  | ST_App1 : forall t1 t1' t2,
         t1 ===> t1' ->
         tapp t1 t2 ===> tapp t1' t2
  | ST_App2 : forall v1 t2 t2',
         value v1 ->
         t2 ===> t2' ->
         tapp v1 t2 ===> tapp v1  t2'
  | ST_IfTrue : forall t1 t2,
      (tif ttrue t1 t2) ===> t1
  | ST_IfFalse : forall t1 t2,
      (tif tfalse t1 t2) ===> t2
  | ST_If : forall t1 t1' t2 t3,
      t1 ===> t1' ->
      (tif t1 t2 t3) ===> (tif t1' t2 t3)

where "t1 '===>' t2" := (step t1 t2).Inductive step : trm -> trm -> Prop :=
  | ST_AppAbs : forall x T t12 v2,
         value v2 ->
         (tapp (tabs x T t12) v2) ===> [x:=v2]t12
  | ST_App1 : forall t1 t1' t2,
         t1 ===> t1' ->
         tapp t1 t2 ===> tapp t1' t2
  | ST_App2 : forall v1 t2 t2',
         value v1 ->
         t2 ===> t2' ->
         tapp v1 t2 ===> tapp v1  t2'
  | ST_IfTrue : forall t1 t2,
      (tif ttrue t1 t2) ===> t1
  | ST_IfFalse : forall t1 t2,
      (tif tfalse t1 t2) ===> t2
  | ST_If : forall t1 t1' t2 t3,
      t1 ===> t1' ->
      (tif t1 t2 t3) ===> (tif t1' t2 t3)

where "t1 '===>' t2" := (step t1 t2).Inductive step : trm -> trm -> Prop :=
  | ST_AppAbs : forall x T t12 v2,
         value v2 ->
         (tapp (tabs x T t12) v2) ===> [x:=v2]t12
  | ST_App1 : forall t1 t1' t2,
         t1 ===> t1' ->
         tapp t1 t2 ===> tapp t1' t2
  | ST_App2 : forall v1 t2 t2',
         value v1 ->
         t2 ===> t2' ->
         tapp v1 t2 ===> tapp v1  t2'
  | ST_IfTrue : forall t1 t2,
      (tif ttrue t1 t2) ===> t1
  | ST_IfFalse : forall t1 t2,
      (tif tfalse t1 t2) ===> t2
  | ST_If : forall t1 t1' t2 t3,
      t1 ===> t1' ->
      (tif t1 t2 t3) ===> (tif t1' t2 t3)

where "t1 '===>' t2" := (step t1 t2).Inductive step : trm -> trm -> Prop :=
  | ST_AppAbs : forall x T t12 v2,
         value v2 ->
         (tapp (tabs x T t12) v2) ===> [x:=v2]t12
  | ST_App1 : forall t1 t1' t2,
         t1 ===> t1' ->
         tapp t1 t2 ===> tapp t1' t2
  | ST_App2 : forall v1 t2 t2',
         value v1 ->
         t2 ===> t2' ->
         tapp v1 t2 ===> tapp v1  t2'
  | ST_IfTrue : forall t1 t2,
      (tif ttrue t1 t2) ===> t1
  | ST_IfFalse : forall t1 t2,
      (tif tfalse t1 t2) ===> t2
  | ST_If : forall t1 t1' t2 t3,
      t1 ===> t1' ->
      (tif t1 t2 t3) ===> (tif t1' t2 t3)

where "t1 '===>' t2" := (step t1 t2).Inductive step : trm -> trm -> Prop :=
  | ST_AppAbs : forall x T t12 v2,
         value v2 ->
         (tapp (tabs x T t12) v2) ===> [x:=v2]t12
  | ST_App1 : forall t1 t1' t2,
         t1 ===> t1' ->
         tapp t1 t2 ===> tapp t1' t2
  | ST_App2 : forall v1 t2 t2',
         value v1 ->
         t2 ===> t2' ->
         tapp v1 t2 ===> tapp v1  t2'
  | ST_IfTrue : forall t1 t2,
      (tif ttrue t1 t2) ===> t1
  | ST_IfFalse : forall t1 t2,
      (tif tfalse t1 t2) ===> t2
  | ST_If : forall t1 t1' t2 t3,
      t1 ===> t1' ->
      (tif t1 t2 t3) ===> (tif t1' t2 t3)

where "t1 '===>' t2" := (step t1 t2).Inductive step : trm -> trm -> Prop :=
  | ST_AppAbs : forall x T t12 v2,
         value v2 ->
         (tapp (tabs x T t12) v2) ===> [x:=v2]t12
  | ST_App1 : forall t1 t1' t2,
         t1 ===> t1' ->
         tapp t1 t2 ===> tapp t1' t2
  | ST_App2 : forall v1 t2 t2',
         value v1 ->
         t2 ===> t2' ->
         tapp v1 t2 ===> tapp v1  t2'
  | ST_IfTrue : forall t1 t2,
      (tif ttrue t1 t2) ===> t1
  | ST_IfFalse : forall t1 t2,
      (tif tfalse t1 t2) ===> t2
  | ST_If : forall t1 t1' t2 t3,
      t1 ===> t1' ->
      (tif t1 t2 t3) ===> (tif t1' t2 t3)

where "t1 '===>' t2" := (step t1 t2).Inductive step : trm -> trm -> Prop :=
  | ST_AppAbs : forall x T t12 v2,
         value v2 ->
         (tapp (tabs x T t12) v2) ===> [x:=v2]t12
  | ST_App1 : forall t1 t1' t2,
         t1 ===> t1' ->
         tapp t1 t2 ===> tapp t1' t2
  | ST_App2 : forall v1 t2 t2',
         value v1 ->
         t2 ===> t2' ->
         tapp v1 t2 ===> tapp v1  t2'
  | ST_IfTrue : forall t1 t2,
      (tif ttrue t1 t2) ===> t1
  | ST_IfFalse : forall t1 t2,
      (tif tfalse t1 t2) ===> t2
  | ST_If : forall t1 t1' t2 t3,
      t1 ===> t1' ->
      (tif t1 t2 t3) ===> (tif t1' t2 t3)

where "t1 '===>' t2" := (step t1 t2).Inductive step : trm -> trm -> Prop :=
  | ST_AppAbs : forall x T t12 v2,
         value v2 ->
         (tapp (tabs x T t12) v2) ===> [x:=v2]t12
  | ST_App1 : forall t1 t1' t2,
         t1 ===> t1' ->
         tapp t1 t2 ===> tapp t1' t2
  | ST_App2 : forall v1 t2 t2',
         value v1 ->
         t2 ===> t2' ->
         tapp v1 t2 ===> tapp v1  t2'
  | ST_IfTrue : forall t1 t2,
      (tif ttrue t1 t2) ===> t1
  | ST_IfFalse : forall t1 t2,
      (tif tfalse t1 t2) ===> t2
  | ST_If : forall t1 t1' t2 t3,
      t1 ===> t1' ->
      (tif t1 t2 t3) ===> (tif t1' t2 t3)

where "t1 '===>' t2" := (step t1 t2).Notation multistep := (multi step).Notation "t1 '===>*' t2" := (multistep t1 t2) (at level 40).Definition context := list (id * ty).Definition empty : context := nil.Inductive bind : context -> id -> ty -> Prop :=
  | BindNow   : forall i a m, bind (cons (i, a) m) i a
  | BindLater : forall i i' a a' m',
                  ~ (i = i') ->
                  bind m' i a -> 
                  bind (cons (i',a') m') i a.Inductive bind : context -> id -> ty -> Prop :=
  | BindNow   : forall i a m, bind (cons (i, a) m) i a
  | BindLater : forall i i' a a' m',
                  ~ (i = i') ->
                  bind m' i a -> 
                  bind (cons (i',a') m') i a.Inductive bind : context -> id -> ty -> Prop :=
  | BindNow   : forall i a m, bind (cons (i, a) m) i a
  | BindLater : forall i i' a a' m',
                  ~ (i = i') ->
                  bind m' i a -> 
                  bind (cons (i',a') m') i a.(* KNOWN DUG. The derivation of SizedProofEqs for bind fails. *)
(* Derive SizedProofEqs for (fun x => bind m x a). *)
(* Derive GenSizedSuchThatCorrect for (fun x => bind m x a). *)

Instance adm_st m a : SuchThatCorrect (fun x => bind m x a) (genST (fun x => bind m x a)).Instance bind_dec m x v : Dec (bind m x v) :=
  {| dec := _ |}.Inductive has_type : context -> trm -> ty -> Prop :=
  | T_Var : forall Gamma i T,
      bind Gamma i T ->
      Gamma |- tvar i \typ T
  | T_Abs : forall Gamma i T11 T12 t12,
      cons (i, T11) Gamma |- t12 \typ T12 ->
      Gamma |- tabs i T11 t12 \typ TArrow T11 T12
  | T_App : forall T11 T12 Gamma t1 t2,
      Gamma |- t1 \typ TArrow T11 T12 ->
      Gamma |- t2 \typ T11 ->
      Gamma |- tapp t1 t2 \typ T12
  | T_True : forall Gamma,
       Gamma |- ttrue \typ TBool
  | T_False : forall Gamma,
       Gamma |- tfalse \typ TBool
  | T_If : forall t1 t2 t3 T Gamma,
       Gamma |- t1 \typ TBool ->
       Gamma |- t2 \typ T ->
       Gamma |- t3 \typ T ->
       Gamma |- tif t1 t2 t3 \typ T

where "Gamma '|-' t '\typ' T" := (has_type Gamma t T).Inductive has_type : context -> trm -> ty -> Prop :=
  | T_Var : forall Gamma i T,
      bind Gamma i T ->
      Gamma |- tvar i \typ T
  | T_Abs : forall Gamma i T11 T12 t12,
      cons (i, T11) Gamma |- t12 \typ T12 ->
      Gamma |- tabs i T11 t12 \typ TArrow T11 T12
  | T_App : forall T11 T12 Gamma t1 t2,
      Gamma |- t1 \typ TArrow T11 T12 ->
      Gamma |- t2 \typ T11 ->
      Gamma |- tapp t1 t2 \typ T12
  | T_True : forall Gamma,
       Gamma |- ttrue \typ TBool
  | T_False : forall Gamma,
       Gamma |- tfalse \typ TBool
  | T_If : forall t1 t2 t3 T Gamma,
       Gamma |- t1 \typ TBool ->
       Gamma |- t2 \typ T ->
       Gamma |- t3 \typ T ->
       Gamma |- tif t1 t2 t3 \typ T

where "Gamma '|-' t '\typ' T" := (has_type Gamma t T).Inductive has_type : context -> trm -> ty -> Prop :=
  | T_Var : forall Gamma i T,
      bind Gamma i T ->
      Gamma |- tvar i \typ T
  | T_Abs : forall Gamma i T11 T12 t12,
      cons (i, T11) Gamma |- t12 \typ T12 ->
      Gamma |- tabs i T11 t12 \typ TArrow T11 T12
  | T_App : forall T11 T12 Gamma t1 t2,
      Gamma |- t1 \typ TArrow T11 T12 ->
      Gamma |- t2 \typ T11 ->
      Gamma |- tapp t1 t2 \typ T12
  | T_True : forall Gamma,
       Gamma |- ttrue \typ TBool
  | T_False : forall Gamma,
       Gamma |- tfalse \typ TBool
  | T_If : forall t1 t2 t3 T Gamma,
       Gamma |- t1 \typ TBool ->
       Gamma |- t2 \typ T ->
       Gamma |- t3 \typ T ->
       Gamma |- tif t1 t2 t3 \typ T

where "Gamma '|-' t '\typ' T" := (has_type Gamma t T).Inductive has_type : context -> trm -> ty -> Prop :=
  | T_Var : forall Gamma i T,
      bind Gamma i T ->
      Gamma |- tvar i \typ T
  | T_Abs : forall Gamma i T11 T12 t12,
      cons (i, T11) Gamma |- t12 \typ T12 ->
      Gamma |- tabs i T11 t12 \typ TArrow T11 T12
  | T_App : forall T11 T12 Gamma t1 t2,
      Gamma |- t1 \typ TArrow T11 T12 ->
      Gamma |- t2 \typ T11 ->
      Gamma |- tapp t1 t2 \typ T12
  | T_True : forall Gamma,
       Gamma |- ttrue \typ TBool
  | T_False : forall Gamma,
       Gamma |- tfalse \typ TBool
  | T_If : forall t1 t2 t3 T Gamma,
       Gamma |- t1 \typ TBool ->
       Gamma |- t2 \typ T ->
       Gamma |- t3 \typ T ->
       Gamma |- tif t1 t2 t3 \typ T

where "Gamma '|-' t '\typ' T" := (has_type Gamma t T).Inductive has_type : context -> trm -> ty -> Prop :=
  | T_Var : forall Gamma i T,
      bind Gamma i T ->
      Gamma |- tvar i \typ T
  | T_Abs : forall Gamma i T11 T12 t12,
      cons (i, T11) Gamma |- t12 \typ T12 ->
      Gamma |- tabs i T11 t12 \typ TArrow T11 T12
  | T_App : forall T11 T12 Gamma t1 t2,
      Gamma |- t1 \typ TArrow T11 T12 ->
      Gamma |- t2 \typ T11 ->
      Gamma |- tapp t1 t2 \typ T12
  | T_True : forall Gamma,
       Gamma |- ttrue \typ TBool
  | T_False : forall Gamma,
       Gamma |- tfalse \typ TBool
  | T_If : forall t1 t2 t3 T Gamma,
       Gamma |- t1 \typ TBool ->
       Gamma |- t2 \typ T ->
       Gamma |- t3 \typ T ->
       Gamma |- tif t1 t2 t3 \typ T

where "Gamma '|-' t '\typ' T" := (has_type Gamma t T).Inductive has_type : context -> trm -> ty -> Prop :=
  | T_Var : forall Gamma i T,
      bind Gamma i T ->
      Gamma |- tvar i \typ T
  | T_Abs : forall Gamma i T11 T12 t12,
      cons (i, T11) Gamma |- t12 \typ T12 ->
      Gamma |- tabs i T11 t12 \typ TArrow T11 T12
  | T_App : forall T11 T12 Gamma t1 t2,
      Gamma |- t1 \typ TArrow T11 T12 ->
      Gamma |- t2 \typ T11 ->
      Gamma |- tapp t1 t2 \typ T12
  | T_True : forall Gamma,
       Gamma |- ttrue \typ TBool
  | T_False : forall Gamma,
       Gamma |- tfalse \typ TBool
  | T_If : forall t1 t2 t3 T Gamma,
       Gamma |- t1 \typ TBool ->
       Gamma |- t2 \typ T ->
       Gamma |- t3 \typ T ->
       Gamma |- tif t1 t2 t3 \typ T

where "Gamma '|-' t '\typ' T" := (has_type Gamma t T).Inductive has_type : context -> trm -> ty -> Prop :=
  | T_Var : forall Gamma i T,
      bind Gamma i T ->
      Gamma |- tvar i \typ T
  | T_Abs : forall Gamma i T11 T12 t12,
      cons (i, T11) Gamma |- t12 \typ T12 ->
      Gamma |- tabs i T11 t12 \typ TArrow T11 T12
  | T_App : forall T11 T12 Gamma t1 t2,
      Gamma |- t1 \typ TArrow T11 T12 ->
      Gamma |- t2 \typ T11 ->
      Gamma |- tapp t1 t2 \typ T12
  | T_True : forall Gamma,
       Gamma |- ttrue \typ TBool
  | T_False : forall Gamma,
       Gamma |- tfalse \typ TBool
  | T_If : forall t1 t2 t3 T Gamma,
       Gamma |- t1 \typ TBool ->
       Gamma |- t2 \typ T ->
       Gamma |- t3 \typ T ->
       Gamma |- tif t1 t2 t3 \typ T

where "Gamma '|-' t '\typ' T" := (has_type Gamma t T).Inductive has_type : context -> trm -> ty -> Prop :=
  | T_Var : forall Gamma i T,
      bind Gamma i T ->
      Gamma |- tvar i \typ T
  | T_Abs : forall Gamma i T11 T12 t12,
      cons (i, T11) Gamma |- t12 \typ T12 ->
      Gamma |- tabs i T11 t12 \typ TArrow T11 T12
  | T_App : forall T11 T12 Gamma t1 t2,
      Gamma |- t1 \typ TArrow T11 T12 ->
      Gamma |- t2 \typ T11 ->
      Gamma |- tapp t1 t2 \typ T12
  | T_True : forall Gamma,
       Gamma |- ttrue \typ TBool
  | T_False : forall Gamma,
       Gamma |- tfalse \typ TBool
  | T_If : forall t1 t2 t3 T Gamma,
       Gamma |- t1 \typ TBool ->
       Gamma |- t2 \typ T ->
       Gamma |- t3 \typ T ->
       Gamma |- tif t1 t2 t3 \typ T

where "Gamma '|-' t '\typ' T" := (has_type Gamma t T).Instance has_type_gen_correct0 Gamma T : SuchThatCorrect (fun t => has_type Gamma t T) 
                                                         (@arbitraryST _ (fun t => has_type Gamma t T) _).Fixpoint lookup (Gamma : context) (i : id) : option ty :=
  match Gamma with 
  | nil => None
  | cons (i',T) Gamma' => 
    if beq_id i i' then Some T
    else lookup Gamma' i
  end.

(* Specifications: *)
Theorem beq_id_true_iff : forall x y : id, beq_id x y = true <-> x = y.
Theorem beq_id_false_iff : forall x y : id, beq_id x y = false <-> x <> y.
Lemma lookup_bind : forall Gamma i T, lookup Gamma i = Some T <-> bind Gamma i T.