use std::collections::HashMap;
use std::fmt::{Display, Formatter, Result};
use std::ops::{Div, Mul};
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum BaseUnit {
METER,
SECOND,
KILOGRAM,
AMPERE,
KELVIN,
MOLE,
CANDELA,
RADIAN,
STERADIAN,
CURRENCY,
INHABITANT,
BIRTH,
DEATH,
}
#[derive(Debug, Clone)]
pub struct Unit {
base_units: HashMap<BaseUnit, i32>,
}
impl Unit {
pub fn new() -> Unit {
Unit {
base_units: HashMap::new(),
}
}
pub fn from_vec(list: Vec<(BaseUnit, i32)>) -> Unit {
let mut res = Unit::new();
for (unit, count) in list {
res.add_single_unit(unit, count);
}
res
}
pub fn get_base_units(&self) -> &HashMap<BaseUnit, i32> {
&self.base_units
}
pub fn get_inverse(&self) -> Unit {
let mut hashmap: HashMap<BaseUnit, i32> = HashMap::new();
for (unit, count) in self.base_units.iter() {
hashmap.insert(*unit, -count);
}
Unit {
base_units: hashmap,
}
}
pub fn add_single_unit(&mut self, unit: BaseUnit, n: i32) {
let count = self.base_units.entry(unit).or_insert(0);
*count += n;
}
fn add_from_hashmap(&mut self, hashmap: &HashMap<BaseUnit, i32>) {
for (unit, count) in hashmap.iter() {
self.add_single_unit(*unit, *count);
}
}
}
impl PartialEq for Unit {
fn eq(&self, other: &Unit) -> bool {
let mut equal = true;
for (unit, count) in self.base_units.iter() {
if *count != 0 {
match other.base_units.get(unit) {
Some(i) => {
if i != count {
equal = false;
}
}
_ => equal = false,
}
}
}
for (unit, count) in other.base_units.iter() {
if *count != 0 {
match self.base_units.get(unit) {
Some(i) => {
if i != count {
equal = false;
}
}
_ => equal = false,
}
}
}
equal
}
}
impl Mul for &Unit {
type Output = Unit;
fn mul(self, other: &Unit) -> Unit {
let mut result = Unit::new();
result.add_from_hashmap(&self.base_units);
result.add_from_hashmap(&other.base_units);
result
}
}
impl Div for &Unit {
type Output = Unit;
fn div(self, other: &Unit) -> Unit {
let mut result = Unit::new();
let inversed_unit = &other.get_inverse();
let inversed_map = inversed_unit.get_base_units();
result.add_from_hashmap(&self.base_units);
result.add_from_hashmap(&inversed_map);
result
}
}
impl Display for Unit {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let mut list_units: Vec<(&BaseUnit, &i32)> = self
.base_units
.iter()
.filter(|(_, val)| **val != 0)
.collect();
list_units.sort_by(|(_, val1), (_, val2)| val2.partial_cmp(val1).unwrap());
if list_units.len() == 0 {
return write!(f, "∅");
}
let mut res = String::new();
let mut iterator = list_units.iter().peekable();
while let Some((unit, count)) = iterator.next() {
res.push_str(match unit {
BaseUnit::METER => "m",
BaseUnit::SECOND => "s",
BaseUnit::KILOGRAM => "kg",
BaseUnit::AMPERE => "A",
BaseUnit::KELVIN => "K",
BaseUnit::MOLE => "mol",
BaseUnit::CANDELA => "cd",
BaseUnit::RADIAN => "rad",
BaseUnit::STERADIAN => "sr",
BaseUnit::CURRENCY => "currency",
BaseUnit::INHABITANT => "inhabitant",
BaseUnit::BIRTH => "birth",
BaseUnit::DEATH => "death",
});
if **count != 1 {
let count_string = count.to_string();
for c in count_string.chars() {
match c {
'-' => res.push_str("⁻"),
'0' => res.push_str("⁰"),
'1' => res.push_str("¹"),
'2' => res.push_str("²"),
'3' => res.push_str("³"),
'4' => res.push_str("⁴"),
'5' => res.push_str("⁵"),
'6' => res.push_str("⁶"),
'7' => res.push_str("⁷"),
'8' => res.push_str("⁸"),
'9' => res.push_str("⁹"),
_ => (),
}
}
}
match iterator.peek() {
Some(_) => res.push_str("·"),
None => (),
}
}
write!(f, "{}", res)
}
}