[Bio] / Sprout / SHSigGenes.pm Repository:
ViewVC logotype

Annotation of /Sprout/SHSigGenes.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.21 - (view) (download) (as text)

1 : parrello 1.1 #!/usr/bin/perl -w
2 :    
3 :     package SHSigGenes;
4 :    
5 :     use strict;
6 :     use Tracer;
7 : parrello 1.20 use CGI qw(-nosticky);
8 : parrello 1.1 use HTML;
9 :     use Sprout;
10 : parrello 1.10 use Time::HiRes;
11 : parrello 1.11 use FIGRules;
12 : parrello 1.16 use RHFeatures;
13 :     use base 'SearchHelper';
14 : parrello 1.1
15 :     =head1 Gene Discrimination Feature Search Helper
16 :    
17 :     =head2 Introduction
18 :    
19 :     This search performs a signature genes comparison. The user selects two genome sets,
20 :     and the search returns genes from a given genome which are common only in the first set
21 :     and not in the second. If the second set is empty, the search will return genes from
22 :     the given genome that are common in the first set.
23 :    
24 :     Gene identity will be computed in this case using bidirectional best hits. If gene X
25 :     from the given genome has a BBH in a specified genome Y, then it is said to occur
26 :     in whatever set includes genome Y. A gene is considered I<common> if it occurs in a
27 :     certain percentage of the genomes of the set.
28 :    
29 :     This search has the following extra parameters.
30 :    
31 :     =over 4
32 :    
33 :     =item given
34 :    
35 :     The ID of the given genome.
36 :    
37 :     =item target[]
38 :    
39 :     The IDs of the genomes in the first (target) set. The given genome is
40 :     automatically considered a part of this set, so it can never be empty.
41 :    
42 :     =item exclusion[]
43 :    
44 :     The IDs of the genomes in the second (exclusion) set. If this set is empty, then
45 :     no genes will be considered common in set 2, causing all genes common in set 1
46 :     to be selected.
47 :    
48 :     =item commonality
49 :    
50 :     Minimum score for a gene to be considered common. The score is equal to the number
51 :     of genomes containing a bidirectional best hit of the gene divided by the total
52 :     number of genomes. The default is C<0.8>. A value of C<1> means a gene must have
53 :     BBHs in all of the genomes to be considered common; a value of C<0> is invalid.
54 :    
55 :     =item cutoff
56 :    
57 :     Maximum match difference for a BBH hit to be considered valid. The default is C<1e-10>.
58 :    
59 : parrello 1.11 =item showMatch
60 :    
61 :     If TRUE, then all the genes in the target set that match the ones in the reference genome
62 :     will be shown in an extra column.
63 :    
64 : parrello 1.1 =back
65 :    
66 :     =head2 Virtual Methods
67 :    
68 :     =head3 Form
69 :    
70 : parrello 1.18 my $html = $shelp->Form();
71 : parrello 1.1
72 :     Generate the HTML for a form to request a new search.
73 :    
74 :     =cut
75 :    
76 :     sub Form {
77 :     # Get the parameters.
78 :     my ($self) = @_;
79 :     # Get the CGI and sprout objects.
80 :     my $cgi = $self->Q();
81 :     my $sprout = $self->DB();
82 :     # Start the form.
83 :     my $retVal = $self->FormStart("Signature Genes");
84 :     # The bulk of this form will be two genome selection menus, one for the first
85 :     # (target) set and one for the second (exclusion) set. Above these two controls
86 :     # there is the selector for the given genome, the commonality and cutoff values,
87 :     # and the submit button. Our first task, then, is to get the genome selection
88 :     # menus.
89 : parrello 1.5 my $givenMenu = $self->NmpdrGenomeMenu('given', 0, [$cgi->param('given')]);
90 : parrello 1.4 my $targetMenu = $self->NmpdrGenomeMenu('target', 'multiple', [$cgi->param('target')], 10, 'exclusion');
91 :     my $excludeMenu = $self->NmpdrGenomeMenu('exclusion', 'multiple', [$cgi->param('exclusion')], 10, 'target');
92 : parrello 1.1 # Get the default values to use for the commonality and cutoff controls.
93 :     my $commonality = $cgi->param('commonality') || "0.8";
94 :     my $cutoff = $cgi->param('cutoff') || "1e-10";
95 : parrello 1.6 my $statistical = $cgi->param('statistical') || 1;
96 : parrello 1.11 my $showMatch = $cgi->param('showMatch') || 0;
97 : parrello 1.12 my $useSims = $cgi->param('useSims') || 0;
98 : parrello 1.13 my $pegsOnly = $cgi->param('pegsOnly') || 1;
99 : parrello 1.9 # Now we build the table rows.
100 : parrello 1.1 my @rows = ();
101 : parrello 1.9 # First we have the given genome.
102 : parrello 1.20 push @rows, CGI::Tr(CGI::td({valign => "top"}, "Reference Genome"),
103 :     CGI::td({colspan => 2}, $givenMenu));
104 : parrello 1.9 # Now show the target and exclusion menus.
105 : parrello 1.20 push @rows, CGI::Tr(CGI::td({valign => "top"}, "Inclusion Genomes (Set 1)"),
106 :     CGI::td({colspan => 2}, $targetMenu));
107 :     push @rows, CGI::Tr(CGI::td({valign => "top"}, "Exclusion Genomes (Set 2)"),
108 :     CGI::td({colspan => 2}, $excludeMenu));
109 : parrello 1.12 # Next, the tuning parameters.
110 : parrello 1.20 push @rows, CGI::Tr(CGI::td("Commonality"),
111 :     CGI::td(CGI::textfield(-name => 'commonality',
112 : parrello 1.1 -value => $commonality,
113 : parrello 1.9 -size => 5))),
114 : parrello 1.20 CGI::Tr(CGI::td(), CGI::td(join(" ",
115 :     CGI::checkbox(-name => 'statistical',
116 : parrello 1.5 -checked => $statistical,
117 :     -value => 1,
118 : parrello 1.16 -label => 'Use Statistical Algorithm') .
119 : parrello 1.17 SearchHelper::Hint("SigGenes",
120 : parrello 1.19 "When two sets of genomes are specified, check this " .
121 : parrello 1.16 "box to use a statistical algorithm designed " .
122 :     "specifically to choose differentiating genes. " .
123 :     "This box has no effect when looking for genes " .
124 :     "in common."),
125 : parrello 1.20 CGI::checkbox(-name => 'useSims',
126 : parrello 1.12 -checked => $useSims,
127 :     -value => 1,
128 : parrello 1.16 -label => 'Use Similarities') .
129 : parrello 1.17 SearchHelper::Hint("SigGenes",
130 :     "Normally, Bidirectional Best Hits are used to " .
131 : parrello 1.16 "find matching genes. Check this box to use " .
132 :     "similarities instead.")))),
133 : parrello 1.20 CGI::Tr(CGI::td(), CGI::td(join(" ",
134 :     CGI::checkbox(-name => 'showMatch',
135 : parrello 1.11 -checked => $showMatch,
136 :     -value => 1,
137 : parrello 1.16 -label => 'Show Matching Genes') .
138 : parrello 1.17 SearchHelper::Hint("SigGenes",
139 :     "Check this button to display the genes matching " .
140 : parrello 1.16 "each gene displayed in the results.")))),
141 : parrello 1.20 CGI::Tr(CGI::td("Cutoff"),
142 :     CGI::td(CGI::textfield(-name => 'cutoff',
143 : parrello 1.1 -value => $cutoff,
144 :     -size => 5)));
145 : parrello 1.9 # Next, the feature filter rows.
146 : parrello 1.16 push @rows, RHFeatures::WordSearchRow($self);
147 :     push @rows, RHFeatures::FeatureFilterFormRows($self);
148 : parrello 1.9 # Finally, the submit button.
149 : parrello 1.1 push @rows, $self->SubmitRow();
150 :     # Create the table.
151 :     $retVal .= $self->MakeTable(\@rows);
152 :     # Close the form.
153 :     $retVal .= $self->FormEnd();
154 :     # Return the result.
155 :     return $retVal;
156 :     }
157 :    
158 :     =head3 Find
159 :    
160 : parrello 1.18 my $resultCount = $shelp->Find();
161 : parrello 1.1
162 :     Conduct a search based on the current CGI query parameters. The search results will
163 :     be written to the session cache file and the number of results will be
164 :     returned. If the search parameters are invalid, a result count of C<undef> will be
165 :     returned and a result message will be stored in this object describing the problem.
166 :    
167 :     =cut
168 :    
169 :     sub Find {
170 :     # Get the parameters.
171 :     my ($self) = @_;
172 :     # Get the sprout and CGI query objects.
173 :     my $cgi = $self->Q();
174 :     my $sprout = $self->DB();
175 : parrello 1.2 # Declare the return variable. If it remains undefined, the caller will
176 :     # assume there was an error.
177 : parrello 1.1 my $retVal;
178 : parrello 1.10 # Create the timers.
179 :     my ($saveTime, $loopCounter, $bbhTimer, $putTimer, $queryTimer) = (0, 0, 0, 0, 0);
180 : parrello 1.2 # Validate the numeric parameters.
181 :     my $commonality = $cgi->param('commonality');
182 :     my $cutoff = $cgi->param('cutoff');
183 :     if ($commonality !~ /^\s*\d(\.\d+)?\s*$/) {
184 :     $self->SetMessage("Commonality value appears invalid, too big, negative, or not a number.");
185 :     } elsif ($commonality <= 0 || $commonality > 1) {
186 :     $self->SetMessage("Commonality cannot be 0 and cannot be greater than 1.");
187 :     } elsif ($cutoff !~ /^\s*\d(.\d+)?(e\-\d+)?\s*$/) {
188 :     $self->SetMessage("Cutoff must be an exponential number (e.g. \"1e-20\" or \"2.5e-11\".");
189 :     } elsif ($cutoff > 1) {
190 :     $self->SetMessage("Cutoff cannot be greater than 1.");
191 :     } else {
192 : parrello 1.16 # Get the result helper.
193 :     my $rhelp = RHFeatures->new($self);
194 :     # Set up the default columns.
195 :     $self->DefaultColumns($rhelp);
196 :     # Add the score at the end.
197 :     $rhelp->AddExtraColumn(score => undef, title => 'Score', style => 'rightAlign', download => 'num');
198 : parrello 1.11 # Find out if we need to show matching genes.
199 :     my $showMatch = $cgi->param('showMatch') || 0;
200 : parrello 1.16 # If we do, add a column for them at the front.
201 :     if ($showMatch) {
202 :     $rhelp->AddExtraColumn(matches => 0, title => 'Matches', style => 'leftAlign', download => 'list');
203 :     }
204 :     # Only proceed if the filtering parameters are valid.
205 :     if ($rhelp->Valid()) {
206 :     # Now we need to gather and validate the genome sets.
207 :     $self->PrintLine("Gathering the target genomes. ");
208 :     my ($givenGenomeID) = $self->GetGenomes('given');
209 : parrello 1.21 Trace("Given genome is $givenGenomeID.") if T(3);
210 : parrello 1.16 my %targetGenomes = map { $_ => 1 } $self->GetGenomes('target');
211 : parrello 1.21 Trace("Target genomes are " . join(", ", sort keys %targetGenomes) . ".") if T(3);
212 : parrello 1.16 $self->PrintLine("Gathering the exclusion genomes. ");
213 :     my %exclusionGenomes = map { $_ => 1 } $self->GetGenomes('exclusion');
214 : parrello 1.21 Trace("Exclusion genomes are " . join(", ", sort keys %exclusionGenomes) . ".") if T(3);
215 : parrello 1.16 $self->PrintLine("Validating the genome sets.<br />");
216 :     # Insure the given genome is not in the exclusion set.
217 :     if ($exclusionGenomes{$givenGenomeID}) {
218 :     $self->SetMessage("The given genome ($givenGenomeID) cannot be in the exclusion set.");
219 :     } else {
220 : parrello 1.21 # Start the output session.
221 :     $self->OpenSession($rhelp);
222 : parrello 1.16 # Insure the given genome is in the target set.
223 :     $targetGenomes{$givenGenomeID} = 1;
224 : parrello 1.21 Trace("$givenGenomeID added to target set.") if T(3);
225 :     # Find out if we want to use a statistical analysis.
226 :     my $statistical = $cgi->param('statistical') || 1;
227 :     # Denote we have not yet found any genomes.
228 :     $retVal = 0;
229 :     # Compute the list of genomes of interest.
230 :     my @allGenomes = (keys %exclusionGenomes, keys %targetGenomes);
231 :     # Get the peg matrix.
232 :     Trace("Requesting matrix.") if T(3);
233 :     $saveTime = time();
234 :     my $bbhMatrix;
235 :     if (! $cgi->param('useSims')) {
236 :     # Here we are using BBHs, which are fast enough to do in one gulp.
237 :     $self->PrintLine("Requesting bidirectional best hits. ");
238 :     $bbhMatrix = $sprout->BBHMatrix($givenGenomeID, $cutoff, @allGenomes);
239 :     } else {
240 :     # Here we are using similarities, which are much more complicated.
241 :     $self->PrintLine("Requesting similarities.<br />");
242 :     # Create a filtering matrix for the results. We only want to keep PEGs in the
243 :     # specified target and exclusion genomes.
244 :     my %keepGenomes = map { $_ => 1 } @allGenomes;
245 :     # Loop through the given genome's features.
246 :     my @features = $sprout->FeaturesOf($givenGenomeID);
247 :     for my $fid (@features) {
248 :     $self->PrintLine("Retrieving similarities for $fid. ");
249 :     # Get this feature's similarities.
250 :     my $simList = $sprout->Sims($fid, 1000, $cutoff, 'fig');
251 :     my $simCount = scalar @{$simList};
252 :     $self->PrintLine("Raw similarity count: $simCount. ");
253 :     # Create the matrix hash for this feature.
254 :     $bbhMatrix->{$fid} = {};
255 :     # Now we need to filter out the similarities that don't land on the target genome.
256 :     $simCount = 0;
257 :     for my $sim (@{$simList}) {
258 :     # Insure this similarity lands on a target genome.
259 :     my $genomeID2 = $sprout->GenomeOf($sim->id2);
260 :     if ($keepGenomes{$genomeID2}) {
261 :     # Here we're keeping the similarity, so we put it in this feature's hash.
262 :     $bbhMatrix->{$fid}->{$sim->id2} = $sim->psc;
263 :     $simCount++;
264 :     }
265 : parrello 1.16 }
266 : parrello 1.21 $self->PrintLine("Similarities retained: $simCount.<br />");
267 : parrello 1.12 }
268 :     }
269 : parrello 1.21 $bbhTimer += time() - $saveTime;
270 :     $self->PrintLine("Time to build matrix: $bbhTimer seconds.<br />");
271 :     Trace("Matrix built.") if T(3);
272 :     # Create a feature query object to loop through the chosen features of the given
273 :     # genome.
274 :     Trace("Creating feature query.") if T(3);
275 : parrello 1.16 $saveTime = time();
276 : parrello 1.21 my $fquery = $rhelp->GetQuery($givenGenomeID);
277 : parrello 1.16 $queryTimer += time() - $saveTime;
278 : parrello 1.21 # Get the sizes of the two sets. This information is useful in computing commonality.
279 :     my $targetSetSize = scalar keys %targetGenomes;
280 :     my $exclusionSetSize = scalar keys %exclusionGenomes;
281 :     # Loop through the features.
282 :     my $done = 0;
283 :     while (! $done) {
284 :     # Get the next feature.
285 :     $saveTime = time();
286 :     my $record = $rhelp->Fetch($fquery);
287 :     $queryTimer += time() - $saveTime;
288 :     if (! $record) {
289 :     $done = 1;
290 :     } else {
291 :     # Get the feature's ID.
292 :     my $fid = $record->PrimaryValue('Feature(id)');
293 :     Trace("Checking feature $fid.") if T(4);
294 :     $self->PrintLine("Checking feature $fid.<br />");
295 :     # Get its list of matching genes. The list is actually a hash mapping each matched gene to its
296 :     # score. All we care about, however, are the matches themselves.
297 :     my $bbhList = $bbhMatrix->{$fid};
298 :     # We next wish to loop through the BBH IDs, counting how many are in each of the
299 :     # sets. If a genome occurs twice, we only want to count the first occurrence, so
300 :     # we have a hash of genomes we've already seen. The hash will map each gene ID
301 :     # to 0, 1, or 2, depending on whether it was found in the reference genome,
302 :     # a target genome, or an exclusion genome.
303 :     my %alreadySeen = ();
304 :     # Save the matching genes in here.
305 :     my %genesMatching = ();
306 :     # Clear the exclusion count.
307 :     my $exclusionCount = 0;
308 :     # Denote that we're in our own genome.
309 :     $alreadySeen{$givenGenomeID} = 0;
310 :     my $targetCount = 1;
311 :     # Loop through the BBHs/Sims.
312 :     for my $bbhPeg (keys %{$bbhList}) {
313 :     # Get the genome ID. We want to find out if this genome is new.
314 :     my $genomeID = $sprout->GenomeOf($bbhPeg);
315 :     if (! exists $alreadySeen{$genomeID}) {
316 :     # It's new, so we check to see which set it's in.
317 :     if ($targetGenomes{$genomeID}) {
318 :     # It's in the target set.
319 :     $targetCount++;
320 :     $alreadySeen{$genomeID} = 1;
321 :     } elsif ($exclusionGenomes{$genomeID}) {
322 :     # It's in the exclusion set.
323 :     $exclusionCount++;
324 :     $alreadySeen{$genomeID} = 2;
325 :     }
326 :     # Note that $alreadySeen{$genomeID} exists in the hash by this
327 :     # point. If it's 1, we need to save the current PEG.
328 :     if ($alreadySeen{$genomeID} == 1) {
329 :     $genesMatching{$bbhPeg} = 1;
330 :     }
331 : parrello 1.12 }
332 : parrello 1.21 }
333 :     # Create a variable to indicate whether or not we want to keep this feature and
334 :     # another for the score.
335 :     my ($okFlag, $score);
336 :     # We need to see if we're using statistics or not. This only matters
337 :     # for a two-set situation.
338 :     if ($statistical && $exclusionSetSize > 0) {
339 :     # This is the magic formula for choosing the differentiating genes. It looks like
340 :     # it has something to do with variance computations, but I'm not sure.
341 :     my $targetNotCount = $targetSetSize - $targetCount;
342 :     my $targetSquare = $targetCount * $targetCount + $targetNotCount * $targetNotCount;
343 :     my $exclusionNotCount = $exclusionSetSize - $exclusionCount;
344 :     my $exclusionSquare = $exclusionCount * $exclusionCount + $exclusionNotCount * $exclusionNotCount;
345 :     my $mixed = $targetCount * $exclusionCount + $targetNotCount * $exclusionNotCount;
346 :     my $inD = 1 - (($exclusionSetSize * $mixed) / ($targetSetSize * $exclusionSquare));
347 :     my $outD = 1 - (($targetSetSize * $mixed) / ($exclusionSetSize * $targetSquare));
348 :     # If the two differentials are greater than one, we keep this feature.
349 :     $score = $inD + $outD;
350 :     $okFlag = ($score > 1);
351 :     # Subtract 1 from the score so it looks like the commonality score.
352 :     $score -= 1.0;
353 :     } else {
354 :     # Check to see if we're common in set 1 and not in set 2.
355 :     my $score1 = IsCommon($targetCount, $targetSetSize, $commonality);
356 :     my $score2 = IsCommon($exclusionCount, $exclusionSetSize, $commonality);
357 :     if ($score1 && ! $score2) {
358 :     # We satisfy the criterion, so we put this feature to the output. The
359 :     # score is essentially $score1, since $score2 is zero.
360 :     $score = $score1;
361 :     $okFlag = 1;
362 : parrello 1.12 }
363 : parrello 1.11 }
364 : parrello 1.21 if ($okFlag) {
365 :     # Put this feature to the output. We have one or two extra columns.
366 :     # First we store the score.
367 :     $rhelp->PutExtraColumns(score => sprintf("%0.3f",$score));
368 :     # Next we add the list of matching genes, but only if "showMatch" is specified.
369 :     if ($showMatch) {
370 :     # The matching genes are in the hash "genesMatching".
371 :     my @genes = sort { FIGRules::FIGCompare($a,$b) } keys %genesMatching;
372 :     # We need to linkify them.
373 :     my $genesHTML = join(", ", map { HTML::fid_link($cgi, $_) } @genes);
374 :     # Now add them as an extra column.
375 :     $rhelp->PutExtraColumns(matches => $genesHTML);
376 :     }
377 :     # Compute a sort key from the feature data and the score.
378 :     my $sort = $rhelp->SortKey($record, sprintf("%0.3f", 1 - $score));
379 :     # Output the feature.
380 :     $saveTime = time();
381 :     $rhelp->PutData($sort, $fid, $record);
382 :     $putTimer += time() - $saveTime;
383 :     # Increase the result count.
384 :     $retVal++;
385 : parrello 1.10 }
386 : parrello 1.21 # Check for a timer trace. We trace every 500 features.
387 :     $loopCounter++;
388 :     if (T(3) && $loopCounter % 500 == 0) {
389 :     Trace("Time spent for $loopCounter features: Put = $putTimer, Query = $queryTimer, BBH = $bbhTimer.");
390 : parrello 1.12 }
391 : parrello 1.11 }
392 : parrello 1.5 }
393 : parrello 1.21 # Close the session file.
394 :     $saveTime = time();
395 :     $self->CloseSession();
396 :     $putTimer += time() - $saveTime;
397 : parrello 1.5 }
398 : parrello 1.4 }
399 : parrello 1.2 }
400 : parrello 1.10 # Trace the timers.
401 :     Trace("Time spent: Put = $putTimer, Query = $queryTimer, BBH = $bbhTimer.") if T(3);
402 : parrello 1.1 # Return the result count.
403 :     return $retVal;
404 :     }
405 :    
406 :     =head3 Description
407 :    
408 : parrello 1.18 my $htmlText = $shelp->Description();
409 : parrello 1.1
410 :     Return a description of this search. The description is used for the table of contents
411 :     on the main search tools page. It may contain HTML, but it should be character-level,
412 :     not block-level, since the description is going to appear in a list.
413 :    
414 :     =cut
415 :    
416 :     sub Description {
417 :     # Get the parameters.
418 :     my ($self) = @_;
419 :     # Return the result.
420 : parrello 1.8 return "Search for genes that are common to a group of organisms or that discriminate between two groups of organisms.";
421 : parrello 1.1 }
422 :    
423 : parrello 1.16 =head3 SearchTitle
424 : parrello 1.15
425 : parrello 1.18 my $titleHtml = $shelp->SearchTitle();
426 : parrello 1.15
427 : parrello 1.16 Return the display title for this search. The display title appears above the search results.
428 :     If no result is returned, no title will be displayed. The result should be an html string
429 :     that can be legally put inside a block tag such as C<h3> or C<p>.
430 : parrello 1.15
431 :     =cut
432 :    
433 : parrello 1.16 sub SearchTitle {
434 : parrello 1.15 # Get the parameters.
435 : parrello 1.16 my ($self) = @_;
436 :     # Compute the title. We extract the relevant clues from the query parameters.
437 :     my $cgi = $self->Q();
438 :     my $type = ($cgi->param('useSims') ? "Similarities" : "Bidirectional Best Hits");
439 :     my $style = ($cgi->param('exclusion') ? "Discriminating" : "Common");
440 :     my $retVal = "$style Genes using $type";
441 :     # Return it.
442 : parrello 1.15 return $retVal;
443 :     }
444 :    
445 : parrello 1.4 =head2 Internal Utilities
446 :    
447 :     =head3 IsCommon
448 :    
449 : parrello 1.18 my $score = SHSigGenes::IsCommon($count, $size, $commonality);
450 : parrello 1.4
451 : parrello 1.11 Return the match score if a specified count indicates a gene is common in a specified set
452 :     and 0 otherwise. Commonality is computed by dividing the count by the size of the set and
453 : parrello 1.4 comparing the result to the minimum commonality ratio. The one exception is
454 : parrello 1.11 if the set size is 0. In that case, this method always returns 0.
455 : parrello 1.4
456 :     =over 4
457 :    
458 :     =item count
459 :    
460 :     Number of elements of the set that have the relevant characteristic.
461 :    
462 :     =item size
463 :    
464 :     Total number of elements in the set.
465 :    
466 :     =item commonality
467 :    
468 :     Minimum count/size ratio for the characteristic to be considered common.
469 :    
470 :     =item RETURN
471 :    
472 :     Returns TRUE if the characteristic is common, else FALSE.
473 :    
474 :     =back
475 :    
476 :     =cut
477 :    
478 :     sub IsCommon {
479 :     # Get the parameters.
480 :     my ($count, $size, $commonality) = @_;
481 :     # Declare the return variable.
482 :     my $retVal = 0;
483 :     # Only procced if the size is positive.
484 :     if ($size > 0) {
485 : parrello 1.11 # Compute the commonality.
486 :     $retVal = $count/$size;
487 :     # If it's too small, clear it.
488 :     if ($retVal < $commonality) {
489 :     $retVal = 0;
490 :     }
491 : parrello 1.4 }
492 :     # Return the result.
493 :     return $retVal;
494 :     }
495 :    
496 : parrello 1.18 1;

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3