falafel 0.0.1.1 → 0.0.2.2

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: 796b681163a3d9d57b47cadcb95f1d5e2e9af1e97a0b841b2e31ddc9c725769b
4
- data.tar.gz: 3811e2bc49acfee5aa9a4e4f27569df022fb68c2ddafbafa10b43a7c92afe4d8
3
+ metadata.gz: 754e7aa62a91e0f16ed875b859d8e208379df55509569dbd5d5a38e93b874fbe
4
+ data.tar.gz: bcbb64b7d4627514c2e3560b80e15ccc3f0dbf721448b96247188f778d8f897c
5
5
  SHA512:
6
- metadata.gz: 96bf15557a50f7a81c8a71d200a0876f7fc445d64d017d3c2011fa088457d61e05327e8ea39c7147d349049a8e8b3811a815c92e04f414109c4505782a8c6613
7
- data.tar.gz: 45573ad63969221aed699f9412d45409c4f72a26c1ec3e83e3bcb4c5fba03c6fb9bdae80d61b3c0a046e4c25a66a00e7c64c00a1841142de675e7eaa45158876
6
+ metadata.gz: 8fda2266343ea3c9c919898f8ebd0381329189cf6eb08f3e281ba20e30e950d6d1bac77356ff91263c7377e77c808c1b0cb224b705dda1f3970b67b294242d9f
7
+ data.tar.gz: b9e9c7b8552c38d9a3f09143e68ac4cb78b5527899052a79ec975e1b6a08b45cef97158c3bc785f9f05bb1267943c38b7fc37a8575ea86ed409bd72a4f5317d8
data/lib/cfg.rb CHANGED
@@ -1,16 +1,16 @@
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'
3
+ require_relative 'cfg_helper/cyk'
4
+ require_relative 'cfg_helper/chomsky_nf'
5
+ require_relative 'cfg_helper/epsilon_free'
6
+ require_relative 'cfg_helper/chaining_free'
7
7
 
8
8
  # Samll CFG impletation
9
9
  # Generate words and check if words are the @lang
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, :reachables,
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,42 +34,27 @@ class CFG
35
34
  end
36
35
  end
37
36
 
38
- def var_reachable?
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
54
- e_free = EpsilonFree.new
55
- @rules_ef_res = e_free.run @rules
37
+ def epsilon_free(custom_rule)
38
+ e_free = CFGHelper::EpsilonFree.new
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
 
59
43
  def chaining_free
60
- c_free = ChainingFree.new
44
+ c_free = CFGHelper::ChainingFree.new
61
45
  @rules_cf_res = c_free.run @rules, @vars
62
46
  @rules_cf = c_free.rebuild_rules @rules_cf_res
63
47
  end
64
48
 
65
49
  def chomsky_nf(custom_rule)
66
- chomsky = ChomskyNF.new
50
+ chomsky = CFGHelper::ChomskyNF.new
67
51
  rules = chomsky.run custom_rule || @rules, @alphabet
68
52
 
69
53
  @chomsky_nf_rules = chomsky.simplify rules
70
54
  end
71
55
 
72
56
  def cyk_run(word)
73
- cyk = CYK.new word, @chomsky_nf_rules
57
+ cyk = CFGHelper::CYK.new word, @chomsky_nf_rules
74
58
 
75
59
  cyk.run
