#!perl
#
#
# Make a file of Tex tags for table elements.
#
# Modification history:
# 24 Aug 2005  TA  Written

use strict;
use warnings;
use Lingua::EN::Numbers qw(num2en num2en_ordinal);
use File::Slurp;
use File::Copy;
use Data::Dumper;
use Getopt::Long;
use File::Temp qw/ tempfile tempdir /;
use Cwd;

my $cwd;
$cwd = getcwd;

sub make_safekey {

    my $key = $_[0];
    my $safekey;
    my $texkey;

    my $cr;
    my $ind;
    my $comp;

    my $safeone;
    my $safetwo;
    my $safethree;
    
    if ( $key !~ /.vect/ ) {  #this is a knot type
	
	if ( $key =~ /(\d+)_(\d+)_(\d+)/ ) { # a link
	    
	    $safeone = $1; $safetwo = $2; $safethree = $3;

	    $cr = num2en($safeone); $comp = num2en($safetwo); $ind = num2en($safethree);
	    $cr =~ s/\s/\-/g; $comp =~ s/\s/\-/g; $ind =~ s/\s/\-/g;
	   
	    $cr =~ s/\b(\w)(\w*)/uc($1).lc($2)/ge;
	    $comp =~ s/\b(\w)(\w*)/uc($1).lc($2)/ge;
	    $ind =~ s/\b(\w)(\w*)/uc($1).lc($2)/ge;
	    
	    $safekey = $cr.$comp.$ind;
	    $safekey =~ s/-//g;
	    $texkey = $safeone."^{".$safetwo."}_{".$safethree."}";

	    $cr = $safeone; $comp = $safetwo; $ind = $safethree;
	    
	} elsif ( $key =~ /(\d+)_(\d+)/ ) { # a knot
	
	    $safeone = $1; 
	    $safetwo = $2;
	    
	    $texkey = $safeone."_{".$safetwo."}";
    
	    $cr = num2en($safeone); $ind = num2en($safetwo);
	    $cr =~ s/\s/\-/g; $ind =~ s/\s/\-/g;

	    $cr =~ s/\b(\w)(\w*)/uc($1).lc($2)/ge;
	    $ind =~ s/\b(\w)(\w*)/uc($1).lc($2)/ge;
	    

	    $safekey = $cr.$ind;
	    $safekey =~ s/-//g;
	
	    $cr = $safeone; $comp = 1; $ind = $safetwo;

	} else {

	    print "FAILED to match key ".$key."\n";

	}


    }
	
    my @retstuff = ($safekey,$texkey,$cr,$comp,$ind);

    return @retstuff;
	    
}

# structuredTable is a general Perl interface to creating tables in several columns in LaTeX document. 
# The basic idea is that we are passed an array of data and want to split it into columns so that we 
# can construct tables consisting of several columns, each continuing the same array of data. The data
# is split into groups by various separators. We assume that the separators contain \addlinespace tags.

# We assume that we are passed a hash containing:

# 'number of columns'
# 'columns per entry' (the number of sub-columns in each table);
# 'column heads' (an array of heads for the columns in the input data)
# 'target height' in points
# 'data' a reference to an array of hashes containing entries 'data' (in TeX form) and 'separator' (if 
#      this is a boundary between groups of data)
# 'topmatter' topmatter from the final tex document in which we're going to insert the table (to set fonts, etc)
# 'caption' caption for the table (in TeX, of course)
# 'label' the labels for the various pages of the table (if it's split across more than one will be '$label 0', '$label 1', etc..

# We then adjust the number of data items in each column, and tweak the height of the separators to get the 
# total column heights to work out as best we can.

# The first thing we use a general mechanism for figuring out the height of a piece of TeX

sub tex_height {

  my $topmatter = shift;  # anything needed to get the block to typeset correctly. Should match topmatter from final doc.
  my $texblock = shift;  # actual block of TeX
  
  my $fh;
  my $filename;
  my $texheight;
  my $dir;

  $dir = tempdir( CLEANUP => 0 );
  ($fh, $filename) = tempfile( "tempXXXXX", SUFFIX => '.tex', DIR => $dir);

  # print "filename was $filename\n";
  
  print $fh $topmatter;
  print $fh "\n\n\\begin{document}\n\n\\newsavebox{\\mybox}\n\\savebox{\\mybox}{\\parbox{4cm}{\n";
  print $fh $texblock;
  print $fh " }}\n\n\\showthe\\ht\\mybox\n\\end{document}\n\n";
  
  my @texoutput;

  open(PDFLATEX,"pdflatex --interaction=nonstopmode $filename |");
  while (<PDFLATEX>) {
    
    if ($_ =~ />(.+)pt\./) { $texheight = $1; }
    push @texoutput, $_;
    
  }
  
  unless (defined $texheight) { die("pdfLatex couldn't TeX this chunk. Probably a package or topmatter problem.\nTeX output was: @texoutput"); }

  # print "texheight was $texheight\n";

  my $auxfile;
  $auxfile = $filename;
  $auxfile =~ s/\/.+\//.\//g;
  $auxfile =~ s/tex/aux/g;
  unlink($auxfile) or die("Couldn't remove $auxfile\n");

  my $logfile;
  $logfile = $filename;
  $logfile =~ s/\/.+\//.\//g;
  $logfile =~ s/tex/log/g;
  unlink($logfile) or die("Couldn't remove $logfile\n");

  return $texheight;
  
 };

sub topmatter {

  my $filename = shift;

  open (FN, "<$filename") or die("Couldn't open $filename.\n");

  my $topmatter;

  texfileline: while (<FN>) {

      last texfileline if ($_ =~ /begin{document}/);
      if ($_ !~ /input/) {
	$topmatter .= $_;
      }
    }

  return $topmatter;

}
      

sub tex_source {

  my $dataref = shift;
  my @data = @{$dataref};
  my $tdref = shift; 
 
  # print "tex_source called with dataref $dataref which points to an array containing ".scalar @data." elements.\n";
  # We print a column in the form $dataelt->{data} $dataelt->{separator} unless a nontrivial separator occurs last.
  # The tex_source has to make a table out of the data column, which means that we need to know columns and headers, too.

  my @output;
  my $dataelt;

  unless (defined $tdref) { 
    
    die("tex_source called with undefined tdref\n");

  }

  unless (defined $dataref) {

    die("tex_source called with undefined dataref\n");

  }

  push (@output, '\begin{tabular}[t]{'.'l@{\hspace{0.1in}}'.('r@{\hspace{0.1in}}' x (($tdref->{'columns per entry'}-2))).'r'.'} \toprule'."\n".$tdref->{'column heads'}."\\\\ \\midrule");
  
  foreach $dataelt ( @data ) {

    push @output, $dataelt->{data}.$dataelt->{separator};

  }

  $dataelt = pop(@data);
  push @data, $dataelt;

  pop @output;
  push @output, $dataelt->{data}; # Fix the last element, so it doesn't appear with a separator

  push(@output,'\bottomrule'."\n".'\end{tabular}'."\n");

  #print "tex_source produced output array of ".scalar @output." elements: @output \n";

  my $output = join("\n",@output);
  
  #print "tex_source produced output $output \n";

  return $output;

}

