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.
@@ -6,19 +6,19 @@ require 'forwardable'
6
6
 
7
7
  module LogicTools
8
8
 
9
- ## A logical variable
9
+ ##
10
+ # Represents a logical variable.
10
11
  class Variable
11
12
 
12
13
  include Comparable
13
14
 
14
- ## The pool of variables
15
+ # The pool of variables
15
16
  @@variables = {}
16
17
 
18
+ ## The current value of the variable (boolean).
17
19
  attr_reader :value
18
20
 
19
- ## Initialize with a name (the value is set to false)
20
- # Params:
21
- # +name+:: the name of the variable
21
+ ## Creates a new variable with +name+ (the value is set to false).
22
22
  def initialize(name)
23
23
  if @@variables.key?(name.to_s)
24
24
  raise "Variable already present."
@@ -30,9 +30,7 @@ module LogicTools
30
30
  @@variables[name.to_s] = self
31
31
  end
32
32
 
33
- ## Set the value
34
- # Params:
35
- # +value+:: the value to set
33
+ # Sets the variable to +value+ (boolean).
36
34
  def value=(value)
37
35
  if (value.respond_to?(:to_i))
38
36
  @value = value.to_i == 0 ? false : true
@@ -41,16 +39,13 @@ module LogicTools
41
39
  end
42
40
  end
43
41
 
44
- ## Checks if a variables exists
45
- # Params:
46
- # +name+:: the name of the variable to check
42
+ ## Checks if variable +name+ exists.
47
43
  def Variable.exists?(name)
48
44
  return @@variables.has_key?(name.to_s)
49
45
  end
50
46
 
51
- ## Get a variable by name, if not existing creates it.
52
- # Params:
53
- # +name+:: the name of the variable to get
47
+ ## Gets a variable by +name+.
48
+ # If there is no such variable yet, creates it.
54
49
  def Variable.get(name)
55
50
  var = @@variables[name.to_s]
56
51
  # print "Got var=#{var.to_s} with value=#{var.value}\n" if var
@@ -58,49 +53,55 @@ module LogicTools
58
53
  return var
59
54
  end
60
55
 
61
- ## Convert to a string
56
+ ## Converts to a string: actually returns a duplicate of the name
57
+ # of the variable.
62
58
  def to_s
63
59
  @name.dup
64
60
  end
65
61
 
66
- ## For print
67
- def inspect
62
+ def inspect # :nodoc:
68
63
  to_s
69
64
  end
70
65
 
71
- ## For comparison
66
+ ## Compares with another object using the name of the variable.
72
67
  def <=>(b)
73
68
  self.to_s <=> b.to_s
74
69
  end
75
70
  end
76
71
 
77
- ## A logical tree node
72
+ ##
73
+ # Represents a node of a tree representing a logical expression.
78
74
  class Node
79
75
  include Enumerable
80
76
 
81
- ## Get the variables of the tree
82
- # Return: the variables into an array, sorted by name
77
+ ## Gets a array containing the variables of the tree sorted by name.
83
78
  def getVariables()
84
79
  result = self.getVariablesRecurse
85
80
  return result.flatten.uniq.sort
86
81
  end
87
82
 
88
- ## Get the operator if any
83
+ ## Gets the operator.
84
+ #
85
+ # Default: +nil+ (none).
89
86
  def op
90
87
  nil
91
88
  end
92
89
 
93
- ## Get the size (number of sons)
94
- # Default: 0
90
+ ## Gets number of children.
91
+ #
92
+ # Default: +0+ (none).
95
93
  def size
96
94
  0
97
95
  end
98
96
 
99
- ## Iterate on each truth table line.
100
- # TODO: generate an Enumerator.
101
- # Iteration parameters:
102
- # vars: the variables of the expression
103
- # val: the value
97
+ ## Iterates on each line of the truth table obtained from the tree
98
+ # rooted by current node.
99
+ #
100
+ # Iteration parameters (for the current line of the truth table):
101
+ # * +vars+: the variables of the expression
102
+ # * +val+: the value of the expression
103
+ # --
104
+ # TODO generate an Enumerator.
104
105
  def each_line
105
106
  # Get the variables
106
107
  vars = self.getVariables
@@ -118,18 +119,22 @@ module LogicTools
118
119
  end
119
120
  end
120
121
 
121
- ## Iterate over each minterm.
122
- # TODO: generate an Enumerator.
122
+ ## Iterates over each minterm of the tree rooted from current node.
123
+ #
123
124
  # Iteration parameters:
124
- # vars: the variables of the expression
125
+ # * +vars+: the variables of the expression
126
+ # --
127
+ # TODO generate an Enumerator.
125
128
  def each_minterm
126
129
  each_line { |vars,val| yield(vars) if val }
