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