// RcppExample.cpp: Part of the R/C++ interface class library, Version 4.2 // // Copyright (C) 2005-2006 Dominick Samperi // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or (at // your option) any later version. // // This library 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 Lesser General Public // License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this library; if not, write to the Free Software Foundation, // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // {{{ Includes, Typedefs, prototypes #include <iostream> #include <iomanip> #include <vector> #include <functional> #include <algorithm> #include <map> #include <sstream> #include <numeric> #include <string> #include <cstring> // // IMS Stuff // #include <ims/alphabet.h> #include <ims/weights.h> #include <ims/isotopedistribution.h> #include <ims/distributionprobabilityscorer.h> #include <ims/composedelement.h> #include <ims/nitrogenrulefilter.h> #include <ims/utils/math.h> #include <ims/base/exception/ioexception.h> #include <ims/decomp/realmassdecomposer.h> #include <ims/decomp/integermassdecomposer.h> #include <ims/decomp/decomputils.h> // // R Stuff // #include "../RcppSrc/Rcpp.hpp" extern "C" { #include <Rdefines.h> #include <Rinternals.h> #include <R_ext/Rdynload.h> } using namespace ims; using namespace std; typedef Alphabet alphabet_t; typedef IsotopeDistribution distribution_t; typedef IntegerMassDecomposer<>::decompositions_type decompositions_t; void initializeCHNOPS(alphabet_t&, const int maxisotopes); void initializeAlphabet(const SEXP l_alphabet, alphabet_t &alphabet, const int maxisotopes); template <typename score_type> SEXP rlistScores(multimap<score_type, ComposedElement, greater<score_type> > scores, int z); // }}} template <typename MassType> SEXP rlistDecompositions(const decompositions_t& decompositions, const alphabet_t& alphabet, const Weights& weights, MassType mass, unsigned int maxNumber); char* exceptionMesg=NULL; float getDBE(const ComposedElement& molecule, int z) { // {{{ return (1 + static_cast<int>(molecule.getElementAbundance("C")) + static_cast<int>(molecule.getElementAbundance("Si")) - 0.5 * (static_cast<int>(molecule.getElementAbundance("H")) +static_cast<int>(molecule.getElementAbundance("F")) +static_cast<int>(molecule.getElementAbundance("Cl")) +static_cast<int>(molecule.getElementAbundance("Br")) +static_cast<int>(molecule.getElementAbundance("I"))) + 0.5 * (static_cast<int>(molecule.getElementAbundance("N")) + static_cast<int>(molecule.getElementAbundance("P")))); }; // }}} char getParity(const ComposedElement& molecule, int charge=0) { // {{{ // return (int)(getDBE(molecule, z) * 2) % 2 == 0 ? 'e' : 'o'; bool masseven = static_cast<int>(molecule.getMass()) % 2 == 0 ? true : false; bool nitrogeneven = static_cast<int>(molecule.getElementAbundance("N")) % 2 == 0 ? true : false; bool chargeeven = abs(charge) % 2 == 0 ? true : false; return (masseven ^ nitrogeneven ^ chargeeven) == true ? 'e' : 'o' ; }; // }}} bool isValidMyNitrogenRule(const ComposedElement& molecule, int z) { // {{{ bool massodd = static_cast<int>(molecule.getNominalMass()) % 2 == 1 ? true : false; bool masseven = !massodd; bool nitrogenodd = static_cast<int>(molecule.getElementAbundance("N")) % 2 == 1 ? true : false; bool nitrogeneven = !nitrogenodd; bool parityodd = getParity(molecule, z) == 'o' ? true : false; bool parityeven = !parityodd; bool zodd = z % 2 == 1 ? true : false; bool zeven = !zodd; return ( zeven & masseven & nitrogeneven ) | ( zeven & massodd & nitrogenodd ) | ( zodd & masseven & nitrogenodd ) | ( zodd & massodd & nitrogeneven ); } // }}} // // Decomposition of Mass / Isotope Pattern // RcppExport SEXP decomposeIsotopes(SEXP v_masses, SEXP v_abundances, SEXP s_error, SEXP l_alphabet, SEXP v_element_order, SEXP z, SEXP i_maxisotopes) { // {{{ typedef DistributionProbabilityScorer scorer_type; typedef scorer_type::score_type score_type; typedef scorer_type::masses_container masses_container; typedef scorer_type::abundances_container abundances_container; typedef distribution_t::peaks_container peaks_container; typedef distribution_t::mass_type mass_type; typedef distribution_t::abundance_type abundance_type; typedef distribution_t::nominal_mass_type nominal_mass_type; typedef multimap<score_type, ComposedElement, greater<score_type> > scores_container; typedef vector<pair<ComposedElement, score_type> > nonnormalized_scores_container; typedef decompositions_t::value_type decomposition_type; // Reset error state exceptionMesg = NULL; SEXP rl=R_NilValue; // Use this when there is nothing to be returned. try { RcppVector<double> masses = RcppVector<double>(v_masses); RcppVector<double> abundances = RcppVector<double>(v_abundances); double error = *REAL(s_error); // converts relative (ppm) in absolute error error *= masses(0) * 1.0e-06; // initializes precision double precision = 1.0e-05; int number_molecules_shown = 100; // initializes alphabet int maxisotopes = INTEGER_VALUE(i_maxisotopes); alphabet_t alphabet; vector<string> elements_order; if (l_alphabet == NULL || length(l_alphabet) < 1 ) { initializeCHNOPS(alphabet, maxisotopes); // initializes order of atoms in which one would // like them to appear in the molecules sequence elements_order.push_back("C"); elements_order.push_back("H"); elements_order.push_back("N"); elements_order.push_back("O"); elements_order.push_back("P"); elements_order.push_back("S"); } else { initializeAlphabet(l_alphabet, alphabet, maxisotopes); int element_length = length(v_element_order); for (int i=0; i<element_length; i++) { elements_order.push_back(string(CHAR(STRING_ELT(v_element_order,i)))); } } // initializes weights Weights weights(alphabet.getMasses(), precision); // checks if weights could become smaller, by dividing on gcd. weights.divideByGCD(); // initializes decomposer RealMassDecomposer decomposer(weights); // normalizes abundances abundance_type abundances_sum = 0.0; for (peaks_container::size_type i = 0; i < abundances.size(); ++i) { abundances_sum += abundances(i); } for (peaks_container::size_type i = 0; i < abundances.size(); ++i) { abundances(i) /= abundances_sum; } // fills peaklist masses and abundances, // since we cannot use masses and abundances - instances of RcppVector object - directly masses_container peaklist_masses; abundances_container peaklist_abundances; for (masses_container::size_type mi = 0; mi < masses.size() && mi < abundances.size(); ++mi) { peaklist_masses.push_back(masses(mi)); peaklist_abundances.push_back(abundances(mi)); } // initializes distribution probability scorer scorer_type scorer(peaklist_masses, peaklist_abundances); // initializes storage to store sum formulas and their non-normalized scores nonnormalized_scores_container nonnormalized_scores; // initializes storage for results: sum formulas and their scores scores_container scores; /////////////////////////////////////////////////////////////////////////////////////// ////////////////////////// Start identification pipeline ///////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// // gets all possible decompositions for the monoisotopic mass with error allowed decompositions_t decompositions = decomposer.getDecompositions(masses(0), error); score_type accumulated_score = 0.0; // for every decomposition: // - chemical filter is applied // - isotopic pattern is calculated // - isotopic pattern is matched against input spectrum for (decompositions_t::iterator decomps_it = decompositions.begin(); decomps_it != decompositions.end(); ++decomps_it) { // creates a candidate molecule out of elemental composition and a set of elements ComposedElement candidate_molecule(*decomps_it, alphabet); // checks on chemical filter // if (!isValidMyNitrogenRule(candidate_molecule, z)) { // continue; // } // updates molecules isotope distribution (since its not calculated upon creation: // it would be time consuming before applying chemical filter) candidate_molecule.updateIsotopeDistribution(); // updates molecules sequence in a order of elements(atoms) one would like it // to appear candidate_molecule.updateSequence(&elements_order); // gets a theoretical isotope distribution of the candidate molecule IsotopeDistribution candidate_molecule_distribution = candidate_molecule.getIsotopeDistribution(); // gets a sequence of the candidate molecule string candidate_molecule_sequence = candidate_molecule.getSequence(); // extracts masses and abundances from isotope distribution of the candidate molecule masses_container candidate_masses = candidate_molecule_distribution.getMasses(); abundances_container candidate_abundances = candidate_molecule_distribution.getAbundances(); // normalizes candidate abundances if the size of the measured peaklist is less than // the size of theoretical isotope distribution. This is always the case since our // theoretical distributions are limited to by default 10 peaks and measured peaklists contain // less than 10 peaks distribution_t::size_type size = min(peaklist_abundances.size(), candidate_abundances.size()); if (size < candidate_abundances.size()) { // normalizes the isotope distribution abundances with respect to the number of elements in peaklist abundance_type sum = accumulate(candidate_abundances.begin(), candidate_abundances.begin() + size, 0.0); if (fabs(sum - 1) > IsotopeDistribution::ABUNDANCES_SUM_ERROR) { abundance_type scale = 1/sum; transform(candidate_abundances.begin(), // begin of source range candidate_abundances.begin() + size, // end of source range candidate_abundances.begin(), // destination bind2nd(multiplies<abundance_type>(), scale)); // operation (*scale) } } // calculates a score score_type score = scorer.score(candidate_masses, candidate_abundances); // stores the sequence with non-normalized score nonnormalized_scores.push_back(make_pair(candidate_molecule, score)); // accumulates scores accumulated_score += score; } for (nonnormalized_scores_container::const_iterator it = nonnormalized_scores.begin(); it != nonnormalized_scores.end(); ++it) { score_type normalized_score = it->second; if (accumulated_score > 0.0) { normalized_score /= accumulated_score; } // stores the sequence with the score scores.insert(make_pair(normalized_score, it->first)); } // Now output to R ... if (scores.size() >0 ) { rl = rlistScores(scores, INTEGER_VALUE(z)); } } catch(std::exception& ex) { exceptionMesg = copyMessageToR(ex.what()); error_return(exceptionMesg); } catch(...) { exceptionMesg = copyMessageToR("unknown reason"); error_return(exceptionMesg); } return rl; } // }}} RcppExport SEXP calculateScore(SEXP v_predictMasses, SEXP v_predictAbundances, SEXP v_measuredMasses, SEXP v_meausuredAbundances) { // {{{ typedef DistributionProbabilityScorer scorer_type; typedef scorer_type::score_type score_type; typedef scorer_type::masses_container masses_container; typedef scorer_type::abundances_container abundances_container; typedef distribution_t::peaks_container peaks_container; typedef distribution_t::mass_type mass_type; typedef distribution_t::abundance_type abundance_type; RcppVector<double> masses = RcppVector<double>(v_predictMasses); RcppVector<double> abundances = RcppVector<double>(v_predictAbundances); // fills peaklist masses and abundances, // since we cannot use masses and abundances - instances of RcppVector object - directly masses_container peaklist_masses; abundances_container peaklist_abundances; for (masses_container::size_type mi = 0; mi < masses.size() && mi < abundances.size(); ++mi) { peaklist_masses.push_back(masses(mi)); peaklist_abundances.push_back(abundances(mi)); } // initializes distribution probability scorer scorer_type scorer(peaklist_masses, peaklist_abundances); masses = RcppVector<double>(v_measuredMasses); abundances = RcppVector<double>(v_meausuredAbundances); //cout << "Mess:" << masses<<"\n"; masses_container mess_masses; abundances_container mess_abundances; // normalizes abundances abundance_type abundances_sum = 0.0; for (peaks_container::size_type i = 0; i < abundances.size(); ++i) { abundances_sum += abundances(i); } for (peaks_container::size_type i = 0; i < abundances.size(); ++i) { abundances(i) /= abundances_sum; } for (masses_container::size_type mi = 0; mi < masses.size() && mi < abundances.size(); ++mi) { mess_masses.push_back(masses(mi)); mess_abundances.push_back(abundances(mi)); } score_type score=scorer.score(mess_masses, mess_abundances); SEXP output; PROTECT(output = NEW_NUMERIC(1)); //cout<< score; REAL(output)[0]=score; UNPROTECT(1); return (output); } // }}} RcppExport SEXP getMolecule(SEXP s_formula, SEXP l_alphabet, SEXP v_element_order, SEXP z, SEXP i_maxisotopes) { // {{{ SEXP rl=R_NilValue; // Use this when there is nothing to be returned. if( (s_formula==NULL) || !isString(s_formula) || length(s_formula) != 1) error("formula is not a single string"); typedef DistributionProbabilityScorer scorer_type; typedef scorer_type::score_type score_type; typedef multimap<score_type, ComposedElement, greater<score_type> > scores_container; // Reset error state exceptionMesg = NULL; // initializes alphabet int maxisotopes = INTEGER_VALUE(i_maxisotopes); alphabet_t alphabet; vector<string> elements_order; if (l_alphabet == NULL || length(l_alphabet) < 1 ) { initializeCHNOPS(alphabet, maxisotopes); // initializes order of atoms in which one would // like them to appear in the molecules sequence elements_order.push_back("C"); elements_order.push_back("H"); elements_order.push_back("N"); elements_order.push_back("O"); elements_order.push_back("P"); elements_order.push_back("S"); } else { initializeAlphabet(l_alphabet, alphabet, maxisotopes); int element_length = length(v_element_order); for (int i=0; i<element_length; i++) { elements_order.push_back(string(CHAR(STRING_ELT(v_element_order,i)))); } } try { // initializes precision double precision = 1.0e-05; // initializes weights Weights weights(alphabet.getMasses(), precision); // initializes storage for scores scores_container scores; ComposedElement molecule( CHARACTER_VALUE(s_formula), alphabet); molecule.updateSequence(&elements_order); molecule.updateIsotopeDistribution(); scores.insert(make_pair(1.0, molecule)); rl = rlistScores(scores, INTEGER_VALUE(z)); } catch(std::exception& ex) { exceptionMesg = copyMessageToR(ex.what()); error(exceptionMesg); } catch(...) { exceptionMesg = copyMessageToR("unknown reason"); error_return(exceptionMesg); } return rl; } // }}} RcppExport SEXP addMolecules(SEXP s_formula1, SEXP s_formula2, SEXP l_alphabet, SEXP v_element_order, SEXP maxisotopes) { // {{{ SEXP rl=R_NilValue; // Use this when there is nothing to be returned. if( (s_formula1==NULL) || s_formula2==NULL || !isString(s_formula1) || !isString(s_formula2) || length(s_formula2) != 1) { error("formula is not a single string"); } typedef DistributionProbabilityScorer scorer_type; typedef scorer_type::score_type score_type; typedef multimap<score_type, ComposedElement, greater<score_type> > scores_container; // initializes alphabet alphabet_t alphabet; vector<string> elements_order; if (l_alphabet == NULL || length(l_alphabet) < 1 ) { initializeCHNOPS(alphabet, 0); // initializes order of atoms in which one would // like them to appear in the molecules sequence elements_order.push_back("C"); elements_order.push_back("H"); elements_order.push_back("N"); elements_order.push_back("O"); elements_order.push_back("P"); elements_order.push_back("S"); } else { initializeAlphabet(l_alphabet, alphabet, 0); int element_length = length(v_element_order); for (int i=0; i<element_length; i++) { elements_order.push_back(string(CHAR(STRING_ELT(v_element_order,i)))); } } // cout << alphabet << endl; // initializes precision double precision = 1.0e-05; // initializes weights Weights weights(alphabet.getMasses(), precision); // initializes storage for scores scores_container scores; ComposedElement molecule( CHARACTER_VALUE(s_formula1), alphabet); ComposedElement molecule2( CHARACTER_VALUE(s_formula2), alphabet); molecule += molecule2; molecule.updateSequence(&elements_order); molecule.updateIsotopeDistribution(); scores.insert(make_pair(1.0, molecule)); rl = rlistScores(scores, 0); return rl; } // }}} RcppExport SEXP subMolecules(SEXP s_formula1, SEXP s_formula2, SEXP l_alphabet, SEXP v_element_order) { // {{{ SEXP rl=R_NilValue; // Use this when there is nothing to be returned. if( (s_formula1==NULL) || s_formula2==NULL || !isString(s_formula1) || !isString(s_formula2) || length(s_formula2) != 1) { error("formula is not a single string"); } typedef DistributionProbabilityScorer scorer_type; typedef scorer_type::score_type score_type; typedef multimap<score_type, ComposedElement, greater<score_type> > scores_container; // initializes alphabet alphabet_t alphabet; vector<string> elements_order; if (l_alphabet == NULL || length(l_alphabet) < 1 ) { initializeCHNOPS(alphabet, 0); // initializes order of atoms in which one would // like them to appear in the molecules sequence elements_order.push_back("C"); elements_order.push_back("H"); elements_order.push_back("N"); elements_order.push_back("O"); elements_order.push_back("P"); elements_order.push_back("S"); } else { initializeAlphabet(l_alphabet, alphabet, 0); int element_length = length(v_element_order); for (int i=0; i<element_length; i++) { elements_order.push_back(string(CHAR(STRING_ELT(v_element_order,i)))); } } // cout << alphabet << endl; // initializes precision double precision = 1.0e-05; // initializes weights Weights weights(alphabet.getMasses(), precision); // initializes storage for scores scores_container scores; ComposedElement molecule( CHARACTER_VALUE(s_formula1), alphabet); ComposedElement molecule2( CHARACTER_VALUE(s_formula2), alphabet); molecule -= molecule2; molecule.updateSequence(&elements_order); molecule.updateIsotopeDistribution(); scores.insert(make_pair(1.0, molecule)); rl = rlistScores(scores, 0); return rl; } // }}} template <typename score_type> SEXP rlistScores(multimap<score_type, ComposedElement, greater<score_type> > scores, int z) { // {{{ typedef DistributionProbabilityScorer scorer_type; typedef multimap<score_type, ComposedElement, greater<score_type> > scores_container; // Build result set to be returned as a list to R. vector<string> formula(scores.size()); RcppVector<double> score(scores.size()); RcppVector<double> exactmass(scores.size()); RcppVector<int> charge(scores.size()); // Chemical rules vector<string> parity(scores.size()); vector<string> valid(scores.size()); RcppVector<double> DBE(scores.size()); SEXP isotopes = PROTECT(allocVector(VECSXP, scores.size())); RcppResultSet rs; unsigned int i = 0; vector<string> colNames(2); colNames[0] = "mass"; colNames[1] = "intensity"; // outputs molecules & their scores. for (typename scores_container::const_iterator it = scores.begin(); it != scores.end(); ++it) { score(i) = it->first; formula[i] = it->second.getSequence(); exactmass(i) = it->second.getMass(); charge(i) = z; // Chemical rules parity[i] = getParity(it->second, z); valid[i] = isValidMyNitrogenRule(it->second, z) ? "Valid" : "Invalid"; DBE(i) = getDBE(it->second, z); IsotopeDistribution isodist = it->second.getIsotopeDistribution(); IsotopeDistribution::masses_container masses = isodist.getMasses(); IsotopeDistribution::abundances_container intensities = isodist.getAbundances(); int ny = masses.size(); SEXP tmp_isotopes = PROTECT(allocMatrix(REALSXP, 2, ny)); for(int j = 0; j < ny; j++) { REAL(tmp_isotopes)[0 + 2*j] = masses[j]; REAL(tmp_isotopes)[1 + 2*j] = intensities[j]; } SET_VECTOR_ELT(isotopes, i, tmp_isotopes); UNPROTECT(1); i++; } rs.add("formula", formula); rs.add("score", score); rs.add("exactmass", exactmass); rs.add("charge", z); rs.add("parity", parity); rs.add("valid", valid); rs.add("DBE", DBE); rs.add("isotopes", isotopes, true); // Get the list to be returned to R. SEXP rl = rs.getReturnList(); if(exceptionMesg != NULL) { error(exceptionMesg); } return rl; // }}} } // // Initialisation of Standard Element Alphabet // void initializeCHNOPS(alphabet_t& chnops, const int maxisotopes) { // {{{ typedef distribution_t::peaks_container peaks_container; typedef distribution_t::nominal_mass_type nominal_mass_type; typedef alphabet_t::element_type element_type; typedef alphabet_t::container elements_type; distribution_t::SIZE = maxisotopes; distribution_t::ABUNDANCES_SUM_ERROR = 0.00001; // Hydrogen nominal_mass_type massH = 1; peaks_container peaksH; peaksH.push_back(peaks_container::value_type(0.007825, 0.99985)); peaksH.push_back(peaks_container::value_type(0.014102, 0.00015)); distribution_t distributionH(peaksH, massH); // Oxygen nominal_mass_type massO = 16; peaks_container peaksO; peaksO.push_back(peaks_container::value_type(-0.005085, 0.99762)); peaksO.push_back(peaks_container::value_type(-0.000868, 0.00038)); peaksO.push_back(peaks_container::value_type(-0.000839, 0.002)); distribution_t distributionO(peaksO, massO); // Carbonate nominal_mass_type massC = 12; peaks_container peaksC; peaksC.push_back(peaks_container::value_type(0.0, 0.9889)); peaksC.push_back(peaks_container::value_type(0.003355, 0.0111)); distribution_t distributionC(peaksC, massC); // Nitrogen nominal_mass_type massN = 14; peaks_container peaksN; peaksN.push_back(peaks_container::value_type(0.003074, 0.99634)); peaksN.push_back(peaks_container::value_type(0.000109, 0.00366)); distribution_t distributionN(peaksN, massN); // Sulfur nominal_mass_type massS = 32; peaks_container peaksS; peaksS.push_back(peaks_container::value_type(-0.027929, 0.9502)); peaksS.push_back(peaks_container::value_type(-0.028541, 0.0075)); peaksS.push_back(peaks_container::value_type(-0.032133, 0.0421)); peaksS.push_back(peaks_container::value_type()); peaksS.push_back(peaks_container::value_type(-0.032919, 0.0002)); distribution_t distributionS(peaksS, massS); // Phosphor nominal_mass_type massP = 31; peaks_container peaksP; peaksP.push_back(peaks_container::value_type(-0.026238, 1.0)); distribution_t distributionP(peaksP, massP); element_type H("H", distributionH); element_type C("C", distributionC); element_type N("N", distributionN); element_type O("O", distributionO); element_type P("P", distributionP); element_type S("S", distributionS); chnops.push_back(H); chnops.push_back(C); chnops.push_back(N); chnops.push_back(O); chnops.push_back(P); chnops.push_back(S); // }}} } // // Initialisation of User-defined Alphabet // /* get the list element named str, or return NULL */ /* http://cran.r-project.org/doc/manuals/R-exts.html#Handling-lists */ SEXP getListElement(SEXP list, char *str) // {{{ { SEXP elmt = R_NilValue, names = getAttrib(list, R_NamesSymbol); int i; for (i = 0; i < length(list); i++) if(strcmp(CHAR(STRING_ELT(names, i)), str) == 0) { elmt = VECTOR_ELT(list, i); break; } return elmt; } // }}} void initializeAlphabet(const SEXP l_alphabet, alphabet_t &alphabet, const int maxisotopes) { // {{{ typedef distribution_t::peaks_container peaks_container; typedef distribution_t::nominal_mass_type nominal_mass_type; typedef alphabet_t::element_type element_type; typedef alphabet_t::container elements_type; distribution_t::SIZE = maxisotopes; distribution_t::ABUNDANCES_SUM_ERROR = 0.0001; for (int i=0; i < length(l_alphabet); i++) { SEXP l = VECTOR_ELT(l_alphabet,i); const char *symbol = STRING_VALUE(getListElement(l, "name")); nominal_mass_type nominalmass = (nominal_mass_type) REAL(getListElement(l, "mass"))[0]; SEXP isotope = getListElement(l, "isotope"); int numisotopes = length(getListElement(isotope, "mass")); double *mass = REAL(getListElement(isotope, "mass")); double *abundance = REAL(getListElement(isotope, "abundance")); peaks_container *peaks = new peaks_container(); for (int j=0; j<numisotopes; j++) { peaks->push_back(peaks_container::value_type(mass[j], abundance[j])); } distribution_t *distribution = new distribution_t(*peaks, nominalmass); element_type element(symbol, *distribution); alphabet.push_back(element); } // }}} } extern "C" { void R_init_disop(DllInfo *info) { /* Register routines, allocate resources. * We call most functions with .Call */ R_CallMethodDef callMethods[] = { {"getMolecule", (void* (*)())&getMolecule, 4}, {"addMolecules", (void* (*)())&addMolecules, 4}, {"subMolecules", (void* (*)())&subMolecules, 4}, {"decomposeIsotopes", (void* (*)())&decomposeIsotopes, 7}, {"calculateScore", (void* (*)())&calculateScore, 7}, {NULL, NULL, 0} }; R_registerRoutines(info, NULL, callMethods, NULL, NULL); } void R_unload_disop(DllInfo *info) { /* Release resources. */ } }