#!/usr/bin/perl -w
use strict;
print "Content-type: text/html\n\n";
print "\<HTML\>\
\<HEAD\>\
\<TITLE\>Quiz\ 3\<\/TITLE\>\
\<\/HEAD\>\
\<BODY\>\
\<SCRIPT\ type\=\"text\/javascript\"\ src\=\"\.\.\/\.\.\/pagetop\.js\"\>\<\/SCRIPT\>\
\<B\>Connected\:\ An\ Internet\ Encyclopedia\<\/B\>\
\<BR\>\
\<EM\>Quiz\ 3\<\/EM\>\<BR\>\
\<HR\>\<CENTER\>\
\<B\>Up\:\<\/B\>\
\<A\ HREF\=\"\.\.\/\.\.\/index\.htm\"\>Connected\:\ An\ Internet\ Encyclopedia\<\/A\>\<BR\>\
\<B\>Up\:\<\/B\>\
\<A\ HREF\=\"\.\.\/index\.htm\"\>Programmed\ Instruction\ Course\<\/A\>\<BR\>\
\<B\>Up\:\<\/B\>\
\<A\ HREF\=\"index\.htm\"\>Subnetting\ and\ CIDR\<\/A\>\<BR\>\
\<\/CENTER\>\
\<B\>Prev\:\<\/B\>\ \<A\ HREF\=\"7\.htm\"\>Subnet\ Masks\ \(cont\)\<\/A\>\<BR\>\
\<B\>Next\:\<\/B\>\ \<A\ HREF\=\"quiz3a\.cgi\"\>Quiz\ 3\ \(cont\)\<\/A\>\<BR\>\
\<HR\>\<P\>\
\<H3\>Quiz\ 3\<\/H3\>\<P\>\
";
print "\<TITLE\>";
print "Quiz\ 3";
print "\<\/TITLE\>";
print "\
\
";


my %FORM;
my $buffer;

if (exists $ENV{"CONTENT_LENGTH"}) {
    read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
} elsif (exists $ENV{"QUERY_STRING"}) {
    $buffer = $ENV{"QUERY_STRING"};
}

# Split the name-value pairs
my @pairs = split(/&/, $buffer);

foreach my $pair (@pairs)
{
    my ($name, $value) = split(/=/, $pair);

    # Un-Webify plus signs and %-encoding
    $value =~ tr/+/ /;
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

    $FORM{$name} = $value;
}


# Play a little game here so we can tag each quiz with a unique ID value,
# to aid in recreating bugs.

my $quizseed;

if (exists $FORM{"seed"}) {
    $quizseed = $FORM{"seed"};
} else {
    srand;
    $quizseed = int rand 10000000000;
}

srand $quizseed;

my $quizType = "difficult";

my $qnum;

my @ordinal = ("first", "second", "third", "fourth");

sub randomPrefixLength {
    my ($type) = @_;
    my $allow0 = 1;
    my $allow32 = 1;
    my $result;

    if ($type eq "easy") {
	$result = 8 * (int rand (3 + $allow0 + $allow32));
	$result += 8 if (not $allow0);
    } elsif ($type eq "medium") {
	$result = 8 * (int rand 4);
	$result += 4 + int rand 3;
    } else {
	$result = 2 + int rand 30 while ($result % 8 == 0);
    }

    return $result;
}

sub mask {
    my ($prefixLen) = @_;

    return 0 if ($prefixLen == 0);

    return (0xffffffff << (32-$prefixLen)) & 0xffffffff;
}

sub keyMask {
    my ($prefixLen) = @_;

    $prefixLen -= 8 while ($prefixLen > 8);

    return (255 << (8-$prefixLen)) & 255;
}

sub keyIndex {
    my ($prefixLen) = @_;

    return 0 if ($prefixLen == 0);
    return int (($prefixLen-1)/8);
}

sub keyByte {
    my ($prefixLen, $address) = @_;

    return ($address >> (8*(3-&keyIndex($prefixLen)))) & 255;
}

sub classA {
    my ($address) = @_;

    return ($address & 0x80000000) == 0;
}

sub classB {
    my ($address) = @_;

    return ($address & 0xc0000000) == 0x80000000;
}

sub classC {
    my ($address) = @_;

    return (($address & 0xe0000000) == 0xc0000000);
}

sub networkField {
    my ($address) = @_;

    if (&classA($address)) {
	return ($address & 0xff000000);
    } elsif (&classB($address)) {
	return ($address & 0xffff0000);
    } elsif (&classC($address)) {
	return ($address & 0xffffff00);
    } else {
	return $address;
    }
}

sub binary {
    my ($value) = @_;
    my $result = "";

    for (my $i=0; $i < 8; $i ++) {
	$result = (($value & 1) ? "1" : "0") . $result;
	$value >>= 1;
    }
    return $result;
}

sub randomAddress {
    return ((1+(int rand 239)) << 24) | (int rand 1<<24);
}

sub randomPrefix {
    my ($type) = @_;
    my $prefixLen = &randomPrefixLength($type);
    my $prefixAddr = &randomAddress;

    $prefixAddr &= &mask($prefixLen);

    # If this is a "medium" difficult quiz, then make sure the
    # "key byte" - the one being manipulated in binary - doesn't
    # exceed 64.  We mask it, strip it off, whittle it down, and put it back

    if ($type eq "medium") {
	my $keyMask = 255 << (8 * (3 - &keyIndex($prefixLen)));
	my $keyByte = $prefixAddr & $keyMask;
	$prefixAddr &= ~ $keyMask;
	$prefixAddr |= $keyByte & (63 << (8 * (3 - &keyIndex($prefixLen))));
    }

    return ($prefixLen, $prefixAddr);
}

