From: Raja R Harinath Date: Sun, 30 Mar 2003 01:56:28 +0000 (+0000) Subject: Speedup Automake::DisjConditions::invert(). X-Git-Tag: Release-1-7-3b~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9319bbc5773f53251513a293ddabe62cbcc39dff;p=thirdparty%2Fautomake.git 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. --- diff --git a/ChangeLog b/ChangeLog index 0963d405e..2e2eaa4f0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2003-03-29 Raja R Harinath + + 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 * tests/yacc8.test: Add a $sleep before modifying Makefile.am so diff --git a/lib/Automake/Condition.pm b/lib/Automake/Condition.pm index b1882d2af..a9a3cb600 100644 --- a/lib/Automake/Condition.pm +++ b/lib/Automake/Condition.pm @@ -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-Enot> -Return a negation of @<$cond> as a list of Cs. +Return a negation of C<$cond> as a list of Cs. This list should be used to construct a C (we cannot return a C from C, because that would make these two packages interdependent). @@ -487,6 +495,37 @@ sub not ($ ) return @res; } +=item C<$cond-Emultiply (@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. + +=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 +=item C -Filter a list of conditions so that only the exclusive ones are -retained. For example, if both C and -C 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 and C 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 + +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 and C 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 Negate a conditional string. diff --git a/lib/Automake/DisjConditions.pm b/lib/Automake/DisjConditions.pm index 48dca6eb2..1a7222439 100644 --- a/lib/Automake/DisjConditions.pm +++ b/lib/Automake/DisjConditions.pm @@ -289,7 +289,7 @@ sub human ($ ) } -=item C<$prod = $set1->multiply ($set2)> +=item C<$prod = $set1-Emultiply ($set2)> Multiply two conditional sets. @@ -317,15 +317,8 @@ The argument can also be a C. 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-Esimplify> Find prime implicants and return a simplified C. @@ -630,7 +623,9 @@ sub simplify ($) =item C<$self-Esub_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-Emultiply ($cond) == $self-Emultiply ($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; } diff --git a/lib/Automake/tests/Condition.pl b/lib/Automake/tests/Condition.pl index ea7bd6a67..900f847e2 100644 --- a/lib/Automake/tests/Condition.pl +++ b/lib/Automake/tests/Condition.pl @@ -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: diff --git a/lib/Automake/tests/DisjConditions.pl b/lib/Automake/tests/DisjConditions.pl index b5147ebca..9df54deb4 100644 --- a/lib/Automake/tests/DisjConditions.pl +++ b/lib/Automake/tests/DisjConditions.pl @@ -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)