<commit_before>use v6;

class Bio::Tools::CodonTable;
our %codons;
our %trcol;
our %iub;

#BEGIN { 
#    #%IUPAC_DNA = Bio::Tools::IUPAC->iupac_iub();    
#    #%IUPAC_AA = Bio::Tools::IUPAC->iupac_iup();
#    #%THREELETTERSYMBOLS = Bio::SeqUtils->valid_aa(2);
#    #$VALID_PROTEIN = '['.join('',Bio::SeqUtils->valid_aa(0)).']';
#}

INIT {
    my @nucs = <t c a g>;
    my $x = 0;
    our %codons;
    our %trcol;
    
    for @nucs -> $i {
        for @nucs -> $j {
            for @nucs -> $k {
                my $codon = "$i$j$k";
                %codons{$codon} = $x;
                %trcol{$x} = $codon;
                $x++;
            }
        }
    }
    use Bio::Tools::IUPAC;
    our %iub = %Bio::Tools::IUPAC::IUB;
}

# first set internal values for all translation tables

# has Str $.gap is rw where {$_.chars == 1} = '-';
has Str $.gap = '-';
    
#has Str $.terminator is rw where { $_.chars == 1 } = '*';
has Str $.terminator is rw = '*';    

#has $.CODONGAP = $GAP x CODONSIZE;
has $.CODONGAP is rw = '---';
#constant NYI    
has Int $.CODONSIZE = 3 ;

has $.id is rw = 1;

# thinking these could go into a simple basic data class
#constant NYI    
has @!NAMES = (
    'Standard',        #1
    'Vertebrate Mitochondrial',#2
    'Yeast Mitochondrial',# 3
    'Mold, Protozoan, and CoelenterateMitochondrial and Mycoplasma/Spiroplasma',#4
    'Invertebrate Mitochondrial',#5
    'Ciliate, Dasycladacean and Hexamita Nuclear',# 6
    '', '',
    'Echinoderm Mitochondrial',#9
    'Euplotid Nuclear',#10
    '"Bacterial"',# 11
    'Alternative Yeast Nuclear',# 12
    'Ascidian Mitochondrial',# 13
    'Flatworm Mitochondrial',# 14
    'Blepharisma Nuclear',# 15
    'Chlorophycean Mitochondrial',# 16
    '', '',  '', '',
    'Trematode Mitochondrial',# 21
    'Scenedesmus obliquus Mitochondrial', #22
    'Thraustochytrium Mitochondrial'  #23
);

has @!TABLES = <
    FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNKKSS**VVVVAAAADDEEGGGG
    FFLLSSSSYY**CCWWTTTTPPPPHHQQRRRRIIMMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNKKSSSSVVVVAAAADDEEGGGG
    FFLLSSSSYYQQCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    '' ''
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNNKSSSSVVVVAAAADDEEGGGG
    FFLLSSSSYY**CCCWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CC*WLLLSPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNKKSSGGVVVVAAAADDEEGGGG
    FFLLSSSSYYY*CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNNKSSSSVVVVAAAADDEEGGGG
    FFLLSSSSYY*QCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY*LCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    '' '' '' ''
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNNKSSSSVVVVAAAADDEEGGGG   
    FFLLSS*SYY*LCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FF*LSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
>;

has @!STARTS = <
    ---M---------------M---------------M----------------------------
    --------------------------------MMMM---------------M------------
    ----------------------------------MM----------------------------
    --MM---------------M------------MMMM---------------M------------
    ---M----------------------------MMMM---------------M------------
    -----------------------------------M----------------------------
    '' ''
    -----------------------------------M----------------------------
    -----------------------------------M----------------------------
    ---M---------------M------------MMMM---------------M------------
    -------------------M---------------M----------------------------
    -----------------------------------M----------------------------
    -----------------------------------M----------------------------
    -----------------------------------M----------------------------
    -----------------------------------M----------------------------
    '' ''  '' ''
    -----------------------------------M---------------M------------  
    -----------------------------------M----------------------------
    --------------------------------M--M---------------M------------