sub formatPrefixNoLen {
    my ($prefixLen, $prefixAddr) = @_;
    my $result = "";

    return "0" if ($prefixLen == 0);

    my $limit = 3 - int(($prefixLen-1)/8);

    for (my $i=3; $i >= $limit; $i--) {
	$result .= ($prefixAddr >> (8*$i)) & 255;
	$result .= "." unless ($i == $limit);
    }

    return $result;
}

sub formatPrefix {
    my ($prefixLen, $prefixAddr) = @_;

    return &formatPrefixNoLen($prefixLen, $prefixAddr) . "/$prefixLen";
}

sub formatAddress {
    my ($addr) = @_;
    my $result = "";

    for (my $i=3; $i>=0; $i--) {
	$result .= ($addr >> (8*$i)) & 255;
	$result .= "." if ($i);
    }
    return $result;
}

sub randomMatchingAddress {
    my ($prefixLen, $prefixAddr) = @_;
    my $mask = &mask($prefixLen);

    return $prefixAddr | (&randomAddress & (~ $mask));
}

sub prefixPermutation {
    my ($prefixLen) = @_;
    my @changes = (1, 2, 3, 4, 5, 6);
    my $change = 0;

    while ($change == 0) {
	$change = $changes[int rand @changes];

	$change <<= (32 - $prefixLen);
	$change &= 0xffffffff;
    }

    return $change;
}

sub randomNonMatchingAddress {
    my ($prefixLen, $prefixAddr) = @_;
    my $result = &randomMatchingAddress($prefixLen, $prefixAddr);

    return $result ^ &prefixPermutation($prefixLen);
}

# Permute a prefix so it no longer matches what it used to

sub permutePrefix {
    my ($prefixLen, $prefixAddr) = @_;

    if (rand() < 0.5 and $prefixLen < 29) {
	if ($quizType eq "easy" and $prefixLen <= 24) {
	    $prefixLen += 8;
	    $prefixAddr |= int(rand 256) << (32 - $prefixLen);
	} else {
	    $prefixLen += 1 + int rand 2;
	    $prefixAddr ^= &prefixPermutation($prefixLen);
	}
    } elsif (rand() < 0.5 and $prefixLen > 3) {
	if ($quizType eq "easy" and $prefixLen >= 8) {
	    $prefixLen -= 8;
	} else {
	    $prefixLen -= 1 + int rand 2;
	    $prefixAddr &= &mask($prefixLen);
	}
	$prefixAddr ^= &prefixPermutation($prefixLen);
    } else {
	$prefixAddr ^= &prefixPermutation($prefixLen);
    }

    return ($prefixLen, $prefixAddr);
}

sub longestCommonPrefix {
    my @addrs = @_;
    my $prefixAddr;
    my $prefixLen;
    my $mask;

    for ($prefixLen=1; $prefixLen<=33; $prefixLen++) {
	last if ($prefixLen == 33);
	$mask = &mask($prefixLen);
	$prefixAddr = $addrs[0] & $mask;

	last if ((grep { ($prefixAddr & $mask) == ($_ & $mask) } @addrs)
		  != (scalar @addrs));
    }
    $prefixLen --;

    return ($prefixLen, $addrs[0] & &mask($prefixLen));
}

sub scramble {
    my @array = @_;

    for (my $i=0; $i < scalar @array; $i++) {
	my $j = int rand scalar @array;
	my $item = $array[$i];
	$array[$i] = $array[$j];
	$array[$j] = $item;
    }
    return @array;
}

sub match {
    my ($addr, @prefix) = @_;

    return ((&mask($prefix[0]) & $addr) == (&mask($prefix[0]) & $prefix[1]));
}

sub contains {
    my ($len1, $addr1, $len2, $addr2) = @_;

    return 0 if ($len1 > $len2);
    return ((&mask($len1) & $addr1) == (&mask($len1) & $addr2));
}

sub explain {
    my ($addr, @prefix) = @_;
    my $prefix1 = 8 * int($prefix[0]/8);

    if ($prefix[0] == 32) {
	print "'32 is the length of an IP address in bits',\n";
	print "'so ", &formatPrefix(@prefix), " is an exact match for ";
	print &formatAddress($prefix[1]), "'";
    } elsif ($prefix[0] == 0) {
	print "'0/0 matches any address'";
    } elsif ($prefix[0]%8 == 0) {
	print "'The prefix length of $prefix[0] is a multiple of eight',\n";
	print "'so the first ", $prefix[0], "/8=", $prefix[0]/8;
	print $prefix[0]==8 ? " byte" : " bytes";
	print " must match ", &formatPrefixNoLen(@prefix), " exactly.'";
    } elsif ((&mask($prefix1) & $addr) != (&mask($prefix1) & $prefix[1])) {
	print "'The prefix length of $prefix[0] is greater than $prefix1',\n";
	print "'so the first ", int($prefix[0]/8);
	print $prefix[0]==8 ? " byte" : " bytes";
	print " must match ", &formatPrefixNoLen($prefix1, $prefix[1]);
	print " exactly.'";
    } else {
	my $binarycorrect = &binary(&keyByte(@prefix));
	print "'", &keyByte(@prefix), " in binary is ", $binarycorrect;
	print " so the ", $ordinal[$prefix1/8], " byte\\'s',\n'first ";
	print "$prefix[0]-$prefix1=" if ($prefix1 != 0);
	print $prefix[0]-$prefix1;
	print (($prefix[0]-$prefix1)==1 ? " bit must be ":" bits must be ");
	print substr($binarycorrect, 0, $prefix[0]-$prefix1), "',\n'";
	print &keyByte($prefix[0], $addr), " in binary is ";
	print &binary(&keyByte($prefix[0], $addr)), "'";
    }
}

