Revision 972a0fa7c31107bd43e2efb347f3ee921335da8a authored by Cécile BARITEL-RUET on 22 March 2019, 16:02:09 UTC, committed by Cécile BARITEL-RUET on 22 March 2019, 16:02:09 UTC
1 parent 5cb65da
Raw File
ecPReduction.ml
open EcUtils
open EcReduction
open EcIdent
open EcFol
open EcTypes
open EcPath
open EcModules
open EcPattern
open Psubst



let rec p_is_true p = match p_destr_app p with
  | { p_node = Pat_Axiom(Axiom_Op (_,op,[],ty)) }, [] ->
     EcPath.p_equal op EcCoreLib.CI_Bool.p_true
     && odfl true (omap (ty_equal tbool) ty)
  | { p_node = Pat_Meta_Name (Some p, _, _) }, [] -> p_is_true p
  (* | pop, [t] -> op_equal pop fop_not && p_is_false t *)
  | _ -> false

and p_is_false p = match p_destr_app p with
  | { p_node = Pat_Axiom(Axiom_Op (_,op,[],ty)) }, [] ->
     EcPath.p_equal op EcCoreLib.CI_Bool.p_false
     && odfl true (omap (ty_equal tbool) ty)
  | { p_node = Pat_Meta_Name (Some p, _, _) }, [] -> p_is_false p
  (* | pop, [t] -> op_equal pop fop_not && p_is_true t *)
  | _ -> false

let p_bool_val p =
  if p_is_true p then Some true
  else if p_is_false p then Some false
  else None

(* -------------------------------------------------------------------------- *)
let p_is_not p = match p with
  | { p_node = Pat_Axiom(Axiom_Op (_,op,[],ot)) } ->
     EcPath.p_equal op EcCoreLib.CI_Bool.p_not
     && odfl true (omap (ty_equal (toarrow [tbool] tbool)) ot)
  | _ -> false

let p_destr_not p = match p_destr_app p with
  | op, [p1] when p_is_not op -> p1
  | _ -> assert false

(* -------------------------------------------------------------------------- *)
let p_not_simpl_opt (p : pattern) =
  let op, _ = p_destr_app p in
  if p_is_not op then Some (p_destr_not p)
  else if p_is_true p then Some p_false
  else if p_is_false p then Some p_true
  else None

let p_not_simpl p = odfl (p_not p) (p_not_simpl_opt p)

let p_imp_simpl_opt (p1 : pattern) (p2 : pattern) =
  match p_bool_val p1, p_bool_val p2 with
  | Some true, _ -> Some p2
  | Some false, _ | _, Some true -> Some p_true
  | _, Some false -> Some (p_not_simpl p1)
  | _ -> if p_equal p1 p2 then Some p_true
         else None

let p_imp_simpl p1 p2 = odfl (p_imp p1 p2) (p_imp_simpl_opt p1 p2)

let p_anda_simpl_opt (p1 : pattern) (p2 : pattern) =
  match p_bool_val p1, p_bool_val p2 with
  | Some true , _          -> Some p2
  | Some false, _          -> Some p_false
  | _         , Some true  -> Some p1
  | _         , Some false -> Some p_false
  | _                      -> None

let p_anda_simpl p1 p2 = odfl (p_anda p1 p2) (p_anda_simpl_opt p1 p2)

let p_ora_simpl_opt (p1 : pattern) (p2 : pattern) =
  match p_bool_val p1, p_bool_val p2 with
  | Some true , _          -> Some p_true
  | Some false, _          -> Some p2
  | _         , Some true  -> Some p_true
  | _         , Some false -> Some p1
  | _                      -> None

let p_ora_simpl p1 p2 = odfl (p_ora p1 p2) (p_ora_simpl_opt p1 p2)

let rec p_iff_simpl_opt (p1 : pattern) (p2 : pattern) =
  if p_equal  p1 p2 then Some p_true
  else
  match p_bool_val p1, p_bool_val p2 with
  | Some true , _          -> Some p2
  | Some false, _          -> Some (p_not_simpl p2)
  | _         , Some true  -> Some p1
  | _         , Some false -> Some (p_not_simpl p1)
  | _ ->
  match p_destr_app p1, p_destr_app p2 with
  | (op1, [p1]), (op2, [p2])
       when (op_equal op1 fop_not && op_equal op2 fop_not) ->
     Some (p_iff_simpl p1 p2)
  | _ -> None

and p_iff_simpl (p1 : pattern) (p2 : pattern) =
  match p_iff_simpl_opt p1 p2 with
  | Some p -> p
  | None -> p_iff p1 p2

let p_and_simpl_opt (p1 : pattern) (p2 : pattern) =
  match p_bool_val p1, p_bool_val p2 with
  | Some false, _ | _, Some false -> Some p_false
  | Some true, _ -> Some p2
  | _, Some true -> Some p1
  | _ -> None

let p_and_simpl p1 p2 = odfl (p_and p1 p2) (p_and_simpl_opt p1 p2)

let p_or_simpl_opt (p1 : pattern) (p2 : pattern) =
  match p_bool_val p1, p_bool_val p2 with
  | Some true, _ | _, Some true -> Some p_true
  | Some false, _ -> Some p2
  | _, Some false -> Some p1
  | _ -> None

let p_or_simpl p1 p2 = odfl (p_or p1 p2) (p_or_simpl_opt p1 p2)

let p_andas_simpl = List.fold_right p_anda_simpl

