#! /usr/local/bin/perl -w

use Getopt::Std;

sub determine_opts	{
					getopts('i:p:',\%parameters);
					if (exists $parameters{'i'})
						{
						$InFile = $parameters{'i'};
						chomp $InFile;
						}
					else
						{
						&exit_screen;
						}
					if (exists $parameters{'p'})
						{
						$processes = $parameters{'p'};
						chomp $processes;
						}
					else
						{
						$processes = 1;
						}
					}
					
sub exit_screen	{
				system "clear";
				print "__________________________________________________________\n\n";
				print "       - Bayesian Segmentation Program -\n";
				print "__________________________________________________________\n";
				print "Input filename with -i\n";
				print "Input number or 2CPU processes with -p\n";
				print "__________________________________________________________\n\n";
				exit;
				}
				
sub txt_to_hash	{
				@ordered_seq = ();
				open FILE, $InFile;
				my @temp = <FILE>;
				close FILE;
				my $join = join '', @temp;
				$join =~ s/>/\n\n\n>/gm;
				%seq = ($join =~ /^>(.+?)\n+?(.+?)\n\n/gsm);
				foreach my $value (keys(%seq))
					{
					$seq{$value} =~ s/\s+?//gs;
					$seq{$value} =~ tr/A-Z/a-z/;
					$ordered_seq[$total_seq] = $value;
					while ($seq{$value} =~ /(\w{1})/gi)
						{
						unless (exists $states{$1})
							{
							$states{$1} = 0;
							++$state_count;
							}
						}
					++$total_seq;
					}
				}
		
sub average_time	{
			my $seq_time = (($seq_end - $seq_start)/($processes));
			if ($avg_time == 0)
				{
				$avg_time = $seq_time;
				$old_average = $avg_time;
				if ($avg_time > 99)
					{
					$avg_print_time = ($avg_time)/(60);
					$avg_format = "minutes";
					$avg_print_time =~ m/(\d+?\.\d)/gm;
					$avg_print_time = $1;
					}
				else 
					{
					$avg_print_time = $seq_time;
					$avg_format = "seconds";
					}
				}
			else	
				{
				$avg_time = (((($old_average)*($time_count)) + ($seq_time))/($time_count + 1));
				$old_average = $avg_time;
				if ($avg_time > 99)
					{
					$avg_print_time = ($avg_time)/(60);
					$avg_format = "minutes";
					$avg_print_time =~ m/(\d+?\.\d)/gm;
					$avg_print_time = $1;
					}
				else 
					{
					$avg_print_time = $avg_time;
					$avg_format = "seconds";
					if ($avg_print_time =~ m/(\d+?\.\d)/gm)
						{
						$avg_print_time = $1;
						}
					}
				}
			
			$estimated_time = ($avg_time)*($seq_rem);
			if ($estimated_time > 172800)
				{
				$est_print_time = ($estimated_time)/(86400);
				$est_format = "days";
				}
			elsif ($estimated_time > 3600)
				{
				$est_print_time = ($estimated_time)/(3600);
				$est_format = "hours";
				}
			elsif ($estimated_time > 99)
				{
				$est_print_time = ($estimated_time)/(60);
				$est_format = "minutes";
				}
			else	
				{
				$est_format = "seconds";
				$est_print_time = $estimated_time;
				}
				
			if ($est_print_time =~ m/(\d+?\.\d)/gm)
				{
				$est_print_time = $1;
				}
			else
				{
				$est_print_time = $estimated_time;
				}
			++$time_count;
			}
				
			

sub do_count2	{
				my $value = shift;
				%count = %states;
				$count{total} = 0;
				while ($value =~ /(\w{1})/gi)
					{
					++$count{$1};
					++$count{total};
					}
				}