sub problem1 {

    # Pick a random prefix; anything other than 0/0 will work

    my @prefix = &randomPrefix($quizType);
    @prefix = &randomPrefix($quizType) while ($prefix[1] == 0);

    # Pick a correct answer - a matching address

    my $correct = &randomMatchingAddress(@prefix);

    # Pick three non-matching address; take care to make them all different

    my @wrongs;
    wrongs: while (@wrongs < 3) {
	my $addr = &randomNonMatchingAddress(@prefix);
	for my $others (@wrongs) {
	    next wrongs if ($others == $addr);
	}
	push @wrongs, $addr;
    }

    my @order = &scramble(0,1,2,3);

    print "Which one of the following addresses matches <TT>", &formatPrefix(@prefix), "</TT>?\n";
    print "<P><CENTER><TABLE>\n";
    foreach my $i (@order) {
	my $addr;
	if ($i == 0) {
	    $addr = $correct;
	} else {
	    $addr = $wrongs[$i-1];
	}
	print "<TR><TD><INPUT TYPE=\"Radio\" NAME=\"ChoiceQ$qnum\" onClick=\"Answer('Q$qnum',\n";
	if ($i == 0) {
	    print "'Correct!";
	} else {
	    print "'Wrong";
	}
	print " - ", &formatAddress($addr);
	if ($i == 0) {
	    print " matches ";
	} else {
	    print " doesn\\'t match ";
	}
	print &formatPrefix(@prefix), "','',\n";

	&explain($addr, @prefix);

	print ")\"><TT>", &formatAddress($addr), "</TT>\n\n";
    }
    print "</TABLE>\n";
    print "<TEXTAREA NAME=\"Q$qnum\" COLS=64 ROWS=6></TEXTAREA>\n";
    print "</CENTER><P>\n\n";
}


sub problem2 {
    my @correct = &randomPrefix($quizType);

    my $addr = &randomMatchingAddress(@correct);

    # Pick three non-matching prefixes; take care to make them all different

    my @wrongs;
    wrongs: while (@wrongs < 3) {
	my @wrong = &permutePrefix(@correct);
	next wrongs if (&match($addr, @wrong));
	for my $others (@wrongs) {
	    next wrongs if ($$others[0] == $wrong[0] and $$others[1] == $wrong[1]);
	}
	push @wrongs, \@wrong;
    }

    my @order = &scramble(0,1,2,3);

    print "Which one of the following prefixes matches <TT>", &formatAddress($addr), "</TT>?\n";
    print "<P><CENTER><TABLE>\n";
    foreach my $i (@order) {
	my @answer;
	if ($i == 0) {
	    @answer = @correct;
	} else {
	    @answer = @{$wrongs[$i-1]};
	}
	print "<TR><TD><INPUT TYPE=\"Radio\" NAME=\"ChoiceQ$qnum\" onClick=\"Answer('Q$qnum',\n";
	if ($i == 0) {
	    print "'Correct!";
	} else {
	    print "'Wrong";
	}
	print " - ", &formatPrefix(@answer);
	if ($i == 0) {
	    print " matches ";
	} else {
	    print " doesn\\'t match ";
	}
	print &formatAddress($addr), "','',\n";

	&explain($addr, @answer);

	print ")\"><TT>", &formatPrefix(@answer), "</TT>\n\n";
    }
    print "</TABLE>\n";
    print "<TEXTAREA NAME=\"Q$qnum\" COLS=64 ROWS=6></TEXTAREA>\n";
    print "</CENTER><P>\n\n";
}

sub problem3 {
    my @prefix1;
    my $addr1;
    my @prefix2;
    my $addr2;
    my @correct;

    do {
	@prefix1 = &randomPrefix($quizType);
	$addr1 = &randomMatchingAddress(@prefix1);

	do {
	    @prefix2 = &permutePrefix(@prefix1);
	    $addr2 = &randomMatchingAddress(@prefix2);
	} while ($addr2 == $addr1);

	@correct = &longestCommonPrefix($addr1, $addr2);
    } while ($quizType ne "easy" and $correct[0] % 8 == 0);

    # Pick three non-matching prefixes; take care to make them all different

    my @wrongs;
    wrongs: while (@wrongs < 3) {
	my @wrong = &permutePrefix(@correct);

	next wrongs if (&match($addr1, @wrong) and &match($addr2, @wrong));
	next wrongs if ($quizType ne "easy" and ($addr1 == $wrong[1] or $addr2 == $wrong[1]));
	for my $others (@wrongs) {
	    next wrongs if ($$others[0] == $wrong[0] and $$others[1] == $wrong[1]);
	}
	push @wrongs, \@wrong;
    }

    my @order = &scramble(0,1,2,3);

    print "Which one of the following prefixes matches both <TT>";
    print &formatAddress($addr1), "</TT> and <TT>";
    print &formatAddress($addr2), "</TT>?\n";
    print "<P><CENTER><TABLE>\n";
    foreach my $i (@order) {
	my @answer;
	if ($i == 0) {
	    @answer = @correct;
	} else {
	    @answer = @{$wrongs[$i-1]};
	}
	print "<TR><TD><INPUT TYPE=\"Radio\" NAME=\"ChoiceQ$qnum\" onClick=\"Answer('Q$qnum',\n";
	if ($i == 0) {
	    print "'Correct!";
	} else {
	    print "'Wrong";
	}
	print " - ", &formatPrefix(@answer);
	if ($i == 0) {
	    print " matches both addresses'";
	} else {
	    print " doesn\\'t match ";

	    for my $addr ($addr1, $addr2) {
		if (not &match($addr, @answer)) {
		    print &formatAddress($addr), "','',\n";
		    &explain($addr, @answer);
		    last;
		}
	    }
	}
	print ")\"><TT>", &formatPrefix(@answer), "</TT>\n\n";
    }
    print "</TABLE>\n";
    print "<TEXTAREA NAME=\"Q$qnum\" COLS=64 ROWS=6></TEXTAREA>\n";
    print "</CENTER><P>\n\n";
}