let rec p_eq_simpl_opt (p1 : pattern) (p2 : pattern) =
  if p_equal p1 p2 then Some p_true
  else match p1.p_node, p2.p_node with
  | Pat_Axiom (Axiom_Int _), Pat_Axiom (Axiom_Int _) -> Some p_false

  | Pat_Axiom (Axiom_Op (_, op1, [], _) ),
    Pat_Axiom (Axiom_Op (_, op2, [], _) )
       when
         (EcPath.p_equal op1 EcCoreLib.CI_Bool.p_true  &&
          EcPath.p_equal op2 EcCoreLib.CI_Bool.p_false  )
      || (EcPath.p_equal op2 EcCoreLib.CI_Bool.p_true  &&
          EcPath.p_equal op1 EcCoreLib.CI_Bool.p_false  )
    -> Some p_false

  | Pat_Fun_Symbol (Sym_Form_App _, [op1; { p_node = Pat_Axiom (Axiom_Int _)}]),
    Pat_Fun_Symbol (Sym_Form_App _, [op2; { p_node = Pat_Axiom (Axiom_Int _)}])
      when op_equal op1 fop_real_of_int && op_equal op2 fop_real_of_int
    -> Some p_false

  | Pat_Fun_Symbol (Sym_Form_Tuple, l1), Pat_Fun_Symbol (Sym_Form_Tuple, l2)
       when List.length l1 = List.length l2 ->
      Some
        (p_andas_simpl
           (List.map2 (fun x y -> p_eq_simpl x y) l1 l2) p_true)

  | _ -> None

and p_eq_simpl p1 p2 = odfl (p_eq p1 p2) (p_eq_simpl_opt p1 p2)


(* -------------------------------------------------------------------------- *)
let p_is_i0 p = try EcBigInt.equal (p_destr_int p) EcBigInt.zero
                with DestrError _ -> false

let p_is_i1 p = try EcBigInt.equal (p_destr_int p) EcBigInt.one
                with DestrError _ -> false


let p_int_le_simpl_opt (p1 : pattern) (p2 : pattern) =
  if p1 = p2 then Some p_true
  else
    try Some (pat_form (f_bool (EcBigInt.compare (p_destr_int p1) (p_destr_int p2) <= 0)))
    with DestrError _ -> None

let p_int_le_simpl p1 p2 = odfl (p_int_le p1 p2) (p_int_le_simpl_opt p1 p2)

let p_int_lt_simpl_opt (p1 : pattern) (p2 : pattern) =
  if p1 = p2 then Some p_false
  else
    try Some (pat_form (f_bool (EcBigInt.compare (p_destr_int p1) (p_destr_int p2) < 0)))
    with DestrError _ -> None

let p_int_lt_simpl p1 p2 = odfl (p_int_lt p1 p2) (p_int_lt_simpl_opt p1 p2)

let p_int_opp_simpl_opt (p : pattern) =
  match p_destr_app p with
  | op, [p] when op_equal op fop_int_opp -> Some p
  | _ -> if p_is_i0 p then Some p_i0 else None

let p_int_opp_simpl p = odfl (p_int_opp p) (p_int_opp_simpl_opt p)

let p_int_add_simpl_opt =
  let try_add_opp p1 p2 =
    try
      let p2 = match p_destr_app p2 with
        | op, [p] when op_equal op fop_int_opp -> p
        | _ -> destr_error "" in
      if p_equal p1 p2 then Some p_i0 else None
    with DestrError _ -> None in

  let try_addc i p =
    try
      let c1, c2 = match p_destr_app p with
        | op, [p1; p2] when op_equal op fop_int_add -> p1, p2
        | _ -> destr_error "" in

      try  let c = p_destr_int c1 in
           Some (p_int_add (p_int (EcBigInt.add c i)) c2)
      with DestrError _ ->
      try  let c = p_destr_int c2 in
           Some (p_int_add c1 (p_int (EcBigInt.add c i)))
      with DestrError _ -> None

    with DestrError _ -> None in

  fun p1 p2 ->
    let i1 = try Some (p_destr_int p1) with DestrError _ -> None in
    let i2 = try Some (p_destr_int p2) with DestrError _ -> None in

    match i1, i2 with
    | Some i1, Some i2 -> Some (p_int (EcBigInt.add i1 i2))

    | Some i1, _ when EcBigInt.equal i1 EcBigInt.zero -> Some p2
    | _, Some i2 when EcBigInt.equal i2 EcBigInt.zero -> Some p1

    | _, _ ->
        let simpls = [
           (fun () -> try_add_opp p1 p2);
           (fun () -> try_add_opp p2 p1);
           (fun () -> i1 |> obind (try_addc^~ p2));
           (fun () -> i2 |> obind (try_addc^~ p1));
        ] in

        List.Exceptionless.find_map (fun f -> f ()) simpls

let p_int_add_simpl p1 p2 = odfl (p_int_add p1 p2) (p_int_add_simpl_opt p1 p2)

let p_int_mul_simpl_opt (p1 : pattern) (p2 : pattern) =
  try  Some (p_int (EcBigInt.mul (p_destr_int p1) (p_destr_int p2)))
  with DestrError _ ->
    if p_is_i0 p1 || p_is_i0 p2 then Some p_i0
    else if p_is_i1 p1 then Some p2
    else if p_is_i1 p2 then Some p1
    else None

let p_int_mul_simpl p1 p2 = odfl (p_int_mul p1 p2) (p_int_mul_simpl_opt p1 p2)

let get_real_of_int (p : pattern) =
  try Some (p_destr_rint p) with DestrError _ -> None

(* -------------------------------------------------------------------- *)
let p_real_le_simpl_opt (p1 : pattern) (p2 : pattern) =
  if p_equal p1 p2 then Some p_true else
    match get_real_of_int p1, get_real_of_int p2 with
    | Some x1, Some x2 -> Some (pat_form (f_bool (EcBigInt.compare x1 x2 <= 0)))
    | _ -> None

let p_real_le_simpl p1 p2 = odfl (p_real_le p1 p2) (p_real_le_simpl_opt p1 p2)

let p_real_lt_simpl_opt (p1 : pattern) (p2 : pattern) =
  if p_equal p1 p2 then Some p_true else
    match get_real_of_int p1, get_real_of_int p2 with
    | Some x1, Some x2 -> Some (pat_form (f_bool (EcBigInt.compare x1 x2 < 0)))
    | _ -> None

let p_real_lt_simpl p1 p2 = odfl (p_real_lt p1 p2) (p_real_lt_simpl_opt p1 p2)

let p_remove_opp (p : pattern) =
  match p_destr_app p with
  | op, [p] when op_equal op fop_real_opp -> Some p
  | _ -> None

let p_remove_add (p : pattern) =
  match p_destr_app p with
  | op, [p1;p2] when op_equal op fop_real_add -> p1, p2
  | _ -> destr_error "p_remove_add"

