falafel 0.0.0.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 821f2ac8523791cce7c4632e69d67a4bee8f208b7e7cf8f1e1e88dbd82e0bc1f
4
- data.tar.gz: b92c2f37f9b2585a5aff6f87307378dbdeb6c452db5ff6dd738cd836f57b4a2a
3
+ metadata.gz: 5180f16b8868a35cf65faa396c7b341166604e2aaf7db8d9f7099c819630497f
4
+ data.tar.gz: 7c773f6a53d8895333d37115cba598dcf60128bff2eaf4dc47e73f7e1ca21298
5
5
  SHA512:
6
- metadata.gz: a40d294e74954d85c19abe0bcd80c222390b7f3703c6a87d471d8fb30ed89aadd0ab5297f7e02c4521c282e6bd55e31e70cee68482a0d36c2b0572c477e05263
7
- data.tar.gz: 1889ce0c9deb9585907fc663e20d73895051fd054a9289b591f7990a3c3494579ad5fbfce79f9e92153bb4882ca621f25e063e8546346015f15660614902a888
6
+ metadata.gz: 0b7470e125d1f803eacc1d207704b90782e439fef8c7fe00dcf0acdb6c27324d24ed2da0666b3c456fe7cc7b255902002f20870e8f93d589482d4d1df3370aca
7
+ data.tar.gz: c8534f9da1cfec2623caa5cf0422f3d03009579374d2183a10341398a93aad87fbf2a0e85aaba0a0a766ee1d8ae62baa02e39bd2a76c7857fa6613629ae64f5a
data/lib/cfg.rb CHANGED
@@ -1,44 +1,80 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'cyk'
4
+ require_relative 'chomsky_nf'
5
+ require_relative 'epsilon_free'
6
+ require_relative 'chaining_free'
7
+
3
8
  # Samll CFG impletation
4
9
  # Generate words and check if words are the @lang
10
+ # generates random words
5
11
  class CFG
6
- attr_accessor :alphabet, :vars, :start_var, :rules
7
- attr_reader :rnd_words
8
- attr_writer :lang
12
+ attr_accessor :start_var, :rules
13
+ attr_reader :rules_ef, :rules_cf, :rnd_words,
14
+ :rules_ef_res, :rules_cf_res, :chomsky_nf_rules,
15
+ :cyk_matrix, :is_in_l
9
16
 
10
17
  def initialize(alphabet, vars, start_var, rules)
11
- @alphabet = alphabet
12
- @vars = vars
13
- @start_var = start_var
14
- @rules = _rules rules
18
+ @start_var = start_var
19
+ @rules = _rules rules
20
+ @rnd_words = []
21
+ @vars = vars
22
+ @alphabet = alphabet
23
+ end
24
+
25
+ def generate_words(count)
15
26
  @rnd_words = []
27
+
28
+ loop do
29
+ word = _expand @start_var
30
+
31
+ @rnd_words << word unless @rnd_words.include? word
32
+
33
+ break if @rnd_words.size == count
34
+ end
16
35
  end
17
36
 
18
- def generate_word
19
- word = _expand @start_var
37
+ def epsilon_free(custom_rule)
38
+ e_free = EpsilonFree.new
39
+ @rules_ef_res = e_free.run custom_rule || @rules
40
+ @rules_ef = e_free.rebuild_rules @rules, @rules_ef_res
41
+ end
20
42
 
21
- return unless @lang.call word
43
+ def chaining_free
44
+ c_free = ChainingFree.new
45
+ @rules_cf_res = c_free.run @rules, @vars
46
+ @rules_cf = c_free.rebuild_rules @rules_cf_res
47
+ end
22
48
 
23
- @rnd_words << word unless @rnd_words.include? word
49
+ def chomsky_nf(custom_rule)
50
+ chomsky = ChomskyNF.new
51
+ rules = chomsky.run custom_rule || @rules, @alphabet
52
+
53
+ @chomsky_nf_rules = chomsky.simplify rules
24
54
  end
25
55
 