76
60
 
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CFGHelper
4
+ # remove chaining rules from CFG
5
+ class ChainingFree
6
+ def run(rules, vars)
7
+ @rules = rules
8
+ @vars = vars
9
+ c_r = _chaining_relation
10
+
11
+ # build transivity relation from _chaining_relation(K) as A
12
+ # remove _chaining_relation from rules
13
+ _rebuild_rules + c_r.map { |r| _transitivity_relation r }.reduce(:concat) - c_r
14
+ end
15
+
16
+ def rebuild_rules(current_rules)
17
+ res = {}
18
+
19
+ @vars.each { |v| res[v] = [] }
20
+
21
+ current_rules.each do |l, r|
22
+ res[l] << r.chars unless res[l].include? r.chars
23
+ end
24
+
25
+ res
26
+ end
27
+
28
+ private
29
+
30
+ def _chaining_relation
31
+ rules = _rebuild_rules
32
+
33
+ rules & @vars.product(@vars)
34
+ end
35
+
36
+ def _rebuild_rules
37
+ result = []
38
+ @rules.each do |var, rule|
39
+ rule.map do |r|
40
+ result << [var, r.join]
41
+ end
42
+ end
43
+
44
+ result
45
+ end
46
+
47
+ def _transitivity_relation(pair)
48
+ transitive_relation = [pair]
49
+
50
+ new_pairs = []
51
+
52
+ _rebuild_rules.each do |a, b|
53
+ next unless pair[1] == a && !transitive_relation.include?([pair[0], b])
54
+
55
+ new_pairs << [pair[0], b]
56
+ end
57
+
58
+ transitive_relation.concat new_pairs
59
+ end
60
+
61
+ def _new_pairs(rules)
62
+ rules.each do |a, b|
63
+ next unless pair[1] == a && !transitive_relation.include?([pair[0], b])
64
+
65
+ new_pairs << [pair[0], b]
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'chomsky_nf_simplifier'
4
+
5
+ module CFGHelper
6
+ # convert to Chomsky Normal Form
7
+ class ChomskyNF
8
+ def run(rules, alphabet)
9
+ new_rules = {}
10
+ @rules = rules
11
+ @alphabet = alphabet
12
+
13
+ # add cases like (A, a), (B, b)
14
+ _add_single_vars new_rules
15
+
16
+ # change rules like (X, aX) to (X, AX)
17
+ _handle_simple_rule new_rules
18
+
19
+ _handle_all_rule new_rules
20
+ end
21
+
22
+ def simplify(rules)
23
+ simplifier = CFGHelper::ChomskyNFSimplifier.new
24
+
25
+ simplifier.run rules, @alphabet
26
+ end
27
+
28
+ private
29
+
30
+ def _add_single_vars(new_rules)
31
+ @rules.values.reduce(:concat).each do |rule|
32
+ rule.select { |var| var == var.downcase }.each do |r|
33
+ new_rules[r.upcase] = r
34
+ end
35
+ end
36
+ end
37
+
38
+ def _handle_simple_rule(new_rules)
39
+ @rules.each do |var, rule|
40
+ new_rules[var] = []
41
+ rule.each do |r|
42
+ next if r.empty?
43
+
44
+ new_rules[var] << r.map(&:upcase)
45
+ end
46
+ end
47
+ end
48
+
49
+ def _handle_all_rule(new_rules)
50
+ new_rules_buffer = []
51
+
52
+ new_rules.each do |var, rules|
53
+ next if _is_character? rules
54
+
55
+ new_rules_buffer << _new_rules_buffer(rules, var)
56
+ end
57
+
58
+ new_rules.merge! new_rules_buffer.reduce(&:merge)
59
+ end
60
+
61
+ def _is_character?(value)
62
+ value.is_a?(String) && value.length == 1
63
+ end
64
+
65
+ def _new_rules_buffer(rules, var)
66
+ helper_vars = _helper_vars rules
67
+ helper_vars.merge! @alphabet.each_with_object({}) { |ele, meme| meme[ele.capitalize] = ele.capitalize }
68
+
69
+ _new_rules rules, var, helper_vars
70
+ end
71
+
72
+ def _check_simplify(rule, holder, current_var)
73
+ return false unless rule.size == 1
74
+
75
+ holder[current_var] << rule.last unless holder[current_var].include? rule
76
+
77
+ true
78
+ end
79
+
80
+ def _new_rules(rules, var, helper_vars)
81
+ holder = {}
82
+
83
+ rules.each do |rule|
84
+ rest = ''
85
+ rule.each_with_index do |head, index|
86
+ current_var = index.zero? ? var : rest
87
+ holder[current_var] ||= []
88
+
89
+ is_simple = _check_simplify rule, holder, current_var
90
+
91
+ break if is_simple
92
+
93
+ rest = _chomsky_nf_vars index, rule, helper_vars
94
+
95
+ nf_rest = "#{head}#{rest}"
96
+
97
+ if index + 2 == rule.size
98
+ if rest.nil?
99
+ rest = rule.last
100
+ nf_rest += rest
101
+ end
102
+
103
+ holder[current_var] << nf_rest unless holder[current_var].include? nf_rest
104
+
105
+ break
106
+ else
107
+ holder[current_var] << nf_rest unless holder[current_var].include? nf_rest
108
+ end
109
+ end
110
+ end
111
+
112
+ holder
113
+ end
114
+
115
+ def _chomsky_nf_vars(index, rule, helper_vars)
116
+ rule_size = rule.size
117
+ rest = rule[(index + 1)..rule_size].join
118
+
119
+ helper_vars.key(rest)
120
+ end
121
+
122
+ def _helper_vars(rules)
123
+ helper_vars = {}
124
+
125
+ rules.each do |rule|
126
+ rule.each_with_index do |_, index|
127
+ letter, rest = _chomsky_nf_helper_vars index, rule, helper_vars.keys
128
+
129
+ break if index + 2 == rule.size
130
+
131
+ next if helper_vars.values.include? rest
132
+
133
+ helper_vars[letter] = rest unless helper_vars.key?(letter) && rest.size > 1
134
+ end
135
+ end
136
+
137
+ helper_vars
138
+ end
139
+
140
+ def _chomsky_nf_helper_vars(index, rule, helper_vars)
141
+ rule_size = rule.size
142
+ letter = helper_vars.empty? ? _find_letter(index) : helper_vars.sort!.last.succ
143
+ rest = rule[(index + 1)..rule_size].join
144
+
145
+ [letter, rest]
146
+ end
147
+
148
+ def _find_letter(n)
149
+ result = 'H'
150
+ n.times { result = result.succ }
151
+
152
+ result
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CFGHelper
4
+ # simplify chomsky normal form
5
+ class ChomskyNFSimplifier
6
+ def run(new_rules, alphabet)
7
+ vars_with_one_letter = _vars_with_one_letter new_rules, alphabet
8
+
9
+ return new_rules if vars_with_one_letter.empty?
10
+
11
+ extendible_rules = _find_extendible_rules new_rules, vars_with_one_letter.keys
12
+
13
+ return new_rules if extendible_rules.empty?
14
+
15
+ missing_rules = _missing_rules extendible_rules, vars_with_one_letter
16
+
17
+ _add_missing_rules missing_rules, new_rules
18
+
19
+ new_rules
20
+ end
21
+
22
+ private
23
+
24
+ def _vars_with_one_letter(new_rules, alphabet)
25
+ vars = {}
26
+
27
+ new_rules.each do |key, values|
28
+ # skip simple rules like (A, a), (B, b),...
29
+ next if values.size == 1
30
+
31
+ letters = values.select { |val| _is_letter val, alphabet }.uniq
32
+ vars[key] = letters
33
+ end
34
+
35
+ vars
36
+ end
37
+
38
+ def _is_letter(val, alphabet)
39
+ val.size == 1 && alphabet.include?(val.downcase)
40
+ end
41
+
42
+ def _find_extendible_rules(new_rules, rules_keys)
43
+ extendible = {}
44
+
45
+ filterd_rules = new_rules.reject { |_, rules| rules.is_a?(String) }
46
+
47
+ filterd_rules.each do |letter, rules|
48
+ rules_keys.each do |rules_key|
49
+ rules.each do |rule|
50
+ next unless rule.include?(rules_key)
51
+
52
+ extendible[letter] ||= []
53
+ extendible[letter] << rule
54
+ end
55
+ end
56
+ end
57
+
58
+ extendible
59
+ end
60
+
61
+ def _missing_rules(extendible_rules, vars)
62
+ res = {}
63
+
64
+ extendible_rules.each do |var_rule, nfs|
65
+ nfs.each do |nf|
66
+ vars.each do |match_char, replacement_chars|
67
+ replacement_chars.each do |rc|
68
+ res[var_rule] ||= []
69
+ res[var_rule] << _combinations(nf, rc, match_char)
70
+ end
71
+ end
72
+
73
+ res[var_rule] = res[var_rule]&.flatten&.uniq
74
+
75
+ _check_special_case nf, res, var_rule, vars
76
+ end
77
+ end
78
+
79
+ res
80
+ end
81
+
82
+ def _combinations(original_string, replacement_char, match_char)
83
+ combinations = []
84
+
85
+ 0.upto(1) do |i|
86
+ 0.upto(1) do |j|
87
+ new_rule = original_string.dup
88
+ new_rule[0] = replacement_char if i == 1 && original_string[0] == match_char
89
+ new_rule[1] = replacement_char if j == 1 && original_string[1] == match_char
90
+ combinations << new_rule
91
+ end
92
+ end
93
+
94
+ combinations.uniq
95
+ end
96
+
97
+ def _add_missing_rules(missing_rules, new_rules)
98
+ missing_rules.each do |key, val|
99
+ next if val.nil?
100
+
101
+ new_rules[key].concat(val).uniq!
102
+ end
103
+ end
104
+
105
+ def _check_special_case(nf, res, var_rule, vars)
106
+ vars.each do |match_char, combi|
107
+ next unless nf == match_char * 2
108
+
109
+ missing_rules = combi.map { |lft| combi.map { |rgt| "#{lft}#{rgt}" } }.flatten
110
+
111
+ res[var_rule].concat(missing_rules)
112
+ end
113
+
114
+ res
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CFGHelper
4
+ # Implemention of cyk algo.
5
+ class CYK
6
+ attr_reader :matrix, :is_in_l
7
+
8
+ def initialize(word, custom_rule)
9
+ @word = word
10
+ @rules = custom_rule.select { |_, val| val.is_a?(Array) }
11
+ @helper_vars = @rules.values.flatten
12
+ end
13
+
14
+ def run
15
+ _cyk_fill_diagonal
16
+ _cyk_fill_matrix
17
+ @is_in_l = _is_in_l?
18
+ end
19
+
20
+ private
21
+
22
+ def _cyk_fill_diagonal
23
+ wrd_lng = @word.length
24
+ @matrix = wrd_lng.times.map { wrd_lng.times.map { [] } }
25
+
26
+ (0..wrd_lng - 1).each do |index|
27
+ @matrix[index][index] = @word[index].upcase
28
+ end
29
+ end
30
+
31
+ def _cyk_fill_matrix
32
+ @matrix.size.times do |limiter|
33
+ (0...@matrix.length - limiter).each do |i|
34
+ j = i + limiter
35
+
36
+ next if i == j
37
+
38
+ helper_var = _helper_var i, j
39
+ matrix_ele_info = _matrix_ele_info i, j
40
+ @matrix[i][j] = _add_to_matrix helper_var, matrix_ele_info
41
+ end
42
+ end
43
+ end
44
+
45
+ def _helper_var(row, col)
46
+ res = '0'
47
+
48
+ (row..col - 1).step(1) do |h|
49
+ p_ = @matrix[row][h]
50
+ q_ = @matrix[h + 1][col]
51
+ res = "#{p_}#{q_}"
52
+
53
+ break if @helper_vars.include? res
54
+ end
55
+
56
+ res
57
+ end
58
+
59
+ def _matrix_ele_info(i, j)
60
+ res = {}
61
+
62
+ res['i'] = i
63
+ res['j'] = j
64
+ res['row_c'] = @matrix.count
65
+
66
+ res
67
+ end
68
+
69
+ def _add_to_matrix(helper_var, matrix_ele_info)
70
+ matrix_ele = '0'
71
+ return if helper_var == matrix_ele
72
+
73
+ keys = []
74
+
75
+ @rules.each do |key, vals|
76
+ vals.each do |val|
77
+ keys << key if val == helper_var
78
+ end
79
+ end
80
+
81
+ _matrix_ele keys, matrix_ele_info
82
+ end
83
+
84
+ def _matrix_ele(keys, matrix_ele_info)
85
+ return 'S' if keys.include?('S') && _is_last_element?(matrix_ele_info)
86
+
87
+ return keys.last if keys.size == 1
88
+
89
+ keys.reject { |key| key == 'S' }.last || '0'
90
+ end
91
+
92
+ def _is_last_element?(matrix_ele_info)
93
+ matrix_ele_info['i'].zero? && matrix_ele_info['j'] == matrix_ele_info['row_c'] - 1
94
+ end
95
+
96
+ def _is_in_l?
97
+ @matrix.first.last == 'S'
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CFGHelper
4
+ # used to clear a CFG from the epsilon rules
5
+ class EpsilonFree
6
+ def run(rules)
7
+ epsilon = _epsilon rules
8
+ rules_ef = {}
9
+
10
+ return if epsilon.empty?
11
+
12
+ epsilon.each do |e_class|
13
+ cleared_rules = _cleared_rules rules[e_class]
14
+ epsilon_free_word = cleared_rules.map(&:join)
15
+ grammer = _grammer_epsilon_free epsilon_free_word, e_class
16
+ rules_ef[e_class] = grammer
17
+ end
18
+
19
+ rules_ef
20
+ end
21
+
22
+ def rebuild_rules(current_rules, rules_epsilon_free)
23
+ missing_vars = current_rules.keys - rules_epsilon_free.keys
24
+
25
+ res = {}
26
+ rules_epsilon_free.each do |key, rules|
27
+ res[key] = []
28
+ rules.each do |rule|
29
+ res[key] << rule.chars
30
+ end
31
+ end
32
+
33
+ missing_vars.each { |v| res[v] = current_rules[v] }
34
+
35
+ res
36
+ end
37
+
38
+ private
39
+
40
+ def _epsilon(rules)
41
+ res = rules.map do |var, rule|
42
+ next unless rule.include? []
43
+
44
+ var
45
+ end
46
+ res.reject(&:nil?)
47
+ end
48
+
49
+ def _cleared_rules(rules)
50
+ rules.reject(&:empty?)
51
+ end
52
+
53
+ def _grammer_epsilon_free(epsilon_free_word, e_class)
54
+ rules_epsilon_location = _new_rules_epsilon_location epsilon_free_word, e_class
55
+
56
+ res = []
57
+ rules_epsilon_location.map do |rule, possibilities|
58
+ possibilities.each do |possibility|
59
+ res << _remove_e_class(possibility, rule.split(''))
60
+ end
61
+
62
+ res << epsilon_free_word.delete(e_class) if possibilities.size == 1
63
+ end
64
+
65
+ res.uniq.reject(&:nil?)
66
+ end
67
+
68
+ def _new_rules_epsilon_location(s_rule, e_class)
69
+ res = {}
70
+ s_rule.each do |rule|
71
+ s_index = []
72
+ rule.each_char.with_index { |char, index| s_index << index if char == e_class }
73
+ res[rule] = _power_set(s_index)
74
+ end
75
+
76
+ res
77
+ end
78
+
79
+ def _power_set(set)
80
+ if set.empty?
81
+ [[]]
82
+ else
83
+ element = set[0]
84
+ subsets = _power_set set[1..]
85
+ subsets + subsets.map { |subset| [element] + subset }
86
+ end
87
+ end
88
+
89
+ def _remove_e_class(possibility, cleared_rules)
90
+ return cleared_rules.join '' if possibility.empty?
91
+
92
+ new_rule = cleared_rules.join ''
93
+ possibility.reverse.each { |index| new_rule.slice! index }
94
+
95
+ new_rule
96
+ end
97
+ end
98
+ end
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: nil, word: nil, n: 2, k: 20
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
- # 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
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.1.1
4
+ version: 0.0.2.2
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-03 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,18 +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
+ - lib/cfg_helper/chaining_free.rb
21
+ - lib/cfg_helper/chomsky_nf.rb
22
+ - lib/cfg_helper/chomsky_nf_simplifier.rb
23
+ - lib/cfg_helper/cyk.rb
24
+ - lib/cfg_helper/epsilon_free.rb
24
25
  - lib/dfa.rb