sub fact_populate	{
					if ($fac_max == 0)
						{
						my $n = 1;
						$fac{0} = 1;
						while ($n < $max + $state_count + 10)
							{
							my $j = $n - 1;
							$fac{$n} = ($n)*($fac{$j});
							++$n;
							}
						}
					elsif ($max > $fac_max + 6)
						{
						my $n = $fac_max + 7;
						while ($n < $max + 30)
							{
							my $j = $n - 1;
							$fac{$n} = ($n)*($fac{$j});
							++$n;
							}
						$fac_max = $max;
						}				
					return %fac;
					}

sub change_prob	{
				my $theta_total = 0;
				my $part2 = 1;
				foreach my $state (keys(%states))
					{
					++$theta_total;
					}
				my $part1 = (($fac{$theta_total})/($part2));
				my $K = 0;
				my $startpoint = 1;
				my $temp = $seq;
				&do_count2($temp);
				my $reversed = reverse $temp;		
				while ($startpoint < $max + 1) 
					{			
					my $part3 = 1;
					foreach my $state (keys(%states))
						{
						$part3 = $part3 * ($fac{$count{$state} + 1});
						}
					$$K[$startpoint] = 	(($part1)*($part3)/($fac{$count{total} + $theta_total}));
					my $amino_acid = chop($reversed);
					--$count{$amino_acid};
					--$count{total};			
					++$startpoint;
					}
				
				open (FILE10, ">$directory\/$hashname.$K\_dat.txt");
				my $i = 1;
				while ($i < $max - $K)
					{
					print FILE10 "$i $$K[$i]\n";
					++$i;
					}
				print FILE10 "\n\n\n";
				close FILE10;
				++$K;

				
				while ($K < 2)
					{
					my $J = $K - 1;
					$startpoint = 1;
					$temp = $seq;
					my $count = length $temp;
					$reversed = reverse $temp;
					while ($startpoint < $max - $K - 1) 
						{ 
						my $reversed2 = $reversed;
						my $hashname2 = $startpoint."hash";
						my $seg_length = 1;
						my $sum_prob = 0;
						my %segment = %states;
						while ($seg_length < $count - $K - 1) 
							{
							my $amino_acid2 = chop($reversed2);
							++$segment{$amino_acid2};
							my $part3 = 1;
							foreach my $state (keys(%segment))
								{
								$part3 *= ($fac{$segment{$state} + 1});
								}
							
							$$hashname2[$seg_length] = ((($part1)*($part3))/($fac{$seg_length + $theta_total}));						
							
							my $position = $seg_length + $startpoint;
												
							$sum_prob += ($$hashname2[$seg_length])*($$J[$position]);
							
							++$seg_length;
							}
						$$K[$startpoint] = ($sum_prob);
						my $amino_acid = chop($reversed);
						--$count;
						++$startpoint;
						}
					open (FILE10, ">$directory\/$hashname.$K\_dat.txt");
					$i = 1;
					while ($i < $max - $K - 1)
						{
						print FILE10 "$i $$K[$i]\n";
						++$i;
						}
					print FILE10 "\n\n\n";
					close FILE10;
					@$J = ();
					++$K;
					}
					
				while ($K < $change_points + 1)
					{
					my $J = $K - 1;
					$startpoint = 1;
					$temp = $seq;
					my $count = length $temp;
					while ($startpoint < $max - $K - 1) 
						{ 
						my $hashname2 = $startpoint."hash";
						my $seg_length = 1;
						my $sum_prob = 0;
						while ($seg_length < $count - $K - 1) 
							{
							my $position = $seg_length + $startpoint;
							
							$sum_prob += ($$hashname2[$seg_length])*($$J[$position]);
							
							++$seg_length;
							}
						$$K[$startpoint] = ($sum_prob);
						++$startpoint;
						--$count;
						}
					open (FILE10, ">$directory\/$hashname.$K\_dat.txt");
					$i = 1;
					while ($i < $max - $K - 1)
						{
						print FILE10 "$i $$K[$i]\n";
						++$i;
						}
					print FILE10 "\n\n\n";
					close FILE10;
					@$J = ();
					++$K;
					}
				}