26
- def dyck?(word)
27
- ->(w) { (w.count('a') - w.count('b')).zero? }.call word
56
+ def cyk_run(word)
57
+ cyk = CYK.new word, @chomsky_nf_rules
58
+
59
+ cyk.run
60
+
61
+ @cyk_matrix = cyk.matrix
62
+ @is_in_l = cyk.is_in_l
28
63
  end
29
64
 
30
65
  private
31
66
 
67
+ def _rules(rules)
68
+ rules.each_value { |k| k.each_with_index { |item, index| k[index] = item[0].is_a?(String) ? item[0].split('') : item } }
69
+ end
70
+
32
71
  def _expand(symbol)
33
72
  production = @rules[symbol]
34
73
 
35
74
  return symbol if production.nil?
36
75
 
37
- rhs = production.sample
38
- rhs.map { |s| _expand(s) }.join
39
- end
76
+ rhs = production.sample # pick up a random element from array
40
77
 
41
- def _rules(rules)
42
- rules.each_value { |k| k.each_with_index { |item, index| k[index] = item[0].is_a?(String) ? item[0].split('') : item } }
78
+ rhs.map { |s| _expand(s) }.join
43
79
  end
44
80
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # remove chaining rules from CFG
4
+ class ChainingFree
5
+ def run(rules, vars)
6
+ @rules = rules
7
+ @vars = vars
8
+ c_r = _chaining_relation
9
+
10
+ # build transivity relation from _chaining_relation(K) as A
11
+ # remove _chaining_relation from rules
12
+ _rebuild_rules + c_r.map { |r| _transitivity_relation r }.reduce(:concat) - c_r
13
+ end
14
+
15
+ def rebuild_rules(current_rules)
16
+ res = {}
17
+
18
+ @vars.each { |v| res[v] = [] }
19
+
20
+ current_rules.each do |l, r|
21
+ res[l] << r.chars unless res[l].include? r.chars
22
+ end
23
+
24
+ res
25
+ end
26
+
27
+ private
28
+
29
+ def _chaining_relation
30
+ rules = _rebuild_rules
31
+
32
+ rules & @vars.product(@vars)
33
+ end
34
+
35
+ def _rebuild_rules
36
+ result = []
37
+ @rules.each do |var, rule|
38
+ rule.map do |r|
39
+ result << [var, r.join]
40
+ end
41
+ end
42
+
43
+ result
44
+ end
45
+
46
+ def _transitivity_relation(pair)
47
+ transitive_relation = [pair]
48
+
49
+ new_pairs = []
50
+
51
+ _rebuild_rules.each do |a, b|
52
+ next unless pair[1] == a && !transitive_relation.include?([pair[0], b])
53
+
54
+ new_pairs << [pair[0], b]
55
+ end
56
+
57
+ transitive_relation.concat new_pairs
58
+ end
59
+
60
+ def _new_pairs(rules)
61
+ rules.each do |a, b|
62
+ next unless pair[1] == a && !transitive_relation.include?([pair[0], b])
63
+
64
+ new_pairs << [pair[0], b]
65
+ end
66
+ end
67
+ end
data/lib/chomsky_nf.rb ADDED
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'chomsky_nf_simplifier'
4
+
5
+ # convert to Chomsky Normal Form
6
+ class ChomskyNF
7
+ def run(rules, alphabet)
8
+ new_rules = {}
9
+ @rules = rules
10
+ @alphabet = alphabet
11
+
12
+ # add cases like (A, a), (B, b)
13
+ _add_single_vars new_rules
14
+
15
+ # change rules like (X, aX) to (X, AX)
16
+ _handle_simple_rule new_rules
17
+
18
+ _handle_all_rule new_rules
19
+ end
20
+
21
+ def simplify(rules)
22
+ simplifier = ChomskyNFSimplifier.new
23
+
24
+ simplifier.run rules, @alphabet
25
+ end
26
+
27
+ private
28
+
29
+ def _add_single_vars(new_rules)
30
+ @rules.values.reduce(:concat).each do |rule|
31
+ rule.select { |var| var == var.downcase }.each do |r|
32
+ new_rules[r.upcase] = r
33
+ end
34
+ end
35
+ end
36
+
37
+ def _handle_simple_rule(new_rules)
38
+ @rules.each do |var, rule|
39
+ new_rules[var] = []
40
+ rule.each do |r|
41
+ next if r.empty?
42
+
43
+ new_rules[var] << r.map(&:upcase)
44
+ end
45
+ end
46
+ end
47
+
48
+ def _handle_all_rule(new_rules)
49
+ new_rules_buffer = []
50
+
51
+ new_rules.each do |var, rules|
52
+ next if _is_character? rules
53
+
54
+ new_rules_buffer << _new_rules_buffer(rules, var)
55
+ end
56
+
57
+ new_rules.merge! new_rules_buffer.reduce(&:merge)
58
+ end
59
+
60
+ def _is_character?(value)
61
+ value.is_a?(String) && value.length == 1
62
+ end
63
+
64
+ def _new_rules_buffer(rules, var)
65
+ helper_vars = _helper_vars rules
66
+ helper_vars.merge! @alphabet.each_with_object({}) { |ele, meme| meme[ele.capitalize] = ele.capitalize }
67
+
68
+ _new_rules rules, var, helper_vars
69
+ end
70
+
71
+ def _check_simplify(rule, holder, current_var)
72
+ return false unless rule.size == 1
73
+
74
+ holder[current_var] << rule.last unless holder[current_var].include? rule
75
+
76
+ true
77
+ end
78
+
79
+ def _new_rules(rules, var, helper_vars)
80
+ holder = {}
81
+
82
+ rules.each do |rule|
83
+ rest = ''
84
+ rule.each_with_index do |head, index|
85
+ current_var = index.zero? ? var : rest
86
+ holder[current_var] ||= []
87
+
88
+ is_simple = _check_simplify rule, holder, current_var
89
+
90
+ break if is_simple
91
+
92
+ rest = _chomsky_nf_vars index, rule, helper_vars
93
+
94
+ nf_rest = "#{head}#{rest}"
95
+
96
+ if index + 2 == rule.size
97
+ if rest.nil?
98
+ rest = rule.last
99
+ nf_rest += rest
100
+ end
101
+
102
+ holder[current_var] << nf_rest unless holder[current_var].include? nf_rest
103
+
104
+ break
105
+ else
106
+ holder[current_var] << nf_rest unless holder[current_var].include? nf_rest
107
+ end
108
+ end
109
+ end
110
+
111
+ holder
112
+ end
113
+
114
+ def _chomsky_nf_vars(index, rule, helper_vars)
115
+ rule_size = rule.size
116
+ rest = rule[(index + 1)..rule_size].join
117
+
118
+ helper_vars.key(rest)
119
+ end
120
+
121
+ def _helper_vars(rules)
122
+ helper_vars = {}
123
+
124
+ rules.each do |rule|
125
+ rule.each_with_index do |_, index|
126
+ letter, rest = _chomsky_nf_helper_vars index, rule, helper_vars.keys
127
+
128
+ break if index + 2 == rule.size
129
+
130
+ next if helper_vars.values.include? rest
131
+
132
+ helper_vars[letter] = rest unless helper_vars.key?(letter) && rest.size > 1
133
+ end
134
+ end
135
+
136
+ helper_vars
137
+ end
138
+
139
+ def _chomsky_nf_helper_vars(index, rule, helper_vars)
140
+ rule_size = rule.size
141
+ letter = helper_vars.empty? ? _find_letter(index) : helper_vars.sort!.last.succ
142
+ rest = rule[(index + 1)..rule_size].join
143
+
144
+ [letter, rest]
145
+ end
146
+
147
+ def _find_letter(n)
148
+ result = 'H'
149
+ n.times { result = result.succ }
150
+
151
+ result
152
+ end
153
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ # simplify chomsky normal form
4
+ class ChomskyNFSimplifier
5
+ def run(new_rules, alphabet)
6
+ vars_with_one_letter = _vars_with_one_letter new_rules, alphabet
7
+
8
+ return new_rules if vars_with_one_letter.empty?
9
+
10
+ extendible_rules = _find_extendible_rules new_rules, vars_with_one_letter.keys
11
+
12
+ return new_rules if extendible_rules.empty?
13
+
14
+ missing_rules = _missing_rules extendible_rules, vars_with_one_letter
15
+
16
+ _add_missing_rules missing_rules, new_rules
17
+
18
+ new_rules
19
+ end
20
+
21
+ private
22
+
23
+ def _vars_with_one_letter(new_rules, alphabet)
24
+ vars = {}
25
+
26
+ new_rules.each do |key, values|
27
+ # skip simple rules like (A, a), (B, b),...
28
+ next if values.size == 1
29
+
30
+ letters = values.select { |val| _is_letter val, alphabet }.uniq
31
+ vars[key] = letters
32
+ end
33
+
34
+ vars
35
+ end
36
+
37
+ def _is_letter(val, alphabet)
38
+ val.size == 1 && alphabet.include?(val.downcase)
39
+ end
40
+
41
+ def _find_extendible_rules(new_rules, rules_keys)
42
+ extendible = {}
43
+
44
+ filterd_rules = new_rules.reject { |_, rules| rules.is_a?(String) }
45
+
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)
50
+
51
+ extendible[letter] ||= []
52
+ extendible[letter] << rule
53
+ end
54
+ end
55
+ end
56
+
57
+ extendible
58
+ end
59
+
60
+ def _missing_rules(extendible_rules, vars)
61
+ res = {}
62
+
63
+ extendible_rules.each do |var_rule, nfs|
64
+ nfs.each do |nf|
65
+ vars.each do |match_char, replacement_chars|
66
+ replacement_chars.each do |rc|
67
+ res[var_rule] ||= []
68
+ res[var_rule] << _combinations(nf, rc, match_char)
69
+ end
70
+ end
71
+
72
+ res[var_rule] = res[var_rule]&.flatten&.uniq
73
+
74
+ _check_special_case nf, res, var_rule, vars
75
+ end
76
+ end
77
+
78
+ res
79
+ end
80
+
81
+ def _combinations(original_string, replacement_char, match_char)
82
+ combinations = []
83
+
84
+ 0.upto(1) do |i|
85
+ 0.upto(1) do |j|
86
+ new_rule = original_string.dup
87
+ new_rule[0] = replacement_char if i == 1 && original_string[0] == match_char
88
+ new_rule[1] = replacement_char if j == 1 && original_string[1] == match_char
89
+ combinations << new_rule
90
+ end
91
+ end
92
+
93
+ combinations.uniq
94
+ end
95
+
96
+ def _add_missing_rules(missing_rules, new_rules)
97
+ missing_rules.each do |key, val|
98
+ next if val.nil?
99
+
100
+ new_rules[key].concat(val).uniq!
101
+ end
102
+ end
103
+
104
+ def _check_special_case(nf, res, var_rule, vars)
105
+ vars.each do |match_char, combi|
106
+ next unless nf == match_char * 2
107
+
108
+ missing_rules = combi.map { |lft| combi.map { |rgt| "#{lft}#{rgt}" } }.flatten
109
+
110
+ res[var_rule].concat(missing_rules)
111
+ end
112
+
113
+ res
114
+ end
115
+ end
data/lib/cyk.rb ADDED
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Implemention of cyk algo.
4
+ class CYK
5
+ attr_reader :matrix, :is_in_l
6
+
7
+ def initialize(word, custom_rule)
8
+ @word = word
9
+ @rules = custom_rule.select { |_, val| val.is_a?(Array) }
10
+ @helper_vars = @rules.values.flatten
11
+ end
12
+
13
+ def run
14
+ _cyk_fill_diagonal
15
+ _cyk_fill_matrix
16
+ @is_in_l = _is_in_l?
17
+ end
18
+
19
+ private
20
+
21
+ def _cyk_fill_diagonal
22
+ wrd_lng = @word.length
23
+ @matrix = wrd_lng.times.map { wrd_lng.times.map { [] } }
24
+
25
+ (0..wrd_lng - 1).each do |index|
26
+ @matrix[index][index] = @word[index].upcase
27
+ end
28
+ end
29
+
30
+ def _cyk_fill_matrix
31
+ @matrix.size.times do |limiter|
32
+ (0...@matrix.length - limiter).each do |i|
33
+ j = i + limiter
34
+
35
+ next if i == j
36
+
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
+ end
41
+ end
42
+ end
43
+
44
+ def _helper_var(row, col)
45
+ res = '0'
46
+
47
+ (row..col - 1).step(1) do |h|
48
+ p_ = @matrix[row][h]
49
+ q_ = @matrix[h + 1][col]
50
+ res = "#{p_}#{q_}"
51
+
52
+ break if @helper_vars.include? res
53
+ end
54
+
55
+ res
56
+ end
57
+
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 = []
73
+
74
+ @rules.each do |key, vals|
75
+ vals.each do |val|
76
+ keys << key if val == helper_var
77
+ end
78
+ end
79
+
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
93
+ end
94
+
95
+ def _is_in_l?
96
+ @matrix.first.last == 'S'
97
+ end
98
+ end
data/lib/dfa.rb CHANGED
@@ -53,26 +53,29 @@ class DFA < Falafel
53
53
  (finals & states.keys).each_with_object({}) { |elt, memo| memo[elt] = states[elt] }