>;



method name() {
    #minus one since array starts at zero
    my ($id) = self.id - 1;
    return @!NAMES[$id];
}

method tables() {
  my %tables;
  for  1 .. @!NAMES ->  $id {
    my $name = @!NAMES[$id-1];
    %tables{$id} = $name if $name;
  }
  return %tables;
}


method translate($seq is copy,
                :$terminator? is copy,
                :$unknown is copy) {
    #    my ($self, $seq) = @_;
    #    $self->throw("Calling translate without a seq argument!") unless defined $seq;
    return '' unless $seq;

    my $id = self.id;
    my ($partial) = 0;
    $partial = 2 if $seq.chars() % $.CODONSIZE == 2;
    
    # TODO: should the standard be uc or lc?  This is pretty inconsistent...
    $seq = lc $seq;
    $seq = $seq.trans('u' => 't');
    
    my $protein = "";
    
    # TODO: lots of redundant code here!
    
    # TODO: some funkiness with negative ranges and variable interpolation
    if $seq ~~ /<-[actg]>/  { #ambiguous chars
        loop (my $i = 0; $i < ($seq.chars - ($.CODONSIZE -1)); $i+=$.CODONSIZE) {
            my $triplet = substr($seq, $i, $.CODONSIZE);
            if $triplet eq $.CODONGAP {
                $protein ~= $.gap;
            }
            elsif  %codons{$triplet}:exists {
                $protein ~= substr(@!TABLES[$id-1], %codons{$triplet}, 1);
            } else {
                $protein ~= self!translate_ambiguous_codon($triplet);                
            }
        }
        
    } else { # simple, strict translation
        loop (my $i = 0; $i < ($seq.chars - ($.CODONSIZE -1)); $i+=$.CODONSIZE) {
            my $triplet = substr($seq, $i, $.CODONSIZE); 
            if $triplet eq $.CODONGAP {
                $protein ~= $.gap;
            }
            if  %codons{$triplet}:exists {
                $protein ~= substr(@!TABLES[$id-1], %codons{$triplet}, 1);
            } else {
                $protein ~= 'X';
            }
        }
    }

    #if $partial == 2 { # 2 overhanging nucleotides
    #    my $triplet = $seq ~ "n";
    #    
    #    if $triplet eq $.CODONGAP {
    #        $protein ~= $.gap;
    #    }
    #    if  %codons{$triplet}:exists {
    #        $protein ~= substr(@!TABLES[$id-1], %codons{$triplet}, 1);
    #    } else {
    #        $protein ~= self!translate_ambiguous_codon($triplet,$partial);                
    #    }
    #}

    return $protein;
}

method revtranslate($value is copy,$coding?){
    my ($id) = self.id;
    my (@aas,  $p);
    my (@codons) = ();

    if ($value.chars == 3 ) {
        $value = lc $value;
        $value = $value.tc;
        #hash comes from SeqUtil.pm crap!
#         $value = %THREELETTERSYMBOLS{$value};
     }
   # if ( defined $value and $value ~~ /$VALID_PROTEIN/ 
   #        and $value.chars == 1 ) {
   #      $value = uc $value;
   #      @aas = @{$IUPAC_AA{$value}};    
   #      foreach my $aa (@aas) {
   #          #print $aa, " -2\n";
   #          $aa = '\*' if $aa eq '*';
   #        while ($TABLES[$id-1] =~ m/$aa/g) {
   #            $p = pos $TABLES[$id-1];
   #            push (@codons, $TRCOL->{--$p});
   #        }
   #      }
   #  }

   # if ($coding and uc ($coding) eq 'RNA') {
   #     for my $i (0..$#codons)  {
   #        $codons[$i] =~ tr/t/u/;
   #     }
   # }
    
   return @codons;    
}

