#!/usr/bin/perl

$filename = $ARGV[0];

die unless (-e $filename);

$dispcount = $ARGV[1];

$dispcount = 999 if ($dispcount == 0);

open FHAND, "<$filename";

$temp = <FHAND>;
chomp $temp;
($dim, $ndim, $format, $edgetype) = split(' ', $temp);
$max = $dim - 1;
$nmax = $ndim - 1;

%rsize = ();

if ($format == 3) {
  for $row (0..$max) {
    $line = <FHAND>;
    chomp $line;
    @stuff = split(';',$line);
    for $col (0..$max) {
      if ($stuff[$col] =~ /\d/) {
        $bmap[$row][$col] = $stuff[$col];
      } else {
        foreach (split(',',$stuff[$col])) {
          $rmap[$row][$col]{$_} = 1;
          $rsize{$_}++;
        }
        $bmap[$row][$col] = "$row,$col";
      }
    }
  }
} else {
  for $row (0..$max) {
    $line = <FHAND>;
    for $col (0..$max) {
      $r = substr($line, $col, 1);
      $rmap[$row][$col]{$r} = 1;
      $rsize{$r}++;
    }
  }
}

########## general prohibitions

@placeok = ();
for $num (1..$ndim) {
  for $row (0..$max) {
    for $col (0..$max) {
      $placeok[$row][$col][$num] = 1;
    }
  }
}

my %pairest = ();   # pair-wise restrictions

sub pair_prohib {
  my ($r1, $c1, $r2, $c2, $n1, $n2) = @_;
  $pairest{"R".$r1."C".$c1."N".$n1} .= " R".$r2."C".$c2."N".$n2;
  $pairest{"R".$r2."C".$c2."N".$n2} .= " R".$r1."C".$c1."N".$n1;
}

############

@consecs = ();
%consecs = {};
@nonconsecs = ();
%nonconsecs = {};
%doubles = ();

@edges = ();
%edges = ();

if ($format == 1 or $format == 3) {
  for $row (0..$max) {
    $line = <FHAND>;
    for $col (0..$max) {
      $cell[$row][$col] = substr($line, $col, 1);
      $cell[$row][$col] = ' ' if ($cell[$row][$col] =~ /^\s*$/);
      if ($cell[$row][$col] eq 's') {
        $cell[$row][$col] = '.';
        $sum_map[$row][$col] = 's';
      } elsif ($cell[$row][$col] eq 'r') {
        $cell[$row][$col] = '.';
        $sum_map[$row][$col] = 'r';
      } elsif ($cell[$row][$col] eq '0') {
        $cell[$row][$col] = 10;
      } else {
        $sum_map[$row][$col] = '.';
      }
    }
  }
} elsif ($format == 2) {
  for $row (0..$max) {
    $line = <FHAND>;
    for $col (0..$max) {
      $cell[$row][$col] = substr($line, ($col * 2), 1);
    }
    for $col (0..($max-1)) {
      $test = substr($line, ($col*2+1), 1);
      if ($test eq '-') {
        push @nonconsecs, "$row,$col,h";
        $nonconsecs{"$row,$col,h"} = 1;
      } elsif ($test eq '|') {
        push @consecs, "$row,$col,h";
        $consecs{"$row,$col,h"} = 1;
      }
    }
    $line = <FHAND>;
    for $col (0..$max) {
      $test = substr($line, ($col * 2), 1);
      if ($test eq '|') {
        push @nonconsecs, "$row,$col,v";
        $nonconsecs{"$row,$col,v"} = 1;
      } elsif ($test eq '-') {
        push @consecs, "$row,$col,v";
        $consecs{"$row,$col,v"} = 1;
      }
      $test = substr($line, ($col * 2 + 1), 1);
      if ($test =~ /[1-9\.\?]/) {
        $doubles{"$row,$col"} = $test;
      }
    }
  }
} elsif ($format == 4) {
  for $row (0..$max) {
    $line = <FHAND>;
    for $col (0..$max) {
      $cell[$row][$col] = substr($line, ($col * 2), 1);
    }
    for $col (0..($max-1)) {
      $test = substr($line, ($col*2+1), 1);
      if ($test =~ /\d/) {
        push @edges, "$row,$col,h,$test";
        $edges{"$row,$col,h,$test"} = 1;
      }
    }
    $line = <FHAND>;
    for $col (0..$max) {
      $test = substr($line, ($col * 2), 1);
      if ($test =~ /\d/) {
        push @edges, "$row,$col,v,$test";
        $edges{"$row,$col,v,$test"} = 1;
      }
    }
  }
}

########  parsing extra lines

%framedata = ();
@thermomap = ();
$thermorow = 0;
$targetsum = 0;
@trinarymap = ();
$trinaryrow = 0;
$has_trinary = 0;