54
54
  end
55
55
 
56
- def _r(finals, states, ri)
57
- r = []
58
- @states.each_with_index do |p, _|
59
- @states.each_with_index do |q, _|
60
- if finals.include?(p) == finals.include?(q)
61
- @delta_star.each_key do |c|
62
- ri_set = _ri_set states, c, p, q
63
- r << (ri_set & ri).flatten if (ri_set & ri).any?
64
- end
65
- end
56
+ def _r(finals, states, steps)
57
+ r = []
58
+ last_r = steps["R#{steps.size - 1}"]
59
+
60
+ last_r.each do |pair|
61
+ @delta_star.each_key do |c|
62
+ pair_frst = pair[0]
63
+ pair_lst = pair[1]
64
+
65
+ ri_set = _ri_set states, c, states.key(pair_frst), states.key(pair_lst)
66
+
67
+ break unless last_r.include? ri_set
68
+
69
+ r << [pair_frst, pair_lst] unless r.include? [pair_frst, pair_lst]
66
70
  end
67
71
  end
68
- r.uniq!
69
72
 
70
73
  r
71
74
  end
72
75
 
73
76
  def _steps_builder(finals, states, steps)
74
77
  0.upto(1_000) do |l|
75
- r = _r finals, states, steps["R#{l}"]
78
+ r = _r finals, states, steps
76
79
  steps["R#{l + 1}"] = r
