File Coverage

lib/Suffix/Trie.pm
Criterion Covered Total %
statement 67 70 95.7
branch 7 10 70.0
condition 1 2 50.0
subroutine 14 15 93.3
pod 0 3 0.0
total 89 100 89.0


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3             # Native perl Suffix Trie
4             # Code borrowed and modified from:
5             # https://rosettacode.org/wiki/Suffix_tree#Perl
6             # Author: Lee Katz
7              
8             package Suffix::Trie;
9             require 5.12.0;
10             our $VERSION=0.1;
11              
12 1     1   695 use strict;
  1         3  
  1         28  
13 1     1   5 use warnings;
  1         2  
  1         28  
14              
15 1     1   7 use File::Basename qw/basename fileparse dirname/;
  1         2  
  1         99  
16 1     1   7 use File::Temp qw/tempdir tempfile/;
  1         2  
  1         71  
17 1     1   7 use Data::Dumper qw/Dumper/;
  1         1  
  1         47  
18 1     1   543 use List::MoreUtils qw/uniq/;
  1         12054  
  1         6  
19              
20 1     1   1015 use Exporter qw/import/;
  1         3  
  1         816  
21             our @EXPORT_OK = qw(
22             @fastqExt @fastaExt
23             );
24              
25             our @fastqExt=qw(.fastq.gz .fastq .fq .fq.gz);
26             our @fastaExt=qw(.fasta .fna .faa .mfa .fas .fa);
27              
28             # TODO if 'die' is imported by a script, redefine
29             # sig die in that script as this function.
30             local $SIG{'__DIE__'} = sub { my $e = $_[0]; $e =~ s/(at [^\s]+? line \d+\.$)/\nStopped $1/; die("$0: ".(caller(1))[3].": ".$e); };
31              
32             =pod
33              
34             =head1 NAME
35              
36             Suffix::Trie
37              
38             =head1 SYNOPSIS
39              
40             A module for pure Perl Suffix Trie. Core code taken from https://rosettacode.org/wiki/Suffix_tree#Perl
41              
42             use strict;
43             use warnings;
44             use Data::Dumper;
45             use Suffix::Trie;
46              
47             my $trie = Suffix::Trie->new("mississippi");
48             # Get all substrings into an array reference
49             print Dumper $trie->suffixes();
50             # Get the actual trie in a hash reference
51             print Dumper $trie->trie;
52              
53             =cut
54              
55             sub new{
56 3     3 0 576 my($class,$str,$settings)=@_;
57              
58             # Initialize the object and then bless it
59 3         9 my $self={
60             str => $str,
61             trie => undef,
62             suffixes => undef,
63             };
64              
65 3         6 bless($self);
66              
67 3         7 $self->_create_trie();
68              
69 3         22 return $self;
70             }
71              
72             # Some getters
73             sub trie{
74 0     0 0 0 my($self)=@_;
75 0         0 return $self->{trie};
76             }
77             sub suffixes{
78 3     3 0 27 my($self)=@_;
79 3 50       9 if(defined $self->{suffixes}){
80 0         0 return $self->{suffixes};
81             }
82              
83             # recurse into the trie and get all keys
84 3         5 my @keys;
85 3         9 _nestedKeys($self->{trie},\@keys);
86 3         90 @keys = sort {$a cmp $b} uniq(@keys);
  617         715  
87 3         14 $self->{suffixes} = \@keys;
88 3         20 return $self->{suffixes};
89             }
90             sub _nestedKeys{
91 272     272   371 my($hashRef, $keys)=@_;
92 272   50     392 $keys //= [];
93 272         556 for my $key(keys(%$hashRef)){
94 269         378 push(@$keys, $key);
95 269         383 _nestedKeys($$hashRef{$key}, $keys);
96             }
97             }
98              
99             sub _create_trie{
100 3     3   6 my($self)=@_;
101 3         6 my $str = $self->{str};
102              
103             # ensure that the string ends in a $
104 3         36 $str=~s/\$*$/\$/;
105            
106             # Test for extraneous $
107 3         7 my $testStr=substr($str, 0, -1); # leaves out the last character
108 3 50       12 die "ERROR: found a dollar sign in the string" if($testStr=~/\$/);
109 3         6 $self->{trie} = _suffix_trie(_suffixHash($str));
110             }
111              
112             # https://rosettacode.org/wiki/Suffix_tree#Perl
113             sub _classify{
114 118     118   164 my $h = {};
115 118         179 for (@_) { push @{$h->{substr($_,0,1)}}, $_ }
  719         800  
  719         1355  
116 118         184 return $h;
117             }
118             # https://rosettacode.org/wiki/Suffix_tree#Perl
119             # TODO expose this function
120             # TODO return list of strings or hash of strings
121             sub _suffixHash{
122 3     3   6 my $str = shift;
123 3         19 map { substr $str, $_ } 0 .. length($str) - 1;
  170         289  
124             }
125             # https://rosettacode.org/wiki/Suffix_tree#Perl
126             sub _suffix_trie {
127 288 50   288   569 return +{} if @_ == 0;
128 288 100       702 return +{ $_[0] => +{} } if @_ == 1;
129 118         173 my $h = {};
130 118         180 my $classif = _classify @_;
131 118         246 for my $key (keys %$classif) {
132             my $subtree = _suffix_trie(
133 285         376 map { substr $_, 1 } @{$classif->{$key}}
  719         1453  
  285         419  
134             );
135 285         652 my @subkeys = keys %$subtree;
136 285 100       458 if (@subkeys == 1) {
137 186         259 my ($subkey) = @subkeys;
138 186         607 $h->{"$key$subkey"} = $subtree->{$subkey};
139 99         181 } else { $h->{$key} = $subtree }
140             }
141 118         266 return $h;
142             }
143              
144              
145             1;