127
130
  end
128
131
 
129
- ## Iterate over each maxterm.
130
- # TODO: generate an Enumerator.
132
+ ## Iterates over each maxterm of the tree rooted from current node.
133
+ #
131
134
  # Iteration parameters:
132
- # vars: the variables of the expression
135
+ # * +vars+: the variables of the expression
136
+ # --
137
+ # TODO generate an Enumerator.
133
138
  def each_maxterm
134
139
  each_line do |vars,val|
135
140
  unless val then
@@ -139,12 +144,14 @@ module LogicTools
139
144
  end
140
145
  end
141
146
 
142
- ## Iterate on the sons (if any, by default: none)
143
- # TODO: generate an Enumerator.
147
+ ## Iterate over the children.
148
+ #
149
+ # --
150
+ # TODO generate an Enumerator.
144
151
  def each
145
152
  end
146
153
 
147
- ## Generates the equivalent standard conjunctive form
154
+ ## Generates the equivalent standard conjunctive form.
148
155
  def to_std_conjunctive
149
156
  # Generate each minterm tree
150
157
  minterms = []
@@ -163,8 +170,8 @@ module LogicTools
163
170
  return minterms.size == 1 ? minterms[0] : NodeOr.new(*minterms)
164
171
  end
165
172
 
166
- ## Generates the equivalent standard disjonctive form
167
- def to_std_disjonctive
173
+ ## Generates the equivalent standard disjunctive form.
174
+ def to_std_disjunctive
168
175
  # Generate each maxterm tree
169
176
  maxterms = []
170
177
  each_maxterm do |vars|
@@ -182,24 +189,24 @@ module LogicTools
182
189
  return maxterms.size == 1 ? maxterms[0] : NodeAnd.new(*maxterms)
183
190
  end
184
191
 
185
- ## Flatten ands, ors and nots
186
- # Default: simply duplicate
192
+ ## Creates a new tree where the *and*, *or* or *not* operator of
193
+ # the current node is flattened.
194
+ #
195
+ # Default: simply duplicates the node.
187
196
  def flatten
188
197
  return self.dup
189
198
  end
190
199
 
191
- ## Flatten hierachical ands, ors and nots.
200
+ ## Creates a new tree where all the *and*, *or* and *not* operators
201
+ # from the current node are flattened.
202
+ #
192
203
  # Default: simply duplicate.
193
- # Return: the new tree
194
204
  def flatten_deep
195
205
  return self.dup
196
206
  end
197
207
 
198
- ## Distribute over a given operator.
199
- # Default distribution: self is unary.
200
- # Params:
201
- # +dop+:: the operator to distribute over
202
- # +node+:: the node to distribute with
208
+ ## Creates a new tree where the current node is distributed over +node+
209
+ # according to the +dop+ operator.
203
210
  def distribute(dop,node)
204
211
  fop = dop == :and ? :or : :and
205
212
  # print "dop=#{dop}, fop=#{fop}, node.op=#{node.op}\n"
@@ -208,146 +215,149 @@ module LogicTools
208
215
  return NodeNary.make(dop, self, *node)
209
216
  elsif (node.op == fop) then
210
217
  # Opposite operator: can distribute
211
- nsons = node.map do |son|
212
- NodeNary.make(dop,son,self).flatten
218
+ nchildren = node.map do |child|
219
+ NodeNary.make(dop,child,self).flatten
213
220
  end
214
- return NodeNary.make(fop,*nsons).flatten
221
+ return NodeNary.make(fop,*nchildren).flatten
215
222
  else
216
223
  # Unary operator: simple product
217
224
  return NodeNary.make(dop, self, node)
218
225
  end
219
226
  end
220
227
 
221
- ## Convert to a sum of product
222
- # Params:
223
- # +flattened+:: tell if the tree is already flattend
224
- # Return: the conversion result
228
+ ## Creates a sum fo product from the tree rooted by current node.
229
+ #
230
+ # Argument +flattened+ tells if the tree is already flattend
225
231
  def to_sum_product(flattened = false)
226
232
  return self.dup
227
233
  end
228
234
 
229
- ## For print
230
- def inspect
235
+ def inspect # :nodoc:
231
236
  to_s
232
237
  end
233
238
 
234
- ## Convert to a symbol:
235
- # Default: to_s.to_sym
239
+ ## Converts to a symbol.
240
+ #
241
+ # There is exactly one symbol per possible tree.
236
242
  def to_sym
237
243
  to_s.to_sym
238
244
  end
239
245
 
240
- ## For hash
241
- def hash
246
+ def hash # :nodoc:
242
247
  to_sym.hash
243
248
  end
244
- def eql?(val)
249
+ def eql?(val) # :nodoc:
245
250
  self == val
246
251
  end
