]> git.ipfire.org Git - thirdparty/automake.git/commitdiff
Speedup Automake::DisjConditions::invert().
authorRaja R Harinath <harinath@acm.org>
Sun, 30 Mar 2003 01:56:28 +0000 (01:56 +0000)
committerRaja R Harinath <harinath@acm.org>
Sun, 30 Mar 2003 01:56:28 +0000 (01:56 +0000)
* lib/Automake/Condition.pm (multiply): New.
* lib/Automake/DisjConditions.pm (_multiply): Use it.
(sub_conditions): Likewise.

* lib/Automake/Condition.pm (reduce): Rename to ...
(reduce_and): ... this.
(reduce_or): New.
* lib/Automake/DisjConditions.pm (_multiply): Use reduce_or().
* lib/Automake/tests/Condition.pl (test_reduce): Rename to ...
(test_reduce_and): ... this.
(test_reduce_or): New.
* lib/Automake/tests/DisjConditions.pl
(test_invert): Update to reflect effect of reduce_or().
(test_simplify): Don't skip invert() on larger inputs.

ChangeLog
lib/Automake/Condition.pm
lib/Automake/DisjConditions.pm
lib/Automake/tests/Condition.pl
lib/Automake/tests/DisjConditions.pl

index 0963d405e765a3372e5259265005cc42c73359c9..2e2eaa4f0a80a1b36f5b9b9dd67edfa69c0cab5c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2003-03-29  Raja R Harinath  <harinath@acm.org>
+
+       Speedup Automake::DisjConditions::invert().
+       * lib/Automake/Condition.pm (multiply): New.
+       * lib/Automake/DisjConditions.pm (_multiply): Use it.
+       (sub_conditions): Likewise.
+
+       * lib/Automake/Condition.pm (reduce): Rename to ...
+       (reduce_and): ... this.
+       (reduce_or): New.
+       * lib/Automake/DisjConditions.pm (_multiply): Use reduce_or().
+       * lib/Automake/tests/Condition.pl (test_reduce): Rename to ...
+       (test_reduce_and): ... this.
+       (test_reduce_or): New.
+       * lib/Automake/tests/DisjConditions.pl 
+       (test_invert): Update to reflect effect of reduce_or().
+       (test_simplify): Don't skip invert() on larger inputs.
+
 2003-03-29  Raja R Harinath  <harinath@cs.umn.edu>
 
        * tests/yacc8.test: Add a $sleep before modifying Makefile.am so
index b1882d2af111d8fd955c57e58ac8246e29dbe61c..a9a3cb600d8816da91137fa1f7131995a4685dde 100644 (file)
@@ -22,7 +22,7 @@ use Carp;
 require Exporter;
 use vars '@ISA', '@EXPORT_OK';
 @ISA = qw/Exporter/;
-@EXPORT_OK = qw/TRUE FALSE reduce/;
+@EXPORT_OK = qw/TRUE FALSE reduce_and reduce_or/;
 
 =head1 NAME
 
@@ -80,10 +80,18 @@ Automake::Condition - record a conjunction of conditionals
   # (Not in this example)
   if ($cond->implies_any ($other, $both)) { ... }
 
-  # Remove superfluous conditionals.
-  # (Returns @cons = ($both) in this example, because
+  # Remove superfluous conditionals assuming they will eventually
+  # be multiplied together.
+  # (Returns @conds = ($both) in this example, because
   # $other and $cond are implied by $both.)
-  @conds = Automake::Condition::reduce ($other, $both, $cond);
+  @conds = Automake::Condition::reduce_and ($other, $both, $cond);
+
+  # Remove superfluous conditionals assuming they will eventually
+  # be summed together.
+  # (Returns @conds = ($cond, $other) in this example, because
+  # $both is a subset condition of $cond: $cond is true whenever $both
+  # is true.)
+  @conds = Automake::Condition::reduce_or ($other, $both, $cond);
 
   # Invert a Condition.  This returns a list of Conditions.
   @conds = $both->not;
@@ -467,7 +475,7 @@ sub implies_any ($@)
 
 =item C<$cond-E<gt>not>
 
