File Coverage

blib/lib/Set/Formula.pm
Criterion Covered Total %
statement 168 208 80.7
branch 71 118 60.1
condition 3 6 50.0
subroutine 10 10 100.0
pod 3 3 100.0
total 255 345 73.9


line stmt bran cond sub pod time code
1             package Set::Formula;
2              
3 2     2   44114 use 5.8.8;
  2         9  
  2         110  
4 2     2   11 use strict;
  2         4  
  2         84  
5 2     2   10 use warnings;
  2         8  
  2         152  
6              
7             require Exporter;
8             our @ISA = qw(Exporter);
9             our @EXPORT = qw(formula_checker formula_calcul equality_checker);
10              
11 2     2   12 use Carp qw(cluck);
  2         3  
  2         4428  
12             our $VERSION = '0.05';
13             my $debug = 0;
14              
15             my %operators = (
16             '+' => \&_union
17             ,'-' => \&_complement
18             ,'^' => \&_intersection
19             );
20              
21             my $counter = 0;
22             #------------------------------------------#
23             sub _union
24             {
25 2     2   4 my ($hrefA, $hrefB, $href_result) = @_;
26 2         14 %{$href_result} = (); # reset this hash
  2         5  
27              
28 2         3 for (keys %{$hrefA}) { ${$href_result}{$_} = 0 }
  2         9  
  8         14  
  8         17  
29 2         5 for (keys %{$hrefB}) { ${$href_result}{$_} = 0 }
  2         7  
  4         5  
  4         9  
30             }
31             #------------------------------------------#
32             sub _complement
33             { # Result := A - B
34 2     2   4 my ($hrefA, $hrefB, $href_result) = @_;
35 2         6 %{$href_result} = (); # reset this hash
  2         9  
36              
37 2         7 for (keys %{$hrefA})
  2         6  
38 4 100       4 { unless (exists ${$hrefB}{$_}) { ${$href_result}{$_} = 0 } }
  4         14  
  2         3  
  2         8  
39             }
40             #------------------------------------------#
41             sub _intersection
42             {
43 4     4   9 my ($hrefA, $hrefB, $href_result) = @_;
44 4         4 %{$href_result} = (); # reset this hash
  4         9  
45              
46 4         5 for (keys %{$hrefB})
  4         13  
47 18 100       20 { if (exists ${$hrefA}{$_}) { ${$href_result}{$_} = 0; } }
  18         50  
  8         9  
  8         22  
48             }
49             #------------------------------------------#
50             sub equality_checker # returns 1 if equal, else 0
51             {
52 2     2 1 3 my ($hrefA, $hrefB, $debug) = @_;
53              
54 2 50       6 $debug = (defined $debug)? 1:0;
55 2 50       6 if ($debug)
56             {
57 0         0 printf "*** %s: running in debug mode ***\n", (caller(0))[3];
58 0         0 print "DEBUG: 1st operand: "; for (sort keys %{$hrefA}) { print "$_ "; }; print "\n";
  0         0  
  0         0  
  0         0  
  0         0  
59 0         0 print "DEBUG: 2nd operand: "; for (sort keys %{$hrefB}) { print "$_ "; }; print "\n";
  0         0  
  0         0  
  0         0  
  0         0  
60             }
61              
62 2         3 for (keys %{$hrefA})
  2         7  
63 4 100       5 { unless (exists ${$hrefB}{$_}) { return 0 } }
  4         12  
  1         5  
64              
65 1         2 for (keys %{$hrefB})
  1         4  
66 3 50       3 { unless (exists ${$hrefA}{$_}) { return 0 } }
  3         10  
  0         0  
67 1         4 return 1;
68             }
69             #------------------------------------------#
70              
71             sub formula_checker # returns true on success and undefined value on error
72             {
73 14     14 1 42 my $formula = shift;
74 14         23 my $remainder;
75 14 50 33     55 my $debug = (defined $ARGV[0]) && $ARGV[0] ?1:0;
76 14 50       32 $debug && printf "*** %s: running in debug mode ***\n", (caller(0))[3];
77 14 50       37 unless (defined $remainder) { $remainder = ''; }
  14         23  
78 14         19 my $parentheses_cnt = 0;
79 14         19 my $parentheses_number_cnt = 0;
80 14         20 my $operator;
81             my $regexpr ;
82              
83             # ---- check "interlacing": start ----
84             # rule 1: 2 operands can't be adjacent
85             # rule 2: 2 operators can't be adjacent
86             # rule 3: operator can be neither first not last formula element
87              
88 14 50       30 $debug && print "DEBUG 10: formula: $formula\n";
89 14         17 $remainder = $formula;
90 14         71 $remainder =~ s/[()]//g; # remove all parentheses
91 14 50       32 $debug && print "DEBUG 20: remainder: $remainder\n";
92 14         22 my $operator_found = 0;
93              
94             INTERLACING:
95 14         16 while(1)
96             {
97 34 100       149 unless ( $remainder =~ /^\s*(\w+)\s*/ )
98             {
99 2 50       6 $debug && cluck "ERROR: remainder of formula $remainder must begin with operand";
100 2         10 return;
101             }
102              
103 32         72 $remainder = $';
104 32 50       64 $debug && print "DEBUG 30: operand: $1 remainder: $remainder\n";
105            
106 32 100       65 unless ($remainder) { last INTERLACING; } # interlacing is correct
  9         203  
107            
108 23         26 $operator_found = 0;
109 23         66 for (keys %operators)
110             {
111 57 50       121 $debug && print "DEBUG 40: operator: $_\n";
112 57         96 $regexpr = qq (^\\s*(\\$_)\\s*);
113 57 50       103 $debug && printf "DEBUG 45: regexpr = %s\n", $regexpr;
114 57 100       1164 if ( $remainder =~ /$regexpr/ )
115             {
116 22         31 $operator_found = 1;
117 22         32 $operator = $_;
118 22         42 $remainder = $';
119 22         49 last;
120             }
121             }
122              
123 23 100       72 unless ($operator_found)
124             {
125 1 50       3 $debug && cluck "ERROR: remainder of formula $remainder must begin with operator";
126 1         6 return;
127             }
128              
129 22 50       44 $debug && printf "DEBUG 50: operator: %s\n", $operator;
130              
131 22 100       52 unless ($remainder)
132             {
133 2 50       8 $debug && cluck "ERROR: formula must begin with operator";
134 2         570 return;
135             }
136             } # end of 'while (1)'
137              
138             # ---- check "interlacing": finish ----
139              
140             # ---- check parentheses: start ----
141 9         13 $remainder = $formula;
142 9 50       19 $debug && printf "DEBUG 60: *** remainder = %s\n", $remainder;
143              
144 9         26 for $operator (keys %operators)
145             {
146 27         62 $regexpr = qq (\\(\\s*\\$operator|\\$operator\\s*\\));
147 27 50       47 $debug && printf "DEBUG 65: *** operator = %-3s regexpr = %s\n", $operator, $regexpr;
148 27 100       699 if ( $remainder =~ /$regexpr/ )
149             {
150 2 50       6 $debug && cluck "ERROR: $& has no sense";
151 2         537 return;
152             }
153             }
154              
155 7         29 while ($remainder =~ /[()]/)
156             {
157 26 50       52 $debug && printf "DEBUG 70: *** remainder = %s\n", $remainder;
158 26         28 $parentheses_number_cnt ++;
159 26         49 $remainder = $';
160 26 50       56 unless (defined $remainder) { $remainder = ''; }
  0         0  
161 26 50       81 $debug && printf "DEBUG 75: *** remainder = %s\n", $remainder;
162 26 100       69 if ( $& =~ /\(/ ) { $parentheses_cnt ++; }
  13         47  
163             else
164             {
165 13         15 $parentheses_cnt --;
166 13 100       57 if ($parentheses_cnt < 0)
167             {
168 2 50       5 $debug && cluck "ERROR: negative parentheses amount at parentheses nr.$parentheses_number_cnt from left";
169 2         15 return;
170             }
171             }
172             } # end of "while ($remainder)"
173              
174 5 100       14 if ($parentheses_cnt)
175             {
176 2 50       5 $debug && cluck "ERROR: more opening parentheses than closing parentheses";
177 2         13 return;
178             }
179 3         21 return 1;
180             } # end of "sub formula_checker"
181             #------------------------------------------#
182             #------------------------------------------#
183              
184             sub formula_calcul
185             {
186 12     12 1 1348 my ($curr_formula, $href_result, $href_HoH_sets, $debug) = @_;
187 12         15 %{$href_result} = (); # reset this hash
  12         24  
188 12 50       29 unless (defined $curr_formula) { $curr_formula = '' }
  0         0  
189              
190 12 50 66     50 $debug = (defined $debug) && $debug ?1:0;
191 12 50       24 $debug && printf "*** %s: running in debug mode ***\n", (caller(0))[3];
192              
193 12 50       21 $debug && print "\n--------------------------\n#1 curr_formula = $curr_formula\n";
194              
195 12         16 my $href_operand_left;
196             my $href_operand_right;
197 0         0 my $operator; # a key of %operators
198 0         0 my $href_remainder;
199 0         0 my $new_name;
200              
201             # parse formula: search internal (...)
202 12         48 while ( $curr_formula =~ /\(([^(]+?)\)/ )
203             {
204             # $curr_formula contains parentheses
205             # $1 is innenmost formula part, that does not contains parentheses.
206             # Opened and closed parentheses are located on boundaries of $1 and don't belong it.
207 6 50       13 $debug && print "#2 in parentheses: $1\n";
208             #------------------------------------------#
209 6 100       35 unless (defined &formula_calcul ($1, $href_result, $href_HoH_sets, $debug))
210 2 50       5 { $debug && cluck "Error in formula \"$1\""; return; }
  2         70  
211              
212 4         9 $new_name = "InTeRmEdIaTe$counter";
213 4         6 $counter++;
214 4         5 for (keys %{$href_result}) { $href_HoH_sets->{$new_name}{$_} = 0; }
  4         13  
  16         38  
215 4 50       13 unless (exists $href_HoH_sets->{$new_name}) { $href_HoH_sets->{$new_name} = () }
  0         0  
216 4         5 %{$href_result} = (); # reset this hash
  4         9  
217              
218 4         17 $curr_formula = "$` $new_name $'";
219 4 50       22 $debug && print "#3 curr_formula = $curr_formula\n\n";
220             }
221              
222             # parenthesesless formula.
223             # Calculate all operators with equal priority from left to right
224 10 50       43 if ( $curr_formula =~ /(\w+)\s*(\S+)\s*(\w+)/ )
225             {
226 10         26 $href_operand_left = $1;
227 10         16 $operator = $2;
228 10         17 $href_operand_right = $3;
229 10         17 $href_remainder = $';
230 10 50       36 if ($operator !~ /^(\+|\-|\^)$/)
231 0 0       0 { $debug && cluck "ERROR: Unknown operator \"$operator\" in formula \"$curr_formula\"\n"; return; }
  0         0  
232              
233 10 50       24 $debug && printf "*** left operand = $href_operand_left, operator = $operator, right operand = $href_operand_right, remainder = %s\n", $href_remainder;
234              
235 10 100       22 unless (exists $href_HoH_sets->{$href_operand_left})
236             {
237 2 50       15 $debug && cluck "ERROR: Unknown left operand \"$href_operand_left\" in formula \"$curr_formula\"\n";
238 2         7 return;
239             }
240              
241 8 50       28 unless (exists $href_HoH_sets->{$href_operand_right})
242             {
243 0 0       0 $debug && cluck "ERROR: Unknown right operand \"$href_operand_right\" in formula \"$curr_formula\"\n";
244 0         0 return;
245             }
246              
247 8 50       16 if ($debug)
248             {
249 0         0 printf "!!! %-13s : ", $href_operand_left;
250 0         0 for ( keys %{$href_HoH_sets->{$href_operand_left}} ) {print "$_ "}
  0         0  
  0         0  
251 0         0 print "\n";
252              
253 0         0 printf "!!! %-13s : ", $href_operand_right;
254 0         0 for ( keys %{$href_HoH_sets->{$href_operand_right}} ) {print "$_ "}
  0         0  
  0         0  
255 0         0 print "\n";
256             }
257              
258 8         32 $operators{$operator} ( $href_HoH_sets->{$href_operand_left}
259             , $href_HoH_sets->{$href_operand_right}
260             , $href_result );
261              
262 8 50       23 if ($debug)
263             {
264 0         0 print "DEBUG: result of ($href_operand_left $operator $href_operand_right) : ";
265 0         0 for (keys %{$href_result}) { print "$_ " }
  0         0  
  0         0  
266 0         0 print "\n";
267             }
268              
269 8 100       28 if ($href_remainder =~ /\S/)
270             {
271 2         3 $new_name = "InTeRmEdIaTe$counter";
272 2         3 $counter++;
273 2         4 for (keys %{$href_result}) { $href_HoH_sets->{$new_name}{$_} = 0 }
  2         8  
  4         18  
274 2 50       8 unless (exists $href_HoH_sets->{$new_name}) { $href_HoH_sets->{$new_name} = () }
  0         0  
275 2         4 %{$href_result} = (); # reset this hash
  2         4  
276              
277 2         6 $curr_formula = "$new_name $href_remainder";
278 2 50       8 $debug && print "#4 curr_formula = $curr_formula\n\n";
279 2 50       5 if (defined &formula_calcul ($curr_formula, $href_result, $href_HoH_sets, $debug))
280 2         9 { return 1; }
281 0 0       0 else { $debug && cluck "ERROR 33\n"; return; }
  0         0  
282             }
283             } # end of "if ( $curr_formula =~ ...)"
284 6         21 return 1;
285             } # end of "sub formula_calcul"
286              
287             1;
288             __END__