247
252
  end
248
253
 
249
- ## A Value node
254
+
255
+ ##
256
+ # Represents a value node.
250
257
  class NodeValue < Node
251
258
 
252
259
  protected
253
- ## Build with a value.
254
- # Params:
255
- # +value+:: the value
256
- def initialize(value)
260
+
261
+ ## Creates a node by +value+.
262
+ def initialize(value) # :nodoc:
257
263
  @value = value
258
264
  @sym = @value.to_s.to_sym
259
265
  end
266
+
260
267
  public
261
268
 
262
- ## Get the variables, recursively, without postprocessing.
263
- # Return: the variables into sets of arrays with possible doublon
264
- def getVariablesRecurse()
269
+ ## Gets the variables, recursively, without postprocessing.
270
+ #
271
+ # Returns the variables into sets of arrays with possible doublon
272
+ def getVariablesRecurse() # :nodoc:
265
273
  return [ ]
266
274
  end
267
275
 
268
- ## Compare with another node.
269
- # Params:
270
- # +n+:: the node to compare with
271
- def ==(n)
272
- return false unless n.is_a?(NodeValue)
273
- return self.eval() == n.eval()
276
+ ## Compares with +node+.
277
+ def ==(node) # :nodoc:
278
+ return false unless node.is_a?(NodeValue)
279
+ return self.eval() == node.eval()
274
280
  end
275
281
 
276
- ## Evaluate.
282
+ ## Computes the value of the node.
277
283
  def eval
278
284
  return @value
279
285
  end
280
286
 
281
- ## Convert to a symbol.
282
- def to_sym
287
+ ## Converts to a symbol.
288
+ def to_sym # :nodoc:
283
289
  return @sym
284
290
  end
285
291
 
286
292
  ## Converts to a string.
287
- def to_s
293
+ def to_s # :nodoc:
288
294
  return @value.to_s
289
295
  end
290
296
  end
291
297
 
292
- ## A true node
298
+ ##
299
+ # Represents a true node.
293
300
  class NodeTrue < NodeValue
294
- ## Intialize as a NodeValue whose value is true.
301
+ ## Creates as a NodeValue whose value is true.
295
302
  def initialize
296
303
  super(true)
297
304
  end
298
305
  end
299
306
 
300
- ## A false node
307
+ ##
308
+ # Represents a false node
301
309
  class NodeFalse < NodeValue
302
- ## Intialize as a NodeValue whose value is false.
310
+ ## Creates as a NodeValue whose value is false.
303
311
  def initialize
304
312
  super(false)
305
313
  end
306
314
  end
307
315
 
308
- ## A variable node
316
+
317
+ ##
318
+ # Represents a variable node.
309
319
  class NodeVar < Node
320
+
321
+ ## The variable held by the node.
310
322
  attr_reader :variable
311
323
 
312
- ## Initialize with the variable name.
313
- # Params:
314
- # +name+:: the name of the variable
324
+ ## Creates a node with variable +name+.
315
325
  def initialize(name)
316
326
  @variable = Variable.get(name)
317
327
  @sym = @variable.to_s.to_sym
318
328
  end
319
329
 
320
- ## Evaluates the node
330
+ ## Computes the value of the node.
321
331
  def eval()
322
332
  return @variable.value
323
333
  end
324
334
 
325
- ## Convert to a symbol
326
- def to_sym
335
+ ## Converts to a symbol.
336
+ def to_sym # :nodoc:
327
337
  return @sym
328
338
  end
329
339
 
330
- ## Get the variables, recursively, without postprocessing
331
- # Return: the variables into sets of arrays with possible doublon
332
- def getVariablesRecurse()
340
+ ## Gets the variables, recursively, without postprocessing.
341
+ #
342
+ # Returns the variables into sets of arrays with possible doublon
343
+ def getVariablesRecurse() # :nodoc:
333
344
  return [ @variable ]
334
345
  end
335
346
 
336
- ## Compare with another node
337
- # Params:
338
- # +n+:: the node to compare with
339
- def ==(n)
340
- return false unless n.is_a?(NodeVar)
341
- return self.variable == n.variable
347
+ ## Compares with +node+.
348
+ def ==(node) # :nodoc:
349
+ return false unless node.is_a?(NodeVar)
350
+ return self.variable == node.variable
342
351
  end
343
352
 
344
- ## Converts to a string
345
- def to_s
353
+ ## Converts to a string.
354
+ def to_s # :nodoc:
346
355
  return variable.to_s
347
356
  end
348
357
  end
349
358
 
350
- ## A binary node
359
+ ##
360
+ # Represents an operator node with multiple children.
351
361
  class NodeNary < Node
352
362
  extend Forwardable
353
363
  include Enumerable