-Return a negation of @<$cond> as a list of C<Condition>s.
+Return a negation of C<$cond> as a list of C<Condition>s.
 This list should be used to construct a C<DisjConditions>
 (we cannot return a C<DisjConditions> from C<Automake::Condition>,
 because that would make these two packages interdependent).
@@ -487,6 +495,37 @@ sub not ($ )
   return @res;
 }
 
+=item C<$cond-E<gt>multiply (@conds)>
+
+Assumption: C<@conds> represent a disjunction of conditions.
+
+Return the result of multiplying C<$cond> with that disjunction.
+The result will be a list of conditions suitable to construct a
+C<DisjConditions>.
+
+=cut
+
+sub multiply ($@)
+{
+  my ($self, @set) = @_;
+  my %res = ();
+  for my $cond (@set)
+    {
+      my $ans = $self->merge ($cond);
+      $res{$ans} = $ans;
+    }
+
+  # FALSE can always be removed from a disjunction.
+  delete $res{FALSE};
+
+  # Now, $self is a common factor of the remaining conditions.
+  # If one of the conditions is $self, we can discard the rest.
+  return ($self, ())
+    if exists $res{$self};
+
+  return (values %res);
+}
+
 =head2 Other helper functions
 
 =over 4
@@ -504,16 +543,16 @@ The C<"FALSE"> conditional.
 use constant TRUE => new Automake::Condition "TRUE";
 use constant FALSE => new Automake::Condition "FALSE";
 
-=item C<reduce (@conds)>
+=item C<reduce_and (@conds)>
 
-Filter a list of conditions so that only the exclusive ones are
-retained.  For example, if both C<COND1_TRUE COND2_TRUE> and
-C<COND1_TRUE> are in the list, discard the latter.
-If the input list is empty, return C<(TRUE)>.
+Return a subset of @conds with the property that the conjunction of
+the subset is the same as the conjunction of @conds.  For example, if
+both C<COND1_TRUE COND2_TRUE> and C<COND1_TRUE> are in the list,
+discard the latter.  If the input list is empty, return C<(TRUE)>.
 
 =cut
 
-sub reduce (@)
+sub reduce_and (@)
 {
   my (@conds) = @_;
   my @ret = ();
@@ -536,6 +575,37 @@ sub reduce (@)
   return @ret;
 }
 
+=item C<reduce_or (@conds)>
+
+Return a subset of @conds with the property that the disjunction of
+the subset is equivalent to the disjunction of @conds.  For example,
+if both C<COND1_TRUE COND2_TRUE> and C<COND1_TRUE> are in the list,
+discard the former.  If the input list is empty, return C<(FALSE)>.
+
+=cut
+
+sub reduce_or (@)
+{
+  my (@conds) = @_;
+  my @ret = ();
+  my $cond;
+  while (@conds > 0)
+    {
+      $cond = shift @conds;
+
+      next
+       if $cond == FALSE;
+      return TRUE
+       if $cond == TRUE;
+
+      push (@ret, $cond)
+       unless $cond->implies_any (@ret, @conds);
+    }
+
+  return FALSE if @ret == 0;
+  return @ret;
+}
+
 =item C<conditional_negate ($condstr)>
 
 Negate a conditional string.
index 48dca6eb2a28642f3443cfa712a5dae91aca826a..1a722243926d3e0ee5ad984caf29bb9e51e703ed 100644 (file)
@@ -289,7 +289,7 @@ sub human ($ )
 }
 
 
-=item C<$prod = $set1->multiply ($set2)>
+=item C<$prod = $set1-E<gt>multiply ($set2)>
 
 Multiply two conditional sets.
 
@@ -317,15 +317,8 @@ The argument can also be a C<Condition>.
 sub _multiply ($@)
 {
   my ($self, @set) = @_;
-  my @res = ();
-  foreach my $selfcond ($self->conds)
-    {
-      foreach my $setcond (@set)
-       {
-         push @res, $selfcond->merge ($setcond);
-       }
-    }
-  return new Automake::DisjConditions @res;
+  my @res = map { $_->multiply (@set) } $self->conds;
+  return new Automake::DisjConditions (Automake::Condition::reduce_or @res);
 }
 
 sub multiply ($$)