sub tex_table {

  my $tdref = shift; 
  my %tabledata = %{$tdref};

  my $numcolumns = $tabledata{'number of columns'};
  my $colsperentry = $tabledata{'columns per entry'};
  my $colheads = $tabledata{'column heads'};
  my $targetheight = $tabledata{'target height'};
  my @data = @{$tabledata{'data'}};
  my $topmatter = $tabledata{'topmatter'};
  my $caption = $tabledata{'caption'};
  my $label = $tabledata{'label'};

  die("Need 'number of columns', 'columns per entry', 'column heads', 'target height' (in pts), \n'data' (an array ref), 'topmatter', and 'caption' to call tex_table.\n") 
    unless (defined $numcolumns && defined $colsperentry && defined $targetheight && defined $topmatter && defined $caption);

  print "tex_table called with $numcolumns columns of target height $targetheight pts and ".scalar @data." pieces of data.\n";

  # job 0 is to clean the input, making sure that separators are defined (even if empty) for all data elts.
  
  # print "Actual data is: Dumper(@data) \n";

  # The idea is that we're going to shuffle the data between the columns until the texheights are as close together as we can manage, 
  # then adjust the separators slightly until they match. We first get an estimate of the size of one line.

  my @linetest;
  $linetest[0] = $data[0];
  my @twolinetest;
  $twolinetest[0] = $data[0];
  $twolinetest[1] = $data[1];

 # print "\t linetest is ".Dumper(@linetest)." and data[0] is $data[0] \n";

  my $lineheight = tex_height($topmatter,tex_source(\@twolinetest,\%tabledata)) - tex_height($topmatter,tex_source(\@linetest,\%tabledata));

  print "Lineheight is: $lineheight. \n";

  my @columns;
  my $page = -1;
  my $column = 0;
  my $entry = 0;

  # columns is a three dimensional array: pages, columns, data.
  
 COLUMN: while ( scalar @data > 0 ) {

    if ($column == 0) { $page++; }
    print "\nColumn $column of page $page.\n\n";

    # goal: fill a column as closely as possible. We start by guessing about the number of lines.
    
    my $initialguess = int($targetheight/$lineheight);
    INITIALPUSH: for (0..$initialguess-1) { 

      push @{$columns[$page][$column]}, shift @data;
      last INITIALPUSH if (scalar @data == 0);

    }

    #print "After initial push/shift we have order \n".tex_source(\@{$columns[$page][$column]});

    if (scalar @{$columns[$page][$column]} == $initialguess) {

      print "\tPushed initialguess of $initialguess data elements into column $column of page $page. Data contains ".scalar @data." items.\n";

    } else {

      print "\tRan out of data and only pushed ".scalar @{$columns[$page][$column]}." data elements into column $column of page $page. Data contains ".scalar @data." items\n";

    }
      
    # Now we are either too large or too small; we check both.

    TOOSMALLPUSH: while (tex_height($topmatter,tex_source(\@{$columns[$page][$column]},\%tabledata)) < $targetheight) {

	last TOOSMALLPUSH if (scalar @data == 0); # We could start with nothing on the data stack.
	push @{$columns[$page][$column]}, shift @data;
	print "After toosmall push/shift we have order \n".tex_source(\@{$columns[$page][$column]},\%tabledata);
      
    }

    while (tex_height($topmatter,tex_source(\@{$columns[$page][$column]},\%tabledata)) > $targetheight) {

      unshift @data, pop @{$columns[$page][$column]};

    }

    print "\tAfter pushing and popping, we have ".scalar @{$columns[$page][$column]}." data elements for tex height of ".(tex_height($topmatter,tex_source(\@{$columns[$page][$column]},\%tabledata)))." and ".scalar @data." items remain\n";

    # We now tune in the separators to hit the target height.

    my $rawstring;
    $rawstring = tex_source(\@{$columns[$page][$column]},\%tabledata);
    
    my $current_sepsize;
    if ($rawstring =~ /\\addlinespace\[(.+?)em\]/) {
      $current_sepsize = $1;
    } else {

      # We don't have a real separator, so we're going to have to go back and adjust the line spacing to make everything come out.
      # This amounts to adding a separator after every column element.

      print "\tNo real separator! \n";
     
      my $dta; 
      foreach $dta (@{$columns[$page][$column]}) {
	$dta->{separator} = "\\addlinespace[0.1em]";
      }

      $current_sepsize = 0.1;

    }

    my $current_height = tex_height($topmatter,tex_source(\@{$columns[$page][$column]},\%tabledata));

    print "\tCalculated current height of $current_height pt and current separatorsize of $current_sepsize em.\n";

    if (scalar @data > 0 || ($targetheight - $current_height) < 2*$lineheight) {

      my $data;
      my $new_sepsize = $current_sepsize + 1.0;
      foreach $data (@{$columns[$page][$column]}) { 
	$data->{separator} =~ s/\\addlinespace\[.+?\]/\\addlinespace[${new_sepsize}em]/g;
      }
      my $new_height = tex_height($topmatter,tex_source(\@{$columns[$page][$column]},\%tabledata));
      
      print "\tChanging separator size to $new_sepsize em results in texheight of $new_height pt.\n";
      
      # We now know that we have ($current_sepsize,$current_height) and ($new_sepsize,$new_height).
      # Using these, we solve for the $final_sepsize which should reach $target_height.
      
      my $m = ($new_height - $current_height)/($new_sepsize - $current_sepsize);
      my $final_sepsize = ($targetheight - $current_height)/$m + $current_sepsize;
      foreach $data (@{$columns[$page][$column]}) { 
	$data->{separator} =~ s/\\addlinespace\[.+?\]/\\addlinespace[${final_sepsize}em]/g;
      }
      my $final_height = tex_height($topmatter,tex_source(\@{$columns[$page][$column]},\%tabledata));
      
      print "\tAfter calculating final separator size of $final_sepsize em, we have a final texheight of $final_height pt (vs target of $targetheight pt) for this column.\n";
      
    } else {

      print "\tLast column is too short to adjust to final height. We do not adjust separators for final height of $current_height pt.\n";
      
    }
    
    $column++;
    if ($column == $numcolumns) { $column = 0; }
    
  }

  # We have now separated the data into columns and pages and tried to make everything match heights.
  # Our goal now is actually to produce the table as a string.

  my $numpages = $page + 1;

  print "\nThe final table has $numpages pages. Top dimension of columns looks like ".(scalar @columns - 1)."\n";

  my @finalpages;
  foreach $page (0..scalar @columns-1) { # the top dimension is pages

    # Wrap each collection of columns in a table environment.

     $finalpages[$page] .=  "\\begin{table}[h]\n \\caption{\\label{$label $page} $caption";
     if ($numpages > 1) {
       $finalpages[$page] .= ", Part ".($page+1)." of $numpages";
     }
     $finalpages[$page] .= "}\n\n";

    # Each column is its own table, separated by \hspace{0.25}.

     $finalpages[$page] .= "\\begin{center}\n\\hfill";
  
    foreach $column (0..scalar @{$columns[$page]}-1) {

      print "column $column of page $page. \n";

      $finalpages[$page] .= tex_source(\@{$columns[$page][$column]},\%tabledata).'\hfill';
    
    }

    $finalpages[$page] .= "\\hfill\\end{center}\n";
    # Now we wrap up the table.

    $finalpages[$page] .= "\\end{table}\n";

  }

  return join("\n\\newpage\n",@finalpages);
    
}


