The Multiplicative Mirror: Squaring, Roots, and the Geometry of Halving

Before we can dive into arbitrary exponentiation and dynamic logarithms, we have to formally cross the boundary into the highest foundational level of the Mercury engine: The Power Tier.

To understand exactly where that boundary lies, we need to look at how we scale numbers by a constant of $2$.

In the Multiplicative Tier, if you want to scale a number by $2$, you use Double ($A + A$) or Half ($A / 2$). Notice the mechanics: you are relying on the Additive Tier’s operations to achieve a Multiplicative result.

But what happens when you need to scale a number by an exponent of $2$? You use Square ($A \cdot A$) and Square Root ($A^{0.5}$). These functions rely on the Multiplicative Tier’s mechanics to achieve a Power result.

Squaring and square roots are not the end of the Multiplicative Tier. They are the absolute foundational constants of the Power Tier. They are the base-2 geometric primitives that the rest of the tier (mercuryPow and mercuryLog) must rely on to survive. Let’s look at how the engine optimizes these foundational operations.

The Geometry of Self-Scaling (mercurySqr)

Squaring is simply multiplication looking in a mirror.

In our earlier post on geometric scaling, we established that multiplying two places physically adds their exponents:

$$B^i \cdot B^j = B^{i+j}$$

When a number is squared, the factors are identical. The spatial footprint perfectly doubles:

$$B^i \cdot B^i = B^{2i}$$

Because of this rigid geometric rule, mercurySqr acts as a highly streamlined version of our standard multiplication engine. Because the factors are identical, the engine only needs to read from one input array (a). The underlying geometric expansion—the nested loops combining places and the 64-bit register capturing the native hardware carry—remains exactly the same. It is the Multiplicative Tier operating at maximum efficiency to serve the Power Tier.

Halving the Footprint (The highbit Payoff)

If squaring a number doubles its spatial footprint, then reversing the operation must cut that footprint in half, with the final boundary landing according to the highest occupied bit.

This geometric certainty unlocks a brilliant hardware optimization. When mercurySqrt initializes, it does not need to execute a complex search loop to figure out where its starting bit should be. It mathematically knows exactly where the highest possible bit of the root must live.

Look at the first three active lines of mercurySqrt:

// Extract the highest 32-bit place of our target number
int h = (int)a[1]; 

// Calculate the absolute highest possible bit of the root
int highbit = ((int) ((h + 1) * 32)) / 2 - 1; 
int lowbit = highbit - Precision * 32 + 1;

By taking the highest base-$2^{32}$ place (h), translating the end of that place into bit-space with (h + 1) * 32, dividing by 2, and then subtracting 1, the engine instantly pinpoints the highest possible bit of the square root.

The subtraction matters because (h + 1) * 32 names the boundary just beyond the highest bit of place h. The actual highest bit is one step lower. Since square root halves the footprint, the root’s highest possible bit is the halved boundary minus one.

Because the loop includes both endpoints, the lowest bit is calculated with a + 1. This makes the scan cover exactly Precision * 32 candidate bits: from the highest possible root bit down through the final retained bit of the requested precision.

This is a zero-cost optimization. It requires no loops, no comparisons, and no trial-and-error. It is an instant jump to the correct binary location, and it only works because the engine strictly respects the spatial geometry established in the tiers below it.

The Constructive Binary Search

Once the ceiling is found, mercurySqrt must construct the rest of the root. It does this through a highly optimized, automated binary search.

Starting from highbit and working down to lowbit, the engine tests each possible binary place to see if it belongs in the final answer:

for (int i = highbit; i >= lowbit; i--) {
    // Generate the test bit
    mercury_2Pow(stack, Precision+1, i, mark); 
    
    // Add the test bit to our running guess
    mercuryAdd(stack, Precision+1, m, mark, q); 
    
    // Square the new guess
    mercurySqr(stack, Precision+1, q, x);       
    
    // Compare the squared guess against our original target
    int s = mercuryCmp(Precision+1, x, A);      

    if (s <= 0) {
        // It fits! Keep the bit in our running root.
        mercuryLoadMercury(stack, Precision+1, q, m); 
        
        // If it is a perfect match, exit early.
        if (s == 0)
            break; 
    }
}

This loop is the definition of a constructive algorithm. It tests a single bit, geometrically expands it (mercurySqr), and compares it to the target (mercuryCmp). If it overshoots the target, the bit is discarded. If it fits, the bit is permanently locked into the running root (m).

It is a flawlessly automated descent that relies entirely on the established geometry of the Multiplicative Tier to dynamically construct its inverse.

Up Next: The Exponentiation Engine

With the base-$2$ constants of the Power Tier firmly established, we are now ready to unleash them.

Next time, we will explore mercuryPow, and see exactly how the engine uses these squaring mechanics to skip millions of redundant multiplications and rapidly calculate massive, arbitrary geometric growth.

JWCEssentials on GitHub

