[gnome-calculator/60-split-out-a-backend-library] gcalc: initial infrastructure for equations solving



commit 5f8cc87dcadb1be8f97de3319398dbc394723f4d
Author: Daniel Espinosa Ortiz <esodan gmail com>
Date:   Thu Jan 3 16:52:30 2019 -0600

    gcalc: initial infrastructure for equations solving

 gcalc/gcalc-constant.vala      |  5 +++
 gcalc/gcalc-expression.vala    |  1 +
 gcalc/gcalc-gconstant.vala     | 77 ++++++++++++++++++++++++++++++++++++++----
 gcalc/gcalc-gexpression.vala   |  4 +++
 gcalc/gcalc-gpolynomial.vala   | 28 +++++++++++++++
 gcalc/gcalc-gresult.vala       |  4 ++-
 gcalc/gcalc-term.vala          | 41 +++++++++++++++++++++-
 gcalc/meson.build              |  1 +
 tests/gcalc-parsing.vala       |  5 +++
 tests/gcalc-solving-basic.vala | 43 ++++++++++++++++++-----
 vapi/mpc.vapi                  | 14 ++++++++
 11 files changed, 205 insertions(+), 18 deletions(-)
---
diff --git a/gcalc/gcalc-constant.vala b/gcalc/gcalc-constant.vala
index e8707a25..21a0670a 100644
--- a/gcalc/gcalc-constant.vala
+++ b/gcalc/gcalc-constant.vala
@@ -19,6 +19,11 @@
  *      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);
 }
 
diff --git a/gcalc/gcalc-expression.vala b/gcalc/gcalc-expression.vala
index 0ea3554f..8842a4e8 100644
--- a/gcalc/gcalc-expression.vala
+++ b/gcalc/gcalc-expression.vala
@@ -21,5 +21,6 @@
 public interface GCalc.Expression : Object {
   public abstract ExpressionContainer expressions { get; }
   public abstract string to_string ();
+  public abstract Result solve ();
 }
 
diff --git a/gcalc/gcalc-gconstant.vala b/gcalc/gcalc-gconstant.vala
index 4658db80..463864f0 100644
--- a/gcalc/gcalc-gconstant.vala
+++ b/gcalc/gcalc-gconstant.vala
@@ -19,26 +19,89 @@
  *      Daniel Espinosa <esodan gmail com>
  */
 public class GCalc.GConstant : GExpression, Constant {
-  private MPFR.Real real_value = MPFR.Real (1000);
+  private MPC.Complex _complex = MPC.Complex (1000);
+  private bool imaginary = false;
+
+  internal unowned MPC.Complex get_complex () { return _complex; }
 
   construct {
-    real_value.set_zero ();
+    _complex.set_double (0.0);
+  }
+  internal GConstant.complex (MPC.Complex complex) {
+    _complex.set (complex);
   }
   public GConstant.integer (int val) {
-    real_value.set_signed_integer ((long) val);
+    _complex.set_double (val);
   }
   public GConstant.unsigned_integer (uint val) {
-    real_value.set_unsigned_integer ((ulong) val);
+    _complex.set_double (val);
   }
   public GConstant.@double (double val) {
-    real_value.set_double (val);
+    _complex.set_double (val);
+  }
+  public GConstant.new_imag (double real, double imag) {
+    _complex.set_double (real, imag);
+    imaginary = true;
   }
 
   // Constant Interface
-  public void zero () { real_value.set_zero (); }
+  public double real () {
+    return _complex.get_real_double ();
+  }
+  public double imag () {
+    return _complex.get_imag_double ();
+  }
+  public void zero () {
+    MPFR.Real r = MPFR.Real (1000);
+    r.set_zero ();
+    _complex.set_mpreal (r);
+  }
+  public Constant add (Constant c)
+    requires (c is GConstant)
+  {
+    var res = MPC.Complex (1000);
+    var p1 = MPC.Complex (1000);
+    p1.set ((c as GConstant).get_complex ());
+    res.add (_complex, p1);
+    var nc = new GConstant.complex (res);
+    return nc as Constant;
+  }
+  public Constant subtract (Constant c)
+    requires (c is GConstant)
+  {
+    var res = MPC.Complex (1000);
+    var p1 = MPC.Complex (1000);
+    p1.set ((c as GConstant).get_complex ());
+    res.subtract (_complex, p1);
+    var nc = new GConstant.complex (res);
+    return nc as Constant;
+  }
+  public Constant multiply (Constant c)
+    requires (c is GConstant)
+  {
+    var res = MPC.Complex (1000);
+    var p1 = MPC.Complex (1000);
+    p1.set ((c as GConstant).get_complex ());
+    res.multiply (_complex, p1);
+    var nc = new GConstant.complex (res);
+    return nc as Constant;
+  }
+  public Constant divide (Constant c)
+    requires (c is GConstant)
+  {
+    var res = MPC.Complex (1000);
+    var p1 = MPC.Complex (1000);
+    p1.set ((c as GConstant).get_complex ());
+    res.divide (_complex, p1);
+    var nc = new GConstant.complex (res);
+    return nc as Constant;
+  }
   // Expression interface
   public override string to_string () {
-    return ""; // FIXME: write down string representation
+    if (imaginary) {
+      return MPC.Complex.to_string (10, 10, _complex);
+    }
+    return "%g".printf (real ());
   }
 }
 
