Data update

This commit is contained in:
Ingy döt Net 2025-06-11 20:16:52 -04:00
parent 72eb4943cb
commit 4d5544505c
2347 changed files with 62432 additions and 16731 deletions

View file

@ -47,4 +47,3 @@ set the shortest prime-exponent combinations that allow valid reconstruction.
__TOC__

View file

@ -0,0 +1,402 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public static class PAdicNumbersBasic
{
public static void Main(string[] args)
{
Console.WriteLine("3-adic numbers:");
Padic padicOne = new Padic(3, -5, 9);
Console.WriteLine("-5 / 9 => " + padicOne);
Padic padicTwo = new Padic(3, 47, 12);
Console.WriteLine("47 / 12 => " + padicTwo);
Padic sum = padicOne.Add(padicTwo);
Console.WriteLine("sum => " + sum);
Console.WriteLine("Rational = " + sum.ConvertToRational());
Console.WriteLine();
Console.WriteLine("7-adic numbers:");
padicOne = new Padic(7, 5, 8);
Console.WriteLine("5 / 8 => " + padicOne);
padicTwo = new Padic(7, 353, 30809);
Console.WriteLine("353 / 30809 => " + padicTwo);
sum = padicOne.Add(padicTwo);
Console.WriteLine("sum => " + sum);
Console.WriteLine("Rational = " + sum.ConvertToRational());
}
}
public sealed class Padic
{
/**
* Create a p-adic, with p = aPrime, from the given rational 'aNumerator' / 'aDenominator'.
*/
public Padic(int aPrime, int aNumerator, int aDenominator)
{
if (aDenominator == 0)
{
throw new ArgumentException("Denominator cannot be zero");
}
prime = aPrime;
digits = new List<int>(DIGITS_SIZE);
order = 0;
// Process rational zero
if (aNumerator == 0)
{
order = MAX_ORDER;
return;
}
// Remove multiples of 'prime' and adjust the order of the p-adic number accordingly
while (FloorMod(aNumerator, prime) == 0)
{
aNumerator /= prime;
order += 1;
}
while (FloorMod(aDenominator, prime) == 0)
{
aDenominator /= prime;
order -= 1;
}
// Standard calculation of p-adic digits
long inverse = ModuloInverse(aDenominator);
while (digits.Count < DIGITS_SIZE)
{
int digit = FloorMod((int)(aNumerator * inverse), prime);
digits.Add(digit);
aNumerator -= digit * aDenominator;
if (aNumerator != 0)
{
// The denominator is not a power of a prime
int count = 0;
while (FloorMod(aNumerator, prime) == 0)
{
aNumerator /= prime;
count += 1;
}
for (int i = count; i > 1; i--)
{
digits.Add(0);
}
}
}
}
/**
* Return the sum of this p-adic number and the given p-adic number.
*/
public Padic Add(Padic aOther)
{
if (prime != aOther.prime)
{
throw new ArgumentException("Cannot add p-adic's with different primes");
}
List<int> result = new List<int>();
// Adjust the digits so that the p-adic points are aligned
for (int i = 0; i < -order + aOther.order; i++)
{
aOther.digits.Insert(0, 0);
}
for (int i = 0; i < -aOther.order + order; i++)
{
digits.Insert(0, 0);
}
// Standard digit by digit addition
int carry = 0;
for (int i = 0; i < Math.Min(digits.Count, aOther.digits.Count); i++)
{
int sum = digits[i] + aOther.digits[i] + carry;
int remainder = FloorMod(sum, prime);
carry = (sum >= prime) ? 1 : 0;
result.Add(remainder);
}
// Reverse the changes made to the digits
for (int i = 0; i < -order + aOther.order; i++)
{
aOther.digits.RemoveAt(0);
}
for (int i = 0; i < -aOther.order + order; i++)
{
digits.RemoveAt(0);
}
return new Padic(prime, result, AllZeroDigits(result) ? MAX_ORDER : Math.Min(order, aOther.order));
}
/**
* Return the Rational representation of this p-adic number.
*/
public Rational ConvertToRational()
{
List<int> numbers = new List<int>(digits);
// Zero
if (numbers.Count == 0 || AllZeroDigits(numbers))
{
return new Rational(0, 1);
}
// Positive integer
if (order >= 0 && EndsWith(numbers, 0))
{
for (int i = 0; i < order; i++)
{
numbers.Insert(0, 0);
}
return new Rational(ConvertToDecimal(numbers), 1);
}
// Negative integer
if (order >= 0 && EndsWith(numbers, prime - 1))
{
NegateList(numbers);
for (int i = 0; i < order; i++)
{
numbers.Insert(0, 0);
}
return new Rational(-ConvertToDecimal(numbers), 1);
}
// Rational
Padic sum = new Padic(prime, new List<int>(digits), order);
Padic self = new Padic(prime, new List<int>(digits), order);
int denominator = 1;
do
{
sum = sum.Add(self);
denominator += 1;
} while (!(EndsWith(sum.digits, 0) || EndsWith(sum.digits, prime - 1)));
bool negative = EndsWith(sum.digits, prime - 1);
if (negative)
{
NegateList(sum.digits);
}
int numerator = negative ? -ConvertToDecimal(sum.digits) : ConvertToDecimal(sum.digits);
if (order > 0)
{
numerator *= (int)Math.Pow(prime, order);
}
if (order < 0)
{
denominator *= (int)Math.Pow(prime, -order);
}
return new Rational(numerator, denominator);
}
/**
* Return a string representation of this p-adic.
*/
public override string ToString()
{
List<int> numbers = new List<int>(digits);
PadWithZeros(numbers);
numbers.Reverse();
string numberString = string.Join("", numbers.Select(n => n.ToString()));
StringBuilder builder = new StringBuilder(numberString);
if (order >= 0)
{
for (int i = 0; i < order; i++)
{
builder.Append("0");
}
builder.Append(".0");
}
else
{
builder.Insert(builder.Length + order, ".");
while (builder.ToString().EndsWith("0"))
{
builder.Remove(builder.Length - 1, 1);
}
}
return " ..." + builder.ToString().Substring(builder.Length - PRECISION - 1);
}
// PRIVATE //
/**
* Create a p-adic, with p = 'aPrime', directly from a list of digits.
*
* With 'aOrder' = 0, the list [1, 2, 3, 4, 5] creates the p-adic ...54321.0
* 'aOrder' > 0 shifts the list 'aOrder' places to the left and
* 'aOrder' < 0 shifts the list 'aOrder' places to the right.
*/
private Padic(int aPrime, List<int> aDigits, int aOrder)
{
prime = aPrime;
digits = new List<int>(aDigits);
order = aOrder;
}
/**
* Return the multiplicative inverse of the given decimal number modulo 'prime'.
*/
private int ModuloInverse(int aNumber)
{
int inverse = 1;
while (FloorMod(inverse * aNumber, prime) != 1)
{
inverse += 1;
}
return inverse;
}
/**
* Transform the given list of digits representing a p-adic number
* into a list which represents the negation of the p-adic number.
*/
private void NegateList(List<int> aDigits)
{
aDigits[0] = FloorMod(prime - aDigits[0], prime);
for (int i = 1; i < aDigits.Count; i++)
{
aDigits[i] = prime - 1 - aDigits[i];
}
}
/**
* Return the given list of base 'prime' integers converted to a decimal integer.
*/
private int ConvertToDecimal(List<int> aNumbers)
{
int decimal_value = 0;
int multiple = 1;
foreach (int number in aNumbers)
{
decimal_value += number * multiple;
multiple *= prime;
}
return decimal_value;
}
/**
* Return whether the given list consists of all zeros.
*/
private static bool AllZeroDigits(List<int> aList)
{
return aList.All(i => i == 0);
}
/**
* The given list is padded on the right by zeros up to a maximum length of 'PRECISION'.
*/
private static void PadWithZeros(List<int> aList)
{
while (aList.Count < DIGITS_SIZE)
{
aList.Add(0);
}
}
/**
* Return whether the given list ends with multiple instances of the given number.
*/
private static bool EndsWith(List<int> aDigits, int aDigit)
{
for (int i = aDigits.Count - 1; i >= aDigits.Count - PRECISION / 2; i--)
{
if (aDigits[i] != aDigit)
{
return false;
}
}
return true;
}
/**
* C# implementation of Java's Math.floorMod
*/
private static int FloorMod(int x, int y)
{
int r = x % y;
// If the signs are different and modulo not zero, adjust result
if ((x ^ y) < 0 && r != 0)
{
r += y;
}
return r;
}
public class Rational
{
private int numerator;
private int denominator;
public Rational(int aNumerator, int aDenominator)
{
if (aDenominator < 0)
{
numerator = -aNumerator;
denominator = -aDenominator;
}
else
{
numerator = aNumerator;
denominator = aDenominator;
}
if (aNumerator == 0)
{
denominator = 1;
}
int gcd = GCD(numerator, denominator);
numerator /= gcd;
denominator /= gcd;
}
public override string ToString()
{
return numerator + " / " + denominator;
}
private int GCD(int aOne, int aTwo)
{
if (aTwo == 0)
{
return Math.Abs(aOne);
}
return GCD(aTwo, FloorMod(aOne, aTwo));
}
}
private List<int> digits;
private int order;
private readonly int prime;
private const int MAX_ORDER = 1000;
private const int PRECISION = 40;
private const int DIGITS_SIZE = PRECISION + 5;
}

