use crate::{global::Float, impl_display_from_to_display, util::ToDisplayAndBrief};
use narsese::api::EvidentNumber;
use std::ops::{Add, BitAnd, BitOr, Div, Mul, Not, Sub};
use thiserror::Error;
type UShort = u32;
const SHORT_MAX: UShort = 10000;
const MULTIPLIER_TO_FLOAT: Float = 0.0001;
const MULTIPLIER_TO_UINT: Float = 10000.0;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ShortFloat {
value: UShort,
}
mod serde {
use super::{ShortFloat, UShort};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
impl Serialize for ShortFloat {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.value.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ShortFloat {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = UShort::deserialize(deserializer)?;
Self::new(value).map_err(serde::de::Error::custom)
}
}
}
#[derive(Debug, Clone, Error)]
pub enum ShortFloatError {
#[error("value out of range: {0}")]
OutOfRange(Float),
}
pub type ShortFloatResult = Result<ShortFloat, ShortFloatError>;
impl ShortFloat {
pub const ZERO: Self = Self::new_unchecked(0);
pub const ONE: Self = Self::new_unchecked(SHORT_MAX);
pub const HALF: Self = Self::new_unchecked(SHORT_MAX / 2);
#[inline(always)]
pub fn new(value: UShort) -> Result<Self, ShortFloatError> {
Self::new_unchecked(value).validate()
}
pub(super) const fn new_unchecked(value: UShort) -> Self {
Self { value }
}
#[inline(always)]
pub fn is_in_range(value: Float) -> bool {
(0.0..=1.0).contains(&value)
}
#[inline(always)]
pub fn value_float(&self) -> Float {
self.value as Float * MULTIPLIER_TO_FLOAT
}
#[inline(always)]
pub fn value_short(&self) -> UShort {
self.value
}
pub fn set_value(&mut self, value: Float) -> Result<(), ShortFloatError> {
self.value = Self::float_to_short_value(value)?;
Ok(())
}
#[inline(always)]
pub fn set_value_unchecked(&mut self, value: Float) {
self.value = Self::float_to_short_value_unchecked(value)
}
pub fn float_to_short_value(value: Float) -> Result<UShort, ShortFloatError> {
match Self::is_in_range(value) {
true => Ok(Self::float_to_short_value_unchecked(value)),
false => Err(ShortFloatError::OutOfRange(value)),
}
}
pub fn float_to_short_value_unchecked(value: Float) -> UShort {
(value * MULTIPLIER_TO_UINT).round() as UShort
}
#[inline(always)]
pub fn is_valid_short(short: UShort) -> bool {
short <= SHORT_MAX
}
#[inline(always)]
pub fn is_valid(&self) -> bool {
Self::is_valid_short(self.value)
}
pub fn check_valid(&self) -> Result<(), ShortFloatError> {
match self.is_valid() {
true => Ok(()),
false => Err(ShortFloatError::OutOfRange(self.value_float())),
}
}
pub fn validate(self) -> Result<Self, ShortFloatError> {
match self.is_valid() {
true => Ok(self),
false => Err(ShortFloatError::OutOfRange(self.value_float())),
}
}
#[inline(always)]
pub fn from_float(value: Float) -> Self {
Self::try_from(value).unwrap()
}
#[inline(always)]
pub fn to_float(&self) -> Float {
self.value_float()
}
pub fn set(&mut self, new_value: Self) {
self.value = new_value.value;
}
pub fn is_zero(&self) -> bool {
self == &Self::ZERO
}
pub fn is_one(&self) -> bool {
self == &Self::ONE
}
pub fn is_half(&self) -> bool {
self == &Self::HALF
}
pub fn value(&self) -> Float {
self.to_float()
}
}
impl ToDisplayAndBrief for ShortFloat {
fn to_display(&self) -> String {
match self.value {
SHORT_MAX => "1.0000".to_string(),
value => format!("0.{value:0>4}"),
}
}
fn to_display_brief(&self) -> String {
match self.value + 50 {
v if v >= SHORT_MAX => "1.00".to_string(),
value => {
let s = (value / 100).to_string();
format!("0.{s:0>2}")
}
}
}
}
impl_display_from_to_display! { ShortFloat }
impl TryFrom<Float> for ShortFloat {
type Error = ShortFloatError;
#[inline]
fn try_from(value: Float) -> Result<Self, Self::Error> {
Ok(Self::new_unchecked(Self::float_to_short_value(value)?))
}
}
impl Add for ShortFloat {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::new(self.value + rhs.value).unwrap()
}
}
impl Sub for ShortFloat {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::new_unchecked(self.value - rhs.value)
}
}
impl Mul for ShortFloat {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self::new_unchecked(mul_div(self.value, rhs.value))
}
}
fn mul_div(x: UShort, y: UShort) -> UShort {
(x * y) / SHORT_MAX
}
impl Div for ShortFloat {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self::new((self.value * SHORT_MAX) / rhs.value).unwrap()
}
}
impl Mul<usize> for ShortFloat {
type Output = Self;
fn mul(self, rhs: usize) -> Self::Output {
Self::from_float(self.to_float().mul(rhs as Float))
}
}
impl Div<usize> for ShortFloat {
type Output = Self;
fn div(self, rhs: usize) -> Self::Output {
Self::from_float(self.to_float().div(rhs as Float))
}
}
impl EvidentNumber for ShortFloat {
#[inline(always)]
fn zero() -> Self {
Self::ZERO
}
#[inline(always)]
fn one() -> Self {
Self::ONE
}
fn root(self, n: usize) -> Self {
self.value_float()
.powf(1.0 / (n as Float))
.try_into()
.unwrap()
}
}
impl Not for ShortFloat {
type Output = Self;
fn not(self) -> Self::Output {
Self::ONE - self
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl BitAnd for ShortFloat {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
self * rhs
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl BitOr for ShortFloat {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self::new_unchecked(self.value + rhs.value - ((self.value * rhs.value) / SHORT_MAX))
}
}
#[macro_export]
macro_rules! short_float {
($float:expr) => {
ShortFloat::from_float($float)
};
(str? $float:expr) => {
$s.parse::<$crate::global::Float>()
.map($crate::entity::ShortFloat::try_from)
};
(str $s:expr) => {
$crate::entity::ShortFloat::try_from($s.parse::<$crate::global::Float>().unwrap()).unwrap()
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ok, util::AResult};
use nar_dev_utils::macro_once;
type SF = ShortFloat;
const DEFAULT_EPSILON: Float = 1.0E-6;
macro_rules! assert_approx_eq {
($epsilon:expr; $v1:expr, $v2:expr) => {
assert!(
($v1 - $v2).abs() < $epsilon,
"{} !≈ {} @ {}",
$v1,
$v2,
$epsilon
)
};
($v1:expr, $v2:expr) => {
assert_approx_eq!(DEFAULT_EPSILON; $v1, $v2)
};
}
#[test]
fn new() -> AResult {
macro_once! {
macro test($( $short:expr )*) {
$(
let _ = SF::new($short);
)*
}
0
10000
90
9000
1024
8192
}
ok!()
}
#[test]
fn value() -> AResult {
macro_once! {
macro test($( $short:expr => $expected:expr )*) {
$(
let sf = SF::new_unchecked($short);
assert_approx_eq!(sf.value_float(), $expected);
)*
}
0 => 0.0
10000 => 1.0
90 => 0.009
9000 => 0.9
1024 => 0.1024
8192 => 0.8192
}
ok!()
}
#[test]
fn set_value() -> AResult {
use ShortFloatError::*;
macro_once! {
macro test($( $short:literal -> $float:expr => $expected:literal @ $pattern:pat)*) {
$(
let mut sf = SF::new_unchecked($short);
let result = sf.set_value($float);
assert_eq!(sf.value, $expected);
assert!(matches!(result, $pattern));
)*
}
0 -> 0.0 => 0 @ Ok(..)
0 -> 1.0 => 10000 @ Ok(..)
0 -> 0.009 => 90 @ Ok(..)
0 -> 0.9 => 9000 @ Ok(..)
0 -> 0.1024 => 1024 @ Ok(..)
0 -> 0.8192 => 8192 @ Ok(..)
0 -> 0.00001 => 0 @ Ok(..)
0 -> 0.00002 => 0 @ Ok(..)
0 -> 0.00003 => 0 @ Ok(..)
0 -> 0.00004 => 0 @ Ok(..)
0 -> 0.00005 => 1 @ Ok(..)
0 -> 0.00006 => 1 @ Ok(..)
0 -> 0.00007 => 1 @ Ok(..)
0 -> 0.00008 => 1 @ Ok(..)
0 -> 0.00009 => 1 @ Ok(..)
0 -> -0.1 => 0 @ Err(OutOfRange(..))
10000 -> 2.0 => 10000 @ Err(OutOfRange(..))
10000 -> Float::INFINITY => 10000 @ Err(OutOfRange(..))
0 -> Float::NEG_INFINITY => 0 @ Err(OutOfRange(..))
10000 -> Float::NAN => 10000 @ Err(..)
}
ok!()
}
#[test]
fn set_value_unchecked() -> AResult {
macro_once! {
macro test($( $short:literal -> $float:expr => $expected:expr)*) {
$(
let mut sf = SF::new_unchecked($short);
sf.set_value_unchecked($float);
assert_eq!(sf.value, $expected, "设置值`{sf:?} -> {}`不符预期`{}`", $float, $expected);
)*
}
0 -> 1.0001 => 10001
0 -> 2.0 => 20000
0 -> 6.5535 => 65535
0 -> -0.1 => 0
0 -> -2.0 => 0
0 -> 1.00001 => 10000
0 -> 1.00002 => 10000
0 -> 1.00003 => 10000
0 -> 1.00004 => 10000
0 -> 1.00005 => 10001
0 -> 1.00006 => 10001
0 -> 1.00007 => 10001
0 -> 1.00008 => 10001
0 -> 1.00009 => 10001
0 -> Float::INFINITY => UShort::MAX
10000 -> Float::NEG_INFINITY => 0
10000 -> Float::NAN => 0
}
ok!()
}
#[test]
fn fmt() -> AResult {
macro_once! {
macro test($( $short:expr => $expected:expr)*) {
$(
let mut sf = SF::new_unchecked($short);
let formatted = format!("{sf}");
assert_eq!(formatted, $expected);
)*
}
10000 => "1.0000"
1024 => "0.1024"
8192 => "0.8192"
0 => "0.0000"
90 => "0.0090"
900 => "0.0900"
}
ok!()
}
#[test]
fn try_from() -> AResult {
use ShortFloatError::*;
macro_once! {
macro test($( $float:expr => $pattern:pat)*) {
$(
let mut result: ShortFloatResult = $float.try_into();
assert!(matches!(result, $pattern));
)*
}
0.0 => Ok(SF {value: 0})
1.0 => Ok(SF {value: 10000})
0.009 => Ok(SF {value: 90})
0.9 => Ok(SF {value: 9000})
0.1024 => Ok(SF {value: 1024})
0.8192 => Ok(SF {value: 8192})
0.00001 => Ok(SF {value: 0})
0.00002 => Ok(SF {value: 0})
0.00003 => Ok(SF {value: 0})
0.00004 => Ok(SF {value: 0})
0.00005 => Ok(SF {value: 1})
0.00006 => Ok(SF {value: 1})
0.00007 => Ok(SF {value: 1})
0.00008 => Ok(SF {value: 1})
0.00009 => Ok(SF {value: 1})
-0.1 => Err(OutOfRange(..))
2.0 => Err(OutOfRange(..))
Float::INFINITY => Err(OutOfRange(..))
Float::NEG_INFINITY => Err(OutOfRange(..))
Float::NAN => Err(..)
}
ok!()
}
#[test]
fn check_valid() -> AResult {
use ShortFloatError::*;
macro_once! {
macro test($( $short:expr => $pattern:pat)*) {
$(
let sf = SF::new_unchecked($short);
assert!(matches!(sf.check_valid(), $pattern));
)*
}
0 => Ok(..)
10000 => Ok(..)
90 => Ok(..)
900 => Ok(..)
9000 => Ok(..)
1024 => Ok(..)
8192 => Ok(..)
10001 => Err(OutOfRange(..))
20000 => Err(OutOfRange(..))
65535 => Err(OutOfRange(..))
}
ok!()
}
macro_rules! sf {
($short:expr) => {
SF::new_unchecked($short)
};
}
#[test]
fn add() -> AResult {
for a in 0..=SHORT_MAX {
for b in 0..=(SHORT_MAX - a) {
assert_eq!(sf!(a) + sf!(b), sf!(a + b))
}
}
ok!()
}
#[test]
fn sub() -> AResult {
for a in 0..=SHORT_MAX {
for b in 0..=a {
assert_eq!(sf!(a) - sf!(b), sf!(a - b))
}
}
ok!()
}
#[test]
fn mul() -> AResult {
assert_eq!(sf!(0) * sf!(0), sf!(0));
assert_eq!(sf!(0) * sf!(SHORT_MAX), sf!(0));
assert_eq!(sf!(SHORT_MAX) * sf!(SHORT_MAX), sf!(SHORT_MAX));
assert_eq!(sf!(7) * sf!(9363), sf!(6)); for a in 0..=SHORT_MAX {
for b in 0..=SHORT_MAX {
assert_eq!(sf!(a) * sf!(b), sf!(a * b / SHORT_MAX))
}
}
ok!()
}
#[test]
fn div() -> AResult {
for a in 1..=SHORT_MAX {
for b in a..=SHORT_MAX {
assert_eq!(sf!(a) / sf!(b), sf!((a * SHORT_MAX) / b))
}
}
ok!()
}
#[test]
fn to_display() -> AResult {
macro_once! {
macro test($( $value:tt => $expected:tt)*) {
$(
assert_eq!(
SF::from_float($value).to_display(),
$expected
);
)*
}
0.0 => "0.0000"
1.0 => "1.0000"
0.9 => "0.9000"
0.1 => "0.1000"
0.42 => "0.4200"
0.137 => "0.1370"
0.442 => "0.4420"
0.1024 => "0.1024"
0.2185 => "0.2185"
}
ok!()
}
#[test]
fn to_display_brief() -> AResult {
macro_once! {
macro test($( $value:tt => $expected:tt)*) {
$(
assert_eq!(
SF::from_float($value).to_display_brief(),
$expected
);
)*
}
0.0 => "0.00"
1.0 => "1.00"
0.9 => "0.90"
0.1 => "0.10"
0.42 => "0.42"
0.137 => "0.14" 0.442 => "0.44" 0.1024 => "0.10" 0.2185 => "0.22" 0.999 => "1.00" 0.9999 => "1.00" }
ok!()
}
}