sub perltable {

    # We expect input in the following form

    # perltable(FILE,[40 50 50 50],["10.1 & 12", "14.1 & 11", "12.1 & 2"],"Temperature & Number of Fish","Water Temperature and Number of Fish Caught");

    my $fname=$_[0];
    my @rows=@{$_[1]};
    my @data=@{$_[2]};
    my $colheads=$_[3];
    my $caption=$_[4];
    my $maxcols=$_[5];
    my $label=$_[6];

    my $tab = 0;
    my $tabdatastart = 0;  #The first piece of data in this table.

    open(TABLE,">${cwd}/data/$fname") or die("Couldn't open $fname:$_\n");
    print "Called perltable with ".scalar @rows." tables of rows and ".scalar @data." data\n";

    foreach $tab (0..scalar @rows) {

	print "Survived to table $tab with datastart $tabdatastart.\n";

	if (!defined($data[$tabdatastart])) { return; }

	my $cols =  int( scalar @data/($rows[$tab] + 1)); 
	if ($cols > $maxcols) { $cols = $maxcols; }
	my $onetab = scalar @data/($maxcols*$rows[0]) < 1;

        # Number of entries / $rows on this table entries per col.

	print "Making Perl Table $tab of $rows[$tab] x $cols entries of ".scalar @data." total pieces of data.\n";
	my $colsperentry = ($colheads =~ tr/&//) + 1;
	print "Counting $colsperentry columns per item in data.\n";

	if (scalar @data - $tabdatastart < $rows[$tab]*$cols) { # We're on the last table

	  if (scalar @data - $tabdatastart < 10) { # We really only have a few to go

	    $cols = 1;
	    $rows[$tab] = int( (scalar @data - $tabdatastart) );

	    print "Balancing last table by resetting rows to $rows[$tab] and cols to $cols\n";

	  } else { # There's more stuff, enough to make a short table look right

	    $rows[$tab] = int( (scalar @data - $tabdatastart + $cols-1)/$cols) + 1;
	    if ($rows[$tab] > 1) { $rows[$tab]--; } 

	    print "Balancing last table by resetting rows to $rows[$tab].\n";

	  }

	}

	print TABLE '\begin{table}[h]'."\n";
	print TABLE '\caption{\label{'.$label.num2en($tab+1).'}';
	if (!$onetab) { print TABLE 'Part '.($tab+1).' of '; }
	print TABLE $caption.'}'."\n";

	foreach my $j (0..$cols-1) {

	    if (defined $data[$tabdatastart + $j*$rows[$tab]]) {

		print TABLE '\begin{tabular}[t]{'.("l" x ($colsperentry)).'} \toprule'."\n";
		print TABLE $colheads."\\\\ \\midrule \n";

		foreach my $i (0..$rows[$tab]) {

		    my $ind = $tabdatastart + $i + $j*$rows[$tab];

		    if (defined $data[$ind]) {

			print TABLE "$data[$ind]";
			
		    } 

		}

		print TABLE '\bottomrule'."\n".'\end{tabular}'."\n";
		print TABLE '\hspace{0.25in}'."\n";

	    }

	}
	
	print TABLE '\end{table}'."\n";

	# We now update $tabdatastart;

	$tabdatastart += $rows[$tab]*$cols;

    }

    $|++;
    close(TABLE);

}

require "${cwd}/bin/tightknot_common.pl";

print STDERR "mkTable 1.0: Generates TeX tables of ropelength data from KnotDB.pl.\n";

my $include_composites= '';

GetOptions ( 'composites' => \$include_composites);

#open(DB,'KnotDB.pl');
my $DBFILE = read_file("${cwd}/knotdb/KnotDB.pl");
my %knots;
eval($DBFILE);

#close(DB);

my %allknots;
%allknots = %knots;

my $numkeys = keys ( %knots );
print "We have $numkeys entries in the original database. Looking for knots.\n";

# first, a pass to remove files from the hash

my $key;
my $nconsidered;
my $ndeleted;

$nconsidered = 0;

foreach $key ( keys %knots ) {

    $nconsidered++;

    if ($key =~ /vect/) {

	delete $knots{$key};
	#print "Deleted $key.\n";
	if (defined $knots{$key}) { die("I tried to delete $key, but it has value $knots{$key}.\n"); }
	$ndeleted++;

	if ($key eq 'kl_8_2_11_cc.vect') { print(" Tried to delete kl_8_2_11_cc.vect here.\n"); }

    }

  }

if ($include_composites) {

    foreach $key ( keys %knots ) {

	unless (defined($knots{$key}{summands})) {

	    delete $knots{$key};

	}
	
    }

    foreach $key ( keys %knots ) {

    	unless ($knots{$key}{comp} == 1) {

	    delete $knots{$key};

	}

    }

}

unless ($include_composites) { 

  foreach $key ( keys %knots ) {

    if (defined($knots{$key}{summands})) { 
      
      delete $knots{$key};
      
    }
    
  }
  
}

my @numkeys;
@numkeys = keys ( %knots );

#print "@numkeys\n";

print "Considered $nconsidered records in the Knot database and deleted $ndeleted.\n";
print "There seem to be ".scalar @numkeys." knot and link types in the database. Generating TeX macros and data...\n";

# now a pass to generate tags 
open(TEX,">${cwd}/data/KnotTags.tex") or die("Can't open KnotTags.tex");

my $safekey;
my $texkey;
my $cr;
my $comp;
my $ind;

foreach $key ( sort(keys %knots) ) {

    unless ($include_composites) {

	$knots{$key}{ropelength} = sprintf("%2.2f",$knots{$key}{ropelength} + 0.005);
	$knots{$key}{upperbound} = sprintf("%2.2f",$knots{$key}{upperbound} + 0.005);
	
    } else {

	$knots{$key}{ropelength} = sprintf("%2.3f",$knots{$key}{ropelength} + 0.0005);
	$knots{$key}{upperbound} = sprintf("%2.3f",$knots{$key}{upperbound} + 0.0005);
	$knots{$key}{residual} = sprintf("%1.5f",$knots{$key}{residual} + 0.000005);
    }

  unless (defined($knots{$key}{summands})) { # Don't make safekeys for composites.

    ($safekey,$texkey,$cr,$comp,$ind) = &make_safekey($key);

    # Now some formatting modifications for the table.

    print TEX "\\newcommand{\\".$safekey."}{".$texkey."}\n";
    print TEX "\\newcommand{\\".$safekey."RRUB}{".$knots{$key}{upperbound}."}\n";
    print TEX "\\newcommand{\\".$safekey."RProp}{".$knots{$key}{ropelength}."}\n";
    
    $knots{$key}{tex} = $texkey;

    $cr = $knots{$key}{cr};
    $comp = $knots{$key}{comp};
    $ind = $knots{$key}{index};

    print "Assigned key to ".$key." of ".$texkey.", cr of ".$cr.", comp of ".$comp.", index of ".$ind." with Latex key of ".$safekey."\n";
   
  } else {

    $knots{$key}{tex} =~ s/\}mr/\}^\{mr\}/g;
    $knots{$key}{tex} =~ s/\}rm/\}^\{rm\}/g;
    $knots{$key}{tex} =~ s/\}m/\}\^m/g;
    $knots{$key}{tex} =~ s/\}r/\}\^r/g;
     
      
  }

}