let p_destr_rdivint (p : pattern) =
  let rec aux isneg p =
    let renorm n d =
      if isneg then (EcBigInt.neg n, d) else (n, d)
    in
    match p_destr_app p with
    | op, [p1;p2] when op_equal op fop_real_mul -> begin
        match p_destr_app p2 with
        | op2, [p2] when op_equal op2 fop_real_inv ->
           let n1, n2 =
             try (p_destr_rint p1, p_destr_rint p2)
             with DestrError _ -> destr_error "p_rdivint"
           in renorm n1 n2
        | _ ->
           try  renorm (p_destr_rint p) EcBigInt.one
           with DestrError _ -> destr_error "p_rdivint"
      end

    | op, [p1] when op_equal op fop_real_inv -> begin
        try  renorm EcBigInt.one (p_destr_rint p1)
        with DestrError _ -> destr_error "p_rdivint"
      end

    | op, [p1] when op_equal op fop_real_opp ->
       aux (not isneg) p1

    | _ ->
       try  renorm (p_destr_rint p) EcBigInt.one
       with DestrError _ -> destr_error "p_rdivint"

  in aux false p

let rec p_real_split p = match p_destr_app p with
  | op, [p1;p2] when op_equal op fop_real_mul -> begin
      match p_destr_app p1, p_destr_app p2 with
      | _         , (op, [p2]) when op_equal op fop_real_inv -> (p1, p2)
      | (op, [p1]), _          when op_equal op fop_real_inv -> (p2, p1)
      | _                                                    -> (p, p_r1)
    end
  | op, [p1] when op_equal op fop_real_inv -> (p_r1, p1)
  | _ -> (p, p_r1)

let p_norm_real_int_div n1 n2 =
  let module BI = EcBigInt in
  let s1 = BI.sign n1 and s2 = BI.sign n2 in
  if s1 = 0 || s2 = 0 then p_r0
  else
    let n1 = BI.abs n1 and n2 = BI.abs n2 in
    let n1, n2 =
      match BI.gcd n1 n2 with
      | n when BI.equal n BI.one -> (n1, n2)
      | n -> (BI.div n1 n, BI.div n2 n)
    in
    let n1 = if (s1 * s2) < 0 then BI.neg n1 else n1 in
    if BI.equal n2 BI.one then p_rint n1
    else p_real_div (p_rint n1) (p_rint n2)

let p_real_add_simpl_opt (p1 : pattern) (p2 : pattern) =
  let module BI = EcBigInt in
  let try_add_opp p1 p2 =
    match p_remove_opp p2 with
    | None -> None
    | Some p2 -> if p_equal p1 p2 then Some p_r0 else None in

  let try_addc i p =
    try
      let c1, c2 = p_remove_add p in
      try let c = p_destr_rint c1 in Some (p_real_add (p_rint (EcBigInt.add c i)) c2)
      with DestrError _ ->
      try let c = p_destr_rint c2 in Some (p_real_add c1 (p_rint (EcBigInt.add c i)))
      with DestrError _ -> None

    with DestrError _ -> None in

  let try_norm_rintdiv p1 p2 =
    try
      let (n1, d1) = p_destr_rdivint p1 in
      let (n2, d2) = p_destr_rdivint p2 in

      Some (p_norm_real_int_div (BI.add (BI.mul n1 d2) (BI.mul n2 d1)) (BI.mul d1 d2))

    with DestrError _ -> None in

  let r1 = try Some (p_destr_rint p1) with DestrError _ -> None in
  let r2 = try Some (p_destr_rint p2) with DestrError _ -> None in

  match r1, r2 with
  | Some i1, Some i2 -> Some (p_rint (BI.add i1 i2))

  | Some i1, _ when BI.equal i1 BI.zero -> Some p2
  | _, Some i2 when BI.equal i2 BI.zero -> Some p1

  | _ ->
     let simpls = [
         (fun () -> try_norm_rintdiv p1 p2);
         (fun () -> try_add_opp p1 p2);
         (fun () -> try_add_opp p2 p1);
         (fun () -> r1 |> obind (try_addc^~ p2));
         (fun () -> r2 |> obind (try_addc^~ p1));
       ] in

     List.Exceptionless.find_map (fun f -> f ()) simpls

let p_real_opp_simpl_opt (p : pattern) =
  match p_destr_app p with
  | op, [p] when op_equal op fop_real_opp -> Some p
  | _ -> None

let p_real_is_zero p =
  try  EcBigInt.equal EcBigInt.zero (p_destr_rint p)
  with DestrError _ -> false

let p_real_is_one p =
  try  EcBigInt.equal EcBigInt.one (p_destr_rint p)
  with DestrError _ -> false


let rec p_real_mul_simpl e p1 p2 : pattern =
  (* let ppe = EcPrinting.PPEnv.ofenv e in
   * EcEnv.notify e `Warning "simplify mul : p_real_mul_simpl (%a) (%a)"
   *   (EcPrinting.pp_pattern ppe) p1
   *   (EcPrinting.pp_pattern ppe) p2; *)
  let (n1, d1) = p_real_split p1 in
  let (n2, d2) = p_real_split p2 in

  p_real_div_simpl_r e
    (p_real_mul_simpl_r e n1 n2)
    (p_real_mul_simpl_r e d1 d2)

and p_real_div_simpl e p1 p2 =
  (* let ppe = EcPrinting.PPEnv.ofenv e in
   * EcEnv.notify e `Warning "simplify mul : p_real_div_simpl (%a) (%a)"
   *   (EcPrinting.pp_pattern ppe) p1
   *   (EcPrinting.pp_pattern ppe) p2; *)
  let (n1, d1) = p_real_split p1 in
  let (n2, d2) = p_real_split p2 in

  p_real_div_simpl_r e
    (p_real_mul_simpl_r e n1 d2)
    (p_real_mul_simpl_r e d1 n2)

