/*! * \param[in] sc Selection collection (used for position evaluation). * \param[in] method Method to use. * \param[in] nargs Number of arguments for keyword matching. * \param[in] args Pointer to the first argument. * \param[in] rpost Reference position type to use (NULL = default). * \returns The created selection element. * * This function handles the creation of a \c t_selelem object for * selection methods that do not take parameters. */ t_selelem * _gmx_sel_init_keyword(gmx_ana_selcollection_t *sc, gmx_ana_selmethod_t *method, int nargs, t_selexpr_value *args, const char *rpost) { t_selelem *root, *child; t_selexpr_param *params, *param; int rc; if (method->nparams > 0) { gmx_bug("internal error"); return NULL; } root = _gmx_selelem_create(SEL_EXPRESSION); child = root; set_method(sc, child, method); /* Initialize the evaluation of keyword matching if values are provided */ if (nargs > 0) { gmx_ana_selmethod_t *kwmethod; switch (method->type) { case INT_VALUE: kwmethod = &sm_keyword_int; break; case STR_VALUE: kwmethod = &sm_keyword_str; break; default: _gmx_selparser_error("unknown type for keyword selection"); goto on_error; } root = _gmx_selelem_create(SEL_EXPRESSION); set_method(sc, root, kwmethod); params = param = _gmx_selexpr_create_param(NULL); param->nval = 1; param->value = _gmx_selexpr_create_value_expr(child); param = _gmx_selexpr_create_param(NULL); param->nval = nargs; param->value = args; params->next = param; if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams, root->u.expr.method->param, root)) { _gmx_selparser_error("error in keyword selection initialization"); goto on_error; } } rc = set_refpos_type(sc->pcc, child, rpost); if (rc != 0) { goto on_error; } return root; /* On error, free all memory and return NULL. */ on_error: _gmx_selelem_free(root); return NULL; }
/*! * \param scanner Scanner data structure. * \param[in] sel The selection element that evaluates the selection. * \returns The created root selection element. * * This function handles the creation of root (\ref SEL_ROOT) \c t_selelem * objects for selections. */ t_selelem * _gmx_sel_init_selection(gmx_sel_lexer_t *scanner, t_selelem *sel) { gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner); t_selelem *root; int rc; if (sel->v.type != POS_VALUE) { gmx_bug("each selection must evaluate to a position"); /* FIXME: Better handling of this error */ return NULL; } root = _gmx_selelem_create(SEL_ROOT); root->child = sel; /* Update the flags */ rc = _gmx_selelem_update_flags(root); if (rc != 0) { _gmx_selelem_free(root); return NULL; } /* Print out some information if the parser is interactive */ if (_gmx_sel_is_lexer_interactive(scanner)) { /* TODO: It would be nice to print the whole selection here */ fprintf(stderr, "Selection parsed\n"); } return root; }
/*! \brief * Parses an expression value for a parameter that takes a variable number of values. * * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \param root Selection element to which child expressions are added. * \returns TRUE if the values were parsed successfully, FALSE otherwise. */ static gmx_bool parse_values_varnum_expr(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param, t_selelem *root) { t_selexpr_value *value; t_selelem *child; t_selelem *expr; if (nval != 1 || !values->bExpr) { gmx_bug("internal error"); return FALSE; } value = values; child = add_child(root, param, value->u.expr); value->u.expr = NULL; if (!child) { return FALSE; } /* Process single-valued expressions */ /* TODO: We should also handle SEL_SINGLEVAL expressions here */ if (child->v.type == POS_VALUE || child->v.type == GROUP_VALUE) { /* Set the value storage */ _gmx_selvalue_setstore(&child->v, param->val.u.ptr); param->val.nr = 1; if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; return TRUE; } if (!(child->flags & SEL_VARNUMVAL)) { _gmx_selparser_error("invalid expression value for parameter '%s'", param->name); return FALSE; } child->flags |= SEL_ALLOCVAL; param->val.nr = -1; *param->nvalptr = param->val.nr; /* Rest of the initialization is done during compilation in * init_method(). */ return TRUE; }
/*! * \param[in] sel Selection to free. */ void _gmx_selelem_free_values(t_selelem *sel) { int i, n; _gmx_selelem_mempool_release(sel); if ((sel->flags & SEL_ALLOCDATA) && sel->v.u.ptr) { /* The number of position/group structures is constant, so the * backup of using sel->v.nr should work for them. * For strings, we report an error if we don't know the allocation * size here. */ n = (sel->v.nalloc > 0) ? sel->v.nalloc : sel->v.nr; switch (sel->v.type) { case STR_VALUE: if (sel->v.nalloc == 0) { gmx_bug("SEL_ALLOCDATA should only be set for allocated STR_VALUE values"); break; } for (i = 0; i < n; ++i) { sfree(sel->v.u.s[i]); } break; case POS_VALUE: for (i = 0; i < n; ++i) { gmx_ana_pos_deinit(&sel->v.u.p[i]); } break; case GROUP_VALUE: for (i = 0; i < n; ++i) { gmx_ana_index_deinit(&sel->v.u.g[i]); } break; default: /* No special handling for other types */ break; } } if (sel->flags & SEL_ALLOCVAL) { sfree(sel->v.u.ptr); } _gmx_selvalue_setstore(&sel->v, NULL); if (sel->type == SEL_SUBEXPRREF && sel->u.param) { sel->u.param->val.u.ptr = NULL; } }
/*! \brief * Parses the values for an enumeration parameter. * * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \returns TRUE if the values were parsed successfully, FALSE otherwise. */ static gmx_bool parse_values_enum(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param) { int i, len, match; if (nval != 1) { _gmx_selparser_error("a single value is required for parameter '%s'", param->name); return FALSE; } if (values->type != STR_VALUE || param->val.type != STR_VALUE) { gmx_bug("internal error"); return FALSE; } if (values->bExpr) { _gmx_selparser_error("expression value for enumerated parameter '%s' not supported", param->name); return FALSE; } len = strlen(values->u.s); i = 1; match = 0; while (param->val.u.s[i] != NULL) { if (strncmp(values->u.s, param->val.u.s[i], len) == 0) { /* Check if there is a duplicate match */ if (match > 0) { _gmx_selparser_error("ambiguous value for parameter '%s'", param->name); return FALSE; } match = i; } ++i; } if (match == 0) { _gmx_selparser_error("invalid value for parameter '%s'", param->name); return FALSE; } param->val.u.s[0] = param->val.u.s[match]; return TRUE; }
/* \brief * Converts a floating point value to integer. * * \param[in] n Number of values in the \p val->u array. * \param[in,out] val Value to convert. * \param[in] cmpt Comparison operator type. * \param[in] bRight TRUE if \p val appears on the right hand size of * \p cmpt. * \returns 0 on success, EINVAL on error. * * The values are rounded such that the same comparison operator can be used. */ static int convert_real_int(int n, t_compare_value *val, e_comparison_t cmpt, gmx_bool bRight) { int i; int *iv; if (!bRight) { cmpt = reverse_comparison_type(cmpt); } snew(iv, n); /* Round according to the comparison type */ for (i = 0; i < n; ++i) { switch (cmpt) { case CMP_LESS: case CMP_GEQ: iv[i] = (int)ceil(val->r[i]); break; case CMP_GTR: case CMP_LEQ: iv[i] = (int)floor(val->r[i]); break; case CMP_EQUAL: case CMP_NEQ: fprintf(stderr, "comparing equality an integer expression and a real value\n"); sfree(iv); return EINVAL; case CMP_INVALID: /* Should not be reached */ gmx_bug("internal error"); sfree(iv); return EINVAL; } } /* Free the previous value if one is present. */ sfree(val->i); val->i = iv; val->flags &= ~CMP_REALVAL; val->flags |= CMP_ALLOCINT; return 0; }
/*! * \param[in,out] sel Selection element to set the type for. * \param[in] vtype Value type for the selection element. * \returns 0 on success, EINVAL if the value type is invalid. * * If the new type is \ref GROUP_VALUE or \ref POS_VALUE, the * \ref SEL_ALLOCDATA flag is also set. * * This function should only be called at most once for each element, * preferably right after calling _gmx_selelem_create(). */ int _gmx_selelem_set_vtype(t_selelem *sel, e_selvalue_t vtype) { if (sel->type == SEL_BOOLEAN && vtype != GROUP_VALUE) { gmx_bug("internal error"); return EINVAL; } if (sel->v.type != NO_VALUE && vtype != sel->v.type) { gmx_call("_gmx_selelem_set_vtype() called more than once"); return EINVAL; } sel->v.type = vtype; if (vtype == GROUP_VALUE || vtype == POS_VALUE) { sel->flags |= SEL_ALLOCDATA; } return 0; }
/*! \brief * Parses the values for a gmx_boolean parameter. * * \param[in] name Name by which the parameter was given. * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \returns TRUE if the values were parsed successfully, FALSE otherwise. */ static gmx_bool parse_values_gmx_bool(const char *name, int nval, t_selexpr_value *values, gmx_ana_selparam_t *param) { gmx_bool bSetNo; int len; if (param->val.type != NO_VALUE) { gmx_bug("internal error"); return FALSE; } if (nval > 1 || (values && values->type != INT_VALUE)) { _gmx_selparser_error("gmx_boolean parameter '%s' takes only a yes/no/on/off/0/1 value", param->name); return FALSE; } bSetNo = FALSE; /* Check if the parameter name is given with a 'no' prefix */ len = strlen(name); if (len > 2 && name[0] == 'n' && name[1] == 'o' && strncmp(name+2, param->name, len-2) == 0) { bSetNo = TRUE; } if (bSetNo && nval > 0) { _gmx_selparser_error("gmx_boolean parameter 'no%s' should not have a value", param->name); return FALSE; } if (values && values->u.i.i1 == 0) { bSetNo = TRUE; } *param->val.u.b = bSetNo ? FALSE : TRUE; return TRUE; }
/*! \brief * Initializes the storage of an expression value. * * \param[in,out] sel Selection element that evaluates the value. * \param[in] param Parameter to receive the value. * \param[in] i The value of \p sel evaluates the value \p i for * \p param. * * Initializes the data pointer of \p sel such that the result is stored * as the value \p i of \p param. * This function is used internally by parse_values_std(). */ static gmx_bool set_expr_value_store(t_selelem *sel, gmx_ana_selparam_t *param, int i) { if (sel->v.type != GROUP_VALUE && !(sel->flags & SEL_SINGLEVAL)) { _gmx_selparser_error("invalid expression value for parameter '%s'", param->name); return FALSE; } switch (sel->v.type) { case INT_VALUE: sel->v.u.i = ¶m->val.u.i[i]; break; case REAL_VALUE: sel->v.u.r = ¶m->val.u.r[i]; break; case STR_VALUE: sel->v.u.s = ¶m->val.u.s[i]; break; case POS_VALUE: sel->v.u.p = ¶m->val.u.p[i]; break; case GROUP_VALUE: sel->v.u.g = ¶m->val.u.g[i]; break; default: /* Error */ gmx_bug("internal error"); return FALSE; } sel->v.nr = 1; sel->v.nalloc = -1; return TRUE; }
/*! \brief * Replaces constant expressions with their values. * * \param[in,out] values First element in the value list to process. */ static void convert_const_values(t_selexpr_value *values) { t_selexpr_value *val; val = values; while (val) { if (val->bExpr && val->u.expr->v.type != GROUP_VALUE && val->u.expr->type == SEL_CONST) { t_selelem *expr = val->u.expr; val->bExpr = FALSE; switch (expr->v.type) { case INT_VALUE: val->u.i.i1 = val->u.i.i2 = expr->v.u.i[0]; break; case REAL_VALUE: val->u.r.r1 = val->u.r.r2 = expr->v.u.r[0]; break; case STR_VALUE: val->u.s = expr->v.u.s[0]; break; case POS_VALUE: copy_rvec(expr->v.u.p->x[0], val->u.x); break; default: gmx_bug("internal error"); break; } _gmx_selelem_free(expr); } val = val->next; } }
/*! * \param[in,out] t Output block. * \param[in] top Topology structure * (only used if \p type is \ref INDEX_RES or \ref INDEX_MOL, can be NULL * otherwise). * \param[in] g Index group * (can be NULL if \p type is \ref INDEX_UNKNOWN). * \param[in] type Type of partitioning to make. * \param[in] bComplete * If true, the index group is expanded to include any residue/molecule * (depending on \p type) that is partially contained in the group. * If \p type is not INDEX_RES or INDEX_MOL, this has no effect. * * \p m should have been initialized somehow (calloc() is enough) unless * \p type is INDEX_UNKNOWN. * \p g should be sorted. */ void gmx_ana_index_make_block(t_blocka *t, t_topology *top, gmx_ana_index_t *g, e_index_t type, bool bComplete) { int i, j, ai; int id, cur; if (type == INDEX_UNKNOWN) { t->nr = 1; snew(t->index, 2); t->nalloc_index = 2; t->index[0] = 0; t->index[1] = 0; t->nra = 0; t->a = NULL; t->nalloc_a = 0; return; } /* bComplete only does something for INDEX_RES or INDEX_MOL, so turn it * off otherwise. */ if (type != INDEX_RES && type != INDEX_MOL) { bComplete = false; } /* Allocate memory for the atom array and fill it unless we are using * completion. */ if (bComplete) { t->nra = 0; /* We may allocate some extra memory here because we don't know in * advance how much will be needed. */ if (t->nalloc_a < top->atoms.nr) { srenew(t->a, top->atoms.nr); t->nalloc_a = top->atoms.nr; } } else { t->nra = g->isize; if (t->nalloc_a < g->isize) { srenew(t->a, g->isize); t->nalloc_a = g->isize; } std::memcpy(t->a, g->index, g->isize*sizeof(*(t->a))); } /* Allocate memory for the block index. We don't know in advance * how much will be needed, so we allocate some extra and free it in the * end. */ if (t->nalloc_index < g->isize + 1) { srenew(t->index, g->isize + 1); t->nalloc_index = g->isize + 1; } /* Clear counters */ t->nr = 0; j = 0; /* j is used by residue completion for the first atom not stored */ id = cur = -1; for (i = 0; i < g->isize; ++i) { ai = g->index[i]; /* Find the ID number of the atom/residue/molecule corresponding to * atom ai. */ switch (type) { case INDEX_ATOM: id = ai; break; case INDEX_RES: id = top->atoms.atom[ai].resind; break; case INDEX_MOL: while (ai >= top->mols.index[id+1]) { id++; } break; case INDEX_UNKNOWN: /* Should not occur */ case INDEX_ALL: id = 0; break; } /* If this is the first atom in a new block, initialize the block. */ if (id != cur) { if (bComplete) { /* For completion, we first set the start of the block. */ t->index[t->nr++] = t->nra; /* And then we find all the atoms that should be included. */ switch (type) { case INDEX_RES: while (top->atoms.atom[j].resind != id) { ++j; } while (j < top->atoms.nr && top->atoms.atom[j].resind == id) { t->a[t->nra++] = j; ++j; } break; case INDEX_MOL: for (j = top->mols.index[id]; j < top->mols.index[id+1]; ++j) { t->a[t->nra++] = j; } break; default: /* Should not be reached */ gmx_bug("internal error"); break; } } else { /* If not using completion, simply store the start of the block. */ t->index[t->nr++] = i; } cur = id; } } /* Set the end of the last block */ t->index[t->nr] = t->nra; /* Free any unnecessary memory */ srenew(t->index, t->nr+1); t->nalloc_index = t->nr+1; if (bComplete) { srenew(t->a, t->nra); t->nalloc_a = t->nra; } }
/*! * \param[in] top Not used. * \param[in] npar Not used (should be 5). * \param[in] param Method parameters (should point to \ref smparams_compare). * \param[in] data Should point to a \c t_methoddata_compare. * \returns 0 if the input data is valid, -1 on error. */ static int init_compare(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data) { t_methoddata_compare *d = (t_methoddata_compare *)data; int n1, n2; /* Store the values */ n1 = init_comparison_value(&d->left, ¶m[0]); n2 = init_comparison_value(&d->right, ¶m[3]); if (n1 == 0 || n2 == 0) { gmx_bug("one of the values for comparison missing"); return -1; } /* Store the comparison type */ d->cmpt = comparison_type(d->cmpop); if (d->cmpt == CMP_INVALID) { gmx_bug("invalid comparison type"); return -1; } /* Convert the values to the same type */ if ((d->left.flags & CMP_REALVAL) && !(d->right.flags & CMP_REALVAL)) { if (d->left.flags & d->right.flags & CMP_DYNAMICVAL) { /* Nothing can be done */ } else if (!(d->right.flags & CMP_DYNAMICVAL)) { convert_int_real(n2, &d->right); } else /* d->left is static */ { if (convert_real_int(n1, &d->left, d->cmpt, FALSE)) { return -1; } } } else if (!(d->left.flags & CMP_REALVAL) && (d->right.flags & CMP_REALVAL)) { if (d->left.flags & d->right.flags & CMP_DYNAMICVAL) { /* Reverse the sides to place the integer on the right */ int flags; d->left.r = d->right.r; d->right.r = NULL; d->right.i = d->left.i; d->left.i = NULL; flags = d->left.flags; d->left.flags = d->right.flags; d->right.flags = flags; d->cmpt = reverse_comparison_type(d->cmpt); } else if (!(d->left.flags & CMP_DYNAMICVAL)) { convert_int_real(n1, &d->left); } else /* d->right is static */ { if (convert_real_int(n2, &d->right, d->cmpt, TRUE)) { return -1; } } } return 0; }
/*! \brief * Parses the values for a parameter that takes a constant number of values. * * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \param root Selection element to which child expressions are added. * \returns TRUE if the values were parsed successfully, FALSE otherwise. * * For integer ranges, the sequence of numbers from the first to second value * is stored, each as a separate value. */ static gmx_bool parse_values_std(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param, t_selelem *root) { t_selexpr_value *value; t_selelem *child; int i, j; gmx_bool bDynamic; /* Handle atom-valued parameters */ if (param->flags & SPAR_ATOMVAL) { if (nval > 1) { _gmx_selparser_error("extra values for parameter '%s'", param->name); return FALSE; } value = values; if (value->bExpr) { child = add_child(root, param, value->u.expr); value->u.expr = NULL; if (!child) { return FALSE; } child->flags |= SEL_ALLOCVAL; if (child->v.type != GROUP_VALUE && (child->flags & SEL_ATOMVAL)) { /* Rest of the initialization is done during compilation in * init_method(). */ /* TODO: Positions are not correctly handled */ param->val.nr = -1; if (param->nvalptr) { *param->nvalptr = -1; } return TRUE; } param->flags &= ~SPAR_ATOMVAL; param->val.nr = 1; if (param->nvalptr) { *param->nvalptr = 1; } param->nvalptr = NULL; if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE || param->val.type == STR_VALUE) { _gmx_selvalue_reserve(¶m->val, 1); } return set_expr_value_store(child, param, 0); } /* If we reach here, proceed with normal parameter handling */ param->val.nr = 1; if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE || param->val.type == STR_VALUE) { _gmx_selvalue_reserve(¶m->val, 1); } param->flags &= ~SPAR_ATOMVAL; param->flags &= ~SPAR_DYNAMIC; } value = values; i = 0; bDynamic = FALSE; while (value && i < param->val.nr) { if (value->type != param->val.type) { _gmx_selparser_error("incorrect value for parameter '%s' skipped", param->name); value = value->next; continue; } if (value->bExpr) { child = add_child(root, param, value->u.expr); /* Clear the expression from the value once it is stored */ value->u.expr = NULL; /* Check that the expression is valid */ if (!child) { return FALSE; } if (!set_expr_value_store(child, param, i)) { return FALSE; } if (child->flags & SEL_DYNAMIC) { bDynamic = TRUE; } } else { /* Value is not an expression */ switch (value->type) { case INT_VALUE: if (value->u.i.i1 <= value->u.i.i2) { for (j = value->u.i.i1; j <= value->u.i.i2 && i < param->val.nr; ++j) { param->val.u.i[i++] = j; } if (j != value->u.i.i2 + 1) { _gmx_selparser_error("extra values for parameter '%s' skipped", param->name); } } else { for (j = value->u.i.i1; j >= value->u.i.i2 && i < param->val.nr; --j) { param->val.u.i[i++] = j; } if (j != value->u.i.i2 - 1) { _gmx_selparser_error("extra values for parameter '%s' skipped", param->name); } } --i; break; case REAL_VALUE: if (value->u.r.r1 != value->u.r.r2) { _gmx_selparser_error("real ranges not supported for parameter '%s'", param->name); return FALSE; } param->val.u.r[i] = value->u.r.r1; break; case STR_VALUE: param->val.u.s[i] = strdup(value->u.s); break; case POS_VALUE: gmx_ana_pos_init_const(¶m->val.u.p[i], value->u.x); break; case NO_VALUE: case GROUP_VALUE: gmx_bug("internal error"); return FALSE; } } ++i; value = value->next; } if (value) { _gmx_selparser_error("extra values for parameter '%s'", param->name); return FALSE; } if (i < param->val.nr) { _gmx_selparser_error("not enough values for parameter '%s'", param->name); return FALSE; } if (!bDynamic) { param->flags &= ~SPAR_DYNAMIC; } if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; return TRUE; }
/*! \brief * Adds a new subexpression reference to a selection element. * * \param[in,out] root Root element to which the subexpression is added. * \param[in] param Parameter for which this expression is a value. * \param[in] expr Expression to add. * \returns The created child element. * * Creates a new \ref SEL_SUBEXPRREF element and adds it into the child * list of \p root. * If \p expr is already a \ref SEL_SUBEXPRREF, it is used as it is. * \ref SEL_ALLOCVAL is cleared for the returned element. */ static t_selelem * add_child(t_selelem *root, gmx_ana_selparam_t *param, t_selelem *expr) { t_selelem *child; int rc; if (root->type != SEL_EXPRESSION && root->type != SEL_MODIFIER) { gmx_bug("unsupported root element for selection parameter parser"); return NULL; } /* Create a subexpression reference element if necessary */ if (expr->type == SEL_SUBEXPRREF) { child = expr; } else { child = _gmx_selelem_create(SEL_SUBEXPRREF); if (!child) { return NULL; } _gmx_selelem_set_vtype(child, expr->v.type); child->child = expr; } /* Setup the child element */ child->flags &= ~SEL_ALLOCVAL; child->u.param = param; if (child->v.type != param->val.type) { _gmx_selparser_error("invalid expression value for parameter '%s'", param->name); goto on_error; } rc = _gmx_selelem_update_flags(child); if (rc != 0) { goto on_error; } if ((child->flags & SEL_DYNAMIC) && !(param->flags & SPAR_DYNAMIC)) { _gmx_selparser_error("parameter '%s' does not support dynamic values", param->name); goto on_error; } if (!(child->flags & SEL_DYNAMIC)) { param->flags &= ~SPAR_DYNAMIC; } /* Put the child element in the correct place */ place_child(root, child, param); return child; on_error: if (child != expr) { _gmx_selelem_free(child); } return NULL; }
/*! \brief * Parses the values for a parameter that takes a variable number of values. * * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \param root Selection element to which child expressions are added. * \returns TRUE if the values were parsed successfully, FALSE otherwise. * * For integer ranges, the sequence of numbers from the first to second value * is stored, each as a separate value. */ static gmx_bool parse_values_varnum(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param, t_selelem *root) { t_selexpr_value *value; int i, j; param->flags &= ~SPAR_DYNAMIC; /* Update nval if there are integer ranges. */ if (param->val.type == INT_VALUE) { value = values; while (value) { if (value->type == INT_VALUE && !value->bExpr) { nval += abs(value->u.i.i2 - value->u.i.i1); } value = value->next; } } /* Check that the value type is actually implemented */ if (param->val.type != INT_VALUE && param->val.type != REAL_VALUE && param->val.type != STR_VALUE && param->val.type != POS_VALUE) { gmx_bug("internal error"); return FALSE; } /* Reserve appropriate amount of memory */ if (param->val.type == POS_VALUE) { gmx_ana_pos_reserve(param->val.u.p, nval, 0); gmx_ana_pos_set_nr(param->val.u.p, nval); gmx_ana_indexmap_init(¶m->val.u.p->m, NULL, NULL, INDEX_UNKNOWN); } else { _gmx_selvalue_reserve(¶m->val, nval); } value = values; i = 0; while (value) { if (value->bExpr) { _gmx_selparser_error("expressions not supported within value lists"); return FALSE; } if (value->type != param->val.type) { gmx_bug("internal error"); return FALSE; } switch (param->val.type) { case INT_VALUE: if (value->u.i.i1 <= value->u.i.i2) { for (j = value->u.i.i1; j <= value->u.i.i2; ++j) { param->val.u.i[i++] = j; } } else { for (j = value->u.i.i1; j >= value->u.i.i2; --j) { param->val.u.i[i++] = j; } } break; case REAL_VALUE: if (value->u.r.r1 != value->u.r.r2) { _gmx_selparser_error("real ranges not supported for parameter '%s'", param->name); return FALSE; } param->val.u.r[i++] = value->u.r.r1; break; case STR_VALUE: param->val.u.s[i++] = strdup(value->u.s); break; case POS_VALUE: copy_rvec(value->u.x, param->val.u.p->x[i++]); break; default: /* Should not be reached */ gmx_bug("internal error"); return FALSE; } value = value->next; } param->val.nr = i; if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; /* Create a dummy child element to store the string values. * This element is responsible for freeing the values, but carries no * other function. */ if (param->val.type == STR_VALUE) { t_selelem *child; child = _gmx_selelem_create(SEL_CONST); _gmx_selelem_set_vtype(child, STR_VALUE); child->name = param->name; child->flags &= ~SEL_ALLOCVAL; child->flags |= SEL_FLAGSSET | SEL_VARNUMVAL | SEL_ALLOCDATA; child->v.nr = param->val.nr; _gmx_selvalue_setstore(&child->v, param->val.u.s); /* Because the child is not group-valued, the u union is not used * for anything, so we can abuse it by storing the parameter value * as place_child() expects, but this is really ugly... */ child->u.param = param; place_child(root, child, param); } return TRUE; }
/*! \brief * Parses the values for a parameter that takes integer or real ranges. * * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \returns TRUE if the values were parsed successfully, FALSE otherwise. */ static gmx_bool parse_values_range(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param) { t_selexpr_value *value; int *idata; real *rdata; int i, j, n; param->flags &= ~SPAR_DYNAMIC; if (param->val.type != INT_VALUE && param->val.type != REAL_VALUE) { gmx_bug("internal error"); return FALSE; } idata = NULL; rdata = NULL; if (param->val.type == INT_VALUE) { snew(idata, nval*2); } else { snew(rdata, nval*2); } value = values; i = 0; while (value) { if (value->bExpr) { _gmx_selparser_error("expressions not supported within range parameters"); return FALSE; } if (value->type != param->val.type) { gmx_bug("internal error"); return FALSE; } if (param->val.type == INT_VALUE) { /* Make sure the input range is in increasing order */ if (value->u.i.i1 > value->u.i.i2) { int tmp = value->u.i.i1; value->u.i.i1 = value->u.i.i2; value->u.i.i2 = tmp; } /* Check if the new range overlaps or extends the previous one */ if (i > 0 && value->u.i.i1 <= idata[i-1]+1 && value->u.i.i2 >= idata[i-2]-1) { idata[i-2] = min(idata[i-2], value->u.i.i1); idata[i-1] = max(idata[i-1], value->u.i.i2); } else { idata[i++] = value->u.i.i1; idata[i++] = value->u.i.i2; } } else { /* Make sure the input range is in increasing order */ if (value->u.r.r1 > value->u.r.r2) { real tmp = value->u.r.r1; value->u.r.r1 = value->u.r.r2; value->u.r.r2 = tmp; } /* Check if the new range overlaps or extends the previous one */ if (i > 0 && value->u.r.r1 <= rdata[i-1] && value->u.r.r2 >= rdata[i-2]) { rdata[i-2] = min(rdata[i-2], value->u.r.r1); rdata[i-1] = max(rdata[i-1], value->u.r.r2); } else { rdata[i++] = value->u.r.r1; rdata[i++] = value->u.r.r2; } } value = value->next; } n = i/2; /* Sort the ranges and merge consequent ones */ if (param->val.type == INT_VALUE) { qsort(idata, n, 2*sizeof(int), &cmp_int_range); for (i = j = 2; i < 2*n; i += 2) { if (idata[j-1]+1 >= idata[i]) { if (idata[i+1] > idata[j-1]) { idata[j-1] = idata[i+1]; } } else { idata[j] = idata[i]; idata[j+1] = idata[i+1]; j += 2; } } } else { qsort(rdata, n, 2*sizeof(real), &cmp_real_range); for (i = j = 2; i < 2*n; i += 2) { if (rdata[j-1]+1 >= rdata[i]) { if (rdata[i+1] > rdata[j-1]) { rdata[j-1] = rdata[i+1]; } } else { rdata[j] = rdata[i]; rdata[j+1] = rdata[i+1]; j += 2; } } } n = j/2; /* Store the values */ if (param->flags & SPAR_VARNUM) { param->val.nr = n; if (param->val.type == INT_VALUE) { srenew(idata, j); _gmx_selvalue_setstore_alloc(¶m->val, idata, j); } else { srenew(rdata, j); _gmx_selvalue_setstore_alloc(¶m->val, rdata, j); } } else { if (n != param->val.nr) { _gmx_selparser_error("the value of parameter '%s' should consist of exactly one range", param->name); sfree(idata); sfree(rdata); return FALSE; } if (param->val.type == INT_VALUE) { memcpy(param->val.u.i, idata, 2*n*sizeof(int)); sfree(idata); } else { memcpy(param->val.u.r, rdata, 2*n*sizeof(real)); sfree(rdata); } } if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; return TRUE; }