close TEX;

my @knotlist = sort{ $knots{$a}{sortorder} <=> $knots{$b}{sortorder} } keys ( %knots );

# Now we spit out the text table.

print "Printing list of ropelengths of knots to flatropelengths.dat..\n";

open(RL,">${cwd}/data/flatropelengths.dat") || die("Couldn't open flatropelengths.dat\n");

foreach $key ( @knotlist ) {
  
  if ($knots{$key}{comp} == 1) {print RL "$key $knots{$key}{upperbound}\n";}

}

close(RL);

print "Printing list of ropelengths of knots and links to allropelengths.dat...\n";

open(RL,">${cwd}/data/allropelengths.dat") || die("Couldn't open allropelengths.dat\n");

foreach $key ( @knotlist ) {

  print RL "$key $knots{$key}{upperbound}\n";

}

close(RL);

print "Printing list of ropelengths of composite links to compositelinks.txt...\n";

 my ($k, $ub, $rso, $res);

open(CLR,">${cwd}/data/compositelinks.txt") || die("Couldn't open compositelinks.txt\n");

foreach $key ( @knotlist ) {

  if (defined($knots{$key}{summands}) && $knots{$key}{comp} > 1) {

    $k = $key;
    $ub = $knots{$key}{upperbound};
    $rso = $knots{$key}{edges}/$ub;
    $res = $knots{$key}{residual};

    write CLR;

  }

}

close(CLR);

#
# Now we output knots which seem to violate the connect sum conjecture.
#

copy("${cwd}/../rrpaper/iopart.cls","${cwd}") or die("Couldn't copy over iopart.cls");
copy("${cwd}/../rrpaper/iopart12.clo","${cwd}") or die("Couldn't copy over iopart12.clo");

if ($include_composites) {
  
  my $summandsum = 0;
  my @CSCviolators;
  
  # Search @knotlist for violators of the conjecture:

  print "Seaching ".scalar @knotlist." entries for violators of connect sum conjecture.\n";
  
  foreach $key (@knotlist) {

    $summandsum = 0;
    
    my %line;
    
    if (defined $knots{$key}{summands}) {
      
      my $summand;
      
      foreach $summand ( @{$knots{$key}{summands}} ) { 
	
	#print standardknottype($summand)."\n";
	$summandsum += $allknots{standardknottype($summand)}{upperbound};
	
      }
      
      my $target = $summandsum - (4*3.1415926 - 4)*(scalar @{$allknots{$key}{summands}} - 1);
      
      if ($knots{$key}{upperbound} > $target) {
	
	# Output the line.
	
	$line{'percentdiff'} = 100*(($knots{$key}{upperbound} - $target)/$target);
	$line{'data'} = '$'.$knots{$key}{tex}.'$ & $'.sprintf("%2.3f",$knots{$key}{upperbound}).'$ & $'.sprintf("%2.3f",$target).'$ & $'.sprintf("%2.3f",100*(($knots{$key}{upperbound} - $target)/$target)).'$ \%  \\\\'."\n";
	$line{'separator'} = " ";

	print "ConnectSumConjecture: ".$line{'data'}."\n";
	push @CSCviolators,\%line;																       
	
      } 
      
    }

  }

  print "Found ".scalar @CSCviolators." violators of the conjecture.\n";

  my @sortedViolators;
  @sortedViolators = reverse sort { $a->{'percentdiff'} <=> $b->{'percentdiff'} } @CSCviolators; 
  
  # structuredTable is a general Perl interface to creating tables in several columns in LaTeX document. 
  # The basic idea is that we are passed an array of data and want to split it into columns so that we 
  # can construct tables consisting of several columns, each continuing the same array of data. The data
  # is split into groups by various separators. We assume that the separators contain \addlinespace tags.
  
  # We assume that we are passed a hash containing:
  
  # 'number of columns'
  # 'columns per entry' (the number of sub-columns in each table);
  # 'column heads' (an array of heads for the columns in the input data)
  # 'target height' in points
  # 'data' a reference to an array of hashes containing entries 'data' (in TeX form) and 'separator' (if 
  #      this is a boundary between groups of data)
  # 'topmatter' topmatter from the final tex document in which we're going to insert the table (to set fonts, etc)
  # 'caption' caption for the table (in TeX, of course)
  # 'label' the labels for the various pages of the table (if it's split across more than one will be '$label 0', '$label 1', etc..
  
  my %tabdata;

  %tabdata = ( 'number of columns' => 2, 'columns per entry' => 4, 'column heads' => 'Composite Knot & \hspace{-5pt}Ropelength & Conjectured & Difference',
	       'target height' => 173, 'data' => \@sortedViolators, 'caption' => "Composite Knots which violate Connect Sum Conjecture",
	       'label' => "CSCTable", 'topmatter' => &topmatter("${cwd}/../rrpaper/compositepaperJPhysA.tex") );
  
  open(KTAB,">${cwd}/tex/CSCViolatorTable.tex") or die("Couldn't open ${cwd}/tex/CSCViolatorTable.tex");
  print KTAB &tex_table(\%tabdata);
  close(KTAB);
  
  copy("${cwd}/tex/CSCViolatorTable.tex","${cwd}/../rrpaper/CSCViolatorTable.tex") or die("Couldn't copy CSCViolatorTable.tex\n");
  print "CSCViolatorTable.tex -> ../rrpaper/CSCViolatorTable.tex\n";
  
}
  

print "Now organizing the table by crossing number...\n";

my @bycrossing;

foreach $key ( @knotlist ) {

    if (defined $knots{$key}{cr}) { push(@{ $bycrossing[$knots{$key}{cr}] },$key); }

}


my @byknottype;
my $crnum;
my $lastkey;
my $startcr;
my $endcr;

unless ($include_composites) {

    $startcr = 2;
    $endcr = 10;

} else {

    $startcr = 6;
    $endcr = 12;

}

my @sortorders;

foreach $crnum ($startcr..$endcr) {

  @{ $bycrossing[$crnum] } = sort { $knots{$a}{sortorder} <=> $knots{$b}{sortorder} } @{ $bycrossing[$crnum] };
  @{ $sortorders[$crnum] } = map { $knots{$_}{sortorder} } @{ $bycrossing[$crnum] }
}



print "Example: 6 crossing knots are @{ $bycrossing[6] } with sort orders @{ $sortorders[6] } \n";
print "Example: 9 crossing knots are @{ $bycrossing[9] } with sort orders @{ $sortorders[9] } \n";

print "Now making list of knots and links with ropelengths....\n";

