aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
formula.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright (c) 2005-2021 by Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4  * info_at_agrum_dot_org
5  *
6  * This library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /**
23  * @file
24  * @brief Headers files for the gum::FormulaPart and gum::Formula classes.
25  *
26  * @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
27  */
28 #ifndef GUM_MATH_FORMULA_H
29 #define GUM_MATH_FORMULA_H
30 
31 #include <iostream>
32 #include <list>
33 #include <memory>
34 #include <random>
35 #include <sstream>
36 #include <stack>
37 #include <string>
38 #include <vector>
39 
40 #include <agrum/agrum.h>
41 #include <agrum/tools/core/hashTable.h>
42 
43 namespace gum {
44 
45  namespace formula {
46  class Scanner;
47  class Parser;
48  } // namespace formula
49 
50  /**
51  * @class FormulaPart
52  * @headerfile formula.h <agrum/tools/core/math/formula.h>
53  * @brief Represents part of a formula.
54  * @ingroup math_group
55  *
56  * This class is used by the gum::Formula class to store intermediate results
57  * when solving the formula using the Shuntin-yard algorithm.
58  */
59  class FormulaPart {
60  public:
61  /// The tokens constituting a formula.
62  enum token_type
63  {
64  NUMBER,
65  OPERATOR,
66  PARENTHESIS,
67  NIL,
68  FUNCTION,
69  ARG_SEP
70  };
71 
72  /// The functions allowed in a formula.
73  enum token_function
74  {
75  exp,
76  log,
77  ln,
78  pow,
79  sqrt,
80  nil
81  };
82 
83  /// The token_type stored by this gum::FormulaPart.
84  token_type type;
85 
86  /**
87  * @brief The value stored by this gum::FormulaPart
88  *
89  * @warning Only one of these three members will hold the value, given the
90  * type of this gum::FormulaPart.
91  */
92  /// @{
93  double number;
94  char character;
95  token_function function;
96  /// @}
97 
98  // ========================================================================
99  /// @name Constructors and destructor
100  // ========================================================================
101  /// @{
102 
103  /**
104  * @brief Class constructor.
105  */
106  FormulaPart();
107 
108  /**
109  * @brief Constructor for doubles.
110  * @param t The token_type of this gum::FormulaPart.
111  * @param n The value of this gum::FormulaPart.
112  */
113  FormulaPart(token_type t, double n);
114 
115  /**
116  * @brief Constructor for chars.
117  * @param t The token_type of this gum::FormulaPart.
118  * @param c The value of this gum::FormulaPart.
119  */
120  FormulaPart(token_type t, char c);
121 
122  /**
123  * @brief Constructor for functions.
124  * @param t The token_type of this gum::FormulaPart.
125  * @param func The value of this gum::FormulaPart.
126  */
127  FormulaPart(token_type t, token_function func);
128 
129  /**
130  * @brief Copy constructor.
131  * @param source The gum::FormulaPart to copy.
132  */
133  FormulaPart(const FormulaPart& source);
134 
135  /**
136  * @brief Move constructor.
137  * @param source The gum::FormulaPart to move.
138  */
139  FormulaPart(FormulaPart&& source);
140 
141  /**
142  * @brief Class destuctor.
143  */
144  ~FormulaPart();
145 
146  /// @}
147  // ========================================================================
148  /// @name Operators
149  // ========================================================================
150  /// @{
151 
152  /**
153  * @brief Copy operator.
154  * @name source The gum::FormulaPart to copy.
155  * @return Returns this gum::FormulaPart.
156  */
157  FormulaPart& operator=(const FormulaPart& source);
158 
159  /**
160  * @brief Move operator.
161  * @name source The gum::FormulaPart to copy.
162  * @return Returns this gum::FormulaPart.
163  */
164  FormulaPart& operator=(FormulaPart&& source);
165 
166  /// @}
167  // ========================================================================
168  /// @name Getters and setters
169  // ========================================================================
170  /// @{
171 
172  /**
173  * @brief Returns a string representation of this gum::FormulaPart value.
174  * @return Returns a string representation of this gum::FormulaPart value.
175  */
176  std::string str() const;
177 
178  /**
179  * @brief Returns true if this gum::FormulaPart is left associative.
180  * @return Returns true if this gum::FormulaPart is left associative.
181  * @throw OperationNotAllowed Raised if the value stored is not an
182  * operator.
183  */
184  bool isLeftAssociative() const;
185 
186  /**
187  * @brief Returns true if this gum::FormulaPart is right associative.
188  * @return Returns true if this gum::FormulaPart is right associative.
189  * @throw OperationNotAllowed Raised if the value stored is not an
190  * operator.
191  */
192  bool isRightAssociative() const;
193 
194  /**
195  * @brief Returns the precedence priority of the value stored in this
196  * gum::FormulaPart.
197  * @return Returns the precedence priority of the value stored in this
198  * gum::FormulaPart.
199  * @throw OperationNotAllowed Raised if the value stored is not an
200  * operator.
201  */
202  int precedence() const;
203 
204  /**
205  * @brief Returns the number of argument of the function stored in this
206  * gum::FormulaPart.
207  * @return Returns the number of argument of the function stored in this
208  * gum::FormulaPart.
209  * @throw OperationNotAllowed Raised if the value stored is not a function.
210  */
211  size_t argc() const;
212 
213  /**
214  * @brief Returns the evaluation of the vector of gum::FormulaPart as
215  * arguments of the value stored in this gum::FormulaPart.
216  *
217  * @warning Args must be backwards !
218  *
219  * @param args The arguments, in backards, passed to the value stored in
220  * this gum::FormulaPart.
221  * @return Returns the evaluation of the vector of gum::FormulaPart as
222  * arguments of the value stored in this gum::FormulaPart.
223  * @throw OperationNotAllowed Raised if the value stored is neither a
224  * function nor an operator.
225  */
226  FormulaPart eval(const std::vector< FormulaPart >& args) const;
227 
228  /// @}
229 
230  private:
231  /**
232  * @brief Returns the evaluation of the vector of gum::FormulaPart as
233  * arguments of the value stored in this gum::FormulaPart.
234  *
235  * @warning Args must be backwards !
236  *
237  * @param args The arguments, in backards, passed to the value stored in
238  * this gum::FormulaPart.
239  * @return Returns the evaluation of the vector of gum::FormulaPart as
240  * arguments of the value stored in this gum::FormulaPart.
241  * @throw OperationNotAllowed Raised if the value stored is not an
242  * operator.
243  */
244  double _operator_eval_(const std::vector< FormulaPart >& args) const;
245 
246  /**
247  * @brief Returns the evaluation of the vector of gum::FormulaPart as
248  * arguments of the value stored in this gum::FormulaPart.
249  *
250  * @warning Args must be backwards !
251  *
252  * @param args The arguments, in backards, passed to the value stored in
253  * this gum::FormulaPart.
254  * @return Returns the evaluation of the vector of gum::FormulaPart as
255  * arguments of the value stored in this gum::FormulaPart.
256  * @throw OperationNotAllowed Raised if the value stored is not a
257  * function.
258  */
259  double _function_eval_(const std::vector< FormulaPart >& args) const;
260 
261  /**
262  * @brief Returns the number of arguments expected by the operator stored
263  * in this gum::FormulaPart.
264  * @return Returns the number of arguments expected by the operator stored
265  * in this gum::FormulaPart.
266  */
267  size_t _operator_argc_() const;
268 
269  /**
270  * @brief Returns the number of arguments expected by the function stored
271  * in this gum::FormulaPart.
272  * @return Returns the number of arguments expected by the function stored
273  * in this gum::FormulaPart.
274  */
275  size_t _function_argc_() const;
276  };
277 
278  // extern class gum::formula::Parser;
279  /**
280  * @brief Evaluates a string as a algebraic formula.
281  * @ingroup math_group
282  *
283  * Implementation of the Shunting-yard algorithm to convert infix notation to
284  * RPN. The gum::Formula::result() method implements the postfix algorithm to
285  * compute the formula result.
286  *
287  * @warning Checking is only done when evaluating the formula !
288  */
289  class Formula {
290  friend class gum::formula::Parser;
291 
292  public:
293  // ========================================================================
294  /// @name Constructors and destructor
295  // ========================================================================
296  /// @{
297 
298  /**
299  * Constructor
300  */
301  /// @{
302  Formula(short s);
303  Formula(unsigned short us);
304  Formula(int i);
305  Formula(unsigned int ui);
306  Formula(long l);
307  Formula(unsigned long ul);
308  Formula(long long l);
309  Formula(unsigned long long ul);
310  Formula(float f);
311  Formula(double d);
312  /// @}
313 
314  /**
315  * @brief Class constructor.
316  * @param f An algebraic formula.
317  */
318  Formula(const std::string& f);
319 
320  /**
321  * @brief Copy constructor.
322  * @param source The gum::Formula to copy.
323  */
324  Formula(const Formula& source);
325 
326  /**
327  * @brief Move constructor.
328  * @param source The gum::Formula to move.
329  */
330  Formula(Formula&& source);
331 
332  /**
333  * @brief Class destructor.
334  */
335  ~Formula();
336 
337  /// @}
338  // ========================================================================
339  /// @name Operators
340  // ========================================================================
341  /// @{
342 
343  /**
344  * @brief Copy operator.
345  * @param source The gum::Formula to copy.
346  * @return Returns this gum::Formula.
347  */
348  Formula& operator=(const Formula& source);
349 
350  /**
351  * @brief Move operator.
352  * @param source The gum::Formula to move.
353  * @return Returns this gum::Formula.
354  */
355  Formula& operator=(Formula&& source);
356 
357  /**
358  * @brief Allows implicit conversion to doubles.
359  */
360  explicit operator double() const { return result(); }
361 
362  /// @}
363  // ========================================================================
364  /// @name Accessors & modifiers
365  // ========================================================================
366  /// @{
367 
368  /**
369  * @brief Returns the variables used by this gum::Formula.
370  * @return Returns the variables used by this gum::Formula.
371  */
372  HashTable< std::string, double >& variables();
373 
374  /**
375  * @brief Returns the variables used by this gum::Formula.
376  * @return Returns the variables used by this gum::Formula.
377  */
378  const HashTable< std::string, double >& variables() const;
379 
380  /**
381  * @brief Returns the result of this gum::Formula.
382  *
383  * Each call to Formula::result() will reevaluate the formulas result.
384  *
385  * @return Returns the result of this gum::Formula.
386  */
387  double result() const;
388 
389  /**
390  * @brief Returns the formula.
391  */
392  const std::string& formula() const;
393 
394  /**
395  * @brief Returns the formula.
396  */
397  std::string& formula();
398 
399  private:
400  /// @}
401  // ========================================================================
402  /// @name Private accessors & modifiers used by the Formula Parser
403  // ========================================================================
404  /// @{
405 
406  /**
407  * @brief Push a number in the formula.
408  * @param v The number to push.
409  */
410  void _push_number_(const double& v);
411 
412  /**
413  * @brief Push an operator in the formula.
414  * @param o The operator to push.
415  */
416  void _push_operator_(char o);
417 
418  /**
419  * @brief Push a left parenthesis in the formula.
420  */
421  void _push_leftParenthesis_();
422 
423  /**
424  * @brief Push a right parenthesis in the formula.
425  */
426  void _push_rightParenthesis_();
427 
428  /**
429  * @brief Push a function in the formula.
430  * @param func The function to push.
431  */
432  void _push_function_(const std::string& func);
433 
434  /**
435  * @brief Push a variable in the formula.
436  */
437  void _push_variable_(const std::string& var);
438 
439  /**
440  * @brief Use this if you don't know if ident is a function or a variable.
441  */
442  void _push_identifier_(const std::string& ident);
443 
444  /**
445  * @brief Push a comma in the formula.
446  */
447  void _push_comma_();
448 
449  /**
450  * @brief Finalize the formula and prepare it for evaluation.
451  */
452  void _finalize_();
453 
454  /// @}
455 
456  /// The formula to evaluate.
457  std::string _formula_;
458 
459  /// The scanner used by the formula.
460  std::unique_ptr< gum::formula::Scanner > _scanner_;
461 
462  /// The parser used by the formula.
463  std::unique_ptr< gum::formula::Parser > _parser_;
464 
465  /// The last token added to the formula.
466  FormulaPart _last_token_;
467 
468  /// The output stack, will contain one value after evaluation.
469  std::vector< FormulaPart > _output_;
470 
471  /// A stack used during evaluation.
472  std::stack< FormulaPart > _stack_;
473 
474  /// The variables available in this formula.
475  HashTable< std::string, double > _variables_;
476 
477  /**
478  * @brief Initialise the formula scanner and parser.
479  */
480  void _initialise_();
481 
482  /**
483  * @brief Pop the operator in the inner formula's stack.
484  * @param o The operator to pop.
485  * @return Returns true if the operator was popped.
486  */
487  bool _popOperator_(FormulaPart o);
488 
489  /**
490  * @brief Evaluate an operator or function and push its result.
491  * @param item The operator or function to reduce.
492  * @param stack The stack to evaluate.
493  */
494  void _reduceOperatorOrFunction_(FormulaPart item, std::stack< FormulaPart >& stack) const;
495 
496  /**
497  * @brief Push an unary operator.
498  * @param o The unary operator to push.
499  */
500  void _push_unaryOperator_(char o);
501 
502  /**
503  * @brief Push an operator.
504  * @param t The operator to push.
505  */
506  void _push_operator_(FormulaPart t);
507 
508  /**
509  * @brief Returns true if o is an unary operator.
510  * @return Returns true if o is an unary operator.
511  */
512  bool _isUnaryOperator_(char o);
513 
514  /**
515  * @brief Push the gum::FormulaPart in the output vector.
516  * @param t The gum::FormulaPart to push.
517  */
518  void _push_output_(FormulaPart t);
519 
520  /**
521  * @brief Push the gum::FormulaPart in the stack.
522  * @param t The gum::FormulaPart to push.
523  */
524  void _push_stack_(FormulaPart t);
525  };
526 
527  // // ========================================================================
528  // /// @name Arithmetic Operators
529  // // ========================================================================
530  // /// @{
531 
532  Formula operator-(const Formula& a);
533 
534  Formula operator+(const Formula& a, const Formula& b);
535 
536  Formula operator-(const Formula& a, const Formula& b);
537 
538  Formula operator*(const Formula& a, const Formula& b);
539 
540  Formula operator/(const Formula& a, const Formula& b);
541 
542  std::string to_string(const Formula& f);
543 
544  std::ostream& operator<<(std::ostream& os, const Formula& f);
545 
546  // /// @}
547 
548 } /* namespace gum */
549 
550 #ifndef GUM_NO_INLINE
551 # include <agrum/tools/core/math/formula_inl.h>
552 #endif // GUM_NO_INLINE
553 
554 #endif /* GUM_MATH_FORMULA_H */