77
80
 
78
81
  return steps if steps["R#{l}"] == r
@@ -95,6 +98,6 @@ class DFA < Falafel
95
98
  lft = states[(_image [p], @delta_star[c])[0]]
96
99
  rgt = states[(_image [q], @delta_star[c])[0]]
97
100
 
98
- [[lft, rgt]]
101
+ [lft, rgt]
99
102
  end
100
103
  end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ # used to clear a CFG from the epsilon rules
4
+ class EpsilonFree
5
+ def run(rules)
6
+ epsilon = _epsilon rules
7
+ rules_ef = {}
8
+
9
+ return if epsilon.empty?
10
+
11
+ epsilon.each do |e_class|
12
+ cleared_rules = _cleared_rules rules[e_class]
13
+ epsilon_free_word = cleared_rules.map(&:join)
14
+ grammer = _grammer_epsilon_free epsilon_free_word, e_class
15
+ rules_ef[e_class] = grammer
16
+ end
17
+
18
+ rules_ef
19
+ end
20
+
21
+ def rebuild_rules(current_rules, rules_epsilon_free)
22
+ missing_vars = current_rules.keys - rules_epsilon_free.keys
23
+
24
+ res = {}
25
+ rules_epsilon_free.each do |key, rules|
26
+ res[key] = []
27
+ rules.each do |rule|
28
+ res[key] << rule.chars
29
+ end
30
+ end
31
+
32
+ missing_vars.each { |v| res[v] = current_rules[v] }
33
+
34
+ res
35
+ end
36
+
37
+ private
38
+
39
+ def _epsilon(rules)
40
+ res = rules.map do |var, rule|
41
+ next unless rule.include? []
42
+
43
+ var
44
+ end
45
+ res.reject(&:nil?)
46
+ end
47
+
48
+ def _cleared_rules(rules)
49
+ rules.reject(&:empty?)
50
+ end
51
+
52
+ def _grammer_epsilon_free(epsilon_free_word, e_class)
53
+ rules_epsilon_location = _new_rules_epsilon_location epsilon_free_word, e_class
54
+
55
+ res = []
56
+ rules_epsilon_location.map do |rule, possibilities|
57
+ possibilities.each do |possibility|
58
+ res << _remove_e_class(possibility, rule.split(''))
59
+ end
60
+
61
+ res << epsilon_free_word.delete(e_class) if possibilities.size == 1
62
+ end
63
+
64
+ res.uniq.reject(&:nil?)
65
+ end
66
+
67
+ def _new_rules_epsilon_location(s_rule, e_class)
68
+ res = {}
69
+ s_rule.each do |rule|
70
+ s_index = []
71
+ rule.each_char.with_index { |char, index| s_index << index if char == e_class }
72
+ res[rule] = _power_set(s_index)
73
+ end
74
+
75
+ res
76
+ end
77
+
78
+ def _power_set(set)
79
+ if set.empty?
80
+ [[]]
81
+ else
82
+ element = set[0]
83
+ subsets = _power_set set[1..]
84
+ subsets + subsets.map { |subset| [element] + subset }
85
+ end
86
+ end
87
+
88
+ def _remove_e_class(possibility, cleared_rules)
89
+ return cleared_rules.join '' if possibility.empty?
90
+
91
+ new_rule = cleared_rules.join ''
92
+ possibility.reverse.each { |index| new_rule.slice! index }
93
+
94
+ new_rule
95
+ end
96
+ end
data/lib/falafel.rb CHANGED
@@ -2,9 +2,13 @@
2
2
 
