logic_tools 0.2.4 → 0.3.0

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,6 +6,7 @@
6
6
 
7
7
  require 'set'
8
8
 
9
+ require "logic_tools/logictree.rb"
9
10
 
10
11
  module LogicTools
11
12
 
@@ -69,7 +70,7 @@ module LogicTools
69
70
  @prime = true # By default assumed prime
70
71
  end
71
72
 
72
- ## Converts to a string
73
+ ## Converts to a string.
73
74
  def to_s # :nodoc:
74
75
  @bits
75
76
  end
@@ -84,7 +85,13 @@ module LogicTools
84
85
  end
85
86
 
86
87
  ## Iterates over the bits of the implicant.
88
+ #
89
+ # Returns an enumerator if no block given.
87
90
  def each(&blk)
91
+ # No block given? Returns an enumerator
92
+ return to_enum(:each) unless block_given?
93
+
94
+ # Block given? Applies it on each bit.
88
95
  @bits.each_char(&blk)
89
96
  end
90
97
 
@@ -212,7 +219,7 @@ module LogicTools
212
219
  ##
213
220
  # Describes a pseudo variable associated to an implicant.
214
221
  #
215
- # Used for the Petrick's method
222
+ # Used for the Petrick's method.
216
223
  class VarImp < Variable
217
224
  @@base = 0 # The index of the VarImp for building the variable names
218
225
 
@@ -371,7 +378,7 @@ module LogicTools
371
378
 
372
379
  # print "Selected prime implicants are: #{selected}\n"
373
380
  # Generate the resulting tree
374
- variables = self.getVariables()
381
+ variables = self.get_variables()
375
382
  # First generate the prime implicants trees
376
383
  selected.map! do |prime|
377
384
  # Generate the litterals
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'forwardable'
6
6
 
7
+
7
8
  module LogicTools
8
9
 
9
10
  ##
@@ -76,11 +77,18 @@ module LogicTools
76
77
  include Enumerable
77
78
 
78
79
  ## Gets a array containing the variables of the tree sorted by name.
79
- def getVariables()
80
- result = self.getVariablesRecurse
80
+ def get_variables()
81
+ result = self.get_variablesRecurse
81
82
  return result.flatten.uniq.sort
82
83
  end
83
84
 
85
+ ## Tells if the node is a parent.
86
+ #
87
+ # Default: +false+.
88
+ def is_parent?
89
+ return false
90
+ end
91
+
84
92
  ## Gets the operator.
85
93
  #
86
94
  # Default: +nil+ (none).
@@ -107,7 +115,7 @@ module LogicTools
107
115
 
108
116
  # Block given? Apply it.
109
117
  # Get the variables
110
- vars = self.getVariables
118
+ vars = self.get_variables
111
119
  # Compute the number of iterations
112
120
  nlines = 2**vars.size
113
121
  # Generates each bit value for the variables and the
@@ -209,19 +217,32 @@ module LogicTools
209
217
  #
210
218
  # Default: simply duplicate.
211
219
  def flatten_deep
220
+ # print "flatten_deep #1 with self=#{self}\n"
212
221
  return self.dup
213
222
  end
214
223
 
224
+ ## Reduce the node by removing redundancy from it.
225
+ #
226
+ # NOTE: this is not the same purpose a Enumerable::reduce.
227
+ def reduce
228
+ # By default, no possible reduction.
229
+ self.clone
230
+ end
231
+
215
232
  ## Creates a new tree where the current node is distributed over +node+
216
233
  # according to the +dop+ operator.
217
234
  def distribute(dop,node)
218
235
  fop = dop == :and ? :or : :and
219
- # print "dop=#{dop}, fop=#{fop}, node.op=#{node.op}\n"
220
236
  if (node.op == dop) then
221
237
  # Same operator: merge self in node
222
238
  return NodeNary.make(dop, self, *node)
223
239
  elsif (node.op == fop) then
