1 /*This program is free software: you can redistribute it and/or modify
2 it under the terms of the GNU General Public License as published by
3 the Free Software Foundation, either version 3 of the License, or
4 (at your option) any later version.
5 
6 This program is distributed in the hope that it will be useful,
7 but WITHOUT ANY WARRANTY; without even the implied warranty of
8 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9 GNU General Public License for more details.
10 
11 You should have received a copy of the GNU General Public License
12 along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
13 /** Copyright: 2022-2023, Ruby The Roobster*/
14 /**Author: Ruby The Roobster, <michaeleverestc79@gmail.com>*/
15 /**Date: January 16, 2023*/
16 /** License:  GPL-3.0*/
17 
18 /// Core part of the dutils math library.
19 module dutils.math.core;
20 
21 public import dutils.math.def;
22 
23 version(DLL)
24 {
25     mixin("export:");
26 }
27 
28 else
29 {
30     mixin("public:");
31 }
32 
33 /********************************************************
34  * Registers a valid function that doesn't already exist.
35  *
36  * Params:
37  *     name =
38  *         The function name.
39  *     func =
40  *         The function parameters and return type.
41  *     def =
42  *         The definition of the function.
43  *
44  * Returns:
45  *     Whether the function was registered.
46  */
47  
48 bool registerFunction(in dstring name, in dstring func, in dstring def) @safe
49 {
50     auto ret = validateFunction(func, def) && name ~ func !in funcList.funcs;
51     if(ret)
52         funcList.funcs[name ~ func] = def;
53     return ret;
54 }
55 
56 ///
57 @safe unittest
58 {
59     dstring func = "(Number)(Number)"d;
60     dstring def = "x1"d;
61     dstring name = "f"d;
62     assert(registerFunction(name, func, def));
63     assert(!registerFunction(name, func, def)); //No registering an already-existing function.
64 }
65 
66 /**************************************************
67  * Removes a function provided that it exists.
68  *
69  * Params:
70  *     name =
71  *         The function name.
72  *     func =
73  *         The function parameters and return type.
74  *
75  * Returns:
76  *     Whether the function exists or not.
77  */
78 bool removeFunction(in dstring name, in dstring func) @safe
79 {
80     if(name ~ func in funcList.funcs)
81     {
82         funcList.funcs.remove(name ~ func);
83         return true;
84     }
85     return false;
86 }
87 
88 ///
89 @safe unittest
90 {
91     dstring func = "(Number,Number)(Number)d";
92     dstring def = "x1*x2"d;
93     dstring name = "f"d;
94     assert(registerFunction(name, func, def)); //Valid, a different function under the same name is a different function in the library's eyes.
95     assert(removeFunction(name, func));
96     assert(!removeFunction(name, func)); //Cannot remove a non-existent function.
97 }
98 
99 /*******************************************
100  * Validates a function.
101  *
102  * Params:
103  *     func =
104  *        The function name and parameters.
105  *     def =
106  *          The definition of the function.
107  * Returns:
108  *     Whether the function is valid or not.
109  */
110  
111 bool validateFunction(in dstring func, in dstring def) @trusted
112 {
113     try
114     {
115         dstring[] params;
116         dstring[] returni;
117         size_t i = 0;
118         getParamsReturns(params, func, i); //Get the function return type.
119         dstring returns;
120         ++i; //Make sure to get out of the closing parenthesis.
121         getParamsReturns(returni, func, i); //Get the parameter types.
122         static foreach(type; typel)
123         {
124             mixin(type ~ "[] " ~ type ~ "ParamList;");
125             mixin(type ~ "[] " ~ type ~ "OperandList;");
126         }
127         returns = returni[0];
128         //Make sure that we know the types of of each parameter.
129         dstring[] paramTypeList = [];
130         dstring returnType = null;
131         for(size_t j = 0; j < params.length; j++)
132         {
133             Switch: final switch(params[j])
134             {
135                 static foreach(type; typel)
136                 {
137                     case type:
138                         mixin("++" ~ type ~ "ParamList.length;");
139                         ++paramTypeList.length;
140                         paramTypeList[j] = type;
141                         break Switch;
142                 }
143             }
144         }
145         //Get the return type.
146         for(size_t j = 0; j < 1; j++)
147         {
148             Switch2: final switch(returns)
149             {
150                 static foreach(type; typel)
151                 {
152                     case type:
153                         returnType = type;
154                         break Switch2;
155                 }
156             }
157         }   
158         //This gets scary.
159         //Buckle up.
160         bool isOperand = false;
161         bool isOp = false;
162         dstring currOperand;
163         dstring currOp;
164         dstring prevOp;
165         dstring tempNum;
166         import std.uni : isNumber;
167         i = 0;
168         long indentation = 0;
169         do
170         {
171             tempNum = ""d;
172             switch(def[i])
173             {
174                 case d('('):
175                     ++indentation;
176                     ++i;
177                     break;
178                 case d(')'):
179                     --indentation;
180                     ++i;
181                     break;
182                 case d('x'):
183                     if(isOperand && !isOp)
184                         return false;
185                     prevOp = currOperand.idup;
186                     isOperand = true;
187                     ++i;
188                     if(!def[i].isNumber) //Forced indexing of parameters.
189                         return  false;
190                     do
191                     {
192                         tempNum ~= def[i];
193                         if(i == def.length-1)
194                         {
195                             if(def[i].isNumber)
196                                 break;
197                             else
198                                 --i;
199                         }
200                         ++i;
201                     }
202                     while(def[i].isNumber);
203                     import std.conv : to;
204                     dstring tempType;
205                     tempType = paramTypeList[to!size_t(tempNum) - 1];
206                     Switch3: final switch(tempType)
207                     {
208                         static foreach(type; typel)
209                         {
210                             case type:
211                                 mixin(type ~ "OperandList ~= new "d ~ type ~ "();"d);
212                                 currOperand = type;
213                                 break Switch3;
214                         }
215                     }
216                     //Op verification.
217                     if(isOp) //Speed on this gonna be O(n^2), where n is typel.keys.length, both compilation and runtime.
218                     {
219                         Switch4: final switch(currOperand)
220                         {
221                             static foreach(type; typel)
222                             {
223                                 case type:
224                                     Switch5: final switch(prevOp)
225                                     {
226                                         static foreach(type2; typel)
227                                         {
228                                             case type2:
229                                                 mixin("bool b = opCheckCrap(" ~ type2 ~ "OperandList[0], "
230                                                 ~ type ~ "OperandList[0], currOp);");
231                                                 if(!b)
232                                                     return false;
233                                                 break Switch5;
234                                         }
235                                     }
236                                     break Switch4;
237                             }
238                         }
239                     }
240                     isOp = false;
241                     if(i == def.length - 1)
242                     {
243                         if(!def[i].isNumber && def[i] != d(')'))
244                         {
245                             return false;
246                         }
247                         else if(def[i].isNumber)
248                             ++i;
249                     }
250                     break;
251                 case d('\\'): //May possibly be even worse than above, as it denotes a special operator. Also ridden with bugs, but I ain't fixin' that until this code is actually used.
252                     if(isOperand && !isOp)
253                         return false;
254                     isOperand = true;
255                     dstring opName;
256                     prevOp = currOperand.idup;
257                     ++i;
258                     do
259                     {
260                         opName ~= def[i];
261                         ++i;
262                     }
263                     while(def[i] != d('('));
264                     //Get the  type of the operand as it is needed for later.
265                     dstring tempTypeCrap = ""d;
266                     do
267                     {
268                         tempTypeCrap ~= def[i];
269                         ++i;
270                     }
271                     while(def[i] != d(')'));
272                     ++i;
273                     currOperand = tempTypeCrap;
274                     if(def[i] != d('('))
275                         return false;
276                     dstring[] tempOps = [];
277                     ++i;
278                     do
279                     {
280                         ++tempOps.length;
281                         do
282                         {
283                             tempOps[$-1] ~= def[i];
284                             ++i;
285                         }
286                         while(def[i] != d(',') && def[i] != d(')')); //Just in case.
287                         if(def[i] == d('(')) //Fix bug involving functions being broken
288                         {
289                             for(ubyte j = 0; j < 2; j++)
290                             {
291                                 do
292                                 {
293                                     tempOps[$-1] ~= def[i];
294                                     ++i;
295                                 }
296                                 while(def[i] != d(')'));
297                             }
298                             tempOps[$-1] ~= d(')');
299                             ++i;
300                         }
301                         if(def[i] != d(',') && def[i] != d(')'))
302                             return false;
303                     }
304                     while(def[i] != d(')'));
305                     ++i;
306                     if(def[i] != '\\')
307                         return false;
308                     //Get the types of the parameters referenced by the special operator call.
309                     dstring[] tempTypes = [];
310                     dstring func2;
311                     foreach(tempOp; tempOps)
312                     {
313                         size_t k = 1;
314                         tempNum = ""d;
315                     
316                         if(tempOp[0] == d('x'))
317                         {
318                             tempNum ~= d('x');
319                             do
320                             {
321                                 tempNum ~= tempOp[k];
322                                 ++k;
323                             }
324                             while(tempOp[k] != d('(') && k != tempOp.length - 1);
325                             if(k == tempOp.length -1)
326                                 k = 1;
327                             else
328                                 goto Func;
329                         }
330                         tempNum = "";
331                         func2 = "("d;
332                         do
333                         {
334                             tempNum ~= tempOp[k];
335                             if(!tempOp[k].isNumber)
336                                 return false;
337                             k++;
338                         }
339                         while(k != tempOp.length);
340                         ++tempTypes.length;
341                         import std.conv : to;
342                         tempTypes[$-1] = paramTypeList[to!size_t(tempNum) - 1];
343                         continue;
344                     
345                         Func:
346                         k = 1;
347                         //Function type header.
348                         ++tempTypes.length;
349                         tempTypes[$-1] = ""d;
350                         //Get the function's return type (very easy, considering how it is specified).
351                         do
352                         {
353                             tempNum ~= tempOp[k];
354                             ++k;
355                         }
356                         while(tempOp[k] != d(')'));
357                         ++k;
358                         tempTypes[$-1] = tempNum;
359                         func2 ~= tempNum;
360                         func2 ~= ")("d;
361                         tempNum = ""d;
362 
363                         //Get the types of the function's parameters.
364                         dstring[] tempTypes2 = [];
365                         do
366                         {
367                             ++k;
368                             ++tempTypes2.length;
369                             do
370                             {
371                                 tempTypes2[$-1] ~= tempOp[k];
372                                 ++k;
373                             }
374                             while(tempOp[k] != d(',') && tempOp[k] != d(')'));
375                         }
376                         while(tempOp[k] != d(')'));
377 
378                         foreach(type; tempTypes2)
379                         {
380                             size_t l = 1;
381                             if(type[0] != d('x'))
382                                 return false;
383                             do
384                             {
385                                 tempNum ~= type[l];
386                                 if(!type[l].isNumber)
387                                     return false;
388                                 ++l;
389                             }
390                             while(l < type.length);
391 
392                             func2 ~= paramTypeList[to!size_t(tempNum) - 1];
393                             func2 ~= d(',');
394                         }
395 
396                         --func2.length;
397                         func2 ~= d(')');
398                         if(func2 !in funcList)
399                             return false;
400                     }
401                     //Verify that the types match.
402                     opName ~= "("d;
403                     foreach(type; tempTypes)
404                     {
405                         opName ~= type;
406                         opName ~= ","d;
407                     }
408                     --opName.length;
409                     opName ~= ")"d;
410                     if(opName !in opList)
411                         return false;
412                     currOperand = opName;
413                     if(isOp) //Speed on this gonna be O(n^2), where n is typel.keys.length, both compilation and runtime.
414                     {
415                         Switch6: final switch(currOperand)
416                         {
417                             static foreach(type; typel)
418                             {
419                                 case type:
420                                     Switch7: final switch(prevOp)
421                                     {
422                                         static foreach(type2; typel)
423                                         {
424                                             case type2:
425                                                 mixin("bool b = opCheckCrap(" ~ type2 ~ "OperandList[0], "
426                                                 ~ type ~ "OperandList[0], currOp);");
427                                                 if(!b)
428                                                     return false;
429                                                 break Switch7;
430                                         }
431                                     }
432                                     break Switch6;
433                             }
434                         }
435                         ++i;
436                     }
437                     isOp = false;
438                     break;
439                 default: // Operators and functions
440                     if(isOp)
441                         return false;
442                     isOp = true;
443                     isOperand = false;
444                     dchar[] tempstr = [];
445                     do
446                     {
447                         tempstr ~= def[i];
448                         if(((def[i] != d('x')) && (def[i] != d('\\'))) && (def[i] != d('(') && def[i] != d(' ')))
449                              ++i;
450                     }
451                     while((def[i] != d('x') && def[i] != d('\\')) && (def[i] != d('(') && def[i] != d(' ')));
452                     
453                     /+if(def[i] != d('(')) // Operators+/
454                         currOp = tempstr.idup;
455                     /+else //  Oh shit oh fuck a function (THIS CODE DOESN'T WROK AND WILL BE FIXED LATER)
456                     {
457                         // We need to get the types of its arguments
458                         tempstr = tempstr[0 .. $-1].dup;
459                         dchar[] tempargs = [];
460                         ++i;
461                         do
462                         {
463                             tempargs ~= def[i];
464                             ++i;
465                         }
466                         while(def[i] != d(')'));
467 
468                         import std.algorithm;
469                         auto indexes = tempargs.splitter(d(','));
470                         foreach(ref index; indexes)
471                             index = index[1 .. $].dup;
472 
473                         size_t[] indices = [];
474                         import std.conv;
475                         foreach(index; indexes)
476                             indices ~= index.to!size_t;
477 
478                         dstring[] temptypes;
479                         foreach(indice; indices)
480                             temptypes ~= paramTypeList[indice];
481 
482                             
483                     }+/
484             }
485         }
486         while(i < def.length);
487         if((isOp || !isOperand) || indentation != 0) //If there are no other syntax errors, ensure the following.
488         {
489             return false;
490         }
491         return true;
492     }
493     catch(Exception e)
494     {
495         return false;
496     }
497 }
498 
499 ///
500 @safe unittest
501 {
502     /********************************************
503     * List of stuff that is invalid but crashes:
504     *
505     *     Whitespace
506     *     Preceding Operators
507     *     Invalid Operators and Characters
508     */ 
509     dstring func = "(Number,Number)(Number)"d;
510     dstring def = "x1*x2"d;
511     assert(validateFunction(func, def));
512     func = "(Number,Number,Number)(Number)"d;
513     def = "x1*x2+x3"d;
514     assert(validateFunction(func, def));
515     def = "(x1"d;
516     assert(!validateFunction(func, def));
517     def = "(x1)"d;
518     assert(validateFunction(func, def));
519     def = "x1)"d;
520     assert(!validateFunction(func, def));
521     def = "x1x2"d;
522     assert(!validateFunction(func, def));
523     def = "x1+"d;
524     assert(!validateFunction(func, def));
525     def = "x1*x2"d;
526     func = "(Number,Number)(Number)"d;
527     assert(registerFunction("f"d, func, def));
528     //Functions within functions were too hard to implement, so we removed them.
529     //def =  "x1* f(x1,x2)(Number)"d;
530     //assert(validateFunction(func, def));
531 }
532 
533 /************************************
534  * Converts a char to a dchar.
535  *
536  * Params:
537  *     c =
538  *        The char to convert.
539  * Returns:
540  *     The char converted to a dchar.
541  */
542 package dchar d(char c) pure @safe
543 {
544     return cast(dchar)c;
545 }
546 
547 private void getParamsReturns(ref dstring[] input, immutable dstring func, ref size_t i) pure @safe //Get the types of the function parameters and the return types.
548 in
549 {
550     assert(func[i] == d('('));
551 }
552 do
553 {
554     ++i;
555     do
556     {
557         ++input.length;
558         do
559         {
560             input[$-1] ~= func[i];
561             ++i;
562         }
563         while(func[i] != d(',') && func[i] != d(')'));
564 
565         if(func[i] != d(')'))
566             ++i;
567     }
568     while(func[i] != d(')'));
569 }
570 
571 //Function that checks whether using op currOp with type as its lhs and type2 as its rhs is valid.
572 private bool opCheckCrap(W, X)(W type, X type2, dstring currOp)//Please god let W and X be inferred from the arguments please.
573 {
574     return type.applyOp(currOp, type2);
575 }
576 
577 ///We need the tuple type for executeFunction.
578 import std.typecons : Tuple;
579 
580 /***********************************************************
581  * Executes a function.
582  *
583  *
584  * Params:
585  *     func =
586  *         The function to execute.
587  *     args =
588  *         The function arguments, expressed as a tuple.
589  *     precision =
590  *         The precision of the returned Mtype.
591  * Returns:
592  *     The result of calling the function.
593  */
594 Return executeFunction(Return, Mtypes...)(in dstring func, in Tuple!(Mtypes) args, ulong precision = 18L) @safe
595 {
596     debug import std;
597     import std.uni : isNumber;
598     
599     // Create temporary stores for each mtype.
600     static foreach(type; typel)
601     {
602         mixin(type ~ "[size_t][size_t]temp" ~ type ~ ";");
603     }
604     dstring[size_t][size_t] parens;
605     parens[0] = [0 : ""d];
606     // Parse the function
607     size_t indentation = 0;
608     size_t[size_t] parenNum;
609     parenNum[0] = 0;
610     for(size_t i = 0; i < funcList[func].length; ++i) // Organize the function into parentheses groups.
611     {
612         switch(funcList[func][i])
613         {
614             case d('('):
615                 ++indentation;
616                 if(indentation !in parens)
617                 {
618                     parens[indentation] = [0 : ""d];
619                     parenNum[indentation] = 0;
620                 }
621                 break;
622             case d(')'):
623                 ++parenNum[indentation];
624                 --indentation;
625                 if(i+1 == funcList[func].length)
626                     parens[indentation][parenNum[indentation]] ~= "()"d;
627                 else if(funcList[func][i+1] == d(')'))
628                     parens[indentation][parenNum[indentation]] ~= "()"d;
629                 break;
630             case d('x'):
631                 do
632                 {
633                     parens[indentation][parenNum[indentation]] ~= funcList[func][i];
634                     ++i;
635                     if(i >= funcList[func].length)
636                         break;
637                 }
638                 while(funcList[func][i].isNumber);
639                 --i;
640                 break;
641             case d('\\'):
642                 do
643                 {
644                     parens[indentation][parenNum[indentation]] ~= funcList[func][i];
645                     ++i;
646                 }
647                 while(funcList[func][i] != d('\\'));
648                 break;
649             default:
650                 if(funcList[func][i-1] == d(')'))
651                     parens[indentation][parenNum[indentation]] ~= "()"d;
652                 parens[indentation][parenNum[indentation]] ~= funcList[func][i];
653         }
654     }
655     //Sort the keys
656     auto keys = parens.keys;
657     import std.algorithm;
658     keys.sort!"b > a";
659     size_t[][] keys2;
660     foreach(key; keys)
661     {
662         ++keys2.length;
663         keys2[$-1] = parens[key].keys.dup;
664     }
665     foreach(ref key; keys2)
666         key.sort!"b > a";
667     foreach_reverse(key; keys)
668     {
669         debug import std.stdio;
670         size_t currParen = 0;
671         foreach(key2; keys2[key])
672         {
673             dstring currOp = ""d;
674             dstring currType = ""d;
675             bool firstOperand = false;
676             for(size_t i = 0; i < parens[key][key2].length; i++)
677             {
678                 //Get to work executing the function.
679                 switch(parens[key][key2][i])
680                 {
681                     case d('('): //Parentheses, also known as a pain in the ass.
682                         static foreach(type; typel)
683                         {
684                             mixin("if(temp" ~ type ~ "[key+1][currParen] !is null)
685                             {
686                                 currType = type;
687                                 if(!firstOperand)
688                                 {
689                                     temp" ~ type ~ "[key][key2] = new " ~ type ~ "(temp" ~ type ~ "[key+1][currParen].val);
690                                 }
691                             }");
692                         }
693                         if(firstOperand)
694                         {
695                             bool c;
696                             static foreach(type; typel)
697                             {
698                                 if(type == currType)
699                                     mixin("c = temp" ~ type ~ "[key][key2].applyOp(currOp, temp" ~ type ~ "[key+1][currParen]);");
700                             }
701                             assert(c);
702                         }
703                         else
704                         {
705                             firstOperand = true;
706                         }
707                         ++i;
708                         ++currParen;
709                         currOp = ""d;
710                         break;
711                     case d('x'): //Input
712                         ++i;
713                         dstring tempIndex = ""d;
714                         dstring tempType = ""d;
715                         do
716                         {
717                             tempIndex ~= parens[key][key2][i];
718                             ++i;
719                             if(i == parens[key][key2].length)
720                                 break;
721                         }
722                         while(parens[key][key2][i].isNumber);
723 
724                         static foreach(arg; 0 .. args.length) // Yes, this little fucker again.  You'll be meeting him alot in this file.
725                         {
726                             if(arg + 1 == to!size_t(tempIndex)) // Generates YandereDev spaghetti code, there is no workaround for this.
727                                 tempType = Unconst!(typeof(args[arg])).stringof;
728                         }
729                         
730                         if(!firstOperand)
731                         {
732                             firstOperand = true;
733                             static foreach(type; typel)
734                             {
735                                 if(type == tempType)
736                                 {
737                                     static foreach(arg; 0 .. args.length)
738                                     {
739                                         if(arg + 1 == to!size_t(tempIndex))
740                                         {
741                                             mixin("temp" ~ type ~ "[key][key2] = new " ~ type ~ "(args[arg].val);");
742                                         }
743                                     }
744                                 }
745                             }
746                             currType = tempType;
747                         }
748                         else
749                         {
750                             bool c;
751                             static foreach(type; typel) // O(x * y) Compile Time.  D is the king of metaprogramming, but it's quite expensive.
752                             {
753                                 if(type == currType)
754                                 {
755                                     static foreach(arg; 0 .. args.length)
756                                     {
757                                         if(arg + 1 == to!size_t(tempIndex))
758                                         {
759                                             mixin("c = temp" ~ type ~ "[key][key2].applyOp(currOp, args[arg]);");
760                                         }
761                                     }
762                                 }
763                             }
764                             assert(c);
765                         }
766                         --i;
767                         currOp = ""d;
768                         break;
769                     case d('\\'): //Operators, such as derivatives, sums, and integrals.
770                         break;
771                     default: //Type specific operators.
772                         do
773                         {
774                             currOp ~= parens[key][key2][i];
775                             ++i;
776                             if(i == parens[key][key2].length)
777                                 break;
778                         }
779                         while(parens[key][key2][i] != d('\\') && parens[key][key2][i] != d('x') && parens[key][key2][i]
780                         != d('('));
781                         --i;
782                 }
783             }
784         }
785     }
786     static foreach(type; typel)
787     {
788         if(type == Return.stringof)
789             mixin("return temp" ~ type ~ "[0][0];");
790     }
791     assert(0, "RubyTheRoobster sucks at programming... Please report this error.");
792 }
793 
794 ///
795 @trusted unittest
796 {
797     Tuple!(Number, Number, Number) a;
798     a[0] = new Number(NumberContainer(BigInt(2), BigInt(0), 0L, 18UL));
799     a[1] = new Number(NumberContainer(BigInt(3), BigInt(0), 0L, 18UL));
800     a[2] = new Number(NumberContainer(BigInt(1), BigInt(0), 0L, 18UL));
801     dstring func = "(Number,Number,Number)(Number)"d;
802     dstring def = "x1*x2*x3"d;
803     auto r = registerFunction("ree"d, func, def);
804     assert(r);
805     auto i = executeFunction!(Number, Number, Number, Number)("ree(Number,Number,Number)(Number)"d, a);
806     assert(i.toDstring == "6+0i"d, cast(char[])i.toDstring.dup);
807     assert(removeFunction("ree"d, func));
808     def = "(x1*x2)*x3"d;
809     assert(registerFunction("ree"d, func, def));
810     i = executeFunction!(Number, Number, Number, Number)("ree(Number,Number,Number)(Number)"d, a);
811     assert(i.toDstring == "6+0i"d, cast(char[])i.toDstring.dup);
812     // All of the above is working
813     def = "x1*(x2*x3)"d;
814     assert(removeFunction("ree"d, func));
815     assert(registerFunction("ree"d, func, def));
816     i = executeFunction!(Number, Number, Number, Number)("ree(Number,Number,Number)(Number)"d, a);
817     assert(i.toDstring == "6+0i"d, cast(char[])i.toDstring.dup);
818     def = "(x1*x2)*x3*x4"d;
819     assert(removeFunction("ree"d, func));
820     func = "(Number,Number,Number,Number)(Number)"d;
821     assert(registerFunction("ree"d, func, def));
822     Tuple!(Number, Number, Number, Number) b;
823     b[0] = new Number(NumberContainer(BigInt(2), BigInt(0), 0L, 18UL));
824     b[1] = new Number(NumberContainer(BigInt(3), BigInt(0), 0L, 18UL));
825     b[2] = new Number(NumberContainer(BigInt(1), BigInt(0), 0L, 18UL));
826     b[3] = new Number(b[2].val);
827     i = executeFunction!(Number, Number, Number, Number, Number)("ree(Number,Number,Number,Number)(Number)"d, b);
828     assert(i.toDstring == "6+0i"d, cast(char[])i.toDstring.dup);
829     def = "x1*x2*(x3*x4)"d;
830     assert(removeFunction("ree"d, func));
831     assert(registerFunction("ree"d, func, def));
832     i = executeFunction!(Number, Number, Number, Number, Number)("ree(Number,Number,Number,Number)(Number)"d, b);
833     assert(i.toDstring == "6+0i"d, cast(char[])i.toDstring.dup);
834     def = "x1*(x2)*x3*(x4)"d;
835     assert(removeFunction("ree"d, func));
836     assert(registerFunction("ree"d, func, def));
837     i = executeFunction!(Number, Number, Number, Number, Number)("ree(Number,Number,Number,Number)(Number)"d, b);
838     assert(i.toDstring == "6+0i", cast(char[])i.toDstring.dup);
839     assert(removeFunction("ree"d, func));
840     def = "((x1)*((x2)*(x3)))*x4"d;
841     assert(registerFunction("ree"d, func, def));
842     i = executeFunction!(Number, Number, Number, Number, Number)("ree(Number,Number,Number,Number)(Number)"d, b);
843     assert(i.toDstring == "6+0i"d, cast(char[])i.toDstring.dup);
844 }