Set Implicit Arguments.

Require Import Coq.Init.Nat.
Require Import BasicDefs.

# Sequences and Strings

General operations and facts about sequences

Definition Seq (X:Type) := nat -> X.

## Equivalence of sequences

Definition seq_equal (X:Type)(w1 w2 : Seq X) := forall n, w1 n = w2 n.
Notation "w1 === w2" := (seq_equal w1 w2) (at level 70).
Hint Unfold seq_equal.

Instance seq_equal_Equivalence X :
Equivalence (@seq_equal X).
Proof.
constructor.
- constructor.
- intros w_1 w_2 E n. now rewrite E.
- intros w_1 w_2 w_3 E_1 E_2 n. now rewrite <-E_2, E_1.
Qed.

## Basic Operations on Sequences

Section SeqOperations.

Context {X:Type}.

Implicit Type (w:Seq X).
Implicit Type (x y: X).

Dropping the first n elements of a sequence
Fixpoint drop n w := match n with
| 0 => w
| S n => drop n (fun n => w (S n))
end.

Prepending an element to a sequence
Definition prepend x w := fun n => match n with
| 0 => x
| S n => w n
end.

prepend_path prepends the first k+1 values of the sequence p on the sequence w, k+1 makes sence with respect to the definition of strings
Fixpoint prepend_prefix k w' w : Seq X:= match k with
| 0 => prepend (w' 0) w
| S k => prepend_prefix k w' (prepend (w' (S k)) w)
end.

Lemma drop_correct n w : forall m, (drop n w) m = w (n + m).
Proof.
revert w. induction n; intros w m.
- reflexivity.
- apply IHn with (w:= (fun n => w ( S n))) (m:=m).
Qed.

Lemma drop_correct' n w m : (drop n w) m = w (m + n).
Proof.
rewrite_eq (m + n = n + m). apply drop_correct.
Qed.

Lemma drop_correct'' n w m : n <= m -> (drop n w) (m - n) = w m.
Proof.
intros L. rewrite drop_correct. f_equal. omega.
Qed.

Lemma prepend_first a (w:Seq X): ((prepend a w) 0) = a.
Proof.
reflexivity.
Qed.

Lemma prepend_rest a (w:Seq X): forall n, ((prepend a w) (S n)) = w n.
Proof.
reflexivity.
Qed.

Lemma prepend_rest2 a (w: Seq X): forall n , n > 0 -> (prepend a w) n = w (pred n).
Proof.
intros n L.
destruct n; oauto.
Qed.

Lemma prepend_prefix_rest k w' w n : prepend_prefix k w' w (S k + n) = w n.
Proof.
revert w' w n. induction k; intros w' w n; cbn.
- reflexivity.
- rewrite_eq (S (S (k + n)) = S k + S n).
now rewrite IHk.
Qed.

Lemma prepend_prefix_rest2 k w' w n : n > k -> prepend_prefix k w' w n = w (n - S k).
Proof.
intros L. remember (n - S k) as m. rewrite_eq (n = S k + m). subst m.
apply prepend_prefix_rest.
Qed.

Lemma prepend_prefix_rest' k w' w n : prepend_prefix k w' w (n + S k) = w n.
Proof.
rewrite_eq (n + S k = S k + n). apply prepend_prefix_rest.
Qed.

Lemma prepend_prefix_begin k w' w n : n <= k -> prepend_prefix k w' w n = w' n.
Proof.
revert k w' w n.
induction k; intros w' w n L; cbn.
- now rewrite_eq (n = 0).
- decide (n = S k) as [->|D].
+ rewrite prepend_prefix_rest2 by omega.
now rewrite_eq (S k - S k = 0).
+ apply IHk. omega.
Qed.

Mapping function on sequences
Definition seq_map (Y : Type) (f: X -> Y) w : (Seq Y) := fun n => f (w n).

Definition seq_zip (X Y: Type) (w: Seq X) (w': Seq Y) := fun n => (w n, w' n).

Global Instance seq_zip_proper (X Y: Type) : Proper (@seq_equal X ==> @seq_equal Y ==> @seq_equal (X*Y)) (@seq_zip X Y).
Proof.
intros w_1 w_1' E_1 w_2 w_2' E_2 n. unfold seq_zip. now rewrite E_1, E_2.
Qed.

Global Instance seq_map_proper (Y: Type) (f: X -> Y): Proper (@seq_equal X ==> @seq_equal Y) (seq_map f).
Proof.
intros w w' E n. unfold seq_map. now rewrite E.
Qed.

End SeqOperations.

Delimit Scope string_scope with string.

## Strings as Prefixes

A String is a nonempty prefix on a sequence: lastindex is the last index in seq which belongs to the string Deriving strings from sequences makes operations on both uinform on easier
Structure String (X:Type) := mkstring {seq :> Seq X; lastindex : nat}.

Notation "| v |" := (lastindex v) : string_scope.
Notation "v [ k ]" := ((seq v) k) (at level 10) : string_scope.

Open Scope string_scope.

### Equivalence on Strings

Because strings consist of sequences, there is no usable equality on them. Define the pointwise equality relation.
Section StringsEq.
Context {X: Type}.
Implicit Types (u v : String X).

Definition strings_equal u v := |u| = |v| /\ (forall n, n <= |u| -> u [n] = v [n]).

Lemma strings_equal_reflexive: reflexive (String X) strings_equal.
Proof.
intros w. split; auto.
Qed.

Lemma strings_equal_symmetric : symmetric (String X) strings_equal.
Proof.
intros w w' [EL EW]. split.
- omega.
- intros n L. symmetry. apply (EW n). omega.
Qed.

Lemma strings_equal_transitive : transitive (String X) strings_equal.
Proof.
intros w_1 w_2 w_3 [EL EW] [FL FW]. split.
- omega.
- intros n L. rewrite EW by omega. apply FW. omega.
Qed.

Global Instance strings_equal_Equivalence: Equivalence strings_equal.
Proof.
constructor.
- apply strings_equal_reflexive.
- apply strings_equal_symmetric.
- apply strings_equal_transitive.
Qed.

Global Instance string_lastindex_proper: Proper (strings_equal ==> eq) (@lastindex X).
Proof.
now intros v v' [EL _].
Qed.
End StringsEq.

### Operations on Strings

Section StringOperations.
Context {X:Type}.
Implicit Types (u v : String X).
Implicit Types (w : Seq X).

Definition prepend_string v w := prepend_prefix (|v|) v w.
Definition concat_strings u v := mkstring (prepend_string u v) ((|u|) + S(|v|)).

Definition drop_string_begin n u := mkstring (drop n u) ((|u|) - n).
Definition drop_string_end n u := mkstring u ((|u|) - n).

Extracts the string from w starting at index n (inclusively) until index m (exclusively)
Definition extract n m w : String X:= mkstring(drop n w) (m - S n).

Cutting something from n exlusively to m inclusively out of a sequence
Definition cut n m w := prepend_string (mkstring w n) (drop m w).
Definition cut_string n m v := concat_strings (mkstring v n) (drop_string_begin m v).

Lemma extract_correct n m w: forall k, (extract n m w) [k] = w (n + k).
Proof.
intros k.
simpl.
apply drop_correct.
Qed.

Definition string_map (X Y: Type) (f: X -> Y) (v: String X) : String Y := (mkstring (seq_map f v) (|v|)).

End StringOperations.

Notation "v ++ w" := (concat_strings v w) : string_scope.
Notation "v +++ w" := (prepend_string v w) (at level 60) : string_scope .
Notation "v == w" := (strings_equal v w) (at level 65): string_scope.

Section StringOperationLemmas.

Context {X:Type}.
Implicit Types (u v : String X).
Implicit Types (w : Seq X).

