diff --git a/source/dutils/math/core.d b/source/dutils/math/core.d
index 9fe174d..b486085 100644
--- a/source/dutils/math/core.d
+++ b/source/dutils/math/core.d
@@ -169,9 +169,89 @@ bool validateFunction(in dstring func, in dstring def) @trusted
do
{
tempNum = ""d;
+ dstring val3;
+ dstring val;
switch(def[i])
{
- case d('('):
+ case d('%'): //Constants
+ debug import std.stdio;
+ if(isOperand && !isOp)
+ return false;
+ isOperand = true;
+ ++i;
+ do
+ {
+ val ~= def[i];
+ ++i;
+ if(i == def.length)
+ return false;
+ }
+ while(def[i] != d('('));
+ ++i;
+ do
+ {
+ val3 ~= def[i];
+ ++i;
+ if(i == def.length)
+ return false;
+ }
+ while(def[i] != d(')'));
+ ++i;
+
+ static foreach(type; typel)
+ {
+ mixin(type ~ " " ~ type ~ "painConstant = new " ~ type ~ "();");
+ }
+
+ static foreach(type; typel)
+ {
+ if(type == val)
+ {
+ try
+ {
+ mixin(type ~ "painConstant.fromDstring(val3);");
+ }
+ catch(Exception e)
+ {
+ return false;
+ }
+ goto endf;
+ }
+ }
+ endf:
+
+ if(isOp)
+ {
+ static foreach(type; typel)
+ {
+ if(currOperand == type)
+ {
+ static foreach(type2; typel)
+ {
+ if(type2 == val)
+ {
+ mixin("if(!" ~ type ~ "painConstant.applyOp(currOp, " ~ type2 ~ "painConstant))
+ {
+ return false;
+ }");
+ goto endf2;
+ }
+ }
+ }
+ }
+ endf2:
+ isOp = false;
+ currOperand = val;
+ }
+
+ if(def[i] != d('%'))
+ {
+ return false;
+ }
+ ++i;
+
+ break;
+ case d('('):
++indentation;
++i;
break;
@@ -445,10 +525,10 @@ bool validateFunction(in dstring func, in dstring def) @trusted
do
{
tempstr ~= def[i];
- if(((def[i] != d('x')) && (def[i] != d('\\'))) && (def[i] != d('(') && def[i] != d(' ')))
+ if(((def[i] != d('x')) && (def[i] != d('\\'))) && (def[i] != d('(') && def[i] != d(' ')) && def[i] != d('%'))
++i;
}
- while((def[i] != d('x') && def[i] != d('\\')) && (def[i] != d('(') && def[i] != d(' ')));
+ while((def[i] != d('x') && def[i] != d('\\')) && (def[i] != d('(') && def[i] != d(' ')) && def[i] != d('%'));
/+if(def[i] != d('(')) // Operators+/
currOp = tempstr.idup;
@@ -525,6 +605,10 @@ bool validateFunction(in dstring func, in dstring def) @trusted
def = "x1*x2"d;
func = "(Number,Number)(Number)"d;
assert(registerFunction("f"d, func, def));
+
+ // Issue 16
+ def = "x1*x2*%Number(5+0i)%"d;
+ assert(validateFunction(func, def));
//Functions within functions were too hard to implement, so we removed them.
//def = "x1* f(x1,x2)(Number)"d;
//assert(validateFunction(func, def));
@@ -579,7 +663,7 @@ import std.typecons : Tuple;
/***********************************************************
* Executes a function.
- *
+ * TODO: Constants
*
* Params:
* func =
@@ -607,10 +691,23 @@ Return executeFunction(Return, Mtypes...)(in dstring func, in Tuple!(Mtypes) arg
size_t indentation = 0;
size_t[size_t] parenNum;
parenNum[0] = 0;
+ bool bruhx = false;
for(size_t i = 0; i < funcList[func].length; ++i) // Organize the function into parentheses groups.
{
switch(funcList[func][i])
{
+ case d('%'): // Bug-free
+ do
+ {
+ parens[indentation][parenNum[indentation]] ~= funcList[func][i];
+ ++i;
+ if(i >= funcList[func].length)
+ break;
+ }
+ while(funcList[func][i] != d(')'));
+ parens[indentation][parenNum[indentation]] ~= ")%"d;
+ ++i;
+ break;
case d('('):
++indentation;
if(indentation !in parens)
@@ -664,24 +761,82 @@ Return executeFunction(Return, Mtypes...)(in dstring func, in Tuple!(Mtypes) arg
}
foreach(ref key; keys2)
key.sort!"b > a";
+
+
foreach_reverse(key; keys)
{
debug import std.stdio;
- size_t currParen = 0;
foreach(key2; keys2[key])
{
dstring currOp = ""d;
dstring currType = ""d;
bool firstOperand = false;
+ size_t currParen = 0;
for(size_t i = 0; i < parens[key][key2].length; i++)
{
- //Get to work executing the function.
+ // Get to work executing the function.
switch(parens[key][key2][i])
{
- case d('('): //Parentheses, also known as a pain in the ass.
+ case d('%'): // Constants (Issue #16)
+ dstring tempType = ""d;
+ ++i;
+ do
+ {
+ tempType ~= parens[key][key2][i];
+ ++i;
+ }
+ while(parens[key][key2][i] != d('('));
+
+ dstring val = ""d;
+ ++i;
+ do
+ {
+ val ~= parens[key][key2][i];
+ ++i;
+ }
+ while(parens[key][key2][i] != d(')'));
+ ++i;
+
+ if(!firstOperand)
+ {
+ firstOperand = true;
+ static foreach(type; typel)
+ {
+ if(type == tempType)
+ {
+ mixin("temp" ~ type ~ "[key][key2] = new " ~ type ~ "();");
+ mixin("temp" ~ type ~ "[key][key2].fromDstring(val);");
+ }
+ }
+ currType = tempType;
+ }
+ else
+ {
+ bool c;
+ static foreach(type2; typel)
+ {
+ if(type2 == tempType)
+ {
+ mixin(type2 ~ " constant = new " ~ type2 ~ "();");
+ constant.fromDstring(val);
+ static foreach(type; typel)
+ {
+ if(type == currType)
+ {
+ mixin("c = temp" ~ type ~ "[key][key2].applyOp(currOp, constant);");
+ }
+ }
+ }
+ }
+ assert(c);
+ currOp = ""d;
+ }
+ break;
+ case d('('): // Parentheses, also known as a pain in the ass.
static foreach(type; typel)
{
- mixin("if(temp" ~ type ~ "[key+1][currParen] !is null)
+ mixin("
+ if(temp" ~ type ~ "[key+1][currParen] !is null)
{
currType = type;
if(!firstOperand)
@@ -766,8 +921,6 @@ Return executeFunction(Return, Mtypes...)(in dstring func, in Tuple!(Mtypes) arg
--i;
currOp = ""d;
break;
- case d('\\'): //Operators, such as derivatives, sums, and integrals.
- break;
default: //Type specific operators.
do
{
@@ -777,7 +930,7 @@ Return executeFunction(Return, Mtypes...)(in dstring func, in Tuple!(Mtypes) arg
break;
}
while(parens[key][key2][i] != d('\\') && parens[key][key2][i] != d('x') && parens[key][key2][i]
- != d('('));
+ != d('(') && parens[key][key2][i] != d('%'));
--i;
}
}
@@ -841,4 +994,12 @@ Return executeFunction(Return, Mtypes...)(in dstring func, in Tuple!(Mtypes) arg
assert(registerFunction("ree"d, func, def));
i = executeFunction!(Number, Number, Number, Number, Number)("ree(Number,Number,Number,Number)(Number)"d, b);
assert(i.toDstring == "6+0i"d, cast(char[])i.toDstring.dup);
+ assert(removeFunction("ree"d, func));
+
+ // Issue 16
+
+ def ~= "+%Number(5+0i)%"d;
+ assert(registerFunction("ree"d, func, def));
+ i = executeFunction!(Number, Number, Number, Number, Number)("ree(Number,Number,Number,Number)(Number)"d, b);
+ assert(i.toDstring == "11+0i"d, cast(char[])i.toDstring.dup);
}
diff --git a/source/dutils/math/def.d b/source/dutils/math/def.d
index 229177b..d4dd86d 100644
--- a/source/dutils/math/def.d
+++ b/source/dutils/math/def.d
@@ -65,34 +65,61 @@ abstract class Mtype(T) if(__traits(hasMember, T, "precision"))
{
this.contained = num;
}
- protected:
+ package:
T contained;
}
-/// Define an Operator as used by dutils.math.
-alias Operator = dstring function(dstring[]) @safe;
+/// Trying to workaround it being impossible to have varying template params.
+package import std.variant;
+
+package struct _variant
+{
+ Variant var;
+ dstring hash;
+}
-/// Container for the list of all operators.
-struct Oplist
+/// Container for the operator list:
+struct OpList
{
- Operator opIndex(dstring op) pure @safe const shared
+ Variant opIndex(dstring hash)
{
- return this.ops[op];
+ Variant ret = false;
+ foreach(op; ops)
+ {
+ if(op.hash == hash)
+ return op.var;
+ }
+ return ret;
}
- auto opBinaryRight(string op)(dstring key) pure @safe const shared if(op == "in" || op == "!in")
+ bool opBinaryRight(string op)(inout(dchar)[] key) @trusted if(op == "in" || op == "!in")
{
- mixin("return key " ~ op ~ " ops;");
+ static if(op == "in")
+ const k = true;
+ else
+ const k = false;
+ foreach(op; ops)
+ {
+ if(op.hash == cast(dstring)key)
+ return k;
+ }
+ return !k;
}
- auto keys() @safe
+ auto keys() const @trusted @property
{
- return this.ops.keys;
+ dstring[] _keys;
+ foreach(op; ops)
+ {
+ _keys ~= op.hash;
+ }
+ return _keys;
}
package:
- Operator[dstring] ops;
+ _variant[] ops;
}
-/// The list of all operators.
-package shared Oplist opList;
+/// The list of all special functions/operators.
+package OpList opList;
+
/// Container for the function list.
struct Funclist
@@ -105,7 +132,7 @@ struct Funclist
{
mixin("return cast(dstring)key " ~ op ~ " funcs;");
}
- auto keys()
+ auto keys() pure nothrow @safe
{
return this.funcs.keys;
}
@@ -119,4 +146,7 @@ package shared Funclist funcList;
package import dutils.math.number; // I'm not sure why this line is here, but I'm too scared to touch it.
/// The list of all types, that has to be kept here and continously updated.
-enum dstring[] typel = ["Number"]; // Too bad that complete modular programming is impossible in D.
+enum dstring[] typel = ["Number"d]; // Too bad that complete modular programming is impossible in D.
+
+/// The list of all types, that has to be kept here and continously updated (string version):
+enum string[] typels = ["Number"];
diff --git a/source/dutils/math/number.d b/source/dutils/math/number.d
index 976e5e5..cbf3bea 100644
--- a/source/dutils/math/number.d
+++ b/source/dutils/math/number.d
@@ -270,7 +270,7 @@ class Number : Mtype!NumberContainer
* op =
* The operator to apply.
* rhs =
- * THe right hand side of the expression.
+ * The right hand side of the expression.
* Returns:
* The result of the expression.
*/
@@ -295,6 +295,21 @@ class Number : Mtype!NumberContainer
{
return (this.contained == rhs.contained);
}
+
+ /*******************************************
+ * Compare the magnitude (currently only
+ * the real part) of two numbers.
+ *
+ * Params:
+ * rhs =
+ * The number to compare `this` to.
+ * Returns:
+ * Whether the result of the comparison is true.
+ */
+ bool opCmp(string op)(in Number rhs) pure const @safe nothrow
+ {
+ return this.contained.opCmp!op(rhs.contained);
+ }
}
///
@@ -517,6 +532,48 @@ struct NumberContainer
return ((this.val == rhs.val) && (this.ival == rhs.ival))
&& ((this.pow10 == rhs.pow10) && (this.precision == rhs.precision));
}
+
+ /********************************************
+ * Compare two Numbers.
+ * TODO: Once exponentiation has been added,
+ * use magnitude.
+ *
+ * Params
+ * rhs =
+ * The Number to compare to.
+ * Returns:
+ * The value of the comparison
+ */
+ bool opCmp(string op)(in NumberContainer rhs) pure @safe nothrow const
+ {
+ BigInt a = this.val;
+ BigInt b = rhs.val;
+
+ if(rhs.pow10 < 0)
+ {
+ a *= BigInt(10)^^(-rhs.pow10);
+ b *= BigInt(10)^^(-rhs.pow10);
+ }
+ else
+ {
+ a *= BigInt(10)^^rhs.pow10;
+ b *= BigInt(10)^^rhs.pow10;
+ }
+
+ if(this.pow10 < 0)
+ {
+ a *= BigInt(10)^^(-this.pow10);
+ b *= BigInt(10)^^(-this.pow10);
+ }
+ else
+ {
+ a *= BigInt(10)^^this.pow10;
+ b *= BigInt(10)^^this.pow10;
+ }
+
+ mixin("return a " ~ op ~ " b;");
+ }
+
package:
BigInt val;
BigInt ival;
@@ -526,3 +583,6 @@ struct NumberContainer
int pow10;
ulong precision;
}
+
+
+
diff --git a/source/dutils/math/package.d b/source/dutils/math/package.d
index aa6266b..67b1247 100644
--- a/source/dutils/math/package.d
+++ b/source/dutils/math/package.d
@@ -10,12 +10,12 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .*/
-/** Copyright: 2022, Ruby The Roobster*/
-/**Author: Ruby The Roobster, */
-/**Date: January 19, 2022*/
-/** License: GPL-3.0**/
+/** Copyright: 2022-2023, Ruby The Roobster*/
+/**Author: Ruby The Roobster, */
+/**Date: March 6, 2023*/
+/** License: GPL-3.0*/
-///Dutils math library
+/// Dutils math library
module dutils.math;
version(DLL)
@@ -31,3 +31,5 @@ else
import dutils.math.core;
/// Implements complex numbers for the math library.
import dutils.math.number;
+/// Implements logarithms
+import dutils.math.log;