foreach $crnum ($startcr..$endcr) {
  
  my $prevline;
  
  foreach $key ( sort { $knots{$a}{sortorder} <=> $knots{$b}{sortorder} } @{ $bycrossing[$crnum] } ) {

    my %line;

    # If the # of components or prime summands changes, output a trimmed midrule to highlight it.
    
    $line{'separator'} = " ";
    
    if (defined($lastkey)) {
      
      if ($knots{$lastkey}{comp} != $knots{$key}{comp}) {
	
	#my $entry = pop(@byknottype);
	#chomp($entry);
	#push(@byknottype,$entry.' \midrule'."\n");
	
	$prevline = pop(@byknottype);
	$prevline->{'separator'} = '\addlinespace[0.45em]\cmidrule(r{1.5em}l{1.5em}){1-3}\addlinespace[0.45em]'."\n";
	push(@byknottype,$prevline);
	
      } else {
	
	# Now check number of prime summands
	
	my $last_summands;
	my $this_summands;
	
	#	    print scalar @{$knots{$lastkey}{summands}};
	#	    print "\n";
	
	unless (defined($knots{$lastkey}{summands})) { $last_summands = 1; } else {$last_summands = scalar @{$knots{$lastkey}{summands}};}
	unless (defined($knots{$key}{summands})) { $this_summands = 1; } else {$this_summands = scalar @{$knots{$key}{summands}};}
	
	if ($last_summands != $this_summands) {
	  
	  #my $entry = pop(@byknottype);
	  #chomp($entry);
	  #push(@byknottype,$entry.' \midrule'."\n");
	  
	  $prevline = pop(@byknottype);
	  $prevline->{'separator'} = '\addlinespace[0.45em]\cmidrule(r{1.5em}l{1.5em}){1-3}\addlinespace[0.45em]'."\n";
	  push(@byknottype,$prevline);
	  
	  # $line{'separator'} = '\addlinespace[0.45em]\cmidrule(r{1.5em}l{1.5em}){1-3}\addlinespace[0.45em]'."\n";
	  
	} elsif ($include_composites) {
	  
	  my $basekey;
	  my $baselastkey;
	  
	  $basekey = $key;
	  $baselastkey = $lastkey;
	  
	  $basekey =~ s/_rm//g;
	  $basekey =~ s/_m//g;
	  $basekey =~ s/_r//g;
	  
	  $baselastkey =~ s/_rm//g;
	  $baselastkey =~ s/_m//g;
	  $baselastkey =~ s/_r//g;
	  
	  if ($basekey ne $baselastkey) {
	    
	    #my $entry = pop(@byknottype);
	    #chomp($entry);
	    #push(@byknottype,$entry.' \midrule'."\n");
	    
	    #$line{'separator'} = "\\addlinespace[0.45em]\n";
	    
	    $prevline = pop(@byknottype);
	    $prevline->{'separator'} = "\\addlinespace[0.45em]\n";
	    push(@byknottype,$prevline);
	    
	  }
	  
	}
	
      }
      
    }
    
    $lastkey = $key;
    
    # Now output the regular line. 
    
    $line{'data'} = '$'.$knots{$key}{tex}.'$ & $'.sprintf("%2.2f",$knots{$key}{ropelength}).'$ & $'.sprintf("%2.2f",$knots{$key}{upperbound}).'$ \\\\'."\n";
    push @byknottype, \%line;
    
  }

  # When crossing number changes, insert an untrimmed midrule.
  
  $lastkey = undef;
  #    my $entry = pop(@byknottype);
  #    chomp($entry);
  #    push(@byknottype,$entry.' \midrule'."\n");
  
  if ($crnum != $endcr) {

    $prevline = pop(@byknottype);
    $prevline->{'separator'} = '\addlinespace[0.45em]\cmidrule(r{0.5em}l{0.5em}){1-3}\addlinespace[0.45em]'."\n";
    push(@byknottype,$prevline);
    
    #$byknottype[$#byknottype-1]{separator} = '\addlinespace[0.45em]\cmidrule(r{0.5em}l{0.5em}){1-3}\addlinespace[0.45em]'."\n";

  }
    
}


# structuredTable is a general Perl interface to creating tables in several columns in LaTeX document. 
# The basic idea is that we are passed an array of data and want to split it into columns so that we 
# can construct tables consisting of several columns, each continuing the same array of data. The data
# is split into groups by various separators. We assume that the separators contain \addlinespace tags.

# We assume that we are passed a hash containing:

# 'number of columns'
# 'columns per entry' (the number of sub-columns in each table);
# 'column heads' (an array of heads for the columns in the input data)
# 'target height' in points
# 'data' a reference to an array of hashes containing entries 'data' (in TeX form) and 'separator' (if 
#      this is a boundary between groups of data)
# 'topmatter' topmatter from the final tex document in which we're going to insert the table (to set fonts, etc)
# 'caption' caption for the table (in TeX, of course)
# 'label' the labels for the various pages of the table (if it's split across more than one will be '$label 0', '$label 1', etc..

my %tabdata = ( 'number of columns' => 3, 'columns per entry' => 3, 'column heads' => 'Link & $\PRop$ & $\Rop$',
		'target height' => 292, 'data' => \@byknottype, 'caption' => "Ropelengths of Tight Knots and Links by Knot Type",
		'label' => "ByKnotTable", 'topmatter' => &topmatter("${cwd}/../rrpaper/compositepaperJPhysA.tex") );

if ($include_composites) {
  $tabdata{'column heads'} =~ s/Link/Knot/g;
  $tabdata{'caption'} =~ s/and Links//g;
}

# now a pass to generate the first actual table

#&perltable("KnotTable.tex",\@rows,\@byknottype,$colheads,$caption,3,"ByKnotTable");    

# In order to tex this, we'll need to copy over the class files from rrpaper

copy("${cwd}/../rrpaper/iopart.cls","${cwd}") or die("Couldn't copy over iopart.cls");
copy("${cwd}/../rrpaper/iopart12.clo","${cwd}") or die("Couldn't copy over iopart12.clo");

open(KTAB,">${cwd}/tex/KnotTable.tex") or die("Couldn't open ${cwd}/tex/KnotTable.tex");
print KTAB &tex_table(\%tabdata);
close(KTAB);

unless ($include_composites) {

 copy("${cwd}/tex/KnotTable.tex","${cwd}/../rrpaper/KnotTable.tex") or die("Couldn't copy KnotTable.tex\n");
 print "KnotTable.tex -> ../rrpaper/KnotTable.tex\n";

} else {

  copy("${cwd}/tex/KnotTable.tex","${cwd}/../rrpaper/CompositeKnotTable.tex") or die("Couldn't copy KnotTable.tex\n");
  print "KnotTable.tex -> ../rrpaper/CompositeKnotTable.tex\n";

}

my @rows;
my $colheads;
my $caption;

print "Now making table of knots ordered by ropelength ... \n";

# ############  Now we output the ordered by ropelength table.

@knotlist = sort{ $knots{$a}{upperbound} <=> $knots{$b}{upperbound} } keys( %knots );

my @byropelength;

foreach $key ( @knotlist ) {

    push(@byropelength,'$'.$knots{$key}{tex}.'$ \\\\'."\n");

}

unless($include_composites) {
    @rows = qw(45 45 45);
} else {
    @rows = qw(45 45 45 45 45);
}

unless($include_composites) {
    $colheads = 'Link';
} else {
    $colheads = 'Knot';
}

