num-complex
num-complex copied to clipboard
Idea: separate types for polar/Cartesian represendations
This might allow users to control which representation is used for the sake of increasing efficiency of calculations.
For example, if a program needs to multiply a large amount of numbers together, they can convert to polar and then back to Cartesian after all of the operations are done.
As far as I can tell, there are (at least) two ways that this could be implemented. One is to have separate structs for each type:
pub struct Cartesian<T> {}
impl<T> Cartesian<T> {
pub fn to_polar(self) -> Polar<T> {}
}
pub struct Polar<T> {}
impl<T> Polar<T> {
pub fn to_cartesian(self) -> Cartesian<T> {}
}
with methods to convert between them. Another way would be to add a second type to Complex<T>
, like so:
use std::marker::PhantomData;
// Representations
type Cartesian;
type Polar;
// R is the marker for represetation
pub struct Complex<T, R> {
a: T,
b: T,
repr: PhantomData<R>
}
impl<T> Complex<T, Cartesian> {
pub fn to_polar(self) -> Complex<T, Polar> {}
}
impl<T> Complex<T, Polar> {
pub fn to_cartesian(self) -> Complex<T, Cartesian> {}
}
where a
is the real part for cartesian, or the angle for polar, and b
is the imaginary part for cartesian, size for polar.
The advantage of the second method would be less code repetition for shared methods, but I'm not sure about the amount of those. The negative would be obviously the more complex code which may bot be as beginner friendly as the current methods.
The advantage of the first method would be that it is clear that these are distinct types, but methods in common must be implemented separately.
Either method would be a breaking change if used as is, but they could both be used behind a wrapper type that is named Complex<T>
which has the same methods as the current version.
The branch is currently on 0.2.0-git (pre-release), so breaking changes are possible.
I'm inclined to leave Complex<T>
alone, because it aligns with what other languages use, like C's _Complex
. In fact, this is why we have a #[repr(C)]
on the struct.
We could add Polar<T>
while leaving Complex<T>
to be Cartesian.
I also prefer Complex<T>
and Polar<T>
; I'll work on this in a bit and see how much of the code I can refactor with it.
@clarcharr Did you ever experiment with Polar<T>
? I'm getting ready to ship 0.2, and one breaking consideration is whether Complex::to_polar
and from_polar
should use such a new type, or how else that might look. Maybe we'd just add From<Complex<T>> for Polar<T>
and vice versa, which we don't need to worry about right now (leaving the current to_polar
and from_polar
as-is).
@cuviper I actually completely forgot, although I have a feeling it would make a lot of computations simpler.