Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 42 additions & 4 deletions integer/src/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,9 +428,10 @@ impl Repr {
buffer.push(ones_word(hi_bits as _));
}

// SAFETY: the bit length has been checked and capacity >= length,
// so capacity is nonzero and larger than 2
unsafe { mem::transmute::<Buffer, Repr>(buffer) }
// Route through `from_buffer` to canonicalise: a 128-bit ones value

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that we can just change the conditions for the branchs above to n <= WORD_BITS_USIZE and n <= DWORD_BITS_USIZE.

// has length 2 here and must become the inline form, otherwise the
// (now scale-sensitive) PartialEq wouldn't see it as equal.
Self::from_buffer(buffer)
}
}

Expand Down Expand Up @@ -554,7 +555,44 @@ impl Drop for Repr {
impl PartialEq for Repr {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_sign_slice() == other.as_sign_slice()
// The encoding is canonical, so a sign or scale (inline vs heap)
// mismatch is immediately unequal; otherwise compare the words directly
// instead of materialising slices via `as_sign_slice`.
let cap_a = self.capacity.get();

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I suggest using let (cap_a, sign_) = self.sign_capacity();, then there is no need for let abs_a = cap_a.unsigned_abs();` below.

let cap_b = other.capacity.get();
// Sign mismatch: zero is canonically positive (no negative zero), so
// a sign disagreement always means unequal values.
if (cap_a > 0) != (cap_b > 0) {
return false;
}
let abs_a = cap_a.unsigned_abs();
let abs_b = cap_b.unsigned_abs();
let inline_a = abs_a <= 2;
let inline_b = abs_b <= 2;
// Mixed scales can never be equal: heap representation requires
// length ≥ 3, which is a strictly larger magnitude than anything
// inline.
if inline_a != inline_b {
return false;
}
// SAFETY: capacity tells us which union variant is live.
unsafe {
if inline_a {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be even more optimized:

match cap_a {
  0..1 => {}, // compare as single words
  2 => {}, // compare as double words
  _ => {}, // compare as slices
}

// Compare as one DoubleWord (tighter codegen than [Word; 2]).
let dw_a = double_word(self.data.inline[0], self.data.inline[1]);
let dw_b = double_word(other.data.inline[0], other.data.inline[1]);
dw_a == dw_b
} else {
let len_a = self.data.heap.1;
let len_b = other.data.heap.1;
if len_a != len_b {
return false;
}
let slice_a = slice::from_raw_parts(self.data.heap.0, len_a);
let slice_b = slice::from_raw_parts(other.data.heap.0, len_b);
slice_a == slice_b
}
}
}
}
impl Eq for Repr {}
Expand Down
29 changes: 29 additions & 0 deletions integer/tests/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,32 @@ fn test_abs_ord() {
assert!(ubig!(12).abs_cmp(&ibig!(-12)).is_eq());
assert!(ubig!(12).abs_cmp(&ibig!(-14)).is_le());
}

#[test]
fn test_eq_across_representations() {
// Regression for Repr::eq's inline-DoubleWord path and its sign/scale
// short-circuits, plus the canonical-encoding requirement it relies on.
// inline equal / unequal (capacity 1 and capacity 2)
assert_eq!(ubig!(5), ubig!(5));
assert_ne!(ubig!(5), ubig!(7));
assert_eq!(ubig!(0x10000000000000000), ubig!(0x10000000000000000)); // 2^64 (cap 2)
assert_ne!(ubig!(0x10000000000000000), ubig!(0x10000000000000001));
// scale mismatch: an inline value can never equal a heap value
assert_ne!(ubig!(5), ubig!(1) << 200);
assert_ne!(ubig!(1) << 200, ubig!(5));
// heap equal / unequal, same length and different length
assert_eq!(ubig!(1) << 200, ubig!(1) << 200);
assert_ne!((ubig!(1) << 200) + ubig!(1), (ubig!(1) << 200) + ubig!(2));
assert_ne!(ubig!(1) << 200, ubig!(1) << 201);
// cross-representation canonical equality: `ones` must compare equal to the
// inline form of the same value (the from_buffer canonicalisation).
assert_eq!(UBig::ones(128), UBig::from(u128::MAX));
assert_eq!(UBig::ones(256), (ubig!(1) << 256) - ubig!(1));
// IBig: zero is canonically positive, so sign disagreement is never equal
assert_eq!(ibig!(-5), ibig!(-5));
assert_ne!(ibig!(5), ibig!(-5));
assert_eq!(ibig!(0), ibig!(0));
assert_ne!(ibig!(0), ibig!(1));
assert_eq!(-(ibig!(1) << 200), -(ibig!(1) << 200));
assert_ne!(-(ibig!(1) << 200), ibig!(1) << 200);
}
Loading