3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
9 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11 * Copyright (c) 2001-2009, The GROMACS development team,
12 * check out http://www.gromacs.org for more information.
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * If you want to redistribute modifications, please consider that
20 * scientific software is very special. Version control is crucial -
21 * bugs must be traceable. We will be happy to consider code for
22 * inclusion in the official distribution, but derived work must not
23 * be called official GROMACS. Details are found in the README & COPYING
24 * files - if they are missing, get the official version at www.gromacs.org.
26 * To help us fund GROMACS development, we humbly ask that you cite
27 * the papers on the package - you can find them in the top README file.
29 * For more info, check our website at http://www.gromacs.org
33 * Implementation of internal selection method for comparison expressions.
42 #include <gmx_fatal.h>
44 #include <selmethod.h>
46 /** Defines the comparison operator for comparison expressions. */
49 CMP_INVALID
, /**< Indicates an error */
54 CMP_EQUAL
, /**< '==' */
58 /** The operand has a single value. */
59 #define CMP_SINGLEVAL 1
60 /** The operand value is dynamic. */
61 #define CMP_DYNAMICVAL 2
62 /** The value is real. */
64 /** The integer array is allocated. */
65 #define CMP_ALLOCINT 16
66 /** The real array is allocated. */
67 #define CMP_ALLOCREAL 32
70 * Data structure for comparison expression operand values.
74 /** Flags that describe the type of the operand. */
76 /** (Array of) integer value(s). */
78 /** (Array of) real value(s). */
83 * Data structure for comparison expression evaluation.
87 /** Comparison operator as a string. */
89 /** Comparison operator type. */
94 t_compare_value right
;
95 } t_methoddata_compare
;
97 /** Allocates data for comparison expression evaluation. */
99 init_data_compare(int npar
, gmx_ana_selparam_t
*param
);
100 /** Initializes data for comparison expression evaluation. */
102 init_compare(t_topology
*top
, int npar
, gmx_ana_selparam_t
*param
, void *data
);
103 /** Frees the memory allocated for comparison expression evaluation. */
105 free_data_compare(void *data
);
106 /** Evaluates comparison expressions. */
108 evaluate_compare(t_topology
*top
, t_trxframe
*fr
, t_pbc
*pbc
,
109 gmx_ana_index_t
*g
, gmx_ana_selvalue_t
*out
, void *data
);
111 /** Parameters for comparison expression evaluation. */
112 static gmx_ana_selparam_t smparams_compare
[] = {
113 {"int1", {INT_VALUE
, -1, {NULL
}}, NULL
,
114 SPAR_OPTIONAL
| SPAR_DYNAMIC
| SPAR_ATOMVAL
},
115 {"real1", {REAL_VALUE
, -1, {NULL
}}, NULL
,
116 SPAR_OPTIONAL
| SPAR_DYNAMIC
| SPAR_ATOMVAL
},
117 {"op", {STR_VALUE
, 1, {NULL
}}, NULL
, 0},
118 {"int2", {INT_VALUE
, -1, {NULL
}}, NULL
,
119 SPAR_OPTIONAL
| SPAR_DYNAMIC
| SPAR_ATOMVAL
},
120 {"real2", {REAL_VALUE
, -1, {NULL
}}, NULL
,
121 SPAR_OPTIONAL
| SPAR_DYNAMIC
| SPAR_ATOMVAL
},
124 /** \internal Selection method data for comparison expression evaluation. */
125 gmx_ana_selmethod_t sm_compare
= {
126 "cmp", GROUP_VALUE
, SMETH_SINGLEVAL
,
127 asize(smparams_compare
), smparams_compare
,
140 * Returns a \c e_comparison_t value corresponding to an operator.
142 * \param[in] str String to process.
143 * \returns The comparison type corresponding to the first one or two
144 * characters of \p str.
146 * \p str can contain any number of characters; only the first two
148 * If the beginning of \p str does not match any of the recognized types,
149 * \ref CMP_INVALID is returned.
151 static e_comparison_t
152 comparison_type(char *str
)
156 case '<': return (str
[1] == '=') ? CMP_LEQ
: CMP_LESS
;
157 case '>': return (str
[1] == '=') ? CMP_GEQ
: CMP_GTR
;
158 case '=': return (str
[1] == '=') ? CMP_EQUAL
: CMP_INVALID
;
159 case '!': return (str
[1] == '=') ? CMP_NEQ
: CMP_INVALID
;
165 * Returns a string corresponding to a \c e_comparison_t value.
167 * \param[in] cmpt Comparison type to convert.
168 * \returns Pointer to a string that corresponds to \p cmpt.
170 * The return value points to a string constant and should not be \p free'd.
172 * The function returns NULL if \p cmpt is not one of the valid values.
175 comparison_type_str(e_comparison_t cmpt
)
179 case CMP_INVALID
: return "INVALID"; break;
180 case CMP_LESS
: return "<"; break;
181 case CMP_LEQ
: return "<="; break;
182 case CMP_GTR
: return ">"; break;
183 case CMP_GEQ
: return ">="; break;
184 case CMP_EQUAL
: return "=="; break;
185 case CMP_NEQ
: return "!="; break;
191 * \param[in] fp File to receive the output.
192 * \param[in] data Should point to a \c t_methoddata_compare.
195 _gmx_selelem_print_compare_info(FILE *fp
, void *data
)
197 t_methoddata_compare
*d
= (t_methoddata_compare
*)data
;
200 /* Print the left value */
201 if ((d
->left
.flags
& CMP_SINGLEVAL
) && !(d
->left
.flags
& CMP_DYNAMICVAL
))
203 if (d
->left
.flags
& CMP_REALVAL
)
205 fprintf(fp
, "%f ", d
->left
.r
[0]);
209 fprintf(fp
, "%d ", d
->left
.i
[0]);
212 /* Print the operator */
213 if (d
->cmpt
!= CMP_INVALID
)
215 fprintf(fp
, "%s", comparison_type_str(d
->cmpt
));
219 fprintf(fp
, "%s", d
->cmpop
);
221 /* Print the right value */
222 if ((d
->right
.flags
& CMP_SINGLEVAL
) && !(d
->right
.flags
& CMP_DYNAMICVAL
))
224 if (d
->right
.flags
& CMP_REALVAL
)
226 fprintf(fp
, " %f", d
->right
.r
[0]);
230 fprintf(fp
, " %d", d
->right
.i
[0]);
237 * \param[in] npar Not used (should be 5).
238 * \param[in,out] param Method parameters (should point to a copy of
239 * \ref smparams_compare).
240 * \returns Pointer to the allocated data (\c t_methoddata_compare).
242 * Allocates memory for a \c t_methoddata_compare structure.
245 init_data_compare(int npar
, gmx_ana_selparam_t
*param
)
247 t_methoddata_compare
*data
;
250 param
[2].val
.u
.s
= &data
->cmpop
;
255 * Reverses a comparison operator.
257 * \param[in] type Comparison operator to reverse.
258 * \returns The correct comparison operator that equals \p type when the
259 * left and right sides are interchanged.
261 static e_comparison_t
262 reverse_comparison_type(e_comparison_t type
)
266 case CMP_LESS
: return CMP_GTR
;
267 case CMP_LEQ
: return CMP_GEQ
;
268 case CMP_GTR
: return CMP_LESS
;
269 case CMP_GEQ
: return CMP_LEQ
;
276 * Initializes the value storage for comparison expression.
278 * \param[out] val Value structure to initialize.
279 * \param[in] param Parameters to use for initialization.
280 * \returns The number of values provided for the value, 0 on error.
283 init_comparison_value(t_compare_value
*val
, gmx_ana_selparam_t param
[2])
288 if (param
[0].flags
& SPAR_SET
)
290 val
->flags
|= (param
[0].flags
& SPAR_DYNAMIC
) ? CMP_DYNAMICVAL
: 0;
291 val
->flags
|= !(param
[0].flags
& SPAR_ATOMVAL
) ? CMP_SINGLEVAL
: 0;
293 val
->i
= param
[0].val
.u
.i
;
295 else if (param
[1].flags
& SPAR_SET
)
297 val
->flags
|= (param
[1].flags
& SPAR_DYNAMIC
) ? CMP_DYNAMICVAL
: 0;
298 val
->flags
|= !(param
[1].flags
& SPAR_ATOMVAL
) ? CMP_SINGLEVAL
: 0;
299 val
->flags
|= CMP_REALVAL
;
301 val
->r
= param
[1].val
.u
.r
;
313 * Converts an integer value to floating point.
315 * \param[in] n Number of values in the \p val->u array.
316 * \param[in,out] val Value to convert.
319 convert_int_real(int n
, t_compare_value
*val
)
325 for (i
= 0; i
< n
; ++i
)
327 rv
[i
] = (real
)val
->i
[i
];
329 /* Free the previous value if one is present. */
332 val
->flags
|= CMP_REALVAL
| CMP_ALLOCREAL
;
336 * Converts a floating point value to integer.
338 * \param[in] n Number of values in the \p val->u array.
339 * \param[in,out] val Value to convert.
340 * \param[in] cmpt Comparison operator type.
341 * \param[in] bRight TRUE if \p val appears on the right hand size of
343 * \returns 0 on success, EINVAL on error.
345 * The values are rounded such that the same comparison operator can be used.
348 convert_real_int(int n
, t_compare_value
*val
, e_comparison_t cmpt
, bool bRight
)
355 cmpt
= reverse_comparison_type(cmpt
);
358 /* Round according to the comparison type */
359 for (i
= 0; i
< n
; ++i
)
365 iv
[i
] = (int)ceil(val
->r
[i
]);
369 iv
[i
] = (int)floor(val
->r
[i
]);
373 fprintf(stderr
, "comparing equality an integer expression and a real value\n");
376 case CMP_INVALID
: /* Should not be reached */
377 gmx_bug("internal error");
382 /* Free the previous value if one is present. */
385 val
->flags
&= ~CMP_REALVAL
;
386 val
->flags
|= CMP_ALLOCINT
;
391 * \param[in] top Not used.
392 * \param[in] npar Not used (should be 5).
393 * \param[in] param Method parameters (should point to \ref smparams_compare).
394 * \param[in] data Should point to a \c t_methoddata_compare.
395 * \returns 0 if the input data is valid, -1 on error.
398 init_compare(t_topology
*top
, int npar
, gmx_ana_selparam_t
*param
, void *data
)
400 t_methoddata_compare
*d
= (t_methoddata_compare
*)data
;
403 /* Store the values */
404 n1
= init_comparison_value(&d
->left
, ¶m
[0]);
405 n2
= init_comparison_value(&d
->right
, ¶m
[3]);
406 if (n1
== 0 || n2
== 0)
408 gmx_bug("one of the values for comparison missing");
411 /* Store the comparison type */
412 d
->cmpt
= comparison_type(d
->cmpop
);
413 if (d
->cmpt
== CMP_INVALID
)
415 gmx_bug("invalid comparison type");
418 /* Convert the values to the same type */
419 if ((d
->left
.flags
& CMP_REALVAL
) && !(d
->right
.flags
& CMP_REALVAL
))
421 if (d
->left
.flags
& d
->right
.flags
& CMP_DYNAMICVAL
)
423 /* Nothing can be done */
425 else if (!(d
->right
.flags
& CMP_DYNAMICVAL
))
427 convert_int_real(n2
, &d
->right
);
429 else /* d->left is static */
431 if (convert_real_int(n1
, &d
->left
, d
->cmpt
, FALSE
))
437 else if (!(d
->left
.flags
& CMP_REALVAL
) && (d
->right
.flags
& CMP_REALVAL
))
439 if (d
->left
.flags
& d
->right
.flags
& CMP_DYNAMICVAL
)
441 /* Reverse the sides to place the integer on the right */
443 d
->left
.r
= d
->right
.r
;
445 d
->right
.i
= d
->left
.i
;
447 flags
= d
->left
.flags
;
448 d
->left
.flags
= d
->right
.flags
;
449 d
->right
.flags
= flags
;
450 d
->cmpt
= reverse_comparison_type(d
->cmpt
);
452 else if (!(d
->left
.flags
& CMP_DYNAMICVAL
))
454 convert_int_real(n1
, &d
->left
);
456 else /* d->right is static */
458 if (convert_real_int(n2
, &d
->right
, d
->cmpt
, TRUE
))
468 * \param data Data to free (should point to a \c t_methoddata_compare).
470 * Frees the memory allocated for \c t_methoddata_compare.
473 free_data_compare(void *data
)
475 t_methoddata_compare
*d
= (t_methoddata_compare
*)data
;
478 if (d
->left
.flags
& CMP_ALLOCINT
)
482 if (d
->left
.flags
& CMP_ALLOCREAL
)
486 if (d
->right
.flags
& CMP_ALLOCINT
)
490 if (d
->right
.flags
& CMP_ALLOCREAL
)
497 * \param[in] top Not used.
498 * \param[in] fr Not used.
499 * \param[in] pbc Not used.
500 * \param[in] g Evaluation index group.
501 * \param[out] out Output data structure (\p out->u.g is used).
502 * \param[in] data Should point to a \c t_methoddata_compare.
503 * \returns 0 for success.
506 evaluate_compare_int(t_topology
*top
, t_trxframe
*fr
, t_pbc
*pbc
,
507 gmx_ana_index_t
*g
, gmx_ana_selvalue_t
*out
, void *data
)
509 t_methoddata_compare
*d
= (t_methoddata_compare
*)data
;
514 for (i
= i1
= i2
= ig
= 0; i
< g
->isize
; ++i
)
521 case CMP_INVALID
: break;
522 case CMP_LESS
: bAccept
= a
< b
; break;
523 case CMP_LEQ
: bAccept
= a
<= b
; break;
524 case CMP_GTR
: bAccept
= a
> b
; break;
525 case CMP_GEQ
: bAccept
= a
>= b
; break;
526 case CMP_EQUAL
: bAccept
= a
== b
; break;
527 case CMP_NEQ
: bAccept
= a
!= b
; break;
531 out
->u
.g
->index
[ig
++] = g
->index
[i
];
533 if (!(d
->left
.flags
& CMP_SINGLEVAL
))
537 if (!(d
->right
.flags
& CMP_SINGLEVAL
))
542 out
->u
.g
->isize
= ig
;
547 * \param[in] top Not used.
548 * \param[in] fr Not used.
549 * \param[in] pbc Not used.
550 * \param[in] g Evaluation index group.
551 * \param[out] out Output data structure (\p out->u.g is used).
552 * \param[in] data Should point to a \c t_methoddata_compare.
553 * \returns 0 for success.
556 evaluate_compare_real(t_topology
*top
, t_trxframe
*fr
, t_pbc
*pbc
,
557 gmx_ana_index_t
*g
, gmx_ana_selvalue_t
*out
, void *data
)
559 t_methoddata_compare
*d
= (t_methoddata_compare
*)data
;
564 for (i
= i1
= i2
= ig
= 0; i
< g
->isize
; ++i
)
567 b
= (d
->right
.flags
& CMP_REALVAL
) ? d
->right
.r
[i2
] : d
->right
.i
[i2
];
571 case CMP_INVALID
: break;
572 case CMP_LESS
: bAccept
= a
< b
; break;
573 case CMP_LEQ
: bAccept
= a
<= b
; break;
574 case CMP_GTR
: bAccept
= a
> b
; break;
575 case CMP_GEQ
: bAccept
= a
>= b
; break;
576 case CMP_EQUAL
: bAccept
= gmx_within_tol(a
, b
, GMX_REAL_EPS
); break;
577 case CMP_NEQ
: bAccept
= !gmx_within_tol(a
, b
, GMX_REAL_EPS
); break;
581 out
->u
.g
->index
[ig
++] = g
->index
[i
];
583 if (!(d
->left
.flags
& CMP_SINGLEVAL
))
587 if (!(d
->right
.flags
& CMP_SINGLEVAL
))
592 out
->u
.g
->isize
= ig
;
597 * \param[in] top Not used.
598 * \param[in] fr Not used.
599 * \param[in] pbc Not used.
600 * \param[in] g Evaluation index group.
601 * \param[out] out Output data structure (\p out->u.g is used).
602 * \param[in] data Should point to a \c t_methoddata_compare.
603 * \returns 0 for success.
606 evaluate_compare(t_topology
*top
, t_trxframe
*fr
, t_pbc
*pbc
,
607 gmx_ana_index_t
*g
, gmx_ana_selvalue_t
*out
, void *data
)
609 t_methoddata_compare
*d
= (t_methoddata_compare
*)data
;
611 if (!((d
->left
.flags
| d
->right
.flags
) & CMP_REALVAL
))
613 return evaluate_compare_int(top
, fr
, pbc
, g
, out
, data
);
617 return evaluate_compare_real(top
, fr
, pbc
, g
, out
, data
);