# Copyright (c) 1997-2006
# Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
# http://www.math.tu-berlin.de/polymake, mailto:polymake@math.tu-berlin.de
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any
# later version: http://www.gnu.org/licenses/gpl.txt.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#----------------------------------------------------------------------------
# $Project: polymake $$Id: Scheduler.pm 7361 2006-05-02 21:37:19Z gawrilow $
use strict;
use namespaces;
####################################################################################
#
# A rule chain under construction
#
package Poly::RuleChain;
use Enum 'Poly::Rule::exec';
use Struct (
[ '$weight' => 'new Poly::Rule::Weight' ], # sum weight of the rules
'@rules', # (Rule) rules scheduled for execution, in proper order
'%consumer', # Rule => { Rule => 1 }
# outgoing arcs in rule precedence graph: rules consuming the output of the given rule
'%supplier', # Rule => [ { Rule => 1 } ]
# incoming arcs in rule precedence graph: rules producing data needed as input by given rule.
# Suppliers are grouped by input properties of the given rule. As soon as the
# supplier list become empty, the rule is put in the 'ready' list (below).
'%sensitive_consumer', # Rule => { Property => [ Rule ] }
'%sensitive_supplier', # Rule => { Property => [ { Rule => 1 } ] }
# additional supplier lists triggered by other rules scheduled prior to this
'@ready', # (Rule) rules whose inputs are all resolved, that is, created by other rules scheduled earlier.
'%preferred', # Rule => [ [ preferred Rule ] ]
# Rule can be taken only if at least one list of preferred Rules completely consists
# of infeasible or previously failed rules.
'$final', # Rule final rule (usually request)
'%run', # (precondition or weight) Rule => success code
'%dyn_weight', # weight Rule => result
$Switches::d>1 || exists $DB::{DB} ? ( # attributes useful for debugging
'$id', # unique identifier
'$children', # number of variants derived from this chain
) : ( ),
);
my $sub_clone=<<'_#_#_#_';
sub {
my ($self)=@_;
#CLONE_PROLOGUE
bless [
$self->weight,
(map { deep_copy($_) } $self->rules, $self->consumer, $self->supplier,
$self->sensitive_consumer, $self->sensitive_supplier, $self->ready),
$self->preferred, $self->final, $self->run, $self->dyn_weight,
#CLONE_TRAILER
], "Poly::RuleChain";
}
_#_#_#_
if ($Switches::d>1 || exists $DB::{DB}) {
$sub_clone =~ s'#CLONE_TRAILER'$self->id.".".(++$self->children), 0,';
require Poly::Scheduler_debug;
}
*clone=eval $sub_clone;
if ($Switches::d || $Switches::v>=2) {
# don't expect it installed everywhere
eval { require Time::HiRes };
if ($@) {
*gettimeofday=sub { times };
*tv_interval=sub { (times)[0]-(shift)->[0] };
} else {
import Time::HiRes qw( gettimeofday tv_interval );
}
}
####################################################################################
package Poly::InitRuleChain;
use Enum 'Poly::Rule::exec';
use Struct (
[ new => '$$' ],
[ '@ISA' => 'Poly::RuleChain' ],
[ '$final' => '#2' ],
$Switches::d>1 || exists $DB::{DB} ? (
[ '$id' => '"0"' ],
[ '$children' => '0' ],
) : ( ),
[ '@Tstart' => '($Switches::v==2 && gettimeofday())' ],
'%producer',
);
if ($Switches::d>1 || exists $DB::{DB}) {
$sub_clone =~ s'#CLONE_PROLOGUE'++$self->id; $self->children=0;';
}
*clone=eval $sub_clone;
####################################################################################
package Poly::RuleChain;
# eliminates given rules, as well as rules becoming infeasible (without supplier)
# or useless (without consumer) due to this elimination
my $sizeof=sizeof();
sub eliminate_rules { # Rule, ... =>
my $self=shift;
my @elim=@_;
my $init= $#$self >= $sizeof;
my $verbose= $init && $Switches::v>2;
my %marked;
@marked{@elim}=();
while (defined (my $rule=shift @elim)) {
if (!$init) {
# if all consumer of a scheduled rule have gone, this variant has no sense more.
foreach my $sched_rule (@{$self->rules}) {
my $sched_cons=$self->consumer->{$sched_rule};
if (delete $sched_cons->{$rule} and !keys %{$sched_cons}) {
undef $self->final;
return;
}
}
}
# some rules depending on the one being eliminated may become infeasible
if (defined (my $cons=$self->consumer->{$rule})) {
foreach my $cons_rule (keys %$cons) {
next if exists $marked{$cons_rule};
# each set of suppliers must contain at least one feasible rule
my $supp_list=$self->supplier->{$cons_rule};
next unless is_ARRAY($supp_list);
foreach my $supp_group (@$supp_list) {
if (delete $supp_group->{$rule}) {
if (! keys %$supp_group) {
dbg_print( " discarding ", $cons_rule->header, ": no more supplier" )
if $verbose;
if ($cons_rule == $self->final) { # request become infeasible
undef $self->final;
return;
}
push @elim, $cons_rule;
$marked{$cons_rule}=1;
last;
}
}
}
}
delete $self->consumer->{$rule};
}
# some supplier of the current rule may become useless
if (defined (my $supp_list=$self->supplier->{$rule})) {
foreach my $supp_group (@$supp_list) {
foreach my $supp_rule (keys %$supp_group) {
next if exists $marked{$supp_rule};
my $cons=$self->consumer->{$supp_rule};
delete $cons->{$rule};
if (! keys %$cons) {
dbg_print( " discarding ", $supp_rule->header, ": no more consumer" )
if $verbose;
delete $self->consumer->{$supp_rule};
push @elim, $supp_rule;
$marked{$supp_rule}=1;
}
}
}
delete $self->supplier->{$rule};
}
delete $self->sensitive_supplier->{$rule};
delete $self->preferred->{$rule} if $init;
}
}
####################################################################################
sub add_rule { # Rule =>
my ($self, $rule)=@_;
# check the consumers - some of them might become ready
my @to_elim;
foreach my $cons_rule (keys %{$self->consumer->{$rule}}) {
my $supp_list=$self->supplier->{$cons_rule};
next unless is_ARRAY($supp_list);
my %supplier;
for (my $i=0; $i<=$#$supp_list; ) {
my $supp_group=$supp_list->[$i];
if (delete $supp_group->{$rule}) {
foreach my $supp_rule (keys %$supp_group) {
$supplier{$supp_rule} |= 1;
}
splice @$supp_list, $i, 1;
} else {
foreach my $supp_rule (keys %$supp_group) {
$supplier{$supp_rule} |= 2;
}
++$i;
}
}
if (!@$supp_list) {
push @{$self->ready}, $cons_rule;
}
while (my ($supp_rule, $code)=each %supplier) {
if ($code==1) {
my $cons=$self->consumer->{$supp_rule};
delete $cons->{$cons_rule};
# found a superfluous supplier
push @to_elim, $supp_rule if !keys %$cons;
}
}
}
if (defined (my $sc=$self->sensitive_consumer->{$rule})) {
while (my ($generator, $rule_list)=each %$sc) {
CONS_RULE:
foreach my $cons_rule (@$rule_list) {
if (ref($self->supplier->{$cons_rule}) and
defined (my $suppliers=delete $self->sensitive_supplier->{$cons_rule}->{$generator})) {
# neither scheduled nor eliminated yet nor triggered by another rule producing sensitive props
foreach my $supp_rule (keys %$suppliers) {
if (defined (my $supp=$self->supplier->{$supp_rule})) {
if (!ref($supp)) {
# the supplier is already scheduled or run - nothing to be afraid of
next CONS_RULE;
}
} else {
# the supplier is eliminated
delete $suppliers->{$supp_rule};
}
}
if (keys %$suppliers) {
# some rules not scheduled yet can deliver the data required for the diff -
# make them regular supplier of the concerned rule
push @{$self->supplier->{$cons_rule}}, $suppliers;
$self->consumer->{$_}->{$rule}=1 for keys %$suppliers;
} else {
# no suppliers avaliable - the rule becomes infeasbile
push @to_elim, $cons_rule;
}
}
}
}
}
$self->eliminate_rules(@to_elim);
}
####################################################################################
sub add_ready_rule {
my ($self, $rule)=@_;
push @{$self->rules}, $rule;
$self->supplier->{$rule}='incl';
$self->weight += $rule->weight;
if (defined (my $dwr=$rule->dyn_weight)) {
# if the dynamic weight is not computed yet, this variant will not be executed as is.
# instead, it might be rebuilt after all postponed preconditions are checked
if (defined (my $dw=$self->dyn_weight->{$dwr})) {
$self->weight += $dw;
}
}
my $pref=$self->preferred->{$rule};
if (defined($pref) && @$pref) {
$self->weight->set_atom($Poly::Rule::Weight::max_major, 1); # preference violation penalty
}
&add_rule;
}
####################################################################################
sub constrain { # ( Rules ) => ( Rules with suppliers )
my $self=shift;
my %keep;
@keep{@_}=();
my $rules=$self->rules;
my @result;
# visit in reverse order, since the consumer are always executed later than their supplier
RULE:
for (my $i=$#$rules; $i>=0; --$i) {
my $rule=$rules->[$i];
if (exists $keep{$rule}) {
unshift @result, $rule;
splice @$rules, $i, 1; # it's a precondition rule - remove it
next;
}
foreach my $cons_rule (keys %{$self->consumer->{$rule}}) {
if (exists $keep{$cons_rule}) {
$keep{$rule}=1;
unshift @result, $rule;
next RULE;
}
}
}
@result;
}
####################################################################################
sub execute { # => number of the failed rule
my ($self, $object, $rules)=@_;
$rules ||= $self->rules;
my $i=0;
my $weight_added;
foreach my $rule (@$rules) {
my ($rc, $retval)=$rule->execute($object);
$self->run->{$rule}=$rc;
if ($rc != $exec_OK) {
if ($@) {
chomp $@;
warn_print( "rule ", $rule->header, " failed: $@" );
undef $@;
}
return $i;
}
if (wantarray && !ref($rule->output) && $rule->output==$Poly::Rule::is_dyn_weight) {
$weight_added=1;
$self->dyn_weight->{$rule}=$retval;
$self->weight += $retval;
}
++$i;
}
wantarray ? (undef, $weight_added) : undef;
}
####################################################################################
sub report {
my ($self)=@_;
return @{$self->rules}
? ( (map { (" ", $_->header, "\n") } @{$self->rules}),
" Sum weight=", $self->weight )
: ("nothing to do");
}
####################################################################################
package Poly::InitRuleChain;
sub good_rule($$$) {
my ($self, $object, $rule)=@_;
my $rc;
!$object->failed_rules->{$rule} && (!defined($rc=$self->run->{$rule}) || $rc != $exec_failed)
}
####################################################################################
# find all rules supplying one of the properties of the given input list of the given rule
# => { Rule => 1 }
sub find_suppliers($$$) {
my ($self, $object, $input)=@_;
my %supp_group;
foreach my $prop (@$input) {
foreach my $rule (@{ $self->producer->{$prop} ||= do {
my $method=$object->can($prop->prod_method);
[ grep { good_rule($self, $object, $_) } @{$method->($object)} ]
}}) {
if (!defined $self->supplier->{$rule}) {
$self->supplier->{$rule}=[ ];
push @{$self->rules}, $rule;
}
$supp_group{$rule}=1;
}
}
return \%supp_group;
}
####################################################################################
# private:
# => undef if applicable, 'text' if infeasible
sub rule_status($$$$) {
my ($self, $object, $rule, $verbose)=@_;
$self->supplier->{$rule} ||= [ ];
foreach my $input_list (@{$rule->input}) {
if (! $object->eval_input_list($input_list)) {
my $suppliers=find_suppliers($self, $object, $input_list);
if (keys %$suppliers) {
push @{$self->supplier->{$rule}}, $suppliers;
$self->consumer->{$_}->{$rule}=1 for keys %$suppliers;
} else {
return $verbose && "no available rules to produce " . join(" | ", map { $_->name } @$input_list);
}
}
}
while (my ($generator, $input_list)=each %{$rule->triggered_diff}) {
if (! $object->eval_input_list($input_list)) {
my $positive_suppliers=find_suppliers($self, $object, $input_list);
delete $positive_suppliers->{$rule};
my %positive; @positive{@$input_list}=();
my @negative=grep { !exists $positive{$_} } @{$generator->diff_sensitive}
or next;
if ($object->eval_input_list(\@negative)) {
if (keys %$positive_suppliers) {
push @{$self->supplier->{$rule}}, $positive_suppliers;
$self->consumer->{$_}->{$rule}=1 for keys %$positive_suppliers;
} else {
return $verbose && (@$input_list
? "no available rules to produce " .
join(" | ", map { $_->name } @$input_list) .
" needed to synchronize output sensitive to diff(".$generator->name.")"
: "produces from scratch output sensitive to diff(".$generator->name.
") but other sensitive properties already exist");
}
} else {
# preserve up to the end of initial()
$self->sensitive_supplier->{$rule}->{$generator}=[ \@negative, $positive_suppliers ];
}
}
}
undef;
}
####################################################################################
# private:
sub resolve_sensitive_suppliers {
my ($self)=@_;
while (my ($rule, $dict)=each %{$self->sensitive_supplier}) {
while (my ($generator, $list)=each %$dict) {
my ($negative, $positive_suppliers)=@$list;
my $cnt=0;
my %seen=%$positive_suppliers;
foreach my $prop (@$negative) {
if (defined (my $producer=$self->producer->{$prop})) {
foreach my $prod_rule (@$producer) {
if (exists $self->supplier->{$prod_rule} and !exists $seen{$prod_rule}) {
++$cnt;
push @{$self->sensitive_consumer->{$prod_rule}->{$generator}}, $rule;
$seen{$prod_rule}=1; # avoid multiple inclusion
}
}
}
}
if ($cnt) {
$dict->{$generator}=$positive_suppliers;
} else {
# no danger: none of the possible sensitive properties is going to be created in this schedule
delete $dict->{$generator};
}
}
if (!keys %$dict) {
# all concerned diff groups are safe
delete $self->sensitive_supplier->{$rule};
}
}
}
####################################################################################
# Object, final Rule =>
sub new {
my $self=&_new;
my ($object)=@_;
my $verbose=$Switches::v>2;
dbg_print( "gathering viable rules" ) if $verbose;
$self->consumer->{$self->final}={ };
if (defined (my $explain=rule_status($self, $object, $self->final, $verbose))) {
dbg_print( " infeasible: ", $self->final->header, ": $explain" ) if $explain;
return undef;
}
$self;
}
####################################################################################
# protected:
sub gather_rules {
my ($self, $object)=@_;
my $verbose=$Switches::v>2;
my (@infeasible, %labels);
# breadth-first search in the implicit graph of object states
RULE:
while (defined (my $rule=shift @{$self->rules})) {
foreach my $precond_rule (@{$rule->preconditions}) {
if (!good_rule($self, $object, $precond_rule)) {
dbg_print( " infeasible: ", $rule->header, ": failed ", $precond_rule->header, " (tested earlier)" )
if $verbose;
push @infeasible, $rule;
next RULE;
}
next if $self->run->{$precond_rule} == $exec_OK; # precondition was successfully checked earlier
if (defined (my $explain=rule_status($self, $object, $precond_rule, $verbose))) {
dbg_print( " infeasible: ", $precond_rule->header, ": $explain\n",
" infeasible: ", $rule->header, " due to failed precondition above" )
if $explain;
push @infeasible, $rule, $precond_rule;
$self->run->{$precond_rule}=$exec_failed;
$self->run->{$rule}=$exec_failed;
next RULE;
}
if (! @{$self->supplier->{$precond_rule}}) {
dbg_print( " ready to evaluate: ", $precond_rule->header )
if $verbose;
if ($Switches::n) {
# we don't apply any rules in dry run mode, even if they are ready to use.
# we must just show it in the final schedule
push @{$self->ready}, $precond_rule;
} else {
my ($rc, $retval)=$precond_rule->execute($object);
if (($self->run->{$precond_rule}=$rc) != $exec_OK) {
if ($@) {
chomp $@;
warn_print( "precondition ", $precond_rule->header, " failed: $@" );
undef $@;
}
dbg_print( " infeasible: ", $rule->header, " due to failed precondition above" )
if $verbose;
push @infeasible, $rule;
$self->run->{$rule}=$exec_failed;
next RULE;
}
$self->dyn_weight->{$precond_rule}=$retval if $precond_rule->output == $Poly::Rule::is_dyn_weight;
next;
}
}
# the precondition rule must be evaluated later, let it become an exclusive supplier of the current production rule
$self->consumer->{$precond_rule}->{$rule}=1;
push @{$self->supplier->{$rule}}, { $precond_rule => 1 };
}
if (defined (my $explain=rule_status($self, $object, $rule, $verbose))) {
dbg_print( " infeasible: ", $rule->header, ": $explain" ) if $explain;
push @infeasible, $rule;
$self->run->{$rule}=$exec_failed;
next RULE;
} elsif (! @{$self->supplier->{$rule}}) { # rule can be immediately applied
push @{$self->ready}, $rule;
dbg_print( " ready to use: ", $rule->header ) if $verbose;
} else {
dbg_print( " applicable: ", $rule->header ) if $verbose;
}
foreach my $label (@{$rule->labels}) {
my $family=$label->wildcard_name;
if (! $labels{$family}++) {
my (@pref, @next);
foreach my $bag (Poly::Preference::Label::get_items_by_rank($object->$family())) {
shift @$bag; # get rid of the rank
@next=grep { good_rule($self, $object, $_) } @$bag;
push @{$self->preferred->{$_}}, @pref for @next;
push @pref, @next;
}
}
}
}
$self->check_rule_graph if $Switches::d>1;
$self->eliminate_rules(@infeasible);
$self->resolve_sensitive_suppliers;
$self->squeeze_prefs;
$self->dump if $Switches::d>=3;
}
####################################################################################
#
# Create the initial rule chain
#
# Object, final Rule =>
sub Poly::RuleChain::initial {
shift; unshift @_, "Poly::InitRuleChain";
my $self=&Poly::InitRuleChain::new
or return undef;
gather_rules($self,@_);
$self;
}
####################################################################################
sub squeeze_prefs {
my ($self)=@_;
my $rc;
while (my ($rule, $list)=each %{$self->preferred}) {
next if !exists $self->supplier->{$rule}; # not relevant
my $r=0;
while ($r<=$#$list) {
my $pref_rule=$list->[$r];
if (!exists $self->supplier->{$pref_rule} # eliminated
|| ($rc=$self->run->{$pref_rule}, defined($rc) && $rc==$exec_failed)) {
splice @$list, $r, 1;
} else {
++$r;
}
}
if ($Switches::v>2) {
if (!$r) {
dbg_print( " preferred: ", $rule->header );
} else {
dbg_print( " not preferred: ", $rule->header,
map { "\n after ".$_->header."\n" } @$list );
}
}
}
}
####################################################################################
sub constrain_to_preferred {
my ($self)=@_;
my @elim;
foreach my $supp_group (@{$self->supplier->{$self->final}}) {
foreach my $supp_rule (keys %$supp_group) {
my $pref=$self->preferred->{$supp_rule};
push @elim, $supp_rule if !defined($pref) || @$pref;
if ($Switches::v>2 and !defined($pref) || @$pref) {
dbg_print( " excluding ", $supp_rule->header );
}
}
}
$self->eliminate_rules(@elim);
die "no available preferred rules left over\n" if !defined($self->final);
}
####################################################################################
sub add_run_rules {
my ($self, $rules, $last_rule)=@_;
# remember: last_rule (if defined) is the index of the failed rule (in $rules)
my $last_failed=defined $last_rule;
$last_rule=$#$rules unless $last_failed;
my $last_succeeded=$last_rule-$last_failed;
# resolve the consumers of the successful rules
foreach my $rule (@$rules[0 .. $last_succeeded]) {
$self->add_rule($rule);
$self->supplier->{$rule}='run';
delete $self->consumer->{$rule}; # force it to be filtered out of @ready during the cloning
}
if ($last_failed) {
$self->eliminate_rules($rules->[$last_rule]);
$self->squeeze_prefs;
}
}
####################################################################################
package Poly::Scheduler::Heap;
sub new {
shift;
bless [ @_ ];
}
sub push {
my ($self, $var)=@_;
push @$self, $var;
my $i=$#$self;
while ($i>0) {
my $parent=($i-1)/2;
last if $var->weight >= $self->[$parent]->weight;
$self->[$i]=$self->[$parent];
$i=$parent;
}
$self->[$i]=$var;
}
sub pop {
my ($self)=@_;
my $top=$self->[0];
if ($#$self>0) {
my $var=pop @$self;
my $i=0;
while ((my $child=$i*2+1)<=$#$self) {
$child++ if $child<$#$self
and $self->[$child]->weight > $self->[$child+1]->weight;
last if $var->weight <= $self->[$child]->weight;
$self->[$i]=$self->[$child];
$i=$child;
}
$self->[$i]=$var;
} else {
@$self=();
}
$top
}
sub reset {
my $self=shift;
@$self=@_;
}
####################################################################################
package Poly::InitRuleChain;
# return value:
# => 1 if some rule chain successfully executed (or there was nothing to do)
# => 0 if no suitable rule chain found
# => -n if n different rule chains tried, but no succeeded (if max_tries defined, n<=max_tries)
# => RuleChain if max_tries==0 and the rule chain found would do
# => undef if max_tries==0 and no suitable rule chain found
sub resolve {
my ($self, $object, $max_tries)=@_;
my $tries=0;
if (defined($self->final)
and do {
if (!@{$self->supplier->{$self->final}}) {
return $self if defined($max_tries) && !$max_tries;
dbg_print( "nothing to do" ) if $Switches::v>2;
return 1;
}
@{$self->ready}
}) {
dbg_print( "composing a minimum weight rule chain" ) if $Switches::v>2;
my ($maxheap, $popcnt)=(1,0);
my $heap=new Poly::Scheduler::Heap($self->clone);
while (defined (my $top=$heap->pop)) {
++$popcnt;
if ($Switches::d>1) {
if (defined $top->final) {
$top->announce_pop;
} else {
$top->dump;
die "dead variant in heap!\n";
}
}
if (@{$top->rules} and
@{$top->supplier->{$self->final}} || defined($top->rules->[-1]->dyn_weight) and
!$Switches::n and
my @postponed= grep { !defined $top->run->{$_} } @{$top->rules->[-1]->preconditions}) {
# the rule added last has unchecked preconditions, and the rule chain is not complete:
# it's the good moment to check them now
dbg_print( "checking postponed preconditions" ) if $Switches::v>2;
my @to_run=$top->constrain(@postponed);
my ($last_failed, $weight_added)=$top->execute($object, \@to_run);
$self->add_run_rules(\@to_run, $last_failed);
last if !defined($self->final) or !@{$self->ready};
if (defined($last_failed) || @to_run != @postponed) {
$heap->reset($self->clone);
next;
} elsif ($weight_added) {
$heap->push($top);
next;
}
}
if (!@{$top->supplier->{$self->final}}) {
# ready
dbg_print( sprintf "minimum weight rule chain constructed in %.3f sec.", tv_interval($self->Tstart) )
if $Switches::v==2;
dbg_print( "|heap|: cur=", $#$heap+2, ", max=$maxheap, #pop=$popcnt\n" ) if $Switches::d;
return $top if defined($max_tries) && !$max_tries;
dbg_print( "rules to execute:\n", $top->report ) if $Switches::d;
my $last_failed=$top->execute($object);
return 1 if !defined $last_failed;
++$tries;
last if defined($max_tries) && $tries >= $max_tries;
if ($Switches::v>=2) {
dbg_print( "trying to find an alternative way" );
@{$self->Tstart}=gettimeofday() if $Switches::v==2;
}
$self->add_run_rules($top->rules, $last_failed);
last if !defined($self->final) or !@{$self->ready};
$heap->reset($self->clone);
$maxheap=1; $popcnt=0;
} else {
# not ready
my $ready_list=$top->ready; $top->ready=[];
while (1) {
my $rule=shift @$ready_list;
if (!defined ($top->consumer->{$rule})) {
# already run
@$ready_list ? next : last;
}
my $var= @$ready_list ? $top->clone : $top;
$var->add_ready_rule($rule);
if (defined($var->final)) {
# revise remaining ready rules - some of them might become useless
push @{$var->ready}, grep { defined($var->consumer->{$_}) } @$ready_list;
if (@{$var->ready}) {
$heap->push($var);
$var->announce_push if $Switches::d>1;
}
}
# eliminate the skipped rule in the stem variant
last if !@$ready_list
or $top->eliminate_rules($rule), !defined($top->final);
}
$maxheap=max($maxheap, $#$heap+1) if $Switches::d;
}
} # end while (heap)
}
return !defined($max_tries) || $max_tries>0 ? -$tries : undef;
}
####################################################################################
1
# Local Variables:
# c-basic-offset:3
# End:
syntax highlighted by Code2HTML, v. 0.9.1