/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2010-2012 University of Bonn. All rights reserved. * * * This file is part of MoleCuilder. * * MoleCuilder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * MoleCuilder is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MoleCuilder. If not, see . */ /* * Formula.cpp * * Created on: Jul 21, 2010 * Author: crueger */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "CodePatterns/MemDebug.hpp" #include "Formula.hpp" #include #include "World.hpp" #include "Element/periodentafel.hpp" #include "Element/element.hpp" #include "CodePatterns/Assert.hpp" #include "CodePatterns/Range.hpp" using namespace std; Formula::Formula() : numElements(0) {} Formula::Formula(const Formula &src) : elementCounts(src.elementCounts), numElements(src.numElements) {} Formula::Formula(const string &formula) : numElements(0) { fromString(formula); } Formula::~Formula() {} Formula &Formula::operator=(const Formula &rhs){ // No self-assignment check needed elementCounts=rhs.elementCounts; numElements=rhs.numElements; return *this; } std::string Formula::toString() const{ stringstream sstr; for(const_iterator iter=end();iter!=begin();){ --iter; sstr << (*iter).first->getSymbol(); if((*iter).second>1) sstr << (*iter).second; } return sstr.str(); } void Formula::fromString(const std::string &formula) throw(FormulaStringParseException){ // make this transactional, in case an error is thrown Formula res; string::const_iterator begin = formula.begin(); string::const_iterator end = formula.end(); res.parseFromString(begin,end,static_cast(0)); (*this)=res; } int Formula::parseMaybeNumber(std::string::const_iterator &it,string::const_iterator &end) throw(FormulaStringParseException){ static const range Numbers = makeRange('0',static_cast('9'+1)); int count = 0; while(it!=end && Numbers.isInRange(*it)) count = (count*10) + ((*it++)-Numbers.first); // one is implicit count = (count!=0)?count:1; return count; } void Formula::parseFromString(std::string::const_iterator &it,string::const_iterator &end,char delimiter) throw(FormulaStringParseException){ // some constants needed for parsing... Assumes ASCII, change if other encodings are used static const range CapitalLetters = makeRange('A',static_cast('Z'+1)); static const range SmallLetters = makeRange('a',static_cast('z'+1)); map delimiters; delimiters['('] = ')'; delimiters['['] = ']'; // clean the formula clear(); for(/*send from above*/;it!=end && *it!=delimiter;/*updated in loop*/){ // we might have a sub formula if(delimiters.count(*it)){ Formula sub; char nextdelim=delimiters[*it]; sub.parseFromString(++it,end,nextdelim); if(!sub.getElementCount()){ throw FormulaStringParseException() << FormulaString( string(it, end) ); } int count = parseMaybeNumber(++it,end); addFormula(sub,count); continue; } string shorthand; // Atom names start with a capital letter if(!CapitalLetters.isInRange(*it)) throw FormulaStringParseException() << FormulaString( string(it, end) ); shorthand+=(*it++); // the rest of the name follows while(it!=end && SmallLetters.isInRange(*it)) shorthand+=(*it++); int count = parseMaybeNumber(it,end); // test if the shorthand exists if(!World::getInstance().getPeriode()->FindElement(shorthand)) throw FormulaStringParseException() << FormulaString( string(it, end) ); // done, we can get the next one addElements(shorthand,count); } if(it==end && delimiter!=0){ throw FormulaStringParseException() << FormulaString( string(it, end) ); } } unsigned int Formula::getElementCount() const{ return numElements; } bool Formula::hasElement(const element *element) const{ ASSERT(element,"Invalid pointer in Formula::hasElement(element*)"); return hasElement(element->getAtomicNumber()); } bool Formula::hasElement(atomicNumber_t Z) const{ ASSERT(Z>0,"Invalid atomic Number"); ASSERT(World::getInstance().getPeriode()->FindElement(Z),"No Element with this number in Periodentafel"); return elementCounts.size()>=Z && elementCounts[Z-1]; } bool Formula::hasElement(const string &shorthand) const{ const element * element = World::getInstance().getPeriode()->FindElement(shorthand); return hasElement(element); } void Formula::operator+=(const element *element){ ASSERT(element,"Invalid pointer in increment of Formula"); operator+=(element->getAtomicNumber()); } void Formula::operator+=(atomicNumber_t Z){ ASSERT(Z>0,"Invalid atomic Number"); ASSERT(World::getInstance().getPeriode()->FindElement(Z),"No Element with this number in Periodentafel"); elementCounts.resize(max(Z,elementCounts.size()),0); // No-op when we already have the right size // might need to update number of elements if(!elementCounts[Z-1]){ numElements++; } elementCounts[Z-1]++; // atomic numbers start at 1 } void Formula::operator+=(const string &shorthand){ const element * element = World::getInstance().getPeriode()->FindElement(shorthand); operator+=(element); } void Formula::operator-=(const element *element){ ASSERT(element,"Invalid pointer in decrement of Formula"); operator-=(element->getAtomicNumber()); } void Formula::operator-=(atomicNumber_t Z){ ASSERT(Z>0,"Invalid atomic Number"); ASSERT(World::getInstance().getPeriode()->FindElement(Z),"No Element with this number in Periodentafel"); ASSERT(elementCounts.size()>=Z && elementCounts[Z-1], "Element not in Formula upon decrement"); elementCounts[Z-1]--; // atomic numbers start at 1 // might need to update number of elements if(!elementCounts[Z-1]){ numElements--; // resize the Array if this was at the last position if(Z==elementCounts.size()){ // find the first element from the back that is not equal to zero set_t::reverse_iterator riter = find_if(elementCounts.rbegin(), elementCounts.rend(), bind1st(not_equal_to(),0)); // see how many elements are in this range set_t::reverse_iterator::difference_type diff = riter - elementCounts.rbegin(); elementCounts.resize(elementCounts.size()-diff); } } } void Formula::operator-=(const string &shorthand){ const element * element = World::getInstance().getPeriode()->FindElement(shorthand); operator-=(element); } void Formula::addElements(const element *element,unsigned int count){ ASSERT(element,"Invalid pointer in Formula::addElements(element*)"); addElements(element->getAtomicNumber(),count); } void Formula::addElements(atomicNumber_t Z,unsigned int count){ if(count==0) return; ASSERT(Z>0,"Invalid atomic Number"); ASSERT(World::getInstance().getPeriode()->FindElement(Z),"No Element with this number in Periodentafel"); elementCounts.resize(max(Z,elementCounts.size()),0); // No-op when we already have the right size // might need to update number of elements if(!elementCounts[Z-1]){ numElements++; } elementCounts[Z-1]+=count; } void Formula::addElements(const string &shorthand,unsigned int count){ const element * element = World::getInstance().getPeriode()->FindElement(shorthand); addElements(element,count); } void Formula::addFormula(const Formula &formula,unsigned int n){ for(Formula::const_iterator iter=formula.begin();iter!=formula.end();++iter){ this->addElements(iter->first,iter->second*n); } } enumeration Formula::enumerateElements() const{ enumeration res(1); for(Formula::const_iterator iter=begin();iter!=end();++iter){ res.add(iter->first); } return res; } const unsigned int Formula::operator[](const element *element) const{ ASSERT(element,"Invalid pointer in access of Formula"); return operator[](element->getAtomicNumber()); } const unsigned int Formula::operator[](atomicNumber_t Z) const{ ASSERT(Z>0,"Invalid atomic Number"); ASSERT(World::getInstance().getPeriode()->FindElement(Z),"No Element with this number in Periodentafel"); if(elementCounts.size()FindElement(shorthand); return operator[](element); } bool Formula::operator==(const Formula &rhs) const{ // quick check... number of elements used if(numElements != rhs.numElements){ return false; } // second quick check, size of vectors (== last element in formula) if(elementCounts.size()!=rhs.elementCounts.size()){ return false; } // slow check: all elements // direct access to internal structure means all element-counts have to be compared // this avoids access to periodentafel to find elements though and is probably faster // in total return equal(elementCounts.begin(), elementCounts.end(), rhs.elementCounts.begin()); } bool Formula::operator!=(const Formula &rhs) const{ return !operator==(rhs); } Formula::iterator Formula::begin(){ return iterator(elementCounts,0); } Formula::const_iterator Formula::begin() const{ // this is the only place where this is needed, so this is better than making it mutable return const_iterator(const_cast(elementCounts),0); } Formula::iterator Formula::end(){ return iterator(elementCounts); } Formula::const_iterator Formula::end() const{ // this is the only place where this is needed, so this is better than making it mutable return const_iterator(const_cast(elementCounts)); } void Formula::clear(){ elementCounts.clear(); numElements = 0; } /**************** Iterator structure ********************/ template Formula::_iterator::_iterator(set_t &_set) : set(&_set) { pos=set->size(); } template Formula::_iterator::_iterator(set_t &_set,size_t _pos) : set(&_set),pos(_pos) { ASSERT(pos<=set->size(),"invalid position in iterator construction"); while(possize() && (*set)[pos]==0) ++pos; } template Formula::_iterator::_iterator(const _iterator &rhs) : set(rhs.set),pos(rhs.pos) {} template Formula::_iterator::~_iterator(){} template Formula::_iterator& Formula::_iterator::operator=(const _iterator &rhs){ set=rhs.set; pos=rhs.pos; return *this; } template bool Formula::_iterator::operator==(const _iterator &rhs){ return set==rhs.set && pos==rhs.pos; } template bool Formula::_iterator::operator!=(const _iterator &rhs){ return !operator==(rhs); } template Formula::_iterator Formula::_iterator::operator++(){ ASSERT(pos!=set->size(),"Incrementing Formula::iterator beyond end"); pos++; while(possize() && (*set)[pos]==0) ++pos; return *this; } template Formula::_iterator Formula::_iterator::operator++(int){ Formula::_iterator retval = *this; ++(*this); return retval; } template Formula::_iterator Formula::_iterator::operator--(){ ASSERT(pos!=0,"Decrementing Formula::iterator beyond begin"); pos--; while(pos>0 && (*set)[pos]==0) --pos; return *this; } template Formula::_iterator Formula::_iterator::operator--(int){ Formula::_iterator retval = *this; --(*this); return retval; } template result_type Formula::_iterator::operator*(){ const element *element = World::getInstance().getPeriode()->FindElement(pos+1); ASSERT(element,"Element with position of iterator not found"); return make_pair(element,(*set)[pos]); } template result_type* Formula::_iterator::operator->(){ // no one can keep this value around, so a static is ok to avoid temporaries static value_type value=make_pair(reinterpret_cast(0),0); // no default constructor for std::pair const element *element = World::getInstance().getPeriode()->FindElement(pos+1); ASSERT(element,"Element with position of iterator not found"); value = make_pair(element,(*set)[pos]); return &value; } // explicit instantiation of all iterator template methods // this is quite ugly, but there is no better way unless we expose iterator implementation // instantiate Formula::iterator template Formula::iterator::_iterator(set_t&); template Formula::iterator::_iterator(set_t&,size_t); template Formula::iterator::_iterator(const Formula::iterator&); template Formula::iterator::~_iterator(); template Formula::iterator &Formula::iterator::operator=(const Formula::iterator&); template bool Formula::iterator::operator==(const Formula::iterator&); template bool Formula::iterator::operator!=(const Formula::iterator&); template Formula::iterator Formula::iterator::operator++(); template Formula::iterator Formula::iterator::operator++(int); template Formula::iterator Formula::iterator::operator--(); template Formula::iterator Formula::iterator::operator--(int); template Formula::value_type Formula::iterator::operator*(); template Formula::value_type *Formula::iterator::operator->(); // instantiate Formula::const_iterator template Formula::const_iterator::_iterator(set_t&); template Formula::const_iterator::_iterator(set_t&,size_t); template Formula::const_iterator::_iterator(const Formula::const_iterator&); template Formula::const_iterator::~_iterator(); template Formula::const_iterator &Formula::const_iterator::operator=(const Formula::const_iterator&); template bool Formula::const_iterator::operator==(const Formula::const_iterator&); template bool Formula::const_iterator::operator!=(const Formula::const_iterator&); template Formula::const_iterator Formula::const_iterator::operator++(); template Formula::Formula::const_iterator Formula::const_iterator::operator++(int); template Formula::Formula::const_iterator Formula::const_iterator::operator--(); template Formula::Formula::const_iterator Formula::const_iterator::operator--(int); template const Formula::value_type Formula::const_iterator::operator*(); template const Formula::value_type *Formula::const_iterator::operator->(); /********************** I/O of Formulas ************************************************/ std::ostream &operator<<(std::ostream &ost,const Formula &formula){ ost << formula.toString(); return ost; }