[gnome-calculator] GCalc: added support to parse complex numbers
- From: Daniel Espinosa Ortiz <despinosa src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-calculator] GCalc: added support to parse complex numbers
- Date: Thu, 31 Oct 2019 18:31:46 +0000 (UTC)
commit acaa1beccbd1eab8b9ec404d8fc9561f9a2da82f
Author: Daniel Espinosa <esodan gmail com>
Date: Thu Oct 31 12:30:22 2019 -0600
GCalc: added support to parse complex numbers
The acceptable format is to prefix a complex number wih 'i'
gcalc/gcalc-constant.vala | 30 ++++++-
gcalc/gcalc-math-term.vala | 1 +
gcalc/gcalc-parameter.vala | 21 +++++
gcalc/gcalc-parser.vala | 168 ++++++++++++++++++++------------------
tests/gcalc-solving-basic.vala | 177 ++++++++++++++++++++++++++++++++++++++++-
5 files changed, 316 insertions(+), 81 deletions(-)
---
diff --git a/gcalc/gcalc-constant.vala b/gcalc/gcalc-constant.vala
index f8d2e03e..24ee2561 100644
--- a/gcalc/gcalc-constant.vala
+++ b/gcalc/gcalc-constant.vala
@@ -126,10 +126,36 @@ public class GCalc.Constant : Expression,
// Expression interface
internal override string to_string () {
+ string s = "";
if (imag () != 0.0) {
- return MPC.Complex.to_string (10, 10, _complex);
+ bool par = false;
+ if (real () != 0.0) {
+ if (parent != null) {
+ s += "(";
+ par = true;
+ }
+ s += "%g".printf (real ());
+ }
+ var im = imag ();
+ if (im < 0.0) {
+ s += "-";
+ im = im * -1.0;
+ } else {
+ if (real () != 0.0) {
+ s += "+";
+ }
+ }
+ s += "i";
+ if (im != 1.0) {
+ s += "%g".printf (im);
+ }
+ if (par) {
+ s += ")";
+ }
+ } else {
+ s = "%g".printf (real ());
}
- return "%g".printf (real ());
+ return s;
}
internal override MathResult solve () {
diff --git a/gcalc/gcalc-math-term.vala b/gcalc/gcalc-math-term.vala
index 6f14426e..6bdb7066 100644
--- a/gcalc/gcalc-math-term.vala
+++ b/gcalc/gcalc-math-term.vala
@@ -110,6 +110,7 @@ public interface GCalc.MathTerm : Object, MathExpression {
public static MathExpression evaluate_constants (MathConstant c1, MathConstant c2, MathOperator op)
throws GLib.Error
{
+ message ("Eval: %s : %s", c1.to_string (), c2.to_string ());
MathExpression res = null;
if (op is MathMinus) {
res = c1.multiply (c2);
diff --git a/gcalc/gcalc-parameter.vala b/gcalc/gcalc-parameter.vala
index ee7fe57e..7022a7d4 100644
--- a/gcalc/gcalc-parameter.vala
+++ b/gcalc/gcalc-parameter.vala
@@ -46,4 +46,25 @@ public class GCalc.Parameter : GCalc.Variable, MathParameter {
v = @value;
return v;
}
+ // Expression
+ internal override string to_string () {
+ string str = name;
+ if (@value is MathConstantNumber) {
+ var c = @value as MathConstantNumber;
+ str = "%g".printf (c.@value ());
+ } else if (@value is MathConstantComplex) {
+ var c = @value as MathConstantComplex;
+ str = "%g".printf (c.real ());
+ double i = c.imag ();
+ if (i != 0) {
+ if (i > 0) {
+ str += "+";
+ } else {
+ str += "-";
+ }
+ str += "%gi".printf (c.imag ());
+ }
+ }
+ return str;
+ }
}
diff --git a/gcalc/gcalc-parser.vala b/gcalc/gcalc-parser.vala
index 1054b8a6..2668e84c 100644
--- a/gcalc/gcalc-parser.vala
+++ b/gcalc/gcalc-parser.vala
@@ -26,6 +26,7 @@ public class GCalc.Parser : Object {
MathExpression current = null;
MathExpression current_parent = null;
MathExpression top_parent = null;
+ Equation eq = null;
bool enable_parameter = false;
Gee.ArrayList<TokenType> expected = new Gee.ArrayList<TokenType> ();
GLib.Scanner scanner;
@@ -51,7 +52,7 @@ public class GCalc.Parser : Object {
*/
public void parse (string str, MathEquationManager eqman) throws GLib.Error {
TokenType token = TokenType.NONE;
- Equation eq = new Equation ();
+ eq = new Equation ();
scanner.input_text (str, str.length);
current = null;
current_parent = null;
@@ -64,76 +65,84 @@ public class GCalc.Parser : Object {
}
string n = token_to_string ();
if (expected.size != 0 && !expected.contains (token)) {
- throw new ParserError.INVALID_TOKEN_ERROR ("Found an unexpected expression");
+ throw new ParserError.INVALID_TOKEN_ERROR (_("Found an unexpected expression"));
}
switch (token) {
case TokenType.IDENTIFIER:
- MathExpression sfunc = eqman.functions.find_named (n);
- if (sfunc != null) {
- sfunc = Object.new (sfunc.get_type ()) as MathExpression;
- if (current == null) {
- var exp = new Polynomial ();
- eq.expressions.add (exp);
- var t = new Term ();
- exp.expressions.add (t);
- t.expressions.add (sfunc);
- current = sfunc;
- current_parent = t;
- top_parent = exp;
- expected.clear ();
- expected.add(TokenType.OPEN_PARENS);
- } else if (current is MathOperator && current_parent is MathTerm && top_parent is
MathPolynomial) {
- current_parent.expressions.add (sfunc);
+ Regex rg = new Regex ("i[0-9]*.*", RegexCompileFlags.ANCHORED, RegexMatchFlags.ANCHORED);
+ if (rg.match (n, RegexMatchFlags.ANCHORED, null)) {
+ string cxn = n.replace ("i", "");
+ double v = double.parse (cxn);
+ var cx = new Constant.complex (0, v);
+ add_constant (cx);
+ } else {
+ MathExpression sfunc = eqman.functions.find_named (n);
+ if (sfunc != null) {
+ sfunc = Object.new (sfunc.get_type ()) as MathExpression;
+ if (current == null) {
+ var exp = new Polynomial ();
+ eq.expressions.add (exp);
+ var t = new Term ();
+ exp.expressions.add (t);
+ t.expressions.add (sfunc);
current = sfunc;
+ current_parent = t;
+ top_parent = exp;
expected.clear ();
- } else if (current is MathTerm && current_parent is MathPolynomial) {
- current.expressions.add (sfunc);
- current_parent = current;
- current = sfunc;
- top_parent = current_parent.parent;
- expected.clear ();
- }
- } else if (n.down () == "def" && current == null) {
- // FIXME: implement function definition
- } else if (n.down () == "def" && current is MathFunction) {
- throw new ParserError.INVALID_TOKEN_ERROR ("Found an unexpected function definition expression");
- } else {
- var v = new Variable (n) as MathExpression;
- if (enable_parameter) {
- v = new Parameter (n) as MathExpression;
- enable_parameter = false;
- }
- var sv = eqman.find_variable (n) as MathVariable;
- if (sv == null) {
- sv = eq.variables.find_named (n) as MathVariable;
+ expected.add(TokenType.OPEN_PARENS);
+ } else if (current is MathOperator && current_parent is MathTerm && top_parent is
MathPolynomial) {
+ current_parent.expressions.add (sfunc);
+ current = sfunc;
+ expected.clear ();
+ } else if (current is MathTerm && current_parent is MathPolynomial) {
+ current.expressions.add (sfunc);
+ current_parent = current;
+ current = sfunc;
+ top_parent = current_parent.parent;
+ expected.clear ();
+ }
+ } else if (n.down () == "def" && current == null) {
+ // FIXME: implement function definition
+ } else if (n.down () == "def" && current is MathFunction) {
+ throw new ParserError.INVALID_TOKEN_ERROR ("Found an unexpected function definition
expression");
+ } else {
+ var v = new Variable (n) as MathExpression;
+ if (enable_parameter) {
+ v = new Parameter (n) as MathExpression;
+ enable_parameter = false;
+ }
+ var sv = eqman.find_variable (n) as MathVariable;
if (sv == null) {
- eq.variables.add (v);
+ sv = eq.variables.find_named (n) as MathVariable;
+ if (sv == null) {
+ eq.variables.add (v);
+ } else {
+ ((MathVariable) v).bind = sv;
+ }
} else {
((MathVariable) v).bind = sv;
}
- } else {
- ((MathVariable) v).bind = sv;
- }
- if (current == null) {
- var exp = new Polynomial ();
- eq.expressions.add (exp);
- var t = new Term ();
- exp.expressions.add (t);
- t.expressions.add (v);
- current = v;
- current_parent = v.parent;
- top_parent = current_parent.parent;
- expected.clear ();
- } else if (current is MathOperator && current_parent is MathTerm && top_parent is
MathPolynomial) {
- current_parent.expressions.add (v);
- current = v;
- expected.clear ();
- } else if (current is MathTerm) {
- current.expressions.add (v);
+ if (current == null) {
+ var exp = new Polynomial ();
+ eq.expressions.add (exp);
+ var t = new Term ();
+ exp.expressions.add (t);
+ t.expressions.add (v);
current = v;
current_parent = v.parent;
top_parent = current_parent.parent;
expected.clear ();
+ } else if (current is MathOperator && current_parent is MathTerm && top_parent is
MathPolynomial) {
+ current_parent.expressions.add (v);
+ current = v;
+ expected.clear ();
+ } else if (current is MathTerm) {
+ current.expressions.add (v);
+ current = v;
+ current_parent = v.parent;
+ top_parent = current_parent.parent;
+ expected.clear ();
+ }
}
}
break;
@@ -144,26 +153,7 @@ public class GCalc.Parser : Object {
throw new ParserError.INVALID_TOKEN_ERROR ("Found an unexpected expression for a constant");
}
var cexp = new Constant.@double (double.parse (n));
- if (current == null) {
- var exp = new Polynomial ();
- eq.expressions.add (exp);
- var t = new Term ();
- exp.expressions.add (t);
- t.expressions.add (cexp);
- current = cexp;
- current_parent = t;
- top_parent = exp;
- } else if ((current is MathOperator || current is MathTerm) && current_parent is MathTerm &&
top_parent is MathPolynomial) {
- current_parent.expressions.add (cexp);
- expected.clear ();
- current = cexp;
- } else if (current is MathTerm && current_parent is MathPolynomial && (top_parent is MathGroup ||
top_parent is MathFunction)) {
- current.expressions.add (cexp);
- top_parent = current_parent;
- current_parent = current;
- current = cexp;
- expected.clear ();
- }
+ add_constant (cexp);
break;
case TokenType.STAR:
var op = new Multiply ();
@@ -411,6 +401,28 @@ public class GCalc.Parser : Object {
expected.clear ();
}
}
+ private void add_constant (MathConstant c) {
+ if (current == null) {
+ var exp = new Polynomial ();
+ eq.expressions.add (exp);
+ var t = new Term ();
+ exp.expressions.add (t);
+ t.expressions.add (c);
+ current = c;
+ current_parent = t;
+ top_parent = exp;
+ } else if ((current is MathOperator || current is MathTerm) && current_parent is MathTerm && top_parent
is MathPolynomial) {
+ current_parent.expressions.add (c);
+ expected.clear ();
+ current = c;
+ } else if (current is MathTerm && current_parent is MathPolynomial && (top_parent is MathGroup ||
top_parent is MathFunction)) {
+ current.expressions.add (c);
+ top_parent = current_parent;
+ current_parent = current;
+ current = c;
+ expected.clear ();
+ }
+ }
/**
* Reads a token at a given position
*/
diff --git a/tests/gcalc-solving-basic.vala b/tests/gcalc-solving-basic.vala
index a4fd4f15..cae20a6c 100644
--- a/tests/gcalc-solving-basic.vala
+++ b/tests/gcalc-solving-basic.vala
@@ -31,6 +31,13 @@ class Tests {
assert (c3 != null);
message (c3.to_string ());
assert (c3.@value () == 6.0);
+ var c4 = new Constant.complex (0.0, -1.0);
+ var c5 = c1.add (c4) as MathConstantComplex;
+ assert (c5 != null);
+ assert (c5.real () == 3.0);
+ assert (c5.imag () == -1.0);
+ message (c5.to_string ());
+ assert (c5.to_string () == "3-i");
});
Test.add_func ("/gcalc/solve/constant/subtract",
()=>{
@@ -76,7 +83,7 @@ class Tests {
assert (c3.real () == -10.0);
assert (c3.imag () == -15.0);
});
- Test.add_func ("/gcalc/solve/constant",
+ Test.add_func ("/gcalc/solve/constant/number",
()=>{
try {
var parser = new Parser ();
@@ -102,6 +109,174 @@ class Tests {
warning ("Error: %s", e.message);
}
});
+ Test.add_func ("/gcalc/solve/parse/constant/complex/positive",
+ ()=>{
+ try {
+ var parser = new Parser ();
+ var eqman = new EquationManager ();
+ parser.parse ("1+i3", eqman);
+ assert (eqman.equations.get_n_items () == 1);
+ var eq = eqman.equations.get_item (0) as MathEquation;
+ assert (eq != null);
+ message ("Equation: %s", eq.to_string ());
+ assert (eq.to_string () == "1+i3");
+ var e = eq.expressions.get_item (0) as MathPolynomial;
+ assert (e != null);
+ message ("Polynomial: %u items - exp: %s", e.expressions.get_n_items (), e.to_string ());
+ var t = e.expressions.get_item (0) as MathTerm;
+ assert (t != null);
+ message ("Real Term: %u items - exp: %s", t.expressions.get_n_items (), t.to_string ());
+ var n = t.expressions.get_item (0) as MathConstantNumber;
+ assert (n != null);
+ assert (n.@value () == 1);
+ var ct = e.expressions.get_item (1) as MathTerm;
+ assert (ct != null);
+ message ("Imag Term: %u items - exp: %s", ct.expressions.get_n_items (), ct.to_string ());
+ message ("1 Term: %s", ct.expressions.get_item (0).get_type ().name ());
+ message ("2 Term: %s", ct.expressions.get_item (1).get_type ().name ());
+ var c = ct.expressions.get_item (1) as MathConstantComplex;
+ assert (c != null);
+ assert (c.real () == 0.0);
+ assert (c.imag () == 3.0);
+ var res = eq.solve ();
+ assert (res != null);
+ assert (res.expression != null);
+ var rc = res.expression as MathConstantComplex;
+ assert (rc != null);
+ message ("MathConstant Result: %s", rc.to_string ());
+ assert (rc.to_string () == "1+i3");
+ assert (rc.real () == 1.0);
+ assert (rc.imag () == 3.0);
+ } catch (GLib.Error e) {
+ warning ("Error: %s", e.message);
+ }
+ });
+ Test.add_func ("/gcalc/solve/parse/constant/complex/negative",
+ ()=>{
+ try {
+ var parser = new Parser ();
+ var eqman = new EquationManager ();
+ parser.parse ("1-i3", eqman);
+ assert (eqman.equations.get_n_items () == 1);
+ var eq = eqman.equations.get_item (0) as MathEquation;
+ assert (eq != null);
+ message ("Equation: %s", eq.to_string ());
+ assert (eq.to_string () == "1-i3");
+ var e = eq.expressions.get_item (0) as MathPolynomial;
+ assert (e != null);
+ message ("Polynomial: %u items - exp: %s", e.expressions.get_n_items (), e.to_string ());
+ var t = e.expressions.get_item (0) as MathTerm;
+ assert (t != null);
+ message ("Real Term: %u items - exp: %s", t.expressions.get_n_items (), t.to_string ());
+ var n = t.expressions.get_item (0) as MathConstantNumber;
+ assert (n != null);
+ assert (n.@value () == 1);
+ var ct = e.expressions.get_item (1) as MathTerm;
+ assert (ct != null);
+ message ("Imag Term: %u items - exp: %s", ct.expressions.get_n_items (), ct.to_string ());
+ message ("1 Term: %s", ct.expressions.get_item (0).get_type ().name ());
+ message ("2 Term: %s", ct.expressions.get_item (1).get_type ().name ());
+ var c = ct.expressions.get_item (1) as MathConstantComplex;
+ assert (c != null);
+ assert (c.real () == 0.0);
+ assert (c.imag () == 3.0);
+ var res = eq.solve ();
+ assert (res != null);
+ assert (res.expression != null);
+ var rc = res.expression as MathConstantComplex;
+ assert (rc != null);
+ message ("MathConstant Result: %s", rc.to_string ());
+ assert (rc.to_string () == "1-i3");
+ assert (rc.real () == 1.0);
+ assert (rc.imag () == -3.0);
+ } catch (GLib.Error e) {
+ warning ("Error: %s", e.message);
+ }
+ });
+ Test.add_func ("/gcalc/solve/parse/constant/complex/real-negative",
+ ()=>{
+ try {
+ var parser = new Parser ();
+ var eqman = new EquationManager ();
+ parser.parse ("-1+i3", eqman);
+ assert (eqman.equations.get_n_items () == 1);
+ var eq = eqman.equations.get_item (0) as MathEquation;
+ assert (eq != null);
+ message ("Equation: %s", eq.to_string ());
+ assert (eq.to_string () == "-1+i3");
+ var e = eq.expressions.get_item (0) as MathPolynomial;
+ assert (e != null);
+ message ("Polynomial: %u items - exp: %s", e.expressions.get_n_items (), e.to_string ());
+ var t = e.expressions.get_item (0) as MathTerm;
+ assert (t != null);
+ message ("Real Term: %u items - exp: %s", t.expressions.get_n_items (), t.to_string ());
+ var n = t.expressions.get_item (1) as MathConstantNumber;
+ assert (n != null);
+ assert (n.@value () == 1);
+ var ct = e.expressions.get_item (1) as MathTerm;
+ assert (ct != null);
+ message ("Imag Term: %u items - exp: %s", ct.expressions.get_n_items (), ct.to_string ());
+ message ("1 Term: %s", ct.expressions.get_item (0).get_type ().name ());
+ message ("2 Term: %s", ct.expressions.get_item (1).get_type ().name ());
+ var c = ct.expressions.get_item (1) as MathConstantComplex;
+ assert (c != null);
+ assert (c.real () == 0.0);
+ assert (c.imag () == 3.0);
+ var res = eq.solve ();
+ assert (res != null);
+ assert (res.expression != null);
+ var rc = res.expression as MathConstantComplex;
+ assert (rc != null);
+ message ("MathConstant Result: %s", rc.to_string ());
+ assert (rc.to_string () == "-1+i3");
+ assert (rc.real () == -1.0);
+ assert (rc.imag () == 3.0);
+ } catch (GLib.Error e) {
+ warning ("Error: %s", e.message);
+ }
+ });
+ Test.add_func ("/gcalc/solve/parse/constant/complex/all-negative",
+ ()=>{
+ try {
+ var parser = new Parser ();
+ var eqman = new EquationManager ();
+ parser.parse ("-1-i3", eqman);
+ assert (eqman.equations.get_n_items () == 1);
+ var eq = eqman.equations.get_item (0) as MathEquation;
+ assert (eq != null);
+ message ("Equation: %s", eq.to_string ());
+ assert (eq.to_string () == "-1-i3");
+ var e = eq.expressions.get_item (0) as MathPolynomial;
+ assert (e != null);
+ message ("Polynomial: %u items - exp: %s", e.expressions.get_n_items (), e.to_string ());
+ var t = e.expressions.get_item (0) as MathTerm;
+ assert (t != null);
+ message ("Real Term: %u items - exp: %s", t.expressions.get_n_items (), t.to_string ());
+ var n = t.expressions.get_item (1) as MathConstantNumber;
+ assert (n != null);
+ assert (n.@value () == 1);
+ var ct = e.expressions.get_item (1) as MathTerm;
+ assert (ct != null);
+ message ("Imag Term: %u items - exp: %s", ct.expressions.get_n_items (), ct.to_string ());
+ message ("1 Term: %s", ct.expressions.get_item (0).get_type ().name ());
+ message ("2 Term: %s", ct.expressions.get_item (1).get_type ().name ());
+ var c = ct.expressions.get_item (1) as MathConstantComplex;
+ assert (c != null);
+ assert (c.real () == 0.0);
+ assert (c.imag () == 3.0);
+ var res = eq.solve ();
+ assert (res != null);
+ assert (res.expression != null);
+ var rc = res.expression as MathConstantComplex;
+ assert (rc != null);
+ message ("MathConstant Result: %s", rc.to_string ());
+ assert (rc.to_string () == "-1-i3");
+ assert (rc.real () == -1.0);
+ assert (rc.imag () == -3.0);
+ } catch (GLib.Error e) {
+ warning ("Error: %s", e.message);
+ }
+ });
Test.add_func ("/gcalc/solve/term/constant",
()=>{
try {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]