3
3
  require_relative 'rx'
4
4
 
5
- # Convert NFA to DFA
5
+ # NFA to DFA
6
6
  # DFA to REG
7
7
  # Minimize DFA
8
+ # Remove epsilons from CFG
9
+ # Remove chaining from CFG
10
+ # Find a chomsky normal form fro CFG
11
+ # Apply CYK alogrithm on CFG and solve word problem
8
12
  class Falafel
9
13
  def self.new
10
14
  instance = allocate
@@ -86,7 +90,7 @@ class Falafel
86
90
  end
87
91
 
88
92
  def nfa_to_reg
89
- RX.new { |rx| rx.build @states, @delta_star }
93
+ RX.new { |rx| rx.build @states, @delta_star, @start, @finals }
90
94
  end
91
95
 
92
96
  def dfa_to_min(automat)
@@ -100,10 +104,10 @@ class Falafel
100
104
  dfa.to_min
101
105
  end
102
106
 
103
- def pump_lemma
107
+ def pump_lemma(lang, length)
104
108
  require_relative 'pump'
105
109
 
106
- Pump.new lang: nil, word: nil, n: 2, k: 20
110
+ Pump.new lang: lang, length: length
107
111
  end
108
112
 
109
113
  def cfg(alphabet, vars_set, start_var, rules)