sub problem4 {
    # Pick a random prefix

    my @prefix;
    my @correct;
    do {
	@prefix = &randomPrefix($quizType);

	@correct = @prefix;

	if ($quizType eq "easy") {
	    $correct[0] -= 8;
	} else {
	    $correct[0] -= 1 + int rand 3;
	}
    } while ($correct[0] < 0 or ($quizType ne "easy" and $correct[0]==0));

    $correct[1] &= &mask($correct[0]);

    # Pick three non-matching prefixes; take care to make them all different

    my @wrongs;
    wrongs: while (@wrongs < 3) {
	my @wrong = &permutePrefix(@correct);
	next wrongs if (&contains(@wrong, @prefix));
	for my $others (@wrongs) {
	    next wrongs if ($$others[0] == $wrong[0] and $$others[1] == $wrong[1]);
	}
	push @wrongs, \@wrong;
    }
    print "Which one of the following address prefixes contains <TT>", &formatPrefix(@prefix), "</TT>?\n";
    print "<P><CENTER><TABLE>\n";

    my @order = &scramble(0,1,2,3);

    foreach my $i (@order) {
	my @answer;
	if ($i == 0) {
	    @answer = @correct;
	} else {
	    @answer = @{$wrongs[$i-1]};
	}
	print "<TR><TD><INPUT TYPE=\"Radio\" NAME=\"ChoiceQ$qnum\" onClick=\"Answer('Q$qnum',\n";
	if ($i == 0) {
	    print "'Correct!";
	} else {
	    print "'Wrong";
	}
	print " - ", &formatPrefix(@answer);
	if ($i == 0) {
	    print " contains ";
	} else {
	    print " doesn\\'t contain ";
	}
	print &formatPrefix(@prefix), "','',\n";

	if ($answer[0] > $prefix[0]) {
	    print "'A $answer[0]-bit prefix can not contain a $prefix[0]-bit prefix',''";
	} else {
	    &explain($prefix[1], @answer);
	}
	print ")\"><TT>", &formatPrefix(@answer), "</TT>\n";
    }
    print "</TABLE>\n";
    print "<TEXTAREA NAME=\"Q$qnum\" COLS=64 ROWS=6></TEXTAREA>\n";
    print "</CENTER><P>\n\n";
}

sub problem5 {
    my @prefix = &randomPrefix("difficult");
    my @prefixlens = ($prefix[0]);

    wrongs: while (@prefixlens < 4) {
	my $prefixlen = int rand 33;
	foreach my $others (@prefixlens) {
	    next wrongs if ($others == $prefixlen);
	}
	push @prefixlens, $prefixlen;
    }

    print "Which subnet mask corresponds to a prefix length of <TT>/", $prefix[0], "</TT>?\n";
    print "<P><CENTER><TABLE>\n";

    @prefixlens = &scramble(@prefixlens);

    foreach my $i (@prefixlens) {
	print "<TR><TD><INPUT TYPE=\"Radio\" NAME=\"ChoiceQ$qnum\" onClick=\"Answer('Q$qnum',\n";
	if ($i == $prefix[0]) {
	    print "'Correct!'";
	} else {
	    print "'Wrong.','','", &formatAddress(&mask($i)), " corresponds to a prefix length of $i, not $prefix[0]'";
	}
	print ")\"><TT>", &formatAddress(&mask($i)), "</TT>\n";
    }
    print "</TABLE>\n";
    print "<TEXTAREA NAME=\"Q$qnum\" COLS=64 ROWS=6></TEXTAREA>\n";
    print "</CENTER><P>\n\n";
}

sub problem6 {
    my $addr = &randomAddress();
    my $a1 = ($addr>>24) & 255;
    my $a2 = ($addr>>16) & 255;
    my $a3 = ($addr>>8) & 255;
    my $a4 = $addr & 255;
    print "Enter an address prefix that matches <TT>", &formatAddress($addr), "</TT>:\n";
    print "<CENTER>\n";
    print "<INPUT SIZE=25 NAME=\"Input$qnum\" onChange=\"doQ6('Input$qnum','Q$qnum',$a1,$a2,$a3,$a4)\">\n";
    print "<TEXTAREA NAME=\"Q$qnum\" COLS=64 ROWS=6></TEXTAREA>\n";
    print "</CENTER><P>\n";
}

sub problem7 {
    my @prefix = &randomPrefix($quizType);
    my $a1 = ($prefix[1]>>24) & 255;
    my $a2 = ($prefix[1]>>16) & 255;
    my $a3 = ($prefix[1]>>8) & 255;
    my $a4 = $prefix[1] & 255;
    print "Enter an address that matches <TT>", &formatPrefix(@prefix), "</TT>:\n";
    print "<CENTER>\n";
    print "<INPUT SIZE=25 NAME=\"Input$qnum\" onChange=\"doQ7('Input$qnum','Q$qnum',$a1,$a2,$a3,$a4,$prefix[0])\">\n";
    print "<TEXTAREA NAME=\"Q$qnum\" COLS=64 ROWS=6></TEXTAREA>\n";
    print "</CENTER><P>\n";
}

