truthtable 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.
- 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
@@ -0,0 +1,40 @@
|
|
1
|
+
class TruthTableObject
|
2
|
+
def initialize
|
3
|
+
@checked = {}
|
4
|
+
@plan = {}
|
5
|
+
@order = []
|
6
|
+
@queue = []
|
7
|
+
end
|
8
|
+
attr_reader :plan, :order
|
9
|
+
|
10
|
+
def next_plan
|
11
|
+
@log = {}
|
12
|
+
@plan, @order = @queue.shift
|
13
|
+
@plan
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](index)
|
17
|
+
s = "v[#{index}]"
|
18
|
+
if @plan.has_key?(s)
|
19
|
+
v = @plan[s]
|
20
|
+
else
|
21
|
+
fplan = @plan.dup
|
22
|
+
fplan[s] = false
|
23
|
+
fkey = fplan.keys.sort.map {|k| "#{k}=#{fplan[k]}" }.join(' ')
|
24
|
+
@order += [s]
|
25
|
+
@plan = fplan
|
26
|
+
v = false
|
27
|
+
if !@checked[fkey]
|
28
|
+
tplan = @plan.dup
|
29
|
+
tplan[s] = true
|
30
|
+
tkey = tplan.keys.sort.map {|k| "#{k}=#{tplan[k]}" }.join(' ')
|
31
|
+
torder = @order.dup
|
32
|
+
torder[-1] = s
|
33
|
+
@queue.unshift [tplan, torder]
|
34
|
+
@checked[tkey] = true
|
35
|
+
@checked[fkey] = true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
v
|
39
|
+
end
|
40
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'truthtable'
|
2
|
+
|
3
|
+
class String
|
4
|
+
def to_bool
|
5
|
+
return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
|
6
|
+
return false if self == false || self.nil? || self.empty? || self =~ (/(false|f|no|n|0)$/i)
|
7
|
+
raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
QM = TruthTable::QM
|
4
|
+
|
5
|
+
describe QM do
|
6
|
+
|
7
|
+
describe "test intern_tbl" do
|
8
|
+
it "can raise ArgumentError" do
|
9
|
+
lambda{ QM.intern_tbl({[0]=>0, []=>1}) }.should raise_error(ArgumentError)
|
10
|
+
lambda{ QM.intern_tbl({[:y]=>0}) }.should raise_error(ArgumentError)
|
11
|
+
lambda{ QM.intern_tbl({[0]=>:y}) }.should raise_error(ArgumentError)
|
12
|
+
lambda{ QM.intern_tbl({[0]=>0, [:x]=>1}) }.should raise_error(ArgumentError)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "can return right values" do
|
16
|
+
QM.intern_tbl({[0]=>0, [:x]=>0}).should == {[-1]=>0}
|
17
|
+
QM.intern_tbl({[0]=>0}).should == {[0]=>0, [1]=>-1}
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "test qm" do
|
23
|
+
it "can be empty" do
|
24
|
+
QM.qm({}).should == []
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can pass sample 1" do
|
28
|
+
tbl = {
|
29
|
+
[0,0,0,0]=>0,
|
30
|
+
[0,0,0,1]=>0,
|
31
|
+
[0,0,1,0]=>0,
|
32
|
+
[0,0,1,1]=>0,
|
33
|
+
[0,1,0,0]=>1,
|
34
|
+
[0,1,0,1]=>0,
|
35
|
+
[0,1,1,0]=>0,
|
36
|
+
[0,1,1,1]=>0,
|
37
|
+
[1,0,0,0]=>1,
|
38
|
+
[1,0,0,1]=>:x,
|
39
|
+
[1,0,1,0]=>1,
|
40
|
+
[1,0,1,1]=>1,
|
41
|
+
[1,1,0,0]=>1,
|
42
|
+
[1,1,0,1]=>0,
|
43
|
+
[1,1,1,0]=>:x,
|
44
|
+
[1,1,1,1]=>1,
|
45
|
+
}
|
46
|
+
QM.qm(tbl).should == [[true, :x, true, :x ],
|
47
|
+
[true, :x, :x, false],
|
48
|
+
[:x, true, false, false]]
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can pass implication" do
|
53
|
+
tbl = {
|
54
|
+
[false,false]=>true,
|
55
|
+
[false,true ]=>true,
|
56
|
+
[true, false]=>false,
|
57
|
+
[true, true ]=>true,
|
58
|
+
}
|
59
|
+
QM.qm(tbl).should == [[false, :x], [:x, true]]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "can shortcut or" do
|
63
|
+
tbl = {
|
64
|
+
[0, 0]=>0,
|
65
|
+
[1, :x]=>1,
|
66
|
+
[0, 1]=>1
|
67
|
+
}
|
68
|
+
QM.qm(tbl).should == [[true, :x], [:x, true]]
|
69
|
+
end
|
70
|
+
|
71
|
+
it "can do 3 ands" do
|
72
|
+
tbl = {
|
73
|
+
[false,:x, :x ]=>false,
|
74
|
+
[:x, false,:x ]=>false,
|
75
|
+
[:x, :x, false]=>false,
|
76
|
+
[true, true, true ]=>true,
|
77
|
+
}
|
78
|
+
QM.qm(tbl).should == [[true, true, true]]
|
79
|
+
end
|
80
|
+
|
81
|
+
it "can do 3 ors" do
|
82
|
+
tbl = {
|
83
|
+
[false,false,false]=>false,
|
84
|
+
[true, :x, :x ]=>true,
|
85
|
+
[:x, true, :x ]=>true,
|
86
|
+
[:x, :x, true ]=>true,
|
87
|
+
}
|
88
|
+
QM.qm(tbl).should == [[true, :x, :x], [:x, true, :x], [:x, :x, true]]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "can do majority" do
|
92
|
+
tbl = {
|
93
|
+
[0,0,0]=>0,
|
94
|
+
[0,0,1]=>0,
|
95
|
+
[0,1,0]=>0,
|
96
|
+
[0,1,1]=>1,
|
97
|
+
[1,0,0]=>0,
|
98
|
+
[1,0,1]=>1,
|
99
|
+
[1,1,0]=>1,
|
100
|
+
[1,1,1]=>1,
|
101
|
+
}
|
102
|
+
QM.qm(tbl).should == [[true, true, :x], [true, :x, true], [:x, true, true]]
|
103
|
+
end
|
104
|
+
|
105
|
+
it "can do 4 bit fib predicate" do
|
106
|
+
tbl = {
|
107
|
+
[0,0,0,0]=>0,
|
108
|
+
[0,0,0,1]=>1, # 1
|
109
|
+
[0,0,1,0]=>1, # 2
|
110
|
+
[0,0,1,1]=>1, # 3
|
111
|
+
[0,1,0,0]=>0,
|
112
|
+
[0,1,0,1]=>1, # 5
|
113
|
+
[0,1,1,0]=>0,
|
114
|
+
[0,1,1,1]=>0,
|
115
|
+
[1,0,0,0]=>1, # 8
|
116
|
+
[1,0,0,1]=>0,
|
117
|
+
[1,0,1,0]=>0,
|
118
|
+
[1,0,1,1]=>0,
|
119
|
+
[1,1,0,0]=>0,
|
120
|
+
[1,1,0,1]=>1, # 13
|
121
|
+
[1,1,1,0]=>0,
|
122
|
+
[1,1,1,1]=>0,
|
123
|
+
}
|
124
|
+
QM.qm(tbl).should == [[true, false, false, false],
|
125
|
+
[false, false, true, :x],
|
126
|
+
[false, :x, false, true],
|
127
|
+
[:x, true, false, true]]
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
describe Truthtable do
|
4
|
+
describe "test tautology" do
|
5
|
+
|
6
|
+
it "can dnf true" do
|
7
|
+
TruthTable.new {|v| true }.dnf.to_bool.should be_true
|
8
|
+
end
|
9
|
+
|
10
|
+
it "can cnf true" do
|
11
|
+
TruthTable.new {|v| true }.cnf.to_bool.should be_true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can formula true" do
|
15
|
+
TruthTable.new {|v| true }.formula.to_bool.should be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can dnf values" do
|
19
|
+
TruthTable.new {|v| v[0] | !v[0] }.dnf.should == "!v[0] | v[0]"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can cnf values" do
|
23
|
+
TruthTable.new {|v| v[0] | !v[0] }.cnf.to_bool.should be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "can formula values" do
|
27
|
+
TruthTable.new {|v| v[0] | !v[0] }.formula.to_bool.should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "test contradiction" do
|
33
|
+
|
34
|
+
it "can dnf false" do
|
35
|
+
TruthTable.new {|v| false }.dnf.to_bool.should be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
it "can cnf false" do
|
39
|
+
TruthTable.new {|v| false }.cnf.to_bool.should be_false
|
40
|
+
end
|
41
|
+
|
42
|
+
it "can formula false" do
|
43
|
+
TruthTable.new {|v| false }.formula.to_bool.should be_false
|
44
|
+
end
|
45
|
+
|
46
|
+
it "can dnf values" do
|
47
|
+
TruthTable.new {|v| v[0] & !v[0] }.dnf.to_bool.should be_false
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can cnf values" do
|
51
|
+
TruthTable.new {|v| v[0] & !v[0] }.cnf.should == "v[0] & !v[0]"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "can formula values" do
|
55
|
+
TruthTable.new {|v| v[0] & !v[0] }.formula.to_bool.should be_false
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
end
|
data/truthtable.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/truthtable/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Tanaka Akira"]
|
6
|
+
gem.email = ["akr@fsij.org"]
|
7
|
+
gem.description = "The truthtable library generates a truth table from a logical formula written in Ruby.
|
8
|
+
The truth table can be converted to a logical formula.
|
9
|
+
DNF, CNF and Quine-McCluskey supported."
|
10
|
+
gem.summary = "Generate truthtable from logical formula"
|
11
|
+
gem.homepage = "http://rubygems.org/gems/truthtable"
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split($\)
|
14
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.name = "truthtable"
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.version = Truthtable::VERSION
|
19
|
+
|
20
|
+
gem.add_development_dependency "rspec"
|
21
|
+
gem.add_development_dependency "gem-release"
|
22
|
+
gem.add_dependency "msgpack"
|
23
|
+
end
|
data/truthtable.rb
ADDED
@@ -0,0 +1,345 @@
|
|
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
|
+
require 'truthtable/qm'
|
28
|
+
|
29
|
+
# = truth table and formula generator from Ruby block
|
30
|
+
#
|
31
|
+
# The truthtable library generates a truth table from
|
32
|
+
# a logical formula written in Ruby.
|
33
|
+
# The truth table can be converted to a logical formula.
|
34
|
+
#
|
35
|
+
# == Author
|
36
|
+
#
|
37
|
+
# Tanaka Akira <akr@fsij.org>
|
38
|
+
#
|
39
|
+
# == Feature
|
40
|
+
#
|
41
|
+
# * generate a truth table from a given block which contains logical formula written in Ruby.
|
42
|
+
# * generate a formula from the table:
|
43
|
+
# * minimal one (obtained by Quine-McCluskey algorithm)
|
44
|
+
# * disjunctive normal form
|
45
|
+
# * conjunctive normal form
|
46
|
+
#
|
47
|
+
# == Usage
|
48
|
+
#
|
49
|
+
# * require
|
50
|
+
#
|
51
|
+
# require 'truthtable'
|
52
|
+
#
|
53
|
+
# * puts, p and pp shows truth table
|
54
|
+
#
|
55
|
+
# puts TruthTable.new {|v| v[0] & v[1] }
|
56
|
+
# #=>
|
57
|
+
# v[0] v[1] |
|
58
|
+
# ----------+--
|
59
|
+
# f f | f
|
60
|
+
# f t | f
|
61
|
+
# t f | f
|
62
|
+
# t t | t
|
63
|
+
#
|
64
|
+
# p TruthTable.new {|v| v[0] & v[1] }
|
65
|
+
# #=> #<TruthTable: !v[0]&!v[1]=>false !v[0]&v[1]=>false v[0]&!v[1]=>false v[0]&v[1]=>true>
|
66
|
+
#
|
67
|
+
# require 'pp'
|
68
|
+
# pp TruthTable.new {|v| v[0] & v[1] }
|
69
|
+
# #=>
|
70
|
+
# #<TruthTable:
|
71
|
+
# !v[0]&!v[1]=>false
|
72
|
+
# !v[0]& v[1]=>false
|
73
|
+
# v[0]&!v[1]=>false
|
74
|
+
# v[0]& v[1]=>true>
|
75
|
+
#
|
76
|
+
# * formula generation
|
77
|
+
#
|
78
|
+
# p TruthTable.new {|v| v[0] }.formula #=>"v[0]"
|
79
|
+
# p TruthTable.new {|v| !v[0] }.formula #=> "!v[0]"
|
80
|
+
# p TruthTable.new {|v| true ^ v[0] }.formula #=> "!v[0]"
|
81
|
+
# p TruthTable.new {|v| v[0] & v[1] }.formula #=> "v[0]&v[1]"
|
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] | v[0]&!v[1]"
|
84
|
+
# p TruthTable.new {|v| v[0] == v[1] }.formula #=> "!v[0]&!v[1] | v[0]&v[1]"
|
85
|
+
#
|
86
|
+
# * shortcuts, && and ||, are also usable but converted to & and |
|
87
|
+
#
|
88
|
+
# p TruthTable.new {|v| v[0] && v[1] }.formula #=> "v[0]&v[1]"
|
89
|
+
# p TruthTable.new {|v| v[0] || v[1] }.formula #=> "v[0] | v[1]"
|
90
|
+
#
|
91
|
+
# * actually any expression (without side effect)
|
92
|
+
#
|
93
|
+
# p TruthTable.new {|v| v[0] ? !v[1] : v[1] }.formula #=> "!v[0]&v[1] | v[0]&!v[1]"
|
94
|
+
#
|
95
|
+
# * any number of inputs
|
96
|
+
#
|
97
|
+
# p TruthTable.new {|v| [v[0], v[1], v[2], v[3]].grep(true).length <= 3 }.formula
|
98
|
+
# #=> "!v[0] | !v[1] | !v[2] | !v[3]"
|
99
|
+
#
|
100
|
+
class TruthTable
|
101
|
+
# :stopdoc:
|
102
|
+
class TruthTableObject
|
103
|
+
def initialize
|
104
|
+
@checked = {}
|
105
|
+
@plan = {}
|
106
|
+
@order = []
|
107
|
+
@queue = []
|
108
|
+
end
|
109
|
+
attr_reader :plan, :order
|
110
|
+
|
111
|
+
def next_plan
|
112
|
+
@log = {}
|
113
|
+
@plan, @order = @queue.shift
|
114
|
+
@plan
|
115
|
+
end
|
116
|
+
|
117
|
+
def [](index)
|
118
|
+
s = "v[#{index}]"
|
119
|
+
if @plan.has_key?(s)
|
120
|
+
v = @plan[s]
|
121
|
+
else
|
122
|
+
fplan = @plan.dup
|
123
|
+
fplan[s] = false
|
124
|
+
fkey = fplan.keys.sort.map {|k| "#{k}=#{fplan[k]}" }.join(' ')
|
125
|
+
@order += [s]
|
126
|
+
@plan = fplan
|
127
|
+
v = false
|
128
|
+
if !@checked[fkey]
|
129
|
+
tplan = @plan.dup
|
130
|
+
tplan[s] = true
|
131
|
+
tkey = tplan.keys.sort.map {|k| "#{k}=#{tplan[k]}" }.join(' ')
|
132
|
+
torder = @order.dup
|
133
|
+
torder[-1] = s
|
134
|
+
@queue.unshift [tplan, torder]
|
135
|
+
@checked[tkey] = true
|
136
|
+
@checked[fkey] = true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
v
|
140
|
+
end
|
141
|
+
end
|
142
|
+
# :startdoc:
|
143
|
+
|
144
|
+
def self.test(&b)
|
145
|
+
r = []
|
146
|
+
o = TruthTableObject.new
|
147
|
+
begin
|
148
|
+
result = !!b.call(o)
|
149
|
+
inputs = o.plan
|
150
|
+
order = o.order
|
151
|
+
r << [inputs, result, order]
|
152
|
+
end while o.next_plan
|
153
|
+
r
|
154
|
+
end
|
155
|
+
|
156
|
+
def initialize(&b)
|
157
|
+
table = TruthTable.test(&b)
|
158
|
+
@table = table
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_s
|
162
|
+
r = ''
|
163
|
+
names = sort_names(all_names.keys)
|
164
|
+
format = ''
|
165
|
+
sep = ''
|
166
|
+
names.each {|name|
|
167
|
+
format << "%-#{name.length}s "
|
168
|
+
sep << '-' * (name.length+1)
|
169
|
+
}
|
170
|
+
format << "| %s\n"
|
171
|
+
sep << "+--\n"
|
172
|
+
r << sprintf(format, *(names + ['']))
|
173
|
+
r << sep
|
174
|
+
@table.each {|inputs, output, order|
|
175
|
+
h = {}
|
176
|
+
each_input(inputs) {|name, input|
|
177
|
+
h[name] = input
|
178
|
+
}
|
179
|
+
args = []
|
180
|
+
names.each {|name|
|
181
|
+
if h.has_key? name
|
182
|
+
args << (h[name] ? 't' : 'f').center(name.length)
|
183
|
+
else
|
184
|
+
args << '?'.center(name.length)
|
185
|
+
end
|
186
|
+
}
|
187
|
+
args << (output ? 't' : 'f')
|
188
|
+
r << sprintf(format, *args)
|
189
|
+
}
|
190
|
+
r
|
191
|
+
end
|
192
|
+
|
193
|
+
# :stopdoc:
|
194
|
+
def inspect
|
195
|
+
result = "#<#{self.class}:"
|
196
|
+
@table.each {|inputs, output, order|
|
197
|
+
term = []
|
198
|
+
each_input(inputs) {|name, input|
|
199
|
+
if input
|
200
|
+
term << name
|
201
|
+
else
|
202
|
+
term << "!#{name}"
|
203
|
+
end
|
204
|
+
}
|
205
|
+
result << " #{term.join('&')}=>#{output}"
|
206
|
+
}
|
207
|
+
result << ">"
|
208
|
+
result
|
209
|
+
end
|
210
|
+
|
211
|
+
def pretty_print(q)
|
212
|
+
q.object_group(self) {
|
213
|
+
q.text ':'
|
214
|
+
q.breakable
|
215
|
+
q.seplist(@table, lambda { q.breakable('; ') }) {|inputs, output, order|
|
216
|
+
term = []
|
217
|
+
each_input(inputs) {|name, input|
|
218
|
+
if input
|
219
|
+
term << " #{name}"
|
220
|
+
else
|
221
|
+
term << "!#{name}"
|
222
|
+
end
|
223
|
+
}
|
224
|
+
q.text "#{term.join('&')}=>#{output}"
|
225
|
+
}
|
226
|
+
}
|
227
|
+
end
|
228
|
+
|
229
|
+
def all_names
|
230
|
+
return @all_names if defined? @all_names
|
231
|
+
@all_names = {}
|
232
|
+
@table.each {|inputs, output, order|
|
233
|
+
order.each {|name|
|
234
|
+
if !@all_names.has_key?(name)
|
235
|
+
@all_names[name] = @all_names.size
|
236
|
+
end
|
237
|
+
}
|
238
|
+
}
|
239
|
+
@all_names
|
240
|
+
end
|
241
|
+
|
242
|
+
def sort_names(names)
|
243
|
+
total_order = all_names
|
244
|
+
names.sort_by {|n| total_order[n] }
|
245
|
+
end
|
246
|
+
|
247
|
+
def each_input(inputs)
|
248
|
+
sort_names(inputs.keys).each {|name|
|
249
|
+
yield name, inputs[name]
|
250
|
+
}
|
251
|
+
end
|
252
|
+
# :startdoc:
|
253
|
+
|
254
|
+
# obtains a formula in disjunctive normal form.
|
255
|
+
def dnf
|
256
|
+
r = []
|
257
|
+
@table.each {|inputs, output|
|
258
|
+
return output.to_s if inputs.empty?
|
259
|
+
next if !output
|
260
|
+
term = []
|
261
|
+
each_input(inputs) {|name, input|
|
262
|
+
if input
|
263
|
+
term << name
|
264
|
+
else
|
265
|
+
term << "!#{name}"
|
266
|
+
end
|
267
|
+
}
|
268
|
+
r << term.join('&')
|
269
|
+
}
|
270
|
+
return "false" if r.empty?
|
271
|
+
r.join(' | ')
|
272
|
+
end
|
273
|
+
|
274
|
+
# obtains a formula in conjunctive normal form.
|
275
|
+
def cnf
|
276
|
+
r = []
|
277
|
+
@table.each {|inputs, output|
|
278
|
+
return output.to_s if inputs.empty?
|
279
|
+
next if output
|
280
|
+
term = []
|
281
|
+
each_input(inputs) {|name, input|
|
282
|
+
if input
|
283
|
+
term << "!#{name}"
|
284
|
+
else
|
285
|
+
term << name
|
286
|
+
end
|
287
|
+
}
|
288
|
+
if term.length == 1
|
289
|
+
r << term.join('|')
|
290
|
+
else
|
291
|
+
r << "(#{term.join('|')})"
|
292
|
+
end
|
293
|
+
}
|
294
|
+
return "true" if r.empty?
|
295
|
+
r.join(' & ')
|
296
|
+
end
|
297
|
+
|
298
|
+
# obtains a minimal formula using Quine-McCluskey algorithm.
|
299
|
+
def formula
|
300
|
+
input_names = all_names
|
301
|
+
input_names_ary = sort_names(input_names.keys)
|
302
|
+
tbl = {}
|
303
|
+
@table.each {|inputs, output|
|
304
|
+
return output.to_s if inputs.empty?
|
305
|
+
inputs2 = [:x] * input_names.length
|
306
|
+
inputs.each {|name, input|
|
307
|
+
inputs2[input_names[name]] = input ? 1 : 0
|
308
|
+
}
|
309
|
+
tbl[inputs2] = output ? 1 : 0
|
310
|
+
}
|
311
|
+
qm = QM.qm(tbl)
|
312
|
+
r = []
|
313
|
+
qm.each {|term|
|
314
|
+
t = []
|
315
|
+
num_dontcare = 0
|
316
|
+
term.each_with_index {|v, i|
|
317
|
+
if v == false
|
318
|
+
t << ("!" + input_names_ary[i])
|
319
|
+
elsif v == true
|
320
|
+
t << input_names_ary[i]
|
321
|
+
else # :x
|
322
|
+
num_dontcare += 1
|
323
|
+
end
|
324
|
+
}
|
325
|
+
if num_dontcare == term.length
|
326
|
+
r << 'true'
|
327
|
+
else
|
328
|
+
r << t.join('&')
|
329
|
+
end
|
330
|
+
}
|
331
|
+
return "false" if r.empty?
|
332
|
+
r.join(' | ')
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
if __FILE__ == $0
|
337
|
+
p TruthTable.new {|v| v[0] & v[1] }.formula
|
338
|
+
p TruthTable.new {|v| v[0] && v[1] }.formula
|
339
|
+
p TruthTable.new {|v| v[0] | v[1] }.formula
|
340
|
+
p TruthTable.new {|v| v[0] || v[1] }.formula
|
341
|
+
p TruthTable.new {|v| v[0] ^ !v[1] }.formula
|
342
|
+
p TruthTable.new {|v| v[0] == v[1] }.formula
|
343
|
+
p TruthTable.new {|v| v[0] == v[1] && v[1] != v[2] || v[3] == v[1] }.formula
|
344
|
+
end
|
345
|
+
|