summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/services/quick-answer-service.c40
-rw-r--r--src/services/quick-answers/math-parser.c14
-rw-r--r--src/services/quick-answers/math.c355
-rw-r--r--src/services/quick-answers/math.h67
4 files changed, 424 insertions, 52 deletions
diff --git a/src/services/quick-answer-service.c b/src/services/quick-answer-service.c
index 70d54b6..8071998 100644
--- a/src/services/quick-answer-service.c
+++ b/src/services/quick-answer-service.c
@@ -53,8 +53,9 @@ struct _FoobarQuickAnswerService
GObject parent_instance;
};
-static void foobar_quick_answer_service_class_init( FoobarQuickAnswerServiceClass* klass );
-static void foobar_quick_answer_service_init ( FoobarQuickAnswerService* self );
+static void foobar_quick_answer_service_class_init( FoobarQuickAnswerServiceClass* klass );
+static void foobar_quick_answer_service_init ( FoobarQuickAnswerService* self );
+static FoobarQuickAnswer* foobar_quick_answer_service_query_math( gchar const* query );
G_DEFINE_FINAL_TYPE( FoobarQuickAnswerService, foobar_quick_answer_service, G_TYPE_OBJECT )
@@ -303,6 +304,19 @@ FoobarQuickAnswer* foobar_quick_answer_service_query(
{
(void)self;
+ FoobarQuickAnswer* result = foobar_quick_answer_service_query_math( query );
+ if ( result ) { return result; }
+
+ return NULL;
+}
+
+//
+// Try to parse the query as a mathematical expression and evaluate it.
+//
+FoobarQuickAnswer* foobar_quick_answer_service_query_math( gchar const* query )
+{
+ FoobarQuickAnswer* result = NULL;
+
gsize token_count;
g_autofree FoobarMathToken* tokens = foobar_math_lex( query, strlen( query ), &token_count );
if ( tokens )
@@ -310,16 +324,24 @@ FoobarQuickAnswer* foobar_quick_answer_service_query(
FoobarMathExpression* expr = foobar_math_parse( tokens, token_count );
if ( expr )
{
- g_print( "---\n" );
foobar_math_expression_print( expr, 0 );
+
+ FoobarMathValue val = { 0 };
+ if ( foobar_math_evaluate( expr, &val ) )
+ {
+ g_autofree gchar* value = foobar_math_value_to_string( val );
+ g_autofree gchar* title = g_strdup_printf( "= %s", value );
+ g_autoptr( GIcon ) icon = g_themed_icon_new( "fluent-calculator-symbolic" );
+ result = foobar_quick_answer_new( );
+ foobar_quick_answer_set_value( result, value );
+ foobar_quick_answer_set_title( result, title );
+ foobar_quick_answer_set_icon( result, icon );
+ foobar_math_value_free( val );
+ }
+
foobar_math_expression_free( expr );
}
}
- (void)foobar_quick_answer_new;
- (void)foobar_quick_answer_set_value;
- (void)foobar_quick_answer_set_icon;
- (void)foobar_quick_answer_set_title;
-
- return NULL;
+ return result;
}
diff --git a/src/services/quick-answers/math-parser.c b/src/services/quick-answers/math-parser.c
index bb94a66..7f93552 100644
--- a/src/services/quick-answers/math-parser.c
+++ b/src/services/quick-answers/math-parser.c
@@ -104,7 +104,7 @@ FoobarMathExpression* parser_process(
next_op = parser_peek( ctx );
}
- lhs = foobar_math_expression_operation( parser_operator( op ), lhs, rhs );
+ lhs = foobar_math_expression_new_operation( parser_operator( op ), lhs, rhs );
op = parser_peek( ctx );
op_precedence = parser_operator_precedence( op );
@@ -148,11 +148,11 @@ FoobarMathExpression* parser_process_identifier( Parser* ctx )
FoobarMathToken const* token = parser_pop( ctx );
if ( parser_match_identifier( token, "pi" ) )
{
- return foobar_math_expression_constant( FOOBAR_MATH_CONSTANT_PI );
+ return foobar_math_expression_new_constant( FOOBAR_MATH_CONSTANT_PI );
}
else if ( parser_match_identifier( token, "e" ) )
{
- return foobar_math_expression_constant( FOOBAR_MATH_CONSTANT_E );
+ return foobar_math_expression_new_constant( FOOBAR_MATH_CONSTANT_E );
}
else if ( parser_match_identifier( token, "exp" ) )
{
@@ -219,13 +219,13 @@ FoobarMathExpression* parser_process_identifier( Parser* ctx )
FoobarMathExpression* parser_process_number( Parser* ctx )
{
FoobarMathToken const* token = parser_pop( ctx );
- FoobarMathValue value;
+ FoobarMathValue value = { 0 };
if ( !foobar_math_value_from_string( token->data, token->length, &value ) )
{
return NULL;
}
- return foobar_math_expression_value( value );
+ return foobar_math_expression_new_value( value );
}
FoobarMathExpression* parser_process_function(
@@ -241,7 +241,7 @@ FoobarMathExpression* parser_process_function(
FoobarMathExpression* arg = parser_process_parens( ctx );
if ( !arg ) { return NULL; }
- return foobar_math_expression_function( function, arg );
+ return foobar_math_expression_new_function( function, arg );
}
FoobarMathExpression* parser_process_parens( Parser* ctx )
@@ -268,7 +268,7 @@ FoobarMathExpression* parser_process_sign( Parser* ctx )
if ( token->type == FOOBAR_MATH_TOKEN_MINUS )
{
- return foobar_math_expression_function( FOOBAR_MATH_FUNCTION_NEGATE, result );
+ return foobar_math_expression_new_function( FOOBAR_MATH_FUNCTION_NEGATE, result );
}
else
{
diff --git a/src/services/quick-answers/math.c b/src/services/quick-answers/math.c
index 5ec79c0..4596af2 100644
--- a/src/services/quick-answers/math.c
+++ b/src/services/quick-answers/math.c
@@ -1,4 +1,7 @@
+#define _GNU_SOURCE
+
#include "services/quick-answers/math.h"
+#include <math.h>
struct _FoobarMathExpression
{
@@ -31,7 +34,7 @@ struct _FoobarMathExpression
};
};
-FoobarMathExpression* foobar_math_expression_value( FoobarMathValue value )
+FoobarMathExpression* foobar_math_expression_new_value( FoobarMathValue value )
{
FoobarMathExpression* res = g_new0( FoobarMathExpression, 1 );
res->type = FOOBAR_MATH_EXPRESSION_VALUE;
@@ -39,7 +42,7 @@ FoobarMathExpression* foobar_math_expression_value( FoobarMathValue value )
return res;
}
-FoobarMathExpression* foobar_math_expression_function(
+FoobarMathExpression* foobar_math_expression_new_function(
FoobarMathFunction function,
FoobarMathExpression* input )
{
@@ -50,7 +53,7 @@ FoobarMathExpression* foobar_math_expression_function(
return res;
}
-FoobarMathExpression* foobar_math_expression_constant( FoobarMathConstant constant )
+FoobarMathExpression* foobar_math_expression_new_constant( FoobarMathConstant constant )
{
FoobarMathExpression* res = g_new0( FoobarMathExpression, 1 );
res->type = FOOBAR_MATH_EXPRESSION_CONSTANT;
@@ -58,7 +61,7 @@ FoobarMathExpression* foobar_math_expression_constant( FoobarMathConstant consta
return res;
}
-FoobarMathExpression* foobar_math_expression_operation(
+FoobarMathExpression* foobar_math_expression_new_operation(
FoobarMathOperation operation,
FoobarMathExpression* lhs,
FoobarMathExpression* rhs )
@@ -107,7 +110,7 @@ void foobar_math_expression_print(
{
case FOOBAR_MATH_EXPRESSION_VALUE:
{
- g_autofree gchar* val = foobar_math_value_format( expr->value.v );
+ g_autofree gchar* val = foobar_math_value_to_string( expr->value.v );
g_print( "%s\n", val );
break;
}
@@ -210,6 +213,13 @@ void foobar_math_expression_print(
}
}
+void foobar_math_value_new_int( FoobarMathValue* out_value )
+{
+ out_value->type = FOOBAR_MATH_VALUE_INT;
+ mpz_init( out_value->int_value.v );
+ out_value->int_value.decimal_places = 0;
+}
+
gboolean foobar_math_value_from_string(
gchar const* input,
gsize input_length,
@@ -230,9 +240,8 @@ gboolean foobar_math_value_from_string(
}
}
- out_value->type = FOOBAR_MATH_VALUE_INT;
+ foobar_math_value_new_int( out_value );
out_value->int_value.decimal_places = input_length - 1 - sep_pos;
- mpz_init( out_value->int_value.v );
if ( mpz_init_set_str( out_value->int_value.v, without_sep, 10 ) == -1 )
{
mpz_clear( out_value->int_value.v );
@@ -242,22 +251,173 @@ gboolean foobar_math_value_from_string(
return TRUE;
}
+void foobar_math_value_from_float(
+ long double value,
+ FoobarMathValue* out_value )
+{
+ out_value->type = FOOBAR_MATH_VALUE_FLOAT;
+ out_value->float_value.v = value;
+}
+
+void foobar_math_value_copy(
+ FoobarMathValue value,
+ FoobarMathValue* out_value )
+{
+ *out_value = value;
+
+ switch ( value.type )
+ {
+ case FOOBAR_MATH_VALUE_INT:
+ mpz_init_set( out_value->int_value.v, value.int_value.v );
+ break;
+ case FOOBAR_MATH_VALUE_FLOAT:
+ break;
+ default:
+ g_warn_if_reached( );
+ break;
+ }
+}
+
void foobar_math_value_free( FoobarMathValue value )
{
- if ( value.type == FOOBAR_MATH_VALUE_INT )
+ switch ( value.type )
{
- mpz_clear( value.int_value.v );
+ case FOOBAR_MATH_VALUE_INT:
+ mpz_clear( value.int_value.v );
+ break;
+ case FOOBAR_MATH_VALUE_FLOAT:
+ break;
+ default:
+ g_warn_if_reached( );
+ break;
}
}
-gboolean foobar_math_evaluate(
- FoobarMathExpression const* expression,
- FoobarMathValue* out_value )
+void foobar_math_value_negate( FoobarMathValue* value )
+{
+ switch ( value->type )
+ {
+ case FOOBAR_MATH_VALUE_INT:
+ mpz_neg( value->int_value.v, value->int_value.v );
+ break;
+ case FOOBAR_MATH_VALUE_FLOAT:
+ value->float_value.v = -value->float_value.v;
+ break;
+ default:
+ g_warn_if_reached( );
+ break;
+ }
+}
+
+gboolean foobar_math_value_add(
+ FoobarMathValue lhs,
+ FoobarMathValue rhs,
+ FoobarMathValue* out_value )
+{
+ if ( foobar_math_value_is_simple_int( lhs ) && foobar_math_value_is_simple_int( rhs ) )
+ {
+ foobar_math_value_new_int( out_value );
+ mpz_add( out_value->int_value.v, lhs.int_value.v, rhs.int_value.v );
+ return TRUE;
+ }
+
+ long double lhs_float = foobar_math_value_to_float( lhs );
+ long double rhs_float = foobar_math_value_to_float( rhs );
+ foobar_math_value_from_float( lhs_float + rhs_float, out_value );
+ return TRUE;
+}
+
+gboolean foobar_math_value_sub(
+ FoobarMathValue lhs,
+ FoobarMathValue rhs,
+ FoobarMathValue* out_value )
+{
+ if ( foobar_math_value_is_simple_int( lhs ) && foobar_math_value_is_simple_int( rhs ) )
+ {
+ foobar_math_value_new_int( out_value );
+ mpz_sub( out_value->int_value.v, lhs.int_value.v, rhs.int_value.v );
+ return TRUE;
+ }
+
+ long double lhs_float = foobar_math_value_to_float( lhs );
+ long double rhs_float = foobar_math_value_to_float( rhs );
+ foobar_math_value_from_float( lhs_float - rhs_float, out_value );
+ return TRUE;
+}
+
+gboolean foobar_math_value_mul(
+ FoobarMathValue lhs,
+ FoobarMathValue rhs,
+ FoobarMathValue* out_value )
+{
+ if ( foobar_math_value_is_simple_int( lhs ) && foobar_math_value_is_simple_int( rhs ) )
+ {
+ foobar_math_value_new_int( out_value );
+ mpz_mul( out_value->int_value.v, lhs.int_value.v, rhs.int_value.v );
+ return TRUE;
+ }
+
+ long double lhs_float = foobar_math_value_to_float( lhs );
+ long double rhs_float = foobar_math_value_to_float( rhs );
+ foobar_math_value_from_float( lhs_float * rhs_float, out_value );
+ return TRUE;
+}
+
+gboolean foobar_math_value_div(
+ FoobarMathValue lhs,
+ FoobarMathValue rhs,
+ FoobarMathValue* out_value )
+{
+ if ( foobar_math_value_is_simple_int( lhs ) && foobar_math_value_is_simple_int( rhs ) )
+ {
+ if ( mpz_sgn( rhs.int_value.v ) == 0 ) { return FALSE; }
+
+ foobar_math_value_new_int( out_value );
+ mpz_t rem;
+ mpz_init( rem );
+ mpz_divmod( out_value->int_value.v, rem, lhs.int_value.v, rhs.int_value.v );
+ gboolean result_is_int = mpz_sgn( rem ) == 0;
+ mpz_clear( rem );
+
+ if ( result_is_int ) { return TRUE; }
+ foobar_math_value_free( *out_value );
+ }
+
+ long double lhs_float = foobar_math_value_to_float( lhs );
+ long double rhs_float = foobar_math_value_to_float( rhs );
+ long double result = lhs_float / rhs_float;
+ if ( isnanl( result ) || isinfl( result ) ) { return FALSE; }
+ foobar_math_value_from_float( result, out_value );
+ return TRUE;
+}
+
+gboolean foobar_math_value_pow(
+ FoobarMathValue lhs,
+ FoobarMathValue rhs,
+ FoobarMathValue* out_value )
{
- return FALSE;
+ if ( foobar_math_value_is_simple_int( lhs ) && foobar_math_value_is_simple_int( rhs ) &&
+ mpz_fits_ulong_p( rhs.int_value.v ) )
+ {
+ foobar_math_value_new_int( out_value );
+ mpz_pow_ui( out_value->int_value.v, lhs.int_value.v, mpz_get_ui( rhs.int_value.v ) );
+ return TRUE;
+ }
+
+ long double lhs_float = foobar_math_value_to_float( lhs );
+ long double rhs_float = foobar_math_value_to_float( rhs );
+ long double result = powl( lhs_float, rhs_float );
+ if ( isnanl( result ) || isinfl( result ) ) { return FALSE; }
+ foobar_math_value_from_float( result, out_value );
+ return TRUE;
+}
+
+gboolean foobar_math_value_is_simple_int( FoobarMathValue value )
+{
+ return value.type == FOOBAR_MATH_VALUE_INT && value.int_value.decimal_places == 0;
}
-gchar* foobar_math_value_format( FoobarMathValue value )
+gchar* foobar_math_value_to_string( FoobarMathValue value )
{
switch ( value.type )
{
@@ -296,3 +456,170 @@ gchar* foobar_math_value_format( FoobarMathValue value )
return NULL;
}
}
+
+long double foobar_math_value_to_float( FoobarMathValue value )
+{
+ switch ( value.type )
+ {
+ case FOOBAR_MATH_VALUE_INT:
+ {
+ mpz_t exp, integer, frac;
+ mpz_inits( exp, integer, frac, NULL );
+ mpz_ui_pow_ui( exp, 10, value.int_value.decimal_places );
+ mpz_mod( frac, value.int_value.v, exp );
+ mpz_div( integer, value.int_value.v, exp );
+ mpf_t f_exp, f_frac;
+ mpf_inits( f_exp, f_frac, NULL );
+ mpf_set_z( f_exp, exp );
+ mpf_set_z( f_frac, frac );
+ mpf_div( f_frac, f_frac, f_exp );
+ long double result = mpz_get_d( integer ) + mpf_get_d( f_frac );
+ mpf_clears( f_exp, f_frac, NULL );
+ mpz_clears( exp, integer, frac, NULL );
+ return result;
+ }
+ case FOOBAR_MATH_VALUE_FLOAT:
+ return value.float_value.v;
+ default:
+ g_warn_if_reached( );
+ return 0;
+ }
+}
+
+gboolean foobar_math_evaluate(
+ FoobarMathExpression const* expr,
+ FoobarMathValue* out_value )
+{
+ switch ( expr->type )
+ {
+ case FOOBAR_MATH_EXPRESSION_VALUE:
+ foobar_math_value_copy( expr->value.v, out_value );
+ return TRUE;
+ case FOOBAR_MATH_EXPRESSION_CONSTANT:
+ switch ( expr->constant.c )
+ {
+ case FOOBAR_MATH_CONSTANT_PI:
+ foobar_math_value_from_float( M_PIl, out_value );
+ return TRUE;
+ case FOOBAR_MATH_CONSTANT_E:
+ foobar_math_value_from_float( M_El, out_value );
+ return TRUE;
+ default:
+ g_warn_if_reached( );
+ return FALSE;
+ }
+ case FOOBAR_MATH_EXPRESSION_FUNCTION:
+ {
+ FoobarMathValue input = { 0 };
+ if ( !foobar_math_evaluate( expr->function.input, &input ) ) { return FALSE; }
+
+ if ( expr->function.f == FOOBAR_MATH_FUNCTION_NEGATE )
+ {
+ *out_value = input;
+ foobar_math_value_negate( out_value );
+ return TRUE;
+ }
+
+ long double input_float = foobar_math_value_to_float( input );
+ foobar_math_value_free( input );
+
+ long double result = NAN;
+ switch ( expr->function.f )
+ {
+ case FOOBAR_MATH_FUNCTION_SIN:
+ result = sinl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_COS:
+ result = cosl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_SEC:
+ result = 1.L / cosl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_CSC:
+ result = 1.L / sinl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_TAN:
+ result = tanl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_COT:
+ result = 1.L / tanl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_ARCSIN:
+ result = asinl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_ARCCOS:
+ result = acosl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_ARCSEC:
+ result = acosl( 1.L / input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_ARCCSC:
+ result = asinl( 1.L / input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_ARCTAN:
+ result = atanl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_ARCCOT:
+ result = atanl( 1.L / input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_EXP:
+ result = expl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_SQRT:
+ result = sqrtl( input_float );
+ break;
+ case FOOBAR_MATH_FUNCTION_NEGATE:
+ default:
+ g_warn_if_reached( );
+ return FALSE;
+ }
+
+ if ( isnanl( result ) || isinfl( result ) ) { return FALSE; }
+
+ foobar_math_value_from_float( result, out_value );
+ return TRUE;
+ }
+ case FOOBAR_MATH_EXPRESSION_OPERATION:
+ {
+ FoobarMathValue lhs = { 0 };
+ if ( !foobar_math_evaluate( expr->operation.lhs, &lhs ) ) { return FALSE; }
+ FoobarMathValue rhs = { 0 };
+ if ( !foobar_math_evaluate( expr->operation.rhs, &rhs ) )
+ {
+ foobar_math_value_free( lhs );
+ return FALSE;
+ }
+
+ gboolean success = FALSE;
+ switch ( expr->operation.o )
+ {
+ case FOOBAR_MATH_OPERATION_ADD:
+ success = foobar_math_value_add( lhs, rhs, out_value );
+ break;
+ case FOOBAR_MATH_OPERATION_SUB:
+ success = foobar_math_value_sub( lhs, rhs, out_value );
+ break;
+ case FOOBAR_MATH_OPERATION_MUL:
+ success = foobar_math_value_mul( lhs, rhs, out_value );
+ break;
+ case FOOBAR_MATH_OPERATION_DIV:
+ success = foobar_math_value_div( lhs, rhs, out_value );
+ break;
+ case FOOBAR_MATH_OPERATION_POW:
+ success = foobar_math_value_pow( lhs, rhs, out_value );
+ break;
+ default:
+ g_warn_if_reached( );
+ break;
+ }
+
+ foobar_math_value_free( lhs );
+ foobar_math_value_free( rhs );
+
+ return success;
+ }
+ default:
+ g_warn_if_reached( );
+ return FALSE;
+ }
+}
diff --git a/src/services/quick-answers/math.h b/src/services/quick-answers/math.h
index 77c587a..6bc07fe 100644
--- a/src/services/quick-answers/math.h
+++ b/src/services/quick-answers/math.h
@@ -98,27 +98,50 @@ typedef enum
typedef struct _FoobarMathExpression FoobarMathExpression;
-FoobarMathToken* foobar_math_lex ( gchar const* input,
- gsize input_length,
- gsize* out_count );
-FoobarMathExpression* foobar_math_parse ( FoobarMathToken const* tokens,
- gsize tokens_count );
-FoobarMathExpression* foobar_math_expression_value ( FoobarMathValue value );
-FoobarMathExpression* foobar_math_expression_function ( FoobarMathFunction function,
- FoobarMathExpression* input );
-FoobarMathExpression* foobar_math_expression_constant ( FoobarMathConstant constant );
-FoobarMathExpression* foobar_math_expression_operation( FoobarMathOperation operation,
- FoobarMathExpression* lhs,
- FoobarMathExpression* rhs );
-void foobar_math_expression_print ( FoobarMathExpression const* expr,
- gint indentation );
-void foobar_math_expression_free ( FoobarMathExpression* expression );
-gboolean foobar_math_value_from_string ( gchar const* input,
- gsize input_length,
- FoobarMathValue* out_value );
-void foobar_math_value_free ( FoobarMathValue value );
-gchar* foobar_math_value_format ( FoobarMathValue value );
-gboolean foobar_math_evaluate ( FoobarMathExpression const* expression,
- FoobarMathValue* out_value );
+FoobarMathToken* foobar_math_lex ( gchar const* input,
+ gsize input_length,
+ gsize* out_count );
+FoobarMathExpression* foobar_math_parse ( FoobarMathToken const* tokens,
+ gsize tokens_count );
+FoobarMathExpression* foobar_math_expression_new_value ( FoobarMathValue value );
+FoobarMathExpression* foobar_math_expression_new_function ( FoobarMathFunction function,
+ FoobarMathExpression* input );
+FoobarMathExpression* foobar_math_expression_new_constant ( FoobarMathConstant constant );
+FoobarMathExpression* foobar_math_expression_new_operation( FoobarMathOperation operation,
+ FoobarMathExpression* lhs,
+ FoobarMathExpression* rhs );
+void foobar_math_expression_print ( FoobarMathExpression const* expr,
+ gint indentation );
+void foobar_math_expression_free ( FoobarMathExpression* expression );
+void foobar_math_value_new_int ( FoobarMathValue* out_value );
+gboolean foobar_math_value_from_string ( gchar const* input,
+ gsize input_length,
+ FoobarMathValue* out_value );
+void foobar_math_value_from_float ( long double value,
+ FoobarMathValue* out_value );
+void foobar_math_value_copy ( FoobarMathValue value,
+ FoobarMathValue* out_value );
+void foobar_math_value_free ( FoobarMathValue value );
+void foobar_math_value_negate ( FoobarMathValue* value );
+gboolean foobar_math_value_add ( FoobarMathValue lhs,
+ FoobarMathValue rhs,
+ FoobarMathValue* out_value );
+gboolean foobar_math_value_sub ( FoobarMathValue lhs,
+ FoobarMathValue rhs,
+ FoobarMathValue* out_value );
+gboolean foobar_math_value_mul ( FoobarMathValue lhs,
+ FoobarMathValue rhs,
+ FoobarMathValue* out_value );
+gboolean foobar_math_value_div ( FoobarMathValue lhs,
+ FoobarMathValue rhs,
+ FoobarMathValue* out_value );
+gboolean foobar_math_value_pow ( FoobarMathValue lhs,
+ FoobarMathValue rhs,
+ FoobarMathValue* out_value );
+gboolean foobar_math_value_is_simple_int ( FoobarMathValue value );
+gchar* foobar_math_value_to_string ( FoobarMathValue value );
+long double foobar_math_value_to_float ( FoobarMathValue value );
+gboolean foobar_math_evaluate ( FoobarMathExpression const* expr,
+ FoobarMathValue* out_value );
G_END_DECLS