while ($line = <FHAND>) {
  chomp $line;
  if ($line =~ /^F/) {
    $line =~ /^F(.)(\d+) (\d+)/;
    ($dir, $idx, $nums) = ($1, $2, $3);
    my @temp = split '', $nums;
    if ($dir eq "u") {
      $framedata{"col"}{"small"}{$idx} = \@temp;
    } elsif ($dir eq "d") {
      $framedata{"col"}{"big"}{$idx} = \@temp;
    } elsif ($dir eq "l") {
      $framedata{"row"}{"small"}{$idx} = \@temp;
    } elsif ($dir eq "r") {
      $framedata{"row"}{"big"}{$idx} = \@temp;
    }
  } elsif ($line =~ /^TR/) {
    $line =~ /^TR(.*)/;
    $has_trinary = 1;
    my $temp = $1;
    for $col (0..$max) {
      $trinarymap[$trinaryrow][$col] = substr($temp, $col*2, 2);
    }
    $trinaryrow++;
  } elsif ($line =~ /^T/) {
    $line =~ /^T(.*)/;
    my $temp = $1;
    for $col (0..$max) {
      $thermomap[$thermorow][$col] = substr($temp, $col, 1);
    }
    $thermorow++;
  } elsif ($line =~ /^SUM/) {
    $line =~ /^SUM (\d+)/;
    $targetsum = $1;
  } elsif ($line =~ /^BAD/) {
    $line =~ /^BAD (\d+) (\d+) (\d+)/;
    die if ($1 > $max or $1 < 0);
    die if ($2 > $max or $2 < 0);
    die if ($3 > $ndim or $3 < 1);
    $placeok[$1][$2][$3] = 1;
  } elsif ($line =~ /^GOOD/) {
    $line =~ /^GOOD (\d+) (\d+) ([\d ]+)/;
    die if ($1 > $max or $1 < 0);
    die if ($2 > $max or $2 < 0);
    my $rr = $1;
    my $cc = $2;
    my @nums = split(" ",$3);
    my $nn;
    my %thash = ();
    for $nn (@nums) {
      die if ($nn > $ndim or $nn < 1);
      $thash{$nn} = 1;
    }
    for $nn (1..$ndim) {
      $placeok[$rr][$cc][$nn] = 0 unless ($thash{$nn});
    }
  }
}
close FHAND;

##########framestuff

for $col (0..$max) {
  if (exists $framedata{"col"}{"small"}{$col}) {
    my @temp = @{$framedata{"col"}{"small"}{$col}};
    my $region = region(0,$col);  
    foreach $num (@temp) {
      for $row (0..$max) {
        if (region($row,$col) ne $region) {
          $placeok[$row][$col][$num] = 0;
        } 
      }
    }
  }
  if (exists $framedata{"col"}{"big"}{$col}) {
    my @temp = @{$framedata{"col"}{"big"}{$col}};
    my $region = region($max,$col);  
    foreach $num (@temp) {
      for $row (0..$max) {
        if (region($row,$col) ne $region) {
          $placeok[$row][$col][$num] = 0;
        } 
      }
    }
  }
}

for $row (0..$max) {
  if (exists $framedata{"row"}{"small"}{$row}) {
    my @temp = @{$framedata{"row"}{"small"}{$row}};
    my $region = region($row,0);  
    foreach $num (@temp) {
      for $col (0..$max) {
        if (region($row,$col) ne $region) {
          $placeok[$row][$col][$num] = 0;
        } 
      }
    }
  }
  if (exists $framedata{"row"}{"big"}{$row}) {
    my @temp = @{$framedata{"row"}{"big"}{$row}};
    my $region = region($row,$max);  
    foreach $num (@temp) {
      for $col (0..$max) {
        if (region($row,$col) ne $region) {
          $placeok[$row][$col][$num] = 0;
        } 
      }
    }
  }
}


########## end frame stuff
########## trinary stuff

for $row (0..$max) {
  for $col (0..$max) {
    my ($first, $second) = split('', $trinarymap[$row][$col]);
    if ($first =~ /\d/) {
      for $num (1..$ndim) {
        next if ( int($num / 3) % 3 == $first );
        $placeok[$row][$col][$num] = 0;
      }
    }
    if ($second =~ /\d/) {
      for $num (1..$ndim) {
        next if ( $num % 3 == $second );
        $placeok[$row][$col][$num] = 0;
      }
    }
  }
}

########## end trinary stuff
########## thermo stuff

@thermomin = ();
@thermomax = ();

for $col (0..$max) {
  for $row (0..$max) {
    $thermomin[$row][$col] = $thermomap[$row][$col];
    $thermomax[$row][$col] = $thermomap[$row][$col];
  }
}

$level = 0;