224
240
  # Opposite operator: can distribute
241
+ # result = NodeNary.make(dop)
242
+ # node.each do |child|
243
+ # result << NodeNary.make(dop,child,self).flatten
244
+ # end
245
+ # return result.flatten
225
246
  nchildren = node.map do |child|
226
247
  NodeNary.make(dop,child,self).flatten
227
248
  end
@@ -256,6 +277,18 @@ module LogicTools
256
277
  def eql?(val) # :nodoc:
257
278
  self == val
258
279
  end
280
+
281
+ ## Tells if +self+ includes +tree+.
282
+ def include?(tree)
283
+ # By default: equality comparison.
284
+ return self == tree
285
+ end
286
+
287
+ ## Tells if +self+ covers +tree+
288
+ def cover?(tree)
289
+ # By default: equality comparison.
290
+ return self == tree
291
+ end
259
292
  end
260
293
 
261
294
 
@@ -268,7 +301,8 @@ module LogicTools
268
301
  ## Creates a node by +value+.
269
302
  def initialize(value) # :nodoc:
270
303
  @value = value
271
- @sym = @value.to_s.to_sym
304
+ # @sym = @value.to_s.to_sym
305
+ @sym = nil
272
306
  end
273
307
 
274
308
  public
@@ -276,7 +310,7 @@ module LogicTools
276
310
  ## Gets the variables, recursively, without postprocessing.
277
311
  #
278
312
  # Returns the variables into sets of arrays with possible doublon
279
- def getVariablesRecurse() # :nodoc:
313
+ def get_variablesRecurse() # :nodoc:
280
314
  return [ ]
281
315
  end
282
316
 
@@ -286,19 +320,31 @@ module LogicTools
286
320
  return self.eval() == node.eval()
287
321
  end
288
322
 
323
+ # Node::include? is now enough.
324
+ # ## Tells if the +self+ includes +tree+.
325
+ # def include?(tree)
326
+ # return ( tree.is_a?(NodeValue) and self.eval() == tree.eval() )
327
+ # end
328
+
289
329
  ## Computes the value of the node.
290
330
  def eval
291
331
  return @value
292
332
  end
293
333
 
334
+ ## Gets the operator.
335
+ def op
336
+ return @value.to_s.to_sym
337
+ end
338
+
294
339
  ## Converts to a symbol.
295
340
  def to_sym # :nodoc:
341
+ @sym = @value.to_s.to_sym unless @sym
296
342
  return @sym
297
343
  end
298
344
 
299
345
  ## Converts to a string.
300
346
  def to_s # :nodoc:
301
- return @value.to_s
347
+ return @value ? "1" : "0"
302
348
  end
303
349
  end
304
350
 
@@ -331,9 +377,23 @@ module LogicTools
331
377
  ## Creates a node with variable +name+.
332
378
  def initialize(name)
333
379
  @variable = Variable.get(name)
334
- @sym = @variable.to_s.to_sym
380
+ # @sym = @variable.to_s.to_sym
381
+ @sym = nil
382
+ end
383
+
384
+ ## Gets the operator.
385
+ #
386
+ # Default: +nil+ (none).
387
+ def op
388
+ :variable
335
389
  end
336
390
 
391
+ # Node::include? is now enough.
392
+ # ## Tells if the +self+ includes +tree+.
393
+ # def include?(tree)
394
+ # return ( tree.is_a?(NodeVar) and self.variable == tree.variable )
395
+ # end
396
+
337
397
  ## Computes the value of the node.
338
398
  def eval()
339
399
  return @variable.value
@@ -341,13 +401,14 @@ module LogicTools
341
401
 
342
402
  ## Converts to a symbol.
343
403
  def to_sym # :nodoc:
404
+ @sym = @variable.to_s.to_sym unless @sym
344
405
  return @sym
345
406
  end
346
407
 
347
408
  ## Gets the variables, recursively, without postprocessing.