and p_real_mul_simpl_r _e p1 p2 =
  (* let ppe = EcPrinting.PPEnv.ofenv e in
   * EcEnv.notify e `Warning "simplify mul : p_real_mul_simpl_r (%a) (%a)"
   *   (EcPrinting.pp_pattern ppe) p1
   *   (EcPrinting.pp_pattern ppe) p2; *)
  if p_real_is_zero p1 || p_real_is_zero p2 then p_r0 else

  if p_real_is_one p1 then p2 else
  if p_real_is_one p2 then p1 else

  try
    p_rint (EcBigInt.Notations.( *^ ) (p_destr_rint p1) (p_destr_rint p2))
  with DestrError _ -> p_real_mul p1 p2

and p_real_div_simpl_r e p1 p2 =
  (* let ppe = EcPrinting.PPEnv.ofenv e in
   * EcEnv.notify e `Warning "simplify mul : p_real_div_simpl_r (%a) (%a)"
   *   (EcPrinting.pp_pattern ppe) p1
   *   (EcPrinting.pp_pattern ppe) p2; *)
  let (p1, p2) =
    try
      let n1 = p_destr_rint p1 in
      let n2 = p_destr_rint p2 in
      let gd = EcBigInt.gcd n1 n2 in

      p_rint (EcBigInt.div n1 gd), p_rint (EcBigInt.div n2 gd)

    with
    | DestrError _ -> (p1, p2)
    | Division_by_zero -> (p_r0, p_r1)

  in p_real_mul_simpl_r e p1 (odfl (p_real_inv p2) (p_real_inv_simpl_opt p2))

and p_real_inv_simpl_opt p =
  match p_destr_app p with
  | op, [p1] when op_equal op fop_real_inv -> Some p1
  | _ ->
     try
       match p_destr_rint p with
       | n when EcBigInt.equal n EcBigInt.zero -> Some p_r0
       | n when EcBigInt.equal n EcBigInt.one  -> Some p_r1
       | _ -> destr_error "destr_rint/inv"
     with DestrError _ -> None

let p_real_mul_simpl_opt e p1 p2 =
  let f1 = p_real_mul_simpl e p1 p2 in
  let f2 = p_real_mul p1 p2 in
  if f1 = f2 then None else Some f1

(* -------------------------------------------------------------------------- *)
let p_if_simpl_opt (p1 : pattern) (p2 : pattern) (p3 : pattern) =
  (* if p_equal p2 p3 then Some p2
   * else  *)
    match p_bool_val p1, p_bool_val p2, p_bool_val p3 with
  | Some true, _, _  -> Some p2
  | Some false, _, _ -> Some p3
  | _, Some true, _  -> Some (p_imp_simpl (p_not_simpl p1) p3)
  | _, Some false, _ -> Some (p_anda_simpl (p_not_simpl p1) p3)
  | _, _, Some true  -> Some (p_imp_simpl p1 p2)
  | _, _, Some false -> Some (p_anda_simpl p1 p2)
  | _, _, _          -> None

let p_proj_simpl (p1 : pattern) (i : int) (ty : ty) =
  match p1.p_node with
  | Pat_Fun_Symbol(Sym_Form_Tuple,pargs) when 0 <= i && i < List.length pargs ->
     List.nth pargs i
  | _ -> p_proj p1 i ty

let ps_app_simpl ?ho op pargs oty =
  odfl (p_app ?ho op pargs oty) (p_betared_opt (p_app ?ho op pargs oty))

let p_forall_simpl b p =
  let b = List.filter (fun (id,_) -> Mid.mem id (pat_fv p)) b in
  p_forall b p

let p_exists_simpl b p =
  let b = List.filter (fun (id,_) -> Mid.mem id (pat_fv p)) b in
  p_exists b p



let update_higher_order p =
  let f p = match p.p_node with
    | Pat_Fun_Symbol
      (Sym_Form_App (ty,_), ({ p_node = Pat_Meta_Name _ } as p)::args) ->
       p_app ~ho:MaybeHO p args ty
    | _ -> p in
  p_map f p

