#!/usr/bin/perl

=head1 Name

	panGenomeAnalysis.pl 

=head1 Description

	run pan- and core-genome analysis from ortholog groups output file.

=head1 Version

	Author: Lifu Song	(lifu.song@tuhh.de)
	Version: 1.1
	
=head1 Usage

  	perl panGenomeAnalysis.pl [options] input_file
	-run     run core/pan (runcp) or run new genes (default)
	-gnumer		Specific the total genome number of input file
	-clunum		Specific the total cluster number of the input file
	-samples	How many random samples will be outputed.

=head1 Exmple

	1. Run core and pan analysis:
	perl panGenomeAnalysis.pl  -run runcp -gnumber 67 -clunum 3000 -samples 1000 input > output
	
	2. Run new genes analysis:
	perl panGenomeAnalysis.pl  -run newgene -gnumber 67 -clunum 3000 -samples 1000 input > output

=cut

use strict;
use Getopt::Long;
use Data::Dumper;
use Math::Combinatorics;
use List::Util qw/sum/;


my $run;
my $g_number;
my $clu_num;
my $total_genes_file;
my $samples;
my $help;


GetOptions(
	"run:s"=>\$run,
	"gnumber:i"=>\$g_number,
	"clunum:i"=>\$clu_num,
	"totalgenes:s"=>\$total_genes_file,
	"samples:i"=>\$samples,
	"help"=>\$help
);
die `pod2text $0` if (@ARGV==0 || $help);

my $in=shift;

my @data;
open(IN,$in)||die("can not open the input file!\n");
while(<IN>){
	chomp;
	s/\a//;
	my @a;
	my $i;
	@a=split(/\t/,$_);
	for($i=1;$i<$g_number+1; $i++){
			if(!$a[$i]){$a[$i]=0;}
			push(@{$data[$i-1]},$a[$i]);
	}
}


my @arr_spec_genes_num;
open(TO,"$total_genes_file")||die("cannot open total genes number file\n!");
my $ss=0;
while(<TO>){
	chomp;
	if($_){
		my @arr=split(/\t/);
		push(@arr_spec_genes_num, $arr[1] - sum(@{$data[$ss]}) );
		$ss++;
	}	
}

warn "Data loaded!\n";

if($run eq "runcp"){&runCP;}
else{&runNewGene;}




sub runCP(){
	warn "Starting to run the core & pan analysis......\n";
	my $i;
	for($i=1;$i<=$g_number; $i++){

		if(&combCount($i,$g_number) <= $samples*2){
			
				my $combinat=Math::Combinatorics->new(count => $i, data => [@data]);
				my @combo=();
				my %spec;
				my $sx;
				my $ii;

				for($ii=0;$ii<@data;$ii++){
					$spec{$data[$ii]}= $arr_spec_genes_num[$ii];
				}
				while(@combo=$combinat->next_combination){
					print "$i\t";
					print coreFix(\@combo);
					print "\t";
					print core(\@combo);
					print "\t";
					my $ttt=(int(pan(\@combo))+ int(&allSpec(\@combo,\%spec)));
					print "$ttt";
					print "\n";	

				}
			}else{				
					my @arr_samples=randConm($samples,$i,$g_number);
					my $sX;

					foreach $sX (@arr_samples){

						my $sY;
						my $spec_genes=0;
						my @combo=();
						foreach $sY (@{$sX}){
							push(@combo,$data[$sY]);
							$spec_genes+=$arr_spec_genes_num[$sY];
						}


						print "$i\t";
					        print coreFix(\@combo);

						print "\t";
						print core(\@combo);

						print "\t";

						print pan(\@combo)+$spec_genes;

					       	print "\n";
						
					}
			}		
	}
}
sub runNewGene(){
	warn "Starting to run the new genes analysis......\n";
	my $i;
	for($i=2;$i<=$g_number; $i++){
		if(&newCount($i) <= $samples ){
			my $combinat=Math::Combinatorics->new(count => $i-1, data => [@data]);
		        my @combo=();
		        while(@combo=$combinat->next_combination){
				my $j=0;
				for($j=0;$j<$g_number;$j++){
		       	 	        if(!isInArr(\@combo,$data[$j])){
					print "$i\t";
	              			print newgenes(\@combo,$data[$j])  +$arr_spec_genes_num[$j];
					print "\n";}else{}
				}
	        	}
		}else{
			my @arr_samples=randConmN($samples,$i,$g_number);
			my $sX;
                        foreach $sX (@arr_samples){
                                my $sY;
                                my @combo=();
				my $newGn=shift(@{$sX});
				my $newGenome=$data[$newGn];
				

                                foreach $sY (@{$sX}){
                                        push(@combo,$data[$sY]);
                                }
				print "$i\t";
				print newgenes(\@combo,$newGenome)+$arr_spec_genes_num[$newGn];
				print "\t";
				print "\n";
                        }
		}
	}
}
sub isInArr(){
	my $arr=shift;
	my $ele=shift;
	my $sItem;
	foreach $sItem (@{$arr}){

		if($sItem eq $ele){
			return 1;
		}
	}	
	
	return 0;

}