JWCEssentials/C/Mercury/Mercury.c

The Power Tier Inverses: Roots, Logarithms, and Dynamic Convergence

In our last post, we explored how mercuryPow uses a binary squaring shortcut to rapidly calculate massive exponents, and how it relies on the square root operation to navigate fractional powers.

Because exponential growth is asymmetrical (the base and the exponent play completely different roles), deconstructing an exponent requires two completely different inverse operations:

In Mercury’s API terms, mercuryLog(a, b, val) computes val = log_b(a). In tier notation, this is the inverse route that solves for the exponent.

$$C = A^B \implies A = \sqrt[B]{C} \quad \text{and} \quad B = \log_A(C)$$

Today, we are going to look at how Mercury solves for both. We will start with the elegant simplicity of the Root, and then dive into the most complex convergence loop in the entire engine: the Logarithm.

The Root: The Elegant Inversion

When solving for a missing base, the strategy remains identical to how we handle division in the lower tiers.

In the Multiplicative Tier, division can always be rewritten as multiplication by a fractional inverse:

$$\frac{A}{B} = A \cdot \left(\frac{1}{B}\right)$$

In the Power Tier, finding a root can always be rewritten as exponentiation by a fractional inverse:

$$\sqrt[B]{A} = A^{\left(\frac{1}{B}\right)}$$

Because we already built mercuryPow to gracefully handle negative fractional bits, Mercury gets the highly complex root function almost for free. We don’t need a massive new algorithm to calculate a root; we simply flip the exponent into a fraction and route it right back through the engine we already built.

Here is the entirety of the mercuryRoot function. It is exactly eight lines of active math:

void mercuryRoot(void *stack, int Precision, uint *a, uint *b, uint *val) {
    uint *p = (uint *) mercuryStackAlloc(stack, (Precision+2)*4);
    uint *one = (uint *) mercuryStackAlloc(stack, (Precision+2)*4);
    mercuryLoadUint(stack, Precision, one, 1);
    
    // Step 1: Calculate the fractional inverse (1 / B)
    mercuryDiv(stack, Precision, one, b, p); 
    mercuryStackFree(stack, (Precision+2)*4);
    
    // Step 2: Pass the fractional inverse back into the positive variant
    mercuryPow(stack, Precision, a, p, val); 
    
    mercuryStackFree(stack, (Precision+2)*4);
}

The Logarithm: Hunting for the Exponent

Solving for a missing base is straightforward. Solving for a missing exponent, however, is a completely different beast.

To compute $B = \log_A(C)$, we are asking the CPU: “To what power must I raise A to get C?” There is no simple fractional inversion for this. The engine must actively construct the exponent bit-by-bit. Mercury handles this through a highly optimized, automated two-phase search.

Phase 1: Finding the Ceiling

Before Mercury can test bits, it needs to know where to start. It does this by actively hunting for the highest necessary bit using two do-while loops.

It first checks if our target is larger or smaller than our base.

  • If the target is larger: The engine starts at bit 0 and steps upward (bit++), squaring the base (mercurySqr) until it overshoots the target. This efficiently finds the highest whole-number bit.

  • If the target is smaller: The engine starts at bit 0 and steps downward (bit--), taking the square root of the base (mercurySqrt) until it drops below the target. This efficiently finds the highest fractional bit.

Once the overshoot happens, Mercury knows exactly where the ceiling is, and it proceeds to the convergence loop.

Phase 2: The Unified Descent

This is where the mathematical purity of the Power Tier shines. Once Mercury finds that starting bit, it iterates downward linearly across the specified precision.

It no longer cares if it is looking at a whole number or a fraction. Because the combinator of this tier is multiplicative, moving down one binary place always means halving the geometric magnitude of the step.

This unified descent is the absolute pinnacle of the Constructive Tiers working in harmony. Whether stepping from bit $2$ to $1$, or from bit $-1$ to $-2$, the engine simply calls mercurySqrt at the bottom of the loop to geometrically halve the step size for the next bit.

To find a logarithm, the engine must compare magnitudes (mercuryCmp), add candidate bits (mercuryAdd), combine running totals (mercuryMul), and dynamically halve its geometric steps (mercurySqrt). Every single operation we have built from the Additive Tier upward converges in this one loop to solve the hardest arbitrary-precision calculation in the library.

Why No Multi-Bit Stride?

If you read our previous post on Division, you might remember that Mercury used a 4-bit “nibble stride” to speed up calculations, pre-computing a table of chunks to subtract all at once.

You might wonder: Why not use a multi-bit stride for Logarithms?

The answer lies in the combinator. In division, scaling is linear—shifting our pre-computed table across the dividend only required cheap bit-shifts. But in the Power Tier, the combinator is multiplicative, and the scaling is geometric.