View file

@ -0,0 +1,310 @@
// 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();

View file

@ -0,0 +1,391 @@
use std::cmp;
use std::sync::atomic::{AtomicI32, Ordering};
use std::cell::UnsafeCell;
// constants
const EMX: i32 = 64; // exponent maximum (if indexing starts at -EMX)
const DMX: i32 = 100000; // approximation loop maximum
const AMX: i32 = 1048576; // argument maximum
const PMAX: i32 = 32749; // prime maximum
// Using atomics for the global variables
static P1: AtomicI32 = AtomicI32::new(0);
static P: AtomicI32 = AtomicI32::new(7); // default prime
static K: AtomicI32 = AtomicI32::new(11); // precision
fn abs(a: i32) -> i32 {
if a >= 0 {
a
} else {
-a
}
}
fn min(a: i32, b: i32) -> i32 {
if a < b {
a
} else {
b
}
}
struct Ratio {
a: i32,
b: i32,
}
struct Padic {
v: i32,
d: [i32; 2 * EMX as usize], // add EMX to index to be consistent with FB
}
impl Padic {
// Create a new Padic with default values
fn new() -> Self {
Padic {
v: 0,
d: [0; 2 * EMX as usize],
}
}
// (re)initialize receiver from Ratio, set 'sw' to print
fn r2pa(&mut self, q: Ratio, sw: i32) -> i32 {
let mut a = q.a;
let mut b = q.b;
if b == 0 {
return 1;
}
if b < 0 {
b = -b;
a = -a;
}
if abs(a) > AMX || b > AMX {
return -1;
}
let p_val = P.load(Ordering::Relaxed);
if p_val < 2 || K.load(Ordering::Relaxed) < 1 {
return 1;
}
// Set P to minimum of P and PMAX
let p_val = cmp::min(p_val, PMAX);
P.store(p_val, Ordering::Relaxed);
// Set K to minimum of K and EMX-1
let k_val = cmp::min(K.load(Ordering::Relaxed), EMX - 1);
K.store(k_val, Ordering::Relaxed);
if sw != 0 {
println!("{}/{} + ", a, b); // numerator, denominator
println!("0({}^{})", p_val, k_val); // prime, precision
}
// (re)initialize
self.v = 0;
P1.store(p_val - 1, Ordering::Relaxed);
self.d = [0; 2 * EMX as usize];
if a == 0 {
return 0;
}
let mut i = 0;
let p1_val = P1.load(Ordering::Relaxed);
// find -exponent of p in b
while b % p_val == 0 {
b = b / p_val;
i -= 1;
}
let mut s = 0;
let r = b % p_val;
// modular inverse for small p
let mut b1 = 1;
while b1 <= p1_val {
s += r;
if s > p1_val {
s -= p_val;
}
if s == 1 {
break;
}
b1 += 1;
}
if b1 == p_val {
println!("r2pa: impossible inverse mod");
return -1;
}
self.v = EMX;
loop {
// find exponent of P in a
while a % p_val == 0 {
a = a / p_val;
i += 1;
}
// valuation
if self.v == EMX {
self.v = i;
}
// upper bound
if i >= EMX {
break;
}
// check precision
if (i - self.v) > k_val {
break;
}
// next digit
self.d[(i + EMX) as usize] = a * b1 % p_val;
if self.d[(i + EMX) as usize] < 0 {
self.d[(i + EMX) as usize] += p_val;
}
// remainder - digit * divisor
a -= self.d[(i + EMX) as usize] * b;
if a == 0 {
break;
}
}
0
}
// Horner's rule
fn dsum(&self) -> i32 {
let t = cmp::min(self.v, 0);
let mut s = 0;
let p_val = P.load(Ordering::Relaxed);
let k_val = K.load(Ordering::Relaxed);
for i in (t..=k_val - 1 + t).rev() {
let r = s;
s *= p_val;
if r != 0 && (s / r - p_val != 0) {
// overflow
s = -1;
break;
}
s += self.d[(i + EMX) as usize];
}
s
}
// add b to receiver
fn add(&self, b: &Padic) -> Padic {
let mut c = 0;
let mut r = Padic::new();
r.v = cmp::min(self.v, b.v);
let p_val = P.load(Ordering::Relaxed);
let p1_val = P1.load(Ordering::Relaxed);
let k_val = K.load(Ordering::Relaxed);
for i in r.v..=k_val + r.v {
c += self.d[(i + EMX) as usize] + b.d[(i + EMX) as usize];
if c > p1_val {
r.d[(i + EMX) as usize] = c - p_val;
c = 1;
} else {
r.d[(i + EMX) as usize] = c;
c = 0;
}
}
r
}
// complement of receiver
fn cmpt(&self) -> Padic {
let mut c = 1;
let mut r = Padic::new();
r.v = self.v;
let p_val = P.load(Ordering::Relaxed);
let p1_val = P1.load(Ordering::Relaxed);
let k_val = K.load(Ordering::Relaxed);
for i in self.v..=k_val + self.v {
c += p1_val - self.d[(i + EMX) as usize];
if c > p1_val {
r.d[(i + EMX) as usize] = c - p_val;
c = 1;
} else {
r.d[(i + EMX) as usize] = c;
c = 0;
}
}
r
}
// rational reconstruction
fn crat(&self) {
let mut fl = false;
let mut s = self.clone();
let mut j = 0;
let mut i = 1;
let p_val = P.load(Ordering::Relaxed);
let p1_val = P1.load(Ordering::Relaxed);
let k_val = K.load(Ordering::Relaxed);
// denominator count
while i <= DMX {
// check for integer
j = k_val - 1 + self.v;
while j >= self.v {
if s.d[(j + EMX) as usize] != 0 {
break;
}
j -= 1;
}
fl = ((j - self.v) * 2) < k_val;
if fl {
fl = false;
break;
}
// check negative integer
j = k_val - 1 + self.v;
while j >= self.v {
if p1_val - s.d[(j + EMX) as usize] != 0 {
break;
}
j -= 1;
}
fl = ((j - self.v) * 2) < k_val;
if fl {
break;
}
// repeatedly add self to s
s = s.add(self);
i += 1;
}
if fl {
s = s.cmpt();
}
// numerator: weighted digit sum
let x = s.dsum();
let mut y = i;
if x < 0 || y > DMX {
println!("{} {}", x, y);
println!("crat: fail");
} else {
// negative powers
let mut i = self.v;
while i <= -1 {
y *= p_val;
i += 1;
}
// negative rational
let final_x = if fl { -x } else { x };
print!("{}", final_x);
if y > 1 {
print!("/{}", y);
}
println!();
}
}
// print expansion
fn printf(&self, sw: i32) {
let t = cmp::min(self.v, 0);
let k_val = K.load(Ordering::Relaxed);
for i in (t..=k_val - 1 + t).rev() {
print!("{}", self.d[(i + EMX) as usize]);
if i == 0 && self.v < 0 {
print!(".");
}
print!(" ");
}
println!();
// rational approximation
if sw != 0 {
self.crat();
}
}
}
// Implement the Clone trait for Padic
impl Clone for Padic {
fn clone(&self) -> Self {
Padic {
v: self.v,
d: self.d,
}
}
}
fn main() {
let data = vec![
/* rational reconstruction depends on the precision
until the dsum-loop overflows */
vec![2, 1, 2, 4, 1, 1],
vec![4, 1, 2, 4, 3, 1],
vec![4, 1, 2, 5, 3, 1],
vec![4, 9, 5, 4, 8, 9],
vec![26, 25, 5, 4, -109, 125],
vec![49, 2, 7, 6, -4851, 2],
vec![-9, 5, 3, 8, 27, 7],
vec![5, 19, 2, 12, -101, 384],
/* two decadic pairs */
vec![2, 7, 10, 7, -1, 7],
vec![34, 21, 10, 9, -39034, 791],
/* familiar digits */
vec![11, 4, 2, 43, 679001, 207],
vec![-8, 9, 23, 9, 302113, 92],
vec![-22, 7, 3, 23, 46071, 379],
vec![-22, 7, 32749, 3, 46071, 379],
vec![35, 61, 5, 20, 9400, 109],
vec![-101, 109, 61, 7, 583376, 6649],
vec![-25, 26, 7, 13, 5571, 137],
vec![1, 4, 7, 11, 9263, 2837],
vec![122, 407, 7, 11, -517, 1477],
/* more subtle */
vec![5, 8, 7, 11, 353, 30809],
];
let mut sw = 0;
let mut a = Padic::new();
let mut b = Padic::new();
for d in data {
let q = Ratio { a: d[0], b: d[1] };
P.store(d[2], Ordering::Relaxed);
K.store(d[3], Ordering::Relaxed);
sw = a.r2pa(q, 1);
if sw == 1 {
break;
}
a.printf(0);
let q_b = Ratio { a: d[4], b: d[5] };
sw = sw | b.r2pa(q_b, 1);
if sw == 1 {
break;
}
if sw == 0 {
b.printf(0);
let c = a.add(&b);
println!("+ =");
c.printf(1);
}
println!();
}
}

