Mathematical functions which involve integers.


§1. Modular arithmetic. For every integer n there is a unique congruence class 0, 1, ..., m-1 modulo m to which that integer belongs, and this function returns it. Note that the normal I6 operation % is signed, which is why we need this function.

[ CongruenceClass n modulus;
    if (modulus > 0) {
        n = n%modulus;
        if (n < 0) return n + modulus;
    }
    return n;
];

§2. Square Root. We only calculate by hand if we have to.

The slower integer method is an old algorithm for extracting binary square roots, taking 2 bits at a time. We start out with one at the highest bit which isn't the sign bit; that used to be worked out as WORD_HIGHBIT/2, but this caused unexpected portability problems (exposing a minor bug in Inform and also glulxe) because of differences in how C compilers handle signed division of constants in the case where the dividend is \(-2^{31}\), the unique number which cannot be negated in 32-bit twos complement arithmetic.

[ SquareRoot num
    op res one n x;
    if (num < 0) {
        IssueRTP("TookSquareRootOfNegativeNumber",
            "You can't take the square root of a negative number.", BasicInformKitRTPs);
        return 1;
    }

    n = VM_SquareRoot(num);
    if (n) return n;

    op = num;
    "one" starts at the highest power of four <= the argument.
    for (one = WORD_NEXTTOHIGHBIT: one > op: one = one/4) ;

    while (one ~= 0) {
        if (op >= res + one) {
            op = op - res - one;
            res = res/2 + one;
        } else {
            res = res/2;
        }
        one = one/4;
    }
    return res;
];

§3. Cube Root. Again, the technique here is a fallback measure. It's an iterative scheme for finding cube roots by Newton-Raphson approximation, not a great method but which, on the narrow ranges of integers we deal with, just about good enough. The square root is used only as a sighting shot.

[ CubeRoot num x y n;
    n = VM_CubeRoot(num);
    if (n) return n;
    if (num < 0) x = -SquareRoot(-num); else x = SquareRoot(num);
    for (n=0: (y ~= x) && (n++ < 100): y = x, x = (2*x + num/x/x)/3) ;
    return x;
];

§4. Absolute value. Of an integer:

[ NUMBER_TY_Abs x; if (x<0) return -x; return x; ];

§5. IntegerDivide. We can't simply use I6's / operator, as that translates directly into a virtual machine opcode which crashes on divide by zero.

[ IntegerDivide A B;
    if (B == 0) { IssueRTP("DividedByZero", "You can't divide by zero.", BasicInformKitRTPs); rfalse; }
    return A/B;
];

§6. IntegerRemainder. Similarly.

[ IntegerRemainder A B;
    if (B == 0) { IssueRTP("DividedByZero", "You can't divide by zero.", BasicInformKitRTPs); rfalse; }
    return A%B;
];

§7. SignedCompare. This routine is hardly ever needed; it wraps up ordinary comparisons.

[ SignedCompare x y;
    if (x > y) return 1;
    if (x == y) return 0;
    return -1;
];