Every single fractional bit in an exponent represents a completely different geometric magnitude (e.g., $A^{1/2}$, then $A^{1/4}$, then $A^{1/8}$). You cannot mathematically bit-shift a square root. To discover the exact size of the next fractional bit’s contribution, the engine must actively execute a new square root on the previous step. Because every bit’s magnitude is geometrically dependent on the one before it, the clean nibble-table optimization used by division does not transfer directly. The log engine is naturally forced toward sequential refinement, evaluating and combining the candidate contributions one by one.

The Convergence Engine (mercuryLog)

Here is the core convergence loop where Mercury hunts down the bits:

// Step downward from our discovered ceiling
for (int i = 0; i <= bits; i++) {
    
    // Test the current bit: Add it to our running exponent register
    mercury_2Pow(stack, InnerPrecision, bit, _bit);
    mercuryAdd(stack, InnerPrecision, reg, _bit, reg);

    // Calculate the new power using our running root 'r' and 'p'
    mercuryMul(stack, PowerPrecision, r, p, r);

    // Compare our test power 'x' against our target 'A'
    s = mercuryCmp(InnerPrecision, x, A) * c;

    if (s > 0) {
        // We overshot! Reject the bit and restore the previous state
        mercuryLoadMercury(stack, InnerPrecision, last, reg);
        mercuryLoadMercury(stack, PowerPrecision, lastR, r);
    } else if (s == 0) {
        // Perfect match! Clean exit.
        mercuryLoadExtendMercury(stack, InnerPrecision, Precision, reg, val);
        goto stackCleanup;
    }
    
    bit--; // Move down to the next smallest bit

    if (i != bits - 1) {
        // The Engine Drive: Halve the exponent step by taking the square root
        mercurySqrt(stack, PowerPrecision, p, p);
    }
}

This is the absolute pinnacle of the Constructive Tiers working in harmony. To find a logarithm, the engine must compare magnitudes (mercuryCmp), add candidate bits (mercuryAdd), combine running totals (mercuryMul), and dynamically halve its geometric steps (mercurySqrt).

Every single operation we have built from the Additive Tier upward converges in this one loop to solve the hardest arbitrary-precision calculation in the library.

Closing the Ladder

This completes the foundational Mercury arithmetic ladder. Addition taught us carry and borrow. Multiplication showed how places combine through additive geometry. Division reversed that geometry with pre-computed nibble tables. Power introduced binary squaring and the mirror of fractional exponents. Root revealed how an inverse can collapse into a call back through pow. Finally, logarithm brought the entire structure together as a constructive search for the missing exponent.

The lesson is that Mercury’s functions are not isolated tricks. They are a tiered family of operations. Each tier introduces a new kind of combination, and each inverse operation reveals what it means to solve for the missing relationship at that tier.

JWCEssentials on GitHub

JWCEssentials/C/Mercury/Mercury.c

The Power Tier: Binary Squaring and the Symmetry of Fractional Exponents

With the linear and geometric foundations of the Mercury engine established, we now step into the highest foundational level of mathematics: The Power Tier.

Unlike addition or multiplication, exponential growth is completely asymmetrical. The base and the exponent play entirely different physical roles. Because of this asymmetry, the inverse operations required to deconstruct an exponent split into two distinct, specialized functions: roots and logarithms.

$$C = A^B \implies A = \sqrt[B]{C} \quad \text{and} \quad B = \log_A(C)$$

In Sigma Language notation, this structural relationship looks like this:

  • C = A pow B; (Exponential combination)

  • A = C root B; (Solving for the Base)

  • B = C log A; (Solving for the Exponent)

In the lower tiers, the inverse operation could often be visualized as a direct reversal: subtraction reverses addition, and division reverses multiplication. In the Power Tier, the reversal splits because the two inputs have different meanings. Solving for the base requires root; solving for the exponent requires log.

Before we can look at how Mercury solves for missing bases and exponents, we first need to look at the forward operation: mercuryPow.

The Binary Shortcut: Exponentiation by Squaring

If you need to calculate $A^{100}$, the naive mathematical approach is to multiply $A \cdot A \cdot A \dots$ one hundred times. For arbitrary-precision numbers scaling across thousands of base-$2^{32}$ places, executing that many massive multiplications would bring a CPU to its knees.

Instead, Mercury uses an algorithmic shortcut called “exponentiation by squaring.” Because Mercury stores the exponent (B) in a native binary format, the engine can read the bits of B to determine exactly which prepared powers of A need to be multiplied into the result.

For example, raising A to the power of 3 normally looks like:

A * A * A

But 3 in binary is 11, meaning:

3 = 2 + 1

So the same expression can be rewritten as:

A^3 = A^2 * A^1

Mercury prepares those powers by repeatedly squaring the running base:

A^1 → A^2 → A^4 → A^8 → ...

Then, whenever the corresponding exponent bit is 1, that prepared power is multiplied into the result. The exponent’s binary digits become instructions for which powers of A participate in the final product.

