#!/usr/bin/perl
#jda@math.umd.edu

use strict;
use Data::Dumper;

use Getopt::Long;
use vars qw($help $quiet $outputFile $split $fundamental $parameter $cayleyString $crossString);

my %options=('h' => \$help,  
	     'o' => \$outputFile,
	     's' => \$split,
	     'f' => \$fundamental,
	     'p' => \$parameter,
	     'cayley' => \$cayleyString,
	     'cross' => \$crossString,
	     'q' => \$quiet);

GetOptions(\%options,qw(h! o=s q! i=s s! f! p=s cayley=s cross=s));

my $inputFile=$ARGV[0];

&help if ($help or !$inputFile);

if ($outputFile){
    $quiet or print "Sending output to file $outputFile\n";
    open(OUT,">$outputFile")||die("Can't open $outputFile for output");
    select(OUT);
}

$crossString='x' unless  (defined($crossString));
$cayleyString ='^' unless  (defined($cayleyString));
my $rank;
if ($split and $fundamental){
    die("Can't use both -s (split) and -f (fundamental). Use -h for help\n");
}
my $start=$split?'split':'fundamental';
my ($size,$rank,$minLength,$maxLength,$maxLengthTau,$baseParameterLength)=&getBasicData;
if (length($baseParameterLength)){
    if ($baseParameterLength == $minLength){
	$start='fundamental';
    }elsif ($baseParameterLength == $maxLength){
	$start='split';
    }else{
	print "\nIgnoring -p: parameter length is $baseParameterLength, but maximum length=$maxLength and minimum length=$minLength";
	$parameter='';
    }
}

$start = ($start =~ /s/)?'split':'fundamental';
unless ($quiet){
    if ($parameter){
	print "\nStarting on parameter $parameter of $start Cartan\n";
    }else{
	print "\nStarting on $start Cartan\n";
    }
    print "Data from file $inputFile\n\n";
}

my $action=&action($parameter) if (length($parameter));
my ($realCayley,$imaginaryCayley)=&getCayley;
my $sequence=&process;

&output($realCayley,$imaginaryCayley,$sequence,$maxLengthTau,$action);

sub process{
    open(IN,"<$inputFile")||die("Can't open $inputFile for input");
    my %step;
    my $C;
    my $cayleyTypes;
    my $cayleys;
    my $startLength;
    if ($start eq 'fundamental'){
	$C='C-';
	$cayleyTypes="r1|r2";
	$startLength=$minLength;
	$cayleys=$realCayley;
    }else{
	$C='C+';
	$cayleyTypes="i1|i2";
	$startLength=$maxLength;
	$cayleys=$imaginaryCayley;
    }
    my @lines;
    foreach my $line (<IN>){
	chomp($line);
#	my ($space,$number,$xy,$cross,$imaginaryCayley,$types,$length,$tau)=
#	    ($line =~ /^(\s*)([0-9\s]*)(\([0-9,\s]*\)).*:\s*([^\(]*)\((.*)\)\s*\[([^\]]*)\]\s*([0-9]*)(\s*[0-9]*)/g);
# 0( 0,6):  0  0  [i1,i1]   1   2   ( 4, *)  ( 5, *)   e

	my ($space,$number,$xy,$length,$cartan,$types,$cross,$imaginaryCayley,$tau)=
    ($line =~ /^(\s*)([0-9\s]*)(\([0-9,\s]*\)).*:\s*([0-9]*)\s*([0-9]*)\s*\[([^\]]*)\]\s*([^\(]*)\((.*)\)\s*(.*)/g);
#	print("$space:$number:$xy:$length:$cartan:$types:$cross:$imaginaryCayley:$tau\n");


	next unless $line;
	push @lines,[$space,$number,$xy,$cross,$imaginaryCayley,$types,$length,$tau];
	if ($start  eq 'fundamental'){
	     @lines=sort {$a->[6] <=> $b->[6]} @lines;
	}else{
	     @lines=sort {$b->[6] <=> $a->[6]} @lines;
	}
    }
    close(IN);


    foreach my $line (@lines){
	my ($space,$number,$xy,$cross,$imaginaryCayley,$types,$length,$tau)=@$line;	
	if ($length == $startLength){
	    $step{$number}="[$number]";
	    next;
	}
	my @types=split ',', $types;
	my @cross = split /\s+/, $cross;
	foreach my $i (0..$rank-1){
	    if ($types[$i] eq $C){
#		$step{$number}=[$i,$types[$i],$cross[$i]];
		$step{$number}=[$i+1,$types[$i],$cross[$i]];
		last;
	    }
	    if ($types[$i] =~ /$cayleyTypes/){
		my $cayley=$cayleys->{$number}->{$i}->[0];
#		$step{$number}=[$i,$types[$i],$cayley];
		$step{$number}=[$i+1,$types[$i],$cayley];
		last;		    
	    }
	}
    }

    my %sequence;
    foreach my $line (@lines){
	my $i=$line->[1];
	my $step=$step{$i};
	    unless (ref($step)){
		$sequence{$i}=$step;
	    }else{
		my ($root,$type,$image)=@$step;
		if ($type =~ /C/){
		    $sequence{$i}=$root.$crossString.$sequence{$image};
		}elsif ($type =~ /$cayleyTypes/){
		    $sequence{$i}=$root.$cayleyString.$sequence{$image};
		}
	    }
    }


#    print "\nSEQUENCE:", Dumper(\%sequence);

    return \%sequence;
}


