Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: relax the constraints of floor_sum #99

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

nebocco
Copy link

@nebocco nebocco commented Jun 9, 2022

ref #91

this PR contains following two changes:

  • Improve floor_sum
  • relax the constraints of floor_sum

@@ -186,24 +186,20 @@ pub fn crt(r: &[i64], m: &[i64]) -> (i64, i64) {
/// assert_eq!(math::floor_sum(6, 5, 4, 3), 13);
/// ```
pub fn floor_sum(n: i64, m: i64, mut a: i64, mut b: i64) -> i64 {
assert!(0 <= n && n < 1i64 << 32);
assert!(1 <= m && m < 1i64 << 32);
let mut ans = 0;
Copy link
Author

@nebocco nebocco Jun 9, 2022

Choose a reason for hiding this comment

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

Since overflows occur in C++ without special handling, they use unsigned long long here to extend the range of values returned correctly; implementing this in Rust would require a lot of casting (as u64) and functions like u64::overflowing_sub. Since this is cumbersome, I've written the internal computation in i64, but this may have led to more cases of overflowing only in the middle of the computation. Which would be better?

Copy link
Collaborator

@TonalidadeHidrica TonalidadeHidrica Aug 21, 2022

Choose a reason for hiding this comment

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

Why not using wrapping_* variants?

src/math.rs Outdated
pub fn floor_sum(n: i64, m: i64, mut a: i64, mut b: i64) -> i64 {
assert!(0 <= n && n < 1i64 << 32);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you just replace it with something like matches!(n, 0..1 << 32)? This is slightly better than (0..1 << 32).contains(&n) because it does not involve taking a reference to n, which may avoid constant-factor performance degradation.

Copy link
Author

Choose a reason for hiding this comment

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

Since the implementation of matches! macro seems to be insufficient, it causes error on parsing. Should I replace it with (0..1<<32).contains(&n) ?

% cargo build
   Compiling ac-library-rs v0.1.0 (/home/user/xxx/ac-library-rs)
error: no rules expected the token `<<`
   --> src/math.rs:190:33
    |
190 |     assert!(matches!(n, 0..1i64 << 32));
    |                                 ^^ no rules expected this token in macro call

error: no rules expected the token `<<`
   --> src/math.rs:191:33
    |
191 |     assert!(matches!(m, 1..1i64 << 32));
    |                                 ^^ no rules expected this token in macro call

error: aborting due to 2 previous errors

error: could not compile `ac-library-rs`.

To learn more, run the command again with --verbose.

src/math.rs Outdated
pub fn floor_sum(n: i64, m: i64, mut a: i64, mut b: i64) -> i64 {
assert!(0 <= n && n < 1i64 << 32);
assert!(1 <= m && m < 1i64 << 32);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here

Copy link
Collaborator

@TonalidadeHidrica TonalidadeHidrica left a comment

Choose a reason for hiding this comment

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

  • Update the docs of floor_sum according to this.

pub fn floor_sum(n: i64, m: i64, mut a: i64, mut b: i64) -> i64 {
assert!((0..1i64 << 32).contains(&n));
assert!((1..1i64 << 32).contains(&m));
let mut ans = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's wrap the variables with std::num::Wrapping<u64>. Note that if you shadow the variables, you need to check if a and b are negative beforehand.

/// `sum_{i=0}^{n-1} floor((ai + b) / m) (mod 2^64)`
/* const */
#[allow(clippy::many_single_char_names)]
pub(crate) fn floor_sum_unsigned(mut n: u64, mut m: u64, mut a: u64, mut b: u64) -> u64 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let this function take Wrapping<u64> instead of u64 to avoid complication; it's an internal function anyway.

@qryxip
Copy link
Member

qryxip commented Apr 8, 2023

@TonalidadeHidrica @mizar How about merging this PR now and fix it later?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants