logic_tools 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.