@@ -355,35 +365,29 @@ module LogicTools
355
365
  attr_reader :op
356
366
 
357
367
  protected
358
- ## Initialize with the operator
359
- # Params:
360
- # +op+:: the operator name
361
- # +sons+:: the sons
362
- def initialize(op,*sons)
363
- # Check the sons
364
- sons.each do |son|
365
- unless son.is_a?(Node) then
366
- raise ArgumentError.new("Not a valid class for a son: "+
367
- "#{son.class}")
368
+ ## Creates a node with operator +op+ and +children+.
369
+ def initialize(op,*children) # :nodoc:
370
+ # Check the children
371
+ children.each do |child|
372
+ unless child.is_a?(Node) then
373
+ raise ArgumentError.new("Not a valid class for a child: "+
374
+ "#{child.class}")
368
375
  end
369
376
  end
370
- # Sons are ok
377
+ # Children are ok
371
378
  @op = op.to_sym
372
- @sons = sons
379
+ @children = children
373
380
  @sym = self.to_s.to_sym
374
381
  end
375
382
 
376
383
  public
377
- ## Create a new nary node
378
- # Params:
379
- # +op+:: the operator name
380
- # +sons+:: the sons
381
- def NodeNary.make(op,*sons)
384
+ ## Creates a node with operator +op+ and +children+ (factory method).
385
+ def NodeNary.make(op,*children)
382
386
  case op
383
387
  when :or
384
- return NodeOr.new(*sons)
388
+ return NodeOr.new(*children)
385
389
  when :and
386
- return NodeAnd.new(*sons)
390
+ return NodeAnd.new(*children)
387
391
  else
388
392
  raise ArgumentError.new("Not a valid operator: #{op}")
389
393
  end
@@ -391,60 +395,55 @@ module LogicTools
391
395
 
392
396
 
393
397
  # Also acts as an array of nodes
394
- def_delegators :@sons, :[], :empty?, :size
395
- def each(&blk)
396
- @sons.each(&blk)
398
+ def_delegators :@children, :[], :empty?, :size
399
+
400
+ ## Iterates over the children.
401
+ def each(&blk) # :nodoc:
402
+ @children.each(&blk)
397
403
  return self
398
404
  end
399
- # def sort_by!(&blk)
400
- # @sons.sort_by!(&blk)
401
- # return self
402
- # end
403
- # def sort!
404
- # @sons.sort_by! {|son| son.sym }
405
- # return self
406
- # end
405
+
406
+ ## Creates a new node whose childrens are sorted.
407
407
  def sort
408
- return NodeNary.make(@op,*@sons.sort_by {|son| son.to_sym })
408
+ return NodeNary.make(@op,*@children.sort_by {|child| child.to_sym })
409
409
  end
410
410
 
411
- ## Create a new node without doublons.
411
+ ## Creates a new node without duplicate in the children.
412
412
  def uniq(&blk)
413
413
  if blk then
414
- nsons = @sons.uniq(&blk)
414
+ nchildren = @children.uniq(&blk)
415
415
  else
416
- nsons = @sons.uniq { |son| son.to_sym }
416
+ nchildren = @children.uniq { |child| child.to_sym }
417
417
  end
418
- if nsons.size == 1 then
419
- return nsons[0]
418
+ if nchildren.size == 1 then
419
+ return nchildren[0]
420
420
  else
421
- return NodeNary.make(@op,*nsons)
421
+ return NodeNary.make(@op,*nchildren)
422
422
  end
423
423
  end
424
424
 
425
- ## Convert to a symbol.
426
- def to_sym
425
+ ## Converts to a symbol.
426
+ def to_sym # :nodoc:
427
427
  return @sym
428
428
  end
429
429
 
430
- ## Get the variables, recursively, without postprocessing.
431
- # Return: the variables into sets of arrays with possible doublon
432
- def getVariablesRecurse()
433
- return @sons.reduce([]) do |res,son|
434
- res.concat(son.getVariablesRecurse)
430
+ ## Gets the variables, recursively, without postprocessing.
431
+ #
432
+ # Returns the variables into sets of arrays with possible doublon
433
+ def getVariablesRecurse() # :nodoc:
434
+ return @children.reduce([]) do |res,child|
435
+ res.concat(child.getVariablesRecurse)
435
436
  end
436
437
  end
437
438
 
438
- ## Compare with another node
439
- # Params:
440
- # +n+:: the node to compare with
441
- def ==(n)
442
- return false unless n.is_a?(Node)
443
- return false unless self.op == n.op
439
+ ## Compares with +node+.
440
+ def ==(node) # :nodoc:
441
+ return false unless node.is_a?(Node)
442
+ return false unless self.op == node.op
444
443
  # There is no find_with_index!
