An abstract operation is a conceptual operation that is not an actual operation in the language but is used to aid in the specification and understanding of a programming concept or system.

The following abstract operations are defined over the BigInt type:

Operation Example Invoked by Result
BigInt::unaryMinus -x Unary - Operator BigInt
BigInt::bitwiseNOT ~x Bitwise NOT Operator (~) BigInt
BigInt::exponentiate x ** y Exponentiation Operator (**) and Math.pow(base, exponent) either a normal completion containing a BigInt or a throw completion
BigInt::multiply x * y Multiplicative Operators BigInt
BigInt::divide x / y Multiplicative Operators either a normal completion containing a BigInt or a throw completion
BigInt::remainder x % y Multiplicative Operators either a normal completion containing a BigInt or a throw completion
BigInt::add x ++
++ x
x + y
Postfix Increment Operator, Prefix Increment Operator, and The Addition Operator (+) BigInt
BigInt::subtract x --
-- x
x - y
Postfix Decrement Operator, Prefix Decrement Operator, and The Subtraction Operator (-) BigInt
BigInt::leftShift x << y The Left Shift Operator(<<) BigInt
BigInt::signedRightShift x >> y The Signed Right Shift Operator(>>) BigInt
Number::unsignedRightShift x >>> y The Unsigned Right Shift Operator (>>>) a throw completion
BigInt::lessThan x < y
x > y
x <= y
x >= y
Relational Operators, via IsLessThan (x, y, LeftFirst) Boolean
BigInt::equal x == y
x != y
x === y
x !== y
Equality Operators, via IsStrictlyEqual (x, y) Boolean
BigInt::sameValue Object.is(x, y) Object internal methods, via SameValue (x, y), to test exact value equality Boolean
BigInt::sameValueZero [x].includes(y) Array, Map, and Set methods, via SameValueZero (x, y), to test value equality, ignoring the difference between +0 and -0 Boolean
BigInt::bitwiseAND x & y Binary Bitwise Operators BigInt
BigInt::bitwiseXOR x ^ y BigInt
BigInt::bitwiseOR x | y BigInt
BigInt::toString String(x) Many expressions and built-in functions, via ToString (argument) String
BigInt::unaryMinus (x)

The abstract operation BigInt::unaryMinus takes argument x (a BigInt) and returns a BigInt. It performs the following steps when called:

  1. If x is 0, return 0.
  2. Return the BigInt value that represents the negation of x.
BigInt.unaryMinus = function (x) {
    if(x === 0n) {
        return BigInt(x);
    }
    return -BigInt(x);
}
BigInt.unaryMinus(100n);
BigInt::bitwiseNOT (x)

The abstract operation BigInt::bitwiseNOT takes argument x (a BigInt) and returns a BigInt. It returns the one's complement of x. It performs the following steps when called:

  1. Return -x - 1.
  2. Return the BigInt value that represents the negation of x.

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.bitwiseNOT = function (x) {
    return -BigInt(x) - 1;
}
BigInt.bitwiseNOT(100n);
BigInt::exponentiate (base, exponent)

The abstract operation BigInt::exponentiate takes arguments base (a BigInt) and exponent (a BigInt) and returns either a normal completion containing a BigInt or a throw completion. It performs the following steps when called:

  1. If exponent < 0, throw a RangeError exception.
  2. If base is 0 and exponent is 0, return 1.
  3. Return the BigInt value that represents base raised to the power exponent.

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.exponentiate = function (base, exponent) {
    if(BigInt(exponent) < 0n) {
        // If exponent < 0, throw a RangeError exception
        throw new RangeError("The exponent must be greater than 0.");
    }
    if(BigInt(base) === 0n && BigInt(exponent) === 0n) {
        // If base is 0 and exponent is 0, return 1
        return BigInt(1);
    }

    // Return the BigInt value that represents base raised to the power exponent
    return BigInt(base) ** BigInt(exponent);
}
BigInt.exponentiate(100n, 100n);
BigInt::multiply (x, y)