sub RV_ch_prob	{
		my $theta_total = 0;
		my $part2 = 1;
		foreach my $state (keys(%states))
			{
			++$theta_total;
			}
		my $part1 = (($fac{$theta_total})/($part2));
		my $K = 0;
		my $startpoint = 1;
		my $temp = reverse $seq;
		&do_count2($temp);
		my $reversed = reverse $temp;		
		while ($startpoint < $max + 1) 
			{			
			my $part3 = 1;
			foreach my $state (keys(%states))
				{
				$part3 *= ($fac{$count{$state} + 1});
				}
			$$K[$startpoint] = 	(($part1)*($part3)/($fac{$count{total} + $theta_total}));
			my $amino_acid = chop($reversed);
			--$count{$amino_acid};
			--$count{total};			
			++$startpoint;
			}
			
		open (FILE10, ">$directory\/$rev_hashname.$K\_dat.txt");
		my $i = 1;
		while ($i < $max - $K)
			{
			print FILE10 "$i $$K[$i]\n";
			++$i;
			}
		print FILE10 "\n\n\n";
		close FILE10;
		++$K;

		
		while ($K < 2)
			{
			my $J = $K - 1;
			$startpoint = 1;
			$temp = reverse $seq;
			my $count = length $temp;
			$reversed = reverse $temp;
			while ($startpoint < $max - $K - 1) 
				{ 
				my $reversed2 = $reversed;
				my $rev_hashname2 = $startpoint."hash";
				my $seg_length = 1;
				my $sum_prob = 0;
				my %segment = %states;
				while ($seg_length < $count - $K - 1) 
					{
					my $amino_acid2 = chop($reversed2);
					++$segment{$amino_acid2};
					my $part3 = 1;
					foreach my $state (keys(%segment))
						{
						$part3 *= ($fac{$segment{$state} + 1});
						}
					
					$$rev_hashname2[$seg_length] = ((($part1)*($part3))/($fac{$seg_length + $theta_total}));								
										
					my $position = $seg_length + $startpoint;
					
					$sum_prob += ($$rev_hashname2[$seg_length])*($$J[$position]);
					
					++$seg_length;
					}
				$$K[$startpoint] = ($sum_prob);
				my $amino_acid = chop($reversed);
				--$count;
				++$startpoint;
				}
			open (FILE10, ">$directory\/$rev_hashname.$K\_dat.txt");
			$i = 1;
			while ($i < $max - $K - 1)
				{
				print FILE10 "$i $$K[$i]\n";
				++$i;
				}
			print FILE10 "\n\n\n";
			close FILE10;
			@$J = ();
			++$K;
			}
			
		while ($K < $change_points + 1)
			{
			my $J = $K - 1;
			$startpoint = 1;
			$temp = reverse $seq;
			$count = length $temp;
			while ($startpoint < $max - $K - 1) 
				{ 
				my $rev_hashname2 = $startpoint."hash";
				my $seg_length = 1;
				my $sum_prob = 0;
				while ($seg_length < $count - $K - 1) 
					{
					my $position = $seg_length + $startpoint;
					
					$sum_prob += ($$rev_hashname2[$seg_length])*($$J[$position]);
					
					++$seg_length;
					}
				$$K[$startpoint] = ($sum_prob);
				--$count;
				++$startpoint;
				}
			open (FILE10, ">$directory\/$rev_hashname.$K\_dat.txt");
			$i = 1;
			while ($i < $max - $K - 1)
				{
				print FILE10 "$i $$K[$i]\n";
				++$i;
				}
			print FILE10 "\n\n\n";
			close FILE10;
			@$J = ();
			++$K;
			}
		}


