falafel 0.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/lib/dfa.rb +100 -0
  3. data/lib/falafel.rb +133 -0
  4. data/lib/pump.rb +30 -0
  5. data/lib/rx.rb +154 -0
  6. metadata +45 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 455d0fee114300f1c5a7f5f68b128412704f6bba8782efc62db6f6d151a1bfdd
4
+ data.tar.gz: b96c52e7fd535cc95e140722e522c959bb860c78ab0b93030240c7fa148995a6
5
+ SHA512:
6
+ metadata.gz: 44076c2c3d1ab9cb52a43493b6e17f63944d7dc2c7ef6842395befec2f27513103e0592a671ff5352ab680381d9e1323ea498cb332926f924ec1a4236ac9c051
7
+ data.tar.gz: bfe088d01ace7256fa535ac8c1e2d041972203535a98ffbce6e9785a7e28bb2aed332aa2239a0f43929d8c4aecb69a422bd5273580e87e373611db122134f403
data/lib/dfa.rb ADDED
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'falafel'
4
+
5
+ # DFA class
6
+ class DFA < Falafel
7
+ attr_accessor :delta_star, :states, :finals, :start
8
+
9
+ def to_min
10
+ finals = _finals
11
+ states = _states
12
+ r0 = _r0 finals, states
13
+ steps = {}
14
+
15
+ steps['R0'] = r0
16
+ finals_hash = _finals_hash finals, states
17
+ steps = _steps_builder finals, states, steps
18
+
19
+ _print finals_hash, finals, states
20
+ _print_steps steps
21
+ end
22
+
23
+ private
24
+
25
+ def _finals
26
+ @finals.map { |f| f.join('').to_i }
27
+ end
28
+
29
+ def _states
30
+ ltr = 'p'
31
+ states = @states.each_with_object({}) do |elt, memo|
32
+ memo[elt] = ltr
33
+ ltr = ltr.succ
34
+ end
35
+
36
+ puts "\nQ=#{states}"
37
+
38
+ states
39
+ end
40
+
41
+ def _r0(finals, states)
42
+ r0 = []
43
+ @states.each_with_index do |p, _|
44
+ @states.each_with_index do |q, _|
45
+ r0 << [states[p], states[q]] if finals.include?(p) == finals.include?(q)
46
+ end
47
+ end
48
+
49
+ r0
50
+ end
51
+
52
+ def _finals_hash(finals, states)
53
+ (finals & states.keys).each_with_object({}) { |elt, memo| memo[elt] = states[elt] }
54
+ end
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
66
+ end
67
+ end
68
+ r.uniq!
69
+
70
+ r
71
+ end
72
+
73
+ def _steps_builder(finals, states, steps)
74
+ 0.upto(1_000) do |l|
75
+ r = _r finals, states, steps["R#{l}"]
76
+ steps["R#{l + 1}"] = r
77
+
78
+ return steps if steps["R#{l}"] == r
79
+ end
80
+ end
81
+
82
+ def _print(finals_hash, finals, states)
83
+ puts "F=#{finals_hash.inspect}"
84
+
85
+ f_equivalent = finals.flat_map { |s| states.values_at(s) }
86
+ q_without_f = (@states - (@states & finals)).flat_map { |s| states.values_at(s) }
87
+ puts "{F, Q\\F }={#{f_equivalent}}, {#{q_without_f}}"
88
+ end
89
+
90
+ def _print_steps(steps)
91
+ steps.each { |k, v| puts "#{k}=#{v}" }
92
+ end
93
+
94
+ def _ri_set(states, c, p, q)
95
+ lft = states[(_image [p], @delta_star[c])[0]]
96
+ rgt = states[(_image [q], @delta_star[c])[0]]
97
+
98
+ [[lft, rgt]]
99
+ end
100
+ end
data/lib/falafel.rb ADDED
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rx'
4
+
5
+ # Convert NFA to DFA
6
+ # DFA to REG
7
+ # Minimize DFA
8
+ class Falafel
9
+ def self.new
10
+ instance = allocate
11
+ yield instance
12
+
13
+ instance
14
+ end
15
+
16
+ def build(*args, states:, starts:, finals:)
17
+ d_a, d_b = args
18
+ @delta_star = { a: d_a, b: d_b }
19
+ @state_set = power_set states
20
+ @finals = finals
21
+ @states = states
22
+ @start = starts
23
+ end
24
+
25
+ # Binary relation for max number
26
+ def relation(max)
27
+ (1..max).flat_map { |x| (1..3).map { |y| [x, y] } }
28
+ end
29
+
30
+ # Concatenation two states
31
+ # Example: [[1, 2]] . [[2, 1]] => [1, 1]
32
+ def compose(lft, rgt)
33
+ lft.flat_map { |x, y1| rgt.select { |y2, _| y1 == y2 }.map { |_, z| [x, z] } }
34
+ end
35
+
36
+ # Power set of Q
37
+ def power_set(states)
38
+ power_set = [[]]
39
+ states.each { |e| power_set.concat(power_set.map { |set| set + [e] }) }
40
+
41
+ power_set
42
+ end
43
+
44
+ def potens_set
45
+ @state_set.each { |set| @delta_star.each { |delta, relation| puts "#{set.inspect}·#{delta}() = #{_image(set, relation).inspect}" } }
46
+ end
47
+
48
+ def nfa_to_dfa
49
+ require_relative 'dfa'
50
+
51
+ dfa = DFA.new { |a| a.build [], [], states: [], starts: @start, finals: [] }
52
+ reachable = []
53
+ done_state = []
54
+ current_state = @start
55
+
56
+ loop do
57
+ @delta_star.each do |delta, relation|
58
+ break if done_state.include? current_state
59
+
60
+ res, rgt, lft = _new_state current_state, relation
61
+
62
+ dfa.states << rgt unless dfa.states.include? rgt
63
+
64
+ dfa.delta_star[delta] << [lft, rgt]
65
+
66
+ reachable << res unless reachable.include? res
67
+
68
+ reachable.delete res if done_state.include? res
69
+ end
70
+
71
+ break if reachable.empty?
72
+
73
+ done_state << current_state unless done_state.include? current_state
74
+
75
+ dfa.finals << current_state if (current_state & @finals).any?
76
+
77
+ current_state = reachable.last
78
+
79
+ reachable.pop
80
+ end
81
+ dfa.finals.uniq!
82
+
83
+ _print_dfa dfa
84
+
85
+ dfa
86
+ end
87
+
88
+ def nfa_to_reg
89
+ RX.new { |rx| rx.build @states, @delta_star }
90
+ end
91
+
92
+ def dfa_to_min(automat)
93
+ d_a = automat.delta_star[:a]
94
+ d_b = automat.delta_star[:b]
95
+ states = automat.states
96
+ starts = automat.start
97
+ finals = automat.finals
98
+
99
+ dfa = DFA.new { |a| a.build d_a, d_b, states: states, starts: starts, finals: finals }
100
+ dfa.to_min
101
+ end
102
+
103
+ def pump_lemma
104
+ require_relative 'pump'
105
+
106
+ Pump.new lang: nil, word: nil, n: 2, k: 20
107
+ end
108
+
109
+ private
110
+
111
+ # alle bilder eine menge m unter r
112
+ def _image(m, r)
113
+ m.flat_map { |x| r.map { |y, z| z if x == y }.compact }.sort.uniq
114
+ end
115
+
116
+ def _concat(set)
117
+ set.to_a.join('').to_i
118
+ end
119
+
120
+ def _new_state(state, relation)
121
+ res = _image state, relation
122
+ rgt = _concat res
123
+ lft = _concat state
124
+
125
+ [res, rgt, lft]
126
+ end
127
+
128
+ def _print_dfa(dfa)
129
+ puts "dfa:\nfinals: #{dfa.finals.map { |ele| _concat(ele).to_i }}"
130
+ puts "states: #{dfa.states.map(&:to_i)}"
131
+ dfa.delta_star.each { |k, v| v.each { |e| puts "(#{e[0]} ,'#{k}', #{e[1]})" } }
132
+ end
133
+ end
data/lib/pump.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Pump Lemma
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
7
+ class Pump
8
+ attr_accessor :lang, :word
9
+
10
+ def initialize(lang:, word:, n:, k:)
11
+ @lang = lang
12
+ @word = word
13
+ @n = n
14
+ @k = k
15
+ end
16
+
17
+ def run
18
+ r, s, t = ''
19
+
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
+
25
+ break if s.length.positive? && (r + s * @k + t) != @word && @lang.call(r + s * @k + t)
26
+ end
27
+
28
+ [r, s, t]
29
+ end
30
+ end
data/lib/rx.rb ADDED
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Regex string builder
4
+ class RX
5
+ def self.new
6
+ instance = allocate
7
+ yield instance
8
+
9
+ instance
10
+ end
11
+
12
+ def build(states, delta_star)
13
+ @states = states
14
+ @delta_star = delta_star
15
+ @empty = "\u2205"
16
+ _to_reg
17
+ end
18
+
19
+ private
20
+
21
+ def _to_reg
22
+ l0 = _l0
23
+ steps = {}
24
+ steps['l0'] = l0
25
+
26
+ @states.size.times do |h|
27
+ l = []
28
+ @states.each_with_index do |_, p|
29
+ l[p] = []
30
+ @states.each_with_index do |_, q|
31
+ l[p][q] = _l steps["l#{h}"], p, q, steps.size - 1
32
+ end
33
+ end
34
+ steps["l#{steps.size}"] = l
35
+ end
36
+ _print_matrix steps
37
+ end
38
+
39
+ def _l0
40
+ l0 = []
41
+ @states.each_with_index do |_, i|
42
+ l0[i] = []
43
+ @states.each_with_index do |_, y|
44
+ l0[i][y] = _letter i, y
45
+ end
46
+ end
47
+
48
+ l0
49
+ end
50
+
51
+ def _l(l, p, q, h)
52
+ case [h == p, h == q]
53
+ in [true, true] then _states_equal l, h
54
+ in [true, false] then _states_begin l, h, q
55
+ in [false, true] then _states_end l, h, p
56
+ else _states_unequal l, p, q, h
57
+ end
58
+ end
59
+
60
+ def _letter_build(l, i, y)
61
+ letter = i == y ? 'ε' : ''
62
+ letter += letter.empty? ? l : "+#{l}"
63
+
64
+ letter
65
+ end
66
+
67
+ def _reachable(delta, i, y)
68
+ (@delta_star[delta] & [[i + 1, y + 1]]).any?
69
+ end
70
+
71
+ def _letter(i, y)
72
+ letter = ''
73
+
74
+ if _reachable :a, i, y
75
+ letter = _letter_build 'a', i, y
76
+ elsif _reachable :b, i, y
77
+ letter = _letter_build 'b', i, y
78
+ else
79
+ letter += '∅'
80
+ letter = 'ε' if i == y
81
+ end
82
+
83
+ letter
84
+ end
85
+
86
+ def _states_equal(l, h)
87
+ "(#{_state_simplify l, h})*"
88
+ end
89
+
90
+ def _states_begin(l, h, q)
91
+ lft = l[h][q]
92
+ rgt = _clear_reg l[h][h]
93
+ letter = "#{lft}.(#{rgt})*"
94
+ letter = @empty if lft == @empty || rgt == @empty
95
+
96
+ letter
97
+ end
98
+
99
+ def _states_end(l, h, p)
100
+ lft = _clear_reg l[h][h]
101
+ rgt = l[h][p]
102
+ letter = "(#{lft})*.#{rgt}"
103
+ letter = @empty if lft == @empty || rgt == @empty
104
+
105
+ letter
106
+ end
107
+
108
+ def _states_unequal(l0, p, q, h)
109
+ lft = l0[p][q]
110
+ lft = lft.dup
111
+ lft.gsub!(/∅/, '')
112
+
113
+ rgt = _states_unequal_rgt l0, p, q, h
114
+ rgt = rgt.dup
115
+ rgt.gsub!(/∅./, '')
116
+
117
+ return "(#{rgt})" if lft.empty?
118
+
119
+ rgt == @empty ? lft : "#{lft}+(#{rgt})"
120
+ end
121
+
122
+ def _state_simplify(l, h)
123
+ l[h][h].size == 3 ? l[h][h][2] : l[h][h]
124
+ end
125
+
126
+ def _clear_reg(str)
127
+ str.gsub(/ε./, '')
128
+ end
129
+
130
+ def _empty?(state)
131
+ state == @empty
132
+ end
133
+
134
+ def _states_unequal_rgt(l, p, q, h)
135
+ letter = _clear_reg l[h][h]
136
+ rgt = "#{l[h][q]}.(#{letter})*.#{l[p][h]}"
137
+ rgt = @empty if _empty?(l[p][h]) || _empty?(l[h][h]) || _empty?(l[h][q])
138
+
139
+ rgt
140
+ 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
+ end
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: falafel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Abed A
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-06-05 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/dfa.rb
20
+ - lib/falafel.rb
21
+ - lib/pump.rb
22
+ - lib/rx.rb
23
+ homepage:
24
+ licenses: []
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 2.6.0
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubygems_version: 3.3.26
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: Falafel is a gem to help you understand autotool better, hopefully
45
+ test_files: []