falafel 0.0.0.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.
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: []