Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
f569711
add regex_of_dfa, matches'_sum_map, IsRegular.regex
chiyunhsu Jul 15, 2026
1152ce8
Add language_sum
chiyunhsu Jul 15, 2026
66dddab
Merge branch 'main' into IsRegularIffRegex
chiyunhsu Jul 16, 2026
ff56e6a
Changes union to sum to preserve Language API
chiyunhsu Jul 17, 2026
9fce529
Initial progress
brooke-gill Jul 21, 2026
d9315b4
Merge branch 'main' into IsRegularIffRegex
chiyunhsu Jul 21, 2026
bdaac4a
Added regex_of_dfa' to avoid equiv with Fin n
chiyunhsu Jul 21, 2026
52f25eb
Merge pull request #3 from chiyunhsu/Chiyun_IsRegularIffRegex
chiyunhsu Jul 21, 2026
1dd27bf
Added regex_of_dfa'
brooke-gill Jul 23, 2026
f141577
Finished iff_dfa'
brooke-gill Jul 26, 2026
1234567
Finished language_sum
brooke-gill Jul 27, 2026
c1fcd5b
minimum golf
chiyunhsu Jul 28, 2026
31decc2
Trial statement relating regex_of_dfa with path
chiyunhsu Jul 28, 2026
47a454a
Added assignments for next week
chiyunhsu Jul 28, 2026
0000000
Finished aux
brooke-gill Jul 31, 2026
1aa5732
Change the assumption of regex_of_dfa to depend only on da
chiyunhsu Aug 2, 2026
3ca35a8
Some progress on main thm; add lemmas on matches
chiyunhsu Aug 3, 2026
fd72bc3
golf on main thm
chiyunhsu Aug 3, 2026
479fd18
Worked on empty_or_char_of_path_supp_empty
brooke-gill Aug 3, 2026
b513f77
Small change
chiyunhsu Aug 4, 2026
a328ab9
Merge branch 'IsRegularIffRegex' of https://github.com/chiyunhsu/csli…
chiyunhsu Aug 4, 2026
3ef773d
Comment out unnecessary assumptions
chiyunhsu Aug 4, 2026
f7a69bf
Added backwards direction
brooke-gill Aug 4, 2026
b424913
Code suggestion and some progress
chiyunhsu Aug 4, 2026
f2cd0f9
Change regex_of_da' to regex_of_flts
chiyunhsu Aug 4, 2026
172b6d4
Add new lemmas
chiyunhsu Aug 4, 2026
36527ad
Deleted unnecessary info
chiyunhsu Aug 4, 2026
62d5507
Finished set_aux
brooke-gill Aug 5, 2026
719d5d7
Major revision
chiyunhsu Aug 7, 2026
1eb056b
Update
chiyunhsu Aug 7, 2026
2c2b450
Finished mem_sum_matches'_iff
brooke-gill Aug 9, 2026
10c728f
Finished isPrefix_splitFirst
brooke-gill Aug 11, 2026
90c9ccc
Finished isSuffix_splitLast
chiyunhsu Aug 11, 2026
2d76705
Added proofs for isSuffix_splitLast, splitLastCompl, splitLast_append
brooke-gill Aug 11, 2026
c5b4be8
Added defs for splitLast_mem, splitLastCompl_mem
brooke-gill Aug 11, 2026
e4f35c8
Assign new tasks
chiyunhsu Aug 11, 2026
751d198
Proved splitFirstCompl_mem
chiyunhsu Aug 11, 2026
147606e
Proved path3
chiyunhsu Aug 17, 2026
02219ae
Finished pathSupp_append
brooke-gill Aug 17, 2026
48af610
Golfed pathSupp_append
brooke-gill Aug 17, 2026
3eb5173
Finished hole #1 of splitLast_mem
brooke-gill Aug 18, 2026
5912e0d
Finished a hole in splitLast_mem
brooke-gill Aug 18, 2026
0dec154
Finished path1 hole
brooke-gill Aug 18, 2026
9d6e458
Finished path1
brooke-gill Aug 18, 2026
2f591f9
Slight golf
chiyunhsu Aug 18, 2026
38a1fc1
Finished part of splitLast_mem
brooke-gill Aug 18, 2026
8337681
Task assigned
chiyunhsu Aug 18, 2026
27af11b
Update
chiyunhsu Aug 18, 2026
f43a101
Proved the harder part of splitLast_mem
chiyunhsu Aug 19, 2026
e88753a
Proved splitLastCompl_mem
chiyunhsu Aug 20, 2026
b550ea1
Merge branch 'main' into IsRegularIffRegex
chiyunhsu Aug 22, 2026
d32d270
Finished first hole
brooke-gill Aug 24, 2026
48c65d1
Finished second hole in splitLast_mem
brooke-gill Aug 24, 2026
458c9bf
Golfing
brooke-gill Aug 24, 2026
efe6912
Assign tasks
chiyunhsu Aug 26, 2026
d80c574
Removed leftover code
brooke-gill Aug 26, 2026
19abbc3
Finished hole 3
brooke-gill Aug 28, 2026
efd54e5
Golfed hole 3
brooke-gill Aug 28, 2026
70fe2a9
One line!!
brooke-gill Aug 28, 2026
301d35d
Finished hole 2
brooke-gill Aug 29, 2026
416bfc1
Finished IsRegular.iff_regex
brooke-gill Aug 30, 2026
0ac954c
Golf a hole
chiyunhsu Aug 31, 2026
c132ee1
golf another hole; please continue to golf it
chiyunhsu Aug 31, 2026
d5eb931
Deleting old codes
chiyunhsu Aug 31, 2026
c80620c
Golfed hole 2 to remove `have`
brooke-gill Sep 1, 2026
08514f4
Golfed hole 2
brooke-gill Sep 1, 2026
5f53ce5
Folded rw statement into simp_all
brooke-gill Sep 1, 2026
43ba335
Restructuring files until before mtr_head_eq
chiyunhsu Sep 1, 2026
7daf804
Discard use of mtr lemmas
chiyunhsu Sep 1, 2026
1ad979d
Finished splitLast section
chiyunhsu Sep 2, 2026
f371d09
Finished kstar section. Regex is WIP
chiyunhsu Sep 2, 2026
d506784
Finish first round of proofreading
chiyunhsu Sep 2, 2026
50bcb62
Minor notational changes
chiyunhsu Sep 2, 2026
25d5608
Typo
chiyunhsu Sep 2, 2026
111ea1f
Fold "rw/grind" into grind
brooke-gill Sep 3, 2026
ed34707
Fold "have"s into grind
brooke-gill Sep 3, 2026
bbe821f
Free golfs
brooke-gill Sep 3, 2026
1ca1b2f
More free golfs
brooke-gill Sep 3, 2026
8c00c42
Rewind some golf
chiyunhsu Sep 3, 2026
3dfe1fb
Added docstring and modified comments
brooke-gill Sep 8, 2026
67b35f8
Small change in comment; delete RegularExpresions.lean
chiyunhsu Sep 8, 2026
60caa7a
Switched the role of splitLast and splitLastCompl
chiyunhsu Sep 8, 2026
ff12620
Delete a commented proof
chiyunhsu Sep 8, 2026
b64f539
Added docstrings to important theorems
brooke-gill Sep 8, 2026
8d895c8
Added docstrings to slightly less important theorems
brooke-gill Sep 8, 2026
0d274b3
Fixed double newlines
brooke-gill Sep 8, 2026
d8cc3ad
Notational Cleanups
chiyunhsu Sep 8, 2026
743492f
Merge branch 'main' into IsRegularIffRegex
chiyunhsu Sep 8, 2026
b717135
Delete one import
chiyunhsu Sep 8, 2026
27204ad
Run lake exe mk_all
chiyunhsu Sep 8, 2026
1c0a2d5
Delete accidental added blank line
chiyunhsu Sep 8, 2026
5ab53cd
Update authors
chiyunhsu Sep 8, 2026
064d881
Added missing docstring
chiyunhsu Sep 8, 2026
a8c7125
Minor
chiyunhsu Sep 9, 2026
7423fca
Change BddPath to BddPathFLTS
chiyunhsu Sep 9, 2026
bf6b5ba
WIP
chiyunhsu Sep 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cslib.lean
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public import Cslib.Computability.Distributed.FLP.ZeroConsensus
public import Cslib.Computability.Languages.Congruences.BuchiCongruence
public import Cslib.Computability.Languages.Congruences.RightCongruence
public import Cslib.Computability.Languages.ExampleEventuallyZero
public import Cslib.Computability.Languages.KleeneAlgorithm
public import Cslib.Computability.Languages.Language
public import Cslib.Computability.Languages.LanguageHom
public import Cslib.Computability.Languages.MyhillNerode
Expand Down
334 changes: 334 additions & 0 deletions Cslib/Computability/Languages/KleeneAlgorithm.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
/-
Copyright (c) 2026 Brooke Gill and Chi-Yun Hsu. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Brooke Gill, Chi-Yun Hsu
-/