sub print_res	{		
				print "Building profile:\n";
				my $j = 0;
				while ($j < $change_points + 1)
					{
					my $filename = "$directory\/$hashname.$j\_dat.txt";
					my $filename2 = "$directory\/$rev_hashname.$j\_dat.txt";
					open (FILE, $filename);
					my @temp = <FILE>;
					close FILE;
					my $join = join '', @temp;
					my $name = "for".$j;
					while ($join =~ /(\d+)\s(\d\..+)$/gm)
						{
						$$name[$1] = $2;
						}
					my $name2 = "rev".$j;
					open (FILE, $filename2);
					my @temp2 = <FILE>;
					close FILE;
					my $join2 = join '', @temp2;
					while ($join2 =~ /(\d+)\s(\d\..+)$/gm)
						{
						$$name2[$1] = $2;
						}
					++$j;
					}
	
				my $i = 0;
				my @PRi = ();
				@PK = ();
				my $total = 0;
				while ($i < $change_points + 1)
					{
					my $name = "for".$i;
					my $name2 = "rev".$i;
					my $temp = 0.5/($i + 1);
					#my $temp = 1;
					$PRi[$i] = (($temp)*($$name[1])*($fac{$i})*($fac{$max - 1 - $i})/($fac{$max - 1})*($fac{$i})*($fac{$max - 1 - $i})/($fac{$max - 1}));
					#$PRi[$i] = ($$name[1])/(($fac{$max-1})/(($fac{$i})*($fac{$max - 1 - $i})));
					#$PRi[$i] = $$name[1]/(($fac{$max-1})/(($fac{$i})*($fac{$max - 1 - $i})));
					$total += $PRi[$i];
					++$i;
					}
															
				$i = 0;
				open (FILE,">$entry\.pkr");
				while ($i < $change_points + 1)
					{
					$PK[$i] = ($PRi[$i]/$total);
					print FILE "P($i|R) $PK[$i]\n";
					++$i;
					}			
				}

sub print_res1	{		
				my $j = 0;
				while ($j < $change_points + 1)
					{
					my $filename = "$directory\/$rev_hashname.$j\_dat.txt";
					open (FILE, $filename);
					my @temp = <FILE>;
					close FILE;
					unlink $filename;
					my $join = join '', @temp;
					my $name = "rev".$j;
					while ($join =~ /(\d+)\s(\d\..+)$/gm)
						{
						$$name[$1] = $2;
						}
					++$j;
					}
				}
				
sub change_pos	{
		my $K_max = 0;		
		my $K_for = 0;
		my $K_rev = 0;
		my $for_pos = 3;
		my @total = ();
		my $for_name = "for"."$K_for";
		my $rev_name = "rev"."$K_rev";
		while ($for_pos < $max - $K_max)
			{
			my $rev_pos = $max - $for_pos + 2;
			my $prob = ($$for_name[$for_pos])*($$rev_name[$rev_pos]);
			$$K_max[$for_pos] += $prob;
			$total[$K_max] += $prob;
			++$for_pos;
			}
					
		++$K_max;
		
		while ($K_max < $change_points + 1)
			{
			my $K_for = 0;
			while ($K_for < $K_max + 1)
				{		
				my $K_rev = $K_max - $K_for;
				$for_name = "for"."$K_for";
				$rev_name = "rev"."$K_rev";
				if ($K_rev == 0)
					{
					$for_pos = 3;
					}
					
				else
					{
					$for_pos = $K_rev + 4;
					}	
								 				
				while ($for_pos < $max - $K_for - 2)
					{
					my $rev_pos = $max - $for_pos + 2;
					my $prob = ($$for_name[$for_pos])*($$rev_name[$rev_pos]);
					$$K_max[$for_pos] += $prob;
					$total[$K_max] += $prob;
					++$for_pos;
					}
				++$K_for;
				}
			++$K_max;
			}
			
		my %final_dist = ();
		my $i = 0;
		while ($i < $change_points)
			{
			my $j = 3;
			while ($j < $max - 2)
				{
				$final_dist{$j} += ((($$i[$j])*($PK[$i]))/($total[$i]));
				++$j;
				}
			++$i;
			}
		
		open (FILE2, ">$entry\.pro");				
		my $j = 3;
		while ($j < $max - 2)
			{
			printf FILE2 "$j $final_dist{$j}\n";
			++$j;
			}
		close FILE2;
		}
		
sub an_res		{
				&print_res;
				&print_res1;
				&change_pos;
				}

sub fork_1		{
				my $fork1 = 'change_prob';
				my $fork2 = 'RV_ch_prob';
				my $pid3 = fork();
				if ($pid3) 
					{
					&$fork1;
					# parent
					waitpid($pid3,0);
					} 
				elsif ($pid3 == 0) 
					{
					&$fork2;
					# child
					exit(0);
					}
				else 
					{
					die "couldn't fork: $!\n";
					}
				}

sub spool_threads	{
					&fork_1;
					&an_res;
					}
			

sub ctrl_panel		{
			$seq_rem = $total_seq - $seq_count;
			system "clear";
			print "__________________________________________________________\n\n";
			print "       - Bayesian Segmentation Program -\n";
			print "__________________________________________________________\n";
			print "\nFILES:\n";
			print "Filename: $InFile\n";
			print "\nPARAMETERS:\n";
			print "Total No. of Character states:\t$state_count\n";
			print "Total No. sequences:\t\t$total_seq\n";
			print "\nSTATUS:\n";
			print "Current sequence:\t\t$entry\n";
			print "Sequence length\t\t\t$max\n";
			print "No. analysed:\t\t\t$seq_count\n";
			print "No. remaining:\t\t\t$seq_rem\n";
			print "Average sequence time:\t\t$avg_print_time $avg_format\n";
			print "Estimated time remaining:\t$est_print_time $est_format\n";
			print "__________________________________________________________\n\n";
			}

sub def_var	{
			$processes = 1;
			$max = 0;
			$entry = '';
			$InFile = '';
			$change_points = 50;
			$total_seq = 0;
			$seq_count = 0;
			$fac_max = 0;
			$time_count = 0;
			$avg_time = 0;
			$old_average = 0;
			$estimated_time = 0;
			$avg_format = '';
			$avg_print_time = 0;
			$est_print_time = 0;	
			$est_format = '';
			$state_count = 0;
			%states = ();
			$process_count = 0;
			}
			
sub initiate_run	{
					my $num = int(rand(999999999999999999));
					$directory = "\/tmp\/BaySeg\_$num";
					system "mkdir $directory";
					&def_var;	
					&determine_opts;
					&txt_to_hash;
					&ctrl_panel;
					my $start = time;
					while ($seq_count < $total_seq)
						{
						$seq_start = time;
						my $main_pid = fork();
						if ($main_pid)
							{
							&ctrl_panel;
							print "Generating probabilities for $processes sequence(s):\n";
							waitpid($main_pid,0);
							}
						elsif ($main_pid == 0)
							{
							$process_count = 0;
							while (($process_count < $processes - 1) && ($process_count + $seq_count < $total_seq - 1))
								{
								$entry = $ordered_seq[$seq_count + $process_count];
								my $pid2 = fork();
								if ($pid2) 
									{
									# parent
									#waitpid($pid2,0);
									} 
								elsif ($pid2 == 0) 
									{
									$rev_hashname = $entry._rev_sum_prob;
									$hashname = $entry._sum_prob;
									%$rev_hashname = ();
									$seq = $seq{$entry};
									$max = length $seq;
									&fact_populate;
									&spool_threads;
									# child
									exit(0);
									}
								++$process_count;
								}
							$entry = $ordered_seq[$seq_count + $process_count];
							my $pid4 = fork();
							if ($pid4) 
								{
								# parent
								#waitpid($pid4,0);
								} 
							elsif ($pid4 == 0) 
								{
								$rev_hashname = $entry._rev_sum_prob;
								$hashname = $entry._sum_prob;
								%$rev_hashname = ();
								$seq = $seq{$entry};
								$max = length $seq;
								&fact_populate;
								&spool_threads;
								#child
								exit(0);
								}
							waitpid($pid4,0);
							exit(0);
							}
						$seq_count += $processes;
						$seq_end = time;
						&average_time;
						}
					my $end = time;
					my $time = $end - $start;
					print "Analysis complete: Runtime = $time seconds\n\n";	
					system "rm -rf $directory";	
					}

&initiate_run;

exit;

