Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b63a42c
Add implementation plan for Python bindings
Jun 27, 2026
86934c5
Add PyO3 0.24 upgrade step to implementation plan
Jun 28, 2026
5841013
Clarify MSRV policy and update plan to PyO3 0.29
Jun 28, 2026
c6062c4
Bump dashu-python to edition 2024 and rust-version 1.85
Jun 28, 2026
9b5e9d5
Fix MSRV 1.68 CI: exclude dashu-python from workspace before cargo pa…
Jun 28, 2026
06ddb49
Fix CI: remove parking_lot/lock_api pinning (only in dashu-python tree)
Jun 28, 2026
05513d3
Merge origin/develop into python
Jun 30, 2026
74cf230
Add dashu-cmplx support and complex bindings plan
Jun 30, 2026
f13d06c
Fix merge: use develop's split CI layout (build.yml + tests.yml)
Jun 30, 2026
d7d0024
Merge remote-tracking branch 'origin/develop' into python
Jul 8, 2026
9ea9a9c
Switch Python float/complex bindings to cached types
Jul 8, 2026
e1c7091
Rearchitect: bare FBig/CBig + module-global ConstCache
Jul 8, 2026
d78d951
Plan: broaden __new__ + add cross-type conversions
Jul 8, 2026
aafd544
Implement full dashu-python bindings (TODO-python.md)
Jul 8, 2026
17aff47
Accept plain int in powi/from_parts/ilog via UniInput
Jul 8, 2026
9ce999f
Accept native Python numbers in all math APIs; rename ratio.rs -> rat…
Jul 8, 2026
c2e4f1b
Implement __format__ (Python format mini-language) for all types
Jul 8, 2026
a4b43fc
Print FBig/DBig in decimal from __str__ (not the float's base)
Jul 8, 2026
5917152
FBig prints in hex; float 'e' default uses native precision
Jul 8, 2026
631dd28
Fix RBig .Ne formatting accuracy (trailing-zero artifact)
Jul 8, 2026
aab35a3
Add Python Binding guide page (shared with the package README)
Jul 8, 2026
592e7f1
Remove TODO-python.md — the implementation plan is complete
Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Remove dashu-python from workspace (requires Rust >= 1.85)
if: matrix.rust == '1.68'
run: sed -i '/^ *"python",$/d' Cargo.toml
- name: Drop postgres/diesel/dev-deps for Rust 1.68
if: matrix.rust == '1.68'
run: python3 .github/workflows/drop_incompatible_deps_for_msrv.py
Expand All @@ -42,13 +45,9 @@ jobs:
run: |
if [ "${{ matrix.rust }}" = "1.68" ]; then
echo "Pinning packages for Rust 1.68:"
echo " parking_lot -> 0.12.3"
echo " lock_api -> 0.4.12"
echo " quote -> 1.0.40"
echo " unicode-ident -> 1.0.13"
echo " zeroize -> 1.8.1"
cargo update -p parking_lot --precise 0.12.3
cargo update -p lock_api --precise 0.4.12
cargo update -p quote --precise 1.0.40
cargo update -p unicode-ident --precise 1.0.13
cargo update -p zeroize --precise 1.8.1
Expand All @@ -59,7 +58,7 @@ jobs:
fi
- run: cargo check --all-features --tests
if: matrix.rust != '1.68'
- run: cargo check --workspace --exclude dashu-python --features "std,num-order,serde,zeroize,rand,num-traits_v02"
- run: cargo check --workspace --features "std,num-order,serde,zeroize,rand,num-traits_v02"
if: matrix.rust == '1.68'

rustdoc:
Expand Down
7 changes: 5 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

dashu is a library set of arbitrary precision numbers implemented in pure Rust, aiming to be a Rust-native alternative to GNU GMP + MPFR.

**MSRV is a hard constraint** — do not bump it unless absolutely necessary. The current MSRV is maintained in `README.md`; when modifying code, ensure it remains compatible.
**MSRV is a hard constraint for core crates only.** Core crates are the `dashu` meta-crate and its direct dependencies: `dashu-base`, `dashu-int`, `dashu-float`, `dashu-ratio`, `dashu-macros`, `dashu-cmplx`. The current MSRV is maintained in each crate's `Cargo.toml` and the top-level `README.md`. When modifying code in core crates, ensure it remains MSRV-compatible.

Secondary crates (`dashu-python`, `benchmark/`, fuzz tests) are **not** bounded by the workspace MSRV policy. They may use newer Rust versions and dependency versions as needed.

## Workspace structure

Expand All @@ -13,6 +15,7 @@ dashu is a library set of arbitrary precision numbers implemented in pure Rust,
| `dashu-float` | `float/` | Arbitrary precision floats (`FBig`, `DBig`, `CachedFBig`) |
| `dashu-ratio` | `rational/` | Arbitrary precision rationals (`RBig`, `Relaxed`) |
| `dashu-macros` | `macros/` | Procedural macros for literal big numbers |
| `dashu-cmplx` | `complex/` | Arbitrary precision complex numbers (`CBig`) |
| `dashu-python` | `python/` | PyO3 Python bindings (not in default members) |
| *(benchmark)* | `benchmark/` | Profiling scratchpad, not a comprehensive benchmark suite |

Expand Down Expand Up @@ -94,5 +97,5 @@ When implementing algorithms that manipulate word arrays (`&[Word]`), prefer the

- **dashu-python is excluded** from workspace tests and clippy — always add `--exclude dashu-python`
- **diesel has two major versions** in the dependency tree — use `diesel@2` (not `diesel` or `diesel@2.x.y`) when pinning in CI
- **MSRV compatibility** — if you add a new dependency, check whether it supports the current MSRV; if not, it may need to be stripped for MSRV builds
- **MSRV compatibility** — for core crates only: if you add a new dependency to a core crate, check whether it supports the current MSRV; if not, it may need to be stripped for MSRV builds. Secondary crates (dashu-python, benchmarks, fuzz tests) are exempt from this check.
- **Sub-crate versions can differ** in minor/patch (e.g. `dashu-int` 0.4.2, `dashu-float` 0.4.4) — keep them in sync when making cross-crate changes
1 change: 1 addition & 0 deletions guide/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- [Trigonometric and Hyperbolic Functions](./ops/trig_n_hyper.md)
- [Bit Manipulation](./ops/bit.md)
- [Number Theoretic](./ops/num_theory.md)
- [Python Binding](./python.md)

---

Expand Down
1 change: 1 addition & 0 deletions guide/src/python.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{#include ../../python/README.md}}
2 changes: 2 additions & 0 deletions python/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
56 changes: 54 additions & 2 deletions python/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,56 @@
# Unreleased

- TODO: support pickle through __reduce__
- TODO: support as much dunder methods as possible: https://docs.cython.org/en/latest/src/userguide/special_methods.html#special-methods
### Add
- Upgraded PyO3 from 0.20 to 0.29 (modern `Bound` API, `IntoPyObject`, edition 2024,
rust-version 1.85). `requires-python` is now `>=3.8`.
- Added `dashu-cmplx` support: a new `CBig` Python type wrapping a bare complex number,
with arithmetic, `real`/`imag`, `conj`/`proj`/`norm`/`abs`/`arg`, transcendentals
(`sin`/`cos`/`tan`/`exp`/`ln`/`sqrt`/`powi`/`powf`), and `__complex__`.
- Arithmetic, comparison, and `__bool__` for `FBig`, `DBig`, and `RBig` (previously
only construction/repr/hash were exposed).
- Integer number theory and bit operations on `UBig`/`IBig`: `sqrt`/`cbrt`/`nth_root`,
`sqr`/`cubic`, `ilog`, `gcd`/`gcd_ext`, `count_ones`/`trailing_zeros`/…,
`__floordiv__`/`__divmod__`, and in-place operators.
- Float/rational methods: predicates (`is_zero`/`is_finite`/`is_infinite`), rounding
(`trunc`/`floor`/`ceil`/`round`/`fract`), `with_precision`/`precision`/`digits`,
`to_int`, `numerator`/`denominator`, `split_at_point`, `sqr`/`cubic`/`pow`.
- Cross-type conversions: `FBig.to_decimal`/`to_binary`/`to_rational`,
`RBig.to_float`/`to_decimal`.
- All numeric function/method arguments now accept plain Python numbers via the
`UniInput` dispatch — e.g. `dashu.sqrt(9.0)`, `FBig(2).powi(300)`, `UBig(12).gcd(8)`,
`UBig(n) += 5`, `RBig.from_parts(1, 3)`. The module-level `math` functions, `powf`,
`atan2`, `gcd`/`gcd_ext`/`lcm`, `is_multiple_of`/`remove`, in-place ops, `powi`,
`from_parts`, `ilog`, and `simplest_from_float` all take native int/float.
- `__format__` now honors the Python format mini-language for all types: scientific
(`e`/`E`), fixed (`f`), general (`g`), integer (`b`/`o`/`d`/`x`/`X`/`c`), with
sign/width/align/fill/zero-pad/grouping and precision. Float formatting preserves
the value's arbitrary precision (e.g. `f"{FBig(2).with_precision(200).exp():.20e}"`).
- Broadened constructors: `FBig`/`DBig`/`RBig`/`CBig` now accept any Python number
(int/float/`Decimal`/`Fraction`) in addition to strings.
- A module-level `math` API (`sin`/`cos`/…/`exp`/`ln`/`sqrt`/`gcd`/`lcm`/…) and a
`dashu.Cache` handle for the global constant cache.
- `FBig` (base 2) now prints in **hexadecimal** by default (`str`/`format`/`'a'`/`'A'`),
e.g. `str(FBig(1.5)) == '0x3p-1'` — lossless, no base-2→base-10 rounding. Decimal
presentations (`'e'`/`'f'`/`'g'`) still convert to base 10. (`DBig` stays decimal.)
- Float format default precision is now the value's native precision (its significant
digits) rather than CPython's fixed 6 — `format(f, 'e')` shows all digits; use `'.6e'`
for the old 6-digit behavior.
- Transcendentals are now panic-free: domain errors raise `ValueError`,
`0/0`-style indeterminate forms raise `ZeroDivisionError`, and overflow/underflow
produce signed infinities/zeros — routed through the panic-free `Context` layer with a
shared thread-local `ConstCache`.

### Fix
- Removed the `todo!()` panics in `UBig.__mod__`, `IBig.__mod__`, and `IBig.__pow__`.

### Changed
- `FBig(f64)` now constructs at f64's native precision (53 bits) so that subsequent
transcendental operations (which require precision > 0) are well-defined.

# TODO (still open)
- support pickle through `__reduce__`
- support as much dunder methods as possible:
https://docs.cython.org/en/latest/src/userguide/special_methods.html#special-methods
- route bare `FBig`/`CBig` arithmetic through the `Context` layer (or guard it) so that
infinite-input / `0/0` cases raise Python exceptions instead of panicking
(transcendentals are already panic-free).
13 changes: 6 additions & 7 deletions python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,30 @@
name = "dashu-python"
version = "0.0.1"
authors = ["Jacob Zhong <cmpute@gmail.com>"]
edition = "2021"
edition = "2024"
description = "Python binding for the dashu numeric types"
keywords = ["mathematics", "numerics"]
categories = ["mathematics", "no-std"]
categories = ["mathematics", "science"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/cmpute/dashu"
homepage = "https://github.com/cmpute/dashu"
documentation = "https://docs.rs/dashu-python"
readme = "README.md"
rust-version = "1.68"
rust-version = "1.85"

[package.metadata.docs.rs]
all-features = true

[dependencies]
dashu-base = { version = "0.4.0", path = "../base", features = ["std"]}
dashu-base = { version = "0.4.0", path = "../base", features = ["std"] }
dashu-int = { version = "0.4.1", path = "../integer", features = ["std", "num-order"] }
dashu-float = { version = "0.4.2", path = "../float", features = ["std", "num-order"] }
dashu-ratio = { version = "0.4.1", path = "../rational", features = ["std", "num-order", "dashu-float"] }
dashu-cmplx = { version = "0.4.5", path = "../complex", features = ["std", "num-order"] }
num-order = "1.2.0"

_num-modular = { optional = true, version = "0.6.1", package = "num-modular", default-features = false }

[dependencies.pyo3]
version = "0.20"
version = "0.29"
features = ["extension-module"]

[lib]
Expand Down
87 changes: 85 additions & 2 deletions python/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,86 @@
# dashu-python
# Python Binding

Python binding to the numeric types in the dashu crates, based on PyO3.
`dashu` brings the dashu arbitrary-precision number types to Python through a native
extension built with [PyO3](https://pyo3.rs). Install the package and import it:

```sh
pip install dashu-rs
```

```python
from dashu import UBig, IBig, RBig, FBig, DBig, CBig
```

## Types

| Type | Backing | Base / rounding |
|------|---------|-----------------|
| `UBig`, `IBig` | arbitrary-precision integer | unsigned / signed |
| `RBig` | arbitrary-precision rational | exact `numerator / denominator` |
| `FBig` | arbitrary-precision float | base 2, round `Zero` |
| `DBig` | arbitrary-precision float | base 10, round `HalfAway` (decimal) |
| `CBig` | arbitrary-precision complex | base 2 |

All types are `Send` + `Sync` (free-threaded-Python compatible). Constructors accept
native Python numbers as well as strings:

```python
UBig(144) # int
FBig(1.5) # float (kept at f64's native precision)
DBig("1.23") # decimal string
RBig(Fraction(1, 3)) # fractions.Fraction
CBig(3.0, 4.0) # (re, im)
```

## Operations

Arithmetic, comparisons, and `bool()` accept any Python number (cross-type dispatch),
so mixed operands just work:

```python
UBig(2) + 3 == 5
FBig(1.5) * 2 == FBig(3.0)
UBig(17) // 5 == 3
divmod(UBig(17), 5) == (UBig(3), UBig(2))
```

- **Integers**: `+ - * / // % **`, comparisons, in-place ops, bit operations
(`& | ^ << >>`), roots (`sqrt`/`cbrt`/`nth_root`), `gcd`/`gcd_ext`, `ilog`, bit
predicates, and `to_words`/`to_chunks`/`to_bytes`.
- **Floats / Decimal**: arithmetic, comparisons, rounding (`trunc`/`floor`/`ceil`/
`round`/`fract`), precision (`precision`/`with_precision`), transcendentals, and
conversions `to_decimal`/`to_binary`/`to_rational`/`to_int`.
- **Rational**: arithmetic, `numerator`/`denominator`, rounding, `sqr`/`pow`, `to_float`.
- **Complex**: arithmetic, `real`/`imag`, `conj`/`proj`/`norm`/`abs`/`arg`, and
transcendentals (`sin`/`cos`/`exp`/`ln`/`sqrt`/...).

Transcendentals are **panic-free**: domain errors raise `ValueError`, indeterminate forms
like `0/0` raise `ZeroDivisionError`, and overflow/underflow yield signed infinities or
zeros. They share a module-wide constant cache (`dashu.Cache`) so repeated calls at
increasing precision reuse the precomputed constants.

A module-level `math` API mirrors the common functions — `dashu.sin`, `dashu.sqrt`,
`dashu.exp`, `dashu.gcd`, `dashu.lcm`, … — and likewise accepts plain Python numbers.

## Formatting

`format()` / f-strings honor the full Python format mini-language:

```python
format(UBig(255), "#x") # '0xff'
format(UBig(10**9), ",") # '1,000,000,000'
format(FBig(2).with_precision(200).exp(), ".20e") # '7.38905609893065022723e+00'
format(DBig("1.5"), ".3e") # '1.500e+00'
format(RBig.from_parts(1, 3), ".4f") # '0.3333'
format(CBig(3.0, 4.0), ".2f") # '(3.00+4.00j)'
```

- **Integers** delegate to Python `int`, so every presentation type works (`b`/`o`/`d`/
`x`/`X`/`c`/`n`) with sign, width, fill, zero-pad, and grouping — with no loss.
- **Floats** (`FBig`/`DBig`) support `e`/`E`/`f`/`g` with precision, sign, width, align,
fill, zero-pad, and grouping. With no explicit precision, all significant digits are
shown (use `.6e` for the fixed 6-digit default).
- **`FBig` is base 2**, so its default `str`/`format` and the `'a'`/`'A'` types print in
**hexadecimal** — lossless, with no base conversion, e.g. `str(FBig(1.5)) == '0x3p-1'`.
Decimal presentations (`'e'`/`'f'`/`'g'`) convert to base 10. `DBig` prints in decimal.
- **`RBig`** defaults to the exact fraction, e.g. `str(RBig(1) / 3) == '1/3'`.
Loading
Loading