#!/s/std/bin/perl -wT
#<xmp> This comment helps some web browsers correctly show this file

use lib '/usr3/chaos/perl/lib';
use CGI qw(:standard);

@Names = param;

print "Content-type: text/html\n\n";

if(!defined($Names[0]))
{
	print <<_EOT;
<HTML><HEAD><TITLE>Wisconsin Driver's License Number Calculator</TITLE></HEAD>
<BODY BGCOLOR=FFFFFF>
<H1>Wisconsin Driver's License Number Calculator</H1>
Wisconsin is one of a few states that doesn't assign driver's
license number sequentially, randomly, or by geographical area.
Instead, a Wisconsin Driver's License Number is a hash based on
your name, date of birth, and gender.
<P>
This program will calculate all but two digits of your Wisconsin's
driver's license number.  No, it's not useful, but it amuses me.
<P>
(If this doesn't amuse you, or you already know your license number,
I also wrote a program that takes your Wisconsin Drive's License
number and figures out whatever it can.
<A HREF="http://www.upl.cs.wisc.edu/cgi-bin/wilicr">Give it a try.</A>
<P>
-- <A HREF="http://www.upl.cs.wisc.edu/~chaos/">Alan De Smet</A>
<P>

<FORM ACTION="http://www.upl.cs.wisc.edu/cgi-bin/wilic" METHOD=GET>
<PRE>
Last Name:      <INPUT TYPE="TEXT" SIZE=40 NAME="LASTNAME">
First Name:     <INPUT TYPE="TEXT" SIZE=40 NAME="FIRSTNAME">
Middle Initial: <INPUT TYPE="TEXT" SIZE=1 NAME="MIDDLEINIT">
Gender:         <INPUT TYPE="RADIO" NAME="GENDER" VALUE="M"> M
                <INPUT TYPE="RADIO" NAME="GENDER" VALUE="F"> F
Date of Birth:
  Year:         <INPUT TYPE="TEXT" SIZE=2 NAME="YEAR">
  Month:        <INPUT TYPE="TEXT" SIZE=2 NAME="MONTH">
  Day:          <INPUT TYPE="TEXT" SIZE=2 NAME="DAY">
</PRE>
<input type=submit value="Submit">
<input type=reset value="Reset">
</FORM>
</BODY></HTML>
_EOT
}
else
{
	print <<_EOT2;
<HTML><HEAD><TITLE>Wisconsin Driver's License Number Calculator</TITLE></HEAD>
<BODY BGCOLOR=FFFFFF>
<H1>Wisconsin Driver's License Number Calculator</H1>
_EOT2
	print '<STRONG>Name: </STRONG>'.param(FIRSTNAME).' '.param(MIDDLEINIT).' '
		.param(LASTNAME)."<BR>\n";
	print '<STRONG>Gender: </STRONG>'.param(GENDER)."<BR>\n";
	print '<STRONG>Date of Birth: </STRONG>'.param(MONTH).'/'.param(DAY).'/'.param(YEAR)."<BR>\n";
	print '<STRONG>License Number: </STRONG>'.
		&FindLicense(param(LASTNAME), param(FIRSTNAME), param(MIDDLEINIT),
			param(YEAR), param(MONTH), param(DAY), param(GENDER));
	print <<_EOT3;
<P>
The last two digits cannot be determined. They represent the number of
people who (except for the last two digits), have the exact same license
number as you, when you got your license.
</BODY></HTML>
_EOT3
}

while(<>)
{
	chomp();
	my($LastName, $FirstName, $MiddleInit, $Year,
		$Month, $Day, $Gender, $DriversNum) = split(/\t/);
	print &FindLicense(split(/\t/));
	print "\n$DriversNum\n";
}

sub FindLicense
{
	my($LastName, $FirstName, $MiddleInitial, $DOBYear, $DOBMonth,
	$DOBDay, $Gender) = @_;

	my($YearTens, $YearOnes);

	if($DOBYear > 99 or $DOBYear < 0) { return("BAD YEAR"); }
	if($DOBMonth > 12 or $DOBMonth < 1) { return("BAD MONTH"); }
	if($DOBDay > 31 or $DOBDay < 1) { return("BAD DAY"); }
	$Gender = uc($Gender);
	if(!($Gender eq 'M' or $Gender eq 'F')) { return("BAD GENDER"); }

	($YearTens, $YearOnes) = split(//,sprintf("%02d",$DOBYear));

	&DLLastName($LastName).'-'.
	&DLFirstNameMiddleInit($FirstName, $MiddleInitial).
	$YearTens.'-'.$YearOnes.
	&DLMonthDayGender($DOBMonth, $DOBDay, $Gender).'-??';
}

sub DLLastName
{
	local($_) = @_;
	my($f, $fc);

	tr/a-z/A-Z/;
	tr/A-Z//cd;

	if($_ eq '') { return(''); }

	($f) = /^(.)/;
	tr/AEHIOUWYBFPVCGJKQSXZDTLMNR/00000000111122222222334556/;
	($fc) = /^(.)/;
	s/^$fc+//;
	tr///cs;
	tr/0//d;
	$_ = $f . $_ . '000';
	s/^(.{4}).*/$1/;
	return($_);
}

sub DLFirstNameMiddleInit
{
	my($FN, $MiddleInit) = @_;
	my($NameNum, %FirstNameLookup, %FirstInitLookup, %MiddleInitLookup);
	my($Return);

	$FN = uc($FN);
	$MiddleInit = uc($MiddleInit);

	%FirstNameLookup =
	(
		'ALBERT'	=> 20, 'ALICE'		=> 20,
		'ANN'		=> 40, 'ANNE'		=> 40, 'ANNA'		=> 40,
		'ANNIE'		=> 40, 'ARTHUR'	=> 40,
		'BERNARD'	=> 80, 'BETTY'		=> 80, 'BETTE'		=> 80,
		'BETTIE'	=> 80,
		'CARL'		=> 120, 'CATHERINE'	=> 120,
		'CHARLES'	=> 140, 'CLARA'		=> 140,
		'DONALD'	=> 180, 'DOROTHY'	=> 180,
		'EDWARD'	=> 220, 'ELIZABETH'	=> 220,
		'FLORENCE'	=> 260, 'FRANK'		=> 260,
		'GEORGE'	=> 300, 'GRACE'		=> 300,
		'HAROLD'	=> 340, 'HARRIET'	=> 340,
		'HARRY'		=> 360, 'HAZEL'		=> 360,
		'HELEN'		=> 380, 'HENRY'		=> 380,
		'JAMES'		=> 440, 'JANE'		=> 440, 'JAYNE'		=> 440,
		'JEAN'		=> 460, 'JOHN'		=> 460,
		'JOAN'		=> 480, 'JOSEPH'	=> 480,
		'MARGARET'	=> 560, 'MARTIN'	=> 560,
		'MARVIN'	=> 580, 'MARY'		=> 580,
		'MELVIN'	=> 600, 'MILDRED'	=> 600,
		'PATRICIA'	=> 680, 'PAUL'		=> 680,
		'RICHARD'	=> 740, 'RUBY'		=> 740,
		'ROBERT'	=> 760, 'RUTH'		=> 760,
		'THELMA'	=> 820, 'THOMAS'	=> 820,
		'WALTER'	=> 900, 'WANDA'		=> 900,
		'WILLIAM'	=> 920, 'WILMA'		=> 920,
	);

	%FirstInitLookup =
	(
		'A' =>   0, 'B' =>  60, 'C' => 100, 'D' => 160, 'E' => 200, 'F' => 240,
		'G' => 280, 'H' => 320, 'I' => 400, 'J' => 420, 'K' => 500, 'L' => 520,
		'M' => 540, 'N' => 620, 'O' => 640, 'P' => 660, 'Q' => 700, 'R' => 720,
		'S' => 780, 'T' => 800, 'U' => 840, 'V' => 860, 'W' => 880, 'X' => 940,
		'Y' => 960, 'Z' => 980,
	);

	%MiddleInitLookup =
	(
		'A' =>  1, 'B' =>  2, 'C' =>  3, 'D' =>  4, 'E' =>  5, 'F' =>  6,
		'G' =>  7, 'H' =>  8, 'I' =>  9, 'J' => 10, 'K' => 11, 'L' => 12,
		'M' => 13, 'N' => 14, 'O' => 14, 'P' => 15, 'Q' => 15, 'R' => 16,
		'S' => 17, 'T' => 18, 'U' => 18, 'V' => 18, 'W' => 19, 'X' => 19,
		'Y' => 19, 'Z' => 19,
	);

	if(defined($FirstNameLookup{$FN}))
		{ $NameNum = $FirstNameLookup{$FN}; }
	elsif(defined($FirstInitLookup{(split(//,$FN))[0]}))
		{ $NameNum = $FirstInitLookup{(split(//,$FN))[0]}; }
	else
		{ return('BAD'); }

	if(defined($MiddleInitLookup{$MiddleInit}))
		{ $NameNum += $MiddleInitLookup{$MiddleInit}; }
	else
		{ return('BAD'); }

	$Return = sprintf("%03d",$NameNum);

	return($Return);
}

sub DLMonthDayGender
{
	my($Month, $Day, $Gender) = @_;
	my($DateNo);

	$DateNo = ($Month - 1) * 40 + $Day;
	if($Gender eq 'F') { $DateNo += 500; }

	$DateNo = sprintf("%03d",$DateNo);

	return($DateNo);
}

#</xmp>
