Module Qcert.Common.Operators.BinaryOperators


Section BinaryOperators.
  Require Import EquivDec.
  Require Import String.
  Require Import Utils.
  Require Import ForeignData.
  Require Import ForeignOperators.

  Context {fdata:foreign_data}.
  Context {fbop:foreign_binary_op}.

  Inductive nat_arith_binary_op
    := NatPlus (* addition *)
     | NatMinus (* substraction *)
     | NatMult (* multiplication *)
     | NatDiv (* division *)
     | NatRem (* remainder *)
     | NatMin (* smallest *)
     | NatMax. (* biggest *)
  
  Inductive float_arith_binary_op
    :=
    | FloatPlus (* addition *)
    | FloatMinus (* substraction *)
    | FloatMult (* multiplication *)
    | FloatDiv (* division *)
    | FloatPow (* exponent *)
    | FloatMin (* min *)
    | FloatMax (* max *)
  .

  Inductive float_compare_binary_op
    :=
    | FloatLt (* less than *)
    | FloatLe (* less than or equal to *)
    | FloatGt (* greater than *)
    | FloatGe (* greater than or equal to *)
  .

  Inductive binary_op : Set :=
  | OpEqual : binary_op (* equality *)
  | OpRecConcat : binary_op (* record concatenation *)
  | OpRecMerge : binary_op (* record merge-concatenation *)
  | OpAnd : binary_op (* boolean conjunction *)
  | OpOr : binary_op (* boolean disjunction *)
  | OpLt : binary_op (* less than *)
  | OpLe : binary_op (* less than or equal to *)
  | OpBagUnion : binary_op (* bag union *)
  | OpBagDiff : binary_op (* bag difference *)
  | OpBagMin : binary_op (* bag min *)
  | OpBagMax : binary_op (* bag max *)
  | OpContains : binary_op (* is an element in a collection *)
  | OpStringConcat : binary_op (* string concatenation *)
  | OpNatBinary : nat_arith_binary_op -> binary_op (* arithmetic operators on integers *)
  | OpFloatBinary : float_arith_binary_op -> binary_op (* arithmetic operators on floats *)
  | OpFloatCompare : float_compare_binary_op -> binary_op (* comparison operators on floats *)
  | OpForeignBinary
      (fb : foreign_binary_op_type) : binary_op (* foreign binary operators *)
  .

  Global Instance nat_arith_binary_op_eqdec : EqDec nat_arith_binary_op eq.
Proof.
    change (forall x y : nat_arith_binary_op, {x = y} + {x <> y}).
    decide equality.
  Defined.

  Global Instance float_arith_binary_op_eqdec : EqDec float_arith_binary_op eq.
Proof.
    change (forall x y : float_arith_binary_op, {x = y} + {x <> y}).
    decide equality.
  Defined.

  Global Instance float_compare_binary_op_eqdec : EqDec float_compare_binary_op eq.
Proof.
    change (forall x y : float_compare_binary_op, {x = y} + {x <> y}).
    decide equality.
  Defined.

  Global Instance binary_op_eqdec : EqDec binary_op eq.
Proof.
    change (forall x y : binary_op, {x = y} + {x <> y}).
    decide equality.
    apply nat_arith_binary_op_eqdec.
    apply float_arith_binary_op_eqdec.
    apply float_compare_binary_op_eqdec.
    apply foreign_binary_op_dec.
  Defined.

  Local Open Scope string.

  Global Instance ToString_nat_binary_op : ToString nat_arith_binary_op
    := {toString :=
          fun (op:nat_arith_binary_op) =>
            match op with
            | NatPlus => "NatPlus"
            | NatMinus => "NatMinus"
            | NatMult => "NatMult"
            | NatMin => "NatMin"
            | NatMax => "NatMax"
            | NatDiv => "NatDiv"
            | NatRem => "NatRem"
            end
       }.

  Global Instance ToString_float_arith_binary_op : ToString float_arith_binary_op
    := {toString :=
          fun (op:float_arith_binary_op) =>
            match op with
            | FloatPlus => "FloatPlus"
            | FloatMinus => "FloatMinus"
            | FloatMult => "FloatMult"
            | FloatDiv => "FloatDiv"
            | FloatPow => "FloatPow"
            | FloatMin => "FloatMin"
            | FloatMax => "FloatMax"
            end
       }.

  Global Instance ToString_float_compare_binary_op : ToString float_compare_binary_op
    := {toString :=
          fun (op:float_compare_binary_op) =>
            match op with
            | FloatLt => "FloatLt"
            | FloatLe => "FloatLe"
            | FloatGt => "FloatGt"
            | FloatGe => "FloatGe"
            end
       }.

  Global Instance ToString_binary_op : ToString binary_op
    := {toString :=
          fun (op:binary_op) =>
            match op with
            | OpEqual => "OpEqual"
            | OpRecConcat => "OpRecConcat"
            | OpRecMerge => "OpRecMerge"
            | OpAnd => "OpAnd"
            | OpOr => "OpOr"
            | OpLt => "OpLt"
            | OpLe => "OpLe"
            | OpBagUnion => "OpBagUnion"
            | OpBagDiff => "OpBagDiff"
            | OpBagMin => "OpBagMin"
            | OpBagMax => "OpBagMax"
            | OpContains => "OpContains"
            | OpStringConcat => "OpStringConcat"
            | OpNatBinary aop => "(OpNatBinary " ++ (toString aop) ++ ")"
            | OpFloatBinary aop => "(OpFloatBinary " ++ (toString aop) ++ ")"
            | OpFloatCompare aop => "(OpFloatCompare " ++ (toString aop) ++ ")"
            | OpForeignBinary fb => toString fb
            end
       }.

End BinaryOperators.

Tactic Notation "binary_op_cases" tactic(first) ident(c) :=
  first;
  [ Case_aux c "OpEqual"%string
  | Case_aux c "OpRecConcat"%string
  | Case_aux c "OpRecMerge"%string
  | Case_aux c "OpAnd"%string
  | Case_aux c "OpOr"%string
  | Case_aux c "OpLt"%string
  | Case_aux c "OpLe"%string
  | Case_aux c "OpBagUnion"%string
  | Case_aux c "OpBagDiff"%string
  | Case_aux c "OpBagMin"%string
  | Case_aux c "OpBagMax"%string
  | Case_aux c "OpContains"%string
  | Case_aux c "OpStringConcat"%string
  | Case_aux c "OpNatBinary"%string
  | Case_aux c "OpFloatBinary"%string
  | Case_aux c "OpFloatCompare"%string
  | Case_aux c "OpForeignBinary"%string].