       1 #!/usr/bin/perl -w
       2 #
       3 # File: SimilaritySearchingFingerprints.pl
       4 # Author: Manish Sud <msud@san.rr.com>
       5 #
       6 # Copyright (C) 2018 Manish Sud. All rights reserved.
       7 #
       8 # This file is part of MayaChemTools.
       9 #
      10 # MayaChemTools is free software; you can redistribute it and/or modify it under
      11 # the terms of the GNU Lesser General Public License as published by the Free
      12 # Software Foundation; either version 3 of the License, or (at your option) any
      13 # later version.
      14 #
      15 # MayaChemTools is distributed in the hope that it will be useful, but without
      16 # any warranty; without even the implied warranty of merchantability of fitness
      17 # for a particular purpose.  See the GNU Lesser General Public License for more
      18 # details.
      19 #
      20 # You should have received a copy of the GNU Lesser General Public License
      21 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
      22 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
      23 # Boston, MA, 02111-1307, USA.
      24 #
      25
      26 use strict;
      27 use FindBin; use lib "$FindBin::Bin/../lib";
      28 use Getopt::Long;
      29 use File::Basename;
      30 use Text::ParseWords;
      31 use Benchmark;
      32 use FileUtil;
      33 use TextUtil;
      34 use SDFileUtil;
      35 use StatisticsUtil;
      36 use PseudoHeap;
      37 use Fingerprints::FingerprintsFileUtil;
      38 use Fingerprints::FingerprintsBitVector;
      39 use Fingerprints::FingerprintsVector;
      40
      41 my($ScriptName, %Options, $StartTime, $EndTime, $TotalTime);
      42
      43 # Autoflush STDOUT
      44 $| = 1;
      45
      46 # Starting message...
      47 $ScriptName = basename($0);
      48 print "\n$ScriptName: Starting...\n\n";
      49 $StartTime = new Benchmark;
      50
      51 # Get the options and setup script...
      52 SetupScriptUsage();
      53 if ($Options{help} || @ARGV != 2) {
      54   die GetUsageFromPod("$FindBin::Bin/$ScriptName");
      55 }
      56
      57 # Process reference and database file names...
      58 my(@FingerprintsFilesList);
      59 ProcessFingerprintsFileNames();
      60
      61 # Process options...
      62 print "Processing options...\n";
      63 my(%OptionsInfo);
      64 ProcessOptions();
      65
      66 # Setup information about fingerprints inut and SD/text output files...
      67 my(%FingerprintsFilesInfo, %OutputFilesInfo, %SimilaritySearchInfo);
      68 print "Checking and retrieving information from reference and database fingerprints files...\n";
      69 RetrieveFingerprintsFilesInfo();
      70
      71 # Perform similarity search...
      72 print "Performing similarity search...\n";
      73 my(%SimilaritySearchResults, %DatabaseFingerprintsFileData);
      74 PerformSimilaritySearch();
      75
      76 print "\n$ScriptName:Done...\n\n";
      77
      78 $EndTime = new Benchmark;
      79 $TotalTime = timediff ($EndTime, $StartTime);
      80 print "Total time: ", timestr($TotalTime), "\n";
      81
      82 ###############################################################################
      83
      84 # Perform similarity search using fingerprints data in reference and database text files...
      85 #
      86 sub PerformSimilaritySearch {
      87
      88   print "\nProcessing fingerprints data for reference molecules...\n";
      89   ReadReferenceFingerprintsData();
      90
      91   InitializeSimilaritySearchResults();
      92   GenerateSimilaritySearchResults();
      93   WriteSimilaritySearchResultFiles();
      94 }
      95
      96 # Find similar molecules from database molecules for individual or multiple reference molecules...
      97 #
      98 sub GenerateSimilaritySearchResults {
      99   my($DatabaseFingerprintsFileIO, $FingerprintsCount, $IgnoredFingerprintsCount, $DatabaseFingerprintsObject, $DatabaseCmpdID, $ReferenceFingerprintsObject, $ReferenceIndex, $ReferenceCmpdID, $ComparisonValue, $FusedComparisonValue, @ComparisonValues);
     100
     101   print "Processing fingerprints data for database molecules...\n";
     102
     103   ($FingerprintsCount, $IgnoredFingerprintsCount) = (0) x 3;
     104
     105   $DatabaseFingerprintsFileIO = Fingerprints::FingerprintsFileUtil::NewFingerprintsFileIO(%{$FingerprintsFilesInfo{Database}{FingerprintsFileIOParameters}});
     106   $DatabaseFingerprintsFileIO->Open();
     107
     108   @ComparisonValues = ();
     109
     110   DATABASEFP: while ($DatabaseFingerprintsFileIO->Read()) {
     111     $FingerprintsCount++;
     112
     113     if (!$DatabaseFingerprintsFileIO->IsFingerprintsDataValid()) {
     114       $IgnoredFingerprintsCount++;
     115       next DATABASEFP;
     116     }
     117     $DatabaseFingerprintsObject = $DatabaseFingerprintsFileIO->GetFingerprints();
     118     $DatabaseCmpdID = $DatabaseFingerprintsFileIO->GetCompoundID();
     119
     120     if ($SimilaritySearchInfo{MultipleReferencesMode}) {
     121       @ComparisonValues = ();
     122     }
     123
     124     REFERENCEFP: for $ReferenceIndex (0 .. $#{$SimilaritySearchInfo{ReferenceCmpdIDsRef}}) {
     125       $ReferenceCmpdID = $SimilaritySearchInfo{ReferenceCmpdIDsRef}->[$ReferenceIndex];
     126       $ReferenceFingerprintsObject = $SimilaritySearchInfo{ReferenceFingerprintsObjectsRef}->[$ReferenceIndex];
     127
     128       $ComparisonValue = CompareReferenceAndDatabaseFingerprintsPair($ReferenceFingerprintsObject, $DatabaseFingerprintsObject);
     129       if (!defined $ComparisonValue) {
     130         next REFERENCEFP;
     131       }
     132
     133       if ($SimilaritySearchInfo{IndividualReferenceMode}) {
     134         CollectSimilaritySearchResults($DatabaseFingerprintsFileIO, $DatabaseCmpdID, $ComparisonValue, $ReferenceCmpdID);
     135       }
     136       elsif ($SimilaritySearchInfo{MultipleReferencesMode}) {
     137         push @ComparisonValues, $ComparisonValue;
     138       }
     139     }
     140
     141     if ($SimilaritySearchInfo{MultipleReferencesMode}) {
     142       $FusedComparisonValue = CalculateGroupFusionComparisonValue(\@ComparisonValues);
     143       if (!defined $FusedComparisonValue) {
     144         next DATABASEFP;
     145       }
     146       CollectSimilaritySearchResults($DatabaseFingerprintsFileIO, $DatabaseCmpdID, $FusedComparisonValue);
     147     }
     148   }
     149   $DatabaseFingerprintsFileIO->Close();
     150
     151   print "Number of fingerprints data entries in database fingerprints file: $FingerprintsCount\n";
     152   print "Number of fingerprints date entries processed successfully: ", ($FingerprintsCount - $IgnoredFingerprintsCount)  , "\n";
     153   print "Number of fingerprints data entries ignored due to missing/invalid data: $IgnoredFingerprintsCount\n\n";
     154 }
     155
     156 # Compare a pair of reference and database fingerprints objects corresponding to bit-vector or
     157 # vectors using specified comparison method and comparison cutoff...
     158 #
     159 sub CompareReferenceAndDatabaseFingerprintsPair {
     160   my($ReferenceFingerprintsObject, $DatabaseFingerprintsObject) = @_;
     161   my($ComparisonMethod, $ComparisonValue);
     162
     163   $ComparisonMethod = $SimilaritySearchInfo{ComparisonMethod};
     164   $ComparisonValue = $ReferenceFingerprintsObject->$ComparisonMethod($DatabaseFingerprintsObject, @{$SimilaritySearchInfo{ComparisonMethodParameters}});
     165
     166   if (!defined $ComparisonValue) {
     167     warn "Warning: Ignoring fingerprints data for reference compound ID ",  $ReferenceFingerprintsObject->GetID(), ": Its comparison with database compound ID, ", $DatabaseFingerprintsObject->GetID(), ", failed.\n";
     168     return undef;
     169   }
     170
     171   $ComparisonValue = sprintf("%.$OptionsInfo{Precision}f", $ComparisonValue);
     172
     173   # Apply any comparison cutoff...
     174   if ($SimilaritySearchInfo{ApplyComparisonCutoff}) {
     175     return $SimilaritySearchInfo{KeepTop} ? ($ComparisonValue >= $SimilaritySearchInfo{ComparisonCutoff} ? $ComparisonValue : undef) : ($ComparisonValue <= $SimilaritySearchInfo{ComparisonCutoff} ? $ComparisonValue : undef);
     176   }
     177   else {
     178     return $ComparisonValue;
     179   }
     180 }
     181
     182 # Calculate group fusion comparison value...
     183 #
     184 sub CalculateGroupFusionComparisonValue {
     185   my($ComparisonValuesRef) = @_;
     186   my($FusedComparisonValue, @ComparisonValues);
     187
     188   if (!@{$ComparisonValuesRef}) {
     189     return undef;
     190   }
     191
     192   if ($SimilaritySearchInfo{SortComparisonValues}) {
     193     @ComparisonValues = sort { $SimilaritySearchInfo{KeepTop} ? ($b <=> $a) : ($a <=> $b) } @{$ComparisonValuesRef};
     194     if ($SimilaritySearchInfo{UsekNN} && ($OptionsInfo{kNN} < scalar @{$ComparisonValuesRef})) {
     195       # Keep only top kNN values for group fusion...
     196       splice @ComparisonValues, $OptionsInfo{kNN};
     197     }
     198     $ComparisonValuesRef = \@ComparisonValues;
     199   }
     200
     201   $FusedComparisonValue = &{$SimilaritySearchInfo{GroupFusionMethodRef}}($ComparisonValuesRef);
     202   if ($SimilaritySearchInfo{ApplyPrecisionDuringFusion}) {
     203     $FusedComparisonValue = sprintf("%.$OptionsInfo{Precision}f", $FusedComparisonValue);
     204   }
     205
     206   return $FusedComparisonValue;
     207 }
     208
     209 # Collect similarity results for individual reference and multiple references search...
     210 #
     211 sub CollectSimilaritySearchResults {
     212   my($DatabaseFingerprintsFileIO, $DatabaseCmpdID, $ComparisonValue, $ReferenceCmpdID) = @_;
     213
     214   if (defined $ReferenceCmpdID) {
     215     $SimilaritySearchResults{$ReferenceCmpdID}->AddKeyValuePair($ComparisonValue, $DatabaseCmpdID);
     216   }
     217   else {
     218     $SimilaritySearchResults{ResultsPseudoHeap}->AddKeyValuePair($ComparisonValue, $DatabaseCmpdID);
     219   }
     220
     221   if ($FingerprintsFilesInfo{Database}{CollectInputFileData}) {
     222     CollectDatabaseFileData($DatabaseCmpdID, $DatabaseFingerprintsFileIO);
     223   }
     224 }
     225
     226 # Initialize similarity results for individual or multiple reference molecules...
     227 #
     228 sub InitializeSimilaritySearchResults {
     229   my($ReferenceCmpdID);
     230
     231   %SimilaritySearchResults = ();
     232
     233   if ($SimilaritySearchInfo{IndividualReferenceMode}) {
     234     for $ReferenceCmpdID (@{$SimilaritySearchInfo{ReferenceCmpdIDsRef}}) {
     235       $SimilaritySearchResults{$ReferenceCmpdID} = new PseudoHeap('Type' => ($SimilaritySearchInfo{KeepTop} ? 'KeepTopN' : 'KeepBottomN'), 'KeyType' => 'Numeric', 'MaxSize' => $OptionsInfo{MaxSimilarMolecules});
     236     }
     237   }
     238   elsif ($SimilaritySearchInfo{MultipleReferencesMode}) {
     239     $SimilaritySearchResults{ResultsPseudoHeap} = new PseudoHeap('Type' => ($SimilaritySearchInfo{KeepTop} ? 'KeepTopN' : 'KeepBottomN'), 'KeyType' => 'Numeric', 'MaxSize' => $OptionsInfo{MaxSimilarMolecules});
     240   }
     241
     242   %DatabaseFingerprintsFileData = ();
     243 }
     244
     245 # Write out results SD and/or CSV/TSV text files for individual or multiple reference molecules...
     246 #
     247 sub WriteSimilaritySearchResultFiles {
     248   my($NewSDFileRef, $NewTextFileRef, $ReferenceCmpdID, $DatabaseCmpdID, $ComparisonValue);
     249
     250   ($NewSDFileRef, $NewTextFileRef) = SetupAndOpenOutputFiles();
     251
     252   if ($SimilaritySearchInfo{IndividualReferenceMode}) {
     253     for $ReferenceCmpdID (@{$SimilaritySearchInfo{ReferenceCmpdIDsRef}}) {
     254       for $ComparisonValue ($SimilaritySearchResults{$ReferenceCmpdID}->GetSortedKeys()) {
     255         for $DatabaseCmpdID ($SimilaritySearchResults{$ReferenceCmpdID}->GetKeyValues($ComparisonValue)) {
     256           WriteDataToOutputFiles($NewSDFileRef, $NewTextFileRef, $ComparisonValue, $DatabaseCmpdID, $ReferenceCmpdID);
     257         }
     258       }
     259     }
     260   }
     261   elsif ($SimilaritySearchInfo{MultipleReferencesMode}) {
     262     for $ComparisonValue ($SimilaritySearchResults{ResultsPseudoHeap}->GetSortedKeys()) {
     263       for $DatabaseCmpdID ($SimilaritySearchResults{ResultsPseudoHeap}->GetKeyValues($ComparisonValue)) {
     264         WriteDataToOutputFiles($NewSDFileRef, $NewTextFileRef, $ComparisonValue, $DatabaseCmpdID);
     265       }
     266     }
     267   }
     268
     269   if ($NewSDFileRef) {
     270     close $NewSDFileRef;
     271   }
     272   if ($NewTextFileRef) {
     273     close $NewTextFileRef;
     274   }
     275 }
     276
     277 # Write individual reference or multiple references similarity results along with any other data to output files...
     278 #
     279 sub WriteDataToOutputFiles {
     280   my($NewSDFileRef, $NewTextFileRef, $ComparisonValue, $DatabaseCmpdID, $ReferenceCmpdID) = @_;
     281
     282   if ($NewSDFileRef) {
     283     WriteMolStringDataToSDOutputFile($DatabaseCmpdID, $NewSDFileRef);
     284     if (defined $ReferenceCmpdID) {
     285       print $NewSDFileRef  ">  <ReferenceCmpdID>\n$ReferenceCmpdID\n\n";
     286     }
     287     print $NewSDFileRef  ">  <DatabaseCmpdID>\n$DatabaseCmpdID\n\n>  <ComparisonValue>\n$ComparisonValue\n\n";
     288     WriteDatabaseDataToSDOutputFile($DatabaseCmpdID, $NewSDFileRef);
     289     print $NewSDFileRef "\$\$\$\$\n";
     290   }
     291
     292   if ($NewTextFileRef) {
     293     my(@LineWords);
     294
     295     @LineWords = ();
     296     if (defined $ReferenceCmpdID) {
     297       push @LineWords, $ReferenceCmpdID;
     298     }
     299     push @LineWords, ($DatabaseCmpdID, $ComparisonValue);
     300
     301     if ($FingerprintsFilesInfo{Database}{OutputDataFields} || $FingerprintsFilesInfo{Database}{OutputDataCols}) {
     302       push @LineWords, RetrieveDatabaseDataForTextOutputFile($DatabaseCmpdID);
     303     }
     304     print $NewTextFileRef JoinWords(\@LineWords, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote}), "\n";
     305   }
     306 }
     307
     308 # Open output files...
     309 #
     310 sub SetupAndOpenOutputFiles {
     311   my($NewSDFileRef, $NewTextFileRef, $NewSDFile, $NewTextFile);
     312
     313   ($NewSDFileRef, $NewTextFileRef) = (undef) x 2;
     314
     315   if ($OptionsInfo{SDOutput}) {
     316     $NewSDFile = $OutputFilesInfo{SDOutFileName};
     317     print "Generating SD file $NewSDFile...\n";
     318     open NEWSDFILE, ">$NewSDFile" or die "Error: Couldn't open $NewSDFile: $! \n";
     319     $NewSDFileRef = \*NEWSDFILE;
     320   }
     321
     322   if ($OptionsInfo{TextOutput}) {
     323     $NewTextFile = $OutputFilesInfo{TextOutFileName};
     324     print "Generating text file $NewTextFile...\n";
     325     open NEWTEXTFILE, ">$NewTextFile" or die "Error: Couldn't open $NewTextFile: $! \n";
     326     $NewTextFileRef = \*NEWTEXTFILE;
     327
     328     WriteTextFileCoulmnLabels(\*NEWTEXTFILE);
     329   }
     330
     331   return ($NewSDFileRef, $NewTextFileRef);
     332 }
     333
     334 # Write out approriate column labels to text file...
     335 #
     336 sub WriteTextFileCoulmnLabels {
     337   my($NewTextFileRef) = @_;
     338   my($Line, @LineWords);
     339
     340   @LineWords = ();
     341
     342   if ($SimilaritySearchInfo{IndividualReferenceMode}) {
     343     push @LineWords, qw(ReferenceCompoundID DatabaseCompoundID ComparisonValue);
     344   }
     345   elsif ($SimilaritySearchInfo{MultipleReferencesMode}) {
     346     push @LineWords, qw(DatabaseCompoundID ComparisonValue);
     347   }
     348
     349   # Add columns for other database fingerprints file data to be written to output file...
     350   if ($FingerprintsFilesInfo{Database}{OutputDataFields}) {
     351     push @LineWords, @{$FingerprintsFilesInfo{Database}{DataFieldsToOutput}};
     352   }
     353   elsif ($FingerprintsFilesInfo{Database}{OutputDataCols}) {
     354     push @LineWords, @{$FingerprintsFilesInfo{Database}{DataColLabelsToOutput}};
     355   }
     356
     357   $Line = JoinWords(\@LineWords, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
     358   print $NewTextFileRef "$Line\n";
     359 }
     360
     361 # Write molecule string data to SD output file...
     362 #
     363 sub WriteMolStringDataToSDOutputFile {
     364   my($DatabaseCmpdID, $NewSDFileRef) = @_;
     365
     366   if ($FingerprintsFilesInfo{Database}{CollectCmpdStringData}) {
     367     my($MolString);
     368
     369     ($MolString) = split /M  END/, $DatabaseFingerprintsFileData{$DatabaseCmpdID};
     370     print $NewSDFileRef "$MolString\nM  END\n";
     371   }
     372   else {
     373     # Just write out an empty molecule data string...
     374     print $NewSDFileRef SDFileUtil::GenerateEmptyCtabBlockLines(), "\n";
     375   }
     376 }
     377
     378 # Write database data from SD or Text database file to SD output file...
     379 #
     380 sub WriteDatabaseDataToSDOutputFile {
     381   my($DatabaseCmpdID, $NewSDFileRef) = @_;
     382
     383   if ($FingerprintsFilesInfo{Database}{OutputDataFields}) {
     384     my($DataFieldLabel, $DataFieldValue, @CmpdLines, %DataFieldLabelAndValues);
     385
     386     @CmpdLines = split /\n/, $DatabaseFingerprintsFileData{$DatabaseCmpdID};
     387     %DataFieldLabelAndValues = GetCmpdDataHeaderLabelsAndValues(\@CmpdLines);
     388
     389     for $DataFieldLabel ($FingerprintsFilesInfo{Database}{OutputCurrentDataFields} ? GetCmpdDataHeaderLabels(\@CmpdLines) : @{$FingerprintsFilesInfo{Database}{DataFieldsToOutput}}) {
     390       $DataFieldValue = exists $DataFieldLabelAndValues{$DataFieldLabel} ? $DataFieldLabelAndValues{$DataFieldLabel} : '';
     391       print $NewSDFileRef  ">  <$DataFieldLabel>\n$DataFieldValue\n\n";
     392     }
     393   }
     394   elsif ($FingerprintsFilesInfo{Database}{OutputDataCols}) {
     395     my($DataColNum, $DataFieldLabel, $DataFieldValue);
     396
     397     for $DataColNum (@{$FingerprintsFilesInfo{Database}{DataColNumsToOutput}}) {
     398       $DataFieldLabel = $FingerprintsFilesInfo{Database}{DataColNumToLabelMap}{$DataColNum};
     399       $DataFieldValue =  $DatabaseFingerprintsFileData{$DatabaseCmpdID}->[$DataColNum];
     400       print $NewSDFileRef  ">  <$DataFieldLabel>\n$DataFieldValue\n\n";
     401     }
     402   }
     403 }
     404
     405 # Retriebe database data from SD or Text database file for text output file...
     406 #
     407 sub RetrieveDatabaseDataForTextOutputFile {
     408   my($DatabaseCmpdID) = @_;
     409
     410   if ($FingerprintsFilesInfo{Database}{OutputDataFields}) {
     411     my(@CmpdLines, %DataFieldLabelAndValues);
     412
     413     @CmpdLines = split /\n/, $DatabaseFingerprintsFileData{$DatabaseCmpdID};
     414     %DataFieldLabelAndValues = GetCmpdDataHeaderLabelsAndValues(\@CmpdLines);
     415
     416     return map { exists $DataFieldLabelAndValues{$_} ? $DataFieldLabelAndValues{$_} : ''} @{$FingerprintsFilesInfo{Database}{DataFieldsToOutput}};
     417   }
     418   elsif ($FingerprintsFilesInfo{Database}{OutputDataCols}) {
     419     if (exists $DatabaseFingerprintsFileData{$DatabaseCmpdID}) {
     420       return map { $DatabaseFingerprintsFileData{$DatabaseCmpdID}->[$_] } (0 .. $#{$FingerprintsFilesInfo{Database}{DataColNumsToOutput}});
     421     }
     422     else {
     423       return ('') x $#{$FingerprintsFilesInfo{Database}{DataColNumsToOutput}};
     424     }
     425   }
     426 }
     427
     428 # Collect database file SD compound string or CSV/TSV data line for generating results
     429 # files..
     430 #
     431 sub CollectDatabaseFileData {
     432   my($DatabaseCmpdID, $DatabaseFingerprintsFileIO) = @_;
     433
     434   if (exists $DatabaseFingerprintsFileData{$DatabaseCmpdID}) {
     435     return;
     436   }
     437
     438   if ($FingerprintsFilesInfo{Database}{CollectCmpdStringData}) {
     439     $DatabaseFingerprintsFileData{$DatabaseCmpdID} = $DatabaseFingerprintsFileIO->GetCompoundString();
     440   }
     441
     442   if ($FingerprintsFilesInfo{Database}{CollectDataLine}) {
     443     my(@DataLineWords);
     444     @DataLineWords = $DatabaseFingerprintsFileIO->GetDataLineWords();
     445     $DatabaseFingerprintsFileData{$DatabaseCmpdID} = \@DataLineWords;
     446   }
     447
     448 }
     449
     450 # Read fingerprints data from reference fingerprints file...
     451 #
     452 sub ReadReferenceFingerprintsData {
     453   my($FingerprintsFileIO);
     454
     455   $FingerprintsFileIO = Fingerprints::FingerprintsFileUtil::NewFingerprintsFileIO(%{$FingerprintsFilesInfo{Reference}{FingerprintsFileIOParameters}});
     456   ($SimilaritySearchInfo{ReferenceCmpdIDsRef}, $SimilaritySearchInfo{ReferenceFingerprintsObjectsRef}) = Fingerprints::FingerprintsFileUtil::ReadAndProcessFingerpritsData($FingerprintsFileIO);
     457
     458 }
     459
     460 # Retrieve information about fingerprints files...
     461 #
     462 sub RetrieveFingerprintsFilesInfo {
     463
     464   %FingerprintsFilesInfo = ();
     465   %OutputFilesInfo = ();
     466   %SimilaritySearchInfo = ();
     467
     468   %{$FingerprintsFilesInfo{Reference}} = ();
     469   %{$FingerprintsFilesInfo{Database}} = ();
     470
     471   # Set up reference and database file names...
     472   $FingerprintsFilesInfo{Reference}{FileName} = $FingerprintsFilesList[0];
     473   $FingerprintsFilesInfo{Database}{FileName} = $FingerprintsFilesList[1];
     474
     475   # Retrieve information about reference and database fingerprints file...
     476   RetrieveReferenceFingerprintsFileInfo();
     477   RetrieveDatabaseFingerprintsFileInfo();
     478
     479   # Setup fingerprints comparison method and associated method parameters...
     480   SetupReferenceAndDatabaseFingerprintsComparisonInfo();
     481
     482   # Retrieve information for output files...
     483   RetrieveOutputFilesInfo();
     484 }
     485
     486 # Setup refrerence and database fingerprints comparison method and associated method parameters...
     487 #
     488 sub SetupReferenceAndDatabaseFingerprintsComparisonInfo {
     489
     490   # Make sure reference and database fingerprints string match...
     491   if (($FingerprintsFilesInfo{Reference}{FirstFingerprintsStringType} !~ /^$FingerprintsFilesInfo{Database}{FirstFingerprintsStringType}$/i) ||
     492      ($FingerprintsFilesInfo{Reference}{FingerprintsBitVectorStringMode} != $FingerprintsFilesInfo{Database}{FingerprintsBitVectorStringMode}) ||
     493      ($FingerprintsFilesInfo{Reference}{FingerprintsVectorStringMode} != $FingerprintsFilesInfo{Database}{FingerprintsVectorStringMode}) ) {
     494     die "Error: First reference fingerprints string type, $FingerprintsFilesInfo{Reference}{FirstFingerprintsStringType}, must match first database fingerprints type, $FingerprintsFilesInfo{Database}{FirstFingerprintsStringType}.\n";
     495   }
     496
     497   if ($FingerprintsFilesInfo{Reference}{FirstFingerprintsStringDescription} !~ /^$FingerprintsFilesInfo{Database}{FirstFingerprintsStringDescription}$/i) {
     498     warn "Warning: First reference fingerprints string description, $FingerprintsFilesInfo{Reference}{FirstFingerprintsStringDescription}, doesn't match first database fingerprints string description, $FingerprintsFilesInfo{Database}{FirstFingerprintsStringDescription}.\n";
     499   }
     500
     501   # Setup individual reference and multiple references search mode...
     502   $SimilaritySearchInfo{IndividualReferenceMode} = undef;
     503   $SimilaritySearchInfo{MultipleReferencesMode} = undef;
     504
     505   if ($OptionsInfo{Mode} =~ /^IndividualReference$/i) {
     506     $SimilaritySearchInfo{IndividualReferenceMode} = 1;
     507   }
     508   elsif ($OptionsInfo{Mode} =~ /^MultipleReferences$/i) {
     509     $SimilaritySearchInfo{MultipleReferencesMode} = 1;
     510   }
     511   else {
     512     die "Error: The value specified, $Options{mode}, for option \"-m, --mode\" is not valid. Allowed values: IndividualReference, MultipleReferences\n";
     513   }
     514
     515   # Set up reference and database fingerprints similarity search method and paramaters...
     516   my($ComparisonMeasure, $ComparisonMethod, $ApplyComparisonCutoff, $ComparisonCutoff, $KeepTop, @ComparisonMethodParameters);
     517
     518   $SimilaritySearchInfo{ComparisonMethod} = '';
     519   @{$SimilaritySearchInfo{ComparisonMethodParameters}} = ();
     520
     521   $SimilaritySearchInfo{ComparisonCutoff} = '';
     522   $SimilaritySearchInfo{KeepTop} = '';
     523
     524   $ComparisonMeasure = ''; $ComparisonMethod = '';
     525   @ComparisonMethodParameters = ();
     526
     527   FINGERPRINTSTYPE: {
     528     if ($FingerprintsFilesInfo{Reference}{FingerprintsBitVectorStringMode}) {
     529       $ComparisonMeasure = $OptionsInfo{SpecifiedBitVectorComparisonMeasure};
     530       $ComparisonMethod = $OptionsInfo{SpecifiedBitVectorComparisonMeasureMethod};
     531
     532       if ($ComparisonMeasure =~ /^TverskySimilarity$/i) {
     533         push @ComparisonMethodParameters, $OptionsInfo{Alpha};
     534       }
     535       elsif ($ComparisonMeasure =~ /^WeightedTverskySimilarity$/i) {
     536         push @ComparisonMethodParameters, $OptionsInfo{Alpha};
     537         push @ComparisonMethodParameters, $OptionsInfo{Beta};
     538       }
     539       elsif ($ComparisonMeasure =~ /^WeightedTanimotoSimilarity$/i) {
     540         push @ComparisonMethodParameters, $OptionsInfo{Beta};
     541       }
     542
     543       last FINGERPRINTSTYPE;
     544     }
     545     if ($FingerprintsFilesInfo{Reference}{FingerprintsVectorStringMode}) {
     546       my($SkipValuesCheck);
     547
     548       $ComparisonMeasure = $OptionsInfo{SpecifiedVectorComparisonMeasure};
     549       $ComparisonMethod = $OptionsInfo{SpecifiedVectorComparisonMeasuresMethod};
     550
     551       push @ComparisonMethodParameters, $OptionsInfo{SpecifiedVectorComparisonMode};
     552
     553       $SkipValuesCheck = $OptionsInfo{Fast} ? 1 : 0;
     554       push @ComparisonMethodParameters, $SkipValuesCheck;
     555
     556       last FINGERPRINTSTYPE;
     557     }
     558     die "Error: Uknown fingerprints string type. Supported values: FingerprintsBitVectorString or FingerprintsVectorString.\n";
     559   }
     560
     561   $ApplyComparisonCutoff = $SimilaritySearchInfo{IndividualReferenceMode} ? 1 : (($SimilaritySearchInfo{MultipleReferencesMode} && $OptionsInfo{GroupFusionApplyCutoff}) ? 1 : 0);
     562
     563   $ComparisonCutoff = ''; $KeepTop = '';
     564   if ($ComparisonMethod =~ /Distance/i) {
     565     $ComparisonCutoff = $OptionsInfo{DistanceCutoff};
     566     $KeepTop = ($OptionsInfo{SearchMode} =~ /^SimilaritySearch$/i) ? 0 : 1;
     567   }
     568   else {
     569     $ComparisonCutoff = $OptionsInfo{SimilarityCutoff};
     570     $KeepTop = ($OptionsInfo{SearchMode} =~ /^SimilaritySearch$/i) ? 1 : 0;
     571   }
     572
     573   $SimilaritySearchInfo{ComparisonMethod} = $ComparisonMethod;
     574   @{$SimilaritySearchInfo{ComparisonMethodParameters}} = @ComparisonMethodParameters;
     575
     576   $SimilaritySearchInfo{ComparisonCutoff} = $ComparisonCutoff;
     577   $SimilaritySearchInfo{KeepTop} = $KeepTop;
     578   $SimilaritySearchInfo{ApplyComparisonCutoff} = $ApplyComparisonCutoff;
     579
     580   # Setup references to group fusion methods...
     581   $SimilaritySearchInfo{GroupFusionMethodRef} = undef;
     582   $SimilaritySearchInfo{ApplyPrecisionDuringFusion} = undef;
     583
     584   FUSIONRULE: {
     585     if ($OptionsInfo{GroupFusionRule} =~ /^Max$/i) {
     586       # It's always the first value in the appropriated sorted list using value of KeepTop...
     587       $SimilaritySearchInfo{GroupFusionMethodRef} = sub { my($ComparisonValuesRef) = @_; return $ComparisonValuesRef->[0]; };
     588       last FUSIONRULE;
     589     }
     590     if ($OptionsInfo{GroupFusionRule} =~ /^Min$/i) {
     591       # It's always the last value in the appropriated sorted list using value of KeepTop...
     592       $SimilaritySearchInfo{GroupFusionMethodRef} = sub { my($ComparisonValuesRef) = @_; return $ComparisonValuesRef->[$#{$ComparisonValuesRef}]; };
     593       last FUSIONRULE;
     594     }
     595     if ($OptionsInfo{GroupFusionRule} =~ /^Mean$/i) {
     596       $SimilaritySearchInfo{GroupFusionMethodRef} = \&StatisticsUtil::Mean;
     597       $SimilaritySearchInfo{ApplyPrecisionDuringFusion} = 1;
     598       last FUSIONRULE;
     599     }
     600     if ($OptionsInfo{GroupFusionRule} =~ /^Median$/i) {
     601       $SimilaritySearchInfo{GroupFusionMethodRef} = \&StatisticsUtil::Median;
     602       $SimilaritySearchInfo{ApplyPrecisionDuringFusion} = 1;
     603       last FUSIONRULE;
     604     }
     605     if ($OptionsInfo{GroupFusionRule} =~ /^Sum$/i) {
     606       $SimilaritySearchInfo{GroupFusionMethodRef} = \&StatisticsUtil::Sum;
     607       $SimilaritySearchInfo{ApplyPrecisionDuringFusion} = 1;
     608       last FUSIONRULE;
     609     }
     610     if ($OptionsInfo{GroupFusionRule} =~ /^Euclidean$/i) {
     611       $SimilaritySearchInfo{GroupFusionMethodRef} = \&StatisticsUtil::Euclidean;
     612       $SimilaritySearchInfo{ApplyPrecisionDuringFusion} = 1;
     613       last FUSIONRULE;
     614     }
     615     die "Error: The value specified, $Options{groupfusionrule}, for option \"-g, --GroupFusionRule\" is not valid. Allowed values: Max, Min, Mean, Median, Sum, Euclidean\n";
     616   }
     617
     618   $SimilaritySearchInfo{UsekNN} = ($OptionsInfo{kNN} !~ /^All$/i) ? 1 : 0;
     619   $SimilaritySearchInfo{SortComparisonValues} = (($OptionsInfo{GroupFusionRule} =~ /^(Max|Min)$/i) || $SimilaritySearchInfo{UsekNN}) ? 1 : 0;
     620 }
     621
     622 # Retrieve information about reference fingerprints file...
     623 #
     624 sub RetrieveReferenceFingerprintsFileInfo {
     625   my($FingerprintsFile, $FileType, $InDelim, $FingerprintsFileIO, $FingerprintsStringMode, $FingerprintsBitVectorStringMode, $FingerprintsVectorStringMode, $FirstFingerprintsStringType, $FirstFingerprintsStringDescription);
     626
     627   $FingerprintsFile = $FingerprintsFilesInfo{Reference}{FileName};
     628   ($FileType, $InDelim) =  RetrieveFingerprintsFileInfo($FingerprintsFile);
     629
     630   $FingerprintsFilesInfo{Reference}{FileType} = $FileType;
     631   $FingerprintsFilesInfo{Reference}{InDelim} = $InDelim;
     632
     633   # Setup reference FingerprintsFileIO parameters...
     634   %{$FingerprintsFilesInfo{Reference}{FingerprintsFileIOParameters}} = RetrieveFingerprintsFileIOParameters('Reference', $FileType, $FingerprintsFile);
     635
     636   # Make sure reference fingerprints data file contains valid and retrieve fingerprints string mode information...
     637   ($FingerprintsStringMode, $FingerprintsBitVectorStringMode, $FingerprintsVectorStringMode, $FirstFingerprintsStringType, $FirstFingerprintsStringDescription) = RetrieveFingerprintsFileFingerprintsStringInfo('Reference', $FingerprintsFile);
     638   $FingerprintsFilesInfo{Reference}{FingerprintsStringMode} = $FingerprintsStringMode;
     639   $FingerprintsFilesInfo{Reference}{FingerprintsBitVectorStringMode} = $FingerprintsBitVectorStringMode;
     640   $FingerprintsFilesInfo{Reference}{FingerprintsVectorStringMode} = $FingerprintsVectorStringMode;
     641   $FingerprintsFilesInfo{Reference}{FirstFingerprintsStringType} = $FirstFingerprintsStringType;
     642   $FingerprintsFilesInfo{Reference}{FirstFingerprintsStringDescription} = $FirstFingerprintsStringDescription;
     643
     644 }
     645
     646 # Retrieve information about database fingerprints file...
     647 #
     648 sub RetrieveDatabaseFingerprintsFileInfo {
     649   my($FingerprintsFile, $FileType, $InDelim, $FingerprintsFileIO, $FingerprintsStringMode, $FingerprintsBitVectorStringMode, $FingerprintsVectorStringMode, $FirstFingerprintsStringType, $FirstFingerprintsStringDescription);
     650
     651   $FingerprintsFile = $FingerprintsFilesInfo{Database}{FileName};
     652   ($FileType, $InDelim) =  RetrieveFingerprintsFileInfo($FingerprintsFile);
     653
     654   $FingerprintsFilesInfo{Database}{FileType} = $FileType;
     655   $FingerprintsFilesInfo{Database}{InDelim} = $InDelim;
     656
     657   # Setup reference FingerprintsFileIO parameters...
     658   %{$FingerprintsFilesInfo{Database}{FingerprintsFileIOParameters}} = RetrieveFingerprintsFileIOParameters('Database', $FileType, $FingerprintsFile);
     659
     660   # Make sure database fingerprints data file contains valid and retrieve fingerprints string mode information...
     661   ($FingerprintsStringMode, $FingerprintsBitVectorStringMode, $FingerprintsVectorStringMode, $FirstFingerprintsStringType, $FirstFingerprintsStringDescription) = RetrieveFingerprintsFileFingerprintsStringInfo('Database', $FingerprintsFile);
     662   $FingerprintsFilesInfo{Database}{FingerprintsStringMode} = $FingerprintsStringMode;
     663   $FingerprintsFilesInfo{Database}{FingerprintsBitVectorStringMode} = $FingerprintsBitVectorStringMode;
     664   $FingerprintsFilesInfo{Database}{FingerprintsVectorStringMode} = $FingerprintsVectorStringMode;
     665   $FingerprintsFilesInfo{Database}{FirstFingerprintsStringType} = $FirstFingerprintsStringType;
     666   $FingerprintsFilesInfo{Database}{FirstFingerprintsStringDescription} = $FirstFingerprintsStringDescription;
     667
     668   # Retrieve database fingerprints data field information for output file...
     669   #
     670   RetrieveDatabaseFingerprintsDataFieldsInfo($FingerprintsFile, $FileType, $InDelim);
     671
     672   # Retrieve database fingerprints text file data columns information for output file...
     673   #
     674   RetrieveDatabaseFingerprintsDataColsInfo($FingerprintsFile, $FileType, $InDelim);
     675
     676   # Any need to collect database compound string or data line for generation of results files...
     677   $FingerprintsFilesInfo{Database}{CollectCmpdStringData} = ($FileType =~ /^SD$/i) ? 1 : 0;
     678   $FingerprintsFilesInfo{Database}{CollectDataLine} = ($FileType =~ /^Text$/i && $OptionsInfo{DatabaseDataColsMode} =~ /^(All|Specify)$/i) ? 1 : 0;
     679   $FingerprintsFilesInfo{Database}{CollectInputFileData} = ($FingerprintsFilesInfo{Database}{CollectCmpdStringData} || $FingerprintsFilesInfo{Database}{CollectDataLine}) ? 1 : 0;
     680
     681   # Set maximum number of similar compounds to find for individual reference of set of multiple
     682   # reference compounds...
     683   #
     684   SetMaximumSimilarMoleculesToRetrieve($FingerprintsFile, $FileType, $InDelim);
     685 }
     686
     687 # Retrieve database fingerprints data field information...
     688 #
     689 sub RetrieveDatabaseFingerprintsDataFieldsInfo {
     690   my($FingerprintsFile, $FileType, $InDelim) = @_;
     691   my($CollectDataFields, $CmpdCount, $AllDataFieldsRef, $CommonDataFieldsRef, @DataFieldsToOutput);
     692
     693   $FingerprintsFilesInfo{Database}{OutputDataFields} = 0;
     694   @{$FingerprintsFilesInfo{Database}{DataFieldsToOutput}} = ();
     695
     696   $FingerprintsFilesInfo{Database}{OutputCurrentDataFields} = 0;
     697
     698   @{$FingerprintsFilesInfo{Database}{AllDataFields}} = ();
     699   @{$FingerprintsFilesInfo{Database}{CommonDataFields}} = ();
     700   @{$FingerprintsFilesInfo{Database}{SpecifiedDatabaseDataFields}} = ();
     701
     702   if ($FileType !~ /^SD$/i) {
     703     return;
     704   }
     705
     706   # No need to go over SD file and collect data fields for SD file during All DatabaseDataFieldsMode as
     707   # they would be retrieved from database SD file compound string during generation of output files...
     708   #
     709   $CollectDataFields = (($OptionsInfo{TextOutput} && $OptionsInfo{DatabaseDataFieldsMode} =~ /^(All|Common)$/i) || ($OptionsInfo{SDOutput} && $OptionsInfo{DatabaseDataFieldsMode} =~ /^Common$/i)) ? 1 : 0;
     710
     711   ($CmpdCount, $AllDataFieldsRef, $CommonDataFieldsRef) = (undef) x 2;
     712
     713   if ($CollectDataFields) {
     714     open SDFILE, "$FingerprintsFile" or die "Error: Couldn't open $FingerprintsFile: $! \n";
     715     ($CmpdCount, $AllDataFieldsRef, $CommonDataFieldsRef) = GetAllAndCommonCmpdDataHeaderLabels(\*SDFILE);
     716     close SDFILE;
     717   }
     718
     719   @DataFieldsToOutput = ();
     720   if ($OptionsInfo{DatabaseDataFieldsMode} =~ /^All$/i) {
     721     if (defined $AllDataFieldsRef) {
     722       push @DataFieldsToOutput, @{$AllDataFieldsRef};
     723       push @{$FingerprintsFilesInfo{Database}{AllDataFields}}, @{$AllDataFieldsRef};
     724     }
     725     else {
     726       # Retrieve and output data fields and values dynamically...
     727       $FingerprintsFilesInfo{Database}{OutputCurrentDataFields} = 1;
     728     }
     729   }
     730   elsif ($OptionsInfo{DatabaseDataFieldsMode} =~ /^Common$/i) {
     731     if (defined $CommonDataFieldsRef) {
     732       push @DataFieldsToOutput, @{$CommonDataFieldsRef};
     733       push @{$FingerprintsFilesInfo{Database}{CommonDataFields}}, @{$CommonDataFieldsRef};
     734     }
     735   }
     736   elsif ($OptionsInfo{DatabaseDataFieldsMode} =~ /^Specify$/i) {
     737     push @DataFieldsToOutput, @{$OptionsInfo{SpecifiedDatabaseDataFields}};
     738     push @{$FingerprintsFilesInfo{Database}{SpecifiedDatabaseDataFields}}, @{$OptionsInfo{SpecifiedDatabaseDataFields}};
     739   }
     740
     741   if ($OptionsInfo{DatabaseDataFieldsMode} !~ /^CompoundID$/i) {
     742     $FingerprintsFilesInfo{Database}{OutputDataFields} = 1;
     743   }
     744
     745   push @{$FingerprintsFilesInfo{Database}{DataFieldsToOutput}}, @DataFieldsToOutput;
     746
     747 }
     748
     749 # Retrieve database fingerprints data columns information...
     750 #
     751 sub RetrieveDatabaseFingerprintsDataColsInfo {
     752   my($FingerprintsFile, $FileType, $InDelim) = @_;
     753   my($Line, $ColNum, $ColLabel, $NumOfCols, @DataColLabels, @DataColLabelsToOutput, @DataColNumsToOutput, %DataColLabelToNumMap, %DataColNumToLabelMap);
     754
     755   $FingerprintsFilesInfo{Database}{OutputDataCols} = 0;
     756
     757   @{$FingerprintsFilesInfo{Database}{DataColLabels}} = ();
     758   %{$FingerprintsFilesInfo{Database}{DataColLabelToNumMap}} = ();
     759   %{$FingerprintsFilesInfo{Database}{DataColNumToLabelMap}} = ();
     760
     761   @{$FingerprintsFilesInfo{Database}{DataColNumsToOutput}} = ();
     762   @{$FingerprintsFilesInfo{Database}{DataColLabelsToOutput}} = ();
     763
     764   if ($FileType !~ /^Text$/i) {
     765     return;
     766   }
     767
     768   @DataColLabels = ();
     769   @DataColLabelsToOutput = ();
     770   @DataColNumsToOutput = ();
     771
     772   %DataColLabelToNumMap = ();
     773   %DataColNumToLabelMap = ();
     774
     775   # Get column label line...
     776   open TEXTFILE, "$FingerprintsFile" or die "Error: Couldn't open $FingerprintsFile: $! \n";
     777   $Line = TextUtil::GetTextLine(\*TEXTFILE);
     778   close TEXTFILE;
     779
     780   $InDelim = ($InDelim =~ /^Tab$/i) ? "\t" : ($InDelim =~ /semicolon/i ? "\;" : "\,");
     781
     782   @DataColLabels = TextUtil::SplitWords($Line, $InDelim);
     783   $NumOfCols = scalar @DataColLabels;
     784
     785   for $ColNum (0 .. $#DataColLabels) {
     786     $ColLabel = $DataColLabels[$ColNum];
     787     $DataColLabelToNumMap{$ColLabel} = $ColNum;
     788     $DataColNumToLabelMap{$ColNum} = $ColLabel;
     789   }
     790
     791   if ($OptionsInfo{DatabaseDataColsMode} =~ /^Specify$/i) {
     792     if ($OptionsInfo{DatabaseColMode} =~ /^ColNum$/i) {
     793       for $ColNum (@{$OptionsInfo{SpecifiedDatabaseDataCols}}) {
     794         if ($ColNum > $NumOfCols) {
     795           die "Error: Column number, $ColNum, specified using \"--DatabaseDataCols\" is not valid: It must be <= $NumOfCols\n";
     796         }
     797         push @DataColNumsToOutput, ($ColNum - 1);
     798       }
     799     }
     800     elsif ($OptionsInfo{DatabaseColMode} =~ /^ColLabel$/i) {
     801       for $ColLabel (@{$OptionsInfo{SpecifiedDatabaseDataCols}}) {
     802         if (!exists $DataColLabelToNumMap{$ColLabel}) {
     803           die "Error: Column label, $ColLabel, specified using \"--DatabaseDataCols\" is not valid: It doesn't exist\n";
     804         }
     805         push @DataColNumsToOutput, $DataColLabelToNumMap{$ColLabel};
     806       }
     807     }
     808   }
     809   elsif ($OptionsInfo{DatabaseDataColsMode} =~ /^All$/i) {
     810     @DataColNumsToOutput = map { $_ } (0 .. $#DataColLabels);
     811   }
     812
     813   # Setup data column labels to output...
     814   if (scalar @DataColNumsToOutput) {
     815     @DataColLabelsToOutput = map { $DataColNumToLabelMap{$_} } (0 .. $#DataColNumsToOutput);
     816   }
     817
     818   $FingerprintsFilesInfo{Database}{OutputDataCols} = scalar @DataColNumsToOutput ? 1 : 0;
     819
     820   @{$FingerprintsFilesInfo{Database}{DataColLabels}} = @DataColLabels;
     821   %{$FingerprintsFilesInfo{Database}{DataColLabelToNumMap}} = %DataColLabelToNumMap;
     822   %{$FingerprintsFilesInfo{Database}{DataColNumToLabelMap}} = %DataColNumToLabelMap;
     823
     824   @{$FingerprintsFilesInfo{Database}{DataColNumsToOutput}} = @DataColNumsToOutput;
     825   @{$FingerprintsFilesInfo{Database}{DataColLabelsToOutput}} = @DataColLabelsToOutput;
     826 }
     827
     828 # Set maximum number of similar compounds to find for individual reference of set of multiple
     829 # reference compounds...
     830 #
     831 sub SetMaximumSimilarMoleculesToRetrieve {
     832   my($FingerprintsFile, $FileType, $InDelim) = @_;
     833   my($MaxSimilarMolecules, $NumOfDatabaseMolecules, $PercentSimilarMolecules, $Line);
     834
     835   if ($OptionsInfo{SimilarCountMode} !~ /^PercentSimilar$/i) {
     836     return;
     837   }
     838
     839   $PercentSimilarMolecules = $OptionsInfo{PercentSimilarMolecules};
     840
     841   # Count database entries to figure out MaxSimilarMolecules using PercentSimilarMolecules
     842   # value...
     843   $NumOfDatabaseMolecules = 0;
     844   if ($FileType =~ /^SD$/i && exists($FingerprintsFilesInfo{Database}{NumOfDatabaseMolecules})) {
     845     # It might already be counted for SD file...
     846     $NumOfDatabaseMolecules = $FingerprintsFilesInfo{Database}{NumOfDatabaseMolecules};
     847   }
     848   else {
     849     print "Calculating maximum number of similar molecules to retrieve for \"PercentSimilar\" value of \"--SimilarCountMode\" option by counting number of molecules in database fingerprints file...\n";
     850     open FINGERPRINTSFILE, "$FingerprintsFile" or die "Error: Couldn't open $FingerprintsFile: $! \n";
     851     FILETYPE: {
     852       if ($FileType =~ /^SD$/i) {
     853         while ($Line = TextUtil::GetTextLine(\*FINGERPRINTSFILE)) {
     854           if ($Line =~ /^\$\$\$\$/) {
     855             $NumOfDatabaseMolecules++;
     856           }
     857         }
     858         last FILETYPE;
     859       }
     860       if ($FileType =~ /^Text$/i) {
     861         # Ignore column label line...
     862         $Line = TextUtil::GetTextLine(\*FINGERPRINTSFILE);
     863         while ($Line = TextUtil::GetTextLine(\*FINGERPRINTSFILE)) {
     864           $NumOfDatabaseMolecules++;
     865         }
     866         last FILETYPE;
     867       }
     868       if ($FileType =~ /^FP$/i) {
     869         while ($Line = TextUtil::GetTextLine(\*FINGERPRINTSFILE)) {
     870           if ($Line !~ /^#/) {
     871             $NumOfDatabaseMolecules++;
     872           }
     873         }
     874         last FILETYPE;
     875       }
     876       $NumOfDatabaseMolecules = 0;
     877     }
     878     close FINGERPRINTSFILE;
     879     $FingerprintsFilesInfo{Database}{NumOfDatabaseMolecules} = $NumOfDatabaseMolecules;
     880   }
     881
     882   $MaxSimilarMolecules = int (($NumOfDatabaseMolecules * $PercentSimilarMolecules)/100);
     883   if ($MaxSimilarMolecules < 1) {
     884     $MaxSimilarMolecules = 1;
     885   }
     886
     887   $OptionsInfo{MaxSimilarMolecules} = $MaxSimilarMolecules;
     888 }
     889
     890 # Retrieve information about fingerprints file...
     891 #
     892 sub RetrieveFingerprintsFileInfo {
     893   my($FingerprintsFile) = @_;
     894   my($FileType, $InDelim, $FileDir, $FileExt, $FileName);
     895
     896   if (!(-e $FingerprintsFile)) {
     897     die "Error: Input fingerprints file, $FingerprintsFile, doesn't exist.\n";
     898   }
     899
     900   $FileType = Fingerprints::FingerprintsFileUtil::GetFingerprintsFileType($FingerprintsFile);
     901   if (IsEmpty($FileType)) {
     902     die "Error: Input file, $FingerprintsFile, is not a fingerprints file.\n";
     903   }
     904
     905   $InDelim = '';
     906   if ($FileType =~ /^Text$/i) {
     907     $FileDir = ""; $FileName = ""; $FileExt = "";
     908     ($FileDir, $FileName, $FileExt) = ParseFileName($FingerprintsFile);
     909     $InDelim = ($FileExt =~ /^tsv$/i) ? 'Tab' : $OptionsInfo{InDelim};
     910   }
     911
     912   return ($FileType, $InDelim);
     913 }
     914
     915 # Retrieve fingerprints file IO parameters...
     916 #
     917 sub RetrieveFingerprintsFileIOParameters {
     918   my($FingerprintsFileMode, $FileType, $FingerprintsFile) = @_;
     919   my(%FingerprintsFileIOParams);
     920
     921   if ($FingerprintsFileMode !~ /^(Reference|Database)$/) {
     922     die "Error: Unknown fingerprints file mode: $FingerprintsFileMode. Supported values: Reference or Database\n";
     923   }
     924
     925   %FingerprintsFileIOParams = ();
     926
     927   FILETYPE: {
     928     if ($FileType =~ /^SD$/i) {
     929       %FingerprintsFileIOParams = ('Name' => $FingerprintsFile, 'Mode' => 'Read', 'FingerprintsStringMode' => $OptionsInfo{FingerprintsMode}, 'ValidateData' => $OptionsInfo{ValidateData}, 'DetailLevel' =>  $OptionsInfo{Detail}, 'FingerprintsFieldLabel' => $OptionsInfo{"${FingerprintsFileMode}FingerprintsField"}, 'CompoundIDMode' => $OptionsInfo{"${FingerprintsFileMode}CompoundIDMode"}, 'CompoundIDFieldLabel' => $OptionsInfo{"${FingerprintsFileMode}CompoundIDField"}, 'CompoundIDPrefix' => $OptionsInfo{"${FingerprintsFileMode}CompoundIDPrefix"});
     930       last FILETYPE;
     931     }
     932     if ($FileType =~ /^FP$/i) {
     933       %FingerprintsFileIOParams = ('Name' => $FingerprintsFile, 'Mode' => 'Read', 'FingerprintsStringMode' => $OptionsInfo{FingerprintsMode}, 'ValidateData' => $OptionsInfo{ValidateData}, 'DetailLevel' =>  $OptionsInfo{Detail});
     934       last FILETYPE;
     935     }
     936     if ($FileType =~ /^Text$/i) {
     937       %FingerprintsFileIOParams = ('Name' => $FingerprintsFile, 'Mode' => 'Read', 'FingerprintsStringMode' => $OptionsInfo{FingerprintsMode}, 'ValidateData' => $OptionsInfo{ValidateData}, 'DetailLevel' =>  $OptionsInfo{Detail}, 'FingerprintsCol' => $OptionsInfo{"${FingerprintsFileMode}FingerprintsCol"}, 'ColMode' => $OptionsInfo{"${FingerprintsFileMode}ColMode"}, 'CompoundIDCol' => $OptionsInfo{"${FingerprintsFileMode}CompoundIDCol"}, 'CompoundIDPrefix' => $OptionsInfo{"${FingerprintsFileMode}CompoundIDPrefix"}, 'InDelim' => $FingerprintsFilesInfo{$FingerprintsFileMode}{InDelim});
     938       last FILETYPE;
     939     }
     940     die "Error: Fingerprints file type, $FileType, is not valid. Supported file types: SD, FP or Text\n";
     941   }
     942
     943   return %FingerprintsFileIOParams;
     944 }
     945
     946 # Make sure fingerprints data file contains valid dta and retrieve fingerprints string mode information...
     947 #
     948 sub RetrieveFingerprintsFileFingerprintsStringInfo {
     949   my($FingerprintsFileMode, $FingerprintsFile) = @_;
     950   my($FingerprintsFileIO, $FingerprintsStringMode, $FingerprintsBitVectorStringMode, $FingerprintsVectorStringMode, $FirstFingerprintsStringType, $FirstFingerprintsStringDescription);
     951
     952   $FingerprintsFileIO = Fingerprints::FingerprintsFileUtil::NewFingerprintsFileIO(%{$FingerprintsFilesInfo{$FingerprintsFileMode}{FingerprintsFileIOParameters}});
     953   if (!$FingerprintsFileIO) {
     954     die "Error: Reference fingerprints file, $FingerprintsFile, contains invalid fingerprints data.\n";
     955   }
     956   if (!$FingerprintsFileIO->IsFingerprintsFileDataValid()) {
     957     die "Error: Reference fingerprints file, $FingerprintsFile, contains invalid fingerprints data.\n";
     958   }
     959
     960   $FingerprintsStringMode = $FingerprintsFileIO->GetFingerprintsStringMode();
     961   $FingerprintsBitVectorStringMode = $FingerprintsFileIO->GetFingerprintsBitVectorStringMode();
     962   $FingerprintsVectorStringMode = $FingerprintsFileIO->GetFingerprintsVectorStringMode();
     963
     964   $FirstFingerprintsStringType = $FingerprintsFileIO->GetFirstFingerprintsStringType();
     965   $FirstFingerprintsStringDescription = $FingerprintsFileIO->GetFirstFingerprintsStringDescription();
     966
     967   $FingerprintsFileIO->Close();
     968
     969   return ($FingerprintsStringMode, $FingerprintsBitVectorStringMode, $FingerprintsVectorStringMode, $FirstFingerprintsStringType, $FirstFingerprintsStringDescription);
     970 }
     971
     972 # Retrieve output files names using reference fingerprints file name...
     973 #
     974 sub RetrieveOutputFilesInfo {
     975   my($FingerprintsFile, $FileDir, $FileExt, $FileName, $OutFileRoot, $SDOutFileName, $TextOutFileName, $SDOutFileExt, $TextOutFileExt, $ReferenceFileName, $DatabaseFileName);
     976
     977   $OutputFilesInfo{OutFileRoot} = '';
     978   $OutputFilesInfo{SDOutFileName} = '';
     979   $OutputFilesInfo{TextOutFileName} = '';
     980
     981   $FingerprintsFile = $FingerprintsFilesInfo{Reference}{FileName};
     982
     983   $FileDir = ""; $FileName = ""; $FileExt = "";
     984   ($FileDir, $FileName, $FileExt) = ParseFileName($FingerprintsFile);
     985
     986   $SDOutFileExt = "sdf";
     987   $TextOutFileExt = ($Options{outdelim} =~ /^tab$/i) ? "tsv" : "csv";
     988
     989   if ($OptionsInfo{OutFileRoot}) {
     990     my ($RootFileDir, $RootFileName, $RootFileExt) = ParseFileName($OptionsInfo{OutFileRoot});
     991     if ($RootFileName && $RootFileExt) {
     992       $FileName = $RootFileName;
     993     }
     994     else {
     995       $FileName = $OptionsInfo{OutFileRoot};
     996     }
     997     $OutFileRoot = $FileName;
     998   }
     999   else {
    1000     $OutFileRoot = "${FileName}SimilaritySearching";
    1001   }
    1002
    1003   $SDOutFileName = "${OutFileRoot}.${SDOutFileExt}";
    1004   $TextOutFileName = "${OutFileRoot}.${TextOutFileExt}";
    1005
    1006   $ReferenceFileName = $FingerprintsFilesInfo{Reference}{FileName};
    1007   $DatabaseFileName = $FingerprintsFilesInfo{Database}{FileName};
    1008
    1009   if ($OptionsInfo{SDOutput}) {
    1010     if ($SDOutFileName =~ /^$ReferenceFileName$/i) {
    1011       die "Error: Same output, $SDOutFileName, and reference input file names.\nSpecify a different name using \"-r --root\" option or use default name.\n";
    1012     }
    1013     if ($SDOutFileName =~ /^$DatabaseFileName$/i) {
    1014       die "Error: Same output, $SDOutFileName, and database input file names.\nSpecify a different name using \"-r --root\" option or use default name.\n";
    1015     }
    1016   }
    1017
    1018   if ($OptionsInfo{TextOutput}) {
    1019     if ($TextOutFileName =~ /^$ReferenceFileName$/i) {
    1020       die "Error: Same output, $TextOutFileName, and reference input file names.\nSpecify a different name using \"-r --root\" option or use default name.\n";
    1021     }
    1022     if ($TextOutFileName =~ /^$DatabaseFileName$/i) {
    1023       die "Error: Same output, $TextOutFileName, and database input file names.\nSpecify a different name using \"-r --root\" option or use default name.\n";
    1024     }
    1025   }
    1026
    1027   if (!$OptionsInfo{OverwriteFiles}) {
    1028     if ($OptionsInfo{SDOutput}) {
    1029       if (-e $SDOutFileName) {
    1030         die "Error: The output file $SDOutFileName already exists.\n";
    1031       }
    1032     }
    1033     if ($OptionsInfo{TextOutput}) {
    1034       if (-e $TextOutFileName) {
    1035         die "Error: The output file $TextOutFileName already exists.\n";
    1036       }
    1037     }
    1038   }
    1039
    1040   $OutputFilesInfo{OutFileRoot} = $OutFileRoot;
    1041   $OutputFilesInfo{SDOutFileName} = $SDOutFileName;
    1042   $OutputFilesInfo{TextOutFileName} = $TextOutFileName;
    1043
    1044 }
    1045
    1046 # Process input fingerprints file names...
    1047 #
    1048 sub ProcessFingerprintsFileNames {
    1049   @FingerprintsFilesList = ();
    1050
    1051   if (@ARGV != 2) {
    1052     die GetUsageFromPod("$FindBin::Bin/$ScriptName");
    1053   }
    1054
    1055   # Reference fingerprints file name...
    1056   push @FingerprintsFilesList, $ARGV[0];
    1057
    1058   # Database fingerprints file name...
    1059   push @FingerprintsFilesList, $ARGV[1];
    1060
    1061 }
    1062
    1063 # Process option values...
    1064 sub ProcessOptions {
    1065   %OptionsInfo = ();
    1066
    1067   $OptionsInfo{Mode} = $Options{mode};
    1068   $OptionsInfo{FingerprintsMode} = $Options{fingerprintsmode};
    1069
    1070   $OptionsInfo{SearchMode} = $Options{searchmode};
    1071
    1072   ProcessBitVectorComparisonOptions();
    1073   ProcessVectorComparisonOptions();
    1074
    1075   $OptionsInfo{GroupFusionRule} = $Options{groupfusionrule};
    1076   $OptionsInfo{GroupFusionApplyCutoff} = ($Options{groupfusionapplycutoff} =~ /^Yes$/i) ? 1 : 0;;
    1077
    1078   $OptionsInfo{SimilarCountMode} = $Options{similarcountmode};
    1079   $OptionsInfo{NumOfSimilarMolecules} = $Options{numofsimilarmolecules};
    1080   $OptionsInfo{PercentSimilarMolecules} = $Options{percentsimilarmolecules};
    1081
    1082   # Set MaxSimilarMolecules to NumOfSimilarMolecules. For PercentSimilar value of SimilarCountMode,
    1083   # it'll be overwritten using number of entries in database fingerprints file and value of PercentSimilarMolecules...
    1084   #
    1085   $OptionsInfo{MaxSimilarMolecules} = $OptionsInfo{NumOfSimilarMolecules};
    1086
    1087   $OptionsInfo{SimilarityCutoff} = $Options{similaritycutoff};
    1088   $OptionsInfo{DistanceCutoff} = $Options{distancecutoff};
    1089
    1090   $OptionsInfo{kNN} = $Options{knn};
    1091   if ($Options{knn} !~ /^All$/i) {
    1092     if (!IsPositiveInteger($Options{knn})) {
    1093       die "Error: The value specified, $Options{knn}, for option \"-k, --KNN\" is not valid. Allowed values: > 0 \n";
    1094     }
    1095   }
    1096
    1097   ProcessReferenceFingerprintsDataOptions();
    1098   ProcessDatabaseFingerprintsDataOptions();
    1099
    1100   $OptionsInfo{Detail} = $Options{detail};
    1101
    1102   $OptionsInfo{InDelim} = $Options{indelim};
    1103   $OptionsInfo{OutDelim} = ($Options{outdelim} =~ /tab/i ) ? "\t" : (($Options{outdelim} =~ /semicolon/i) ? "\;" : "\,");
    1104   $OptionsInfo{OutQuote} = ($Options{quote} =~ /^Yes$/i) ? 1 : 0;
    1105
    1106   $OptionsInfo{Output} = $Options{output};
    1107   $OptionsInfo{SDOutput} = ($Options{output} =~ /^(SD|Both)$/i) ? 1 : 0;
    1108   $OptionsInfo{TextOutput} = ($Options{output} =~ /^(Text|Both)$/i) ? 1 : 0;
    1109
    1110   $OptionsInfo{OverwriteFiles} = $Options{overwrite} ? 1 : 0;
    1111   $OptionsInfo{OutFileRoot} = $Options{root} ? $Options{root} : 0;
    1112
    1113   $OptionsInfo{Fast} = $Options{fast} ? 1 : 0;
    1114   $OptionsInfo{ValidateData} = $Options{fast} ? 0 : 1;
    1115
    1116   $OptionsInfo{Precision} = $Options{precision};
    1117 }
    1118
    1119 # Process options related to comparion of bit vector strings...
    1120 #
    1121 sub ProcessBitVectorComparisonOptions {
    1122   # Setup supported bit vector similarity coefficients for bit vector strings...
    1123   my($ComparisonMeasure, $SupportedComparisonMeasure, @SupportedComparisonMeasures, %SupportedComparisonMeasuresNameMap, %SupportedComparisonMeasuresMethodMap);
    1124
    1125   @SupportedComparisonMeasures = ();
    1126   %SupportedComparisonMeasuresNameMap = ();
    1127   %SupportedComparisonMeasuresMethodMap = ();
    1128
    1129   for $SupportedComparisonMeasure (Fingerprints::FingerprintsBitVector::GetSupportedSimilarityCoefficients()) {
    1130     # Similarity coefficient function/method names contain "Coefficient" in their names.
    1131     # So take 'em out and setup a map to original function/method name...
    1132     $ComparisonMeasure = $SupportedComparisonMeasure;
    1133     $ComparisonMeasure =~ s/Coefficient$//;
    1134
    1135     push @SupportedComparisonMeasures, $ComparisonMeasure;
    1136     $SupportedComparisonMeasuresNameMap{lc($ComparisonMeasure)} = $ComparisonMeasure;
    1137     $SupportedComparisonMeasuresMethodMap{lc($ComparisonMeasure)} = $SupportedComparisonMeasure;
    1138   }
    1139
    1140   # Setup similarity coefficient to use for calculating similarity matrices for bit vector strings...
    1141   my($SpecifiedMeasure, $SpecifiedComparisonMeasureName, $SpecifiedComparisonMeasureMethod);
    1142
    1143   $SpecifiedComparisonMeasureName = '';
    1144   $SpecifiedComparisonMeasureMethod = '';
    1145
    1146   $SpecifiedMeasure = $Options{bitvectorcomparisonmode};
    1147
    1148   if (! exists $SupportedComparisonMeasuresMethodMap{lc($SpecifiedMeasure)} )  {
    1149       die "Error: The value specified, $SpecifiedMeasure, for option \"-b --BitVectorComparisonMode\" is not valid.\nAllowed values:", JoinWords(\@SupportedComparisonMeasures, ", ", 0), "\n";
    1150   }
    1151
    1152   $SpecifiedComparisonMeasureMethod = $SupportedComparisonMeasuresMethodMap{lc($SpecifiedMeasure)};
    1153   $SpecifiedComparisonMeasureName = $SupportedComparisonMeasuresNameMap{lc($SpecifiedMeasure)};
    1154
    1155   $OptionsInfo{BitVectorComparisonMode} = $Options{bitvectorcomparisonmode};
    1156
    1157   $OptionsInfo{SpecifiedBitVectorComparisonMeasure} = $SpecifiedMeasure;
    1158   $OptionsInfo{SpecifiedBitVectorComparisonMeasureName} = $SpecifiedComparisonMeasureName;
    1159   $OptionsInfo{SpecifiedBitVectorComparisonMeasureMethod} = $SpecifiedComparisonMeasureMethod;
    1160
    1161   # Make sure valid alpha parameter is specified for Tversky calculation...
    1162   $OptionsInfo{Alpha} = '';
    1163   if ($SpecifiedMeasure =~ /^(TverskySimilarity|WeightedTverskySimilarity)$/i) {
    1164     if (IsEmpty($Options{alpha})) {
    1165       die "Error: You must specify a value for \"-a, --alpha\" option in \"TverskySimilarity or WeightedTverskySimilarity\" \"-m --mode\". \n";
    1166     }
    1167     my($Alpha);
    1168     $Alpha = $Options{alpha};
    1169     if (!(IsFloat($Alpha) && $Alpha >=0 && $Alpha <= 1)) {
    1170       die "Error: The value specified, $Options{alpha}, for option \"-a, --alpha\" is not valid. Allowed values: >= 0 and <= 1\n";
    1171     }
    1172     $OptionsInfo{Alpha} = $Alpha;
    1173   }
    1174
    1175   # Make sure valid beta parameter is specified for WeightedTanimoto and WeightedTversky
    1176   # calculations...
    1177   $OptionsInfo{Beta} = '';
    1178   if ($SpecifiedMeasure =~ /^(WeightedTverskySimilarity|WeightedTanimotoSimilarity)$/i) {
    1179     if (IsEmpty($Options{beta})) {
    1180       die "Error: You must specify a value for \"-b, --beta\" option in \"WeightedTverskySimilarity or WeightedTanimotoSimilarity\" \"-m --mode\". \n";
    1181     }
    1182     my($Beta);
    1183     $Beta = $Options{beta};
    1184     if (!(IsFloat($Beta) && $Beta >=0 && $Beta <= 1)) {
    1185       die "Error: The value specified, $Options{beta}, for option \"-b, --beta\" is not valid. Allowed values: >= 0 and <= 1\n";
    1186     }
    1187     $OptionsInfo{Beta} = $Beta;
    1188   }
    1189 }
    1190
    1191 # Process options related to comparion of vector strings...
    1192 #
    1193 sub ProcessVectorComparisonOptions {
    1194   # Setup specified similarity coefficients for vector strings..
    1195   my($ComparisonMeasure, $SupportedComparisonMeasure, @SupportedComparisonMeasures, %SupportedComparisonMeasuresNameMap, %SupportedComparisonMeasuresMethodMap);
    1196
    1197   @SupportedComparisonMeasures = ();
    1198   %SupportedComparisonMeasuresNameMap = ();
    1199   %SupportedComparisonMeasuresMethodMap = ();
    1200   for $SupportedComparisonMeasure (Fingerprints::FingerprintsVector::GetSupportedDistanceAndSimilarityCoefficients()) {
    1201     # Similarity and distance coefficient function/method names contain "Coefficient" in their names.
    1202     # So take 'em out and setup a map to original function/method name...
    1203     $ComparisonMeasure = $SupportedComparisonMeasure;
    1204     if ($ComparisonMeasure =~ /Coefficient$/i) {
    1205       $ComparisonMeasure =~ s/Coefficient$//i;
    1206     }
    1207     push @SupportedComparisonMeasures, $ComparisonMeasure;
    1208     $SupportedComparisonMeasuresNameMap{lc($ComparisonMeasure)} = $ComparisonMeasure;
    1209     $SupportedComparisonMeasuresMethodMap{lc($ComparisonMeasure)} = $SupportedComparisonMeasure;
    1210   }
    1211
    1212   # Setup a list of similarity coefficients to use for calculating similarity matrices for bit vector strings...
    1213   my($SpecifiedMeasure, $SpecifiedComparisonMeasureName, $SpecifiedComparisonMeasureMethod);
    1214
    1215   $SpecifiedComparisonMeasureName = '';
    1216   $SpecifiedComparisonMeasureMethod = '';
    1217
    1218   $SpecifiedMeasure = $Options{vectorcomparisonmode};
    1219   $SpecifiedMeasure =~ s/ //g;
    1220
    1221   if (! exists($SupportedComparisonMeasuresMethodMap{lc($SpecifiedMeasure)})) {
    1222     die "Error: The value specified, $SpecifiedMeasure, for option \"-v --VectorComparisonMode\" is not valid.\nAllowed values:", JoinWords(\@SupportedComparisonMeasures, ", ", 0), "\n";
    1223   }
    1224
    1225   $SpecifiedComparisonMeasureMethod = $SupportedComparisonMeasuresMethodMap{lc($SpecifiedMeasure)};
    1226   $SpecifiedComparisonMeasureName = $SupportedComparisonMeasuresNameMap{lc($SpecifiedMeasure)};
    1227
    1228   $OptionsInfo{VectorComparisonMode} = $Options{vectorcomparisonmode};
    1229
    1230   $OptionsInfo{SpecifiedVectorComparisonMeasure} = $SpecifiedMeasure;
    1231   $OptionsInfo{SpecifiedVectorComparisonMeasuresName} = $SpecifiedComparisonMeasureName;
    1232   $OptionsInfo{SpecifiedVectorComparisonMeasuresMethod} = $SpecifiedComparisonMeasureMethod;
    1233
    1234   # Setup specified vector comparison calculation modes...
    1235   my($SpecifiedFormulism);
    1236
    1237   $SpecifiedFormulism = $Options{vectorcomparisonformulism};
    1238   $SpecifiedFormulism =~ s/ //g;
    1239   if ($SpecifiedFormulism !~ /^(AlgebraicForm|BinaryForm|SetTheoreticForm)$/i) {
    1240     die "Error: The value specified, $SpecifiedFormulism, for option \"--VectorComparisonFormulism\" is not valid. Allowed values: AlgebraicForm, BinaryForm or SetTheoreticForm\n";
    1241   }
    1242
    1243   $OptionsInfo{VectorComparisonFormulism} = $Options{vectorcomparisonformulism};
    1244   $OptionsInfo{SpecifiedVectorComparisonMode} = $SpecifiedFormulism;
    1245
    1246 }
    1247
    1248 # Process options related to data retrieval from reference fingerprints SD and CSV/TSV
    1249 # text files...
    1250 #
    1251 sub ProcessReferenceFingerprintsDataOptions {
    1252
    1253   $OptionsInfo{ReferenceCompoundIDPrefix} = $Options{referencecompoundidprefix} ? $Options{referencecompoundidprefix} : 'Cmpd';
    1254
    1255   # Compound ID and fingerprints column options for text files...
    1256
    1257   $OptionsInfo{ReferenceColMode} = $Options{referencecolmode};
    1258
    1259   if (IsNotEmpty($Options{referencecompoundidcol})) {
    1260     if ($Options{referencecolmode} =~ /^ColNum$/i) {
    1261       if (!IsPositiveInteger($Options{referencecompoundidcol})) {
    1262         die "Error: Column value, $Options{referencecompoundidcol}, specified using \"--ReferenceCompoundIDCol\" is not valid: Allowed integer values: > 0\n";
    1263       }
    1264     }
    1265     $OptionsInfo{ReferenceCompoundIDCol} = $Options{referencecompoundidcol};
    1266   }
    1267   else {
    1268     $OptionsInfo{ReferenceCompoundIDCol} = 'AutoDetect';
    1269   }
    1270
    1271   if (IsNotEmpty($Options{referencefingerprintscol})) {
    1272     if ($Options{referencecolmode} =~ /^ColNum$/i) {
    1273       if (!IsPositiveInteger($Options{referencefingerprintscol})) {
    1274         die "Error: Column value, $Options{referencefingerprintscol}, specified using \"--ReferenceFingerprintsCol\" is not valid: Allowed integer values: > 0\n";
    1275       }
    1276     }
    1277     $OptionsInfo{ReferenceFingerprintsCol} = $Options{referencefingerprintscol};
    1278   }
    1279   else {
    1280     $OptionsInfo{ReferenceFingerprintsCol} = 'AutoDetect';
    1281   }
    1282
    1283   if (IsNotEmpty($Options{referencecompoundidcol}) && IsNotEmpty($Options{referencefingerprintscol})) {
    1284     if (IsPositiveInteger($Options{referencecompoundidcol}) && IsPositiveInteger($Options{referencefingerprintscol})) {
    1285       if (($Options{referencecompoundidcol} == $Options{referencefingerprintscol})) {
    1286         die "Error: Values specified using \"--ReferenceCompoundIDCol\" and \"--ReferenceFingerprintsCol\", $Options{referencecompoundidcol}, must be different.\n";
    1287       }
    1288     }
    1289     else {
    1290       if (($Options{referencecompoundidcol} eq $Options{referencefingerprintscol})) {
    1291         die "Error: Values specified using \"--ReferenceCompoundIDCol\" and \"--ReferenceFingerprintsCol\", $Options{referencecompoundidcol}, must be different.\n";
    1292       }
    1293     }
    1294   }
    1295
    1296   # Compound ID and fingerprints field options for SD files...
    1297
    1298   $OptionsInfo{ReferenceCompoundIDMode} = $Options{referencecompoundidmode};
    1299   $OptionsInfo{ReferenceCompoundIDField} = '';
    1300
    1301   if ($Options{referencecompoundidmode} =~ /^DataField$/i && !$Options{referencecompoundidfield}) {
    1302     die "Error: You must specify a value for \"--ReferenceCompoundIDField\" option in \"DataField\" \"--ReferenceCompoundIDMode\". \n";
    1303   }
    1304   if ($Options{referencecompoundidfield}) {
    1305     $OptionsInfo{ReferenceCompoundIDField} = $Options{referencecompoundidfield};
    1306   }
    1307
    1308   if (IsNotEmpty($Options{referencefingerprintsfield})) {
    1309     $OptionsInfo{ReferenceFingerprintsField} = $Options{referencefingerprintsfield};
    1310   }
    1311   else {
    1312     $OptionsInfo{ReferenceFingerprintsField} = 'AutoDetect';
    1313   }
    1314
    1315   if ($Options{referencecompoundidfield} && IsNotEmpty($Options{referencefingerprintsfield})) {
    1316     if (($Options{referencecompoundidfield} eq $Options{referencefingerprintsfield})) {
    1317       die "Error: Values specified using \"--ReferenceCompoundIDField\" and \"--ReferenceFingerprintsfield\", $Options{referencecompoundidfield}, must be different.\n";
    1318     }
    1319   }
    1320
    1321 }
    1322
    1323 # Process options related to data retrieval from database fingerprints SD and CSV/TSV
    1324 # text files...
    1325 #
    1326 sub ProcessDatabaseFingerprintsDataOptions {
    1327
    1328   $OptionsInfo{DatabaseCompoundIDPrefix} = $Options{databasecompoundidprefix} ? $Options{databasecompoundidprefix} : 'Cmpd';
    1329
    1330   # Compound ID and fingerprints column options for text files...
    1331
    1332   $OptionsInfo{DatabaseColMode} = $Options{databasecolmode};
    1333
    1334   if (IsNotEmpty($Options{databasecompoundidcol})) {
    1335     if ($Options{databasecolmode} =~ /^ColNum$/i) {
    1336       if (!IsPositiveInteger($Options{databasecompoundidcol})) {
    1337         die "Error: Column value, $Options{databasecompoundidcol}, specified using \"--DatabaseCompoundIDCol\" is not valid: Allowed integer values: > 0\n";
    1338       }
    1339     }
    1340     $OptionsInfo{DatabaseCompoundIDCol} = $Options{databasecompoundidcol};
    1341   }
    1342   else {
    1343     $OptionsInfo{DatabaseCompoundIDCol} = 'AutoDetect';
    1344   }
    1345
    1346   if (IsNotEmpty($Options{databasefingerprintscol})) {
    1347     if ($Options{databasecolmode} =~ /^ColNum$/i) {
    1348       if (!IsPositiveInteger($Options{databasefingerprintscol})) {
    1349         die "Error: Column value, $Options{databasefingerprintscol}, specified using \"--DatabaseFingerprintsCol\" is not valid: Allowed integer values: > 0\n";
    1350       }
    1351     }
    1352     $OptionsInfo{DatabaseFingerprintsCol} = $Options{databasefingerprintscol};
    1353   }
    1354   else {
    1355     $OptionsInfo{DatabaseFingerprintsCol} = 'AutoDetect';
    1356   }
    1357
    1358   if (IsNotEmpty($Options{databasecompoundidcol}) && IsNotEmpty($Options{databasefingerprintscol})) {
    1359     if (IsPositiveInteger($Options{databasecompoundidcol}) && IsPositiveInteger($Options{databasefingerprintscol})) {
    1360       if (($Options{databasecompoundidcol} == $Options{databasefingerprintscol})) {
    1361         die "Error: Values specified using \"--DatabaseCompoundIDCol\" and \"--DatabaseFingerprintsCol\", $Options{databasecompoundidcol}, must be different.\n";
    1362       }
    1363     }
    1364     else {
    1365       if (($Options{databasecompoundidcol} eq $Options{databasefingerprintscol})) {
    1366         die "Error: Values specified using \"--DatabaseCompoundIDCol\" and \"--DatabaseFingerprintsCol\", $Options{databasecompoundidcol}, must be different.\n";
    1367       }
    1368     }
    1369   }
    1370
    1371   # Database data column options for text files...
    1372
    1373   $OptionsInfo{DatabaseDataColsMode} = $Options{databasedatacolsmode};
    1374   $OptionsInfo{DatabaseDataCols} = '';
    1375   @{$OptionsInfo{SpecifiedDatabaseDataCols}} = ();
    1376
    1377   if ($Options{databasedatacolsmode} =~ /^Specify$/i) {
    1378     my($DatabaseDataCols, $DatabaseColNum, @SpecifiedDataCols);
    1379
    1380     if (!$Options{databasedatacols}) {
    1381       die "Error: You must specify a value for \"--DatabaseDataCols\" option in \"Specify\" \"--DatabaseDataColsMode\". \n";
    1382     }
    1383     $DatabaseDataCols = $Options{databasedatacols};
    1384
    1385     if ($Options{databasecolmode} =~ /^ColNum$/i) {
    1386       $DatabaseDataCols =~ s/ //g;
    1387       @SpecifiedDataCols = split /\,/, $DatabaseDataCols;
    1388       for $DatabaseColNum (@SpecifiedDataCols) {
    1389         if (!IsPositiveInteger($DatabaseColNum)) {
    1390           die "Error: Column value, $DatabaseColNum, specified using \"--DatabaseDataCols\" is not valid: Allowed integer values: > 0\n";
    1391         }
    1392       }
    1393     }
    1394     else {
    1395       @SpecifiedDataCols = split /\,/, $DatabaseDataCols;
    1396     }
    1397     $OptionsInfo{DatabaseDataCols} = $DatabaseDataCols;
    1398     push @{$OptionsInfo{SpecifiedDatabaseDataCols}}, @SpecifiedDataCols;
    1399   }
    1400   elsif ($Options{databasedatacolsmode} =~ /^All$/i) {
    1401     $OptionsInfo{DatabaseDataCols} = 'All';
    1402   }
    1403
    1404   if ($OptionsInfo{DatabaseDataColsMode} =~ /^Specify$/i && !$OptionsInfo{DatabaseDataCols}) {
    1405     die "Error: You must specify a value for \"--DatabaseDataCols\" option in \"Specify\" \"--DatabaseDataColsMode\". \n";
    1406   }
    1407
    1408   # Compound ID and fingerprints field options for SD files...
    1409
    1410   $OptionsInfo{DatabaseCompoundIDMode} = $Options{databasecompoundidmode};
    1411   $OptionsInfo{DatabaseCompoundIDField} = $Options{databasecompoundidfield} ? $Options{databasecompoundidfield} : '';
    1412
    1413   if ($Options{databasecompoundidmode} =~ /^DataField$/i) {
    1414     if (!$Options{databasecompoundidfield}) {
    1415       die "Error: You must specify a value for \"--DatabaseCompoundIDField\" option in \"DataField\" \"--DatabaseCompoundIDMode\". \n";
    1416     }
    1417     $OptionsInfo{DatabaseCompoundIDField} = $Options{databasecompoundidfield};
    1418   }
    1419
    1420
    1421   if (IsNotEmpty($Options{databasefingerprintsfield})) {
    1422     $OptionsInfo{DatabaseFingerprintsField} = $Options{databasefingerprintsfield};
    1423   }
    1424   else {
    1425     $OptionsInfo{DatabaseFingerprintsField} = 'AutoDetect';
    1426   }
    1427
    1428   if ($Options{databasecompoundidfield} && IsNotEmpty($Options{databasefingerprintsfield})) {
    1429     if (($Options{databasecompoundidfield} eq $Options{databasefingerprintsfield})) {
    1430       die "Error: Values specified using \"--DatabaseCompoundIDField\" and \"--DatabaseFingerprintsfield\", $Options{databasecompoundidfield}, must be different.\n";
    1431     }
    1432   }
    1433
    1434   # Database data field options for SD files...
    1435
    1436   $OptionsInfo{DatabaseDataFieldsMode} = $Options{databasedatafieldsmode};
    1437   $OptionsInfo{DatabaseDataFields} = '';
    1438   @{$OptionsInfo{SpecifiedDatabaseDataFields}} = ();
    1439
    1440   if ($Options{databasedatafieldsmode} =~ /^Specify$/i && !$Options{databasedatafields}) {
    1441     die "Error: You must specify a value for \"--DatabaseDataFields\" option in \"Specify\" \"--DatabaseDataFieldsMode\". \n";
    1442   }
    1443   if ($Options{databasedatafields}) {
    1444     my(@SpecifiedDataFields);
    1445     $OptionsInfo{DatabaseDataFields} = $Options{databasedatafields};
    1446
    1447     @SpecifiedDataFields = split /\,/, $Options{databasedatafields};
    1448     push @{$OptionsInfo{SpecifiedDatabaseDataFields}}, @SpecifiedDataFields;
    1449   }
    1450 }
    1451
    1452 # Setup script usage  and retrieve command line arguments specified using various options...
    1453 sub SetupScriptUsage {
    1454
    1455   # Retrieve all the options...
    1456   %Options = ();
    1457
    1458   $Options{alpha} = 0.5;
    1459   $Options{beta} = 1;
    1460
    1461   $Options{bitvectorcomparisonmode} = "TanimotoSimilarity";
    1462
    1463   $Options{databasecolmode} = 'colnum';
    1464
    1465   $Options{databasecompoundidprefix} = 'Cmpd';
    1466   $Options{databasecompoundidmode} = 'LabelPrefix';
    1467
    1468   $Options{databasedatacolsmode} = 'CompoundID';
    1469   $Options{databasedatafieldsmode} = 'CompoundID';
    1470
    1471   $Options{distancecutoff} = 10;
    1472
    1473   $Options{referencecolmode} = 'colnum';
    1474
    1475   $Options{referencecompoundidprefix} = 'Cmpd';
    1476   $Options{referencecompoundidmode} = 'LabelPrefix';
    1477
    1478   $Options{detail} = 1;
    1479
    1480   $Options{fingerprintsmode} = 'AutoDetect';
    1481   $Options{groupfusionrule} = 'Max';
    1482   $Options{groupfusionapplycutoff} = 'Yes';
    1483
    1484   $Options{knn} = 'All';
    1485
    1486   $Options{mode} = 'MultipleReferences';
    1487
    1488   $Options{numofsimilarmolecules} = 10;
    1489   $Options{percentsimilarmolecules} = 1;
    1490
    1491   $Options{indelim} = 'comma';
    1492   $Options{outdelim} = 'comma';
    1493   $Options{quote} = 'yes';
    1494
    1495   $Options{output} = 'text';
    1496
    1497   $Options{precision} = 2;
    1498
    1499   $Options{searchmode} = 'SimilaritySearch';
    1500
    1501   $Options{similarcountmode} = 'NumOfSimilar';
    1502
    1503   $Options{similaritycutoff} = 0.75;
    1504
    1505   $Options{vectorcomparisonmode} = 'TanimotoSimilarity';
    1506   $Options{vectorcomparisonformulism} = 'AlgebraicForm';
    1507
    1508   if (!GetOptions(\%Options, "alpha=f", "beta=f", "bitvectorcomparisonmode|b=s", "databasecolmode=s", "databasecompoundidcol=s", "databasecompoundidprefix=s", "databasecompoundidfield=s", "databasecompoundidmode=s", "databasedatacols=s", "databasedatacolsmode=s", "databasedatafields=s", "databasedatafieldsmode=s", "databasefingerprintscol=s", "databasefingerprintsfield=s", "distancecutoff=f", "detail|d=i", "fast|f", "fingerprintsmode=s", "groupfusionrule|g=s", , "groupfusionapplycutoff=s", "help|h", "indelim=s", "knn|k=s", "mode|m=s", "numofsimilarmolecules|n=i", "outdelim=s", "output=s", "overwrite|o", "percentsimilarmolecules|p=f", "precision=s", "quote|q=s", "referencecolmode=s", "referencecompoundidcol=s", "referencecompoundidprefix=s", "referencecompoundidfield=s", "referencecompoundidmode=s", "referencefingerprintscol=s", "referencefingerprintsfield=s", "root|r=s", "searchmode|s=s", "similarcountmode=s", "similaritycutoff=f", "vectorcomparisonmode|v=s", "vectorcomparisonformulism=s", "workingdir|w=s")) {
    1509     die "\nTo get a list of valid options and their values, use \"$ScriptName -h\" or\n\"perl -S $ScriptName -h\" command and try again...\n";
    1510   }
    1511   if ($Options{workingdir}) {
    1512     if (! -d $Options{workingdir}) {
    1513       die "Error: The value specified, $Options{workingdir}, for option \"-w --workingdir\" is not a directory name.\n";
    1514     }
    1515     chdir $Options{workingdir} or die "Error: Couldn't chdir $Options{workingdir}: $! \n";
    1516   }
    1517   if ($Options{databasecolmode} !~ /^(ColNum|ColLabel)$/i) {
    1518     die "Error: The value specified, $Options{databasecolmode}, for option \"--DatabaseColMode\" is not valid. Allowed values: ColNum, or ColLabel\n";
    1519   }
    1520   if ($Options{databasecompoundidmode} !~ /^(DataField|MolName|LabelPrefix|MolNameOrLabelPrefix)$/i) {
    1521     die "Error: The value specified, $Options{databasecompoundidmode}, for option \"--DatabaseCompoundIDMode\" is not valid. Allowed values: DataField, MolName, LabelPrefix or MolNameOrLabelPrefix\n";
    1522   }
    1523   if ($Options{databasedatacolsmode} !~ /^(All|Specify|CompoundID)$/i) {
    1524     die "Error: The value specified, $Options{databasedatacolsmode}, for option \"--DatabaseDataColsMode\" is not valid. Allowed values: All, Specify, or CompoundID\n";
    1525   }
    1526   if ($Options{databasedatafieldsmode} !~ /^(All|Common|Specify|CompoundID)$/i) {
    1527     die "Error: The value specified, $Options{databasedatafieldsmode}, for option \"--DatabaseDataFieldsMode\" is not valid. Allowed values: All, Common, Specify, or CompoundID\n";
    1528   }
    1529   if (!IsPositiveInteger($Options{detail})) {
    1530     die "Error: The value specified, $Options{detail}, for option \"-d, --detail\" is not valid. Allowed values: > 0 \n";
    1531   }
    1532   if ($Options{fingerprintsmode} !~ /^(AutoDetect|FingerprintsBitVectorString|FingerprintsVectorString)$/i) {
    1533     die "Error: The value specified, $Options{fingerprintsmode}, for option \"--FingerprintsMode\" is not valid. Allowed values: AutoDetect, FingerprintsBitVectorString or FingerprintsVectorString \n";
    1534   }
    1535   if ($Options{groupfusionrule} !~ /^(Max|Min|Mean|Median|Sum|Euclidean)$/i) {
    1536     die "Error: The value specified, $Options{groupfusionrule}, for option \"-g, --GroupFusionRule\" is not valid. Allowed values: Max, Min, Mean, Median, Sum, Euclidean\n";
    1537   }
    1538   if ($Options{groupfusionapplycutoff} !~ /^(Yes|No)$/i) {
    1539     die "Error: The value specified, $Options{quote}, for option \"--GroupFusionApplyCutoff\" is not valid. Allowed values: Yes or No\n";
    1540   }
    1541   if ($Options{indelim} !~ /^(comma|semicolon)$/i) {
    1542     die "Error: The value specified, $Options{indelim}, for option \"--InDelim\" is not valid. Allowed values: comma, or semicolon\n";
    1543   }
    1544   if ($Options{mode} !~ /^(IndividualReference|MultipleReferences)$/i) {
    1545     die "Error: The value specified, $Options{mode}, for option \"-m, --mode\" is not valid. Allowed values: IndividualReference, MultipleReferences\n";
    1546   }
    1547   if (!IsPositiveInteger($Options{numofsimilarmolecules})) {
    1548     die "Error: The value specified, $Options{numofsimilarmolecules}, for option \"-n, --NumOfSimilarMolecules\" is not valid. Allowed values: > 0 \n";
    1549   }
    1550   if ($Options{outdelim} !~ /^(comma|semicolon|tab)$/i) {
    1551     die "Error: The value specified, $Options{outdelim}, for option \"--OutDelim\" is not valid. Allowed values: comma, tab, or semicolon\n";
    1552   }
    1553   if ($Options{output} !~ /^(SD|text|both)$/i) {
    1554     die "Error: The value specified, $Options{output}, for option \"--output\" is not valid. Allowed values: SD, text, or both\n";
    1555   }
    1556   if (!(IsFloat($Options{percentsimilarmolecules}) && $Options{percentsimilarmolecules} > 0 && $Options{percentsimilarmolecules} <= 100)) {
    1557     die "Error: The value specified, $Options{percentsimilarmolecules}, for option \"-p, --PercentSimilarMolecules\" is not valid. Allowed values: > 0 and <= 100 \n";
    1558   }
    1559   if ($Options{quote} !~ /^(Yes|No)$/i) {
    1560     die "Error: The value specified, $Options{quote}, for option \"-q --quote\" is not valid. Allowed values: Yes or No\n";
    1561   }
    1562   if (!IsPositiveInteger($Options{precision})) {
    1563     die "Error: The value specified, $Options{precision}, for option \"--precision\" is not valid. Allowed values: > 0 \n";
    1564   }
    1565   if ($Options{referencecolmode} !~ /^(ColNum|ColLabel)$/i) {
    1566     die "Error: The value specified, $Options{referencecolmode}, for option \"--ReferenceColMode\" is not valid. Allowed values: ColNum, or ColLabel\n";
    1567   }
    1568   if ($Options{referencecompoundidmode} !~ /^(DataField|MolName|LabelPrefix|MolNameOrLabelPrefix)$/i) {
    1569     die "Error: The value specified, $Options{referencecompoundidmode}, for option \"--ReferenceCompoundIDMode\" is not valid. Allowed values: DataField, MolName, LabelPrefix or MolNameOrLabelPrefix\n";
    1570   }
    1571   if ($Options{searchmode} !~ /^(SimilaritySearch|DissimilaritySearch)$/i) {
    1572     die "Error: The value specified, $Options{searchmode}, for option \"-s, --SearchMode\" is not valid. Allowed values: SimilaritySearch, DissimilaritySearch \n";
    1573   }
    1574   if ($Options{similarcountmode} !~ /^(NumOfSimilar|PercentSimilar)$/i) {
    1575     die "Error: The value specified, $Options{similarcountmode}, for option \"--SimilarCountMode\" is not valid. Allowed values: NumOfSimilar, PercentSimilar \n";
    1576   }
    1577 }
    1578