The abstract operation BigInt::multiply takes arguments x (a BigInt) and y (a BigInt) and returns a BigInt. It performs the following steps when called:

  1. Return the BigInt value that represents the product of x and y.

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.multiply = function (x, y) {
    // Return the BigInt value that represents the product of x and y
    return BigInt(x) * BigInt(y);
}
BigInt.multiply(100n, 100n);
BigInt::divide (x, y)

The abstract operation BigInt::divide takes arguments x (a BigInt) and y (a BigInt) and returns either a normal completion containing a BigInt or a throw completion. It performs the following steps when called:

  1. If y is 0, throw a RangeError exception.
  2. Let quotient be x / y.
  3. Return truncate(quotient).

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.divide = function (x, y) {
    // If y is 0, throw a RangeError exception.
    if(BigInt(y) === 0n) {
        throw new RangeError("The divisor must be greater than 0.");
    }

    // Let quotient be x / y
    let quotient = BigInt(x) / BigInt(y);

    // Return truncate(quotient)
    return BigInt(quotient);
}
BigInt.divide(100n, 2n);
BigInt::remainder (n, d)

The abstract operation BigInt::remainder takes arguments n (a BigInt) and d (a BigInt) and returns either a normal completion containing a BigInt or a throw completion. It performs the following steps when called:

  1. If d is 0, throw a RangeError exception.
  2. If n is 0, return 0.
  3. Let quotient be n / d.
  4. Let q be truncate(quotient).
  5. Return n - (d × q).

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.remainder = function (n, d) {
    // If d is 0, throw a RangeError exception
    if(BigInt(d) === 0n) {
        throw new RangeError("The divisor must be greater than 0.");
    }

    // If n is 0, return 0
    if(BigInt(n) === 0n) {
        return BigInt(0);
    }

    // Let quotient be n / d
    let quotient = BigInt(n) / BigInt(d);

    // Let q be truncate(quotient)
    let q = quotient;

    // Return n - (d × q)
    return BigInt(n) - (BigInt(d) * BigInt(q));
}
BigInt.remainder(13n, 3n);
BigInt::add (x, y)

The abstract operation BigInt::add takes arguments x (a BigInt) and y (a BigInt) and returns a BigInt. It performs the following steps when called:

  1. Return the BigInt value that represents the sum of x and y.

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.add = function (x, y) {
    // Return the BigInt value that represents the sum of x and y
    return BigInt(x) + BigInt(y);
}
BigInt.add(13n, 3n);
BigInt::subtract (x, y)

The abstract operation BigInt::subtract takes arguments x (a BigInt) and y (a BigInt) and returns a BigInt. It performs the following steps when called:

  1. Return the BigInt value that represents the difference x minus y.

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.subtract = function (x, y) {
    // Return the BigInt value that represents the difference x minus y
    return BigInt(x) - BigInt(y);
}
BigInt.add(13n, 3n);
BigInt::leftShift (x, y)

The abstract operation BigInt::leftShift takes arguments x (a BigInt) and y (a BigInt) and returns a BigInt. It performs the following steps when called:

  1. If y < 0, then
    1. Return the BigInt value that represents $$x / 2^-y$$, rounding down to the nearest integer, including for negative numbers.
  2. Return the BigInt value that represents $$x × 2^y$$.

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.leftShift = function (x, y) {
    if(y < 0) {
        // If y < 0, then return the BigInt value that represents x / 2^-y, rounding down to the nearest integer, including for negative numbers.
        return BigInt(x) / BigInt(2) ** BigInt(-y);
    }

    // Return the BigInt value that represents x × 2^y.
    return BigInt(x) * BigInt(2) ** BigInt(y);
}
BigInt.leftShift(13n, -3n);
BigInt::signedRightShift (x, y)