This same bit-selection principle also appears one tier below in binary multiplication. To multiply by 3, we can write:

A * 3 = A + A + A = (A * 2) + A

Since 3 is binary 11, the multiplication engine can prepare A, A * 2, A * 4, and so on, then add the prepared values whose multiplier bits are set.

The Power Tier follows the same pattern at a higher order. Instead of preparing values with doubling and combining them with addition, it prepares powers with squaring and combines them with multiplication. The key is to use the correct construction and combination operations for the tier.

Here is the exact loop inside mercuryPow that handles the whole-number portion of the exponent:

// Process the integer bits (from bit 0 up to the highest bit)
for (int bit = 0; bit <= highBit; bit++) {
    
    // If the bit is 1, multiply our running base 'p' into our result 'r'
    if (mercuryGetBit(stack, Precision, bit, b) == 1) {
        mercuryMul(stack, Precision + 1, r, p, r);
    }

    // Always square the running base 'p' to prepare for the next bit
    if (bit != highBit) mercurySqr(stack, Precision + 1, p, p);
}

By reading the binary places and squaring the base at every step (e.g., $A^1 \to A^2 \to A^4 \to A^8$), Mercury geometrically skips over the redundant math. To calculate $A^{100}$, the engine does not need 100 multiplications—it only needs a handful of squares and strategic multiplies, dynamically arriving at the answer in a fraction of the time.

The Mirror: Fractional Exponents and Tier Symmetry

What happens if the exponent is not a whole integer? What if we need to calculate $A^{2.5}$?

This is where the true symmetry of the Power Tier reveals itself in the silicon. The identity position (bit $0$) acts as a mirror. When moving left into positive bits ($1, 2, 3 \dots$), Mercury scales the base by squaring it. But when moving right into the fractional bits ($-1, -2, -3 \dots$), Mercury must scale the base in the exact opposite direction.

To do this, it is mathematically forced to call upon its tier sibling: the square root.

// Process the fractional bits (from bit -1 down to the lowest bit)
for (int bit = -1; bit >= lowBit; bit--) {
    
    // Always square root the running base 'p' to prepare for the next fractional bit
    mercurySqrt(stack, Precision + 1, p, p);

    // If the bit is 1, multiply our running base 'p' into our result 'r'
    if (mercuryGetBit(stack, Precision, bit, b) == 1) {
        mercuryMul(stack, Precision + 1, r, p, r);
    }
}

Bit $-1$ represents $A^{0.5}$, which is exactly $\sqrt{A}$. Bit $-2$ represents $A^{0.25}$, which is the square root of the square root.

By simply swapping mercurySqr for mercurySqrt as it crosses the binary point, the engine seamlessly handles massive fractional powers. It proves that within the Power Tier, these operations are not isolated functions—they are fundamentally dependent on one another. You literally cannot build a comprehensive pow engine without a sqrt engine.

Implementation Note: The Guard Place

If you look closely at the C loops above, you will notice that the math functions are called with Precision + 1 rather than the standard Precision.

When you square massive numbers repeatedly, minor rounding errors at the absolute lowest bits can cascade upwards and corrupt the final answer. By temporarily computing the entire exponentiation sequence with one extra 32-bit “guard place,” Mercury insulates the working precision from cumulative geometric error. Once the calculation is completely finished, the guard place is cleanly truncated, leaving a pristine result.

Up Next: The Convergence

Now that we have seen how mercuryPow utilizes mercurySqrt to navigate fractional exponents, we are perfectly positioned to tackle the most complex function in the library.

In our next post, we will look at mercuryLog, and explore how Mercury dynamically hunts for a missing exponent by using roots and powers to converge on the answer bit by bit.

JWCEssentials on GitHub

JWCEssentials/C/Mercury/Mercury.c

The Execution Engine: Sliding Windows and Triple-Nested Loops in Mercury Division

In our last post, “The Division Stage-Setter,” we explored how Mercury outsmarts the CPU division limit. Instead of blindly guessing and subtracting to find a quotient, mercuryAbsDiv builds a multi-dimensional Table[8][16] array in its scratch memory—effectively memorizing its own times tables for the divisor across every possible 4-bit nibble shift.

But a pre-computed table is only useful if you have an engine built to navigate it. Today, we are looking at the execution phase: a triple-nested loop that dynamically slides our pre-computed grid across the dividend, turning a massive arbitrary-precision division problem into a lightning-fast sequence of pure lookups and subtractions.

To understand how the engine works without getting lost in the syntax, we are going to build it piece by piece.

Phase 1: Navigating the Places (The Outer Loop)

Division requires us to start at the most significant parts of our number and work our way down. The outermost loop of our execution engine handles this macro-level movement.

It starts at the highest 32-bit place of the dividend and slowly steps down to the identity place (the ones place).

// Step down through the 32-bit places of the dividend
for (int i = Precision - 1; i >= 0; i--) {
    
    // ... nibble and subtraction logic goes here ...
    
    dividend[1]++; // Shift the dividend window for the next place
}