$caption = "Knot and Link Types sorted by Ropelength";

&perltable("KnotTableByRop.tex",\@rows,\@byropelength,$colheads,$caption,4,"ByRopTable");

#
# Now we give the table of residuals. 
#

print "Making table of residuals...\n";

@byknottype = ( );

foreach $crnum ($startcr..$endcr) {
  
  my $prevline;
  
  foreach $key ( sort { $knots{$a}{sortorder} <=> $knots{$b}{sortorder} } @{ $bycrossing[$crnum] } ) {

    my %line;

    # If the # of components or prime summands changes, output a trimmed midrule to highlight it.
    
    $line{'separator'} = " ";
    
    if (defined($lastkey)) {
      
      if ($knots{$lastkey}{comp} != $knots{$key}{comp}) {
	
	#my $entry = pop(@byknottype);
	#chomp($entry);
	#push(@byknottype,$entry.' \midrule'."\n");
	
	$prevline = pop(@byknottype);
	$prevline->{'separator'} = '\addlinespace[0.45em]\cmidrule(r{1.5em}l{1.5em}){1-2}\addlinespace[0.45em]'."\n";
	push(@byknottype,$prevline);
	
      } else {
	
	# Now check number of prime summands
	
	my $last_summands;
	my $this_summands;
	
	#	    print scalar @{$knots{$lastkey}{summands}};
	#	    print "\n";
	
	unless (defined($knots{$lastkey}{summands})) { $last_summands = 1; } else {$last_summands = scalar @{$knots{$lastkey}{summands}};}
	unless (defined($knots{$key}{summands})) { $this_summands = 1; } else {$this_summands = scalar @{$knots{$key}{summands}};}
	
	if ($last_summands != $this_summands) {
	  
	  #my $entry = pop(@byknottype);
	  #chomp($entry);
	  #push(@byknottype,$entry.' \midrule'."\n");
	  
	  $prevline = pop(@byknottype);
	  $prevline->{'separator'} = '\addlinespace[0.45em]\cmidrule(r{1.5em}l{1.5em}){1-2}\addlinespace[0.45em]'."\n";
	  push(@byknottype,$prevline);
	  
	  # $line{'separator'} = '\addlinespace[0.45em]\cmidrule(r{1.5em}l{1.5em}){1-3}\addlinespace[0.45em]'."\n";
	  
	} elsif ($include_composites) {
	  
	  my $basekey;
	  my $baselastkey;
	  
	  $basekey = $key;
	  $baselastkey = $lastkey;
	  
	  $basekey =~ s/_rm//g;
	  $basekey =~ s/_m//g;
	  $basekey =~ s/_r//g;
	  
	  $baselastkey =~ s/_rm//g;
	  $baselastkey =~ s/_m//g;
	  $baselastkey =~ s/_r//g;
	  
	  if ($basekey ne $baselastkey) {
	    
	    #my $entry = pop(@byknottype);
	    #chomp($entry);
	    #push(@byknottype,$entry.' \midrule'."\n");
	    
	    #$line{'separator'} = "\\addlinespace[0.45em]\n";
	    
	    $prevline = pop(@byknottype);
	    $prevline->{'separator'} = "\\addlinespace[0.45em]\n";
	    push(@byknottype,$prevline);
	    
	  }
	  
	}
	
      }
      
    }
    
    $lastkey = $key;
    
    # Now output the regular line. 
    
    $line{'data'} = '$'.$knots{$key}{tex}.'$ & $'.sprintf("%2.2f",1000*$knots{$key}{residual}).' \times 10^{-3}$ \\\\'."\n";
    push @byknottype, \%line;
    
  }

  # When crossing number changes, insert an untrimmed midrule.
  
  $lastkey = undef;
  #    my $entry = pop(@byknottype);
  #    chomp($entry);
  #    push(@byknottype,$entry.' \midrule'."\n");
  
  if ($crnum != $endcr) {

    $prevline = pop(@byknottype);
    $prevline->{'separator'} = '\addlinespace[0.45em]\cmidrule(r{0.5em}l{0.5em}){1-2}\addlinespace[0.45em]'."\n";
    push(@byknottype,$prevline);
    
    #$byknottype[$#byknottype-1]{separator} = '\addlinespace[0.45em]\cmidrule(r{0.5em}l{0.5em}){1-3}\addlinespace[0.45em]'."\n";

  }
    
}


# structuredTable is a general Perl interface to creating tables in several columns in LaTeX document. 
# The basic idea is that we are passed an array of data and want to split it into columns so that we 
# can construct tables consisting of several columns, each continuing the same array of data. The data
# is split into groups by various separators. We assume that the separators contain \addlinespace tags.

# We assume that we are passed a hash containing:

# 'number of columns'
# 'columns per entry' (the number of sub-columns in each table);
# 'column heads' (an array of heads for the columns in the input data)
# 'target height' in points
# 'data' a reference to an array of hashes containing entries 'data' (in TeX form) and 'separator' (if 
#      this is a boundary between groups of data)
# 'topmatter' topmatter from the final tex document in which we're going to insert the table (to set fonts, etc)
# 'caption' caption for the table (in TeX, of course)
# 'label' the labels for the various pages of the table (if it's split across more than one will be '$label 0', '$label 1', etc..

%tabdata = ( 'number of columns' => 3, 'columns per entry' => 2, 'column heads' => 'Link & \hspace{-5pt}Residual',
	     'target height' => 292, 'data' => \@byknottype, 'caption' => "Residuals of Tight Knots and Links by Knot Type",
	     'label' => "ResidualTable", 'topmatter' => &topmatter("${cwd}/../rrpaper/compositepaperJPhysA.tex") );

if ($include_composites) {
  $tabdata{'column heads'} =~ s/Link/Knot/g;
  $tabdata{'caption'} =~ s/and Links//g;
}

open(KTAB,">${cwd}/tex/ResidualTable.tex") or die("Couldn't open ${cwd}/tex/ResidualTable.tex");
print KTAB &tex_table(\%tabdata);
close(KTAB);

unless ($include_composites) {

 copy("${cwd}/tex/ResidualTable.tex","${cwd}/../rrpaper/ResidualTable.tex") or die("Couldn't copy ResidualTable.tex\n");
 print "ResidualTable.tex -> ../rrpaper/ResidualTable.tex\n";

} else {

  copy("${cwd}/tex/ResidualTable.tex","${cwd}/../rrpaper/CompositeResidualTable.tex") or die("Couldn't copy ResidualTable.tex\n");
  print "ResidualTable.tex -> ../rrpaper/CompositeResidualTable.tex\n";

}

 
  #
  # Now we output the strutplots
  #
  
open(BESTIARY,">${cwd}/data/Bestiary.tex") or die("Couldn't open Bestiary.tex\n"); 
my $keynum = 0;