sub thmin_update {
  die if ($level > $max * 10);
  my ($row, $col) = @_;
  if ($thermomin[$row][$col] eq ".") {
    $thermomin[$row][$col] = 1;
  } elsif ($thermomin[$row][$col] eq "o") {
    $thermomin[$row][$col] = 1;
  } elsif ($thermomin[$row][$col] =~ /([udlr])/) {
    my $let = $1;
    my ($trow, $tcol) = ($row, $col);
    $trow-- if ($let eq "u");
    $trow++ if ($let eq "d");
    $tcol-- if ($let eq "l");
    $tcol++ if ($let eq "r");
    $level++;
    thmin_update($trow, $tcol);
    $level--;
    $thermomin[$row][$col] = $thermomin[$trow][$tcol] + 1;
  }
}

sub thmax_update {
  my ($row, $col) = @_;
  if ($thermomax[$row][$col] eq ".") {
    $thermomax[$row][$col] = $dim;
  } elsif ($thermomax[$row][$col] =~ /[udlro]/) {
    my $sofar = $dim + 1;

    my ($trow, $tcol) = ($row, $col);

    ($trow, $tcol) = ($row, $col);
    $trow--;  my $let = "d"; if ($row > 0 and $thermomap[$trow][$tcol] eq $let) {
      thmax_update($trow, $tcol) if ($thermomax[$trow][$tcol] eq $let);
      $sofar = $thermomax[$trow][$tcol] if ($thermomax[$trow][$tcol] < $sofar);
    }
    ($trow, $tcol) = ($row, $col);
    $trow++;  my $let = "u"; if ($row < $max and $thermomap[$trow][$tcol] eq $let) {
      thmax_update($trow, $tcol) if ($thermomax[$trow][$tcol] eq $let);
      $sofar = $thermomax[$trow][$tcol] if ($thermomax[$trow][$tcol] < $sofar);
    }
    ($trow, $tcol) = ($row, $col);
    $tcol--;  my $let = "r"; if ($row > 0 and $thermomap[$trow][$tcol] eq $let) {
      thmax_update($trow, $tcol) if ($thermomax[$trow][$tcol] eq $let);
      $sofar = $thermomax[$trow][$tcol] if ($thermomax[$trow][$tcol] < $sofar);
    }
    ($trow, $tcol) = ($row, $col);
    $tcol++;  my $let = "l"; if ($row < $max and $thermomap[$trow][$tcol] eq $let) {
      thmax_update($trow, $tcol) if ($thermomax[$trow][$tcol] eq $let);
      $sofar = $thermomax[$trow][$tcol] if ($thermomax[$trow][$tcol] < $sofar);
    }

    $thermomax[$row][$col] = $sofar - 1;
  }
}

for $col (0..$max) {
  for $row (0..$max) {
    thmin_update($row, $col);
    thmax_update($row, $col);
  }
}

for $row (0..$max) {
  for $col (0..$max) {
    next if (!defined($thermomin[$row][$col]));
    for $num (1..$dim) {
      $placeok[$row][$col][$num] = 0 if ($num < $thermomin[$row][$col]);
      $placeok[$row][$col][$num] = 0 if ($num > $thermomax[$row][$col]);
      for $num2 (($num+1)..$dim) {
        if ($thermomap[$row][$col] eq "u") {
          pair_prohib($row, $col, $row-1, $col, $num, $num2);
        } elsif ($thermomap[$row][$col] eq "d") {
          pair_prohib($row, $col, $row+1, $col, $num, $num2);
        } elsif ($thermomap[$row][$col] eq "l") {
          pair_prohib($row, $col, $row, $col-1, $num, $num2);
        } elsif ($thermomap[$row][$col] eq "r") {
          pair_prohib($row, $col, $row, $col+1, $num, $num2);
        }
      }
    }
  }
}

########## end thermo stuff


sub cleangrid {
  my @grid = ();
  for $r (0..$max) {
    for $c (0..$max) {
      my @temp = ();
      push @grid, \@temp;
    }
  }
  return @grid;
}

#########################

sub region {
  @regs = regions(@_);
  if (scalar(@regs) == 1) {
    return $regs[0];
  } else {
    foreach (@regs) {
      return $_ if (/^a/);
    }
  }
  return 'xx';
}

sub artregion {
  my ($row, $col) = @_;
  my $bm = $bmap[$row][$col];
  if ($bm =~ /\d/) {
    ($row, $col) = split(',',$bm);
  }
  $col = 0 if ($col < 0);
  $row = 0 if ($row < 0);
  return region($row, $col);
}

sub regions {
  my ($row, $col) = @_;
  return keys %{$rmap[$row][$col]};
}