Lemma wrap_string v n: n =|v| -> v == mkstring v n.
Proof.
intros ->.
destruct v; cbn. reflexivity.
Qed.

Lemma prepend_string_begin v w n : n <= |v| -> (v +++ w) n = v [n].
Proof.
intros L. unfold prepend_string. now rewrite prepend_prefix_begin.
Qed.

Lemma prepend_string_rest v w n : n > |v| -> (v +++ w) n = w (n - S(|v|)).
Proof.
intros L. unfold prepend_string. now rewrite prepend_prefix_rest2.
Qed.

Lemma prepend_string_rest' v w n : (v +++ w) (S (|v|) + n) = w n.
Proof.
rewrite prepend_string_rest by omega. f_equal. omega.
Qed.

Lemma prepend_string_rest'' v w n k : k = S(|v|) -> (v +++ w) (k + n) = w n.
Proof.
intros ->. apply prepend_string_rest'.
Qed.

Lemma revert_prepend_string v w : drop (S (|v|)) (v +++ w) === w.
Proof.
intros n. rewrite drop_correct. rewrite prepend_string_rest by omega. f_equal. omega.
Qed.

Lemma revert_drop k w : mkstring w k +++ (drop (S k) w) === w.
Proof.
intros n. decide (n <= k).
- now rewrite prepend_string_begin by comega.
- rewrite prepend_string_rest by comega. rewrite drop_correct. f_equal. comega.
Qed.

Lemma concat_string_begin u v n : n <= |u| -> (u ++ v) [n] = u [n].
Proof.
intros L. unfold concat_strings. cbn. now apply prepend_string_begin.
Qed.

Lemma concat_string_rest u v n : n > |u| -> (u ++ v) [n] = v [n - S(|u|)].
Proof.
intros L. unfold concat_strings. cbn. now apply prepend_string_rest.
Qed.

Lemma concat_strings_step k w v : (mkstring w (S k)) ++ v == (mkstring w k) ++ (mkstring (prepend (w (S k)) v) (S(|v|))).
Proof.
split; cbn; oauto.
Qed.

Lemma concat_strings_length (u v : String X): |u ++ v| = |u| + S(|v|).
Proof. reflexivity. Qed.

Lemma concat_prepend_string_associative u v w :(u ++ v) +++ w === u +++ (v +++ w).
Proof.
intros n.
decide (n <= |u|) as [D|D].
- repeat rewrite prepend_string_begin by comega. now rewrite concat_string_begin.
- symmetry. rewrite prepend_string_rest by comega.
decide (n <= |u| + S (|v|)) as [D'|D']; cbn in D'.
+ repeat rewrite prepend_string_begin by comega.
now rewrite concat_string_rest by comega.
+ repeat rewrite prepend_string_rest by comega. f_equal. cbn. omega.
Qed.

Lemma concat_strings_associative t u v : (t ++ u) ++ v == t ++ (u ++ v).
Proof.
split.
- cbn. omega.
- intros n L. apply concat_prepend_string_associative.
Qed.

Lemma revert_concat_first u v: u == drop_string_end (S(|v|)) (u ++ v).
Proof.
unfold drop_string_end. split; cbn.
- omega.
- intros n L. now rewrite prepend_string_begin.
Qed.

Lemma revert_concat_second u v: v == drop_string_begin (S(|u|)) (u ++ v).
Proof.
unfold drop_string_begin. split.
- simpl. omega.
- intros n L. simpl. rewrite drop_correct.
rewrite prepend_string_rest by omega. f_equal. omega.
Qed.

Lemma concat_extract w i_1 i_2 i_3: i_1 < i_2 -> i_2 < i_3 -> (extract i_1 i_2 w) ++ (extract i_2 i_3 w) == extract i_1 i_3 w.
Proof.
intros L1 L2. split.
- comega.
- intros n L.
decide (n <= (i_2 - S i_1)) as [D|D].
+ now rewrite concat_string_begin by comega.
+ rewrite concat_string_rest by comega.
unfold extract. cbn. repeat rewrite drop_correct. f_equal. omega.
Qed.

Lemma extract_from_drop w n m k: (extract n m (drop k w)) == (extract (n + k) (m + k) w).
Proof.
repeat split; cbn; oauto.
intros i B. simpl. repeat rewrite drop_correct. f_equal. omega.
Qed.

Lemma extract_from_drop' w n m k: k <=n-> (extract (n-k) (m-k) (drop k w)) == (extract n m w).
Proof.
intros L. split; cbn; oauto.
intros i B. simpl. repeat rewrite drop_correct. f_equal. omega.
Qed.

Lemma drop_extract w n m: (extract n (S m) w) == drop_string_begin n (mkstring w m).
Proof.
split; auto.
Qed.

Lemma extract_prepend v w n m : (extract (n + S (| v |)) (m + S (| v |)) (v +++ w )) == extract n m w.
Proof.
split.
- cbn. omega.
- intros k L. cbn. cbn in L. rewrite !drop_correct.
rewrite prepend_string_rest by comega. f_equal. omega.
Qed.

Lemma cut_string_cut i j w k: i < j <= k -> cut_string i j (mkstring w k) == (mkstring (cut i j w) (S k - j + i)).
Proof.
intros L. split.
- cbn. destruct j; omega.
- now intros.
Qed.

Lemma cut_front n m w : forall k, k <= n -> (cut n m w) k = w k.
Proof.
intros k L. unfold cut. now rewrite prepend_string_begin.
Qed.

Lemma cut_rest n m w: forall k, (cut n m w) (S n + k) = w (m + k).
Proof.
intros k. unfold cut. now rewrite prepend_string_rest', drop_correct.
Qed.

End StringOperationLemmas.

Instance seq_eq_index (X:Type) n: Proper (@seq_equal X ==> eq) (fun w => w n).
Proof.
intros w w' Ew . apply Ew.
Qed.

Instance drop_proper (X:Type): Proper (eq ==> @seq_equal X ==> @seq_equal X) drop.
Proof.
intros n n' En w w' Ew k.
rewrite !drop_correct. subst n. now apply Ew.
Qed.

Instance prepend_string_proper (X:Type): Proper (@strings_equal X ==> @seq_equal X ==> @seq_equal X) (fun v w => v +++ w).
Proof.
intros v v' [EvL Ev] w w' Ew n. decide (n <= |v|).
- repeat rewrite prepend_string_begin by (try rewrite <- EvL; auto). now apply Ev.
- repeat rewrite prepend_string_rest by (try rewrite <- EvL; omega). rewrite <-EvL. apply Ew.
Qed.

Instance prepend_string_proper_index (X:Type): Proper (@strings_equal X ==> @seq_equal X ==> @eq nat ==> @eq X) (fun v w n=> (v +++ w) n).
Proof.
intros v v' [EvL Ev] w w' Ew n n' En. subst n'. decide (n <= |v|).
- repeat rewrite prepend_string_begin by (try rewrite <- EvL; auto). now apply Ev.
- repeat rewrite prepend_string_rest by (try rewrite <- EvL; omega). rewrite <-EvL. apply Ew.
Qed.

Instance cancat_strings_proper (X:Type): Proper (@strings_equal X ==> @strings_equal X ==> @strings_equal X) (@concat_strings X).
Proof.
intros u u' [Eu1 Eu2] v v' [Ev1 Ev2]. split.
- cbn. rewrite Eu1, Ev1. omega.
- intros n L. decide (n <= |u|).
+ repeat rewrite concat_string_begin by (try rewrite <-Eu1; auto). now apply Eu2.
+ repeat rewrite concat_string_rest by (try rewrite <-Ev1; omega). rewrite <-Eu1. apply Ev2. cbn in L. omega.
Qed.

Instance extract_proper (X:Type) : Proper (eq ==> eq ==> (@seq_equal X) ==> strings_equal) extract.
Proof.
intros n n' <- m m' <- w w' Ew. split; auto.
intros k L. cbn. now rewrite !drop_correct, Ew.
Qed.

