#!/usr/bin/perl
#!/bin/perl



$StudentName{'referencea'} = 'Kofi Laing Reference';
$StudentEmail{'referencea'} = 'laing@cs.tufts.edu';
$StudentName{'referenceb'} = 'Kofi Laing Reference';
$StudentEmail{'referenceb'} = 'aklaing@gmail.com';


$StepBound = 120000;


sub makestate {
	local($State, $Label, $InitialOrNot, $FinalOrNot) = @_;
	if ($InitialOrNot) { 
	    $InitStateIndex = $State;
	    # print "Setting Initial State to $InitStateIndex\n";
	}
	if ($FinalOrNot) { 
	    push(@FinalStateList, $State); 
	    # print "Setting Final States to include $State\n";
	}
}



sub transition {
    local($State, $Next, $Read, $Write, $Move) = @_;

    if (! $Seen{$State}{$Read}) {
	$Seen{$State}{$Read} = 1;
	$Transition{$State}{$Read} = [$Next,$Write,$Move];
	if ($Debug) {
	    print "Setting from=$State read=$Read next=$Next write=$Write move=$Move\n";
	}
    } else {
	&kmyerror(10, "Nondeterminism found and ignored.\n");
    }
}


sub kmyerror { local($Number, $String) = @_;

	print "ERROR[$Login, $FileName, $Number]: ", $String, "\n";

}



sub readxmlfile {

    local($FileName) = @_;

    $InitStateIndex = '';
    @FinalStateList = ();
    %Seen = ();
    %Transition = ();

    open(FILE, "< $FileName") ||
	&kmyerror(100, "Could not open the file");

    $XML = join('', <FILE>);

    unless ($XML =~ /<type>turing<\/type>/) {
	&kmyerror(2093, "This is not a Turing Machine");
    }

    unless ($XML =~ /<initial\/>/) {
	&kmyerror(2099, "This does not have an initial state.");
    }

    unless ($XML =~ /<final\/>/) {
	&kmyerror(2099, "This does not have a final state.");
    }

    $XML =~ s/&#13;//g;

    $XML =~ s/<state id="(\d+)">\s*<x>-?\d+\.\d+<\/x>\s*<y>-?\d+\.\d+<\/y>\s*(<label>(.*)<\/label>)?\s*(<initial\/>)?\s*(<final\/>)?\s*<\/state>/&makestate($1, $3, $4, $5)/ge;

    $XML =~ s/<transition>\s*
	      <from>(\d+)<\/from>\s*
	      <to>(\d+)<\/to>\s*
	      (<read\/>|<read>(.*)<\/read>)\s*
	      (<write\/>|<write>(.*)<\/write>)\s*
	      <move>((L|R|S))<\/move>\s*
              <\/transition>/&transition($1, $2, $4, $6, $7)/xge;
}



sub dohalt { local($String) = @_;
	print "\n\nInput=$String\n";
	print "Output=", join('', @TMTape), "\n";
	if ($CurState == -1) {
	    print "System REJECTED, because of unspecified transition.\n";
	}
}


sub printstate { 

	foreach $Char (0..$#TMTape) { 
		($Char == $HeadPos ? print 'V' : print ' ');
	}	
	print "\n";
	foreach $Char (@TMTape) { 
	    if ($Char ne '') {
		print $Char; 
	    } else {
		print '~';
	    }
	}
	print "\n";
	print "St=$CurState Rd=$Read Nx=$Next Wr=$Write Mv=$Move\n";
	print "\n\n";
}