The abstract operation BigInt::signedRightShift takes arguments x (a BigInt) and y (a BigInt) and returns a BigInt. It performs the following steps when called:

  1. Return BigInt::leftShift(x, -y).

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.leftShift = function (x, y) {
    if(y < 0) {
        // If y < 0, then return the BigInt value that represents x / 2^-y, rounding down to the nearest integer, including for negative numbers.
        return BigInt(x) / BigInt(2) ** BigInt(-y);
    }

    // Return the BigInt value that represents x × 2^y.
    return BigInt(x) * BigInt(2) ** BigInt(y);
};
BigInt.signedRightShift = function (x, y) {
    // Return BigInt::leftShift(x, -y).
    return BigInt.leftShift(x, -y);
};
BigInt.signedRightShift(13n, -3n);
BigInt::unsignedRightShift (x, y)

The abstract operation BigInt::unsignedRightShift takes arguments x (a BigInt) and y (a BigInt) and returns a throw completion. It performs the following steps when called:

  1. Throw a TypeError exception.

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.unsignedRightShift = function (x, y) {
    throw new TypeError("Invalid operator") 
};
BigInt.unsignedRightShift(13n, -3n);
BigInt::lessThan (x, y)

The abstract operation BigInt::lessThan takes arguments x (a BigInt) and y (a BigInt) and returns a Boolean. It performs the following steps when called:

  1. If x < y, return true; otherwise return false.

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.lessThan = function (x, y) {
    if(BigInt(x) < BigInt(y)) {
        // If x < y, return true; 
        return true;
    }else {
        // otherwise return false
        return false;
    }
};
BigInt.lessThan(13n, -13n);
BigInt::equal (x, y)

The abstract operation BigInt::equal takes arguments x (a BigInt) and y (a BigInt) and returns a Boolean. It performs the following steps when called:

  1. If x = y, return true; otherwise return false.

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.lessThan = function (x, y) {
    if(BigInt(x) === BigInt(y)) {
        // If x = y, return true; 
        return true;
    }else {
        // otherwise return false
        return false;
    }
};
BigInt.lessThan(13n, -13n);
BinaryAnd (x, y)

The abstract operation BinaryAnd takes arguments x (0 or 1) and y (0 or 1) and returns 0 or 1. It performs the following steps when called:

  1. If x = 1 and y = 1, return 1.
  2. Else, return 0.

If we translated the steps into Javascript codes, this is more or less it would look like:

function BinaryAnd(x, y) {
    if(x === BigInt(1) && y === BigInt(1)) {
        // If x = 1 and y = 1, return 1;
        return BigInt(1);
    }else {
        // otherwise return false
        return BigInt(0);
    }
};
BinaryAnd(1n, 0n);
BinaryOr (x, y)

The abstract operation BinaryOr takes arguments x (0 or 1) and y (0 or 1) and returns 0 or 1. It performs the following steps when called:

  1. If x = 1 or y = 1, return 1.
  2. Else, return 0.

If we translated the steps into Javascript codes, this is more or less it would look like:

function BinaryOr(x, y) {
    if(x === BigInt(1) || y === BigInt(1)) {
        // If x = 1 or y = 1, return true; 
        return BigInt(1);
    }else {
        // otherwise return false
        return BigInt(0);
    }
};
BinaryOr(1n, 0n);
BinaryXor (x, y)

The abstract operation BinaryXor takes arguments x (0 or 1) and y (0 or 1) and returns 0 or 1. It performs the following steps when called:

  1. If x = 1 and y = 0, return 1.
  2. Else if x = 0 and y = 1, return 1.
  3. Else, return 0.

If we translated the steps into Javascript codes, this is more or less it would look like:

function BinaryXor(x, y) {
    if(x === BigInt(1) && y === BigInt(0)) {
        // If x = 1 and y = 0, return 1 
        return BigInt(1);
    } else if(x === BigInt(0) && y === BigInt(1)) {
        // Else if x = 0 and y = 1, return 1
        return BigInt(1);
    } else {
        // Else, return 0
        return BigInt(0);
    }
};
BinaryXor(1n, 0n);
BigIntBitwiseOp (op, x, y)

