truthtable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/CONTRIBUTORS +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +27 -0
- data/README +102 -0
- data/Rakefile +2 -0
- data/lib/truthtable.rb +197 -0
- data/lib/truthtable/qm.rb +366 -0
- data/lib/truthtable/truth_table_object.rb +40 -0
- data/lib/truthtable/version.rb +3 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/truthtable/qm_spec.rb +133 -0
- data/spec/truthtable/truthtable_spec.rb +61 -0
- data/truthtable.gemspec +23 -0
- data/truthtable.rb +345 -0
- metadata +114 -0
data/.gitignore
ADDED
data/CONTRIBUTORS
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
truthtable (0.0.1)
|
5
|
+
msgpack
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.1.3)
|
11
|
+
msgpack (0.4.7)
|
12
|
+
rspec (2.11.0)
|
13
|
+
rspec-core (~> 2.11.0)
|
14
|
+
rspec-expectations (~> 2.11.0)
|
15
|
+
rspec-mocks (~> 2.11.0)
|
16
|
+
rspec-core (2.11.1)
|
17
|
+
rspec-expectations (2.11.2)
|
18
|
+
diff-lcs (~> 1.1.3)
|
19
|
+
rspec-mocks (2.11.1)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
rspec
|
26
|
+
truthtable!
|
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# truthtable.rb - truth table and formula generator
|
2
|
+
#
|
3
|
+
# Copyright (C) 2007 Tanaka Akira <akr@fsij.org>
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
(1) Redistributions of source code must retain the above copyright notice, this
|
9
|
+
list of conditions and the following disclaimer.
|
10
|
+
(2) Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
(3) The name of the author may not be used to endorse or promote products
|
14
|
+
derived from this software without specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
17
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
18
|
+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
19
|
+
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
20
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
21
|
+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
22
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
23
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
24
|
+
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
25
|
+
OF SUCH DAMAGE.
|
26
|
+
|
27
|
+
(The modified BSD licence)
|
data/README
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
= truthtable - truth table to logical formula
|
2
|
+
|
3
|
+
The truthtable library generates a truth table from a logical formula written in Ruby.
|
4
|
+
The truth table can be converted to a logical formula.
|
5
|
+
|
6
|
+
== Install
|
7
|
+
|
8
|
+
gem install 'truthtable'
|
9
|
+
|
10
|
+
== Features
|
11
|
+
|
12
|
+
* generate a truth table from a given block written in Ruby.
|
13
|
+
* generate a formula from the table:
|
14
|
+
* minimal one (obtained by Quine-McCluskey algorithm)
|
15
|
+
* disjunctive normal form
|
16
|
+
* conjunctive normal form
|
17
|
+
|
18
|
+
== Usage
|
19
|
+
|
20
|
+
require 'truthtable'
|
21
|
+
|
22
|
+
* puts, p and pp shows truth table
|
23
|
+
|
24
|
+
puts TruthTable.new {|v| v[0] & v[1] }
|
25
|
+
#=>
|
26
|
+
v[0] v[1] |
|
27
|
+
----------+--
|
28
|
+
f f | f
|
29
|
+
f t | f
|
30
|
+
t f | f
|
31
|
+
t t | t
|
32
|
+
|
33
|
+
p TruthTable.new {|v| v[0] & v[1] }
|
34
|
+
#=> #<TruthTable: !v[0]&!v[1]=>false !v[0]&v[1]=>false v[0]&!v[1]=>false v[0]&v[1]=>true>
|
35
|
+
|
36
|
+
require 'pp'
|
37
|
+
pp TruthTable.new {|v| v[0] & v[1] }
|
38
|
+
#=>
|
39
|
+
#<TruthTable:
|
40
|
+
!v[0]&!v[1]=>false
|
41
|
+
!v[0]& v[1]=>false
|
42
|
+
v[0]&!v[1]=>false
|
43
|
+
v[0]& v[1]=>true>
|
44
|
+
|
45
|
+
* '?' is shown for non-evaluated variable
|
46
|
+
|
47
|
+
puts TruthTable.new {|v| v[0] && v[1] }'
|
48
|
+
#=>
|
49
|
+
v[0] v[1] |
|
50
|
+
----------+--
|
51
|
+
f ? | f
|
52
|
+
t f | f
|
53
|
+
t t | t
|
54
|
+
|
55
|
+
puts TruthTable.new {|v| v[0] || v[1] }'
|
56
|
+
#=>
|
57
|
+
v[0] v[1] |
|
58
|
+
----------+--
|
59
|
+
f f | f
|
60
|
+
f t | t
|
61
|
+
t ? | t
|
62
|
+
|
63
|
+
puts TruthTable.new {|v| !v[0] ? !v[1] : !v[2] }'
|
64
|
+
#=>
|
65
|
+
v[0] v[1] v[2] |
|
66
|
+
---------------+--
|
67
|
+
f f ? | t
|
68
|
+
f t ? | f
|
69
|
+
t ? f | t
|
70
|
+
t ? t | f
|
71
|
+
|
72
|
+
* formula generation
|
73
|
+
|
74
|
+
p TruthTable.new {|v| !v[0] }.formula #=> "!v[0]"
|
75
|
+
p TruthTable.new {|v| v[0] & v[1] }.formula #=> "v[0]&v[1]"
|
76
|
+
p TruthTable.new {|v| v[0] | v[1] }.formula #=> "v[0] | v[1]"
|
77
|
+
p TruthTable.new {|v| v[0] ^ v[1] }.formula #=> "!v[0]&v[1] | v[0]&!v[1]"
|
78
|
+
p TruthTable.new {|v| v[0] == v[1] }.formula #=> "!v[0]&!v[1] | v[0]&v[1]"
|
79
|
+
|
80
|
+
* shortcuts, && and ||, are not preserved
|
81
|
+
|
82
|
+
p TruthTable.new {|v| v[0] && v[1] }.formula #=> "v[0]&v[1]"
|
83
|
+
p TruthTable.new {|v| v[0] || v[1] }.formula #=> "v[0] | v[1]"
|
84
|
+
|
85
|
+
* actually any expression (without side effect)
|
86
|
+
|
87
|
+
p TruthTable.new {|v| v[0] ? !v[1] : v[1] }.formula #=> "!v[0]&v[1] | v[0]&!v[1]"
|
88
|
+
|
89
|
+
* any number of inputs
|
90
|
+
|
91
|
+
p TruthTable.new {|v| [v[0], v[1], v[2], v[3]].grep(true).length <= 3 }.formula
|
92
|
+
#=> "!v[0] | !v[1] | !v[2] | !v[3]"
|
93
|
+
|
94
|
+
|
95
|
+
== Reference Manual
|
96
|
+
|
97
|
+
See rdoc/classes/TruthTable.html or
|
98
|
+
http://www.a-k-r.org/truthtable/rdoc/TruthTable.html
|
99
|
+
|
100
|
+
== Home Page
|
101
|
+
|
102
|
+
http://www.a-k-r.org/truthtable/
|
data/Rakefile
ADDED
data/lib/truthtable.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
require "truthtable/version"
|
2
|
+
require "truthtable/truth_table_object"
|
3
|
+
require "truthtable/qm"
|
4
|
+
|
5
|
+
class TruthTable
|
6
|
+
|
7
|
+
def self.test(&b)
|
8
|
+
r = []
|
9
|
+
o = TruthTableObject.new
|
10
|
+
begin
|
11
|
+
result = !!b.call(o)
|
12
|
+
inputs = o.plan
|
13
|
+
order = o.order
|
14
|
+
r << [inputs, result, order]
|
15
|
+
end while o.next_plan
|
16
|
+
r
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(&b)
|
20
|
+
table = TruthTable.test(&b)
|
21
|
+
@table = table
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
r = ''
|
26
|
+
names = sort_names(all_names.keys)
|
27
|
+
format = ''
|
28
|
+
sep = ''
|
29
|
+
names.each {|name|
|
30
|
+
format << "%-#{name.length}s "
|
31
|
+
sep << '-' * (name.length+1)
|
32
|
+
}
|
33
|
+
format << "| %s\n"
|
34
|
+
sep << "+--\n"
|
35
|
+
r << sprintf(format, *(names + ['']))
|
36
|
+
r << sep
|
37
|
+
@table.each {|inputs, output, order|
|
38
|
+
h = {}
|
39
|
+
each_input(inputs) {|name, input|
|
40
|
+
h[name] = input
|
41
|
+
}
|
42
|
+
args = []
|
43
|
+
names.each {|name|
|
44
|
+
if h.has_key? name
|
45
|
+
args << (h[name] ? 't' : 'f').center(name.length)
|
46
|
+
else
|
47
|
+
args << '?'.center(name.length)
|
48
|
+
end
|
49
|
+
}
|
50
|
+
args << (output ? 't' : 'f')
|
51
|
+
r << sprintf(format, *args)
|
52
|
+
}
|
53
|
+
r
|
54
|
+
end
|
55
|
+
|
56
|
+
# :stopdoc:
|
57
|
+
def inspect
|
58
|
+
result = "#<#{self.class}:"
|
59
|
+
@table.each {|inputs, output, order|
|
60
|
+
term = []
|
61
|
+
each_input(inputs) {|name, input|
|
62
|
+
if input
|
63
|
+
term << name
|
64
|
+
else
|
65
|
+
term << "!#{name}"
|
66
|
+
end
|
67
|
+
}
|
68
|
+
result << " #{term.join('&')}=>#{output}"
|
69
|
+
}
|
70
|
+
result << ">"
|
71
|
+
result
|
72
|
+
end
|
73
|
+
|
74
|
+
def pretty_print(q)
|
75
|
+
q.object_group(self) {
|
76
|
+
q.text ':'
|
77
|
+
q.breakable
|
78
|
+
q.seplist(@table, lambda { q.breakable('; ') }) {|inputs, output, order|
|
79
|
+
term = []
|
80
|
+
each_input(inputs) {|name, input|
|
81
|
+
if input
|
82
|
+
term << " #{name}"
|
83
|
+
else
|
84
|
+
term << "!#{name}"
|
85
|
+
end
|
86
|
+
}
|
87
|
+
q.text "#{term.join('&')}=>#{output}"
|
88
|
+
}
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def all_names
|
93
|
+
return @all_names if defined? @all_names
|
94
|
+
@all_names = {}
|
95
|
+
@table.each {|inputs, output, order|
|
96
|
+
order.each {|name|
|
97
|
+
if !@all_names.has_key?(name)
|
98
|
+
@all_names[name] = @all_names.size
|
99
|
+
end
|
100
|
+
}
|
101
|
+
}
|
102
|
+
@all_names
|
103
|
+
end
|
104
|
+
|
105
|
+
def sort_names(names)
|
106
|
+
total_order = all_names
|
107
|
+
names.sort_by {|n| total_order[n] }
|
108
|
+
end
|
109
|
+
|
110
|
+
def each_input(inputs)
|
111
|
+
sort_names(inputs.keys).each {|name|
|
112
|
+
yield name, inputs[name]
|
113
|
+
}
|
114
|
+
end
|
115
|
+
# :startdoc:
|
116
|
+
|
117
|
+
# obtains a formula in disjunctive normal form.
|
118
|
+
def dnf
|
119
|
+
r = []
|
120
|
+
@table.each {|inputs, output|
|
121
|
+
return output.to_s if inputs.empty?
|
122
|
+
next if !output
|
123
|
+
term = []
|
124
|
+
each_input(inputs) {|name, input|
|
125
|
+
if input
|
126
|
+
term << name
|
127
|
+
else
|
128
|
+
term << "!#{name}"
|
129
|
+
end
|
130
|
+
}
|
131
|
+
r << term.join('&')
|
132
|
+
}
|
133
|
+
return "false" if r.empty?
|
134
|
+
r.join(' | ')
|
135
|
+
end
|
136
|
+
|
137
|
+
# obtains a formula in conjunctive normal form.
|
138
|
+
def cnf
|
139
|
+
r = []
|
140
|
+
@table.each {|inputs, output|
|
141
|
+
return output.to_s if inputs.empty?
|
142
|
+
next if output
|
143
|
+
term = []
|
144
|
+
each_input(inputs) {|name, input|
|
145
|
+
if input
|
146
|
+
term << "!#{name}"
|
147
|
+
else
|
148
|
+
term << name
|
149
|
+
end
|
150
|
+
}
|
151
|
+
if term.length == 1
|
152
|
+
r << term.join('|')
|
153
|
+
else
|
154
|
+
r << "(#{term.join('|')})"
|
155
|
+
end
|
156
|
+
}
|
157
|
+
return "true" if r.empty?
|
158
|
+
r.join(' & ')
|
159
|
+
end
|
160
|
+
|
161
|
+
# obtains a minimal formula using Quine-McCluskey algorithm.
|
162
|
+
def formula
|
163
|
+
input_names = all_names
|
164
|
+
input_names_ary = sort_names(input_names.keys)
|
165
|
+
tbl = {}
|
166
|
+
@table.each {|inputs, output|
|
167
|
+
return output.to_s if inputs.empty?
|
168
|
+
inputs2 = [:x] * input_names.length
|
169
|
+
inputs.each {|name, input|
|
170
|
+
inputs2[input_names[name]] = input ? 1 : 0
|
171
|
+
}
|
172
|
+
tbl[inputs2] = output ? 1 : 0
|
173
|
+
}
|
174
|
+
qm = QM.qm(tbl)
|
175
|
+
r = []
|
176
|
+
qm.each {|term|
|
177
|
+
t = []
|
178
|
+
num_dontcare = 0
|
179
|
+
term.each_with_index {|v, i|
|
180
|
+
if v == false
|
181
|
+
t << ("!" + input_names_ary[i])
|
182
|
+
elsif v == true
|
183
|
+
t << input_names_ary[i]
|
184
|
+
else # :x
|
185
|
+
num_dontcare += 1
|
186
|
+
end
|
187
|
+
}
|
188
|
+
if num_dontcare == term.length
|
189
|
+
r << 'true'
|
190
|
+
else
|
191
|
+
r << t.join('&')
|
192
|
+
end
|
193
|
+
}
|
194
|
+
return "false" if r.empty?
|
195
|
+
r.join(' | ')
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,366 @@
|
|
1
|
+
require 'msgpack'
|
2
|
+
|
3
|
+
class TruthTable
|
4
|
+
# Quine-McCluskey algorithm
|
5
|
+
module QM
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# implements Quine-McCluskey algorithm.
|
9
|
+
# It minimize the boolean function given by <i>tbl</i>.
|
10
|
+
#
|
11
|
+
# For example, the 3-inputs majority function is given as follows.
|
12
|
+
#
|
13
|
+
# tbl = {
|
14
|
+
# # P Q R
|
15
|
+
# [false, false, false] => false,
|
16
|
+
# [false, false, true ] => false,
|
17
|
+
# [false, true, false] => false,
|
18
|
+
# [false, true, true ] => true,
|
19
|
+
# [true, false, false] => false,
|
20
|
+
# [true, false, true ] => true,
|
21
|
+
# [true, true, false] => true,
|
22
|
+
# [true, true, true ] => true,
|
23
|
+
# }
|
24
|
+
# TruthTable::QM.qm(tbl)
|
25
|
+
# #=>
|
26
|
+
# [[true, true, :x], [true, :x, true], [:x, true, true]] # P&Q | P&R | Q&R
|
27
|
+
# # P Q P R Q R
|
28
|
+
#
|
29
|
+
# For another example, the implication function is given as follows.
|
30
|
+
#
|
31
|
+
# tbl = {
|
32
|
+
# # P Q
|
33
|
+
# [false, false] => true,
|
34
|
+
# [false, true ] => true,
|
35
|
+
# [true, false] => false,
|
36
|
+
# [true, true ] => true,
|
37
|
+
# }
|
38
|
+
# TruthTable::QM.qm(tbl)
|
39
|
+
# #=>
|
40
|
+
# [[false, :x], [:x, true]] # ~P | Q
|
41
|
+
# # ~P Q
|
42
|
+
#
|
43
|
+
# <i>tbl</i> is a hash to represent a boolean function.
|
44
|
+
# If the function has N variables,
|
45
|
+
# all key of <i>tbl</i> must be an array of N elements.
|
46
|
+
#
|
47
|
+
# A element of the key array and a value of the hash should be one of follows:
|
48
|
+
# - false, 0
|
49
|
+
# - true, 1
|
50
|
+
# - :x
|
51
|
+
#
|
52
|
+
# 0 is same as false.
|
53
|
+
#
|
54
|
+
# 1 is same as false.
|
55
|
+
#
|
56
|
+
# :x means "don't care".
|
57
|
+
#
|
58
|
+
# For example, 3-inputs AND function can be given as follows.
|
59
|
+
#
|
60
|
+
# tbl = {
|
61
|
+
# [false, :x, :x ] => false,
|
62
|
+
# [:x, false, :x ] => false,
|
63
|
+
# [:x, :x, false] => false,
|
64
|
+
# [true, true, true ] => true,
|
65
|
+
# }
|
66
|
+
#
|
67
|
+
# :x can be used for a value of <i>tbl</i> too.
|
68
|
+
# It means that the evaluated result of minimized boolean function is not specified:
|
69
|
+
# it may be evaluated to true or false.
|
70
|
+
#
|
71
|
+
# tbl = {
|
72
|
+
# [false, false] => false,
|
73
|
+
# [false, true ] => true,
|
74
|
+
# [true, false] => false,
|
75
|
+
# [true, true ] => :x
|
76
|
+
# }
|
77
|
+
#
|
78
|
+
# If <i>tbl</i> doesn't specify some combination of
|
79
|
+
# input variables, it assumes :x for such combination.
|
80
|
+
# The above function can be specified as follows.
|
81
|
+
#
|
82
|
+
# tbl = {
|
83
|
+
# [false, false] => false,
|
84
|
+
# [false, true ] => true,
|
85
|
+
# [true, false] => false,
|
86
|
+
# }
|
87
|
+
#
|
88
|
+
# QM.qm returns an array of arrays which represents
|
89
|
+
# the minimized boolean function.
|
90
|
+
#
|
91
|
+
# The minimized boolean function is a
|
92
|
+
# disjunction of terms such as "term1 | term2 | term3 | ...".
|
93
|
+
#
|
94
|
+
# The inner array represents a term.
|
95
|
+
# A term is a conjunction of input variables and negated input variables: "P & ~Q & ~R & S & ...".
|
96
|
+
#
|
97
|
+
def qm(tbl)
|
98
|
+
return [] if tbl.empty?
|
99
|
+
tbl = intern_tbl(tbl)
|
100
|
+
prime_implicants = find_prime_implicants(tbl)
|
101
|
+
essential_prime_implicants, chart = make_chart(prime_implicants, tbl)
|
102
|
+
additional_prime_implicants = search_minimal_combination(chart)
|
103
|
+
(essential_prime_implicants.keys + additional_prime_implicants).sort.reverse.map {|t|
|
104
|
+
extern_term(t)
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
# :stopdoc:
|
109
|
+
|
110
|
+
def has_intersection?(t1, t2)
|
111
|
+
[t1,t2].transpose.all? {|v1, v2|
|
112
|
+
v1 == -1 || v2 == -1 || v1 == v2
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
def implication?(t1, t2)
|
117
|
+
[t1,t2].transpose.all? {|v1, v2|
|
118
|
+
v2 == -1 || v1 == v2
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
INTERN = {
|
123
|
+
false => 0,
|
124
|
+
true => 1,
|
125
|
+
0 => 0,
|
126
|
+
1 => 1,
|
127
|
+
:x => -1,
|
128
|
+
}
|
129
|
+
def intern_tbl(tbl)
|
130
|
+
result = {}
|
131
|
+
num_inputs = nil
|
132
|
+
tbl.each {|inputs, output|
|
133
|
+
if !num_inputs
|
134
|
+
num_inputs = inputs.length
|
135
|
+
else
|
136
|
+
if inputs.length != num_inputs
|
137
|
+
raise ArgumentError, "different number of inputs"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
inputs2 = inputs.map {|v|
|
141
|
+
if !INTERN.has_key?(v)
|
142
|
+
raise ArgumentError, "unexpected input: #{v.inspect}"
|
143
|
+
end
|
144
|
+
INTERN[v]
|
145
|
+
}
|
146
|
+
if !INTERN.has_key?(output)
|
147
|
+
raise ArgumentError, "unexpected output: #{output.inspect}"
|
148
|
+
end
|
149
|
+
result[inputs2] = INTERN[output]
|
150
|
+
}
|
151
|
+
result_keys = result.keys
|
152
|
+
0.upto(result_keys.length-2) {|i|
|
153
|
+
ki = result_keys[i]
|
154
|
+
next if !result[ki]
|
155
|
+
(i+1).upto(result_keys.length-1) {|j|
|
156
|
+
kj = result_keys[j]
|
157
|
+
next if !result[kj]
|
158
|
+
if has_intersection?(ki, kj)
|
159
|
+
if result[ki] != result[kj]
|
160
|
+
raise ArgumentError, "inconsistent table"
|
161
|
+
end
|
162
|
+
if implication?(ki, kj)
|
163
|
+
result.delete ki
|
164
|
+
elsif implication?(kj, ki)
|
165
|
+
result.delete kj
|
166
|
+
end
|
167
|
+
end
|
168
|
+
}
|
169
|
+
}
|
170
|
+
not_specified = []
|
171
|
+
0.upto((1 << num_inputs)-1) {|n|
|
172
|
+
inputs = (0...num_inputs).map {|i| n[i] }
|
173
|
+
if result.all? {|inputs_pat, output| !has_intersection?(inputs, inputs_pat) }
|
174
|
+
not_specified << inputs
|
175
|
+
end
|
176
|
+
}
|
177
|
+
not_specified.each {|inputs|
|
178
|
+
result[inputs] = -1
|
179
|
+
}
|
180
|
+
result
|
181
|
+
end
|
182
|
+
|
183
|
+
EXTERN = {
|
184
|
+
0 => false,
|
185
|
+
1 => true,
|
186
|
+
-1 => :x
|
187
|
+
}
|
188
|
+
def extern_term(t)
|
189
|
+
t.map {|v| EXTERN[v] }
|
190
|
+
end
|
191
|
+
|
192
|
+
def combine(t1, t2)
|
193
|
+
num_diffs = 0
|
194
|
+
r = [t1,t2].transpose.map {|v1, v2|
|
195
|
+
if v1 == v2
|
196
|
+
v1
|
197
|
+
elsif v1 == 0 && v2 == 1
|
198
|
+
num_diffs += 1
|
199
|
+
return nil if 1 < num_diffs
|
200
|
+
-1
|
201
|
+
elsif v1 == 1 && v2 == 0
|
202
|
+
num_diffs += 1
|
203
|
+
return nil if 1 < num_diffs
|
204
|
+
-1
|
205
|
+
else
|
206
|
+
return nil
|
207
|
+
end
|
208
|
+
}
|
209
|
+
if num_diffs == 1
|
210
|
+
r
|
211
|
+
else
|
212
|
+
nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def combine2(t1, t2)
|
217
|
+
num_diffs = 0
|
218
|
+
r = [t1,t2].transpose.map {|v1, v2|
|
219
|
+
if v1 == v2
|
220
|
+
v1
|
221
|
+
elsif v1 == 0 && v2 == 1
|
222
|
+
num_diffs += 1
|
223
|
+
return nil if 1 < num_diffs
|
224
|
+
-1
|
225
|
+
elsif v1 == 1 && v2 == 0
|
226
|
+
num_diffs += 1
|
227
|
+
return nil if 1 < num_diffs
|
228
|
+
-1
|
229
|
+
elsif v2 == -1
|
230
|
+
v1
|
231
|
+
else
|
232
|
+
return nil
|
233
|
+
end
|
234
|
+
}
|
235
|
+
if num_diffs == 1
|
236
|
+
r
|
237
|
+
else
|
238
|
+
nil
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def find_prime_implicants(tbl)
|
243
|
+
num_inputs = nil
|
244
|
+
implicants_sets = []
|
245
|
+
tbl.each {|inputs, output|
|
246
|
+
num_inputs = inputs.length
|
247
|
+
next if output == 0
|
248
|
+
num_dontcares = inputs.grep(-1).length
|
249
|
+
num_ones = inputs.grep(1).length
|
250
|
+
implicants_sets[num_dontcares] ||= []
|
251
|
+
implicants_sets[num_dontcares][num_ones] ||= {}
|
252
|
+
implicants_sets[num_dontcares][num_ones][inputs] = true
|
253
|
+
}
|
254
|
+
combined = {}
|
255
|
+
0.upto(num_inputs-1) {|num_dontcares|
|
256
|
+
#isets = implicants_sets[num_dontcares]
|
257
|
+
isets = implicants_sets[num_dontcares].clone unless implicants_sets[num_dontcares].nil?
|
258
|
+
next if !isets
|
259
|
+
0.upto(isets.length-2) {|num_ones|
|
260
|
+
next if !isets[num_ones] || !isets[num_ones+1]
|
261
|
+
isets[num_ones].each_key {|t1|
|
262
|
+
isets[num_ones+1].each_key {|t2|
|
263
|
+
if t = combine(t1, t2)
|
264
|
+
combined[t1] = combined[t2] = true
|
265
|
+
implicants_sets[num_dontcares+1] ||= []
|
266
|
+
implicants_sets[num_dontcares+1][num_ones] ||= {}
|
267
|
+
implicants_sets[num_dontcares+1][num_ones][t] = true
|
268
|
+
end
|
269
|
+
}
|
270
|
+
}
|
271
|
+
}
|
272
|
+
isets.each {|ts1|
|
273
|
+
next if !ts1
|
274
|
+
ts1.each_key {|t1|
|
275
|
+
(num_dontcares+1).upto(num_inputs-1) {|num_dontcares2|
|
276
|
+
#isets2 = implicants_sets[num_dontcares2]
|
277
|
+
isets2 = MessagePack.unpack(implicants_sets[num_dontcares2].to_msgpack)
|
278
|
+
next if !isets2
|
279
|
+
isets2.each {|ts2|
|
280
|
+
next if !ts2
|
281
|
+
ts2.each_key {|t2|
|
282
|
+
if t = combine2(t1, t2)
|
283
|
+
combined[t1] = true
|
284
|
+
num_ones = t1.grep(1).length
|
285
|
+
implicants_sets[num_dontcares+1] ||= []
|
286
|
+
implicants_sets[num_dontcares+1][num_ones] ||= {}
|
287
|
+
implicants_sets[num_dontcares+1][num_ones][t] = true
|
288
|
+
end
|
289
|
+
}
|
290
|
+
}
|
291
|
+
}
|
292
|
+
}
|
293
|
+
}
|
294
|
+
}
|
295
|
+
prime_implicants = {}
|
296
|
+
implicants_sets.each {|isets|
|
297
|
+
next if !isets
|
298
|
+
isets.each {|ts|
|
299
|
+
next if !ts
|
300
|
+
ts.each_key {|t|
|
301
|
+
next if combined[t]
|
302
|
+
prime_implicants[t] = true
|
303
|
+
}
|
304
|
+
}
|
305
|
+
}
|
306
|
+
prime_implicants
|
307
|
+
end
|
308
|
+
|
309
|
+
def make_chart(prime_implicants, tbl)
|
310
|
+
essential_prime_implicants = {}
|
311
|
+
chart = []
|
312
|
+
tbl.each {|inputs, output|
|
313
|
+
next if output != 1
|
314
|
+
pi_list = []
|
315
|
+
prime_implicants.each_key {|pi|
|
316
|
+
if implication?(inputs, pi)
|
317
|
+
pi_list << pi
|
318
|
+
end
|
319
|
+
}
|
320
|
+
if pi_list.length == 1
|
321
|
+
essential_prime_implicants[pi_list[0]] = true
|
322
|
+
else
|
323
|
+
chart << pi_list
|
324
|
+
end
|
325
|
+
}
|
326
|
+
chart.reject! {|pi_list| pi_list.any? {|pi| essential_prime_implicants[pi] } }
|
327
|
+
return essential_prime_implicants, chart
|
328
|
+
end
|
329
|
+
|
330
|
+
def search_minimal_combination(chart)
|
331
|
+
return [] if chart.empty?
|
332
|
+
all_pi = {}
|
333
|
+
chart.each {|pi_list|
|
334
|
+
pi_list.each {|pi|
|
335
|
+
all_pi[pi] = true
|
336
|
+
}
|
337
|
+
}
|
338
|
+
q = {}
|
339
|
+
all_pi.each_key {|pi|
|
340
|
+
q[[pi]] = true
|
341
|
+
}
|
342
|
+
while true
|
343
|
+
next_q = {}
|
344
|
+
found = []
|
345
|
+
until q.empty?
|
346
|
+
pi_set0, _ = q.shift
|
347
|
+
pi_set = {}
|
348
|
+
pi_set0.each {|pi| pi_set[pi] = true }
|
349
|
+
if chart.all? {|pi_list| pi_list.any? {|pi| pi_set[pi] }}
|
350
|
+
found << pi_set0
|
351
|
+
end
|
352
|
+
all_pi.each_key {|pi|
|
353
|
+
next if pi_set[pi]
|
354
|
+
next_q[(pi_set0 + [pi]).sort] = true
|
355
|
+
}
|
356
|
+
end
|
357
|
+
if !found.empty?
|
358
|
+
return found.sort.first
|
359
|
+
end
|
360
|
+
q = next_q
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
# :startdoc:
|
365
|
+
end
|
366
|
+
end
|