sub simulatetm { 
        local($String, $Regexp, $ExpectedDecision) = @_;

	local($TMDecision, $FinalState, $Next, $Write, $Move);
	
	if ($Debug) {
	    print "Starting Simulation Group\n";
	    print "Starting with String $String\n";
	    print "Starting with Regexp $Regexp\n";
	    print "Starting with ExpectedDecision $ExpectedDecision\n";
        }

	@TMTape = split(//, $String);
	unshift(@TMTape, '');
	push(@TMTape, '');
	$HeadPos = 1;
	$CurState = $InitStateIndex;
	if ($Debug) { 
	    print "Setting Current State to initstateindex=$InitStateIndex\n";
	} # only useful for tracing single program.
	$TMDecision = 2;
	$Feedback .= "     $String ";
	$TimeSteps = 0;
	while($TMDecision == 2) {
		foreach $FinalState (@FinalStateList) {
		    if ($FinalState == $CurState) {
			$TMDecision = 1; # means terminate accept
			$Feedback .= " accepted   ";
		    }
		}
		if ($StepBound < ++$TimeSteps) {
		    $TMDecision = 3;
		    $Feedback .= " TERMINATED ";
		    if ($Debug) { 
			print "Terminating after numsteps=$TimeSteps\n";
		    } # only useful for tracing single program.
		} elsif ($TMDecision != 1) {
		    $Read = $TMTape[$HeadPos];
		    if (! $Seen{$CurState}{$Read}) {
                        # no action specified
			if ($Debug) { 
			    print "No Action Specified for state=$State and read=<$Read>\n";
			} # only useful for tracing single program.
			$TMDecision = 0; # means terminate reject
			$Feedback .= " rejected   ";
			# $Next = -1;
			# $Write = $Read;
			# $Move = 'S';
		    } else { # make the move
			($Next, $Write, $Move) = @{$Transition{$CurState}{$Read}};
			if ($Debug) { &printstate(); } # only useful for tracing single program.
			$TMTape[$HeadPos] = $Write;
			if ($Move eq 'R') {
			    $HeadPos++;
			    if ($HeadPos > $#TMTape) { push(@TMTape, ''); }
			} elsif ($Move eq 'L') {
			    $HeadPos--;
			    if ($HeadPos < 0) { 
				unshift(@TMTape, ''); 
				$HeadPos++;
			    }
			}
			$CurState = $Next;
		    }
	        }
        }

	$TapeString = join('', @TMTape); 
	if ($Debug) {
	    print "Ending Simulation Group\n";
	    print "Ending with FinalState $CurState\n";
	    print "Ending with Regexp $Regexp\n";
	    print "Ending with TapeString $TapeString\n";
	    print "Ending with TimeSteps $TimeSteps\n";
	}

	if ($TapeString =~ $Regexp) {
	    if ($ExpectedDecision == $TMDecision) {
		$Feedback .= " correct           // $Points\n";
		if ($Debug) { print "Ending with correct decision\n"; }
		return 1;
	    } else { 
		$Feedback .= "          wrong    // 0\n";		
		if ($Debug) { print "Ending with wrong decision\n"; }
		return 0; 
	    }
	} else { 
	    $Feedback .= "          mismatch // 0\n";		
	    if ($Debug) { print "Ending with tape mismatch\n"; }
	    return 0; 
	}
}


sub testtm { 
    local($Argument, $Regexp, $Decision, $Points) = @_;
    $Total += $Points;
    if (&simulatetm($Argument, $Regexp, $Decision)) {  # the TM executed correctly, ...
	$Score += $Points; 
    }
}



sub gradetm { 
    local ($DirPrefix, $Person, $ComSuffix) = @_;
    local($FileName) = "$DirPrefix/$Person/$ComSuffix";
    local($Points);
    local($Login, $Submission) = ($Person =~ /^(.*)\.(\d)$/);
    $Score = 0;
    $Total = 0;
    &readxmlfile($FileName);

    # return;

    $Feedback = '';
    $Feedback .= "________________________________________________________________________\n";
    $Feedback .= "\n     LOGIN = $Login\n";
    $Feedback .= "SUBMISSION = $Submission\n";
    $Feedback .= "FILE = $FileName\n\n";
    $Feedback .= "Termination occurs after $StepBound steps\n";
    $Feedback .= "POINTS_____INPUT______RESULT________VERDICT______SCORE\n";
    open(FILE, "< $TestFile");
    while($Ln = <FILE>) {
	chop($Ln);
	($Argument, $Regexp, $Decision, $Points) =
	    ($Ln =~ /^(\S+)\s+(\S+)\s+(\d+)\s+(\d+)$/);
	$Feedback .= "     $Points";
	&testtm($Argument, $Regexp, $Decision, $Points);
    }
    close FILE;



    if ($Total) {
	$FinalScore = ($Score/$Total * 100);
	
	$Feedback .=         "\n***********************************\n";
	$Feedback .= sprintf("********* SCORE = %d/%d = %.2f%\n", $Score, $Total, $FinalScore);
	$Feedback .=         "***********************************\n\n";
	$Feedback .= "________________________________________________________________________\n";
	print $Feedback;
	printf REPORT $Feedback;
	$LineFeedback = "$Login.$Submission";
	$LineFeedback .= sprintf("--> %d/%d = %.2f%\n", $Score, $Total, $FinalScore);
	printf REPORTA $LineFeedback;
    } else {
	print "I want to divide by zero\n";
    }


#    $SendCommand = "| /bin/mailx -s TMScores $StudentEmail{$Login}";
#    print $SendCommand, "\n";
#    if (1) { # Mail out results
#	open(MAILOUT, $SendCommand);
#	print MAILOUT $Feedback;
#	close MAILOUT;
#    }

}


($Debug, $DirPrefix, $FileNames, $ComSuffix, $TestFile, $OutFile) = @ARGV;

open(FILENAMES, "< $FileNames");
open(REPORT, "> $OutFile.long");
open(REPORTA, "> $OutFile.short");
while($FileName = <FILENAMES>) {
    chop($FileName);
    print $FileName, "\n";
    &gradetm($DirPrefix, $FileName, $ComSuffix);
}
close FILENAMES;
close REPORT;
close REPORTA;

# issues of closure I have to make my thing such that it executes a maximum of 16000 states or something.