MayaChemTools

   1 package MolecularDescriptors::MolecularComplexityDescriptors;
   2 #
   3 # File: MolecularComplexityDescriptors.pm
   4 # Author: Manish Sud <msud@san.rr.com>
   5 #
   6 # Copyright (C) 2024 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 Carp;
  28 use Exporter;
  29 use Scalar::Util ();
  30 use TextUtil ();
  31 use MathUtil ();
  32 use Atom;
  33 use Molecule;
  34 use MolecularDescriptors::MolecularDescriptors;
  35 use AtomTypes::AtomicInvariantsAtomTypes;
  36 use AtomTypes::FunctionalClassAtomTypes;
  37 use Fingerprints::AtomTypesFingerprints;
  38 use Fingerprints::ExtendedConnectivityFingerprints;
  39 use Fingerprints::MACCSKeys;
  40 use Fingerprints::PathLengthFingerprints;
  41 use Fingerprints::TopologicalAtomPairsFingerprints;
  42 use Fingerprints::TopologicalAtomTripletsFingerprints;
  43 use Fingerprints::TopologicalAtomTorsionsFingerprints;
  44 use Fingerprints::TopologicalPharmacophoreAtomPairsFingerprints;
  45 use Fingerprints::TopologicalPharmacophoreAtomTripletsFingerprints;
  46 
  47 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  48 
  49 @ISA = qw(MolecularDescriptors::MolecularDescriptors Exporter);
  50 @EXPORT = qw();
  51 @EXPORT_OK = qw(GetDescriptorNames GetMolecularComplexityTypeAbbreviation);
  52 
  53 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
  54 
  55 # Setup class variables...
  56 my($ClassName, @DescriptorNames);
  57 _InitializeClass();
  58 
  59 # Overload Perl functions...
  60 use overload '""' => 'StringifyMolecularComplexityDescriptors';
  61 
  62 # Class constructor...
  63 sub new {
  64   my($Class, %NamesAndValues) = @_;
  65 
  66   # Initialize object...
  67   my $This = $Class->SUPER::new();
  68   bless $This, ref($Class) || $Class;
  69   $This->_InitializeMolecularComplexityDescriptors();
  70 
  71   $This->_InitializeMolecularComplexityDescriptorsProperties(%NamesAndValues);
  72 
  73   return $This;
  74 }
  75 
  76 # Initialize class ...
  77 sub _InitializeClass {
  78   #Class name...
  79   $ClassName = __PACKAGE__;
  80 
  81   # Descriptor names...
  82   @DescriptorNames = ('MolecularComplexity');
  83 
  84 }
  85 
  86 # Get descriptor names as an array.
  87 #
  88 # This functionality can be either invoked as a class function or an
  89 # object method.
  90 #
  91 sub GetDescriptorNames {
  92   return @DescriptorNames;
  93 }
  94 
  95 # Initialize object data...
  96 #
  97 sub _InitializeMolecularComplexityDescriptors {
  98   my($This) = @_;
  99 
 100   # Type of MolecularDescriptor...
 101   $This->{Type} = 'MolecularComplexity';
 102 
 103   #
 104   # The current release of MayaChemTools supports calculation of molecular complexity
 105   # corresponding to number of bits-set or unique keys [ Ref 117-119 ] in molecular
 106   # fingerprints. The following types of fingerprints based molecular complexity measures
 107   # are supported:
 108   #
 109   # AtomTypesFingerprints
 110   # ExtendedConnectivityFingerprints
 111   # MACCSKeys
 112   # PathLengthFingerprints
 113   # TopologicalAtomPairsFingerprints
 114   # TopologicalAtomTripletsFingerprints
 115   # TopologicalAtomTorsionsFingerprints
 116   # TopologicalPharmacophoreAtomPairsFingerprints
 117   # TopologicalPharmacophoreAtomTripletsFingerprints
 118   #
 119   # Default: MACCSKeys
 120   #
 121   $This->{MolecularComplexityType} = '';
 122 
 123   # Atom types to use for generating fingerprints...
 124   #
 125   # Currently supported values are: AtomicInvariantsAtomTypes, DREIDINGAtomTypes,
 126   # EStateAtomTypes, FunctionalClassAtomTypes, MMFF94AtomTypes, SLogPAtomTypes,
 127   # SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes
 128   #
 129   # Notes:
 130   #   . AtomicInvariantsAtomTypes for all supported MolecularComplexityType except for
 131   #     TopologicalPharmacophoreAtomPairsFingerprints and TopologicalPharmacophoreAtomTripletsFingerprints
 132   #   . This value is not used for MACCSKeys
 133   #   . FunctionalClassAtomTypes is the only valid value during topological pharmacophore fingerprints.
 134   #
 135   #   . Default values for AtomicInvariantsToUse and FunctionalClassesToUse are set appropriately
 136   #     for different types of fingerprints as shown below.
 137   #
 138   #     MolecularComplexityType              AtomicInvariantsToUse
 139   #
 140   #     AtomTypesFingerprints                AS, X, BO, H, FC
 141   #     TopologicalAtomPairsFingerprints     AS, X, BO, H, FC
 142   #     TopologicalAtomTripletsFingerprints  AS, X, BO, H, FC
 143   #     TopologicalAtomTorsionsFingerprints  AS, X, BO, H, FC
 144   #
 145   #     ExtendedConnectivityFingerprints     AS, X, BO, H, FC, MN
 146   #     PathLengthFingerprints               AS
 147   #
 148   #     Default for FunctionalClassesToUse for all fingerprints is set to:
 149   #
 150   #     HBD, HBA, PI, NI, Ar, Hal
 151   #
 152   #     except for the following two MolecularComplexityType fingerprints:
 153   #
 154   #     TopologicalPharmacophoreAtomPairsFingerprints     HBD, HBA, PI, NI, H
 155   #     TopologicalPharmacophoreAtomTripletsFingerprints  HBD, HBA, PI, NI, H, Ar
 156   #
 157   $This->{AtomIdentifierType} = '';
 158 
 159   # Size of MACCS key set: 166 or 322...
 160   #
 161   $This->{MACCSKeysSize} = 166;
 162 
 163   # Atomic neighborhoods radius for extended connectivity fingerprints...
 164   $This->{NeighborhoodRadius} = 2;
 165 
 166   # Minimum and maximum path lengths to use for path length fingerprints...
 167   $This->{MinPathLength} = 1;
 168   $This->{MaxPathLength} = 8;
 169 
 170   # By default bond symbols are included in atom path strings used to generate path length
 171   # fingerprints... ...
 172   $This->{UseBondSymbols} = 1;
 173 
 174   # Minimum and maximum bond distance between atom pairs during topological
 175   # atom pairs/triplets fingerprints...
 176   $This->{MinDistance} = 1;
 177   $This->{MaxDistance} = 10;
 178 
 179   # Determines whether to apply triangle inequality to distance triplets...
 180   #
 181   # Default for TopologicalAtomTripletsFingerprints: 0
 182   # Default for TopologicalPharmacophoreAtomTripletsFingerprints: 1
 183   #
 184   $This->{UseTriangleInequality} = '';
 185 
 186   # Distance bin size used for binning distances during generation of
 187   # topological pharmacophore atom triplets fingerprints...
 188   #
 189   $This->{DistanceBinSize} = 2;
 190 
 191   # Normalization methodology to use for scaling the number of bits-set or unique keys
 192   # for:
 193   #
 194   # ExtendedConnectivityFingerprints
 195   # TopologicalPharmacophoreAtomPairsFingerprints
 196   # TopologicalPharmacophoreAtomTripletsFingerprints
 197   #
 198   # This option is gnored for all other types of fingerprints.
 199   #
 200   # Possible values during extended connectivity fingerprints: None or ByHeavyAtomsCount. Default:
 201   # None.
 202   #
 203   # Possible values during topological pharmacophore atom pairs and tripletes fingerprints: None,
 204   # or ByPossibleKeysCount. Default: None. ByPossibleKeysCount corresponds to total number of
 205   # possible topological pharmacophore atom pairs or triplets in a molecule.
 206   #
 207   #
 208   $This->{NormalizationMethodology} = 'None';
 209 
 210   # Intialize descriptor names and values...
 211   $This->_InitializeDescriptorNamesAndValues(@DescriptorNames);
 212 
 213   return $This;
 214 }
 215 
 216 # Initialize object properties...
 217 #
 218 sub _InitializeMolecularComplexityDescriptorsProperties {
 219   my($This, %NamesAndValues) = @_;
 220 
 221   my($Name, $Value, $MethodName);
 222   while (($Name, $Value) = each  %NamesAndValues) {
 223     $MethodName = "Set${Name}";
 224     $This->$MethodName($Value);
 225   }
 226 
 227   # Make sure MolecularComplexityType is set...
 228   if (!exists $NamesAndValues{MolecularComplexityType}) {
 229     $This->{MolecularComplexityType} = 'MACCSKeys';
 230   }
 231 
 232   # Make sure AtomIdentifierType is set...
 233   if ($This->{MolecularComplexityType} !~ /^MACCSKeys$/i) {
 234     if (!exists $NamesAndValues{AtomIdentifierType}) {
 235       $This->_InitializeAtomIdentifierType();
 236     }
 237   }
 238 
 239   # Make sure UseTriangleInequality is set...
 240   if ($This->{MolecularComplexityType} =~ /^(TopologicalAtomTripletsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) {
 241     if (!exists $NamesAndValues{UseTriangleInequality}) {
 242       $This->{UseTriangleInequality} =  ($This->{MolecularComplexityType} =~ /^TopologicalPharmacophoreAtomTripletsFingerprints$/i) ? 1 : 0;
 243     }
 244   }
 245 
 246   return $This;
 247 }
 248 
 249 # Initialize atom identifer type...
 250 #
 251 sub _InitializeAtomIdentifierType {
 252   my($This) = @_;
 253   my($AtomIdentifierType);
 254 
 255   if ($This->{MolecularComplexityType} =~ /^MACCSKeys$/i) {
 256     return $This;
 257   }
 258 
 259   $AtomIdentifierType = ($This->{MolecularComplexityType} =~ /^(TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) ? 'FunctionalClassAtomTypes' : 'AtomicInvariantsAtomTypes';
 260 
 261   $This->SetAtomIdentifierType($AtomIdentifierType);
 262 
 263   return $This;
 264 }
 265 
 266 # Get abbreviation for specified molecular complexity type or using descriptors object...
 267 #
 268 # This functionality can be either invoked as a class function or an
 269 # object method.
 270 #
 271 sub GetMolecularComplexityTypeAbbreviation {
 272   my($FirstParameter) = @_;
 273   my($This, $ComplexityType, %ComplexityTypeToAbbrev);
 274 
 275   if (_IsMolecularComplexityDescriptors($FirstParameter)) {
 276     $This = $FirstParameter;
 277     $ComplexityType = $This->{MolecularComplexityType};
 278   }
 279   else {
 280     $ComplexityType = $FirstParameter;
 281   }
 282 
 283   %ComplexityTypeToAbbrev = (lc 'AtomTypesFingerprints' => 'ATFP', lc 'ExtendedConnectivityFingerprints' => 'ECFP',
 284                              lc 'MACCSKeys' => 'MACCSKeys', lc 'PathLengthFingerprints' => 'PLFP',
 285                              lc 'TopologicalAtomPairsFingerprints' => 'TAPFP', lc 'TopologicalAtomTripletsFingerprints' => 'TATFP',
 286                              lc 'TopologicalAtomTorsionsFingerprints' => 'TATFP',
 287                              lc 'TopologicalPharmacophoreAtomPairsFingerprints' => 'TPAPFP',
 288                              lc 'TopologicalPharmacophoreAtomTripletsFingerprints' => 'TPATFP');
 289 
 290   return exists $ComplexityTypeToAbbrev{lc $ComplexityType} ? $ComplexityTypeToAbbrev{lc $ComplexityType} : '';
 291 }
 292 
 293 # Set MACCS key set size...
 294 #
 295 sub SetMACCSKeysSize {
 296   my($This, $Value) = @_;
 297 
 298   if (!TextUtil::IsPositiveInteger($Value)) {
 299     croak "Error: ${ClassName}->SetMACCSKeysSize: Size value, $Value, is not valid:  It must be a positive integer...";
 300   }
 301   if ($Value !~ /^(166|322)/i) {
 302     croak "Error: ${ClassName}->SetMACCSKeysSize: The current release of MayaChemTools doesn't support MDL MACCS $Value keys...";
 303   }
 304   $This->{MACCSKeysSize} = $Value;
 305 
 306   return $This;
 307 }
 308 
 309 # Set minimum path length...
 310 #
 311 sub SetMinPathLength {
 312   my($This, $Value) = @_;
 313 
 314   if (!TextUtil::IsPositiveInteger($Value)) {
 315     croak "Error: ${ClassName}->SetMinPathLength: MinPathLength value, $Value, is not valid:  It must be a positive integer...";
 316   }
 317   $This->{MinPathLength} = $Value;
 318 
 319   return $This;
 320 }
 321 
 322 # Set maximum path length...
 323 #
 324 sub SetMaxPathLength {
 325   my($This, $Value) = @_;
 326 
 327   if (!TextUtil::IsPositiveInteger($Value)) {
 328     croak "Error: ${ClassName}->SetMaxPathLength: MaxPathLength value, $Value, is not valid:  It must be a positive integer...";
 329   }
 330   $This->{MaxPathLength} = $Value;
 331 
 332   return $This;
 333 }
 334 
 335 # Set minimum  bond distance between atom pairs during topological and topological
 336 # pharmacophore atom pairs/triplets fingerprints...
 337 #
 338 sub SetMinDistance {
 339   my($This, $Value) = @_;
 340 
 341   if (!TextUtil::IsPositiveInteger($Value)) {
 342     croak "Error: ${ClassName}->SetMinDistance: MinDistance value, $Value, is not valid:  It must be a positive integer...";
 343   }
 344   $This->{MinDistance} = $Value;
 345 
 346   return $This;
 347 }
 348 
 349 # Set maximum  bond distance between atom pairs during topological and topological
 350 # pharmacophore atom pairs/triplets fingerprints...
 351 #
 352 sub SetMaxDistance {
 353   my($This, $Value) = @_;
 354 
 355   if (!TextUtil::IsPositiveInteger($Value)) {
 356     croak "Error: ${ClassName}->SetMaxDistance: MaxDistance value, $Value, is not valid:  It must be a positive integer...";
 357   }
 358   $This->{MaxDistance} = $Value;
 359 
 360   return $This;
 361 }
 362 
 363 # Set atom neighborhood radius...
 364 #
 365 sub SetNeighborhoodRadius {
 366   my($This, $Value) = @_;
 367 
 368   if (!TextUtil::IsInteger($Value)) {
 369     croak "Error: ${ClassName}->SetNeighborhoodRadius: NeighborhoodRadius value, $Value, is not valid:  It must be an  integer...";
 370   }
 371 
 372   if ($Value < 0 ) {
 373     croak "Error: ${ClassName}->SetNeighborhoodRadius: NeighborhoodRadius value, $Value, is not valid:  It must be >= 0...";
 374   }
 375   $This->{NeighborhoodRadius} = $Value;
 376 
 377   return $This;
 378 }
 379 
 380 # Set molecular complexity type...
 381 #
 382 sub SetMolecularComplexityType {
 383   my($This, $Value) = @_;
 384 
 385   if ($Value !~ /^(AtomTypesFingerprints|ExtendedConnectivityFingerprints|MACCSKeys|PathLengthFingerprints|TopologicalAtomPairsFingerprints|TopologicalAtomTripletsFingerprints|TopologicalAtomTorsionsFingerprints|TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) {
 386     croak "Error: ${ClassName}->SetMolecularComplexityType: MolecularComplexityType value, $Value, is not valid. Supported values: AtomTypesFingerprints, ExtendedConnectivityFingerprints, MACCSKeys, PathLengthFingerprints, TopologicalAtomPairsFingerprints, TopologicalAtomTripletsFingerprints, TopologicalAtomTorsionsFingerprints, TopologicalPharmacophoreAtomPairsFingerprints, or TopologicalPharmacophoreAtomTripletsFingerprints...";
 387   }
 388 
 389   $This->{MolecularComplexityType} = $Value;
 390 
 391   return $This;
 392 }
 393 
 394 # Set distance bin size for binning pharmacophore atom pair distances in atom triplets...
 395 #
 396 sub SetDistanceBinSize {
 397   my($This, $Value) = @_;
 398 
 399   if (!TextUtil::IsPositiveInteger($Value)) {
 400     croak "Error: ${ClassName}->SetDistanceBinSize: DistanceBinSize value, $Value, is not valid:  It must be a positive integer...";
 401   }
 402   $This->{DistanceBinSize} = $Value;
 403 
 404   return $This;
 405 }
 406 
 407 # Set normalization methodology to use for scaling the number of bits-set or unique keys
 408 # in fingerprints...
 409 #
 410 sub SetNormalizationMethodology {
 411   my($This, $Value) = @_;
 412 
 413   if ($Value !~ /^(ByHeavyAtomsCount|ByPossibleKeysCount|None)$/i) {
 414     croak "Error: ${ClassName}->SetNormalizationMethodology: NormalizationMethodology value, $Value, is not valid. Supported values: None, ByHeavyAtomsCount or ByPossibleKeysCount...";
 415   }
 416 
 417   if ($This->{MolecularComplexityType}) {
 418     if ($This->{MolecularComplexityType} !~ /^(ExtendedConnectivityFingerprints|TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) {
 419       croak "Error: ${ClassName}->SetNormalizationMethodology: Normalization is not supported for MolecularComplexityType: $This->{MolecularComplexityType}. Valid MolecularComplexityType values: ExtendedConnectivityFingerprints, TopologicalPharmacophoreAtomPairsFingerprints, or TopologicalPharmacophoreAtomTripletsFingerprints...\n";
 420     }
 421 
 422     if ($This->{MolecularComplexityType} =~ /^ExtendedConnectivityFingerprints$/i && $Value !~ /^(ByHeavyAtomsCount|None)$/i) {
 423       croak "Error: ${ClassName}->SetNormalizationMethodology: NormalizationMethodology value, $Value, is not valid for MolecularComplexityType: $This->{MolecularComplexityType}. Supported values: None or ByHeavyAtomsCount...";
 424     }
 425 
 426     if ($This->{MolecularComplexityType} =~ /^(TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i && $Value !~ /^(ByPossibleKeysCount|None)$/i) {
 427       croak "Error: ${ClassName}->SetNormalizationMethodology: NormalizationMethodology value, $Value, is not valid for MolecularComplexityType: $This->{MolecularComplexityType}. Supported values: None or ByPossibleKeysCount...";
 428     }
 429   }
 430 
 431   $This->{NormalizationMethodology} = $Value;
 432 
 433   return $This;
 434 }
 435 
 436 # Set intial atom identifier type..
 437 #
 438 sub SetAtomIdentifierType {
 439   my($This, $IdentifierType) = @_;
 440 
 441   if ($IdentifierType !~ /^(AtomicInvariantsAtomTypes|FunctionalClassAtomTypes|DREIDINGAtomTypes|EStateAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) {
 442     croak "Error: ${ClassName}->SetAtomIdentifierType: Specified value, $IdentifierType, for AtomIdentifierType is not vaild. Supported types in current release of MayaChemTools: AtomicInvariantsAtomTypes, FunctionalClassAtomTypes, DREIDINGAtomTypes, EStateAtomTypes, MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes and UFFAtomTypes.";
 443   }
 444 
 445   # FunctionalClassAtomTypes is the only valid atom identifier type for pharmacophore fingerprints...
 446   if ($This->{MolecularComplexityType} =~ /^(TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) {
 447     if ($IdentifierType !~ /^FunctionalClassAtomTypes$/i) {
 448       croak "Error: ${ClassName}->SetAtomIdentifierType: Specified value, $IdentifierType, for AtomIdentifierType is not vaild. Supported type for $This->{MolecularComplexityType} complexity type: FunctionalClassAtomTypes.";
 449     }
 450   }
 451 
 452   if ($This->{AtomIdentifierType}) {
 453     croak "Error: ${ClassName}->SetAtomIdentifierType: Can't change intial atom identifier type:  It's already set...";
 454   }
 455 
 456   $This->{AtomIdentifierType} = $IdentifierType;
 457 
 458   # Initialize identifier type information...
 459   $This->_InitializeAtomIdentifierTypeInformation();
 460 
 461   return $This;
 462 }
 463 
 464 # Calculate molecular complexity [ Ref 117-119 ] of a molecule using its fingerprints.
 465 #
 466 # The current release of MayaChemTools supports calculation of molecular complexity
 467 # corresponding to the number of bits-set or unique keys in molecular fingerprints. The
 468 # following types of fingerprints based molecular complexity measures are supported:
 469 #
 470 # AtomTypesFingerprints
 471 # ExtendedConnectivityFingerprints
 472 # MACCSKeys
 473 # PathLengthFingerprints
 474 # TopologicalAtomPairsFingerprints
 475 # TopologicalAtomTripletsFingerprints
 476 # TopologicalAtomTorsionsFingerprints
 477 # TopologicalPharmacophoreAtomPairsFingerprints
 478 # TopologicalPharmacophoreAtomTripletsFingerprints
 479 #
 480 # After the molecular complexity value has been calculated, it can also be normalized by
 481 # by scaling the number of bits-set or unique keys for following types of fingerprints:
 482 #
 483 # ExtendedConnectivityFingerprints
 484 # TopologicalPharmacophoreAtomPairsFingerprints
 485 # TopologicalPharmacophoreAtomTripletsFingerprints
 486 #
 487 # Two types of normalization methodologies are supported: by heavy atoms count for
 488 # extended connectivity fingerprints; by possible keys count for topological pharmacophore
 489 # atom pairs and triplets fingerprints.
 490 #
 491 sub GenerateDescriptors {
 492   my($This) = @_;
 493 
 494   # Initialize descriptor values...
 495   $This->_InitializeDescriptorValues();
 496 
 497   # Check availability of molecule...
 498   if (!$This->{Molecule}) {
 499     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Molecule data is not available: Molecule object hasn't been set...";
 500     return undef;
 501   }
 502 
 503   # Calculate descriptor values...
 504   if (!$This->_CalculateDescriptorValues()) {
 505     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Couldn't calculate MolecularComplexity values corresponding to assigned MolecularComplexity atom types...";
 506     return undef;
 507   }
 508 
 509   # Set final descriptor values...
 510   $This->_SetFinalDescriptorValues();
 511 
 512   return $This;
 513 }
 514 
 515 # Calculate molecular complexity value...
 516 #
 517 sub _CalculateDescriptorValues {
 518   my($This) = @_;
 519   my($FingerprintsObject, $MethodName);
 520 
 521   # Setup fingerprints object and generate fingerprints...
 522   $MethodName = "_Setup" . $This->{MolecularComplexityType};
 523   $FingerprintsObject = $This->$MethodName();
 524 
 525   $FingerprintsObject->GenerateFingerprints();
 526 
 527   # Make sure atom types fingerprints generation is successful...
 528   if (!$FingerprintsObject->IsFingerprintsGenerationSuccessful()) {
 529     return undef;
 530   }
 531 
 532   if (!$This->_CalculateMolecularComplexity($FingerprintsObject)) {
 533     return undef;
 534   }
 535 
 536   # Normalize molecular complexity...
 537   if ($This->{NormalizationMethodology} !~ /^None$/i) {
 538     if (!$This->_NormalizeMolecularComplexity($FingerprintsObject)) {
 539       return undef;
 540     }
 541   }
 542 
 543   return $This;
 544 }
 545 
 546 # Setup atom types fingerprints...
 547 #
 548 sub _SetupAtomTypesFingerprints {
 549   my($This) = @_;
 550   my($FingerprintsObject);
 551 
 552   $FingerprintsObject = new Fingerprints::AtomTypesFingerprints('Molecule' => $This->{Molecule}, 'Type' => 'AtomTypesCount', 'AtomIdentifierType' => $This->{AtomIdentifierType},  'IgnoreHydrogens' => 1);
 553   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
 554 
 555   return $FingerprintsObject;
 556 }
 557 
 558 # Setup extended connectivity fingerprints...
 559 #
 560 sub _SetupExtendedConnectivityFingerprints {
 561   my($This) = @_;
 562   my($FingerprintsObject);
 563 
 564   $FingerprintsObject = new Fingerprints::ExtendedConnectivityFingerprints('Molecule' => $This->{Molecule}, 'Type' => 'ExtendedConnectivity', 'NeighborhoodRadius' => $This->{NeighborhoodRadius}, 'AtomIdentifierType' => $This->{AtomIdentifierType});
 565   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
 566 
 567   return $FingerprintsObject;
 568 }
 569 
 570 # Setup MACCS keys...
 571 #
 572 sub _SetupMACCSKeys {
 573   my($This) = @_;
 574   my($FingerprintsObject);
 575 
 576   $FingerprintsObject = new Fingerprints::MACCSKeys('Molecule' => $This->{Molecule}, 'Type' => 'MACCSKeyBits', 'Size' => $This->{MACCSKeysSize});
 577 
 578   return $FingerprintsObject;
 579 }
 580 
 581 # Set up path length fingerprints...
 582 #
 583 sub _SetupPathLengthFingerprints {
 584   my($This) = @_;
 585   my($FingerprintsObject);
 586 
 587   $FingerprintsObject = new Fingerprints::PathLengthFingerprints('Molecule' => $This->{Molecule}, 'Type' => 'PathLengthCount', 'AtomIdentifierType' => $This->{AtomIdentifierType}, 'MinLength' => $This->{MinPathLength}, 'MaxLength' => $This->{MaxPathLength}, 'AllowRings' => 1, 'AllowSharedBonds' => 1, 'UseBondSymbols' => $This->{UseBondSymbols}, 'UseUniquePaths' => 1);
 588   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
 589 
 590   return $FingerprintsObject;
 591 }
 592 
 593 # Setup topological atom pairs fingerprints...
 594 #
 595 sub _SetupTopologicalAtomPairsFingerprints {
 596   my($This) = @_;
 597   my($FingerprintsObject);
 598 
 599   $FingerprintsObject = new Fingerprints::TopologicalAtomPairsFingerprints('Molecule' => $This->{Molecule}, 'MinDistance' => $This->{MinDistance}, 'MaxDistance' => $This->{MaxDistance}, 'AtomIdentifierType' => $This->{AtomIdentifierType});
 600   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
 601 
 602   return $FingerprintsObject;
 603 }
 604 
 605 # Setup topological atom triplets fingerprints...
 606 #
 607 sub _SetupTopologicalAtomTripletsFingerprints {
 608   my($This) = @_;
 609   my($FingerprintsObject);
 610 
 611   $FingerprintsObject = new Fingerprints::TopologicalAtomTripletsFingerprints('Molecule' => $This->{Molecule}, 'MinDistance' => $This->{MinDistance}, 'MaxDistance' => $This->{MaxDistance}, 'UseTriangleInequality' => $This->{UseTriangleInequality}, 'AtomIdentifierType' => $This->{AtomIdentifierType});
 612   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
 613 
 614   return $FingerprintsObject;
 615 }
 616 
 617 # Setup topological atom torsions fingerprints...
 618 #
 619 sub _SetupTopologicalAtomTorsionsFingerprints {
 620   my($This) = @_;
 621   my($FingerprintsObject);
 622 
 623   $FingerprintsObject = new Fingerprints::TopologicalAtomTorsionsFingerprints('Molecule' => $This->{Molecule},  'AtomIdentifierType' => $This->{AtomIdentifierType});
 624 
 625   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
 626 
 627   return $FingerprintsObject;
 628 }
 629 
 630 # Setup TopologicalPharmacophoreAtomPairsFingerprints...
 631 #
 632 sub _SetupTopologicalPharmacophoreAtomPairsFingerprints {
 633   my($This) = @_;
 634   my($FingerprintsObject, $AtomPairsSetSizeToUse);
 635 
 636   # Use fixed size to get total number of possible keys for normalization...
 637   $AtomPairsSetSizeToUse = ($This->{NormalizationMethodology} =~ /^ByPossibleKeysCount$/i) ? 'FixedSize' : 'ArbitrarySize';
 638 
 639   $FingerprintsObject = new Fingerprints::TopologicalPharmacophoreAtomPairsFingerprints('Molecule' => $This->{Molecule}, 'AtomPairsSetSizeToUse' => $AtomPairsSetSizeToUse, 'MinDistance' => $This->{MinDistance}, 'MaxDistance' => $This->{MaxDistance}, 'AtomTypesToUse' => \@{$This->{FunctionalClassesToUse}}, 'NormalizationMethodology' => 'None', 'ValuesPrecision' => 2);
 640 
 641   return $FingerprintsObject;
 642 }
 643 
 644 # Setup TopologicalPharmacophoreAtomTripletsFingerprints...
 645 #
 646 sub _SetupTopologicalPharmacophoreAtomTripletsFingerprints {
 647   my($This) = @_;
 648   my($FingerprintsObject, $AtomTripletsSetSizeToUse);
 649 
 650   # Use fixed size to get total number of possible keys for normalization...
 651   $AtomTripletsSetSizeToUse = ($This->{NormalizationMethodology} =~ /^ByPossibleKeysCount$/i) ? 'FixedSize' : 'ArbitrarySize';
 652 
 653   $FingerprintsObject = new Fingerprints::TopologicalPharmacophoreAtomTripletsFingerprints('Molecule' => $This->{Molecule}, 'AtomTripletsSetSizeToUse' => $AtomTripletsSetSizeToUse, 'MinDistance' => $This->{MinDistance}, 'MaxDistance' => $This->{MaxDistance}, 'DistanceBinSize' => $This->{DistanceBinSize}, 'UseTriangleInequality' => $This->{UseTriangleInequality}, 'AtomTypesToUse' => \@{$This->{FunctionalClassesToUse}});
 654 
 655   return $FingerprintsObject;
 656 }
 657 
 658 # Normalize molecular complexity value...
 659 #
 660 sub _NormalizeMolecularComplexity {
 661   my($This, $FingerprintsObject) = @_;
 662 
 663   if ($This->{MolecularComplexityType} =~ /^ExtendedConnectivityFingerprints$/i && $This->{NormalizationMethodology} =~ /^ByHeavyAtomsCount$/i) {
 664     return $This->_NormalizeMolecularComplexityByHeavyAtomsCount($FingerprintsObject);
 665   }
 666   elsif ($This->{MolecularComplexityType} =~ /^(TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i && $This->{NormalizationMethodology} =~ /^ByPossibleKeysCount$/i) {
 667     return $This->_NormalizeMolecularComplexityByPossibleKeysCount($FingerprintsObject);
 668   }
 669   else {
 670     warn "Warning: ${ClassName}->_NormalizeMolecularComplexity: NormalizationMethodology value, $This->{NormalizationMethodology}, is not valid. Supported values: ByHeavyAtomsCount or ByPossibleKeysCount...";
 671   }
 672   return undef;
 673 }
 674 
 675 # Normalize molecular complexity value by heavy atom count...
 676 #
 677 sub _NormalizeMolecularComplexityByHeavyAtomsCount {
 678   my($This, $FingerprintsObject) = @_;
 679   my($NumOfHeavyAtoms, $NormalizedComplexity);
 680 
 681   $NumOfHeavyAtoms = $This->{Molecule}->GetNumOfHeavyAtoms();
 682   if (!$NumOfHeavyAtoms) {
 683     return $This;
 684   }
 685 
 686   $NormalizedComplexity = $This->{MolecularComplexity} / $NumOfHeavyAtoms;
 687   $This->{MolecularComplexity} = MathUtil::round($NormalizedComplexity, 2) + 0;
 688 
 689   return $This;
 690 }
 691 
 692 # Normalize molecular complexity value by possible keys count...
 693 #
 694 sub _NormalizeMolecularComplexityByPossibleKeysCount {
 695   my($This, $FingerprintsObject) = @_;
 696   my($NumOfPossibleKeys, $NormalizedComplexity);
 697 
 698   $NumOfPossibleKeys = $FingerprintsObject->GetFingerprintsVector()->GetNumOfValues();
 699   if (!$NumOfPossibleKeys) {
 700     return $This;
 701   }
 702 
 703   $NormalizedComplexity = $This->{MolecularComplexity} / $NumOfPossibleKeys;
 704   $This->{MolecularComplexity} = MathUtil::round($NormalizedComplexity, 2) + 0;
 705 
 706   return $This;
 707 }
 708 
 709 # Calculate molecular complexity value using fingerprints objects...
 710 #
 711 sub _CalculateMolecularComplexity {
 712   my($This, $FingerprintsObject) = @_;
 713 
 714   if ($FingerprintsObject->GetVectorType() =~ /^FingerprintsBitVector$/i) {
 715     return $This->_CalculateMolecularComplexityUsingFingerprintsBitVector($FingerprintsObject->GetFingerprintsBitVector());
 716   }
 717   elsif ($FingerprintsObject->GetVectorType() =~ /^FingerprintsVector$/i) {
 718     return $This->_CalculateMolecularComplexityUsingFingerprintsVector($FingerprintsObject->GetFingerprintsVector());
 719   }
 720   else {
 721     warn "Warning: ${ClassName}->_CalculateMolecularComplexity: Fingerprints vector type  is not valid. Supported values: FingerprintsBitVector or FingerprintsVector...";
 722   }
 723 
 724   return undef;
 725 }
 726 
 727 # Calculate molecular complexity value using fingerprints vector...
 728 #
 729 sub _CalculateMolecularComplexityUsingFingerprintsVector {
 730   my($This, $FingerprintsVector) = @_;
 731 
 732   $This->{MolecularComplexity} = ($FingerprintsVector->GetType() =~ /^(OrderedNumericalValues|NumericalValues)$/i) ? $FingerprintsVector->GetNumOfNonZeroValues() : $FingerprintsVector->GetNumOfValues();
 733 
 734   return $This;
 735 }
 736 
 737 # Calculate molecular complexity value using fingerprints vector...
 738 #
 739 sub _CalculateMolecularComplexityUsingFingerprintsBitVector {
 740   my($This, $FingerprintsBitVector) = @_;
 741 
 742   $This->{MolecularComplexity} = $FingerprintsBitVector->GetNumOfSetBits();
 743 
 744   return $This;
 745 }
 746 
 747 # Setup final descriptor values...
 748 #
 749 sub _SetFinalDescriptorValues {
 750   my($This) = @_;
 751 
 752   $This->{DescriptorsGenerated} = 1;
 753 
 754   $This->SetDescriptorValues($This->{MolecularComplexity});
 755 
 756   return $This;
 757 }
 758 
 759 # Set atom identifier type to use for generating fingerprints...
 760 #
 761 sub _SetAtomIdentifierTypeValuesToUse {
 762   my($This, $FingerprintsObject) = @_;
 763 
 764   if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
 765     $FingerprintsObject->SetAtomicInvariantsToUse(\@{$This->{AtomicInvariantsToUse}});
 766   }
 767   elsif ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
 768     $FingerprintsObject->SetFunctionalClassesToUse(\@{$This->{FunctionalClassesToUse}});
 769   }
 770   elsif ($This->{AtomIdentifierType} =~ /^(DREIDINGAtomTypes|EStateAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) {
 771     # Nothing to do for now...
 772   }
 773   else {
 774     croak "Error: The value specified, $This->{AtomIdentifierType}, for option \"-a, --AtomIdentifierType\" is not valid. Supported atom identifier types in current release of MayaChemTools: AtomicInvariantsAtomTypes, DREIDINGAtomTypes, EStateAtomTypes, FunctionalClassAtomTypes, MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes\n";
 775   }
 776 }
 777 
 778 # Initialize atom indentifier type information...
 779 #
 780 # Current supported values:
 781 #
 782 # AtomicInvariantsAtomTypes, FunctionalClassAtomTypes, DREIDINGAtomTypes, EStateAtomTypes,
 783 # MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes
 784 #
 785 sub _InitializeAtomIdentifierTypeInformation {
 786   my($This) = @_;
 787 
 788   IDENTIFIERTYPE: {
 789     if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
 790       $This->_InitializeAtomicInvariantsAtomTypesInformation();
 791       last IDENTIFIERTYPE;
 792     }
 793     if ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
 794       $This->_InitializeFunctionalClassAtomTypesInformation();
 795       last IDENTIFIERTYPE;
 796     }
 797     if ($This->{AtomIdentifierType} =~ /^(DREIDINGAtomTypes|EStateAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) {
 798       # Nothing to do for now...
 799       last IDENTIFIERTYPE;
 800     }
 801     carp "Warning: ${ClassName}->_InitializeAtomIdentifierTypeInformation: Unknown atom indentifier type $This->{AtomIdentifierType}...";
 802   }
 803   return $This;
 804 }
 805 
 806 # Initialize atomic invariants atom types, generated by AtomTypes::AtomicInvariantsAtomTypes
 807 # class, to use for generating initial atom identifiers...
 808 #
 809 # Let:
 810 #   AS = Atom symbol corresponding to element symbol
 811 #
 812 #   X<n>   = Number of non-hydrogen atom neighbors or heavy atoms attached to atom
 813 #   BO<n> = Sum of bond orders to non-hydrogen atom neighbors or heavy atoms attached to atom
 814 #   LBO<n> = Largest bond order of non-hydrogen atom neighbors or heavy atoms attached to atom
 815 #   SB<n> = Number of single bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
 816 #   DB<n> = Number of double bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
 817 #   TB<n> = Number of triple bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
 818 #   H<n>   = Number of implicit and explicit hydrogens for atom
 819 #   Ar     = Aromatic annotation indicating whether atom is aromatic
 820 #   RA     = Ring atom annotation indicating whether atom is a ring
 821 #   FC<+n/-n> = Formal charge assigned to atom
 822 #   MN<n> = Mass number indicating isotope other than most abundant isotope
 823 #   SM<n> = Spin multiplicity of atom. Possible values: 1 (singlet), 2 (doublet) or 3 (triplet)
 824 #
 825 # Then:
 826 #
 827 #   Atom type generated by AtomTypes::AtomicInvariantsAtomTypes class corresponds to:
 828 #
 829 #     AS.X<n>.BO<n>.LBO<n>.<SB><n>.<DB><n>.<TB><n>.H<n>.Ar.RA.FC<+n/-n>.MN<n>.SM<n>
 830 #
 831 # Except for AS which is a required atomic invariant in atom types, all other atomic invariants are
 832 # optional.
 833 #
 834 # Default atomic invariants used for generating inital atom identifiers are [ Ref 24 ]:
 835 #
 836 #   AS, X<n>, BO<n>, H<n>, FC<+n/-n>, MN<n>
 837 #
 838 # In addition to usage of abbreviations for specifying atomic invariants, the following descriptive words
 839 # are also allowed:
 840 #
 841 # X : NumOfNonHydrogenAtomNeighbors or NumOfHeavyAtomNeighbors
 842 # BO : SumOfBondOrdersToNonHydrogenAtoms or SumOfBondOrdersToHeavyAtoms
 843 # LBO : LargestBondOrderToNonHydrogenAtoms or LargestBondOrderToHeavyAtoms
 844 # SB :  NumOfSingleBondsToNonHydrogenAtoms or NumOfSingleBondsToHeavyAtoms
 845 # DB : NumOfDoubleBondsToNonHydrogenAtoms or NumOfDoubleBondsToHeavyAtoms
 846 # TB : NumOfTripleBondsToNonHydrogenAtoms or NumOfTripleBondsToHeavyAtoms
 847 # H :  NumOfImplicitAndExplicitHydrogens
 848 # Ar : Aromatic
 849 # RA : RingAtom
 850 # FC : FormalCharge
 851 # MN : MassNumber
 852 # SM : SpinMultiplicity
 853 #
 854 sub _InitializeAtomicInvariantsAtomTypesInformation {
 855   my($This) = @_;
 856 
 857   @{$This->{AtomicInvariantsToUse}} = ();
 858 
 859   if ($This->{MolecularComplexityType} =~ /^(AtomTypesFingerprints|TopologicalAtomPairsFingerprints|TopologicalAtomTripletsFingerprints|TopologicalAtomTorsionsFingerprints)$/i) {
 860     @{$This->{AtomicInvariantsToUse}} = ('AS', 'X', 'BO', 'H', 'FC');
 861   }
 862   elsif ($This->{MolecularComplexityType} =~ /^ExtendedConnectivityFingerprints$/i) {
 863     @{$This->{AtomicInvariantsToUse}} = ('AS', 'X', 'BO', 'H', 'FC', 'MN');
 864   }
 865   elsif ($This->{MolecularComplexityType} =~ /^PathLengthFingerprints$/i) {
 866     @{$This->{AtomicInvariantsToUse}} = ('AS');
 867   }
 868 
 869   return $This;
 870 }
 871 
 872 # Initialize functional class atom types, generated by AtomTypes::FunctionalClassAtomTypes
 873 # class, to use for generating initial atom identifiers...
 874 #
 875 # Let:
 876 #   HBD: HydrogenBondDonor
 877 #   HBA: HydrogenBondAcceptor
 878 #   PI :  PositivelyIonizable
 879 #   NI : NegativelyIonizable
 880 #   Ar : Aromatic
 881 #   Hal : Halogen
 882 #   H : Hydrophobic
 883 #   RA : RingAtom
 884 #   CA : ChainAtom
 885 #
 886 # Then:
 887 #
 888 #   Functiononal class atom type specification for an atom corresponds to:
 889 #
 890 #     Ar.CA.H.HBA.HBD.Hal.NI.PI.RA
 891 #
 892 #   Default functional classes used are: HBD, HBA, PI, NI, Ar, Hal
 893 #
 894 #   FunctionalAtomTypes are assigned using the following definitions [ Ref 60-61, Ref 65-66 ]:
 895 #
 896 #     HydrogenBondDonor: NH, NH2, OH
 897 #     HydrogenBondAcceptor: N[!H], O
 898 #     PositivelyIonizable: +, NH2
 899 #     NegativelyIonizable: -, C(=O)OH, S(=O)OH, P(=O)OH
 900 #
 901 sub _InitializeFunctionalClassAtomTypesInformation {
 902   my($This) = @_;
 903 
 904   @{$This->{FunctionalClassesToUse}} = ();
 905 
 906   if ($This->{MolecularComplexityType} =~ /^(AtomTypesFingerprints|ExtendedConnectivityFingerprints|PathLengthFingerprints|TopologicalAtomPairsFingerprints|TopologicalAtomTripletsFingerprints|TopologicalAtomTorsionsFingerprints)$/i) {
 907     @{$This->{FunctionalClassesToUse}} = ('HBD', 'HBA', 'PI', 'NI', 'Ar', 'Hal');
 908   }
 909   elsif ($This->{MolecularComplexityType} =~ /^TopologicalPharmacophoreAtomPairsFingerprints$/i) {
 910     @{$This->{FunctionalClassesToUse}} = ('HBD', 'HBA', 'PI', 'NI', 'H');
 911   }
 912   elsif ($This->{MolecularComplexityType} =~ /^TopologicalPharmacophoreAtomTripletsFingerprints$/i) {
 913     @{$This->{FunctionalClassesToUse}} = ('HBD', 'HBA', 'PI', 'NI', 'H', 'Ar');
 914   }
 915 
 916   return $This;
 917 }
 918 
 919 # Set atomic invariants to use for generation of intial atom indentifiers...
 920 #
 921 sub SetAtomicInvariantsToUse {
 922   my($This, @Values) = @_;
 923   my($FirstValue, $TypeOfFirstValue, $AtomicInvariant, $SpecifiedAtomicInvariant, @SpecifiedAtomicInvariants, @AtomicInvariantsToUse);
 924 
 925   if (!@Values) {
 926     carp "Warning: ${ClassName}->SetAtomicInvariantsToUse: No values specified...";
 927     return;
 928   }
 929 
 930   if ($This->{AtomIdentifierType} !~ /^AtomicInvariantsAtomTypes$/i) {
 931     carp "Warning: ${ClassName}->SetAtomicInvariantsToUse: AtomicInvariantsToUse can't be set for InitialAtomIdentifierType of $This->{AtomIdentifierType}...";
 932     return;
 933   }
 934 
 935   $FirstValue = $Values[0];
 936   $TypeOfFirstValue = ref $FirstValue;
 937 
 938   @SpecifiedAtomicInvariants = ();
 939   @AtomicInvariantsToUse = ();
 940 
 941   if ($TypeOfFirstValue =~ /^ARRAY/) {
 942     push @SpecifiedAtomicInvariants, @{$FirstValue};
 943   }
 944   else {
 945     push @SpecifiedAtomicInvariants, @Values;
 946   }
 947 
 948   # Make sure specified AtomicInvariants are valid...
 949   for $SpecifiedAtomicInvariant (@SpecifiedAtomicInvariants) {
 950     if (!AtomTypes::AtomicInvariantsAtomTypes::IsAtomicInvariantAvailable($SpecifiedAtomicInvariant)) {
 951       croak "Error: ${ClassName}->SetAtomicInvariantsToUse: Specified atomic invariant, $SpecifiedAtomicInvariant, is not supported...\n ";
 952     }
 953     $AtomicInvariant = $SpecifiedAtomicInvariant;
 954     push @AtomicInvariantsToUse, $AtomicInvariant;
 955   }
 956 
 957   # Set atomic invariants to use...
 958   @{$This->{AtomicInvariantsToUse}} = ();
 959   push @{$This->{AtomicInvariantsToUse}}, @AtomicInvariantsToUse;
 960 
 961   return $This;
 962 }
 963 
 964 # Set functional classes to use for generation of intial atom indentifiers...
 965 #
 966 sub SetFunctionalClassesToUse {
 967   my($This, @Values) = @_;
 968   my($FirstValue, $TypeOfFirstValue, $FunctionalClass, $SpecifiedFunctionalClass, @SpecifiedFunctionalClasses, @FunctionalClassesToUse);
 969 
 970   if (!@Values) {
 971     carp "Warning: ${ClassName}->SetFunctionalClassesToUse: No values specified...";
 972     return;
 973   }
 974 
 975   if ($This->{AtomIdentifierType} !~ /^FunctionalClassAtomTypes$/i) {
 976     carp "Warning: ${ClassName}->SetFunctionalClassesToUse: FunctionalClassesToUse can't be set for InitialAtomIdentifierType of $This->{AtomIdentifierType}...";
 977     return;
 978   }
 979 
 980   $FirstValue = $Values[0];
 981   $TypeOfFirstValue = ref $FirstValue;
 982 
 983   @SpecifiedFunctionalClasses = ();
 984   @FunctionalClassesToUse = ();
 985 
 986   if ($TypeOfFirstValue =~ /^ARRAY/) {
 987     push @SpecifiedFunctionalClasses, @{$FirstValue};
 988   }
 989   else {
 990     push @SpecifiedFunctionalClasses, @Values;
 991   }
 992 
 993   # Make sure specified FunctionalClasses are valid...
 994   for $SpecifiedFunctionalClass (@SpecifiedFunctionalClasses) {
 995     if (!AtomTypes::FunctionalClassAtomTypes::IsFunctionalClassAvailable($SpecifiedFunctionalClass)) {
 996       croak "Error: ${ClassName}->SetFunctionalClassesToUse: Specified functional class, $SpecifiedFunctionalClass, is not supported...\n ";
 997     }
 998     push @FunctionalClassesToUse, $SpecifiedFunctionalClass;
 999   }
1000 
1001   # Set functional classes to use...
1002   @{$This->{FunctionalClassesToUse}} = ();
1003   push @{$This->{FunctionalClassesToUse}}, @FunctionalClassesToUse;
1004 
1005   return $This;
1006 }
1007 
1008 # Return a string containg data for MolecularComplexityDescriptors object...
1009 #
1010 sub StringifyMolecularComplexityDescriptors {
1011   my($This) = @_;
1012   my($ComplexityDescriptorsString, $Nothing);
1013 
1014   $ComplexityDescriptorsString = "MolecularDescriptorType: $This->{Type}; MolecularComplexityType: $This->{MolecularComplexityType}; " . $This->_StringifyDescriptorNamesAndValues();
1015 
1016   # Setup fingerprints specific information...
1017   if ($This->{MolecularComplexityType} =~ /^MACCSKeys$/i) {
1018     $ComplexityDescriptorsString .= "; MACCSKeysSize = $This->{MACCSKeysSize}";
1019   }
1020   elsif ($This->{MolecularComplexityType} =~ /^ExtendedConnectivityFingerprints$/i) {
1021     $ComplexityDescriptorsString .= "; NeighborhoodRadius = $This->{NeighborhoodRadius}; NormalizationMethodology = $This->{NormalizationMethodology}";
1022   }
1023   elsif ($This->{MolecularComplexityType} =~ /^PathLengthFingerprints$/i) {
1024     $ComplexityDescriptorsString .= "; MinPathLength = $This->{MinPathLength}; MaxPathLength = $This->{MaxPathLength}; UseBondSymbols: " . ($This->{UseBondSymbols} ? "Yes" : "No");
1025   }
1026   elsif ($This->{MolecularComplexityType} =~ /^TopologicalAtomPairsFingerprints$/i) {
1027     $ComplexityDescriptorsString .= "; MinDistance = $This->{MinDistance}; MaxDistance = $This->{MaxDistance}";
1028   }
1029   elsif ($This->{MolecularComplexityType} =~ /^TopologicalAtomTripletsFingerprints$/i) {
1030     $ComplexityDescriptorsString .= "; MinDistance = $This->{MinDistance}; MaxDistance = $This->{MaxDistance}; UseTriangleInequality: " . ($This->{UseTriangleInequality} ? "Yes" : "No");
1031   }
1032   elsif ($This->{MolecularComplexityType} =~ /^TopologicalAtomTorsionsFingerprints$/i) {
1033     $ComplexityDescriptorsString .= "; MinDistance = $This->{MinDistance}; MaxDistance = $This->{MaxDistance}";
1034   }
1035   elsif ($This->{MolecularComplexityType} =~ /^TopologicalPharmacophoreAtomPairsFingerprints$/i) {
1036     $ComplexityDescriptorsString .= "; MinDistance = $This->{MinDistance}; MaxDistance = $This->{MaxDistance}; NormalizationMethodology = $This->{NormalizationMethodology}";
1037   }
1038   elsif ($This->{MolecularComplexityType} =~ /^TopologicalPharmacophoreAtomTripletsFingerprints$/i) {
1039     $ComplexityDescriptorsString .= "; MinDistance = $This->{MinDistance}; MaxDistance = $This->{MaxDistance}; NormalizationMethodology = $This->{NormalizationMethodology};  DistanceBinSize: $This->{DistanceBinSize}; UseTriangleInequality: " . ($This->{UseTriangleInequality} ? "Yes" : "No");
1040   }
1041 
1042   # Setup atom identifier information...
1043   if ($This->{MolecularComplexityType} =~ /^(AtomTypesFingerprints|ExtendedConnectivityFingerprints|PathLengthFingerprints|TopologicalAtomPairsFingerprints|TopologicalAtomTripletsFingerprints|TopologicalAtomTorsionsFingerprints|TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) {
1044     $ComplexityDescriptorsString .= "; AtomIdentifierType = $This->{AtomIdentifierType}";
1045 
1046     if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
1047       my($AtomicInvariant, @AtomicInvariants, @AtomicInvariantsOrder, %AvailableAtomicInvariants);
1048 
1049       @AtomicInvariantsOrder = AtomTypes::AtomicInvariantsAtomTypes::GetAtomicInvariantsOrder();
1050       %AvailableAtomicInvariants = AtomTypes::AtomicInvariantsAtomTypes::GetAvailableAtomicInvariants();
1051 
1052       for $AtomicInvariant (@AtomicInvariantsOrder) {
1053         push @AtomicInvariants, "$AtomicInvariant: $AvailableAtomicInvariants{$AtomicInvariant}";
1054       }
1055 
1056       $ComplexityDescriptorsString .= "; AtomicInvariantsToUse: <" . TextUtil::JoinWords(\@{$This->{AtomicInvariantsToUse}}, ", ", 0) . ">";
1057       $ComplexityDescriptorsString .= "; AtomicInvariantsOrder: <" . TextUtil::JoinWords(\@AtomicInvariantsOrder, ", ", 0) . ">";
1058       $ComplexityDescriptorsString .= "; AvailableAtomicInvariants: <" . TextUtil::JoinWords(\@AtomicInvariants, ", ", 0) . ">";
1059     }
1060     elsif ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
1061       my($FunctionalClass, @FunctionalClasses, @FunctionalClassesOrder, %AvailableFunctionalClasses);
1062 
1063       @FunctionalClassesOrder = AtomTypes::FunctionalClassAtomTypes::GetFunctionalClassesOrder();
1064       %AvailableFunctionalClasses = AtomTypes::FunctionalClassAtomTypes::GetAvailableFunctionalClasses();
1065 
1066       for $FunctionalClass (@FunctionalClassesOrder) {
1067         push @FunctionalClasses, "$FunctionalClass: $AvailableFunctionalClasses{$FunctionalClass}";
1068       }
1069 
1070       $ComplexityDescriptorsString .= "; FunctionalClassesToUse: <" . TextUtil::JoinWords(\@{$This->{FunctionalClassesToUse}}, ", ", 0) . ">";
1071       $ComplexityDescriptorsString .= "; FunctionalClassesOrder: <" . TextUtil::JoinWords(\@FunctionalClassesOrder, ", ", 0) . ">";
1072       $ComplexityDescriptorsString .= "; AvailableFunctionalClasses: <" . TextUtil::JoinWords(\@FunctionalClasses, ", ", 0) . ">";
1073     }
1074   }
1075   return $ComplexityDescriptorsString;
1076 }
1077 
1078 # Is it a MolecularComplexityDescriptors object?
1079 sub _IsMolecularComplexityDescriptors {
1080   my($Object) = @_;
1081 
1082   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
1083 }
1084