sub try_solve {

  @sol = ();
  $solcount = -1;

  @ts_grid = ();
 
  my @lines = ();   # to be fed to dance3.

  my %columns = ();   # columns for dancing.
  # my %npcolumns = ();   # non-primary columns for dancing.

  foreach $item (0..$#edges) {
    @data = split(',',$edges[$item]);
    my $digit = pop(@data);
    if (pop(@data) eq 'h') {
      push @data, $data[0], $data[1]+1;
    } else { 
      push @data, $data[0]+1, $data[1];
    }
    for $num1 (1..$ndim) {
      for $num2 (1..$ndim) {
        if ($edgetype == 0) {
          # the digit is a product constraint
          next if ($num1 * $num2 % 10 == $digit);
          pair_prohib(@data, $num1, $num2);
        } elsif ($edgetype == 1) {
          # the digit is an intdiv constraint
          next if (int($num1 / $num2) == $digit);
          next if (int($num2 / $num1) == $digit);
          pair_prohib(@data, $num1, $num2);
        }
      }
    }
  }
  foreach $item (0..$#consecs) {
    @data = split(',',$consecs[$item]);
    if (pop(@data) eq 'h') {
      push @data, $data[0], $data[1]+1;
    } else { 
      push @data, $data[0]+1, $data[1];
    }
    for $num1 (1..$ndim) {
      for $num2 (1..$ndim) {
        next if ($num1 - $num2 == 1 or $num1 - $num2 == -1);
        pair_prohib(@data, $num1, $num2);
      }
    }
  }
  foreach $item (0..$#nonconsecs) {
    @data = split(',',$nonconsecs[$item]);
    if (pop(@data) eq 'h') {
      push @data, $data[0], $data[1]+1;
    } else { 
      push @data, $data[0]+1, $data[1];
    }
    for $num1 (1..$ndim) {
      for $num2 (1..$ndim) {
        next if ($num1 - $num2 != 1 and $num1 - $num2 != -1);
        pair_prohib(@data, $num1, $num2);
      }
    }
  }

  $givencounter = 0;

  for $row (0..$max) {
    for $col (0..$max) {

      @regs = regions($row, $col);

      if (scalar(@regs) == 0) {
        $ts_grid[$row][$col] = 'x';
        next;
      }
  
      $arg = region($row, $col);
      $ts_grid[$row][$col] = '.';
  
      next if ($cell[$row][$col] eq ' ');

      # each cell is occupied one or two times.
      if (!exists $doubles{"$row,$col"}) {
        $columns{"OR".$row."C".$col} = 1;
      } elsif ($doubles{"$row,$col"} eq '?') {
        $columns{"OR".$row."C".$col." 2 1"} = 1;
      } elsif ($doubles{"$row,$col"} =~ /[\.1-9]/) {
        $columns{"OR".$row."C".$col." 2"} = 1;
      } else {
        $columns{"OR".$row."C".$col} = 1;
      }

      for $num (1..$ndim) {
        # each num is in one.
        $npcolumns{"R".$row."C".$col."N".$num." 99"} = 1;

        if ($format != 3 and scalar(@regs) == 1) {
          # each num appears in each row once, and in each col once.
          $columns{"R".$row."N".$num} = 1;
          $columns{"C".$col."N".$num} = 1;
          if ($rsize{$arg} == $dim) {
            # each num appears in this region exactly once.
            $columns{"A".$arg."N".$num} = 1;
          } elsif ($rsize{$arg} < $dim) {
            # each num appears in this region no more than once.
            $npcolumns{"A".$arg."N".$num} = 1;
          } elsif ($rsize{$arg} % $dim == 0) {
            $mult = $rsize{$arg} / $dim;
            # each num appears in this region exactly $mult times.
            $columns{"A".$arg."N".$num." ".$mult} = 1;
          } else {
            $mult = int($rsize{$arg} / $dim);
            $mmax = $mult+1;
            # each num appears in this region at least $mult times and no more than $mmax times.
            $columns{"A".$arg."N".$num." ".$mmax." ".$mult} = 1;
          }
        } else {
          # handle "row" and "column" regions of display type 3 differently.  They get called "AX".
          foreach (@regs) {
            my $arg = $_;
            if ($rsize{$arg} == $dim) {
              $columns{"AX".$arg."N".$num} = 1;
            } elsif ($rsize{$arg} < $dim) {
              # each num appears in this region no more than once.
              $npcolumns{"AX".$arg."N".$num} = 1;
            } elsif ($rsize{$arg} % $dim == 0) {
              $mult = $rsize{$arg} / $dim;
              # each num appears in this region exactly $mult times.
              $columns{"AX".$arg."N".$num." ".$mult} = 1;
            } else {
              $mult = int($rsize{$arg} / $dim);
              $mmax = $mult+1;
              # each num appears in this region at least $mult times and no more than $mmax times.
              $columns{"AX".$arg."N".$num." ".$mmax." ".$mult} = 1;
            }
          }
        }


        # each row, col, area has targetsum
        $columns{"TSR$row $targetsum"} = 1;
        $columns{"TSC$col $targetsum"} = 1;
        $columns{"TSA$arg $targetsum"} = 1;

        # skip this placement if the placement constraint is not ok.
        next if ($placeok[$row][$col][$num] == 0);

        my @tokens;
  
        if ($cell[$row][$col] ne '.' && $cell[$row][$col] == $num) {
          # this is the first given
          push @tokens, "G".$givencounter;
          $columns{"G".$givencounter} = 1;
          $givencounter++;
          if ($doubles{"$row,$col"} ne '?') {
            # this cell cannot have anything smaller
            for $temp (1..($num-1)) {
              push @tokens, "R".$row."C".$col."N".$temp." 1";
            }
          }
        }
        if ($doubles{"$row,$col"} eq $num) {
          # this is the second given
          push @tokens, "G".$givencounter;
          $columns{"G".$givencounter} = 1;
          $givencounter++;
          # this cell cannot have anything bigger
          for $temp (($num+1)..($ndim)) {
            push @tokens, "R".$row."C".$col."N".$temp." 1";
          }
        }

        push @tokens, "OR".$row."C".$col;
        push @tokens, "R".$row."C".$col."N".$num." 99";

        if ($format != 3 and scalar(@regs) == 1) {
          push @tokens, "R".$row."N".$num;
          push @tokens, "C".$col."N".$num;
          push @tokens, "A".$arg."N".$num;
        } else {
          # handle "row" and "column" regions differently.
          foreach (@regs) {
            push @tokens, "AX".$_."N".$num;
          }
        }

        push @tokens, $pairest{"R".$row."C".$col."N".$num};

        if ($sum_map[$row][$col] eq "r") {
          # try both with sum and without.
          push @lines, join (" ", @tokens);
          push @tokens, "TSR$row $num";
          push @tokens, "TSC$col $num";
          push @tokens, "TSA$arg $num";
        } elsif ($sum_map[$row][$col] eq "s") {
          # each token contributes self to targetsum.
          push @tokens, "TSR$row $num";
          push @tokens, "TSC$col $num";
          push @tokens, "TSA$arg $num";
        }

        push @lines, join (" ", @tokens);
      }
    }
  }

  unshift @lines, (join " ", (sort keys %columns), "|", (sort keys %npcolumns));

  open FHAND, ">tempfile.sudovar";
  print FHAND (join "\n", @lines);
  print FHAND "\n";
  close FHAND;

#  print "tempfile written\n";

  open FHAND, "~/dance3/dance3 -$dispcount <tempfile.sudovar |";


  while ($line = <FHAND>) {
#    print "A ", $line;
    chomp $line;
    if ($line =~ /^\s*$/) {
      $solcount++;
      my @gg = cleangrid();
      $sol[$solcount] = \@gg;
    } elsif ($line =~ /R(\d+)C(\d+)N(\d+) 99/) {
      my $r = $1;
      my $c = $2;
      my $n = $3;
      $sol[$solcount][$r * $dim + $c][$n]++;
      if ($line =~ /TS/) {
        $ts_grid[$r][$c] = 's';
      }
    }
  }

  close FHAND;
  
}