diff --git a/gcalc/gcalc-gexpression.vala b/gcalc/gcalc-gexpression.vala
index ec56fd68..23a77d47 100644
--- a/gcalc/gcalc-gexpression.vala
+++ b/gcalc/gcalc-gexpression.vala
@@ -24,5 +24,9 @@ public class GCalc.GExpression : Object, Expression {
   public new virtual string to_string () {
     return "";
   }
+  public new virtual Result solve () {
+    var e = new GErrorResult ("Invalid expression");
+    return new GResult.with_error (this, e as ErrorResult);
+  }
 }
 
diff --git a/gcalc/gcalc-gpolynomial.vala b/gcalc/gcalc-gpolynomial.vala
index b3cf610d..09f3e550 100644
--- a/gcalc/gcalc-gpolynomial.vala
+++ b/gcalc/gcalc-gpolynomial.vala
@@ -19,5 +19,33 @@
  *      Daniel Espinosa <esodan gmail com>
  */
 public class GCalc.GPolynomial : GExpression, Polynomial {
+  public override Result solve () {
+    Expression res = null;
+    Term current = null;
+    foreach (Expression e in expressions) {
+      var r = e.solve ();
+      if (!r.is_valid) {
+        return r;
+      }
+      if (r.expression is Term) {
+        var t = r.expression as Term;
+        if (current == null) {
+          current = t;
+          continue;
+        }
+        try {
+          current.sum (t);
+        } catch (GLib.Error err) {
+          var nerr = new GErrorResult (err.message);
+          return new GResult.with_error ((Expression) new GExpression (), (ErrorResult) nerr) as Result;
+        }
+      }
+      if (r.expression is Constant) {
+        res = r.expression;
+        break;
+      }
+    }
+    return new GResult (res) as Result;
+  }
 }
 
diff --git a/gcalc/gcalc-gresult.vala b/gcalc/gcalc-gresult.vala
index 5cb4d29b..3d9998f4 100644
--- a/gcalc/gcalc-gresult.vala
+++ b/gcalc/gcalc-gresult.vala
@@ -18,7 +18,7 @@
  * Authors:
  *      Daniel Espinosa <esodan gmail com>
  */
-public class GCalc.GResult : Object {
+public class GCalc.GResult : Object, Result {
   private Expression _expression;
   private ErrorResult _error;
   public GResult (Expression exp) {
@@ -29,6 +29,8 @@ public class GCalc.GResult : Object {
     _expression = exp;
     _error = error;
   }
+
+  // Result
   public bool is_valid { get { return _error == null; } }
   public string to_string () {
     return expression.to_string ();
diff --git a/gcalc/gcalc-term.vala b/gcalc/gcalc-term.vala
index db11643b..258ba529 100644
--- a/gcalc/gcalc-term.vala
+++ b/gcalc/gcalc-term.vala
@@ -18,5 +18,44 @@
  * Authors:
  *      Daniel Espinosa <esodan gmail com>
  */
-public interface GCalc.Term : Object, Expression {}
+public interface GCalc.Term : Object, Expression {
+  public virtual Expression sum (Term t) throws GLib.Error {
+    if (t.expressions.get_n_items () == 0) {
+      return this;
+    }
+    Expression current = null;
+    Operator current_operator = null;
+    bool first = true;
+    foreach (Expression e in expressions) {
+      if (e is Operator) {
+        if (!(e is Minus || e is Plus) && first) {
+          throw new TermError.INVALID_OPERATOR ("Incorrect position for operator in expression");
+        }
+        if (e is Minus && first) {
+          current = new GConstant.@double (-1.0);
+          first = false;
+        }
+        current_operator = e as Operator;
+      } else if (e is Constant) {
+        if (current == null) {
+          current = e;
+          first = false;
+        } else {
+          if (current is Constant) {
+            if (current_operator != null) {
+              if (e is Minus && e is Multiply) {
+                current = (current as Constant).multiply (e as Constant);
+              }
+            }
+          }
+        }
+      }
+    }
+    return current;
+  }
+}
+
+public errordomain GCalc.TermError {
+  INVALID_OPERATOR
+}
 
diff --git a/gcalc/meson.build b/gcalc/meson.build
index bbc268f8..2b0936c2 100644
--- a/gcalc/meson.build
+++ b/gcalc/meson.build
@@ -90,6 +90,7 @@ deps = [
        libxml,
        libsoup,
        vala_dep,
+       libmath,
        gee
 ]
 
diff --git a/tests/gcalc-parsing.vala b/tests/gcalc-parsing.vala
index 06a4fc9e..515a05ec 100644
--- a/tests/gcalc-parsing.vala
+++ b/tests/gcalc-parsing.vala
@@ -504,6 +504,11 @@ class Tests {
         warning ("Error: %s", error.message);
       }
     });
+    Test.add_func ("/gcalc/parser/constant/to_string",
+    ()=>{
+      Constant c = new GConstant.@double (-1.0) as Constant;
+      assert ("-1" in c.to_string ());
+    });
     return Test.run ();
   }
 }
