Skip to content

Add general Rem and Num implementations for Complex<T> #327

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

Merged
merged 6 commits into from
Sep 20, 2017

Conversation

carrutstick
Copy link
Contributor

This should address #209 with eyes towards addressing #321.

It was a little tricky to get Rem working for a general Num, and I had to add a PartialOrd constraint to get it working, but I think it should be fairly robust.

I could probably use extra eyes on the from_str_radix function, as I mostly lifted the code from the from_str function and I may be missing some subtleties in how that works.

@cuviper
Copy link
Member

cuviper commented Aug 10, 2017

Just FYI it's going to be a week or so before I can review this in depth.

type FromStrRadixErr = ParseComplexError<T::FromStrRadixErr>;

/// Parses `a +/- bi`; `ai +/- b`; `a`; or `bi` where `a` and `b` are of type `T`
fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>
Copy link
Member

@cuviper cuviper Aug 20, 2017

Choose a reason for hiding this comment

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

This is identical to the from_str implementation, apart from nested calls to from_str_radix instead, right? Let's see if we can use a common helper function instead, something like:

fn from_str_any<T, E, F: Fn(&str) -> Result<T, E>>(s: &str, from: F) -> Result<Complex<T>, E> { ... }
  • from_str calls from_str_any(s, T::from_str)
  • from_str_radix calls from_str_any(s, |s| T::from_str_radix(s, radix))

let neg = zero.clone() - one.clone();
// Traverse the 3x3 square of gaussian integers surrounding our
// current approximation, and select the one whose product with
// `modulus` is closest to `self`.
Copy link
Member

Choose a reason for hiding this comment

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

So you're basically rounding the division to the nearest gaussian? I think this is makes it overly complicated, whereas simply rounding towards zero will at least agree with pure-real Rem.

e.g. 2.0 % 3.0 == 2.0, but in Complex your implementation gives -1.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The funny thing about rounding towards zero is that you can end up with remainders that have larger magnitude than your modulus. For instance you'd have (3 + 3i) % 4 == 3 + 3i, which has greater magnitude than 4.

Of course if we go with lexicographical ordering of complex numbers, similar issues might be unavoidable. I'm not sure there is any sensible scheme where the remainder is never lexicographically greater than the modulus.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, that is a funny example. Do you have one that would fail lexicographical ordering?

I guess we see why ordering and remainders aren't usually implemented for complex numbers. But if we do implement them, I think it's a fundamental property that operations on Complex values with zero imaginary should match the equivalent operation on the real part alone.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So one example is (1+5i) % 2i: your quotient is 2.5-.5i, which rounds to 2, and then your remainder is 1+1i, which is lexicographically larger than your modulus.

Are you aware of any compiler warnings along the line of #[deprecated] that would be appropriate for functions that may have unexpected behavior? And what would you think of applying them to rem and partial_cmp?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

P.S. I thing having complex numbers with zero imaginary component try to match behavior with real numbers is a fine principle; just pointing out places where it can get weird.

@@ -758,6 +810,15 @@ impl<T: Clone + Num> Div<T> for Complex<T> {
}
}

impl<T: Clone + Num + PartialOrd> Rem<T> for Complex<T> {
Copy link
Member

Choose a reason for hiding this comment

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

I think now this doesn't need PartialOrd, right? And we can revert the related macro changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, good point.

@cuviper
Copy link
Member

cuviper commented Sep 19, 2017

bors r+

bors bot added a commit that referenced this pull request Sep 19, 2017
327: Add general Rem and Num implementations for Complex<T> r=cuviper a=carrutstick

This should address #209 with eyes towards addressing #321.

It was a little tricky to get `Rem` working for a general `Num`, and I had to add a `PartialOrd` constraint to get it working, but I think it should be fairly robust.

I could probably use extra eyes on the `from_str_radix` function, as I mostly lifted the code from the `from_str` function and I may be missing some subtleties in how that works.
@bors
Copy link
Contributor

bors bot commented Sep 20, 2017

Build succeeded

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.

2 participants