use crate::fixed::NonNegativeFixed;
use crate::generated::consequence_mass::case_studies::N;
use crate::generated_profile::{
ALLOCATOR_BETA_MAX_BITS, ALLOCATOR_MASS_MAX_BITS, ALLOCATOR_MASS_MIN_BITS,
ALLOCATOR_PRICE_GAIN_MAX_BITS,
};
use super::StabilityRefusal;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct AllocatorRuntimeProfile {
pub identity: &'static str,
pub version: &'static str,
pub beta_max: NonNegativeFixed,
pub mass_min: NonNegativeFixed,
pub mass_max: NonNegativeFixed,
pub price_gain_max: NonNegativeFixed,
}
pub const BCINR_CMCA_ALLOCATOR_V0_1: AllocatorRuntimeProfile = AllocatorRuntimeProfile {
identity: "BCINR-CMCA-Allocator",
version: "v0.1",
beta_max: NonNegativeFixed::from_bits(ALLOCATOR_BETA_MAX_BITS),
mass_min: NonNegativeFixed::from_bits(ALLOCATOR_MASS_MIN_BITS),
mass_max: NonNegativeFixed::from_bits(ALLOCATOR_MASS_MAX_BITS),
price_gain_max: NonNegativeFixed::from_bits(ALLOCATOR_PRICE_GAIN_MAX_BITS),
};
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct FeasibleRegion {
pub(super) beta_max: NonNegativeFixed,
pub(super) m_min: NonNegativeFixed,
pub(super) m_max: NonNegativeFixed,
pub(super) mu_max: NonNegativeFixed,
}
impl FeasibleRegion {
pub const CURRENT: Self = Self {
beta_max: BCINR_CMCA_ALLOCATOR_V0_1.beta_max,
m_min: BCINR_CMCA_ALLOCATOR_V0_1.mass_min,
m_max: BCINR_CMCA_ALLOCATOR_V0_1.mass_max,
mu_max: BCINR_CMCA_ALLOCATOR_V0_1.price_gain_max,
};
pub const fn profile_identity(&self) -> &'static str {
BCINR_CMCA_ALLOCATOR_V0_1.identity
}
const SUM_TOLERANCE_BITS: u32 = NonNegativeFixed::ONE.val / 100;
pub fn admit_inputs(&self, mu: &[NonNegativeFixed; N]) -> Result<(), StabilityRefusal> {
for &m in mu {
if self.mu_max.val < m.val {
return Err(StabilityRefusal::PriceGainUnsafe);
}
}
Ok(())
}
pub fn contains_allocation(&self, allocation: &[NonNegativeFixed; N]) -> bool {
let mut sum: u64 = 0;
for &a in allocation {
if a.val > NonNegativeFixed::ONE.val {
return false;
}
sum += a.val as u64;
}
let target = NonNegativeFixed::ONE.val as u64;
sum.abs_diff(target) <= Self::SUM_TOLERANCE_BITS as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contains_allocation_true_for_a_normalized_distribution() {
let allocation: [NonNegativeFixed; N] =
core::array::from_fn(|_| NonNegativeFixed::from_bits(65536 / N as u32));
assert!(FeasibleRegion::CURRENT.contains_allocation(&allocation));
}
#[test]
fn contains_allocation_false_for_the_documented_degenerate_baseline() {
const CORRECT_MU_COST: [u32; N] = [4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096];
let allocation: [NonNegativeFixed; N] =
core::array::from_fn(|i| NonNegativeFixed::from_bits(CORRECT_MU_COST[i]));
assert!(!FeasibleRegion::CURRENT.contains_allocation(&allocation));
}
#[test]
fn admit_inputs_refuses_mu_strictly_above_mu_max() {
let mut mu = [NonNegativeFixed::ZERO; N];
mu[3] = NonNegativeFixed::from_bits(FeasibleRegion::CURRENT.mu_max.val + 1);
assert_eq!(
FeasibleRegion::CURRENT.admit_inputs(&mu),
Err(StabilityRefusal::PriceGainUnsafe)
);
}
#[test]
fn admit_inputs_admits_mu_at_or_below_mu_max() {
let mut mu = [NonNegativeFixed::ZERO; N];
mu[3] = FeasibleRegion::CURRENT.mu_max;
assert!(FeasibleRegion::CURRENT.admit_inputs(&mu).is_ok());
}
#[test]
fn current_profile_preserves_exact_q16_bits() {
assert_eq!(BCINR_CMCA_ALLOCATOR_V0_1.beta_max.to_bits(), 6553);
assert_eq!(BCINR_CMCA_ALLOCATOR_V0_1.mass_min.to_bits(), 6);
assert_eq!(BCINR_CMCA_ALLOCATOR_V0_1.mass_max.to_bits(), 65536000);
assert_eq!(BCINR_CMCA_ALLOCATOR_V0_1.price_gain_max.to_bits(), 6553600);
}
#[test]
fn feasible_region_current_matches_allocator_profile() {
assert_eq!(
FeasibleRegion::CURRENT.beta_max,
BCINR_CMCA_ALLOCATOR_V0_1.beta_max
);
assert_eq!(
FeasibleRegion::CURRENT.m_min,
BCINR_CMCA_ALLOCATOR_V0_1.mass_min
);
assert_eq!(
FeasibleRegion::CURRENT.m_max,
BCINR_CMCA_ALLOCATOR_V0_1.mass_max
);
assert_eq!(
FeasibleRegion::CURRENT.mu_max,
BCINR_CMCA_ALLOCATOR_V0_1.price_gain_max
);
}
#[test]
fn profile_identity_is_queryable() {
assert_eq!(
FeasibleRegion::CURRENT.profile_identity(),
"BCINR-CMCA-Allocator"
);
}
}