Big numbers
Integers and fractions of any size, exact: factorials, powers, gcd, modular powers, and fractions such as 1/3 + 1/6 = 1/2 with no rounding.
Version 1.0.0 · 5 functions · 4 crates
Functions
What use bignum.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use bignum_calc, bignum_factorial from bignum (Quillo 0.6).
bignum_calc(a: str, op: str, b: str) → stror an errorWhole-number arithmetic on numbers given as text: op is "+", "-", "*", "/" (rounded toward minus infinity), "%" (never negative for a positive divisor), "^" (power), "gcd", "lcm", "cmp" (-1, 0 or 1). Results are text, exact, up to about 2.4 million digits; _, , and spaces in the input are ignored.
bignum_factorial(n: int) → anyor an errorn! exactly (n from 0 to 20,000), with its number of digits: { value, digits }.
bignum_pow_mod(base: str, exponent: str, modulus: str) → stror an errorbase to the power exponent, modulo modulus, fast even for exponents with hundreds of digits: the step of RSA, Diffie-Hellman and primality tests.
fraction_calc(a: str, op: str, b: str) → stror an errorExact fractions: a and b as "3/4", "-2", or "0.15" (read exactly as 3/20); op is "+", "-", "*", "/". The answer is in lowest terms, as "a/b" (or a whole number).
fraction_decimal(fraction: str, digits: int) → anyor an errorA fraction ("1/7") as a decimal with digits places (0 to 10,000), rounded half away from zero, and the fraction in lowest terms: { decimal, fraction }.
Get it
$ quillo install bignum # bignum.qwer into this folder$ quillo install bignum --example # and its example, bignum.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/bignum.qwer$ curl -O https://quillolang.org/plugins/files/bignum.qweThen, in your program, all of it or only what you call:
use bignum.qweruse bignum_calc, bignum_factorial from bignum // Quillo 0.6: only theseIt uses these crates, which cargo fetches the first time the plugin is built:
num-bigint = "0.4"num-rational = "0.4"num-integer = "0.1"num-traits = "0.2"Example
// Exact numbers for a maths quiz site and a money ledger: huge integers, and fractions that never round.//// quillo run bignum.qwe then open http://localhost:8080/biguse "bignum.qwer"route GET /big = { two_to_200: bignum_calc("2", "^", "200")?, times: bignum_calc("123456789012345678901234567890", "*", "987654321098765432109876543210")?, gcd: bignum_calc("1071", "gcd", "462")?, hundred_factorial_digits: bignum_factorial(100)?.digits, pow_mod: bignum_pow_mod("4", "13", "497")?,}// Splitting a bill three ways and adding the shares back up: exactly the bill again.route GET /split ? { total: str = "100.00" } { let share = fraction_calc(total, "/", "3")? { share, share_cents: fraction_decimal(share, 2)?.decimal, back: fraction_calc(fraction_calc(share, "+", share)?, "+", share)? }}route GET /fractions = { sum: fraction_calc("1/3", "+", "1/6")?, tenth: fraction_calc("0.1", "+", "0.2")?, seventh: fraction_decimal("1/7", 20)?.decimal }$ quillo run bignum.qwe$ curl 'localhost:8080/big'$ curl 'localhost:8080/split'$ curl 'localhost:8080/fractions'Source
//! Integers and fractions of any size, exact: factorials, powers, gcd and modular powers on numbers with thousands of digits, and fractions like 1/3 + 1/6 = 1/2 with no rounding. Numbers cross as text.//! version: 1.0.0//!//! [dependencies]//! num-bigint = "0.4"//! num-rational = "0.4"//! num-integer = "0.1"//! num-traits = "0.2"use num_bigint::BigInt;use num_integer::Integer;use num_rational::BigRational;use num_traits::{One, Signed, ToPrimitive, Zero};use serde_json::{json, Value};const MAX_DIGITS: usize = 200_000;const MAX_BITS: u64 = 8_000_000;fn int(s: &str) -> Result<BigInt, String> { let t: String = s.trim().chars().filter(|c| *c != '_' && *c != ',' && *c != ' ').collect(); if t.len() > MAX_DIGITS { return Err("a number is at most 200,000 digits here".into()); } t.parse::<BigInt>().map_err(|_| format!("`{}` is not a whole number", s))}fn frac(s: &str) -> Result<BigRational, String> { let t = s.trim(); if let Some((n, d)) = t.split_once('/') { let d = int(d)?; if d.is_zero() { return Err(format!("`{}` divides by zero", s)); } return Ok(BigRational::new(int(n)?, d)); } if let Some((w, f)) = t.split_once('.') { // A decimal is exact too: 0.1 is 1/10. let neg = w.trim().starts_with('-'); let digits = f.trim(); if !digits.chars().all(|c| c.is_ascii_digit()) { return Err(format!("`{}` is not a number", s)); } let scale = BigInt::from(10).pow(digits.len() as u32); let whole = int(if w.trim().is_empty() || w.trim() == "-" { "0" } else { w })?; let part = if digits.is_empty() { BigInt::zero() } else { int(digits)? }; let n = whole.abs() * &scale + part; return Ok(BigRational::new(if neg { -n } else { n }, scale)); } Ok(BigRational::from_integer(int(t)?))}fn show(r: &BigRational) -> String { if r.is_integer() { r.numer().to_string() } else { format!("{}/{}", r.numer(), r.denom()) }}/// Whole-number arithmetic on numbers given as text: `op` is "+", "-", "*", "/" (rounded toward minus infinity), "%" (never negative for a positive divisor), "^" (power), "gcd", "lcm", "cmp" (-1, 0 or 1)./// Results are text, exact, up to about 2.4 million digits; `_`, `,` and spaces in the input are ignored.pub fn bignum_calc(a: &str, op: &str, b: &str) -> Result<String, String> { let (x, y) = (int(a)?, int(b)?); Ok(match op.trim() { "+" => (x + y).to_string(), "-" => (x - y).to_string(), "*" => (x * y).to_string(), "/" | "%" if y.is_zero() => return Err("division by zero".into()), "/" => x.div_floor(&y).to_string(), "%" => x.mod_floor(&y).to_string(), "^" => { let e = y.to_u32().ok_or("the power is a whole number from 0 to 4 billion")?; if x.bits().saturating_mul(e as u64) > MAX_BITS { return Err("the result would have more than 2.4 million digits".into()); } x.pow(e).to_string() } "gcd" => x.gcd(&y).to_string(), "lcm" => x.lcm(&y).to_string(), "cmp" => match x.cmp(&y) { std::cmp::Ordering::Less => "-1".into(), std::cmp::Ordering::Equal => "0".into(), std::cmp::Ordering::Greater => "1".into(), }, other => return Err(format!("`{}` is not an operation: + - * / % ^ gcd lcm cmp", other)), })}/// n! exactly (n from 0 to 20,000), with its number of digits: `{ value, digits }`.pub fn bignum_factorial(n: i64) -> Result<Value, String> { if !(0..=20_000).contains(&n) { return Err("n is 0 to 20,000".into()); } let mut f = BigInt::one(); for i in 2..=n { f *= i; } let s = f.to_string(); Ok(json!({ "digits": s.len(), "value": s }))}/// `base` to the power `exponent`, modulo `modulus`, fast even for exponents with hundreds of digits: the step of RSA, Diffie-Hellman and primality tests.pub fn bignum_pow_mod(base: &str, exponent: &str, modulus: &str) -> Result<String, String> { let (b, e, m) = (int(base)?, int(exponent)?, int(modulus)?); if m.is_zero() || m.is_negative() { return Err("the modulus is a whole number above 0".into()); } if e.is_negative() { return Err("the exponent is 0 or more".into()); } Ok(b.modpow(&e, &m).to_string())}/// Exact fractions: `a` and `b` as "3/4", "-2", or "0.15" (read exactly as 3/20); `op` is "+", "-", "*", "/". The answer is in lowest terms, as "a/b" (or a whole number).pub fn fraction_calc(a: &str, op: &str, b: &str) -> Result<String, String> { let (x, y) = (frac(a)?, frac(b)?); let r = match op.trim() { "+" => x + y, "-" => x - y, "*" => x * y, "/" if y.is_zero() => return Err("division by zero".into()), "/" => x / y, other => return Err(format!("`{}` is not an operation: + - * /", other)), }; Ok(show(&r))}/// A fraction ("1/7") as a decimal with `digits` places (0 to 10,000), rounded half away from zero, and the fraction in lowest terms: `{ decimal, fraction }`.pub fn fraction_decimal(fraction: &str, digits: i64) -> Result<Value, String> { let r = frac(fraction)?; let d = digits.clamp(0, 10_000) as u32; let scale = BigInt::from(10).pow(d); let scaled = r.clone() * BigRational::from_integer(scale.clone()); let rounded = scaled.round().to_integer(); let neg = rounded.is_negative(); let s = rounded.abs().to_string(); let s = if s.len() <= d as usize { format!("{}{}", "0".repeat(d as usize + 1 - s.len()), s) } else { s }; let (w, f) = s.split_at(s.len() - d as usize); let dec = format!("{}{}{}", if neg { "-" } else { "" }, w, if d > 0 { format!(".{}", f) } else { String::new() }); Ok(json!({ "decimal": dec, "fraction": show(&r) }))}Related
fractions.qwer · 1.0.0Exact fractions for recipes, carpentry and school maths: "1 1/2", "3/8" and "12.5%" read and calculated without rounding, expressions worked out, decimals turned into simple fractions, measures rounded to 1/16 inch, and recipes scaled.7 views · 9 downloadsFinance finance.qwer · 1.0.0Loan payments and amortization tables, compound growth with deposits, net present value and internal rate of return, in cents.7 views · 6 downloadsClustering clustering.qwer · 1.0.0Data put into groups: k-means with k chosen by the elbow and silhouette, DBSCAN for groups of any shape and their outliers (in metres for latitude and longitude), and hierarchical clustering, on rows of numbers or maps.7 views · 7 downloadsFrequency spectrum fft.qwer · 1.0.0The frequency spectrum of samples from a microphone or sensor: frequencies and their strength, the dominant ones found between bins, and the signal's level.8 views · 7 downloadsGraphs graph.qwer · 1.0.0Graphs given as edges: shortest paths with distances, task order by topological sort with cycles named, connected groups, and what a node reaches.7 views · 7 downloadsMatrices matrix.qwer · 1.0.0Matrices as lists of rows: linear systems solved, inverses, determinants, products and transposes, and least-squares fits such as a trend line.7 views · 8 downloadsThread 0 posts
On Big numbers: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.