1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
//! This file contains proofs related to integer logarithms. These are
//! part of the math standard library.
//!
//! It's based on the following file from the Dafny math standard
//! library:
//! `Source/DafnyStandardLibraries/src/Std/Arithmetic/Logarithm.dfy`.
//! That file has the following copyright notice:
//! /*******************************************************************************
//! * Original: Copyright (c) Microsoft Corporation *
//! SPDX-License-Identifier: MIT * * Modifications and Extensions:
//! Copyright by the contributors to the Dafny Project *
//! SPDX-License-Identifier: MIT
//! *******************************************************************************/
#[allow(unused_imports)]
use super::super::prelude::*;
verus! {
use super::super::calc_macro::*;
#[cfg(verus_keep_ghost)]
use super::div_mod::{
lemma_div_pos_is_pos,
lemma_div_decreases,
lemma_div_is_ordered,
lemma_div_multiples_vanish,
};
#[cfg(verus_keep_ghost)]
use super::super::math::{div as div1};
#[cfg(verus_keep_ghost)]
use super::super::arithmetic::mul::{lemma_mul_increases, lemma_mul_is_commutative};
#[cfg(verus_keep_ghost)]
use super::super::arithmetic::power::{pow, lemma_pow_positive};
/// This function recursively defines the integer logarithm. It's only
/// meaningful when the base of the logarithm `base` is greater than 1,
/// and when the value whose logarithm is taken, `pow`, is non-negative.
#[verifier::opaque]
pub open spec fn log(base: int, pow: int) -> int
recommends
base > 1,
pow >= 0,
decreases pow,
{
// In Dafny, we can invoke lemmas in functions to establish
// termination. Here in Verus, instead, we add the second
// conditions `pow / base >= pow` and `pow / base < 0` to show
// termination.
if pow < base || pow / base >= pow || pow / base < 0 {
0
} else {
1 + log(base, pow / base)
}
}
/// Proof that since `pow` is less than `base`, its logarithm in that base is 0
pub proof fn lemma_log0(base: int, pow: int)
requires
base > 1,
0 <= pow < base,
ensures
log(base, pow) == 0,
{
reveal(log);
}
/// Proof that since `pow` is greater than or equal to `base`, its
/// logarithm in that base is 1 more than the logarithm of `pow /
/// base`
pub broadcast proof fn lemma_log_s(base: int, pow: int)
requires
base > 1,
pow >= base,
ensures
#![trigger log(base, div1(pow, base))]
pow / base >= 0,
log(base, pow) == 1 + log(base, pow / base),
{
broadcast use lemma_div_pos_is_pos, lemma_div_decreases;
reveal(log);
}
/// Proof that the integer logarithm is always nonnegative. Specifically,
/// `log(base, pow) >= 0`.
pub proof fn lemma_log_nonnegative(base: int, pow: int)
requires
base > 1,
0 <= pow,
ensures
log(base, pow) >= 0,
decreases pow,
{
reveal(log);
if !(pow < base || pow / base >= pow || pow / base < 0) {
lemma_log_nonnegative(base, pow / base);
}
}
/// Proof that since `pow1` is less than or equal to `pow2`, the
/// integer logarithm of `pow1` in base `base` is less than or equal
/// to that of `pow2`.
pub proof fn lemma_log_is_ordered(base: int, pow1: int, pow2: int)
requires
base > 1,
0 <= pow1 <= pow2,
ensures
log(base, pow1) <= log(base, pow2),
decreases pow1,
{
reveal(log);
if pow2 < base {
assert(log(base, pow1) == 0 == log(base, pow2));
} else if pow1 < base {
assert(log(base, pow1) == 0);
lemma_log_nonnegative(base, pow2);
} else {
broadcast use lemma_div_pos_is_pos, lemma_div_is_ordered, lemma_div_decreases;
lemma_log_is_ordered(base, pow1 / base, pow2 / base);
}
}
/// Proof that the integer logarithm of `pow(base, n)` in base `base` is `n`
pub proof fn lemma_log_pow(base: int, n: nat)
requires
base > 1,
ensures
log(base, pow(base, n)) == n,
decreases n,
{
if n == 0 {
reveal(pow);
reveal(log);
} else {
let n_minus_1: nat = (n - 1) as nat;
lemma_pow_positive(base, n);
calc! {
(==)
log(base, pow(base, n)); (==) {
reveal(pow);
}
log(base, base * pow(base, n_minus_1)); (==) {
lemma_pow_positive(base, n_minus_1);
lemma_mul_increases(pow(base, n_minus_1), base);
lemma_mul_is_commutative(pow(base, n_minus_1), base);
lemma_log_s(base, base * pow(base, n_minus_1));
}
1 + log(base, (base * pow(base, n_minus_1)) / base); (==) {
lemma_div_multiples_vanish(pow(base, n_minus_1), base);
}
1 + log(base, pow(base, n_minus_1)); (==) {
lemma_log_pow(base, n_minus_1);
}
1 + (n - 1);
}
}
}
} // verus!