#!/usr/bin/perl

#----Program: tptp2T
#----Selects Problems and Solutions satisfying given constraints
#----Alex Roederer, based on the tptp1T script by Geoff Sutcliffe
#----V2.3 Feb 17th, 2008
#----Fixed by Geoff April 2016

use strict "vars"; 
use File::Basename;
#--------------------------------------------------------------------------------------------------
#----Defines default TPTP home directory
my $TPTPDirectory = '/home/tptp/TPTP';

#----Replaces default with user defined
if (exists($ENV{TPTP})) {
    $TPTPDirectory = $ENV{TPTP};
}

#----Other directories, file names, etc
my $SortedProblemFileName = "/tmp/tptp2T_$$";
my $ScriptDir = dirname($0);
my $StatisticsFileName = "$ScriptDir/../Documents/ProblemAndSolutionStatistics";
#--------------------------------------------------------------------------------------------------
#----Define hashes that will represent the formated problem and solution
    my %ProblemIndexHash;
    my %SolutionIndexHash; 
    my %AbbreviationHash; 

#----Set indexes into array and file names
#----Indices
    %ProblemIndexHash = (
        'Problem' => 0,
        'Form' => 1,
        'Status' => 2,
        'Rating' => 3,
        'SPC' => 4,
        'Formulae' => 5, 
        'UnitFormulae' => 6, 
        'TypeFormulae' => 7,
        'Atoms' => 8,
        'EqualityAtoms' => 9,
        'Connectives' => 10,
        'FOOLs' => 11,
        'Arithmetics' => 12,
        'Types' => 13,
        'Symbols' => 14,
        'Predicates' => 15,
        'PredicateArities' => 16,
        'Functions' => 17,
        'FunctionArities' => 18, 
        'Variables' => 19,
        'Lambdas' => 20,
        'Universals' => 21,
        'Existentials' => 22,
        'PiUniversals' => 23,
        'NonClassicalConnectives' => 24,
        'MultiModalNCCs' => 25,
#----These are extra computed statistics, not taken from the data file
        'Domain' => 26, 
        'SupType' => 27, 
#----Number of statistics from data file
        'NumberOfStatistics' => 28,
    );

    %SolutionIndexHash = (
        'SystemName' => 0,
        'Result' => 1,
        'ResultTime' => 2, 
        'Output' => 3,
        'SZSType' => 4, 
        'SolutionDepth' => 5,
        'SolutionLeaves' => 6,
        'SolutionFormulae' => 7,
        'SolutionUnitFormulae' => 8, 
        'SolutionTypeFormulae' => 9,
        'SolutionAtoms' => 10,
        'SolutionEqualityAtoms' => 11,
        'SolutionConnectives' => 12,
        'SolutionFOOLs' => 13,
        'SolutionArithmetics' => 14,
        'SolutionTypes' => 15,
        'SolutionSymbols' => 16,
        'SolutionPredicates' => 17,
        'SolutionPredicateArities' => 18,
        'SolutionFunctions' => 19,
        'SolutionFunctionArities' => 20, 
        'SolutionVariables' => 21,
        'SolutionLambdas' => 22,
        'SolutionUniversals' => 23,
        'SolutionExistentials' => 24,
        'SolutionPIVariables' => 25,
        'SolutionNonClassicalConnectives' => 26,
        'SolutionMultiModalNCCs' => 27,
#----These are extra computed statistics, not taken from the data file
        'SOLNumberOfStatistics' => 28, #----Number from data file
    );

#----Converts full words into ontology abbreviations
    %AbbreviationHash = (
        'Success' => 'SUC',
        'Theorem' => 'THM',
        'ContradictoryAxioms' => 'CAX',
        'FiniteTheorem' => 'FTH',
        'CounterSatisfiable' => 'CSA',
        'Satisfiable' => 'SAT',
        'Unsatisfiable' => 'UNS',
        'FinitelySatisfiable' => 'FSA',
        'FinitelyUnsatisfiable' => 'FUN',

        'NoSuccess' => 'NOS',
        'Open' => 'OPN',
        'Timeout' => 'TMO',
        'GaveUp' => 'GUP',
        'Error' => 'ERR',
        'Unknown' => 'UNK',

        'Solution' => 'Sol',
        'Proof' => 'Prf',
        'Refutation' => 'Ref',
        'CNFRefutation' => 'CRf',
        'Model' => 'Mod',
        'FiniteModel' => 'FMo',
        'Saturation' => 'Sat',
        'Assurance' => 'Ass',
        'None' => 'Non',
    );

#----Call Main Function
    DoMain();
