summaryrefslogtreecommitdiff
path: root/src/services/quick-answers
diff options
context:
space:
mode:
Diffstat (limited to 'src/services/quick-answers')
-rw-r--r--src/services/quick-answers/math-lexer.c248
-rw-r--r--src/services/quick-answers/math-parser.c414
-rw-r--r--src/services/quick-answers/math.c851
-rw-r--r--src/services/quick-answers/math.h148
-rw-r--r--src/services/quick-answers/math.test.c153
-rw-r--r--src/services/quick-answers/meson.build9
6 files changed, 0 insertions, 1823 deletions
diff --git a/src/services/quick-answers/math-lexer.c b/src/services/quick-answers/math-lexer.c
deleted file mode 100644
index 7fbe877..0000000
--- a/src/services/quick-answers/math-lexer.c
+++ /dev/null
@@ -1,248 +0,0 @@
-#include "services/quick-answers/math.h"
-
-//
-// FoobarMathToken:
-//
-// A token in a mathematical expression. The token contents are left as-is and need not be null-terminated. For example,
-// "sin(pi) - 3" would be represented as:
-// - IDENTIFIER: "sin"
-// - PAREN_OPEN: "("
-// - IDENTIFIER: "pi"
-// - PAREN_CLOSE: ")"
-// - MINUS: "-"
-// - NUMBER: "3"
-//
-
-//
-// Lexer:
-//
-// The lexer context/state which is passed around.
-//
-
-typedef struct _Lexer Lexer;
-
-struct _Lexer
-{
- GArray* result;
- gchar const* input;
- gsize input_length;
- gsize position;
- gsize token_start;
-};
-
-static gboolean char_is_whitespace( char c );
-static gboolean char_is_digit ( char c );
-static gboolean char_is_identifier( char c );
-
-static char lexer_peek ( Lexer const* ctx,
- gsize offset );
-static char lexer_pop ( Lexer* ctx );
-static void lexer_begin_token ( Lexer* ctx );
-static void lexer_commit_token ( Lexer* ctx,
- FoobarMathTokenType type );
-static void lexer_commit_single( Lexer* ctx,
- FoobarMathTokenType type );
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Lexing
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Divide an input string into a list of tokens.
-//
-// On success, this will return a newly allocated array of tokens which should be freed using g_free. The number of
-// elements will be written into out_count.
-//
-// On error, this will return NULL.
-//
-FoobarMathToken* foobar_math_lex(
- gchar const* input,
- gsize input_length,
- gsize* out_count )
-{
- g_autoptr( GArray ) result = g_array_new( FALSE, TRUE, sizeof( FoobarMathToken ) );
-
- Lexer ctx = { 0 };
- ctx.result = result;
- ctx.input = input;
- ctx.input_length = input_length;
-
- while ( ctx.position < input_length )
- {
- if ( char_is_whitespace( lexer_peek( &ctx, 0 ) ) )
- {
- // Skip whitespace.
-
- lexer_pop( &ctx );
- }
- else if ( char_is_digit( lexer_peek( &ctx, 0 ) ) )
- {
- // Process numbers as a whole, don't allow whitespace inbetween.
-
- lexer_begin_token( &ctx );
- while ( char_is_digit( lexer_peek( &ctx, 0 ) ) )
- {
- lexer_pop( &ctx );
- }
-
- if ( lexer_peek( &ctx, 0 ) == '.' && char_is_digit( lexer_peek( &ctx, 1 ) ) )
- {
- lexer_pop( &ctx );
- while ( char_is_digit( lexer_peek( &ctx, 0 ) ) )
- {
- lexer_pop( &ctx );
- }
- }
-
- lexer_commit_token( &ctx, FOOBAR_MATH_TOKEN_NUMBER );
- }
- else if ( char_is_identifier( lexer_peek( &ctx, 0 ) ) )
- {
- // Don't validate identifiers, leave that to the parser.
-
- lexer_begin_token( &ctx );
- while ( char_is_identifier( lexer_peek( &ctx, 0 ) ) )
- {
- lexer_pop( &ctx );
- }
- lexer_commit_token( &ctx, FOOBAR_MATH_TOKEN_IDENTIFIER );
- }
- else if ( lexer_peek( &ctx, 0 ) == '(' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_PAREN_OPEN );
- }
- else if ( lexer_peek( &ctx, 0 ) == ')' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_PAREN_CLOSE );
- }
- else if ( lexer_peek( &ctx, 0 ) == '+' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_PLUS );
- }
- else if ( lexer_peek( &ctx, 0 ) == '-' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_MINUS );
- }
- else if ( lexer_peek( &ctx, 0 ) == '*' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_MUL );
- }
- else if ( lexer_peek( &ctx, 0 ) == '/' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_DIV );
- }
- else if ( lexer_peek( &ctx, 0 ) == '^' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_POW );
- }
- else
- {
- // Unknown character.
-
- return NULL;
- }
- }
-
- return g_array_steal( result, out_count );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Character Classification
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Check if a character is a whitespace symbol (space, tab, carriage return).
-//
-gboolean char_is_whitespace( char c )
-{
- return c == ' ' || c == '\t' || c == '\r';
-}
-
-//
-// Check if a character is a decimal digit.
-//
-gboolean char_is_digit( char c )
-{
- return c >= '0' && c <= '9';
-}
-
-//
-// Check if a character belongs to an identifier (like "sin", "pi", etc.).
-//
-gboolean char_is_identifier( char c )
-{
- // Identifiers are all lowercase ASCII.
-
- return c >= 'a' && c <= 'z';
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// State Helpers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Peek at the current character without consuming it.
-//
-// If this is the end of the input string, return 0.
-//
-char lexer_peek(
- Lexer const* ctx,
- gsize offset )
-{
- gsize position = ctx->position + offset;
- if ( position >= ctx->input_length ) { return 0; }
-
- return ctx->input[position];
-}
-
-//
-// Consume the current character, moving forward to the next one.
-//
-// If this is the end of the input string, return 0.
-//
-char lexer_pop( Lexer* ctx )
-{
- if ( ctx->position >= ctx->input_length ) { return 0; }
-
- return ctx->input[ctx->position++];
-}
-
-//
-// Start recording a token by remembering the current position.
-//
-// The current character will be the first one in the token.
-//
-void lexer_begin_token( Lexer* ctx )
-{
- ctx->token_start = ctx->position;
-}
-
-//
-// Stop recording a token, saving the string between the call to lexer_begin_token as the specified type.
-//
-// The current character will not be a part of the token.
-//
-void lexer_commit_token(
- Lexer* ctx,
- FoobarMathTokenType type )
-{
- FoobarMathToken token = { 0 };
- token.data = &ctx->input[ctx->token_start];
- token.length = ctx->position - ctx->token_start;
- token.type = type;
- g_array_append_val( ctx->result, token );
-}
-
-//
-// Consume the current character and record it as a token.
-//
-// This is equivalent to calling lexer_begin_token, lexer_pop, and lexer_commit_token.
-//
-void lexer_commit_single(
- Lexer* ctx,
- FoobarMathTokenType type )
-{
- lexer_begin_token( ctx );
- lexer_pop( ctx );
- lexer_commit_token( ctx, type );
-}
diff --git a/src/services/quick-answers/math-parser.c b/src/services/quick-answers/math-parser.c
deleted file mode 100644
index 17d976d..0000000
--- a/src/services/quick-answers/math-parser.c
+++ /dev/null
@@ -1,414 +0,0 @@
-#include "services/quick-answers/math.h"
-
-//
-// Parser:
-//
-// The parser context/state which is passed around.
-//
-
-typedef struct _Parser Parser;
-
-struct _Parser
-{
- FoobarMathToken const* tokens;
- gsize tokens_count;
- gsize position;
-};
-
-//
-// InfixPrecedence:
-//
-// Value indicating how strongly infix operators should bind. The higher the value, the stronger they should bind.
-//
-
-typedef enum
-{
- INFIX_PRECEDENCE_INVALID = -1,
- INFIX_PRECEDENCE_NONE = 0,
- INFIX_PRECEDENCE_ADDITIVE = 1,
- INFIX_PRECEDENCE_MULTIPLICATIVE = 2,
- INFIX_PRECEDENCE_EXPONENTS = 3,
-} InfixPrecedence;
-
-static FoobarMathExpression* parser_process ( Parser* ctx,
- FoobarMathExpression* lhs,
- InfixPrecedence min_precedence );
-static FoobarMathExpression* parser_process_single ( Parser* ctx,
- gboolean allow_sign );
-static FoobarMathExpression* parser_process_identifier ( Parser* ctx );
-static FoobarMathExpression* parser_process_number ( Parser* ctx );
-static FoobarMathExpression* parser_process_function ( Parser* ctx,
- FoobarMathFunction function );
-static FoobarMathExpression* parser_process_parens ( Parser* ctx );
-static FoobarMathExpression* parser_process_sign ( Parser* ctx );
-static FoobarMathToken const* parser_peek ( Parser const* ctx );
-static FoobarMathToken const* parser_pop ( Parser* ctx );
-static gboolean parser_match_identifier ( FoobarMathToken const* token,
- gchar const* identifier );
-static InfixPrecedence parser_operator_precedence( FoobarMathToken const* token );
-static FoobarMathOperation parser_operator ( FoobarMathToken const* token );
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Parsing
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Transform a sequence of tokens into a mathematical expression.
-//
-// On success, this will return a newly allocated expression which should be freed using foobar_math_expression_free.
-//
-// On error, this will return NULL.
-//
-FoobarMathExpression* foobar_math_parse(
- FoobarMathToken const* tokens,
- gsize tokens_count )
-{
- Parser ctx = { 0 };
- ctx.tokens = tokens;
- ctx.tokens_count = tokens_count;
-
- FoobarMathExpression* lhs = parser_process_single( &ctx, TRUE );
- if ( !lhs ) { return NULL; }
-
- FoobarMathExpression* expr = parser_process( &ctx, lhs, INFIX_PRECEDENCE_NONE );
- if ( !expr ) { return NULL; }
-
- if ( parser_peek( &ctx ) )
- {
- foobar_math_expression_free( expr );
- return NULL;
- }
-
- return expr;
-}
-
-//
-// Process tokens until the end of the current precedence scope (indicated by min_precedence), starting at an operator.
-//
-FoobarMathExpression* parser_process(
- Parser* ctx,
- FoobarMathExpression* lhs,
- InfixPrecedence min_precedence )
-{
- FoobarMathToken const* op = parser_peek( ctx );
- InfixPrecedence op_precedence = parser_operator_precedence( op );
- while ( op_precedence >= min_precedence )
- {
- parser_pop( ctx );
-
- FoobarMathExpression* rhs = parser_process_single( ctx, FALSE );
- if ( !rhs )
- {
- foobar_math_expression_free( lhs );
- return NULL;
- }
-
- FoobarMathToken const* next_op = parser_peek( ctx );
- while ( parser_operator_precedence( next_op ) > op_precedence )
- {
- rhs = parser_process( ctx, rhs, op_precedence + 1 );
- if ( !rhs )
- {
- foobar_math_expression_free( lhs );
- return NULL;
- }
-
- next_op = parser_peek( ctx );
- }
-
- lhs = foobar_math_expression_new_operation( parser_operator( op ), lhs, rhs );
-
- op = parser_peek( ctx );
- op_precedence = parser_operator_precedence( op );
- }
-
- return lhs;
-}
-
-//
-// Process a single value which may be either a constant, function, number, expression enclosed in parenthesis or
-// negation.
-//
-// If an unexpected token is encountered (i.e. an operator), this will return FALSE.
-//
-FoobarMathExpression* parser_process_single(
- Parser* ctx,
- gboolean allow_sign )
-{
- FoobarMathToken const* token = parser_peek( ctx );
- if ( !token ) { return NULL; }
-
- switch ( token->type )
- {
- case FOOBAR_MATH_TOKEN_IDENTIFIER:
- return parser_process_identifier( ctx );
- case FOOBAR_MATH_TOKEN_NUMBER:
- return parser_process_number( ctx );
- case FOOBAR_MATH_TOKEN_PAREN_OPEN:
- return parser_process_parens( ctx );
- case FOOBAR_MATH_TOKEN_MINUS:
- case FOOBAR_MATH_TOKEN_PLUS:
- if ( !allow_sign ) { return NULL; }
- return parser_process_sign( ctx );
- case FOOBAR_MATH_TOKEN_PAREN_CLOSE:
- case FOOBAR_MATH_TOKEN_MUL:
- case FOOBAR_MATH_TOKEN_DIV:
- case FOOBAR_MATH_TOKEN_POW:
- return NULL;
- default:
- g_warn_if_reached( );
- return NULL;
- }
-}
-
-//
-// Parse an identifier.
-//
-// If the identifier is a function, this will expected parenthesis and an expression afterwards.
-//
-FoobarMathExpression* parser_process_identifier( Parser* ctx )
-{
- FoobarMathToken const* token = parser_pop( ctx );
- if ( parser_match_identifier( token, "pi" ) )
- {
- return foobar_math_expression_new_constant( FOOBAR_MATH_CONSTANT_PI );
- }
- else if ( parser_match_identifier( token, "e" ) )
- {
- return foobar_math_expression_new_constant( FOOBAR_MATH_CONSTANT_E );
- }
- else if ( parser_match_identifier( token, "exp" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_EXP );
- }
- else if ( parser_match_identifier( token, "sqrt" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_SQRT );
- }
- else if ( parser_match_identifier( token, "sin" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_SIN );
- }
- else if ( parser_match_identifier( token, "cos" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_COS );
- }
- else if ( parser_match_identifier( token, "sec" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_SEC );
- }
- else if ( parser_match_identifier( token, "csc" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_CSC );
- }
- else if ( parser_match_identifier( token, "tan" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_TAN );
- }
- else if ( parser_match_identifier( token, "cot" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_COT );
- }
- else if ( parser_match_identifier( token, "arcsin" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCSIN );
- }
- else if ( parser_match_identifier( token, "arccos" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCCOS );
- }
- else if ( parser_match_identifier( token, "arcsec" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCSEC );
- }
- else if ( parser_match_identifier( token, "arccsc" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCCSC );
- }
- else if ( parser_match_identifier( token, "arctan" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCTAN );
- }
- else if ( parser_match_identifier( token, "arccot" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCCOT );
- }
- else
- {
- return NULL;
- }
-}
-
-//
-// Process a numeric value.
-//
-FoobarMathExpression* parser_process_number( Parser* ctx )
-{
- FoobarMathToken const* token = parser_pop( ctx );
- FoobarMathValue value = { 0 };
- if ( !foobar_math_value_from_string( token->data, token->length, &value ) )
- {
- return NULL;
- }
-
- return foobar_math_expression_new_value( value );
-}
-
-//
-// Process a function's parameter which should be enclosed in parenthesis.
-//
-FoobarMathExpression* parser_process_function(
- Parser* ctx,
- FoobarMathFunction function )
-{
- FoobarMathToken const* opening_paren = parser_peek( ctx );
- if ( !opening_paren || opening_paren->type != FOOBAR_MATH_TOKEN_PAREN_OPEN )
- {
- return NULL;
- }
-
- FoobarMathExpression* arg = parser_process_parens( ctx );
- if ( !arg ) { return NULL; }
-
- return foobar_math_expression_new_function( function, arg );
-}
-
-//
-// Process an expression enclosed in parenthesis.
-//
-FoobarMathExpression* parser_process_parens( Parser* ctx )
-{
- parser_pop( ctx );
- FoobarMathExpression* lhs = parser_process_single( ctx, TRUE );
- if ( !lhs ) { return NULL; }
- FoobarMathExpression* result = parser_process( ctx, lhs, INFIX_PRECEDENCE_NONE );
- if ( !result ) { return NULL; }
- FoobarMathToken const* closing_paren = parser_pop( ctx );
- if ( !closing_paren || closing_paren->type != FOOBAR_MATH_TOKEN_PAREN_CLOSE )
- {
- foobar_math_expression_free( result );
- return NULL;
- }
- return result;
-}
-
-//
-// Process a sign (- or +) which may be used for negation of an expression.
-//
-FoobarMathExpression* parser_process_sign( Parser* ctx )
-{
- FoobarMathToken const* token = parser_pop( ctx );
- FoobarMathExpression* result = parser_process_single( ctx, FALSE );
- if ( !result ) { return NULL; }
-
- if ( token->type == FOOBAR_MATH_TOKEN_MINUS )
- {
- return foobar_math_expression_new_function( FOOBAR_MATH_FUNCTION_NEGATE, result );
- }
- else
- {
- return result;
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// State Helpers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Peek at the current token without consuming it.
-//
-// If this is the end of the token list, return NULL.
-//
-FoobarMathToken const* parser_peek( Parser const* ctx )
-{
- if ( ctx->position >= ctx->tokens_count ) { return NULL; }
-
- return &ctx->tokens[ctx->position];
-}
-
-//
-// Consume the current token.
-//
-// If this is the end of the token list, return NULL.
-//
-FoobarMathToken const* parser_pop( Parser* ctx )
-{
- if ( ctx->position >= ctx->tokens_count ) { return NULL; }
-
- return &ctx->tokens[ctx->position++];
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helpers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Match a token's string against the given identifier string.
-//
-// This is necessary because the token's content is not a null-terminated string.
-//
-gboolean parser_match_identifier(
- FoobarMathToken const* token,
- gchar const* identifier )
-{
- if ( strlen( identifier ) != token->length ) { return FALSE; }
-
- return !strncmp( token->data, identifier, token->length );
-}
-
-//
-// Get the precedence of an infix operator token or INVALID.
-//
-InfixPrecedence parser_operator_precedence( FoobarMathToken const* token )
-{
- if ( token )
- {
- switch ( token->type )
- {
- case FOOBAR_MATH_TOKEN_PLUS:
- case FOOBAR_MATH_TOKEN_MINUS:
- return INFIX_PRECEDENCE_ADDITIVE;
- case FOOBAR_MATH_TOKEN_MUL:
- case FOOBAR_MATH_TOKEN_DIV:
- return INFIX_PRECEDENCE_MULTIPLICATIVE;
- case FOOBAR_MATH_TOKEN_POW:
- return INFIX_PRECEDENCE_EXPONENTS;
- case FOOBAR_MATH_TOKEN_IDENTIFIER:
- case FOOBAR_MATH_TOKEN_NUMBER:
- case FOOBAR_MATH_TOKEN_PAREN_OPEN:
- case FOOBAR_MATH_TOKEN_PAREN_CLOSE:
- default:
- break;
- }
- }
-
- return INFIX_PRECEDENCE_INVALID;
-}
-
-//
-// Get the operation for an infix operator token. This requires the token to be valid.
-//
-FoobarMathOperation parser_operator( FoobarMathToken const* token )
-{
- switch ( token->type )
- {
- case FOOBAR_MATH_TOKEN_PLUS:
- return FOOBAR_MATH_OPERATION_ADD;
- case FOOBAR_MATH_TOKEN_MINUS:
- return FOOBAR_MATH_OPERATION_SUB;
- case FOOBAR_MATH_TOKEN_MUL:
- return FOOBAR_MATH_OPERATION_MUL;
- case FOOBAR_MATH_TOKEN_DIV:
- return FOOBAR_MATH_OPERATION_DIV;
- case FOOBAR_MATH_TOKEN_POW:
- return FOOBAR_MATH_OPERATION_POW;
- case FOOBAR_MATH_TOKEN_IDENTIFIER:
- case FOOBAR_MATH_TOKEN_NUMBER:
- case FOOBAR_MATH_TOKEN_PAREN_OPEN:
- case FOOBAR_MATH_TOKEN_PAREN_CLOSE:
- default:
- g_warn_if_reached( );
- return 0;
- }
-}
diff --git a/src/services/quick-answers/math.c b/src/services/quick-answers/math.c
deleted file mode 100644
index 69219e2..0000000
--- a/src/services/quick-answers/math.c
+++ /dev/null
@@ -1,851 +0,0 @@
-#define _GNU_SOURCE
-
-#include "services/quick-answers/math.h"
-#include <math.h>
-
-//
-// FoobarMathExpression:
-//
-// A fully parsed mathematical expression which can be evaluated, for example "sin(pi) - 3" would be represented as:
-// - Operation: SUB
-// - Function: SIN
-// - Constant: PI
-// - Value: 3
-//
-
-struct _FoobarMathExpression
-{
- FoobarMathExpressionType type;
-
- union
- {
- struct
- {
- FoobarMathValue v;
- } value;
-
- struct
- {
- FoobarMathFunction f;
- FoobarMathExpression* input;
- } function;
-
- struct
- {
- FoobarMathConstant c;
- } constant;
-
- struct
- {
- FoobarMathOperation o;
- FoobarMathExpression* lhs;
- FoobarMathExpression* rhs;
- } operation;
- };
-};
-
-//
-// FoobarMathValue:
-//
-// Representation of a value. The goal of this memory structure is to store the value as an integer for as long as
-// possible. Because of this, there is a "decimal_places" field, so the actual value of an integer is
-// $v * 10^(-decimal_places)$.
-//
-// If absolutely necessary (for example, because we use a function like sin), the value is converted to a floating-point
-// value.
-//
-// Note that we use GMP to store the integer value to allow for arbitrarily large values. Because of this, the structure
-// should always be freed using foobar_math_value_free.
-//
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Expressions
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Helper to allocate a new FoobarMathExpression representing a value.
-//
-// Ownership of "value" is transferred to the new expression.
-//
-FoobarMathExpression* foobar_math_expression_new_value( FoobarMathValue value )
-{
- FoobarMathExpression* res = g_new0( FoobarMathExpression, 1 );
- res->type = FOOBAR_MATH_EXPRESSION_VALUE;
- res->value.v = value;
- return res;
-}
-
-//
-// Helper to allocate a new FoobarMathExpression representing a function to be evaluated with another expression as its
-// parameter.
-//
-// Ownership of "input" is transferred to the new expression.
-//
-FoobarMathExpression* foobar_math_expression_new_function(
- FoobarMathFunction function,
- FoobarMathExpression* input )
-{
- FoobarMathExpression* res = g_new0( FoobarMathExpression, 1 );
- res->type = FOOBAR_MATH_EXPRESSION_FUNCTION;
- res->function.f = function;
- res->function.input = input;
- return res;
-}
-
-//
-// Helper to allocate a new FoobarMathExpression representing a known constant.
-//
-FoobarMathExpression* foobar_math_expression_new_constant( FoobarMathConstant constant )
-{
- FoobarMathExpression* res = g_new0( FoobarMathExpression, 1 );
- res->type = FOOBAR_MATH_EXPRESSION_CONSTANT;
- res->constant.c = constant;
- return res;
-}
-
-//
-// Helper to allocate a new FoobarMathExpression representing an operation to be evaluated with two expressions as its
-// parameters.
-//
-// Ownership of "lhs" and "rhs" is transferred to the new expression.
-//
-FoobarMathExpression* foobar_math_expression_new_operation(
- FoobarMathOperation operation,
- FoobarMathExpression* lhs,
- FoobarMathExpression* rhs )
-{
- FoobarMathExpression* res = g_new0( FoobarMathExpression, 1 );
- res->type = FOOBAR_MATH_EXPRESSION_OPERATION;
- res->operation.o = operation;
- res->operation.lhs = lhs;
- res->operation.rhs = rhs;
- return res;
-}
-
-//
-// Release the memory associated with a mathematical expression, along with the expressions it owns.
-//
-void foobar_math_expression_free( FoobarMathExpression* expression )
-{
- if ( expression )
- {
- switch ( expression->type )
- {
- case FOOBAR_MATH_EXPRESSION_VALUE:
- foobar_math_value_free( expression->value.v );
- break;
- case FOOBAR_MATH_EXPRESSION_FUNCTION:
- foobar_math_expression_free( expression->function.input );
- break;
- case FOOBAR_MATH_EXPRESSION_CONSTANT:
- break;
- case FOOBAR_MATH_EXPRESSION_OPERATION:
- foobar_math_expression_free( expression->operation.lhs );
- foobar_math_expression_free( expression->operation.rhs );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-
- g_free( expression );
- }
-}
-
-//
-// Print a tree representation of a mathematical expression.
-//
-// This is mainly useful for debugging. "indentation" describes the current indentation level for the recursive call
-// (should be 0 initially).
-//
-void foobar_math_expression_print(
- FoobarMathExpression const* expr,
- gint indentation )
-{
- for (gint i = 0; i < indentation; ++i) { g_print("| "); }
- switch ( expr->type )
- {
- case FOOBAR_MATH_EXPRESSION_VALUE:
- {
- g_autofree gchar* val = foobar_math_value_to_string( expr->value.v );
- g_print( "%s\n", val );
- break;
- }
- case FOOBAR_MATH_EXPRESSION_FUNCTION:
- switch ( expr->function.f )
- {
- case FOOBAR_MATH_FUNCTION_NEGATE:
- g_print( "-\n" );
- break;
- case FOOBAR_MATH_FUNCTION_SIN:
- g_print( "sin\n" );
- break;
- case FOOBAR_MATH_FUNCTION_COS:
- g_print( "cos\n" );
- break;
- case FOOBAR_MATH_FUNCTION_SEC:
- g_print( "sec\n" );
- break;
- case FOOBAR_MATH_FUNCTION_CSC:
- g_print( "csc\n" );
- break;
- case FOOBAR_MATH_FUNCTION_TAN:
- g_print( "tan\n" );
- break;
- case FOOBAR_MATH_FUNCTION_COT:
- g_print( "cot\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCSIN:
- g_print( "arcsin\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCCOS:
- g_print( "arccos\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCSEC:
- g_print( "arcsec\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCCSC:
- g_print( "arccsc\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCTAN:
- g_print( "arctan\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCCOT:
- g_print( "arccot\n" );
- break;
- case FOOBAR_MATH_FUNCTION_EXP:
- g_print( "exp\n" );
- break;
- case FOOBAR_MATH_FUNCTION_SQRT:
- g_print( "sqrt\n" );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
- foobar_math_expression_print( expr->function.input, indentation + 1 );
- break;
- case FOOBAR_MATH_EXPRESSION_CONSTANT:
- switch ( expr->constant.c )
- {
- case FOOBAR_MATH_CONSTANT_PI:
- g_print( "pi\n" );
- break;
- case FOOBAR_MATH_CONSTANT_E:
- g_print( "e\n" );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
- break;
- case FOOBAR_MATH_EXPRESSION_OPERATION:
- switch ( expr->operation.o )
- {
- case FOOBAR_MATH_OPERATION_ADD:
- g_print( "+\n" );
- break;
- case FOOBAR_MATH_OPERATION_SUB:
- g_print( "-\n" );
- break;
- case FOOBAR_MATH_OPERATION_MUL:
- g_print( "*\n" );
- break;
- case FOOBAR_MATH_OPERATION_DIV:
- g_print( "/\n" );
- break;
- case FOOBAR_MATH_OPERATION_POW:
- g_print( "^\n" );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
- foobar_math_expression_print( expr->operation.lhs, indentation + 1 );
- foobar_math_expression_print( expr->operation.rhs, indentation + 1 );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-}
-
-//
-// Evaluate an expression, producing a value.
-//
-// On success (indicated by the return value TRUE), the value should be freed using foobar_math_value_free.
-//
-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;
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Values
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Initialize a new integer value to zero.
-//
-// After this, the structure should be freed using foobar_math_value_free.
-//
-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;
-}
-
-//
-// Initialize a new value from its floating point representation.
-//
-// Although technically not necessary, you should still call foobar_math_value_free on the returned value.
-//
-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;
-}
-
-//
-// Parse a value from its string representation. This will always return an integer representation.
-//
-// On success (return value TRUE), the structure should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_from_string(
- gchar const* input,
- gsize input_length,
- FoobarMathValue* out_value )
-{
- g_autofree gchar* without_sep = g_new0( gchar, input_length + 1 );
- gsize sep_pos = input_length - 1;
- gsize j = 0;
- for ( gsize i = 0; i < input_length; ++i )
- {
- if ( input[i] == '.' )
- {
- sep_pos = i;
- }
- else
- {
- without_sep[j++] = input[i];
- }
- }
-
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = input_length - 1 - sep_pos;
- if ( mpz_init_set_str( out_value->int_value.v, without_sep, 10 ) == -1 )
- {
- mpz_clear( out_value->int_value.v );
- return FALSE;
- }
-
- return TRUE;
-}
-
-//
-// Create a copy of a value, allocating necessary memory.
-//
-// After this, the structure should be freed using foobar_math_value_free.
-//
-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;
- }
-}
-
-//
-// Release memory associated with a value.
-//
-// This is necessary because the integer representation is stored using GMP, which can store arbitrarily large values.
-//
-void foobar_math_value_free( FoobarMathValue value )
-{
- switch ( value.type )
- {
- case FOOBAR_MATH_VALUE_INT:
- mpz_clear( value.int_value.v );
- break;
- case FOOBAR_MATH_VALUE_FLOAT:
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Operations
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Operation equivalent to "value *= -1".
-//
-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;
- }
-}
-
-//
-// Operation setting "out_value = lhs + rhs".
-//
-// After this, the result value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_add(
- FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value )
-{
- if ( foobar_math_value_unify_integers( lhs, rhs ) )
- {
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = lhs->int_value.decimal_places;
- 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;
-}
-
-//
-// Operation setting "out_value = lhs - rhs".
-//
-// After this, the result value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_sub(
- FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value )
-{
- if ( foobar_math_value_unify_integers( lhs, rhs ) )
- {
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = lhs->int_value.decimal_places;
- 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;
-}
-
-//
-// Operation setting "out_value = lhs * rhs".
-//
-// After this, the result value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_mul(
- FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value )
-{
- if ( lhs->type == FOOBAR_MATH_VALUE_INT && rhs->type == FOOBAR_MATH_VALUE_INT )
- {
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = lhs->int_value.decimal_places + rhs->int_value.decimal_places;
- 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;
-}
-
-//
-// Operation setting "out_value = lhs / rhs".
-//
-// After this, the result value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_div(
- FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value )
-{
- if ( lhs->type == FOOBAR_MATH_VALUE_INT && rhs->type == FOOBAR_MATH_VALUE_INT )
- {
- if ( mpz_sgn( rhs->int_value.v ) == 0 ) { return FALSE; }
-
- if ( rhs->int_value.decimal_places > lhs->int_value.decimal_places )
- {
- foobar_math_value_unify_integers( lhs, rhs );
- }
-
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = lhs->int_value.decimal_places - rhs->int_value.decimal_places;
- 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;
-}
-
-//
-// Operation setting "out_value = lhs ^ rhs".
-//
-// After this, the result value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_pow(
- FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value )
-{
- if ( lhs->type == FOOBAR_MATH_VALUE_INT &&
- rhs->type == FOOBAR_MATH_VALUE_INT &&
- rhs->int_value.decimal_places == 0 &&
- mpz_fits_ulong_p( rhs->int_value.v ) )
- {
- unsigned long exp = mpz_get_ui( rhs->int_value.v );
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = lhs->int_value.decimal_places * exp;
- mpz_pow_ui( out_value->int_value.v, lhs->int_value.v, exp );
- 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;
-}
-
-//
-// Ensure that two integer values have the same number of decimal places.
-//
-// This may change the in-memory-representation of a value, but not its actual value. It only increases the precision of
-// one value
-//
-// If either a or b is not stored as an integer, this will return FALSE.
-//
-gboolean foobar_math_value_unify_integers(
- FoobarMathValue* a,
- FoobarMathValue* b )
-{
- if ( a->type != FOOBAR_MATH_VALUE_INT || b->type != FOOBAR_MATH_VALUE_INT ) { return FALSE; }
-
- // Extend the less precise representation so both numbers have the same number of decimal places.
-
- gsize decimal_places = MAX( a->int_value.decimal_places, b->int_value.decimal_places );
-
- mpz_t exp;
- mpz_init( exp );
-
- if ( decimal_places > a->int_value.decimal_places )
- {
- mpz_ui_pow_ui( exp, 10, decimal_places - a->int_value.decimal_places );
- mpz_mul( a->int_value.v, a->int_value.v, exp );
- }
-
- if ( decimal_places > b->int_value.decimal_places )
- {
- mpz_ui_pow_ui( exp, 10, decimal_places - b->int_value.decimal_places );
- mpz_mul( b->int_value.v, b->int_value.v, exp );
- }
-
- mpz_clear( exp );
-
- a->int_value.decimal_places = b->int_value.decimal_places = decimal_places;
-
- return TRUE;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Conversions
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Format a value as a string.
-//
-// The result is a null-terminated string which should be freed using g_free.
-//
-gchar* foobar_math_value_to_string( FoobarMathValue value )
-{
- switch ( value.type )
- {
- case FOOBAR_MATH_VALUE_INT:
- {
- gsize cap = mpz_sizeinbase( value.int_value.v, 10 );
- cap = MAX( cap, value.int_value.decimal_places + 1 ); // pad with zeroes if necessary
- gchar* result = g_new0( gchar, cap + 3 ); // length + null terminator + sign + separator
- mpz_get_str( result, 10, value.int_value.v );
- gsize len = strlen( result ); // actual size may be 1 char less (+ len now also includes sign)
- gboolean has_sign = result[0] == '-';
- if ( has_sign )
- {
- // add the sign back later, makes everything else easier
- result += 1;
- len -= 1;
- }
- // pad with leading zeroes if necessary
- gsize padded_len = MAX( len, value.int_value.decimal_places + 1 );
- memmove( &result[padded_len - len], result, len + 1 );
- memset( result, '0', padded_len - len );
- len = padded_len;
- gboolean trailing_zeros = TRUE;
- gsize decimal_places = value.int_value.decimal_places;
- gsize actual_decimal_places = decimal_places;
- for ( gsize i = 0; i < decimal_places; ++i )
- {
- if ( trailing_zeros && result[len - 1 - i] == '0' )
- {
- result[len - 1 - i] = '\0';
- actual_decimal_places -= 1;
- }
- else
- {
- trailing_zeros = FALSE;
- }
- }
- if ( actual_decimal_places > 0 )
- {
- memmove( &result[len + 1 - decimal_places], &result[len - decimal_places], decimal_places + 1 );
- result[len - decimal_places] = '.';
- }
- if ( has_sign )
- {
- result -= 1;
- len += 1;
- }
- return result;
- }
- case FOOBAR_MATH_VALUE_FLOAT:
- {
- gchar* result = g_strdup_printf( "%.30Lf", value.float_value.v );
-
- // trim trailing zeros
- size_t len = strlen( result );
- for ( gsize i = 0; i < len; ++i )
- {
- if ( result[len - 1 - i] == '.' )
- {
- result[len - 1 - i] = '\0';
- break;
- }
- else if ( result[len - 1 - i] == '0' )
- {
- result[len - 1 - i] = '\0';
- }
- else
- {
- break;
- }
- }
-
- return result;
- }
- default:
- g_warn_if_reached( );
- return NULL;
- }
-}
-
-//
-// Convert a value to its floating-point representation.
-//
-// This may be a lossy conversion and should only be done if absolutely necessary.
-//
-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;
- }
-}
diff --git a/src/services/quick-answers/math.h b/src/services/quick-answers/math.h
deleted file mode 100644
index fafa73d..0000000
--- a/src/services/quick-answers/math.h
+++ /dev/null
@@ -1,148 +0,0 @@
-#pragma once
-
-#include <gio/gio.h>
-#include <gmp.h>
-
-G_BEGIN_DECLS
-
-typedef enum
-{
- FOOBAR_MATH_VALUE_INT,
- FOOBAR_MATH_VALUE_FLOAT,
-} FoobarMathValueType;
-
-typedef struct _FoobarMathValue FoobarMathValue;
-
-struct _FoobarMathValue
-{
- FoobarMathValueType type;
-
- union
- {
- struct
- {
- mpz_t v;
- gsize decimal_places;
- } int_value;
-
- struct
- {
- long double v;
- } float_value;
- };
-};
-
-typedef enum
-{
- FOOBAR_MATH_TOKEN_IDENTIFIER,
- FOOBAR_MATH_TOKEN_NUMBER,
- FOOBAR_MATH_TOKEN_PAREN_OPEN,
- FOOBAR_MATH_TOKEN_PAREN_CLOSE,
- FOOBAR_MATH_TOKEN_PLUS,
- FOOBAR_MATH_TOKEN_MINUS,
- FOOBAR_MATH_TOKEN_MUL,
- FOOBAR_MATH_TOKEN_DIV,
- FOOBAR_MATH_TOKEN_POW,
-} FoobarMathTokenType;
-
-typedef struct _FoobarMathToken FoobarMathToken;
-
-struct _FoobarMathToken
-{
- FoobarMathTokenType type;
- gchar const* data;
- gsize length;
-};
-
-typedef enum
-{
- FOOBAR_MATH_EXPRESSION_VALUE,
- FOOBAR_MATH_EXPRESSION_FUNCTION,
- FOOBAR_MATH_EXPRESSION_CONSTANT,
- FOOBAR_MATH_EXPRESSION_OPERATION,
-} FoobarMathExpressionType;
-
-typedef enum
-{
- FOOBAR_MATH_FUNCTION_NEGATE,
- FOOBAR_MATH_FUNCTION_SIN,
- FOOBAR_MATH_FUNCTION_COS,
- FOOBAR_MATH_FUNCTION_SEC,
- FOOBAR_MATH_FUNCTION_CSC,
- FOOBAR_MATH_FUNCTION_TAN,
- FOOBAR_MATH_FUNCTION_COT,
- FOOBAR_MATH_FUNCTION_ARCSIN,
- FOOBAR_MATH_FUNCTION_ARCCOS,
- FOOBAR_MATH_FUNCTION_ARCSEC,
- FOOBAR_MATH_FUNCTION_ARCCSC,
- FOOBAR_MATH_FUNCTION_ARCTAN,
- FOOBAR_MATH_FUNCTION_ARCCOT,
- FOOBAR_MATH_FUNCTION_EXP,
- FOOBAR_MATH_FUNCTION_SQRT,
-} FoobarMathFunction;
-
-typedef enum
-{
- FOOBAR_MATH_CONSTANT_PI,
- FOOBAR_MATH_CONSTANT_E,
-} FoobarMathConstant;
-
-typedef enum
-{
- FOOBAR_MATH_OPERATION_ADD,
- FOOBAR_MATH_OPERATION_SUB,
- FOOBAR_MATH_OPERATION_MUL,
- FOOBAR_MATH_OPERATION_DIV,
- FOOBAR_MATH_OPERATION_POW,
-} FoobarMathOperation;
-
-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_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 );
-gboolean foobar_math_evaluate ( FoobarMathExpression const* expr,
- FoobarMathValue* out_value );
-void foobar_math_expression_free ( FoobarMathExpression* expression );
-void foobar_math_value_new_int ( FoobarMathValue* out_value );
-void foobar_math_value_from_float ( long double value,
- FoobarMathValue* out_value );
-gboolean foobar_math_value_from_string ( gchar const* input,
- gsize input_length,
- 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_unify_integers ( FoobarMathValue* a,
- FoobarMathValue* b );
-gchar* foobar_math_value_to_string ( FoobarMathValue value );
-long double foobar_math_value_to_float ( FoobarMathValue value );
-
-G_END_DECLS
diff --git a/src/services/quick-answers/math.test.c b/src/services/quick-answers/math.test.c
deleted file mode 100644
index 416b576..0000000
--- a/src/services/quick-answers/math.test.c
+++ /dev/null
@@ -1,153 +0,0 @@
-#include "services/quick-answers/math.h"
-#include <mutest.h>
-
-#define SERIALIZATION_TEST( value, identifier ) \
- static void serialization_value_##identifier##_spec( void ) \
- { \
- FoobarMathValue val; \
- \
- gchar const input[] = #value; \
- mutest_expect( \
- "parse result", \
- mutest_bool_value( foobar_math_value_from_string( input, sizeof(input) - 1, &val) ), \
- mutest_to_be_true, \
- NULL ); \
- g_autofree gchar* output = foobar_math_value_to_string( val ); \
- mutest_expect( \
- "string representation", \
- mutest_string_value( output ), \
- mutest_to_be, \
- input, \
- NULL ); \
- \
- foobar_math_value_free( val ); \
- }
-
-SERIALIZATION_TEST( 0, 0 )
-SERIALIZATION_TEST( 1, 1 )
-SERIALIZATION_TEST( -1, neg_1 )
-SERIALIZATION_TEST( 1.2, 1_2 )
-SERIALIZATION_TEST( -1.2, neg_1_2 )
-SERIALIZATION_TEST( 1.30002, 1_30002 )
-SERIALIZATION_TEST( -1.30002, neg_1_30002 )
-SERIALIZATION_TEST( 0.2, 0_2 )
-SERIALIZATION_TEST( -0.2, neg_0_2 )
-SERIALIZATION_TEST( 0.00002, 0_00002 )
-SERIALIZATION_TEST( -0.00002, neg_0_00002 )
-SERIALIZATION_TEST( 123, 123 )
-SERIALIZATION_TEST( 123.456, 123_456 )
-SERIALIZATION_TEST( -123, neg_123 )
-SERIALIZATION_TEST( -123.456, neg_123_456 )
-
-#undef SERIALIZATION_TEST
-
-static void serialization_suite( void )
-{
- mutest_it( "value 0", serialization_value_0_spec );
- mutest_it( "value 1", serialization_value_1_spec );
- mutest_it( "value -1", serialization_value_neg_1_spec );
- mutest_it( "value 1.2", serialization_value_1_2_spec );
- mutest_it( "value -1.2", serialization_value_neg_1_2_spec );
- mutest_it( "value 1.30002", serialization_value_1_30002_spec );
- mutest_it( "value -1.30002", serialization_value_neg_1_30002_spec );
- mutest_it( "value 0.2", serialization_value_0_2_spec );
- mutest_it( "value -0.2", serialization_value_neg_0_2_spec );
- mutest_it( "value 0.00002", serialization_value_0_00002_spec );
- mutest_it( "value -0.00002", serialization_value_neg_0_00002_spec );
- mutest_it( "value 123", serialization_value_123_spec );
- mutest_it( "value 123.456", serialization_value_123_456_spec );
- mutest_it( "value -123", serialization_value_neg_123_spec );
- mutest_it( "value -123.456", serialization_value_neg_123_456_spec );
-}
-#define OPERATION_TEST( op, value_a, value_b, value_res, identifier ) \
- static void operation_##identifier##_spec( void ) \
- { \
- FoobarMathValue a, b, res; \
- \
- gchar const input_a[] = #value_a; \
- gchar const input_b[] = #value_b; \
- mutest_expect( \
- "input a parsed", \
- mutest_bool_value( foobar_math_value_from_string( input_a, sizeof(input_a) - 1, &a) ), \
- mutest_to_be_true, \
- NULL ); \
- mutest_expect( \
- "input b parsed", \
- mutest_bool_value( foobar_math_value_from_string( input_b, sizeof(input_b) - 1, &b) ), \
- mutest_to_be_true, \
- NULL ); \
- foobar_math_value_##op( &a, &b, &res ); \
- g_autofree gchar* output = foobar_math_value_to_string( res ); \
- mutest_expect( \
- "result", \
- mutest_string_value( output ), \
- mutest_to_be, \
- #value_res, \
- NULL ); \
- \
- foobar_math_value_free( a ); \
- foobar_math_value_free( b ); \
- foobar_math_value_free( res ); \
- }
-
-OPERATION_TEST( add, 123, 456, 579, 123_plus_456)
-OPERATION_TEST( add, 123.456, 11, 134.456, 123_456_plus_11)
-OPERATION_TEST( add, 11, 123.456, 134.456, 11_plus_123_456)
-OPERATION_TEST( sub, 123, 456, -333, 123_minus_456)
-OPERATION_TEST( sub, 123.456, 11, 112.456, 123_456_minus_11)
-OPERATION_TEST( sub, 11, 123.456, -112.456, 11_minus_123_456)
-OPERATION_TEST( mul, 123, 3, 369, 123_times_3 )
-OPERATION_TEST( mul, 12, 0.05, 0.6, 12_times_0_05 )
-OPERATION_TEST( mul, 0.05, 12, 0.6, 0_05_times_12 )
-OPERATION_TEST( div, 369, 3, 123, 369_div_3 )
-OPERATION_TEST( div, 369, 2, 184.5, 369_div_2 )
-OPERATION_TEST( div, 12, 0.05, 240, 12_div_0_05 )
-OPERATION_TEST( div, 0.08, 4, 0.02, 0_08_div_4 )
-OPERATION_TEST( pow, 2, 3, 8, 2_pow_3 )
-OPERATION_TEST( pow, 4, 0.5, 2, 4_pow_0_5 )
-
-#undef OPERATION_TEST
-
-static void addition_suite( void )
-{
- mutest_it( "calculate 123 + 456", operation_123_plus_456_spec );
- mutest_it( "calculate 123.456 + 11", operation_123_456_plus_11_spec );
- mutest_it( "calculate 11 + 123.456", operation_11_plus_123_456_spec );
-}
-
-static void subtraction_suite( void )
-{
- mutest_it( "calculate 123 - 456", operation_123_minus_456_spec );
- mutest_it( "calculate 123.456 - 11", operation_123_456_minus_11_spec );
- mutest_it( "calculate 11 - 123.456", operation_11_minus_123_456_spec );
-}
-
-static void multiplication_suite( void )
-{
- mutest_it( "calculate 123 * 3", operation_123_times_3_spec );
- mutest_it( "calculate 12 * 0.05", operation_12_times_0_05_spec );
- mutest_it( "calculate 0.05 * 12", operation_0_05_times_12_spec );
-}
-
-static void division_suite( void )
-{
- mutest_it( "calculate 369 / 3", operation_369_div_3_spec );
- mutest_it( "calculate 369 / 2", operation_369_div_2_spec );
- mutest_it( "calculate 12 / 0.05", operation_12_div_0_05_spec );
- mutest_it( "calculate 0.08 / 4", operation_0_08_div_4_spec );
-}
-
-static void power_suite( void )
-{
- mutest_it( "calculate 2 ^ 3", operation_2_pow_3_spec );
- mutest_it( "calculate 4 ^ 0.5", operation_4_pow_0_5_spec );
-}
-
-MUTEST_MAIN(
- mutest_describe( "Serialization", serialization_suite );
- mutest_describe( "Addition", addition_suite );
- mutest_describe( "Subtraction", subtraction_suite );
- mutest_describe( "Multiplication", multiplication_suite );
- mutest_describe( "Division", division_suite );
- mutest_describe( "Power", power_suite );
-)
diff --git a/src/services/quick-answers/meson.build b/src/services/quick-answers/meson.build
deleted file mode 100644
index ae468c2..0000000
--- a/src/services/quick-answers/meson.build
+++ /dev/null
@@ -1,9 +0,0 @@
-foobar_sources += files(
- 'math-lexer.c',
- 'math-parser.c',
- 'math.c',
-)
-
-foobar_tests += {
- 'math': files('math.test.c'),
-}