logic_tools 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 68b786dcea5db0011ebf3b7c4c8452ce7b1fd689
4
- data.tar.gz: 8685d8a03576b28ba0601ca57a3a3dc22d3b5b9f
3
+ metadata.gz: a2329d237d7038309287c2907d60d5a383760642
4
+ data.tar.gz: d24b42399dcc9f835856e7ede9a539414f703dc1
5
5
  SHA512:
6
- metadata.gz: e77ca46d2725b382528d08d9b180ea1864c3247c5c9114f4215be309333262dce348ba0d00b982034e5b26d9b1db96406991627c361bf05115c0815b0c049c5a
7
- data.tar.gz: db8fa9b7b684f0597a9c50097c53893cfa8904cededad976f871c8b22d08dd6a197cd2fbca1a8b1a0ff53d04edd74ea6d0a96f72cbe8a928a4efc2762d523075
6
+ metadata.gz: 34433d985130bf65444113bc969347925f65d427b8400613f1daa0a2598a62158467abcd87c9c4fc67143faf2698dc5dbdb7c35d385222e906ec8ceeb0780c0b
7
+ data.tar.gz: ac8e94c8d5ebd78c7cffa01c14dbd7fa626cd88a4af95be1a2cfebcbd26fa9ff3a8b058fb940495a177ed845b8348d72e5c474e00252a4581c5a56bf6600595c
data/README.md CHANGED
@@ -2,14 +2,12 @@
2
2
 
3
3
  LogicTools is a set of command-line tools for processing logic expressions.
4
4
  The tools include:
5
- * simplify\_qm: for simplifying a logic expression.
6
- * std\_conj: for computing the conjunctive normal form of a logic expression.
7
- * std\_dij: for computing the disjunctive normal form a of logic expression.
8
- * truth\_tbl: for generating the truth table of a logic expression.
9
5
 
10
- ## Disclaimer
6
+ * __simplify\_qm__: for simplifying a logic expression.
7
+ * __std\_conj__: for computing the conjunctive normal form of a logic expression.
8
+ * __std\_dij__: for computing the disjunctive normal form a of logic expression.
9
+ * __truth\_tbl__: for generating the truth table of a logic expression.
11
10
 
12
- There is still a nasty bug in the logic simplifying tool (simplify_qm) which makes it give a wrong result. Fortunately, it seems to happen in some rare cases only.
13
11
 
14
12
  ## Installation
15
13
 
@@ -29,10 +27,16 @@ Or install it yourself as:
29
27
 
30
28
  ## Usage
31
29
 
32
- LogicTools is a command line-based set of tool. Each tool is used as follows:
33
- tool\_name "logical expression"
30
+ LogicTools is a command line-based set of tool. Each tool is used as follows for processing a single expression:
31
+
32
+ $ tool_name "single logical expression"
33
+
34
+ Multiple expressions stored into a file can also be processed as follows:
35
+
36
+ $ tool_name -f <filename>
34
37
 
35
38
  The logical expression is an expression where:
39
+
36
40
  * a logical variable is represented by a single alphabetical character (hence there is in total 56 possible variables);
37
41
  * a logical OR is represented by a '+' character;
38
42
  * a logical AND is represented by a '.' character (but it can be omitted);
@@ -40,32 +44,41 @@ The logical expression is an expression where:
40
44
  * opening and closing parenthesis are represented by, respectively, '(' and ')' characters.
41
45
 
42
46
  Important notice:
47
+
43
48
  * the priority among logical operators is as follows: NOT > AND > OR
44
49
  * logical expressions must be put between quotes (the '"' character).
45
50
 
46
51
  For instance the followings are valid logical expression using the a,b and c variables:
47
- "ab+ac"
48
- "a.b.c"
49
- "a+b+!c"
50
- "a~(b+~c)"
52
+
53
+ "ab+ac"
54
+ "a.b.c"
55
+ "a+b+!c"
56
+ "a~(b+~c)"
51
57
 
52
58
  Finally, here are a few examples of LogicTool usage:
59
+
53
60
  * simplifying the expression a+ab:
54
- simplify\_qm "a+ab"
55
- -> a
61
+
62
+ $ simplify_qm "a+ab"
63
+ -> a
56
64
  * compute the conjunctive normal form of the expression a+ab:
57
- std\_conj "a+ab"
58
- -> ab+a~b
65
+
66
+ $ std_conj "a+ab"
67
+ -> ab+a~b
68
+
59
69
  * compute the disjunctive normal form of the expression a+ab:
