use core::cmp::Ordering;
use core::fmt::Display;
use core::ops::{Add, Sub};
use core::str::FromStr;
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub trait Antifragile {
type Stressor: Copy + Add<Output = Self::Stressor> + Sub<Output = Self::Stressor>;
type Payoff: Copy + Add<Output = Self::Payoff> + PartialOrd;
fn payoff(&self, stressor: Self::Stressor) -> Self::Payoff;
fn twin(r: Self::Payoff) -> Self::Payoff {
r + r
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
#[must_use]
pub enum Triad {
Fragile,
Robust,
Antifragile,
}
impl Triad {
pub const ALL: [Self; 3] = [Self::Fragile, Self::Robust, Self::Antifragile];
#[inline]
pub fn iter() -> impl Iterator<Item = Self> {
Self::ALL.into_iter()
}
#[inline]
#[must_use]
pub const fn rank(self) -> u8 {
self as u8
}
#[inline]
#[must_use]
pub const fn is_antifragile(self) -> bool {
matches!(self, Triad::Antifragile)
}
#[inline]
#[must_use]
pub const fn is_fragile(self) -> bool {
matches!(self, Triad::Fragile)
}
#[inline]
#[must_use]
pub const fn is_robust(self) -> bool {
matches!(self, Triad::Robust)
}
#[inline]
pub const fn opposite(self) -> Self {
match self {
Self::Antifragile => Self::Fragile,
Self::Fragile => Self::Antifragile,
Self::Robust => Self::Robust,
}
}
}
impl PartialOrd for Triad {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Triad {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.rank().cmp(&other.rank())
}
}
impl Default for Triad {
#[inline]
fn default() -> Self {
Self::Robust
}
}
impl From<Triad> for u8 {
#[inline]
fn from(triad: Triad) -> Self {
triad.rank()
}
}
impl From<Triad> for &'static str {
#[inline]
fn from(triad: Triad) -> Self {
match triad {
Triad::Antifragile => "antifragile",
Triad::Fragile => "fragile",
Triad::Robust => "robust",
}
}
}
impl Display for Triad {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Antifragile => write!(f, "Antifragile (benefits from volatility)"),
Self::Fragile => write!(f, "Fragile (harmed by volatility)"),
Self::Robust => write!(f, "Robust (unaffected by volatility)"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidTriadValue(pub u8);
impl Display for InvalidTriadValue {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "invalid triad value: {} (expected 0, 1, or 2)", self.0)
}
}
#[cfg(feature = "std")]
impl Error for InvalidTriadValue {}
impl TryFrom<u8> for Triad {
type Error = InvalidTriadValue;
#[inline]
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Fragile),
1 => Ok(Self::Robust),
2 => Ok(Self::Antifragile),
n => Err(InvalidTriadValue(n)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParseTriadError;
impl Display for ParseTriadError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"invalid triad string (expected \"antifragile\", \"fragile\", or \"robust\")"
)
}
}
#[cfg(feature = "std")]
impl Error for ParseTriadError {}
impl FromStr for Triad {
type Err = ParseTriadError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.eq_ignore_ascii_case("antifragile") {
Ok(Self::Antifragile)
} else if s.eq_ignore_ascii_case("fragile") {
Ok(Self::Fragile)
} else if s.eq_ignore_ascii_case("robust") {
Ok(Self::Robust)
} else {
Err(ParseTriadError)
}
}
}
pub trait TriadAnalysis: Antifragile {
#[inline]
fn classify(&self, at: Self::Stressor, delta: Self::Stressor) -> Triad
where
Self::Payoff: Sub<Output = Self::Payoff> + Default + PartialOrd,
{
let f_x = self.payoff(at);
let f_x_plus = self.payoff(at + delta);
let f_x_minus = self.payoff(at - delta);
let sum = f_x_plus + f_x_minus;
let twin_f_x = Self::twin(f_x);
if sum > twin_f_x {
Triad::Antifragile
} else if sum < twin_f_x {
Triad::Fragile
} else {
Triad::Robust
}
}
#[inline]
fn classify_with_tolerance(
&self,
at: Self::Stressor,
delta: Self::Stressor,
epsilon: Self::Payoff,
) -> Triad
where
Self::Payoff: Sub<Output = Self::Payoff> + Default + PartialOrd,
{
let f_x = self.payoff(at);
let f_x_plus = self.payoff(at + delta);
let f_x_minus = self.payoff(at - delta);
let sum = f_x_plus + f_x_minus;
let twin_f_x = Self::twin(f_x);
let diff = if sum >= twin_f_x {
sum - twin_f_x
} else {
twin_f_x - sum
};
if diff <= epsilon {
Triad::Robust
} else if sum > twin_f_x {
Triad::Antifragile
} else {
Triad::Fragile
}
}
#[inline]
#[must_use]
fn is_antifragile(&self, at: Self::Stressor, delta: Self::Stressor) -> bool
where
Self::Payoff: Sub<Output = Self::Payoff> + Default + PartialOrd,
{
self.classify(at, delta) == Triad::Antifragile
}
#[inline]
#[must_use]
fn gains_from_stress(&self, low: Self::Stressor, high: Self::Stressor) -> bool {
self.payoff(high) > self.payoff(low)
}
#[inline]
#[must_use]
fn is_stable(&self, low: Self::Stressor, high: Self::Stressor, threshold: Self::Payoff) -> bool
where
Self::Payoff: Sub<Output = Self::Payoff>,
{
let payoff_low = self.payoff(low);
let payoff_high = self.payoff(high);
if payoff_high >= payoff_low {
payoff_high - payoff_low <= threshold
} else {
payoff_low - payoff_high <= threshold
}
}
}
impl<T: Antifragile> TriadAnalysis for T {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Verified<T> {
inner: T,
classification: Triad,
}
impl<T: Antifragile> Verified<T>
where
T::Payoff: Sub<Output = T::Payoff> + Default + PartialOrd,
{
#[must_use]
pub fn check(system: T, at: T::Stressor, delta: T::Stressor) -> Self {
let classification = system.classify(at, delta);
Self {
inner: system,
classification,
}
}
#[inline]
pub const fn classification(&self) -> Triad {
self.classification
}
#[inline]
#[must_use]
pub const fn inner(&self) -> &T {
&self.inner
}
#[inline]
#[must_use]
pub fn into_inner(self) -> T {
self.inner
}
#[inline]
#[must_use]
pub const fn is_antifragile(&self) -> bool {
self.classification.is_antifragile()
}
#[inline]
#[must_use]
pub const fn is_fragile(&self) -> bool {
self.classification.is_fragile()
}
#[inline]
#[must_use]
pub const fn is_robust(&self) -> bool {
self.classification.is_robust()
}
#[inline]
pub fn re_verify(&mut self, at: T::Stressor, delta: T::Stressor) {
self.classification = self.inner.classify(at, delta);
}
#[inline]
#[must_use]
pub fn still_holds(&self, at: T::Stressor, delta: T::Stressor) -> bool {
self.inner.classify(at, delta) == self.classification
}
}
impl<T> AsRef<T> for Verified<T> {
#[inline]
fn as_ref(&self) -> &T {
&self.inner
}
}
impl<T> core::ops::Deref for Verified<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: Antifragile + Default> Default for Verified<T>
where
T::Stressor: Default,
T::Payoff: Sub<Output = T::Payoff> + Default + PartialOrd,
{
fn default() -> Self {
let system = T::default();
let at = T::Stressor::default();
Self::check(system, at, at)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct ConvexFn; struct ConcaveFn; struct LinearFn {
slope: f64,
intercept: f64,
}
impl Antifragile for ConvexFn {
type Stressor = f64;
type Payoff = f64;
fn payoff(&self, x: Self::Stressor) -> Self::Payoff {
x * x
}
}
impl Antifragile for ConcaveFn {
type Stressor = f64;
type Payoff = f64;
fn payoff(&self, x: Self::Stressor) -> Self::Payoff {
x.abs().sqrt()
}
}
impl Antifragile for LinearFn {
type Stressor = f64;
type Payoff = f64;
fn payoff(&self, x: Self::Stressor) -> Self::Payoff {
self.slope * x + self.intercept
}
}
#[test]
fn test_convex_is_antifragile() {
let system = ConvexFn;
assert!(system.is_antifragile(10.0, 1.0));
assert_eq!(system.classify(10.0, 1.0), Triad::Antifragile);
}
#[test]
fn test_concave_is_fragile() {
let system = ConcaveFn;
assert_eq!(system.classify(10.0, 1.0), Triad::Fragile);
}
#[test]
fn test_linear_is_robust() {
let system = LinearFn {
slope: 2.0,
intercept: 5.0,
};
assert_eq!(system.classify(10.0, 1.0), Triad::Robust);
}
#[test]
fn test_gains_from_stress() {
let convex = ConvexFn;
assert!(convex.gains_from_stress(1.0, 2.0));
let concave = ConcaveFn;
assert!(concave.gains_from_stress(1.0, 4.0)); }
#[test]
fn test_verified_wrapper() {
let system = ConvexFn;
let verified = Verified::check(system, 10.0, 1.0);
assert_eq!(verified.classification(), Triad::Antifragile);
}
#[test]
fn test_triad_display() {
assert_eq!(
format!("{}", Triad::Antifragile),
"Antifragile (benefits from volatility)"
);
assert_eq!(
format!("{}", Triad::Fragile),
"Fragile (harmed by volatility)"
);
assert_eq!(
format!("{}", Triad::Robust),
"Robust (unaffected by volatility)"
);
}
#[test]
fn test_triad_ordering() {
assert!(Triad::Fragile < Triad::Robust);
assert!(Triad::Robust < Triad::Antifragile);
assert!(Triad::Fragile < Triad::Antifragile);
assert_eq!(Triad::Fragile.rank(), 0);
assert_eq!(Triad::Robust.rank(), 1);
assert_eq!(Triad::Antifragile.rank(), 2);
let mut triads = vec![Triad::Antifragile, Triad::Fragile, Triad::Robust];
triads.sort();
assert_eq!(
triads,
vec![Triad::Fragile, Triad::Robust, Triad::Antifragile]
);
}
#[test]
fn test_triad_predicates() {
assert!(Triad::Antifragile.is_antifragile());
assert!(!Triad::Antifragile.is_fragile());
assert!(!Triad::Antifragile.is_robust());
assert!(Triad::Fragile.is_fragile());
assert!(!Triad::Fragile.is_antifragile());
assert!(!Triad::Fragile.is_robust());
assert!(Triad::Robust.is_robust());
assert!(!Triad::Robust.is_antifragile());
assert!(!Triad::Robust.is_fragile());
}
#[test]
fn test_verified_predicates() {
let system = ConvexFn;
let verified = Verified::check(system, 10.0, 1.0);
assert!(verified.is_antifragile());
assert!(!verified.is_fragile());
assert!(!verified.is_robust());
}
#[test]
fn test_triad_default() {
assert_eq!(Triad::default(), Triad::Robust);
}
#[test]
fn test_triad_from_u8() {
assert_eq!(Triad::try_from(0_u8), Ok(Triad::Fragile));
assert_eq!(Triad::try_from(1_u8), Ok(Triad::Robust));
assert_eq!(Triad::try_from(2_u8), Ok(Triad::Antifragile));
assert_eq!(Triad::try_from(3_u8), Err(InvalidTriadValue(3)));
assert_eq!(Triad::try_from(255_u8), Err(InvalidTriadValue(255)));
}
#[test]
fn test_triad_into_u8() {
assert_eq!(u8::from(Triad::Fragile), 0);
assert_eq!(u8::from(Triad::Robust), 1);
assert_eq!(u8::from(Triad::Antifragile), 2);
}
#[test]
fn test_triad_into_str() {
assert_eq!(<&str>::from(Triad::Antifragile), "antifragile");
assert_eq!(<&str>::from(Triad::Fragile), "fragile");
assert_eq!(<&str>::from(Triad::Robust), "robust");
}
#[test]
fn test_verified_deref() {
let system = ConvexFn;
let verified = Verified::check(system, 10.0, 1.0);
assert!((verified.payoff(5.0) - 25.0).abs() < f64::EPSILON);
}
#[test]
fn test_invalid_triad_value_display() {
let err = InvalidTriadValue(42);
assert_eq!(
format!("{err}"),
"invalid triad value: 42 (expected 0, 1, or 2)"
);
}
#[test]
fn test_triad_from_str() {
assert_eq!("antifragile".parse::<Triad>(), Ok(Triad::Antifragile));
assert_eq!("Antifragile".parse::<Triad>(), Ok(Triad::Antifragile));
assert_eq!("ANTIFRAGILE".parse::<Triad>(), Ok(Triad::Antifragile));
assert_eq!("fragile".parse::<Triad>(), Ok(Triad::Fragile));
assert_eq!("Fragile".parse::<Triad>(), Ok(Triad::Fragile));
assert_eq!("robust".parse::<Triad>(), Ok(Triad::Robust));
assert_eq!("ROBUST".parse::<Triad>(), Ok(Triad::Robust));
assert_eq!("invalid".parse::<Triad>(), Err(ParseTriadError));
assert_eq!("".parse::<Triad>(), Err(ParseTriadError));
}
#[test]
fn test_parse_triad_error_display() {
let err = ParseTriadError;
assert_eq!(
format!("{err}"),
"invalid triad string (expected \"antifragile\", \"fragile\", or \"robust\")"
);
}
#[test]
fn test_classify_at_zero() {
let system = ConvexFn;
let _ = system.classify(0.0, 0.1);
}
#[test]
fn test_classify_with_zero_delta() {
let system = ConvexFn;
assert_eq!(system.classify(10.0, 0.0), Triad::Robust);
}
#[test]
fn test_classify_negative_stressor() {
let system = ConvexFn;
assert_eq!(system.classify(-10.0, 1.0), Triad::Antifragile);
}
#[test]
fn test_triad_opposite() {
assert_eq!(Triad::Antifragile.opposite(), Triad::Fragile);
assert_eq!(Triad::Fragile.opposite(), Triad::Antifragile);
assert_eq!(Triad::Robust.opposite(), Triad::Robust);
assert_eq!(Triad::Antifragile.opposite().opposite(), Triad::Antifragile);
}
#[test]
fn test_triad_iter() {
let all: Vec<_> = Triad::iter().collect();
assert_eq!(all, vec![Triad::Fragile, Triad::Robust, Triad::Antifragile]);
assert_eq!(Triad::ALL.len(), 3);
}
#[test]
fn test_classify_with_tolerance_returns_antifragile() {
let system = ConvexFn;
let result = system.classify_with_tolerance(10.0, 1.0, 1e-10);
assert_eq!(result, Triad::Antifragile);
}
#[test]
fn test_classify_with_tolerance_returns_fragile() {
let system = ConcaveFn;
let result = system.classify_with_tolerance(10.0, 1.0, 1e-10);
assert_eq!(result, Triad::Fragile);
}
#[test]
fn test_classify_with_tolerance_boundary() {
let convex = ConvexFn;
assert_eq!(
convex.classify_with_tolerance(10.0, 1.0, 1.0),
Triad::Antifragile
);
assert_eq!(
convex.classify_with_tolerance(10.0, 1.0, 2.0),
Triad::Robust
);
assert_eq!(
convex.classify_with_tolerance(10.0, 1.0, 3.0),
Triad::Robust
);
}
#[test]
fn test_classify_with_tolerance_fragile_boundary() {
let concave = ConcaveFn;
assert_eq!(
concave.classify_with_tolerance(10.0, 1.0, 1e-10),
Triad::Fragile
);
assert_eq!(
concave.classify_with_tolerance(10.0, 1.0, 10.0),
Triad::Robust
);
}
#[test]
fn test_is_antifragile_returns_false() {
let linear = LinearFn {
slope: 1.0,
intercept: 0.0,
};
assert!(!linear.is_antifragile(10.0, 1.0));
let concave = ConcaveFn;
assert!(!concave.is_antifragile(10.0, 1.0));
}
#[test]
fn test_gains_from_stress_returns_false() {
struct DecreasingSystem;
impl Antifragile for DecreasingSystem {
type Stressor = f64;
type Payoff = f64;
fn payoff(&self, x: Self::Stressor) -> Self::Payoff {
-x }
}
let system = DecreasingSystem;
assert!(!system.gains_from_stress(1.0, 2.0)); }
#[test]
fn test_gains_from_stress_boundary() {
struct ConstantSystem;
impl Antifragile for ConstantSystem {
type Stressor = f64;
type Payoff = f64;
fn payoff(&self, _x: Self::Stressor) -> Self::Payoff {
5.0
}
}
let system = ConstantSystem;
assert!(!system.gains_from_stress(1.0, 2.0)); }
#[test]
fn test_is_stable_returns_false() {
let convex = ConvexFn;
assert!(!convex.is_stable(1.0, 10.0, 1.0));
}
#[test]
fn test_is_stable_boundary_conditions() {
struct KnownSystem;
impl Antifragile for KnownSystem {
type Stressor = f64;
type Payoff = f64;
fn payoff(&self, x: Self::Stressor) -> Self::Payoff {
x * 2.0 }
}
let system = KnownSystem;
assert!(system.is_stable(5.0, 10.0, 10.0));
assert!(!system.is_stable(5.0, 10.0, 9.0));
assert!(system.is_stable(10.0, 5.0, 10.0));
assert!(!system.is_stable(10.0, 5.0, 9.0));
}
#[test]
fn test_verified_is_fragile_returns_true() {
let concave = ConcaveFn;
let verified = Verified::check(concave, 10.0, 1.0);
assert!(verified.is_fragile());
assert!(!verified.is_antifragile());
assert!(!verified.is_robust());
}
#[test]
fn test_verified_is_robust_returns_true() {
let linear = LinearFn {
slope: 2.0,
intercept: 5.0,
};
let verified = Verified::check(linear, 10.0, 1.0);
assert!(verified.is_robust());
assert!(!verified.is_antifragile());
assert!(!verified.is_fragile());
}
#[test]
fn test_verified_is_antifragile_returns_false() {
let concave = ConcaveFn;
let verified = Verified::check(concave, 10.0, 1.0);
assert!(!verified.is_antifragile());
let linear = LinearFn {
slope: 1.0,
intercept: 0.0,
};
let verified = Verified::check(linear, 10.0, 1.0);
assert!(!verified.is_antifragile());
}
#[test]
fn test_verified_re_verify_changes_classification() {
struct VariableSystem;
impl Antifragile for VariableSystem {
type Stressor = f64;
type Payoff = f64;
fn payoff(&self, x: Self::Stressor) -> Self::Payoff {
if x > 0.0 {
x * x } else {
x.abs().sqrt() }
}
}
let system = VariableSystem;
let mut verified = Verified::check(system, 10.0, 1.0);
assert_eq!(verified.classification(), Triad::Antifragile);
verified.re_verify(10.0, 0.0);
assert_eq!(verified.classification(), Triad::Robust);
}
#[test]
fn test_verified_still_holds_returns_false() {
let convex = ConvexFn;
let verified = Verified::check(convex, 10.0, 1.0);
assert_eq!(verified.classification(), Triad::Antifragile);
assert!(!verified.still_holds(10.0, 0.0));
}
#[test]
fn test_verified_still_holds_returns_true() {
let convex = ConvexFn;
let verified = Verified::check(convex, 10.0, 1.0);
assert!(verified.still_holds(5.0, 1.0));
assert!(verified.still_holds(20.0, 2.0));
}
#[test]
fn test_classify_with_tolerance_exact_boundary() {
let linear = LinearFn {
slope: 2.0,
intercept: 5.0,
};
let result = linear.classify_with_tolerance(10.0, 1.0, -1.0);
assert_eq!(result, Triad::Fragile);
}
}