// Rational class for rational number representation class Rational { constructor(numerator, denominator) { if (denominator < 0) { this.numerator = -numerator; this.denominator = -denominator; } else { this.numerator = numerator; this.denominator = denominator; } if (numerator === 0) { this.denominator = 1; } const divisor = this.gcd(Math.abs(this.numerator), this.denominator); this.numerator = Math.floor(this.numerator / divisor); this.denominator = Math.floor(this.denominator / divisor); } gcd(a, b) { while (b !== 0) { const temp = b; b = a % b; a = temp; } return a; } toString() { return `${this.numerator} / ${this.denominator}`; } } // P_adic class for p-adic number representation class P_adic { static PRECISION = 40; static ORDER_MAX = 1000; static DIGITS_SIZE = P_adic.PRECISION + 5; // Create a P_adic number with p = 'prime', from the given rational 'numerator' / 'denominator' constructor(prime, numerator, denominator, digits = null, order = null) { this.prime = prime; // If digits and order are provided, use them directly (for internal use) if (digits !== null && order !== null) { this.digits = digits; this.order = order; return; } if (denominator === 0) { throw new Error("Denominator cannot be zero"); } this.order = 0; // Process rational zero if (numerator === 0) { this.digits = Array(P_adic.DIGITS_SIZE).fill(0); this.order = P_adic.ORDER_MAX; return; } // Remove multiples of 'prime' and adjust the order of the P_adic number accordingly while (this.moduloPrime(numerator) === 0) { numerator = Math.floor(numerator / prime); this.order += 1; } while (this.moduloPrime(denominator) === 0) { denominator = Math.floor(denominator / prime); this.order -= 1; } // Standard calculation of P_adic digits const inverse = this.moduloInverse(denominator); this.digits = []; while (this.digits.length < P_adic.DIGITS_SIZE) { const digit = this.moduloPrime(numerator * inverse); this.digits.push(digit); numerator -= digit * denominator; if (numerator !== 0) { // The denominator is not a power of a prime let count = 0; while (this.moduloPrime(numerator) === 0) { numerator = Math.floor(numerator / prime); count += 1; } for (let i = count; i > 1; --i) { this.digits.push(0); } } } } // Return the sum of this P_adic number with the given P_adic number add(other) { if (this.prime !== other.prime) { throw new Error("Cannot add p-adic's with different primes"); } let this_digits = [...this.digits]; let other_digits = [...other.digits]; let result = []; // Adjust the digits so that the P_adic points are aligned for (let i = 0; i < -this.order + other.order; ++i) { other_digits.unshift(0); } for (let i = 0; i < -other.order + this.order; ++i) { this_digits.unshift(0); } // Standard digit by digit addition let carry = 0; for (let i = 0; i < Math.min(this_digits.length, other_digits.length); ++i) { const sum = this_digits[i] + other_digits[i] + carry; const remainder = sum % this.prime; carry = (sum >= this.prime) ? 1 : 0; result.push(remainder); } return new P_adic( this.prime, null, null, result, this.allZeroDigits(result) ? P_adic.ORDER_MAX : Math.min(this.order, other.order) ); } // Return the Rational representation of this P_adic number convertToRational() { let numbers = [...this.digits]; // Zero if (numbers.length === 0 || this.allZeroDigits(numbers)) { return new Rational(0, 1); } // Positive integer if (this.order >= 0 && this.endsWith(numbers, 0)) { for (let i = 0; i < this.order; ++i) { numbers.unshift(0); } return new Rational(this.convertToDecimal(numbers), 1); } // Negative integer if (this.order >= 0 && this.endsWith(numbers, this.prime - 1)) { this.negateDigits(numbers); for (let i = 0; i < this.order; ++i) { numbers.unshift(0); } return new Rational(-this.convertToDecimal(numbers), 1); } // Rational const copy = new P_adic(this.prime, null, null, [...this.digits], this.order); let sum = new P_adic(this.prime, null, null, [...this.digits], this.order); let denominator = 1; do { sum = sum.add(copy); denominator += 1; } while (!(this.endsWith(sum.digits, 0) || this.endsWith(sum.digits, this.prime - 1))); const negative = this.endsWith(sum.digits, this.prime - 1); if (negative) { this.negateDigits(sum.digits); } let numerator = negative ? -this.convertToDecimal(sum.digits) : this.convertToDecimal(sum.digits); if (this.order > 0) { numerator *= Math.pow(this.prime, this.order); } if (this.order < 0) { denominator *= Math.pow(this.prime, -this.order); } return new Rational(numerator, denominator); } // Return a string representation of this P_adic number toString() { let numbers = [...this.digits]; this.padWithZeros(numbers); let result = ""; for (let i = numbers.length - 1; i >= 0; --i) { result += this.digits[i].toString(); } if (this.order >= 0) { for (let i = 0; i < this.order; ++i) { result += "0"; } result += ".0"; } else { result = result.slice(0, result.length + this.order) + "." + result.slice(result.length + this.order); while (result[result.length - 1] === '0') { result = result.substring(0, result.length - 1); } } return " ..." + result.substring(result.length - P_adic.PRECISION - 1); } // Transform the given array of digits representing a P_adic number // into an array which represents the negation of the P_adic number negateDigits(numbers) { numbers[0] = this.moduloPrime(this.prime - numbers[0]); for (let i = 1; i < numbers.length; ++i) { numbers[i] = this.prime - 1 - numbers[i]; } } // Return the multiplicative inverse of the given number modulo 'prime' moduloInverse(number) { let inverse = 1; while (this.moduloPrime(inverse * number) !== 1) { inverse += 1; } return inverse; } // Return the given number modulo 'prime' in the range 0...'prime' - 1 moduloPrime(number) { const div = number % this.prime; return (div >= 0) ? div : div + this.prime; } // The given array is padded on the right by zeros up to a maximum length of 'DIGITS_SIZE' padWithZeros(array) { while (array.length < P_adic.DIGITS_SIZE) { array.push(0); } } // Return the given array of base 'prime' integers converted to a decimal integer convertToDecimal(numbers) { let decimal = 0; let multiple = 1; for (const number of numbers) { decimal += number * multiple; multiple *= this.prime; } return decimal; } // Return whether the given array consists of all zeros allZeroDigits(numbers) { for (const number of numbers) { if (number !== 0) { return false; } } return true; } // Return whether the given array ends with multiple instances of the given number endsWith(numbers, number) { for (let i = numbers.length - 1; i >= numbers.length - P_adic.PRECISION / 2; --i) { if (numbers[i] !== number) { return false; } } return true; } } // Main function equivalent function main() { console.log("3-adic numbers:"); let padic_one = new P_adic(3, -2, 87); console.log("-2 / 87 => " + padic_one.toString()); let padic_two = new P_adic(3, 4, 97); console.log("4 / 97 => " + padic_two.toString()); let sum = padic_one.add(padic_two); console.log("sum => " + sum.toString()); console.log("Rational = " + sum.convertToRational().toString()); console.log(); console.log("7-adic numbers:"); padic_one = new P_adic(7, 5, 8); console.log("5 / 8 => " + padic_one.toString()); padic_two = new P_adic(7, 353, 30809); console.log("353 / 30809 => " + padic_two.toString()); sum = padic_one.add(padic_two); console.log("sum => " + sum.toString()); console.log("Rational = " + sum.convertToRational().toString()); console.log(); } // Run the main function main();