sub problem8 {
    my @prefix1 = &randomPrefix($quizType);
    my @prefix2 = &permutePrefix(@prefix1);
    my $addr1 = &randomMatchingAddress(@prefix1);
    my $addr2 = &randomMatchingAddress(@prefix2);
    my @prefix = &longestCommonPrefix($addr1, $addr2);
    my $a1 = ($prefix[1]>>24) & 255;
    my $a2 = ($prefix[1]>>16) & 255;
    my $a3 = ($prefix[1]>>8) & 255;
    my $a4 = $prefix[1] & 255;
    print "Enter the longest prefix that matches both <TT>", &formatAddress($addr1), "</TT> and <TT>", &formatAddress($addr2), "</TT>:\n";
    print "<CENTER>\n";
    print "<INPUT SIZE=25 NAME=\"Input$qnum\" onChange=\"doQ8('Input$qnum','Q$qnum',$a1,$a2,$a3,$a4,$prefix[0])\">\n";
    print "<TEXTAREA NAME=\"Q$qnum\" COLS=64 ROWS=6></TEXTAREA>\n";
    print "</CENTER><P>\n";
}

sub problem9 {
    my @answers = (- int rand 33);

  wrongs: while (@answers < 4) {
      my $masklen = 2 + int rand 31;
      my $answer = &mask($masklen);
      my $mistakebit;

      do {
	  $mistakebit = 1 + int rand 31;
      } while ($mistakebit >= $masklen);

      $answer ^= 1 << (32 - $mistakebit);
      foreach my $others (@answers) {
	  next wrongs if ($others == $answer);
      }
      push @answers, $answer;
  }

    print "Which of the following is a valid subnet mask?\n";
    print "<P><CENTER><TABLE>\n";

    @answers = &scramble(@answers);

    foreach my $i (@answers) {
	print "<TR><TD><INPUT TYPE=\"Radio\" NAME=\"ChoiceQ$qnum\" onClick=\"Answer('Q$qnum',\n";
	if ($i <= 0) {
	    print "'Correct!'";
	} else {
	    print "'Wrong.'";
	}
	print ")\"><TT>";
	if ($i <= 0) {
	    print &formatAddress(&mask(- $i));
	} else {
	    print &formatAddress($i);
	}
	print "</TT>\n";
    }
    print "</TABLE>\n";
    print "<TEXTAREA NAME=\"Q$qnum\" COLS=64 ROWS=6></TEXTAREA>\n";
    print "</CENTER><P>\n\n";
}

sub problem10 {
    my @prefix1;

  getprefix1: {
      @prefix1 = &randomPrefix('difficult');
      redo getprefix1
	  if ((&classA($prefix1[1]) and $prefix1[0] <= 9)
	      or (&classB($prefix1[1]) and $prefix1[0] <= 17)
	      or (&classC($prefix1[1]) and $prefix1[0] <= 25)
	      or (not &classA($prefix1[1]) and not &classB($prefix1[1])
		  and not &classC($prefix1[1]))
	      or ($prefix1[0] > 30));
  }

    my @wrong;
  wrong: {
      @wrong = &permutePrefix(@prefix1);
      redo wrong
	  if ((&networkField($prefix1[1]) != &networkField($wrong[1]))
	      or $prefix1[0] == $wrong[0]);
  }

    my @corrects;
    corrects: while (@corrects < 3) {
	my @correct = &permutePrefix(@prefix1);
	next corrects
	    if ((&networkField($prefix1[1]) == &networkField($correct[1]))
		and $prefix1[0] != $correct[0]);
	for my $others (@corrects) {
	    next corrects if ($$others[0] == $correct[0] and $$others[1] == $correct[1]);
	}
	push @corrects, \@correct;
    }

    my @order = &scramble(0,1,2,3);

    print "<TT>", &formatPrefix(@prefix1), "</TT> has been assigned to a\n";
    print "subnet in a network using the RIP routing protocol.  Which of\n";
    print "the following prefixes can <EM>not</EM> be assigned to another\n";
    print "subnet?\n";

    print "<P><CENTER><TABLE>\n";
    foreach my $i (@order) {
	my $prefix;
	if ($i == 0) {
	    $prefix = \@wrong;
	} else {
	    $prefix = $corrects[$i-1];
	}
	print "<TR><TD><INPUT TYPE=\"Radio\" NAME=\"ChoiceQ$qnum\" onClick=\"Answer('Q$qnum',\n";
	if ($i == 0) {
	    print "'Correct!'";
	} else {
	    print "'Wrong. ", &formatPrefix(@$prefix);
	    if (&networkField($$prefix[1]) != &networkField($prefix1[1])) {
		print " is part of a different classful network than',";
	    } elsif ($$prefix[0] == $prefix1[0]) {
		print " has the same prefix length as',";
	    }
	    print "'", &formatPrefix(@prefix1);
	    print ", so it\\'s an acceptable subnet'";
	}
	print ")\"><TT>", &formatPrefix(@$prefix), "</TT>\n\n";
    }
    print "</TABLE>\n";
    print "<TEXTAREA NAME=\"Q$qnum\" COLS=64 ROWS=6></TEXTAREA>\n";
    print "</CENTER><P>\n\n";
}

print "Quiz ID code: classful-$quizseed
<P>

<SCRIPT>
var dopost = false

function Answer(question) {
   document.FORM[question].value = \"\"
   for (var i=1; i<Answer.arguments.length; i++) {
      document.FORM[question].value += Answer.arguments[i]
      if (i < Answer.arguments.length-1) {
          document.FORM[question].value += unescape(\"%0d%0a\")
      }
   }
}

function doQ6(input, output, target_a, target_b, target_c, target_d) {
   var userin = document.FORM[input].value
   var start = 0
   var end = -1
   var elementsSeen = 0
   var address = new Array(0,0,0,0)
   var mask = new Array(0,0,0,0)
   var target = new Array(target_a, target_b, target_c, target_d)
   var ordinal = new Array(\"first\", \"second\", \"third\", \"fourth\")
   var prefixlen
   var i

   document.FORM[output].value = \"\"

   while (((end = userin.indexOf(\".\", end+1)) != -1) ||
          ((end = userin.indexOf(\"/\", end+1)) != -1)) {
      if (elementsSeen == 4) {
         document.FORM[output].value = \"Dotted quad portion must contain no more than four numbers\"
         return
      }
      if (end-start == 0) {
         document.FORM[output].value = \"Syntax: A.B.C.D/N\"
         return
      }
      address[elementsSeen] = userin.slice(start,end)
      if ((address[elementsSeen] < 0) || (address[elementsSeen] > 255)) {
         document.FORM[output].value = \"Dotted quad elements must be between 0 and 255\"
         return
      }

      // Check for NaN on systems that implement it, but do so in a way
      // that works on those that don't
      if ((! (address[elementsSeen] >= 0)) &&
          (! (address[elementsSeen] <= 255))) {
         document.FORM[output].value = \"Syntax: A.B.C.D/N\"
	 return
      }

      // Check for NaN on systems that don't implement it
      if (address[elementsSeen] == 0 &&
          ((address[elementsSeen].length != 1) ||
           (address[elementsSeen].charAt(0) != \"0\"))) {
         document.FORM[output].value = \"Syntax: A.B.C.D/N\"
	 return
      }

      if (address[elementsSeen]!=0 && address[elementsSeen].charAt(0)==\"0\") {
         document.FORM[output].value = \"By convention, leading zeros are not written\"
	 return
      }

      elementsSeen ++
      start = end+1

      if (userin.charAt(end) == \"/\") break
   }

   if (end == -1 || userin.charAt(end) != \"/\") {
      document.FORM[output].value = \"Missing prefix length\"
      return
   }
   if (end == userin.length-1) {
      document.FORM[output].value = \"Missing prefix length\"
      return
   }
   prefixlen = userin.slice(end+1)

   // Check for NaN on systems that implement it, but do so in a way
   // that works on those that don't
   if ((! (prefixlen >= 0)) && (! (prefixlen <= 255))) {
      document.FORM[output].value = \"Syntax: A.B.C.D/N\"
      return
   }

   // Check for NaN on systems that don't implement it
   if (prefixlen == 0 &&
       ((prefixlen.length != 1) ||
        (prefixlen.charAt(0) != \"0\"))) {
      document.FORM[output].value = \"Syntax: A.B.C.D/N\"
      return
   }

   if (prefixlen!=0 && prefixlen.charAt(0)==\"0\") {
      document.FORM[output].value = \"By convention, leading zeros are not written\"
      return
   }

   if (prefixlen < 0) {
      document.FORM[output].value = \"Prefix length must be between 0 and 32\"
      return
   } else if (prefixlen <= 8) {
      mask[0] = (255<<(8-prefixlen))&255
      mask[1] = 0
      mask[2] = 0
      mask[3] = 0
   } else if (prefixlen > 8 && prefixlen <= 16) {
      mask[0] = 255
      mask[1] = (255<<(16-prefixlen))&255
      mask[2] = 0
      mask[3] = 0
      if (elementsSeen < 2) {
         document.FORM[output].value = \"Must specify two bytes of address for a prefix length of \" + prefixlen
	 return
      }
   } else if (prefixlen > 16 && prefixlen <= 24) {
      mask[0] = 255
      mask[1] = 255
      mask[2] = (255<<(24-prefixlen))&255
      mask[3] = 0
      if (elementsSeen < 3) {
         document.FORM[output].value = \"Must specify three bytes of address for a prefix length of \" + prefixlen
	 return
      }
   } else if (prefixlen > 24 && prefixlen <= 32) {
      mask[0] = 255
      mask[1] = 255
      mask[2] = 255
      mask[3] = (255<<(32-prefixlen))&255
      if (elementsSeen < 4) {
         document.FORM[output].value = \"Must specify four bytes of address for a prefix length of \" + prefixlen
	 return
      }
   } else {
      document.FORM[output].value = \"Prefix length must be between 0 and 32\"
      return
   }

   for (i=0; i<4; i++) {
      if ((address[i] & mask[i]) != address[i]) {
         document.FORM[output].value += \"Wrong - Trailing bits not zero in \"
	 document.FORM[output].value += ordinal[i] + \" byte\"
	 document.FORM[output].value += unescape(\"%0d%0a\")
         return
      }
   }

   for (i=0; i<4; i++) {
      if ((address[i] & mask[i]) != (target[i] & mask[i])) {
         document.FORM[output].value += \"Wrong - Prefix doesn't match in \"
	 document.FORM[output].value += ordinal[i] + \" byte\"
         return
      }
   }

   if (prefixlen == 0) {
      document.FORM[output].value += \"Well, I guess that's a correct answer\"
      document.FORM[output].value += unescape(\"%0d%0a\")
      document.FORM[output].value += \"Of course, a prefix of 0/0 matches anything\"
      document.FORM[output].value += unescape(\"%0d%0a\")
      document.FORM[output].value += \"Why not try to be a little more creative?\"
      return
   }

   document.FORM[output].value += \"Correct!\"
   document.FORM[output].value += unescape(\"%0d%0a%0d%0a\")

   if (prefixlen==8 || prefixlen==16 || prefixlen==24 || prefixlen==32) {
      document.FORM[output].value += \"You know, a byte-boundary prefix is pretty easy\"
      document.FORM[output].value += unescape(\"%0d%0a\")
      document.FORM[output].value += \"Try again, and challenge yourself with something like /14\"
   }
}

function doQ7(input, output, target_a, target_b, target_c, target_d, prefixlen) {
   var userin = document.FORM[input].value
   var start = 0
   var end = -1
   var elementsSeen = 0
   var address = new Array(0,0,0,0)
   var mask = new Array(0,0,0,0)
   var target = new Array(target_a, target_b, target_c, target_d)
   var ordinal = new Array(\"first\", \"second\", \"third\", \"fourth\")
   var i

   document.FORM[output].value = \"\"

   while (elementsSeen < 3 && (end = userin.indexOf(\".\", end+1)) != -1) {
      if (end-start == 0) {
         document.FORM[output].value = \"Syntax: A.B.C.D\"
         return
      }
      address[elementsSeen] = userin.slice(start,end)
      if ((address[elementsSeen] < 0) || (address[elementsSeen] > 255)) {
         document.FORM[output].value = \"Dotted quad elements must be between 0 and 255\"
         return
      }

      // Check for NaN on systems that implement it, but do so in a way
      // that works on those that don't
      if ((! (address[elementsSeen] >= 0)) &&
          (! (address[elementsSeen] <= 255))) {
         document.FORM[output].value = \"Syntax: A.B.C.D\"
	 return
      }

      // Check for NaN on systems that don't implement it
      if (address[elementsSeen] == 0 &&
          ((address[elementsSeen].length != 1) ||
           (address[elementsSeen].charAt(0) != \"0\"))) {
         document.FORM[output].value = \"Syntax: A.B.C.D\"
	 return
      }

      if (address[elementsSeen]!=0 && address[elementsSeen].charAt(0)==\"0\") {
         document.FORM[output].value = \"By convention, leading zeros are not written\"
	 return
      }

      elementsSeen ++
      start = end+1
   }

   if (elementsSeen != 3) {
      document.FORM[output].value = \"Syntax: A.B.C.D\"
      return
   }
   if (end == userin.length-1) {
      document.FORM[output].value = \"Syntax: A.B.C.D\"
      return
   }
   address[elementsSeen] = userin.slice(end+1)

   if ((address[elementsSeen] < 0) || (address[elementsSeen] > 255)) {
      document.FORM[output].value = \"Dotted quad elements must be between 0 and 255\"
      return
   }

   // Check for NaN on systems that implement it, but do so in a way
   // that works on those that don't
   if ((! (address[elementsSeen] >= 0)) &&
       (! (address[elementsSeen] <= 255))) {
      document.FORM[output].value = \"Syntax: A.B.C.D\"
      return
   }

   // Check for NaN on systems that don't implement it
   if (address[elementsSeen] == 0 &&
       ((address[elementsSeen].length != 1) ||
        (address[elementsSeen].charAt(0) != \"0\"))) {
      document.FORM[output].value = \"Syntax: A.B.C.D\"
      return
   }

   if (address[elementsSeen]!=0 && address[elementsSeen].charAt(0)==\"0\") {
      document.FORM[output].value = \"By convention, leading zeros are not written\"
      return
   }

   elementsSeen ++

   if (prefixlen < 0) {
      document.FORM[output].value = \"Prefix length must be between 0 and 32\"
      return
   } else if (prefixlen <= 8) {
      mask[0] = (255<<(8-prefixlen))&255
      mask[1] = 0
      mask[2] = 0
      mask[3] = 0
   } else if (prefixlen > 8 && prefixlen <= 16) {
      mask[0] = 255
      mask[1] = (255<<(16-prefixlen))&255
      mask[2] = 0
      mask[3] = 0
   } else if (prefixlen > 16 && prefixlen <= 24) {
      mask[0] = 255
      mask[1] = 255
      mask[2] = (255<<(24-prefixlen))&255
      mask[3] = 0
   } else if (prefixlen > 24 && prefixlen <= 32) {
      mask[0] = 255
      mask[1] = 255
      mask[2] = 255
      mask[3] = (255<<(32-prefixlen))&255
   } else {
      document.FORM[output].value = \"Prefix length must be between 0 and 32\"
      return
   }

   for (i=0; i<4; i++) {
      if ((address[i] & mask[i]) != (target[i] & mask[i])) {
         document.FORM[output].value += \"Wrong - Address doesn't match in \"
	 document.FORM[output].value += ordinal[i] + \" byte\"
         return
      }
   }

   document.FORM[output].value += \"Correct!\"

   if (address[0] == target[0] && address[1] == target[1] &&
       address[2] == target[2] && address[3] == target[3] && prefixlen != 32) {
      document.FORM[output].value += unescape(\"%0d%0a%0d%0a\")
      document.FORM[output].value += \"Specifying an address exactly equal to the prefix\"
      document.FORM[output].value += unescape(\"%0d%0a\")
      document.FORM[output].value += \"is a bit trivial, don't you think?\"
      document.FORM[output].value += unescape(\"%0d%0a\")
      document.FORM[output].value += \"Why not try to come up with something a bit different?\"
   }
}

function doQ8(input, output, target_a, target_b, target_c, target_d, plen) {
   var userin = document.FORM[input].value
   var start = 0
   var end = -1
   var elementsSeen = 0
   var address = new Array(0,0,0,0)
   var mask = new Array(0,0,0,0)
   var target = new Array(target_a, target_b, target_c, target_d)
   var ordinal = new Array(\"first\", \"second\", \"third\", \"fourth\")
   var prefixlen
   var i

   document.FORM[output].value = \"\"

   while (((end = userin.indexOf(\".\", end+1)) != -1) ||
          ((end = userin.indexOf(\"/\", end+1)) != -1)) {
      if (elementsSeen == 4) {
         document.FORM[output].value = \"Dotted quad portion must contain no more than four numbers\"
         return
      }
      if (end-start == 0) {
         document.FORM[output].value = \"Syntax: A.B.C.D/N\"
         return
      }
      address[elementsSeen] = userin.slice(start,end)
      if ((address[elementsSeen] < 0) || (address[elementsSeen] > 255)) {
         document.FORM[output].value = \"Dotted quad elements must be between 0 and 255\"
         return
      }

      // Check for NaN on systems that implement it, but do so in a way
      // that works on those that don't
      if ((! (address[elementsSeen] >= 0)) &&
          (! (address[elementsSeen] <= 255))) {
         document.FORM[output].value = \"Syntax: A.B.C.D/N\"
	 return
      }

      // Check for NaN on systems that don't implement it
      if (address[elementsSeen] == 0 &&
          ((address[elementsSeen].length != 1) ||
           (address[elementsSeen].charAt(0) != \"0\"))) {
         document.FORM[output].value = \"Syntax: A.B.C.D/N\"
	 return
      }

      if (address[elementsSeen]!=0 && address[elementsSeen].charAt(0)==\"0\") {
         document.FORM[output].value = \"By convention, leading zeros are not written\"
	 return
      }

      elementsSeen ++
      start = end+1

      if (userin.charAt(end) == \"/\") break
   }

   if (end == -1 || userin.charAt(end) != \"/\") {
      document.FORM[output].value = \"Missing prefix length\"
      return
   }
   if (end == userin.length-1) {
      document.FORM[output].value = \"Missing prefix length\"
      return
   }
   prefixlen = userin.slice(end+1)

   // Check for NaN on systems that implement it, but do so in a way
   // that works on those that don't
   if ((! (prefixlen >= 0)) && (! (prefixlen <= 255))) {
      document.FORM[output].value = \"Syntax: A.B.C.D/N\"
      return
   }

   // Check for NaN on systems that don't implement it
   if (prefixlen == 0 &&
       ((prefixlen.length != 1) ||
        (prefixlen.charAt(0) != \"0\"))) {
      document.FORM[output].value = \"Syntax: A.B.C.D/N\"
      return
   }

   if (prefixlen!=0 && prefixlen.charAt(0)==\"0\") {
      document.FORM[output].value = \"By convention, leading zeros are not written\"
      return
   }

   if (prefixlen < 0) {
      document.FORM[output].value = \"Prefix length must be between 0 and 32\"
      return
   } else if (prefixlen <= 8) {
      mask[0] = (255<<(8-prefixlen))&255
      mask[1] = 0
      mask[2] = 0
      mask[3] = 0
   } else if (prefixlen > 8 && prefixlen <= 16) {
      mask[0] = 255
      mask[1] = (255<<(16-prefixlen))&255
      mask[2] = 0
      mask[3] = 0
      if (elementsSeen < 2) {
         document.FORM[output].value = \"Must specify two bytes of address for a prefix length of \" + prefixlen
	 return
      }
   } else if (prefixlen > 16 && prefixlen <= 24) {
      mask[0] = 255
      mask[1] = 255
      mask[2] = (255<<(24-prefixlen))&255
      mask[3] = 0
      if (elementsSeen < 3) {
         document.FORM[output].value = \"Must specify three bytes of address for a prefix length of \" + prefixlen
	 return
      }
   } else if (prefixlen > 24 && prefixlen <= 32) {
      mask[0] = 255
      mask[1] = 255
      mask[2] = 255
      mask[3] = (255<<(32-prefixlen))&255
      if (elementsSeen < 4) {
         document.FORM[output].value = \"Must specify four bytes of address for a prefix length of \" + prefixlen
	 return
      }
   } else {
      document.FORM[output].value = \"Prefix length must be between 0 and 32\"
      return
   }

   for (i=0; i<4; i++) {
      if ((address[i] & mask[i]) != address[i]) {
         document.FORM[output].value += \"Wrong - Trailing bits not zero in \"
	 document.FORM[output].value += ordinal[i] + \" byte\"
	 document.FORM[output].value += unescape(\"%0d%0a\")
         return
      }
   }

   if (plen != prefixlen) {
      document.FORM[output].value += \"Wrong - the first \" + plen
      document.FORM[output].value += \" bits match between these addresses\"
      document.FORM[output].value += unescape(\"%0d%0a\")
      return
   }

   if (target_a != address[0] || target_b != address[1] || target_c != address[2] || target_d != address[3]) {
      document.FORM[output].value += \"Wrong - the correct answer is \"
      document.FORM[output].value += target_a
      if (plen > 8) {
         document.FORM[output].value += \".\" + target_b
         if (plen > 16) {
            document.FORM[output].value += \".\" + target_c
            if (plen > 24) {
                document.FORM[output].value += \".\" + target_d
            }
         }
      }
      document.FORM[output].value += \"/\" + plen
      document.FORM[output].value += unescape(\"%0d%0a\")
      return
   }

   document.FORM[output].value += \"Correct!\"
   document.FORM[output].value += unescape(\"%0d%0a\")
}

</SCRIPT>
<FORM NAME=\"FORM\" ACTION=\"quiz3.cgi\" METHOD=POST onSubmit=\"return dopost\">\n\n";

$qnum=1;
&problem9;

$qnum=2;
&problem5;

$qnum=3;
&problem10;

print "</FORM>\n";

print "\
";
print "\<P\>\<HR\>\
\<CENTER\>\<B\>Next\:\<\/B\>\ \<A\ HREF\=\"quiz3a\.cgi\"\>Quiz\ 3\ \(cont\)\<\/A\>\<\/CENTER\>\<HR\>\
\<B\>Connected\:\ An\ Internet\ Encyclopedia\<\/B\>\
\<BR\>\
\<EM\>Quiz\ 3\<\/EM\>\
\<\/BODY\>\
\<\/HTML\>\
";