@@ -114,7 +118,6 @@ class Falafel
114
118
 
115
119
  private
116
120
 
117
- # alle bilder eine menge m unter r
118
121
  def _image(m, r)
119
122
  m.flat_map { |x| r.map { |y, z| z if x == y }.compact }.sort.uniq
120
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
- # n is the length of the string you want to pump
6
- # k is the potens of the middle word
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:, word:, n:, k:)
11
- @lang = lang
12
- @word = word
13
- @n = n
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
- (0...@word.length - @n + 1).each do |i|
21
- r = @word[0...i]
22
- s = @word[i...i + @n]
23
- t = @word[i + @n..]
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
- break if s.length.positive? && (r + s * @k + t) != @word && @lang.call(r + s * @k + t)
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
data/lib/rx.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  # Regex string builder
4
4
  class RX
5
+ attr_reader :final_reg
6
+
5
7
  def self.new
6
8
  instance = allocate
7
9
  yield instance
@@ -9,31 +11,52 @@ class RX
9
11
  instance
10
12
  end
11
13
 
12
- def build(states, delta_star)
14
+ def build(states, delta_star, start, finals)
13
15
  @states = states
14
16
  @delta_star = delta_star
17
+ @start = start
18
+ @finals = finals
15
19
  @empty = "\u2205"
