104 lines
3.6 KiB
Rust
104 lines
3.6 KiB
Rust
//! Exact numeric foundation for analysis; no model, expression, or package resolver.
|
|
//!
|
|
//! Values use reduced rational coordinates. All nontrivial arithmetic takes one
|
|
//! request-local [WorkMeter]. The future evaluator must also charge *expanded*
|
|
//! expression/call/property/list/target visits and retain the same logical walk on
|
|
//! cache hits. Stored AST size is not a dynamic execution limit.
|
|
//!
|
|
//! Approximate engineering functions/irrational unit scales require a subsequent
|
|
//! explicit numeric profile; rationals do not implement degree/radian conversion
|
|
//! or transcendental mathematics.
|
|
|
|
mod dimension;
|
|
mod exact;
|
|
mod meter;
|
|
mod quantity;
|
|
|
|
pub use dimension::{Dimension, DimensionAxis};
|
|
pub use exact::{Exact, ExactWire};
|
|
pub use meter::{
|
|
EvaluationStep, NumericOperation, WorkExhaustion, WorkLimits, WorkMeter, WorkResource,
|
|
WorkSite, WorkUsage,
|
|
};
|
|
pub use quantity::{NumericType, NumericValue, ResolvedKind, Unit};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use thiserror::Error;
|
|
|
|
/// Profile identity includes the accounting rules, not crate-internal timing.
|
|
pub const WORK_PROFILE: &str = "Q1-logical-work-1";
|
|
pub const MAX_COMPONENT_BITS: u64 = 4096;
|
|
/// Cross products require 8192 bits; their sum can require one additional bit.
|
|
pub const MAX_INTERMEDIATE_BITS: u64 = 8193;
|
|
pub const MAX_COMPONENT_DIGITS: usize = 1234;
|
|
pub const MAX_DECIMAL_EXPONENT: i32 = 128;
|
|
pub const MAX_INTEGER_POWER: i8 = 16;
|
|
/// Includes ratio punctuation/whitespace without permitting unbounded scanning.
|
|
pub const MAX_LITERAL_BYTES: usize = 2 * MAX_COMPONENT_DIGITS + 32;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum NumberComponent {
|
|
Numerator,
|
|
Denominator,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
|
|
pub enum NumericLimit {
|
|
#[error("request work exhausted: {0}")]
|
|
Work(WorkExhaustion),
|
|
#[error("{component:?} uses {bits} bits, above the {limit}-bit {stage} limit")]
|
|
Bits {
|
|
component: NumberComponent,
|
|
bits: u64,
|
|
limit: u64,
|
|
stage: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
|
|
pub enum NumericError {
|
|
#[error("numeric limit: {0}")]
|
|
Limit(NumericLimit),
|
|
#[error("invalid numeric literal")]
|
|
InvalidLiteral,
|
|
#[error("numeric literal exceeds its digit or byte limit")]
|
|
LiteralTooLong,
|
|
#[error("decimal exponent is outside -128 through 128")]
|
|
DecimalExponent,
|
|
#[error("wire rational is not in canonical reduced form")]
|
|
NonCanonicalWire,
|
|
#[error("rational denominator is zero")]
|
|
ZeroDenominator,
|
|
#[error("division by zero")]
|
|
DivisionByZero,
|
|
#[error("zero to the power zero is undefined in this profile")]
|
|
ZeroPowerZero,
|
|
#[error("integer power is outside -16 through 16")]
|
|
IntegerPower,
|
|
#[error("dimension exponent at {axis:?} is outside -32 through 32: {value}")]
|
|
DimensionExponent { axis: DimensionAxis, value: i32 },
|
|
#[error("unit scale must be strictly positive")]
|
|
UnitScale,
|
|
#[error("a linear unit must have zero offset")]
|
|
LinearOffset,
|
|
#[error("an affine point must have a distinct linear difference kind")]
|
|
DifferenceKind,
|
|
#[error("numeric kinds or dimensions are incompatible")]
|
|
IncompatibleTypes,
|
|
#[error("operation is not defined for affine points")]
|
|
AffineOperation,
|
|
#[error("AsKind requires an anonymous matching dimension and a linear target")]
|
|
KindAssertion,
|
|
#[error("request work limits exceed the provisional reference ceilings")]
|
|
InvalidWorkLimits,
|
|
}
|
|
|
|
impl NumericError {
|
|
pub fn is_terminal_work_exhaustion(&self) -> bool {
|
|
matches!(self, Self::Limit(NumericLimit::Work(_)))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|