use std::fmt::{Debug, Formatter};
use std::iter::zip;
use std::ops::{BitAnd, BitOr, BitXor, Div, Mul, Sub};
use std::time::SystemTime;
use big_rational_str::BigRationalExt;
use chrono::{DateTime, Duration, NaiveDateTime};
use num::*;
use num::traits::Inv;
use regex::Captures;
use crate::{regex, util};
use crate::context::{Context, Format};
use crate::error::MyResult;
use crate::meaning::Meaning;
#[derive(PartialEq)]
pub struct Value {
pub number: Option<BigRational>,
pub meaning: Meaning,
pub comment: Option<String>,
}
const CHUNK_DIGITS: usize = 8;
const CHUNK_VALUE: usize = 0x1_0000_0000;
const ROUNDING_LIMIT: usize = 6;
impl Value {
pub const fn new(number: Option<BigRational>) -> Value {
Self {
number,
meaning: Meaning::Plain,
comment: None,
}
}
pub fn with_meaning(mut self, meaning: Meaning) -> Value {
self.meaning = meaning;
return self;
}
pub fn with_comment(mut self, comment: &str) -> Value {
self.comment = Some(String::from(comment));
return self;
}
fn with_rounding(mut self) -> Value {
if let Some(number) = &self.number {
if let Some(multiplier) = Self::measure_multiplier(number) {
if let Some(number) = number.checked_mul(&multiplier) {
let number = number.round();
if let Some(number) = number.checked_div(&multiplier) {
self.number = Some(number);
}
}
}
}
return self;
}
fn measure_multiplier(number: &BigRational) -> Option<BigRational> {
let string = big_rational_str::big_rational_ref_to_string(number);
if let Some(period) = string.find('.') {
let string = util::remove_char(&string, period);
for offset in 1..=ROUNDING_LIMIT {
if let Some((start, count)) = Self::measure_offset(&string, offset) {
if let Some(start) = start.checked_sub(period) {
if let Some(remain) = string.len().checked_sub(start + count) {
if remain >= count * 2 {
let multiplier = BigInt::from(10)
.pow(start as u32);
let multiplier = BigInt::from(10)
.pow(offset as u32)
.sub(&BigInt::from(1))
.mul(&multiplier);
return Some(BigRational::from_integer(multiplier));
}
}
}
}
}
}
return None;
}
fn measure_offset(string: &str, offset: usize) -> Option<(usize, usize)> {
let left = string.chars();
let right = string.chars().skip(offset);
let mut span = None;
for (index, (left, right)) in zip(left, right).enumerate() {
if let Some((start, count)) = span {
if left == right {
span = Some((start, count + 1));
} else if count >= ROUNDING_LIMIT {
return span;
} else {
span = None;
}
} else {
if left == right {
span = Some((index, 0));
}
}
}
return None;
}
pub fn from_string(context: &Context, number: &str) -> MyResult<Value> {
if let Format::Base16(_) = context.format {
return Self::from_hexadecimal(number);
} else if let Some(value) = Self::from_time(number) {
return Ok(value);
} else if let Some(value) = Self::from_delta(number) {
return Ok(value);
} else if let Some(number) = number.strip_prefix(".") {
let number = format!("0.{}", number);
return Self::from_decimal(&number);
} else if let Some(number) = number.strip_prefix("-.") {
let number = format!("-0.{}", number);
return Self::from_decimal(&number);
} else {
return Self::from_decimal(number);
}
}
fn from_time(time: &str) -> Option<Value> {
if let Some(time) = Self::parse_time(time) {
let numer = time.and_utc().timestamp_millis();
let numer = BigInt::from(numer);
let denom = BigInt::from(1000);
let number = BigRational::new(numer, denom);
let value = Self::new(Some(number)).with_meaning(Meaning::Time);
return Some(value);
}
return None;
}
fn parse_time(time: &str) -> Option<NaiveDateTime> {
let regex = regex!(r#"^\d+-\d+-\d+T\d+:\d+(:\d+(\.\d+)?)?$"#);
if let Some(captures) = regex.captures(time) {
let format = if captures.get(1).is_none() {
"%Y-%m-%dT%H:%M"
} else if captures.get(2).is_none() {
"%Y-%m-%dT%H:%M:%S"
} else {
"%Y-%m-%dT%H:%M:%S%.f" };
return NaiveDateTime::parse_from_str(time, format).ok();
}
return None;
}
fn from_delta(delta: &str) -> Option<Value> {
if let Some(delta) = Self::parse_delta(delta) {
let numer = delta.num_milliseconds();
let numer = BigInt::from(numer);
let denom = BigInt::from(1000);
let number = BigRational::new(numer, denom);
let value = Self::new(Some(number)).with_meaning(Meaning::Delta);
return Some(value);
}
return None;
}
fn parse_delta(delta: &str) -> Option<Duration> {
let regex = regex!(r#"^(-)?(?:(?:(\d+):)?(\d+):)?(\d+):(\d+)(?:\.(\d+))?$"#);
if let Some(captures) = regex.captures(delta) {
let negative = captures.get(1).is_some();
let days = Self::parse_capture(&captures, 2, None);
let hours = Self::parse_capture(&captures, 3, None) + (days * 24);
let minutes = Self::parse_capture(&captures, 4, None) + (hours * 60);
let seconds = Self::parse_capture(&captures, 5, None) + (minutes * 60);
let nanos = Self::parse_capture(&captures, 6, Some(9)) + (seconds * 1_000_000_000);
let delta = Duration::nanoseconds(nanos);
let delta = if negative { -delta } else { delta };
return Some(delta);
}
return None;
}
fn parse_capture(captures: &Captures, index: usize, expected: Option<usize>) -> i64 {
if let Some(capture) = captures.get(index) {
let number = capture.as_str().parse::<i64>().unwrap_or_default();
if let Some(expected) = expected {
let length = capture.as_str().len();
if let Some(diff) = expected.checked_sub(length) {
return number * pow(10, diff);
}
if let Some(diff) = length.checked_sub(expected) {
return number / pow(10, diff);
}
}
return number;
}
return 0;
}
fn from_decimal(number: &str) -> MyResult<Value> {
let number = BigRational::from_dec_str(number)?;
let value = Self::new(Some(number));
return Ok(value);
}
fn from_hexadecimal(number: &str) -> MyResult<Value> {
let number = BigInt::from_str_radix(number, 16)?;
let number = BigRational::from_integer(number);
let value = Self::new(Some(number));
return Ok(value);
}
pub fn to_strings(&self, context: &Context) -> (String, String, Option<String>) {
if let Some(number) = &self.number {
self.format_ratio(context, number, &self.comment)
} else {
(String::from("NaN"), String::from(""), self.comment.clone())
}
}
fn format_ratio(
&self,
context: &Context,
number: &BigRational,
comment: &Option<String>,
) -> (String, String, Option<String>) {
if self.meaning == Meaning::Time {
let number = number * BigInt::from(1000);
let number = number.to_i64().unwrap_or_default();
let number = Self::format_time(number);
return self.format_split(context, number, comment);
} else if self.meaning == Meaning::Delta {
let number = number * BigInt::from(1000);
let number = number.to_i64().unwrap_or_default();
let number = Self::format_delta(number);
return self.format_split(context, number, comment);
} else if let Format::Base16(chunks) = context.format {
let number = number.to_integer();
let number = Self::format_hexadecimal(number, chunks, context.separator);
return (number, String::from(""), comment.clone());
} else if let Some(precision) = context.precision {
let power = BigInt::from(10).pow(precision as u32);
let number = number.mul(&power).round().div(&power);
let number = big_rational_str::big_rational_to_string(number);
return self.format_split(context, number, comment);
} else {
let number = big_rational_str::big_rational_ref_to_string(number);
return self.format_split(context, number, comment);
}
}
fn format_time(millis: i64) -> String {
let time = DateTime::from_timestamp_millis(millis).unwrap_or_default();
return format!("{}", time.format("%Y-%m-%dT%H:%M:%S%.3f"));
}
fn format_delta(millis: i64) -> String {
let (minus, millis) = (if millis < 0 { "-" } else { "" }, abs(millis));
let (seconds, millis) = (millis / 1000, millis % 1000);
let (minutes, seconds) = (seconds / 60, seconds % 60);
let (hours, minutes) = (minutes / 60, minutes % 60);
let (days, hours) = (hours / 24, hours % 24);
if days != 0 {
return format!("{}{}:{:02}:{:02}:{:02}.{:03}", minus, days, hours, minutes, seconds, millis);
} else if hours != 0 {
return format!("{}{:02}:{:02}:{:02}.{:03}", minus, hours, minutes, seconds, millis);
} else if minutes != 0 {
return format!("{}{:02}:{:02}.{:03}", minus, minutes, seconds, millis);
} else {
return format!("{}{:02}.{:03}", minus, seconds, millis);
}
}
fn format_hexadecimal(
mut number: BigInt,
chunks: usize,
separator: bool,
) -> String {
if number.is_negative() {
let range = BigInt::from(CHUNK_VALUE).pow(chunks as u32);
number += range;
}
let number = format!("{:x}", number);
let padding = util::create_padding('0', chunks * CHUNK_DIGITS, number.len());
let number = format!("{}{}", padding, number);
if separator {
return number.as_bytes()
.chunks(CHUNK_DIGITS)
.map(std::str::from_utf8)
.collect::<Result<Vec<&str>, _>>()
.unwrap_or_default()
.join(" ");
}
return number;
}
fn format_split(
&self,
context: &Context,
number: String,
comment: &Option<String>,
) -> (String, String, Option<String>) {
let index = number.find('.').unwrap_or(number.len());
let (integer, fraction) = number.split_at(index);
let integer = self.modify_integer(context, integer);
let fraction = self.modify_fraction(context, fraction);
return (integer, fraction, comment.clone());
}
fn modify_integer(&self, context: &Context, number: &str) -> String {
if self.meaning == Meaning::Plain && context.separator {
let index = if number.starts_with('-') { 1 } else { 0 };
let (sign, number) = number.split_at(index);
let number = number.as_bytes()
.rchunks(3)
.rev()
.map(std::str::from_utf8)
.collect::<Result<Vec<&str>, _>>()
.unwrap_or_default()
.join(",");
return format!("{}{}", sign, number);
}
return String::from(number);
}
fn modify_fraction(&self, context: &Context, number: &str) -> String {
if self.meaning == Meaning::Plain {
if let Some(precision) = context.precision {
if !number.is_empty() || precision > 0 {
let index = if number.starts_with('.') { 1 } else { 0 };
let number = &number[index..];
let precision = precision as usize;
return ".".chars()
.chain(number.chars().take(precision))
.chain(std::iter::repeat('0'))
.take(precision + 1)
.collect::<String>();
}
}
}
return String::from(number);
}
pub fn measure_hexadecimal(&self) -> usize {
if let Some(number) = &self.number {
let number = number.to_integer();
return Self::count_hexadecimal(number);
} else {
return 1;
}
}
fn count_hexadecimal(mut number: BigInt) -> usize {
if number.is_negative() {
number = number.abs() - 1;
}
let count = number.iter_u32_digits().count().max(1);
let chunk = number.iter_u32_digits().last().unwrap_or_default();
return if chunk & 0x80000000 != 0 { count + 1 } else { count };
}
fn perform_unary(
value: Value,
function: fn(BigRational) -> Option<BigRational>,
) -> Value {
if let Some(number) = value.number {
Self::new(function(number)).with_rounding()
} else {
value
}
}
fn perform_binary(
lhs: Value,
rhs: Value,
function: fn(BigRational, BigRational) -> Option<BigRational>,
) -> Value {
if let Some(lhs) = lhs.number {
if let Some(rhs) = rhs.number {
Self::new(function(lhs, rhs)).with_rounding()
} else {
rhs
}
} else {
lhs
}
}
fn perform_series(
values: Vec<Value>,
function: fn(Vec<BigRational>) -> BigRational,
) -> Value {
let values = values.into_iter().filter_map(|x| x.number).collect();
let result = function(values);
return Self::new(Some(result)).with_rounding();
}
pub fn calc_add(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pdt, Meaning::ddt, Meaning::ttx)?;
let result = Self::perform_binary(lhs, rhs, |x, y| x.checked_add(&y));
return Ok(result.with_meaning(meaning));
}
pub fn calc_sub(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pdx, Meaning::ddx, Meaning::ttd)?;
let result = Self::perform_binary(lhs, rhs, |x, y| x.checked_sub(&y));
return Ok(result.with_meaning(meaning));
}
pub fn calc_mul(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pdx, Meaning::dxx, Meaning::xxx)?;
let result = Self::perform_binary(lhs, rhs, |x, y| x.checked_mul(&y));
return Ok(result.with_meaning(meaning));
}
pub fn calc_div(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pxx, Meaning::dxx, Meaning::xxx)?;
let result = Self::perform_binary(lhs, rhs, |x, y| x.checked_div(&y));
return Ok(result.with_meaning(meaning));
}
pub fn calc_mod(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pxx, Meaning::dxx, Meaning::xxx)?;
let result = Self::perform_binary(lhs, rhs, Self::checked_mod);
return Ok(result.with_meaning(meaning));
}
fn checked_mod(lhs: BigRational, rhs: BigRational) -> Option<BigRational> {
lhs.checked_div(&rhs)
.map(|result| &result - result.to_integer())
.map(|result| result * rhs)
}
pub fn calc_neg(value: Value) -> MyResult<Value> {
let meaning = value.meaning.pdx()?;
let result = Self::perform_unary(value, |x| Some(-x));
return Ok(result.with_meaning(meaning));
}
pub fn calc_inv(value: Value) -> MyResult<Value> {
let meaning = value.meaning.pxx()?;
let result = Self::perform_unary(value, Self::checked_inv);
return Ok(result.with_meaning(meaning));
}
fn checked_inv(x: BigRational) -> Option<BigRational> {
if x.is_zero() { None } else { Some(x.inv()) }
}
pub fn calc_pow(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pxx, Meaning::xxx, Meaning::xxx)?;
let result = Self::perform_binary(lhs, rhs, Self::checked_pow);
return Ok(result.with_meaning(meaning));
}
fn checked_pow(lhs: BigRational, rhs: BigRational) -> Option<BigRational> {
if rhs.is_integer() {
if let Some(rhs) = rhs.to_i32() {
return Some(lhs.pow(rhs));
}
} else {
if let Some(lhs) = lhs.to_f64() {
if let Some(rhs) = rhs.to_f64() {
let result = f64::powf(lhs, rhs);
return BigRational::from_f64(result);
}
}
}
return None;
}
pub fn calc_sqrt(value: Value) -> MyResult<Value> {
let meaning = value.meaning.pxx()?;
let result = Self::perform_unary(value, Self::checked_sqrt);
return Ok(result.with_meaning(meaning));
}
fn checked_sqrt(number: BigRational) -> Option<BigRational> {
if let Some(number) = number.to_f64() {
let result = number.sqrt();
return BigRational::from_f64(result);
}
return None;
}
pub fn calc_sum(values: Vec<Value>) -> MyResult<Value> {
let result = Self::perform_series(values, |x| x.into_iter().sum());
return Ok(result);
}
pub fn calc_prod(values: Vec<Value>) -> MyResult<Value> {
let result = Self::perform_series(values, |x| x.into_iter().product());
return Ok(result);
}
pub fn calc_and(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pxx, Meaning::xxx, Meaning::xxx)?;
let result = Self::perform_binary(lhs, rhs, Self::checked_and);
return Ok(result.with_meaning(meaning));
}
pub fn calc_or(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pxx, Meaning::xxx, Meaning::xxx)?;
let result = Self::perform_binary(lhs, rhs, Self::checked_or);
return Ok(result.with_meaning(meaning));
}
pub fn calc_xor(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pxx, Meaning::xxx, Meaning::xxx)?;
let result = Self::perform_binary(lhs, rhs, Self::checked_xor);
return Ok(result.with_meaning(meaning));
}
fn checked_and(lhs: BigRational, rhs: BigRational) -> Option<BigRational> {
let lhs = lhs.to_integer();
let rhs = rhs.to_integer();
let result = lhs.bitand(rhs);
return Some(BigRational::from(result));
}
fn checked_or(lhs: BigRational, rhs: BigRational) -> Option<BigRational> {
let lhs = lhs.to_integer();
let rhs = rhs.to_integer();
let result = lhs.bitor(rhs);
return Some(BigRational::from(result));
}
fn checked_xor(lhs: BigRational, rhs: BigRational) -> Option<BigRational> {
let lhs = lhs.to_integer();
let rhs = rhs.to_integer();
let result = lhs.bitxor(rhs);
return Some(BigRational::from(result));
}
pub fn calc_shl(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pxx, Meaning::xxx, Meaning::xxx)?;
let result = Self::perform_binary(lhs, rhs, |x, y| Self::checked_shl(x, y));
return Ok(result.with_meaning(meaning));
}
pub fn calc_shr(lhs: Value, rhs: Value) -> MyResult<Value> {
let meaning = lhs.meaning.combine(rhs.meaning, Meaning::pxx, Meaning::xxx, Meaning::xxx)?;
let result = Self::perform_binary(lhs, rhs, |x, y| Self::checked_shl(x, -y));
return Ok(result.with_meaning(meaning));
}
fn checked_shl(lhs: BigRational, rhs: BigRational) -> Option<BigRational> {
if let Some(rhs) = rhs.to_i32() {
let two = BigRational::new(BigInt::from(2), BigInt::from(1));
return Some(lhs * two.pow(rhs));
} else {
return None;
}
}
pub fn calc_now() -> MyResult<Value> {
let time = SystemTime::now();
let time = time.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default();
let numer = time.as_millis();
let numer = BigInt::from(numer);
let denom = BigInt::from(1000);
let number = BigRational::new(numer, denom);
let result = Self::new(Some(number)).with_meaning(Meaning::Time);
return Ok(result);
}
pub fn cast_plain(value: Value) -> MyResult<Value> {
Ok(value.with_meaning(Meaning::Plain))
}
pub fn cast_delta(value: Value) -> MyResult<Value> {
Ok(value.with_meaning(Meaning::Delta))
}
pub fn cast_time(value: Value) -> MyResult<Value> {
Ok(value.with_meaning(Meaning::Time))
}
}
impl Default for Value {
fn default() -> Self {
Self::new(Some(BigRational::zero()))
}
}
impl Clone for Value {
fn clone(&self) -> Self {
let number = self.number.clone();
let mut value = Value::new(number);
value.meaning = self.meaning;
value.comment = self.comment.clone();
return value;
}
}
impl Debug for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Some(number) = &self.number {
write!(f, "{}", big_rational_str::big_rational_ref_to_string(number))
} else {
write!(f, "NaN")
}
}
}
#[cfg(test)]
mod tests {
use num::{BigInt, BigRational, CheckedMul, CheckedSub, FromPrimitive, ToPrimitive};
use pretty_assertions::assert_eq;
use crate::context::{Context, Format};
use crate::error::{EngineError, MyError, MyResult};
use crate::meaning::Meaning;
use crate::value::Value;
const VALUE_NAN: Value = Value::new(None);
#[test]
fn test_decimal_is_created_from_string() {
let context = Context::new();
assert_eq!(value(0, 1), Value::from_string(&context, "0").unwrap());
assert_eq!(value(42, 1), Value::from_string(&context, "42").unwrap());
assert_eq!(value(-42, 1), Value::from_string(&context, "-42").unwrap());
assert_eq!(value(12345, 100), Value::from_string(&context, "123.45").unwrap());
assert_eq!(value(-12345, 100), Value::from_string(&context, "-123.45").unwrap());
assert_eq!(value(999, 1000), Value::from_string(&context, ".999").unwrap());
assert_eq!(value(-999, 1000), Value::from_string(&context, "-.999").unwrap());
assert!(Value::from_string(&context, "").is_err());
assert!(Value::from_string(&context, "foo").is_err());
}
#[test]
fn test_hexadecimal_is_created_from_lowercase() {
let context = Context::new().with_format(Format::Base16(0));
assert_eq!(value(0, 1), Value::from_string(&context, "0").unwrap());
assert_eq!(value(0xffff, 1), Value::from_string(&context, "ffff").unwrap());
assert_eq!(value(0xffffffff, 1), Value::from_string(&context, "ffffffff").unwrap());
assert_eq!(value(0xffffffffffff, 1), Value::from_string(&context, "ffffffffffff").unwrap());
assert_eq!(value(0x0123456789abcdef, 1), Value::from_string(&context, "0123456789abcdef").unwrap());
assert!(Value::from_string(&context, "").is_err());
assert!(Value::from_string(&context, "foo").is_err());
}
#[test]
fn test_hexadecimal_is_created_from_uppercase() {
let context = Context::new().with_format(Format::Base16(0));
assert_eq!(value(0, 1), Value::from_string(&context, "0").unwrap());
assert_eq!(value(0xffff, 1), Value::from_string(&context, "FFFF").unwrap());
assert_eq!(value(0xffffffff, 1), Value::from_string(&context, "FFFFFFFF").unwrap());
assert_eq!(value(0xffffffffffff, 1), Value::from_string(&context, "FFFFFFFFFFFF").unwrap());
assert_eq!(value(0x0123456789abcdef, 1), Value::from_string(&context, "0123456789ABCDEF").unwrap());
assert!(Value::from_string(&context, "").is_err());
assert!(Value::from_string(&context, "FOO").is_err());
}
#[test]
fn test_time_is_created_from_string() {
let context = Context::new();
let summer1 = Value::from_string(&context, "2024-06-30T23:59").unwrap();
let summer2 = Value::from_string(&context, "2024-06-30T23:59:59").unwrap();
let summer3 = Value::from_string(&context, "2024-06-30T23:59:59.999").unwrap();
let winter1 = Value::from_string(&context, "2024-12-31T23:59").unwrap();
let winter2 = Value::from_string(&context, "2024-12-31T23:59:59").unwrap();
let winter3 = Value::from_string(&context, "2024-12-31T23:59:59.999").unwrap();
assert_eq!(value(1719791940000, 1000).with_meaning(Meaning::Time), summer1);
assert_eq!(value(1719791999000, 1000).with_meaning(Meaning::Time), summer2);
assert_eq!(value(1719791999999, 1000).with_meaning(Meaning::Time), summer3);
assert_eq!(value(1735689540000, 1000).with_meaning(Meaning::Time), winter1);
assert_eq!(value(1735689599000, 1000).with_meaning(Meaning::Time), winter2);
assert_eq!(value(1735689599999, 1000).with_meaning(Meaning::Time), winter3);
}
#[test]
fn test_delta_is_created_from_string() {
let context = Context::new();
let minutes1 = Value::from_string(&context, "34:56").unwrap();
let minutes2 = Value::from_string(&context, "34:56.7").unwrap();
let minutes3 = Value::from_string(&context, "34:56.789").unwrap();
let minutes4 = Value::from_string(&context, "-34:56").unwrap();
let minutes5 = Value::from_string(&context, "-34:56.7").unwrap();
let minutes6 = Value::from_string(&context, "-34:56.789").unwrap();
let hours1 = Value::from_string(&context, "12:34:56").unwrap();
let hours2 = Value::from_string(&context, "12:34:56.7").unwrap();
let hours3 = Value::from_string(&context, "12:34:56.789").unwrap();
let hours4 = Value::from_string(&context, "-12:34:56").unwrap();
let hours5 = Value::from_string(&context, "-12:34:56.7").unwrap();
let hours6 = Value::from_string(&context, "-12:34:56.789").unwrap();
let days1 = Value::from_string(&context, "9:12:34:56").unwrap();
let days2 = Value::from_string(&context, "9:12:34:56.7").unwrap();
let days3 = Value::from_string(&context, "9:12:34:56.789").unwrap();
let days4 = Value::from_string(&context, "-9:12:34:56").unwrap();
let days5 = Value::from_string(&context, "-9:12:34:56.7").unwrap();
let days6 = Value::from_string(&context, "-9:12:34:56.789").unwrap();
assert_eq!(value(2096000, 1000).with_meaning(Meaning::Delta), minutes1);
assert_eq!(value(2096700, 1000).with_meaning(Meaning::Delta), minutes2);
assert_eq!(value(2096789, 1000).with_meaning(Meaning::Delta), minutes3);
assert_eq!(value(-2096000, 1000).with_meaning(Meaning::Delta), minutes4);
assert_eq!(value(-2096700, 1000).with_meaning(Meaning::Delta), minutes5);
assert_eq!(value(-2096789, 1000).with_meaning(Meaning::Delta), minutes6);
assert_eq!(value(45296000, 1000).with_meaning(Meaning::Delta), hours1);
assert_eq!(value(45296700, 1000).with_meaning(Meaning::Delta), hours2);
assert_eq!(value(45296789, 1000).with_meaning(Meaning::Delta), hours3);
assert_eq!(value(-45296000, 1000).with_meaning(Meaning::Delta), hours4);
assert_eq!(value(-45296700, 1000).with_meaning(Meaning::Delta), hours5);
assert_eq!(value(-45296789, 1000).with_meaning(Meaning::Delta), hours6);
assert_eq!(value(822896000, 1000).with_meaning(Meaning::Delta), days1);
assert_eq!(value(822896700, 1000).with_meaning(Meaning::Delta), days2);
assert_eq!(value(822896789, 1000).with_meaning(Meaning::Delta), days3);
assert_eq!(value(-822896000, 1000).with_meaning(Meaning::Delta), days4);
assert_eq!(value(-822896700, 1000).with_meaning(Meaning::Delta), days5);
assert_eq!(value(-822896789, 1000).with_meaning(Meaning::Delta), days6);
}
#[test]
fn test_value_with_repeating_zero_or_nine_is_rounded() {
let number1 = fudge(ratio(17, 8)).and_then(opposite); let number2 = fudge(ratio(16, 8)).and_then(opposite); let number3 = fudge(ratio(16, 8)); let number4 = fudge(ratio(17, 8)); assert_eq!(value(15, 8), Value::new(number1).with_rounding());
assert_eq!(value(16, 8), Value::new(number2).with_rounding());
assert_eq!(value(16, 8), Value::new(number3).with_rounding());
assert_eq!(value(17, 8), Value::new(number4).with_rounding());
}
#[test]
fn test_value_with_repeating_one_to_eight_is_rounded() {
let number1 = fudge(ratio(908992, 9000)); let number2 = fudge(ratio(1088924, 99000)); let number3 = fudge(ratio(1997457, 999000)); let number4 = fudge(ratio(19995891, 9999000)); assert_eq!(value(908992, 9000), Value::new(number1).with_rounding());
assert_eq!(value(1088924, 99000), Value::new(number2).with_rounding());
assert_eq!(value(1997457, 999000), Value::new(number3).with_rounding());
assert_eq!(value(19995891, 9999000), Value::new(number4).with_rounding());
}
#[test]
fn test_decimal_is_formatted_with_default() {
let context = Context::new();
assert_eq!((String::from("0"), String::from(""), None), value(0, 1).to_strings(&context));
assert_eq!((String::from("1"), String::from(""), None), value(1, 1).to_strings(&context));
assert_eq!((String::from("12"), String::from(""), None), value(12, 1).to_strings(&context));
assert_eq!((String::from("123"), String::from(""), None), value(123, 1).to_strings(&context));
assert_eq!((String::from("1234"), String::from(""), None), value(1234, 1).to_strings(&context));
assert_eq!((String::from("12345"), String::from(""), None), value(12345, 1).to_strings(&context));
assert_eq!((String::from("123456"), String::from(""), None), value(123456, 1).to_strings(&context));
assert_eq!((String::from("1234567"), String::from(""), None), value(1234567, 1).to_strings(&context));
assert_eq!((String::from("12345678"), String::from(""), None), value(12345678, 1).to_strings(&context));
assert_eq!((String::from("123456789"), String::from(""), None), value(123456789, 1).to_strings(&context));
assert_eq!((String::from("-1"), String::from(""), None), value(-1, 1).to_strings(&context));
assert_eq!((String::from("-12"), String::from(""), None), value(-12, 1).to_strings(&context));
assert_eq!((String::from("-123"), String::from(""), None), value(-123, 1).to_strings(&context));
assert_eq!((String::from("-1234"), String::from(""), None), value(-1234, 1).to_strings(&context));
assert_eq!((String::from("-12345"), String::from(""), None), value(-12345, 1).to_strings(&context));
assert_eq!((String::from("-123456"), String::from(""), None), value(-123456, 1).to_strings(&context));
assert_eq!((String::from("-1234567"), String::from(""), None), value(-1234567, 1).to_strings(&context));
assert_eq!((String::from("-12345678"), String::from(""), None), value(-12345678, 1).to_strings(&context));
assert_eq!((String::from("-123456789"), String::from(""), None), value(-123456789, 1).to_strings(&context));
assert_eq!((String::from("0"), String::from(".00001"), None), value(1, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".0001"), None), value(1, 10000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".001"), None), value(1, 1000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".01"), None), value(1, 100).to_strings(&context));
assert_eq!((String::from("0"), String::from(".1"), None), value(1, 10).to_strings(&context));
assert_eq!((String::from("0"), String::from(".4994"), None), value(49940, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".49949"), None), value(49949, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".4995"), None), value(49950, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".49951"), None), value(49951, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".9"), None), value(9, 10).to_strings(&context));
assert_eq!((String::from("0"), String::from(".99"), None), value(99, 100).to_strings(&context));
assert_eq!((String::from("0"), String::from(".999"), None), value(999, 1000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".9999"), None), value(9999, 10000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".99999"), None), value(99999, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".00001"), None), value(-1, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".0001"), None), value(-1, 10000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".001"), None), value(-1, 1000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".01"), None), value(-1, 100).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".1"), None), value(-1, 10).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".4994"), None), value(-49940, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".49949"), None), value(-49949, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".4995"), None), value(-49950, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".49951"), None), value(-49951, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".9"), None), value(-9, 10).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".99"), None), value(-99, 100).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".999"), None), value(-999, 1000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".9999"), None), value(-9999, 10000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".99999"), None), value(-99999, 100000).to_strings(&context));
}
#[test]
fn test_decimal_is_formatted_with_separator() {
let context = Context::new().with_separator(true);
assert_eq!((String::from("0"), String::from(""), None), value(0, 1).to_strings(&context));
assert_eq!((String::from("1"), String::from(""), None), value(1, 1).to_strings(&context));
assert_eq!((String::from("12"), String::from(""), None), value(12, 1).to_strings(&context));
assert_eq!((String::from("123"), String::from(""), None), value(123, 1).to_strings(&context));
assert_eq!((String::from("1,234"), String::from(""), None), value(1234, 1).to_strings(&context));
assert_eq!((String::from("12,345"), String::from(""), None), value(12345, 1).to_strings(&context));
assert_eq!((String::from("123,456"), String::from(""), None), value(123456, 1).to_strings(&context));
assert_eq!((String::from("1,234,567"), String::from(""), None), value(1234567, 1).to_strings(&context));
assert_eq!((String::from("12,345,678"), String::from(""), None), value(12345678, 1).to_strings(&context));
assert_eq!((String::from("123,456,789"), String::from(""), None), value(123456789, 1).to_strings(&context));
assert_eq!((String::from("-1"), String::from(""), None), value(-1, 1).to_strings(&context));
assert_eq!((String::from("-12"), String::from(""), None), value(-12, 1).to_strings(&context));
assert_eq!((String::from("-123"), String::from(""), None), value(-123, 1).to_strings(&context));
assert_eq!((String::from("-1,234"), String::from(""), None), value(-1234, 1).to_strings(&context));
assert_eq!((String::from("-12,345"), String::from(""), None), value(-12345, 1).to_strings(&context));
assert_eq!((String::from("-123,456"), String::from(""), None), value(-123456, 1).to_strings(&context));
assert_eq!((String::from("-1,234,567"), String::from(""), None), value(-1234567, 1).to_strings(&context));
assert_eq!((String::from("-12,345,678"), String::from(""), None), value(-12345678, 1).to_strings(&context));
assert_eq!((String::from("-123,456,789"), String::from(""), None), value(-123456789, 1).to_strings(&context));
assert_eq!((String::from("0"), String::from(".00001"), None), value(1, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".0001"), None), value(1, 10000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".001"), None), value(1, 1000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".01"), None), value(1, 100).to_strings(&context));
assert_eq!((String::from("0"), String::from(".1"), None), value(1, 10).to_strings(&context));
assert_eq!((String::from("0"), String::from(".4994"), None), value(49940, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".49949"), None), value(49949, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".4995"), None), value(49950, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".49951"), None), value(49951, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".9"), None), value(9, 10).to_strings(&context));
assert_eq!((String::from("0"), String::from(".99"), None), value(99, 100).to_strings(&context));
assert_eq!((String::from("0"), String::from(".999"), None), value(999, 1000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".9999"), None), value(9999, 10000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".99999"), None), value(99999, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".00001"), None), value(-1, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".0001"), None), value(-1, 10000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".001"), None), value(-1, 1000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".01"), None), value(-1, 100).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".1"), None), value(-1, 10).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".4994"), None), value(-49940, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".49949"), None), value(-49949, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".4995"), None), value(-49950, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".49951"), None), value(-49951, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".9"), None), value(-9, 10).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".99"), None), value(-99, 100).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".999"), None), value(-999, 1000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".9999"), None), value(-9999, 10000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".99999"), None), value(-99999, 100000).to_strings(&context));
}
#[test]
fn test_decimal_is_formatted_with_precision() {
let context = Context::new().with_precision(Some(3));
assert_eq!((String::from("0"), String::from(".000"), None), value(0, 1).to_strings(&context));
assert_eq!((String::from("1"), String::from(".000"), None), value(1, 1).to_strings(&context));
assert_eq!((String::from("12"), String::from(".000"), None), value(12, 1).to_strings(&context));
assert_eq!((String::from("123"), String::from(".000"), None), value(123, 1).to_strings(&context));
assert_eq!((String::from("1234"), String::from(".000"), None), value(1234, 1).to_strings(&context));
assert_eq!((String::from("12345"), String::from(".000"), None), value(12345, 1).to_strings(&context));
assert_eq!((String::from("123456"), String::from(".000"), None), value(123456, 1).to_strings(&context));
assert_eq!((String::from("1234567"), String::from(".000"), None), value(1234567, 1).to_strings(&context));
assert_eq!((String::from("12345678"), String::from(".000"), None), value(12345678, 1).to_strings(&context));
assert_eq!((String::from("123456789"), String::from(".000"), None), value(123456789, 1).to_strings(&context));
assert_eq!((String::from("-1"), String::from(".000"), None), value(-1, 1).to_strings(&context));
assert_eq!((String::from("-12"), String::from(".000"), None), value(-12, 1).to_strings(&context));
assert_eq!((String::from("-123"), String::from(".000"), None), value(-123, 1).to_strings(&context));
assert_eq!((String::from("-1234"), String::from(".000"), None), value(-1234, 1).to_strings(&context));
assert_eq!((String::from("-12345"), String::from(".000"), None), value(-12345, 1).to_strings(&context));
assert_eq!((String::from("-123456"), String::from(".000"), None), value(-123456, 1).to_strings(&context));
assert_eq!((String::from("-1234567"), String::from(".000"), None), value(-1234567, 1).to_strings(&context));
assert_eq!((String::from("-12345678"), String::from(".000"), None), value(-12345678, 1).to_strings(&context));
assert_eq!((String::from("-123456789"), String::from(".000"), None), value(-123456789, 1).to_strings(&context));
assert_eq!((String::from("0"), String::from(".000"), None), value(1, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".000"), None), value(1, 10000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".001"), None), value(1, 1000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".010"), None), value(1, 100).to_strings(&context));
assert_eq!((String::from("0"), String::from(".100"), None), value(1, 10).to_strings(&context));
assert_eq!((String::from("0"), String::from(".499"), None), value(49940, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".499"), None), value(49949, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".500"), None), value(49950, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".500"), None), value(49951, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".900"), None), value(9, 10).to_strings(&context));
assert_eq!((String::from("0"), String::from(".990"), None), value(99, 100).to_strings(&context));
assert_eq!((String::from("0"), String::from(".999"), None), value(999, 1000).to_strings(&context));
assert_eq!((String::from("1"), String::from(".000"), None), value(9999, 10000).to_strings(&context));
assert_eq!((String::from("1"), String::from(".000"), None), value(99999, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".000"), None), value(-1, 100000).to_strings(&context));
assert_eq!((String::from("0"), String::from(".000"), None), value(-1, 10000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".001"), None), value(-1, 1000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".010"), None), value(-1, 100).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".100"), None), value(-1, 10).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".499"), None), value(-49940, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".499"), None), value(-49949, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".500"), None), value(-49950, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".500"), None), value(-49951, 100000).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".900"), None), value(-9, 10).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".990"), None), value(-99, 100).to_strings(&context));
assert_eq!((String::from("-0"), String::from(".999"), None), value(-999, 1000).to_strings(&context));
assert_eq!((String::from("-1"), String::from(".000"), None), value(-9999, 10000).to_strings(&context));
assert_eq!((String::from("-1"), String::from(".000"), None), value(-99999, 100000).to_strings(&context));
}
#[test]
fn test_recurring_is_formatted_with_precision() {
let value = value(1, 3);
let format = |precision: Option<u8>| {
let context = Context::new().with_precision(precision);
let (_, fraction, _) = value.to_strings(&context);
return fraction;
};
assert_eq!(String::from(""), format(Some(0)));
assert_eq!(String::from(".333333"), format(Some(6)));
assert_eq!(String::from(".(3)"), format(None));
}
#[test]
fn test_hexadecimal_is_formatted_with_default() {
let context = Context::new().with_format(Format::Base16(3));
assert_eq!((String::from("000000000000000000000000"), String::from(""), None), value(0, 1).to_strings(&context));
assert_eq!((String::from("000000000000000000000001"), String::from(""), None), value(1, 1).to_strings(&context));
assert_eq!((String::from("00000000000000007ffffffe"), String::from(""), None), value(2147483646, 1).to_strings(&context));
assert_eq!((String::from("00000000000000007fffffff"), String::from(""), None), value(2147483647, 1).to_strings(&context));
assert_eq!((String::from("000000000000000080000000"), String::from(""), None), value(2147483648, 1).to_strings(&context));
assert_eq!((String::from("000000000000000080000001"), String::from(""), None), value(2147483649, 1).to_strings(&context));
assert_eq!((String::from("0000000000000000fffffffe"), String::from(""), None), value(4294967294, 1).to_strings(&context));
assert_eq!((String::from("0000000000000000ffffffff"), String::from(""), None), value(4294967295, 1).to_strings(&context));
assert_eq!((String::from("000000000000000100000000"), String::from(""), None), value(4294967296, 1).to_strings(&context));
assert_eq!((String::from("000000000000000100000001"), String::from(""), None), value(4294967297, 1).to_strings(&context));
assert_eq!((String::from("000000007ffffffffffffffe"), String::from(""), None), value(9223372036854775806, 1).to_strings(&context));
assert_eq!((String::from("000000007fffffffffffffff"), String::from(""), None), value(9223372036854775807, 1).to_strings(&context));
assert_eq!((String::from("000000008000000000000000"), String::from(""), None), value(9223372036854775808, 1).to_strings(&context));
assert_eq!((String::from("000000008000000000000001"), String::from(""), None), value(9223372036854775809, 1).to_strings(&context));
assert_eq!((String::from("00000000fffffffffffffffe"), String::from(""), None), value(18446744073709551614, 1).to_strings(&context));
assert_eq!((String::from("00000000ffffffffffffffff"), String::from(""), None), value(18446744073709551615, 1).to_strings(&context));
assert_eq!((String::from("000000010000000000000000"), String::from(""), None), value(18446744073709551616, 1).to_strings(&context));
assert_eq!((String::from("000000010000000000000001"), String::from(""), None), value(18446744073709551617, 1).to_strings(&context));
assert_eq!((String::from("ffffffff0000000000000000"), String::from(""), None), value(-18446744073709551616, 1).to_strings(&context));
assert_eq!((String::from("ffffffff0000000000000001"), String::from(""), None), value(-18446744073709551615, 1).to_strings(&context));
assert_eq!((String::from("ffffffff8000000000000000"), String::from(""), None), value(-9223372036854775808, 1).to_strings(&context));
assert_eq!((String::from("ffffffff8000000000000001"), String::from(""), None), value(-9223372036854775807, 1).to_strings(&context));
assert_eq!((String::from("ffffffffffffffff00000000"), String::from(""), None), value(-4294967296, 1).to_strings(&context));
assert_eq!((String::from("ffffffffffffffff00000001"), String::from(""), None), value(-4294967295, 1).to_strings(&context));
assert_eq!((String::from("ffffffffffffffff80000000"), String::from(""), None), value(-2147483648, 1).to_strings(&context));
assert_eq!((String::from("ffffffffffffffff80000001"), String::from(""), None), value(-2147483647, 1).to_strings(&context));
assert_eq!((String::from("fffffffffffffffffffffffe"), String::from(""), None), value(-2, 1).to_strings(&context));
assert_eq!((String::from("ffffffffffffffffffffffff"), String::from(""), None), value(-1, 1).to_strings(&context));
}
#[test]
fn test_hexadecimal_is_formatted_with_separator() {
let context = Context::new().with_format(Format::Base16(3)).with_separator(true);
assert_eq!((String::from("00000000 00000000 00000000"), String::from(""), None), value(0, 1).to_strings(&context));
assert_eq!((String::from("00000000 00000000 00000001"), String::from(""), None), value(1, 1).to_strings(&context));
assert_eq!((String::from("00000000 00000000 7ffffffe"), String::from(""), None), value(2147483646, 1).to_strings(&context));
assert_eq!((String::from("00000000 00000000 7fffffff"), String::from(""), None), value(2147483647, 1).to_strings(&context));
assert_eq!((String::from("00000000 00000000 80000000"), String::from(""), None), value(2147483648, 1).to_strings(&context));
assert_eq!((String::from("00000000 00000000 80000001"), String::from(""), None), value(2147483649, 1).to_strings(&context));
assert_eq!((String::from("00000000 00000000 fffffffe"), String::from(""), None), value(4294967294, 1).to_strings(&context));
assert_eq!((String::from("00000000 00000000 ffffffff"), String::from(""), None), value(4294967295, 1).to_strings(&context));
assert_eq!((String::from("00000000 00000001 00000000"), String::from(""), None), value(4294967296, 1).to_strings(&context));
assert_eq!((String::from("00000000 00000001 00000001"), String::from(""), None), value(4294967297, 1).to_strings(&context));
assert_eq!((String::from("00000000 7fffffff fffffffe"), String::from(""), None), value(9223372036854775806, 1).to_strings(&context));
assert_eq!((String::from("00000000 7fffffff ffffffff"), String::from(""), None), value(9223372036854775807, 1).to_strings(&context));
assert_eq!((String::from("00000000 80000000 00000000"), String::from(""), None), value(9223372036854775808, 1).to_strings(&context));
assert_eq!((String::from("00000000 80000000 00000001"), String::from(""), None), value(9223372036854775809, 1).to_strings(&context));
assert_eq!((String::from("00000000 ffffffff fffffffe"), String::from(""), None), value(18446744073709551614, 1).to_strings(&context));
assert_eq!((String::from("00000000 ffffffff ffffffff"), String::from(""), None), value(18446744073709551615, 1).to_strings(&context));
assert_eq!((String::from("00000001 00000000 00000000"), String::from(""), None), value(18446744073709551616, 1).to_strings(&context));
assert_eq!((String::from("00000001 00000000 00000001"), String::from(""), None), value(18446744073709551617, 1).to_strings(&context));
assert_eq!((String::from("ffffffff 00000000 00000000"), String::from(""), None), value(-18446744073709551616, 1).to_strings(&context));
assert_eq!((String::from("ffffffff 00000000 00000001"), String::from(""), None), value(-18446744073709551615, 1).to_strings(&context));
assert_eq!((String::from("ffffffff 80000000 00000000"), String::from(""), None), value(-9223372036854775808, 1).to_strings(&context));
assert_eq!((String::from("ffffffff 80000000 00000001"), String::from(""), None), value(-9223372036854775807, 1).to_strings(&context));
assert_eq!((String::from("ffffffff ffffffff 00000000"), String::from(""), None), value(-4294967296, 1).to_strings(&context));
assert_eq!((String::from("ffffffff ffffffff 00000001"), String::from(""), None), value(-4294967295, 1).to_strings(&context));
assert_eq!((String::from("ffffffff ffffffff 80000000"), String::from(""), None), value(-2147483648, 1).to_strings(&context));
assert_eq!((String::from("ffffffff ffffffff 80000001"), String::from(""), None), value(-2147483647, 1).to_strings(&context));
assert_eq!((String::from("ffffffff ffffffff fffffffe"), String::from(""), None), value(-2, 1).to_strings(&context));
assert_eq!((String::from("ffffffff ffffffff ffffffff"), String::from(""), None), value(-1, 1).to_strings(&context));
}
#[test]
fn test_hexadecimal_is_measured_from_valid() {
assert_eq!(1, value(0, 1).measure_hexadecimal()); assert_eq!(1, value(1, 1).measure_hexadecimal()); assert_eq!(1, value(2147483646, 1).measure_hexadecimal()); assert_eq!(1, value(2147483647, 1).measure_hexadecimal()); assert_eq!(2, value(2147483648, 1).measure_hexadecimal()); assert_eq!(2, value(2147483649, 1).measure_hexadecimal()); assert_eq!(2, value(4294967294, 1).measure_hexadecimal()); assert_eq!(2, value(4294967295, 1).measure_hexadecimal()); assert_eq!(2, value(4294967296, 1).measure_hexadecimal()); assert_eq!(2, value(4294967297, 1).measure_hexadecimal()); assert_eq!(2, value(9223372036854775806, 1).measure_hexadecimal()); assert_eq!(2, value(9223372036854775807, 1).measure_hexadecimal()); assert_eq!(3, value(9223372036854775808, 1).measure_hexadecimal()); assert_eq!(3, value(9223372036854775809, 1).measure_hexadecimal()); assert_eq!(3, value(18446744073709551614, 1).measure_hexadecimal()); assert_eq!(3, value(18446744073709551615, 1).measure_hexadecimal()); assert_eq!(3, value(18446744073709551616, 1).measure_hexadecimal()); assert_eq!(3, value(18446744073709551617, 1).measure_hexadecimal()); assert_eq!(3, value(-18446744073709551616, 1).measure_hexadecimal()); assert_eq!(3, value(-18446744073709551615, 1).measure_hexadecimal()); assert_eq!(2, value(-9223372036854775808, 1).measure_hexadecimal()); assert_eq!(2, value(-9223372036854775807, 1).measure_hexadecimal()); assert_eq!(2, value(-4294967296, 1).measure_hexadecimal()); assert_eq!(2, value(-4294967295, 1).measure_hexadecimal()); assert_eq!(1, value(-2147483648, 1).measure_hexadecimal()); assert_eq!(1, value(-2147483647, 1).measure_hexadecimal()); assert_eq!(1, value(-2, 1).measure_hexadecimal()); assert_eq!(1, value(-1, 1).measure_hexadecimal()); }
#[test]
fn test_time_is_formatted_without_separator() {
let context = Context::new().with_separator(true).with_precision(Some(6));
assert_time("2024-06-30T12:00:00", ".000", value(1719748800000, 1000), &context);
assert_time("2024-06-30T12:00:00", ".987", value(1719748800987, 1000), &context);
assert_time("2024-12-31T12:00:00", ".000", value(1735646400000, 1000), &context);
assert_time("2024-12-31T12:00:00", ".987", value(1735646400987, 1000), &context);
}
#[test]
fn test_delta_is_formatted_without_separator() {
let context = Context::new().with_separator(true).with_precision(Some(6));
assert_delta("00", ".000", value(0, 1), &context);
assert_delta("59", ".999", value(59999, 1000), &context);
assert_delta("01:00", ".000", value(60000, 1000), &context);
assert_delta("59:59", ".999", value(3599999, 1000), &context);
assert_delta("01:00:00", ".000", value(3600000, 1000), &context);
assert_delta("23:59:59", ".999", value(86399999, 1000), &context);
assert_delta("1:00:00:00", ".000", value(86400000, 1000), &context);
assert_delta("-59", ".999", value(-59999, 1000), &context);
assert_delta("-01:00", ".000", value(-60000, 1000), &context);
assert_delta("-59:59", ".999", value(-3599999, 1000), &context);
assert_delta("-01:00:00", ".000", value(-3600000, 1000), &context);
assert_delta("-23:59:59", ".999", value(-86399999, 1000), &context);
assert_delta("-1:00:00:00", ".000", value(-86400000, 1000), &context);
}
fn assert_time(integer: &str, fraction: &str, value: Value, context: &Context) {
let expected = (String::from(integer), String::from(fraction), None);
let actual = value.with_meaning(Meaning::Time).to_strings(context);
assert_eq!(expected, actual);
}
fn assert_delta(integer: &str, fraction: &str, value: Value, context: &Context) {
let expected = (String::from(integer), String::from(fraction), None);
let actual = value.with_meaning(Meaning::Delta).to_strings(context);
assert_eq!(expected, actual);
}
#[test]
fn test_decimal_is_formatted_from_nan() {
let context = Context::new();
assert_eq!((String::from("NaN"), String::from(""), None), VALUE_NAN.to_strings(&context));
}
#[test]
fn test_hexadecimal_is_measured_from_nan() {
assert_eq!(1, VALUE_NAN.measure_hexadecimal());
}
#[test]
fn test_plain_values_are_added_by_value() {
assert_eq!(Value::calc_add(value(55, 10), value(25, 10)).ok(), Some(value(8, 1)));
assert_eq!(Value::calc_add(value(55, 10), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_add(VALUE_NAN, value(25, 10)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_add(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_added_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_add, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_ok(Value::calc_add, Meaning::Plain, Meaning::Delta), Some(Meaning::Delta));
assert_eq!(perform_binary_ok(Value::calc_add, Meaning::Plain, Meaning::Time), Some(Meaning::Time));
assert_eq!(perform_binary_ok(Value::calc_add, Meaning::Delta, Meaning::Plain), Some(Meaning::Delta));
assert_eq!(perform_binary_ok(Value::calc_add, Meaning::Delta, Meaning::Delta), Some(Meaning::Delta));
assert_eq!(perform_binary_ok(Value::calc_add, Meaning::Delta, Meaning::Time), Some(Meaning::Time));
assert_eq!(perform_binary_ok(Value::calc_add, Meaning::Time, Meaning::Plain), Some(Meaning::Time));
assert_eq!(perform_binary_ok(Value::calc_add, Meaning::Time, Meaning::Delta), Some(Meaning::Time));
assert_eq!(perform_binary_err(Value::calc_add, Meaning::Time, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_are_subtracted_by_value() {
assert_eq!(Value::calc_sub(value(55, 10), value(25, 10)).ok(), Some(value(3, 1)));
assert_eq!(Value::calc_sub(value(55, 10), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_sub(VALUE_NAN, value(25, 10)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_sub(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_subtracted_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_sub, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_ok(Value::calc_sub, Meaning::Plain, Meaning::Delta), Some(Meaning::Delta));
assert_eq!(perform_binary_err(Value::calc_sub, Meaning::Plain, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_ok(Value::calc_sub, Meaning::Delta, Meaning::Plain), Some(Meaning::Delta));
assert_eq!(perform_binary_ok(Value::calc_sub, Meaning::Delta, Meaning::Delta), Some(Meaning::Delta));
assert_eq!(perform_binary_err(Value::calc_sub, Meaning::Delta, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_ok(Value::calc_sub, Meaning::Time, Meaning::Plain), Some(Meaning::Time));
assert_eq!(perform_binary_ok(Value::calc_sub, Meaning::Time, Meaning::Delta), Some(Meaning::Time));
assert_eq!(perform_binary_ok(Value::calc_sub, Meaning::Time, Meaning::Time), Some(Meaning::Delta));
}
#[test]
fn test_plain_values_are_multiplied_by_value() {
assert_eq!(Value::calc_mul(value(55, 10), value(25, 10)).ok(), Some(value(1375, 100)));
assert_eq!(Value::calc_mul(value(55, 10), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_mul(VALUE_NAN, value(25, 10)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_mul(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_multiplied_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_mul, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_ok(Value::calc_mul, Meaning::Plain, Meaning::Delta), Some(Meaning::Delta));
assert_eq!(perform_binary_err(Value::calc_mul, Meaning::Plain, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_ok(Value::calc_mul, Meaning::Delta, Meaning::Plain), Some(Meaning::Delta));
assert_eq!(perform_binary_err(Value::calc_mul, Meaning::Delta, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_mul, Meaning::Delta, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_mul, Meaning::Time, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_mul, Meaning::Time, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_mul, Meaning::Time, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_are_divided_by_value() {
assert_eq!(Value::calc_div(value(55, 10), value(25, 10)).ok(), Some(value(22, 10)));
assert_eq!(Value::calc_div(value(55, 10), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_div(VALUE_NAN, value(25, 10)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_div(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_divided_by_zero() {
assert_eq!(Value::calc_div(value(55, 10), value(0, 1)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_div(VALUE_NAN, value(0, 1)).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_divided_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_div, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_err(Value::calc_div, Meaning::Plain, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_div, Meaning::Plain, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_ok(Value::calc_div, Meaning::Delta, Meaning::Plain), Some(Meaning::Delta));
assert_eq!(perform_binary_err(Value::calc_div, Meaning::Delta, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_div, Meaning::Delta, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_div, Meaning::Time, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_div, Meaning::Time, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_div, Meaning::Time, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_are_remaindered_by_value() {
assert_eq!(Value::calc_mod(value(55, 10), value(25, 10)).ok(), Some(value(5, 10)));
assert_eq!(Value::calc_mod(value(55, 10), value(-25, 10)).ok(), Some(value(5, 10)));
assert_eq!(Value::calc_mod(value(-55, 10), value(25, 10)).ok(), Some(value(-5, 10)));
assert_eq!(Value::calc_mod(value(-55, 10), value(-25, 10)).ok(), Some(value(-5, 10)));
assert_eq!(Value::calc_mod(value(55, 10), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_mod(value(-55, 10), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_mod(VALUE_NAN, value(25, 10)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_mod(VALUE_NAN, value(-25, 10)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_mod(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_remaindered_by_zero() {
assert_eq!(Value::calc_mod(value(55, 10), value(0, 1)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_mod(VALUE_NAN, value(0, 1)).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_remaindered_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_mod, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_err(Value::calc_mod, Meaning::Plain, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_mod, Meaning::Plain, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_ok(Value::calc_mod, Meaning::Delta, Meaning::Plain), Some(Meaning::Delta));
assert_eq!(perform_binary_err(Value::calc_mod, Meaning::Delta, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_mod, Meaning::Delta, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_mod, Meaning::Time, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_mod, Meaning::Time, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_mod, Meaning::Time, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_are_negated_by_value() {
assert_eq!(Value::calc_neg(value(5, 10)).ok(), Some(value(-5, 10)));
assert_eq!(Value::calc_neg(value(-5, 10)).ok(), Some(value(5, 10)));
assert_eq!(Value::calc_neg(VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_negated_by_meaning() {
assert_eq!(perform_unary_ok(Value::calc_neg, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_unary_ok(Value::calc_neg, Meaning::Delta), Some(Meaning::Delta));
assert_eq!(perform_unary_err(Value::calc_neg, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_are_inverted_if_valid() {
assert_eq!(Value::calc_inv(value(5, 10)).ok(), Some(value(10, 5)));
assert_eq!(Value::calc_inv(value(-5, 10)).ok(), Some(value(-10, 5)));
assert_eq!(Value::calc_inv(VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_inverted_if_zero() {
assert_eq!(Value::calc_inv(value(0, 1)).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_inverted_by_meaning() {
assert_eq!(perform_unary_ok(Value::calc_inv, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_unary_err(Value::calc_inv, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_unary_err(Value::calc_inv, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_are_raised_to_integer_power() {
assert_eq!(Value::calc_pow(value(3, 1), value(4, 1)).ok(), Some(value(81, 1)));
assert_eq!(Value::calc_pow(value(3, 1), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_pow(VALUE_NAN, value(4, 1)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_pow(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_raised_to_fractional_power() {
assert_eq!(Value::calc_pow(value(3, 1), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_pow(VALUE_NAN, value(1, 2)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_pow(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_raised_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_pow, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_err(Value::calc_pow, Meaning::Plain, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_pow, Meaning::Plain, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_pow, Meaning::Delta, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_pow, Meaning::Delta, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_pow, Meaning::Delta, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_pow, Meaning::Time, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_pow, Meaning::Time, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_pow, Meaning::Time, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_have_square_root_if_positive() {
assert_eq!(Value::calc_sqrt(VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_have_square_root_if_negative() {
assert_eq!(Value::calc_sqrt(value(-3, 1)).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_have_square_root_by_meaning() {
assert_eq!(perform_unary_ok(Value::calc_sqrt, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_unary_err(Value::calc_sqrt, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_unary_err(Value::calc_sqrt, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_are_summed() {
assert_eq!(Some(value(0, 1)), Value::calc_sum(vec![]).ok());
assert_eq!(Some(value(0, 1)), Value::calc_sum(vec![VALUE_NAN, VALUE_NAN, VALUE_NAN]).ok());
assert_eq!(Some(value(25, 10)), Value::calc_sum(vec![value(25, 10), VALUE_NAN, VALUE_NAN]).ok());
assert_eq!(Some(value(65, 10)), Value::calc_sum(vec![value(25, 10), value(4, 1), VALUE_NAN]).ok());
assert_eq!(Some(value(120, 10)), Value::calc_sum(vec![value(25, 10), value(4, 1), value(55, 10)]).ok());
}
#[test]
fn test_plain_values_are_producted() {
assert_eq!(Some(value(1, 1)), Value::calc_prod(vec![]).ok());
assert_eq!(Some(value(1, 1)), Value::calc_prod(vec![VALUE_NAN, VALUE_NAN, VALUE_NAN]).ok());
assert_eq!(Some(value(25, 10)), Value::calc_prod(vec![value(25, 10), VALUE_NAN, VALUE_NAN]).ok());
assert_eq!(Some(value(100, 10)), Value::calc_prod(vec![value(25, 10), value(4, 1), VALUE_NAN]).ok());
assert_eq!(Some(value(550, 10)), Value::calc_prod(vec![value(25, 10), value(4, 1), value(55, 10)]).ok());
}
#[test]
fn test_plain_values_have_bitwise_and_by_value() {
assert_eq!(Value::calc_and(value(0xff00, 1), value(0xf0f0, 1)).ok(), Some(value(0xf000, 1)));
assert_eq!(Value::calc_and(value(0xff00, 1), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_and(VALUE_NAN, value(0xf0f0, 1)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_and(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_have_bitwise_and_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_and, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_err(Value::calc_and, Meaning::Plain, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_and, Meaning::Plain, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_and, Meaning::Delta, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_and, Meaning::Delta, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_and, Meaning::Delta, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_and, Meaning::Time, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_and, Meaning::Time, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_and, Meaning::Time, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_have_bitwise_or_by_value() {
assert_eq!(Value::calc_or(value(0xff00, 1), value(0xf0f0, 1)).ok(), Some(value(0xfff0, 1)));
assert_eq!(Value::calc_or(value(0xff00, 1), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_or(VALUE_NAN, value(0xf0f0, 1)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_or(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_have_bitwise_or_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_or, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_err(Value::calc_or, Meaning::Plain, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_or, Meaning::Plain, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_or, Meaning::Delta, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_or, Meaning::Delta, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_or, Meaning::Delta, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_or, Meaning::Time, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_or, Meaning::Time, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_or, Meaning::Time, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_have_bitwise_xor_by_value() {
assert_eq!(Value::calc_xor(value(0xff00, 1), value(0xf0f0, 1)).ok(), Some(value(0x0ff0, 1)));
assert_eq!(Value::calc_xor(value(0xff00, 1), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_xor(VALUE_NAN, value(0xf0f0, 1)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_xor(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_have_bitwise_xor_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_xor, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_err(Value::calc_xor, Meaning::Plain, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_xor, Meaning::Plain, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_xor, Meaning::Delta, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_xor, Meaning::Delta, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_xor, Meaning::Delta, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_xor, Meaning::Time, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_xor, Meaning::Time, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_xor, Meaning::Time, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_are_shifted_left_by_value() {
assert_eq!(Value::calc_shl(value(35, 10), value(29, 10)).ok(), Some(value(14, 1)));
assert_eq!(Value::calc_shl(value(-35, 10), value(29, 10)).ok(), Some(value(-14, 1)));
assert_eq!(Value::calc_shl(value(35, 10), value(-29, 10)).ok(), Some(value(875, 1000)));
assert_eq!(Value::calc_shl(value(-35, 10), value(-29, 10)).ok(), Some(value(-875, 1000)));
assert_eq!(Value::calc_shl(value(35, 10), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_shl(value(-35, 10), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_shl(VALUE_NAN, value(29, 10)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_shl(VALUE_NAN, value(-29, 10)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_shl(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_shifted_left_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_shl, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_err(Value::calc_shl, Meaning::Plain, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shl, Meaning::Plain, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shl, Meaning::Delta, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shl, Meaning::Delta, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shl, Meaning::Delta, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shl, Meaning::Time, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shl, Meaning::Time, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shl, Meaning::Time, Meaning::Time), Some(EngineError::BadTimeOp));
}
#[test]
fn test_plain_values_are_shifted_right_by_value() {
assert_eq!(Value::calc_shr(value(35, 10), value(29, 10)).ok(), Some(value(875, 1000)));
assert_eq!(Value::calc_shr(value(-35, 10), value(29, 10)).ok(), Some(value(-875, 1000)));
assert_eq!(Value::calc_shr(value(35, 10), value(-29, 10)).ok(), Some(value(14, 1)));
assert_eq!(Value::calc_shr(value(-35, 10), value(-29, 10)).ok(), Some(value(-14, 1)));
assert_eq!(Value::calc_shr(value(35, 10), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_shr(value(-35, 10), VALUE_NAN).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_shr(VALUE_NAN, value(29, 10)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_shr(VALUE_NAN, value(-29, 10)).ok(), Some(VALUE_NAN));
assert_eq!(Value::calc_shr(VALUE_NAN, VALUE_NAN).ok(), Some(VALUE_NAN));
}
#[test]
fn test_plain_values_are_shifted_right_by_meaning() {
assert_eq!(perform_binary_ok(Value::calc_shr, Meaning::Plain, Meaning::Plain), Some(Meaning::Plain));
assert_eq!(perform_binary_err(Value::calc_shr, Meaning::Plain, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shr, Meaning::Plain, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shr, Meaning::Delta, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shr, Meaning::Delta, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shr, Meaning::Delta, Meaning::Time), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shr, Meaning::Time, Meaning::Plain), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shr, Meaning::Time, Meaning::Delta), Some(EngineError::BadTimeOp));
assert_eq!(perform_binary_err(Value::calc_shr, Meaning::Time, Meaning::Time), Some(EngineError::BadTimeOp));
}
fn perform_unary_ok(
function: fn(Value) -> MyResult<Value>,
meaning: Meaning,
) -> Option<Meaning> {
let meaning = value(1, 1).with_meaning(meaning);
let result = function(meaning);
return result.ok().map(get_meaning);
}
fn perform_unary_err(
function: fn(Value) -> MyResult<Value>,
meaning: Meaning,
) -> Option<EngineError> {
let meaning = value(1, 1).with_meaning(meaning);
let result = function(meaning);
return result.err().and_then(filter_error);
}
fn perform_binary_ok(
function: fn(Value, Value) -> MyResult<Value>,
lhs: Meaning,
rhs: Meaning,
) -> Option<Meaning> {
let lhs = value(1, 1).with_meaning(lhs);
let rhs = value(1, 1).with_meaning(rhs);
let result = function(lhs, rhs);
return result.ok().map(get_meaning);
}
fn perform_binary_err(
function: fn(Value, Value) -> MyResult<Value>,
lhs: Meaning,
rhs: Meaning,
) -> Option<EngineError> {
let lhs = value(1, 1).with_meaning(lhs);
let rhs = value(1, 1).with_meaning(rhs);
let result = function(lhs, rhs);
return result.err().and_then(filter_error);
}
fn get_meaning(value: Value) -> Meaning {
value.meaning
}
fn filter_error(error: MyError) -> Option<EngineError> {
match error {
MyError::Engine(error) => Some(error),
_ => None,
}
}
fn value(numer: i128, denom: i128) -> Value {
let number = ratio(numer, denom);
return Value::new(Some(number));
}
fn ratio(numer: i128, denom: i128) -> BigRational {
let numer = BigInt::from(numer);
let denom = BigInt::from(denom);
return BigRational::new(numer, denom);
}
fn fudge(number: BigRational) -> Option<BigRational> {
number.to_f64()
.map(f64::sqrt)
.and_then(BigRational::from_f64)
.and_then(square)
}
fn square(number: BigRational) -> Option<BigRational> {
number.checked_mul(&number)
}
fn opposite(number: BigRational) -> Option<BigRational> {
ratio(2, 1).checked_mul(&number.round()).and_then(|x| x.checked_sub(&number))
}
}