Commit 1c5e0773 authored by Daniel Espinosa Ortiz's avatar Daniel Espinosa Ortiz

gcalc: add new library for math parsing/solving

Fix issue #60

This new library is independent from the internal one.

GCalc provides following features:

* Parse complicated constant equations
* Parse pre-defined functions (equivalent to the ones in Calculator)
* Parse complicated equations with pre-defined functions in them
* Parsing expressions to an Object Oriented tree representation using GObject classes
* Define arbitrary variables using complicated expressions, with both other variables or constants
* Evaluate expressions using defined variables
* Uses MPC as back end for calculation so complex numbers will be available soon
* No Gtk+ dependencies
parent d47f81b5
Pipeline #77281 failed with stages
in 60 minutes and 2 seconds
......@@ -2,12 +2,12 @@
image: ubuntu:rolling
before_script:
- apt-get update
- apt-get install -q -y --no-install-recommends meson valac gcc gettext itstool libgtk-3-dev libgtksourceview-4-dev libmpc-dev libmpfr-dev libsoup2.4-dev libxml2-dev
- apt-get install -q -y --no-install-recommends meson valac gcc gettext itstool libgtk-3-dev libgtksourceview-4-dev libmpc-dev libmpfr-dev libsoup2.4-dev libxml2-dev libgee-0.8-dev libvala-0.42
.before_script_template: &fedora_before_script
image: fedora:latest
before_script:
- dnf install -y meson vala itstool gtk3-devel gtksourceview4-devel libmpc-devel mpfr-devel libsoup-devel libxml2-devel
- dnf install -y meson vala itstool gtk3-devel gtksourceview4-devel libmpc-devel mpfr-devel libsoup-devel libxml2-devel libgee-devel vala-devel
.build_template: &meson_build
stage: build
......@@ -55,4 +55,4 @@ test:fedora:
dependencies:
- build:fedora
<<: *fedora_before_script
<<: *meson_test
<<: *meson_test
\ No newline at end of file
/* gcalc-assign.vala
*
* Copyright (C) 2018 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public interface GCalc.Assign : Object, Expression, Operator, BinaryOperator {
public Expression evaluate () throws GLib.Error {
if (expressions.get_n_items () != 2) {
throw new AssigError.INVALID_STRUCTURE_ERROR ("Invalid number of expressions in assign");
}
var v = expressions.get_item (0) as Variable;
if (v == null) {
throw new AssigError.INVALID_STRUCTURE_ERROR ("Invalid variable object in assign");
}
var p = expressions.get_item (1) as Polynomial;
if (p == null) {
throw new AssigError.INVALID_STRUCTURE_ERROR ("Invalid polynomial object in assign");
}
var ca = p.evaluate () as Constant;
if (ca == null) {
throw new AssigError.INVALID_STRUCTURE_ERROR ("Invalid polynomial evaluation in assign; should a constant no Variable update was done");
}
v.@value = ca;
return v.@value;
}
}
public errordomain GCalc.AssigError {
INVALID_STRUCTURE_ERROR
}
/* gcalc-binary-operator.vala
*
* Copyright (C) 2018 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public interface GCalc.BinaryOperator : Object, Expression, Operator {}
/* gcalc-constant.vala
*
* Copyright (C) 2018 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public interface GCalc.Constant : Object, Expression {
public abstract double real ();
public abstract double imag ();
public abstract void zero ();
public abstract Constant add (Constant c);
public abstract Constant multiply (Constant c);
public abstract Constant divide (Constant c);
public abstract Constant neg ();
public abstract Constant pow (Constant c);
}
/* gcalc-division.vala
*
* Copyright (C) 2018 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public interface GCalc.Division : Object, Expression, Operator, BinaryOperator {}
/* gcalc-error-result.vala
*
* Copyright (C) 2018 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public interface GCalc.ErrorResult : Object, Result {
public abstract string message { get; }
}
/* gcalc-expression-container.vala
*
* Copyright (C) 2018 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public class GCalc.ExpressionContainer : Gee.ArrayList<Expression>, GLib.ListModel {
public weak Expression parent { get; set; }
public new void add (Expression exp) {
(this as Gee.ArrayList<Expression>).add (exp);
exp.parent = parent;
}
public new Expression remove_at (int index) {
var r = (this as Gee.ArrayList<Expression>).remove_at (index);
if (r != null) {
r.parent = null;
}
return r;
}
public new Expression remove (Expression exp) {
var i = (this as Gee.ArrayList<Expression>).index_of (exp);
return remove_at (i);
}
// GLib.ListModel
public Object? get_item (uint position) {
return (this as Gee.ArrayList<Expression>).@get ((int) position) as Object;
}
public Type get_item_type () {
return typeof (Expression);
}
public uint get_n_items () {
return (this as Gee.ArrayList<Expression>).size;
}
public Object? get_object (uint position) {
return get_item (position);
}
public Expression? find (Expression exp) {
foreach (Expression e in this) {
if (exp is Variable && e is Variable) {
if ((exp as Variable).name == (e as Variable).name) {
return e;
}
}
}
return null;
}
public Expression? find_named (string name) {
foreach (Expression e in this) {
if (e is Variable) {
if ((e as Variable).name == name) {
return e;
}
}
if (e is Function) {
if ((e as Function).name == name) {
return e;
}
}
}
return null;
}
}
/* gcalc-expression-hash-table.vala
*
* Copyright (C) 2019 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public class GCalc.ExpressionHashMap : Gee.HashMap<uint,Expression> {
public weak Expression parent { get; set; }
public void add (Expression exp)
requires (exp is Hashable)
{
(this as Gee.HashMap<uint,Expression>).set (((Hashable) exp).hash (), exp);
exp.parent = parent;
}
public void remove (Expression exp) {
(this as Gee.HashMap<uint,Expression>).unset (((Hashable) exp).hash ());
}
public Expression find_named (string name) {
return (this as Gee.HashMap<uint,Expression>).@get (name.hash ());
}
}
/* gcalc-expresion.vala
*
* Copyright (C) 2018 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public interface GCalc.Expression : Object {
public abstract weak Expression parent { get; set; }
public abstract ExpressionContainer expressions { get; }
public abstract string to_string ();
public abstract Result solve ();
}
public interface GCalc.ErrorExpression : Object, Expression {
}
/* gcalc-function-acos.vala
*
* Copyright (C) 2019 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public class GCalc.GFunctionAcos : GFunction {
construct {
name = "acos";
n_params = 1;
param_types.add (new GConstant ());
}
public override Expression evaluate () throws GLib.Error
{
verify_params ();
GConstant c = null;
var exp = expressions.get_item (0) as Expression;
if (exp == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid parameter type. Expected %s", typeof(Expression).name ());
}
var ev = exp.solve ();
if (ev is ErrorResult) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression: %s", ((ErrorResult) ev).message);
}
if (ev is Result) {
c = ((Result) ev).expression as GConstant;
}
if (c == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression in result");
}
var p1 = MPC.Complex (1000);
p1.set (c.get_complex ());
var res = MPC.Complex (1000);
res.acos (p1);
var nc = new GConstant.internal_complex (res);
return nc as Expression;
}
}
/* gcalc-function-acosh.vala
*
* Copyright (C) 2019 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public class GCalc.GFunctionAcosh : GFunction {
construct {
name = "acosh";
n_params = 1;
param_types.add (new GConstant ());
}
public override Expression evaluate () throws GLib.Error
{
verify_params ();
GConstant c = null;
var exp = expressions.get_item (0) as Expression;
if (exp == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid parameter type. Expected %s", typeof(Expression).name ());
}
var ev = exp.solve ();
if (ev is ErrorResult) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression: %s", ((ErrorResult) ev).message);
}
if (ev is Result) {
c = ((Result) ev).expression as GConstant;
}
if (c == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression in result");
}
var p1 = MPC.Complex (1000);
p1.set (c.get_complex ());
var res = MPC.Complex (1000);
res.acosh (p1);
var nc = new GConstant.internal_complex (res);
return nc as Expression;
}
}
/* gcalc-function-asin.vala
*
* Copyright (C) 2019 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public class GCalc.GFunctionAsin : GFunction {
construct {
name = "asin";
n_params = 1;
param_types.add (new GConstant ());
}
public override Expression evaluate () throws GLib.Error
{
verify_params ();
GConstant c = null;
var exp = expressions.get_item (0) as Expression;
if (exp == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid parameter type. Expected %s", typeof(Expression).name ());
}
var ev = exp.solve ();
if (ev is ErrorResult) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression: %s", ((ErrorResult) ev).message);
}
if (ev is Result) {
c = ((Result) ev).expression as GConstant;
}
if (c == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression in result");
}
var p1 = MPC.Complex (1000);
p1.set (c.get_complex ());
var res = MPC.Complex (1000);
res.asin (p1);
var nc = new GConstant.internal_complex (res);
return nc as Expression;
}
}
/* gcalc-function-asinh.vala
*
* Copyright (C) 2019 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public class GCalc.GFunctionAsinh : GFunction {
construct {
name = "asinh";
n_params = 1;
param_types.add (new GConstant ());
}
public override Expression evaluate () throws GLib.Error
{
verify_params ();
GConstant c = null;
var exp = expressions.get_item (0) as Expression;
if (exp == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid parameter type. Expected %s", typeof(Expression).name ());
}
var ev = exp.solve ();
if (ev is ErrorResult) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression: %s", ((ErrorResult) ev).message);
}
if (ev is Result) {
c = ((Result) ev).expression as GConstant;
}
if (c == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression in result");
}
var p1 = MPC.Complex (1000);
p1.set (c.get_complex ());
var res = MPC.Complex (1000);
res.asinh (p1);
var nc = new GConstant.internal_complex (res);
return nc as Expression;
}
}
/* gcalc-function-atan.vala
*
* Copyright (C) 2019 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public class GCalc.GFunctionAtan : GFunction {
construct {
name = "atan";
n_params = 1;
param_types.add (new GConstant ());
}
public override Expression evaluate () throws GLib.Error
{
verify_params ();
GConstant c = null;
var exp = expressions.get_item (0) as Expression;
if (exp == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid parameter type. Expected %s", typeof(Expression).name ());
}
var ev = exp.solve ();
if (ev is ErrorResult) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression: %s", ((ErrorResult) ev).message);
}
if (ev is Result) {
c = ((Result) ev).expression as GConstant;
}
if (c == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression in result");
}
var p1 = MPC.Complex (1000);
p1.set (c.get_complex ());
var res = MPC.Complex (1000);
res.atan (p1);
var nc = new GConstant.internal_complex (res);
return nc as Expression;
}
}
/* gcalc-function-atanh.vala
*
* Copyright (C) 2019 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public class GCalc.GFunctionAtanh : GFunction {
construct {
name = "atanh";
n_params = 1;
param_types.add (new GConstant ());
}
public override Expression evaluate () throws GLib.Error
{
verify_params ();
GConstant c = null;
var exp = expressions.get_item (0) as Expression;
if (exp == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid parameter type. Expected %s", typeof(Expression).name ());
}
var ev = exp.solve ();
if (ev is ErrorResult) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression: %s", ((ErrorResult) ev).message);
}
if (ev is Result) {
c = ((Result) ev).expression as GConstant;
}
if (c == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression in result");
}
var p1 = MPC.Complex (1000);
p1.set (c.get_complex ());
var res = MPC.Complex (1000);
res.atanh (p1);
var nc = new GConstant.internal_complex (res);
return nc as Expression;
}
}
/* gcalc-function-cos.vala
*
* Copyright (C) 2019 Daniel Espinosa <esodan@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Daniel Espinosa <esodan@gmail.com>
*/
public class GCalc.GFunctionCos : GFunction {
construct {
name = "cos";
n_params = 1;
param_types.add (new GConstant ());
}
public override Expression evaluate () throws GLib.Error
{
verify_params ();
GConstant c = null;
var exp = expressions.get_item (0) as Expression;
if (exp == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid parameter type. Expected %s", typeof(Expression).name ());
}
var ev = exp.solve ();
if (ev is ErrorResult) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression: %s", ((ErrorResult) ev).message);
}
if (ev is Result) {
c = ((Result) ev).expression as GConstant;
}
if (c == null) {
throw new FunctionError.INVOCATION_ERROR ("Invalid expression in result");