Module pretty

Require Export numbers option.
Require Import Ascii String ars.

Infix "+:+" := String.append (at level 60, right associativity) : C_scope.
Arguments String.append _ _ : simpl never.

Instance assci_eq_dec : ∀ a1 a2, Decision (a1 = a2) := ascii_dec.
Instance string_eq_dec (s1 s2 : string) : Decision (s1 = s2).
Proof. solve_decision. Defined.
Instance: Injective (=) (=) (String.append s1).
Proof. intros s1 ???. induction s1; simplify_equality'; f_equal'; auto. Qed.

Class Pretty A := pretty : Astring.
Definition pretty_N_char (x : N) : ascii :=
  match x with
  | 0 => "0" | 1 => "1" | 2 => "2" | 3 => "3" | 4 => "4"
  | 5 => "5" | 6 => "6" | 7 => "7" | 8 => "8" | _ => "9"
  end%char%N.
Definition pretty_N_go (x : N)
    (go : ∀ y, (y < x)%Nstringstring) (s : string) : string :=
  match decide (0 < x)%N with
  | left H => go (x `div` 10)%N (N.div_lt x 10 H eq_refl)
     (String (pretty_N_char (x `mod` 10)) s)
  | right _ => s
  end.
Instance pretty_N : Pretty N := λ x,
  Fix_F _ pretty_N_go (wf_guard 32 N.lt_wf_0 x) ""%string.
Instance pretty_N_injective : Injective (@eq N) (=) pretty.
Proof.
  assert (∀ x y, x < 10 → y < 10 →
    pretty_N_char x = pretty_N_char yx = y)%N.
  { compute; intros. by repeat (discriminate || case_match). }
  set (f x (acc : Acc _ x) := Fix_F _ pretty_N_go acc).
  cut (∀ x acc y acc' s s', length s = length s' →
    f x acc s = f y acc' s' → x = ys = s').
  { intros help x y ?. eapply help; eauto. }
  assert (∀ x acc s, ¬length (f x acc s) < length s) as help.
  { setoid_rewrite <-Nat.le_ngt.
    fix 2; intros x [?] s; simpl. unfold pretty_N_go; fold pretty_N_go.
    destruct (decide (0 < x))%N; [|auto].
    etransitivity; [|eauto]. simpl; lia. }
  fix 2; intros x [?] y [?] s s' ?; simpl.
  unfold pretty_N_go; fold pretty_N_go; intros Hs.
  destruct (decide (0 < x))%N, (decide (0 < y))%N;
    try match goal with
    | H : f ?x ?acc ?s = _ |- _ =>
       destruct (help x acc s); rewrite H; simpl; lia
    | H : _ = f ?x ?acc ?s |- _ =>
       destruct (help x acc s); rewrite <-H; simpl; lia
    end; auto with lia.
  apply pretty_N_injective in Hs; [|by f_equal']; destruct Hs.
  simplify_equality; split; [|done].
  rewrite (N.div_mod x 10), (N.div_mod y 10) by done.
  auto using N.mod_lt with f_equal.
Qed.

Instance pretty_Z : Pretty Z := λ x,
  match x with
  | Z0 => "" | Zpos x => pretty (Npos x) | Zneg x => "-" +:+ pretty (Npos x)
  end%string.