348
409
  #
349
410
  # Returns the variables into sets of arrays with possible doublon
350
- def getVariablesRecurse() # :nodoc:
411
+ def get_variablesRecurse() # :nodoc:
351
412
  return [ @variable ]
352
413
  end
353
414
 
@@ -359,7 +420,11 @@ module LogicTools
359
420
 
360
421
  ## Converts to a string.
361
422
  def to_s # :nodoc:
362
- return variable.to_s
423
+ result = variable.to_s
424
+ # Variables using more than one character are parenthesized
425
+ # to avoid confunsion with the AND operator.
426
+ result = "{" + result + "}" if (result.size > 1)
427
+ return result
363
428
  end
364
429
  end
365
430
 
@@ -383,7 +448,8 @@ module LogicTools
383
448
  # Children are ok
384
449
  @op = op.to_sym
385
450
  @children = children
386
- @sym = self.to_s.to_sym
451
+ # @sym = self.to_s.to_sym
452
+ @sym = nil
387
453
  end
388
454
 
389
455
  public
@@ -399,10 +465,23 @@ module LogicTools
399
465
  end
400
466
  end
401
467
 
468
+ ## Tells if the node is a parent.
469
+ def is_parent?
470
+ return true
471
+ end
402
472
 
403
473
  # Also acts as an array of nodes
404
474
  def_delegators :@children, :[], :empty?, :size
405
475
 
476
+ ## Adds a +child+.
477
+ def add(child)
478
+ unless child.is_a?(Node) then
479
+ raise ArgumentError.new("Not a valid class for a child: "+
480
+ "#{child.class}")
481
+ end
482
+ @children << child
483
+ end
484
+
406
485
  ## Iterates over the children.
407
486
  def each(&blk) # :nodoc:
408
487
  # No block given? Return an enumerator.
@@ -434,15 +513,16 @@ module LogicTools
434
513
 
435
514
  ## Converts to a symbol.
436
515
  def to_sym # :nodoc:
516
+ @sym = self.to_s.to_sym unless @sym
437
517
  return @sym
438
518
  end
439
519
 
440
520
  ## Gets the variables, recursively, without postprocessing.
441
521
  #
442
522
  # Returns the variables into sets of arrays with possible doublon
443
- def getVariablesRecurse() # :nodoc:
523
+ def get_variablesRecurse() # :nodoc:
444
524
  return @children.reduce([]) do |res,child|
445
- res.concat(child.getVariablesRecurse)
525
+ res.concat(child.get_variablesRecurse)
446
526
  end
447
527
  end
448
528
 
@@ -450,6 +530,7 @@ module LogicTools
450
530
  def ==(node) # :nodoc:
451
531
  return false unless node.is_a?(Node)
452
532
  return false unless self.op == node.op
533
+ return false unless self.size == node.size
453
534
  # There is no find_with_index!
454
535
  # return ! @children.find_with_index {|child,i| child != node[i] }
455
536
  @children.each_with_index do |child,i|
@@ -458,6 +539,46 @@ module LogicTools
458
539
  return true
459
540
  end
460
541
 
542
+ ## Tells if +self+ includes +tree+.
543
+ #
544
+ # NOTE: * This is a tree inclusion, not a logical inclusion.
545
+ # * It is assumed that the trees are sorted.
546
+ def include?(tree)
547
+ # Check from current node.
548
+ if self.op == tree.op and self.size >= tree.size then
549
+ return true unless tree.each.with_index.find do |child,i|
550
+ child != @children[i]
551
+ end
552
+ end
553
+ # Check each child.
554
+ @children.each do |child|
555
+ return true if child.include?(tree)
556
+ end
557
+ # Do not include.
558
+ return false
559
+ end
560
+
561
+ ## Tells if +self+ covers +tree+.
562
+ #
563
+ # NOTE: * It is assumed that the trees are sorted.
564
+ # * There might still be cover even when the result is false.
565
+ # For exact cover checking, please use the LogicTools::Cover
566
+ # class.
567
+ def cover?(tree)
568
+ # Different operators, no probable cover.
569
+ return false if self.op != tree.op
570
+ # Check for equality with one child.
571
+ return true unless tree.each.with_index.find do |child,i|
572
+ child != @children[i]
573
+ end
574
+ # Check each child.
575
+ @children.each do |child|
576
+ return true if ( child.op == self.op and child.cover?(tree) )
577
+ end
578
+ # Do not include.
579
+ return false
580
+ end
581
+
461
582
  # WRONG
