RosettaCodeData/Task/Angles-geometric-normalizat.../Rust/angles-geometric-normalizat...

77 lines
1.9 KiB
Plaintext

use std::{
marker::PhantomData,
f64::consts::PI,
};
pub trait AngleUnit: Copy {
const TURN: f64;
const NAME: &'static str;
}
macro_rules! unit {
($name:ident, $value:expr, $string:expr) => (
#[derive(Debug, Copy, Clone)]
struct $name;
impl AngleUnit for $name {
const TURN: f64 = $value;
const NAME: &'static str = $string;
}
);
}
unit!(Degrees, 360.0, "Degrees");
unit!(Radians, PI * 2.0, "Radians");
unit!(Gradians, 400.0, "Gradians");
unit!(Mils, 6400.0, "Mils");
#[derive(Copy, Clone, PartialEq, PartialOrd)]
struct Angle<T: AngleUnit>(f64, PhantomData<T>);
impl<T: AngleUnit> Angle<T> {
pub fn new(val: f64) -> Self {
Self(val, PhantomData)
}
pub fn normalize(self) -> Self {
Self(self.0 % T::TURN, PhantomData)
}
pub fn val(self) -> f64 {
self.0
}
pub fn convert<U: AngleUnit>(self) -> Angle<U> {
Angle::new(self.0 * U::TURN / T::TURN)
}
pub fn name(self) -> &'static str {
T::NAME
}
}
fn print_angles<T: AngleUnit>() {
let angles = [-2.0, -1.0, 0.0, 1.0, 2.0, 6.2831853, 16.0, 57.2957795, 359.0, 399.0, 6399.0, 1000000.0];
println!("{:<12} {:<12} {:<12} {:<12} {:<12} {:<12}", "Angle", "Unit", "Degrees", "Gradians", "Mils", "Radians");
for &angle in &angles {
let deg = Angle::<T>::new(angle).normalize();
println!("{:<12} {:<12} {:<12.4} {:<12.4} {:<12.4} {:<12.4}",
angle,
deg.name(),
deg.convert::<Degrees>().val(),
deg.convert::<Gradians>().val(),
deg.convert::<Mils>().val(),
deg.convert::<Radians>().val(),
);
}
println!();
}
fn main() {
print_angles::<Degrees>();
print_angles::<Gradians>();
print_angles::<Mils>();
print_angles::<Radians>();
}