molecule.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:21k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: molecule.cpp,v $
  4.  * PRODUCTION Revision 1000.2  2004/06/01 18:28:41  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.43
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: molecule.cpp,v 1000.2 2004/06/01 18:28:41 gouriano Exp $
  10. * ===========================================================================
  11. *
  12. *                            PUBLIC DOMAIN NOTICE
  13. *               National Center for Biotechnology Information
  14. *
  15. *  This software/database is a "United States Government Work" under the
  16. *  terms of the United States Copyright Act.  It was written as part of
  17. *  the author's official duties as a United States Government employee and
  18. *  thus cannot be copyrighted.  This software/database is freely available
  19. *  to the public for use. The National Library of Medicine and the U.S.
  20. *  Government have not placed any restriction on its use or reproduction.
  21. *
  22. *  Although all reasonable efforts have been taken to ensure the accuracy
  23. *  and reliability of the software and data, the NLM and the U.S.
  24. *  Government do not and cannot warrant the performance or results that
  25. *  may be obtained by using this software or data. The NLM and the U.S.
  26. *  Government disclaim all warranties, express or implied, including
  27. *  warranties of performance, merchantability or fitness for any particular
  28. *  purpose.
  29. *
  30. *  Please cite the author in any work or product based on this material.
  31. *
  32. * ===========================================================================
  33. *
  34. * Authors:  Paul Thiessen
  35. *
  36. * File Description:
  37. *      Classes to hold molecules
  38. *
  39. * ===========================================================================
  40. */
  41. #ifdef _MSC_VER
  42. #pragma warning(disable:4018)   // disable signed/unsigned mismatch warning in MSVC
  43. #endif
  44. #include <ncbi_pch.hpp>
  45. #include <corelib/ncbistd.hpp>
  46. #include <objects/mmdb1/Residue_graph.hpp>
  47. #include <objects/mmdb1/Molecule_id.hpp>
  48. #include <objects/seqloc/Seq_id.hpp>
  49. #include <objects/seqloc/PDB_seq_id.hpp>
  50. #include <objects/general/Object_id.hpp>
  51. #include <objects/mmdb1/Residue_id.hpp>
  52. #include <objects/seqloc/PDB_mol_id.hpp>
  53. #include "molecule.hpp"
  54. #include "residue.hpp"
  55. #include "bond.hpp"
  56. #include "style_manager.hpp"
  57. #include "structure_set.hpp"
  58. #include "coord_set.hpp"
  59. #include "atom_set.hpp"
  60. #include "chemical_graph.hpp"
  61. #include "molecule_identifier.hpp"
  62. #include "show_hide_manager.hpp"
  63. #include "cn3d_tools.hpp"
  64. #include "opengl_renderer.hpp"
  65. #include "cn3d_colors.hpp"
  66. USING_NCBI_SCOPE;
  67. USING_SCOPE(objects);
  68. BEGIN_SCOPE(Cn3D)
  69. const int Molecule::NO_DOMAIN_SET = -1;
  70. Molecule::Molecule(ChemicalGraph *parentGraph,
  71.     const CMolecule_graph& graph,
  72.     const ResidueGraphList& standardDictionary,
  73.     const ResidueGraphList& localDictionary) :
  74.     StructureBase(parentGraph), type(eOther), sequence(NULL), nDomains(0)
  75. {
  76.     int gi = MoleculeIdentifier::VALUE_NOT_SET, pdbChain = MoleculeIdentifier::VALUE_NOT_SET;
  77.     string pdbID, accession;
  78.     // get ID, name, and type
  79.     id = graph.GetId().Get();
  80.     CMolecule_graph::TDescr::const_iterator k, ke=graph.GetDescr().end();
  81.     for (k=graph.GetDescr().begin(); k!=ke; ++k) {
  82.         if (k->GetObject().IsMolecule_type()) {
  83.             type = static_cast<eType>(k->GetObject().GetMolecule_type());
  84.         }
  85.         if (k->GetObject().IsName()) {
  86.             name = k->GetObject().GetName();
  87.         }
  88.     }
  89.     // get Seq-id for biopolymer chains (for sequence alignment stuff)
  90.     if (IsProtein() || IsNucleotide()) {
  91.         if (graph.IsSetSeq_id()) {
  92.             if (graph.GetSeq_id().IsGi())
  93.                 gi = graph.GetSeq_id().GetGi();
  94.             else if (graph.GetSeq_id().IsPdb()) {
  95.                 pdbID = graph.GetSeq_id().GetPdb().GetMol().Get();
  96.                 if (graph.GetSeq_id().GetPdb().IsSetChain())
  97.                     pdbChain = graph.GetSeq_id().GetPdb().GetChain();
  98.                 else
  99.                     pdbChain = ' ';
  100.             }
  101.             else if (graph.GetSeq_id().IsLocal() && graph.GetSeq_id().GetLocal().IsStr()) {
  102.                 accession = graph.GetSeq_id().GetLocal().GetStr();
  103.                 // special case where local accession is actually a PDB chain + extra stuff
  104.                 if (pdbID.size() == 0 && accession.size() >= 7 &&
  105.                         accession[4] == ' ' && accession[6] == ' ' && isalpha(accession[5])) {
  106.                     pdbID = accession.substr(0, 4);
  107.                     pdbChain = accession[5];
  108.                     accession.erase();
  109.                 }
  110.             }
  111.         }
  112.         if (gi == MoleculeIdentifier::VALUE_NOT_SET && pdbID.size() == 0 && accession.size() == 0) {
  113.             ERRORMSG("Molecule::Molecule() - biopolymer molecule, but can't get Seq-id");
  114.             return;
  115.         }
  116.     }
  117.     // if no PDB id assigned to biopolymer, assign default PDB identifier from parent, assuming 'name'
  118.     // is actually the chainID if this is a biopolymer
  119.     const StructureObject *object;
  120.     if (!GetParentOfType(&object)) return;
  121.     if ((IsProtein() || IsNucleotide()) && pdbID.size() == 0) {
  122.         pdbID = object->pdbID;
  123.         if (name.size() == 1) pdbChain = name[0];
  124.     }
  125.     // load residues from SEQUENCE OF Residue, storing virtual bonds along the way
  126.     CMolecule_graph::TInter_residue_bonds::const_iterator j, je, jOrig;
  127.     if (graph.IsSetInter_residue_bonds()) {
  128.         j = graph.GetInter_residue_bonds().begin();
  129.         je = graph.GetInter_residue_bonds().end();
  130.     }
  131.     const Residue *prevResidue = NULL;
  132.     const Bond *prevBond = NULL;
  133.     CMolecule_graph::TResidue_sequence::const_iterator i, ie=graph.GetResidue_sequence().end();
  134.     int nResidues = 0;
  135.     for (i=graph.GetResidue_sequence().begin(); i!=ie; ++i) {
  136.         const Residue *residue = new Residue(this, (*i).GetObject(), id,
  137.             standardDictionary, localDictionary);
  138.         residues[residue->id] = residue;
  139.         ++nResidues;
  140.         // this assumption is frequently made elsewhere, relating seqLocs (numbering from zero)
  141.         // to residue ID's (numbering from one) - so enforce it here
  142.         if (residue->id != nResidues)
  143.             ERRORMSG("Residue ID's must be ordered consecutively starting with one");
  144.         // virtual bonds between successive alphas
  145.         if (prevResidue && prevResidue->alphaID != Residue::NO_ALPHA_ID &&
  146.             residue->alphaID != Residue::NO_ALPHA_ID) {
  147.             bool foundReal = false;
  148.             // see if there's a "real" inter-residue bond between these in the chemical graph
  149.             if (graph.IsSetInter_residue_bonds()) {
  150.                 jOrig = j;
  151.                 do {
  152.                     if ((j->GetObject().GetAtom_id_1().GetResidue_id().Get() == prevResidue->id &&
  153.                          j->GetObject().GetAtom_id_2().GetResidue_id().Get() == residue->id) ||
  154.                         (j->GetObject().GetAtom_id_2().GetResidue_id().Get() == prevResidue->id &&
  155.                          j->GetObject().GetAtom_id_1().GetResidue_id().Get() == residue->id))
  156.                         foundReal = true;
  157.                     if (++j == je) j = graph.GetInter_residue_bonds().begin();
  158.                 } while (!foundReal && j != jOrig);
  159.             }
  160.             // for alpha only models, there are no inter-residue bonds in the
  161.             // chemical graph, so check inter-atomic distances for cases where either one
  162.             // of the two residues is alpha only.
  163.             if (!foundReal && (IsProtein() || IsNucleotide()) &&
  164.                 (residue->NAtomsWithAnyCoords() == 1 || prevResidue->NAtomsWithAnyCoords() == 1)) {
  165.                 // get atom coordinates
  166.                 AtomPntr ap1(id, residue->id, residue->alphaID);
  167.                 const AtomCoord* atom1 = object->coordSets.front()->atomSet->GetAtom(ap1, true, true);
  168.                 AtomPntr ap2(id, prevResidue->id, prevResidue->alphaID);
  169.                 const AtomCoord* atom2 = object->coordSets.front()->atomSet->GetAtom(ap2, true, true);
  170.                 // check distance - ok if <= 5.0 Angstroms (or 10.0 for nucleotides)
  171.                 if (atom1 && atom2 &&
  172.                         (atom1->site - atom2->site).length() <=
  173.                             (IsProtein() ? 5.0 : 10.0))
  174.                     foundReal = true;
  175.             }
  176.             if (foundReal) {
  177.                 const Bond *bond = MakeBond(this,
  178.                     id, prevResidue->id, prevResidue->alphaID,
  179.                     id, residue->id, residue->alphaID,
  180.                     Bond::eVirtual);
  181.                 if (bond) {
  182.                     interResidueBonds.push_back(bond);
  183.                     if (prevBond) {
  184.                         (const_cast<Bond *>(prevBond))->nextVirtual = bond;
  185.                         (const_cast<Bond *>(bond))->previousVirtual = prevBond;
  186.                     }
  187.                 }
  188.                 prevBond = bond;
  189.             } else
  190.                 prevBond = NULL;
  191.         } else
  192.             prevBond = NULL;
  193.         prevResidue = residue;
  194.     }
  195.     residueDomains.resize(residues.size(), NO_DOMAIN_SET);
  196.     // keep s.s. maps only for protein chains
  197.     if (IsProtein()) residueSecondaryStructures.resize(residues.size(), eCoil);
  198.     // load inter-residue bonds from SEQUENCE OF Inter-residue-bond OPTIONAL
  199.     if (graph.IsSetInter_residue_bonds()) {
  200.         je = graph.GetInter_residue_bonds().end();
  201.         for (j=graph.GetInter_residue_bonds().begin(); j!=je; ++j) {
  202.             int order = j->GetObject().IsSetBond_order() ?
  203.                 j->GetObject().GetBond_order() : Bond::eUnknown;
  204.             const Bond *bond = MakeBond(this,
  205.                 j->GetObject().GetAtom_id_1(),
  206.                 j->GetObject().GetAtom_id_2(),
  207.                 order);
  208.             if (bond) interResidueBonds.push_back(bond);
  209.             // add to disulfide map if virtual disulfide added or this bond is flagged as disulfide
  210.             if (parentGraph->CheckForDisulfide(this,
  211.                     j->GetObject().GetAtom_id_1(), j->GetObject().GetAtom_id_2(),
  212.                     &interResidueBonds, const_cast<Bond*>(bond), this) ||
  213.                 (bond && bond->order == Bond::eRealDisulfide)) {
  214.                 disulfideMap[j->GetObject().GetAtom_id_1().GetResidue_id().Get()] =
  215.                     j->GetObject().GetAtom_id_2().GetResidue_id().Get();
  216.                 disulfideMap[j->GetObject().GetAtom_id_2().GetResidue_id().Get()] =
  217.                     j->GetObject().GetAtom_id_1().GetResidue_id().Get();
  218.             }
  219.         }
  220.     }
  221.     // get identifier
  222.     identifier = MoleculeIdentifier::GetIdentifier(this, pdbID, pdbChain, gi, accession);
  223. }
  224. Vector Molecule::GetResidueColor(int sequenceIndex) const
  225. {
  226.     static const Vector gray(.5,.5,.5);
  227.     // this assumes that the "index" - the position of the residue in the sequence,
  228.     // starting from zero, is always one less than the residueID from the ASN1
  229.     // data, which starts from one.
  230.     ResidueMap::const_iterator r = residues.find(sequenceIndex + 1);
  231.     if (r == residues.end()) return gray;
  232.     const Residue *residue = r->second;
  233.     // if no known alpha atom, just use gray
  234.     if (residue->alphaID == Residue::NO_ALPHA_ID) return gray;
  235.     AtomPntr atom(id, residue->id, residue->alphaID);
  236.     // just use the first AtomSet (e.g. first model) of this object
  237.     const StructureObject *object;
  238.     if (!GetParentOfType(&object)) return gray;
  239.     const AtomCoord *atomCoord = object->coordSets.front()->atomSet->
  240.         GetAtom(atom, parentSet->showHideManager->OverlayConfEnsembles(), true);
  241.     if (!atomCoord) return gray;
  242.     AtomStyle style;
  243.     if (!parentSet->styleManager->GetAtomStyle(residue, atom, atomCoord, &style)) return gray;
  244.     return style.color;
  245. }
  246. int Molecule::GetAlphaCoords(int nResidues, const int *seqIndexes, const Vector * *coords) const
  247. {
  248.     const StructureObject *object;
  249.     if (!GetParentOfType(&object)) return false;
  250.     if (object->coordSets.size() != 1) {
  251.         ERRORMSG("Can't align structures with multiple CoordSets");
  252.         return -1;
  253.     }
  254.     int nCoords = 0;
  255.     for (int i=0; i<nResidues; ++i) {
  256.         int rID = seqIndexes[i] + 1;    // residueIDs start at 1
  257.         ResidueMap::const_iterator r = residues.find(rID);
  258.         if (r == residues.end()) {
  259.             ERRORMSG("Can't find residueID " << rID
  260.                 << " in " << identifier->pdbID << " chain '"
  261.                 << (char) identifier->pdbChain << "'");
  262.             return -1;
  263.         }
  264.         int aID = (r->second->alphaID);
  265.         if (aID == Residue::NO_ALPHA_ID) {
  266.             WARNINGMSG("No alpha atom in residueID " << rID
  267.                 << " from " << identifier->pdbID << " chain '"
  268.                 << (char) identifier->pdbChain << "'");
  269.             coords[i] = NULL;
  270.             continue;
  271.         }
  272.         AtomPntr atom(id, rID, aID);
  273.         const AtomCoord* atomCoord = object->coordSets.front()->atomSet->GetAtom(atom);
  274.         if (!atomCoord) {
  275.             WARNINGMSG("Can't get AtomCoord for (m"
  276.                 << id << ",r" << rID << ",a" << aID << ")");
  277.             coords[i] = NULL;
  278.             continue;
  279.         }
  280.         coords[i] = &(atomCoord->site);
  281.         ++nCoords;
  282.     }
  283.     return nCoords;
  284. }
  285. bool Molecule::DrawAllWithTerminiLabels(const AtomSet *atomSet) const
  286. {
  287.     // draw regular objects
  288.     if (!DrawAll(atomSet)) return false;
  289.     // add termini labels
  290.     if ((IsProtein() || IsNucleotide()) && NResidues() >= 2 && parentSet->showHideManager->IsVisible(this)) {
  291.         const StyleSettings& settings = parentSet->styleManager->GetGlobalStyle();
  292.         if ((IsProtein() && settings.proteinLabels.terminiOn) ||
  293.             (IsNucleotide() && settings.nucleotideLabels.terminiOn)) {
  294.             // try to color labels to contrast with background
  295.             static const Vector white(1,1,1), black(0,0,0);
  296.             const Vector& labelColor =
  297.                 Colors::IsLightColor(settings.backgroundColor) ? black : white;
  298.             // do start (N or 5') and end (C or 3') labels
  299.             for (int startTerminus=1; startTerminus>=0; --startTerminus) {
  300.                 // determine color and location - assumes sequential residue id's (from 1)
  301.                 const Vector *alphaPos = NULL, *prevPos = NULL;
  302.                 int res = startTerminus ? 1 : residues.size(),
  303.                     resEnd = startTerminus ? residues.size() : 1,
  304.                     resInc = startTerminus ? 1 : -1;
  305.                 // find coordinates of two terminal alpha atoms
  306.                 const Residue *termRes = NULL;
  307.                 for (; res!=resEnd; res+=resInc) {
  308.                     const Residue *residue = residues.find(res)->second;
  309.                     if (residue->alphaID != Residue::NO_ALPHA_ID) {
  310.                         AtomPntr ap(id, res, residue->alphaID);
  311.                         const AtomCoord *atom =
  312.                             atomSet->GetAtom(ap, parentSet->showHideManager->OverlayConfEnsembles(), true);
  313.                         if (atom) {
  314.                             if (!alphaPos) {
  315.                                 alphaPos = &(atom->site);
  316.                                 termRes = residue;
  317.                             } else if (!prevPos) {
  318.                                 prevPos = &(atom->site);
  319.                                 break;
  320.                             }
  321.                         }
  322.                     }
  323.                 }
  324.                 if (!(alphaPos && prevPos)) {
  325.                     WARNINGMSG("Molecule::DrawAllWithTerminiLabels() - "
  326.                         << "can't get two terminal alpha coords");
  327.                     continue;
  328.                 }
  329.                 if (!parentSet->showHideManager->IsVisible(termRes)) continue;
  330.                 Vector labelPosition = *alphaPos + 0.5 * (*alphaPos - *prevPos);
  331.                 // determine label text
  332.                 CNcbiOstrstream oss;
  333.                 if (IsProtein()) {
  334.                     if (startTerminus)
  335.                         oss << "N";
  336.                     else
  337.                         oss << "C";
  338.                 } else {
  339.                     if (startTerminus)
  340.                         oss << "5'";
  341.                     else
  342.                         oss << "3'";
  343.                 }
  344.                 if (identifier->pdbChain != MoleculeIdentifier::VALUE_NOT_SET && identifier->pdbChain != ' ')
  345.                     oss << " (" << (char) identifier->pdbChain << ')';
  346.                 oss << '';
  347.                 // draw label
  348.                 string labelText = oss.str();
  349.                 delete oss.str();
  350.                 parentSet->renderer->DrawLabel(labelText, labelPosition, labelColor);
  351.             }
  352.         }
  353.     }
  354.     return true;
  355. }
  356. END_SCOPE(Cn3D)
  357. /*
  358. * ---------------------------------------------------------------------------
  359. * $Log: molecule.cpp,v $
  360. * Revision 1000.2  2004/06/01 18:28:41  gouriano
  361. * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.43
  362. *
  363. * Revision 1.43  2004/05/21 21:41:39  gorelenk
  364. * Added PCH ncbi_pch.hpp
  365. *
  366. * Revision 1.42  2004/05/20 18:49:21  thiessen
  367. * don't do structure realignment if < 3 coords present
  368. *
  369. * Revision 1.41  2004/03/15 17:34:03  thiessen
  370. * prefer prefix vs. postfix ++/-- operators
  371. *
  372. * Revision 1.40  2004/02/19 17:04:57  thiessen
  373. * remove cn3d/ from include paths; add pragma to disable annoying msvc warning
  374. *
  375. * Revision 1.39  2003/06/21 08:18:58  thiessen
  376. * show all atoms with coordinates, even if not in all coord sets
  377. *
  378. * Revision 1.38  2003/02/03 19:20:04  thiessen
  379. * format changes: move CVS Log to bottom of file, remove std:: from .cpp files, and use new diagnostic macros
  380. *
  381. * Revision 1.37  2002/04/19 17:34:03  thiessen
  382. * fix for alpha-only nucleotides
  383. *
  384. * Revision 1.36  2002/02/01 13:55:31  thiessen
  385. * fix labeling bug when domain hidden
  386. *
  387. * Revision 1.35  2002/02/01 00:41:21  thiessen
  388. * tweaks
  389. *
  390. * Revision 1.34  2002/01/24 20:08:16  thiessen
  391. * fix local id problem
  392. *
  393. * Revision 1.33  2001/12/12 14:04:13  thiessen
  394. * add missing object headers after object loader change
  395. *
  396. * Revision 1.32  2001/10/16 21:49:07  thiessen
  397. * restructure MultiTextDialog; allow virtual bonds for alpha-only PDB's
  398. *
  399. * Revision 1.31  2001/08/24 00:41:35  thiessen
  400. * tweak conservation colors and opengl font handling
  401. *
  402. * Revision 1.30  2001/08/21 01:10:45  thiessen
  403. * add labeling
  404. *
  405. * Revision 1.29  2001/08/09 19:07:13  thiessen
  406. * add temperature and hydrophobicity coloring
  407. *
  408. * Revision 1.28  2001/07/16 15:35:37  thiessen
  409. * fix unaligned chain identifier ommission
  410. *
  411. * Revision 1.27  2001/06/21 02:02:33  thiessen
  412. * major update to molecule identification and highlighting ; add toggle highlight (via alt)
  413. *
  414. * Revision 1.26  2001/04/04 00:27:14  thiessen
  415. * major update - add merging, threader GUI controls
  416. *
  417. * Revision 1.25  2001/03/23 23:31:56  thiessen
  418. * keep atom info around even if coords not all present; mainly for disulfide parsing in virtual models
  419. *
  420. * Revision 1.24  2001/03/23 04:18:52  thiessen
  421. * parse and display disulfides
  422. *
  423. * Revision 1.23  2001/02/09 20:17:32  thiessen
  424. * ignore atoms w/o alpha when doing structure realignment
  425. *
  426. * Revision 1.22  2001/02/08 23:01:50  thiessen
  427. * hook up C-toolkit stuff for threading; working PSSM calculation
  428. *
  429. * Revision 1.21  2000/12/15 15:51:47  thiessen
  430. * show/hide system installed
  431. *
  432. * Revision 1.20  2000/12/01 19:35:57  thiessen
  433. * better domain assignment; basic show/hide mechanism
  434. *
  435. * Revision 1.19  2000/11/30 15:49:39  thiessen
  436. * add show/hide rows; unpack sec. struc. and domain features
  437. *
  438. * Revision 1.18  2000/11/13 18:06:53  thiessen
  439. * working structure re-superpositioning
  440. *
  441. * Revision 1.17  2000/11/11 21:15:54  thiessen
  442. * create Seq-annot from BlockMultipleAlignment
  443. *
  444. * Revision 1.16  2000/09/15 19:24:22  thiessen
  445. * allow repeated structures w/o different local id
  446. *
  447. * Revision 1.15  2000/09/11 22:57:32  thiessen
  448. * working highlighting
  449. *
  450. * Revision 1.14  2000/09/08 20:16:55  thiessen
  451. * working dynamic alignment views
  452. *
  453. * Revision 1.13  2000/09/03 18:46:48  thiessen
  454. * working generalized sequence viewer
  455. *
  456. * Revision 1.12  2000/08/28 23:47:18  thiessen
  457. * functional denseg and dendiag alignment parsing
  458. *
  459. * Revision 1.11  2000/08/28 18:52:42  thiessen
  460. * start unpacking alignments
  461. *
  462. * Revision 1.10  2000/08/27 18:52:21  thiessen
  463. * extract sequence information
  464. *
  465. * Revision 1.9  2000/08/17 14:24:05  thiessen
  466. * added working StyleManager
  467. *
  468. * Revision 1.8  2000/08/11 12:58:31  thiessen
  469. * added worm; get 3d-object coords from asn1
  470. *
  471. * Revision 1.7  2000/08/04 22:49:03  thiessen
  472. * add backbone atom classification and selection feedback mechanism
  473. *
  474. * Revision 1.6  2000/08/03 15:12:23  thiessen
  475. * add skeleton of style and show/hide managers
  476. *
  477. * Revision 1.5  2000/07/27 13:30:51  thiessen
  478. * remove 'using namespace ...' from all headers
  479. *
  480. * Revision 1.4  2000/07/18 02:41:33  thiessen
  481. * fix bug in virtual bonds and altConfs
  482. *
  483. * Revision 1.3  2000/07/17 04:20:49  thiessen
  484. * now does correct structure alignment transformation
  485. *
  486. * Revision 1.2  2000/07/16 23:19:11  thiessen
  487. * redo of drawing system
  488. *
  489. * Revision 1.1  2000/07/11 13:45:30  thiessen
  490. * add modules to parse chemical graph; many improvements
  491. *
  492. */