Proposal: Add complex cis function
I propose to add the complex variant of the real $\mathrm{cis}$ function. It would be defined as $\mathrm{cis}\colon\mathbb{C}\to\mathbb{C}$ with
$$\mathrm{cis}(z) = \exp(iz) = \exp(-\mathrm{Im}(z))\mathrm{cis}(\mathrm{Re}(z))$$
This complex $\mathrm{cis}$ function has applications in physics and numerical mathematics.
Since there's already fn cis(T) -> Complex<T>, maybe this could be fn into_cis(self) -> Self? Is there any naming precedent from other languages for this form?
Implementations in other languages that I found so far include:
- Intel's C++ compiler has only
double _Complex cis(double). (I guess in C's naming convention one would havecisfor a function with a real andccisfor a function with a complex argument.) - Common Lisp has only
cis real => complex - julia has
cis(z::Real)andcis(z::Complex)via function overloading - D has
Complex!real expi(real y)
So, it could also be named expi or ccis.
There is a technical problem in the implementation. num::Complex::cis() has the following signature: pub fn cis(phase: T) -> Complex<T>
This is not correct deduction of types. I suggest changing the signature to something that decouples phase type from number type.
let y: Complex<i32> = num::Complex::cis(1)
The above example shows why this type sharing is wrong. changing the following phase to 1.0 changes the Complex number completely.
@a-samea that's a strange example -- $cis(1) \approx 0.54 + 0.84i$, so it would have to round or truncate badly for Complex<i32>.
Yes, this is an strange example. The point was that phase data type has nothing to do with the number data type. I am also sorry to bring it into this discussion. I later found out that it was irrelevant to the cis function I was using. I did not know how these were implemented but i sensed a potential bug.
I would rather propose to add
impl<T: Neg<Output = T>> Complex<T> {
pub fn mul_i(self) -> Self {
Self {
re: -self.im,
im: self.re,
}
}
}
so you can write your cis function as z.mul_i().exp(). Seems like a much cleaner solution.
Multiplication of i and z are automatically handled via operator overloading.
Complex::I * z
This the output of your function in practice.
@a-samea your solution would have the same performance for floats due to the compiler optimizing the multiplication with 0,1 out, I assume, but I like that explicit, as for types like BigRational one can not guarantee that optimization to take place.