method is_start_codon($value is copy) {
   my $id = self.id;

   $value  = lc $value;
   $value =$value.trans('u'=>'t');

   if ( $value.chars != 3  )  {
       return 0;
   }
   else {
       my $result = 1;
       my @ms = map { substr(@!STARTS[$id-1],%codons{$_},1) }, self!unambiquous_codons($value);
       for @ms -> $c {
           $result = 0 if $c ne 'M';
       }
       return $result;
   }    
}

method is_ter_codon($value is copy) {
    my $id = self.id;

    $value  = lc $value;
    $value  =$value.trans('u' =>'t');

    if ( $value.chars != 3  )  {
        return 0;
    }
    else {
        my $result = 1;
        my @ms = map { substr(@!TABLES[$id-1],%codons{$_},1) }, self!unambiquous_codons($value);
        for @ms -> $c {
            $result = 0 if $c ne $.terminator;
        }
        return $result;
    }    
}


method is_unknown_codon($value is copy) {
   my $id = self.id;

   $value  = lc $value;
   $value  = $value.trans('u' => 't');

   if ( $value.chars != 3  )  {
       return 1;
   }
   else {
       my $result = 0;
       my @cs = map { substr(@!TABLES[$id-1],%codons{$_},1) }, self!unambiquous_codons($value);
       $result = 1 if @cs.elems == 0;
       return $result;
   }    
}

method translate_strict($value is copy) {
   my ($id) = self.id;

   $value  = lc $value;
   $value  = $value.trans('u' => 't');

   if ($value.chars != 3 ) {
       return '';
   }
   elsif (!(defined %codons{$value}))  {
       return 'X';
   }
   else {
       return substr(@!TABLES[$id-1],%codons{$value},1);
   }    
}

method add_table($table,$name? = 'Custom' ~ @!NAMES.elems +1 ,$starts? = @!STARTS[0]) {
    #where { $table.chars == 64 and $starts.length == 64   } 
    #some reason getting a syntax error when adding where clause

     push @!NAMES, $name;
     push @!TABLES, $table;
     push @!STARTS, $starts;

    return @!NAMES.elems;    
}

method reverse_translate_all(*@params) {
    return 'NYI';
}

method !translate_ambiguous_codon($triplet, $partial? = 0) {
    my $id = self.id;
    my $aa='';
    my @codons = self!unambiquous_codons($triplet);
    my %aas;
    
    for @codons -> $codon {
        %aas{substr(@!TABLES[$id-1],%codons{$codon},1)} = 1;
    }
    my $count =  %aas.keys.elems;
    if $count == 1  {
        $aa = (%aas.keys)[0];
    }
    elsif $count == 2 {
        if (%aas{'D'} and %aas{'N'}) {
            $aa = 'B';
         }
        elsif (%aas{'E'} and %aas{'Q'}) {
            $aa = 'Z';
        } else {
            $partial ?? ($aa = '') !! ($aa = 'X');
        }
    } else {
        $partial ?? ($aa = '') !!  ($aa = 'X');
    }
    return $aa;
}


method !unambiquous_codons($value) {
    my @nts;
    my @codons;
    my ($i, $j, $k);
    @nts = map { %iub{uc $_}} , $value.comb();
        for @(@nts[0]) ->  $i {
            #hack for now, since @nts has Any() values. Normally in p5 code you would have undef which would not loop at all
            last if !defined $i;
            for @(@nts[1]) -> $j {
                for @(@nts[2]) -> $k {
                    @codons.push(lc "$i$j$k");
                }
            }
        }
    return @codons;
}

<commit_msg>add comment with masak's translate one-liner<commit_after>use v6;

class Bio::Tools::CodonTable;
our %codons;
our %trcol;
our %iub;

# my $dna = "ttaagg"; sub translate($dna) { "FFLLSSSSYY!!CC!WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG".comb[map { :4($_) }, $dna.trans("tcag" => "0123").comb(/.../)] }; say translate($dna)