The abstract operation BigIntBitwiseOp takes arguments op (&, ^, or |), x (a BigInt), and y (a BigInt) and returns a BigInt. It performs the following steps when called:

  1. Set x to x.
  2. Set y to y.
  3. Let result be 0.
  4. Let shift be 0.
  5. Repeat, until (x = 0 or x = -1) and (y = 0 or y = -1),
    1. Let xDigit be x modulo 2.
    2. Let yDigit be y modulo 2.
    3. If op is &, set result to $$result + 2^shift × BinaryAnd(xDigit, yDigit)$$.
    4. Else if op is |, set result to $$result + 2^shift × BinaryOr(xDigit, yDigit)$$.
    5. Else
      1. Assert: op is ^.
      2. Set result to $$result + 2^shift × BinaryXor(xDigit, yDigit)$$.
    6. Set shift to shift + 1.
    7. Set x to $$(x - xDigit) / 2$$.
    8. Set y to $$(y - yDigit) / 2$$.
  6. If op is &, let tmp be BinaryAnd(x modulo 2, y modulo 2).
  7. Else if op is |, let tmp be BinaryOr(x modulo 2, y modulo 2).
  8. Else,
    1. Assert: op is ^.
    2. Let tmp be BinaryXor(x modulo 2, y modulo 2).
  9. If tmp ≠ 0, then
    1. Set result to $$result - 2^shift$$.
    2. NOTE: This extends the sign.
  10. Return the BigInt value for result.

If we translated the steps into Javascript codes, this is more or less it would look like:

function BigIntBitwiseOp(op, x, y) {
    // Set x to x
    x = BigInt(x);
    // Set y to y
    y = BigInt(y);
    // Let result be 0
    let result = BigInt(0);
    // Let shift be 0
    let shift = BigInt(0);
    // Repeat, until (x = 0 or x = -1) and (y = 0 or y = -1)
    do {
        // Let xDigit be x modulo 2
        let xDigit = x % BigInt(2);
        // Let yDigit be y modulo 2
        let yDigit = y % BigInt(2);
        
        if(op === '&') {
            // If op is &, set result to result + 2^shift × BinaryAnd(xDigit, yDigit).
            result = result + (BigInt(2) ** shift) * BinaryAnd(xDigit, yDigit);
            
        } else if(op === '|') {
            // Else if op is |, set result to result + 2^shift × BinaryOr(xDigit, yDigit)
            result = result + BigInt(2) ** shift * BinaryOr(xDigit, yDigit);
        } else {
            // Else op is ^.
            // Set result to result + 2^shift × BinaryXor(xDigit, yDigit)
            result = result + BigInt(2) ** shift * BinaryXor(xDigit, yDigit);
        }
        // Set shift to shift + 1
        shift = shift + BigInt(1);
        // Set x to (x - xDigit) / 2
        x = (x - xDigit) / BigInt(2);
        // Set y to (y - yDigit) / 2
        y = (y - yDigit) / BigInt(2);
        
        if((x === BigInt(0) || x === BigInt(-1)) && (y === BigInt(0) || y === BigInt(-1))) {
            break;
        }
    }
    while (true);

    if(op === '&') {
        // If op is &, let tmp be BinaryAnd(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryAnd(x % BigInt(2), y % BigInt(2)));
        
    } else if(op === '|') {
        // Else if op is |, let tmp be BinaryOr(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryOr(x % BigInt(2), y % BigInt(2)));
    } else {
        // Else
        // Assert: op is ^
        // Let tmp be BinaryXor(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryXor(x % BigInt(2), y % BigInt(2)));
    }

    if(tmp !== BigInt(0)) {
        result = result - BigInt(2) ** shift;
    }
    
    return BigInt(result);
};
BigInt::bitwiseAND (x, y)

The abstract operation BigInt::bitwiseAND takes arguments x (a BigInt) and y (a BigInt) and returns a BigInt. It performs the following steps when called:

  1. Return BigIntBitwiseOp(&, x, y).