@@ -382,7 +375,7 @@ sub invert($ )
   return $res;
 }
 
-=item C<$simp = $set->simplify>
+=item C<$simp = $set-E<gt>simplify>
 
 Find prime implicants and return a simplified C<DisjConditions>.
 
@@ -630,7 +623,9 @@ sub simplify ($)
 =item C<$self-E<gt>sub_conditions ($cond)>
 
 Return the subconditions of C<$self> that contains C<$cond>, with
-C<$cond> stripped.
+C<$cond> stripped.  More formally, return C<$res> such that
+C<$res-E<gt>multiply ($cond) == $self-E<gt>multiply ($cond)> and
+C<$res> does not mention any of the variables in $cond.
 
 For instance, consider:
 
@@ -656,17 +651,16 @@ sub sub_conditions ($$)
   my ($self, $subcond) = @_;
 
   # Make $subcond blindingly apparent in the DisjConditions.
-  # For instance `$a->_multiply($b)' (from the POD example) is:
-  #   new Automake::DisjConditions
+  # For instance `$b->multiply($a->conds)' (from the POD example) is:
   #    (new Automake::Condition ("FALSE"),
   #     new Automake::Condition ("A_TRUE", "B_FALSE", "C_FALSE"),
   #     new Automake::Condition ("A_TRUE", "B_FALSE", "C_TRUE"),
-  #     new Automake::Condition ("FALSE"));
-  my $prod = $self->_multiply ($subcond);
+  #     new Automake::Condition ("FALSE"))
+  my @prodconds = $subcond->multiply ($self->conds);
 
   # Now, strip $subcond from the remaining (i.e., non-false) Conditions.
   my @res;
-  foreach my $c ($prod->conds)
+  foreach my $c (@prodconds)
     {
       push @res, $c->strip ($subcond) unless $c->false;
     }
index ea7bd6a6779f5743eef0aade42e76f7ea7ce769f..900f847e22b383d1087f2d5c4c93106c57398d5b 100644 (file)
@@ -90,9 +90,11 @@ sub test_true_when ()
   return $failed;
 }
 