#BEGIN { 
#    #%IUPAC_DNA = Bio::Tools::IUPAC->iupac_iub();    
#    #%IUPAC_AA = Bio::Tools::IUPAC->iupac_iup();
#    #%THREELETTERSYMBOLS = Bio::SeqUtils->valid_aa(2);
#    #$VALID_PROTEIN = '['.join('',Bio::SeqUtils->valid_aa(0)).']';
#}

INIT {
    my @nucs = <t c a g>;
    my $x = 0;
    our %codons;
    our %trcol;
    
    for @nucs -> $i {
        for @nucs -> $j {
            for @nucs -> $k {
                my $codon = "$i$j$k";
                %codons{$codon} = $x;
                %trcol{$x} = $codon;
                $x++;
            }
        }
    }
    use Bio::Tools::IUPAC;
    our %iub = %Bio::Tools::IUPAC::IUB;
}

# first set internal values for all translation tables

# has Str $.gap is rw where {$_.chars == 1} = '-';
has Str $.gap = '-';
    
#has Str $.terminator is rw where { $_.chars == 1 } = '*';
has Str $.terminator is rw = '*';    

#has $.CODONGAP = $GAP x CODONSIZE;
has $.CODONGAP is rw = '---';
#constant NYI    
has Int $.CODONSIZE = 3 ;

has $.id is rw = 1;

# thinking these could go into a simple basic data class
#constant NYI    
has @!NAMES = (
    'Standard',        #1
    'Vertebrate Mitochondrial',#2
    'Yeast Mitochondrial',# 3
    'Mold, Protozoan, and CoelenterateMitochondrial and Mycoplasma/Spiroplasma',#4
    'Invertebrate Mitochondrial',#5
    'Ciliate, Dasycladacean and Hexamita Nuclear',# 6
    '', '',
    'Echinoderm Mitochondrial',#9
    'Euplotid Nuclear',#10
    '"Bacterial"',# 11
    'Alternative Yeast Nuclear',# 12
    'Ascidian Mitochondrial',# 13
    'Flatworm Mitochondrial',# 14
    'Blepharisma Nuclear',# 15
    'Chlorophycean Mitochondrial',# 16
    '', '',  '', '',
    'Trematode Mitochondrial',# 21
    'Scenedesmus obliquus Mitochondrial', #22
    'Thraustochytrium Mitochondrial'  #23
);

has @!TABLES = <
    FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNKKSS**VVVVAAAADDEEGGGG
    FFLLSSSSYY**CCWWTTTTPPPPHHQQRRRRIIMMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNKKSSSSVVVVAAAADDEEGGGG
    FFLLSSSSYYQQCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    '' ''
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNNKSSSSVVVVAAAADDEEGGGG
    FFLLSSSSYY**CCCWLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CC*WLLLSPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNKKSSGGVVVVAAAADDEEGGGG
    FFLLSSSSYYY*CCWWLLLLPPPPHHQQRRRRIIIMTTTTNNNKSSSSVVVVAAAADDEEGGGG
    FFLLSSSSYY*QCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FFLLSSSSYY*LCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    '' '' '' ''
    FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNNKSSSSVVVVAAAADDEEGGGG   
    FFLLSS*SYY*LCC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
    FF*LSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG
>;

has @!STARTS = <
    ---M---------------M---------------M----------------------------
    --------------------------------MMMM---------------M------------
    ----------------------------------MM----------------------------
    --MM---------------M------------MMMM---------------M------------
    ---M----------------------------MMMM---------------M------------
    -----------------------------------M----------------------------
    '' ''
    -----------------------------------M----------------------------
    -----------------------------------M----------------------------
    ---M---------------M------------MMMM---------------M------------
    -------------------M---------------M----------------------------
    -----------------------------------M----------------------------
    -----------------------------------M----------------------------
    -----------------------------------M----------------------------
    -----------------------------------M----------------------------
    '' ''  '' ''
    -----------------------------------M---------------M------------  
    -----------------------------------M----------------------------
    --------------------------------M--M---------------M------------
