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.
- checksums.yaml +7 -0
- data/lib/dfa.rb +100 -0
- data/lib/falafel.rb +133 -0
- data/lib/pump.rb +30 -0
- data/lib/rx.rb +154 -0
- 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: []
|