MayaChemTools

   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