The crucial piece of logic here is dividend[1]++. In Mercury’s architecture, index [1] holds the exponent. By incrementing it at the end of every loop, we physically shift our working frame of reference down the array. We are systematically sliding the divisor’s window across the dividend.

Phase 2: Slicing into Nibbles (The Middle Loop)

Once our window is locked onto a specific 32-bit place, we need to zoom in.

As we established in the previous post, our pre-computed table is based on 4-bit nibbles. Since there are exactly 8 nibbles in a 32-bit word, our middle loop slices the current place into 8 discrete steps, starting from the highest nibble ($7$) down to the lowest ($0$).

for (int i = Precision - 1; i >= 0; i--) {
    
    // Step down through the 8 nibbles of the current place
    for (int p = 7; p >= 0; p--) {
        
        // ... lookup and strike logic goes here ...
        
    }

    dividend[1]++;
}

The variable p here maps exactly to the q dimension of our Table[8][16]. It tells the engine exactly which layer of our shifted, pre-multiplied grid we need to look at for this specific spatial position.

Phase 3: The Lookup and Strike (The Inner Loop)

Now we drop the hammer. Inside the innermost loop, the engine tests the pre-computed multiples of our divisor, checking the largest possible value ($15$, or 0xF in hex) down to $0$.

for (int i = Precision - 1; i >= 0; i--) {
    for (int p = 7; p >= 0; p--) {
        
        // Test the pre-computed multiples from 15 down to 0
        for (int d = 0xF; d >= 0; d--) {
            uint *x = Table[p][d]; // Grab the pre-computed chunk

            // Does it fit?
            if (mercuryAbsCmp(Precision, dividend, x) >= 0) {
                
                // Strike: Subtract it from the dividend
                mercurySub(stack, Precision, dividend, x, dividend);

                // Log the quotient piece in its exact spatial position
                reg[i] += ((uint) d) << (p * 4);

                // ... exit logic ...
                d = -1; // Break the inner loop, move to the next nibble
            }
        }
    }
    dividend[1]++;
}

This is the ultimate payoff of our preparation. There is no guesswork. The engine simply checks if the pre-computed chunk fits (mercuryAbsCmp). If it does, it executes a single subtraction (mercurySub) and instantly logs the quotient piece directly into the result array (reg[i] += ((uint) d) << (p * 4)), perfectly aligning it using the exact same bit-shift math we used to generate the table.

Once a fit is found and subtracted, it forces d = -1 to break the inner loop and instantly move on to the next nibble.

The Clean Exit: A Surgical Optimization

If you look closely at the full mercuryAbsDiv source code, there is one final optimization nestled inside that innermost strike loop.

Every time Mercury performs a successful subtraction, it checks if the dividend has reached absolute zero. If it has, there is no reason to continue shifting the window or testing lower places. The math is completely finished. To immediately halt the engine, Mercury uses a direct goto ret; jump.

                    if (mercuryIsZero(Precision, dividend)) {
                        goto ret; // Break out of all three loops instantly
                    }

While the goto statement is often treated as taboo in modern, high-level application development, in bare-metal C engine architecture, it is a surgical tool. It allows the processor to instantly break out of a triple-nested loop the exact millisecond the work is done, without having to cascade through multiple break conditions or evaluate a chain of boolean flags. It saves precious clock cycles and guarantees a perfectly clean exit.

Up Next: Entering the Power Tier

With addition, subtraction, multiplication, and division firmly established, we have conquered the linear and geometric foundations of the Mercury engine.

Next time, we step into the highest foundational level of mathematics: The Power Tier. We will explore how exponential growth forces our inverse operations to split into roots and logarithms, and how Mercury relies on those exact structural relationships to dynamically converge on the hardest calculations in silicon.

JWCEssentials on GitHub

JWCEssentials/C/Mercury/Mercury.c

The Division Stage-Setter: Pre-computed Tables and Reversing Spatial Geometry

In our previous look at the Multiplicative Tier, we established that multiplication is fundamentally a spatial expansion. When multiplying two numbers, the resulting memory footprint respects an $(X+Y) – 1 + 1$ relationship. The baseline width expands outward from the overlapping identity places, with a final potential carry determining the absolute maximum width.

Division must respect this exact same spatial geometry—but in reverse. Instead of expanding outward, division requires aligning a fixed-width window (the divisor) with the most significant places of the dividend, and systematically sliding it down toward the identity place.

The Grade-School Analogy: Why Division is Hard for a CPU

When we perform long division on paper—for example, $7450 \div 25$—we do not repeatedly subtract 25 from 7450. We align the 25 under the 74 and ask, “How many times does this fit?” We immediately know the answer is roughly 2 or 3 because we have a memorized 1-9 multiplication table in our heads.