Section FiniteIterationOfString.

Context {X:Type}.
Implicit Type (u v t : String X).

Fixpoint fin_iter v n := match n with
| 0 => v
| S n => v ++ (fin_iter v n)
end.

Lemma fin_iter_step v n : fin_iter v (S n) == v ++ (fin_iter v n).
Proof.
reflexivity.
Qed.

Lemma fin_iter_base v : fin_iter v 0 == v.
Proof.
reflexivity.
Qed.

Lemma fin_iter_split u v n: fin_iter (u++v) (S n) == u ++ (fin_iter (v ++ u) n) ++ v.
Proof.
induction n; cbn.
- now rewrite !concat_strings_associative.
- rewrite IHn. now rewrite !concat_strings_associative.
Qed.

Lemma fin_iter_length v n : |fin_iter v n| = S n * S (|v|) - 1.
Proof.
induction n; cbn.
- omega.
- rewrite IHn. rewrite Nat.mul_succ_l. remember (n * S(|v|)) as Z. omega.
Qed.

Lemma fin_iter_length_bound v n : n <= |fin_iter v n| .
Proof.
induction n; cbn; omega.
Qed.

Lemma fin_iter_sum v n m : fin_iter v (S n + m) == (fin_iter v n) ++ (fin_iter v m).
Proof.
induction n.
- reflexivity.
- rewrite_eq (S (S n) + m = S (S n + m)). rewrite fin_iter_step. rewrite IHn. cbn. now rewrite concat_strings_associative.
Qed.

Lemma fin_iter_fin_iter v n m : (fin_iter (fin_iter v n) m) == (fin_iter v (S m * S n -1)).
Proof.
induction m; cbn.
- now rewrite_eq (n + 0 - 0= n).
- rewrite IHm. cbn. rewrite <-fin_iter_sum.
remember (m * S n) as Z. now rewrite_eq (S n + (n + Z - 0) = n + S(n + Z) - 0).
Qed.
End FiniteIterationOfString.

## Strictly Monotone Sequences on nat

Section StrictlyMonotoneSeqs.

Implicit Types (f: Seq nat).

Definition s_monotone (f: Seq nat):= forall n, f n < f ( S n).

Lemma s_monotone_id : s_monotone (fun n => n).
Proof.
intros n. omega.
Qed.