let rec h_red_args
          (p_f : 'a -> pattern)
          (f :  EcEnv.LDecl.hyps -> reduction_info -> Psubst.p_subst ->'a -> pattern option)
          (hyps : EcEnv.LDecl.hyps) (ri : reduction_info) (s : Psubst.p_subst) = function
  | [] -> None
  | a :: r ->
     match f hyps ri s a with
     | Some a -> Some (a :: (List.map p_f r))
     | None -> omap (fun l -> (p_f a)::l) (h_red_args p_f f hyps ri s r)

let rec p_is_record (hyps : EcEnv.LDecl.hyps) (p : pattern) =
  match p_destr_app p with
  | { p_node = Pat_Axiom (Axiom_Op (_, p,_,_)) }, _ ->
     EcEnv.Op.is_record_ctor (EcEnv.LDecl.toenv hyps) p
  | _ -> false

let reduce_local_opt (hyps : EcEnv.LDecl.hyps) (ri : reduction_info)
      (id : Name.t) : pattern option =
  if ri.delta_h id && EcEnv.LDecl.can_unfold id hyps then
      Some (pat_form (EcEnv.LDecl.unfold id hyps))
  else None

let can_eta x (f, args) =
  match List.rev args with
  | { f_node = Flocal y } :: args ->
     let check v = not (Mid.mem x v.f_fv) in
     id_equal x y && List.for_all check (f :: args)
  | _ -> false

let p_can_eta x (f, args) =
  match List.rev args with
  | { p_node = (Pat_Axiom (Axiom_Local (y,_))
                | Pat_Meta_Name (None, y, _))} :: args ->
     let check v = not (Mid.mem x (FV.pattern0 v)) in
     id_equal x y && List.for_all check (f :: args)
  | _ -> false

let print h s =
  let env = EcEnv.LDecl.toenv h in
  EcEnv.notify env `Warning "rrrrr Reduction : %s" s

let rec h_red_pattern_opt ?(verbose:bool=false) eq
          (hyps : EcEnv.LDecl.hyps) (ri : reduction_info)
          (s : Psubst.p_subst) (p : pattern) =
  let h_red_pattern_opt = h_red_pattern_opt ~verbose in
  try
    match p.p_node with
    | Pat_Meta_Name (None,n,_) ->
       let o = Mid.find_opt n s.ps_patloc in
       if verbose && is_some o then print hyps "meta name"; o
    | Pat_Meta_Name (Some p,_,_) -> h_red_pattern_opt eq hyps ri s p
    | Pat_Sub p ->
       omap (fun x -> mk_pattern (Pat_Sub x) OGTany)
         (h_red_pattern_opt eq hyps ri s p)
    | Pat_Or _ -> None
    | Pat_Red_Strat _ -> None
    | Pat_Axiom a -> h_red_axiom_opt ~verbose eq hyps ri s a
    | Pat_Fun_Symbol (symbol,lp) ->
       match symbol, lp with
       (* β-reduction *)
       | Sym_Form_App _,
         { p_node = Pat_Fun_Symbol (Sym_Quant (Llambda,_),[_])}::_
         | Sym_Form_App _,
           { p_node =
               Pat_Meta_Name
                 (Some
                    { p_node = Pat_Fun_Symbol (Sym_Quant (Llambda,_),[_])},_,_)}::_
            when ri.beta ->
          let o = p_betared_opt p in
          if verbose && is_some o then print hyps "beta"; o

       (* ζ-reduction *)
       | Sym_Form_App (ty,ho),
         { p_node = (Pat_Meta_Name (None, id, _))} :: pargs
            (* when ri.zeta *)
         -> begin
           let o = omap (fun op -> p_app_simpl ~ho op pargs ty)
                     (Mid.find_opt id s.ps_patloc) in
           if verbose && is_some o then print hyps "zeta 1"; o
         end

       (* ζ-reduction *)
       | Sym_Form_App (oty,ho),
         { p_node = Pat_Axiom (Axiom_Local (id,_))} :: pargs
            when ri.zeta -> begin
           match reduce_local_opt hyps ri id with
           | None -> None
           | Some op ->
              let o = Some (p_app_simpl ~ho op pargs oty) in
              if verbose && is_some o then print hyps "zeta 2"; o
         end

       (* ζ-reduction *)
       | Sym_Form_Let (LSymbol(x,_)), [p1;p2] when ri.zeta ->
          let s = Psubst.p_bind_local Psubst.p_subst_id x p1 in
          if verbose then print hyps "zeta 3";
          Some (Psubst.p_subst s p2)

       (* ι-reduction (let-tuple) *)
       | Sym_Form_Let (LTuple ids),
         [({ p_node = Pat_Fun_Symbol (Sym_Form_Tuple, lp)}
           | { p_node =
                 Pat_Meta_Name
                   (Some
                      { p_node =
                          Pat_Fun_Symbol (Sym_Form_Tuple, lp)},_,_)})
         ;p2]
            when ri.zeta ->
          let s = List.fold_left2 (fun s (x,_) p -> Psubst.p_bind_local s x p)
                    Psubst.p_subst_id ids lp in
          if verbose then print hyps "iota 1";
          Some (Psubst.p_subst s p2)

       (* ι-reduction (let-records) *)
       | Sym_Form_Let (LRecord (_, ids)), [p1;p2]
            when ri.iota && p_is_record hyps p1 ->
          let args  = snd (p_destr_app p1) in
          let subst =
            List.fold_left2 (fun subst (x, _) e ->
                match x with
                | None   -> subst
                | Some x -> Psubst.p_bind_local subst x e)
              Psubst.p_subst_id ids args
          in
          if verbose then print hyps "iota 2";
          Some (Psubst.p_subst subst p2)

       (* ι-reduction (records projection) *)
       | Sym_Form_App (oty,ho),
         (({ p_node = Pat_Axiom (Axiom_Op (_, op, _, _)) }
           | { p_node =
                 Pat_Meta_Name (Some { p_node = Pat_Axiom (Axiom_Op (_, op, _, _)) },_,_)})
          as p1) :: pargs
            when ri.iota && EcEnv.Op.is_projection (EcEnv.LDecl.toenv hyps) op -> begin
           let op =
             match pargs with
             | [mk] -> begin
                 match odfl mk (h_red_pattern_opt eq hyps ri s mk) with
                 | { p_node =
                       Pat_Fun_Symbol
                         (Sym_Form_App _,
                          { p_node = Pat_Axiom (Axiom_Op (_, mkp,_,_))} :: pargs)} ->
                    if not (EcEnv.Op.is_record_ctor (EcEnv.LDecl.toenv hyps) mkp)
                    then None
                    else
                      let v = oget (EcEnv.Op.by_path_opt op (EcEnv.LDecl.toenv hyps)) in
                      let v = proj3_2 (EcDecl.operator_as_proj v) in
                      let v = try Some(List.nth pargs v)
                              with _ -> None in
                      begin
                        match v with
                        | None -> None
                        | Some v -> h_red_pattern_opt eq hyps ri s v
                      end
                 | _ -> None
               end
             | _ -> None
           in match op with
              | None ->
                 let o = omap (fun x -> p_app ~ho x pargs oty)
                           (h_red_pattern_opt eq hyps ri s p1) in
                 if verbose && is_some o then print hyps "iota 3"; o
              | _ -> if verbose then print hyps "iota 3"; op
         end

       (* ι-reduction (tuples projection) *)
       | Sym_Form_Proj (i,ty), [p1] when ri.iota ->
          let p' = p_proj_simpl p1 i ty in
          if p = p'
          then (let o = omap (fun x -> p_proj x i ty) (h_red_pattern_opt eq hyps ri s p1) in
               if verbose && is_some o then print hyps "iota 4 1"; o)
          else (if verbose then print hyps "iota 4 2"; Some p')

       (* ι-reduction (if-then-else) *)
       | Sym_Form_If, [p1;p2;p3] when ri.iota -> begin
           match p_if_simpl_opt p1 p2 p3 with
           | None ->
              let o = omap (fun x -> p_if x p2 p3)
                        (h_red_pattern_opt eq hyps ri s p1) in
              if verbose && is_some o then print hyps "iota 5 1"; o
           | Some _ as p -> if verbose then print hyps "iota 5 2"; p
         end

       (* ι-reduction (match-fix) *)
       | Sym_Form_App (ty,ho),
         (({ p_node = Pat_Axiom (Axiom_Op (_, op, lty, _)) }
           | { p_node =
                 Pat_Meta_Name
                   (Some
                      { p_node = Pat_Axiom (Axiom_Op (_, op, lty, _)) },_,_)})
          as p1) :: args
            when ri.iota
                 && EcEnv.Op.is_fix_def (EcEnv.LDecl.toenv hyps) op -> begin
           try
             let op  = oget (EcEnv.Op.by_path_opt op (EcEnv.LDecl.toenv hyps)) in
             let fix = EcDecl.operator_as_fix op in
             if List.length args <> snd (fix.EcDecl.opf_struct) then
               raise EcEnv.NotReducible
             else
               let pargs = Array.of_list args in
               let myfun (opb, acc) v =
                 let v = pargs.(v) in
                 let v = odfl v (h_red_pattern_opt eq hyps ri s v) in
                 match p_destr_app v with
                 | { p_node = Pat_Axiom (Axiom_Op (_, op, _, _)) }, cargs
                      when EcEnv.Op.is_dtype_ctor (EcEnv.LDecl.toenv hyps) op -> begin
                     let idx = EcEnv.Op.by_path op (EcEnv.LDecl.toenv hyps) in
                     let idx = snd (EcDecl.operator_as_ctor idx) in
                     match opb with
                     | EcDecl.OPB_Leaf _    -> assert false
                     | EcDecl.OPB_Branch bs ->
                        ((Parray.get bs idx).EcDecl.opb_sub, cargs :: acc)
                   end
                 | _ -> raise EcEnv.NotReducible in
               let pargs = List.fold_left myfun
                             (fix.EcDecl.opf_branches, [])
                             (fst fix.EcDecl.opf_struct) in
               let pargs, (bds, body) =
                 match pargs with
                 | EcDecl.OPB_Leaf (bds, body), cargs -> (List.rev cargs, (bds, body))
                 | _ -> assert false in

               let s =
                 List.fold_left2
                   (fun s (x,_) fa -> Psubst.p_bind_local s x fa)
                   Psubst.p_subst_id fix.EcDecl.opf_args args in

               let s =
                 List.fold_left2
                   (fun s bds cargs ->
                     List.fold_left2
                       (fun s (x,_) fa -> Psubst.p_bind_local s x fa)
                       s bds cargs)
                   s bds pargs in

               let body = EcFol.form_of_expr EcFol.mhr body in
               let body =
                 EcFol.Fsubst.subst_tvar
                   (EcTypes.Tvar.init (List.map fst op.EcDecl.op_tparams) lty) body in
               if verbose then print hyps "iota 6 1";
               Some (Psubst.p_subst s (pat_form body))
           with
           | EcEnv.NotReducible ->
              let o = omap (fun x -> p_app ~ho x args ty)
                (h_red_pattern_opt eq hyps ri s p1) in
              if verbose && is_some o then print hyps "iota 6 2"; o
         end

       (* μ-reduction *)
       | Sym_Form_Glob, [mp;mem] when ri.modpath ->
          if verbose then print hyps "mu 1";
          let p' = match mp.p_node, mem.p_node with
            | Pat_Axiom (Axiom_Mpath mp), Pat_Axiom (Axiom_Memory m) ->
               let f  = f_glob mp m in
               let f' = EcEnv.NormMp.norm_glob (EcEnv.LDecl.toenv hyps) m mp in
               if f_equal f f' then None
               else Some (pat_form f')
            | _ ->
               match h_red_pattern_opt eq hyps ri s mp with
               | Some mp' when not (p_equal mp mp') -> Some (p_glob mp' mem)
               | _ ->
                  omap (fun x -> p_glob mp x) (h_red_pattern_opt eq hyps ri s mem) in
          p'

       (* μ-reduction *)
       | Sym_Form_Pvar ty, [ppv;m] ->
          if verbose then print hyps "mu 2";
          let pv = match ppv.p_node with
            | Pat_Axiom (Axiom_Prog_Var pv) ->
               let pv' = EcEnv.NormMp.norm_pvar (EcEnv.LDecl.toenv hyps) pv in
               if pv_equal pv pv' then
                 omap (p_pvar ppv ty) (h_red_pattern_opt eq hyps ri s m)
               else Some (p_pvar (pat_pv pv') ty m)
            | _ ->
               match h_red_pattern_opt eq hyps ri s ppv with
               | Some pv -> Some (p_pvar pv ty m)
               | None -> omap (p_pvar ppv ty) (h_red_pattern_opt eq hyps ri s m) in
          pv

       (* logical reduction *)
       | Sym_Form_App (ty,ho),
         (({ p_node = Pat_Axiom (Axiom_Op (_, op, tys, _))}
           | { p_node =
                 Pat_Meta_Name
                   (Some
                      { p_node = Pat_Axiom (Axiom_Op (_, op, tys, _))},_,_)})
          as fo) :: args
            when is_some ri.logic && is_logical_op op
         ->
          let pcompat =
            match oget ri.logic with `Full -> true | `ProductCompat -> false
          in

          let p' =
            match op_kind op, args with
            | Some (`Not), [f1]    when pcompat ->
               if verbose then print hyps "!";
               p_not_simpl_opt f1
            | Some (`Imp), [f1;f2] when pcompat ->
               if verbose then print hyps "=>";
               p_imp_simpl_opt f1 f2
            | Some (`Iff), [f1;f2] when pcompat ->
               if verbose then print hyps "<=>";
               p_iff_simpl_opt f1 f2


            | Some (`And `Asym), [f1;f2] ->
               if verbose then print hyps "&&";
               p_anda_simpl_opt f1 f2
            | Some (`Or  `Asym), [f1;f2] ->
               if verbose then print hyps "||";
               p_ora_simpl_opt f1 f2
            | Some (`And `Sym ), [f1;f2] ->
               if verbose then print hyps "/\\";
               p_and_simpl_opt f1 f2
            | Some (`Or  `Sym ), [f1;f2] ->
               if verbose then print hyps "\\/";
               p_or_simpl_opt f1 f2
            | Some (`Int_le   ), [f1;f2] ->
               if verbose then print hyps "Int.(<=)";
               p_int_le_simpl_opt f1 f2
            | Some (`Int_lt   ), [f1;f2] ->
               if verbose then print hyps "Int.(<)";
               p_int_lt_simpl_opt f1 f2
            | Some (`Real_le  ), [f1;f2] ->
               if verbose then print hyps "Real.(<=)";
               p_real_le_simpl_opt f1 f2
            | Some (`Real_lt  ), [f1;f2] ->
               if verbose then print hyps "Real.(<)";
               p_real_lt_simpl_opt f1 f2
            | Some (`Int_add  ), [f1;f2] ->
               if verbose then print hyps "Int.(+)";
               p_int_add_simpl_opt f1 f2
            | Some (`Int_opp  ), [f]     ->
               if verbose then print hyps "Int.[-]";
               p_int_opp_simpl_opt f
            | Some (`Int_mul  ), [f1;f2] ->
               if verbose then print hyps "Int.( * )";
               p_int_mul_simpl_opt f1 f2
            | Some (`Real_add ), [f1;f2] ->
               if verbose then print hyps "Real.(+)";
               p_real_add_simpl_opt f1 f2
            | Some (`Real_opp ), [f]     ->
               if verbose then print hyps "Real.[-]";
               p_real_opp_simpl_opt f
            | Some (`Real_mul ), [f1;f2] ->
               if verbose then print hyps "Real.( * )";
               p_real_mul_simpl_opt (EcEnv.LDecl.toenv hyps) f1 f2
            | Some (`Real_inv ), [f]     ->
               if verbose then print hyps "inv";
               p_real_inv_simpl_opt f
            | Some (`Eq       ), [f1;f2] -> begin
                if verbose then print hyps "(=)";
                match (p_destr_app f1), (p_destr_app f2) with
                | ({ p_node = Pat_Axiom (Axiom_Op (_, op1, _, _))}, args1),
                  ({ p_node = Pat_Axiom (Axiom_Op (_, op2, _, _))}, args2)
                     when EcEnv.Op.is_dtype_ctor (EcEnv.LDecl.toenv hyps) op1
                          && EcEnv.Op.is_dtype_ctor (EcEnv.LDecl.toenv hyps) op2 ->

                   let idx p =
                     let idx = EcEnv.Op.by_path p (EcEnv.LDecl.toenv hyps) in
                     snd (EcDecl.operator_as_ctor idx)
                   in
                   if   idx op1 <> idx op2
                   then Some p_false
                   else Some (p_ands (List.map2 p_eq args1 args2))

                | _ -> if p_equal f1 f2 || eq hyps ri f1 f2 then Some p_true
                       else p_eq_simpl_opt f1 f2
              end

            | _ when ri.delta_p op -> begin
                match h_red_op_opt hyps ri s op tys with
                | None -> None
                | Some op -> Some (p_app_simpl ~ho op args ty)
              end

            | _ -> Some p
          in
          begin
            match p' with
            | Some p -> Some p
            | _ ->
               if verbose then print hyps "logical other";
               omap
                 (fun x -> p_app fo x ty)
                 (h_red_args (fun x -> x) (h_red_pattern_opt eq)
                    hyps ri s args)
          end

       (* δ-reduction *)
       | Sym_Form_App (ty,_ho),
         ({ p_node = Pat_Axiom (Axiom_Op (delta, op,lty,_)) }
          | { p_node =
                Pat_Meta_Name
                  (Some
                     { p_node = Pat_Axiom (Axiom_Op (delta, op,lty,_)) },_,_)})
         :: args
            when delta && ri.delta_p op ->
          if verbose then
            (print hyps "delta";
             let env = EcEnv.LDecl.toenv hyps in
             EcEnv.notify env `Warning "%a"
               EcPrinting.pp_path op);
          let op = h_red_op_opt hyps ri s op lty in
          omap (fun op -> update_higher_order (p_app_simpl op args ty)) op

       (* η-reduction *)
       | Sym_Quant (Llambda, (x, OGTty (Some t1))::binds),
         [{ p_node = Pat_Fun_Symbol (Sym_Form_App (ty,ho), pn::pargs)}]
            when p_can_eta x (pn, pargs) ->
          if verbose then print hyps "eta";
          Some (p_quant Llambda binds
                  (p_app ~ho pn (List.take (List.length pargs - 1) pargs)
                     (omap (tfun t1) ty)))

       (* contextual rule - let *)
       | Sym_Form_Let lp, [p1;p2] ->
          if verbose then print hyps "context let";
          omap (fun p1 -> p_let lp p1 p2) (h_red_pattern_opt eq hyps ri s p1)

       (* Contextual rule - application args. *)
       | Sym_Form_App (ty,ho), p1::args ->
          if verbose then
            (print hyps "context app";
             let env = EcEnv.LDecl.toenv hyps in
             let ppe = EcPrinting.PPEnv.ofenv env in
             EcEnv.notify env `Warning "------------ %a : (%s, %s, %s) delta"
               (EcPrinting.pp_pattern ppe) p1
               (match p1.p_node with
                | Pat_Axiom (Axiom_Op (b,_,_,_)) -> if b then "true" else "false"
                | _ -> "false")
               (match p1.p_node with
                | Pat_Axiom (Axiom_Op (_,p,_,_)) ->
                   if ri.delta_p p then "true" else "false"
                | _ -> "false")
               (if ri.delta_p (EcPath.psymbol "test") then "true" else "false"));

          omap (fun p1 -> p_app ~ho p1 args ty) (h_red_pattern_opt eq hyps ri s p1)

       (* (\* Contextual rule - bindings *\)
        * | Sym_Form_Quant (Lforall as t, b), [p1]
        *   | Sym_Form_Quant (Lexists as t, b), [p1]
        *      when ri.logic = Some `Full -> begin
        *     let ctor = match t with
        *       | Lforall -> f_forall_simpl
        *       | Lexists -> f_exists_simpl
        *       | _       -> assert false in
        *
        *     try
        *       let env = Mod.add_mod_binding b env in
        *       ctor b (h_red ri env hyps f1)
        *     with NotReducible ->
        *       let f' = ctor b f1 in
        *       if f_equal f f' then raise NotReducible else f'
        *   end *)

       | _ -> None
  with
  | EcEnv.NotReducible -> None

and h_red_axiom_opt ?(verbose:bool=false) eq hyps ri s (a : axiom) =
  try match a with
      | Axiom_Hoarecmp _    -> None
      | Axiom_Memory m      ->
         let o = h_red_mem_opt hyps ri s m in
         if verbose && is_some o then print hyps "ax : mem"; o
      | Axiom_MemEnv m      ->
         let o = h_red_memenv_opt hyps ri s m in
         if verbose && is_some o then print hyps "ax : mem"; o
      | Axiom_Prog_Var pv   ->
         let o = h_red_prog_var_opt hyps ri s pv in
         if verbose && is_some o then print hyps "ax : mem"; o
      | Axiom_Op (b,op,l,_) ->
         let o = if b then h_red_op_opt hyps ri s op l else None in
         if verbose && is_some o then print hyps "ax : mem"; o
      | Axiom_Mpath_top m   ->
         let o = h_red_mpath_top_opt hyps ri s m in
         if verbose && is_some o then print hyps "ax : mem"; o
      | Axiom_Mpath m       ->
         let o = h_red_mpath_opt hyps ri s m in
         if verbose && is_some o then print hyps "ax : mem"; o
      | Axiom_Stmt stmt     ->
         let o = h_red_stmt_opt eq hyps ri s stmt in
         if verbose && is_some o then print hyps "ax : mem"; o
      | Axiom_Lvalue lv     ->
         let o = h_red_lvalue_opt hyps ri s lv in
         if verbose && is_some o then print hyps "ax : mem"; o
      | Axiom_Xpath x       ->
         let o = h_red_xpath_opt hyps ri s x in
         if verbose && is_some o then print hyps "ax : mem"; o
      | Axiom_Local (id,_t) ->
         let o = reduce_local_opt hyps ri id in
         if verbose && is_some o then print hyps "ax : mem"; o
      | Axiom_Int _         -> None
  with
  | EcEnv.NotReducible -> None

and h_red_mem_opt _hyps ri s m : pattern option =
  if ri.delta_h m
  then
    match MName.find_opt m s.ps_patloc with
    | Some _ as p -> p
    | None -> MName.find_opt m s.ps_patloc
  else None

and h_red_memenv_opt _hyps ri s m =
  if ri.delta_h (fst m)
  then MName.find_opt (fst m) s.ps_patloc
  else None

and h_red_prog_var_opt hyps ri s pv =
  omap (fun x -> p_prog_var x pv.pv_kind) (h_red_xpath_opt hyps ri s pv.pv_name)

and h_red_op_opt hyps ri _s op lty =
  if ri.delta_p op
  then
    try Some (pat_form (EcEnv.Op.reduce (EcEnv.LDecl.toenv hyps) op lty))
    with EcEnv.NotReducible -> None
  else None

and h_red_mpath_top_opt _hyps ri s m =
  if ri.modpath
  then
    match m with
    | `Concrete _ -> None
    | `Local id -> MName.find_opt id s.ps_patloc
  else None

and h_red_mpath_opt hyps ri s m =
  if ri.modpath
  then match h_red_mpath_top_opt hyps ri s m.m_top with
       | Some p ->
          Some (p_mpath p (List.map pat_mpath m.m_args))
       | None ->
          omap (fun l -> p_mpath (pat_mpath_top m.m_top) l)
            (h_red_args pat_mpath h_red_mpath_opt hyps ri s m.m_args)
  else None

and h_red_stmt_opt eq hyps ri s stmt =
  omap (fun l -> pat_fun_symbol Sym_Stmt_Seq l)
    (h_red_args (fun x -> x) (h_red_pattern_opt eq) hyps ri s
       (List.map pat_instr stmt.s_node))

and h_red_lvalue_opt hyps ri s = function
  | LvVar (pv,ty) ->
     omap (fun x -> p_lvalue_var x ty) (h_red_prog_var_opt hyps ri s pv)
  | LvTuple l ->
     omap p_lvalue_tuple
       (h_red_args (fun (pv,ty) -> pat_lvalue (LvVar(pv,ty)))
          (fun hyps ri s x -> h_red_lvalue_opt hyps ri s (LvVar x)) hyps ri s l)
  | LvMap _ -> None

and h_red_xpath_opt hyps ri s x =
  if ri.modpath
  then match h_red_mpath_opt hyps ri s x.x_top with
       | Some p -> Some (p_xpath p (pat_op x.x_sub [] None))
       | None -> None
  else None

let rec test_eq f p1 p2 =
  match p_destr_app p1, p_destr_app p2 with
  | (pop1, [p11;p13]), (pop2, [p21;p23])
       when op_equal pop1 f && op_equal pop2 f -> begin
      match p_destr_app p11, p_destr_app p23 with
      | (pop1, [p11;p12]), (pop2, [p21;p22])
           when op_equal pop1 f && op_equal pop2 f ->
         pat_eq p11 p21 && pat_eq p12 p22 && pat_eq p13 p23
      | _ ->
      match p_destr_app p13, p_destr_app p21 with
      | (pop1, [p12;p13]), (pop2, [p21;p22])
           when op_equal pop1 f && op_equal pop2 f ->
         pat_eq p11 p21 && pat_eq p12 p22 && pat_eq p13 p23
      | _ -> false
    end

  | _ -> false

and pat_eq p1 p2 = p1 = p2
  || test_eq fop_int_add p1 p2
  || test_eq fop_int_mul p1 p2
  || test_eq fop_real_add p1 p2
  || test_eq fop_real_mul p1 p2

let h_red_pattern_opt ?(verbose:bool=false) eq h r s p =
  match h_red_pattern_opt ~verbose eq h r s p with
  | None -> None
  | Some p' ->
     if pat_eq p p'
     then begin
         (if verbose then
            let env = EcEnv.LDecl.toenv h in
            let ppe = EcPrinting.PPEnv.ofenv env in
            EcEnv.notify env `Warning
              "reduction was equal to previous patterns : %a"
              (EcPrinting.pp_pattern ppe) p');
           None
       end
     else Some p'

let h_red_axiom_opt ?(verbose:bool=false) eq h r s a =
  match h_red_axiom_opt ~verbose eq h r s a with
  | None -> None
  | Some p' -> if pat_eq (pat_axiom a) p' then None else Some p'
back to top