#--------------------------------------------------------------------------------------------------
#----DoMain Function determines if data is to be taken from arguments or files-
#--------------------------------------------------------------------------------------------------
sub DoMain {

#---Pushes STDERR to STDOUT
    close(STDERR);
    open(STDERR,">&STDOUT") || die("ERROR: Cannot redirect STDERR to STDOUT\n");

#----Flush output make pipes hot!
    $| = 1;

#----Check arguments, if none, given or -h is called, print help
    if (@ARGV == 0 || ($ARGV[0] eq "-h") || ($ARGV[0] eq "--h")) { 
        Usage();
        die("\n");
    }

#----Look if the data is to be taken from the files, or from the arguments
    if (@ARGV >= 2 && $ARGV[0] eq "-d") { 

#----Delete the -d
        shift @ARGV;
#DEBUG print("OUT CMND Statistics: @ARGV\n");
        CheckProblemFromCommandLineData(@ARGV);
    } else {
#DEBUG print("OUT LIST Statistics: @ARGV\n"); 
        ListProblemsFromStatistics(@ARGV);
    }
}
#--------------------------------------------------------------------------------------------------
#----Usage prints instructions for use if insufficient arguments provided
#--------------------------------------------------------------------------------------------------
sub Usage {

    print("Adapted from the tptp1T script by Alex Roederer, University of Miami, 2007

Usage -

tptp2T [-f <FileName>] [-q1 or -q2 or -q3] [-pp or -ps or -pps] {[-]<Constraint> {[and/or] [-]<Constraint>}}

<FileName> lists the problems to be considered (default is all).
-q sets quietness: 1=continuous update, 2=final count, 3=quiet
-pp prints problem lines, -ps prints solution lines, -pps prints both 
(Defaults: Only Problem Constraints = -pp
           Only Solution Constraints = -ps
           Both types of Constraints = -pps)
- (hyphen) negates the meaning of any constraint. 
or allows for logical or between constraints.
and allows for logical and between constraints.
a space between constraints is treated as an and. 
Curley braces {} allow for grouping of terms.  

For constraints in which an upper and lower bound are required,
a - (hyphen) may be used to indicate don't care. 

A <Constraint> is selected from: 
    
    Problem Constraints:

#----No differentiation between TH0/1 or TF0/1
    Domains ALG ANA ... TOP
    Form _ (one of THF, TXF, NHF, NXF, TFF, FOF, CNF, ANY - default is ANY)
    Status _ (one of Theorem, ContradictoryAxioms, CounterSatisfiable, Unsatisfiable, Satisfiable, Unknown, Open)
    Rating _ _
    Formulae _ _ 
    UnitFormulae _ _ 
    TypeFomulae _ _
    Atoms _ _
    EqualityAtoms _ _
    Equality
    PureEquality
    UnitEquality 
    FOOLs 
    Arithmetic 
    Types _ _ 
    Symbols _ _ 
    Predicates _ _ 
    PredicateArity _ _ 
    Propositional 
    Functions _ _ 
    FunctionArity _ _ 
    Variables
    Universals
    Existentials
    Lambdas
    PiUniversals
    NonClassicalConnectives
    MultiModalNCCs

    Solution Constraints (multiple sets allowed): 
    
    System Name[---Version] (System ANY for any system)
    Result _ (SZS value, e.g., THM) 
    ResultTime _ _
    Output _ (SZS value, e.g., Ref)
    SolutionFormulae _ _ 
    SolutionLeaves _ _
    SolutionDepth _ _
    SolutionAtoms _ _
    Equality
    Arithmetic
    Selectivity _ _
    Girth _ _
");
}
#--------------------------------------------------------------------------------------------------
#---This is antiquated and probably no longer supported. Use at your own peril.
sub CheckProblemFromCommandLineData {
    my (@StatisticsAndConstraints) = @_;

    my @Statistics;
    my $StatisticNumber;

#----Extract the constraints into arrays, first all into @Statistics
    @Statistics = split(/[ \t\n]+/,shift(@StatisticsAndConstraints));
#----Check that the next one is the -c flag
    if (scalar(@Statistics) != $ProblemIndexHash{'NumberOfStatistics'} ||
shift(@StatisticsAndConstraints) ne "-c") {
        die("ERROR: Incorrect number of statistics\n");
    }

#----Check the constraints and print an answer
#    if (CheckConstraints(\@StatisticsAndConstraints,\@Statistics)) {
#        print("yes\n");
#    } else {
        print("Sorry, this feature is not currently supported.\n");
#    }
}
#--------------------------------------------------------------------------------------------------
sub ListProblemsFromStatistics {
    my (@Constraints) = @_;

    my $PrintOption;
    my $QuietnessOption; 
    my $ProblemListFileHandle = "ProblemListFileHandle";
    my $StatisticsFileHandle = "StatisticsFileHandle";
    my $StatisticsLine;
    my $ProblemName;
    my @Statistics;
    my @TempStats; 
    my $ProblemType; 
    my $GoodProblem; 

    my @SolutionStatistics;
    my @SolutionLines; 

    my @ProblemStatistics;
    my $ProblemLine;  
    my $indexThroughProblems;
    my $SomeSolutionExists; 
    my $SystemChecksIndex; 
   
    my $Garbage; 
 
    my $FinalResultHolder; 
    my $ConstraintsStackArrayRef; 

    my $SystemChecksResults;         
    my $NumberOfSystems; 

    my $PrintFlag; 
    my $Counter;
    my $ProblemCounter = 0;
    my $SolutionCounter = 0; 
    my $IdleFlag = 0;
    my $CurrentDomain = "???"; 
    my $LastDomain = "???"; 

    my @GoodSolutions; 
    my @SolutionNamesNotToPrint;  
    my $SolutionNameNotToPrint; 

    my $CurrentConstraint = 0; 
    my @ConstraintsStack; 
    my @ConstraintsStackSave; 
    my $ConstraintsStackRef; 
    my @ConstraintsStackSaveForPrinting; 
    my $ResultValuePushedThrough = 1; 
    my $SystemAnyChosen; 

    my $PrintableLine;

#----If a file specified for problems to consider, use it
    if (@Constraints >= 2 && $Constraints[0] eq "-f") { 
#----Delete the -f
        shift @Constraints;
#----Sort the problem list so it has the same order as the statistics files
        system("sort -o $SortedProblemFileName -k 1.1,1.6 -k 1.7,1.7r -k 1.8n $Constraints[0]");
#----Delete the file name
        shift @Constraints; 
        open($ProblemListFileHandle,"<$SortedProblemFileName") || 
die("ERROR: Cannot open and sort $Constraints[0]\n");

#----Otherwise use the Statistics (all problems considered)
    } else {
        open($ProblemListFileHandle, "<$StatisticsFileName") || 
die "ERROR: Cannot open $StatisticsFileName";
    }

#----Look for quietness flag
    if ($Constraints[0] =~ /^-q/) {
        $QuietnessOption = shift(@Constraints); 
    } else {
        $QuietnessOption = "-q2"; 
    }

#----Look for print options flag
    if ($Constraints[0] =~ /^-p/) {
        $PrintOption = shift(@Constraints);
    } else {

        $PrintFlag = 0; 
        $Counter = 0;
        $PrintOption = "-pps"; 

        while (defined ($Constraints[$Counter])) {
            if (($Constraints[$Counter]) =~ /syst/i) {
                    $PrintFlag = 1;
            }
            $Counter++; 
        }
        if ($PrintFlag == 0) {
            $PrintOption = "-pp"; 
        } 
    }

#----Here we create @ConstraintsStack, that contains the system names and logical operators. This 
#----will be filled in to later check that the entire logical systems construct has been satisfied, 
#----before the problem is printed. 
    @ConstraintsStack = (); 
    $ConstraintsStackRef = CreateConstraintsStack(\@Constraints,\@ConstraintsStack); 
    @ConstraintsStack = @$ConstraintsStackRef; 
    @ConstraintsStackSave = @ConstraintsStack; 
#DEGBUG print("ConstraintsStack: @ConstraintsStack\n"); 

#----Open the statistics file
    open($StatisticsFileHandle,"<$StatisticsFileName") || 
die("ERROR: Cannot open $StatisticsFileName\n");

#----Print appropriate headers - get them from the statistics file
    $StatisticsLine = <$StatisticsFileHandle>;
    chomp($StatisticsLine);
    if ($StatisticsLine !~ /Problem/) {
        die("ERROR: No Problem header in statistics file\n");
    }
    if ($QuietnessOption le "-q2") {
        if ($PrintOption =~ /^-pp/) {
            print("$StatisticsLine\n");
        }
        if ($PrintOption =~ /s/) {
            $StatisticsLine = <$StatisticsFileHandle>;
            chomp($StatisticsLine);
            if ($StatisticsLine !~ /Solution/) {
                die("ERROR: No Problem header in statistics file\n");
            }
            $PrintableLine = $StatisticsLine;
            $PrintableLine =~ s/Solution   /   Solution/;
            print("$PrintableLine\n");
        }
    }

#----Look at each problem named in the "problem list"
    while (defined($ProblemName = GetNextProblemName($ProblemListFileHandle))) {
#----Extract the corresponding statistics lines from the statistics files
#DEBUG print("ProblemName block: $ProblemName\n");
        $StatisticsLine = <$StatisticsFileHandle>;
        chomp($StatisticsLine);
        while (defined($StatisticsLine) && $StatisticsLine !~ /^\Q${ProblemName}\E/) {
            $StatisticsLine = <$StatisticsFileHandle>;
            chomp($StatisticsLine);
        }
        if (!defined($StatisticsLine)) {
            die("ERROR: No statistics for $ProblemName\n");
        }

#----These will be used to hold the statistics lines. 
        @SolutionStatistics = (); 
        @SolutionLines = ();

#----For each statistics line (skipping blank lines)
        while (defined($StatisticsLine) && $StatisticsLine !~ /^ *$/) {
#----Split up the statistics line
            @Statistics = split (/\s+/, $StatisticsLine);
#----Check if length is "correct" (fits one of the three formats)
            if ($StatisticsLine =~ /^[A-Za-z]{3}[0-9]{3}\^/) {
                $ProblemType = "THF";
            } elsif ($StatisticsLine =~ /^[A-Za-z]{3}[0-9]{3}[_]/) {
                $ProblemType = "TFF";
            } elsif ($StatisticsLine =~ /^[A-Za-z]{3}[0-9]{3}\+/) {
                $ProblemType = "FOF";
            } elsif ($StatisticsLine =~ /^[A-Za-z]{3}[0-9]{3}-/) {
                $ProblemType = "CNF";
            } elsif ($StatisticsLine =~ /^.+---.+/)  {
                $ProblemType = "SOL";
            } else {
                die("ERROR: Cannot work out type for \"$StatisticsLine\"\n"); 
            }

#----Space statistics out according type of problem
            if ($ProblemType eq "THF" || $ProblemType eq "TFF" || $ProblemType eq "FOF" || 
$ProblemType eq "CNF") {
                $ProblemLine = $StatisticsLine; 
                $Statistics[$ProblemIndexHash{'Domain'}] = substr($Statistics[0],0,3); 
                $Statistics[$ProblemIndexHash{'SupType'}] = $ProblemType; 
                $#Statistics = $ProblemIndexHash{'SupType'};
#----Save the problem statistics (@Statistics now gets used for each solution)
                @ProblemStatistics = @Statistics;
            } elsif ($ProblemType eq "SOL") {
                push (@SolutionLines,$StatisticsLine); 
                $Statistics[$ProblemIndexHash{'SupType'}] = $ProblemType; 
                $#Statistics = $ProblemIndexHash{'SupType'};
#----Make a reference to a copy of the statistics and save in list
                push (@SolutionStatistics, [ @Statistics ]);
            } else {
                die("ERROR: Not THF, TFF, FOF, CNF, or SOL\n");
            }
            $StatisticsLine = <$StatisticsFileHandle>;
            chomp($StatisticsLine);
        }

#----Get the current domain 
        $CurrentDomain = $ProblemStatistics[$ProblemIndexHash{'Domain'}]; 

#DEBUG print("Checking $ProblemStatistics[$ProblemIndexHash{'Problem'}] with rating $ProblemStatistics[$ProblemIndexHash{'Rating'}]\n");

#----Call CheckConstraints on the Problem to determine Goodness. 
        ($GoodProblem,$Garbage) = CheckConstraints($ProblemStatistics[$ProblemIndexHash{'SupType'}],
\@ConstraintsStack,\@Constraints,\@ProblemStatistics,\@ProblemStatistics);
#DEBUG print("Good Problem: $GoodProblem\n");

#----IMMEDIATE ABORT: We've passed our last domain of interest
        if ($GoodProblem == -99) {
#----If required, print the prob and sol counts
            if ($QuietnessOption le "-q2") {
                print("\n\% $ProblemCounter problems and $SolutionCounter solutions found\n"); 
            }
#----Close the files, and exit back to main.
            close($StatisticsFileHandle); 
            close($ProblemListFileHandle);
            system("rm -f $SortedProblemFileName");
            return(1); 
        }
#----END OF IMMEDIATE ABORT

#----If we've found a good problem, start processing the solutions
        if ($GoodProblem) {
            @ConstraintsStack = @ConstraintsStackSave;
            $SomeSolutionExists = 0; 
            $indexThroughProblems = 0;
            @GoodSolutions = (); 

#----Go through the statistics lines. Each time through the loop, CheckConstraints is called.
#----It returns a Result and the Constraints Stack. The result is put into @GoodSolutions. The 
#----Stack is captured.
 
            while (defined($SolutionStatistics[$indexThroughProblems])) {
                ($FinalResultHolder,$ConstraintsStackArrayRef) = 
CheckConstraints("SOL",\@ConstraintsStack,\@Constraints,$SolutionStatistics[$indexThroughProblems],
\@ProblemStatistics);
                @ConstraintsStack = @$ConstraintsStackArrayRef; 
                $GoodSolutions[$indexThroughProblems] = $FinalResultHolder; 
                $indexThroughProblems++; 
            }
#DEBUG print("GoodSolutions: @GoodSolutions and @ConstraintsStack\n"); 
#DEBUG print("ConstraintsStack before changes: @ConstraintsStack\n");

            $CurrentConstraint = 0; 
            $SystemChecksIndex = 0;

#----All solution lines are finished, and the Constraints Stack is finished. 
#----If any of the S: markers in the ConstraintsStack don't have any
#----RES:* after them, we assume it's because that system constraint 
#----failed for all lines, so we set it to RES:0 
            while (defined($CurrentConstraint = $ConstraintsStack[$SystemChecksIndex])) {
                if ($CurrentConstraint =~ m/^S/ && $CurrentConstraint !~ m/RES:[0-9]+$/) {
                    $ConstraintsStack[$SystemChecksIndex] .= "RES:0";
                }
                $SystemChecksIndex++;  
            }
#DEBUG print("ConstraintsStack After First Sub: @ConstraintsStack\n"); 

            @ConstraintsStackSaveForPrinting = @ConstraintsStack; 
            $ResultValuePushedThrough = 1; 
            $SystemChecksIndex = 0;
           
#----Now, go through the ConstraintsStack, and mark off any 
#----lines that, while true, contribute nothing to the answer ( for example, 
#----1 and 0 would evaluate to 0, and the first solution line, while true
#----in regards to its system constraint, does not make the entire 
#----expression true, so it shouldn't be printed).
 
            while (defined($CurrentConstraint = 
$ConstraintsStackSaveForPrinting[$SystemChecksIndex])) {
                if ($CurrentConstraint =~ /^S.*0$/) {
                    $ResultValuePushedThrough = 0; 
                } elsif (($CurrentConstraint =~ /^S.*1$/ || 
$CurrentConstraint =~ /^S.*7$/) && $ResultValuePushedThrough == 0) {
                    $ConstraintsStackSaveForPrinting[$SystemChecksIndex] =~ 
s/1$/0/; 
                } elsif ($CurrentConstraint =~ /or/) {
                    $ResultValuePushedThrough = 1; 
                }
            $SystemChecksIndex++; 
            }

            $ResultValuePushedThrough = 1; 
            $SystemChecksIndex--;
            
#----And go through in the other direction, right to left, to catch any that 
#----were located at the beginning of a clause. 
            while ($SystemChecksIndex > -1 && defined($CurrentConstraint = 
$ConstraintsStackSaveForPrinting[$SystemChecksIndex])) {
                if ($CurrentConstraint =~ /^S.*0$/) {
                    $ResultValuePushedThrough = 0; 
                } elsif (($CurrentConstraint =~ /^S.*1$/ || 
$CurrentConstraint =~ /^S.*7$/)  && $ResultValuePushedThrough == 0) {
                    $ConstraintsStackSaveForPrinting[$SystemChecksIndex] =~ 
s/1$/0/; 
                } elsif ($CurrentConstraint =~ /or/) {
                    $ResultValuePushedThrough = 1; 
                }
            $SystemChecksIndex--; 
            }
#DEBUG print("Printing Save After: @ConstraintsStackSaveForPrinting\n");

            @SolutionNamesNotToPrint = (); 
            $SystemAnyChosen = 0; 

#----Now that the string is fixed up, go through the ConstraintsStack, 
#----and pull out the names for systems that are RES:0 so we don't print them. 

            $SystemChecksIndex = 0; 
            while (defined($CurrentConstraint = 
$ConstraintsStackSaveForPrinting[$SystemChecksIndex])) {
#DEBUG print("Current: $CurrentConstraint!\n");
                if ($CurrentConstraint =~ /^S.*0$/ && 
$CurrentConstraint !~ /^S:ANY/) {
                    $CurrentConstraint =~ s/^S://; 
                    $CurrentConstraint =~ s/RES:0$//; 
                    push(@SolutionNamesNotToPrint,$CurrentConstraint); 
                } elsif ($CurrentConstraint =~ /^S:ANY/ && 
$CurrentConstraint =~ /RES:7$/) {
                    $SystemAnyChosen = 1; 
                }
                $SystemChecksIndex++; 
            }
#DEBUG print("SolutionNamesNotToPrint : @SolutionNamesNotToPrint\n"); 
#DEBUG print("GoodSolutions: @GoodSolutions\n"); 

#----Get each name that shouldn't be printed, and find the corresponding
#----entry in the GoodSolutions array, and set it to 0.
             foreach $SolutionNameNotToPrint (@SolutionNamesNotToPrint) {
                 $indexThroughProblems = 0;
                 while (defined($SolutionLines[$indexThroughProblems])) {
                     if ($SolutionLines[$indexThroughProblems] =~ 
/^$SolutionNameNotToPrint/) {
                         $GoodSolutions[$indexThroughProblems] = 0; 
                     }
                     $indexThroughProblems++; 
                 }
             }

#DEBUG print("GoodSolutions After: @GoodSolutions\n"); 

#----Pass through GoodSolutions Array, and check to see that something 
#----is nonzero, and set SomeSolutionExists
            $SomeSolutionExists=0; 
            $indexThroughProblems = 0; 

            while(defined($GoodSolutions[$indexThroughProblems])) { 
                if ($GoodSolutions[$indexThroughProblems] > 0) {
                    $SomeSolutionExists = 1; 
                }
                $indexThroughProblems++; 
            }
#DEBUG print("Stack After: @ConstraintsStack\n");

#----In preparation for the meta-check (to see if the block, as a whole, 
#----satisfies the constraints), change every instance of "S:SystemNameRes:X"
#----to the value of X, so it may be passed to the LogicParser. 
            $SystemChecksResults = 1; 
            $SystemChecksIndex = 0;
            while (defined($CurrentConstraint = 
$ConstraintsStack[$SystemChecksIndex])) {
                 if ($CurrentConstraint =~ m/^S/) {
                     if ($CurrentConstraint =~ m/RES:0$/) {
                         $ConstraintsStack[$SystemChecksIndex] = 0;
                     } elsif ($CurrentConstraint =~ m/RES:[1-9][0-9]*$/) {
                         $ConstraintsStack[$SystemChecksIndex] = 1;
                     }
                 }
                 $SystemChecksIndex++; 
            }
#DEBUG print("Send to parser: @ConstraintsStack\n");            

#----Check the ConstraintsStack now for truthiness. 
            ($SystemChecksResults,$Garbage) = LogicParser("SOL",\@ConstraintsStack);

#----If some solution remains to be printed, and the solution block as a whole
#----satisfies the set of solution constraints provided, print:
            if (scalar(@GoodSolutions) == 0 || 
($SomeSolutionExists && $SystemChecksResults)) {
                if ($PrintOption eq "-pp" || $PrintOption eq "-pps") {
#----This prints the line before we tear it up into pieces
                    if (defined($ProblemLine)) {
                        if ($IdleFlag > 0 && $QuietnessOption le "-q1") {
                            print("\n"); 
                        }
                        print("$ProblemLine\n"); 
                        $IdleFlag = 0; 
                        $ProblemCounter++; 
                    }
                }
#----If solution lines are desired, go through the GoodSolutions array. 
#----For each entry that is nonzero, print the corresponding line. 
                if ($PrintOption eq "-ps" || $PrintOption eq "-pps") {
                    $indexThroughProblems = 0;

                    while (defined($GoodSolutions[$indexThroughProblems])) {
                        if ($GoodSolutions[$indexThroughProblems] > 0) {
                           $PrintableLine = $SolutionLines[$indexThroughProblems];
                           $PrintableLine =~ s/   //;
                           print("   $PrintableLine\n");
                           $IdleFlag = 0; 
                           $SolutionCounter++; 
                        }
                        $indexThroughProblems++; 
                    }    
                }   
            }                 
        }
#----Otherwise, if the problem was bad, or the solution block failed to meet
#----the desired constraints, we are not printing, so we sit idle. 
        if (!$GoodProblem || !$SystemChecksResults || !$SomeSolutionExists) {
            if ($QuietnessOption le "-q1") {
                if ($IdleFlag == 0) {
                    print("\%"); 
                }
                if ($IdleFlag > 0 && $IdleFlag % 200 == 0) {
                    if ($CurrentDomain ne $LastDomain) {
                        print("$CurrentDomain"); 
                        $LastDomain = $CurrentDomain; 
                    }
                        print("."); 
                } 
                $IdleFlag++; 
            }
        }
    }   


    if ($QuietnessOption le "-q2") {
        print("\n\% $ProblemCounter problems and $SolutionCounter solutions found\n"); 
    }

#----Close all the files
    close($StatisticsFileHandle); 
    close($ProblemListFileHandle);
    system("rm -f $SortedProblemFileName");
}
#--------------------------------------------------------------------------------------------------
#----GetNextProblemName gets the next problem name. Seriously.------------------
#--------------------------------------------------------------------------------------------------
sub GetNextProblemName {
    my ($ProblemListFileHandle) = @_;

    my $ProblemListLine;
    my $ProblemName;

    while (defined($ProblemListLine = <$ProblemListFileHandle>)) {
        chomp($ProblemListLine);
#DEBUG print("Looking at name line $ProblemListLine\n");
#----If its a line that specifies a problem or solution
        if ($ProblemListLine =~ /^[A-Z][A-Z][A-Z][0-9][0-9][0-9]/) {
#----Extract the problem name (this RE is real - no space at end for my file
#----lists)
            ($ProblemName) = ($ProblemListLine =~ /^([^ ]*)/);
#DEBUG print("Found name $ProblemName\n");
            return($ProblemName);
        }
    }
    return(undef);
}
#--------------------------------------------------------------------------------------------------
#----CreateConstraintsStack: Creates Logical Systems Construct-----------------
#--------------------------------------------------------------------------------------------------
sub CreateConstraintsStack {

    my ($ConstraintsRef,$ConstraintsStackRef) = @_; 
    my @ConstraintsArrayFROZEN = @$ConstraintsRef; 
    my @Constraints = @ConstraintsArrayFROZEN; 
    my @ConstraintsStack = @$ConstraintsStackRef; 

    my $ConstraintIndex; 

    my $LeftParen;
    my $RightParen; 

#----Go through the constraints provided. 
#----When you find a logical connector, just push it onto the stack. 
#----When you find a system constraint, prepend S: to the name of the system,
#----Then push it on the stack. 
#----If you find anything else, throw it away. 
    while (@Constraints) {
#DEBUG print ("Constraints Array: @Constraints\n"); 
        if (@Constraints[0] eq "and" || @Constraints[0] eq "or" ||
@Constraints[0] eq "{" || @Constraints[0] eq "}") {
            push(@ConstraintsStack, shift(@Constraints)); 
        } elsif ($Constraints[0] =~ /system/i) {
            push(@ConstraintsStack, "S:@Constraints[1]"); 
            shift(@Constraints); 
        } else {
            shift(@Constraints);
        } 
   }

    $LeftParen = 0; 
    $RightParen = 0; 

    $ConstraintIndex = 0; 

#----Count the parenthesis, make sure they match. (This may be handled
#----by the shell...?)
    foreach $ConstraintIndex (@ConstraintsStack) {
        if ($ConstraintIndex eq "{") {
            $LeftParen++; 
        } elsif ($ConstraintIndex eq "}") {
            $RightParen++;
        }
    }

    unless ($LeftParen == $RightParen) {
        print("ERROR- USAGE: MISSMATCHED PARENTHESIS!"); 
        return(-99); 
    }

#DEBUG print("Important Systemchecks: @ConstraintsStack\n");

return(\@ConstraintsStack);  

}
#--------------------------------------------------------------------------------------------------
#----Checks a set of Statistics against Constraints
sub CheckConstraints {
    my ($ProblemType,$SystemChecksArrayRef,$ConstraintsArrayRef,$StatisticsArrayRef,
$ProblemStatisticsArrayRef) = @_;

    my @Constraints = @$ConstraintsArrayRef;
    my @SystemChecks = @$SystemChecksArrayRef;
    my $ConstraintsUsed;
    my $Result;
    my $ResultSave; 
    my $TempResult; 
    my $FinalResult = 0;
    my $ShiftIndex;
    my $GottenPastProblemConstraints = 0; 
    my $SystemChecksIndex = 0; 

    my $Garbage; 

    my @ConstraintsResultsStack; 
    my $ConstraintsResult;
    my @ConstraintsResultsStackTemp; 

#DEBUG print ("Statistics: @$StatisticsArrayRef\n"); 
    while (@Constraints) {
#DEBUG print ("Constraints Array: @Constraints\n"); 

#----For each constraint on the stack, if it's a logical constraint, just push it onto the new 
#----Constraints Results Stack. If it's a system constraint, push the name of the system with 
#----S: prepnded.
        while (@Constraints[0] eq "and" || @Constraints[0] eq "or" ||
        @Constraints[0] eq "{" || @Constraints[0] eq "}") {
            push(@ConstraintsResultsStack, shift(@Constraints)); 
        }
        if ($Constraints[0] =~ /system/i) {
            push(@ConstraintsResultsStack,"S:@Constraints[1]") or 
die("Unmatched System Constraint");  
        } elsif (!@Constraints) {
            push(@ConstraintsResultsStack, "S:"); 
        }

#----Otherwise, check the constraint using CheckConstraint. 
        if (@Constraints) {
            ($ConstraintsUsed,$Result) = CheckConstraint($ProblemStatisticsArrayRef,$ProblemType,
$StatisticsArrayRef,@Constraints);
#DEBUG print("ConstraintsUsed = $ConstraintsUsed Result = $Result\n");

#----Shift off the arguments used, then push the result onto the ResultsStack. 
            foreach $ShiftIndex (1..$ConstraintsUsed) {
                shift(@Constraints);
            }
            push(@ConstraintsResultsStack, $Result); 
        }
    }

#DEBUG print("Stack: @ConstraintsResultsStack\n"); 
#----Now that all the Constraints have been processed, if this is problem line,
    if ($ProblemType ne "SOL") { 
        @ConstraintsResultsStackTemp = (); 
        $Result = shift(@ConstraintsResultsStack); 

#----Go through the array, get rid of all the System Constraints (set to 1).
        if ($Result =~ m/^S:/) {
            push(@ConstraintsResultsStackTemp,"1"); 
        } else {
            while ($Result !~ m/^S:/ && defined($Result)) {
                push(@ConstraintsResultsStackTemp,$Result); 
                $Result = shift(@ConstraintsResultsStack); 
            }
        }
#DEBUG print("Thing to check for logic: @ConstraintsResultsStackTemp\n"); 
        ($FinalResult,$Garbage) = LogicParser($ProblemType,\@ConstraintsResultsStackTemp);
#DEBUG print("FinalResult on that: $FinalResult\n"); 
#----Otherwise, we're at a system line
    } else {
        $ResultSave = 0; 
#DEBUG print("ConstraintsResults Before anything: @ConstraintsResultsStack\n");  
        $Result = shift(@ConstraintsResultsStack);
        while ($Result !~ m/^S/ && @ConstraintsResultsStack) {
            $Result = shift(@ConstraintsResultsStack); 
        }
#DEBUG print("ConstraintsResults sans problem stuff: @ConstraintsResultsStack and Res is $Result\n");  

#----If there are no solution constraints, then we print everything.
        if (!@ConstraintsResultsStack) {
            return(1,\@SystemChecks); 
        }
        $ResultSave = $Result; 
        $Result = shift(@ConstraintsResultsStack);  

#---Otherwise, we start consoldating the Results
        while (defined($Result)) {
            @ConstraintsResultsStackTemp = (); 
            while (
$Result !~ /^S/ && 
~( ($Result eq "and" || $Result eq "or" || $Result eq "{" || $Result eq "}") && 
   $ConstraintsResultsStack[0] =~ /^S/) && 
@ConstraintsResultsStack) {
                push(@ConstraintsResultsStackTemp,$Result); 
                $Result = shift(@ConstraintsResultsStack); 
            }

            if (!@ConstraintsResultsStack) {
                push(@ConstraintsResultsStackTemp,$Result); 
            }

            if ($ResultSave) {
#DEBUG print("Piece of Stack in focus: @ConstraintsResultsStackTemp\n"); 
                $Garbage = pop(@ConstraintsResultsStackTemp); 
                while (($Garbage eq "and" || $Garbage eq "or" || $Garbage eq "{" || 
$Garbage eq "}" || $Garbage eq "S") && @ConstraintsResultsStackTemp) {
#DEBUG print("Found some garbage at end of stack: $Garbage\n"); 
                    $Garbage = pop(@ConstraintsResultsStackTemp); 
                }

                push(@ConstraintsResultsStackTemp,$Garbage); 
#DEBUG print("Const: @ConstraintsResultsStackTemp\n");                 
#----Once you've found everything up to the next System Constraint, 
#----Pass it to the Logic Parser, and save the result. 
                ($TempResult,$Garbage) = LogicParser($ProblemType,\@ConstraintsResultsStackTemp); 
                if ($TempResult) {
                    $FinalResult = $TempResult; 
                }

                $SystemChecksIndex = 0; 
                while (defined(@SystemChecks[$SystemChecksIndex])) {
#DEBUG print("SysChecks: @SystemChecks,Ressave is  $ResultSave and $TempResult\n"); 
                    if (@SystemChecks[$SystemChecksIndex] eq $ResultSave && $TempResult > 0) {
                        @SystemChecks[$SystemChecksIndex] = $ResultSave."RES:".$TempResult; 
                    }
                    $SystemChecksIndex++; 
                }
#DEBUG print("Check for logic: @ConstraintsResultsStackTemp , match to $ResultSave in SysChecks\n");
#DEBUG print("ResultTemp: $TempResult and Finalresult: $FinalResult\n");  
            }
            $ResultSave = $Result; 
            $Result = shift(@ConstraintsResultsStack);  
        }
    }
#DEBUG print("Stack: @ConstraintsResultsStack\n"); 
#DEBUG print("SystemChecks: @SystemChecks\n"); 
#DEBUG print("FinalResult = $FinalResult\n"); 

    if ($FinalResult == -99) {
        return(-99,\@SystemChecks); 
    } elsif ($FinalResult == 7) {
        return(7,\@SystemChecks); 
    } elsif ($FinalResult >= 1) {
        $FinalResult = 1; 
    }    

#DEBUG print("Final: $FinalResult\n"); 
    return($FinalResult,\@SystemChecks);
}
#--------------------------------------------------------------------------------------------------
#----LogicParser: Parse a stack of logical operators and parethesis
#--------------------------------------------------------------------------------------------------
sub LogicParser {
    my ($ProblemType,$ConstraintsResultsStackRef) = @_; 

    my @ConstraintsResultsStack = @$ConstraintsResultsStackRef;  
    my $FinalResult = 1; 
    my $CurrentResult; 
    my $FinalResultHolder; 
    my $ConstraintsResultsStackRef; 

    while(@ConstraintsResultsStack) {
#DEBUG print("Current Results Stack:@ConstraintsResultsStack\n"); 
        $CurrentResult = shift(@ConstraintsResultsStack); 
#DEBUG print("Current Result : $CurrentResult\n"); 
#DEBUG print("At the top: Look: $CurrentResult\n"); 

        if ($CurrentResult eq "or") {
#DEBUG print("In the or\n"); 
            $CurrentResult = shift(@ConstraintsResultsStack); 
#DEBUG print("Grab next: $CurrentResult\n"); 
            if ($CurrentResult =~ /^[+-]?\d+$/) {
                if ($CurrentResult == -99) {
                    $FinalResult += 0; 
                } else {
                    $FinalResult += $CurrentResult; 
                }
            } elsif ($CurrentResult eq "{") {
                ($FinalResultHolder,$ConstraintsResultsStackRef) = LogicParser($ProblemType,
\@ConstraintsResultsStack);
                @ConstraintsResultsStack = @$ConstraintsResultsStackRef; 
                $FinalResult += $FinalResultHolder; 
#DEBUG print("After recursing, here's where we're at: @ConstraintsResultsStack , and CurrentRes is $CurrentResult\n"); 
            } elsif ($CurrentResult eq "}") {
                return ($FinalResult); 
            }
        } elsif ($CurrentResult eq "and") {
            $CurrentResult = shift(@ConstraintsResultsStack); 
           if ($CurrentResult =~ /^[+-]?\d+$/) {
                if ($CurrentResult == -99) {
                    $FinalResult = -99; 
                } else {
                    $FinalResult *= $CurrentResult; 
                }
            } elsif ($CurrentResult eq "{") {
                ($FinalResultHolder,$ConstraintsResultsStackRef) = LogicParser($ProblemType,
\@ConstraintsResultsStack);
                @ConstraintsResultsStack = @$ConstraintsResultsStackRef; 
                $FinalResult *= $FinalResultHolder; 
#DEBUG print("After recursing, here's where we're at: @ConstraintsResultsStack , and CurrentRes is $CurrentResult\n"); 
            } elsif ($CurrentResult eq "}") {
                return ($FinalResult); 
            } 
        } elsif ($CurrentResult eq "{") {
            ($FinalResultHolder,$ConstraintsResultsStackRef) = LogicParser($ProblemType,
\@ConstraintsResultsStack); 
            @ConstraintsResultsStack = @$ConstraintsResultsStackRef; 
            $FinalResult *= $FinalResultHolder; 
#DEBUG print("After recursing, here's where we're at: @ConstraintsResultsStack , and CurrentRes is $CurrentResult\n"); 

        } elsif ($CurrentResult eq "}") {
            return ($FinalResult,\@ConstraintsResultsStack); 
        } else {
            $FinalResult *= $CurrentResult; 
        }

#DEBUG print("End of function: $FinalResult\n"); 
    } 
#DEBUG print("End of sub: $FinalResult\n"); 
    return ($FinalResult,\@ConstraintsResultsStack); 
}
#--------------------------------------------------------------------------------------------------
#----CheckConstraint: Remove negation
#--------------------------------------------------------------------------------------------------
sub CheckConstraint {
    my ($ProblemStatisticsArrayRef,$ProblemType,$StatisticsArrayRef, $Constraint,
@RestOfConstraints) = @_;

    my $Negated;
    my $ConstraintsUsed;
    my $Result;

#----See if it's negated
    if ($Constraint =~ /^-/) {
        $Constraint = substr($Constraint,1);
        $Negated = 1;
    } else {
        $Negated = 0;
    }

    ($ConstraintsUsed,$Result) = CheckUnnegatedConstraint($ProblemStatisticsArrayRef,$ProblemType,
$StatisticsArrayRef,$Constraint,@RestOfConstraints);
#DEBUG print("Result: $Result, where Constraint was $Constraint\n"); 
    if ($Result < 0) {
        if ($Result == -99) {
            if ($Negated) {
                return($ConstraintsUsed,1); 
            } else {
                return($ConstraintsUsed,-99); 
            }
        }
        return($ConstraintsUsed,1); 
    } elsif ($Result == 7) { 
        if ($Negated) {
            return($ConstraintsUsed,0); 
        } else { 
            return($ConstraintsUsed,7); 
        }
    } else {
        return($ConstraintsUsed,abs($Result - $Negated));
    }
}
#--------------------------------------------------------------------------------------------------
#----Return number of arguments used and result.
sub CheckUnnegatedConstraint {
    my ($ProblemStatisticsArrayRef,$ProblemType,$StatisticsArrayRef,$Constraint,
@RestOfConstraints) = @_;

    my $ArgumentsUsed;
    my @ArgumentsUsedArray; 

#DEBUG print("Constraint: $Constraint\n");
#DEBUG print("    Values: @RestOfConstraints\n");
#DEBUG print("Statistics: @$StatisticsArrayRef\n");
#DEBUG print("ProbStats : @$ProblemStatisticsArrayRef\n");

    if ($ProblemType ne "SOL") {
        if ($Constraint =~ /^Domain/i) {
            @ArgumentsUsedArray = Domains($ProblemType,$StatisticsArrayRef,@RestOfConstraints);        
            return(($ArgumentsUsedArray[0])+1,$ArgumentsUsedArray[1]);
        }
        if ($Constraint =~ /^Form$/i) {
            return(2,CheckForm($StatisticsArrayRef,$RestOfConstraints[0])); 
        }
        if ($Constraint =~ /^MultiModal$/i) {
            return(1,HasSome('MultiModalNCCs',$StatisticsArrayRef,\%ProblemIndexHash));
        }
#        if ($Constraint =~ /^Version/i) {
#            return(2,CheckVersion($StatisticsArrayRef,$RestOfConstraints[0])); 
#        }
        if ($Constraint =~ /^Status/i) {
            return(2,CheckStatus($ProblemType,$StatisticsArrayRef,$RestOfConstraints[0])); 
        }
        if ( 
$Constraint =~ /^Rating/i ||
$Constraint =~ /^Formulae/i ||
$Constraint =~ /^UnitFormulae/i ||
$Constraint =~ /^TypeFormulae/i ||
$Constraint =~ /^Atoms/i ||
$Constraint =~ /^EqualityAtoms/i ||
$Constraint =~ /^Types/i ||
$Constraint =~ /^Symbols/i ||
$Constraint =~ /^Predicates/i ||
$Constraint =~ /^Functions/i ) {
            return(3,CheckValueInRange($Constraint,$StatisticsArrayRef,\%ProblemIndexHash,
$RestOfConstraints[0],$RestOfConstraints[1])); 
        }

        if (
$Constraint =~ /^PredicateArity/i ||
$Constraint =~ /^FunctionArity/i) {
            return(3,CheckArity($Constraint,$StatisticsArrayRef,$RestOfConstraints[0],
$RestOfConstraints[1]));
        }

        if ($Constraint =~ /^Propositional/i) {
            return(1,CheckArity('PredicateArity',$StatisticsArrayRef,0,0));
        }

        if ($Constraint =~ /^Arithmetic$/i) {
            return(1,HasSome('Arithmetics',$StatisticsArrayRef,\%ProblemIndexHash));
        }
        if (
$Constraint =~ /^Variables/i ||
$Constraint =~ /^Lambdas/i ||
$Constraint =~ /^Universals/i ||
$Constraint =~ /^Existentials/i ||
$Constraint =~ /^PiUniversals/i ||
$Constraint =~ /^FOOLs/i) {
            return(1,HasSome($Constraint,$StatisticsArrayRef,\%ProblemIndexHash));
        }

        if ($Constraint =~ /^Equality/i) {
            return(1,Equality($ProblemType,$StatisticsArrayRef));
        }
        if ($Constraint =~ /^PureEquality/i) {
            return(1,PureEquality($ProblemType,$StatisticsArrayRef));
        }
        if ($Constraint =~ /^UnitEquality/i) {
            return(1,UnitEquality($ProblemType,$StatisticsArrayRef));
        }
    } else {

#----SOLUTION CONDITIONS
        if ($Constraint =~ /^System/i) {
            return(2,System($ProblemType,$StatisticsArrayRef,$RestOfConstraints[0])); 
        }

        if (
$Constraint =~ /^ResultTime/i ||
$Constraint =~ /^SolutionDepth/i ||
$Constraint =~ /^SolutionFormulae/i ||
$Constraint =~ /^SolutionLeaves/i ) {
            return(3,CheckValueInRange($Constraint,$StatisticsArrayRef,\%SolutionIndexHash,
$RestOfConstraints[0],$RestOfConstraints[1]));
        }

        if ($Constraint =~ /^Result$/i) {
            return(2,Result($ProblemType,$StatisticsArrayRef,$RestOfConstraints[0])); 
        }
        if ($Constraint =~ /^Output/i) {
            return(2,Output($ProblemType,$StatisticsArrayRef,$RestOfConstraints[0])); 
        }

        if ($Constraint =~ /^Equality/i) {
            return(1,HasSome('SolutionEqualityAtoms',$StatisticsArrayRef,\%SolutionIndexHash));
        }

        if ($Constraint =~ /^Arithmetic/i) {
            return(1,HasSome('SolutionArithmetics',$StatisticsArrayRef,\%SolutionIndexHash));
        }
        if ($Constraint =~ /^Selectivity/i) {
            return(3,Selectivity($ProblemType,$StatisticsArrayRef,$RestOfConstraints[0],
$RestOfConstraints[1],$ProblemStatisticsArrayRef)); 
        }
        if ($Constraint =~ /^Girth/i) {
            return(3,Girth($ProblemType,$StatisticsArrayRef,$RestOfConstraints[0],
$RestOfConstraints[1])); 
        }
    }

#----Default return 1 used, and it failed
    return(1,0);
}
#--------------------------------------------------------------------------------------------------
#==================================================================================================
#----Problem constraints
#==================================================================================================
#--------------------------------------------------------------------------------------------------
#----This is only used once on the first problem if a domain constraint
#----is given. After that a check is made in the main loop.
sub Domains { 
    my ($ProblemType,$StatisticsArrayRef,@Constraints) = @_;

    my $NumberOfDomains = 0;
    my @Domains = ();
    my $ProblemDomain;
    my $domainIndex; 
    my $greaterThanEverything; 
    
#----Get domains off constraint list
    while ($Constraints[$NumberOfDomains] =~ /^[A-Z]{3}$/) {
        $Domains[$NumberOfDomains] = $Constraints[$NumberOfDomains];
        $NumberOfDomains++;
    }

    if ($ProblemType eq "SOL") {
            return($NumberOfDomains,-1);
    }

    $ProblemDomain = $$StatisticsArrayRef[$ProblemIndexHash{'Domain'}];

    if (grep(/$ProblemDomain/,@Domains)) {
        return($NumberOfDomains,1);
    } else {
        $domainIndex = 0; 
        $greaterThanEverything = 1; 
        while (defined($Domains[$domainIndex])) {
            if ($ProblemDomain lt $Domains[$domainIndex]) {
                $greaterThanEverything = 0;
            } 
            $domainIndex++; 
        }

        if ($greaterThanEverything == 0) {
            return($NumberOfDomains,0);
        } else {
           return($NumberOfDomains,-99);  
        }

    }
}
#--------------------------------------------------------------------------------------------------
sub CheckForm {
    my ($StatisticsArrayRef,$CheckString) = @_; 

    my $ProblemForm;

    $ProblemForm = $$StatisticsArrayRef[$ProblemIndexHash{'Form'}];
#----Replace it from SPC, which is more precise
    ($ProblemForm) = ($$StatisticsArrayRef[$ProblemIndexHash{'SPC'}] =~ /^(...).*/);

    return ($CheckString eq "ANY" || $ProblemForm eq $CheckString ||
($CheckString eq "DHF" && ($ProblemForm eq "DH1" || $ProblemForm eq "DH0")) ||
($CheckString eq "THF" && ($ProblemForm eq "TH1" || $ProblemForm eq "TH0")) ||
($CheckString eq "TXF" && ($ProblemForm eq "TX1" || $ProblemForm eq "TX0")) ||
($CheckString eq "NHF" && ($ProblemForm eq "NH1" || $ProblemForm eq "NH0")) ||
($CheckString eq "NXF" && ($ProblemForm eq "NX1" || $ProblemForm eq "NX0")) ||
($CheckString eq "TFF" && ($ProblemForm eq "TF1" || $ProblemForm eq "TF0")));
}
#--------------------------------------------------------------------------------------------------
sub CheckVersion {
    my ($StatisticsArrayRef,$CheckString) = @_; 
    
    my %FormLetterHash = (
        'Standard' => 'S',
        'Incomplete' => 'I',
        'INC' => 'I',
        'Augmented' => 'A',
        'Especial' => 'E',
        'Biased' => 'B',
    );
    my $CheckLetter;

    if (defined($CheckLetter = $FormLetterHash{$CheckString})) {
        return($$StatisticsArrayRef[$ProblemIndexHash{'Version'}] eq $CheckLetter);
    } else {
        return(0);
    }
}
#--------------------------------------------------------------------------------------------------
sub CheckStatus {
    my ($ProblemType,$StatisticsArrayRef,$CheckString) = @_; 
    
    my $AbbreviatedConstraint;


    if (defined($AbbreviatedConstraint = $AbbreviationHash{$CheckString})) {
        $CheckString = $AbbreviatedConstraint; 
    }
    return($$StatisticsArrayRef[$ProblemIndexHash{'Status'}] eq $CheckString);
}
#--------------------------------------------------------------------------------------------------
sub CheckValueInRange {
    my ($Field,$StatisticsArrayRef,$IndexHashRef,$LowerBound,$UpperBound) = @_;
    my $Value = 0; 

    $Value = $$StatisticsArrayRef[$$IndexHashRef{$Field}];

    return(($Value =~ /^[0-9][0-9\.]*/) && ExceptionCheck($Value,$LowerBound,$UpperBound)); 
}
#--------------------------------------------------------------------------------------------------
sub HasSome {
    my ($Field,$StatisticsArrayRef,$IndexHashRef) = @_;

    return($$StatisticsArrayRef[$$IndexHashRef{$Field}] > 0);
}
#--------------------------------------------------------------------------------------------------
sub CheckArity {
    my ($Constraint,$StatisticsArrayRef,$LowerBound,$UpperBound) = @_;

    my $Field;
    my $MinMax;
    my $SymbolType;
    my $MinimalArity;
    my $MaximalArity;

    ($SymbolType) = ($Constraint =~ /(.*)Arity/);
    $Field = "${SymbolType}Arities";

    if (!exists($ProblemIndexHash{$Field})) {
        return(0);
    }
    ($MinimalArity,$MaximalArity) = 
($$StatisticsArrayRef[$ProblemIndexHash{$Field}] =~ /([0-9-]*)\-([0-9-]*)/);
#DEBUG print("MinMax $MinMax SymbolType $SymbolType Field $Field MinimalArity $MinimalArity MaximalArity $MaximalArity LowerBound $LowerBound UpperBound $UpperBound\n");
    return(
ExceptionCheck($MinimalArity,$LowerBound,9999999) &&
ExceptionCheck($MaximalArity,0,$UpperBound));
}
#--------------------------------------------------------------------------------------------------
sub Equality {
    my ($ProblemType,$StatisticsArrayRef) = @_;

    return(HasSome('EqualityAtoms',$StatisticsArrayRef,\%ProblemIndexHash));
}
#--------------------------------------------------------------------------------------------------
sub PureEquality { 
    my ($ProblemType,$StatisticsArrayRef) = @_;

    return(Equality($ProblemType,$StatisticsArrayRef) &&
$$StatisticsArrayRef[$ProblemIndexHash{'Atoms'}] == 
$$StatisticsArrayRef[$ProblemIndexHash{'EqualityAtoms'}]);
}
#--------------------------------------------------------------------------------------------------
sub UnitEquality {
    my ($ProblemType,$StatisticsArrayRef) = @_;

    return($ProblemType eq "CNF" && 
PureEquality($ProblemType,$StatisticsArrayRef) &&
$$StatisticsArrayRef[$ProblemIndexHash{'Formulae'}] == 
$$StatisticsArrayRef[$ProblemIndexHash{'Atoms'}]);
}
#--------------------------------------------------------------------------------------------------
#==================================================================================================
#----SOL only constraints
#==================================================================================================
#--------------------------------------------------------------------------------------------------
sub System {
    my ($ProblemType,$StatisticsArrayRef,$CheckString) = @_;

    my $SolutionName; 
    if ($ProblemType ne "SOL") {
        return(-1); 
    }

    $SolutionName = $$StatisticsArrayRef[$SolutionIndexHash{'SystemName'}]; 

    if ($CheckString =~ /^ANY/) {
        return(7); 
    }
#DEBUG print("$SolutionName is solution name, $CheckString is check string here. Should it return?"); 
    return($SolutionName =~ /^$CheckString/i)
}
#--------------------------------------------------------------------------------------------------
sub Result {
    my ($ProblemType,$StatisticsArrayRef,$CheckString) = @_; 
    
    my $AbbreviatedConstraint;

    if ($ProblemType ne "SOL") {
        return(-1);
    }

    $AbbreviatedConstraint = $AbbreviationHash{$CheckString}; 
    if (defined($AbbreviatedConstraint)) {
        $CheckString = $AbbreviatedConstraint; 
    }     
    
    if ($CheckString eq "SUC") {
        return(
($$StatisticsArrayRef[$SolutionIndexHash{'Result'}] eq "SUC") ||
Result($ProblemType,$StatisticsArrayRef,"THM") ||
Result($ProblemType,$StatisticsArrayRef,"FTH") ||
Result($ProblemType,$StatisticsArrayRef,"CSA") ||
Result($ProblemType,$StatisticsArrayRef,"SAT") ||
Result($ProblemType,$StatisticsArrayRef,"UNS") ||
Result($ProblemType,$StatisticsArrayRef,"FSA") ||
Result($ProblemType,$StatisticsArrayRef,"FUN")
        );
    } elsif ($CheckString eq "NOS") {
        return(
($$StatisticsArrayRef[$SolutionIndexHash{'Result'}] eq "NOS") ||
Result($ProblemType,$StatisticsArrayRef,"TMO") ||
Result($ProblemType,$StatisticsArrayRef,"GUP") ||
Result($ProblemType,$StatisticsArrayRef,"ERR") ||
Result($ProblemType,$StatisticsArrayRef,"UNK")
        );
    } else {
        return($$StatisticsArrayRef[$SolutionIndexHash{'Result'}] eq $CheckString); 
    }
}
#--------------------------------------------------------------------------------------------------
sub ResultTime {
    my ($ProblemType,$StatisticsArrayRef,$LowerBound,$UpperBound) = @_;
    my $ResultTime = 0; 

    if ($ProblemType ne "SOL") {
        return(-1); 
    }
    
    $ResultTime = $$StatisticsArrayRef[$SolutionIndexHash{'ResultTime'}];

    return(ExceptionCheck($ResultTime,$LowerBound,$UpperBound)); 
}
#--------------------------------------------------------------------------------------------------
sub Output {
    my ($ProblemType,$StatisticsArrayRef,$CheckString) = @_; 
    
    my $AbbreviatedConstraint;

    if ($ProblemType ne "SOL") {
        return(-1); 
    }

    $AbbreviatedConstraint = $AbbreviationHash{$CheckString}; 
    if (defined($AbbreviatedConstraint)) {
        $CheckString = $AbbreviatedConstraint; 
    }

    if ($CheckString eq "Sol") {
        return(
($$StatisticsArrayRef[$SolutionIndexHash{'Output'}] eq "Sol") ||
Output($ProblemType,$StatisticsArrayRef,"Prf") ||
Output($ProblemType,$StatisticsArrayRef,"Mod")
        );
    } elsif ($CheckString eq "Prf") {
        return(
($$StatisticsArrayRef[$SolutionIndexHash{'Output'}] eq "Prf") ||
Output($ProblemType,$StatisticsArrayRef,"Ref")
        );
    } elsif ($CheckString eq "Ref") {
        return(
($$StatisticsArrayRef[$SolutionIndexHash{'Output'}] eq "Ref") ||
Output($ProblemType,$StatisticsArrayRef,"CRf")
        );
    } elsif ($CheckString eq "Mod") {
        return(
($$StatisticsArrayRef[$SolutionIndexHash{'Output'}] eq "Mod") ||
Output($ProblemType,$StatisticsArrayRef,"FMo") ||
Output($ProblemType,$StatisticsArrayRef,"Sat") 
        );
    } else {
        return($$StatisticsArrayRef[$SolutionIndexHash{'Output'}] eq $CheckString); 
    }
}
#--------------------------------------------------------------------------------------------------
sub Selectivity {
    my ($ProblemType,$StatisticsArrayRef,$LowerBound,$UpperBound,$ProblemStatisticsArrayRef) = @_; 

    my $Leaves;
    my $Formulae; 
    my $OriginalProblemType; 
    my $SolRatio; 

    if ($ProblemType ne "SOL") {
        return(-1); 
    }
    $OriginalProblemType = $$ProblemStatisticsArrayRef[$ProblemIndexHash{'SupType'}]; 
    $Formulae = $$ProblemStatisticsArrayRef[$ProblemIndexHash{'Formulae'}]; 
 
    $Leaves = $$StatisticsArrayRef[$SolutionIndexHash{'SolutionLeaves'}];
    if ($Leaves eq "-") {
        return(0); 
    }

    if ($Formulae == 0) {
#----Divide by zero. What should we do here? 
        return(0); 
    }
 
    $SolRatio = $Leaves/$Formulae; 
    $Leaves = ExceptionCheck($SolRatio,$LowerBound,$UpperBound);
    return(ExceptionCheck($SolRatio,$LowerBound,$UpperBound)); 
}
#--------------------------------------------------------------------------------------------------
sub Girth {
    my ($ProblemType,$StatisticsArrayRef,$LowerBound,$UpperBound) = @_; 

    my $Leaves;
    my $Depth;
    my $GirthRatio; 

    if ($ProblemType ne "SOL") {
        return(-1); 
    }
  
    $Leaves = $$StatisticsArrayRef[$SolutionIndexHash{'SolutionLeaves'}];
    $Depth = $$StatisticsArrayRef[$SolutionIndexHash{'SolutionDepth'}]; 

    if ($Leaves eq "-") {
        return(0);
    }

    if ($Depth == 0) {
        return(0);
    }

    $GirthRatio = $Leaves/$Depth; 
    return(ExceptionCheck($GirthRatio,$LowerBound,$UpperBound)); 
}
#--------------------------------------------------------------------------------------------------
sub ExceptionCheck {
    my ($Statistic,$LowerBound,$UpperBound) = @_; 

    if ($Statistic eq "-") {
        return(0);
    } elsif ($UpperBound eq "-" && $LowerBound eq "-") {
        return(1); 
    } elsif ($UpperBound eq "-") {
        return($Statistic >= $LowerBound); 
    } elsif ($LowerBound eq "-") {
        return($Statistic <= $UpperBound); 
    } elsif (($Statistic >= $LowerBound) && ($Statistic <= $UpperBound)) {
        return(1);
    } else {
        return(0); 
    }
} 
#--------------------------------------------------------------------------------------------------
