From a44d51e557c4549cd742104efdb1a918a910c98a Mon Sep 17 00:00:00 2001
From: Jim <0xJ1M@users.noreply.github.com>
Date: Thu, 19 Mar 2026 19:04:18 +0000
Subject: [PATCH] WIP
---
.../MathEngine/AST/Nodes/NodeFactory.cs | 2 +-
MathEngine/MathEngine/MMU/Memory.cs | 56 ++++
MathEngine/MathEngine/MathEngine.csproj | 7 +
MathEngine/MathEngine/types/BigInteger.cs | 247 ++++++++++++++++
MathEngine/MathEngine/types/DecimalBuilder.cs | 264 ++++++++++++++++++
MathEngine/MathRunner/Program.cs | 9 +-
MathEngine/common/UnmanagedMMU.dll | Bin 0 -> 9216 bytes
7 files changed, 583 insertions(+), 2 deletions(-)
create mode 100644 MathEngine/MathEngine/MMU/Memory.cs
create mode 100644 MathEngine/MathEngine/types/BigInteger.cs
create mode 100644 MathEngine/MathEngine/types/DecimalBuilder.cs
create mode 100644 MathEngine/common/UnmanagedMMU.dll
diff --git a/MathEngine/MathEngine/AST/Nodes/NodeFactory.cs b/MathEngine/MathEngine/AST/Nodes/NodeFactory.cs
index d7115cd..33109ff 100644
--- a/MathEngine/MathEngine/AST/Nodes/NodeFactory.cs
+++ b/MathEngine/MathEngine/AST/Nodes/NodeFactory.cs
@@ -32,7 +32,7 @@ namespace MathEngine.AST.Nodes
return new BinaryNode(LeftBranch, RightBranch, (a, b) => a / b);
case TokenType.Exponentiation:
throw new NotImplementedException("Exponentiation is not supported at this time!");
- return new BinaryNode(LeftBranch, RightBranch, (a, b) => a ^ b);
+ // return new BinaryNode(LeftBranch, RightBranch, (a, b) => a ^ b);
default:
throw new NotImplementedException("Attempted to create a BinaryNode with an invalid operation!");
}
diff --git a/MathEngine/MathEngine/MMU/Memory.cs b/MathEngine/MathEngine/MMU/Memory.cs
new file mode 100644
index 0000000..fe75030
--- /dev/null
+++ b/MathEngine/MathEngine/MMU/Memory.cs
@@ -0,0 +1,56 @@
+using System.Runtime.CompilerServices;
+using System.Text;
+using UnmanagedMMU;
+
+
+namespace MathEngine.MemoryManagement
+{
+ internal unsafe class Memory
+ {
+ public static readonly SegmentedPool ScratchWorkspace;
+
+ static Memory()
+ {
+ ScratchWorkspace = new SegmentedPool(initialSegments: 16, zeroMemory: true);
+ }
+
+ ///
+ /// Copies a range of elements from the specified source buffer to the specified destination buffer.
+ ///
+ /// The buffer to copy elements from.
+ /// The zero-based index in at which copying begins.
+ /// The buffer to copy elements to.
+ /// The zero-based index in at which storing begins.
+ /// The number of elements to copy.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void MemCopy(UInt32* source, int sourceIndex, UInt32* destination, int destinationIndex, int count)
+ {
+ // Calculate byte offsets from element indices
+ UInt32* srcPtr = source + sourceIndex;
+ UInt32* destPtr = destination + destinationIndex;
+
+ Unsafe.CopyBlock(destPtr, srcPtr, (uint)(count * sizeof(UInt32)));
+ }
+
+#if DEBUG
+ ///
+ /// Prints the contents of the unmanaged memory block pointed to by
+ ///
+ /// * pointer to the memory block to print
+ /// The length of the memory block pointed to by
+ private static void PrintUnmanagedArray(T* arr, int len) where T : unmanaged
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("[");
+ for (int i = 0; i < len - 1; i++)
+ {
+ sb.Append(arr[i]);
+ sb.Append(", ");
+ }
+ sb.Append(arr[len - 1]);
+ sb.Append("]");
+ Console.WriteLine(sb.ToString());
+ }
+#endif
+ }
+}
diff --git a/MathEngine/MathEngine/MathEngine.csproj b/MathEngine/MathEngine/MathEngine.csproj
index 5e22321..738b201 100644
--- a/MathEngine/MathEngine/MathEngine.csproj
+++ b/MathEngine/MathEngine/MathEngine.csproj
@@ -5,6 +5,7 @@
enable
enable
True
+ true
@@ -21,4 +22,10 @@
+
+
+ ..\common\UnmanagedMMU.dll
+
+
+
diff --git a/MathEngine/MathEngine/types/BigInteger.cs b/MathEngine/MathEngine/types/BigInteger.cs
new file mode 100644
index 0000000..6ed18df
--- /dev/null
+++ b/MathEngine/MathEngine/types/BigInteger.cs
@@ -0,0 +1,247 @@
+using MathEngine.MemoryManagement;
+
+namespace MathEngine.types
+{
+ public unsafe struct BigInteger
+ {
+ private UInt32* _num;
+ private int _len;
+ private int _sgn;
+
+ private static BigInteger _zero = new BigInteger(0);
+
+ public BigInteger(int val)
+ {
+ _num = Memory.ScratchWorkspace.AllocateAsPointer(16);
+ _len = 1;
+ if (val < 0)
+ {
+ _sgn = -1;
+ _num[0] = (UInt32)(-val);
+ return;
+ }
+ if (val > 0)
+ {
+ _sgn = 1;
+ _num[0] = (UInt32)(val);
+ return;
+ }
+ else
+ {
+ _sgn = 0;
+ _num[0] = 0;
+ return;
+ }
+ }
+
+ public BigInteger(UInt32 val)
+ {
+ _num = Memory.ScratchWorkspace.AllocateAsPointer(16);
+ _len = 1;
+ if (val > 0)
+ {
+ _sgn = 1;
+ _num[0] = val;
+ return;
+ }
+ else
+ {
+ _sgn = 0;
+ _num[0] = 0;
+ return;
+ }
+ }
+
+ private static unsafe int CompareAbs(in BigInteger X, in BigInteger Y)
+ {
+ if (X._len != Y._len)
+ return X._len.CompareTo(Y._len);
+ for (int i = X._len - 1; i >= 0; i--)
+ {
+ if (X._num[i] != Y._num[i])
+ return X._num[i].CompareTo(Y._num[i]);
+ }
+ return 0;
+ }
+
+ public static BigInteger Add(BigInteger X, BigInteger Y)
+ {
+ if (X._sgn == 0)
+ {
+ return Y;
+ }
+ if (Y._sgn == 0)
+ {
+ return X;
+ }
+
+ if (X._sgn == Y._sgn)
+ {
+ int n = X._len;
+ int m = Y._len;
+ int len = Math.Max(n, m);
+ UInt32* res = Memory.ScratchWorkspace.AllocateAsPointer(len + 1);
+
+ ulong carry = 0;
+ int i = 0;
+ for (; i < Math.Min(n, m); i++)
+ {
+ ulong sum = (ulong)X._num[i] + Y._num[i] + carry;
+ res[i] = (UInt32)sum;
+ carry = sum >> 32;
+ }
+
+ UInt32* longer = n > m ? X._num : Y._num;
+ for (; i < len; i++)
+ {
+ ulong sum = (ulong)longer[i] + carry;
+ res[i] = (UInt32)sum;
+ carry = sum >> 32;
+ }
+
+ if (carry != 0)
+ res[len++] = (UInt32)carry;
+
+ BigInteger result;
+ result._num = res;
+ result._len = len;
+ result._sgn = X._sgn;
+ return result;
+ }
+
+ int cmp = CompareAbs(X, Y);
+ BigInteger bigger, smaller;
+ int resultSign;
+
+ if (cmp >= 0)
+ {
+ bigger = X;
+ smaller = Y;
+ resultSign = X._sgn;
+ }
+ else
+ {
+ bigger = Y;
+ smaller = X;
+ resultSign = Y._sgn;
+ }
+
+ // Perform magnitude subtraction: bigger - smaller
+ int nB = bigger._len, nS = smaller._len;
+ UInt32* resSub = Memory.ScratchWorkspace.AllocateAsPointer(nB);
+
+ long borrow = 0;
+ int iSub = 0;
+ for (; iSub < nS; iSub++)
+ {
+ long diff = (long)bigger._num[iSub] - smaller._num[iSub] - borrow;
+ if (diff < 0)
+ {
+ diff += 1L << 32;
+ borrow = 1;
+ }
+ else
+ {
+ borrow = 0;
+ }
+ resSub[iSub] = (UInt32)diff;
+ }
+
+ for (; iSub < nB; iSub++)
+ {
+ long diff = (long)bigger._num[iSub] - borrow;
+ if (diff < 0)
+ {
+ diff += 1L << 32;
+ borrow = 1;
+ }
+ else
+ {
+ borrow = 0;
+ }
+ resSub[iSub] = (UInt32)diff;
+ }
+
+ // Trim leading zeros
+ int lenSub = nB;
+ while (lenSub > 0 && resSub[lenSub - 1] == 0) lenSub--;
+
+ BigInteger resultSub;
+ resultSub._num = resSub;
+ resultSub._len = lenSub;
+ resultSub._sgn = (lenSub == 0) ? 0 : resultSign;
+ return resultSub;
+ }
+
+ public static BigInteger Subtract(BigInteger X, BigInteger Y)
+ {
+ BigInteger negY = Y;
+ negY._sgn = (Y._sgn == 0) ? 0 : -Y._sgn;
+ return Add(X, negY);
+ }
+
+ public static BigInteger Multiply(BigInteger X, BigInteger Y)
+ {
+ int n = X._len, m = Y._len;
+ if (n == 0 || m == 0)
+ {
+ BigInteger zero;
+ zero._num = Memory.ScratchWorkspace.AllocateAsPointer(1);
+ zero._num[0] = 0;
+ zero._len = 0;
+ zero._sgn = 0;
+ return zero;
+ }
+
+ int len = n + m;
+ UInt32* res = Memory.ScratchWorkspace.AllocateAsPointer(len);
+ for (int i = 0; i < len; i++) res[i] = 0;
+
+ for (int i = 0; i < n; i++)
+ {
+ ulong carry = 0;
+ ulong xi = X._num[i];
+ for (int j = 0; j < m; j++)
+ {
+ ulong prod = (ulong)res[i + j] + xi * Y._num[j] + carry;
+ res[i + j] = (UInt32)prod;
+ carry = prod >> 32;
+ }
+ res[i + m] = (UInt32)((ulong)res[i + m] + carry);
+ }
+
+ // Trim leading zeros
+ int finalLen = len;
+ while (finalLen > 0 && res[finalLen - 1] == 0) finalLen--;
+
+ BigInteger result;
+ result._num = res;
+ result._len = finalLen;
+ result._sgn = X._sgn * Y._sgn;
+ return result;
+ }
+
+
+ public static BigInteger Divide(BigInteger X, BigInteger Y)
+ {
+ if (Y == zero)
+ }
+
+ public static BigInteger operator *(BigInteger X, BigInteger Y)
+ {
+ return Multiply(X, Y);
+ }
+
+ public override string ToString()
+ {
+ // create a copy of the underlying number
+ // we want to keep immutability
+ if (_len == 0)
+ {
+ return "0";
+ }
+ return DecimalBuilder.ConvertBigIntegerToString(_num, _len);
+ }
+ }
+
+}
diff --git a/MathEngine/MathEngine/types/DecimalBuilder.cs b/MathEngine/MathEngine/types/DecimalBuilder.cs
new file mode 100644
index 0000000..7761355
--- /dev/null
+++ b/MathEngine/MathEngine/types/DecimalBuilder.cs
@@ -0,0 +1,264 @@
+using MathEngine.MemoryManagement;
+using System;
+using System.Runtime.InteropServices.Marshalling;
+using System.Text;
+
+namespace MathEngine.types
+{
+ ///
+ /// Class that converts a representation to its decimal base 10 representaiton
+ ///
+ internal unsafe static class DecimalBuilder
+ {
+ private const UInt32 DECBASE = 1_000_000_000;
+ private const int THRESHOLD = 8;
+
+
+ ///
+ /// Performs simple division of by base 10^9using Knuth's algorithm.
+ ///
+ /// Pointer to a LSW ordered Uint32 array containing the digits to divide
+ /// The length of the array pointed to by
+ /// When this function returns, contains the base 10^9 representation of
+ /// Then length of
+ private static int SimpleDiv(UInt32* x, int xLen, out UInt32* result)
+ {
+ // possible over allocation, oh well!
+ result = Memory.ScratchWorkspace.AllocateAsPointer(THRESHOLD);
+ int digitIndex = 0;
+ while (xLen > 0)
+ {
+ ulong rem = 0;
+ for (int i = xLen - 1; i >= 0; i--)
+ {
+ ulong cur = (rem << 32) + x[i];
+ x[i] = (UInt32)(cur / DECBASE);
+ rem = cur % DECBASE;
+ }
+
+ result[digitIndex] = (UInt32)rem;
+ digitIndex++;
+
+ while (xLen > 0 && x[xLen - 1] == 0)
+ {
+ xLen--;
+ }
+ }
+
+ if (digitIndex == 0)
+ {
+ result[0] = 0;
+ digitIndex++;
+ }
+ return digitIndex;
+ }
+
+ ///
+ /// Performs multiplication of two of and in base 10^9.
+ ///
+ /// Pointer to a LSW ordered Uint32 array containing the digits to be multiplied by
+ /// The number of elements that contains
+ /// Pointer to a LSW ordered Uint32 array containing the digits to be multiplied by
+ /// The number of elements that contains
+ /// When this function returns, contains the base 10^9 representation of the multiplicaiton of and
+ /// Then length of
+ private static int DecMultiply(UInt32* x, int xLen, UInt32* y, int yLen, out UInt32* result)
+ {
+ int tmpLen = (xLen + yLen);
+ UInt64* tmp = Memory.ScratchWorkspace.AllocateAsPointer(xLen + yLen);
+
+ for (int i = 0; i < xLen; i++)
+ {
+ ulong carry = 0;
+ for (int j = 0; j < yLen; j++)
+ {
+ ulong cur = tmp[i + j] + (ulong)x[i] * y[j] + carry;
+ tmp[i + j] = cur % DECBASE;
+ carry = cur / DECBASE;
+ }
+ int k = i + yLen;
+ while (carry != 0)
+ {
+ ulong cur = tmp[k] + carry;
+ tmp[k] = cur % DECBASE;
+ carry = cur / DECBASE;
+ k++;
+ }
+ }
+
+ //convert to uint and trim leading zeros
+ result = Memory.ScratchWorkspace.AllocateAsPointer(tmpLen);
+ UInt32[] res = new UInt32[tmpLen];
+ for (int i = 0; i < tmpLen; i++)
+ {
+ result[i] = (UInt32)(tmp[i]);
+ }
+
+ int sz = res.Length;
+ while (sz > 1 && result[sz - 1] == 0)
+ {
+ sz--;
+ }
+ return sz;
+ }
+
+ ///
+ /// Performs addition of two of and in base 10^9.
+ ///
+ /// Pointer to a LSW ordered Uint32 array containing the digits to be added to by
+ /// The number of elements that contains
+ /// Pointer to a LSW ordered Uint32 array containing the digits to be added to by
+ /// The number of elements that contains
+ /// When this function returns, contains the base 10^9 representation of the addition of and
+ /// Then length of
+ private static int DecAdd(UInt32* x, int xLen, UInt32* y, int yLen, out UInt32* result)
+ {
+ int sz = Math.Max(xLen, yLen); // Test against with xLen ^ ((xLen ^ yLen) & ((xLen - yLen) >> 31))
+
+ result = Memory.ScratchWorkspace.AllocateAsPointer(sz + 1);
+
+ ulong carry = 0;
+
+ for (int i = 0; i < sz; i++)
+ {
+ ulong xv = i < xLen ? x[i] : 0;
+ ulong yv = i < yLen ? y[i] : 0;
+ ulong cur = xv + yv + carry;
+ result[i] = (UInt32)(cur % DECBASE);
+ carry = cur / DECBASE;
+ }
+
+ if (carry != 0)
+ {
+ result[sz] = (UInt32)carry;
+ return sz + 1;
+ }
+ //else
+ //{
+ // Array.Resize(ref res, sz); //?
+ //}
+ return sz;
+ }
+
+ ///
+ /// Performs addition of two of and in base 10^9.
+ ///
+ /// Pointer to a LSW ordered Uint32 array containing the digits to be added to by
+ /// The number of elements that contains
+ /// Pointer to a LSW ordered Uint32 array containing the digits to be added to by
+ /// The number of elements that contains
+ /// When this function returns, contains the base 10^9 representation of the addition of and
+ /// Then length of
+ private static int PowerBtoN(int k, out UInt32* result)
+ {
+ const ulong B = 1UL << 32;
+ result = Memory.ScratchWorkspace.AllocateAsPointer(1);
+ result[0] = 1;
+ int resultLen = 1;
+
+ // repeat multiply by B k times;
+ // in prod this can be simplified
+
+ for (int i = 0; i < k; i++)
+ {
+ resultLen = MultiplyByWord(result, resultLen, B, out UInt32* tResult);
+ // assign the multiplication result back into result and go again
+ result = tResult;
+ }
+ return resultLen;
+ }
+
+ private static int MultiplyByWord(UInt32* x, int xLen, ulong b, out UInt32* result)
+ {
+ result = Memory.ScratchWorkspace.AllocateAsPointer(xLen + 2);
+ ulong carry = 0;
+
+ for (int i = 0; i < xLen; i++)
+ {
+ ulong cur = (ulong)x[i] * b + carry;
+ result[i] = (UInt32)(cur % DECBASE);
+ carry = cur / DECBASE;
+ }
+
+ int pos = xLen;
+
+ while (carry != 0)
+ {
+ result[pos++] = (UInt32)(carry % DECBASE);
+ carry /= DECBASE;
+ }
+
+ //trim zeros
+ int sz = pos;
+ while (sz > 1 && result[sz - 1] == 0)
+ {
+ sz--;
+ }
+ return sz;
+ }
+
+ ///
+ /// Computes the base 10^9 representation of _num recursively if the number of digits is large
+ ///
+ /// Pointer to a LSW ordered Uint32 array containing the digits to convert
+ /// The length of the array pointed to by
+ /// When this function returns, contains the base 10^9 representation of
+ ///
+ private static int ToBase10_9(UInt32* digits, int len, out UInt32* result)
+ {
+ while (len > 0 && digits[len - 1] == 0)
+ {
+ len--;
+ }
+
+ if (len == 0)
+ {
+ result = Memory.ScratchWorkspace.AllocateAsPointer(1);
+ result[0] = 0;
+ return 1;
+ }
+ if (len < THRESHOLD)
+ {
+ return SimpleDiv(digits, len, out result);
+ }
+ int n = len / 2;
+ UInt32* lo = Memory.ScratchWorkspace.AllocateAsPointer(n);
+ UInt32* hi = Memory.ScratchWorkspace.AllocateAsPointer(len - n);
+ Memory.MemCopy(digits, 0, lo, 0, n);
+ Memory.MemCopy(digits, n, hi, 0, len - n);
+
+ int dloLen = ToBase10_9(lo, n, out UInt32* dlo);
+ int dhiLen = ToBase10_9(hi, len - n, out UInt32* dhi);
+
+ int powLen = PowerBtoN(n, out UInt32* pow);
+
+ int dhiScaledLen = DecMultiply(dhi, dhiLen, pow, powLen, out UInt32* dhiScaled);
+ return DecAdd(dhiScaled, dhiScaledLen, dlo, dloLen, out result);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static string ConvertBigIntegerToString(UInt32* num, int numLen)
+ {
+
+ UInt32* cpy = Memory.ScratchWorkspace.AllocateAsPointer(numLen);
+ Memory.MemCopy(num, 0, cpy, 0, numLen);
+ int dLen = ToBase10_9(cpy, numLen, out UInt32* digits);
+
+ StringBuilder sb = new();
+
+ sb.Append(digits[dLen - 1].ToString());
+
+ for (int i = dLen - 2; i >= 0; i--)
+ {
+ sb.Append(digits[i].ToString("D9"));
+ }
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/MathEngine/MathRunner/Program.cs b/MathEngine/MathRunner/Program.cs
index 6f010bb..774441b 100644
--- a/MathEngine/MathRunner/Program.cs
+++ b/MathEngine/MathRunner/Program.cs
@@ -1,4 +1,5 @@
-using MathEngine.Expression;
+using MathEngine.types;
+using MathEngine.Expression;
namespace MathRunner
{
@@ -6,6 +7,12 @@ namespace MathRunner
{
static void Main(string[] args)
{
+ BigInteger x = new(123456);
+ for (int i = 0; i < 4; i++)
+ {
+ x = x * x;
+ }
+ Console.WriteLine(x.ToString());
Console.WriteLine("Evaluting expression...");
Expression exp = new("54+2+1");
Console.WriteLine(exp.Evaluate());
diff --git a/MathEngine/common/UnmanagedMMU.dll b/MathEngine/common/UnmanagedMMU.dll
new file mode 100644
index 0000000000000000000000000000000000000000..0d235d0c96b23a6b1c38962a28aa797dca4d244b
GIT binary patch
literal 9216
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
axc!0)2ISvRo~hZve!&*}a=8CT5BwMXjzJm#
literal 0
HcmV?d00001