445
- # return ! @sons.find_with_index {|son,i| son != n[i] }
446
- @sons.each_with_index do |son,i|
447
- return false if son != n[i]
444
+ # return ! @children.find_with_index {|child,i| child != node[i] }
445
+ @children.each_with_index do |child,i|
446
+ return false if child != node[i]
448
447
  end
449
448
  return true
450
449
  end
@@ -457,7 +456,7 @@ module LogicTools
457
456
  # fop = @op == :and ? :or : :and
458
457
  # # Gather the terms by factor
459
458
  # terms = Hash.new {|h,k| h[k] = [] }
460
- # @sons.each do |term|
459
+ # @children.each do |term|
461
460
  # if (term.op == fop) then
462
461
  # # There are factors
463
462
  # term.each { |fact| terms[fact] << term }
@@ -468,32 +467,34 @@ module LogicTools
468
467
  # end
469
468
  # # Keep only the shortest term per factor
470
469
  # terms.each_key {|k| terms[k] = terms[k].min_by {|term| term.size} }
471
- # nsons = terms.values
470
+ # nchildren = terms.values
472
471
  # # Avoid doublons
473
- # nsons.uniq!
472
+ # nchildren.uniq!
474
473
  # # Generate the result
475
- # if (nsons.size == 1)
476
- # return nsons[0]
474
+ # if (nchildren.size == 1)
475
+ # return nchildren[0]
477
476
  # else
478
- # return NodeNary.make(@op,*nsons)
477
+ # return NodeNary.make(@op,*nchildren)
479
478
  # end
480
479
  # end
481
480
 
482
- ## Reduce a node: remove its redudancies using absbortion rules.
483
- # NOTE: NEED to CONSIDER X~X and X+~X CASES
481
+ ## Reduces a node: remove its redundancies using the absbortion rules.
482
+ #
483
+ # --
484
+ # TODO consider the X~X and X+~X cases.
484
485
  def reduce
485
486
  # The operator used for the factors
486
487
  fop = @op == :and ? :or : :and
487
488
  # Gather the terms converted to a sorted string for fast
488
489
  # comparison
489
- terms = @sons.map do |son|
490
- if (son.op == fop) then
491
- [ son, son.sort.to_s ]
490
+ terms = @children.map do |child|
491
+ if (child.op == fop) then
492
+ [ child, child.sort.to_s ]
492
493
  else
493
- [ son, son.to_s ]
494
+ [ child, child.to_s ]
494
495
  end
495
496
  end
496
- nsons = []
497
+ nchildren = []
497
498
  # Keep only the terms that do not contain another one
498
499
  terms.each_with_index do |term0,i|
499
500
  skipped = false
@@ -505,48 +506,47 @@ module LogicTools
505
506
  break
506
507
  end
507
508
  end
508
- nsons << term0[0] unless skipped # Term has not been skipped
509
+ nchildren << term0[0] unless skipped # Term has not been skipped
509
510
  end
510
- # Avoid doublons
511
- nsons.uniq!
511
+ # Avoid duplicates
512
+ nchildren.uniq!
512
513
  # Generate the result
513
- if (nsons.size == 1)
514
- return nsons[0]
514
+ if (nchildren.size == 1)
515
+ return nchildren[0]
515
516
  else
516
- return NodeNary.make(@op,*nsons)
517
+ return NodeNary.make(@op,*nchildren)
517
518
  end
518
519
  end
519
520
 
520
521
  ## Flatten ands, ors and nots.