diff --git a/tests/gcalc-solving-basic.vala b/tests/gcalc-solving-basic.vala
index 932f4e38..5d54ee31 100644
--- a/tests/gcalc-solving-basic.vala
+++ b/tests/gcalc-solving-basic.vala
@@ -23,16 +23,41 @@ class Tests {
   {
     GLib.Intl.setlocale (GLib.LocaleCategory.ALL, "");
     Test.init (ref args);
-    Test.add_func ("/gcalc/parser/constant/integer",
+    Test.add_func ("/gcalc/solve/constant/add",
     ()=>{
-      try {
-        var parser = new Parser ();
-        var eqman = new GMathEquationManager ();
-        parser.parse ("1", eqman);
-
-      } catch (GLib.Error error) {
-        warning ("Error: %s", error.message);
-      }
+      var c1 = new GConstant.@double (3.0);
+      var c2 = new GConstant.@double (3.0);
+      var c3 = c1.add (c2);
+      assert (c3 != null);
+      message (c3.to_string ());
+      assert (c3.real () == 6.0);
+    });
+    Test.add_func ("/gcalc/solve/constant/subtract",
+    ()=>{
+      var c1 = new GConstant.@double (9.0);
+      var c2 = new GConstant.@double (3.0);
+      var c3 = c1.subtract (c2);
+      assert (c3 != null);
+      message (c3.to_string ());
+      assert (c3.real () == 6.0);
+    });
+    Test.add_func ("/gcalc/solve/constant/multiply",
+    ()=>{
+      var c1 = new GConstant.@double (3.0);
+      var c2 = new GConstant.@double (3.0);
+      var c3 = c1.multiply (c2);
+      assert (c3 != null);
+      message (c3.to_string ());
+      assert (c3.real () == 9.0);
+    });
+    Test.add_func ("/gcalc/solve/constant/devide",
+    ()=>{
+      var c1 = new GConstant.@double (9.0);
+      var c2 = new GConstant.@double (3.0);
+      var c3 = c1.divide (c2);
+      assert (c3 != null);
+      message (c3.to_string ());
+      assert (c3.real () == 3.0);
     });
     return Test.run ();
   }
diff --git a/vapi/mpc.vapi b/vapi/mpc.vapi
index 00e6c138..bc818344 100644
--- a/vapi/mpc.vapi
+++ b/vapi/mpc.vapi
@@ -121,5 +121,19 @@ namespace MPC {
         public int asinh (Complex op, Round rnd = Round.NEAREST);
         public int acosh (Complex op, Round rnd = Round.NEAREST);
         public int atanh (Complex op, Round rnd = Round.NEAREST);
+
+        public double get_real_double () {
+          var r = MPFR.Real (1000);
+          r.set (get_real ().val);
+          return r.get_double ();
+        }
+        public double get_imag_double () {
+          var i = MPFR.Real (1000);
+          i.set (get_imag ().val);
+          return i.get_double ();
+        }
+
+        [CCode (cname="mpc_get_str")]
+        public static string to_string (int @base, size_t digits, Complex op, Round rnd = Round.NEAREST);
     }
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]