Require Import BinNums Nat List BinInt.
Require Import Lia.
Require Import LibUtils Isomorphism ListAdd.
Import ListNotations.
Module Internal.
Inductive BinaryDigit
  := | 
bin_digit0 | 
bin_digit1.
Fixpoint pos_to_digits (
p:
positive) : 
list BinaryDigit
  := 
match p with
     | 
xI p' => 
bin_digit1 :: (
pos_to_digits p')
     | 
xO p' => 
bin_digit0 :: (
pos_to_digits p')
     | 
xH => [
bin_digit1]
     
end.
Definition N_to_digits (
n:
N) : 
list BinaryDigit
  := 
match n with
     | 
N0 => 
nil
     | 
Npos p => 
pos_to_digits p
     end.
Fixpoint digits_to_pos (
l:
list BinaryDigit) : 
positive
  := 
match l with
     | 
nil => 
xH
     | [ 
_ ] => 
xH
     | 
bin_digit0 :: 
l' => 
xO (
digits_to_pos l')
     | 
bin_digit1 :: 
l' => 
xI (
digits_to_pos l')
     
end.
Fixpoint cleanup_zeros (
l:
list BinaryDigit) : 
list BinaryDigit
  := 
match l with
     | 
nil => 
nil
     | 
bin_digit0 :: 
l => 
cleanup_zeros l
     | 
_ => 
l
     end.
Definition fixup_trailing_zeros l
  := (
rev (
cleanup_zeros (
rev l))).
Lemma cleanup_zeros_app_middle l1 l2 : 
cleanup_zeros (
l1 ++ 
bin_digit1::
l2) = (
cleanup_zeros l1 ++ 
bin_digit1::
l2).
Proof.
  revert l2.
  induction l1; simpl; trivial; intros.
  destruct a; eauto.
Qed.
Lemma fixup_trailing_zeros_app_middle l1 l2 : 
fixup_trailing_zeros (
l1 ++ 
bin_digit1::
l2) = (
l1 ++ 
bin_digit1::
fixup_trailing_zeros l2).
Proof.
Lemma fixup_trailing_zeros_end0 l1 : 
fixup_trailing_zeros (
l1 ++ [
bin_digit0]) = 
fixup_trailing_zeros l1.
Proof.
Lemma fixup_trailing_zeros_end1 l1 : 
fixup_trailing_zeros (
l1 ++ [
bin_digit1]) = 
l1 ++ [
bin_digit1].
Proof.
Definition digits_to_N (
l:
list BinaryDigit) : 
N
       := 
let l_clean := 
fixup_trailing_zeros l in
          match l_clean with
          | 
nil => 
N0
          | 
_ => 
Npos (
digits_to_pos l_clean)
          
end.
Lemma pos_to_digits_nnil p : 
pos_to_digits p <> 
nil.
Proof.
  destruct p; simpl; congruence.
Qed.
Definition canon_digits digits := { 
ld | 
digits = 
ld ++ [
bin_digit1] }.
Lemma canon_digits_dec digits : 
canon_digits digits + {
last digits bin_digit0 = 
bin_digit0}.
Proof.
  unfold canon_digits.
  
induction digits; 
simpl.
  - 
right; 
trivial.
  - 
destruct IHdigits.
    + 
destruct s as [
ld pf].
      
left.
      
rewrite pf.
      
exists (
a::
ld); 
simpl.
      
trivial.
    + 
destruct digits.
      * 
destruct a; 
simpl; [
eauto | ].
        
left; 
exists nil; 
simpl.
        
trivial.
      * 
right; 
trivial.
Qed.
 
Lemma cleanup_zeros_form l :
  {
l1 |
       
l = 
l1 ++ 
cleanup_zeros l
       /\ (
forall x, 
In x l1 -> 
x = 
bin_digit0)
       /\ (
forall a l2, 
cleanup_zeros l = 
a::
l2 -> 
a = 
bin_digit1)}.
Proof.
  induction l.
  - 
exists nil; 
simpl.
    
intuition congruence.
  - 
destruct IHl as [
l1 [
H1 [
H2 H3]]].
    
destruct a.
    + 
exists (
bin_digit0 :: 
l1).
      
simpl.
      
rewrite <- 
H1.
      
intuition.
    + 
exists nil; 
simpl; 
intuition congruence.
Qed.
 
Lemma cleanup_zeros_zero digits :
  
cleanup_zeros digits = 
nil <-> 
Forall (
eq bin_digit0) 
digits.
Proof.
  rewrite Forall_forall.
  
induction digits; 
simpl.
  - 
intuition.
  - 
destruct a; 
simpl.
    + 
intuition.
    + 
split; 
intros HH.
      * 
discriminate.
      * 
specialize (
HH bin_digit1).
        { 
cut_to HH.
          - 
discriminate.
          - 
eauto.
        } 
Qed.
 
              
Lemma fixup_trailing_zeros_canon digits :
  
canon_digits (
fixup_trailing_zeros digits) +
  {
Forall (
eq bin_digit0) 
digits}.
Proof.
Lemma fixup_trailing_zeros_canon_if digits :
  ~ 
Forall (
eq bin_digit0) 
digits ->
  
canon_digits (
fixup_trailing_zeros digits).
Proof.
Lemma fixup_trailing_zeros_of_canon digits :
  
canon_digits digits ->
  
fixup_trailing_zeros digits = 
digits.
Proof.
Lemma Forall_rev {
A} 
P (
l:
list A) : 
Forall P l <-> 
Forall P (
rev l).
Proof.
Lemma fixup_trailing_zeros_zero digits :
  
fixup_trailing_zeros digits = 
nil <-> 
Forall (
eq bin_digit0) 
digits.
Proof.
Lemma pos_to_digits_canon p : 
canon_digits (
pos_to_digits p).
Proof.
  induction p; 
simpl.
  - 
destruct IHp.
    
rewrite e.
    
exists (
bin_digit1 :: 
x).
    
reflexivity.
  - 
destruct IHp.
    
rewrite e.
    
exists (
bin_digit0 :: 
x).
    
reflexivity.
  - 
exists nil.
    
reflexivity.
Qed.
 
Lemma digits_to_pos_to_digits p : 
digits_to_pos (
pos_to_digits p) = 
p.
Proof.
Lemma digits_to_N_to_digits n : 
digits_to_N (
N_to_digits n) = 
n.
Proof.
Lemma cleanup_zeros_app_repeat l d : 
cleanup_zeros ((
repeat bin_digit0 d) ++ 
l) = 
cleanup_zeros l.
Proof.
  induction d; simpl; trivial.
Qed.
Lemma repeat_plus_app  {
A} (
d:
A) 
n1 n2 : 
repeat d (
n1+
n2) = 
repeat d n1 ++ 
repeat d n2.
Proof.
  revert n2.
  induction n1; simpl; trivial; intros.
  rewrite IHn1; trivial.
Qed.
Lemma repeat_rev {
A} (
d:
A) 
n : 
rev (
repeat d n) = 
repeat d n.
Proof.
  induction n; 
trivial.
  
transitivity (
repeat d (
n + 1)).
  - 
rewrite repeat_plus_app.
    
simpl.
    
rewrite IHn.
    
trivial.
  - 
f_equal.
    
lia.
Qed.
 
  
Lemma fixup_trailing_zeros_app_repeat x d : 
fixup_trailing_zeros (
x ++ 
repeat bin_digit0 d) = 
fixup_trailing_zeros x.
Proof.
Lemma digits_to_N_to_digits_rep n x : 
digits_to_N (
N_to_digits n ++ 
repeat bin_digit0 x) = 
n.
Proof.
Lemma digits_to_N_pos_to_digits_rep p x : 
digits_to_N (
pos_to_digits p ++ 
repeat bin_digit0 x) = 
Npos p.
Proof.
Lemma pos_to_digits_to_pos digits :
  
canon_digits digits ->
  
pos_to_digits (
digits_to_pos digits) = 
digits.
Proof.
  induction digits; 
simpl.
  - 
intros [? ?].
    
symmetry in e.
    
apply app_eq_nil in e.
    
destruct e.
    
discriminate.
  - 
intros [
ld eqq].
    
destruct ld; 
simpl in eqq; 
inversion eqq; 
clear eqq.
    + 
reflexivity.
    + 
subst.
      
cut_to IHdigits; [ | 
eauto].
      
destruct b; 
simpl.
      * 
destruct (
ld ++ [
bin_digit1]); 
simpl in *; 
congruence.
      * 
destruct (
ld ++ [
bin_digit1]); 
simpl in *; 
congruence.
      * 
eexists; 
reflexivity.
Qed.
 
Lemma N_to_digits_to_N_fixup digits :
  
N_to_digits (
digits_to_N digits) = 
fixup_trailing_zeros digits.
Proof.
Lemma N_to_digits_to_N digits :
  
canon_digits digits ->
  
N_to_digits (
digits_to_N digits) = 
digits.
Proof.
Fixpoint interleave {
A:
Type} (
l1 l2 : 
list A) {
struct l1} : 
list A
  := 
match l1, 
l2 with
     | 
nil, 
l2 => 
l2
     | 
l1, 
nil => 
l1
     | 
x::
l1', 
y::
l2' => 
x::
y::(
interleave l1' 
l2')
     
end.
Lemma interleave_length_eq {
A:
Type} (
l1 l2 : 
list A) :
  
length (
interleave l1 l2) = 
length l1 + 
length l2.
Proof.
  revert l2.
  induction l1; destruct l2; simpl in *; trivial.
  - auto.
  - rewrite IHl1.
    lia.
Qed.
Fixpoint uninterleave {
A:
Type} (
l : 
list A) : (
list A*
list A)
  := 
match l with
     | 
x::
y::
l' => 
let (
l1,
l2) := 
uninterleave l' 
in
                   (
x::
l1,
y::
l2)
     | 
_ => (
nil, 
nil)
     
end.
Lemma uninterleave_interleave {
A:
Type} (
l1 l2:
list A) :
  
length l1 = 
length l2 ->
  (
uninterleave (
interleave l1 l2)) = (
l1, 
l2).
Proof.
  revert l2.
  induction l1; destruct l2; simpl; trivial; intros eqq; try discriminate.
  inversion eqq; clear eqq.
  rewrite (IHl1 _ H0).
  trivial.
Qed.
Lemma uninterleave_unfold {
A:
Type} (
l:
list A) 
a b :
  (
uninterleave (
a::
b::
l)) = (
a::(
fst (
uninterleave l)), 
b::(
snd (
uninterleave l))).
Proof.
Program Fixpoint EvenList_ind {
A:
Type} (
P:
list A->
Prop)
    (
pfnil:
P nil)
    (
pfconscons:
forall a b l, 
P l -> 
P (
a::
b::
l)) 
l {
struct l} :
  
PeanoNat.Nat.Even (
length l) -> 
P l
  := 
fun pfl =>
       
match l with
       | 
nil => 
pfnil
       | 
x::
y::
l => 
pfconscons x y l (
EvenList_ind P pfnil pfconscons l _)
       | 
_::
_ => 
_
       end.
Next Obligation.
  destruct pfl as [n npf].
  simpl in npf.
  exists (n-1).
  destruct n; simpl; [lia | ].
  destruct n; simpl; [lia | ].
  simpl in npf.
  inversion npf.
  lia.
Qed.
Next Obligation.
  destruct (
wildcard'0); 
simpl in *.
  - 
destruct pfl as [? ?]; 
lia.
  - 
elim (
H _ _ _ (
eq_refl _)).
Qed.
 
Lemma uninterleave_odd_skip {
A:
Type} (
l:
list A) 
a :
  
PeanoNat.Nat.Even (
length l) ->
  (
uninterleave (
l ++ [
a])) = 
uninterleave l.
Proof.
  intros pfeven.
  
revert a.
  
pattern l.
  
revert l pfeven.
  
apply EvenList_ind.
  - 
simpl; 
trivial.
  - 
intros.
    
replace ((
a :: 
b :: 
l) ++ [
a0]) 
with (
a :: 
b :: (
l ++ [
a0])) 
by reflexivity.
    
repeat rewrite uninterleave_unfold.
    
rewrite H.
    
trivial.
Qed.
 
Lemma uninterleave_even_end {
A:
Type} (
l:
list A) 
a b :
  
PeanoNat.Nat.Even (
length l) ->
  (
uninterleave (
l ++ [
a;
b])) = (
fst (
uninterleave l) ++ [
a], 
snd (
uninterleave l) ++ [
b]).
Proof.
  intros pfeven.
  
revert a b.
  
pattern l.
  
revert l pfeven.
  
apply EvenList_ind.
  - 
simpl; 
trivial.
  - 
intros.
    
replace ((
a :: 
b :: 
l) ++ [
a0; 
b0]) 
with (
a :: 
b :: (
l ++ [
a0; 
b0])) 
by reflexivity.
    
repeat rewrite uninterleave_unfold.
    
rewrite H.
    
trivial.
Qed.
 
Lemma interleave_uninterleave {
A:
Type} (
l:
list A) :
  
PeanoNat.Nat.Even (
length l) ->
  (
interleave (
fst (
uninterleave l)) (
snd (
uninterleave l))) = 
l.
Proof.
Definition interleave_with_end_padding {
A:
Type} (
l1:
list A) (
def1:
A) (
l2:
list A) (
def2:
A) : 
list A
 := 
interleave (
l1 ++ 
repeat def1 (
length l2 - 
length l1)) (
l2 ++ 
repeat def2 (
length l1 - 
length l2)).
Lemma interleave_with_end_padding_ge {
A:
Type} (
l1:
list A) (
def1:
A) (
l2:
list A) (
def2:
A) :
  
length l1 >= 
length l2 ->
  
interleave_with_end_padding l1 def1 l2 def2 = 
interleave l1 (
l2 ++ 
repeat def2 (
length l1 - 
length l2)).
Proof.
Lemma interleave_with_end_padding_le {
A:
Type} (
l1:
list A) (
def1:
A) (
l2:
list A) (
def2:
A) :
  
length l1 <= 
length l2 ->
  
interleave_with_end_padding l1 def1 l2 def2 = 
interleave (
l1 ++ 
repeat def1 (
length l2 - 
length l1)) 
l2.
Proof.
Lemma interleave_with_end_padding_eq {
A:
Type} (
l1:
list A) (
def1:
A) (
l2:
list A) (
def2:
A) :
  
length l1 = 
length l2 ->
  
interleave_with_end_padding l1 def1 l2 def2 = 
interleave l1 l2.
Proof.
Definition encode_digits_pair (
x y : 
list BinaryDigit) : 
list BinaryDigit
  := 
interleave_with_end_padding x bin_digit0 y bin_digit0.
Definition decode_digits_pair (
xy : 
list BinaryDigit) : (
list BinaryDigit * 
list BinaryDigit)
  := 
uninterleave xy.
Definition encode_pair (
x:
N) (
y:
N) : 
N
  := 
digits_to_N (
encode_digits_pair (
N_to_digits x) (
N_to_digits y)).
Definition make_even_digits digits
  := 
if Nat.even (
length digits) 
then digits else (
digits ++ [
bin_digit0]).
Definition decode_pair_to_digits (
xy:
N) : (
list BinaryDigit)*(
list BinaryDigit)
  :=  
let digits := 
N_to_digits xy in
       let digits' := 
make_even_digits digits in
       decode_digits_pair digits'.
Definition decode_pair (
xy:
N) : 
N*
N
  := 
if BinNat.N.eq_dec xy BinNat.N.zero then (
BinNat.N.zero,
BinNat.N.zero)%
N else
       let xypair := 
decode_pair_to_digits xy in
       (
digits_to_N (
fst xypair), 
digits_to_N (
snd xypair)).
Lemma decode_pair_to_digits_digits_to_N digits :
  
canon_digits digits ->
  
decode_pair_to_digits (
digits_to_N digits) = 
uninterleave (
make_even_digits digits).
Proof.
  
Lemma interleave_in1 {
A} (
l1 l2:
list A) :
  
forall x,
  
In x l1 ->
  
In x (
interleave l1 l2).
Proof.
  revert l2.
  induction l1; simpl.
  - intuition.
  - intros l2 x [eqq |inx].
    + subst.
      destruct l2; simpl; tauto.
    + destruct l2; simpl; eauto.
Qed.
Lemma interleave_in2 {
A} (
l1 l2:
list A) :
  
forall x,
  
In x l2 ->
  
In x (
interleave l1 l2).
Proof.
  revert l2.
  induction l1; simpl.
  - intuition.
  - intros l2 x eqq.
    destruct l2; simpl in *; intuition eauto.
Qed.
Lemma in_interleave  {
A} (
l1 l2:
list A) :
  
forall x,
    
In x (
interleave l1 l2) ->
    
In x l1 \/ 
In x l2.
Proof.
  revert l2.
  induction l1; simpl; [ eauto | ].
  destruct l2; simpl; [eauto | ].
  intuition subst.
  destruct (IHl1 _ _ H); tauto.
Qed.
  
  
Lemma interleave_with_end_padding_in1 {
A} (
l1 l2:
list A) 
def1 def2:
  
forall x,
  
In x l1 ->
  
In x (
interleave_with_end_padding l1 def1 l2 def2).
Proof.
Lemma interleave_with_end_padding_in2 {
A} (
l1 l2:
list A) 
def1 def2:
  
forall x,
  
In x l2 ->
  
In x (
interleave_with_end_padding l1 def1 l2 def2).
Proof.
Lemma in_interleave_with_end_padding {
A} (
l1 l2:
list A) 
def1 def2:
  
forall x,
    
In x (
interleave_with_end_padding l1 def1 l2 def2) ->
    
In x l1 \/ 
In x l2 \/ 
x = 
def1 \/ 
x = 
def2.
Proof.
  Hint Resolve repeat_spec : 
list.
  
intros.
  
destruct (
in_interleave _ _ x H)
  ; 
rewrite in_app_iff in *
  ; 
intuition eauto with list.
Qed.
 
  
Lemma encode_digits_pair0 l1 l2 :
  
Forall (
eq bin_digit0) (
encode_digits_pair l1 l2) <->
  
Forall (
eq bin_digit0) 
l1 /\
  
Forall (
eq bin_digit0) 
l2.
Proof.
Lemma N_to_digits0 n : 
Forall (
eq bin_digit0) (
N_to_digits n) <-> 
n = 
BinNat.N.zero.
Proof.
Lemma digits_to_N_fixup_encode_0 n1 n2 :
  
digits_to_N (
encode_digits_pair (
N_to_digits n1) (
N_to_digits n2)) = 
BinNat.N.zero ->
  
n1 = 
BinNat.N.zero /\ 
n2 = 
BinNat.N.zero.
Proof.
Lemma interleave_decompose {
A} (
digits1 digits2:
list A) :
  
digits1 <> 
nil ->
  
length digits1 = 
length digits2 ->
 
exists (
l : 
list A),
    
forall d1 d2 : 
A,
    
interleave digits1 digits2 =
    
l ++ [
last digits1 d1; 
last digits2 d2].
Proof.
  revert digits2.
  
induction digits1; [
intuition | ].
  
destruct digits2; 
simpl; 
intros; [
discriminate | ].
  
destruct digits1; 
simpl.
  - 
destruct digits2; 
simpl in *; [ | 
lia].
    
exists nil; 
simpl; 
trivial.
  - 
destruct digits2; 
simpl in *; 
try discriminate.
    
inversion H0.
    
specialize (
IHdigits1 (
a2::
digits2)).
    
simpl in IHdigits1.
    
destruct IHdigits1 as [
l leq]
    ; 
intuition (
try congruence).
    
exists (
a::
a0::
l).
    
intros d1 d2.
    
rewrite (
leq d1 d2).
    
simpl; 
trivial.
Qed.
 
Lemma last_app_nnil {
A:
Type} (
l1 l2:
list A) (
d:
A) :
  
l2 <> 
nil ->
  
last (
l1 ++ 
l2) 
d = 
last l2 d.
Proof.
  intros.
  
induction l1; 
simpl; 
trivial.
  
rewrite IHl1.
  
assert (
l1 ++ 
l2 <> 
nil).
  { 
intros eqq. 
apply app_eq_nil in eqq. 
intuition. }
  
destruct (
l1 ++ 
l2); 
congruence.
Qed.
 
Lemma last_repeat_nzero {
A:
Type} (
a:
A) 
n d : 
n > 0 -> 
last (
repeat a n) 
d = 
a.
Proof.
  induction n; [lia | ].
  simpl.
  destruct n; simpl in *; trivial.
  intuition.
Qed.
Lemma last_repeat_same {
A:
Type} (
a:
A) 
n : 
last (
repeat a n) 
a = 
a.
Proof.
Lemma interleave_with_end_padding_decompose_eq {
A} (
digits1:
list A) 
defA digits2 defB :
  
digits1 <> 
nil ->
  
length digits1 = 
length digits2 ->
  
exists l,
  
forall d1 d2,
    
interleave_with_end_padding digits1 defA digits2 defB = 
l ++ [
last digits1 d1; 
last digits2 d2].
Proof.
                                                        
Lemma interleave_with_end_padding_decompose_gt {
A} (
digits1:
list A) 
defA digits2 defB :
  
length digits1 > 
length digits2 ->
  
exists l,
  
forall d,
    
interleave_with_end_padding digits1 defA digits2 defB = 
l ++ [
last digits1 d; 
defB].
Proof.
Lemma interleave_with_end_padding_decompose_lt {
A} (
digits1:
list A) 
defA digits2 defB :
  
length digits1 < 
length digits2 ->
  
exists l,
  
forall d,
    
interleave_with_end_padding digits1 defA digits2 defB = 
l ++ [
defA ; 
last digits2 d].
Proof.
Lemma is_nil {
A:
Type} (
l:
list A) : {
l = 
nil} + {
l <> 
nil}.
Proof.
  destruct l.
  - left; trivial.
  - right; intro; discriminate.
Defined.
Lemma interleave_with_end_padding_Even {
A:
Type} (
l1:
list A) (
def1:
A) (
l2:
list A) (
def2:
A) :
  
PeanoNat.Nat.Even (
length (
interleave_with_end_padding l1 def1 l2 def2)).
Proof.
Lemma interleave_with_end_padding_even {
A:
Type} (
l1:
list A) (
def1:
A) (
l2:
list A) (
def2:
A) :
  
Nat.even (
length (
interleave_with_end_padding l1 def1 l2 def2)) = 
true.
Proof.
Lemma fixup_trailing_zeros_app_two x d : 
fixup_trailing_zeros (
x ++ [
d; 
bin_digit1]) = (
x ++ [
d; 
bin_digit1]).
Proof.
Definition padded_interleave_result n1 n2 :
  
n1 <> 
BinNat.N.zero \/ 
n2 <> 
BinNat.N.zero ->
  (
exists l, (
interleave_with_end_padding (
N_to_digits n1) 
bin_digit0 (
N_to_digits n2) 
bin_digit0) = 
l ++ [
bin_digit1]) \/
  (
exists l, (
interleave_with_end_padding (
N_to_digits n1) 
bin_digit0 (
N_to_digits n2) 
bin_digit0) = 
l ++ [
bin_digit1 ; 
bin_digit0]).
Proof.
Lemma make_even_digits_fixup_trailing_zeros n1 n2 :
(
make_even_digits
   (
fixup_trailing_zeros
      (
interleave_with_end_padding (
N_to_digits n1) 
bin_digit0 (
N_to_digits n2) 
bin_digit0)))
= 
interleave_with_end_padding (
N_to_digits n1) 
bin_digit0 (
N_to_digits n2) 
bin_digit0.
Proof.
Lemma decode_encode_pair (
n1 n2:
N) :
  
decode_pair (
encode_pair n1 n2) = (
n1, 
n2).
Proof.
Lemma canon_digits_even_break digits :
  
canon_digits digits ->
  
Nat.even (
length digits) = 
true ->
  { 
ld & {
a | 
digits = 
ld ++ [
a; 
bin_digit1] }}.
Proof.
  intros [
l lpf] 
ev.
  
rewrite lpf.
  
assert (
lcons: 
l <> 
nil).
  { 
intros ?; 
subst.
    
simpl in *.
    
discriminate.
  }
  
destruct (
exists_last lcons) 
as [
x [
a pf]].
  
subst.
  
rewrite app_ass; 
simpl.
  
eauto.
Qed.
 
Lemma fixup_canon_uninterleave_even_snd digits : 
  
canon_digits digits ->
  
Nat.even (
length digits) = 
true ->
  
fixup_trailing_zeros (
snd (
uninterleave digits)) = 
snd (
uninterleave digits).
Proof.
Lemma fixup_canon_uninterleave_odd_fst digits a : 
  
canon_digits digits ->
  
Nat.even (
length digits) = 
false ->
  
fixup_trailing_zeros (
fst (
uninterleave (
digits ++ [
a]))) = 
fst (
uninterleave (
digits ++ [
a])).
Proof.
Lemma uninterleave_length_eq_even {
A} (
l:
list A) :
  
Nat.even (
length l) = 
true ->
  
length (
fst (
uninterleave l)) = 
length (
snd (
uninterleave l)).
Proof.
Lemma uninterleave_length_eq_odd {
A} (
l:
list A) :
  
Nat.odd (
length l) = 
true ->
  
length (
fst (
uninterleave l)) = 
length (
snd (
uninterleave l)).
Proof.
    
Lemma uninterleave_length_eq {
A} (
l:
list A) :
  
length (
fst (
uninterleave l)) = 
length (
snd (
uninterleave l)).
Proof.
Lemma cleanup_zeros_repeat_form l :
  
exists n,
    
l = 
repeat bin_digit0 n ++ 
cleanup_zeros l.
Proof.
  induction l; 
simpl.
  - 
exists 0.
    
simpl; 
trivial.
  - 
destruct IHl as [
n eqq].
    
destruct a.
    + 
exists (
S n); 
simpl.
      
rewrite <- 
eqq.
      
trivial.
    + 
exists 0.
      
simpl; 
trivial.
Qed.
 
Lemma fixup_trailing_zeros_repeat_form l :
  
exists n,
    
l = 
fixup_trailing_zeros l ++ 
repeat bin_digit0 n.
Proof.
Lemma interleave_with_end_padding_fixup_uninterleave_make_even digits :
  
canon_digits digits ->
  (
interleave_with_end_padding
     (
fixup_trailing_zeros (
fst (
uninterleave (
make_even_digits digits)))) 
bin_digit0
     (
fixup_trailing_zeros (
snd (
uninterleave (
make_even_digits digits)))) 
bin_digit0)
  = 
digits
  \/
  (
interleave_with_end_padding
     (
fixup_trailing_zeros (
fst (
uninterleave (
make_even_digits digits)))) 
bin_digit0
     (
fixup_trailing_zeros (
snd (
uninterleave (
make_even_digits digits)))) 
bin_digit0)
  = 
digits ++ [
bin_digit0].
Proof.
Lemma interleave_with_end_padding_fixup_uninterleave_make_even_repeat digits :
  
canon_digits digits ->
  
exists n, 
  (
interleave_with_end_padding
     (
fixup_trailing_zeros (
fst (
uninterleave (
make_even_digits digits)))) 
bin_digit0
     (
fixup_trailing_zeros (
snd (
uninterleave (
make_even_digits digits)))) 
bin_digit0)
  = 
digits ++ 
repeat bin_digit0 n.
Proof.
Lemma encode_decode_pair (
n:
N) :
  
encode_pair (
fst (
decode_pair n)) (
snd (
decode_pair n)) = 
n.
Proof.
End Internal.
Program Instance N_pair_encoder : 
Isomorphism (
N*
N) 
N
  := {
      
iso_f '(
x,
y) := 
Internal.encode_pair x y ;
      
iso_b xy := 
Internal.decode_pair xy
    }.
Next Obligation.
Next Obligation.
Global Instance pair_encoder {
A} (
iso:
Isomorphism A N) : 
Isomorphism (
A*
A) 
A
  := 
Isomorphism_trans (
Isomorphism_trans (
Isomorphism_prod iso iso) 
N_pair_encoder) (
Isomorphism_symm iso).
Global Instance nat_pair_encoder : 
Isomorphism (
nat*
nat) 
nat := 
pair_encoder nat_to_N_iso.
   
Lemma pair_encode_contains_square (
n: 
nat) :
     
exists (
c : 
nat),
     
forall (
n1 n2 : 
nat),
       (
n1 <= 
n)%
nat -> (
n2 <= 
n)%
nat ->
       (
iso_f (
Isomorphism:=
nat_pair_encoder) (
n1, 
n2) <= 
c)%
nat.
   Proof.
   Lemma pair_encode_contains_square2 (
n: 
nat) :
     
exists m2, 
       
incl (
list_prod (
seq 0 (
S n)) (
seq 0 (
S n))) (
map iso_b (
seq 0 (
S m2))).
   Proof.
   Lemma square_contains_pair_encode (
c : 
nat) :
     
exists (
n : 
nat),
     
forall (
c1 : 
nat), (
c1 <= 
c)%
nat ->
        
let (
n1, 
n2) := 
iso_b (
Isomorphism:=
nat_pair_encoder) 
c1 in
        (
max n1 n2 <= 
n)%
nat.
   Proof.
   Lemma square_contains_pair_encode2 (
n : 
nat) :
     
exists m1,
       
incl (
map iso_b (
seq 0 (
S n))) (
list_prod (
seq 0 (
S m1)) (
seq 0 (
S m1))).
   Proof.