foreach $crnum (2..10) {
    
    print "Now outputting pagerefs for $crnum crossing knots and links...\n";
    
    print BESTIARY '\chapter{$'.$crnum.'$ crossing knots and links}'."\n";

    my $prcap = 'Page Numbers of $'.$crnum.'$ crossing knots and links';
    my @pagerefs = ();

    my @indexkeys;

    foreach my $key ( @{ $bycrossing[$crnum] } ) {

	($safekey,$texkey,$cr,$comp,$ind) = &make_safekey($key);		
	push(@pagerefs,'$'.$texkey.'$, p. \pageref{'.$safekey.'Page} \\\\'."\n");

    }
    
    my @prrows = qw(40 40 40 40 40);
    my $prcolhead = "Link";

    my $tfname = "Cr".$crnum."Table.tex";

    &perltable($tfname,\@prrows,\@pagerefs,$prcolhead,$prcap,5,"PageRefTab".$crnum);

    print BESTIARY '\input{'.$tfname.'}'."\n\n";

     # We have now output the chapter table. We go ahead and make pages now.

    print "    Writing knot ";
     
    foreach my $localkey ( @{ $bycrossing[$crnum] } ) {

	$keynum++;
	print " $localkey ";

	($safekey,$texkey,$cr,$comp,$ind) = &make_safekey($localkey);
	
	print BESTIARY '\newpage'."\n";
	print BESTIARY '\label{'.$safekey.'Page}'."\n";
	if ($comp == 1) { 

	    print BESTIARY '\index{'.$cr.'@'.$cr.' crossing links!'.$comp.'@with '.$comp.' component!'.$keynum.'@$'.$texkey.'$}'."\n";

	} else {

	    print BESTIARY '\index{'.$cr.'@'.$cr.' crossing links!'.$comp.'@with '.$comp.' components!'.$keynum.'@$'.$texkey.'$}'."\n";

	}

	# Now include the mugshots
        
	if (!defined $knots{$localkey}{mugshot}) { 

	    warn("Mugshot not defined for $localkey.\n"); 

	}

	my $textvar = '}.jpg';

	$knots{$localkey}{mugshot}[0] =~ s/.jpg/$textvar/;
	$knots{$localkey}{mugshot}[1] =~ s/.jpg/$textvar/;
	$knots{$localkey}{mugshot}[2] =~ s/.jpg/$textvar/;

	print BESTIARY '\begin{tabular}{ll} \includegraphics[width=1.9in]{./mugshots/{'.$knots{$localkey}{mugshot}[0].'} & \includegraphics[width=1.9in]{./mugshots/{'.$knots{$localkey}{mugshot}[1].'} \\\\'."\n".'\includegraphics[width=1.9in]{./mugshots/{'.$knots{$localkey}{mugshot}[2].'} & \end{tabular} '."\n\n".'\vspace{-2.8in}'."\n\n";
	
	print BESTIARY '\hspace{0.6in}\includegraphics[width=4.3in,viewport=150 145 430 560]{./strutplots/'.$knots{$localkey}{strutplot}.'} \vfill'."\n\n";

	# Now we print the table text.
	
	print BESTIARY '\begin{center} \begin{tabular}{lllllllll} \toprule'."\n";
	print BESTIARY 'Link & $\PRop$ & $\Rop$ & Filename & Verts & Struts & $\kappa$ range & Kink & Straight \\\\ \midrule'."\n";
	
	print BESTIARY '$'.$texkey.'$ & $'.$knots{$localkey}{ropelength}.'$ & $'.$knots{$localkey}{upperbound}.'$ & \verb!'.$knots{$localkey}{filename}.'! & $'.$knots{$localkey}{edges}.'$ & $'.$knots{$localkey}{struts}.'$ & $['.$knots{$localkey}{klo}.','.$knots{$localkey}{khi}.']$ & $'.$knots{$localkey}{kinks}.'$ & $'.$knots{$localkey}{straight}.'$ \\\\'."\n";
	
	print BESTIARY '\bottomrule'."\n".'\end{tabular} \end{center}'."\n\n";
	
    }
     
     print "\n\n";
     
 }


close BESTIARY;   
    
# Now we make the table of best and worst knots and links by crossing number


unless ($include_composites) {

    print "Now writing table of best and worst knots and links by crossing number to BestWorstTable.tex .\n";
    open (BW,">${cwd}/data/BestWorstTable.tex") || die ("Couldn't open BestWorstTable.tex\n");

} else {

  print "Now writing table of best and worst COMPOSITE knots by crossing number to CompositeBestWorstTable.tex .\n";
  open (BW,">${cwd}/data/CompositeBestWorstTable.tex") || die ("Couldn't open CompositeBestWorstTable.tex\n");  

}

print BW "\\begin{tabular}[t]{lcccc}\n\\toprule";
print BW "\$\\Cr\$ & \$\\Rop\$ & Links \\\\\n";
print BW "\\midrule\n";

unless($include_composites) {
  print BW "\$3\$   & \$32.74\$ & \$3_1\$ & & \\\\\n";
}

