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, 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);
}
return Some(Rat::zero());
}
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);
}
match dir {
Dir::Nearest => Rat::zero(),
Dir::Down => {
if neg {
Rat { num: -1, den: eps_den }
} else {
Rat::zero()
}
},
Dir::Up => {
if neg {
Rat::zero()
} else {
Rat { num: 1, den: eps_den }
}
},
}
}
#[verifier::external_body]
pub fn to_f64(q: Rat) -> f64 {
(q.num as f64) / (q.den as f64)
}
}
#[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.num, self.den)
}
}
#[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.num)?;
t.serialize_element(&self.den)?;
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
}
}
}
}