sub print_image {
  my @data = @{$_[0]};
  for $r (0..$max) {
    for $c (0..$max) {
      print $data[$r * $dim + $c];
    }
    print "\n";
  }
  print "\n";
}

sub genprintgrid {
  my @result = ();
  # blanks
  for $r (0..($dim*4)) {
    for $c (0..($dim*6)) {
      $result[$r][$c] = ' ';
    }
  }
  # rows
  for $r (0..($dim)) {
    for $c (0..($dim*6)) {
      $result[$r*4][$c] = '-';
    }
  }
  # cols
  for $r (0..($dim*4)) {
    for $c (0..($dim)) {
      $result[$r][$c*6] = '|';
    }
  }
  # ints
  for $r (0..($dim)) {
    for $c (0..($dim)) {
      $result[$r*4][$c*6] = '+';
    }
  }
  # row region boundaries
  for $r (0..($max)) {
    for $c (0..($max-1)) {
      if (artregion($r,$c) ne artregion($r,$c+1)) {
        $result[$r*4+1][$c*6+6] = '"';
        $result[$r*4+2][$c*6+6] = '"';
        $result[$r*4+3][$c*6+6] = '"';
      }
    }
  }
  # col region boundaries
  for $r (0..($max-1)) {
    for $c (0..($max)) {
      if (artregion($r,$c) ne artregion($r+1,$c)) {
        $result[$r*4+4][$c*6+1] = '=';
        $result[$r*4+4][$c*6+2] = '=';
        $result[$r*4+4][$c*6+3] = '=';
        $result[$r*4+4][$c*6+4] = '=';
        $result[$r*4+4][$c*6+5] = '=';
      }
    }
  }
  # erase some borders for tile sudoku.
  for $r (0..($max)) {
    for $c (0..($max)) {
      $bm = $bmap[$r][$c];
      next if ($bm !~ /\d/);
      ($myr, $myc) = split(',',$bm);

      if ($bmap[$r][$c] ne "$r,$c") {
        $result[$r*4+2][$c*6+2] = '-';
      }
      if ($c != 0 and $bmap[$r][$c] eq $bmap[$r][$c-1]) {
        $result[$r*4+1][$c*6] = ' ';
        $result[$r*4+2][$c*6] = ' ';
        $result[$r*4+3][$c*6] = ' ';
      }
      if ($c != $max and $bmap[$r][$c] eq $bmap[$r][$c+1]) {
        $result[$r*4+1][$c*6+6] = ' ';
        $result[$r*4+2][$c*6+6] = ' ';
        $result[$r*4+3][$c*6+6] = ' ';
      }
      if ($r != 0 and $bmap[$r][$c] eq $bmap[$r-1][$c]) {
        $result[$r*4][$c*6+1] = ' ';
        $result[$r*4][$c*6+2] = ' ';
        $result[$r*4][$c*6+3] = ' ';
        $result[$r*4][$c*6+4] = ' ';
        $result[$r*4][$c*6+5] = ' ';
      }
      if ($r != $max and $bmap[$r][$c] eq $bmap[$r+1][$c]) {
        $result[$r*4+4][$c*6+1] = ' ';
        $result[$r*4+4][$c*6+2] = ' ';
        $result[$r*4+4][$c*6+3] = ' ';
        $result[$r*4+4][$c*6+4] = ' ';
        $result[$r*4+4][$c*6+5] = ' ';
      }
    }
  }
  return @result;
}