foreach $crnum ($startcr..$endcr) {

    my @byrop;
    @byrop = sort { $knots{$a}{upperbound} <=> $knots{$b}{upperbound} } @{ $bycrossing[$crnum] };

    print "Cr: $crnum Shortest: $byrop[0] Longest: $byrop[$#byrop] \n";

    my ($fkey,$ftexkey,$fcr,$fcomp,$find);
    my ($lkey,$ltexkey,$lcr,$lcomp,$lind);

    $ftexkey = $knots{$byrop[0]}{tex};
    $fcr = $knots{$byrop[0]}{cr};
    $fcomp = $knots{$byrop[0]}{comp};
    $find = $knots{$byrop[0]}{index};

    $ltexkey = $knots{$byrop[$#byrop]}{tex};
    $lcr = $knots{$byrop[$#byrop]}{cr};
    $lcomp = $knots{$byrop[$#byrop]}{comp};
    $lind = $knots{$byrop[$#byrop]}{index};

    print BW "\$".$crnum."\$ & \$ [".$knots{$byrop[0]}{upperbound}.",".$knots{$byrop[$#byrop]}{upperbound}."] \$ & \$ $ftexkey \$, \$ $ltexkey \$ \\\\ \n";

}

print BW "\\bottomrule\n";
print BW "\\end{tabular}\n";

close BW;



unless ($include_composites) {

    print "Constructing table of knots and links for knots only in BestWorstTableKnots.tex...\n";
    open (BW,">${cwd}/data/BestWorstTableKnots.tex") || die ("Couldn't open BestWorstTableKnots.tex\n");

} else {

    print "Constructing table of knots and links for composite knots only in BestWorstTableKnots.tex...\n";
    open (BW,">${cwd}/data/CompositeBestWorstTableKnots.tex") || die ("Couldn't open CompositeBestWorstTableKnots.tex\n");

}

print BW "\\begin{tabular}[t]{lcccc}\n\\toprule";
print BW "\$\\Cr\$ & \$\\Rop\$ & Knots \\\\\n";
print BW "\\midrule\n";

unless ($include_composites) {

  my $start;

  if ($startcr < 3) { 

    $start = 3;

  } else {

    $start = $startcr;

  }

  foreach $crnum ($start..$endcr) {
	
	my @byrop = ( );
	
	foreach ( @{ $bycrossing[$crnum] } ) { 
	    
	    if ($knots{$_}{comp} == 1) { push (@byrop,$_); }
	    
	}

	@byrop = sort { $knots{$a}{upperbound} <=> $knots{$b}{upperbound} } @byrop;
	
	print "Cr: $crnum Shortest: $byrop[0] Longest: $byrop[$#byrop] \n";
	
	my ($fkey,$ftexkey,$fcr,$fcomp,$find);
	my ($lkey,$ltexkey,$lcr,$lcomp,$lind);
	
	$ftexkey = $knots{$byrop[0]}{tex};
	$fcr = $knots{$byrop[0]}{cr};
	$fcomp = $knots{$byrop[0]}{comp};
	$find = $knots{$byrop[0]}{index};
	
	$ltexkey = $knots{$byrop[$#byrop]}{tex};
	$lcr = $knots{$byrop[$#byrop]}{cr};
	$lcomp = $knots{$byrop[$#byrop]}{comp};
	$lind = $knots{$byrop[$#byrop]}{index};
	
	print BW "\$".$crnum."\$ & \$ [".$knots{$byrop[0]}{upperbound}.",".$knots{$byrop[$#byrop]}{upperbound}."] \$ & \$ $ftexkey \$, \$ $ltexkey \$ \\\\ \n";
  
    } 

} else {

    foreach $crnum ($startcr..$endcr) {
	
	my @byrop = ( );
	
	foreach ( @{ $bycrossing[$crnum] } ) { 
	    
	    if ($knots{$_}{comp} == 1) { push (@byrop,$_); }
	    
	}

	@byrop = sort { $knots{$a}{upperbound} <=> $knots{$b}{upperbound} } @byrop;
	
	print "Cr: $crnum Shortest: $byrop[0] Longest: $byrop[$#byrop] \n";
	
	my ($fkey,$ftexkey,$fcr,$fcomp,$find);
	my ($lkey,$ltexkey,$lcr,$lcomp,$lind);
	
	$ftexkey = $knots{$byrop[0]}{tex};
	$fcr = $knots{$byrop[0]}{cr};
	$fcomp = $knots{$byrop[0]}{comp};
	$find = $knots{$byrop[0]}{index};
	
	$ltexkey = $knots{$byrop[$#byrop]}{tex};
	$lcr = $knots{$byrop[$#byrop]}{cr};
	$lcomp = $knots{$byrop[$#byrop]}{comp};
	$lind = $knots{$byrop[$#byrop]}{index};
	
	print BW "\$".$crnum."\$ & \$ [".$knots{$byrop[0]}{upperbound}.",".$knots{$byrop[$#byrop]}{upperbound}."] \$ & \$ $ftexkey \$, \$ $ltexkey \$ \\\\ \n";
  


    } 
	
}

print BW "\\bottomrule\n";
print BW "\\end{tabular}\n";

close BW;

unless ($include_composites) {
  
  print "Now copying files to ../rrpaper/\n";

  copy("${cwd}/data/BestWorstTable.tex","${cwd}/../rrpaper/BestWorstTable.tex") or die("Couldn't copy BestWorstTable.tex\n");
  print "BestWorstTable.tex -> ../rrpaper/BestWorstTable.tex\n";

  copy("${cwd}/data/BestWorstTableKnots.tex","${cwd}/../rrpaper/BestWorstTableKnots.tex") or die("Couldn't copy BestWorstTable.tex\n");
  print "BestWorstTableKnots.tex -> ../rrpaper/BestWorstTableKnots.tex\n";

  copy("${cwd}/data/KnotTags.tex","${cwd}/../rrpaper/KnotTags.tex") or die("Couldn't copy KnotTags.tex\n");
  print "KnotTags.tex -> ../rrpaper/KnotTags.tex\n";
  
  copy("${cwd}/tex/ResidualTable.tex","${cwd}/../rrpaper/ResidualTable.tex") or die("Couldn't copy ResidualTable.tex\n");
  print "ResidualTable.tex -> ../rrpaper/ResidualTable.tex\n";
  
  copy("${cwd}/data/KnotTableByRop.tex","${cwd}/../rrpaper/KnotTableByRop.tex") or die("Couldn't copy KnotTableByRop.tex\n");
  print "KnotTableByRop.tex -> ../rrpaper/KnotTableByRop.tex\n";
  
  copy("${cwd}/tex/KnotTable.tex","${cwd}/../rrpaper/KnotTable.tex") or die("Couldn't copy KnotTable.tex\n");
  print "KnotTable.tex -> ../rrpaper/KnotTable.tex\n";
  
  copy("${cwd}/data/KnotTags.tex","${cwd}/../rrpaper/KnotTags.tex") or die("Couldn't copy KnotTags.tex\n");
  print "KnotTags.tex -> ../rrpaper/KnotTags.tex\n";

} else {

  print "Now copying COMPOSITE files to ../rrpaper/\n";

  copy("${cwd}/data/CompositeBestWorstTableKnots.tex","${cwd}/../rrpaper/CompositeBestWorstTableKnots.tex") or die("Couldn't copy BestWorstTable.tex\n");
  print "CompositeBestWorstTableKnots.tex -> ../rrpaper/CompositeBestWorstTableKnots.tex\n";

  copy("${cwd}/data/CompositeBestWorstTable.tex","${cwd}/../rrpaper/CompositeBestWorstTable.tex") or die("Couldn't copy BestWorstTable.tex\n");
  print "CompositeBestWorstTable.tex -> ../rrpaper/CompositeBestWorstTable.tex\n";

  copy("${cwd}/data/KnotTags.tex","${cwd}/../rrpaper/CompositeKnotTags.tex") or die("Couldn't copy KnotTags.tex\n");
  print "KnotTags.tex -> ../rrpaper/CompositeKnotTags.tex\n";
  
  copy("${cwd}/tex/ResidualTable.tex","${cwd}/../rrpaper/CompositeResidualTable.tex") or die("Couldn't copy ResidualTable.tex\n");
  print "ResidualTable.tex -> ../rrpaper/CompositeResidualTable.tex\n";
  
  copy("${cwd}/data/KnotTableByRop.tex","${cwd}/../rrpaper/CompositeKnotTableByRop.tex") or die("Couldn't copy KnotTableByRop.tex\n");
  print "KnotTableByRop.tex -> ../rrpaper/CompositeKnotTableByRop.tex\n";
  
  copy("${cwd}/tex/KnotTable.tex","${cwd}/../rrpaper/CompositeKnotTable.tex") or die("Couldn't copy KnotTable.tex\n");
  print "KnotTable.tex -> ../rrpaper/CompositeKnotTable.tex\n";
  
  copy("${cwd}/data/KnotTags.tex","${cwd}/../rrpaper/CompositeKnotTags.tex") or die("Couldn't copy KnotTags.tex\n");
  print "KnotTags.tex -> ../rrpaper/CompositeKnotTags.tex\n";
  
}

unlink("${cwd}/iopart.cls")  or die("Couldn't remove iopart.cls");
unlink("${cwd}/iopart12.clo") or die("Couldn't remove iopart12.clo");


format CLR_TOP = 

     Ropelength of Composite Links (summary table)

     Link Type           Ropelength       Resolution      Residual
     ------------------- ---------------  --------------  --------------
.

format CLR =
     @<<<<<<<<<<<<<<<<<< @####.#########  @####.########  @####.########
     $k,                 $ub,             $rso,           $res
.