If we translated the steps into Javascript codes, this is more or less it would look like:

function BinaryAnd(x, y) {
    if(x === BigInt(1) && y === BigInt(1)) {
        // If x = y, return true; 
        return BigInt(1);
    }else {
        // otherwise return false
        return BigInt(0);
    }
};
function BigIntBitwiseOp(op, x, y) {
    // Set x to x
    x = BigInt(x);
    // Set y to y
    y = BigInt(y);
    // Let result be 0
    let result = BigInt(0);
    // Let shift be 0
    let shift = BigInt(0);
    // Repeat, until (x = 0 or x = -1) and (y = 0 or y = -1)
    do {
        // Let xDigit be x modulo 2
        let xDigit = x % BigInt(2);
        // Let yDigit be y modulo 2
        let yDigit = y % BigInt(2);
        
        if(op === '&') {
            // If op is &, set result to result + 2^shift × BinaryAnd(xDigit, yDigit).
            result = result + (BigInt(2) ** shift) * BinaryAnd(xDigit, yDigit);
            
        } else if(op === '|') {
            // Else if op is |, set result to result + 2^shift × BinaryOr(xDigit, yDigit)
            result = result + BigInt(2) ** shift * BinaryOr(xDigit, yDigit);
        } else {
            // Else op is ^.
            // Set result to result + 2^shift × BinaryXor(xDigit, yDigit)
            result = result + BigInt(2) ** shift * BinaryXor(xDigit, yDigit);
        }
        // Set shift to shift + 1
        shift = shift + BigInt(1);
        // Set x to (x - xDigit) / 2
        x = (x - xDigit) / BigInt(2);
        // Set y to (y - yDigit) / 2
        y = (y - yDigit) / BigInt(2);
        
        if((x === BigInt(0) || x === BigInt(-1)) && (y === BigInt(0) || y === BigInt(-1))) {
            break;
        }
    }
    while (true);

    if(op === '&') {
        // If op is &, let tmp be BinaryAnd(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryAnd(x % BigInt(2), y % BigInt(2)));
        
    } else if(op === '|') {
        // Else if op is |, let tmp be BinaryOr(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryOr(x % BigInt(2), y % BigInt(2)));
    } else {
        // Else
        // Assert: op is ^
        // Let tmp be BinaryXor(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryXor(x % BigInt(2), y % BigInt(2)));
    }

    if(tmp !== BigInt(0)) {
        result = result - BigInt(2) ** shift;
    }
    
    return BigInt(result);
};
BigInt.bitwiseAND = function (x, y) {
    return BigIntBitwiseOp('&', x, y);
}
BigInt.bitwiseAND(102n, 875n);
BigInt::bitwiseXOR (x, y)

The abstract operation BigInt::bitwiseXOR takes arguments x (a BigInt) and y (a BigInt) and returns a BigInt. It performs the following steps when called:

  1. Return BigIntBitwiseOp(^, x, y).

If we translated the steps into Javascript codes, this is more or less it would look like:

