falafel 0.0.1.1 → 0.0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/cfg.rb +3 -19
- data/lib/chomsky_nf.rb +3 -3
- data/lib/chomsky_nf_simplifier.rb +35 -30
- data/lib/cyk.rb +34 -10
- data/lib/epsilon_free.rb +1 -1
- data/lib/falafel.rb +2 -3
- data/lib/pump.rb +52 -13
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5180f16b8868a35cf65faa396c7b341166604e2aaf7db8d9f7099c819630497f
|
4
|
+
data.tar.gz: 7c773f6a53d8895333d37115cba598dcf60128bff2eaf4dc47e73f7e1ca21298
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b7470e125d1f803eacc1d207704b90782e439fef8c7fe00dcf0acdb6c27324d24ed2da0666b3c456fe7cc7b255902002f20870e8f93d589482d4d1df3370aca
|
7
|
+
data.tar.gz: c8534f9da1cfec2623caa5cf0422f3d03009579374d2183a10341398a93aad87fbf2a0e85aaba0a0a766ee1d8ae62baa02e39bd2a76c7857fa6613629ae64f5a
|
data/lib/cfg.rb
CHANGED
@@ -10,7 +10,7 @@ require_relative 'chaining_free'
|
|
10
10
|
# generates random words
|
11
11
|
class CFG
|
12
12
|
attr_accessor :start_var, :rules
|
13
|
-
attr_reader :rules_ef, :rules_cf, :rnd_words,
|
13
|
+
attr_reader :rules_ef, :rules_cf, :rnd_words,
|
14
14
|
:rules_ef_res, :rules_cf_res, :chomsky_nf_rules,
|
15
15
|
:cyk_matrix, :is_in_l
|
16
16
|
|
@@ -18,7 +18,6 @@ class CFG
|
|
18
18
|
@start_var = start_var
|
19
19
|
@rules = _rules rules
|
20
20
|
@rnd_words = []
|
21
|
-
@reachables = []
|
22
21
|
@vars = vars
|
23
22
|
@alphabet = alphabet
|
24
23
|
end
|
@@ -35,24 +34,9 @@ class CFG
|
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
|
-
def
|
39
|
-
@rules[@start_var].each do |rule|
|
40
|
-
@vars.each { |var| @reachables << var if rule.include? var }
|
41
|
-
end
|
42
|
-
@reachables.uniq!
|
43
|
-
end
|
44
|
-
|
45
|
-
def var_productive?
|
46
|
-
''
|
47
|
-
end
|
48
|
-
|
49
|
-
def var_reduced?
|
50
|
-
''
|
51
|
-
end
|
52
|
-
|
53
|
-
def epsilon_free
|
37
|
+
def epsilon_free(custom_rule)
|
54
38
|
e_free = EpsilonFree.new
|
55
|
-
@rules_ef_res = e_free.run @rules
|
39
|
+
@rules_ef_res = e_free.run custom_rule || @rules
|
56
40
|
@rules_ef = e_free.rebuild_rules @rules, @rules_ef_res
|
57
41
|
end
|
58
42
|
|
data/lib/chomsky_nf.rb
CHANGED
@@ -46,15 +46,15 @@ class ChomskyNF
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def _handle_all_rule(new_rules)
|
49
|
-
new_rules_buffer =
|
49
|
+
new_rules_buffer = []
|
50
50
|
|
51
51
|
new_rules.each do |var, rules|
|
52
52
|
next if _is_character? rules
|
53
53
|
|
54
|
-
new_rules_buffer
|
54
|
+
new_rules_buffer << _new_rules_buffer(rules, var)
|
55
55
|
end
|
56
56
|
|
57
|
-
new_rules.merge! new_rules_buffer
|
57
|
+
new_rules.merge! new_rules_buffer.reduce(&:merge)
|
58
58
|
end
|
59
59
|
|
60
60
|
def _is_character?(value)
|
@@ -3,15 +3,15 @@
|
|
3
3
|
# simplify chomsky normal form
|
4
4
|
class ChomskyNFSimplifier
|
5
5
|
def run(new_rules, alphabet)
|
6
|
-
|
6
|
+
vars_with_one_letter = _vars_with_one_letter new_rules, alphabet
|
7
7
|
|
8
|
-
return new_rules if
|
8
|
+
return new_rules if vars_with_one_letter.empty?
|
9
9
|
|
10
|
-
|
10
|
+
extendible_rules = _find_extendible_rules new_rules, vars_with_one_letter.keys
|
11
11
|
|
12
|
-
return new_rules if
|
12
|
+
return new_rules if extendible_rules.empty?
|
13
13
|
|
14
|
-
missing_rules =
|
14
|
+
missing_rules = _missing_rules extendible_rules, vars_with_one_letter
|
15
15
|
|
16
16
|
_add_missing_rules missing_rules, new_rules
|
17
17
|
|
@@ -20,56 +20,57 @@ class ChomskyNFSimplifier
|
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
-
def
|
23
|
+
def _vars_with_one_letter(new_rules, alphabet)
|
24
24
|
vars = {}
|
25
25
|
|
26
26
|
new_rules.each do |key, values|
|
27
|
+
# skip simple rules like (A, a), (B, b),...
|
27
28
|
next if values.size == 1
|
28
29
|
|
29
|
-
values.
|
30
|
-
|
31
|
-
|
32
|
-
vars[key] ||= []
|
33
|
-
vars[key] << val
|
34
|
-
end
|
30
|
+
letters = values.select { |val| _is_letter val, alphabet }.uniq
|
31
|
+
vars[key] = letters
|
35
32
|
end
|
36
33
|
|
37
34
|
vars
|
38
35
|
end
|
39
36
|
|
40
|
-
def
|
41
|
-
|
37
|
+
def _is_letter(val, alphabet)
|
38
|
+
val.size == 1 && alphabet.include?(val.downcase)
|
39
|
+
end
|
42
40
|
|
43
|
-
|
44
|
-
|
41
|
+
def _find_extendible_rules(new_rules, rules_keys)
|
42
|
+
extendible = {}
|
45
43
|
|
46
|
-
|
47
|
-
vars.each_key do |k|
|
48
|
-
next unless v.include?(k)
|
44
|
+
filterd_rules = new_rules.reject { |_, rules| rules.is_a?(String) }
|
49
45
|
|
50
|
-
|
46
|
+
filterd_rules.each do |letter, rules|
|
47
|
+
rules_keys.each do |rules_key|
|
48
|
+
rules.each do |rule|
|
49
|
+
next unless rule.include?(rules_key)
|
51
50
|
|
52
|
-
|
53
|
-
|
51
|
+
extendible[letter] ||= []
|
52
|
+
extendible[letter] << rule
|
54
53
|
end
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
58
|
-
|
57
|
+
extendible
|
59
58
|
end
|
60
59
|
|
61
|
-
def
|
60
|
+
def _missing_rules(extendible_rules, vars)
|
62
61
|
res = {}
|
63
62
|
|
64
|
-
|
65
|
-
nfs.
|
63
|
+
extendible_rules.each do |var_rule, nfs|
|
64
|
+
nfs.each do |nf|
|
66
65
|
vars.each do |match_char, replacement_chars|
|
67
66
|
replacement_chars.each do |rc|
|
68
|
-
new_rules = _combinations nf, rc, match_char
|
69
67
|
res[var_rule] ||= []
|
70
|
-
res[var_rule] <<
|
68
|
+
res[var_rule] << _combinations(nf, rc, match_char)
|
71
69
|
end
|
72
70
|
end
|
71
|
+
|
72
|
+
res[var_rule] = res[var_rule]&.flatten&.uniq
|
73
|
+
|
73
74
|
_check_special_case nf, res, var_rule, vars
|
74
75
|
end
|
75
76
|
end
|
@@ -93,7 +94,11 @@ class ChomskyNFSimplifier
|
|
93
94
|
end
|
94
95
|
|
95
96
|
def _add_missing_rules(missing_rules, new_rules)
|
96
|
-
missing_rules.each
|
97
|
+
missing_rules.each do |key, val|
|
98
|
+
next if val.nil?
|
99
|
+
|
100
|
+
new_rules[key].concat(val).uniq!
|
101
|
+
end
|
97
102
|
end
|
98
103
|
|
99
104
|
def _check_special_case(nf, res, var_rule, vars)
|
@@ -102,7 +107,7 @@ class ChomskyNFSimplifier
|
|
102
107
|
|
103
108
|
missing_rules = combi.map { |lft| combi.map { |rgt| "#{lft}#{rgt}" } }.flatten
|
104
109
|
|
105
|
-
res[var_rule]
|
110
|
+
res[var_rule].concat(missing_rules)
|
106
111
|
end
|
107
112
|
|
108
113
|
res
|
data/lib/cyk.rb
CHANGED
@@ -13,7 +13,7 @@ class CYK
|
|
13
13
|
def run
|
14
14
|
_cyk_fill_diagonal
|
15
15
|
_cyk_fill_matrix
|
16
|
-
_is_in_l?
|
16
|
+
@is_in_l = _is_in_l?
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
@@ -34,9 +34,9 @@ class CYK
|
|
34
34
|
|
35
35
|
next if i == j
|
36
36
|
|
37
|
-
helper_var
|
38
|
-
|
39
|
-
@matrix[i][j]
|
37
|
+
helper_var = _helper_var i, j
|
38
|
+
matrix_ele_info = _matrix_ele_info i, j
|
39
|
+
@matrix[i][j] = _add_to_matrix helper_var, matrix_ele_info
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -55,20 +55,44 @@ class CYK
|
|
55
55
|
res
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
59
|
-
res =
|
60
|
-
|
58
|
+
def _matrix_ele_info(i, j)
|
59
|
+
res = {}
|
60
|
+
|
61
|
+
res['i'] = i
|
62
|
+
res['j'] = j
|
63
|
+
res['row_c'] = @matrix.count
|
64
|
+
|
65
|
+
res
|
66
|
+
end
|
67
|
+
|
68
|
+
def _add_to_matrix(helper_var, matrix_ele_info)
|
69
|
+
matrix_ele = '0'
|
70
|
+
return if helper_var == matrix_ele
|
71
|
+
|
72
|
+
keys = []
|
61
73
|
|
62
74
|
@rules.each do |key, vals|
|
63
75
|
vals.each do |val|
|
64
|
-
|
76
|
+
keys << key if val == helper_var
|
65
77
|
end
|
66
78
|
end
|
67
79
|
|
68
|
-
|
80
|
+
_matrix_ele keys, matrix_ele_info
|
81
|
+
end
|
82
|
+
|
83
|
+
def _matrix_ele(keys, matrix_ele_info)
|
84
|
+
return 'S' if keys.include?('S') && _is_last_element?(matrix_ele_info)
|
85
|
+
|
86
|
+
return keys.last if keys.size == 1
|
87
|
+
|
88
|
+
keys.reject { |key| key == 'S' }.last || '0'
|
89
|
+
end
|
90
|
+
|
91
|
+
def _is_last_element?(matrix_ele_info)
|
92
|
+
matrix_ele_info['i'].zero? && matrix_ele_info['j'] == matrix_ele_info['row_c'] - 1
|
69
93
|
end
|
70
94
|
|
71
95
|
def _is_in_l?
|
72
|
-
@
|
96
|
+
@matrix.first.last == 'S'
|
73
97
|
end
|
74
98
|
end
|
data/lib/epsilon_free.rb
CHANGED
data/lib/falafel.rb
CHANGED
@@ -104,10 +104,10 @@ class Falafel
|
|
104
104
|
dfa.to_min
|
105
105
|
end
|
106
106
|
|
107
|
-
def pump_lemma
|
107
|
+
def pump_lemma(lang, length)
|
108
108
|
require_relative 'pump'
|
109
109
|
|
110
|
-
Pump.new lang:
|
110
|
+
Pump.new lang: lang, length: length
|
111
111
|
end
|
112
112
|
|
113
113
|
def cfg(alphabet, vars_set, start_var, rules)
|
@@ -118,7 +118,6 @@ class Falafel
|
|
118
118
|
|
119
119
|
private
|
120
120
|
|
121
|
-
# alle bilder eine menge m unter r
|
122
121
|
def _image(m, r)
|
123
122
|
m.flat_map { |x| r.map { |y, z| z if x == y }.compact }.sort.uniq
|
124
123
|
end
|
data/lib/pump.rb
CHANGED
@@ -2,29 +2,68 @@
|
|
2
2
|
|
3
3
|
# Pump Lemma
|
4
4
|
# It takes language as lambda function and a word as a string
|
5
|
-
#
|
6
|
-
#
|
5
|
+
# lang is a regular expression like a^*b^* given as lambda function and set by user
|
6
|
+
# word is the word to check pumping
|
7
|
+
# length is the length of the string you want to pump
|
8
|
+
# return is_regular to tell if lang is regular and decomposition to show why
|
7
9
|
class Pump
|
8
10
|
attr_accessor :lang, :word
|
11
|
+
attr_reader :is_regular, :decomposition
|
9
12
|
|
10
|
-
def initialize(lang:,
|
11
|
-
@lang
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@k = k
|
13
|
+
def initialize(lang:, length:)
|
14
|
+
@lang = lang
|
15
|
+
@length = length
|
16
|
+
@word = ''
|
15
17
|
end
|
16
18
|
|
17
|
-
def run
|
19
|
+
def run(show_pros:)
|
20
|
+
_clear_old_run
|
21
|
+
|
18
22
|
r, s, t = ''
|
19
23
|
|
20
|
-
|
21
|
-
r =
|
22
|
-
|
23
|
-
|
24
|
+
@word.length.times do |leng|
|
25
|
+
r, s, t = _sigma_chars leng
|
26
|
+
|
27
|
+
pump_up_down_res = _pump_up_down_res r, s, t, show_pros
|
28
|
+
not_in_language = pump_up_down_res.include? false
|
24
29
|
|
25
|
-
|
30
|
+
@is_regular = false if not_in_language
|
31
|
+
|
32
|
+
break if not_in_language
|
26
33
|
end
|
27
34
|
|
35
|
+
@decomposition = [r, s, t]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def _clear_old_run
|
41
|
+
@decomposition = []
|
42
|
+
@is_regular = true
|
43
|
+
end
|
44
|
+
|
45
|
+
# ∃r, s, t ∈ Σ^∗
|
46
|
+
def _sigma_chars(leng)
|
47
|
+
r = @word[0...leng] || ''
|
48
|
+
s = @word[leng...leng + @length] || ''
|
49
|
+
t = @word[leng + @length..] || ''
|
50
|
+
|
28
51
|
[r, s, t]
|
29
52
|
end
|
53
|
+
|
54
|
+
# ∀k ≥ 0 : r·s^k·t ∈ L
|
55
|
+
def _pump_up_down_res(r, s, t, show_pros)
|
56
|
+
pump_up_down_res = []
|
57
|
+
|
58
|
+
20.times do |k|
|
59
|
+
puts "r: #{r}, s: #{s}, t: #{t}, k: #{k}, r + s * k + t: #{r + s * k + t}" if show_pros
|
60
|
+
pump_up_down_res << _pump_condtion_satisfied?(r, s, t, k)
|
61
|
+
end
|
62
|
+
|
63
|
+
pump_up_down_res
|
64
|
+
end
|
65
|
+
|
66
|
+
def _pump_condtion_satisfied?(r, s, t, k)
|
67
|
+
(s.length.positive? && @lang.call(r + s * k + t))
|
68
|
+
end
|
30
69
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: falafel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abed Alkedda
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-12 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -28,7 +28,7 @@ files:
|
|
28
28
|
- lib/rx.rb
|
29
29
|
homepage: https://github.com/AbedAlkedda/AFS
|
30
30
|
licenses:
|
31
|
-
-
|
31
|
+
- ''
|
32
32
|
metadata: {}
|
33
33
|
post_install_message:
|
34
34
|
rdoc_options: []
|
@@ -48,5 +48,6 @@ requirements: []
|
|
48
48
|
rubygems_version: 3.3.26
|
49
49
|
signing_key:
|
50
50
|
specification_version: 4
|
51
|
-
summary: Falafel is a gem
|
51
|
+
summary: Falafel is a gem that will hopefully help you better understand automata
|
52
|
+
and formal languages
|
52
53
|
test_files: []
|