16
20
  _to_reg
17
21
  end
18
22
 
23
+ def print_matrix
24
+ @steps.each do |key, value|
25
+ puts key
26
+ value.transpose.each do |column|
27
+ column.each do |element|
28
+ printf("\t|%-#{value.map { |c| c.max_by(&:length).length }.max}s", element)
29
+ end
30
+ puts '|'
31
+ end
32
+ end
33
+ _final_reg
34
+ end
35
+
36
+ def _final_reg
37
+ start = @start[0] - 1
38
+ final = @finals[0] - 1
39
+ lang = @steps["l#{@steps.size - 1}"]
40
+ @final_reg = lang[start][final]
41
+ end
42
+
19
43
  private
20
44
 
21
45
  def _to_reg
22
46
  l0 = _l0
23
- steps = {}
24
- steps['l0'] = l0
47
+ @steps = {}
48
+ @steps['l0'] = l0
25
49
 
26
50
  @states.size.times do |h|
27
51
  l = []
28
52
  @states.each_with_index do |_, p|
29
53
  l[p] = []
30
54
  @states.each_with_index do |_, q|
31
- l[p][q] = _l steps["l#{h}"], p, q, steps.size - 1
55
+ l[p][q] = _l @steps["l#{h}"], p, q, @steps.size - 1
32
56
  end
33
57
  end
34
- steps["l#{steps.size}"] = l
58
+ @steps["l#{@steps.size}"] = l
35
59
  end
36
- _print_matrix steps
37
60
  end
38
61
 
39
62
  def _l0
@@ -138,17 +161,4 @@ class RX
138
161
 
139
162
  rgt
140
163
  end
141
-
142
- def _print_matrix(steps)
143
- steps.each do |key, value|
144
- puts key
145
- max_width = value.map { |column| column.max_by(&:length).length }.max
146
- value.transpose.each do |column|
147
- column.each do |element|
148
- printf("\t|%-#{max_width}s", element)
149
- end
150
- puts '|'
151
- end
152
- end
153
- end
154
164
  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.0.2
4
+ version: 0.0.2.1
5
5
  platform: ruby
6
6
  authors:
7
- - Abed A
7
+ - Abed Alkedda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-21 00:00:00.000000000 Z
11
+ date: 2023-08-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -17,12 +17,18 @@ extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
19
  - lib/cfg.rb
20
+ - lib/chaining_free.rb
21
+ - lib/chomsky_nf.rb
22
+ - lib/chomsky_nf_simplifier.rb
23
+ - lib/cyk.rb
20
24
  - lib/dfa.rb
25
+ - lib/epsilon_free.rb
21
26
  - lib/falafel.rb
22
27
  - lib/pump.rb
23
28
  - lib/rx.rb
24
- homepage:
25
- licenses: []
29
+ homepage: https://github.com/AbedAlkedda/AFS
30
+ licenses:
31
+ - ''
26
32
  metadata: {}
27
33
  post_install_message:
28
34
  rdoc_options: []
@@ -42,5 +48,6 @@ requirements: []
42
48
  rubygems_version: 3.3.26
43
49
  signing_key:
44
50
  specification_version: 4
45
- summary: Falafel is a gem to help you understand autotool better, hopefully
51
+ summary: Falafel is a gem that will hopefully help you better understand automata
52
+ and formal languages
46
53
  test_files: []