sub sp {
  return ' ' if ($_[0] eq '.');
  return 'X' if ($_[0] eq ' ');
  return $_[0];
}

sub raw_puzzle {
  my @answer = ();
  for $r (0..$max) {
    for $c (0..$max) {
      my @temp;
      push @temp, sp($cell[$r][$c]);
      if (exists $doubles{"$r,$c"}) {
        push @temp, sp($doubles{"$r,$c"});
      }
      if ($has_trinary) {
        $trinarymap[$r][$c] =~ s/\./ /g;
        @temp = join("\t", split('', $trinarymap[$r][$c]), "");
      }
      $answer[$r][$c] = \@temp;
    }
  }
  my @toprint = raw_printable(@answer);
  open PRPR, ">$filename.puzzle.raw";
  print_raw(@toprint);
  if (@edges) {
    print_edges_raw();
  }
  close PRPR;
  print "$filename.puzzle.raw created.\n";
}

sub printable_puzzle {
  my @answer = ();
  for $r (0..$max) {
    for $c (0..$max) {
      my @temp;
      push @temp, sp($cell[$r][$c]);
      if (exists $doubles{"$r,$c"}) {
        push @temp, sp($doubles{"$r,$c"});
      }
      $answer[$r][$c] = \@temp;
    }
  }
  my @toprint = printable(@answer);
  open PRPR, ">$filename.puzzle";
  print_printable(@toprint);
  close PRPR;
  print "$filename.puzzle created.\n";
}

sub raw_solution {
  my @answer = ();
  for $r (0..$max) {
    for $c (0..$max) {
      my @temp;
      $val = $r * $dim + $c;
      for $n (1..$ndim) {
        if ($_[$val][$n] == 1) {
          if ($has_trinary) {
            push @temp, (int($n/3)%3) . ($n%3);
          } else {
            push @temp, $n;
          }
        }
      }
      @temp = sort @temp;
      $answer[$r][$c] = \@temp;
    }
  }
  my @toprint = raw_printable(@answer);
  open PRPR, ">$filename.solution.raw";
  print_raw(@toprint);
  close PRPR;
  print "$filename.solution.raw created.\n";
}

sub printable_solution {
  my @answer = ();
  for $r (0..$max) {
    for $c (0..$max) {
      my @temp;
      $val = $r * $dim + $c;
      for $n (1..$ndim) {
        if ($_[$val][$n] == 1) {
          push @temp, $n;
        }
      }
      @temp = sort @temp;
      $answer[$r][$c] = \@temp;
    }
  }
  my @toprint = printable(@answer);
  open PRPR, ">$filename.solution";
  print_printable(@toprint);
  close PRPR;
  print "$filename.solution created.\n";
}
  
sub raw_printable {
  my @toprint = ();

  for $r (0..$max) {
    for $c (0..$max) {
      my @stuff = @{$_[$r][$c]};
      if (scalar(@stuff) == 0) {
        # nothing
      } elsif (scalar(@stuff) == 1) {
        $toprint[$r][$c] = "\t".$stuff[0];
      } else {
        $toprint[$r][$c] = "\t".$stuff[0]."/".$stuff[1];
      }
    }
  }
  return @toprint;
}

