logic_tools 0.2.2 → 0.2.3

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 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