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;