From Stdlib Require Import ZArith.
From Stdlib Require Import ConstructiveEpsilon.

Definition Z_inj_nat (z : Z) : nat :=
  match z with
  | Z0 => 0
  | Zpos p => Pos.to_nat (p~0)
  | Zneg p => Pos.to_nat (Pos.pred_double p)
  end.

Definition Z_inj_nat_rev (n : nat) : Z :=
  match n with
  | O => 0
  | S n' => match Pos.of_nat n with
            | xH =>   Zneg xH
            | xO p => Zpos p
            | xI p => Zneg (Pos.succ p)
            end
  end.

Lemma Pos_pred_double_inj: forall (p q : positive),
    Pos.pred_double p = Pos.pred_double q -> p = q.
Proof.
  intros p q H.
  apply (f_equal Pos.succ) in H.
  do 2 rewrite Pos.succ_pred_double in H.
  inversion H; reflexivity.
Qed.

Lemma Z_inj_nat_id: forall (z : Z),
  Z_inj_nat_rev (Z_inj_nat z) = z.
Proof.
  intros z.
  unfold Z_inj_nat, Z_inj_nat_rev.
  destruct z eqn:Hdz.
  - reflexivity.
  - rewrite Pos2Nat.id.
    destruct (Pos.to_nat p~0) eqn:Hd.
    + pose proof Pos2Nat.is_pos p~0 as H.
      rewrite <- Nat.neq_0_lt_0 in H.
      exfalso; apply H, Hd.
    + reflexivity.
  - rewrite Pos2Nat.id.
    destruct (Pos.to_nat (Pos.pred_double p)) eqn: Hd.
    + pose proof Pos2Nat.is_pos (Pos.pred_double p) as H.
      rewrite <- Nat.neq_0_lt_0 in H.
      exfalso; apply H, Hd.
    + destruct (Pos.pred_double p) eqn:Hd2.
      * rewrite <- Pos.pred_double_succ in Hd2.
        apply Pos_pred_double_inj in Hd2.
        rewrite Hd2; reflexivity.
      * apply (f_equal Pos.succ) in Hd2.
        rewrite Pos.succ_pred_double in Hd2.
        rewrite <- Pos.xI_succ_xO in Hd2.
        inversion Hd2.
      * change xH with (Pos.pred_double xH) in Hd2.
        apply Pos_pred_double_inj in Hd2.
        rewrite Hd2; reflexivity.
Qed.

Lemma Z_inj_nat_inj: forall (x y : Z),
    Z_inj_nat x = Z_inj_nat y -> x = y.
Proof.
  intros x y H.
  apply (f_equal Z_inj_nat_rev) in H.
  do 2 rewrite Z_inj_nat_id in H.
  assumption.
Qed.

Lemma constructive_indefinite_ground_description_Z:
  forall P : Z -> Prop,
  (forall z : Z, {P z} + {~ P z}) ->
  (exists z : Z, P z) -> {z : Z | P z}.
Proof.
  apply (constructive_indefinite_ground_description Z Z_inj_nat Z_inj_nat_rev Z_inj_nat_id).
Qed.