function BinaryXor(x, y) {
    if(x === BigInt(1) && y === BigInt(0)) {
        // If x = 1 and y = 0, return 1 
        return BigInt(1);
    } else if(x === BigInt(0) && y === BigInt(1)) {
        // Else if x = 0 and y = 1, return 1
        return BigInt(1);
    } else {
        // Else, return 0
        return BigInt(0);
    }
};
function BigIntBitwiseOp(op, x, y) {
    // Set x to x
    x = BigInt(x);
    // Set y to y
    y = BigInt(y);
    // Let result be 0
    let result = BigInt(0);
    // Let shift be 0
    let shift = BigInt(0);
    // Repeat, until (x = 0 or x = -1) and (y = 0 or y = -1)
    do {
        // Let xDigit be x modulo 2
        let xDigit = x % BigInt(2);
        // Let yDigit be y modulo 2
        let yDigit = y % BigInt(2);
        
        if(op === '&') {
            // If op is &, set result to result + 2^shift × BinaryAnd(xDigit, yDigit).
            result = result + (BigInt(2) ** shift) * BinaryAnd(xDigit, yDigit);
            
        } else if(op === '|') {
            // Else if op is |, set result to result + 2^shift × BinaryOr(xDigit, yDigit)
            result = result + BigInt(2) ** shift * BinaryOr(xDigit, yDigit);
        } else {
            // Else op is ^.
            // Set result to result + 2^shift × BinaryXor(xDigit, yDigit)
            result = result + BigInt(2) ** shift * BinaryXor(xDigit, yDigit);
        }
        // Set shift to shift + 1
        shift = shift + BigInt(1);
        // Set x to (x - xDigit) / 2
        x = (x - xDigit) / BigInt(2);
        // Set y to (y - yDigit) / 2
        y = (y - yDigit) / BigInt(2);
        
        if((x === BigInt(0) || x === BigInt(-1)) && (y === BigInt(0) || y === BigInt(-1))) {
            break;
        }
    }
    while (true);

    if(op === '&') {
        // If op is &, let tmp be BinaryAnd(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryAnd(x % BigInt(2), y % BigInt(2)));
        
    } else if(op === '|') {
        // Else if op is |, let tmp be BinaryOr(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryOr(x % BigInt(2), y % BigInt(2)));
    } else {
        // Else
        // Assert: op is ^
        // Let tmp be BinaryXor(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryXor(x % BigInt(2), y % BigInt(2)));
    }

    if(tmp !== BigInt(0)) {
        result = result - BigInt(2) ** shift;
    }
    
    return BigInt(result);
};
BigInt.bitwiseXOR = function (x, y) {
    return BigIntBitwiseOp('^', x, y);
}
BigInt.bitwiseXOR(102n, 875n);
BigInt::bitwiseOR (x, y)

The abstract operation BigInt::bitwiseOR takes arguments x (a BigInt) and y (a BigInt) and returns a BigInt. It performs the following steps when called:

  1. Return BigIntBitwiseOp(|, x, y).

If we translated the steps into Javascript codes, this is more or less it would look like:

function BinaryOr(x, y) {
    if(x === BigInt(1) || y === BigInt(1)) {
        // If x = 1 or y = 1, return true; 
        return BigInt(1);
    }else {
        // otherwise return false
        return BigInt(0);
    }
};
function BigIntBitwiseOp(op, x, y) {
    // Set x to x
    x = BigInt(x);
    // Set y to y
    y = BigInt(y);
    // Let result be 0
    let result = BigInt(0);
    // Let shift be 0
    let shift = BigInt(0);
    // Repeat, until (x = 0 or x = -1) and (y = 0 or y = -1)
    do {
        // Let xDigit be x modulo 2
        let xDigit = x % BigInt(2);
        // Let yDigit be y modulo 2
        let yDigit = y % BigInt(2);
        
        if(op === '&') {
            // If op is &, set result to result + 2^shift × BinaryAnd(xDigit, yDigit).
            result = result + (BigInt(2) ** shift) * BinaryAnd(xDigit, yDigit);
            
        } else if(op === '|') {
            // Else if op is |, set result to result + 2^shift × BinaryOr(xDigit, yDigit)
            result = result + BigInt(2) ** shift * BinaryOr(xDigit, yDigit);
        } else {
            // Else op is ^.
            // Set result to result + 2^shift × BinaryXor(xDigit, yDigit)
            result = result + BigInt(2) ** shift * BinaryXor(xDigit, yDigit);
        }
        // Set shift to shift + 1
        shift = shift + BigInt(1);
        // Set x to (x - xDigit) / 2
        x = (x - xDigit) / BigInt(2);
        // Set y to (y - yDigit) / 2
        y = (y - yDigit) / BigInt(2);
        
        if((x === BigInt(0) || x === BigInt(-1)) && (y === BigInt(0) || y === BigInt(-1))) {
            break;
        }
    }
    while (true);

    if(op === '&') {
        // If op is &, let tmp be BinaryAnd(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryAnd(x % BigInt(2), y % BigInt(2)));
        
    } else if(op === '|') {
        // Else if op is |, let tmp be BinaryOr(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryOr(x % BigInt(2), y % BigInt(2)));
    } else {
        // Else
        // Assert: op is ^
        // Let tmp be BinaryXor(x modulo 2, y modulo 2)
        tmp = BigInt(BinaryXor(x % BigInt(2), y % BigInt(2)));
    }

    if(tmp !== BigInt(0)) {
        result = result - BigInt(2) ** shift;
    }
    
    return BigInt(result);
};
BigInt.bitwiseOR = function (x, y) {
    return BigIntBitwiseOp('|', x, y);
}
BigInt.bitwiseOR(102n, 875n);
BigInt::toString (x, radix)

