Library hanoi.triangular


From mathcomp Require Import all_ssreflect.


Set Implicit Arguments.

Unset Strict Implicit.

Unset Printing Implicit Defensive.


Lemma prednDl m n : 0 < m (m + n).-1 = m.-1 + n.

Proof.
by case: m. Qed.

Lemma prednDr m n : 0 < n (m + n).-1 = m + n.-1.

Proof.
by case: n ⇒ // n; rewrite addnS. Qed.

Lemma leq_sub_sub a b c : ((a - b) - (c - b)) a - c.

Proof.

have [aLb|bLa] := leqP b a; last first.

  rewrite (_ : a - _ = 0) ?sub0n //.

  by apply/eqP; rewrite subn_eq0 ltnW.

have [cLb|bLc] := leqP c b; last first.

  by rewrite subnBA ?subnK // ltnW.

rewrite (_ : c - _ = 0); last by apply/eqP; rewrite subn_eq0.

by rewrite subn0 leq_sub2l.

Qed.


Lemma leq_sub_add b a c : a - c (a - b) + (b - c).

Proof.

rewrite leq_subLR addnC -addnA.

have [/subnK->|H] := leqP c b.

  have [/subnK->//|H] := leqP b a.

  apply: leq_trans (ltnW H) _.

  by apply: leq_addl.

have := ltnW H.

rewrite -subn_eq0 ⇒ /eqP→.

rewrite addnC -leq_subLR.

by apply: leq_sub2l; apply: ltnW.

Qed.



Definition delta n := (n.+1 × n)./2.


Notation "'Δ' n " := (delta n) (format "'Δ' n", at level 10).


Compute zip (iota 1 20) (map delta (iota 1 20)).


Lemma deltaS n : delta n.+1 = delta n + n.+1.

Proof.

rewrite /delta -addn2 mulnDl mulnC halfD.

rewrite !odd_mul andbF add0n mul2n.

by rewrite -{4}(half_bit_double n.+1 false).

Qed.


Lemma delta_gt0 n : 0 < n 0 < delta n.

Proof.
by case: n ⇒ // n _; rewrite deltaS addnS ltnS leq_addr. Qed.

Lemma deltaE n : delta n = \sum_(i < n.+1) i.

Proof.

elim: n ⇒ [|n IH]; first by rewrite big_ord_recl big_ord0.

by rewrite big_ord_recr /= -IH deltaS.

Qed.


Compute zip (iota 0 11) (map delta (iota 0 11)).


Lemma delta_le m n : m n delta m delta n.

Proof.
by moveH; apply/half_leq/leq_mul. Qed.

Lemma delta_square n : (8 × delta n).+1 = n.*2.+1 ^ 2.

Proof.

elim: n ⇒ // n IH.

rewrite deltaS mulnDr -addSn IH.

rewrite doubleS -addn1 -addnS -addSn addn1.

rewrite sqrnD -addnA /=.

congr (_ + _).

rewrite mulnS.

rewrite [_ × 2]mulSn mulnDr addnA.

congr (_ + _).

by rewrite mulnCA -muln2 -!mulnA mulnC.

Qed.


Lemma geq_deltann n : n delta n.

Proof.

by case: n ⇒ // n; rewrite deltaS addnS ltnS leq_addl.

Qed.



Definition troot n :=
 let l := iota 0 n.+2 in
 (find (fun xn < delta x) l).-1.


Notation "∇ n" := (troot n) (format "∇ n", at level 10).


Compute zip (iota 0 11) (map troot (iota 0 11)).


Lemma troot_gt0 n : 0 < n 0 < troot n.

Proof.
by case: n. Qed.

Lemma delta_root_le m : delta (troot m) m.

Proof.

rewrite /troot leqNgt.

set l := iota _ _; set f := (fun __).

case E : _.-1 ⇒ [|n] //.

have /(before_find 0) :
   (find f l).-1 < find f l by rewrite prednK // E.

rewrite E nth_iota // /f ⇒ [->//|].

rewrite -[m.+2](size_iota 0) -E prednK; first by apply: find_size.

by case: find E.

Qed.


Lemma delta_root_gt m : m < delta (troot m).+1.

Proof.

rewrite /troot leqNgt.

set l := iota _ _; set f := (fun __).

have Hfl : has f l.

  apply/hasP; m.+1; first by rewrite mem_iota leq0n leqnn.

  rewrite /f /delta -{1}[m.+1](half_bit_double _ false).

  by apply/half_leq; rewrite add0n -mul2n leq_mul2r orbT.

have := nth_find 0 Hfl; rewrite {1}/f.

case E : _.-1 ⇒ [|n] //.

  case: find E ⇒ // [] [|n] //.

  by rewrite nth_iota //=; case: (m).

rewrite nth_iota.

  by rewrite -E prednK // ltnNge ltnS.

by rewrite -(size_iota 0 m.+2) -has_find.

Qed.


Lemma root_delta_le m n : (n troot m) = (delta n m).

Proof.

case: leqP ⇒ [/delta_le/leq_trans->//|dmLn].

  apply: delta_root_le.

apply/sym_equal/idP/negP.

rewrite -ltnNge.

by apply: leq_trans (delta_root_gt _) (delta_le dmLn).

Qed.


Lemma root_delta_lt m n : (troot m < n) = (m < delta n).

Proof.
by rewrite ltnNge root_delta_le -ltnNge. Qed.

Lemma troot_le m n : m n troot m troot n.

Proof.

by movemLn; rewrite root_delta_le (leq_trans (delta_root_le _)).

Qed.


Lemma trootE m n : (troot m == n) = (delta n m < delta n.+1).

Proof.

rewrite ltnNge -!root_delta_le -ltnNge.

by rewrite ltnS -eqn_leq.

Qed.


Lemma troot_delta n : troot (delta n) = n.

Proof.
by apply/eqP; rewrite trootE leqnn deltaS -addn1 leq_add2l. Qed.

Lemma leq_rootnn n : troot n n.

Proof.

by rewrite -{2}[n]troot_delta troot_le // geq_deltann.

Qed.



Definition tmod n := n - delta (troot n).


Lemma tmod_delta n : tmod (delta n) = 0.

Proof.
by rewrite /tmod troot_delta subnn. Qed.

Lemma tmodE n : n = delta (troot n) + tmod n.

Proof.
by rewrite addnC (subnK (delta_root_le _)). Qed.

Lemma tmod_le n : tmod n troot n.

Proof.
by rewrite leq_subLR -ltnS -addnS -deltaS delta_root_gt. Qed.

Lemma ltn_root m n : troot m < troot n m < n.

Proof.

rewrite root_delta_le deltaS ⇒ /(leq_trans _) → //.

by rewrite {1}[m]tmodE ltn_add2l ltnS tmod_le.

Qed.


Lemma leq_mod m n : troot m = troot n (tmod m tmod n) = (m n).

Proof.

by movetmEtn; rewrite {2}[m]tmodE {2}[n]tmodE tmEtn leq_add2l.

Qed.


Lemma ltn_mod m n : troot m = troot n (tmod m < tmod n) = (m < n).

Proof.

by movetmEtn; rewrite {2}[m]tmodE {2}[n]tmodE tmEtn ltn_add2l.

Qed.


Lemma troot_mod_case m :
         ((troot m.+1 == troot m) && (tmod m.+1 == (tmod m).+1))
      ||
         [&& troot m.+1 == (troot m).+1, tmod m.+1 == 0 & tmod m == troot m].

Proof.

have := troot_le (leqnSn m).

rewrite leq_eqVlt ⇒ /orP[/eqP He|He].

  by rewrite /tmod -He subSn ?eqxx // {2}[m]tmodE leq_addr.

rewrite orbC.

have: troot m.+1 == (troot m).+1.

  rewrite trootE (leq_trans (delta_le He)) //; last first.

    by rewrite {2}[m.+1]tmodE leq_addr.

  rewrite !deltaS {1}[m]tmodE -addnS -addnS -addnA.

  by rewrite leq_add2l (leq_trans _ (leq_addl _ _)) // !ltnS tmod_le.

move/eqPHe1.

rewrite He1 eqxx.

have := eqxx m.+1.

rewrite {1}[m]tmodE {1}[m.+1]tmodE He1 deltaS -addnS.

rewrite -!addnA eqn_add2l addSn eqSS ⇒ /eqP He2.

have := tmod_le m.

rewrite leq_eqVlt ⇒ /orP[/eqP He3|]; last first.

  by rewrite He2 ltnNge leq_addr.

rewrite He3 -(eqn_add2l (tmod m)) {1}He3 -He2 addn0.

by rewrite !eqxx.

Qed.


Lemma troot_mod_le m n :
   m n =
   ((troot m < troot n) || ((troot m == troot n) && (tmod m tmod n))).

Proof.

case: leqP ⇒ [|dmGdn] /= ; last first.

  apply/idP.

  apply: (leq_trans (_ : _ delta (troot m).+1)).

    by rewrite ltnW // delta_root_gt.

  apply: (leq_trans (_ : _ delta (troot n))).

    by apply: delta_le.

  by apply: delta_root_le.

rewrite leq_eqVlt ⇒ /orP[/eqP dnEdm|dmLdn].

  rewrite dnEdm eqxx /=.

  by rewrite {1}[m]tmodE {1}[n]tmodE dnEdm leq_add2l.

rewrite (gtn_eqF dmLdn) /=.

apply/idP/negP.

rewrite -ltnNge.

apply: (leq_trans (delta_root_gt _)).

apply: (leq_trans _ (delta_root_le _)).

by apply: delta_le.

Qed.


Lemma troot_mod_lt m n :
   m < n =
   ((troot m < troot n) || ((troot m == troot n) && (tmod m < tmod n))).

Proof.

case: (leqP (troot n) (troot m)) ⇒ [|dmGdn] /= ; last first.

  apply/idP.

  apply: (leq_trans (delta_root_gt _)).

  apply: (leq_trans (delta_le dmGdn)).

  by apply: delta_root_le.

rewrite leq_eqVlt ⇒ /orP[/eqP dnEdm|dmLdn].

  rewrite dnEdm eqxx /=.

  by rewrite {1}[m]tmodE {1}[n]tmodE dnEdm ltn_add2l.

rewrite (gtn_eqF dmLdn) /=.

apply/idP/negP.

rewrite -ltnNge ltnS ltnW //.

apply: (leq_trans (delta_root_gt _)).

apply: (leq_trans _ (delta_root_le _)).

by apply: delta_le.

Qed.



Definition tpair n := (troot n - tmod n, tmod n).


Compute zip (iota 0 20) (map tpair (iota 0 20)).


Definition pairt p := delta (p.1 + p.2) + p.2.


Lemma tpairt n : pairt (tpair n) = n.

Proof.

rewrite /tpair /pairt /= (subnK (tmod_le _)).

by rewrite /tmod addnC subnK // delta_root_le.

Qed.


Lemma tpairt_inv p : tpair (pairt p) = p.

Proof.

case: pa b.

rewrite /tpair /pairt /= /tmod.

have ->: ∇(Δ(a + b) + b) = a + b.

  apply/eqP.

  rewrite trootE leq_addr /= deltaS.

  by rewrite addnS ltnS addnCA leq_addl.

by rewrite [delta _ + _]addnC !addnK.

Qed.