462
583
  ## Reduce a node: remove its redudancies using absbortion rules
463
584
  # NOTE: NEED to CONSIDER X~X and X+~X CASES
@@ -495,28 +616,37 @@ module LogicTools
495
616
  def reduce
496
617
  # The operator used for the factors
497
618
  fop = @op == :and ? :or : :and
498
- # Gather the terms converted to a sorted string for fast
499
- # comparison
619
+ # Gather the terms to sorted nodes
500
620
  terms = @children.map do |child|
501
- if (child.op == fop) then
502
- [ child, child.sort.to_s ]
503
- else
504
- [ child, child.to_s ]
505
- end
621
+ child.op == fop ? child.sort : child
506
622
  end
507
623
  nchildren = []
508
624
  # Keep only the terms that do not contain another one
625
+ # TODO: this loop could be faster I think...
509
626
  terms.each_with_index do |term0,i|
510
627
  skipped = false
511
628
  terms.each_with_index do |term1,j|
512
629
  next if (i==j) # Same term
513
- if (term0[1].include?(term1[1])) and term0[1]!=term1[1] then
514
- # term0 contains term1 but is different, skip it
630
+ # Checks the X~X or X+~X cases.
631
+ if ( term0.op == :not and term0.child == term1 ) or
632
+ ( term1.op == :not and term1.child == term0 ) then
633
+ # Reduceable to 0 or 1
634
+ return self.op == :and ? NodeFalse.new : NodeTrue.new
635
+ end
636
+ # Checks the covering.
637
+ next if (term0.op != term1.op) # Different operators
638
+ # if (term0.include?(term1) and term0 != term1) then
639
+ # # term0 contains term1 but is different, skip it
640
+ # skipped = true
641
+ # break
642
+ # end
643
+ if term1.cover?(term0) then
644
+ # term1 covers term0 skip term0.
515
645
  skipped = true
516
- break
646
+ # break
517
647
  end
518
648
  end
519
- nchildren << term0[0] unless skipped # Term has not been skipped
649
+ nchildren << term0 unless skipped # Term has not been skipped
520
650
  end
521
651
  # Avoid duplicates
522
652
  nchildren.uniq!
@@ -599,7 +729,7 @@ module LogicTools
599
729
 
600
730
  ## Duplicates the node.
601
731
  def dup # :nodoc:
602
- return NodeAnd.new(@children.map(&:dup))
732
+ return NodeAnd.new(*@children.map(&:dup))
603
733
  end
604
734
 
605
735
  ## Computes the value of the node.
@@ -613,15 +743,13 @@ module LogicTools
613
743
  def to_sum_product(flattened = false) # :nodoc:
614
744
  # Flatten if required
615
745
  node = flattened ? self : self.flatten_deep
616
- # print "node = #{node}\n"
746
+ return node unless node.is_parent?
617
747
  # Convert each child to sum of product
618
748
  nchildren = node.map {|child| child.to_sum_product(true) }
619
- # print "nchildren = #{nchildren}\n"
620
749
  # Distribute
621
750
  while(nchildren.size>1)
622
751
  dist = []
623
752
  nchildren.each_slice(2) do |left,right|
624
- # print "left=#{left}, right=#{right}\n"
625
753
  if right then