A CPU does not have a memorized multiplication table for arbitrary, multi-place base-$2^{32}$ numbers. If we force the computer to blindly guess and repeatedly subtract to find the ratio, division becomes incredibly slow and computationally expensive.

The Mercury Solution: The Pre-computed Table

To solve this, mercuryAbsDiv executes a brilliant algorithmic shortcut: it builds its own “memorized” multiplication table inside the scratch memory before it ever starts dividing.

Creating a full base-$2^{32}$ table would require 4.2 billion entries, which is physically impossible. Instead, Mercury slices the 32-bit places down into 4-bit “nibbles.” This reduces the required search space to just 16 entries ($0$ through $15$).

Here is the exact C code where Mercury learns its “times tables” by repeatedly adding the divisor to populate the base nibble level:

// Setup the base level (q=0) for the first nibble
mercuryLoadZero(stack, Precision, Table[0][0]);
mercuryLoadMercury(stack, Precision, divisor, Table[0][1]);

// Build 2 through 15 by repeatedly adding the divisor
for (int i = 2; i < 16; i++) {
    mercuryAdd(stack, Precision, Table[0][i-1], divisor, Table[0][i]);
}

Expanding the Grid and Sliding the Window

Once Mercury knows how the divisor scales from $1$ to $15$, it leverages our spatial alignment rules to expand that knowledge across the entire 32-bit place.

Because a 32-bit place consists of 8 discrete 4-bit nibbles, Mercury simply takes that first 16-entry table and shifts it across the remaining 7 levels.

// Shift the base table across the remaining 7 nibble positions
for (int q = 1; q < 8; q++) {
    mercuryLoadZero(stack, Precision, Table[q][0]);

    for (int i = 1; i < 16; i++) {
        // Shift each pre-multiplied value left by 4 bits (q * 4)
        mercuryShift(stack, Precision, Table[0][i], q * 4, Table[q][i]);
    }
}

The bit-shift math here is elegantly simple. The variable q represents the nibble index (from $1$ to $7$). Since each nibble is exactly 4 bits wide, shifting the base table by $q \cdot 4$ perfectly aligns the pre-multiplied values with the 4th, 8th, 12th, and eventually the 28th bit of the 32-bit word.

By generating this Table[8][16] grid, Mercury turns guessing into a bounded lookup: at each nibble position, there are only sixteen possible candidates to test. It looks at a nibble of the dividend, checks its custom table, grabs the largest pre-multiplied value that fits, and subtracts it. Because the table was built relative to the divisor, the engine can flawlessly slide these pre-calculated chunks across the dividend until it reaches the end.

A massive, highly complex arbitrary-precision division problem is elegantly reduced to a highly efficient lookup-and-subtract loop.

q * 4 is not arbitrary code. It is the direct mechanical translation of eight 4-bit nibbles inside one 32-bit place.

Ready to Strike We now have a fully populated Table[8][16] sitting safely in our scratch memory. By shifting the base nibble values across the 32-bit geometry, Mercury has effectively memorized its times tables for this specific divisor.

But having the knowledge is only half the battle.

In our next post, we will unleash the execution engine. We will look at the triple-nested loop that puts this table to work, dynamically sliding our pre-computed grid across the dividend to turn a massive arbitrary-precision division problem into a lightning-fast sequence of pure lookups and subtractions.

JWCEssentials on GitHub

JWCEssentials/C/Mercury/Mercury.c

The Multiplicative Tier: Geometric Scaling and 64-bit Synergy in Mercury

In our last post, we explored the Additive Tier, establishing how Mercury handles direct linear steps. Today, we step up to the second level of the mathematical architecture: The Multiplicative Tier.

At this tier, we move away from simple counting and enter the realm of geometric scaling.

The Theory of Geometric Scaling

It is common to teach multiplication simply as “fast addition”—and while that is functionally true for small integers, it is the wrong mental model for arbitrary-precision engines. Multiplication applies one value as a scale to the entire magnitude of another.

Because multiplication is symmetrical (the order of factors does not change the product), its inverse operation is used to solve for either of the original variables:

$$C = A \cdot B \implies B = \frac{C}{A} \quad \text{and} \quad A = \frac{C}{B}$$

In our Sigma Language notation, we express this structural relationship linearly:

  • C = A * B; (Multiplicative combination)
  • B = C / A; (Solving for the right ratio)
  • A = C / B; (Solving for the left ratio)

Just as subtraction was the tool to find a missing additive difference, division is strictly the tool to find the missing scale. Before we can look at division, however, we need to look at how Mercury handles that massive geometric expansion in silicon.

32-bit Places in a 64-bit World

When Mercury hands a scaling operation off to mercuryAbsMul, the elegance of using a base-$2^{32}$ positional format shines through.

If we multiply two full 32-bit places together, the largest possible product is still less than 2^64, so a 64-bit register can hold the entire intermediate product. Because modern processors feature 64-bit Arithmetic Logic Units (ALUs), we can multiply two base-$2^{32}$ “places” together and capture the entire result natively, without overflowing the hardware register.

