#!/usr/bin/perl

main();

sub main{
    Set_Parameters();
    Generate_Population();
    $loop_counter = 0;
    while($Top < 100){
        $loop_counter++;
        Score_Flibs();
        Identify_Flibs();
        Breed();
        Mutate();
    }
}

sub Set_Parameters{
    $score_run = 100;
    $target_sequence = "010011";
    $periods = length($target_sequence);
    $numsyms = 0;
    @symbols = split(//g, $target_sequence);
    for($i = 0; $i < @symbols; $i++){
        if($symbols[$i] > $numsyms){
            $numsyms = $symbols[$i];
        }
    }
    $numsyms++;
    @e = split(//g, $target_sequence);
    $states = 4;
    $noflibs = 10;
    $BreedingRate = 30;
}

sub Generate_Population{
    for($i = 0; $i<$noflibs; $i++){
        for($x = 1; $x <= (($numsyms * 2) * $states)/2; $x++){
            push(@{flib.$i}, int(rand($numsyms)));
            push(@{flib.$i}, int(rand($states)));
        }
    }
}

sub Score_Flibs{
    for($i = 0; $i < $noflibs; $i++){
        $Score[$i] = 0;
        $State[$i] = 0;
    }
    
    for($Outer = 0; $Outer < $score_run; $Outer++){
        $CurrentSymbol = $e[$Outer % $periods];
        if(($Outer % $periods) < ($periods - 1)){
            $NextSymbol = $e[($Outer % $periods) + 1];
        }
        else
        {
            $NextSymbol = $e[0];
        }
        
        for($Inner = 0; $Inner < $noflibs; $Inner++){
            $locus = (($numsyms * 2) * $State[$Inner]) + (2 * $CurrentSymbol);
            if(${flib.$Inner}[$locus] == $NextSymbol){
                $Score[$Inner]++;
            }
            $State[$Inner] = ${flib.$Inner}[$locus+1];
        }
    }
}

sub Identify_Flibs{
    $Top = 0;
    $Bottom = 100;
    for($i = 0; $i < $noflibs; $i++){
        if($Top < $Score[$i]){
            $Top = $Score[$i];
            $TopIndex = $i;
        }
        if($Bottom > $Score[$i]){
            $Bottom = $Score[$i];
            $BottomIndex = $i;
        }
    }
    if ($Top > $PreviousScore){
        print "$loop_counter $Top ";
        for($i = 0; $i < @{flib.$TopIndex}; $i+=2){
            print ${flib.$TopIndex}[$i];
            print chr(${flib.$TopIndex}[$i+1]+65);
        }
        print "\n";
        $PreviousScore = $Top;
    }
}

sub Breed{
    if(int(rand(101)) <= $BreedingRate){ 
        $RandomFlib = int(rand($noflibs));
        while($RandomFlib == $TopIndex){
            $RandomFlib = int(rand($noflibs));
        }
        $c1 = int(rand((($numsyms * 2) * $states))); 
        $c2 = int(rand((($numsyms * 2) * $states)));
        while($c1 == $c2){ 
                $c2 = int(rand((($numsyms * 2) * $states)));
            }
        if($c1 > $c2){ 
            $c2 += $c1;
            $c1 = $c2 - $c1;
            $c2 -= $c1;
        }
        for($i = 0; $i < $c1; $i++){ 
            ${flib.$BottomIndex}[$i] = ${flib.$RandomFlib}[$i];
        }
        for($i = $c1; $i <= $c2; $i++){ 
            ${flib.$BottomIndex}[$i] = ${flib.$TopIndex}[$i];
        }
        for($i = ($c2 + 1); $i < (($numsyms * 2) * $states); $i++){ 
            ${flib.$BottomIndex}[$i] = ${flib.$RandomFlib}[$i];
        }
    }
}    

sub Mutate{
    $RandomFlib = int(rand($noflibs));
    $RandomLocus = int(rand((($numsyms * 2) * $states)));
    if($RandomLocus % 2 == 0){ 
        ${flib.$RandomFlib}[$RandomLocus] = int(rand($numsyms));
    }
    else
    {
        ${flib.$RandomFlib}[$RandomLocus] = int(rand($states));
    }
}