sub getBasicData{
    open(IN,"<$inputFile")||die("Can't open $inputFile for input");
    my $size=0;
    my $minLength=1000;
    my $maxLength=0;
    my $maxLengthTau=0;
    my $baseParameterLength;
#one run to get size, rank, max/min lengths
    foreach my $line (<IN>){
	$line =~ s/\#.*//;
	next unless $line;

	my ($space,$number,$xy,$length,$cartan,$types,$cross,$cayley,$tau)=
    ($line =~ /^(\s*)([0-9\s]*)(\([0-9,\s]*\)).*:\s*([0-9]*)\s*([0-9]*)\s*\[([^\]]*)\]\s*([^\(]*)\((.*)\)\s*(.*)/g);

#	my ($number,$cross,$cayley,$types,$length,$tau)=
#	    ($line =~ /^([0-9\s]*).*:\s*([^\(]*)\(\s*(.*)\)\s*\[([^\]]*)\]\s*([0-9]*)\s*([0-9]*)/g);


	$number =~ s/\s//g;
	$size=$number if ($number >$size);
	$minLength=$length if ($length < $minLength);
	$maxLength=$length if ($length > $maxLength);
	$maxLengthTau=length($tau) if (length($tau)>$maxLengthTau);
	unless ($rank){
	    my @cayley=split(/\)\s*\(/, $cayley);	
	    $rank=scalar(@cayley);
	}
	$baseParameterLength=$length if ($parameter eq $number);
    }
    close(IN);
#    $quiet or print "size: $size\nrank: $rank\nminLength: $minLength\nmaxLength: $maxLength\nmaxLengthTau: $maxLengthTau\n";
    return ($size,$rank,$minLength,$maxLength,$maxLengthTau,$baseParameterLength);
}

sub getCayley{
    my %realCayley;
    my %imaginaryCayley;
    open(IN,"<$inputFile")||die("Can't open $inputFile for input");
    foreach my $line (<IN>){
#    my ($number,$cross,$imaginaryCayley,$types,$length,$tau)=
#	($line =~ /^([0-9\s]*).*:\s*([^\(]*)\(\s*(.*)\)\s*\[([^\]]*)\]\s*([0-9]*)\s*([0-9]*)/g);
	my ($space,$number,$xy,$length,$cartan,$types,$cross,$imaginaryCayley,$tau)=
    ($line =~ /^(\s*)([0-9\s]*)(\([0-9,\s]*\)).*:\s*([0-9]*)\s*([0-9]*)\s*\[([^\]]*)\]\s*([^\(]*)\((.*)\)\s*(.*)/g);

    $line =~ s/\#.*//;
    next unless $line;
    $number =~ s/\s//g;
    $imaginaryCayley =~ s/\s//g;
    my @cross = split /\s+/, $cross;
    my @imaginaryCayley=split /\)\(/, $imaginaryCayley;
    my @types=split ',', $types;
    foreach my  $i (0..$#imaginaryCayley){
	my $cayleyPair=$imaginaryCayley[$i];
	foreach my $c (split ',', $cayleyPair){
	    next if ($c eq '*');
	    push @{$realCayley{$c}->{$i}}, $number;
	    push @{$imaginaryCayley{$number}->{$i}}, $c;
	}
    }
}
close(IN);

unless ($quiet){
#    print "REAL CAYLEY:",  Dumper(\%realCayley);
#    print "IM CAYLEY:",  Dumper(\%imaginaryCayley);
}
return (\%realCayley,\%imaginaryCayley);
}
sub output{
    my ($realCayley,$imaginaryCayley,$sequence,$maxLengthTau,$action)=@_;
    my %realCayley=%$realCayley;
    my %imaginaryCayley=%$imaginaryCayley;
    open(IN,"<$inputFile")||die("Can't open $inputFile for input");
    foreach my $line (<IN>){
	chomp($line);
#	my ($space,$number,$xy,$cross,$imaginaryCayley,$types,$length,$tau)=
#	    ($line =~ /^(\s*)([0-9\s]*)(\([0-9,\s]*\))(.*:\s*[^\(]*)\((.*)\)(\s*\[[^\]]*\]\s*)([0-9]*\s*)([0-9]*)/g);
	my ($space,$number,$xy,$length,$cartan,$types,$cross,$imaginaryCayley,$tau)=
    ($line =~ /^(\s*)([0-9\s]*)(\([0-9,\s]*\)).*:\s*([0-9]*)\s*([0-9]*)\s*\[([^\]]*)\]\s*([^\(]*)\((.*)\)\s*(.*)/g);
#	print "$space:$number:$xy:$length:$cartan:$types:$cross:$imaginaryCayley:$tau\n";
	$line =~ s/\#.*//;
	next unless $line;
	#put in real cayleys
	my @imaginaryCayley=split /\)  \(/, $imaginaryCayley;
	foreach my $i (0..$#imaginaryCayley){
	    next unless ($realCayley{$number});
	    next unless ($realCayley{$number}->{$i});
	    my @realCayleyPair=@{$realCayley{$number}->{$i}};
	    my $c1=$realCayleyPair[0];
	    my $c2=$realCayleyPair[1]||"*";
	    my $ic=$imaginaryCayley[$i];
	    my @ic=split ',', $ic;
	    my $star=$ic[0];
	    $c1=" "x(length($star)-length($c1)).$c1;
	    $c2=" "x(length($star)-length($c2)).$c2;
	    $imaginaryCayley[$i]="$c1,$c2";
	}
	$imaginaryCayley=join ')  (', @imaginaryCayley;
	my $tauSpace=" "x($maxLengthTau-length($tau));
	$tau = $tau.$tauSpace;
#	$line = "$space$number$xy$cross($imaginaryCayley)$types$length$tau";
#	$line = "$space$number$xy$length$cartan$types$cross$imaginaryCayley$tau";

	print $line;
	my $seq=$sequence->{$number};
	if (length($parameter)){
	    (my $base = $seq) =~ s/.*\[([0-9]*)\]/\1/g;
#	    print "base:$base\n";
#	    print "test:$number:$action->{$number}\n";
	    $seq =~ s/\[$base\]/$action->{$base}/;
	}
	print "$tauSpace    $seq";
	print "\n";
    }
}

