Skip to content

Commit 9ce999f

Browse files
Jacob Zhongclaude
andcommitted
Accept native Python numbers in all math APIs; rename ratio.rs -> rational.rs
All numeric function/method arguments now go through UniInput so a plain Python int/float works directly — dashu.sqrt(9.0), FBig(2).powi(300), UBig(12).gcd(8), UBig(n) += 5, RBig.from_parts(1, 3), etc. (into_fpy now builds floats at f64's native precision so transcendentals are well-defined). Rewrote the test suite to use native Python types throughout. Renamed python/src/ratio.rs to rational.rs. Also untracked the tests/__pycache__ artifacts and added a .gitignore. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 17aff47 commit 9ce999f

20 files changed

Lines changed: 312 additions & 355 deletions

python/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__pycache__/
2+
*.pyc

python/CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
`to_int`, `numerator`/`denominator`, `split_at_point`, `sqr`/`cubic`/`pow`.
1717
- Cross-type conversions: `FBig.to_decimal`/`to_binary`/`to_rational`,
1818
`RBig.to_float`/`to_decimal`.
19-
- `powi`, `from_parts`, and `ilog` now accept a plain Python `int` (or a dashu
20-
integer) via the `UniInput` dispatch, so e.g. `FBig(12).powi(300)` works.
19+
- All numeric function/method arguments now accept plain Python numbers via the
20+
`UniInput` dispatch — e.g. `dashu.sqrt(9.0)`, `FBig(2).powi(300)`, `UBig(12).gcd(8)`,
21+
`UBig(n) += 5`, `RBig.from_parts(1, 3)`. The module-level `math` functions, `powf`,
22+
`atan2`, `gcd`/`gcd_ext`/`lcm`, `is_multiple_of`/`remove`, in-place ops, `powi`,
23+
`from_parts`, `ilog`, and `simplest_from_float` all take native int/float.
2124
- Broadened constructors: `FBig`/`DBig`/`RBig`/`CBig` now accept any Python number
2225
(int/float/`Decimal`/`Fraction`) in addition to strings.
2326
- A module-level `math` API (`sin`/`cos`/…/`exp`/`ln`/`sqrt`/`gcd`/`lcm`/…) and a

python/dashu.pyi

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ class UBig:
5454
def __mod__(self, other: Number) -> "UBig|IBig|FBig|DBig|RBig": ...
5555
def __divmod__(self, other: int | UBig | IBig) -> tuple: ...
5656
def __pow__(self, exp: int | UBig, modulus: Optional[int | UBig] = ...) -> UBig: ...
57-
def __iadd__(self, other: UBig) -> None: ...
58-
def __isub__(self, other: UBig) -> None: ...
59-
def __imul__(self, other: UBig) -> None: ...
60-
def __iand__(self, other: UBig) -> None: ...
61-
def __ior__(self, other: UBig) -> None: ...
62-
def __ixor__(self, other: UBig) -> None: ...
57+
def __iadd__(self, other: int | UBig) -> None: ...
58+
def __isub__(self, other: int | UBig) -> None: ...
59+
def __imul__(self, other: int | UBig) -> None: ...
60+
def __iand__(self, other: int | UBig) -> None: ...
61+
def __ior__(self, other: int | UBig) -> None: ...
62+
def __ixor__(self, other: int | UBig) -> None: ...
6363
def __ilshift__(self, other: int) -> None: ...
6464
def __irshift__(self, other: int) -> None: ...
6565
def __pos__(self) -> UBig: ...
@@ -85,10 +85,10 @@ class UBig:
8585
def sqr(self) -> UBig: ...
8686
def cubic(self) -> UBig: ...
8787
def ilog(self, base: int | UBig) -> int: ...
88-
def is_multiple_of(self, divisor: UBig) -> bool: ...
89-
def remove(self, factor: UBig) -> int: ...
90-
def gcd(self, other: UBig) -> UBig: ...
91-
def gcd_ext(self, other: UBig) -> tuple[UBig, IBig, IBig]: ...
88+
def is_multiple_of(self, divisor: int | UBig) -> bool: ...
89+
def remove(self, factor: int | UBig) -> int: ...
90+
def gcd(self, other: int | UBig) -> UBig: ...
91+
def gcd_ext(self, other: int | UBig) -> tuple[UBig, IBig, IBig]: ...
9292
# bit operations
9393
def count_ones(self) -> int: ...
9494
def count_zeros(self) -> Optional[int]: ...
@@ -137,12 +137,12 @@ class IBig:
137137
def __mod__(self, other: Number) -> "UBig|IBig|FBig|DBig|RBig": ...
138138
def __divmod__(self, other: int | UBig | IBig) -> tuple: ...
139139
def __pow__(self, exp: int | UBig, modulus: Optional[int | UBig] = ...) -> IBig: ...
140-
def __iadd__(self, other: IBig) -> None: ...
141-
def __isub__(self, other: IBig) -> None: ...
142-
def __imul__(self, other: IBig) -> None: ...
143-
def __iand__(self, other: IBig) -> None: ...
144-
def __ior__(self, other: IBig) -> None: ...
145-
def __ixor__(self, other: IBig) -> None: ...
140+
def __iadd__(self, other: int | IBig) -> None: ...
141+
def __isub__(self, other: int | IBig) -> None: ...
142+
def __imul__(self, other: int | IBig) -> None: ...
143+
def __iand__(self, other: int | IBig) -> None: ...
144+
def __ior__(self, other: int | IBig) -> None: ...
145+
def __ixor__(self, other: int | IBig) -> None: ...
146146
def __ilshift__(self, other: int) -> None: ...
147147
def __irshift__(self, other: int) -> None: ...
148148
def __pos__(self) -> IBig: ...
@@ -255,7 +255,7 @@ class FBig:
255255
def asin(self) -> FBig: ...
256256
def acos(self) -> FBig: ...
257257
def atan(self) -> FBig: ...
258-
def atan2(self, x: FBig) -> FBig: ...
258+
def atan2(self, x: float | int | FBig) -> FBig: ...
259259
def sinh(self) -> FBig: ...
260260
def cosh(self) -> FBig: ...
261261
def tanh(self) -> FBig: ...
@@ -269,7 +269,7 @@ class FBig:
269269
def sqrt(self) -> FBig: ...
270270
def cbrt(self) -> FBig: ...
271271
def nth_root(self, n: int) -> FBig: ...
272-
def powf(self, w: FBig) -> FBig: ...
272+
def powf(self, w: float | int | FBig) -> FBig: ...
273273
def powi(self, n: int | IBig) -> FBig: ...
274274

275275

@@ -327,7 +327,7 @@ class DBig:
327327
def asin(self) -> DBig: ...
328328
def acos(self) -> DBig: ...
329329
def atan(self) -> DBig: ...
330-
def atan2(self, x: DBig) -> DBig: ...
330+
def atan2(self, x: float | int | DBig) -> DBig: ...
331331
def sinh(self) -> DBig: ...
332332
def cosh(self) -> DBig: ...
333333
def tanh(self) -> DBig: ...
@@ -341,7 +341,7 @@ class DBig:
341341
def sqrt(self) -> DBig: ...
342342
def cbrt(self) -> DBig: ...
343343
def nth_root(self, n: int) -> DBig: ...
344-
def powf(self, w: DBig) -> DBig: ...
344+
def powf(self, w: float | int | DBig) -> DBig: ...
345345
def powi(self, n: int | IBig) -> DBig: ...
346346

347347

@@ -397,7 +397,7 @@ class RBig:
397397
@staticmethod
398398
def from_parts(numerator: int | IBig, denominator: int | UBig) -> RBig: ...
399399
@staticmethod
400-
def simplest_from_float(f: FBig) -> Optional[RBig]: ...
400+
def simplest_from_float(f: float | int | FBig) -> Optional[RBig]: ...
401401

402402

403403
class CBig:
@@ -458,31 +458,31 @@ class Cache:
458458
def auto(obj: Number) -> "UBig|IBig|FBig|DBig|RBig": ...
459459
def autos(s: str) -> "UBig|IBig|FBig|DBig|RBig": ...
460460

461-
def sin(x: FBig) -> FBig: ...
462-
def cos(x: FBig) -> FBig: ...
463-
def tan(x: FBig) -> FBig: ...
464-
def asin(x: FBig) -> FBig: ...
465-
def acos(x: FBig) -> FBig: ...
466-
def atan(x: FBig) -> FBig: ...
467-
def atan2(y: FBig, x: FBig) -> FBig: ...
468-
def sinh(x: FBig) -> FBig: ...
469-
def cosh(x: FBig) -> FBig: ...
470-
def tanh(x: FBig) -> FBig: ...
471-
def asinh(x: FBig) -> FBig: ...
472-
def acosh(x: FBig) -> FBig: ...
473-
def atanh(x: FBig) -> FBig: ...
474-
def exp(x: FBig) -> FBig: ...
475-
def expm1(x: FBig) -> FBig: ...
476-
def log(x: FBig) -> FBig: ...
477-
def log1p(x: FBig) -> FBig: ...
478-
def ln(x: FBig) -> FBig: ...
479-
def ln_1p(x: FBig) -> FBig: ...
480-
def sqrt(x: FBig) -> FBig: ...
481-
def cbrt(x: FBig) -> FBig: ...
482-
def nth_root(x: FBig, n: int) -> FBig: ...
483-
def powf(x: FBig, y: FBig) -> FBig: ...
484-
def powi(x: FBig, n: int | IBig) -> FBig: ...
485-
def hypot(x: FBig, y: FBig) -> FBig: ...
486-
def gcd(a: UBig, b: UBig) -> UBig: ...
487-
def gcd_ext(a: UBig, b: UBig) -> tuple[UBig, IBig, IBig]: ...
488-
def lcm(a: UBig, b: UBig) -> UBig: ...
461+
def sin(x: Number) -> FBig: ...
462+
def cos(x: Number) -> FBig: ...
463+
def tan(x: Number) -> FBig: ...
464+
def asin(x: Number) -> FBig: ...
465+
def acos(x: Number) -> FBig: ...
466+
def atan(x: Number) -> FBig: ...
467+
def atan2(y: Number, x: Number) -> FBig: ...
468+
def sinh(x: Number) -> FBig: ...
469+
def cosh(x: Number) -> FBig: ...
470+
def tanh(x: Number) -> FBig: ...
471+
def asinh(x: Number) -> FBig: ...
472+
def acosh(x: Number) -> FBig: ...
473+
def atanh(x: Number) -> FBig: ...
474+
def exp(x: Number) -> FBig: ...
475+
def expm1(x: Number) -> FBig: ...
476+
def log(x: Number) -> FBig: ...
477+
def log1p(x: Number) -> FBig: ...
478+
def ln(x: Number) -> FBig: ...
479+
def ln_1p(x: Number) -> FBig: ...
480+
def sqrt(x: Number) -> FBig: ...
481+
def cbrt(x: Number) -> FBig: ...
482+
def nth_root(x: Number, n: int) -> FBig: ...
483+
def powf(x: Number, y: Number) -> FBig: ...
484+
def powi(x: Number, n: int | IBig) -> FBig: ...
485+
def hypot(x: Number, y: Number) -> FBig: ...
486+
def gcd(a: int | UBig, b: int | UBig) -> UBig: ...
487+
def gcd_ext(a: int | UBig, b: int | UBig) -> tuple[UBig, IBig, IBig]: ...
488+
def lcm(a: int | UBig, b: int | UBig) -> UBig: ...

python/src/convert.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,10 @@ impl<'a> UniInput<'a> {
249249
Self::BUint(x) => Ok(FPy(FBig::from(x.0.clone()))),
250250
Self::BInt(x) => Ok(FPy(FBig::from(x.0.clone()))),
251251
Self::OBInt(x) => Ok(FPy(FBig::from(x))),
252-
Self::Float(x) => FBig::try_from(x).map(FPy).map_err(conversion_error_to_py),
252+
Self::Float(x) => FBig::try_from(x)
253+
.map(|f| f.with_precision(f64::MANTISSA_DIGITS as usize).value())
254+
.map(FPy)
255+
.map_err(conversion_error_to_py),
253256
Self::BFloat(x) => Ok(FPy(x.0.clone())),
254257
Self::BRational(x) => FBig::try_from(x.0.clone())
255258
.map(FPy)

python/src/float.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,8 @@ impl FPy {
341341
let ctx = self.0.context();
342342
Ok(Self(unwrap_float(ctx.nth_root(n, self.0.repr()), ctx)?))
343343
}
344-
fn powf(&self, w: &Self) -> PyResult<Self> {
344+
fn powf(&self, w: UniInput<'_>) -> PyResult<Self> {
345+
let w = w.into_fpy()?;
345346
let ctx = self.0.context();
346347
let res = with_cache(|c| ctx.powf(self.0.repr(), w.0.repr(), Some(c)));
347348
Ok(Self(unwrap_float(res, ctx)?))
@@ -351,7 +352,8 @@ impl FPy {
351352
let ctx = self.0.context();
352353
Ok(Self(unwrap_float(ctx.powi(self.0.repr(), n), ctx)?))
353354
}
354-
fn atan2(&self, x: &Self) -> PyResult<Self> {
355+
fn atan2(&self, x: UniInput<'_>) -> PyResult<Self> {
356+
let x = x.into_fpy()?;
355357
let ctx = self.0.context();
356358
let res = with_cache(|c| ctx.atan2(self.0.repr(), x.0.repr(), Some(c)));
357359
Ok(Self(unwrap_float(res, ctx)?))
@@ -623,7 +625,8 @@ impl DPy {
623625
let ctx = self.0.context();
624626
Ok(Self(unwrap_float(ctx.nth_root(n, self.0.repr()), ctx)?))
625627
}
626-
fn powf(&self, w: &Self) -> PyResult<Self> {
628+
fn powf(&self, w: UniInput<'_>) -> PyResult<Self> {
629+
let w = w.into_dpy()?;
627630
let ctx = self.0.context();
628631
let res = with_cache(|c| ctx.powf(self.0.repr(), w.0.repr(), Some(c)));
629632
Ok(Self(unwrap_float(res, ctx)?))
@@ -633,7 +636,8 @@ impl DPy {
633636
let ctx = self.0.context();
634637
Ok(Self(unwrap_float(ctx.powi(self.0.repr(), n), ctx)?))
635638
}
636-
fn atan2(&self, x: &Self) -> PyResult<Self> {
639+
fn atan2(&self, x: UniInput<'_>) -> PyResult<Self> {
640+
let x = x.into_dpy()?;
637641
let ctx = self.0.context();
638642
let res = with_cache(|c| ctx.atan2(self.0.repr(), x.0.repr(), Some(c)));
639643
Ok(Self(unwrap_float(res, ctx)?))

python/src/int.rs

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -566,20 +566,20 @@ impl UPy {
566566
}
567567
Ok(self.0.ilog(&base))
568568
}
569-
fn is_multiple_of(&self, divisor: &Self) -> bool {
570-
self.0.is_multiple_of(&divisor.0)
569+
fn is_multiple_of(&self, divisor: UniInput<'_>) -> PyResult<bool> {
570+
Ok(self.0.is_multiple_of(&divisor.into_ubig()?))
571571
}
572-
fn remove(&mut self, factor: &Self) -> PyResult<usize> {
572+
fn remove(&mut self, factor: UniInput<'_>) -> PyResult<usize> {
573573
self.0
574-
.remove(&factor.0)
574+
.remove(&factor.into_ubig()?)
575575
.ok_or_else(|| PyValueError::new_err("the factor does not divide this number"))
576576
}
577-
fn gcd(&self, other: &Self) -> Self {
578-
UPy(Gcd::gcd(&self.0, &other.0))
577+
fn gcd(&self, other: UniInput<'_>) -> PyResult<Self> {
578+
Ok(UPy(Gcd::gcd(&self.0, &other.into_ubig()?)))
579579
}
580-
fn gcd_ext(&self, other: &Self) -> (Self, IPy, IPy) {
581-
let (g, s, t) = ExtendedGcd::gcd_ext(&self.0, &other.0);
582-
(UPy(g), IPy(s), IPy(t))
580+
fn gcd_ext(&self, other: UniInput<'_>) -> PyResult<(Self, IPy, IPy)> {
581+
let (g, s, t) = ExtendedGcd::gcd_ext(&self.0, &other.into_ubig()?);
582+
Ok((UPy(g), IPy(s), IPy(t)))
583583
}
584584

585585
/********** bit operations **********/
@@ -759,28 +759,34 @@ impl UPy {
759759
}
760760

761761
#[inline]
762-
fn __iadd__(&mut self, other: &Self) {
763-
self.0 += &other.0;
762+
fn __iadd__(&mut self, other: UniInput<'_>) -> PyResult<()> {
763+
self.0 += &other.into_ubig()?;
764+
Ok(())
764765
}
765766
#[inline]
766-
fn __isub__(&mut self, other: &Self) {
767-
self.0 -= &other.0;
767+
fn __isub__(&mut self, other: UniInput<'_>) -> PyResult<()> {
768+
self.0 -= &other.into_ubig()?;
769+
Ok(())
768770
}
769771
#[inline]
770-
fn __imul__(&mut self, other: &Self) {
771-
self.0 *= &other.0;
772+
fn __imul__(&mut self, other: UniInput<'_>) -> PyResult<()> {
773+
self.0 *= &other.into_ubig()?;
774+
Ok(())
772775
}
773776
#[inline]
774-
fn __iand__(&mut self, other: &Self) {
775-
self.0 &= &other.0;
777+
fn __iand__(&mut self, other: UniInput<'_>) -> PyResult<()> {
778+
self.0 &= &other.into_ubig()?;
779+
Ok(())
776780
}
777781
#[inline]
778-
fn __ior__(&mut self, other: &Self) {
779-
self.0 |= &other.0;
782+
fn __ior__(&mut self, other: UniInput<'_>) -> PyResult<()> {
783+
self.0 |= &other.into_ubig()?;
784+
Ok(())
780785
}
781786
#[inline]
782-
fn __ixor__(&mut self, other: &Self) {
783-
self.0 ^= &other.0;
787+
fn __ixor__(&mut self, other: UniInput<'_>) -> PyResult<()> {
788+
self.0 ^= &other.into_ubig()?;
789+
Ok(())
784790
}
785791
#[inline]
786792
fn __ilshift__(&mut self, other: usize) {
@@ -1151,28 +1157,34 @@ impl IPy {
11511157
}
11521158

11531159
#[inline]
1154-
fn __iadd__(&mut self, other: &Self) {
1155-
self.0 += &other.0;
1160+
fn __iadd__(&mut self, other: UniInput<'_>) -> PyResult<()> {
1161+
self.0 += &other.into_ibig()?;
1162+
Ok(())
11561163
}
11571164
#[inline]
1158-
fn __isub__(&mut self, other: &Self) {
1159-
self.0 -= &other.0;
1165+
fn __isub__(&mut self, other: UniInput<'_>) -> PyResult<()> {
1166+
self.0 -= &other.into_ibig()?;
1167+
Ok(())
11601168
}
11611169
#[inline]
1162-
fn __imul__(&mut self, other: &Self) {
1163-
self.0 *= &other.0;
1170+
fn __imul__(&mut self, other: UniInput<'_>) -> PyResult<()> {
1171+
self.0 *= &other.into_ibig()?;
1172+
Ok(())
11641173
}
11651174
#[inline]
1166-
fn __iand__(&mut self, other: &Self) {
1167-
self.0 &= &other.0;
1175+
fn __iand__(&mut self, other: UniInput<'_>) -> PyResult<()> {
1176+
self.0 &= &other.into_ibig()?;
1177+
Ok(())
11681178
}
11691179
#[inline]
1170-
fn __ior__(&mut self, other: &Self) {
1171-
self.0 |= &other.0;
1180+
fn __ior__(&mut self, other: UniInput<'_>) -> PyResult<()> {
1181+
self.0 |= &other.into_ibig()?;
1182+
Ok(())
11721183
}
11731184
#[inline]
1174-
fn __ixor__(&mut self, other: &Self) {
1175-
self.0 ^= &other.0;
1185+
fn __ixor__(&mut self, other: UniInput<'_>) -> PyResult<()> {
1186+
self.0 ^= &other.into_ibig()?;
1187+
Ok(())
11761188
}
11771189
#[inline]
11781190
fn __ilshift__(&mut self, other: usize) {

python/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ mod convert;
1010
mod float;
1111
mod int;
1212
mod math;
13-
mod ratio;
13+
mod rational;
1414
mod types;
1515
mod utils;
1616
mod words;

0 commit comments

Comments
 (0)