25
- - lib/epsilon_free.rb
26
26
  - lib/falafel.rb
27
27
  - lib/pump.rb
28
28
  - lib/rx.rb
29
29
  homepage: https://github.com/AbedAlkedda/AFS
30
30
  licenses:
31
- - Ruby
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 to help you understand Automata better, hopefully
51
+ summary: Falafel is a gem that will hopefully help you better understand automata
52
+ and formal languages
52
53
  test_files: []
data/lib/chaining_free.rb DELETED
@@ -1,67 +0,0 @@
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 DELETED
@@ -1,153 +0,0 @@
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
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
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # simplify chomsky normal form
4
- class ChomskyNFSimplifier
5
- def run(new_rules, alphabet)
6
- var_has_one_letter = _var_has_one_letter new_rules, alphabet
7
-
8
- return new_rules if var_has_one_letter.empty?
9
-
10
- rules = _find_modify_rules new_rules, var_has_one_letter
11
-
12
- return new_rules if rules.empty?
13
-
14
- missing_rules = _generate_missing_rules rules, var_has_one_letter
15
-
16
- _add_missing_rules missing_rules, new_rules
17
-
18
- new_rules
19
- end
20
-
21
- private
22
-
23
- def _var_has_one_letter(new_rules, alphabet)
24
- vars = {}
25
-
26
- new_rules.each do |key, values|
27
- next if values.size == 1
28
-
29
- values.each do |val|
30
- next unless val.size == 1 && alphabet.include?(val.downcase)
31
-
32
- vars[key] ||= []
33
- vars[key] << val
34
- end
35
- end
36
-
37
- vars
38
- end
39
-
40
- def _find_modify_rules(new_rules, vars)
41
- modify = {}
42
-
43
- new_rules.each do |key, val|
44
- next if val.is_a?(String)
45
-
46
- val.each do |v|
47
- vars.each_key do |k|
48
- next unless v.include?(k)
49
-
50
- res = val.select { |x| x.include? k }
51
-
52
- modify[key] ||= []
53
- modify[key] << res
54
- end
55
- end
56
- end
57
-
58
- modify
59
- end
60
-
61
- def _generate_missing_rules(rules, vars)
62
- res = {}
63
-
64
- rules.each do |var_rule, nfs|
65
- nfs.flatten.each do |nf|
66
- vars.each do |match_char, replacement_chars|
67
- replacement_chars.each do |rc|
68
- new_rules = _combinations nf, rc, match_char
69
- res[var_rule] ||= []
70
- res[var_rule] << new_rules
71
- end
72
- end
73
- _check_special_case nf, res, var_rule, vars
74
- end
75
- end
76
-
77
- res
78
- end
79
-
80
- def _combinations(original_string, replacement_char, match_char)
81
- combinations = []
82
-
83
- 0.upto(1) do |i|
84
- 0.upto(1) do |j|
85
- new_rule = original_string.dup
86
- new_rule[0] = replacement_char if i == 1 && original_string[0] == match_char
87
- new_rule[1] = replacement_char if j == 1 && original_string[1] == match_char
88
- combinations << new_rule
89
- end
90
- end
91
-
92
- combinations.uniq
93
- end
94
-
95
- def _add_missing_rules(missing_rules, new_rules)
96
- missing_rules.each { |key, val| new_rules[key] = val.flatten.uniq! }
97
- end
98
-
99
- def _check_special_case(nf, res, var_rule, vars)
100
- vars.each do |match_char, combi|
101
- next unless nf == match_char * 2
102
-
103
- missing_rules = combi.map { |lft| combi.map { |rgt| "#{lft}#{rgt}" } }.flatten
104
-
105
- res[var_rule] << missing_rules
106
- end
107
-
108
- res
109
- end
110
- end
data/lib/cyk.rb DELETED
@@ -1,74 +0,0 @@
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?
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
-
39
- @matrix[i][j] = _add_to_matrix helper_var
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 _add_to_matrix(rule)
59
- res = '0'
60
- return if rule == res
61
-
62
- @rules.each do |key, vals|
63
- vals.each do |val|
64
- res = key if val == rule
65
- end
66
- end
67
-
68
- res
69
- end
70
-
71
- def _is_in_l?
72
- @is_in_l = @matrix.first.last == 'S'
73
- end
74
- end
data/lib/epsilon_free.rb DELETED
@@ -1,96 +0,0 @@
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
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