* 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@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
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
# (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;
=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).
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
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 = ();
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.
}
-=item C<$prod = $set1->multiply ($set2)>
+=item C<$prod = $set1-E<gt>multiply ($set2)>
Multiply two conditional sets.
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 ($$)
return $res;
}
-=item C<$simp = $set->simplify>
+=item C<$simp = $set-E<gt>simplify>
Find prime implicants and return a simplified C<DisjConditions>.
=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:
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;
}
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"]],
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);
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:
[[["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"],
# 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)