sub printable {
  my @toprint = genprintgrid();

  for $r (0..$max) {
    for $c (0..$max) {
      my @stuff = @{$_[$r][$c]};
      if (scalar(@stuff) == 0) {
        # nothing
      } elsif (scalar(@stuff) == 1) {
        $toprint[$r*4+2][$c*6+3] = $stuff[0];
        if ($has_trinary) {
          $toprint[$r*4+2][$c*6+3] = ' ';
          if ($stuff[0] eq ' ') {
            my ($one, $two) = split('',$trinarymap[$r][$c]);
            $toprint[$r*4+2][$c*6+2] = $one;
            $toprint[$r*4+2][$c*6+4] = $two;
          } else {
            $toprint[$r*4+2][$c*6+2] = int($stuff[0] / 3) % 3;
            $toprint[$r*4+2][$c*6+4] = int($stuff[0] % 3);
          }
        }
      } else {
        $toprint[$r*4+1][$c*6+2] = $stuff[0];
        $toprint[$r*4+3][$c*6+4] = $stuff[1];
        $toprint[$r*4+2][$c*6+3] = '/';
        $toprint[$r*4+1][$c*6+4] = '/';
        $toprint[$r*4+3][$c*6+2] = '/';
      }
      if ($sum_map[$r][$c] eq 's') {
        $toprint[$r*4+2][$c*6+2] = 's';
      }
      if ($thermomap[$r][$c] eq 'u') {
        $toprint[$r*4-1][$c*6+3] = '^';
        $toprint[$r*4+0][$c*6+3] = '^';
        $toprint[$r*4+1][$c*6+3] = '^';
      } elsif ($thermomap[$r][$c] eq 'd') {
        $toprint[$r*4+3][$c*6+3] = 'v';
        $toprint[$r*4+4][$c*6+3] = 'v';
        $toprint[$r*4+5][$c*6+3] = 'v';
      } elsif ($thermomap[$r][$c] eq 'r') {
        $toprint[$r*4+2][$c*6+5] = '>';
        $toprint[$r*4+2][$c*6+6] = '>';
        $toprint[$r*4+2][$c*6+7] = '>';
      } elsif ($thermomap[$r][$c] eq 'l') {
        $toprint[$r*4+2][$c*6-1] = '<';
        $toprint[$r*4+2][$c*6+0] = '<';
        $toprint[$r*4+2][$c*6+1] = '<';
      } elsif ($thermomap[$r][$c] =~ /\d+/) {
        $toprint[$r*4+2][$c*6+3] = $thermomap[$r][$c];
      }
      if ($format == 2) {
        if ($c != $max and $consecs{"$r,$c,h"} != 1) {
          $toprint[$r*4+1][$c*6+6] = ' ';
          $toprint[$r*4+2][$c*6+6] = ' ';
          $toprint[$r*4+3][$c*6+6] = ' ';
        }
        if ($c != $max and $nonconsecs{"$r,$c,h"} == 1) {
          $toprint[$r*4+2][$c*6+6] = 'X';
        }
        if ($r != $max and $consecs{"$r,$c,v"} != 1) {
          $toprint[$r*4+4][$c*6+1] = ' ';
          $toprint[$r*4+4][$c*6+2] = ' ';
          $toprint[$r*4+4][$c*6+3] = ' ';
          $toprint[$r*4+4][$c*6+4] = ' ';
          $toprint[$r*4+4][$c*6+5] = ' ';
        }
        if ($r != $max and $nonconsecs{"$r,$c,v"} == 1) {
          $toprint[$r*4+4][$c*6+3] = 'X';
        }
        
      }
      if ($format == 4) {
        foreach $digit (0..9) {
          if ($c != $max and $edges{"$r,$c,h,$digit"} == 1) {
            $toprint[$r*4+2][$c*6+6] = $digit;
          }
          if ($r != $max and $edges{"$r,$c,v,$digit"} == 1) {
            $toprint[$r*4+4][$c*6+3] = $digit;
          }
        }
      }
    }
  }
  return @toprint;
}

sub print_printable {
  for $r (0..($dim*4)) {
    for $c (0..($dim*6)) {
      print $_[$r][$c];
      print PRPR $_[$r][$c];
    }
    print PRPR "\n";
    print "\n";
  }
}

sub print_edges_raw {
  for $r (0..($dim-1)) {
    for $c (0..($dim-1)) {
      print PRPR "\t";
      for $n (0..9) {
        if ($edges{"$r,$c,h,$n"}) {
          print PRPR $n;
        }
      }
    }
    print PRPR "\n";
  }
  print PRPR "\n";
  for $r (0..($dim-1)) {
    for $c (0..($dim-1)) {
      print PRPR "\t";
      for $n (0..9) {
        if ($edges{"$r,$c,v,$n"}) {
          print PRPR $n;
        }
      }
    }
    print PRPR "\n";
  }
  print PRPR "\n";
}

sub print_raw {
  for $r (0..($dim-1)) {
    for $c (0..($dim-1)) {
      print $_[$r][$c];
      print PRPR $_[$r][$c];
    }
    print PRPR "\n";
    print "\n";
  }
}