module

public import Cslib.Computability.Automata.Acceptors.Acceptor
public import Cslib.Computability.Automata.DA.Basic
public import Cslib.Computability.Automata.NA.Basic
public import Mathlib.Computability.Language
public import Mathlib.Computability.RegularExpressions

/-!
# Kleene's Algorithm

Kleene's algorithm constructs a regular expresssion by induction on a bound `k` that restricts
which interior states an execution may pass through.
It is used to prove `Cslib.Language.IsRegular.iff_regex`, that every language accepted by
a DFA comprised of finite states is the language of a regular expression.
We implement Kleene's algorithm for the more general case of an NFA instead of DFA,
since `Execution` is developed for `LTS` but not `FLTS`
The special case where the NFA has only one start state and one accept state is proved in
`regex_of_nfa_singleton_start_accept` in this file.

## Main definitions
- `BddPathLTS`: A labelled transition system containing a start state, last state, and
a specific bound on all interior states
- `Regex lts i j k`: The regular expression for the executions from state `i` to state `j`,
whose interior states are all under a specific bound `k`

## Main results
- `regex_of_nfa_singleton_start_accept`: NFA with one start state and one accept state have
a matching regular expression
- `language_bddpath_eq_nfa`: A bound that has reached the total number of states no longer
constrains anything
- `language_bddpath_eq_regex`: `Regex lts i j k` matches the language of the NFA with
start state `i`, accept state `j`, and interior states below `k`