sub action{
    my $parameter=shift;
    open(IN,"<$inputFile")||die("Can't open $inputFile for input");
    my $C;
    my $crossTypes;
    my $startLength;
    if ($start eq 'fundamental'){
	$startLength=$minLength;
	$crossTypes='i1|i2';
    }else{
	$C='C+';
	$startLength=$maxLength;
	$crossTypes='r1|r2';
    }
    my %lines;
    foreach my $line (<IN>){
	chomp($line);

#	my ($space,$number,$xy,$cross,$imaginaryCayley,$types,$length,$tau)=
#	    ($line =~ /^(\s*)([0-9\s]*)(\([0-9,\s]*\)).*:\s*([^\(]*)\((.*)\)\s*\[([^\]]*)\]\s*([0-9]*)(\s*[0-9]*)/g);

	my ($space,$number,$xy,$length,$cartan,$types,$cross,$imaginaryCayley,$tau)=
    ($line =~ /^(\s*)([0-9\s]*)(\([0-9,\s]*\)).*:\s*([0-9]*)\s*([0-9]*)\s*\[([^\]]*)\]\s*([^\(]*)\((.*)\)\s*(.*)/g);

	next unless $line;
	next unless ($length == $startLength);

	$lines{$number}=[$space,$number,$xy,$cross,$imaginaryCayley,$types,$length,$tau];
    }
    close(IN);
    my %action;

    $action{$parameter}="[$parameter]";
    my %done;
    my %actionTemp;
    while (scalar(keys %action) < scalar(keys %lines)){
	foreach my $i (keys %action){
	    next if ($done{$i});
	    my $line=$lines{$i};
	    my $types=$line->[5];
	    my @types=split ',', $types;
	    my $crosses=$line->[3];
	    my @crosses = split /\s+/, $crosses;
	    foreach my $j (0..$#types){
		my $type=$types[$j];
		next unless ($type =~ /$crossTypes/);
		my $image=$crosses[$j];
		next if ($action{$image});
		$actionTemp{$image}=$j.$crossString.$action{$i};
	    }
	}
	foreach my $i (keys %actionTemp){
	$action{$i}=$actionTemp{$i};
    }
    }
    return \%action;
}

