Theory Lyndon_Addition

(*  Title:      Combinatorics_Words_Lyndon.Lyndon_Addition
    Author:     Štěpán Holub, Charles University
    Author:     Štěpán Starosta, CTU in Prague

Part of Combinatorics on Words Formalized. See https://gitlab.com/formalcow/combinatorics-on-words-formalized/
*)

theory Lyndon_Addition
  imports Lyndon Szpilrajn.Szpilrajn

begin

subsection "The minimal relation"

text ‹We define the minimal relation which guarantees the lexicographic minimality of w compared to its
nontrivial conjugates.›

inductive_set rotate_rel :: "'a list  'a rel" for w
  where  "0 < n  n < |w|  (mismatch_pair w (rotate n w))  rotate_rel w"

text‹A word is Lyndon iff the corresponding order of letters is compatible with  @{term "rotate_rel w"}.›

lemma (in linorder) rotate_rel_iff: assumes "w  ε"
  shows  "Lyndon w  rotate_rel w  {(x,y). x < y}" (is "?L  ?R")
proof
  assume "Lyndon w" show "rotate_rel w  {(x,y). x < y}"
  proof
    fix  x assume "x  rotate_rel w"
    then obtain n where "x = mismatch_pair w (rotate n w)" and "0 < n" and "n < |w|"
      using rotate_rel.cases by blast
    have "w <lex rotate n w"
      using LyndonD[OF Lyndon w 0 < n n < |w|].
    from this[unfolded lexordp_conv_lexord]
      prim_no_rotate[OF Lyndon_prim[OF Lyndon w] 0 < n  n < |w|]
    show "x  {(a, b). a < b}"
      using lexord_mismatch[of w "rotate n w" "{(a,b). a < b}", folded x = mismatch_pair w (rotate n w)]
        rotate n w  w rotate_comp_eq[of w n]
      unfolding irrefl_def by blast
  qed
next
  assume "?R"
  show "?L"
    unfolding Lyndon.simps
  proof(simp add: assms)
    have "w <lex rotate n w" if "0 < n"  "n < |w|" for n
    proof-
      have "¬ w  rotate n w"
        using rotate_comp_eq[of w n] subsetD[OF ?R, OF rotate_rel.intros[OF 0 < n n < |w|]]
          mismatch_pair_lcp[of w "rotate n w"] by fastforce
      from mismatch_lexord_linorder[OF this subsetD[OF ?R, OF rotate_rel.intros[OF 0 < n n < |w|]]]
      show "w <lex rotate n w".
    qed
    thus "n. 0 < n  n < |w|  w <lex rotate n w"  by blast
  qed
qed

text‹It is well known that an acyclic order can be extended to a total strict linear order. This means that
a word is Lyndon with respect to some order iff its @{term "rotate_rel w"} is acyclic.
›
lemma Lyndon_rotate_rel_iff:
  "acyclic (rotate_rel w)  ( r. strict_linear_order r  rotate_rel w  r)" (is "?L  ?R")
proof
  assume "?R" thus "?L"
    unfolding strict_linear_order_on_def acyclic_def irrefl_def
    using trancl_id trancl_mono by metis
next
  assume "?L" thus "?R"
    using acyclic_order_extension by auto
qed

lemma slo_linorder: "strict_linear_order r  class.linorder (λ a b. (a,b)  r=) (λ a b. (a,b)  r)"
    unfolding strict_linear_order_on_def strict_partial_order_def irrefl_def trans_def total_on_def
    by unfold_locales blast+

text‹Application examples›

lemma assumes "w  ε" and "acyclic (rotate_rel w)" shows "primitive w"
proof-
  obtain r where "strict_linear_order r" and "rotate_rel w  r"
    using Lyndon_rotate_rel_iff assms by blast

  interpret r: linorder "λ a b. (a,b)  r=" "λ a b. (a,b)  r"
    using slo_linorder[OF strict_linear_order r].

  have "r.Lyndon w"
    using r.rotate_rel_iff[OF w  ε] rotate_rel w  r by blast

  from r.Lyndon_prim[OF this]
  show "primitive w".

qed

lemma assumes "w  ε" and "acyclic (rotate_rel w)" shows "¬ bordered w"
proof-
  obtain r where "strict_linear_order r" and "rotate_rel w  r"
    using Lyndon_rotate_rel_iff assms by blast

  interpret r: linorder "λ a b. (a,b)  r=" "λ a b. (a,b)  r"
    using slo_linorder[OF strict_linear_order r].

  have "r.Lyndon w"
    using r.rotate_rel_iff[OF w  ε] rotate_rel w  r by blast

  from r.Lyndon_unbordered[OF this]
  show "¬ bordered w".
qed

end