521
- # Default: simply duplicate
522
- def flatten
523
- return NodeNary.make(@op,*(@sons.reduce([]) do |nsons,son|
524
- if (son.op == self.op) then
525
- nsons.push(*son)
522
+ def flatten # :nodoc:
523
+ return NodeNary.make(@op,*(@children.reduce([]) do |nchildren,child|
524
+ if (child.op == self.op) then
525
+ nchildren.push(*child)
526
526
  else
527
- nsons << son
527
+ nchildren << child
528
528
  end
529
529
  end)).reduce
530
530
  end
531
531
 
532
- ## Flatten hierachical ands, ors and nots, removing redudancies.
533
- # Return: the new tree
534
- def flatten_deep
535
- return NodeNary.make(@op,*(@sons.reduce([]) do |nsons,son|
536
- son = son.flatten_deep
537
- if (son.op == self.op) then
538
- nsons.push(*son)
532
+ ## Creates a new tree where all the *and*, *or* and *not* operators
533
+ # from the current node are flattened.
534
+ #
535
+ # Default: simply duplicate.
536
+ def flatten_deep # :nodoc:
537
+ return NodeNary.make(@op,*(@children.reduce([]) do |nchildren,child|
538
+ child = child.flatten_deep
539
+ if (child.op == self.op) then
540
+ nchildren.push(*child)
539
541
  else
540
- nsons << son
542
+ nchildren << child
541
543
  end
542
544
  end)).reduce
543
545
  end
544
546
 
545
- ## Distribute over a given operator.
546
- # Params:
547
- # +dop+:: the operator to distribute over
548
- # +node+:: the node to distribute with
549
- def distribute(dop,node)
547
+ ## Creates a new tree where the current node is distributed over +node+
548
+ # according to the +dop+ operator.
549
+ def distribute(dop,node) # :nodoc:
550
550
  fop = dop == :and ? :or : :and
551
551
  # print "dop=#{dop} fop=#{fop} self.op=#{@op}\n"
552
552
  if (@op == dop) then
@@ -556,60 +556,61 @@ module LogicTools
556
556
  # self operator if fop
557
557
  if (node.op == fop) then
558
558
  # node operator is also fop: (a+b)(c+d) or ab+cd case
559
- nsons = []
560
- self.each do |son0|
561
- node.each do |son1|
562
- # print "son0=#{son0}, son1=#{son1}\n"
563
- nsons << NodeNary.make(dop, son0, son1).flatten
564
- # print "nsons=#{nsons}\n"
559
+ nchildren = []
560
+ self.each do |child0|
561
+ node.each do |child1|
562
+ # print "child0=#{child0}, child1=#{child1}\n"
563
+ nchildren <<
564
+ NodeNary.make(dop, child0, child1).flatten
565
+ # print "nchildren=#{nchildren}\n"
565
566
  end
566
567
  end
567
- return NodeNary.make(fop,*nsons).flatten
568
+ return NodeNary.make(fop,*nchildren).flatten
568
569
  else
569
570
  # node operator is not fop: (a+b)c or ab+c case
570
- nsons = self.map do |son|
571
- NodeNary.make(dop,son,node).flatten
571
+ nchildren = self.map do |child|
572
+ NodeNary.make(dop,child,node).flatten
572
573
  end
573
- return NodeNary.make(fop,*nsons).flatten
574
+ return NodeNary.make(fop,*nchildren).flatten
574
575
  end
575
576
  end
576
577
  end
577
578
  end
578
579
 
579
- ## An AND node
580
+
581
+ ##
582
+ # Represents an AND node
580
583
  class NodeAnd < NodeNary
581
- ## Initialize by building a new nary node whose operator is and.
582
- # Params:
583
- # +sons+:: the sons
584
- def initialize(*sons)
585
- super(:and,*sons)
584
+
585
+ # Creates a new AND node with +children+.
586
+ def initialize(*children)
587
+ super(:and,*children)
586
588
  end
587
589
 
588
590
  ## Duplicates the node.
589
- def dup
590
- return NodeAnd.new(@sons.map(&:dup))
591
+ def dup # :nodoc:
592
+ return NodeAnd.new(@children.map(&:dup))
591
593
  end
592
594
 
593
- ## Evaluate the node.
595
+ ## Computes the value of the node.
594
596
  def eval()
595
- return !@sons.any? {|son| son.eval() == false }
597
+ return !@children.any? {|child| child.eval() == false }
596
598
  end
597
599
 
598
- ## Convert to a sum of product.
599
- # Params:
600
- # +flattened+:: tell of the tree is already flatttend
601
- # Return: the conversion result
602
- def to_sum_product(flattened = false)
600
+ ## Creates a sum fo product from the tree rooted by current node.
601
+ #
602
+ # Argument +flattened+ tells if the tree is already flattend
603
+ def to_sum_product(flattened = false) # :nodoc:
603
604
  # Flatten if required
604
605
  node = flattened ? self : self.flatten_deep
605
606
  # print "node = #{node}\n"
606
- # Convert each son to sum of product
607
- nsons = node.map {|son| son.to_sum_product(true) }
608
- # print "nsons = #{nsons}\n"
607
+ # Convert each child to sum of product
608
+ nchildren = node.map {|child| child.to_sum_product(true) }
609
+ # print "nchildren = #{nchildren}\n"
609
610
  # Distribute
610
- while(nsons.size>1)
611
+ while(nchildren.size>1)
611
612
  dist = []
612
- nsons.each_slice(2) do |left,right|
613
+ nchildren.each_slice(2) do |left,right|
613
614
  # print "left=#{left}, right=#{right}\n"
614
615
  if right then
615
616
  dist << (left.op == :or ? left.distribute(:and,right) :
@@ -619,28 +620,28 @@ module LogicTools
619
620
  end
620
621
  end
621
622
  # print "dist=#{dist}\n"
622
- nsons = dist
623
+ nchildren = dist
623
624
  end
624
- # print "Distributed nsons=#{nsons}\n"
625
+ # print "Distributed nchildren=#{nchildren}\n"
625
626
  # Generate the or
626
- if (nsons.size > 1)
627
- return NodeOr.new(*nsons)
627
+ if (nchildren.size > 1)
628
+ return NodeOr.new(*nchildren)
628
629
  else
629
- return nsons[0]
630
+ return nchildren[0]
630
631
  end
631
632
  end
632
633
 
633
634
  ## Convert to a string.
634
- def to_s
635
+ def to_s # :nodoc:
635
636
  return @str if @str
636
637
  @str = ""
637
- # Convert the sons to a string
638
- @sons.each do |son|
639
- if (son.op == :or) then
638
+ # Convert the children to a string
639
+ @children.each do |child|
640
+ if (child.op == :or) then
640
641
  # Yes, need parenthesis
641
- @str << ( "(" + son.to_s + ")" )
642
+ @str << ( "(" + child.to_s + ")" )
642
643
  else
643
- @str << son.to_s
644
+ @str << child.to_s
644
645
  end
645
646
  end
646
647
  return @str
@@ -648,159 +649,161 @@ module LogicTools
648
649
  end
649
650
 
650
651
 
651
- ## An OR node
652
+ ##
653
+ # Represents an OR node
652
654
  class NodeOr < NodeNary
653
- ## Initialize by building a new nary node whose operator is or
654
- # @param sons the sons
655
- def initialize(*sons)
656
- super(:or,*sons)
655
+
656
+ # Creates a new OR node with +children+.
657
+ def initialize(*children)
658
+ super(:or,*children)
657
659
  end
658
660
 
659
- ## Duplicates the node
660
- def dup
661
- return NodeOr.new(@sons.map(&:dup))
661
+ ## Duplicates the node.
662
+ def dup # :nodoc:
663
+ return NodeOr.new(@children.map(&:dup))
662
664
  end
663
665
 
664
- ## Evaluate the node
666
+ ## Computes the value of the node.
665
667
  def eval
666
- return @sons.any? {|son| son.eval() == true }
668
+ return @children.any? {|child| child.eval() == true }
667
669
  end
668
670
 
669
- ## Convert to a sum of product
670
- # @param flattened tell of the tree is already flatttend
671
- # @return the conversion result
672
- def to_sum_product(flattened = false)
673
- return NodeOr.new(*@sons.map {|son| son.to_sum_product(flatten) })
671
+ ## Creates a sum fo product from the tree rooted by current node.
672
+ #
673
+ # Argument +flattened+ tells if the tree is already flattend
674
+ def to_sum_product(flattened = false) # :nodoc:
675
+ return NodeOr.new(
676
+ *@children.map {|child| child.to_sum_product(flatten) } )
674
677
  end
675
678
 
676
- ## Converts to a string
677
- def to_s
679
+ ## Converts to a string.
680
+ def to_s # :nodoc:
678
681
  return @str if @str
679
- # Convert the sons to string a insert "+" between them
680
- @str = @sons.join("+")
682
+ # Convert the children to string a insert "+" between them
683
+ @str = @children.join("+")
681
684
  return @str
682
685
  end
683
686
  end
684
687
 
685
688
 
686
689
 
687
- # An unary node
690
+ ##
691
+ # Represents an unary node.
688
692
  class NodeUnary < Node
689
- attr_reader :op, :son
690
-
691
- ## Initialize with the operator.
692
- # Params:
693
- # +op+:: the operator name
694
- # +son+:: the son node
695
- def initialize(op,son)
696
- if !son.is_a?(Node) then
697
- raise ArgumentError.new("Not a valid object for son.")
693
+ attr_reader :op, :child
694
+
695
+ ## Creates a node with operator +op+ and a +child+.
696
+ def initialize(op,child)
697
+ if !child.is_a?(Node) then
698
+ raise ArgumentError.new("Not a valid object for child.")
698
699
  end
699
700
  @op = op.to_sym
700
- @son = son
701
+ @child = child
701
702
  @sym = self.to_s.to_sym
702
703
  end
703
704
 
704
- ## Get the size (number of sons).
705
- def size
705
+ ## Gets the number of children.
706
+ def size # :nodoc:
706
707
  1
707
708
  end
708
709
 
709
- # ## Set the son node
710
- # # @param son the node to set
711
- # def son=(son)
710
+ # ## Set the child node
711
+ # # @param child the node to set
712
+ # def child=(child)
712
713
  # # Checks it is a valid object
713
- # if !son.is_a?(Node) then
714
- # raise ArgumentError.new("Not a valid object for son.")
714
+ # if !child.is_a?(Node) then
715
+ # raise ArgumentError.new("Not a valid object for child.")
715
716
  # else
716
- # @son = son
717
+ # @child = child
717
718
  # end
718
719
  # end
719
720
 
720
- ## Get the variables in an array recursively.
721
- # Return: the variables into an array with possible doublon
722
- def getVariablesRecurse()
723
- return @son.getVariablesRecurse
721
+ ## Gets the variables, recursively, without postprocessing.
722
+ #
723
+ # Returns the variables into sets of arrays with possible doublon
724
+ def getVariablesRecurse() # :nodoc:
725
+ return @child.getVariablesRecurse
724
726
  end
725
727
 
726
- ## Iterate on the sons.
727
- def each
728
- yield(@son)
728
+ ## Iterates over the children.
729
+ def each # :nodoc:
730
+ yield(@child)
729
731
  end
730
732
 
731
- ## Compare with another node.
732
- # Params:
733
- # +n+:: the node to compare with
734
- def ==(n)
735
- return false unless n.is_a?(Node)
733
+ ## Compares with +node+.
734
+ def ==(node) # :nodoc:
735
+ return false unless node.is_a?(Node)
736
736
  return false unless self.op == n.op
737
- return self.son == n.son
737
+ return self.child == node.child
738
738
  end
739
739
 
740
- ## Convert to a symbol.
741
- def to_sym
740
+ ## Converts to a symbol.
741
+ def to_sym # :nodoc:
742
742
  return @sym
743
743
  end
744
744
  end
745
745
 
746
- ## A NOT node
746
+ ##
747
+ # Represents a NOT node.
747
748
  class NodeNot < NodeUnary
748
- ## Initialize by building a new unary node whose operator is not.
749
- # Params:
750
- # +son+:: the son
751
- def initialize(son)
752
- super(:not,son)
749
+ ## Creates a NOT node with a +child+.
750
+ def initialize(child)
751
+ super(:not,child)
753
752
  end
754
753
 
755
754
  ## Duplicates the node.
756
- def dup
757
- return NodeNot.new(@son.dup)
755
+ def dup # :nodoc:
756
+ return NodeNot.new(@child.dup)
758
757
  end
759
758
 
760
- ## Evaluate the node.
761
- def eval
762
- return !son.eval
759
+ ## Computes the value of the node.
760
+ def eval # :nodoc:
761
+ return !child.eval
763
762
  end
764
763
 
765
- ## Flatten ands, ors and nots.
766
- # Default: simply duplicate
767
- def flatten
768
- if nson.op == :not then
769
- return nson
764
+ ## Creates a new tree where the *and*, *or* or *not* operator of
765
+ # the current node is flattened.
766
+ #
767
+ # Default: simply duplicates the node.
768
+ def flatten # :nodoc:
769
+ nchild = @child.flatten
770
+ if nchild.op == :not then
771
+ return nchild.child
770
772
  else
771
- return NodeNot.new(nson)
773
+ return NodeNot.new(nchild)
772
774
  end
773
775
  end
774
776
 
775
- ## Flatten hierachical ands, ors and nots.
776
- # Return: the new tree
777
- def flatten_deep
778
- nson = @son.flatten_deep
779
- if nson.op == :not then
780
- return nson
777
+ ## Creates a new tree where all the *and*, *or* and *not* operators
778
+ # from the current node are flattened.
779
+ #
780
+ # Default: simply duplicate.
781
+ def flatten_deep # :nodoc:
782
+ nchild = @child.flatten_deep
783
+ if nchild.op == :not then
784
+ return nchild.child
781
785
  else
782
- return NodeNot.new(nson)
786
+ return NodeNot.new(child)
783
787
  end
784
788
  end
785
789
 
786
- ## Convert to a sum of product.
787
- # Params:
788
- # +flattened+:: tell of the tree is already flatttend
789
- # Return: the conversion result
790
- def to_sum_product(flattened = false)
791
- return NodeNot.new(@son.to_sum_product(flatten))
790
+ ## Creates a sum fo product from the tree rooted by current node.
791
+ #
792
+ # Argument +flattened+ tells if the tree is already flattend
793
+ def to_sum_product(flattened = false) # :nodoc:
794
+ return NodeNot.new(@child.to_sum_product(flatten))
792
795
  end
793
796
 
794
797
  ## Converts to a string.
795
- def to_s
798
+ def to_s # :nodoc:
796
799
  return @str if @str
797
- # Is the son a binary node?
798
- if son.op == :or || son.op == :and then
800
+ # Is the child a binary node?
801
+ if child.op == :or || child.op == :and then
799
802
  # Yes must put parenthesis
800
- @str = "~(" + son.to_s + ")"
803
+ @str = "~(" + child.to_s + ")"
801
804
  else
802
805
  # No
803
- @str = "~" + son.to_s
806
+ @str = "~" + child.to_s
804
807
  end
805
808
  return @str
806
809
  end