File Coverage

blib/lib/Text/Brew.pm
Criterion Covered Total %
statement 87 112 77.6
branch 11 28 39.2
condition 1 2 50.0
subroutine 13 13 100.0
pod 0 1 0.0
total 112 156 71.7


line stmt bran cond sub pod time code
1             package Text::Brew;
2              
3 2     2   47190 use strict;
  2         10  
  2         123  
4 2     2   12 use warnings;
  2         4  
  2         57  
5 2     2   9 use Exporter;
  2         8  
  2         99  
6 2     2   11 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  2         4  
  2         328  
7              
8             $VERSION = '0.02';
9             @ISA = qw(Exporter);
10             @EXPORT = ();
11             @EXPORT_OK = qw(&distance);
12             %EXPORT_TAGS = ();
13              
14 2     2   12 use constant INITIAL => 'INITIAL';
  2         10  
  2         184  
15 2     2   11 use constant DEL => 'DEL';
  2         3  
  2         131  
16 2     2   11 use constant INS => 'INS';
  2         4  
  2         92  
17 2     2   11 use constant SUBST => 'SUBST';
  2         3  
  2         115  
18 2     2   9 use constant MATCH => 'MATCH';
  2         5  
  2         98  
19 2     2   24 use constant None => [];
  2         4  
  2         1884  
20              
21             sub _best {
22              
23 118     118   133 my ($sub_move,$ins_move,$del_move)=@_;
24              
25 118         103 my ($increment,$move1,$move2,$move3,$tb1,$tb2,$tb3);
26              
27 118         182 ($increment,$move1,$tb1)=@$sub_move;
28 118         148 my $cost_with_sub=$increment+$tb1->[0];
29              
30 118         165 ($increment,$move2,$tb2)=@$ins_move;
31 118         166 my $cost_with_ins=$increment+$tb2->[0];
32              
33 118         160 ($increment,$move3,$tb3)=@$del_move;
34 118         137 my $cost_with_del=$increment+$tb3->[0];
35              
36 118         136 my $best_cost=$cost_with_sub;
37 118         100 my $move=$move1;
38 118         107 my $tb=$tb1;
39              
40 118 100       191 if ($cost_with_ins < $best_cost) {
41              
42 32         25 $best_cost=$cost_with_ins;
43 32         25 $move=$move2;
44 32         35 $tb=$tb2;
45             }
46              
47 118 100       196 if ($cost_with_del < $best_cost) {
48              
49 32         25 $best_cost=$cost_with_del;
50 32         27 $move=$move3;
51 32         32 $tb=$tb3;
52             }
53              
54 118 100       210 if ($best_cost == $tb->[0]) {$move=MATCH}
  29         32  
55 118         540 return [$best_cost,$move,$tb];
56             }
57              
58             sub _edit_path {
59              
60 8     8   15 my ($string1,$string2,$refc)=@_;
61              
62 8         10 my $m=length($string1);
63 8         9 my $n=length($string2);
64              
65 8         12 my ($matchCost,$insCost,$delCost,$substCost)=@$refc;
66 8         11 my @d;
67              
68 8         27 $d[0][0]=[0,INITIAL,None];
69              
70 8         20 foreach my $i (0 .. $m-1) {
71              
72 31         40 my $sofar= $d[$i][0][0];
73              
74             # cost move tb
75 31         94 $d[$i+1][0]=[$sofar+$delCost, DEL , $d[$i][0]];
76             }
77              
78 8         15 foreach my $j (0 .. $n-1) {
79              
80 31         39 my $sofar= $d[0][$j][0];
81              
82             # cost move tb
83 31         72 $d[0][$j+1]=[$sofar+$insCost, INS , $d[0][$j]];
84             }
85              
86 8         13 foreach my $i (0 .. $m-1) {
87              
88 31         44 my $string1_i=substr($string1,$i,1);
89              
90 31         46 foreach my $j (0 .. $n-1) {
91              
92 118         180 my $string2_i=substr($string2,$j,1);
93 118         111 my $subst;
94              
95 118 100       167 if ($string1_i eq $string2_i) {
96              
97 29         37 $subst=$matchCost;
98              
99             } else {
100              
101 89         83 $subst=$substCost;
102             }
103              
104             # cost move tb
105 118         439 $d[$i+1][$j+1]=_best([$subst, SUBST , $d[$i][$j]],
106             [$insCost, INS , $d[$i+1][$j]],
107             [$delCost, DEL , $d[$i][$j+1]]);
108             }
109             }
110              
111 8         56 return $d[$m][$n];
112             }
113              
114             sub distance {
115              
116 8     8 0 8237 my ($string1,$string2,$optional_ref)=@_;
117 8         9 my $output;
118             my $cost;
119              
120 8 50       19 if ($optional_ref) {
121              
122 0 0       0 if (ref($optional_ref) ne "HASH") {
123              
124 0         0 warn "Text::Brew: options not well formed, using default";
125              
126             } else {
127              
128 0         0 foreach my $key (keys %$optional_ref) {
129              
130 0 0       0 if ($key eq "-cost") {
    0          
131              
132 0         0 $cost=$$optional_ref{'-cost'};
133 0 0       0 if (ref($cost) ne "ARRAY") {
134              
135 0         0 require Carp;
136 0         0 Carp::croak("Text::Brew: -cost option requires an array");
137              
138             } else {
139              
140 0 0       0 if (@$cost < 4) {
141              
142 0         0 warn "Text::Brew: array cost not well formed, using default";
143 0         0 $cost=undef;
144             }
145             }
146              
147             } elsif ($key eq "-output") {
148              
149 0         0 $output=$$optional_ref{'-output'};
150              
151             } else {
152              
153 0         0 require Carp;
154 0         0 Carp::croak("Text::Brew: $key is not a valid option");
155             }
156             }
157             }
158             }
159              
160 8   50     41 $cost ||= [0,1,1,1];
161 8 50       18 $output='both' if (!defined $output);
162              
163 8 50       20 if ($output eq 'both') {
    0          
    0          
164              
165 8         18 my $tb=_edit_path($string1,$string2,$cost);
166 8         15 my $distance=$tb->[0];
167 8         6 my $arrayref_edits;
168              
169 8         16 while (defined $tb->[0]) {
170              
171 44         61 unshift @$arrayref_edits,$tb->[1];
172 44         78 $tb=$tb->[2];
173             }
174              
175 8         24 return $distance,$arrayref_edits;
176              
177             } elsif ($output eq 'distance') {
178              
179 0           my $tb=_edit_path($string1,$string2,$cost);
180 0           my $distance=$tb->[0];
181              
182 0           return $distance;
183              
184             } elsif ($output eq 'edits') {
185              
186 0           my $tb=_edit_path($string1,$string2,$cost);
187 0           my $arrayref_edits;
188              
189 0           while (defined $tb->[0]) {
190              
191 0           unshift @$arrayref_edits,$tb->[1];
192 0           $tb=$tb->[2];
193             }
194              
195 0           return $arrayref_edits;
196              
197             } else {
198              
199 0           require Carp;
200 0           Carp::croak("Text::Brew: -output option must be 'distance' or 'both' or 'edits', not $output");
201             }
202             }
203              
204             1;
205              
206             __END__