MayaChemTools

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