File Coverage

blib/lib/String/Eertree.pm
Criterion Covered Total %
statement 70 70 100.0
branch 14 16 87.5
condition 5 6 83.3
subroutine 14 14 100.0
pod 2 8 25.0
total 105 114 92.1


line stmt bran cond sub pod time code
1             package String::Eertree;
2 6     6   1008532 use warnings;
  6         33  
  6         206  
3 6     6   36 use strict;
  6         10  
  6         144  
4              
5 6     6   3230 use Syntax::Construct qw{ // };
  6         17759  
  6         39  
6              
7 6     6   3687 use Moo;
  6         58291  
  6         34  
8              
9 6     6   10479 use String::Eertree::Node;
  6         21  
  6         5776  
10              
11             has nodes => (is => 'ro', default => sub { [
12             'String::Eertree::Node'->new(link => 0, length => -1, pos => -1),
13             'String::Eertree::Node'->new(link => 0, length => 0, pos => 0)
14             ]});
15             has string => (is => 'ro', required => 1);
16             has max => (is => 'rwp', default => 0);
17             has _count_finished => (is => 'rw', default => 0);
18              
19             sub node {
20 1486     1486 0 2232 my ($self, $index) = @_;
21 1486 50       2423 die "Invalid index $index." if $index < 0;
22              
23 1486         4943 return $self->nodes->[$index]
24             }
25              
26             sub at {
27 468     468 0 685 my ($self, $pos) = @_;
28 468         1628 return substr $self->string, $pos, 1
29             }
30              
31             sub BUILD {
32 16     16 0 8944 my ($self) = @_;
33 16         39 my $i = 0;
34 16         158 $self->add($i++, $_) for split //, $self->string;
35             };
36              
37             sub Push {
38 194     194 0 317 my ($self, $node) = @_;
39 194         247 push @{ $self->nodes }, $node;
  194         465  
40             }
41              
42 392     392 0 500 sub Last { $#{ $_[0]->nodes } }
  392         3837  
43              
44             sub add {
45 200     200 0 384 my ($self, $index, $char) = @_;
46              
47 200         277 my $new_node;
48 200         335 my $p = $self->max;
49 200         362 while ($self->node($p)) {
50 421         691 my $node = $self->node($p);
51 421 100       920 my $pos = $node->length == -1
52             ? $index
53             : $index - $node->length - 1;
54 421 100 100     891 if ($pos >= 0 && $self->at($pos) eq $char) {
55 200 100       3336 if (exists $node->edge->{$char}) {
56 6         122 $new_node = $self->node($node->edge->{$char});
57 6         20 $new_node->increment_count;
58 6         93 $self->_set_max($node->edge->{$char});
59             return
60 6         60 }
61 194         3323 $new_node = 'String::Eertree::Node'->new(
62             pos => $pos,
63             length => $index - $pos + 1);
64 194         4094 $node->edge->{$char} = $self->Last + 1;
65             last
66 194         1754 }
67 221         453 $p = $node->link;
68             }
69              
70 194         477 $self->Push($new_node);
71 194         335 $self->_set_max($self->Last);
72              
73 194 100       459 if ($new_node->length == 1) {
74 62         109 $new_node->_set_link(1);
75             return
76 62         186 }
77              
78 132         243 my $q = $self->node($p)->link;
79 132         199 while (1) {
80 166 100       266 my $pos = $self->node($q)->length == -1
81             ? $index
82             : $index - $self->node($q)->length - 1;
83 166 100 66     438 if ($pos >= 0 && $self->at($pos) eq $char) {
84 132         246 $new_node->_set_link($self->node($q)->edge->{$char});
85             last
86 132         1246 }
87 34         71 $q = $self->node($q)->link;
88             }
89             }
90              
91             sub uniq_palindromes {
92 10     10 1 37 my ($self) = @_;
93 10         15 return grep length, map $_->string($self), @{ $self->nodes }
  10         32  
94             }
95              
96             sub palindromes {
97 2     2 1 11 my ($self) = @_;
98 2         6 $self->_count;
99             return map {
100 20         45 grep length, ($_->string($self)) x $_->count
101 2         3 } @{ $self->nodes }
  2         5  
102             }
103              
104             sub _count {
105 2     2   5 my ($self) = @_;
106 2 50       8 return if $self->_count_finished;
107              
108 2         6 $self->_count_finished(1);
109 2         13 for my $node (reverse @{ $self->nodes }) {
  2         9  
110 20         39 $self->node($node->link)->increment_count($node->count);
111             }
112             }
113              
114             =head1 NAME
115              
116             String::Eertree - Build the palindromic tree aka Eertree for a string
117              
118             =head1 VERSION
119              
120             Version 0.02
121              
122             =cut
123              
124             our $VERSION = '0.02';
125              
126             =head1 SYNOPSIS
127              
128             Eertrees make it possible to find palindrome substrings of a string in a very
129             fast way.
130              
131             use String::Eertree;
132              
133             my $tree = 'String::Eertree'->new(string => 'referee');
134             my @palindromes = $tree->uniq_palindromes; # r e f efe refer ere ee
135              
136             =head1 METHODS
137              
138             =head2 new
139              
140             'String::Eertree'->new(string => 'xxx')
141              
142             The constructor. Use the named argument C to specify the string you
143             want to analyse.
144              
145             =head2 string
146              
147             my $string = $tree->string;
148              
149             The original string the tree was constructed from (see above).
150              
151             =head2 uniq_palindromes
152              
153             my @palindromes = $tree->uniq_palindromes;
154              
155             Returns all distinct palindrome substrings of the string.
156              
157             =head2 palindromes
158              
159             my @palindromes = $tree->palindromes;
160              
161             Returns all the palindrome substrings of the string, each substring can be
162             repeated if it's present at different positions in the string.
163              
164             =head1 AUTHOR
165              
166             E. Choroba, C<< >>
167              
168             =head1 BUGS
169              
170             Please report any bugs or feature requests to C, or through
171             the web interface at L. I will be notified, and then you'll
172             automatically be notified of progress on your bug as I make changes.
173              
174             =head1 SUPPORT
175              
176             You can find documentation for this module with the perldoc command.
177              
178             perldoc String::Eertree
179              
180              
181             You can also look for information at:
182              
183             =over 4
184              
185             =item * RT: CPAN's request tracker (report bugs here)
186              
187             L
188              
189             =item * CPAN Ratings
190              
191             L
192              
193             =item * Search CPAN
194              
195             L
196              
197             =back
198              
199             =head1 ACKNOWLEDGEMENTS
200              
201             Thanks Mohammad S Anwar (MANWAR) for introducing me to the idea.
202              
203             Thanks L for a clean Python
204             implementation.
205              
206             Thanks Mikhail Rubinchik and Arseny M. Shur for inventing the eertree
207             (arXiv:1506.04862v2 [cs.DS] 17 Aug 2015).
208              
209             =head1 LICENSE AND COPYRIGHT
210              
211             This software is Copyright (c) 2022 by E. Choroba.
212              
213             This is free software, licensed under:
214              
215             The Artistic License 2.0 (GPL Compatible)
216              
217             =cut
218              
219             __PACKAGE__