>;



method name() {
    #minus one since array starts at zero
    my ($id) = self.id - 1;
    return @!NAMES[$id];
}

method tables() {
  my %tables;
  for  1 .. @!NAMES ->  $id {
    my $name = @!NAMES[$id-1];
    %tables{$id} = $name if $name;
  }
  return %tables;
}


method translate($seq is copy,
                :$terminator? is copy,
                :$unknown is copy) {
    #    my ($self, $seq) = @_;
    #    $self->throw("Calling translate without a seq argument!") unless defined $seq;
    return '' unless $seq;

    my $id = self.id;
    my ($partial) = 0;
    $partial = 2 if $seq.chars() % $.CODONSIZE == 2;
    
    # TODO: should the standard be uc or lc?  This is pretty inconsistent...
    $seq = lc $seq;
    $seq = $seq.trans('u' => 't');
    
    my $protein = "";
    
    # TODO: lots of redundant code here!
    
    # TODO: some funkiness with negative ranges and variable interpolation
    if $seq ~~ /<-[actg]>/  { #ambiguous chars
        loop (my $i = 0; $i < ($seq.chars - ($.CODONSIZE -1)); $i+=$.CODONSIZE) {
            my $triplet = substr($seq, $i, $.CODONSIZE);
            if $triplet eq $.CODONGAP {
                $protein ~= $.gap;
            }
            elsif  %codons{$triplet}:exists {
                $protein ~= substr(@!TABLES[$id-1], %codons{$triplet}, 1);
            } else {
                $protein ~= self!translate_ambiguous_codon($triplet);                
            }
        }
        
    } else { # simple, strict translation
        loop (my $i = 0; $i < ($seq.chars - ($.CODONSIZE -1)); $i+=$.CODONSIZE) {
            my $triplet = substr($seq, $i, $.CODONSIZE); 
            if $triplet eq $.CODONGAP {
                $protein ~= $.gap;
            }
            if  %codons{$triplet}:exists {
                $protein ~= substr(@!TABLES[$id-1], %codons{$triplet}, 1);
            } else {
                $protein ~= 'X';
            }
        }
    }

    #if $partial == 2 { # 2 overhanging nucleotides
    #    my $triplet = $seq ~ "n";
    #    
    #    if $triplet eq $.CODONGAP {
    #        $protein ~= $.gap;
    #    }
    #    if  %codons{$triplet}:exists {
    #        $protein ~= substr(@!TABLES[$id-1], %codons{$triplet}, 1);
    #    } else {
    #        $protein ~= self!translate_ambiguous_codon($triplet,$partial);                
    #    }
    #}

    return $protein;
}

method revtranslate($value is copy,$coding?){
    my ($id) = self.id;
    my (@aas,  $p);
    my (@codons) = ();

    if ($value.chars == 3 ) {
        $value = lc $value;
        $value = $value.tc;
        #hash comes from SeqUtil.pm crap!
#         $value = %THREELETTERSYMBOLS{$value};
     }
   # if ( defined $value and $value ~~ /$VALID_PROTEIN/ 
   #        and $value.chars == 1 ) {
   #      $value = uc $value;
   #      @aas = @{$IUPAC_AA{$value}};    
   #      foreach my $aa (@aas) {
   #          #print $aa, " -2\n";
   #          $aa = '\*' if $aa eq '*';
   #        while ($TABLES[$id-1] =~ m/$aa/g) {
   #            $p = pos $TABLES[$id-1];
   #            push (@codons, $TRCOL->{--$p});
   #        }
   #      }
   #  }

   # if ($coding and uc ($coding) eq 'RNA') {
   #     for my $i (0..$#codons)  {
   #        $codons[$i] =~ tr/t/u/;
   #     }
   # }
    
   return @codons;    
}