Lemma s_monotone_unbouded_ge (f: Seq nat): s_monotone f -> (forall n, Sigma i, f i > n).
Proof.
intros A n.
induction n.
- exists 1. specialize (A 0). omega.
- destruct IHn as [n' P].
exists (S n').
specialize (A n'). omega.
Qed.

Lemma s_monotone_unbouded (f: Seq nat): s_monotone f ->
(forall n, Sigma i, f i >= n).
Proof.
intros A n.
destruct (s_monotone_unbouded_ge A n) as [i P].
exists i. omega.
Qed.

Lemma s_monotone_ge f: s_monotone f -> forall n, n <= f n.
Proof.
intros F n.
induction n.
- omega.
- specialize (F n). omega.
Qed.

Lemma s_monotone_strict_order_preserving' f k m : s_monotone f -> f k < f (m + S k).
Proof.
intros F. induction m; cbn.
- apply F.
- specialize (F (m + S k)). omega.
Qed.

Lemma s_monotone_strict_order_preserving f k n: s_monotone f -> k < n -> f k < f n.
Proof.
intros F L.
remember (n - S k) as z. assert (n = z + S k) by omega. subst n.
now apply s_monotone_strict_order_preserving'.
Qed.

Lemma s_monotone_order_preserving f k n: s_monotone f -> k <= n -> f k <= f n.
Proof.
intros F L.
decide (k = n).
- subst n. omega.
- enough (f k < f n) by omega.
apply s_monotone_strict_order_preserving; oauto.
Qed.

Lemma s_monotone_order_preserving_backwards f k n: s_monotone f -> f k <= f n -> k <= n.
Proof.
intros F L.
decide (k <= n).
- auto.
- exfalso.
assert (n < k) as H by omega.
apply s_monotone_order_preserving with (f:=f) in H; auto.
specialize (F n). omega.
Qed.

Lemma s_monotone_ge_zero g: s_monotone g -> g 0 > 0 -> forall n, g n > 0.
Proof.
intros Inc GZ. intros n. destruct n; auto.
- enough (g (S n) >= S n) by omega. now apply s_monotone_ge.
Qed.

Lemma s_monotone_ge_zero' g: s_monotone g -> forall n, 1 <= n -> g n > 0.
Proof.
intros Inc n G. pose (s_monotone_ge Inc n ). omega.
Qed.

Lemma s_monotone_drop g k : s_monotone g -> s_monotone (drop k g).
Proof.
intros Inc n. repeat rewrite drop_correct'. apply Inc.
Qed.

Chaining strictly monotone sequences preserves strict monotonicity.
Lemma compose_s_monotone f g: s_monotone f -> s_monotone g -> s_monotone (fun n => g ( f n)).
Proof.
intros F G n.
apply s_monotone_strict_order_preserving; auto.
Qed.

For a given function f, fix f 0 = 0. This preserves strict monotonicity.
Definition fix_first_zero (f:nat -> nat) := fun n => match n with
| 0 => 0
| S n => f (S n) end.

Lemma fix_first_zero_s_monotone f : s_monotone f -> s_monotone (fix_first_zero f).
Proof.
intros IncF n. destruct n; simpl.
- specialize (IncF 0). omega.
- apply IncF.
Qed.

End StrictlyMonotoneSeqs.

## ω-Concattenation

Concattenating infinitely many strings (a sequence of strings) to a sequence
Section InfiniteConcat.

Context {X:Type}.
Implicit Types (f: Seq (String X)).

Helper function which translates the indices: it returns a tuple (w, i) such that (f w) [i] is the desired character at index n
Fixpoint concat_inf_indices f n := match n with
| 0 => (0,0)
| S n => match (concat_inf_indices f n) with (i, j) =>
if (decision (j = |f i|))
then (S i, 0)
else (i , S j) end end.

Definition concat_inf f : Seq X := fun n =>
(f (fst (concat_inf_indices f n )))[snd (concat_inf_indices f n)].

Calculates the begin index of (f n) in (concat_inf f)
Fixpoint begin_in f n := match n with
|0 => 0
|S n => S (| f n|) + (begin_in f n) end.

Lemma s_monotone_begin_in f: s_monotone (begin_in f).
Proof.
intros n. cbn. omega.
Qed.

Various correctness lemmata

Lemma concat_inf_index_in_string f i n : concat_inf_indices f n = (i, 0) ->
forall k, k <= |f i| -> concat_inf_indices f (k + n) = (i , k).
Proof.
intros C.
apply (complete_induction (P:= fun k => (k <= |f i| -> concat_inf_indices f (k + n) = (i, k)))).
- auto.
- intros k IHk L. cbn.
rewrite IHk by omega.
destruct decision.
+ exfalso. omega.
+ reflexivity.
Qed.

Lemma concat_inf_index_begin f i: concat_inf_indices f (begin_in f i) = (i, 0).
Proof.
induction i ; cbn.
- reflexivity.
- rewrite concat_inf_index_in_string with (i:=i) by auto.
now trivial_decision.
Qed.

Lemma concat_inf_index_correct f i k: k <= |f i| -> concat_inf_indices f (k + begin_in f i) = (i, k).
Proof.
intros L.
apply concat_inf_index_in_string ; auto.
now apply concat_inf_index_begin.
Qed.

Lemma concat_inf_index_in_bounds f n i k : concat_inf_indices f n = (i , k) -> k <= |f i|.
Proof.
revert i; revert k.
induction n; intros k i C; cbn in C.
- assert (k = 0) as H by congruence.
rewrite H. omega.
- destruct (concat_inf_indices f n) as [i' k'] eqn:H.
destruct decision.
+ assert (k = 0) by congruence. subst k. omega.
+ assert (i' = i) by congruence. subst i.
assert (S k' = k) by congruence. subst k.
enough (k' <= |f i'|) by omega. auto.
Qed.

Lemma concat_inf_index_step_correct f n i k: concat_inf_indices f n = (i, k) ->
concat_inf_indices f (S n) = (i, (S k)) \/
(concat_inf_indices f (S n) = (S i, 0) /\ k = |f i|).
Proof.
intros L. cbn. rewrite L.
destruct decision; tauto.
Qed.

To show that concat_inf_indices is injective, we define the lexicographical order on pairs of natural numbers and show that concat_inf_indices is strictly s_monotone
Definition nat_pair_le (x y : nat * nat) := (fst x < fst y) \/ (fst x = fst y /\ (snd x < snd y)).

Lemma nat_pair_le_not_equal (x y: nat * nat):nat_pair_le x y -> x <> y.
Proof.
intros E. unfold nat_pair_le in E. destruct x, y. cbn in E. destruct E.
- assert (n <> n1) by omega. congruence.
- assert (n0 <> n2) by omega. congruence.
Qed.

Lemma concat_inf_index_le f n m: n < m -> nat_pair_le (concat_inf_indices f n) (concat_inf_indices f m) .
Proof.
intros L. induction L; cbn.
- destruct (concat_inf_indices f n), decision; [left | right]; cbn; omega.
- destruct (concat_inf_indices f n) as [w i]; destruct (concat_inf_indices f m) as [w' i'].
destruct decision; unfold nat_pair_le in IHL; simpl in IHL; unfold nat_pair_le; simpl; omega.
Qed.

Lemma concat_if_indices_lower_bound f m n: m >= begin_in f n -> fst (concat_inf_indices f m) >= n.
Proof.
intros H'.
destruct (concat_inf_indices f m) as [w i] eqn:H.
decide (m = begin_in f n).
+ subst m. rewrite concat_inf_index_begin in H. cbn.
enough (w = n) by omega. congruence.
+ assert (begin_in f n < m) as D by omega.
pose proof (concat_inf_index_le f D) as Z.
rewrite concat_inf_index_begin,H in Z.
compute in Z. cbn. omega.
Qed.

Lemma concat_inf_index_injective f n n' : concat_inf_indices f n = concat_inf_indices f n' -> n = n'.
Proof.
decide (n = n').
- auto.
- intros H. exfalso. decide (n < n') as [D|D].
+ now apply (nat_pair_le_not_equal (concat_inf_index_le f D)).
+ assert (n' < n) as D' by omega.
now apply (nat_pair_le_not_equal (concat_inf_index_le f D')).
Qed.

Lemma concat_inf_index_to_begin f n i k : concat_inf_indices f n = (i, k) -> n = (begin_in f i) + k /\ (n < begin_in f (S i)).
Proof.
intros C.
assert ( k <= |f i|) by apply (concat_inf_index_in_bounds C).
rewrite <- (concat_inf_index_correct (f:=f)) in C by now apply concat_inf_index_in_bounds with (n:=n).
apply concat_inf_index_injective with (n:=n) in C.
split; cbn; oauto.
Qed.

Lemma concat_inf_correct f i k : k <= |f i| -> concat_inf f (k + (begin_in f i)) = (f i) [k].
Proof.
unfold concat_inf. intros L.
now rewrite concat_inf_index_correct.
Qed.

End InfiniteConcat.

Lemma begin_in_equal (X Y: Type) (f: Seq (String X)) (g:Seq (String Y)):
(forall n , |f n| = |g n|) -> begin_in f === begin_in g.
Proof.
intros EL n. induction n.
- reflexivity.
- cbn. now rewrite <-IHn, EL.
Qed.

Lemma concat_inf_index_equal (X Y: Type) (f: Seq (String X)) (g:Seq (String Y)):
(forall n , |f n| = |g n|) -> concat_inf_indices f === concat_inf_indices g.
Proof.
intros EL n. induction n.
- reflexivity.
- cbn. rewrite <-IHn.
destruct (concat_inf_indices f n) as [wi i].
now rewrite <-(EL wi).
Qed.

Lemma concat_inf_extensional (X:Type) (f f': Seq (String X)): (forall n, (f n == f' n)) -> concat_inf f === concat_inf f'.
Proof.
intros E n.
unfold concat_inf. rewrite <-concat_inf_index_equal with (f:=f)(g:=f').
- apply E. destruct (concat_inf_indices f n) eqn:H. now apply concat_inf_index_in_bounds in H.
- intros m. apply E.
Qed.

Lemma extract_concat_inf (X:Type) (W: Seq (String X)) n : extract (begin_in W n ) (begin_in W (S n) ) (concat_inf W) == W n.
Proof.
split.
- cbn. omega.
- intros m L. cbn. cbn in L. rewrite drop_correct.
now rewrite Nat.add_comm, concat_inf_correct by omega.
Qed.

## ω-Iteration of a String

The ω-iteration of a string is defined using concat_inf

Definition omega_iteration (X: Type) (u:String X) := concat_inf (fun _ => u).

Notation " w 'to_omega'" := (omega_iteration w) (at level 50).

Lemma omega_iteration_extensional (X:Type) (u v: String X) : u == v -> u to_omega === v to_omega.
Proof.
unfold omega_iteration. intros E. now apply concat_inf_extensional.
Qed.

Instance omega_iteration_proper X: Proper (@strings_equal X ==> @seq_equal X) (@omega_iteration X).
Proof.
intros A B D. now apply omega_iteration_extensional.
Qed.

Instance omega_iteration_proper_index X: Proper (@strings_equal X ==> eq ==> eq) (fun w n=> (@omega_iteration X w n)).
Proof.
intros A B E. intros n m D. rewrite D. now apply omega_iteration_extensional.
Qed.

Lemma extract_omega_iter (X:Type) (v: String X) k: extract (k *S(|v|) )((S k) * S(|v|) ) (v to_omega) == v.
Proof.
unfold omega_iteration.
assert (forall k, begin_in (fun _ => v) k = k * S(|v|))as H. {
clear k. intros k. induction k.
- reflexivity.
- simpl. rewrite IHk. reflexivity. }
rewrite <-2H.
apply extract_concat_inf.
Qed.

Section OmegaIteration.

Context {X:Type}.
Implicit Type t u v: String X.
Implicit Type w: Seq X.

Lemma concat_inf_indices_step (f: Seq (String X)) n: concat_inf_indices f (S n) = match (concat_inf_indices f n) with (i, j) =>
if (decision (j = |f i|))
then (S i, 0)
else (i , S j) end.
Proof. reflexivity. Qed.

Lemma concat_inf_indices_constant_first v i: i <= |v| -> i = snd (concat_inf_indices (fun _ => v) (i)).
Proof.
intros L. induction i.
- reflexivity.
- cbn. destruct concat_inf_indices. cbn in IHi. rewrite IHi; oauto. destruct decision as [D|D].
+ exfalso. rewrite <-IHi in D; oauto.
+ reflexivity.
Qed.

Lemma concat_inf_indices_constant_step v n : snd (concat_inf_indices (fun _ => v) n) = snd (concat_inf_indices (fun _ => v) (n + S(|v|))).
Proof.
induction n.
- cbn. destruct concat_inf_indices as [i j] eqn:H. rewrite decision_true.
+ reflexivity.
+ change (snd (i,j) = |v|). rewrite <-H. rewrite <-concat_inf_indices_constant_first; oauto.
- cbn. destruct (concat_inf_indices (fun _ : nat => v) n). destruct (concat_inf_indices (fun _ : nat => v) (n + S (| v |))).
cbn in IHn. rewrite IHn. now destruct decision.
Qed.

Lemma concat_inf_indices_constant_step' v n : n > |v| -> snd (concat_inf_indices (fun _ => v) n) = snd (concat_inf_indices (fun _ => v) (n - S(|v|))).
Proof.
intros L. remember (n - S(|v|)) as Z. rewrite_eq (n = Z + S(|v|)).
symmetry. apply concat_inf_indices_constant_step.
Qed.

Lemma vv_omega_unfold v : (v to_omega) === v +++ (v to_omega).
Proof.
intros n. unfold omega_iteration, concat_inf. cbn.
decide (n<= |v|).
- rewrite prepend_string_begin by auto. f_equal. symmetry.
now apply concat_inf_indices_constant_first.
- rewrite prepend_string_rest by omega. f_equal.
apply concat_inf_indices_constant_step'. omega.
Qed.

Lemma omega_iter_first v n: n <= |v| -> (v to_omega) n = v [n].
Proof.
intros L. rewrite vv_omega_unfold.
now rewrite prepend_string_begin by omega.
Qed.

Lemma omega_iter_first_new v : (v to_omega) (S(|v|)) = v[0].
Proof.
rewrite vv_omega_unfold. rewrite prepend_string_rest by omega.
rewrite omega_iter_first by omega. f_equal. omega.
Qed.

Lemma omega_iter_unfold v n : (v to_omega) === (fin_iter v n) +++ (v to_omega).
Proof.
induction n.
- apply vv_omega_unfold.
- rewrite fin_iter_step, concat_prepend_string_associative, <-IHn.
apply vv_omega_unfold.
Qed.

Lemma eq_string_access (v v': String X) n: n <= |v| -> v == v' -> v[n] = v'[n].
Proof.
intros L [E1 E2]. now apply E2.
Qed.
Lemma eq_string_access' (v v': String X) n: v == v' -> n <= |v| -> v[n] = v'[n].
Proof.
intros [E1 E2] L. now apply E2.
Qed.

Lemma nat_equation_1 n m: S m * S (S n) - 1 = S m + (S m * S n - 1).
Proof.
rewrite Nat.mul_succ_r, plus_comm.
remember (S m * S n) as Z. assert (Z > 0) by (subst Z ; apply mul_ge_0; omega).
now rewrite_eq ( S m + Z - 1 = S m + (Z - 1)).
Qed.

Lemma fin_iter_to_omega v n: v to_omega === (fin_iter v n) to_omega.
Proof.
induction n.
- reflexivity.
- intros m.
rewrite (omega_iter_unfold v m). rewrite (omega_iter_unfold (fin_iter v (S n)) m).
repeat rewrite prepend_string_begin by apply fin_iter_length_bound. symmetry.
rewrite eq_string_access with (v':= (fin_iter v m) ++ (fin_iter v (S m * (S n) -1))).
+ now rewrite concat_string_begin by apply fin_iter_length_bound.
+ apply fin_iter_length_bound.
+ now rewrite fin_iter_fin_iter, <-fin_iter_sum, nat_equation_1.
Qed.

Lemma fin_iter_index v n k l: l <= |v| -> k <= n -> (fin_iter v n) [k * (S (|v|)) + l] = v [l].
Proof.
intros Ll. revert k. induction n; intros k Lk.
- cbn. now rewrite_eq (k = 0).
- rewrite (eq_string_access' (fin_iter_step v n)).
+ destruct k.
* rewrite concat_string_begin; cbn; oauto.
* rewrite concat_string_rest.
-- cbn. rewrite <-IHn with (k := k) by omega. f_equal.
remember (k * S(|v|))as Z. omega.
-- enough (S k * S (| v |) > |v|) by omega.
cbn. remember (k * S (| v |)) as Z. omega.
+ rewrite fin_iter_length, Nat.mul_succ_l.
enough (k * S(|v|) <= S n * S(|v|)) by omega.
apply Nat.mul_le_mono_nonneg_r; omega.
Qed.

Lemma fin_iter_last_index v n k: 0 < k <= S n -> (fin_iter v n) [k * S(|v|) - 1] = v[|v|].
Proof.
intros L.
destruct k. {exfalso. omega. }
rewrite Nat.mul_succ_l.
remember (k * S(|v|)) as C. rewrite_eq (C + S (| v |) - 1 = C + |v|). subst C.
apply fin_iter_index; omega.
Qed.

Lemma le_add k n m : k <= n -> k <= n + m.
Proof. omega. Qed.

Lemma concat_strings_omega_unfold u v : (u ++ v) to_omega === u +++ (v ++ u) to_omega.
Proof.
intros m.
decide (m <= |u|) as [D|D].
- rewrite (omega_iter_unfold (u ++ v) m).
rewrite prepend_string_begin by apply fin_iter_length_bound.
rewrite prepend_string_begin by auto.
destruct m.
+ cbn. now rewrite prepend_string_begin.
+ assert (fin_iter (u ++ v) (S m) == (u ++ v) ++ (fin_iter (u++v) m)) as H. { now rewrite fin_iter_step. }
rewrite eq_string_access with(v':=(u ++ v) ++ (fin_iter (u++v) m)).
* now repeat rewrite concat_string_begin by (cbn;omega).
* apply fin_iter_length_bound.
* now rewrite fin_iter_step.
- rewrite (omega_iter_unfold (u++v) (S m)).
rewrite (omega_iter_unfold (v++u) (S m)).
rewrite prepend_string_begin by (rewrite <-fin_iter_length_bound; omega).
rewrite prepend_string_rest by omega.
rewrite prepend_string_begin by (rewrite <-fin_iter_length_bound; omega).
rewrite <-concat_string_rest by omega.
rewrite <-concat_string_begin with (v0:= u) by (rewrite <-fin_iter_length_bound; omega).
apply eq_string_access.
+ rewrite concat_strings_length. apply le_add. rewrite <-fin_iter_length_bound. omega.
+ rewrite (fin_iter_split v u).
repeat rewrite <-concat_strings_associative.
now rewrite <-fin_iter_step.
Qed.

End OmegaIteration.

## Predicates Satsified Infinitely Often by a Sequence

Definition infiniteP X (P: X -> Prop) (w: Seq X) := forall n, exists m, m >= n /\ P (w m).

Section Infinite.
Context {X:Type}.
Implicit Types (w: Seq X) (v: String X) (x:X).

Notation infinite x w := (infiniteP (fun y => y = x) w).

Lemma finite_prefix_preserves_infiniteP v w P: infiniteP P w -> infiniteP P (v +++ w).
Proof.
intros I n. destruct (I n) as [m [L Q]].
exists (S (|v|) + m). split.
- omega.
- now rewrite prepend_string_rest'.
Qed.

Lemma finite_prefix_preserves_infinite x v w : infinite x w -> infinite x (v +++ w) .
Proof. apply finite_prefix_preserves_infiniteP. Qed.

Lemma drop_preserves_infiniteP w P n: infiniteP P w -> infiniteP P (drop n w).
Proof.
intros I k. destruct (I (k+n)) as [m [L Q]].
exists (m-n). split.
- omega.
- rewrite drop_correct''; oauto.
Qed.

Lemma drop_preserves_infinite s w n: infinite s w -> infinite s (drop n w).
Proof. apply drop_preserves_infiniteP. Qed.

Definition holdsIn P v:= exists k, k <= |v| /\ P (v[k]).
Implicit Types (f: Seq (String X)).

Lemma concat_inf_infiniteP P f:
infiniteP (holdsIn P) f -> infiniteP P (concat_inf f) .
Proof.
intros F.
intros n. destruct (F n) as [m [L [k [L' Fin]]]].
exists (k + (begin_in f m) ). split.
- enough (begin_in f m >= m) by omega.
apply s_monotone_ge, s_monotone_begin_in.
- now rewrite concat_inf_correct.
Qed.

Lemma concat_inf_infiniteP_inverse P f:
infiniteP P (concat_inf f) -> infiniteP (holdsIn P) f.
Proof.
intros Fin n. destruct (Fin (begin_in f n)) as [m [E Q]].
unfold concat_inf in Q. destruct (concat_inf_indices f m) as [w i]eqn:H.
exists w. split.
- apply concat_if_indices_lower_bound in E.
now rewrite H in E.
- exists i. split.
+ apply (concat_inf_index_in_bounds H).
+ assumption.
Qed.

Lemma omega_iteration_infiniteP P v: holdsIn P v -> infiniteP P (v to_omega).
Proof.
intros H. unfold omega_iteration. apply concat_inf_infiniteP.
intros m. exists m. split; oauto.
Qed.

Global Instance infiniteP_proper {P}: Proper (@seq_equal X ==> iff) (infiniteP P).
Proof.
intros w w' E. split; intros I n; destruct (I n) as [m Q]; exists m; [rewrite <-E | rewrite E]; auto.
Qed.

End Infinite.

Notation infinite x w := (infiniteP (fun y => y = x) w).

## ω-Filter

Computing the next position at which a predicate holds in a sequence
Section FindNextPosition.
Given a sequence w on a type X, a decidable predicate on X, a number n and a proof that P holds at some m, n <= m, we can construct the smallest greater equal n, at which P holds.

Context{X: Type}.
Variable P : X -> Prop.
Context{decP: forall x, dec (P x)}.
Variable w: Seq X.

Define a function which gives the next index in the sequence starting at a given one that fulfills the predicate
Section NextPosition.
Context {n m : nat}.
Variable (L : n <= m).
Variable (F : P (w m)).

Definition next_position: nat.
Proof.
pose (P' := fun j => P ((drop n w) j)).
assert(exists j, P' j) as H. { exists (m - n). subst P'. cbn. rewrite drop_correct. now rewrite_eq (n + (m - n) = m). }
apply cc_nat_first in H.
destruct H as [k _].
exact (n + k). auto.
Defined.

Lemma next_position_correct : P (w next_position).
Proof.
unfold next_position. cbn. destruct cc_nat_first as [k [Q _]]. now rewrite drop_correct in Q.
Qed.

Lemma next_position_bounds : n <= next_position <= m.
Proof.
unfold next_position. cbn. destruct cc_nat_first as [k [_ Q]]. split; oauto.
decide (n + k <= m); auto. exfalso.
specialize (Q (m-n)). apply Q; oauto. rewrite drop_correct. now rewrite_eq (n + (m - n) = m).
Qed.

Lemma next_position_all : forall j, n <= j < next_position -> ~ P(w j).
Proof.
unfold next_position. cbn. destruct cc_nat_first as [k [_ Q]].
intros j L'. specialize (Q (j - n)). rewrite drop_correct in Q.
rewrite_eq (n + (j - n) = j) in Q. apply Q. omega.
Qed.

Lemma can_find_next_position: (Sigma k, n <=k<=m /\ (P (w k)) /\ forall m', n <= m' < k -> ~(P (w m'))).
Proof.
exists next_position. auto using next_position_correct, next_position_bounds, next_position_all.
Qed.
End NextPosition.

Variant which requires m to be packed existentially
Section NextPositionExists.
Context{n: nat}.
Variable (L: exists m, n <= m /\ P (w m)).

Definition next_position_exists : nat.
Proof.
apply cc_nat in L. destruct L as [m [Q1 Q2]]. apply (next_position Q1 Q2). auto.
Defined.

Lemma next_position_exists_increasing : n <= next_position_exists.
Proof.
unfold next_position_exists. destruct cc_nat as [k [Q1 Q2]]. apply next_position_bounds.
Qed.

Lemma next_position_exists_correct: P (w (next_position_exists)).
Proof.
unfold next_position_exists. destruct cc_nat as [k [Q1 Q2]]. apply next_position_correct.
Qed.

Lemma next_position_exists_all : forall k, n <= k < next_position_exists -> ~ P (w k).
Proof.
unfold next_position_exists. destruct cc_nat as [k [Q1 Q2]]. apply next_position_all.
Qed.

End NextPositionExists.

End FindNextPosition.

Definition of the ω-Filter
Section InfiniteFilter.
Given a sequence w on a type X, a decidable predicate P on X and a proof that there are infinitely many positions in w at which P holds, there is function infinite_filter : Seq nat which has the following properties:
• infinite_filter gives a strictly monotone sequence
• P holds at all positions in w given by infinite_filter
• infinite_filter gives all positions in w, at which P holds

Context {X:Type}.
Variable (w: Seq X).
Variable (P : X -> Prop).
Context {decP: forall x, dec (P x)}.
Variable (infP: infiniteP P w).

Lemma ge_to_le n m : m >= n -> n <= m. Proof. omega. Qed.

Arguments cc_nat [p] [p_dec] _.
Arguments can_find_next_position [X] [P] [decP] [w] [n] [m] _ _.
Arguments next_position_exists [X] [P] [decP] [w] _ _.

Fixpoint infinite_filter (n : nat) : nat.
Proof.
induction n.
- apply (next_position_exists (w:=w) 0). apply infP.
- apply (next_position_exists (w:=w) (S(infinite_filter n))). apply infP.
Defined.

Lemma infinite_filter_s_monotone : s_monotone infinite_filter.
Proof.
intros n. cbn. pose (next_position_exists_increasing (infP (S (infinite_filter n)))). omega.
Qed.

Lemma infinite_filter_correct : (forall n, P ( w (infinite_filter n))).
Proof.
intros n. destruct n; cbn; apply next_position_exists_correct.
Qed.

Lemma infinite_filter_all n: forall k, infinite_filter n < k < infinite_filter (S n) -> ~ P (w k).
Proof.
simpl. apply next_position_exists_all.
Qed.

Lemma infinite_filter_zero: P (w 0) -> infinite_filter 0 = 0.
Proof.
intros P0. cbn. unfold next_position_exists. destruct cc_nat as [j [Q1 Q2]].
decide (next_position (X:=X) Q1 Q2 = 0); auto. exfalso.
eapply (next_position_all (X:=X) (L := Q1) (F:= Q2) (j := 0)(w:=w)); oauto.
Qed.

End InfiniteFilter.

## Ultimately Periodic Sequences

Ultimately periodic sequences are represented by a pair of strings.

Structure UPSeq (X:Type) := mkUPSeq
{ prefix: String X;
loop : String X
}.

Definition unfold (X:Type) (a :UPSeq X) := ((prefix a) +++ (loop a) to_omega).
Definition up_equal (X:Type) (a b: UPSeq X) := unfold a === unfold b.

Instance up_equal_Equivalence X :
Equivalence (@up_equal X).
Proof.
constructor.
- constructor.
- intros w_1 w_2 E n. now rewrite E.
- intros w_1 w_2 w_3 E_1 E_2 n. now rewrite <-E_2, E_1.
Qed.

Section UltimatelyPeriodicSequences.

Notation "a ==== b" := (up_equal a b) (at level 71).
Section Basic.
Context {X Y:Type}.
Implicit Types (u v : String X) (a b : UPSeq X).

Definition up_map (f: X -> Y) a : UPSeq Y := mkUPSeq (string_map f (prefix a)) (string_map f (loop a)).

Lemma up_map_correct (f: X -> Y) a: unfold (up_map f a) === seq_map f (unfold a).
Proof.
intros n. destruct a as [u v].
decide (n <= |u|) as [D|D]; unfold seq_map, unfold.
- now rewrite !prepend_string_begin by auto.
- rewrite !prepend_string_rest by comega. cbn. unfold seq_map. cbn. f_equal.
unfold omega_iteration, concat_inf. f_equal. f_equal. apply concat_inf_index_equal. auto.
Qed.

Lemma up_prefix u v: prefix (mkUPSeq u v) = u.
Proof. reflexivity. Qed.

Lemma up_loop u v: loop (mkUPSeq u v) = v.
Proof. reflexivity. Qed.

Global Instance unfold_proper: Proper (@up_equal X ==> @seq_equal X) (@unfold X).
Proof.
intros a b E. auto.
Qed.

Global Instance up_map_proper (f: X->Y) : Proper (@up_equal X ==> @up_equal Y) (fun a => up_map f a).
Proof.
intros a b E. unfold up_equal. rewrite !up_map_correct.
intros n. unfold seq_map. f_equal. apply E.
Qed.
End Basic.

Definition swap (X Y: Type) := fun (t:X * Y) => match t with (x,y) => (y,x) end.
Definition string_zip (X Y :Type) (v: String X) (u : String Y) : String (X * Y) := mkstring (seq_zip v u) (|v|).

Definition split_first (X:Type) (v: String X) k := (extract 0 k v).
Definition split_second (X:Type) (v: String X) k := (extract k (S (|v|)) v).

Lemma split_string_eq (X:Type) (v:String X) n : 0 < n <= |v| -> (split_first v n) ++ (split_second v n) == v.
Proof.
intros L. split.
- comega.
- intros k L'. decide (k < n).
+ now rewrite concat_string_begin by comega.
+ rewrite concat_string_rest by comega. cbn. rewrite drop_correct. f_equal. omega.
Qed.

Section StringZip.
Context{X Y: Type}.

Lemma string_zip_fst_up (u_1 v_1: String X) (u_2 v_2: String Y):
seq_map fst ((string_zip u_1 u_2) +++ (string_zip v_1 v_2) to_omega) === u_1 +++ v_1 to_omega.
Proof.
intros n. decide ( n <= |u_1|); unfold seq_map.
- now rewrite !prepend_string_begin by comega.
- rewrite !prepend_string_rest by comega. unfold omega_iteration, concat_inf. cbn.
f_equal. f_equal. now apply concat_inf_index_equal.
Qed.

Lemma string_zip_snd_up (u_1 v_1: String X) (u_2 v_2: String Y): |u_1| = |u_2| -> |v_1| = |v_2| ->
seq_map snd ((string_zip u_1 u_2) +++ (string_zip v_1 v_2) to_omega) === u_2 +++ v_2 to_omega.
Proof.
intros Lu Lv n. decide ( n <= |u_2|); unfold seq_map.
- now rewrite !prepend_string_begin by comega.
- rewrite !prepend_string_rest by comega. unfold omega_iteration, concat_inf. cbn.
f_equal. f_equal. rewrite Lu. now apply concat_inf_index_equal.
Qed.
End StringZip.

Section UPZip''.

Context {X Y : Type}.
Variables (a : UPSeq X) (b: UPSeq Y).

Notation split_pos := (|prefix a| - |prefix b|).
Notation loop'b := (fin_iter (loop b) (|prefix a|)).

Definition up_zip' : UPSeq Y :=
if (decision (|prefix a| = |prefix b|))
then b
else mkUPSeq (prefix b ++ split_first (loop'b) split_pos)
((split_second (loop'b) split_pos) ++ (split_first (loop'b) split_pos)).

Lemma up_zip'_equiv: |prefix a| >= |prefix b| -> up_zip' ==== b.
Proof.
intros L. unfold up_equal, unfold, up_zip'. destruct decision; auto.
rewrite up_prefix, up_loop, concat_prepend_string_associative, <-concat_strings_omega_unfold.
rewrite split_string_eq.
- now rewrite <-fin_iter_to_omega.
- rewrite <-fin_iter_length_bound. omega.
Qed.

Lemma up_zip'_prefix_length: |prefix a| >= |prefix b| -> |prefix a| = |prefix up_zip'|.
Proof.
intros L. unfold up_zip'. destruct decision; comega.
Qed.

Definition up_zip'' : UPSeq (X * Y) :=
mkUPSeq (string_zip (prefix a) (prefix b))
(string_zip (fin_iter (loop a) (|loop b|) ) (fin_iter (loop b) (|loop a|))).

Lemma up_zip''_proj1 : up_map fst up_zip'' ==== a.
Proof.
unfold up_equal. rewrite up_map_correct. unfold up_zip'', unfold.
now rewrite up_prefix, up_loop, string_zip_fst_up, <-fin_iter_to_omega.
Qed.

Lemma up_zip''_proj2 : |prefix a| = |prefix b| -> up_map snd up_zip'' ==== b.
Proof.
intros L. unfold up_equal. rewrite up_map_correct. unfold up_zip'', unfold.
rewrite up_prefix, up_loop, string_zip_snd_up, <-fin_iter_to_omega; auto.
rewrite !fin_iter_length. f_equal. rewrite Nat.mul_comm. f_equal.
Qed.

End UPZip''.

Section UPZip.

Context {X Y : Type}.
Variables (a : UPSeq X) (b: UPSeq Y).

Definition up_zip := if (decision (|prefix a| >= |prefix b|))
then up_zip'' a (up_zip' a b)
else up_zip''(up_zip' b a) b.

Lemma up_zip_proj1 : up_map fst up_zip ==== a.
Proof.
unfold up_zip. destruct decision as [D|D].
- now rewrite up_zip''_proj1.
- now rewrite up_zip''_proj1, up_zip'_equiv by omega.
Qed.

Lemma up_zip_proj2 : up_map snd up_zip ==== b.
Proof.
unfold up_zip. destruct decision as [D|D].
- rewrite up_zip''_proj2 by now apply up_zip'_prefix_length. now rewrite up_zip'_equiv.
- rewrite up_zip''_proj2.
+ reflexivity.
+ symmetry. apply up_zip'_prefix_length. omega.
Qed.

Lemma up_zip_correct: unfold up_zip === seq_zip (unfold a) (unfold b).
Proof.
intros n. unfold seq_zip.
enough (fst (unfold up_zip n) = unfold a n /\ snd (unfold up_zip n) = unfold b n) as [H1 H2].
- destruct (unfold up_zip n ). cbn in *. congruence.
- split.
+ pose up_zip_proj1 as Z. unfold up_equal in Z. rewrite up_map_correct in Z. unfold seq_map in Z. apply Z.
+ pose up_zip_proj2 as Z. unfold up_equal in Z. rewrite up_map_correct in Z. unfold seq_map in Z. apply Z.
Qed.

End UPZip.

Global Instance up_zip_proper (X Y : Type): Proper (@up_equal X ==> @up_equal Y ==> @up_equal (X*Y)) up_zip.
Proof.
intros a a' Ea b b' Eb. intros n.
rewrite 2up_zip_correct. unfold seq_zip. now rewrite Ea, Eb.
Qed.

End UltimatelyPeriodicSequences.

## Sequences over Finite Alphabet

Require Import FinType.

### Cosntruction of Duplicates

Lemma can_find_duplicate (X: finType) k (r: Seq X) : (Sigma n1 n2, n1 < n2 <= k /\ r n1 = r n2) + {k <= Cardinality X }.
Proof.
decide (k <= Cardinality X).
- now right.
- left.
assert (k > Cardinality X) as H by omega.
assert (dec (exists n2, n2 <= k /\ (exists n1, n1 < n2 /\ r n1 = r n2))) as [D|D] by auto.
+ apply cc_nat in D; auto using and_dec. destruct D as [n1 [L D]].
apply cc_nat in D; auto. destruct D as [n2 [L2 D]]. exists n2, n1 ; split; oauto.
+ enough (S k <= Cardinality X) by omega.
rewrite <-card_finType_Le_K.
apply (pidgeonHole_inj (f:= fun (k : finType_Le_K k) => r (proj1_sig k))).
intros x y E.
decide (x = y) as [D' |D']; auto.
exfalso. apply D. destruct x as [x px]; destruct y as [y py].
decide (x < y).
* exists y. split. now apply pure_le_k_iff. exists x. split; oauto.
* assert (y <> x). { intros <-. apply D'. f_equal. apply pure_eq. }
exists x. split. now apply pure_le_k_iff. exists y. split; oauto.
Qed.

Lemma can_find_duplicate' (X: finType) k_l k_u (r: Seq X): (Sigma n1 n2, k_l <= n1 < n2 /\ n2 <= k_u /\ r n1 = r n2) + {k_u - k_l <= Cardinality X }.
Proof.
destruct (can_find_duplicate (k_u - k_l) (drop k_l r)) as [[n1 [n2 [L E]]]|D].
- left. exists (k_l + n1), (k_l + n2). repeat rewrite drop_correct in E. repeat split; oauto.
- now right.
Qed.

### Interpretation of {n|n<=k} as Prefix of Sequences

Section StringsOfConstantLength.

Variable (m : nat).
Context {X : finType}.

Definition ConstLengthString := X^(finType_Le_K m).

Lemma dummy : (finType_Le_K m).
Proof.
exists 0. apply pure_equiv. unfold le_k. omega.
Qed.

Definition to_seq (b:ConstLengthString) := fun (n:nat) => match (decision (pure (le_k m) n) ) with
| left D => applyVect b (exist (pure (le_k m )) n D)
| right D => applyVect b dummy
end.
Definition to_bounded (w:Seq X): ConstLengthString := vectorise (fun (n:(finType_Le_K m)) => match n with exist _ n _ => w n end).

Lemma to_bounded_unchanged (w:Seq X) n (L: n <= m ): applyVect (to_bounded w) (create_Le_K L) = w n.
Proof.
unfold to_bounded. now rewrite apply_vectorise_inverse.
Qed.

Lemma to_seq_unchanged (b:ConstLengthString) n (L: n <= m ): to_seq b n = applyVect b (create_Le_K L).
Proof.
unfold to_seq.
destruct (decision (pure (le_k m) n)) as [D|D].
- f_equal. unfold create_Le_K.
f_equal. apply pure_eq.
- exfalso. apply D. now apply pure_equiv.
Qed.

Lemma to_seq_unchanged' (b:ConstLengthString) (N: finType_Le_K m): to_seq b (proj1_sig N) = applyVect b N.
Proof.
unfold to_seq. destruct decision as [D|D].
- f_equal. destruct N. f_equal. apply pure_eq.
- exfalso. apply D. now destruct N.
Qed.

Lemma bounded_unchanged (r:Seq X) n : n <=m-> to_seq (to_bounded r) n = r n.
Proof.
intros L.
rewrite (to_seq_unchanged (to_bounded r) L).
apply to_bounded_unchanged.
Qed.

Lemma bounded_unchanged_string (w: Seq X) : mkstring w m == mkstring (to_seq(to_bounded w)) m.
Proof.
split; cbn; auto. intros n L. now rewrite bounded_unchanged.
Qed.

End StringsOfConstantLength.

Arguments to_bounded {m} {X} w.

# Languages of Strings and Sequences

Notation SeqLang X := (Language (Seq X)).
Notation "x ^0\$0" := (universal_language (X:=Seq x)) (at level 20).
Notation StringLang X := (Language (String X)).

Extensionality on strings: a string language is extensional if it does only care about the values of the valid indicies of w
Definition StringLang_Extensional (X:Type) (l: StringLang X):= forall (v v' : String X ), l v -> v == v' -> l v'.
Definition SeqLang_Extensional (X:Type) (l: SeqLang X):= forall (w w' : Seq X ), l w -> w === w' -> l w'.

## Image and Preimage

Definition PreImage(X Y :Type)(f: X -> Y) (L: SeqLang Y) :SeqLang X := fun w => L(seq_map f w).
Definition Image (X Y: Type) (f: X-> Y)(L : SeqLang X) : SeqLang Y := fun w => exists w', seq_map f w' === w /\ L w'.

## ω-Iteration of a String Languages

Definition lang_o_iter (A:Type) (l : StringLang A) : SeqLang A := fun w =>
exists (f: Seq (String A)), (forall n, l ( f n)) /\ (w === concat_inf f).

Notation "l ^00" := (lang_o_iter l) (at level 20).
Section LanguageOmegaIteration.

Context{A:Type}.
Variable l: StringLang A.

(* Imagine the omega of the two 00, but I cannot use letters, otherwise coq doc pretty printing
does not like the ^ in front !*)

The second definition is weaker. If the string language is extensional, both definitions are equivalent.

Lemma lang_o_iter_extract w : (exists f, s_monotone f /\ (f 0 = 0) /\ (forall n,l (extract (f n) (f (S n)) w) )) -> l^00 w.
Proof.
intros [f [Inc [B L]]].
pose (f' := fun n => (extract (f n) (f (S n)) w)).
exists f'. split.
- apply L.
- intros n. unfold concat_inf. destruct concat_inf_indices as [i j] eqn:H. cbn.
destruct (concat_inf_index_to_begin H) as [H1 H2].
rewrite H1. rewrite drop_correct. f_equal. f_equal. clear H1 H2 H.
induction i; cbn; auto.
rewrite IHi. specialize (Inc i). omega.
Qed.

Lemma string_omega_omega_iteration v: l v -> l^00 (v to_omega).
Proof.
intros EL. now exists (fun _ => v).
Qed.

Lemma lang_omega_iteration_extensional: StringLang_Extensional l -> SeqLang_Extensional (l^00).
Proof.
intros EL. intros w w' [f [P Q]] Ew.
exists f. split; auto. now rewrite <-Ew, Q.
Qed.

End LanguageOmegaIteration.

Instance lang_omega_iteration_proper A: Proper (@language_eq (String A) ==> @language_eq (Seq A)) (@lang_o_iter A).
Proof.
intros L1 L2 E. intros w. split; firstorder.
Qed.

## Prepending a String on a Sequence Language

Definition lang_prepend (X:Type) (lf : StringLang X) (ls: SeqLang X) : (SeqLang X) :=
fun w => exists v w', w === v +++ w' /\ lf v /\ ls w'.

Notation "l1 'o' l2" := (lang_prepend l1 l2) (at level 50).

Lemma in_lang_prepend (X:Type) (L_1: StringLang X) (L_2: SeqLang X) v w: L_1 v -> L_2 w -> (L_1 o L_2) (v +++ w).
Proof.
intros L1 L2. exists v, w. repeat split; auto.
Qed.

Global Instance lang_prepend_proper X: Proper (@language_eq (String X) ==> @language_eq (Seq X) ==> @language_eq (Seq X)) (@lang_prepend X).
Proof.
intros LS1 LS2 ES L1 L2 E. intros w. split; firstorder.
Qed.

Lemma split_position_lang_prepend X lf ls (w: Seq X):
(exists n, lf (mkstring w n) /\ ls (drop (S n) w)) -> (lf o ls) w.
Proof.
intros [n [P R]].
exists (mkstring w n), (drop (S n) w). repeat split; auto.
now rewrite revert_drop.
Qed.

Lemma prepend_on_omega_iteration (A:Type) (l_1 l_2 : StringLang A) w : (l_1 o l_2^00) w
-> exists v f_w, l_1 v /\ (forall k, l_2 (f_w k)) /\ w === (v +++ (concat_inf f_w )) .
Proof.
intros [v [w' [E [P [f [Q R]]]]]].
exists v, f. repeat split; auto. now rewrite E, R.
Qed.