1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
// robust/exact/backend.rs — The single seam between the robust engine and its
// arbitrary-precision arithmetic library.
//
// Every module in src/robust that needs unbounded integers or rationals goes
// through the aliases and re-exports here; nothing else in the crate names
// `dashu_int` or `dashu_ratio` directly. Swapping the backend is therefore a
// change to this file alone, plus a re-verification of the backend-coupled
// hot spots inventoried below.
//
// The types are aliases, not newtypes, on purpose: the call sites use the
// backend's operator impls directly, and wrapping them would either hide those
// or require re-implementing several hundred trait impls with no behavioral
// gain. What the call sites must NOT use directly is any *constructor*,
// *accessor* or *trait method* whose name or shape is backend-specific — those
// go through the small helper layer at the bottom of this file (`rat_new`,
// `numer`/`denom`, `rat_is_zero`, `mul_int_uint`, `hash_rational`, …), which is
// the only code that has to change when the backend does.
//
// ─── Phase-2 checklist: backend-coupled hot spots ───────────────────────────
//
// These are the places whose *correctness* (not just compilation) depends on
// how the backend represents and normalizes values. A backend swap must
// re-verify each one.
//
// 1. `hash_rational` / `rat_eq` (below), backing `rational.rs`'s
// `R2Key` / `R3Key`. Structural hashing that reaches into the backend's
// representation: the sign, then the little-endian limbs of the
// numerator magnitude, then the limbs of the denominator. A replacement
// backend must expose an equivalent canonical limb/word view (or the
// hash must be rewritten, e.g. over a byte serialization). Note the limb
// count is not hashed; the `0xfeed` separator between numerator and
// denominator is what keeps the stream unambiguous. Preserve that if the
// limb width changes.
//
// 2. Canonicality invariant behind (1). `R2Key`/`R3Key` equality is
// `rat_eq`, i.e. *field* equality (numer and denom compared
// independently), which equals value equality only if every stored
// rational is fully reduced with a positive denominator. Today that
// holds because all stored rationals come from `rat_new` (gcd-reducing),
// `rat_from_f64`, or the arithmetic operators — all of which
// canonicalize. A replacement backend must auto-reduce in its equivalent
// of `rat_new` and must normalize the sign onto the numerator, or (1)
// breaks silently.
//
// 3. `predicates.rs` construction sites that rely on "exactly one gcd":
// `line_plane_intersect`, `line_line_intersect_2d`, `lift_to_plane` and
// `segment_param` each build every output coordinate with a single
// `rat_new`, so the reduction cost is paid exactly once per coordinate.
// The gcd is a measured hot spot; re-measure it on a swap.
//
// 4. `intpred.rs` `Int` fallback tier: `scaled_big` (exact dyadic scaling
// via `Int::from(i64) << usize`) and `sign_big`. This is the unbounded
// tier below i64/i128 and is where big-integer performance shows up on
// degenerate meshes.
//
// 5. `rational.rs::rat_to_f64` — correctly rounded (nearest, ties to even)
// rational -> f64. It works on the *unsigned magnitude* type through
// `numer_mag`/`denom`, `uint_bits`, `uint_bit`, shifts by usize, `Ord`,
// `/`, `-`, `*`, `+= u32` and `Uint::to_u64()`.
// It must NOT be replaced by any backend-provided `to_f64()` without a
// differential test — some backends' rational->float conversions are not
// correctly rounded (num-rational's `ToPrimitive for Ratio` is not).
// dashu's `RBig::to_f64` *is* correctly rounded and agrees with ours on
// every f64 boundary category and on thousands of random multi-word
// rationals (`tests::rat_to_f64_matches_the_backend_oracle`), but we keep
// our own routine so a backend upgrade can never silently move an output
// vertex; the backend's version stays wired up as the test oracle.
// (`rat_to_f64` is the only rational->f64 rounding path in the engine.)
//
// 6. `predicates.rs::Homog2(pub Int, pub Int, pub Int)` — a homogeneous 2D
// point holding backend integers directly, and the `[Int; 3]` normals
// from `tri_normal_int` / `dot_point_raw` / `dot_diff_raw`, plus
// `tri_tri.rs`'s `type Frac = (Int, Int)`. These are public-ish
// API surfaces whose signatures change with the backend type. (They stay
// inside the crate: the FFI and wasm layers only ever see opaque
// handles and f64s.)
//
// 7. `rat_from_f64` (`rational.rs::rat`) must be exact for every finite f64
// and must return `None` for NaN and infinity — the engine relies on the
// panic path never triggering for finite input.
use Cow;
use ;
/// Unbounded signed integer. Magnitudes up to two [`dashu_int::Word`]s live
/// inline in the value, so the small integers that dominate the robust
/// engine's exact tier cost no allocation at all.
pub type Int = IBig;
/// Unsigned magnitude companion of [`Int`] (used by `rat_to_f64` and by the
/// rational denominators, which are always positive).
pub type Uint = UBig;
/// Exact rational, always stored fully reduced with the sign on the numerator.
/// `RBig` enforces that canonical form in its type (the non-reducing variant
/// is a separate type, `Relaxed`, which this crate never uses), which is what
/// makes the field-wise `rat_eq` / `hash_rational` below sound — see items (1)
/// and (2).
pub type Rational = RBig;
/// The `num_traits` items the exact-arithmetic call sites need on [`Int`] and
/// [`Uint`], re-exported so backend-dependent trait paths live in one place
/// too. (Nothing outside this file relies on these traits being implemented
/// for [`Rational`] — that is what the `rat_*` helpers below are for, since a
/// rational backend need not implement `num_traits` at all.)
pub use ;
// ─── Rational construction ───────────────────────────────────────────────────
/// `numer / denom`, fully reduced with the sign normalized onto the numerator.
/// This is the single gcd whose cost item (3) above accounts for.
///
/// Panics if `denom` is zero, like every backend's equivalent.
/// The integer `v` as a rational (denominator 1).
/// Exact conversion of an f64; `None` for NaN and infinity. Every finite f64
/// is a dyadic rational, so no information is lost — see item (7).
// ─── Rational inspection ─────────────────────────────────────────────────────
/// Signed numerator of a canonical rational.
/// Denominator of a canonical rational, always strictly positive — hence the
/// unsigned type, which is what the homogenizing predicates and `rat_to_f64`
/// both want.
/// Magnitude of the numerator. Returned as a `Cow` because some backends store
/// the sign inside the integer and cannot hand out a borrowed unsigned view.
/// Field-wise equality of two *canonical* rationals — value equality without
/// the backend's general (unreduced-tolerant) comparison, which is most
/// expensive exactly when the values ARE equal. See item (2).
/// Structural hash of a canonical rational — see item (1).
// ─── Int / Uint bridging ─────────────────────────────────────────────────────
//
// Denominators are unsigned but the homogenized predicate coordinates are
// signed, so the two types meet in the homogenization helpers. Backends differ
// in which mixed-type operator impls they provide; these three helpers are the
// only places that need to know.
/// Magnitude of a signed integer, for the unsigned core of `rat_to_f64` and
/// its `int_ratio_to_f64` sibling.
/// Number of bits in the magnitude (0 for zero).
/// Value of bit `n` (little-endian) of the magnitude.
/// The backend's own correctly rounded rational -> f64, used *only* as a test
/// oracle for `rational.rs::rat_to_f64` (see item (5) and
/// `tests::rat_to_f64_matches_the_backend_oracle`). The engine keeps its own
/// routine: it is the one that has been validated against the C++ reference,
/// and it must not silently change with a backend upgrade.