method is_start_codon($value is copy) {
   my $id = self.id;

   $value  = lc $value;
   $value =$value.trans('u'=>'t');

   if ( $value.chars != 3  )  {
       return 0;
   }
   else {
       my $result = 1;
       my @ms = map { substr(@!STARTS[$id-1],%codons{$_},1) }, self!unambiquous_codons($value);
       for @ms -> $c {
           $result = 0 if $c ne 'M';
       }
       return $result;
   }    
}

method is_ter_codon($value is copy) {
    my $id = self.id;

    $value  = lc $value;
    $value  =$value.trans('u' =>'t');

    if ( $value.chars != 3  )  {
        return 0;
    }
    else {
        my $result = 1;
        my @ms = map { substr(@!TABLES[$id-1],%codons{$_},1) }, self!unambiquous_codons($value);
        for @ms -> $c {
            $result = 0 if $c ne $.terminator;
        }
        return $result;
    }    
}


method is_unknown_codon($value is copy) {
   my $id = self.id;

   $value  = lc $value;
   $value  = $value.trans('u' => 't');

   if ( $value.chars != 3  )  {
       return 1;
   }
   else {
       my $result = 0;
       my @cs = map { substr(@!TABLES[$id-1],%codons{$_},1) }, self!unambiquous_codons($value);
       $result = 1 if @cs.elems == 0;
       return $result;
   }    
}

method translate_strict($value is copy) {
   my ($id) = self.id;

   $value  = lc $value;
   $value  = $value.trans('u' => 't');

   if ($value.chars != 3 ) {
       return '';
   }
   elsif (!(defined %codons{$value}))  {
       return 'X';
   }
   else {
       return substr(@!TABLES[$id-1],%codons{$value},1);
   }    
}

method add_table($table,$name? = 'Custom' ~ @!NAMES.elems +1 ,$starts? = @!STARTS[0]) {
    #where { $table.chars == 64 and $starts.length == 64   } 
    #some reason getting a syntax error when adding where clause

     push @!NAMES, $name;
     push @!TABLES, $table;
     push @!STARTS, $starts;

    return @!NAMES.elems;    
}

method reverse_translate_all(*@params) {
    return 'NYI';
}

method !translate_ambiguous_codon($triplet, $partial? = 0) {
    my $id = self.id;
    my $aa='';
    my @codons = self!unambiquous_codons($triplet);
    my %aas;
    
    for @codons -> $codon {
        %aas{substr(@!TABLES[$id-1],%codons{$codon},1)} = 1;
    }
    my $count =  %aas.keys.elems;
    if $count == 1  {
        $aa = (%aas.keys)[0];
    }
    elsif $count == 2 {
        if (%aas{'D'} and %aas{'N'}) {
            $aa = 'B';
         }
        elsif (%aas{'E'} and %aas{'Q'}) {
            $aa = 'Z';
        } else {
            $partial ?? ($aa = '') !! ($aa = 'X');
        }
    } else {
        $partial ?? ($aa = '') !!  ($aa = 'X');
    }
    return $aa;
}


method !unambiquous_codons($value) {
    my @nts;
    my @codons;
    my ($i, $j, $k);
    @nts = map { %iub{uc $_}} , $value.comb();
        for @(@nts[0]) ->  $i {
            #hack for now, since @nts has Any() values. Normally in p5 code you would have undef which would not loop at all
            last if !defined $i;
            for @(@nts[1]) -> $j {
                for @(@nts[2]) -> $k {
                    @codons.push(lc "$i$j$k");
                }
            }
        }
    return @codons;
}


{'fraction_non_alphanumeric': 0.24480773658761226, 'fraction_numerical': 0.009256274464655767, 'mean_word_length': 1.5982292414453219, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4598276', 'n_tokens_mistral': 8316, 'n_tokens_neox': 7763, 'n_words': 1546}