Here is the inner engine of mercuryAbsMul that performs this feat:

// Iterate through the places of variable 'b'
for (int bi = bs; bi <= br; bi++) {
    uint bx = b[2 + bi + bq]; // Load the current place of 'b'
    
    int ai;
    ulong reg = 0; // The 64-bit accumulator register

    // Multiply against the places of variable 'a'
    for (ai = as; ai <= ar; ai++) {
        int place = ai + bi - adj;

        if (place >= 0) {
            uint ax = a[2 + ai + aq]; // Load the current place of 'a'

            // Multiply the two 32-bit places, add the carry, and add the existing scratch value
            reg += (ulong) bx * (ulong) ax + scratch[place];

            // Store the bottom 32 bits as the answer for this place
            scratch[place] = (uint) reg;
            
            // Shift the register right to push the top 32 bits forward as the carry
            reg >>= 32;
        } else {
            reg = 0;
        }
    }
    
    // Process any remaining carry for this row
    int place = ai + bi - adj;
    if (place >= 0) {
        reg += scratch[place];
        scratch[place] = (uint) reg;
    }
}

The Accumulator and Scratch Staging

The magic happens inside that innermost for loop. We are not just multiplying bx and ax. We are accumulating three distinct values into our 64-bit reg:

  1. The product of the two current 32-bit places.
  2. The carry over from the previous place.
  3. The value already sitting in the scratch array from earlier multiplication passes.

Once those are added together, the logic mirrors our Additive Tier perfectly. The bottom 32 bits are saved to the scratch array (scratch[place] = (uint) reg;), and the top 32 bits are shifted forward for the next loop (reg >>= 32;).

Once again, by staging all of this geometric expansion safely inside the scratch stack, Mercury ensures that the output variable (val) is never corrupted mid-calculation.

Why Two 32-bit Places Need 64 Bits

There is a deeper reason that Mercury uses a 64-bit register for multiplication. It is not only because the hardware happens to provide one. It is because multiplication combines magnitudes, and the size of the result is measured by adding the places of the factors.

In a positional number system, a digit does not stand alone. A digit at place i represents:

aᵢ × B^i

and a digit at place j represents:

bⱼ × B^j

When those two places are multiplied, their coefficients multiply, but their places add:

(aᵢ × B^i) × (bⱼ × B^j)
= (aᵢ × bⱼ) × B^(i + j)

That addition in the exponent is not a coincidence. The Multiplicative Tier is built on top of the Additive Tier. Multiplication scales magnitudes, but the placement of the resulting product is still governed by additive displacement.

For Mercury, the base is B = 2^32. Each place can hold any value from 0 to 2^32 - 1. The largest possible single-place product is therefore:

(2^32 - 1) × (2^32 - 1)
= 2^64 - 2^33 + 1

That value is smaller than 2^64, so it fits completely inside an unsigned 64-bit register. Mercury can multiply two full 32-bit places without losing a single bit of the product.

Once the product is staged in the 64-bit register, Mercury follows the same carry discipline introduced in the Additive Tier: the low 32 bits become the current output place, and the high 32 bits move forward as carry.

The Bridge to Division

If multiplication is scaling up by multiplying 32-bit places natively, division is the process of scaling down.

However, division is notoriously expensive for a CPU. Instead of guessing factors and repeatedly subtracting (which would take an eternity for arbitrary-precision numbers), Mercury uses a brilliant algorithmic shortcut to solve the inverse ratio: the nibble-sized precomputed table.

In the next post, we will look at how mercuryAbsDiv pre-computes the entire search space of the dividend, turning massive division problems into highly efficient lookup-and-subtract loops.

 

JWCEssentials on GitHub

JWCEssentials/C/Mercury/Mercury.c

Carrying the One in Base 232: Why Mercury Addition Works

Mercury does not abandon grade-school arithmetic. It changes the size of the digit.

That one idea explains a surprising amount of the Mercury arbitrary-precision engine. When we work in decimal, each digit holds a value from 0 to 9. When a column grows too large, we keep the part that belongs in the current column and carry the rest into the next one.

Mercury does exactly the same thing. The only difference is that Mercury’s “digits” are not decimal digits. They are 32-bit unsigned integer limbs.

In other words, Mercury works in base 232.

Every Base Has Columns

In base 10, the largest single digit is 9. If we add:

9 + 9 + 1 = 19

the current column keeps the 9, and the next column receives a carry of 1.

In base 16, the largest single digit is F, which is decimal 15. If we add:

F + F + 1 = 1F

the current column keeps F, and the next column receives a carry of 1.

Mercury uses the same rule, just with much larger digits:

0xFFFFFFFF + 0xFFFFFFFF + 1 = 0x1FFFFFFFF

The current 32-bit limb keeps:

0xFFFFFFFF

and the next limb receives:

1