## References

* [J. E. Hopcroft, R. Motwani, J. D. Ullman,
*InTroduction to Automata Theory, Languages, and Computation*][Hopcroft2006]
-/

@[expose] public section

namespace Cslib.Language

open scoped LTS

variable {Symbol : Type*}

open Automata Acceptor

variable {n : ℕ}

/-- A Bounded Path (`BddPathLTS`) has states `Fin n` and accepts strings (lists of symbols)
starting with state `start` and ending with state `last`
with the interior states less than `bound`. -/
structure BddPathLTS (n : ℕ) (Symbol : Type*) extends LTS (Fin n) Symbol where
/-- The start state of the path. -/
start : Fin n
/-- The last state of the path. -/
last : Fin n
/-- The bound for interior states of the path. -/
bound : ℕ

instance : Acceptor (BddPathLTS n Symbol) Symbol where
Accepts (p : BddPathLTS n Symbol) (xs : List Symbol) :=
∃ ss : List (Fin n),
p.toLTS.Execution p.start xs p.last ss ∧
∀ idx, ∀ _ : 0 < idx ∧ idx + 1 < ss.length, ss[idx] < p.bound

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a nitpick: use i instead of idx. I understand that you want to use i, j, k etc to denote states from Fin n. But I think it is clearer to keep denoting the latter using s, t, r, etc.

-- ∀ s ∈ ss.tail.dropLast, s < p.bound

theorem language_bddpath_eq_nfa (lts : LTS (Fin n) Symbol) (i j : Fin n) {k : ℕ} (hk : n ≤ k) :
language (BddPathLTS.mk lts i j k) =
language (NA.FinAcc.mk {Tr := lts.Tr, start := {i}} {j}) := by
simp [language, Accepts]
grind
Comment on lines +59 to +81

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this whole block of code can be replaced by:

namespace Cslib.LTS

variable {Symbol : Type*} {n : ℕ}

