/* * Checks that the units of the function are consistent * for a function returning value with same units as argument(s) * * If inconsistent units are found, an error message is logged. */ void ArgumentsUnitsCheck::checkSameUnitsAsArgs (const Model& m, const ASTNode& node, const SBase & sb, bool inKL, int reactNo) { /* check that node has children */ if (node.getNumChildren() == 0) { return; } UnitDefinition * ud; UnitDefinition * tempUD = NULL; unsigned int n; unsigned int i = 0; UnitFormulaFormatter *unitFormat = new UnitFormulaFormatter(&m); ud = unitFormat->getUnitDefinition(node.getChild(i), inKL, reactNo); /* get the first child that is not a parameter with undeclared units */ while (unitFormat->getContainsUndeclaredUnits() && i < node.getNumChildren()-1) { delete ud; i++; unitFormat->resetFlags(); ud = unitFormat->getUnitDefinition(node.getChild(i), inKL, reactNo); } /* check that all children have the same units * unless one of the children is a parameter with undeclared units * which is not tested */ for (n = i+1; n < node.getNumChildren(); n++) { unitFormat->resetFlags(); tempUD = unitFormat->getUnitDefinition(node.getChild(n), inKL, reactNo); if (!unitFormat->getContainsUndeclaredUnits()) { if (!UnitDefinition::areIdenticalSIUnits(ud, tempUD)) { logInconsistentSameUnits(node, sb); } } delete tempUD; } delete unitFormat; delete ud; for (n = 0; n < node.getNumChildren(); n++) { checkUnits(m, *node.getChild(n), sb, inKL, reactNo); } }
/** * Checks that the functions have either one or two arguments */ void NumberArgsMathCheck::checkSpecialCases(const Model& m, const ASTNode& node, const SBase & sb) { if (node.getNumChildren() < 1 || node.getNumChildren() > 2) { logMathConflict(node, sb); } for (unsigned int n = 0; n < node.getNumChildren(); n++) { checkMath(m, *node.getChild(n), sb); } }
END_TEST START_TEST (test_element_selector) { const char* s = wrapMathML ( " <apply>" " <selector/>" " <ci> A </ci>" " <ci> i </ci>" " </apply>" ); N = readMathMLFromStringWithNamespaces(s, NS); fail_unless( N != NULL ); fail_unless( N->getType() == AST_ORIGINATES_IN_PACKAGE); fail_unless( N->getExtendedType() == AST_LINEAR_ALGEBRA_SELECTOR); fail_unless( N->getNumChildren() == 2); fail_unless( N->getPackageName() == "arrays"); ASTNode * child = N->getChild(0); fail_unless( child != NULL ); fail_unless( child->getType() == AST_NAME); fail_unless( child->getExtendedType() == AST_NAME); fail_unless( strcmp(child->getName(), "A") == 0); fail_unless( child->getNumChildren() == 0); fail_unless( child->getPackageName() == "core"); child = N->getChild(1); fail_unless( child != NULL ); fail_unless( child->getType() == AST_NAME); fail_unless( child->getExtendedType() == AST_NAME); fail_unless( strcmp(child->getName(), "i") == 0); fail_unless( child->getNumChildren() == 0); ArraysASTPlugin* plugin = static_cast<ArraysASTPlugin*>(N->getPlugin("arrays")); fail_unless(plugin != NULL); fail_unless(plugin->getASTType() == AST_LINEAR_ALGEBRA_SELECTOR); }
/* * Checks that the second argument of a piecewise returns a boolean * * If not, an error message is logged. */ void PieceBooleanMathCheck::checkPiece (const Model& m, const ASTNode& node, const SBase & sb) { unsigned int numChildren = node.getNumChildren(); unsigned int numPieces = numChildren; if ((numChildren % 2) != 0) numPieces--; for (unsigned int n = 1; n < numPieces; n += 2) { // if we have a mangled node for some reason // usually we have read an incorrect node // need to be sure there is a child // NOTE: piecewise hits this issue because old behaviour // meant it lost the piece and otherwise qualifiers ASTNode * child = node.getChild(n); if (child != NULL) { // need to pass the model here in case we have used a functionDefinition // as the piece child if (!child->returnsBoolean(&m)) { logMathConflict(node, sb); } } } }
/** * Constructor that makes a ConverterASTNode from an ASTNode. */ ConverterASTNode::ConverterASTNode(const ASTNode &templ): ASTNode(templ.getType()) { if (this->getType() == AST_RATIONAL) { this->mDenominator = templ.getDenominator(); this->mInteger = templ.getNumerator(); } else if (this->getType() == AST_REAL || this->getType() == AST_REAL_E) { this->mExponent = templ.getExponent(); this->mReal = templ.getMantissa(); } if (this->getType() == AST_PLUS || this->getType() == AST_MINUS || this->getType() == AST_TIMES || this->getType() == AST_DIVIDE || this->getType() == AST_POWER) { this->mChar = templ.getCharacter(); } else if (this->getType() == AST_INTEGER) { this->mInteger = templ.getInteger(); } if ((!this->isOperator()) && (!this->isNumber())) { this->setName(templ.getName()); } unsigned int counter; for (counter = 0; counter < templ.getNumChildren(); counter++) { this->addChild(new ConverterASTNode(*templ.getChild(counter))); } };
/** * Checks that the units of the arguments * of the function are dimensionless * and that there is only one argument * * If inconsistent units are found, an error message is logged. */ void ArgumentsUnitsCheckWarnings::checkDimensionlessArgs (const Model& m, const ASTNode& node, const SBase & sb, bool inKL, int reactNo) { /* check that node has children */ if (node.getNumChildren() == 0) { return; } UnitDefinition *dim = new UnitDefinition(m.getSBMLNamespaces()); Unit *unit = new Unit(m.getSBMLNamespaces()); unit->setKind(UNIT_KIND_DIMENSIONLESS); unit->initDefaults(); UnitDefinition * tempUD; dim->addUnit(unit); UnitFormulaFormatter *unitFormat = new UnitFormulaFormatter(&m); tempUD = unitFormat->getUnitDefinition(node.getChild(0), inKL, reactNo); if (tempUD->getNumUnits() != 0 && !UnitDefinition::areEquivalent(dim, tempUD)) { logInconsistentDimensionless(node, sb); } delete tempUD; delete dim; delete unit; delete unitFormat; }
unsigned int ASTPiecewiseFunctionNode::getNumChildren() const { /* HACK TO REPLICATE OLD AST */ unsigned int numChildren = 0; for (unsigned int i = 0; i < getNumPiece(); i++) { ASTBase * base = ASTFunctionBase::getChild(i); ASTNode * piece = dynamic_cast<ASTNode*>(base); if (piece != NULL && piece->getType() == AST_CONSTRUCTOR_PIECE) { numChildren += piece->getNumChildren(); } else { // fail safe - a piece should have 2 children numChildren += 2; } } if (getHasOtherwise() == true) { numChildren++; } return numChildren; }
/** * Checks that the functionDefinition referred to by a <ci> element * has the appropriate number of arguments. * * If not, an error message is logged. */ void FunctionNoArgsMathCheck::checkNumArgs (const Model& m, const ASTNode& node, const SBase & sb) { /* this rule was only introduced level 2 version 4 */ if (m.getLevel() > 2 || (m.getLevel() == 2 && m.getVersion() > 3)) { if (m.getFunctionDefinition(node.getName()) != NULL) { /* functiondefinition math */ const ASTNode * fdMath = m.getFunctionDefinition(node.getName())->getMath(); if (fdMath != NULL) { /* We have a definition for this function. Does the defined number of arguments equal the number used here? */ if (node.getNumChildren() != m.getFunctionDefinition(node.getName())->getNumArguments()) { logMathConflict(node, sb); } } } } }
void NumberArgsMathCheck::checkPiecewise(const Model&, const ASTNode& node, const SBase & sb) { if (node.getNumChildren() == 0) { logMathConflict(node, sb); } }
/** * In MathML, <plus/> and <times/> are n-ary operators but the infix * FormulaParser represents them as binary operators. To ensure a * consistent AST representation, this function is part of the n-ary to * binary reduction process. */ static void reduceBinary (ASTNode& node) { if (node.getNumChildren() == 2) { ASTNode* op = new ASTNode( node.getType() ); node.swapChildren(op); node.prependChild(op); } }
/** * Checks that the function has only one argument */ void NumberArgsMathCheck::checkUnary(const Model& m, const ASTNode& node, const SBase & sb) { if (node.getNumChildren() != 1) { logMathConflict(node, sb); } else { checkMath(m, *node.getLeftChild(), sb); } }
bool ArraysASTPlugin::hasUnambiguousPackageInfixGrammar(const ASTNode *child) const { ASTBase* function = const_cast<ASTBase*>(getParentASTObject()); if (function == NULL) return false; if (function->getType() != AST_ORIGINATES_IN_PACKAGE) return false; if (function->getPackageName() != "arrays") return false; // cast the function to an ASTNode ASTNode* newAST = dynamic_cast<ASTNode*>(function); if (newAST == NULL) { return false; } const ArraysASTPlugin* aap = static_cast<const ArraysASTPlugin*>(function->getPlugin("arrays")); switch(aap->getASTType()) { case AST_LINEAR_ALGEBRA_SELECTOR: if (newAST->getNumChildren() == 0) return true; if (newAST->getChild(0) == child) return false; //The *first* child of the selector needs parentheses in some situations! return true; //All other children are separated by commas, and thus don't need parentheses. case AST_LINEAR_ALGEBRA_VECTOR_CONSTRUCTOR: #if (0) case AST_LINEAR_ALGEBRA_MATRIX_CONSTRUCTOR: case AST_LINEAR_ALGEBRA_DETERMINANT: case AST_LINEAR_ALGEBRA_TRANSPOSE: case AST_LINEAR_ALGEBRA_VECTOR_PRODUCT: case AST_LINEAR_ALGEBRA_SCALAR_PRODUCT: case AST_LINEAR_ALGEBRA_OUTER_PRODUCT: case AST_LINEAR_ALGEBRA_MATRIXROW_CONSTRUCTOR: #endif case AST_QUALIFIER_CONDITION: case AST_LOGICAL_EXISTS: case AST_LOGICAL_FORALL: case AST_QUALIFIER_LOWLIMIT: case AST_STATISTICS_MEAN: case AST_STATISTICS_MEDIAN: case AST_STATISTICS_MODE: case AST_STATISTICS_MOMENT: case AST_QUALIFIER_MOMENTABOUT: case AST_SERIES_PRODUCT: case AST_STATISTICS_SDEV: case AST_SERIES_SUM: case AST_QUALIFIER_UPLIMIT: case AST_STATISTICS_VARIANCE: return true; //Everything is either a function or has unambiguous syntax. case AST_ARRAYS_UNKNOWN: return false; } return false; }
int ASTLambdaFunctionNode::removeChild(unsigned int n) { int removed = LIBSBML_OPERATION_FAILED; /* need to keep track of whether we have removed a bvar */ unsigned int numBvars = getNumBvars(); if (numBvars == 0) { /* we are removing the body - if the index is appropriate */ return ASTFunctionBase::removeChild(n); } if (n < numBvars) { // we are removing a bvar setNumBvars(numBvars - 1); /* HACK TO REPLICATE OLD AST */ /* Hack to remove memory since the overall * remove does not remove memory * but the old api does not give access to the new * intermediate parents so these can never * be explicilty deleted by the user * * in this case the first remove is accessible */ ASTBase * base = ASTFunctionBase::getChild(n); ASTNode * bvar = dynamic_cast<ASTNode*>(base); if (bvar != NULL && bvar->getNumChildren() == 1) { removed = bvar->removeChild(0); if (removed == LIBSBML_OPERATION_SUCCESS) { ASTBase * removedAST = NULL; removedAST = this->ASTFunctionBase::getChild(n); removed = ASTFunctionBase::removeChild(n); if (removedAST != NULL) delete removedAST; } } } else { // we are removing the body removed = ASTFunctionBase::removeChild(n); } return removed; }
/* * Checks that the arguments to logical operators are all boolean * * If not, an error message is logged. */ void LogicalArgsMathCheck::checkMathFromLogical (const Model&, const ASTNode& node, const SBase & sb) { unsigned int n; for (n = 0; n < node.getNumChildren(); n++) { if (!node.getChild(n)->isBoolean()) { logMathConflict(node, sb); } } }
/* * Checks that the arguments of numeric functions are consistent * * If not, an error message is logged. */ void NumericArgsMathCheck::checkNumericArgs (const Model& m, const ASTNode& node, const SBase & sb) { unsigned int n; for (n = 0; n < node.getNumChildren(); n++) { if (!returnsNumeric(m, node.getChild(n))) { logMathConflict(node, sb); } } }
/* * Checks that the units of the delay function are consistent * * If inconsistent units are found, an error message is logged. */ void ArgumentsUnitsCheck::checkUnitsFromDelay (const Model& m, const ASTNode& node, const SBase & sb, bool inKL, int reactNo) { /* check that node has two children */ if (node.getNumChildren() != 2) { return; } if (!m.getSBMLNamespaces()->getNamespaces()) { #if 0 cout << "[DEBUG] XMLNS IS NULL" << endl; #endif } /* delay(x, t) * no restrictions on units of x * but t must have units of time */ UnitDefinition *time = new UnitDefinition(m.getSBMLNamespaces()); Unit *unit = new Unit(m.getSBMLNamespaces()); unit->setKind(UNIT_KIND_SECOND); unit->initDefaults(); UnitDefinition * tempUD; time->addUnit(unit); UnitFormulaFormatter *unitFormat = new UnitFormulaFormatter(&m); tempUD = unitFormat->getUnitDefinition(node.getRightChild(), inKL, reactNo); if (!unitFormat->getContainsUndeclaredUnits()) { if (!UnitDefinition::areEquivalent(time, tempUD)) { logInconsistentDelay(node, sb); } } delete time; delete tempUD; delete unit; delete unitFormat; checkUnits(m, *node.getLeftChild(), sb, inKL, reactNo); }
bool ArraysASTPlugin::hasCorrectNumberArguments(int type) const { bool correctNumArgs = true; ASTBase* function = const_cast<ASTBase*>(getMath()); // so getMath will only return teh node if it is a vector or matrix if (function == NULL) { function = const_cast<ASTBase*>(getParentASTObject()); if (function == NULL) { return false; } } // cast the function to an ASTNode ASTNode * newAST = dynamic_cast<ASTNode*>(function); // double check we are working with the right thing if (newAST == NULL) { return false; } else if (newAST->getExtendedType() != type) { return false; } unsigned int numChildren = newAST->getNumChildren(); switch (type) { case AST_LINEAR_ALGEBRA_SELECTOR: if (numChildren < 1 || numChildren > 3) { correctNumArgs = false; } break; default: break; } return correctNumArgs; }
ASTBase* ASTLambdaFunctionNode::getChild (unsigned int n) const { /* HACK TO REPLICATE OLD AST */ /* do not return a node with the bvar type * return the child of the bvar type */ if (ASTFunctionBase::getNumChildren() <= n) { return NULL; } if (ASTFunctionBase::getChild(n)->getType() == AST_QUALIFIER_BVAR) { ASTBase * base = ASTFunctionBase::getChild(n); ASTNode * bvar = dynamic_cast<ASTNode*>(base); //if (base->getFunction() != NULL) //{ // bvar = static_cast<ASTFunction*>(base->getFunction()); //} //else //{ // bvar = static_cast<ASTFunction*>(base); //} if (bvar != NULL) { if (bvar->getNumChildren() > 0) { return bvar->getChild(0); } else { return NULL; } } else { return NULL; } } else { return ASTFunctionBase::getChild(n); } }
ASTBase* ASTNaryFunctionNode::getChild (unsigned int n) const { if (this->getType() != AST_FUNCTION_ROOT) { return ASTFunctionBase::getChild(n); } else { /* HACK TO REPLICATE OLD AST */ /* do not return a node with the degree type * return the child of the degree */ if (ASTFunctionBase::getNumChildren() <= n) { return NULL; } if (ASTFunctionBase::getChild(n)->getType() == AST_QUALIFIER_DEGREE) { ASTBase * base = ASTFunctionBase::getChild(n); ASTNode * degree = dynamic_cast<ASTNode*>(base); if (degree != NULL) { if (degree->getNumChildren() > 0) { return degree->getChild(0); } else { return NULL; } } else { return NULL; } } else { return ASTFunctionBase::getChild(n); } } }
int ASTBinaryFunctionNode::removeChild (unsigned int n) { int removed = LIBSBML_OPERATION_FAILED; if (this->getType() != AST_FUNCTION_LOG) { removed = ASTFunctionBase::removeChild(n); } else { /* HACK TO REPLICATE OLD AST */ /* Hack to remove memory since the overall * remove does not remove memory * but the old api does not give access to the new * intermediate parents so these can never * be explicilty deleted by the user * * in this case the first remove is accessible */ if (ASTFunctionBase::getChild(n)->getType() == AST_QUALIFIER_LOGBASE) { ASTBase * base = ASTFunctionBase::getChild(n); ASTNode * logbase = dynamic_cast<ASTNode*>(base); if (logbase != NULL && logbase->getNumChildren() == 1) { removed = logbase->removeChild(0); if (removed == LIBSBML_OPERATION_SUCCESS) { ASTBase * removedAST = NULL; removedAST = this->ASTFunctionBase::getChild(n); removed = ASTFunctionBase::removeChild(n); if (removedAST != NULL) delete removedAST; } } } else { removed = ASTFunctionBase::removeChild(n); } } return removed; }
bool ArraysASTPlugin::isWellFormedNode(int type) const { bool valid = hasCorrectNumberArguments(type); ASTBase* function = const_cast<ASTBase*>(getMath()); // so getMath will only return teh node if it is a vector or matrix if (function == NULL) { function = const_cast<ASTBase*>(getParentASTObject()); if (function == NULL) { return false; } } // cast the function to an ASTNode ASTNode * newAST = dynamic_cast<ASTNode*>(function); // double check we are working with the right thing if (newAST == NULL) { return false; } else if (newAST->getExtendedType() != type) { return false; } unsigned int numChildren = newAST->getNumChildren(); unsigned int i = 0; // check number of arguments while (valid && i < numChildren) { valid = newAST->getChild(i)->isWellFormedNode(); i++; } return valid; }
/** * Ensures the given ASTNode has the appropriate number of arguments. If * arguments are missing, appropriate defaults (per the MathML 2.0 * specification) are added: * * log (x) -> log (10, x) * root(x) -> root( 2, x) */ static void checkFunctionArgs (ASTNode& node) { if (node.getNumChildren() == 1) { if (node.getType() == AST_FUNCTION_LOG) { ASTNode* child = new ASTNode; child->setValue(10); node.prependChild(child); } else if (node.getType() == AST_FUNCTION_ROOT) { ASTNode* child = new ASTNode; child->setValue(2); node.prependChild(child); } } }
/** * Checks that the arguments of the branches of a piecewise are consistent * * If not, an error message is logged. */ void PiecewiseValueMathCheck::checkPiecewiseArgs (const Model& m, const ASTNode& node, const SBase & sb) { unsigned int numChildren = node.getNumChildren(); /* arguments must return consistent types */ for (unsigned int n = 0; n < numChildren; n += 2) { if (returnsNumeric(m, node.getChild(n)) && !returnsNumeric(m, node.getLeftChild())) { logMathConflict(node, sb); } else if (node.getChild(n)->isBoolean() && !node.getLeftChild()->isBoolean()) { logMathConflict(node, sb); } } }
pair<string, list<string> > MMOMath::_generateAlgebraic (pair<list<string>, ASTNode*> function, list<string> args, ASTNode *node) { ASTNode *repNode = new ASTNode (*function.second); _processNode (repNode); list<string>::iterator defArgs = function.first.begin (); list<string> variables; for (list<string>::iterator it = args.begin (); it != args.end (); it++) { repNode->renameSIdRefs (*defArgs, *it); defArgs++; } string ret = MMOUtils::getInstance ()->getExp (repNode); _getVariables (repNode, &variables); int childs = node->getNumChildren (), i; for (i = 0; i < childs; i++) { node->removeChild (0); } if (_type == MATH_ZERO_CROSSING) { node->setType (repNode->getType ()); childs = repNode->getNumChildren (); for (i = 0; i < childs; i++) { node->addChild (new ASTNode (*repNode->getChild (i))); } } else { node->setType (AST_NAME); node->setName (ret.c_str ()); node->setId ("REPLACED_FUNCTION"); } delete repNode; return (pair<string, list<string> > (ret, variables)); }
/** * Checks that the arguments to eq or neq are consistent * i.e. have same type both boolean or both numeric * * If an inconsistency is found, an error message is logged. */ void EqualityArgsMathCheck::checkArgs (const Model& m, const ASTNode& node, const SBase & sb) { /* check that node has two children */ if (node.getNumChildren() != 2) { return; } /* arguments must return consistent value types */ if (returnsNumeric(m, node.getLeftChild()) && !returnsNumeric(m, node.getRightChild())) { logMathConflict(node, sb); } else if (node.getLeftChild()->isBoolean() && !node.getRightChild()->isBoolean()) { logMathConflict(node, sb); } }
/* * Checks that the second argument of a piecewise returns a boolean * * If not, an error message is logged. */ void PieceBooleanMathCheck::checkPiece (const Model&, const ASTNode& node, const SBase & sb) { unsigned int numChildren = node.getNumChildren(); unsigned int numPieces = numChildren; #ifdef LIBSBML_USE_LEGACY_MATH if ((numChildren % 2) != 0) numPieces--; #else numPieces = 2 * node.getNumPiece(); if (numPieces > numChildren) { // the piecewise is not correct return; } #endif for (unsigned int n = 1; n < numPieces; n += 2) { // if we have a mangled node for some reason // usually we have read an incorrect node // need to be sure there is a child // NOTE: piecewise hits this issue because old behaviour // meant it lost the piece and otherwise qualifiers ASTNode * child = node.getChild(n); if (child != NULL) { if (!child->returnsBoolean()) { logMathConflict(node, sb); } } } }
void ArraysASTPlugin::visitMatrix( const ASTNode *parent, const ASTNode *node, StringBuffer_t *sb, const L3ParserSettings* settings) const { //Note: if there is one or zero rows, or if any of the rows have zero children, a matrix is declared as a function, and 'visitMatrix' won't be called. unsigned int numChildren = node->getNumChildren(); StringBuffer_appendChar(sb, '{'); for (unsigned int child=0; child<numChildren; child++) { ASTNode* row = node->getChild(child); if (child>0) { StringBuffer_appendChar(sb, ';'); StringBuffer_appendChar(sb, ' '); } for (unsigned int c2=0; c2<row->getNumChildren(); c2++) { if (c2>0) { StringBuffer_appendChar(sb, ','); StringBuffer_appendChar(sb, ' '); } L3FormulaFormatter_visit(row, row->getChild(c2), sb, settings); } } StringBuffer_appendChar(sb, '}'); }
/** * Checks that the units of the power function are consistent * * If inconsistent units are found, an error message is logged. * * The two arguments to power, which are of the form power(a, b) * with the meaning a^b, should be as follows: * (1) if the second argument is an integer, * then the first argument can have any units; * (2) if the second argument b is a rational number n/m, * it must be possible to derive the m-th root of (a{unit})n, * where {unit} signifies the units associated with a; * otherwise, (3) the units of the first argument must be “dimensionless”. * The second argument (b) should always have units of “dimensionless”. * */ void PowerUnitsCheck::checkUnitsFromPower (const Model& m, const ASTNode& node, const SBase & sb, bool inKL, int reactNo) { /* check that node has 2 children */ if (node.getNumChildren() != 2) { return; } double value; UnitDefinition dim(m.getSBMLNamespaces()); Unit unit(m.getSBMLNamespaces()); unit.setKind(UNIT_KIND_DIMENSIONLESS); unit.initDefaults(); dim.addUnit(&unit); UnitFormulaFormatter *unitFormat = new UnitFormulaFormatter(&m); UnitDefinition *tempUD = NULL; UnitDefinition *unitsArg1, *unitsArgPower; unitsArg1 = unitFormat->getUnitDefinition(node.getLeftChild(), inKL, reactNo); unsigned int undeclaredUnits = unitFormat->getContainsUndeclaredUnits(); ASTNode *child = node.getRightChild(); unitFormat->resetFlags(); unitsArgPower = unitFormat->getUnitDefinition(child, inKL, reactNo); unsigned int undeclaredUnitsPower = unitFormat->getContainsUndeclaredUnits(); // The second argument (b) should always have units of “dimensionless”. // or it has undeclared units that we assume are correct if (undeclaredUnitsPower == 0 && !UnitDefinition::areEquivalent(&dim, unitsArgPower)) { logNonDimensionlessPowerConflict(node, sb); } // The first argument is dimensionless then it doesnt matter // what the power is if (undeclaredUnits == 0 && !UnitDefinition::areEquivalent(&dim, unitsArg1)) { // if not argument needs to be an integer or a rational unsigned int isInteger = 0; unsigned int isRational = 0; unsigned int isExpression = 0; /* power must be an integer * but need to check that it is not a real * number that is integral * i.e. mathml <cn> 2 </cn> will record a "real" */ if (child->isRational()) { isRational = 1; } else if (child->isInteger()) { isInteger = 1; } else if (child->isReal()) { if (ceil(child->getReal()) == child->getReal()) { isInteger = 1; } } else if (child->getNumChildren() > 0) { // power might itself be an expression tempUD = unitFormat->getUnitDefinition(child, inKL, reactNo); UnitDefinition::simplify(tempUD); if (tempUD->isVariantOfDimensionless()) { SBMLTransforms::mapComponentValues(&m); double value = SBMLTransforms::evaluateASTNode(child); SBMLTransforms::clearComponentValues(); #if defined(__linux) if (!std::isnan(value)) #else if (!isnan(value)) #endif { if (floor(value) != value) isExpression = 1; else isInteger = 1; } else { isExpression = 1; } } else { /* here the child is an expression with units * flag the expression as not checked */ isExpression = 1; } } else { // power could be a parameter or a speciesReference in l3 const Parameter *param = NULL; const SpeciesReference *sr = NULL; if (child->isName()) { /* Parameters may be declared in two places (the model and the * kinetic law's local parameter list), so we have to check both. */ if (sb.getTypeCode() == SBML_KINETIC_LAW) { const KineticLaw* kl = dynamic_cast<const KineticLaw*>(&sb); /* First try local parameters and if null is returned, try * the global parameters */ if (kl != NULL) { param = kl->getParameter(child->getName()); } } if (param == NULL) { param = m.getParameter(child->getName()); } if (param == NULL && m.getLevel() > 2) { // could be a species reference sr = m.getSpeciesReference(child->getName()); } } if (param != NULL) { /* We found a parameter with this identifier. */ if (UnitDefinition::areEquivalent(&dim, unitsArgPower) || undeclaredUnitsPower) { value = param->getValue(); if (value != 0) { if (ceil(value) == value) { isInteger = 1; } } } else { /* No parameter definition found for child->getName() */ logUnitConflict(node, sb); } } else if (sr != NULL) { // technically here there is an issue // stoichiometry is dimensionless SBMLTransforms::mapComponentValues(&m); double value = SBMLTransforms::evaluateASTNode(child, &m); SBMLTransforms::clearComponentValues(); // but it may not be an integer #if defined(__linux) if (!std::isnan(value)) #else if (!isnan(value)) #endif // we cant check { isExpression = 1; } else { if (ceil(value) == value) { isInteger = 1; } } } } if (isRational == 1) { //FIX-ME will need sorting for double exponents //* (2) if the second argument b is a rational number n/m, //* it must be possible to derive the m-th root of (a{unit})n, //* where {unit} signifies the units associated with a; unsigned int impossible = 0; for (unsigned int n = 0; impossible == 0 && n < unitsArg1->getNumUnits(); n++) { if ((int)(unitsArg1->getUnit(n)->getExponent()) * child->getInteger() % child->getDenominator() != 0) impossible = 1; } if (impossible) logRationalPowerConflict(node, sb); } else if (isExpression == 1) { logExpressionPowerConflict(node, sb); } else if (isInteger == 0) { logNonIntegerPowerConflict(node, sb); } } // if (!areEquivalent(dim, unitsPower)) // { // /* 'v' does not have units of dimensionless. */ // /* If the power 'n' is a parameter, check if its units are either // * undeclared or declared as dimensionless. If either is the case, // * the value of 'n' must be an integer. // */ // const Parameter *param = NULL; // if (child->isName()) // { // /* Parameters may be declared in two places (the model and the // * kinetic law's local parameter list), so we have to check both. // */ // if (sb.getTypeCode() == SBML_KINETIC_LAW) // { // const KineticLaw* kl = dynamic_cast<const KineticLaw*>(&sb); // /* First try local parameters and if null is returned, try // * the global parameters */ // if (kl != NULL) // { // param = kl->getParameter(child->getName()); // } // } // if (param == NULL) // { // param = m.getParameter(child->getName()); // } // // } // if (param != NULL) // { // /* We found a parameter with this identifier. */ // if (areEquivalent(dim, unitsArgPower) || unitFormat->hasUndeclaredUnits(child)) // { // value = param->getValue(); // if (value != 0) // { // if (ceil(value) != value) // { // logUnitConflict(node, sb); // } // } // } // else // { ///* No parameter definition found for child->getName() */ // logUnitConflict(node, sb); // } // } // else if (child->isFunction() || child->isOperator()) // { // /* cannot test whether the value will be appropriate */ // if (!areEquivalent(dim, unitsArgPower)) // { // logUnitConflict(node, sb); // } // } // /* power must be an integer // * but need to check that it is not a real // * number that is integral // * i.e. mathml <cn> 2 </cn> will record a "real" // */ // else if (!child->isInteger()) // { // if (!child->isReal()) // { // logUnitConflict(node, sb); // } // else if (ceil(child->getReal()) != child->getReal()) // { // logUnitConflict(node, sb); // } // } // } // else if (!areEquivalent(dim, unitsArgPower)) // { // /* power (3, k) */ // logUnitConflict(node, sb); // } checkUnits(m, *node.getLeftChild(), sb, inKL, reactNo); delete unitFormat; delete unitsArg1; delete unitsArgPower; }
bool ASTNaryFunctionNode::isSqrt() const { bool valid = false; if (getType() == AST_FUNCTION_ROOT) { // a sqrt can have either one child that is not the degree qualifier // or two where the first is the degree of 2 if (getNumChildren() == 1) { /* HACK to replicate OLD AST whic says a sqrt must have two children*/ valid = false; //ASTBase * base1 = getChild(0); //if (base1->isQualifier() == false) //{ // valid = true; //} } else if (getNumChildren() == 2) { ASTBase * base1 = ASTFunctionBase::getChild(0); ASTFunction* fun = dynamic_cast<ASTFunction*>(base1); if (fun != NULL) { if (fun->getType() == AST_QUALIFIER_DEGREE && fun->getNumChildren() == 1) { ASTBase *base2 = fun->getChild(0); if (base2->getType() == AST_INTEGER) { ASTNumber *child = static_cast<ASTNumber*>(base2); if (child->getInteger() == 2) { valid = true; } } } } else { // here we are working the ASTNode so the casting // is more difficult ASTNode* newAST = dynamic_cast<ASTNode*>(base1); if (newAST != NULL && newAST->getType() == AST_QUALIFIER_DEGREE && newAST->getNumChildren() == 1) { ASTNode* newAST1 = newAST->getChild(0); if (newAST1->getType() == AST_INTEGER) { if (newAST1->getInteger() == 2) { valid = true; } } } else { if (newAST != NULL && newAST->getType() == AST_INTEGER) { if (newAST->getInteger() == 2) { valid = true; } } } } } } return valid; }
bool ASTNaryFunctionNode::isLog10() const { bool valid = false; if (getType() == AST_FUNCTION_LOG) { // a log can have either one child that is not the logbase qualifier // or two where the first is the logbase of 10 if (getNumChildren() == 1) { ASTBase * base1 = ASTFunctionBase::getChild(0); if (base1->isQualifier() == false) { valid = true; } } else if (getNumChildren() == 2) { ASTBase * base1 = ASTFunctionBase::getChild(0); ASTFunction* fun = dynamic_cast<ASTFunction*>(base1); if (fun != NULL) { if (fun->getType() == AST_QUALIFIER_LOGBASE && fun->getNumChildren() == 1) { ASTBase *base2 = fun->getChild(0); if (base2->getType() == AST_INTEGER) { ASTNumber *child = static_cast<ASTNumber*>(base2); if (child->getInteger() == 10) { valid = true; } } } } else { // here we are working the ASTNode so the casting // is more difficult ASTNode* newAST = dynamic_cast<ASTNode*>(base1); if (newAST != NULL && newAST->getType() == AST_QUALIFIER_LOGBASE && newAST->getNumChildren() == 1 ) { ASTNode* newAST1 = newAST->getChild(0); if (newAST1->getType() == AST_INTEGER) { if (newAST1->getInteger() == 10) { valid = true; } } } else { if (newAST != NULL && newAST->getType() == AST_INTEGER) { if (newAST->getInteger() == 10) { valid = true; } } } } } } return valid; }