That is all a carry is.

Why the Carry Can Only Be 0 or 1

This is not a special trick of binary computers. It is a property of positional number systems.

In any base B, a single digit can only range from:

0 to B - 1

When adding two digits plus an incoming carry, the largest possible value is:

(B - 1) + (B - 1) + 1 = 2B - 1

That means the result may cross into the next column, but it can only cross once. The outgoing carry can only be 0 or 1.

For Mercury, the base is:

B = 2^32

So the largest possible single-limb addition is:

(2^32 - 1) + (2^32 - 1) + 1 = 2^33 - 1

That result needs only 33 bits. A 64-bit register has more than enough room to hold it safely.

The Mercury Addition Loop

This is why Mercury can use a 64-bit register to add two 32-bit limbs:

ulong reg = 0;

for (; i <= bh && i <= h; i++) {
    reg += ((slong) a[2+i-al] + (slong) b[2+i-bl]);

    scratch[i - l] = (uint) reg;

    reg >>= 32;
}

The low 32 bits become the output limb:

scratch[i - l] = (uint) reg;

Then Mercury shifts the register right by 32 bits:

reg >>= 32;

That removes the limb we already stored and leaves only the carry for the next position.

If there was no overflow, the carry is 0. If the limb overflowed, the carry is 1.

That is the whole secret: Mercury uses the processor’s native 64-bit arithmetic as a temporary workspace for base-232 digit arithmetic.

Borrowing Is the Same Story in Reverse

Subtraction follows the same positional logic, but instead of carrying forward a positive overflow, it carries forward a borrow.

Consider the smallest possible underflow in one limb:

0x00000000 - 0x00000001

The current limb does not have enough value to complete the subtraction. So it borrows one unit from the next limb. But one unit in the next limb is worth exactly 232 in the current limb.

So the calculation becomes:

0x100000000 - 0x00000001 = 0xFFFFFFFF

The current limb receives:

0xFFFFFFFF

and the next limb receives a borrow of:

-1

The Mercury Subtraction Loop

That is why Mercury’s subtraction loop uses a signed 64-bit register:

slong reg = 0;

for (; i <= bh && i <= h; i++) {
    reg += ((slong) a[2+i-al] - (slong) b[2+i-bl]);

    if (reg < 0) {
        reg += 0x100000000LL;
        scratch[i - l] = (uint) reg;
        reg = -1;
    } else {
        scratch[i - l] = (uint) reg;
        reg = 0;
    }
}

The signed register makes the borrow visible immediately. If reg drops below zero, Mercury knows the current limb had to borrow from the next place.

To repair the current limb, Mercury adds back:

0x100000000

which is exactly 232. Then it stores the repaired 32-bit limb and carries -1 forward into the next position.

This is not a shortcut around subtraction. It is subtraction in positional notation, written directly in the machine’s natural word size.

The Limb Train

A helpful way to picture this is as a train of limbs. Each car holds one base-232 digit:


[00000001] [FFFFFFFF] [FFFFFFFF]
                    + [00000001]
-------------------------------------
[00000002] [00000000] [00000000]

The rightmost limb overflows first. It rolls from FFFFFFFF to 00000000 and sends a carry to the next limb. That limb also rolls over and sends a carry onward. Finally, the left limb receives the carry and increases from 00000001 to 00000002.

This is the same thing that happens in decimal when:

1999 + 1 = 2000

Mercury simply does it in a base where each digit contains 32 bits of information.

Bounded Overflow

This gives Mercury a very useful design property: bounded overflow.

For addition, the bounds are simple:

32-bit limb + 32-bit limb + carry = at most 33 bits

For multiplication, the next tier, the bounds are larger:

32-bit limb * 32-bit limb = at most 64 bits

That is why base 232 is such a natural foundation for this engine. Addition has room to carry. Multiplication has room to stage a limb product. The machine’s native 64-bit arithmetic becomes the safe workbench for 32-bit arbitrary-precision digits.

Why This Matters

The Additive Tier may look simple, but it establishes the discipline that the rest of Mercury builds on.

A large number is not magic. It is a sequence of bounded digits.

A carry is not magic. It is the part of a column that no longer fits.

A borrow is not magic. It is one unit from the next column, reinterpreted in the current column’s base.

Once those ideas are clear, the rest of arbitrary-precision arithmetic becomes much less mysterious. Multiplication is not a different universe. It is the next tier: many limb products, many carries, and the same positional logic scaled upward.

Mercury does not abandon grade-school arithmetic. It changes the size of the digit.

Up Next

The Additive Tier gives us a stable, linear foundation. Next time, we will step up to the Multiplicative Tier, where we move into geometric scaling and explore how decimal long division perfectly explains Mercury’s nibble-sized pre-multiplication table.

Also, how does multiplying two 32bit values produce precisely 64bits?

JWCEssentials on GitHub

JWCEssentials/C/Mercury/Mercury.c