def BddExec (lts : LTS (Fin n) Symbol) (bound : ℕ)
    (start : Fin n) (xs : List Symbol) (last : Fin n) (ss : List (Fin n)) : Prop :=
  lts.Execution start xs last ss ∧ ss.AllButFirstLast (· < bound)

def bddLang (lts : LTS (Fin n) Symbol) (bound : ℕ) (start last : Fin n) : Language Symbol :=
  { xs | ∃ ss, lts.BddExec bound start xs last ss }

end Cslib.LTS

Note that your main proof will refer to different starts, lasts, and bounds in the same statement, so it is better to have them as explicit parameters rather than as fields of structures which you need to construct and destruct all the time.


open List

section splitLast

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is probably better to put the splitLast section after the splitFirst section, because the latter seems easier to reason about than the former.


/-- Starting at state `i`, the function `splitLast` sends a string to its longest prefix
ending at state `k`. -/
def splitLast {lts : LTS (Fin n) Symbol} {i j : Fin n} {xs : List Symbol} {ss : List (Fin n)}
(_ : LTS.Execution lts i xs j ss) (k : Fin n) : List Symbol :=
xs.take (xs.length - (ss.reverse.tail.findIdx (· = k) + 1))

/-- Starting at state `i`, the function `splitLastCompl` sends a string to its shortest suffix
starting at state `k`. -/
def splitLastCompl {lts : LTS (Fin n) Symbol} {i j : Fin n} {xs : List Symbol} {ss : List (Fin n)}
(_ : LTS.Execution lts i xs j ss) (k : Fin n) : List Symbol :=
xs.drop (xs.length - (ss.reverse.tail.findIdx (· = k) + 1))