626
754
  dist << (left.op == :or ? left.distribute(:and,right) :
627
755
  right.distribute(:and,left))
@@ -629,10 +757,8 @@ module LogicTools
629
757
  dist << left
630
758
  end
631
759
  end
632
- # print "dist=#{dist}\n"
633
760
  nchildren = dist
634
761
  end
635
- # print "Distributed nchildren=#{nchildren}\n"
636
762
  # Generate the or
637
763
  if (nchildren.size > 1)
638
764
  return NodeOr.new(*nchildren)
@@ -670,7 +796,7 @@ module LogicTools
670
796
 
671
797
  ## Duplicates the node.
672
798
  def dup # :nodoc:
673
- return NodeOr.new(@children.map(&:dup))
799
+ return NodeOr.new(*@children.map(&:dup))
674
800
  end
675
801
 
676
802
  ## Computes the value of the node.
@@ -709,7 +835,13 @@ module LogicTools
709
835
  end
710
836
  @op = op.to_sym
711
837
  @child = child
712
- @sym = self.to_s.to_sym
838
+ # @sym = self.to_s.to_sym
839
+ @sym = nil
840
+ end
841
+
842
+ ## Tells if the node is a parent.
843
+ def is_parent?
844
+ return true
713
845
  end
714
846
 
715
847
  ## Gets the number of children.
@@ -731,8 +863,8 @@ module LogicTools
731
863
  ## Gets the variables, recursively, without postprocessing.
732
864
  #
733
865
  # Returns the variables into sets of arrays with possible doublon
734
- def getVariablesRecurse() # :nodoc:
735
- return @child.getVariablesRecurse
866
+ def get_variablesRecurse() # :nodoc:
867
+ return @child.get_variablesRecurse
736
868
  end
737
869
 
738
870
  ## Iterates over the children.
@@ -747,12 +879,22 @@ module LogicTools
747
879
  ## Compares with +node+.
748
880
  def ==(node) # :nodoc:
749
881
  return false unless node.is_a?(Node)
750
- return false unless self.op == n.op
882
+ return false unless self.op == node.op
751
883
  return self.child == node.child
752
884
  end
753
885
 
886
+ ## Tells if the +self+ includes +tree+.
887
+ def include?(tree)
888
+ return true if self == tree # Same tree, so inclusion.
889
+ # Check the child
890
+ return true if @child.include?(tree)
891
+ # Do not include
892
+ return false
893
+ end
894
+
754
895
  ## Converts to a symbol.
755
896
  def to_sym # :nodoc:
897
+ @sym = self.to_s.to_sym unless @sym
756
898
  return @sym
757
899
  end
758
900
  end
@@ -805,7 +947,29 @@ module LogicTools
805
947
  #
806
948
  # Argument +flattened+ tells if the tree is already flattend
807
949
  def to_sum_product(flattened = false) # :nodoc:
808
- return NodeNot.new(@child.to_sum_product(flatten))
950
+ # return NodeNot.new(@child.to_sum_product(flatten))
951
+ # Flatten deeply if required.
952
+ nnode = flattened ? self : self.flatten_deep
953
+ if (nnode.op != :not) then
954
+ # Not a NOT any longer.
955
+ return nnode.to_sum_product
956
+ end
957
+ # Still a NOT, so apply De Morgan's law.
958
+ child = nnode.child
959
+ if child.op == :or then
960
+ # Can apply De Morgan's law for OR.
961
+ return NodeAnd.new( *child.each.map do |n|
962
+ NodeNot.new(n).to_sum_product
963
+ end ).to_sum_product
964
+ elsif child.op == :and then
965
+ # Can apply De Morgan's law for AND.
966
+ return NodeOr.new( *child.each.map do |n|
967
+ NodeNot.new(n).to_sum_product
968
+ end )
969
+ else
970
+ # Nothing to do more.
971
+ return nnode
972
+ end
809
973
  end
810
974
 
811
975
  ## Converts to a string.