60
- std\_dij "a+ab"
61
- -> (a+b)(a+~b)
70
+
71
+ $ std_dij "a+ab"
72
+ -> (a+b)(a+~b)
73
+
62
74
  * compute the truth table of the expression a+ab:
63
- truth\_tbl "a+ab"
64
- -> a b
65
- 0 0 0
66
- 0 1 0
67
- 1 0 1
68
- 1 1 1
75
+
76
+ $ truth_tbl "a+ab"
77
+ -> a b
78
+ 0 0 0
79
+ 0 1 0
80
+ 1 0 1
81
+ 1 1 1
69
82
 
70
83
  ## Development
71
84
 
@@ -84,12 +97,6 @@ The gem is available as open source under the terms of the [MIT License](http://
84
97
 
85
98
 
86
99
  ## To do
87
- ### High priority
88
- * Fix the bug that makes simplify\_qm produce sometimes a wrong result.
89
- * Downgrade the user interface: actually the interface can do much more than what is described in the documentation, but this is not really useful, so it should better to remove these features.
90
-
91
- ### Low priority
92
-
93
100
  * Add a more efficient alternative command to simplify_qm, based on the Espresso algorithm for example.
94
101
 
95
102
 
data/lib/logic_tools.rb CHANGED
@@ -1,5 +1,12 @@
1
1
  require "logic_tools/version"
2
2
 
3
+ ######################################################################
4
+ #
5
+ # == LogicTools
6
+ # This module includes all the classes and methods used
7
+ # by the *logic_tools* set of tools.
8
+ #
9
+ ######################################################################
10
+
3
11
  module LogicTools
4
- # Your code goes here...
5
12
  end
@@ -0,0 +1,43 @@
1
+
2
+ module LogicTools
3
+
4
+
5
+ #########################################################################
6
+ # Common command line interface for all Logic Tools
7
+ #########################################################################
8
+
9
+
10
+ ## Displays a short help message.
11
+ def help_short
12
+ name = File.basename($0)
13
+ puts "Usage: #{name} <\"logic expression\">"
14
+ puts " or: #{name} -f <file name>"
15
+ end
16
+
17
+ ## Gets an iterator over the input expression
18
+ # (obtained either through options or a through file).
19
+ def each_input
20
+ # No block? Return an enumerator
21
+ return enum_for(:each_input) unless block_given?
22
+ # A block? Interrate with it
23
+ # Process the arguments
24
+ if ($*.empty?) then
25
+ # No arguments, shows the help and end.
26
+ help_short
27
+ exit(1)
28
+ end
29
+ if $*[0] == "-f" or $*[0] == "--file" then
30
+ # Work from a file, iterate on each line
31
+ exprs = File.read($*[1])
32
+ exprs.gsub!(/\r\n?/, "\n")
33
+ exprs.each_line do |line|
34
+ yield(line)
35
+ end
36
+ elsif $*[0] == "-h" or $*[0] == "--help" then
37
+ help_short
38
+ else
39
+ # Work directly on the arguments as an expression
40
+ yield($*.join)
41
+ end
42
+ end
43
+ end
@@ -1,6 +1,6 @@
1
- ##########################
2
- # Truth table generator #
3
- ##########################
1
+ ########################################################
2
+ # Parse a string and convert to a logic tree #
3
+ ########################################################
4
4
 
5
5
  # For parsing the inputs
6
6
  require 'parslet'
@@ -11,24 +11,25 @@ require "logic_tools/logictree.rb"
11
11
 
12
12
 
13
13
 
14
- ########################################################
15
- # Parse a string and convert to a logic tree
16
14
 
17
15
 
18
16
  module LogicTools
19
17
 
20
- ## The parser of logic expressions
18
+ ## The parser of logic expressions.
21
19
  class Parser < Parslet::Parser
22
20
 
23
21
  # True / false
24
22
  rule(:tru) { str("1") }
25
23
  rule(:fal) { str("0") }
26
24
  # Variable
27
- rule(:var) { match('[A-Za-uw-z]') }
25
+ # rule(:var) { match('[A-Za-uw-z]') }
26
+ rule(:var) { match('[A-Za-z]') }
28
27
  # And operator
29
- rule(:andop) { str("&&") | match('[&\.\*^]') }
28
+ # rule(:andop) { str("&&") | match('[&\.\*^]') }
29
+ rule(:andop) { str(".") }
30
30
  # Or operator
31
- rule(:orop) { match('[+|v]') }
31
+ # rule(:orop) { match('[+|v]') }
32
+ rule(:orop) { str("+") }
32
33
  # Not operator
33
34
  rule(:notop) { match('[~!]') }
34
35
 
@@ -43,7 +44,7 @@ module LogicTools
43
44
  end
44
45
 
45
46
 
46
- ## The logic tree generator from the syntax tree
47
+ ## The logic tree generator from the syntax tree.
47
48
  class Transform < Parslet::Transform
48
49
 
49
50
  # Terminal rules
@@ -77,10 +78,8 @@ module LogicTools
77
78
  end
78
79
 
79
80
 
80
- ## The parser/gerator main fuction: converts a string to a logic tree
81
- # Param:
82
- # +str+:: the string to parse
83
- # Return: the resulting logic tree
81
+ ## The parser/gerator main fuction: converts the text in +str+ to a
82
+ # logic tree.
84
83
  def string2logic(str)
85
84
  # Remove the spaces
86
85
  str = str.gsub(/\s+/, "")
@@ -10,8 +10,8 @@ require 'set'
10
10
  module LogicTools
11
11
 
12
12
 
13
- ## Converts an array of variable to bit vector according to their value
14
- # @param vars the array of variables to convert
13
+ ## Converts the array of variables +var+ to a bit vector according to
14
+ # their values
15
15
  def vars2int(vars)
16
16
  res = ""
17
17
  vars.each_with_index do |var,i|
@@ -24,24 +24,33 @@ module LogicTools
24
24
 
25
25
 
26
26
 
27
- # Class describing an implicant
27
+ ##
28
+ # Represents a logic implicant
28
29
  class Implicant
29
30
  include Enumerable
30
- attr_reader :mask, # The positions of the x
31
- :bits, # The bit vector of the implicant
32
- :count, # The number of 1 of the implicant
33
- :covers, # The bit values covered by the implicant
34
- :prime # Tell if the implicant is prime or not
35
- attr_accessor :var # The variable associated with the implicant
36
- # Do not interfer at all with the class, so
37
- # public and fully accessible
31
+
32
+ ## The positions of the *X* in the implicant.
33
+ attr_reader :mask
34
+ ## The bit vector of the implicant.
35
+ attr_reader :bits
36
+ ## The number of *1* of the implicant.
37
+ attr_reader :count
38
+ ## The bit values covered by the implicant.
39
+ attr_reader :covers
40
+ ## Tell if the implicant is prime or not.
41
+ attr_reader :prime
42
+ ## The variable associated with the implicant
43
+ # Do not interfer at all with the class, so
44
+ # public and fully accessible
45
+ attr_accessor :var
46
+
38
47
  protected
39
48
  attr_writer :covers
40
49
  public
41
50
 
42
- ## Create an implicant
43
- # @param base if Implicant: copy constructor <br>
44
- # otherwise: creat a new implicant from a bit string
51
+ ## Creates a new implicant from +base+.
52
+ #
53
+ # Argument +base+ can be either another implicant or a bit string.
45
54
  def initialize(base)
46
55
  if base.is_a?(Implicant)
47
56
  @covers = base.covers.dup
@@ -51,7 +60,7 @@ module LogicTools
51
60
  else
52
61
  @bits = base.to_s
53
62
  unless @bits.match(/^[01]*$/)
54
- raise "Invalid bit string for an initial implicant: " + @bits
63
+ raise "Invalid bit string for an initial implicant: "+ @bits
55
64
  end
56
65
  @mask = " " * @bits.size
57
66
  @count = @bits.count("1")
@@ -60,50 +69,44 @@ module LogicTools
60
69
  @prime = true # By default assumed prime
61
70
  end
62
71
 
63
- ## Convert to a string
64
- def to_s
72
+ ## Converts to a string
73
+ def to_s # :nodoc:
65
74
  @bits
66
75
  end
67
76
 
68
- ## inspect
69
- def inspect
77
+ def inspect #:nodoc:
70
78
  @bits.dup
71
79
  end
72
80
 
73
- ## Set the prime status
74
- # @param st the new status (true or false)
81
+ ## Sets the prime status to +st+ (true or false).
75
82
  def prime=(st)
76
83
  @prime = st ? true : false
77
84
  end
78
85
 
79
- ## Iterate overs the bits of the implicant
86
+ ## Iterates over the bits of the implicant.
80
87
  def each(&blk)
81
88
  @bits.each_char(&blk)
82
89
  end
83
90
 
84
- # Compare implicants
85
- # @param imp the implicant (or simply bit string) to compare with
86
- def ==(imp)
87
- @bits == imp.to_s
91
+ ## Compares with +implicant+
92
+ def ==(implicant) # :nodoc:
93
+ @bits == implicant.to_s
88
94
  end
89
- def <=>(imp)
90
- @bits <=> imp.to_s
95
+ def <=>(implicant) #:nodoc:
96
+ @bits <=> implicant.to_s
91
97
  end
92
98
 
93
- # duplicates the implicant
94
- def dup
99
+ ## duplicates the implicant.
100
+ def dup # :nodoc:
95
101
  Implicant.new(self)
96
102
  end
97
103
 
98
- ## Get a bit by index
99
- # @param i the index in the bit string of the implicant
104
+ ## Gets the value of bit +i+.
100
105
  def [](i)
101
106
  @bits[i]
102
107
  end
103
108
 
104
- ## Set a bit by index
105
- # @param i the index in the bit string of the implicant
106
- # @param b the bit to set
109
+ ## Sets the value of bit +i+ to +b+.
107
110
  def []=(i,b)
108
111
  raise "Invalid bit value: #{b}" unless ["0","1","x"].include?(b)
109
112
  return if @bits[i] == b # Already set
@@ -117,16 +120,14 @@ module LogicTools
117
120
  end
118
121
 
119
122
 
120
- ## Merge with another implicant
121
- # @param imp the implicant to merge with
122
- # @return the resulting implicant
123
- def merge(imp)
124
- # Has imp the same mask?
125
- return nil unless imp.mask == @mask
123
+ ## Creates a new implicant merging current implicant with +imp+.
124
+ def merge(implicant)
125
+ # Has implicant the same mask?
126
+ return nil unless implicant.mask == @mask
126
127
  # First look for a 1-0 or 0-1 difference
127
128
  found = nil
128
129
  @bits.each_char.with_index do |b0,i|
129
- b1 = imp.bits[i]
130
+ b1 = implicant.bits[i]
130
131
  # Bits are different
131
132
  if (b0 != b1) then
132
133
  # Stop if there where already a difference
@@ -146,7 +147,7 @@ module LogicTools
146
147
  # And update its x
147
148
  merged[found] = "x"
148
149
  # Finally update its covers
149
- merged.covers = @covers | imp.covers
150
+ merged.covers = @covers | implicant.covers
150
151
  return merged
151
152
  end
152
153
  # No merge
@@ -154,67 +155,72 @@ module LogicTools
154
155
  end
155
156
  end
156
157
 
157
- # Class describing a group of implicants with only singletons, sortable
158
- # by number of ones
158
+
159
+ ##
160
+ # Represents a group of implicants with only singletons, sortable
161
+ # by number of ones.
159
162
  class SameXImplicants
160
163
  include Enumerable
161
164
 
162
- ## Default constructor
165
+ ## Creates a group of implicants.
163
166
  def initialize
164
167
  @implicants = []
165
168
  @singletons = Set.new # Set used for ensuring each implicant is
166
169
  # present only once in the group
167
170
  end
168
171
 
169
- ## Ge the size of the group
172
+ ## Gets the number of implicants of the group.
170
173
  def size
171
174
  @implicants.size
172
175
  end
173
176
 
174
- ## Iterate of the implicants
177
+ ## Iterates over the implicants of the group.
175
178
  def each(&blk)
176
179
  @implicants.each(&blk)
177
180
  end
178
181
 
179
- ## Access by index
180
- # @param i the index
182
+ ## Gets implicant +i+.
181
183
  def [](i)
182
184
  @implicants[i]
183
185
  end
184
186
 
185
- ## Add an implicant
186
- # @param imp the implicant to add
187
- def add(imp)
188
- return if @singletons.include?(imp.bits) # Implicant already present
189
- @implicants << imp
190
- @singletons.add(imp.bits.dup)
187
+ ## Adds +implicant+ to the group.
188
+ def add(implicant)
189
+ # Nothing to do if +implicant+ is already present.
190
+ return if @singletons.include?(implicant.bits)
191
+ @implicants << implicant
192
+ @singletons.add(implicant.bits.dup)
191
193
  end
194
+
192
195
  alias :<< :add
193
196
 
194
- # Sort the implicants by number of ones
197
+ ## Sort the implicants by number of ones.
195
198
  def sort!
196
- @implicants.sort_by! {|imp| imp.count }
199
+ @implicants.sort_by! {|implicant| implicant.count }
197
200
  end
198
201
 
199
- # Convert to a string
200
- def to_s
202
+ ## Converts to a string
203
+ def to_s # :nodoc:
201
204
  @implicants.to_s
202
205
  end
203
- def inspect
206
+
207
+ def inspect # :nodoc:
204
208
  to_s
205
209
  end
206
210
  end
207
211
 
208
- ## Class describing a pseudo variable associated to an implicant
212
+ ##
213
+ # Describes a pseudo variable associated to an implicant.
214
+ #
209
215
  # Used for the Petrick's method
210
216
  class VarImp < Variable
211
217
  @@base = 0 # The index of the VarImp for building the variable names
212
218
 
219
+ ## The implicant the pseudo variable is associated to.
213
220
  attr_reader :implicant
214
221
 
215
- ## Create the variable
216
- # @param imp the implicant to create the variable from
217
- def initialize(imp)
222
+ ## Creates a pseudo variable assoctiated to an +implicant+.
223
+ def initialize(implicant)
218
224
  # Create the name of the variable
219
225
  name = nil
220
226
  begin
@@ -224,19 +230,20 @@ module LogicTools
224
230
  # Create the variable
225
231
  super(name)
226
232
  # Associate it with the implicant
227
- @implicant = imp
228
- imp.var = self
233
+ @implicant = implicant
234
+ implicant.var = self
229
235
  end
230
236
  end
231
237
 
232
238
 
233
239
 
234
- # Enhance the Node class with expression simplifying
240
+ ## Enhances the Node class with expression simplifying.
235
241
  class Node
236
242
 
237
243
  ## Generates an equivalent but simplified representation of the
238
- # function.<br>
239
- # Uses the Quine-Mc Cluskey method
244
+ # expression represented by the tree rooted by the current node.
245
+ #
246
+ # Uses the Quine-Mc Cluskey method.
240
247
  def simplify
241
248
  # Step 1: get the generators
242
249
 
@@ -254,8 +261,8 @@ module LogicTools
254
261
 
255
262
  # Convert the minterms to implicants without x
256
263
  minterms.each do |term|
257
- imp = Implicant.new(term)
258
- implicants[imp.mask] << imp
264
+ implicant = Implicant.new(term)
265
+ implicants[implicant.mask] << implicant
259
266
  end
260
267
 
261
268
  # print "implicants = #{implicants}\n"
@@ -272,17 +279,18 @@ module LogicTools
272
279
  group.sort! # Sort by number of one
273
280
  size = group.size
274
281
  # print "size = #{size}\n"
275
- group.each_with_index do |imp0,i0|
276
- # print "imp0 = #{imp0}, i0=#{i0}\n"
282
+ group.each_with_index do |implicant0,i0|
283
+ # print "implicant0 = #{implicant0}, i0=#{i0}\n"
277
284
  ((i0+1)..(size-1)).each do |i1|
278
285
  # Get the next implicant
279
- imp1 = group[i1]
280
- # print "imp1 = #{imp1}, i1=#{i1}\n"
281
- # No need to look further if the number of 1 of imp1
282
- # is more than one larger than imp0's
283
- break if imp1.count > imp0.count+1
286
+ implicant1 = group[i1]
287
+ # print "implicant1 = #{implicant1}, i1=#{i1}\n"
288
+ # No need to look further if the number of 1 of
289
+ # implicant1 is more than one larger than
290
+ # implicant0's
291
+ break if implicant1.count > implicant0.count+1
284
292
  # Try to merge
285
- mrg = imp0.merge(imp1)
293
+ mrg = implicant0.merge(implicant1)
286
294
  # print "mrg = #{mrg}\n"
287
295
  # Can merge
288
296
  if mrg then
@@ -290,14 +298,14 @@ module LogicTools
290
298
  # Indicate than a merged happend
291
299
  has_merged = true
292
300
  # Mark the initial generators as not prime
293
- imp0.prime = imp1.prime = false
301
+ implicant0.prime = implicant1.prime = false
294
302
  end
295
303
  end
296
304
  # Is the term prime?
297
- if imp0.prime then
298
- # print "imp0 is prime\n"
305
+ if implicant0.prime then
306
+ # print "implicant0 is prime\n"
299
307
  # Yes add it to the generators
300
- generators << imp0
308
+ generators << implicant0
301
309
  end
302
310
  end
303
311
  end
@@ -359,7 +367,7 @@ module LogicTools
359
367
  end
360
368
 
361
369
  # Sort by variable order
362
- selected.sort_by! { |imp| imp.bits }
370
+ selected.sort_by! { |implicant| implicant.bits }
363
371
 
364
372
  # print "Selected prime implicants are: #{selected}\n"
365
373
  # Generate the resulting tree