Require Import List.

Inductive pure_term : Type :=
  | pure_var : pure_term
  | pure_app : pure_term pure_term pure_term
  | pure_abs : pure_term pure_term.

Inductive poly_type : Type :=
  | poly_var : poly_type
  | poly_arr : poly_type poly_type poly_type
  | poly_abs : poly_type poly_type.

Definition environment := list poly_type.

Definition funcomp {X Y Z} (g : Y Z) (f : X Y) :=
   x g (f x).

Definition scons {X: Type} (x : X) (xi : X) :=
   n match n with | 0 x | S n n end.

Fixpoint ren_poly_type (xi : ) (s : poly_type) : poly_type :=
  match s return poly_type with
  | poly_var x poly_var ( x)
  | poly_arr s t poly_arr (ren_poly_type s) (ren_poly_type t)
  | poly_abs s poly_abs (ren_poly_type (scons 0 (funcomp S )) s)
  end.

Fixpoint subst_poly_type (sigma : poly_type) (s : poly_type) : poly_type :=
  match s return poly_type with
  | poly_var s s
  | poly_arr s t poly_arr (subst_poly_type s) (subst_poly_type t)
  | poly_abs s poly_abs (subst_poly_type (scons (poly_var 0) (funcomp (ren_poly_type S) )) s)
  end.

Inductive type_assignment : environment pure_term poly_type Prop :=
| type_assignment_var {Gamma x t} :
    nth_error x = Some t
    type_assignment (pure_var x) t
| type_assignment_app {Gamma M N s t} :
    type_assignment M (poly_arr s t) type_assignment N s
    type_assignment (pure_app M N) t
| type_assignment_abs {Gamma M s t} :
    type_assignment (s :: ) M t
    type_assignment (pure_abs M) (poly_arr s t)
| type_assignment_inst {Gamma M s t} :
    type_assignment M (poly_abs s)
    type_assignment M (subst_poly_type (scons t poly_var) s)
| type_assignment_gen {Gamma M s} :
    type_assignment (map (ren_poly_type S) ) M s
    type_assignment M (poly_abs s).

Definition SysF_TC : environment * pure_term * poly_type Prop :=
   '(Gamma, M, t) type_assignment M t.

Definition SysF_TYP : pure_term Prop :=
   M Gamma t, type_assignment M t.

Definition SysF_INH : environment * poly_type Prop :=
   '(Gamma, t) M, type_assignment M t.