use anodized::spec;
#[spec]
trait MinFinder<T: PartialOrd> {
#[spec(
total,
requires: [
!input.is_empty(),
],
ensures: |ref output| [
input.iter().all(|item| output <= item),
input.iter().any(|item| output == item) || input.is_empty(),
],
)]
fn find_min(input: &[T]) -> T;
}
pub struct ValidNarrowing;
#[spec]
impl MinFinder<f32> for ValidNarrowing {
#[spec(
// Stronger than trait qualifiers: is also `pure` (`deterministic` and `effectfree`).
functional,
// Weaker than trait precondition: allows `input` to be empty.
requires: [],
ensures: |ref output| [
input.iter().all(|item| output <= item),
input.iter().any(|item| output == item)
|| (input.is_empty() && *output == f32::INFINITY),
],
)]
#[warn(unused_comparisons)]
fn find_min(input: &[f32]) -> f32 {
let mut min = f32::INFINITY;
for item in input.iter().copied() {
if item < min {
min = item;
}
}
min
}
}
pub struct StrongerImplPre;
#[spec]
impl MinFinder<i32> for StrongerImplPre {
#[spec(
total,
// INVALID - Stronger than trait precondition: requires sorted `input`.
requires: [
!input.is_empty(),
input.is_sorted(),
],
ensures: |ref output| [
input.iter().all(|item| output <= item),
input.iter().any(|item| output == item) || input.is_empty(),
],
)]
fn find_min(input: &[i32]) -> i32 {
input[0]
}
}
pub struct WeakerImplPost;
#[spec]
impl MinFinder<u32> for WeakerImplPost {
#[spec(
total,
requires: [
!input.is_empty(),
],
// INVALID - Weaker than trait postcondition: `input` may be ignored completely.
ensures: |ref output| [
input.iter().all(|item| output <= item),
],
)]
fn find_min(input: &[u32]) -> u32 {
let _ = input;
0
}
}
#[test]
fn runtime_allows_valid_narrowing() {
let seq = [5.0, -42.0, std::f32::consts::PI];
assert_eq!(ValidNarrowing::find_min(&seq), -42.0);
}
#[cfg(all(anodized_print, anodized_panic))]
#[test]
#[should_panic(expected = "precondition failed:\
\n input.is_sorted()")]
fn runtime_rejects_stronger_impl_precondition() {
let seq = [5, -42, 3];
assert_eq!(StrongerImplPre::find_min(&seq), -42);
}
#[cfg(all(anodized_print, anodized_panic))]
#[test]
#[should_panic(expected = "postcondition failed:\
\n input.iter().any(| item | output == item) || input.is_empty()")]
fn runtime_rejects_weaker_impl_postcondition() {
let seq = [5, 42, 3];
assert_eq!(WeakerImplPost::find_min(&seq), 3);
}
#[spec]
pub trait Matrix<T> {
fn count_rows(&self) -> usize;
fn count_cols(&self) -> usize;
#[spec(
requires: [
input.count_rows() == self.count_cols(),
],
ensures: |ref output| [
output.count_rows() == self.count_rows(),
output.count_cols() == input.count_cols(),
],
)]
fn mul<Input: Matrix<T>, Output: Matrix<T>>(&self, input: &Input) -> Output;
}
pub struct DiagonalMatrix<T>(Vec<T>);
#[spec]
impl<T> Matrix<T> for DiagonalMatrix<T> {
#[spec(ensures: |output| output == self.count_cols())]
fn count_rows(&self) -> usize {
self.0.len()
}
#[spec(ensures: |output| output == self.count_rows())]
fn count_cols(&self) -> usize {
self.0.len()
}
#[spec(
requires: [
input.count_rows() == self.count_cols(),
],
ensures: |ref output| [
output.count_rows() == input.count_rows(),
output.count_cols() == input.count_cols(),
],
)]
fn mul<Input: Matrix<T>, Output: Matrix<T>>(&self, input: &Input) -> Output {
let _ = input;
todo!()
}
}