       1 package Bond;
       2 #
       3 # $RCSfile: Bond.pm,v $
       4 # $Date: 2015/02/28 20:47:02 $
       5 # $Revision: 1.40 $
       6 #
       7 # Author: Manish Sud <msud@san.rr.com>
       8 #
       9 # Copyright (C) 2015 Manish Sud. All rights reserved.
      10 #
      11 # This file is part of MayaChemTools.
      12 #
      13 # MayaChemTools is free software; you can redistribute it and/or modify it under
      14 # the terms of the GNU Lesser General Public License as published by the Free
      15 # Software Foundation; either version 3 of the License, or (at your option) any
      16 # later version.
      17 #
      18 # MayaChemTools is distributed in the hope that it will be useful, but without
      19 # any warranty; without even the implied warranty of merchantability of fitness
      20 # for a particular purpose.  See the GNU Lesser General Public License for more
      21 # details.
      22 #
      23 # You should have received a copy of the GNU Lesser General Public License
      24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
      25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
      26 # Boston, MA, 02111-1307, USA.
      27 #
      28
      29 use strict;
      30 use Carp;
      31 use Exporter;
      32 use Storable ();
      33 use Scalar::Util ();
      34 use ObjectProperty;
      35
      36 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      37
      38 @ISA = qw(ObjectProperty Exporter);
      39 @EXPORT = qw();
      40 @EXPORT_OK = qw();
      41
      42 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
      43
      44 # Setup class variables...
      45 my($ClassName, $ObjectID);
      46 _InitializeClass();
      47
      48 # Overload Perl functions...
      49 use overload '""' => 'StringifyBond';
      50
      51 # Class constructor...
      52 sub new {
      53   my($Class, %NamesAndValues) = @_;
      54
      55   # Initialize object...
      56   my $This = {};
      57   bless $This, ref($Class) || $Class;
      58   $This->_InitializeBond();
      59
      60   $This->_InitializeBondProperties(%NamesAndValues);
      61
      62   return $This;
      63 }
      64
      65 # Initialize object data...
      66 #
      67 sub _InitializeBond {
      68   my($This) = @_;
      69   my($ObjectID) = _GetNewObjectID();
      70
      71   # All other property names and values along with all Set/Get<PropertyName> methods
      72   # are implemented on-demand using ObjectProperty class.
      73   $This->{ID} = $ObjectID;
      74
      75   # Bond from and to atoms indicate begining and ending bond atoms...
      76   $This->{BondFromAtom} = undef;
      77   $This->{BondToAtom} = undef;
      78
      79   $This->{BondOrder} = '';
      80   $This->{BondType} = '';
      81   $This->{BondStereochemistry} = '';
      82 }
      83
      84 # Initialize class ...
      85 sub _InitializeClass {
      86   #Class name...
      87   $ClassName = __PACKAGE__;
      88
      89   # ID to keep track of objects...
      90   $ObjectID = 0;
      91 }
      92
      93 # Initialize bond properties...
      94 sub _InitializeBondProperties {
      95   my($This, %NamesAndValues) = @_;
      96
      97   my($Name, $Value, $MethodName);
      98   while (($Name, $Value) = each  %NamesAndValues) {
      99     $MethodName = "Set${Name}";
     100     $This->$MethodName($Value);
     101   }
     102
     103   if (!exists $NamesAndValues{'Atoms'}) {
     104     carp "Warning: ${ClassName}->new: Bond object instantiated without specifying atoms list...";
     105   }
     106   if (!exists $NamesAndValues{'BondOrder'}) {
     107     carp "Warning: ${ClassName}->new: Bond object instantiated without setting bond order...";
     108   }
     109   return $This;
     110 }
     111
     112 # Setup an explicit SetID method to block setting of ID by AUTOLOAD function...
     113 sub SetID {
     114   my($This, $Value) = @_;
     115
     116   carp "Warning: ${ClassName}->SetID: Object ID can't be changed: it's used for internal tracking...";
     117
     118   return $This;
     119 }
     120
     121 # Setup an explicit SetMolecule method to block setting of ID by AUTOLOAD function...
     122 sub SetMolecule {
     123   my($This, $Value) = @_;
     124
     125   carp "Warning: ${ClassName}->SetMolecule: Molecule property can't be changed: it's used for internal tracking...";
     126
     127   return $This;
     128 }
     129
     130 # Assign bond to  molecule...
     131 sub _SetMolecule {
     132   my($This, $Molecule) = @_;
     133
     134   $This->{Molecule} = $Molecule;
     135
     136   # Weaken the reference to disable increment of reference count; otherwise,
     137   # it it becomes a circular reference and destruction of Molecule object doesn't
     138   # get initiated which in turn disables destruction of bond object.
     139   #
     140   Scalar::Util::weaken($This->{Molecule});
     141
     142   return $This;
     143 }
     144
     145 # Set bond atoms...
     146 sub SetAtoms {
     147   my($This, @Values) = @_;
     148
     149   if (!@Values) {
     150     croak "Error: ${ClassName}->SetAtoms: No atoms specified...";
     151   }
     152
     153   my($FirstValue, $TypeOfFirstValue, $Atom1, $Atom2, $AtomID1, $AtomID2);
     154   $FirstValue = $Values[0];
     155   $TypeOfFirstValue = ref $FirstValue;
     156
     157   if ($TypeOfFirstValue =~ /^ARRAY/) {
     158     # Initialize using array refernce...
     159     if (@{$FirstValue} != 2) {
     160       croak "Warning: ${ClassName}->SetAtoms: Number of atoms specified in bond object is not equal to 2...";
     161     }
     162     ($Atom1, $Atom2) = @{$FirstValue};
     163   }
     164   else {
     165     # It's a list of values...
     166     if (@Values != 2) {
     167       croak "Warning: ${ClassName}->SetAtoms: Number of atoms specified in bond object is not equal to 2...";
     168     }
     169     ($Atom1, $Atom2) = @Values;
     170   }
     171
     172   $AtomID1 = $Atom1->GetID(); $AtomID2 = $Atom2->GetID();
     173   if ($AtomID1 == $AtomID2) {
     174       croak "Warning: ${ClassName}->SetAtoms: Can't specify same atoms to create a bond...";
     175   }
     176
     177   $This->{BondFromAtom} = $Atom1;
     178   $This->{BondToAtom} = $Atom2;
     179
     180   return $This;
     181 }
     182
     183 # Get bond atoms as array. In scalar context, return number of atoms involved in
     184 # bond...
     185 #
     186 sub GetAtoms {
     187   my($This) = @_;
     188
     189   return wantarray ? ($This->{BondFromAtom}, $This->{BondToAtom}) : 2;
     190 }
     191
     192 # Get atom bonded to specified atom...
     193 sub GetBondedAtom {
     194   my($This, $Atom) = @_;
     195   my($Atom1, $Atom2, $AtomID1, $AtomID2, $AtomID);
     196
     197   ($Atom1, $Atom2) = $This->GetAtoms();
     198   $AtomID1 = $Atom1->GetID(); $AtomID2 = $Atom2->GetID(); $AtomID = $Atom->GetID();
     199
     200   return ($AtomID1 == $AtomID) ? $Atom2 : (($AtomID2 == $AtomID) ? $Atom1 : undef) ;
     201 }
     202
     203 # Get common atom between two bonds...
     204 sub GetCommonAtom {
     205   my($This, $Other) = @_;
     206   my($Atom1, $Atom2, $AtomID1, $AtomID2, $OtherAtom1, $OtherAtom2, $OtherAtomID1, $OtherAtomID2);
     207
     208   ($Atom1, $Atom2) = $This->GetAtoms();
     209   $AtomID1 = $Atom1->GetID(); $AtomID2 = $Atom2->GetID();
     210
     211   ($OtherAtom1, $OtherAtom2) = $Other->GetAtoms();
     212   $OtherAtomID1 = $OtherAtom1->GetID(); $OtherAtomID2 = $OtherAtom2->GetID();
     213
     214   return ($AtomID1 == $OtherAtomID1 || $AtomID1 == $OtherAtomID2) ? $Atom1 : (($AtomID2 == $OtherAtomID1 || $AtomID2 == $OtherAtomID2) ? $Atom2 : undef) ;
     215 }
     216
     217 # Get bond from atom indicating begining atom of a bond...
     218 sub GetBondFromAtom {
     219   my($This) = @_;
     220
     221   return $This->{BondFromAtom};
     222 }
     223
     224 # Get begin bond atom...
     225 sub GetBondBeginAtom {
     226   my($This) = @_;
     227
     228   return $This->GetBondFromAtom();
     229 }
     230
     231 # Get bond to atom indicating ending atom of a bond...
     232 sub GetBondToAtom {
     233   my($This) = @_;
     234
     235   return $This->{BondToAtom};
     236 }
     237
     238 # Get begin bond atom...
     239 sub GetBondEndAtom {
     240   my($This) = @_;
     241
     242   return $This->GetBondToAtom();
     243 }
     244
     245 # Switch bond from and to atoms...
     246 sub SwitchBondFromAndToAtoms {
     247   my($This) = @_;
     248   my($FromAtom, $ToAtom);
     249
     250   ($FromAtom, $ToAtom) = $This->GetAtoms();
     251
     252   $This->{BondFromAtom} = $ToAtom;
     253   $This->{BondToAtom} = $FromAtom;
     254
     255   return $This;
     256 }
     257
     258 # Set bond order...
     259 #
     260 # Possible BondOrder are: 0 = None, 1 = Single, 1.5 = Aromatic, 2 = Double, 3 = Triple, 4 = Quadruple,
     261 # 5 = Quintuple, 6 = Sextuple, 7 = Septuple
     262 #
     263 # Possible BondType for different BondOrders are:
     264 #
     265 # 0 : None, Ionic, Unspecified
     266 # 1 : Single, Dative, Coordinate, SingleOrDouble, SingleOrAromatic, Tautomeric
     267 # 1.5 : Aromatic, Resonance, SingleOrAromatic, DoubleOrAromatic
     268 # 2 : Double, Tautomeric, SingleOrDouble, DoubleOrAromatic
     269 # 3 : Triple
     270 # 4 : Quadruple
     271 # 5 : Quintuple
     272 # 6 : Sextuple
     273 # 7 : Septuple
     274 #
     275 # Note: BondType Any is valid for all BondOrders.
     276 #
     277 # Possible BondStereochemistry values for different BondOrders are:
     278 #
     279 # 0 : None, Unspecified
     280 # 1 : Wedge, Up, Hash, Down, Wavy, WedgeOrHash, UpOrDown, Upward, Downward, None, Unspecified
     281 # 2 : Cis, Trans, Z, E, DoubleCross, CisOrTrans, None, Unspecified
     282 #
     283 # Notes:
     284 #   . BondType property is automatically assigned using default BondType values for
     285 #     specified BondOrder.
     286 #   . BondType values can also be explicit set.
     287 #   . To make bonds aromatic in a ring, explicitly set "Aromatic" property for bond/atoms and make sure
     288 #     appropriate BondOrder values are assigned.
     289 #   . Dative or coordinate bond types are treated as single bond types with explicit formal charge
     290 #     of + and - on first and second bond atoms.
     291 #
     292 sub SetBondOrder {
     293   my($This, $BondOrder) = @_;
     294
     295   if ($BondOrder !~ /^(0|1|1.5|2|3|4|5|6|7)$/) {
     296     croak "Error: ${ClassName}->SetBondOrder: BondOrder value $BondOrder is not valid. Supported values: 0, 1, 1.5, 2, 3, 4, 5, 6, 7";
     297   }
     298
     299   # Set bond order and type...
     300   my($BondType);
     301
     302   BONDORDER: {
     303       if ($BondOrder == 1) {$BondType = 'Single'; last BONDORDER; }
     304       if ($BondOrder == 1.5) {$BondType = 'Aromatic'; last BONDORDER; }
     305       if ($BondOrder == 2) {$BondType = 'Double'; last BONDORDER; }
     306       if ($BondOrder == 3) {$BondType = 'Triple'; last BONDORDER; }
     307       if ($BondOrder == 4) {$BondType = 'Quadruple'; last BONDORDER; }
     308       if ($BondOrder == 5) {$BondType = 'Quintuple'; last BONDORDER; }
     309       if ($BondOrder == 6) {$BondType = 'Sextuple'; last BONDORDER; }
     310       if ($BondOrder == 7) {$BondType = 'Septuple'; last BONDORDER; }
     311       $BondType = '';
     312       $BondOrder = 0;
     313   }
     314   $This->{BondType} = $BondType;
     315   $This->{BondOrder} = $BondOrder;
     316
     317   return $This;
     318
     319 }
     320
     321 # Set bond type for a specific bond...
     322 #
     323 sub SetBondType {
     324   my($This, $BondType) = @_;
     325
     326   if ($BondType !~ /^(None|Ionic|Unspecified|Single|Dative|Coordinate|SingleOrDouble|SingleOrAromatic|Aromatic|Tautomeric|Resonance|DoubleOrAromatic|Double|Triple|Quadruple|Any)$/i) {
     327     croak "Error: ${ClassName}->SetBondType: BondType value $BondType is not valid. Supported values: None, Ionic, Unspecified, Single, Dative, Coordinate, SingleOrDouble, SingleOrAromatic, Aromatic, Tautomeric, Resonance, DoubleOrAromatic, Double, Triple, Quadruple, Any...";
     328   }
     329
     330   # Make sure its a valid BondType value for BondOrder...
     331   my($BondOrder, $ValidBondType);
     332
     333   $ValidBondType = 0;
     334   $BondOrder = $This->{BondOrder};
     335
     336   BONDORDER: {
     337       if ($BondOrder == 0 && $BondType =~ /^(None|Ionic|Unspecified|Any)$/i) {$ValidBondType = 1; last BONDORDER; }
     338       if ($BondOrder == 1 && $BondType =~ /^(Single|Dative|Coordinate|SingleOrDouble|SingleOrAromatic|Tautomeric|Any)$/i) {$ValidBondType = 1; last BONDORDER; }
     339       if ($BondOrder == 1.5 && $BondType =~ /^(Aromatic|Resonance|SingleOrAromatic|DoubleOrAromatic|Any)$/i) {$ValidBondType = 1; last BONDORDER; }
     340       if ($BondOrder == 2 && $BondType =~ /^(Double|SingleOrDouble|DoubleOrAromatic|Tautomeric|Any)$/i ) {$ValidBondType = 1; last BONDORDER; }
     341       if ($BondOrder == 3 && $BondType =~ /^(Triple|Any)$/i) {$ValidBondType = 1; last BONDORDER; }
     342       if ($BondOrder == 4 && $BondType =~ /^(Quadruple|Any)$/i) {$ValidBondType = 1; last BONDORDER; }
     343       if ($BondOrder == 5 && $BondType =~ /^(Quintuple|Any)$/i) {$ValidBondType = 1; last BONDORDER; }
     344       if ($BondOrder == 6 && $BondType =~ /^(Sextuple|Any)$/i) {$ValidBondType = 1; last BONDORDER; }
     345       if ($BondOrder == 7 && $BondType =~ /^(Septuple|Any)$/i) {$ValidBondType = 1; last BONDORDER; }
     346       $ValidBondType = 0;
     347   }
     348
     349   if (!$ValidBondType) {
     350     carp "Warning: ${ClassName}->SetBondType: Assigning invalid BondType value, $BondType, for BondOrder $BondOrder...";
     351   }
     352
     353   $This->{BondType} = $BondType;
     354
     355   # Set explicit formal charge for atoms involved in Dative or coordinate bonds...
     356   if ($BondType =~ /^(Dative|Coordinate)$/i) {
     357     my($Atom1, $Atom2) = $This->GetAtoms();
     358     $Atom1->SetFormalCharge('1');
     359     $Atom2->SetFormalCharge('-1');
     360   }
     361
     362   return $This;
     363 }
     364
     365 # Set bond stereochemistry...
     366 #
     367 # Single bond stereochemistry:
     368 #
     369 # None, Unspecified: Not a stereo bond or unspecified
     370 #
     371 # Wedge, Up : Wedge end pointing up
     372 # Hash, Down: Wedge end pointing down
     373 # Wavy, WedgeOrHash, UpOrDown: Wedge end up or down
     374 #
     375 # Note: Wedge starts at begin atom of bond making wedge pointed end always at this
     376 #       atom.
     377 #
     378 # Upward: Single bond around cis/trans double bonds pointing upward
     379 # Downward: Single bond around cis/trans double bonds pointing upward
     380 #
     381 # Note: Upward/downward bonds start at atoms involved in cis/trans double bond.
     382 #
     383 # Double bond stereochemistry:
     384 #
     385 # None, Unspecified: Not a stereo bond or unspecified
     386 #
     387 # Z, cis: Similar groups on same side of double bond
     388 # E, trans: Similar groups on different side of double bond
     389 #
     390 # CisOrTrans, DoubleCross: cis or trans
     391 #
     392 sub SetBondStereochemistry {
     393   my($This, $BondStereochemistry) = @_;
     394
     395   if ($BondStereochemistry !~ /^(None|Unspecified|Wedge|Hash|Up|Down|Wavy|WedgeOrHash|UpOrDown|Upward|Downward|Cis|Trans|Z|E|DoubleCross|CisOrTrans)$/i) {
     396     croak "Error: ${ClassName}->SetBondStereochemistry: BondStereochemistry value $BondStereochemistry is not valid. Supported values: None, Unspecified, Wedge, Hash, Up, Down, Wavy, WedgeOrHash, UpOrDown, Upward, Downward, Cis, Trans, Z, E, DoubleCross, CisOrTrans...";
     397   }
     398
     399   # Make sure its a valid BondStereochemistry value for BondOrder...
     400   my($BondOrder, $ValidBondType);
     401
     402   $ValidBondType = 0;
     403   $BondOrder = $This->{BondOrder};
     404
     405   BONDORDER: {
     406       if ($BondOrder == 0 && $BondStereochemistry =~ /^(None|Unspecified)$/i) {$ValidBondType = 1; last BONDORDER; }
     407       if ($BondOrder == 1 && $BondStereochemistry =~ /^(Wedge|Hash|Up|Down|Wavy|WedgeOrHash|UpOrDown|Upward|Downward|None|Unspecified)$/i) {$ValidBondType = 1; last BONDORDER; }
     408       if ($BondOrder == 2 && $BondStereochemistry =~ /^(Cis|Trans|Z|E|DoubleCross|CisOrTrans|None|Unspecified)$/i ) {$ValidBondType = 1; last BONDORDER; }
     409       $ValidBondType = 0;
     410   }
     411
     412   if (!$ValidBondType) {
     413     carp "Warning: ${ClassName}->SetBondStereochemistry: Assigning invalid BondStereochemistry value, $BondStereochemistry, for BondOrder $BondOrder...";
     414   }
     415
     416   $This->{BondStereochemistry} = $BondStereochemistry;
     417
     418   return $This;
     419 }
     420
     421 # Is it a single bond?
     422 sub IsSingle {
     423   my($This) = @_;
     424
     425   return ($This->{BondOrder} == 1) ? 1 : 0;
     426 }
     427
     428 # Is it a double bond?
     429 sub IsDouble {
     430   my($This) = @_;
     431
     432   return ($This->{BondOrder} == 2) ? 1 : 0;
     433 }
     434
     435 # Is it a triple bond?
     436 sub IsTriple {
     437   my($This) = @_;
     438
     439   return ($This->{BondOrder} == 3) ? 1 : 0;
     440 }
     441
     442 # Is it a quadruple bond?
     443 sub IsQuadruple {
     444   my($This) = @_;
     445
     446   return ($This->{BondOrder} == 4) ? 1 : 0;
     447 }
     448
     449 # Is aromatic property set for the bond?
     450 sub IsAromatic {
     451   my($This) = @_;
     452   my($Aromatic);
     453
     454   $Aromatic = $This->GetAromatic();
     455
     456   return ((defined($Aromatic) && $Aromatic) || $This->{BondOrder} == 1.5) ? 1 : 0;
     457 }
     458
     459 # Is it a quintuple bond?
     460 sub IsQuintuple {
     461   my($This) = @_;
     462
     463   return ($This->{BondOrder} == 5) ? 1 : 0;
     464 }
     465
     466 # Is it a sextuple bond?
     467 sub IsSextuple {
     468   my($This) = @_;
     469
     470   return ($This->{BondOrder} == 6) ? 1 : 0;
     471 }
     472
     473 # Is bond type specified?
     474 sub IsBondTypeSpecified {
     475   my($This) = @_;
     476
     477   return ($This->{BondType} && $This->{BondType} !~ /^(None|Unspecified)$/i) ? 1 : 0;
     478 }
     479
     480 # Is it a dative or coordinate bond?
     481 sub IsDative {
     482   my($This) = @_;
     483
     484   return ($This->{BondType} =~ /^(Dative|Coordinate)$/i) ? 1 : 0;
     485 }
     486
     487 # Is it a dative or coordinate bond?
     488 sub IsCoordinate {
     489   my($This) = @_;
     490
     491   return $This->IsDative();
     492 }
     493
     494 # Is it a ionic bond?
     495 sub IsIonic {
     496   my($This) = @_;
     497
     498   return ($This->{BondType} =~ /^Ionic$/i) ? 1 : 0;
     499 }
     500
     501 # Is it a tautomeric bond?
     502 sub IsTautomeric {
     503   my($This) = @_;
     504
     505   return ($This->{BondType} =~ /^Tautomeric$/i) ? 1 : 0;
     506 }
     507
     508 # Is bond stereochemistry specified?
     509 sub IsBondStereochemistrySpecified {
     510   my($This) = @_;
     511
     512   return ($This->{BondStereochemistry} && $This->{BondStereochemistry} !~ /^(None|Unspecified)$/i) ? 1 : 0;
     513 }
     514
     515 # Is it a Wedge or Up single bond?
     516 sub IsWedge {
     517   my($This) = @_;
     518
     519   return $This->IsUp();
     520 }
     521
     522 # Is it a Wedge or Up single bond?
     523 sub IsUp {
     524   my($This) = @_;
     525
     526   return ($This->{BondStereochemistry} =~ /^(Wedge|Up)$/i) ? 1 : 0;
     527 }
     528
     529 # Is it a Hash or Down single bond?
     530 sub IsHash {
     531   my($This) = @_;
     532
     533   return $This->IsDown();
     534 }
     535
     536 # Is it a Hash or Down single bond?
     537 sub IsDown {
     538   my($This) = @_;
     539
     540   return ($This->{BondStereochemistry} =~ /^(Hash|Down)$/i) ? 1 : 0;
     541 }
     542
     543 # Is it a Wavy, WedgeOrHash or UpOrDown single bond?
     544 sub IsWedgeOrHash {
     545   my($This) = @_;
     546
     547   return $This->IsUpOrDown();
     548 }
     549
     550 # Is it a Wavy, WedgeOrHash or UpOrDown single bond?
     551 sub IsUpOrDown {
     552   my($This) = @_;
     553
     554   return ($This->{BondStereochemistry} =~ /^(Wavy|WedgeOrHash|UpOrDown)$/i) ? 1 : 0;
     555 }
     556
     557 # Is it a upward single bond?
     558 sub IsUpward {
     559   my($This) = @_;
     560
     561   return ($This->{BondStereochemistry} =~ /^Upward$/i) ? 1 : 0;
     562 }
     563
     564 # Is it a Downward single bond?
     565 sub IsDownward {
     566   my($This) = @_;
     567
     568   return ($This->{BondStereochemistry} =~ /^Downward$/i) ? 1 : 0;
     569 }
     570 # Is it a cis or Z double bond?
     571 sub IsCis {
     572   my($This) = @_;
     573
     574   return ($This->{BondStereochemistry} =~ /^(Cis|Z)$/i) ? 1 : 0;
     575 }
     576
     577 # Is it a trans or E double bond?
     578 sub IsTrans {
     579   my($This) = @_;
     580
     581   return ($This->{BondStereochemistry} =~ /^(Trans|E)$/i) ? 1 : 0;
     582 }
     583
     584 # Is it a cis or trans double bond?
     585 sub IsCisOrTrans {
     586   my($This) = @_;
     587
     588   return ($This->{BondStereochemistry} =~ /^(CisOrTrans|DoubleCross)$/i) ? 1 : 0;
     589 }
     590
     591 # Delete bond...
     592 sub DeleteBond {
     593   my($This) = @_;
     594
     595   # Is this atom in a molecule?
     596   if (!$This->HasProperty('Molecule')) {
     597     # Nothing to do...
     598     return $This;
     599   }
     600   my($Molecule);
     601   $Molecule = $This->GetProperty('Molecule');
     602   $Molecule->DeleteBond($This);
     603
     604   return $This;
     605 }
     606
     607 # Copy bond and all its associated data...
     608 sub Copy {
     609   my($This) = @_;
     610   my($Bond);
     611
     612   $Bond = Storable::dclone($This);
     613
     614   return $Bond;
     615 }
     616
     617 # Is bond in a ring?
     618 #
     619 sub IsInRing {
     620   my($This) = @_;
     621
     622   # Is this bond in a molecule?
     623   if (!$This->HasProperty('Molecule')) {
     624     return undef;
     625   }
     626   my($Molecule);
     627   $Molecule = $This->GetProperty('Molecule');
     628
     629   return $Molecule->_IsBondInRing($This);
     630 }
     631
     632 # Is bond not in a ring?
     633 #
     634 sub IsNotInRing {
     635   my($This) = @_;
     636
     637   # Is this bond in a molecule?
     638   if (!$This->HasProperty('Molecule')) {
     639     return undef;
     640   }
     641   my($Molecule);
     642   $Molecule = $This->GetProperty('Molecule');
     643
     644   return $Molecule->_IsBondNotInRing($This);
     645 }
     646
     647 # Is bond only in one ring?
     648 #
     649 sub IsOnlyInOneRing {
     650   my($This) = @_;
     651
     652   # Is this bond in a molecule?
     653   if (!$This->HasProperty('Molecule')) {
     654     return undef;
     655   }
     656   my($Molecule);
     657   $Molecule = $This->GetProperty('Molecule');
     658
     659   return $Molecule->_IsBondInOnlyOneRing($This);
     660 }
     661
     662 # Is bond in a ring of specific size?
     663 #
     664 sub IsInRingOfSize {
     665   my($This, $RingSize) = @_;
     666
     667   # Is this bond in a molecule?
     668   if (!$This->HasProperty('Molecule')) {
     669     return undef;
     670   }
     671   my($Molecule);
     672   $Molecule = $This->GetProperty('Molecule');
     673
     674   return $Molecule->_IsBondInRingOfSize($This, $RingSize);
     675 }
     676
     677 # Get size of smallest ring containing the bond...
     678 #
     679 sub GetSizeOfSmallestRing {
     680   my($This) = @_;
     681
     682   # Is this bond in a molecule?
     683   if (!$This->HasProperty('Molecule')) {
     684     return undef;
     685   }
     686   my($Molecule);
     687   $Molecule = $This->GetProperty('Molecule');
     688
     689   return $Molecule->_GetSizeOfSmallestBondRing($This);
     690 }
     691
     692 # Get size of largest ring containing the bond...
     693 #
     694 sub GetSizeOfLargestRing {
     695   my($This) = @_;
     696
     697   # Is this bond in a molecule?
     698   if (!$This->HasProperty('Molecule')) {
     699     return undef;
     700   }
     701   my($Molecule);
     702   $Molecule = $This->GetProperty('Molecule');
     703
     704   return $Molecule->_GetSizeOfLargestBondRing($This);
     705 }
     706
     707 # Get number of  rings containing the bond...
     708 #
     709 sub GetNumOfRings {
     710   my($This) = @_;
     711
     712   # Is this bond in a molecule?
     713   if (!$This->HasProperty('Molecule')) {
     714     return undef;
     715   }
     716   my($Molecule);
     717   $Molecule = $This->GetProperty('Molecule');
     718
     719   return $Molecule->_GetNumOfBondRings($This);
     720 }
     721
     722 # Get number of  rings with odd size containing the bond...
     723 #
     724 sub GetNumOfRingsWithOddSize {
     725   my($This) = @_;
     726
     727   # Is this bond in a molecule?
     728   if (!$This->HasProperty('Molecule')) {
     729     return undef;
     730   }
     731   my($Molecule);
     732   $Molecule = $This->GetProperty('Molecule');
     733
     734   return $Molecule->_GetNumOfBondRingsWithOddSize($This);
     735 }
     736
     737 # Get number of  rings with even size containing the bond...
     738 #
     739 sub GetNumOfRingsWithEvenSize {
     740   my($This) = @_;
     741
     742   # Is this bond in a molecule?
     743   if (!$This->HasProperty('Molecule')) {
     744     return undef;
     745   }
     746   my($Molecule);
     747   $Molecule = $This->GetProperty('Molecule');
     748
     749   return $Molecule->_GetNumOfBondRingsWithEvenSize($This);
     750 }
     751
     752 # Get number of  rings with specified size containing the bond...
     753 #
     754 sub GetNumOfRingsWithSize {
     755   my($This, $RingSize) = @_;
     756
     757   # Is this bond in a molecule?
     758   if (!$This->HasProperty('Molecule')) {
     759     return undef;
     760   }
     761   my($Molecule);
     762   $Molecule = $This->GetProperty('Molecule');
     763
     764   return $Molecule->_GetNumOfBondRingsWithSize($This, $RingSize);
     765 }
     766
     767 # Get number of  rings with size less than specified containing the bond...
     768 #
     769 sub GetNumOfRingsWithSizeLessThan {
     770   my($This, $RingSize) = @_;
     771
     772   # Is this bond in a molecule?
     773   if (!$This->HasProperty('Molecule')) {
     774     return undef;
     775   }
     776   my($Molecule);
     777   $Molecule = $This->GetProperty('Molecule');
     778
     779   return $Molecule->_GetNumOfBondRingsWithSizeLessThan($This, $RingSize);
     780 }
     781
     782 # Get number of  rings with size greater than specified size containing the bond...
     783 #
     784 sub GetNumOfRingsWithSizeGreaterThan {
     785   my($This, $RingSize) = @_;
     786
     787   # Is this bond in a molecule?
     788   if (!$This->HasProperty('Molecule')) {
     789     return undef;
     790   }
     791   my($Molecule);
     792   $Molecule = $This->GetProperty('Molecule');
     793
     794   return $Molecule->_GetNumOfBondRingsWithSizeGreaterThan($This, $RingSize);
     795 }
     796
     797 # Get all rings as an array of references to arrays containing ring atoms...
     798 #
     799 sub GetRings {
     800   my($This) = @_;
     801
     802   # Is this bond in a molecule?
     803   if (!$This->HasProperty('Molecule')) {
     804     return undef;
     805   }
     806   my($Molecule);
     807   $Molecule = $This->GetProperty('Molecule');
     808
     809   return $Molecule->_GetBondRings($This);
     810 }
     811
     812 # Get smallest ring as an array containing ring atoms...
     813 #
     814 sub GetSmallestRing {
     815   my($This) = @_;
     816
     817   # Is this bond in a molecule?
     818   if (!$This->HasProperty('Molecule')) {
     819     return undef;
     820   }
     821   my($Molecule);
     822   $Molecule = $This->GetProperty('Molecule');
     823
     824   return $Molecule->_GetSmallestBondRing($This);
     825 }
     826
     827 # Get largest ring as an array containing ring atoms...
     828 #
     829 sub GetLargestRing {
     830   my($This) = @_;
     831
     832   # Is this bond in a molecule?
     833   if (!$This->HasProperty('Molecule')) {
     834     return undef;
     835   }
     836   my($Molecule);
     837   $Molecule = $This->GetProperty('Molecule');
     838
     839   return $Molecule->_GetLargestBondRing($This);
     840 }
     841
     842 # Get odd size rings an array of references to arrays containing ring atoms...
     843 #
     844 sub GetRingsWithOddSize {
     845   my($This) = @_;
     846
     847   # Is this bond in a molecule?
     848   if (!$This->HasProperty('Molecule')) {
     849     return undef;
     850   }
     851   my($Molecule);
     852   $Molecule = $This->GetProperty('Molecule');
     853
     854   return $Molecule->_GetBondRingsWithOddSize($This);
     855 }
     856
     857 # Get even size rings an array of references to arrays containing ring atoms...
     858 #
     859 sub GetRingsWithEvenSize {
     860   my($This) = @_;
     861
     862   # Is this bond in a molecule?
     863   if (!$This->HasProperty('Molecule')) {
     864     return undef;
     865   }
     866   my($Molecule);
     867   $Molecule = $This->GetProperty('Molecule');
     868
     869   return $Molecule->_GetBondRingsWithEvenSize($This);
     870 }
     871
     872 # Get rings with specified size  an array of references to arrays containing ring atoms...
     873 #
     874 sub GetRingsWithSize {
     875   my($This, $RingSize) = @_;
     876
     877   # Is this bond in a molecule?
     878   if (!$This->HasProperty('Molecule')) {
     879     return undef;
     880   }
     881   my($Molecule);
     882   $Molecule = $This->GetProperty('Molecule');
     883
     884   return $Molecule->_GetBondRingsWithSize($This, $RingSize);
     885 }
     886
     887 # Get rings with size less than specfied size as an array of references to arrays containing ring atoms...
     888 #
     889 sub GetRingsWithSizeLessThan {
     890   my($This, $RingSize) = @_;
     891
     892   # Is this bond in a molecule?
     893   if (!$This->HasProperty('Molecule')) {
     894     return undef;
     895   }
     896   my($Molecule);
     897   $Molecule = $This->GetProperty('Molecule');
     898
     899   return $Molecule->_GetBondRingsWithSizeLessThan($This, $RingSize);
     900 }
     901
     902 # Get rings with size greater than specfied size as an array of references to arrays containing ring atoms...
     903 #
     904 sub GetRingsWithSizeGreaterThan {
     905   my($This, $RingSize) = @_;
     906
     907   # Is this bond in a molecule?
     908   if (!$This->HasProperty('Molecule')) {
     909     return undef;
     910   }
     911   my($Molecule);
     912   $Molecule = $This->GetProperty('Molecule');
     913
     914   return $Molecule->_GetBondRingsWithSizeGreaterThan($This, $RingSize);
     915 }
     916
     917 # Get next object ID...
     918 sub _GetNewObjectID {
     919   $ObjectID++;
     920   return $ObjectID;
     921 }
     922
     923 # Return a string containing bond and other properties...
     924 sub StringifyBond {
     925   my($This) = @_;
     926   my($BondString, $ID, $BondOrder, $BondType, $Stereochemistry, $AtomsString, $RingBond, $AromaticBond, $NumOfRings, $Atom1, $Atom2);
     927
     928   $ID = $This->GetID();
     929   $BondOrder = $This->GetBondOrder();
     930   if (!defined $BondOrder) {
     931     $BondOrder = "undefined";
     932   }
     933   $BondType = $This->GetBondType();
     934   if (!defined $BondOrder) {
     935     $BondType = "undefined";
     936   }
     937   if (defined($BondOrder) && $BondOrder == 2) {
     938     $Stereochemistry = $This->GetStereochemistry();
     939     if (!defined $Stereochemistry) {
     940       $Stereochemistry = "undefined";
     941     }
     942   }
     943   $RingBond = $This->IsInRing();
     944   if (defined $RingBond) {
     945     $RingBond = $RingBond  ? 'Yes' : 'No';
     946     $NumOfRings = $This->GetNumOfRings();
     947   }
     948   else {
     949     $RingBond = 'undefined';
     950     $NumOfRings = 'undefined';
     951   }
     952
     953   $AromaticBond = $This->GetAromatic();
     954   if (defined $AromaticBond) {
     955     $AromaticBond = $AromaticBond  ? 'Yes' : 'No';
     956   }
     957   else {
     958     $AromaticBond = 'undefined';
     959   }
     960
     961   ($Atom1, $Atom2) = $This->GetAtoms();
     962   $AtomsString = "Atoms: undefined";
     963   if (defined($Atom1) && defined($Atom2)) {
     964     my($Atom, $AtomID, $AtomCount, $AtomName, $AtomSymbol, $AtomicNumber, @BondAtoms);
     965     @BondAtoms = ();
     966     push @BondAtoms, ($Atom1, $Atom2);
     967     $AtomCount = 0;
     968     $AtomsString = "";
     969     for $Atom (@BondAtoms) {
     970       $AtomCount++;
     971       $AtomID = $Atom->GetID();
     972       $AtomName = $Atom->GetName();
     973       $AtomSymbol = $Atom->GetAtomSymbol();
     974       $AtomicNumber = $Atom->GetAtomicNumber();
     975       if ($AtomCount == 1) {
     976         $AtomsString .= "FirstAtom:";
     977       }
     978       else {
     979         $AtomsString .= "; SecondAtom:";
     980       }
     981       $AtomsString .= " [ ID: $AtomID; Name: \"$AtomName\"; AtomSymbol: \"$AtomSymbol\"; AtomicNumber: $AtomicNumber ]";
     982     }
     983   }
     984   $BondString = "Bond: ID: $ID; BondOrder: $BondOrder; BondType: $BondType; RingBond: $RingBond; NumOfBondRings: $NumOfRings; AromaticBond: $AromaticBond;";
     985   if (defined($BondOrder) && $BondOrder == 2) {
     986   $BondString .= " Stereochemistry: $Stereochemistry;";
     987   }
     988   $BondString .= " $AtomsString";
     989
     990   return $BondString;
     991 }
     992