-sub test_reduce ()
+sub test_reduce_and ()
 {
   my @tests = (# If no conditions are given, TRUE should be returned
+              [[], ["TRUE"]],
+              # An empty condition is TRUE
               [[""], ["TRUE"]],
               # A single condition should be passed through unchanged
               [["FOO"], ["FOO"]],
@@ -149,7 +151,7 @@ sub test_reduce ()
       my @inconds = map { new Automake::Condition $_ } @$inref;
       my @outconds = map { (new Automake::Condition $_)->string } @$outref;
       my @res =
-       map { $_->string } (Automake::Condition::reduce (@inconds));
+       map { $_->string } (Automake::Condition::reduce_and (@inconds));
       my $result = join (",", sort @res);
       my $exresult = join (",", @outconds);
 
@@ -164,7 +166,83 @@ sub test_reduce ()
   return $failed;
 }
 
-exit (test_basics || test_true_when || test_reduce);
+sub test_reduce_or ()
+{
+  my @tests = (# If no conditions are given, FALSE should be returned
+              [[], ["FALSE"]],
+              # An empty condition is TRUE
+              [[""], ["TRUE"]],
+              # A single condition should be passed through unchanged
+              [["FOO"], ["FOO"]],
+              [["FALSE"], ["FALSE"]],
+              [["TRUE"], ["TRUE"]],
+              # FALSE and TRUE should be discarded and overwhelm
+              # the result, respectively
+              [["FOO", "TRUE"], ["TRUE"]],
+              [["FOO", "FALSE"], ["FOO"]],
+              # Repetitions should be removed
+              [["FOO", "FOO"], ["FOO"]],
+              [["FALSE", "FOO", "FOO"], ["FOO"]],
+              [["FOO", "FALSE", "FOO"], ["FOO"]],
+              [["FOO", "FOO", "FALSE"], ["FOO"]],
+              # Two different conditions should be preserved,
+              # but FALSEs should be removed
+              [["FOO", "BAR"], ["BAR,FOO"]],
+              [["FALSE", "FOO", "BAR"], ["BAR,FOO"]],
+              [["FOO", "FALSE", "BAR"], ["BAR,FOO"]],
+              [["FOO", "BAR", "FALSE"], ["BAR,FOO"]],
+              # A condition implying another condition should be removed.
+              [["FOO BAR", "BAR"], ["BAR"]],
+              [["BAR", "FOO BAR"], ["BAR"]],
+              [["FALSE", "FOO BAR", "BAR"], ["BAR"]],
+              [["FOO BAR", "FALSE", "BAR"], ["BAR"]],
+              [["FOO BAR", "BAR", "FALSE"], ["BAR"]],
+
+              [["BAR FOO", "BAR"], ["BAR"]],
+              [["BAR", "BAR FOO"], ["BAR"]],
+              [["FALSE", "BAR FOO", "BAR"], ["BAR"]],
+              [["BAR FOO", "FALSE", "BAR"], ["BAR"]],
+              [["BAR FOO", "BAR", "FALSE"], ["BAR"]],
+
+              # Check that reduction happens even when there are
+              # two conditions to remove.
+              [["FOO", "FOO BAR", "BAR"], ["BAR,FOO"]],
+              [["FOO", "FOO BAR", "BAZ", "FOO BAZ"], ["BAZ,FOO"]],
+              [["FOO", "FOO BAR", "BAZ", "FOO BAZ", "FOO BAZ BAR"],
+               ["BAZ,FOO"]],
+
+              # Duplicated condionals should be removed
+              [["FOO", "BAR", "BAR"], ["BAR,FOO"]],
+
+              # Equivalent conditions in different forms should be
+              # reduced: which one is left is unfortunately order
+              # dependent.
+              [["BAR FOO", "FOO BAR"], ["FOO BAR"]],
+              [["FOO BAR", "BAR FOO"], ["BAR FOO"]]);
+
+  my $failed = 0;
+  foreach (@tests)
+    {
+      my ($inref, $outref) = @$_;
+      my @inconds = map { new Automake::Condition $_ } @$inref;
+      my @outconds = map { (new Automake::Condition $_)->string } @$outref;
+      my @res =
+       map { $_->string } (Automake::Condition::reduce_or (@inconds));
+      my $result = join (",", sort @res);
+      my $exresult = join (",", @outconds);
+
+      if ($result ne $exresult)
+       {
+         print '"'.join(",", @$inref) . '" => "' .
+           $result . '" expected "' .
+             $exresult . '"' . "\n";
+         $failed = 1;
+       }
+    }
+  return $failed;
+}
+
+exit (test_basics || test_true_when || test_reduce_and || test_reduce_or);
 
 ### Setup "GNU" style for perl-mode and cperl-mode.
 ## Local Variables:
index b5147ebcac206c4b5a945b8af8262679cedaf9e3..9df54deb4ace54c57412c225899b8838d0770f7e 100644 (file)
@@ -55,8 +55,6 @@ sub test_invert ()
               [[["COND1_TRUE", "COND2_TRUE"],
                 ["COND3_FALSE", "COND2_TRUE"]],
                [["COND2_FALSE"],
-                ["COND2_FALSE", "COND3_TRUE"],
-                ["COND1_FALSE", "COND2_FALSE"],
                 ["COND1_FALSE", "COND3_TRUE"]]],
 
               [[["COND1_TRUE", "COND2_TRUE"],
@@ -247,9 +245,6 @@ sub test_simplify ()
 
       # Also exercize invert() while we are at it.
 
-      # FIXME: Can't run invert() with too much conditions, this is too slow.
-      next if $#{$t->[0][0]} > 8;
-
       my $inv1 = $set->invert->simplify;
       my $inv2 = $sim->invert->simplify;
       if ($inv1 != $inv2)