(************************************************************************)
(*         *      The Rocq Prover / The Rocq Development Team           *)
(*  v      *         Copyright INRIA, CNRS and contributors             *)
(* <O___,, * (see version control and CREDITS file for authors & dates) *)
(*   \VV/  **************************************************************)
(*    //   *    This file is distributed under the terms of the         *)
(*         *     GNU Lesser General Public License Version 2.1          *)
(*         *     (see LICENSE file for the text of the license)         *)
(************************************************************************)

(** An alternative to [zify] in ML parametrised by user-provided classes instances.

    The framework has currently several limitations that are in place for simplicity.
    For instance, we only consider binary operators of type [Op: S -> S -> S].
    Another limitation is that our injection theorems e.g. [TBOpInj],
    are using Leibniz equality; the payoff is that there is no need for morphisms...
 *)

(** An injection [InjTyp S T] declares an injection
    from source type S to target type T.
*)
Class InjTyp (S : Type) (T : Type) :=
  mkinj {
      (* [inj] is the injection function *)
      inj  : S -> T;
      pred : T -> Prop;
      (* [cstr] states that [pred] holds for any injected element.
         [cstr (inj x)] is introduced in the goal for any leaf
         term of the form [inj x]
       *)
      cstr : forall x, pred (inj x)
    }.(************************************************************************)
(*         *      The Rocq Prover / The Rocq Development Team           *)
(*  v      *         Copyright INRIA, CNRS and contributors             *)
(* <O___,, * (see version control and CREDITS file for authors & dates) *)
(*   \VV/  **************************************************************)
(*    //   *    This file is distributed under the terms of the         *)
(*         *     GNU Lesser General Public License Version 2.1          *)
(*         *     (see LICENSE file for the text of the license)         *)
(************************************************************************)

(** An alternative to [zify] in ML parametrised by user-provided classes instances.

    The framework has currently several limitations that are in place for simplicity.
    For instance, we only consider binary operators of type [Op: S -> S -> S].
    Another limitation is that our injection theorems e.g. [TBOpInj],
    are using Leibniz equality; the payoff is that there is no need for morphisms...
 *)

(** An injection [InjTyp S T] declares an injection
    from source type S to target type T.
*)
Class InjTyp (S : Type) (T : Type) :=
  mkinj {
      (* [inj] is the injection function *)
      inj  : S -> T;
      pred : T -> Prop;
      (* [cstr] states that [pred] holds for any injected element.
         [cstr (inj x)] is introduced in the goal for any leaf
         term of the form [inj x]
       *)
      cstr : forall x, pred (inj x)
    }.(************************************************************************)
(*         *      The Rocq Prover / The Rocq Development Team           *)
(*  v      *         Copyright INRIA, CNRS and contributors             *)
(* <O___,, * (see version control and CREDITS file for authors & dates) *)
(*   \VV/  **************************************************************)
(*    //   *    This file is distributed under the terms of the         *)
(*         *     GNU Lesser General Public License Version 2.1          *)
(*         *     (see LICENSE file for the text of the license)         *)
(************************************************************************)

(** An alternative to [zify] in ML parametrised by user-provided classes instances.

    The framework has currently several limitations that are in place for simplicity.
    For instance, we only consider binary operators of type [Op: S -> S -> S].
    Another limitation is that our injection theorems e.g. [TBOpInj],
    are using Leibniz equality; the payoff is that there is no need for morphisms...
 *)

(** An injection [InjTyp S T] declares an injection
    from source type S to target type T.
*)
Class InjTyp (S : Type) (T : Type) :=
  mkinj {
      (* [inj] is the injection function *)
      inj  : S -> T;
      pred : T -> Prop;
      (* [cstr] states that [pred] holds for any injected element.
         [cstr (inj x)] is introduced in the goal for any leaf
         term of the form [inj x]
       *)
      cstr : forall x, pred (inj x)
    }.(************************************************************************)
(*         *      The Rocq Prover / The Rocq Development Team           *)
(*  v      *         Copyright INRIA, CNRS and contributors             *)
(* <O___,, * (see version control and CREDITS file for authors & dates) *)
(*   \VV/  **************************************************************)
(*    //   *    This file is distributed under the terms of the         *)
(*         *     GNU Lesser General Public License Version 2.1          *)
(*         *     (see LICENSE file for the text of the license)         *)
(************************************************************************)

(** An alternative to [zify] in ML parametrised by user-provided classes instances.

    The framework has currently several limitations that are in place for simplicity.
    For instance, we only consider binary operators of type [Op: S -> S -> S].
    Another limitation is that our injection theorems e.g. [TBOpInj],
    are using Leibniz equality; the payoff is that there is no need for morphisms...
 *)

(** An injection [InjTyp S T] declares an injection
    from source type S to target type T.
*)
Class InjTyp (S : Type) (T : Type) :=
  mkinj {
      (* [inj] is the injection function *)
      inj  : S -> T;
      pred : T -> Prop;
      (* [cstr] states that [pred] holds for any injected element.
         [cstr (inj x)] is introduced in the goal for any leaf
         term of the form [inj x]
       *)
      cstr : forall x, pred (inj x)
    }.(************************************************************************)
(*         *      The Rocq Prover / The Rocq Development Team           *)
(*  v      *         Copyright INRIA, CNRS and contributors             *)
(* <O___,, * (see version control and CREDITS file for authors & dates) *)
(*   \VV/  **************************************************************)
(*    //   *    This file is distributed under the terms of the         *)
(*         *     GNU Lesser General Public License Version 2.1          *)
(*         *     (see LICENSE file for the text of the license)         *)
(************************************************************************)

(** An alternative to [zify] in ML parametrised by user-provided classes instances.

    The framework has currently several limitations that are in place for simplicity.
    For instance, we only consider binary operators of type [Op: S -> S -> S].
    Another limitation is that our injection theorems e.g. [TBOpInj],
    are using Leibniz equality; the payoff is that there is no need for morphisms...
 *)

(** An injection [InjTyp S T] declares an injection
    from source type S to target type T.
*)
Class InjTyp (S : Type) (T : Type) :=
  mkinj {
      (* [inj] is the injection function *)
      inj  : S -> T;
      pred : T -> Prop;
      (* [cstr] states that [pred] holds for any injected element.
         [cstr (inj x)] is introduced in the goal for any leaf
         term of the form [inj x]
       *)
      cstr : forall x, pred (inj x)
    }.(** [BinOp Op] declares a source operator [Op: S1 -> S2 -> S3].
 *)
Class BinOp {S1 S2 S3 T1 T2 T3:Type} (Op : S1 -> S2 -> S3) {I1 : InjTyp S1 T1} {I2 : InjTyp S2 T2} {I3 : InjTyp S3 T3} :=
  mkbop {
      (* [TBOp] is the target operator after injection of operands. *)
      TBOp : T1 -> T2 -> T3;
      (* [TBOpInj] states the correctness of the injection. *)
      TBOpInj : forall (n:S1) (m:S2), inj (Op n m) = TBOp (inj n) (inj m)
    }.(** [BinOp Op] declares a source operator [Op: S1 -> S2 -> S3].
 *)
Class BinOp {S1 S2 S3 T1 T2 T3:Type} (Op : S1 -> S2 -> S3) {I1 : InjTyp S1 T1} {I2 : InjTyp S2 T2} {I3 : InjTyp S3 T3} :=
  mkbop {
      (* [TBOp] is the target operator after injection of operands. *)
      TBOp : T1 -> T2 -> T3;
      (* [TBOpInj] states the correctness of the injection. *)
      TBOpInj : forall (n:S1) (m:S2), inj (Op n m) = TBOp (inj n) (inj m)
    }.(** [BinOp Op] declares a source operator [Op: S1 -> S2 -> S3].
 *)
Class BinOp {S1 S2 S3 T1 T2 T3:Type} (Op : S1 -> S2 -> S3) {I1 : InjTyp S1 T1} {I2 : InjTyp S2 T2} {I3 : InjTyp S3 T3} :=
  mkbop {
      (* [TBOp] is the target operator after injection of operands. *)
      TBOp : T1 -> T2 -> T3;
      (* [TBOpInj] states the correctness of the injection. *)
      TBOpInj : forall (n:S1) (m:S2), inj (Op n m) = TBOp (inj n) (inj m)
    }.(** [BinOp Op] declares a source operator [Op: S1 -> S2 -> S3].
 *)
Class BinOp {S1 S2 S3 T1 T2 T3:Type} (Op : S1 -> S2 -> S3) {I1 : InjTyp S1 T1} {I2 : InjTyp S2 T2} {I3 : InjTyp S3 T3} :=
  mkbop {
      (* [TBOp] is the target operator after injection of operands. *)
      TBOp : T1 -> T2 -> T3;
      (* [TBOpInj] states the correctness of the injection. *)
      TBOpInj : forall (n:S1) (m:S2), inj (Op n m) = TBOp (inj n) (inj m)
    }.(** [Unop Op] declares a source operator [Op : S1 -> S2]. *)
Class UnOp {S1 S2 T1 T2:Type} (Op : S1 -> S2) {I1 : InjTyp S1 T1} {I2 : InjTyp S2 T2}  :=
  mkuop {
      (* [TUOp] is the target operator after injection of operands. *)
      TUOp : T1 -> T2;
      (* [TUOpInj] states the correctness of the injection. *)
      TUOpInj : forall (x:S1), inj (Op x) = TUOp (inj x)
    }.(** [Unop Op] declares a source operator [Op : S1 -> S2]. *)
Class UnOp {S1 S2 T1 T2:Type} (Op : S1 -> S2) {I1 : InjTyp S1 T1} {I2 : InjTyp S2 T2}  :=
  mkuop {
      (* [TUOp] is the target operator after injection of operands. *)
      TUOp : T1 -> T2;
      (* [TUOpInj] states the correctness of the injection. *)
      TUOpInj : forall (x:S1), inj (Op x) = TUOp (inj x)
    }.(** [Unop Op] declares a source operator [Op : S1 -> S2]. *)
Class UnOp {S1 S2 T1 T2:Type} (Op : S1 -> S2) {I1 : InjTyp S1 T1} {I2 : InjTyp S2 T2}  :=
  mkuop {
      (* [TUOp] is the target operator after injection of operands. *)
      TUOp : T1 -> T2;
      (* [TUOpInj] states the correctness of the injection. *)
      TUOpInj : forall (x:S1), inj (Op x) = TUOp (inj x)
    }.(** [Unop Op] declares a source operator [Op : S1 -> S2]. *)
Class UnOp {S1 S2 T1 T2:Type} (Op : S1 -> S2) {I1 : InjTyp S1 T1} {I2 : InjTyp S2 T2}  :=
  mkuop {
      (* [TUOp] is the target operator after injection of operands. *)
      TUOp : T1 -> T2;
      (* [TUOpInj] states the correctness of the injection. *)
      TUOpInj : forall (x:S1), inj (Op x) = TUOp (inj x)
    }.(** [CstOp Op] declares a source constant [Op : S]. *)
Class CstOp {S T:Type} (Op : S) {I : InjTyp S T} :=
  mkcst {
      (* [TCst] is the target constant. *)
      TCst : T;
      (* [TCstInj] states the correctness of the injection. *)
      TCstInj : inj Op = TCst
    }.(** [CstOp Op] declares a source constant [Op : S]. *)
Class CstOp {S T:Type} (Op : S) {I : InjTyp S T} :=
  mkcst {
      (* [TCst] is the target constant. *)
      TCst : T;
      (* [TCstInj] states the correctness of the injection. *)
      TCstInj : inj Op = TCst
    }.(** [CstOp Op] declares a source constant [Op : S]. *)
Class CstOp {S T:Type} (Op : S) {I : InjTyp S T} :=
  mkcst {
      (* [TCst] is the target constant. *)
      TCst : T;
      (* [TCstInj] states the correctness of the injection. *)
      TCstInj : inj Op = TCst
    }.(** [CstOp Op] declares a source constant [Op : S]. *)
Class CstOp {S T:Type} (Op : S) {I : InjTyp S T} :=
  mkcst {
      (* [TCst] is the target constant. *)
      TCst : T;
      (* [TCstInj] states the correctness of the injection. *)
      TCstInj : inj Op = TCst
    }.(** In the framework, [Prop] is mapped to [Prop] and the injection is phrased in
    terms  of [=] instead of [<->].
*)

(** [BinRel R] declares the injection of a binary relation. *)
Class BinRel {S:Type} {T:Type} (R : S -> S -> Prop) {I : InjTyp S T}  :=
    mkbrel {
        TR : T -> T -> Prop;
        TRInj : forall n m : S, R n m <->  TR (@inj _ _ I n) (inj m)
      }.(** In the framework, [Prop] is mapped to [Prop] and the injection is phrased in
    terms  of [=] instead of [<->].
*)

(** [BinRel R] declares the injection of a binary relation. *)
Class BinRel {S:Type} {T:Type} (R : S -> S -> Prop) {I : InjTyp S T}  :=
    mkbrel {
        TR : T -> T -> Prop;
        TRInj : forall n m : S, R n m <->  TR (@inj _ _ I n) (inj m)
      }.(** In the framework, [Prop] is mapped to [Prop] and the injection is phrased in
    terms  of [=] instead of [<->].
*)

(** [BinRel R] declares the injection of a binary relation. *)
Class BinRel {S:Type} {T:Type} (R : S -> S -> Prop) {I : InjTyp S T}  :=
    mkbrel {
        TR : T -> T -> Prop;
        TRInj : forall n m : S, R n m <->  TR (@inj _ _ I n) (inj m)
      }.(** In the framework, [Prop] is mapped to [Prop] and the injection is phrased in
    terms  of [=] instead of [<->].
*)

(** [BinRel R] declares the injection of a binary relation. *)
Class BinRel {S:Type} {T:Type} (R : S -> S -> Prop) {I : InjTyp S T}  :=
    mkbrel {
        TR : T -> T -> Prop;
        TRInj : forall n m : S, R n m <->  TR (@inj _ _ I n) (inj m)
      }.(** [PropOp Op] declares morphisms for [<->].
    This will be used to deal with e.g. [and], [or],... *)

Class PropOp (Op : Prop -> Prop -> Prop) :=
  mkprop {
      op_iff : forall (p1 p2 q1 q2:Prop), (p1 <-> q1) -> (p2 <-> q2) -> (Op p1 p2 <-> Op q1 q2)
    }.(** [PropOp Op] declares morphisms for [<->].
    This will be used to deal with e.g. [and], [or],... *)

Class PropOp (Op : Prop -> Prop -> Prop) :=
  mkprop {
      op_iff : forall (p1 p2 q1 q2:Prop), (p1 <-> q1) -> (p2 <-> q2) -> (Op p1 p2 <-> Op q1 q2)
    }.(** [PropOp Op] declares morphisms for [<->].
    This will be used to deal with e.g. [and], [or],... *)

Class PropOp (Op : Prop -> Prop -> Prop) :=
  mkprop {
      op_iff : forall (p1 p2 q1 q2:Prop), (p1 <-> q1) -> (p2 <-> q2) -> (Op p1 p2 <-> Op q1 q2)
    }.Class PropUOp (Op : Prop -> Prop) :=
  mkuprop {
      uop_iff :   forall (p1 q1 :Prop), (p1 <-> q1) -> (Op p1 <-> Op q1)
    }.Class PropUOp (Op : Prop -> Prop) :=
  mkuprop {
      uop_iff :   forall (p1 q1 :Prop), (p1 <-> q1) -> (Op p1 <-> Op q1)
    }.Class PropUOp (Op : Prop -> Prop) :=
  mkuprop {
      uop_iff :   forall (p1 q1 :Prop), (p1 <-> q1) -> (Op p1 <-> Op q1)
    }.(** Once the term is injected, terms can be replaced by their specification.
    NB1: The Ltac code is currently limited to (Op: Z -> Z -> Z)
    NB2: This is not sufficient to cope with [Z.div] or [Z.mod]
 *)
Class BinOpSpec {T1 T2 T3: Type} (Op : T1 -> T2 -> T3)  :=
  mkbspec {
      BPred : T1 -> T2 -> T3 -> Prop;
      BSpec : forall x y, BPred x y (Op x y)
    }.(** Once the term is injected, terms can be replaced by their specification.
    NB1: The Ltac code is currently limited to (Op: Z -> Z -> Z)
    NB2: This is not sufficient to cope with [Z.div] or [Z.mod]
 *)
Class BinOpSpec {T1 T2 T3: Type} (Op : T1 -> T2 -> T3)  :=
  mkbspec {
      BPred : T1 -> T2 -> T3 -> Prop;
      BSpec : forall x y, BPred x y (Op x y)
    }.(** Once the term is injected, terms can be replaced by their specification.
    NB1: The Ltac code is currently limited to (Op: Z -> Z -> Z)
    NB2: This is not sufficient to cope with [Z.div] or [Z.mod]
 *)
Class BinOpSpec {T1 T2 T3: Type} (Op : T1 -> T2 -> T3)  :=
  mkbspec {
      BPred : T1 -> T2 -> T3 -> Prop;
      BSpec : forall x y, BPred x y (Op x y)
    }.(** Once the term is injected, terms can be replaced by their specification.
    NB1: The Ltac code is currently limited to (Op: Z -> Z -> Z)
    NB2: This is not sufficient to cope with [Z.div] or [Z.mod]
 *)
Class BinOpSpec {T1 T2 T3: Type} (Op : T1 -> T2 -> T3)  :=
  mkbspec {
      BPred : T1 -> T2 -> T3 -> Prop;
      BSpec : forall x y, BPred x y (Op x y)
    }.Class UnOpSpec {T1 T2: Type} (Op : T1 -> T2)  :=
  mkuspec {
      UPred : T1 -> T2 -> Prop;
      USpec : forall x, UPred x (Op x)
    }.Class UnOpSpec {T1 T2: Type} (Op : T1 -> T2)  :=
  mkuspec {
      UPred : T1 -> T2 -> Prop;
      USpec : forall x, UPred x (Op x)
    }.Class UnOpSpec {T1 T2: Type} (Op : T1 -> T2)  :=
  mkuspec {
      UPred : T1 -> T2 -> Prop;
      USpec : forall x, UPred x (Op x)
    }.Class UnOpSpec {T1 T2: Type} (Op : T1 -> T2)  :=
  mkuspec {
      UPred : T1 -> T2 -> Prop;
      USpec : forall x, UPred x (Op x)
    }.(** After injections, e.g. nat -> Z,
    the fact that Z.of_nat x * Z.of_nat y is positive is lost.
    This information can be recovered using instance of the  [Saturate] class.
*)
Class Saturate {T: Type} (Op : T -> T -> T) :=
  mksat {
      (** Given [Op x y],
          - [PArg1] is the pre-condition of x
          - [PArg2] is the pre-condition of y
          - [PRes]  is the pos-condition of (Op x y) *)
      PArg1 : T -> Prop;
      PArg2 : T -> Prop;
      PRes  : T -> T -> T -> Prop;
      (** [SatOk] states the correctness of the reasoning *)
      SatOk : forall x y, PArg1 x -> PArg2 y -> PRes x y (Op x y)
    }.(** After injections, e.g. nat -> Z,
    the fact that Z.of_nat x * Z.of_nat y is positive is lost.
    This information can be recovered using instance of the  [Saturate] class.
*)
Class Saturate {T: Type} (Op : T -> T -> T) :=
  mksat {
      (** Given [Op x y],
          - [PArg1] is the pre-condition of x
          - [PArg2] is the pre-condition of y
          - [PRes]  is the pos-condition of (Op x y) *)
      PArg1 : T -> Prop;
      PArg2 : T -> Prop;
      PRes  : T -> T -> T -> Prop;
      (** [SatOk] states the correctness of the reasoning *)
      SatOk : forall x y, PArg1 x -> PArg2 y -> PRes x y (Op x y)
    }.(** After injections, e.g. nat -> Z,
    the fact that Z.of_nat x * Z.of_nat y is positive is lost.
    This information can be recovered using instance of the  [Saturate] class.
*)
Class Saturate {T: Type} (Op : T -> T -> T) :=
  mksat {
      (** Given [Op x y],
          - [PArg1] is the pre-condition of x
          - [PArg2] is the pre-condition of y
          - [PRes]  is the pos-condition of (Op x y) *)
      PArg1 : T -> Prop;
      PArg2 : T -> Prop;
      PRes  : T -> T -> T -> Prop;
      (** [SatOk] states the correctness of the reasoning *)
      SatOk : forall x y, PArg1 x -> PArg2 y -> PRes x y (Op x y)
    }.(** After injections, e.g. nat -> Z,
    the fact that Z.of_nat x * Z.of_nat y is positive is lost.
    This information can be recovered using instance of the  [Saturate] class.
*)
Class Saturate {T: Type} (Op : T -> T -> T) :=
  mksat {
      (** Given [Op x y],
          - [PArg1] is the pre-condition of x
          - [PArg2] is the pre-condition of y
          - [PRes]  is the pos-condition of (Op x y) *)
      PArg1 : T -> Prop;
      PArg2 : T -> Prop;
      PRes  : T -> T -> T -> Prop;
      (** [SatOk] states the correctness of the reasoning *)
      SatOk : forall x y, PArg1 x -> PArg2 y -> PRes x y (Op x y)
    }.(** After injections, e.g. nat -> Z,
    the fact that Z.of_nat x * Z.of_nat y is positive is lost.
    This information can be recovered using instance of the  [Saturate] class.
*)
Class Saturate {T: Type} (Op : T -> T -> T) :=
  mksat {
      (** Given [Op x y],
          - [PArg1] is the pre-condition of x
          - [PArg2] is the pre-condition of y
          - [PRes]  is the pos-condition of (Op x y) *)
      PArg1 : T -> Prop;
      PArg2 : T -> Prop;
      PRes  : T -> T -> T -> Prop;
      (** [SatOk] states the correctness of the reasoning *)
      SatOk : forall x y, PArg1 x -> PArg2 y -> PRes x y (Op x y)
    }.(** After injections, e.g. nat -> Z,
    the fact that Z.of_nat x * Z.of_nat y is positive is lost.
    This information can be recovered using instance of the  [Saturate] class.
*)
Class Saturate {T: Type} (Op : T -> T -> T) :=
  mksat {
      (** Given [Op x y],
          - [PArg1] is the pre-condition of x
          - [PArg2] is the pre-condition of y
          - [PRes]  is the pos-condition of (Op x y) *)
      PArg1 : T -> Prop;
      PArg2 : T -> Prop;
      PRes  : T -> T -> T -> Prop;
      (** [SatOk] states the correctness of the reasoning *)
      SatOk : forall x y, PArg1 x -> PArg2 y -> PRes x y (Op x y)
    }.(* )Arguments PRes {_ _} _. *)

(* The [ZifyInst.saturate] iterates over all the instances
   and for every pattern of the form
   [H1 : PArg1 ?x , H2 : PArg2 ?y , T : context[Op ?x ?y] |- _ ]
   [H1 : PArg1 ?x , H2 : PArg2 ?y |- context[Op ?x ?y] ]
   asserts  (SatOK x y H1 H2) *)

(** The rest of the file is for internal use by the ML tactic.
    There are  data-structures and lemmas used to inductively construct
    the injected terms. *)

(** The data-structures [injterm] and [injected_prop]
    are used to store source and target expressions together
    with a correctness proof. *)

Record injterm {S T: Type} (I : S -> T) :=
  mkinjterm { source : S ; target : T ; inj_ok : I source = target}.(* )Arguments PRes {_ _} _. *)

(* The [ZifyInst.saturate] iterates over all the instances
   and for every pattern of the form
   [H1 : PArg1 ?x , H2 : PArg2 ?y , T : context[Op ?x ?y] |- _ ]
   [H1 : PArg1 ?x , H2 : PArg2 ?y |- context[Op ?x ?y] ]
   asserts  (SatOK x y H1 H2) *)

(** The rest of the file is for internal use by the ML tactic.
    There are  data-structures and lemmas used to inductively construct
    the injected terms. *)

(** The data-structures [injterm] and [injected_prop]
    are used to store source and target expressions together
    with a correctness proof. *)

Record injterm {S T: Type} (I : S -> T) :=
  mkinjterm { source : S ; target : T ; inj_ok : I source = target}.(* )Arguments PRes {_ _} _. *)

(* The [ZifyInst.saturate] iterates over all the instances
   and for every pattern of the form
   [H1 : PArg1 ?x , H2 : PArg2 ?y , T : context[Op ?x ?y] |- _ ]
   [H1 : PArg1 ?x , H2 : PArg2 ?y |- context[Op ?x ?y] ]
   asserts  (SatOK x y H1 H2) *)

(** The rest of the file is for internal use by the ML tactic.
    There are  data-structures and lemmas used to inductively construct
    the injected terms. *)

(** The data-structures [injterm] and [injected_prop]
    are used to store source and target expressions together
    with a correctness proof. *)

Record injterm {S T: Type} (I : S -> T) :=
  mkinjterm { source : S ; target : T ; inj_ok : I source = target}.(* )Arguments PRes {_ _} _. *)

(* The [ZifyInst.saturate] iterates over all the instances
   and for every pattern of the form
   [H1 : PArg1 ?x , H2 : PArg2 ?y , T : context[Op ?x ?y] |- _ ]
   [H1 : PArg1 ?x , H2 : PArg2 ?y |- context[Op ?x ?y] ]
   asserts  (SatOK x y H1 H2) *)

(** The rest of the file is for internal use by the ML tactic.
    There are  data-structures and lemmas used to inductively construct
    the injected terms. *)

(** The data-structures [injterm] and [injected_prop]
    are used to store source and target expressions together
    with a correctness proof. *)

Record injterm {S T: Type} (I : S -> T) :=
  mkinjterm { source : S ; target : T ; inj_ok : I source = target}.(* )Arguments PRes {_ _} _. *)

