falafel 0.0.0.2 → 0.0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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: []