sub combCount(){
	my $a=shift;
	my $b=shift;
	
	return nn($b)/(nn($a)*nn($b-$a));
}
sub newCount(){
	my $a=shift;
	my $b=shift;
	return nn($b)/(nn($a)*nn($b-$a-1));

}

sub nn(){
	my $n=int(shift);
	if($n>1){
		return $n* &nn($n-1);
	}else{return 1}
}
sub allSpec(){
	my $combo=shift;
	my $spec=shift;
	my $sx;
	my $total=0;
	foreach $sx(@{$combo}){
		$total += $spec->{$sx};
	}
	return $total;
}

sub newgenes(){
	my $data=shift;
	my $newgenome=shift;
        my $i;
	my $newGnum=0;
        for($i=0;$i<$clu_num;$i++){

                my $tmp=0;
		my $j;
                for($j=0;$j<= $#{$data};$j++){
                       $tmp+=$data->[$j]->[$i];
                }

		if($newgenome->[$i] > 0  and $tmp <=0){

			$newGnum+=$newgenome->[$i];
		}
                
        }
	return $newGnum;

}

sub core(){
	my $d=shift;
	my $c_size=0;
	my $i;
	for($i=0;$i<$clu_num;$i++){

		my @tmp=();
		my $j;
		for($j=0;$j<=$#{$d};$j++){
			push(@tmp,$d->[$j]->[$i]);
			
		}
		$c_size+=c(@tmp);
	}
	return $c_size;
}

sub coreFix(){
        my $d=shift;	
        my $c_size=0;
        my $i;
	

        for($i=0;$i<$clu_num;$i++){
                my @tmp=();
		my $j=0;
                for($j=0;$j<= $#{$d};$j++){
                        push(@tmp,$d->[$j]->[$i]);
			
                }
		my @tmp2=();
		for($j=0;$j<$g_number;$j++){
			push(@tmp2,$data[$j]->[$i]);
			
		}
		
                $c_size+=cFix(\@tmp,\@tmp2);
		
		
        }
        return $c_size;
}


sub pan(){
        my $d=shift;
        my $p_size=0;
	my $i;
        for($i=0;$i<$clu_num;$i++){
                my  @tmp=();
		my $j;
                for($j=0;$j<@{$d};$j++){
                        push(@tmp,$d->[$j]->[$i]);
                }
                $p_size+=p(@tmp);
         }
         return $p_size;
}

sub cFix(){
	my $a=shift;
	my $b=shift;
	my $c=$a->[0];
	my $sx;
	foreach $sx (@{$a}){
		if($sx<$c){$c=$sx;}		
	}
		
	if($c > 0){return $c;}else{
		if(&zeroNum($b) > 1){return 0;}else{
		
		 if($b->[0]*$b->[1]*$b->[2]*$b->[3] >0){ return 1; #For Complete genomes.
			}else{return 0;}
		}
		
	}
}

sub zeroNum(){
	my $arr=shift;
	my $a;
	my $n;
	foreach $a (@{$arr}){
		if($a <= 0){
			$n++;		
		}
	}
	return $n;
}

sub c(){
	my $a=shift;
	my $b=$a;
	while($b ne ""){
		if($b<$a){
			$a=$b;
		}
	$b=shift;
	}	
	if(defined($a)){
	return $a;}else{
		return 0;
	}
}

sub p(){
	my $a=shift;
	my $b=shift;
	while($b ne ""){
		if($b>$a){$a=$b}
		$b=shift;
	}
	return $a;
}
sub randCnm(){
	my $n=shift;
	my $m=shift;
	my @arr1=(0..($m-1));
	my $i;
	my $rn;
	my %hs=();
	my @arr2=();
	for($i=0;$i<$n;$i++){
		$rn=int(rand($m-$i));	
		@arr1=(sort{$a<=>$b}(@arr1));
		$hs{$arr1[$rn]}=1;
		push(@arr2,($arr1[$rn]));
		$arr1[$rn]=$m+1;
	}
	return  @arr2;
}

sub randConm(){
        my $o=shift;
        my $n=shift;
        my $m=shift;
        my @data;
        my %hs;
        my $i;
        for($i=0;$i<$o;$i++){
                my @arr = &randCnm($n,$m);
                while(defined($hs{join(" ", sort {$a<=>$b} @arr)})){ @arr = &randCnm($n,$m); }
                $hs{join(" ",sort {$a<=>$b} @arr)}=1;
                push(@{$data[$i]},@arr);
        }
        return @data;
}


sub randConmN(){
	my $o=shift;
	my $n=shift;
	my $m=shift;
	my @data;
	my %hs;
	my $i;
	for($i=0;$i<$o;$i++){
		my @arr = &randCnm($n,$m);
		while(defined($hs{join(" ", ($arr[0],sort {$a<=>$b} @arr))})){ @arr = &randCnm($n,$m); }
		$hs{join(" ",($arr[0],sort {$a<=>$b} @arr))}=1;
		push(@{$data[$i]},@arr);
	}	
	return @data;
}