(* The [ZifyInst.saturate] iterates over all the instances
   and for every pattern of the form
   [H1 : PArg1 ?x , H2 : PArg2 ?y , T : context[Op ?x ?y] |- _ ]
   [H1 : PArg1 ?x , H2 : PArg2 ?y |- context[Op ?x ?y] ]
   asserts  (SatOK x y H1 H2) *)

(** The rest of the file is for internal use by the ML tactic.
    There are  data-structures and lemmas used to inductively construct
    the injected terms. *)

(** The data-structures [injterm] and [injected_prop]
    are used to store source and target expressions together
    with a correctness proof. *)

Record injterm {S T: Type} (I : S -> T) :=
  mkinjterm { source : S ; target : T ; inj_ok : I source = target}.Record injprop  :=
  mkinjprop {
      source_prop : Prop ; target_prop : Prop ;
      injprop_ok : source_prop <-> target_prop}.Record injprop  :=
  mkinjprop {
      source_prop : Prop ; target_prop : Prop ;
      injprop_ok : source_prop <-> target_prop}.Record injprop  :=
  mkinjprop {
      source_prop : Prop ; target_prop : Prop ;
      injprop_ok : source_prop <-> target_prop}.Record injprop  :=
  mkinjprop {
      source_prop : Prop ; target_prop : Prop ;
      injprop_ok : source_prop <-> target_prop}.Record injprop  :=
  mkinjprop {
      source_prop : Prop ; target_prop : Prop ;
      injprop_ok : source_prop <-> target_prop}.(** Lemmas for building rewrite rules. *)

Definition PropOp_iff (Op : Prop -> Prop -> Prop) :=
  forall (p1 p2 q1 q2:Prop), (p1 <-> q1) -> (p2 <-> q2) -> (Op p1 p2 <-> Op q1 q2).Definition PropUOp_iff (Op : Prop -> Prop) :=
  forall (p1 q1 :Prop), (p1 <-> q1) -> (Op p1 <-> Op q1).

Lemma impl_morph : forall (s1 s2 t1 t2:Prop), s1 <-> t1 -> s2 <-> t2 -> ((s1 -> s2) <-> (t1 -> t2)).