use super::super::calc_macro::*;
#[allow(unused_imports)]
use super::super::prelude::*;
verus! {
use super::super::arithmetic::div_mod::*;
#[cfg(verus_keep_ghost)]
use super::super::arithmetic::internals::general_internals::{is_le};
#[cfg(verus_keep_ghost)]
use super::super::arithmetic::mul::{
lemma_mul_inequality,
lemma_mul_nonnegative,
lemma_mul_strictly_increases,
lemma_mul_left_inequality,
group_mul_basics,
lemma_mul_increases,
lemma_mul_is_commutative,
group_mul_is_distributive,
lemma_mul_is_associative,
};
#[cfg(verus_keep_ghost)]
use super::internals::mul_internals::{group_mul_properties_internal, lemma_mul_induction_auto};
#[cfg(verus_keep_ghost)]
use super::super::math::{sub as sub1};
pub open spec fn pow(b: int, e: nat) -> int
decreases e,
{
if e == 0 {
1
} else {
b * pow(b, (e - 1) as nat)
}
}
pub broadcast proof fn lemma_pow0(b: int)
ensures
#[trigger] pow(b, 0) == 1,
{
reveal(pow);
}
proof fn lemma_mul_basics_auto()
ensures
forall|x: int| #[trigger] (0 * x) == 0,
forall|x: int| #[trigger] (x * 0) == 0,
forall|x: int| #[trigger] (x * 1) == x,
forall|x: int| #[trigger] (1 * x) == x,
{
broadcast use group_mul_basics;
}
pub broadcast proof fn lemma_pow1(b: int)
ensures
#[trigger] pow(b, 1) == b,
{
calc! {
(==)
pow(b, 1); {
reveal(pow);
}
b * pow(b, 0); {
lemma_pow0(b);
}
b * 1; {
lemma_mul_basics_auto();
}
b;
}
}
pub broadcast proof fn lemma0_pow(e: nat)
requires
e > 0,
ensures
#[trigger] pow(0, e) == 0,
decreases e,
{
reveal(pow);
lemma_mul_basics_auto();
if e != 1 {
lemma0_pow((e - 1) as nat);
}
}
pub broadcast proof fn lemma1_pow(e: nat)
ensures
#[trigger] pow(1, e) == 1,
decreases e,
{
reveal(pow);
lemma_mul_basics_auto();
if e != 0 {
lemma1_pow((e - 1) as nat);
}
}
pub broadcast proof fn lemma_square_is_pow2(x: int)
ensures
#[trigger] pow(x, 2) == x * x,
{
reveal_with_fuel(pow, 3);
}
pub broadcast proof fn lemma_pow_positive(b: int, e: nat)
requires
b > 0,
ensures
0 < #[trigger] pow(b, e),
{
reveal(pow);
broadcast use lemma_mul_increases;
broadcast use lemma_pow0;
lemma_mul_induction_auto(e as int, |u: int| 0 <= u ==> 0 < pow(b, u as nat));
}
proof fn lemma_mul_is_associative_auto()
ensures
forall|x: int, y: int, z: int|
#![trigger x * (y * z)]
#![trigger (x * y) * z]
x * (y * z) == (x * y) * z,
{
broadcast use lemma_mul_is_associative;
}
pub broadcast proof fn lemma_pow_adds(b: int, e1: nat, e2: nat)
ensures
#[trigger] pow(b, e1 + e2) == pow(b, e1) * pow(b, e2),
decreases e1,
{
if e1 == 0 {
calc! {
(==)
pow(b, e1) * pow(b, e2); {
lemma_pow0(b);
}
1 * pow(b, e2); {
lemma_mul_basics_auto();
}
pow(b, 0 + e2);
}
} else {
calc! {
(==)
pow(b, e1) * pow(b, e2); {
reveal(pow);
}
(b * pow(b, (e1 - 1) as nat)) * pow(b, e2); {
lemma_mul_is_associative_auto();
}
b * (pow(b, (e1 - 1) as nat) * pow(b, e2)); {
lemma_pow_adds(b, (e1 - 1) as nat, e2);
}
b * pow(b, (e1 - 1 + e2) as nat); {
reveal(pow);
}
pow(b, e1 + e2);
}
}
}
pub broadcast proof fn lemma_pow_sub_add_cancel(b: int, e1: nat, e2: nat)
requires
e1 >= e2,
ensures
#[trigger] pow(b, (e1 - e2) as nat) * pow(b, e2) == pow(b, e1),
decreases e1,
{
lemma_pow_adds(b, (e1 - e2) as nat, e2);
}
pub broadcast proof fn lemma_pow_subtracts(b: int, e1: nat, e2: nat)
requires
b > 0,
e1 <= e2,
ensures
pow(b, e1) > 0,
#[trigger] pow(b, (e2 - e1) as nat) == pow(b, e2) / pow(b, e1) > 0,
{
broadcast use lemma_pow_positive;
calc! {
(==)
pow(b, e2) / pow(b, e1); {
lemma_pow_sub_add_cancel(b, e2, e1);
}
pow(b, (e2 - e1) as nat) * pow(b, e1) / pow(b, e1); {
lemma_div_by_multiple(pow(b, (e2 - e1) as nat), pow(b, e1));
}
pow(b, (e2 - e1) as nat);
}
}
proof fn lemma_mul_is_distributive_auto()
ensures
forall|x: int, y: int, z: int| #[trigger] (x * (y + z)) == x * y + x * z,
forall|x: int, y: int, z: int| #[trigger] ((y + z) * x) == y * x + z * x,
forall|x: int, y: int, z: int| #[trigger] (x * (y - z)) == x * y - x * z,
forall|x: int, y: int, z: int| #[trigger] ((y - z) * x) == y * x - z * x,
{
broadcast use group_mul_is_distributive;
}
pub broadcast proof fn lemma_pow_multiplies(a: int, b: nat, c: nat)
ensures
0 <= b * c,
#[trigger] pow(pow(a, b), c) == pow(a, b * c),
decreases c,
{
lemma_mul_nonnegative(b as int, c as int);
if c == 0 {
lemma_mul_basics_auto();
calc! {
(==)
pow(a, (b * c) as nat); {
lemma_pow0(a);
}
1; {
lemma_pow0(pow(a, b));
}
pow(pow(a, b), c);
}
} else {
calc! {
(==)
b * c - b; {
lemma_mul_basics_auto();
}
b * c - b * 1; { lemma_mul_is_distributive_auto() }
b * (c - 1);
}
lemma_mul_nonnegative(b as int, c - 1);
assert(0 <= b * c - b);
calc! {
(==)
pow(a, b * c); {}
pow(a, (b + b * c - b) as nat); {
lemma_pow_adds(a, b, (b * c - b) as nat);
}
pow(a, b) * pow(a, (b * c - b) as nat); {}
pow(a, b) * pow(a, (b * (c - 1)) as nat); {
lemma_pow_multiplies(a, b, (c - 1) as nat);
}
pow(a, b) * pow(pow(a, b), (c - 1) as nat); {
reveal(pow);
}
pow(pow(a, b), c);
}
}
}
proof fn lemma_mul_is_commutative_auto()
ensures
forall|x: int, y: int| #[trigger] (x * y) == y * x,
{
broadcast use lemma_mul_is_commutative;
}
pub broadcast proof fn lemma_pow_distributes(a: int, b: int, e: nat)
ensures
#[trigger] pow(a * b, e) == pow(a, e) * pow(b, e),
decreases e,
{
reveal(pow);
lemma_mul_basics_auto();
if e >= 1 {
calc! {
(==)
pow(a * b, e); {
reveal(pow);
}
(a * b) * pow(a * b, (e - 1) as nat); {
lemma_pow_distributes(a, b, (e - 1) as nat);
}
(a * b) * (pow(a, (e - 1) as nat) * pow(b, (e - 1) as nat)); {
lemma_mul_is_associative_auto();
lemma_mul_is_commutative_auto();
assert((a * b * pow(a, (e - 1) as nat)) * pow(b, (e - 1) as nat) == (a * pow(
a,
(e - 1) as nat,
) * b) * pow(b, (e - 1) as nat));
}
(a * pow(a, (e - 1) as nat)) * (b * pow(b, (e - 1) as nat)); {
reveal(pow);
}
pow(a, e) * pow(b, e);
}
}
}
pub broadcast group group_pow_properties {
lemma_pow0,
lemma_pow1,
lemma_pow_distributes,
lemma_pow_adds,
lemma_pow_sub_add_cancel,
group_mul_properties_internal,
lemma_mul_increases,
lemma_mul_strictly_increases,
}
proof fn lemma_pow_properties_prove_pow_auto()
ensures
forall|x: int| pow(x, 0) == 1,
forall|x: int| #[trigger] pow(x, 1) == x,
forall|x: int, y: int| y == 0 ==> #[trigger] pow(x, y as nat) == 1,
forall|x: int, y: int| y == 1 ==> #[trigger] pow(x, y as nat) == x,
forall|x: int, y: int| 0 < x && 0 < y ==> x <= #[trigger] (x * y as nat),
forall|x: int, y: int| 0 < x && 1 < y ==> x < #[trigger] (x * y as nat),
forall|x: int, y: nat, z: nat| #[trigger] pow(x, y + z) == pow(x, y) * pow(x, z),
forall|x: int, y: nat, z: nat|
y >= z ==> #[trigger] pow(x, (y - z) as nat) * pow(x, z) == pow(x, y),
forall|x: int, y: nat, z: nat| #[trigger] pow(x * y, z) == pow(x, z) * pow(y as int, z),
{
reveal(pow);
broadcast use group_pow_properties;
}
pub broadcast proof fn lemma_pow_strictly_increases(b: nat, e1: nat, e2: nat)
requires
1 < b,
e1 < e2,
ensures
#[trigger] pow(b as int, e1) < #[trigger] pow(b as int, e2),
{
let f = |e: int| 0 < e ==> pow(b as int, e1) < pow(b as int, (e1 + e) as nat);
assert forall|i: int| (#[trigger] is_le(0, i) && f(i)) implies f(i + 1) by {
calc! {
(<=)
pow(b as int, (e1 + i) as nat); (<=) {
lemma_pow_positive(b as int, (e1 + i) as nat);
lemma_mul_left_inequality(pow(b as int, (e1 + i) as nat), 1, b as int);
}
pow(b as int, (e1 + i) as nat) * b; (<=) {
lemma_pow1(b as int);
}
pow(b as int, (e1 + i) as nat) * pow(b as int, 1); (<=) {
lemma_pow_adds(b as int, (e1 + i) as nat, 1nat);
}
pow(b as int, (e1 + i + 1) as nat);
}
assert(0 < i ==> pow(b as int, e1) < pow(b as int, (e1 + i) as nat));
if (i == 0) {
assert(pow(b as int, e1) < pow(b as int, (e1 + 1) as nat)) by {
reveal(pow);
assert(pow(b as int, e1) < b * pow(b as int, e1)) by {
assert(pow(b as int, e1) > 0) by {
broadcast use lemma_pow_positive;
}
lemma_mul_strictly_increases(b as int, pow(b as int, e1));
};
};
}
assert(f(i + 1));
}
lemma_mul_induction_auto(e2 - e1, f);
}
pub broadcast proof fn lemma_pow_increases(b: nat, e1: nat, e2: nat)
requires
b > 0,
e1 <= e2,
ensures
#[trigger] pow(b as int, e1) <= #[trigger] pow(b as int, e2),
{
if e1 != e2 {
if b > 1 {
lemma_pow_strictly_increases(b, e1, e2);
} else {
lemma1_pow(e1);
lemma1_pow(e2);
}
}
}
pub broadcast proof fn lemma_pow_strictly_increases_converse(b: nat, e1: nat, e2: nat)
requires
b > 0,
#[trigger] pow(b as int, e1) < #[trigger] pow(b as int, e2),
ensures
e1 < e2,
{
if e1 >= e2 {
lemma_pow_increases(b, e2, e1);
assert(false);
}
}
pub broadcast proof fn lemma_pow_increases_converse(b: nat, e1: nat, e2: nat)
requires
1 < b,
#[trigger] pow(b as int, e1) <= #[trigger] pow(b as int, e2),
ensures
e1 <= e2,
{
if e1 > e2 {
lemma_pow_strictly_increases(b, e2, e1);
assert(false);
}
}
pub broadcast proof fn lemma_pull_out_pows(b: nat, x: nat, y: nat, z: nat)
requires
b > 0,
ensures
0 <= x * y,
0 <= y * z,
#[trigger] pow(pow(b as int, x * y), z) == pow(pow(b as int, x), y * z),
{
lemma_mul_nonnegative(x as int, y as int);
lemma_mul_nonnegative(y as int, z as int);
lemma_pow_positive(b as int, x);
calc! {
(==)
pow(pow(b as int, x * y), z); {
lemma_pow_multiplies(b as int, x, y);
}
pow(pow(pow(b as int, x), y), z); {
lemma_pow_multiplies(pow(b as int, x), y, z);
}
pow(pow(b as int, x), y * z);
}
}
pub proof fn lemma_pow_division_inequality(x: nat, b: nat, e1: nat, e2: nat)
requires
b > 0,
e2 <= e1,
x < pow(b as int, e1),
ensures
pow(b as int, e2) > 0,
#[trigger] (x as int / pow(b as int, e2)) < #[trigger] pow(b as int, (e1 - e2) as nat),
{
broadcast use lemma_pow_positive;
assert(x as int / pow(b as int, e2) >= pow(b as int, (e1 - e2) as nat) ==> false) by {
if x as int / pow(b as int, e2) >= pow(b as int, (e1 - e2) as nat) {
lemma_mul_inequality(
pow(b as int, (e1 - e2) as nat),
x as int / pow(b as int, e2),
pow(b as int, e2),
);
lemma_fundamental_div_mod(x as int, pow(b as int, e2));
broadcast use lemma_mul_is_commutative, group_mod_properties;
lemma_pow_adds(b as int, (e1 - e2) as nat, e2);
}
};
}
pub broadcast proof fn lemma_pow_mod(b: nat, e: nat)
requires
b > 0,
e > 0,
ensures
#[trigger] pow(b as int, e) % b as int == 0,
{
reveal(pow);
assert(pow(b as int, e) % b as int == (b * pow(b as int, (e - 1) as nat)) % b as int);
assert((b * pow(b as int, (e - 1) as nat)) % b as int == (pow(b as int, (e - 1) as nat) * b)
% b as int) by {
broadcast use lemma_mul_is_commutative;
};
assert((pow(b as int, (e - 1) as nat) * b) % b as int == 0) by {
broadcast use lemma_pow_positive;
lemma_mod_multiples_basic(pow(b as int, (e - 1) as nat), b as int);
};
}
pub broadcast proof fn lemma_pow_mod_noop(b: int, e: nat, m: int)
requires
m > 0,
ensures
#[trigger] pow(b % m, e) % m == pow(b, e) % m,
decreases e,
{
reveal(pow);
broadcast use group_mod_properties;
if e > 0 {
calc! {
(==)
pow(b % m, e) % m; {}
((b % m) * pow(b % m, (e - 1) as nat)) % m; {
lemma_mul_mod_noop_general(b, pow(b % m, (e - 1) as nat), m);
}
((b % m) * (pow(b % m, (e - 1) as nat) % m) % m) % m; {
lemma_pow_mod_noop(b, (e - 1) as nat, m);
}
((b % m) * (pow(b, (e - 1) as nat) % m) % m) % m; {
lemma_mul_mod_noop_general(b, pow(b, (e - 1) as nat), m);
}
(b * (pow(b, (e - 1) as nat)) % m) % m; {}
(b * (pow(b, (e - 1) as nat))) % m; {}
pow(b, e) % m;
}
}
}
}