/-- If the execution of `xs` from `i` to `j` has `k` as the largest interior state, then
`splitLast flts i k xs` is a path from `i` to `k` whose interior states are all below `k + 1`. -/
theorem splitLast_mem {lts : LTS (Fin n) Symbol} {i j k : Fin n} {xs : List Symbol}
{ss : List (Fin n)} (hex : lts.Execution i xs j ss)
(hbdd : ∀ idx, ∀ _ : 0 < idx ∧ idx + 1 < ss.length, ss[idx] < k.val + 1)
(hbdd' : ¬(∀ idx, ∀ _ : 0 < idx ∧ idx + 1 < ss.length, ss[idx] < k)) :
splitLast hex k ∈ language (BddPathLTS.mk lts i k (k + 1)) := by sorry

/-- If the execution of `xs` from `i` to `j` has `k` as the largest interior state, then
`splitLastCompl flts i k xs` is a path from `k` to `j` whose interior states are all below `k`. -/
theorem splitLastCompl_mem {lts : LTS (Fin n) Symbol} {i j k : Fin n} {xs : List Symbol}
{ss : List (Fin n)} (hex : lts.Execution i xs j ss)
(hbdd : ∀ idx, ∀ _ : 0 < idx ∧ idx + 1 < ss.length, ss[idx] < k.val + 1)
(hbdd' : ¬(∀ idx, ∀ _ : 0 < idx ∧ idx + 1 < ss.length, ss[idx] < k)) :
splitLastCompl hex k ∈ language (BddPathLTS.mk lts k j k) := by sorry

/-- Part of the recursion step of Kleene's algorithm.
An execution from `i` to `j` whose interior states are all at below `k + 1` either
has no interior state equal to `k`, or it splits at its last visit to `k` into
an execution from `i` to `k` with interior states below `k + 1`,
followed by an execution from `k` to `j` with interior states below `k`. -/
theorem language_bddpath_splitLast (lts : LTS (Fin n) Symbol) (i j k : Fin n) :
language (BddPathLTS.mk lts i j (k + 1)) = language (BddPathLTS.mk lts i j k) +
(language (BddPathLTS.mk lts i k (k + 1)) * language (BddPathLTS.mk lts k j k)) := by sorry

end splitLast

section splitFirst

/-- Starting at state `i`, the function `splitFirst` sends a string to its shortest prefix
ending at state `k`. -/
def splitFirst {lts : LTS (Fin n) Symbol} {i j : Fin n} {xs : List Symbol} {ss : List (Fin n)}
(_ : LTS.Execution lts i xs j ss) (k : Fin n) : List Symbol :=
xs.take (ss.tail.findIdx (· = k) + 1)

/-- Starting at state `i`, the function `splitFirstCompl` sends a string to its longest suffix
starting at state `k`. -/
def splitFirstCompl {lts : LTS (Fin n) Symbol} {i j : Fin n} {xs : List Symbol} {ss : List (Fin n)}
(_ : LTS.Execution lts i xs j ss) (k : Fin n) : List Symbol :=
xs.drop (ss.tail.findIdx (· = k) + 1)
Comment on lines +128 to +138

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is in general a good idea to include only those parameters which are actually needed:

def splitFirst (xs : List Symbol) (ss : List (Fin n)) (k : Fin n) : List Symbol :=
  xs.take (ss.findIdxButFirst (· = k))

def splitFirstCompl (xs : List Symbol) (ss : List (Fin n)) (k : Fin n) : List Symbol :=
  xs.drop (ss.findIdxButFirst (· = k))

The same comment applies to SplitLast[Compl] above.


theorem splitFirst_mem {lts : LTS (Fin n) Symbol} {i k : Fin n} {xs : List Symbol}
{ss : List (Fin n)} (hex : lts.Execution i xs k ss)
(hbdd : ∀ idx, ∀ _ : 0 < idx ∧ idx + 1 < ss.length, ss[idx] < k.val + 1) :
splitFirst hex k ∈ language (BddPathLTS.mk lts i k k) := by
use ss.take (ss.tail.findIdx (· = k) + 1 + 1)
by_cases hss : ss.tail = []
· simp only [splitFirst, hss, findIdx_nil, zero_add, Nat.reduceAdd, length_take, lt_min_iff,
Order.lt_two_iff, add_le_iff_nonpos_left, nonpos_iff_eq_zero, getElem_take, Fin.val_fin_lt,
forall_and_index]
have : ss.length ≤ ss.tail.length + 1 := by rw [List.length_tail]; omega
grind
have t : (ss.tail.findIdx (· = k)) < ss.tail.length :=
findIdx_lt_length.mpr ⟨ss.tail.getLast hss, by grind⟩
simp only [splitFirst]
obtain ⟨hspec, hmin⟩ := (List.findIdx_eq t).mp rfl
constructor
· convert (LTS.Execution.split hex (ss.tail.findIdx (· = k) + 1) (by grind)).1
grind
· simp only [length_take, lt_min_iff, getElem_take, Fin.val_fin_lt]
intro idx ⟨hidx1, ⟨hidx2, hlength⟩⟩
have hidx2' : idx - 1 < (ss.tail.findIdx (· = k)) := by grind
simp only [Order.lt_add_one_iff, Fin.val_fin_le] at hbdd
apply lt_of_le_of_ne (hbdd idx ⟨hidx1, hlength⟩)
simpa [Nat.sub_add_cancel hidx1] using hmin (idx - 1) hidx2'

theorem splitFirstCompl_mem {lts : LTS (Fin n) Symbol} {i k : Fin n} {xs : List Symbol}
{ss : List (Fin n)} (hex : lts.Execution i xs k ss)
(hbdd : ∀ idx, ∀ _ : 0 < idx ∧ idx + 1 < ss.length, ss[idx] < k.val + 1) :
splitFirstCompl hex k ∈ language (BddPathLTS.mk lts k k (k + 1)) := by
by_cases hss : ss.tail = []
· use ss
have : ss.length ≤ ss.tail.length + 1 := by rw [List.length_tail]; omega
have : xs = [] := by grind
simp only [this, splitFirstCompl, drop_nil, Order.lt_add_one_iff, Fin.val_fin_le,
forall_and_index] at hex ⊢
grind
use ss.drop (ss.tail.findIdx (· = k) + 1)
have t : (ss.tail.findIdx (· = k)) < ss.tail.length :=
findIdx_lt_length.mpr ⟨ss.tail.getLast hss, by grind⟩
simp only [splitFirstCompl]
obtain ⟨hspec, hmin⟩ := (List.findIdx_eq t).mp rfl
constructor
· convert (LTS.Execution.split hex (ss.tail.findIdx (· = k) + 1) (by grind)).2
grind
· simp only [length_drop, getElem_drop]
intro idx hidx
exact hbdd (ss.tail.findIdx (· = k) + 1 + idx) (by grind)

/-- Part of the recursion step of Kleene's algorithm.
An execution from `i` to `j` whose interior states are all below `k + 1` splits upon
first reaching `k`.
The part before is an executionfrom `i` to `k` with interior states below `k`.
The part after is an exection from `k` to `k` with interior states below `k + 1`. -/
theorem language_bddpath_splitFirst (lts : LTS (Fin n) Symbol) (i k : Fin n) :
language (BddPathLTS.mk lts i k (k + 1)) =
language (BddPathLTS.mk lts i k k) * language (BddPathLTS.mk lts k k (k + 1)) := by
ext xs
rw [Language.mem_mul]
constructor
· intro ⟨ss, ⟨hex, hbdd⟩⟩
use splitFirst hex k, splitFirst_mem hex hbdd,
splitFirstCompl hex k, splitFirstCompl_mem hex hbdd,
take_append_drop _ _
· intro ⟨ys, ⟨⟨ssy, ⟨hyex, hybdd⟩⟩, ⟨zs, ⟨⟨ssz, ⟨hzex, hzbdd⟩⟩, happend⟩⟩⟩⟩
use ssy ++ ssz.tail
constructor
· simpa [happend] using LTS.Execution.comp hyex hzex
· simp only [Fin.val_fin_lt, forall_and_index, Order.lt_add_one_iff, Fin.val_fin_le,
length_append, length_tail] at hybdd hzbdd ⊢
intro idx hidx1 hidx2
rw [List.getElem_append]
rcases lt_trichotomy (idx + 1) ssy.length with h | h | h
· simp only [(by omega : idx < ssy.length), ↓reduceDIte]
apply le_of_lt
exact hybdd idx hidx1 h
· simp only [(by omega : idx < ssy.length), ↓reduceDIte]
have eq : idx = ssy.length - 1 := by omega
subst idx
exact le_of_eq hyex.last
· have hlength : ¬idx < ssy.length := by omega
simp only [hlength, ↓reduceDIte, getElem_tail]
have hidx1' : 0 < idx - ssy.length + 1 := by omega
have hidx2' : idx - ssy.length + 1 + 1 < ssz.length := by omega
exact hzbdd (idx - ssy.length + 1) hidx1' hidx2'

end splitFirst

open Computability

section kstar

theorem kstar_eq {α : Type*} (l : Language α) : l∗ = (l - 1)∗ := by
ext x
rw [Language.kstar_def_nonempty, Language.mem_kstar]
exact ⟨fun ⟨S, hx, h⟩ => ⟨S, ⟨hx, fun y ys => h y ys⟩⟩,
fun ⟨S, ⟨hx, h⟩⟩ => ⟨S, hx, fun y ys => h y ys⟩⟩

/-- Part of the recursion step of Kleene's algorithm.
An execution from `k` to `k` whose interior states are all below `k + 1` is a concatenation of
executions from `k` to `k` whose interior states are all below `k`. -/
theorem language_bddpath_kstar (lts : LTS (Fin n) Symbol) (k : Fin n) :
language (BddPathLTS.mk lts k k (k + 1)) = (language (BddPathLTS.mk lts k k k))∗ := by sorry

end kstar

open RegularExpression

section Regex

theorem mem_sum_matches'_iff {α : Type*} (L : List (RegularExpression α)) (x : List α) :
x ∈ (L.sum).matches' ↔ ∃ P ∈ L, x ∈ P.matches' := by
induction L with
| nil => simp
| cons head tail ih =>
simp only [sum_cons, matches', Language.mem_add, ih, mem_cons, exists_eq_or_imp]

variable [Fintype Symbol]

/-- Regex i j k is the regex for the path from state i to state j passing through states < k.
When k = 0, i = j, the regex is ε union all characters from state i to state i.
When k = 0, i ≠ j, the regex is all characters from state i to state j.
For k + 1, the regex is the union of Regex i j k and
(Regex i k k) (Regex k k k)∗ (Regex k j k). -/
noncomputable def Regex (lts : LTS (Fin n) Symbol) [∀ i j, DecidablePred fun x => lts.Tr i x j]
(i j : Fin n) : ℕ → RegularExpression Symbol
| 0 =>
let chars := (Finset.univ.filter
(fun x : Symbol ↦ lts.Tr i x j)).toList.map RegularExpression.char
if i = j then 1 + chars.sum else chars.sum
| k + 1 =>
if h : n ≤ k then Regex lts i j k
else
let kFin : Fin n := ⟨k, by omega⟩
Regex lts i j k + Regex lts i kFin k * (Regex lts kFin kFin k).star * Regex lts kFin j k

/-- The correctness of Kleene's algorithm.
`Regex lts i j k` exactly matches the sTrings that have a run starting
at `i`, ending at `j`, and having all interior states below `k`. -/
theorem language_bddpath_eq_regex {k : ℕ} {lts : LTS (Fin n) Symbol}
[∀ i j, DecidablePred fun x => lts.Tr i x j] {i j : Fin n} :
language (BddPathLTS.mk lts i j k) = (Regex lts i j k).matches' := by
induction k generalizing i j with
| zero =>
ext xs
simp only [mem_language, Accepts, Regex] -- not_lt_zero,
refine ⟨fun ⟨ss, ⟨hex, hbdd⟩⟩ ↦ ?_, fun h ↦ ?_⟩
· split_ifs with heq
· simp only [matches', Language.mem_add, mem_sum_matches'_iff]
sorry
· rw [mem_sum_matches'_iff]
sorry
· split_ifs at h with heq
· simp only [matches', Language.mem_add, mem_sum_matches'_iff] at h
sorry
· rw [mem_sum_matches'_iff] at h
sorry
| succ k ih =>
simp only [Regex]
split_ifs with hk
· rw [← ih, language_bddpath_eq_nfa lts i j hk, language_bddpath_eq_nfa lts i j (by omega)]
rw [language_bddpath_splitLast (k := ⟨k, by omega⟩), language_bddpath_splitFirst,
language_bddpath_kstar]
grind [matches'_add, matches'_mul, matches'_star]

theorem language_nfa_eq_regex_of_singleton_start_accept {nfa : NA.FinAcc (Fin n) Symbol}
[∀ i j, DecidablePred fun x => nfa.toLTS.Tr i x j] {s t : Fin n}
(hstart : nfa.start = {s}) (haccept : nfa.accept = {t}) :
language nfa = (Regex nfa.toLTS s t n).matches' := by
simp [← language_bddpath_eq_regex, language, Accepts, hstart, haccept, LTS.mTr_iff_execution]
rfl

end Regex

/-- An NFA with exactly one accepting state has a matching regular expression. -/
theorem regex_of_nfa_singleton_start_accept [Finite Symbol] {State : Type*} [Finite State]
(nfa : NA.FinAcc State Symbol)
(hstart : ∃ s, nfa.start = {s}) (haccept : ∃ t, nfa.accept = {t}) :
∃ r : RegularExpression Symbol, language nfa = r.matches' := by
have : Fintype State := Fintype.ofFinite State
let e := Fintype.equivFin State
obtain ⟨s, hs⟩ := hstart
obtain ⟨t, ht⟩ := haccept
set nfa' := NA.FinAcc.mk
{Tr := fun s1 a s2 => nfa.Tr (e.symm s1) a (e.symm s2), start := {e s}} {e t} with hnfa'
have language_eq : language nfa = language nfa' := by
ext xs
have nfa_eq : nfa'.MTr (e s) xs (e t) ↔ nfa.MTr s xs t := by sorry
simp only [mem_language, Accepts, hs, Set.mem_singleton_iff, ht, exists_eq_left, hnfa']
rw [nfa_eq]
have : Fintype Symbol := Fintype.ofFinite Symbol
classical
simpa [language_eq] using
⟨_, language_nfa_eq_regex_of_singleton_start_accept (by dsimp) (by dsimp)⟩

end Cslib.Language
Loading
Loading