#!/usr/bin/perl

# tpr04b.pl - tsort golf game test program  VERSION 1.9

use strict;

my $script = 'tsort.pl';

# -----------------------------------------------------

# [data, should_fail]

my @cases = (

[<<END, 0],
a b
c d
b c
END

[<<END, 0],
a d
c d
b d
END

[<<END, 0],
b c
f g
e f
e f
a b
END

[<<END, 0],
a b
b c
a a
END

[<<END, 0],
0 1
1 2
2 3
END

[<<END, 0],
0 0
0 1
1 2
2 3
END

[<<END, 0],
a a
b b
END

[<<END, 1],
a b
b a
END

[<<END, 0],
aa a
aa aa
c aa
END

[<<END,0],
a aa
c aa
b aa
END

[<<END,1],
a aa
c aa
b aa
aa a
END

[<<END, 0],
a a
END

[<<END, 0],
a a
a a
a a
a a
END

[<<END, 0],
.... .....
... ....
.. ...
. ..
END

[<<END, 0],
.... .....
... ....
... ....
.. ...
. ..
END

[<<END,0],
g |
| a
a +
+ *
* [
[ ]
] (
( )
END

[<<END,1],
g |
| a
a +
+ *
* [
[ ]
] (
( |
( )
END

[<<END, 1],
.... ....
.... ...
... ....
.. ...
. ..
END

[<<END, 0],
one two
two three
three four
four five
five six
six seven
seven eight
eight nine
nine ten
END

[<<END, 0],
nine ten
eight nine
seven eight
six seven
five six
four five
three four
two three
one two
END

[<<END, 0],
tennine ten
eight tennine
nine three
one nine
END

[<<END, 0],
0 -1
-1 2
2 3
3 -4
-4 5
5 -6
-6 7
7 -8
-8 9
END

[<<END, 1],
0 -1
-1 2
2 3
3 -4
-4 5
5 -6
-6 7
7 -8
-8 9
9 0
END

[<<END, 0],
nine ten
eight nine
nine ten
eight nine
seven eight
six seven
three four
two three
one two
seven eight
six seven
six seven
one two
six seven
one two
six seven
six seven
one two
one two
six seven
six seven
one two
six seven
five six
four five
five six
four five
three four
two three
one two
END

[<<END, 0],
nine ten
eight nine
seven eight
six seven
five six
four five
three four
two three
one two
nine ten
eight nine
seven eight
six seven
five six
four five
three four
two three
one two
END

[<<END, 0],
two three
a b
b c
c d
five six
d e
e f
ten a
g h
h i
one two
i j
j k
tenine ten
k l
l m
m n
seven eight
n o
o p
p q
six seven
q r
four five
r s
s t
t u
nine tenine
u v
v w
w x
x y
x y
nine tenine
y z
a z
k s
z aleph
aleph bet
bet gimel
gimel dalet
dalet hei
eigth nine
hei vav
vav zain
zain het
three four
het tet
tet yud
yud kaf
kaf lamed
lamed mem
mem nun
nun samech
samech ain
ain pei
pei tsadi
tsadi kuf
kuf resh
resh shin
shin tav
a tav
z tav
tsadi kuf
END

[<<END, 1],
a b
b c
c d
d e
e f
g h
h i
i j
j k
k l
l m
m n
n o
o p
p q
q r
r s
s t
t u
u v
v w
w x
x y
x y
y z
a z
k s
z aleph
aleph bet
bet gimel
gimel dalet
dalet hei
hei vav
vav zain
zain het
het tet
tet yud
yud kaf
kaf lamed
lamed mem
mem nun
nun samech
samech ain
ain pei
pei tsadi
tsadi kuf
kuf resh
resh shin
shin tav
a tav
z tav
tsadi kuf
dalet z
END

[<<END,0],
chloroformise develling
resynchronise platinise
Tuscanise rebourbonise
colourises motorisation
decarboniser pathometres
desensitises dandyise
licencelesses chromising
motorisation propositionise
realisers demonisation
colonisables pseudoanaemia's
catechisables decilitres
packetising specialisers
unimmortalise revolutioniser
amouristic gentilises
matronises polarogramme
posterises romanticise
hyperemphasises hybridiser
dualises unneighbourliness
savouriness phenolise
roubles standardiser
sulphurise photoionisation
enthronise unsabred's
regularisation labourables
polarogramme spectroheliogramme's
fanaticise colourises
signalises inoffenciveness
symmetrises reroyalises
standardiser baconise
baconise romanticises
hyperemphasises camphourate
prefavourites matronises
turbidimetres theatricalisation
unrevelationises homologised
recogniser amouristic
synthesise atmometres
demonisation metagrammes
reroyalises unsavouries
emotionise Harvardises
dandyise dualises
demonisation grueller
barogramme bushelling
succouring neutralisation
romanticise accoutre
gentilises racemised
synthesise caponised
scrutinisingly dieselises
regionalise electrohomoeopathy
harmonises enthronise
circularises emotionise
camphouric colonisables
geometre desensitises
matronises totemisation
labourite's camphourate
unsepulchred memorialisers
unfavoured foreignises
pressuriser noncrystallisings
amortises fiscalisation
radiosterilisation parasitise
nebulises unsepulchred
unrevelationises fiscalisation
pseudoanaemia's posterises
irregularise watercolourist
totemisation racemised
Bessemerises hysterectomise
prefavourites pathometres
eternising labourite's
supercarbonises hybridiser
pedantises uncivilise
tumourous licencelesses
revolutioniser preutilisation
bushelling flavour's
decolouriser dieselises
Tuscanise eternising
theatricalisation prefavourites
neutralisation pseudoanaemia's
cryptogrammes signalises
Londonisation supercarbonises
homologised unimmortalised
memorialisers Tuscanise
hybridiser metagrammes
offence unrevelationises
noncrystallisings subjectivises
resynchronise turbidimetres
uncriticisables pressuriser
sulphurise viscometre
scrutinisingly camphourate
hysterectomise dualises
develling succouring
desterilise Londonisation
hebraised tensiometre
uncivilise civilisable
fanaticise pedantises
camphourate syphon's
unneighbourliness catechisables
syphon's packetising
supercarbonises rebourbonise
enthronise inhumanise
labourables armourially
immobilises preutilisation
hebraised tantalisingly
licencelesses savouriness
chromising rebourbonise
outtyrannise subjectivises
relativises tantaliser
unsabred's disequalisers
familiarisinglies polarogramme
chromising Bessemerises
caponised repopularise
humanisation scrutinisingly
uncriticisables undramatisables
romanticises regionalise
romanticising unsabred's
hyperemphasises uncriticisables
electrohomoeopathy literalise
bushelling camphourate
symmetrises pseudoanaemia's
platinise aromatisation
spirochaetes decentralises
dieselises pedantises
grueller synthesise
revolutioniser fanaticise
watercolourist roubles
repopularise uncriticisables
atmometres tensiometre
dualises regularisation
unmissionised romanticising
cavillation behaviouristic
fiscalisation familiarisinglies
unsavouries sceptring
Turkise circularises
decentralises irregularise
matronises inoffenciveness
colonisables solecise
mechanised baconise
pyridinises chloroformise
harmonises metagrammes
flavour's mechanised
pathometres ozoniser
literalise sulphurise
theatricalisation cavillation
mechanised chloroformise
inoffenciveness unmissionised
hybridiser decilitres
parasitise chromising
motorisation reroyalises
specialisers Turkise
civilisable pyridinises
fossilise hebraised
ozoniser relativises
tensiometre desterilise
armourially decolouriser
viscometre realisers
tantalisingly offence
photoionisation nebulises
motorisation uncivilise
preutilisation amortises
scrutinisingly outtyrannise
roubles Tuscanise
tumourous pressuriser
disequalisers regularisation
rebourbonise signalises
botanising radiosterilisation
foreignises humanisation
ternise undramatisables
undramatisables immobilises
accoutre decarboniser
spectroheliogramme's cryptogrammes
familiarisinglies chloroformise
definitising pathometres
decilitres ternise
Tuscanise unneighbourliness
solecise definitising
savouriness unfavoured
hysterectomise viscometre
gentilises harmonises
racemised geometre
aromatisation fossilise
baconise romanticising
sceptring camphouric
propositionise botanising
Harvardises unimmortalise
propositionise resynchronise
unimmortalised symmetrises
metagrammes turbidimetres
literalise repopularise
irregularise propositionise
hysterectomise hybridiser
subjectivises hybridiser
phenolise hyperemphasises
inhumanise phenolise
syphon's develling
behaviouristic prefavourites
rebourbonise caponised
labourite's amourist's
definitising botanising
barogramme bushelling
standardiser barogramme
amourist's recogniser
outtyrannise spirochaetes
electrohomoeopathy propositionise
END

[<<END,1],
chloroformise develling
resynchronise platinise
Tuscanise rebourbonise
colourises motorisation
decarboniser pathometres
desensitises dandyise
licencelesses chromising
motorisation propositionise
realisers demonisation
colonisables pseudoanaemia's
catechisables decilitres
packetising specialisers
unimmortalise revolutioniser
amouristic gentilises
matronises polarogramme
posterises romanticise
hyperemphasises hybridiser
dualises unneighbourliness
savouriness phenolise
roubles standardiser
sulphurise photoionisation
enthronise unsabred's
regularisation labourables
polarogramme spectroheliogramme's
fanaticise colourises
signalises inoffenciveness
symmetrises reroyalises
standardiser baconise
baconise romanticises
hyperemphasises camphourate
prefavourites matronises
turbidimetres theatricalisation
unrevelationises homologised
recogniser amouristic
synthesise atmometres
demonisation metagrammes
reroyalises unsavouries
emotionise Harvardises
dandyise dualises
demonisation grueller
barogramme bushelling
succouring neutralisation
romanticise accoutre
gentilises racemised
synthesise caponised
scrutinisingly dieselises
regionalise electrohomoeopathy
sulphurise roubles
harmonises enthronise
circularises emotionise
camphouric colonisables
geometre desensitises
matronises totemisation
labourite's camphourate
unsepulchred memorialisers
unfavoured foreignises
pressuriser noncrystallisings
amortises fiscalisation
radiosterilisation parasitise
nebulises unsepulchred
unrevelationises fiscalisation
pseudoanaemia's posterises
irregularise watercolourist
totemisation racemised
Bessemerises hysterectomise
prefavourites pathometres
eternising labourite's
supercarbonises hybridiser
pedantises uncivilise
tumourous licencelesses
revolutioniser preutilisation
bushelling flavour's
decolouriser dieselises
Tuscanise eternising
theatricalisation prefavourites
neutralisation pseudoanaemia's
cryptogrammes signalises
Londonisation supercarbonises
homologised unimmortalised
memorialisers Tuscanise
hybridiser metagrammes
offence unrevelationises
noncrystallisings subjectivises
resynchronise turbidimetres
uncriticisables pressuriser
sulphurise viscometre
scrutinisingly camphourate
hysterectomise dualises
develling succouring
desterilise Londonisation
hebraised tensiometre
uncivilise civilisable
fanaticise pedantises
camphourate syphon's
unneighbourliness catechisables
syphon's packetising
supercarbonises rebourbonise
enthronise inhumanise
labourables armourially
immobilises preutilisation
hebraised tantalisingly
licencelesses savouriness
chromising rebourbonise
outtyrannise subjectivises
relativises tantaliser
unsabred's disequalisers
familiarisinglies polarogramme
chromising Bessemerises
caponised repopularise
humanisation scrutinisingly
uncriticisables undramatisables
romanticises regionalise
romanticising unsabred's
hyperemphasises uncriticisables
electrohomoeopathy literalise
bushelling camphourate
symmetrises pseudoanaemia's
platinise aromatisation
spirochaetes decentralises
dieselises pedantises
grueller synthesise
revolutioniser fanaticise
watercolourist roubles
repopularise uncriticisables
atmometres tensiometre
dualises regularisation
unmissionised romanticising
cavillation behaviouristic
fiscalisation familiarisinglies
unsavouries sceptring
Turkise circularises
decentralises irregularise
matronises inoffenciveness
colonisables solecise
mechanised baconise
pyridinises chloroformise
harmonises metagrammes
flavour's mechanised
pathometres ozoniser
literalise sulphurise
theatricalisation cavillation
mechanised chloroformise
inoffenciveness unmissionised
hybridiser decilitres
parasitise chromising
motorisation reroyalises
specialisers Turkise
civilisable pyridinises
fossilise hebraised
ozoniser relativises
tensiometre desterilise
armourially decolouriser
viscometre realisers
tantalisingly offence
photoionisation nebulises
motorisation uncivilise
preutilisation amortises
scrutinisingly outtyrannise
roubles Tuscanise
tumourous pressuriser
disequalisers regularisation
rebourbonise signalises
botanising radiosterilisation
foreignises humanisation
ternise undramatisables
undramatisables immobilises
accoutre decarboniser
spectroheliogramme's cryptogrammes
familiarisinglies chloroformise
definitising pathometres
decilitres ternise
Tuscanise unneighbourliness
solecise definitising
savouriness unfavoured
hysterectomise viscometre
gentilises harmonises
racemised geometre
aromatisation fossilise
baconise romanticising
sceptring camphouric
propositionise botanising
Harvardises unimmortalise
propositionise resynchronise
unimmortalised symmetrises
metagrammes turbidimetres
literalise repopularise
irregularise propositionise
hysterectomise hybridiser
subjectivises hybridiser
phenolise hyperemphasises
inhumanise phenolise
syphon's develling
behaviouristic prefavourites
rebourbonise caponised
labourite's amourist's
definitising botanising
barogramme bushelling
standardiser barogramme
amourist's recogniser
outtyrannise spirochaetes
electrohomoeopathy propositionise
END

);

# -----------------------------------------------------

# get file contents for scoring and validation

open IN, $script or die "could not open $script: $!";
my $contents = join '', <IN>;
close IN;

for($contents)
	{
	tr/\r//;			# remove all CRs
	s/\s*\z/\n/;	# trim end;
	/^#!perl\s/ or die "Script does not start with #!perl\\s";
	/\brand\b/ and die "Script contains rand() function";
	/[^ -~\n\t]/ and die "Script contains illegal character @{[ord $&]}";
	}

my $have_stderr_redirect = 1;

if ($^O eq 'MSWin32') {
   if (Win32::IsWinNT()) {
      print "You are running Windows NT/2000\n";
   } else {
      print "You are running Windows, but not Windows NT/2000\n";
      $have_stderr_redirect = 0;
   }
} else {
   print "Congratulations! You are not running Windows.\n";
}

sub GolfScore {
	 my $golf = length($contents) - 8;
	 my ($body) = $contents =~ /#!perl(.*)\n\z/s;
	 my $whitespace = () = $body =~ /\s/g;
	 my $letters = $body =~ tr/a-zA-Z//;	# hehe - he said tr/// not y///
	 my $total = 10 * $whitespace + 3 * $letters +
	 	1 * (length($contents) - $whitespace - $letters);
	 my $fraction = length($contents) / $total;
	 $fraction = 0.99 if $fraction > 0.99;
   return sprintf '%.2f', $golf + $fraction;
}

sub PrintGolfScore {
   my @scr = @_;
   my $tot = 0;
   for my $s (@scr) {
      my $g = GolfScore($s);
      print "$s: $g\n";
      $tot += $g;
   }
   print "You shot a round of $tot strokes.\n";
}

sub BuildFile {
   my ($fname, $data) = @_;
   local (*FF);
   open(FF, '>'.$fname) or die "error: open '$fname'";
   print FF $data;
   close(FF);
}

my $testnumber = 1;

sub CheckOneTsort {
   my ($data, $shouldfail) = @_;
   my $intmp  = 'in.tmp';
   my $errtmp = 'err.tmp';
   BuildFile($intmp, $data);
   my $cmd = "$^X $script $intmp";
   $cmd .= " 2>$errtmp" if $have_stderr_redirect;
   printf "%3d: running: '$cmd'...", $testnumber++;
   my $out = `$cmd`;
	 my $rc = $? >> 8;
   print "done.\n";
	 if($shouldfail)
	 	{
		die "\nOops, you failed to exit with a non-zero exit code for case:\n$data"
			unless $rc;
		return 1;	# it passed
		}
	 else
	 	{
		die "\nOops, failed, you exited with a non-zero exit code $rc for case:\n$data"
			if $rc;
		}
   if ($have_stderr_redirect) {
			if(-s $errtmp)
				{
				open ERR, $errtmp or die "error $! opening $errtmp";
				local $/;
				print <ERR>;
				close ERR;
        die "oops, you wrote to stderr (see $errtmp)\n";
				}
   } else {
      warn "warning: cannot check you did not write to" .
           " stderr on this platform.\n";
   }
	 if (not ValidateTsort($data, $out)) {
      die "\nOops, you failed.\n";
   }
}

# -----------------------------------------------------

sub ValidateTsort
	{
	my ($input, $output, %names, %positions, @positions) = @_;
	die "output has space or tab at end of line for case:\n$input"
		if $output =~ /[ \t]\n/;
	die "output is not formatted properly for case:\n$input"
		unless $output =~ /^([!-~]+\n)+\z/;
	@names{$input =~ /[!-~]+/g} = ();
	@positions = $output =~ /[!-~]+/g;
	@positions == keys %names or
		die "output has incorrect number of node names for case:\n$input";
	"@{[sort keys %names]}" eq "@{[sort @positions]}" or
		die "output node names do not match input node names for case:\n$input";
	@positions{@positions} = 1 .. @positions;
	while( $input =~ /([!-~]+)\s+([!-~]+)/g )
		{
		$positions{$1} <= $positions{$2} or
			die "$1 is after $2 in output, should be before for case:\n$input";
		}
	return 1;
	}

sub CheckTsort {
   my ($scr) = @_;
   for my $r (@cases) { CheckOneTsort($r->[0], $r->[1]) }
}

# -----------------------------------------------------

select(STDERR);$|=1;select(STDOUT);$|=1;  # auto-flush
-f $script or die "error: file '$script' not found.\n";
PrintGolfScore($script);
CheckTsort($script);
PrintGolfScore($script);
print "\nHooray, you PASSED.\n\n";

#use File::Slurp;
#append_file 'run.log', "Score: @{[GolfScore()]}  @{[
	#scalar localtime]}\n\n$contents\n";

