use verus_builtin_macros::verus;
#[allow(unused_imports)]
use verus_builtin::*;
#[allow(unused_imports)]
use vstd::prelude::*;
#[allow(unused_imports)]
use crate::model::*;
#[allow(unused_imports)]
use crate::round::*;
use crate::types::{Dir, MAX_DECIMAL_MANTISSA, MAX_DECIMAL_SCALE, Rat};
verus! {
#[verifier::external_body]
pub fn f64_decompose(v: f64) -> (r: Option<(bool, u64, i32)>)
ensures
r.is_some() ==> {
let t = r.unwrap();
&&& t.1 <= 9007199254740992u64 &&& -1074 <= t.2 <= 971
},
{
let bits: u64 = v.to_bits();
let neg: bool = (bits >> 63) != 0;
let raw_exp: u64 = (bits >> 52) & 0x7ff;
let frac: u64 = bits & 0x000f_ffff_ffff_ffff;
if raw_exp == 0x7ff {
return None;
}
if raw_exp == 0 {
Some((neg, frac, -1074))
} else {
Some((neg, frac + 0x0010_0000_0000_0000u64, raw_exp as i32 - 1075))
}
}
pub fn from_f64_dir(v: f64, dir: Dir) -> (r: Option<Rat>)
ensures
r.is_some() ==> r.unwrap().wf(),
{
match f64_decompose(v) {
None => None,
Some((neg, mant, e)) => from_parts_dir(neg, mant, e, dir),
}
}
pub open spec fn parts_num(neg: bool, mant: u64, e: i32) -> int {
let m = if neg {
-(mant as int)
} else {
mant as int
};
if e >= 0 {
m * pow2(e as nat)
} else {
m
}
}
pub open spec fn parts_den(e: i32) -> int {
if e >= 0 {
1int
} else {
pow2((-e) as nat)
}
}
pub fn from_parts_dir(neg: bool, mant: u64, e: i32, dir: Dir) -> (r: Option<Rat>)
requires
mant <= 9007199254740992u64,
-1074 <= e <= 971,
ensures
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> r.unwrap() == round_frac(parts_num(neg, mant, e), parts_den(e), dir),
r.is_some() ==> !saturated(parts_num(neg, mant, e), parts_den(e)),
(r.is_some() && dir == Dir::Down) ==> q_le_frac(
r.unwrap(),
parts_num(neg, mant, e),
parts_den(e),
),
(r.is_some() && dir == Dir::Up) ==> q_ge_frac(
r.unwrap(),
parts_num(neg, mant, e),
parts_den(e),
),
r.is_some() ==> within_error_bound(
r.unwrap(),
parts_num(neg, mant, e),
parts_den(e),
),
r.is_none() ==> abs_int(parts_num(neg, mant, e)) > pow2(61) * parts_den(e),
{
if mant > 9007199254740992u64 || e < -1074 || e > 971 {
return None;
}
if mant == 0 {
proof {
if e >= 0 {
assert(parts_num(neg, mant, e) == 0) by (nonlinear_arith)
requires
parts_num(neg, mant, e) == 0 * pow2(e as nat),
;
}
assert(parts_den(e) > 0) by {
lemma_pow2_pos((-e) as nat);
}
lemma_r2_r3_directed(parts_num(neg, mant, e), parts_den(e), dir);
assert(gcd_int(0, 1) == 1) by {
reveal_with_fuel(gcd_nat, 3);
}
Rat::lemma_from_raw_spec_wf(0, 1);
}
return Some(Rat::from_raw_parts(0, 1));
}
proof {
lemma_pow2_61();
lemma_pow2_62();
lemma_pow2_124();
lemma_pow2_126();
}
if e >= 0 {
proof {
lemma_pow2_pos(e as nat);
let m = if neg {
-(mant as int)
} else {
mant as int
};
lemma_abs_mul_pos(m, pow2(e as nat));
assert(abs_int(m) == mant as int);
assert(abs_int(parts_num(neg, mant, e)) == (mant as int) * pow2(e as nat));
}
if e > 64 {
proof {
lemma_pow2_mono(62nat, e as nat);
lemma_pow2_pos(61nat);
lemma_pow2_pos(e as nat);
assert(pow2(62) == 2 * pow2(61));
assert(abs_int(parts_num(neg, mant, e)) >= pow2(e as nat))
by (nonlinear_arith)
requires
abs_int(parts_num(neg, mant, e)) == (mant as int) * pow2(e as nat),
mant >= 1,
pow2(e as nat) > 0,
;
assert(parts_den(e) == 1);
assert(abs_int(parts_num(neg, mant, e)) > pow2(61) * parts_den(e))
by (nonlinear_arith)
requires
abs_int(parts_num(neg, mant, e)) >= pow2(e as nat),
pow2(62) <= pow2(e as nat),
pow2(62) == 2 * pow2(61),
pow2(61) > 0,
parts_den(e) == 1,
;
}
return None;
}
let p: i128 = pow2_i128(e as u32);
proof {
lemma_pow2_mono(e as nat, 64nat);
lemma_pow2_64();
assert((mant as int) * (p as int) <= 9007199254740992int
* 18446744073709551616int) by (nonlinear_arith)
requires
0 <= mant <= 9007199254740992int,
0 < p <= 18446744073709551616int,
;
}
let mag: i128 = (mant as i128) * p;
proof {
assert(abs_int(parts_num(neg, mant, e)) == mag as int) by (nonlinear_arith)
requires
abs_int(parts_num(neg, mant, e)) == (mant as int) * pow2(e as nat),
mag as int == (mant as int) * (p as int),
p as int == pow2(e as nat),
;
}
if mag > 2305843009213693952i128 {
return None;
}
let n: i128 = if neg {
0 - mag
} else {
mag
};
proof {
assert(pow2(0) == 1);
assert(-((mant as int) * pow2(e as nat)) == (-(mant as int)) * pow2(e as nat))
by (nonlinear_arith);
assert(n as int == parts_num(neg, mant, e));
assert(parts_den(e) == 1);
assert(!saturated(n as int, 1int)) by (nonlinear_arith)
requires
abs_int(n as int) <= pow2(61),
max_mag() == pow2(62) - 1,
pow2(62) == 2 * pow2(61),
pow2(61) > 0,
;
lemma_r2_r3_directed(n as int, 1int, dir);
}
Some(round_frac_exec(n, 1, dir))
} else if e >= -124 {
let s: u32 = (0 - e) as u32;
let d: i128 = pow2_i128(s);
let n: i128 = if neg {
0 - (mant as i128)
} else {
mant as i128
};
proof {
lemma_pow2_mono(s as nat, 124nat);
lemma_pow2_124();
lemma_pow2_126();
lemma_pow2_pos(s as nat);
assert(n as int == parts_num(neg, mant, e));
assert(d as int == parts_den(e));
assert(abs_int(n as int) <= max_mag() * (d as int)) by (nonlinear_arith)
requires
abs_int(n as int) <= 9007199254740992int,
max_mag() == pow2(62) - 1,
pow2(62) == 4611686018427387904int,
d as int >= 1,
;
lemma_r2_r3_directed(n as int, d as int, dir);
}
Some(round_frac_exec(n, d, dir))
} else {
proof {
let s = (-e) as nat;
let n = parts_num(neg, mant, e);
let d = parts_den(e);
assert(d == pow2(s));
lemma_pow2_pos(s);
lemma_pow2_mono(125nat, s);
lemma_pow2_125();
assert(abs_int(n) * pow2(62) <= 9007199254740992int
* 4611686018427387904int) by (nonlinear_arith)
requires
abs_int(n) <= 9007199254740992int,
pow2(62) == 4611686018427387904int,
;
assert(abs_int(n) * pow2(62) < d) by (nonlinear_arith)
requires
abs_int(n) * pow2(62) <= 9007199254740992int * 4611686018427387904int,
pow2(125) == 42535295865117307932921825928971026432int,
pow2(125) <= d,
;
assert(abs_int(n) >= 1);
assert(n != 0);
assert((n > 0) == !neg);
lemma_round_frac_subgrid(n, d, dir);
lemma_r2_r3_directed(n, d, dir);
}
Some(tiny(neg, dir))
}
}
pub fn tiny(neg: bool, dir: Dir) -> (r: Rat)
ensures
r.wf(),
r == crate::round::subgrid_endpoint(!neg, dir),
{
let eps_den: i64 = 2305843009213693952; proof {
lemma_max_mag_pow2();
lemma_pow2_61();
lemma_pow2_62();
crate::gcd::lemma_gcd_pos(1nat, 2305843009213693952nat);
crate::gcd::lemma_gcd_le(1nat, 2305843009213693952nat);
assert(gcd_int(0, 1) == 1) by {
reveal_with_fuel(gcd_nat, 3);
}
Rat::lemma_from_raw_spec_wf(0, 1);
Rat::lemma_from_raw_spec_wf((-1int) as i64, eps_den);
Rat::lemma_from_raw_spec_wf(1, eps_den);
}
match dir {
Dir::Nearest => Rat::from_raw_parts(0, 1),
Dir::Down => {
if neg {
Rat::from_raw_parts(-1, eps_den)
} else {
Rat::from_raw_parts(0, 1)
}
},
Dir::Up => {
if neg {
Rat::from_raw_parts(0, 1)
} else {
Rat::from_raw_parts(1, eps_den)
}
},
}
}
#[verifier::external_body]
pub fn to_f64(q: Rat) -> f64 {
(q.numerator() as f64) / (q.denominator() as f64)
}
pub fn pow10_i128(n: u32) -> (r: i128)
requires
n <= MAX_DECIMAL_SCALE,
ensures
1 <= r <= 10000000000000000000000000000i128,
r == pow10(n as nat),
{
reveal_with_fuel(pow10, 30);
match n {
0 => 1,
1 => 10,
2 => 100,
3 => 1000,
4 => 10000,
5 => 100000,
6 => 1000000,
7 => 10000000,
8 => 100000000,
9 => 1000000000,
10 => 10000000000,
11 => 100000000000,
12 => 1000000000000,
13 => 10000000000000,
14 => 100000000000000,
15 => 1000000000000000,
16 => 10000000000000000,
17 => 100000000000000000,
18 => 1000000000000000000,
19 => 10000000000000000000,
20 => 100000000000000000000,
21 => 1000000000000000000000,
22 => 10000000000000000000000,
23 => 100000000000000000000000,
24 => 1000000000000000000000000,
25 => 10000000000000000000000000,
26 => 100000000000000000000000000,
27 => 1000000000000000000000000000,
_ => 10000000000000000000000000000,
}
}
pub fn from_decimal128_dir(mantissa: i128, scale: u32, dir: Dir) -> (r: Option<Rat>)
ensures
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> r.unwrap() == round_frac(mantissa as int, pow10(scale as nat), dir),
(r.is_some() && !crate::round::saturated(mantissa as int, pow10(scale as nat))) ==> {
&&& dir == Dir::Down ==> q_le_frac(r.unwrap(), mantissa as int, pow10(scale as nat))
&&& dir == Dir::Up ==> q_ge_frac(r.unwrap(), mantissa as int, pow10(scale as nat))
&&& within_error_bound(r.unwrap(), mantissa as int, pow10(scale as nat))
},
r.is_none() <==> (scale > MAX_DECIMAL_SCALE || mantissa > MAX_DECIMAL_MANTISSA
|| mantissa < -MAX_DECIMAL_MANTISSA),
{
if scale > MAX_DECIMAL_SCALE {
return None;
}
if mantissa > MAX_DECIMAL_MANTISSA || mantissa < -MAX_DECIMAL_MANTISSA {
return None;
}
let d: i128 = pow10_i128(scale);
proof {
lemma_pow2_124();
lemma_pow2_126();
assert(abs_int(mantissa as int) <= MAX_DECIMAL_MANTISSA as int);
assert((MAX_DECIMAL_MANTISSA as int) < (pow2(126)));
assert(d as int <= 10000000000000000000000000000int);
assert(10000000000000000000000000000int <= pow2(124));
if !crate::round::saturated(mantissa as int, d as int) {
crate::round::lemma_r2_r3_directed(mantissa as int, d as int, dir);
}
}
Some(round_frac_exec(mantissa, d, dir))
}
pub fn from_decimal128_exact(mantissa: i128, scale: u32) -> (r: Option<Rat>)
ensures
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> q_is(r.unwrap(), mantissa as int, pow10(scale as nat)),
r.is_none() <==> !(scale <= MAX_DECIMAL_SCALE && mantissa <= MAX_DECIMAL_MANTISSA
&& mantissa >= -MAX_DECIMAL_MANTISSA
&& crate::round::exact_path(mantissa as int, pow10(scale as nat))),
{
if scale > MAX_DECIMAL_SCALE {
return None;
}
if mantissa > MAX_DECIMAL_MANTISSA || mantissa < -MAX_DECIMAL_MANTISSA {
return None;
}
let d: i128 = pow10_i128(scale);
proof {
lemma_pow2_124();
lemma_pow2_126();
assert(abs_int(mantissa as int) <= MAX_DECIMAL_MANTISSA as int);
assert((MAX_DECIMAL_MANTISSA as int) < (pow2(126)));
assert(d as int <= 10000000000000000000000000000int);
assert(10000000000000000000000000000int <= pow2(124));
}
if crate::round::exact_path_exec(mantissa, d) {
proof {
crate::round::lemma_r1_identity(mantissa as int, d as int, Dir::Nearest);
}
Some(round_frac_exec(mantissa, d, Dir::Nearest))
} else {
None
}
}
pub const RATIO_N_LIMIT: i128 = 85070591730234615865843651857942052863;
pub const RATIO_D_LIMIT: i128 = 21267647932558653966460912964485513216;
pub fn from_ratio128_dir(n: i128, d: i128, dir: Dir) -> (r: Option<Rat>)
ensures
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> r.unwrap() == round_frac(
crate::q::signed_den_num(n as int, d as int),
abs_int(d as int),
dir,
),
(r.is_some() && !crate::round::saturated(
crate::q::signed_den_num(n as int, d as int),
abs_int(d as int),
)) ==> {
&&& dir == Dir::Down ==> q_le_frac(
r.unwrap(),
crate::q::signed_den_num(n as int, d as int),
abs_int(d as int),
)
&&& dir == Dir::Up ==> q_ge_frac(
r.unwrap(),
crate::q::signed_den_num(n as int, d as int),
abs_int(d as int),
)
&&& within_error_bound(
r.unwrap(),
crate::q::signed_den_num(n as int, d as int),
abs_int(d as int),
)
},
r.is_none() <==> (d == 0 || n > RATIO_N_LIMIT || n < -RATIO_N_LIMIT || d > RATIO_D_LIMIT
|| d < -RATIO_D_LIMIT),
{
if d == 0 {
return None;
}
if n > RATIO_N_LIMIT || n < -RATIO_N_LIMIT {
return None;
}
if d > RATIO_D_LIMIT || d < -RATIO_D_LIMIT {
return None;
}
let mut nn: i128 = n;
let mut dd: i128 = d;
if dd < 0 {
nn = 0 - nn;
dd = 0 - dd;
}
proof {
lemma_pow2_124();
lemma_pow2_126();
if !crate::round::saturated(nn as int, dd as int) {
crate::round::lemma_r2_r3_directed(nn as int, dd as int, dir);
}
}
Some(round_frac_exec(nn, dd, dir))
}
pub fn from_ratio128_exact(n: i128, d: i128) -> (r: Option<Rat>)
ensures
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> q_is(
r.unwrap(),
crate::q::signed_den_num(n as int, d as int),
abs_int(d as int),
),
r.is_none() <==> !(d != 0 && n <= RATIO_N_LIMIT && n >= -RATIO_N_LIMIT
&& d <= RATIO_D_LIMIT && d >= -RATIO_D_LIMIT && crate::round::exact_path(
crate::q::signed_den_num(n as int, d as int),
abs_int(d as int),
)),
{
if d == 0 {
return None;
}
if n > RATIO_N_LIMIT || n < -RATIO_N_LIMIT {
return None;
}
if d > RATIO_D_LIMIT || d < -RATIO_D_LIMIT {
return None;
}
let mut nn: i128 = n;
let mut dd: i128 = d;
if dd < 0 {
nn = 0 - nn;
dd = 0 - dd;
}
proof {
lemma_pow2_124();
lemma_pow2_126();
}
if crate::round::exact_path_exec(nn, dd) {
proof {
crate::round::lemma_r1_identity(nn as int, dd as int, Dir::Nearest);
}
Some(round_frac_exec(nn, dd, Dir::Nearest))
} else {
None
}
}
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl core::fmt::Display for Rat {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}/{}", self.numerator(), self.denominator())
}
}
#[cfg(feature = "serde")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl serde::Serialize for Rat {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeTuple;
let mut t = s.serialize_tuple(2)?;
t.serialize_element(&self.numerator())?;
t.serialize_element(&self.denominator())?;
t.end()
}
}
#[cfg(feature = "serde")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl<'de> serde::Deserialize<'de> for Rat {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Rat, D::Error> {
use serde::de::Error;
let (num, den) = <(i64, i64) as serde::Deserialize>::deserialize(d)?;
Rat::new(num, den).ok_or_else(|| {
D::Error::custom("the-q: (num, den) pair is not a representable rational")
})
}
}
const POS_SAT_STR: &str = ">max";
const NEG_SAT_STR: &str = "<-max";
const POS_INF_STR: &str = "inf";
const NEG_INF_STR: &str = "-inf";
const NAN_STR: &str = "nan";
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl core::fmt::Display for crate::ext::Q {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
crate::ext::Q::Number(x) => write!(f, "{}", x),
crate::ext::Q::PosSat => f.write_str(POS_SAT_STR),
crate::ext::Q::NegSat => f.write_str(NEG_SAT_STR),
crate::ext::Q::PosInf => f.write_str(POS_INF_STR),
crate::ext::Q::NegInf => f.write_str(NEG_INF_STR),
crate::ext::Q::Nan => f.write_str(NAN_STR),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum ParseQError {
Malformed,
IntOverflow,
ZeroDenominator,
OutOfBudget,
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl core::fmt::Display for ParseQError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(match self {
ParseQError::Malformed => "the-q: not a rational or a recognised special",
ParseQError::IntOverflow => "the-q: numeral does not fit an i64",
ParseQError::ZeroDenominator => "the-q: denominator is zero",
ParseQError::OutOfBudget => "the-q: value is outside the width budget",
})
}
}
impl std::error::Error for ParseQError {}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl core::str::FromStr for crate::ext::Q {
type Err = ParseQError;
fn from_str(s: &str) -> Result<Self, ParseQError> {
use crate::ext::Q;
if s.eq_ignore_ascii_case(NAN_STR) {
return Ok(Q::Nan);
}
if s.eq_ignore_ascii_case(POS_INF_STR) {
return Ok(Q::PosInf);
}
if s.eq_ignore_ascii_case(NEG_INF_STR) {
return Ok(Q::NegInf);
}
if s.eq_ignore_ascii_case(POS_SAT_STR) {
return Ok(Q::PosSat);
}
if s.eq_ignore_ascii_case(NEG_SAT_STR) {
return Ok(Q::NegSat);
}
let (num_str, den_str) = match s.split_once('/') {
Some((n, d)) => (n, d),
None => (s, "1"),
};
let num: i64 = parse_i64(num_str)?;
let den: i64 = parse_i64(den_str)?;
if den == 0 {
return Err(ParseQError::ZeroDenominator);
}
match crate::types::Rat::new(num, den) {
Some(x) => Ok(Q::Number(x)),
None => Err(ParseQError::OutOfBudget),
}
}
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
fn parse_i64(s: &str) -> Result<i64, ParseQError> {
match s.parse::<i64>() {
Ok(v) => Ok(v),
Err(_) => {
let digits = s.strip_prefix(['+', '-']).unwrap_or(s);
if !digits.is_empty() && digits.bytes().all(|b| b.is_ascii_digit()) {
Err(ParseQError::IntOverflow)
} else {
Err(ParseQError::Malformed)
}
}
}
}
#[cfg(feature = "serde")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl serde::Serialize for crate::ext::Q {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
match self {
crate::ext::Q::Number(x) => x.serialize(s),
crate::ext::Q::PosSat => s.serialize_str(POS_SAT_STR),
crate::ext::Q::NegSat => s.serialize_str(NEG_SAT_STR),
crate::ext::Q::PosInf => s.serialize_str(POS_INF_STR),
crate::ext::Q::NegInf => s.serialize_str(NEG_INF_STR),
crate::ext::Q::Nan => s.serialize_str(NAN_STR),
}
}
}
#[cfg(feature = "serde")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl<'de> serde::Deserialize<'de> for crate::ext::Q {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = crate::ext::Q;
fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(
"a [num, den] pair or one of \"nan\", \"inf\", \"-inf\", \">max\", \"<-max\"",
)
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
use crate::ext::Q;
match v {
NAN_STR => Ok(Q::Nan),
POS_INF_STR => Ok(Q::PosInf),
NEG_INF_STR => Ok(Q::NegInf),
POS_SAT_STR => Ok(Q::PosSat),
NEG_SAT_STR => Ok(Q::NegSat),
_ => Err(E::custom("the-q: unrecognised special-value string")),
}
}
fn visit_seq<A: serde::de::SeqAccess<'de>>(
self,
mut seq: A,
) -> Result<Self::Value, A::Error> {
use serde::de::Error;
let num: i64 = seq
.next_element()?
.ok_or_else(|| A::Error::custom("the-q: missing numerator"))?;
let den: i64 = seq
.next_element()?
.ok_or_else(|| A::Error::custom("the-q: missing denominator"))?;
if seq.next_element::<serde::de::IgnoredAny>()?.is_some() {
return Err(A::Error::custom("the-q: expected exactly two elements"));
}
crate::types::Rat::new(num, den)
.map(crate::ext::Q::Number)
.ok_or_else(|| {
A::Error::custom("the-q: (num, den) pair is not a representable rational")
})
}
}
d.deserialize_any(V)
}
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn q_from_f64(v: f64) -> crate::ext::Q {
use crate::ext::Q;
if v.is_nan() {
return Q::Nan;
}
if v.is_infinite() {
return if v.is_sign_negative() {
Q::NegInf
} else {
Q::PosInf
};
}
match from_f64_dir(v, crate::types::Dir::Nearest) {
Some(x) => Q::Number(x),
None => {
if v.is_sign_negative() {
Q::NegSat
} else {
Q::PosSat
}
}
}
}
#[cfg(feature = "rust_decimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_rust_decimal_dir(v: rust_decimal::Decimal, dir: Dir) -> Rat {
match from_decimal128_dir(v.mantissa(), v.scale(), dir) {
Some(x) => x,
None => unreachable!(
"the-q: rust_decimal::Decimal violated its own (mantissa, scale) invariant"
),
}
}
#[cfg(feature = "rust_decimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn q_from_rust_decimal(v: rust_decimal::Decimal) -> crate::ext::Q {
use crate::ext::Q;
let mantissa = v.mantissa();
let scale = v.scale();
let d = pow10_i128(scale);
if crate::q::magnitude_fits_exec(mantissa, d) {
Q::Number(from_rust_decimal_dir(v, crate::types::Dir::Nearest))
} else if mantissa > 0 {
Q::PosSat
} else {
Q::NegSat
}
}
#[cfg(feature = "rust_decimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl From<rust_decimal::Decimal> for crate::ext::Q {
fn from(v: rust_decimal::Decimal) -> Self {
q_from_rust_decimal(v)
}
}
#[cfg(feature = "rust_decimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn exact_from_rust_decimal(
v: rust_decimal::Decimal,
) -> Result<crate::exact::Exact, crate::exact::ExactError> {
match from_decimal128_exact(v.mantissa(), v.scale()) {
Some(x) => Ok(crate::exact::Exact::new(x)),
None => Err(crate::exact::ExactError::Inexact),
}
}
#[cfg(feature = "rust_decimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl TryFrom<rust_decimal::Decimal> for crate::exact::Exact {
type Error = crate::exact::ExactError;
fn try_from(v: rust_decimal::Decimal) -> Result<Self, Self::Error> {
exact_from_rust_decimal(v)
}
}
#[cfg(any(
feature = "fixed",
feature = "num-rational",
feature = "num-bigint",
feature = "bigdecimal"
))]
#[cfg_attr(verus_keep_ghost, verifier::external)]
fn q_from_ratio128(n: i128, d: i128) -> crate::ext::Q {
use crate::ext::Q;
if n > RATIO_N_LIMIT || n < -RATIO_N_LIMIT {
return if n < 0 { Q::NegSat } else { Q::PosSat };
}
if crate::q::magnitude_fits_exec(n, d) {
Q::Number(
from_ratio128_dir(n, d, crate::types::Dir::Nearest)
.expect("the-q: n and d were just bound-checked above"),
)
} else if n > 0 {
Q::PosSat
} else {
Q::NegSat
}
}
#[cfg(any(
feature = "fixed",
feature = "num-rational",
feature = "num-bigint",
feature = "bigdecimal"
))]
#[cfg_attr(verus_keep_ghost, verifier::external)]
fn exact_from_ratio128(n: i128, d: i128) -> Result<crate::exact::Exact, crate::exact::ExactError> {
match from_ratio128_exact(n, d) {
Some(x) => Ok(crate::exact::Exact::new(x)),
None => Err(crate::exact::ExactError::Inexact),
}
}
#[cfg(feature = "fixed")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_fixed_dir<F>(v: F, dir: Dir) -> Option<Rat>
where
F: fixed::traits::Fixed<Bits = i128>,
{
if F::FRAC_NBITS > 126 {
return None;
}
let d = crate::round::pow2_i128(F::FRAC_NBITS);
from_ratio128_dir(v.to_bits(), d, dir)
}
#[cfg(feature = "fixed")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_fixed_exact<F>(v: F) -> Option<Rat>
where
F: fixed::traits::Fixed<Bits = i128>,
{
if F::FRAC_NBITS > 126 {
return None;
}
let d = crate::round::pow2_i128(F::FRAC_NBITS);
from_ratio128_exact(v.to_bits(), d)
}
#[cfg(feature = "fixed")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn q_from_fixed<F>(v: F) -> crate::ext::Q
where
F: fixed::traits::Fixed<Bits = i128>,
{
if F::FRAC_NBITS > 126 {
return q_from_f64(v.to_num::<f64>());
}
let d = crate::round::pow2_i128(F::FRAC_NBITS);
q_from_ratio128(v.to_bits(), d)
}
#[cfg(feature = "fixed")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn exact_from_fixed<F>(v: F) -> Result<crate::exact::Exact, crate::exact::ExactError>
where
F: fixed::traits::Fixed<Bits = i128>,
{
if F::FRAC_NBITS > 126 {
return Err(crate::exact::ExactError::Inexact);
}
let d = crate::round::pow2_i128(F::FRAC_NBITS);
exact_from_ratio128(v.to_bits(), d)
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_num_rational_i64_dir(v: num_rational::Ratio<i64>, dir: Dir) -> Rat {
from_ratio128_dir(i128::from(*v.numer()), i128::from(*v.denom()), dir)
.expect("the-q: an i64 pair is always within from_ratio128_dir's domain")
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_num_rational_i64_exact(v: num_rational::Ratio<i64>) -> Option<Rat> {
from_ratio128_exact(i128::from(*v.numer()), i128::from(*v.denom()))
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn q_from_num_rational_i64(v: num_rational::Ratio<i64>) -> crate::ext::Q {
q_from_ratio128(i128::from(*v.numer()), i128::from(*v.denom()))
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl From<num_rational::Ratio<i64>> for crate::ext::Q {
fn from(v: num_rational::Ratio<i64>) -> Self {
q_from_num_rational_i64(v)
}
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn exact_from_num_rational_i64(
v: num_rational::Ratio<i64>,
) -> Result<crate::exact::Exact, crate::exact::ExactError> {
exact_from_ratio128(i128::from(*v.numer()), i128::from(*v.denom()))
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl TryFrom<num_rational::Ratio<i64>> for crate::exact::Exact {
type Error = crate::exact::ExactError;
fn try_from(v: num_rational::Ratio<i64>) -> Result<Self, Self::Error> {
exact_from_num_rational_i64(v)
}
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_big_rational_dir(v: &num_rational::BigRational, dir: Dir) -> Option<Rat> {
let n = i128::try_from(v.numer()).ok()?;
let d = i128::try_from(v.denom()).ok()?;
from_ratio128_dir(n, d, dir)
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_big_rational_exact(v: &num_rational::BigRational) -> Option<Rat> {
let n = i128::try_from(v.numer()).ok()?;
let d = i128::try_from(v.denom()).ok()?;
from_ratio128_exact(n, d)
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn q_from_big_rational(v: &num_rational::BigRational) -> crate::ext::Q {
match (i128::try_from(v.numer()), i128::try_from(v.denom())) {
(Ok(n), Ok(d)) => q_from_ratio128(n, d),
_ => q_from_f64(num_traits::ToPrimitive::to_f64(v).unwrap_or(f64::NAN)),
}
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl From<num_rational::BigRational> for crate::ext::Q {
fn from(v: num_rational::BigRational) -> Self {
q_from_big_rational(&v)
}
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn exact_from_big_rational(
v: &num_rational::BigRational,
) -> Result<crate::exact::Exact, crate::exact::ExactError> {
let n = i128::try_from(v.numer()).map_err(|_| crate::exact::ExactError::Inexact)?;
let d = i128::try_from(v.denom()).map_err(|_| crate::exact::ExactError::Inexact)?;
exact_from_ratio128(n, d)
}
#[cfg(feature = "num-rational")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl TryFrom<num_rational::BigRational> for crate::exact::Exact {
type Error = crate::exact::ExactError;
fn try_from(v: num_rational::BigRational) -> Result<Self, Self::Error> {
exact_from_big_rational(&v)
}
}
#[cfg(feature = "num-bigint")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_bigint_dir(v: &num_bigint::BigInt, dir: Dir) -> Option<Rat> {
let n = i128::try_from(v).ok()?;
from_ratio128_dir(n, 1, dir)
}
#[cfg(feature = "num-bigint")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_bigint_exact(v: &num_bigint::BigInt) -> Option<Rat> {
let n = i128::try_from(v).ok()?;
from_ratio128_exact(n, 1)
}
#[cfg(feature = "num-bigint")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn q_from_bigint(v: &num_bigint::BigInt) -> crate::ext::Q {
use crate::ext::Q;
match i128::try_from(v) {
Ok(n) => q_from_ratio128(n, 1),
Err(_) => {
if v.sign() == num_bigint::Sign::Minus {
Q::NegSat
} else {
Q::PosSat
}
}
}
}
#[cfg(feature = "num-bigint")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl From<num_bigint::BigInt> for crate::ext::Q {
fn from(v: num_bigint::BigInt) -> Self {
q_from_bigint(&v)
}
}
#[cfg(feature = "num-bigint")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn exact_from_bigint(
v: &num_bigint::BigInt,
) -> Result<crate::exact::Exact, crate::exact::ExactError> {
let n = i128::try_from(v).map_err(|_| crate::exact::ExactError::Inexact)?;
exact_from_ratio128(n, 1)
}
#[cfg(feature = "num-bigint")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl TryFrom<num_bigint::BigInt> for crate::exact::Exact {
type Error = crate::exact::ExactError;
fn try_from(v: num_bigint::BigInt) -> Result<Self, Self::Error> {
exact_from_bigint(&v)
}
}
#[cfg(feature = "bigdecimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
fn bigdecimal_ratio_i128(v: &bigdecimal::BigDecimal) -> Option<(i128, i128)> {
let (digits, scale) = v.as_bigint_and_scale();
if scale >= 0 {
if scale > i64::from(MAX_DECIMAL_SCALE) {
return None;
}
let n = i128::try_from(digits.as_ref()).ok()?;
let d = pow10_i128(u32::try_from(scale).ok()?);
Some((n, d))
} else {
let shift = scale.checked_neg()?;
if shift > 40 {
return None;
}
let factor = num_bigint::BigInt::from(10u8).pow(u32::try_from(shift).ok()?);
let n_big = digits.into_owned() * factor;
let n = i128::try_from(&n_big).ok()?;
Some((n, 1))
}
}
#[cfg(feature = "bigdecimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_bigdecimal_dir(v: &bigdecimal::BigDecimal, dir: Dir) -> Option<Rat> {
let (n, d) = bigdecimal_ratio_i128(v)?;
from_ratio128_dir(n, d, dir)
}
#[cfg(feature = "bigdecimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn from_bigdecimal_exact(v: &bigdecimal::BigDecimal) -> Option<Rat> {
let (n, d) = bigdecimal_ratio_i128(v)?;
from_ratio128_exact(n, d)
}
#[cfg(feature = "bigdecimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn q_from_bigdecimal(v: &bigdecimal::BigDecimal) -> crate::ext::Q {
match bigdecimal_ratio_i128(v) {
Some((n, d)) => q_from_ratio128(n, d),
None => q_from_f64(num_traits::ToPrimitive::to_f64(v).unwrap_or(f64::NAN)),
}
}
#[cfg(feature = "bigdecimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl From<bigdecimal::BigDecimal> for crate::ext::Q {
fn from(v: bigdecimal::BigDecimal) -> Self {
q_from_bigdecimal(&v)
}
}
#[cfg(feature = "bigdecimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
pub fn exact_from_bigdecimal(
v: &bigdecimal::BigDecimal,
) -> Result<crate::exact::Exact, crate::exact::ExactError> {
let (n, d) = bigdecimal_ratio_i128(v).ok_or(crate::exact::ExactError::Inexact)?;
exact_from_ratio128(n, d)
}
#[cfg(feature = "bigdecimal")]
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl TryFrom<bigdecimal::BigDecimal> for crate::exact::Exact {
type Error = crate::exact::ExactError;
fn try_from(v: bigdecimal::BigDecimal) -> Result<Self, Self::Error> {
exact_from_bigdecimal(&v)
}
}