Require Import Corelib.Setoids.Setoid.
Require Import Ltac2.Ltac2.

Axiom f: nat -> nat.
Definition g := f.

Axiom Foo1: nat -> Prop.
Axiom Foo2: nat -> Prop.
Axiom Impl: forall n: nat, Foo1 (f n) -> Foo2 (f n).

Create HintDb foo discriminated.
#[export] Hint Constants Opaque : foo.
#[export] Hint Resolve Impl : foo.

Goal forall x, Foo1 (f x) -> Foo2 (g x).
Proof.
  auto with foo.
  #[export] Hint Transparent g : foo.
  auto with foo.
Qed.

Goal forall (x: nat), exists y, f x = g y.
Proof.
  intros.
  eexists.
  unify f g.
  lazy_match! goal with
  | [ |- ?a ?b = ?rhs ] => unify ($a $b) $rhs
  end.
Abort.

Goal forall (x: nat), exists y, f x = g y.
Proof.
  intros.
  eexists.
  Unification.unify TransparentState.full 'f 'g.
  lazy_match! goal with
  | [ |- ?a ?b = ?rhs ] => Unification.unify_with_full_ts '($a $b) rhs
  end.
Abort.

Goal True.
Proof.
  Fail Unification.unify TransparentState.empty '(1 + 1) '2.
  Unification.unify TransparentState.full '(1 + 1) '2.
  Unification.unify (TransparentState.current ()) '(1 + 1) '2.
  Opaque Nat.add.
  Fail Unification.unify (TransparentState.current ()) '(1 + 1) '2.
  Succeed Unification.unify TransparentState.full '(1 + 1) '2.
  exact I.
Qed.


(* Test that by clause of assert doesn't eat all semicolons:
   https://github.com/rocq-prover/rocq/issues/17491 *)
Goal forall (a: nat), a = a.
Proof.
  intros.
  assert (a = a) by Std.reflexivity ();
  assumption.
Qed.

(* Test that notations in by clause still work: *)
Goal forall (a: nat), a = a.
Proof.
  intros.
  assert (a = a) by exact eq_refl;
  assumption.
Qed.

Goal forall x, (forall (y : unit), y = x) -> forall (x: unit), x = x.
Proof.
  intros x H y.
  rewrite -> H at 1 2.
  reflexivity.
Qed.

Goal forall x, (forall (y : unit), x = y) -> forall (x: unit), x = x.
Proof.
  intros x H y.
  rewrite <- H at 1 2.
  reflexivity.
Qed.

Goal forall x, (forall y : unit, y = x) -> forall y : unit, y = y.
Proof.
  intros x H y.
  setoid_rewrite H at 1 2.
  setoid_rewrite <- (H y) at 1 2.
  setoid_rewrite H.
  reflexivity.
Qed.

Axiom x : unit.
Axiom H : forall y : unit, y = x.
Goal forall y : unit, y = y.
Proof.
  assert (H2 : tt = x).
  { setoid_rewrite H with (y := tt).
    reflexivity. }
  setoid_rewrite H with (y := tt) at 1 in H2.
  intros y.
  setoid_rewrite H with (y := y) at 1.
  setoid_rewrite H with (y := y) at 1.
  reflexivity.
Qed.


(* view intro patterns *)
Axiom lem : forall x:nat, bool -> x = x.

Axiom lem' : 3 = 3 -> False.

Goal bool -> 2 = 2.
Proof.
  Fail intros a%lem.
  eintros a%lem.
  revert a.
  intros b%lem'.
  destruct b.
Qed.

(* assert interns and typechecks the type as a type
   (bug #15094) *)

Inductive my_type :=
| my_el : my_type.

Definition my_coercion (_ : my_type) := True.

Coercion my_coercion : my_type >-> Sortclass.

Goal False.
Proof.
  (* expected type is sortclass -> coercion inserted *)
  assert my_el by exact I.

  (* interned as with type scope locally open *)
  assert (H' : nat * (0 * 0 = 0)) by (split; first [exact 0 | reflexivity]).
Abort.

Goal nat.
Proof.
  Std.apply true false [fun () => Control.plus (fun () => 'I) (fun _ => '0), Std.NoBindings] None.
Qed.

(* eassumption *)
Goal forall (x : nat) y z, x = y -> y = z -> x = z.
Proof.
  intros **.
  etransitivity; eassumption.
Qed.

(* cycle *)
Goal nat * bool.
Proof.
  split.
  all: cycle 1.
  - exact true.
  - exact 0.
Qed.

(* focus *)
Goal bool * nat * nat.
Proof.
  repeat split.
  all: Control.focus 2 3 (fun () => exact 0).
  exact true.
Qed.

(* exfalso *)
Goal False -> nat * nat.
Proof.
  intros oops.
  split.
  all: exfalso; assumption.
Qed.