The abstract operation BigInt::toString takes arguments x (a BigInt) and radix (an integer in the inclusive interval from 2 to 36) and returns a String. It represents x as a String using a positional numeral system with radix radix. The digits used in the representation of a BigInt using radix r are taken from the first r code units of "0123456789abcdefghijklmnopqrstuvwxyz" in order. The representation of BigInts other than 0 never includes leading zeroes. It performs the following steps when called:

  1. If x < 0, return the string-concatenation of "-" and BigInt::toString(-x, radix).
  2. Return the String value consisting of the representation of x using radix radix.

If we translated the steps into Javascript codes, this is more or less it would look like:

BigInt.toString = function (x, radix) {
    if(x < BigInt(0)) {
        return "-" + BigInt.toString(-x, radix);
    }

    let result = "";
    let number = BigInt(x);

    while(number > BigInt(0n)) {    
        let remainder = number % BigInt(radix);
            
        if (remainder === BigInt(10n)) {
            result = "A" + result;
        } else if (remainder === BigInt(11n)) {
            result = "B" + result;
        } else if (remainder === BigInt(12n)) {
            result = "C" + result;
        } else if (remainder === BigInt(13n)) {
            result = "D" + result;
        } else if (remainder === BigInt(14n)) {
            result = "E" + result;
        } else if (remainder === BigInt(15n)) {
            result = "F" + result;
        } else if (remainder === BigInt(16n)) {
            result = "G" + result;
        } else if (remainder === BigInt(17n)) {
            result = "H" + result;
        } else if (remainder === BigInt(18n)) {
            result = "I" + result;
        } else if (remainder === BigInt(19n)) {
            result = "J" + result;
        } else if (remainder === BigInt(20n)) {
            result = "K" + result;
        } else if (remainder === BigInt(21n)) {
            result = "L" + result;
        } else if (remainder === BigInt(22n)) {
            result = "M" + result;
        } else if (remainder === BigInt(23n)) {
            result = "N" + result;
        } else if (remainder === BigInt(24n)) {
            result = "O" + result;
        } else if (remainder === BigInt(25n)) {
            result = "P" + result;
        } else if (remainder === BigInt(26n)) {
            result = "Q" + result;
        } else if (remainder === BigInt(27n)) {
            result = "R" + result;
        } else if (remainder === BigInt(28n)) {
            result = "S" + result;
        } else if (remainder === BigInt(29n)) {
            result = "T" + result;
        } else if (remainder === BigInt(30n)) {
            result = "U" + result;
        } else if (remainder === BigInt(31n)) {
            result = "V" + result;
        } else if (remainder === BigInt(32n)) {
            result = "W" + result;
        } else if (remainder === BigInt(33n)) {
            result = "X" + result;
        } else if (remainder === BigInt(34n)) {
            result = "Y" + result;
        } else if (remainder === BigInt(35n)) {
            result = "Z" + result;
        } else {
            result = remainder + result;
        }    
        
        number = number / radix;
    }
    return result;
}
BigInt.toString(123456789012345678901234567890n, 16n);