View file

@ -0,0 +1,357 @@
const std = @import("std");
const math = std.math;
const stdout = std.io.getStdOut().writer();
const print = std.debug.print;
// Constants
const EMX: i32 = 64; // exponent maximum (if indexing starts at -EMX)
const DMX: i32 = 100000; // approximation loop maximum
const AMX: i32 = 1048576; // argument maximum
const PMAX: i32 = 32749; // prime maximum
// Global variables
var P1: i32 = 0;
var P: i32 = 7; // default prime
var K: i32 = 11; // precision
// Helper functions
fn abs(a: i32) i32 {
return if (a >= 0) a else -a;
}
fn min(a: i32, b: i32) i32 {
return if (a < b) a else b;
}
const Ratio = struct {
a: i32,
b: i32,
};
const Padic = struct {
v: i32,
d: [2 * EMX]i32, // add EMX to index to be consistent
// Create a new Padic with default values
fn new() Padic {
return Padic{
.v = 0,
.d = [_]i32{0} ** (2 * EMX),
};
}
// (re)initialize receiver from Ratio, set 'sw' to print
fn r2pa(self: *Padic, q: Ratio, sw: i32) i32 {
var a = q.a;
var b = q.b;
if (b == 0) {
return 1;
}
if (b < 0) {
b = -b;
a = -a;
}
if (abs(a) > AMX or b > AMX) {
return -1;
}
if (P < 2 or K < 1) {
return 1;
}
// Set P to minimum of P and PMAX
P = @min(P, PMAX);
// Set K to minimum of K and EMX-1
K = @min(K, EMX - 1);
if (sw != 0) {
print("{d}/{d} + \n", .{a, b}); // numerator, denominator
print("0({d}^{d})\n", .{P, K}); // prime, precision
}
// (re)initialize
self.v = 0;
P1 = P - 1;
// std.mem.set(i32, &self.d, 0);
if (a == 0) {
return 0;
}
var i: i32 = 0;
// find -exponent of p in b
while ( @rem(b, P) == 0) {
b = @divTrunc(b, P);
i -= 1;
}
var s: i32 = 0;
const r = @rem(b, P);
// modular inverse for small p
var b1: i32 = 1;
while (b1 <= P1) {
s += r;
if (s > P1) {
s -= P;
}
if (s == 1) {
break;
}
b1 += 1;
}
if (b1 == P) {
print("r2pa: impossible inverse mod\n", .{});
return -1;
}
self.v = EMX;
while (true) {
// find exponent of P in a
while ( @rem(a, P)==0) {
a = @divTrunc(a, P);
i += 1;
}
// valuation
if (self.v == EMX) {
self.v = i;
}
// upper bound
if (i >= EMX) {
break;
}
// check precision
if ((i - self.v) > K) {
break;
}
// next digit
self.d[@intCast(i + EMX)] = @mod(a * b1, P);
if (self.d[@intCast(i + EMX)] < 0) {
self.d[@intCast(i + EMX)] += P;
}
// remainder - digit * divisor
a -= self.d[@intCast(i + EMX)] * b;
if (a == 0) {
break;
}
}
return 0;
}
// Horner's rule
fn dsum(self: *const Padic) i32 {
const t = @min(self.v, 0);
var s: i32 = 0;
var i = K - 1 + t;
while (i >= t) : (i -= 1) {
const r = s;
s *= P;
if (r != 0 and (@divTrunc(s, r) - P != 0)) {
// overflow
s = -1;
break;
}
s += self.d[@intCast(i + EMX)];
}
return s;
}
// add b to receiver
fn add(self: *const Padic, b: *const Padic) Padic {
var c: i32 = 0;
var r = Padic.new();
r.v = @min(self.v, b.v);
var i: i32 = r.v;
while (i <= K + r.v) : (i += 1) {
c += self.d[@intCast(i + EMX)] + b.d[@intCast(i + EMX)];
if (c > P1) {
r.d[@intCast(i + EMX)] = c - P;
c = 1;
} else {
r.d[@intCast(i + EMX)] = c;
c = 0;
}
}
return r;
}
// complement of receiver
fn cmpt(self: *const Padic) Padic {
var c: i32 = 1;
var r = Padic.new();
r.v = self.v;
var i: i32 = self.v;
while (i <= K + self.v) : (i += 1) {
c += P1 - self.d[@intCast(i + EMX)];
if (c > P1) {
r.d[@intCast(i + EMX)] = c - P;
c = 1;
} else {
r.d[@intCast(i + EMX)] = c;
c = 0;
}
}
return r;
}
// rational reconstruction
fn crat(self: *const Padic) void {
var fl = false;
var s = self.*;
var j: i32 = 0;
var i: i32 = 1;
// denominator count
while (i <= DMX) : (i += 1) {
// check for integer
j = K - 1 + self.v;
while (j >= self.v) : (j -= 1) {
if (s.d[@intCast(j + EMX)] != 0) {
break;
}
}
fl = ((j - self.v) * 2) < K;
if (fl) {
fl = false;
break;
}
// check negative integer
j = K - 1 + self.v;
while (j >= self.v) : (j -= 1) {
if (P1 - s.d[@intCast(j + EMX)] != 0) {
break;
}
}
fl = ((j - self.v) * 2) < K;
if (fl) {
break;
}
// repeatedly add self to s
s = s.add(self);
}
if (fl) {
s = s.cmpt();
}
// numerator: weighted digit sum
const x = s.dsum();
var y = i;
if (x < 0 or y > DMX) {
print("{d} {d}\n", .{x, y});
print("crat: fail\n", .{});
} else {
// negative powers
var i_pow = self.v;
while (i_pow <= -1) : (i_pow += 1) {
y *= P;
}
// negative rational
const final_x = if (fl) -x else x;
print("{d}", .{final_x});
if (y > 1) {
print("/{d}", .{y});
}
print("\n", .{});
}
}
// print expansion
fn printf(self: *const Padic, sw: i32) void {
const t = @min(self.v, 0);
var i = K - 1 + t;
while (i >= t) : (i -= 1) {
print("{d}", .{self.d[@intCast(i + EMX)]});
if (i == 0 and self.v < 0) {
print(".", .{});
}
print(" ", .{});
}
print("\n", .{});
// rational approximation
if (sw != 0) {
self.crat();
}
}
};
pub fn main() !void {
const data = [_][6]i32{
// rational reconstruction depends on the precision
// until the dsum-loop overflows
[_]i32{ 2, 1, 2, 4, 1, 1 },
[_]i32{ 4, 1, 2, 4, 3, 1 },
[_]i32{ 4, 1, 2, 5, 3, 1 },
[_]i32{ 4, 9, 5, 4, 8, 9 },
[_]i32{ 26, 25, 5, 4, -109, 125 },
[_]i32{ 49, 2, 7, 6, -4851, 2 },
[_]i32{ -9, 5, 3, 8, 27, 7 },
[_]i32{ 5, 19, 2, 12, -101, 384 },
// two decadic pairs
[_]i32{ 2, 7, 10, 7, -1, 7 },
[_]i32{ 34, 21, 10, 9, -39034, 791 },
// familiar digits
[_]i32{ 11, 4, 2, 43, 679001, 207 },
[_]i32{ -8, 9, 23, 9, 302113, 92 },
[_]i32{ -22, 7, 3, 23, 46071, 379 },
[_]i32{ -22, 7, 32749, 3, 46071, 379 },
[_]i32{ 35, 61, 5, 20, 9400, 109 },
[_]i32{ -101, 109, 61, 7, 583376, 6649 },
[_]i32{ -25, 26, 7, 13, 5571, 137 },
[_]i32{ 1, 4, 7, 11, 9263, 2837 },
[_]i32{ 122, 407, 7, 11, -517, 1477 },
// more subtle
[_]i32{ 5, 8, 7, 11, 353, 30809 },
};
var sw: i32 = 0;
var a = Padic.new();
var b = Padic.new();
for (data) |d| {
const q = Ratio{ .a = d[0], .b = d[1] };
P = d[2];
K = d[3];
sw = a.r2pa(q, 1);
if (sw == 1) {
break;
}
a.printf(0);
const q_b = Ratio{ .a = d[4], .b = d[5] };
sw = sw | b.r2pa(q_b, 1);
if (sw == 1) {
break;
}
if (sw == 0) {
b.printf(0);
const c = a.add(&b);
print("+ =\n", .{});
c.printf(1);
}
print("\n", .{});
}
}