sub print_images {
  @answer = ();
  for $r (0..$max) {
    for $c (0..$max) {
      $val = $r * $dim + $c;
      for $snum (0..(-1 + scalar @_)) {
        for $n (1..$ndim) {
          $answer[$val]{$n} += $_[$snum][$val][$n];
        }
      }
    }
  }
  @lengths = ();
  $maxlength = 0;
  for $r (0..$max) {
    for $c (0..$max) {
      $val = $r * $dim + $c;
      $lengths[$val] = 0;
      for $n (1..$ndim) {
        $length[$val]++ if (exists $answer[$val]{$n});
        $maxlength = $length[$val] if ($length[$val] > $maxlength); 
      }
    }
  }
  for $r (0..$max) {
    for $c (0..$max) {
      $val = $r * $dim + $c;
      for $n (1..$ndim) {
        if ($answer[$val]{$n} != 0) {
          print $n % 10;
        } else {
          print $ts_grid[$r][$c];
        }
      }
      print " ";
    }
    print "\n";
    if ($dispcount > 999) {
      for $c (0..$max) {
        $val = $r * $dim + $c;
        for $n (1..$ndim) {
          if ($answer[$val]{$n} != 0) {
            print int($answer[$val]{$n} / 1000) % 10;
          } else {
            print " ";
          }
        }
        print " ";
      }
      print "\n";
    }
    for $c (0..$max) {
      $val = $r * $dim + $c;
      for $n (1..$ndim) {
        if ($answer[$val]{$n} != 0) {
          print int($answer[$val]{$n} / 100) % 10;
        } else {
          print " ";
        }
      }
      print " ";
    }
    print "\n";
    for $c (0..$max) {
      $val = $r * $dim + $c;
      for $n (1..$ndim) {
        if ($answer[$val]{$n} != 0) {
          print int($answer[$val]{$n} / 10) % 10;
        } else {
          print " ";
        }
      }
      print " ";
    }
    print "\n";
    for $c (0..$max) {
      $val = $r * $dim + $c;
      for $n (1..$ndim) {
        if ($answer[$val]{$n} != 0) {
          print ($answer[$val]{$n} % 10);
        } else {
          print " ";
        }
      }
      print " ";
    }
    print "\n";
    for $c (0..$max) {
      $val = $r * $dim + $c;
      for $n (1..$ndim) {
        if ($answer[$val]{$n} != 0) {
          print " ";
        } else {
          print " ";
        }
      }
      print " ";
    }
    print "\n";
  }
  print "\n";
}

sub repeat_until_unique {
  while ((scalar @sol) == 2) {
    my @check0 = conve(@{$sol[0]});
    my @check1 = conve(@{$sol[1]});
    my $rval = 1;
    while ((int($rval/$row_count) + $rval % $row_count) % 2 == 1 or
           $check0[$rval] == $check1[$rval]) {
      $rval = int(rand($row_count * $col_count));
    }
    $grid[$rval] = $check1[$rval];
    try_solve;
    print "-------------------------\n";
    print (scalar @sol);
    print "\n";
    print_image2($sol[0]);
    print_image2($sol[1]);
    print "-------------------------\n";
  }
}

sub destroy_while_unique {
  my $rval = int(rand($row_count * $col_count));
  my $sval = $rval + 1;
  $sval = 0 if ($sval >= $row_count * $col_count);
  while ($sval != $rval ) {
    if ($grid[$sval] ne '+' and $grid[$sval] ne '?') {
      print "trying to remove $sval -- ";
      $oldval = $grid[$sval];
      $grid[$sval] = '+';
      try_solve;
      if ((scalar @sol) == 2) {
        print "failed!\n";
        $grid[$sval] = $oldval;
      } else {
        print "success!\n";
        $rval = $sval;
      }
    }
    $sval++;
    $sval = 0 if ($sval >= $row_count * $col_count);
  }
}

printable_puzzle();
raw_puzzle();
try_solve;

print "Finished running\n";

print (scalar @sol);
print "\n";
#for (0..$dim) {
#  print_image($sol[$_]);
#}
print_images(@sol);

if (scalar(@sol) == 1) {
  printable_solution(@{$sol[0]});
  raw_solution(@{$sol[0]});
} else {
  print `~/dance3/dance3 < tempfile.sudovar`;
}

#repeat_until_unique;
#destroy_while_unique;

#print "\n";
#try_solve;
#print_image2($sol[0]);

#print "      this.rowcount = $row_count; this.colcount = $col_count; ";
#print "this.answer = new Array(";
#for (0..($row_count*$col_count-1)) {
#  print "," if ($_);
#  $term = $sol[0][$_];
#  if ($term eq '-') {
#    print "6";
#  } elsif ($term eq '|') {
#    print "7";
#  } elsif ($term eq '+') {
#    print "5";
#  } else {
#    print $term;
#  }
#}
#print ");\n";
#