sub help{
    print "This script reads a file, containing block information, and gives some additional information
about sequences of Cayley transforms and cross actions. The file should be the output of the block command of 
atlas.

Usage: blockj [-o output] [-s|-f] [--cayley ...] [--cross ...] [-p parameter] [-q] inputFile

The output is the same as the output of the block command of atlas
with the following changes:

1) Real Cayley transforms are listed
2) An additional column gives every parameter as a sequence of Cayley
transforms and complex cross actions applied to a parameter coming from the
fundamental Cartan (the default) or the split Cartan
3) If a parameter is chosen with -p, the final column instead gives
every parameter as a sequence of Cayley transforms, complex cross
actions, and imaginary (the default, or real) cross actions, applied
to this parameter.

Options:

-f             start on fundamental Cartan (the default)
-s             start on split Cartan
-p parameter   reduce every parameter to this one
--cross X      denote cross action with X (default: x)
--cayley _     denote Cayley transforms with  _ (default: ^)
-o outputFile  send output to outputFile
-q             quiet 

The parameter -p is ignored if it isn't on the fundamental or split Cartan, and overrides -s 
or -f if it is.

Examples: 

blockj blockC2

 Take blockC2 (output of block command of atlas),insert real
 Cayley transforms, and write every parameter as a series of Cayley
 transform/cross actions applied to a discrete series

blockj blockC2 -s 
  
  Same as above but start with the principal series

blockj blockC2 -p 1

 Same as above, but related every parameter back to the large
 discrete series with parameter 1

blockj blockC2 --cross X
  
 Write cross actions with X instead of x

blockj blockC2 --cross ''
 
 Write cross actions with no extra character.

Sample output:

blockj blockC2  -p 1

Starting on parameter 1 of fundamental Cartan
Data from file blockC2

 0( 0,6):  0  0  [i1,i1]   1   2   ( 4, *)  ( 5, *)   e          0x[1]
 1( 1,6):  0  0  [i1,i1]   0   3   ( 4, *)  ( 6, *)   e          [1]
 2( 2,6):  0  0  [ic,i1]   2   0   ( *, *)  ( 5, *)   e          1x0x[1]
 3( 3,6):  0  0  [ic,i1]   3   1   ( *, *)  ( 6, *)   e          1x[1]
 4( 4,5):  1  1  [r1,C+]   4   9   ( 0, 1)  ( *, *)   1          1^0x[1]
 5( 5,4):  1  2  [C+,r1]   7   5   ( *, *)  ( 0, 2)   2          2^0x[1]
 6( 6,4):  1  2  [C+,r1]   8   6   ( *, *)  ( 1, 3)   2          2^[1]
 7( 7,3):  2  2  [C-,i1]   5   8   ( *, *)  (10, *)   1,2,1      1x2^0x[1]
 8( 8,3):  2  2  [C-,i1]   6   7   ( *, *)  (10, *)   1,2,1      1x2^[1]
 9( 9,2):  2  1  [i2,C-]   9   4   (10,11)  ( *, *)   2,1,2      2x1^0x[1]
10(10,0):  3  3  [r2,r1]  11  10   ( 9, *)  ( 7, 8)   2,1,2,1    1^2x1^0x[1]
11(10,1):  3  3  [r2,rn]  10  11   ( 9, *)  ( *, *)   2,1,2,1    1^2x1^0x[1]

blockj blockC2  

Starting on fundamental Cartan
Data from file blockC2

 0( 0,6):  0  0  [i1,i1]   1   2   ( 4, *)  ( 5, *)   e          [0]
 1( 1,6):  0  0  [i1,i1]   0   3   ( 4, *)  ( 6, *)   e          [1]
 2( 2,6):  0  0  [ic,i1]   2   0   ( *, *)  ( 5, *)   e          [2]
 3( 3,6):  0  0  [ic,i1]   3   1   ( *, *)  ( 6, *)   e          [3]
 4( 4,5):  1  1  [r1,C+]   4   9   ( 0, 1)  ( *, *)   1          1^[0]
 5( 5,4):  1  2  [C+,r1]   7   5   ( *, *)  ( 0, 2)   2          2^[0]
 6( 6,4):  1  2  [C+,r1]   8   6   ( *, *)  ( 1, 3)   2          2^[1]
 7( 7,3):  2  2  [C-,i1]   5   8   ( *, *)  (10, *)   1,2,1      1x2^[0]
 8( 8,3):  2  2  [C-,i1]   6   7   ( *, *)  (10, *)   1,2,1      1x2^[1]
 9( 9,2):  2  1  [i2,C-]   9   4   (10,11)  ( *, *)   2,1,2      2x1^[0]
10(10,0):  3  3  [r2,r1]  11  10   ( 9, *)  ( 7, 8)   2,1,2,1    1^2x1^[0]
11(10,1):  3  3  [r2,rn]  10  11   ( 9, *)  ( *, *)   2,1,2,1    1^2x1^[0]

Note: the name of this command is a pun on the atlas command blockd, which stands for \"block-David\". 
";
    exit;
}


