const fn modinverse(a: u64) -> u64 {
let a = a | 1;
let mut x = a.wrapping_mul(3) ^ 2;
x = x.wrapping_mul(2u64.wrapping_sub(a.wrapping_mul(x)));
x = x.wrapping_mul(2u64.wrapping_sub(a.wrapping_mul(x)));
x = x.wrapping_mul(2u64.wrapping_sub(a.wrapping_mul(x)));
x = x.wrapping_mul(2u64.wrapping_sub(a.wrapping_mul(x)));
assert!(a.wrapping_mul(x) == 1);
x
}
#[inline(never)] const fn check_parameters_or_die(offset: u64, scale: u64, checking: (u64, u64)) {
use crate::vouch::vouch;
let _ = vouch(offset, scale, checking, 0);
let _ = vouch(offset, scale, checking, 1);
let _ = vouch(offset, scale, checking, 2);
let _ = vouch(offset, scale, checking, 0x110d2ae90b38f555u64);
}
#[inline(never)]
pub const fn derive_parameters(scale: u64, unoffset: u64) -> (u64, u64, (u64, u64)) {
use crate::check::CHECKING_TAG;
use crate::check::WANTED_SUM;
use crate::vouch::VOUCHING_TAG;
let scale = scale | 1; let unscale = modinverse(scale).wrapping_neg();
let offset = unscale.wrapping_mul(unoffset).wrapping_sub(WANTED_SUM);
let scale = scale ^ VOUCHING_TAG;
let unscale = unscale ^ CHECKING_TAG;
check_parameters_or_die(offset, scale, (unoffset, unscale));
(offset, scale, (unoffset, unscale))
}
#[test]
fn test_inverse() {
assert_eq!(modinverse(u64::MAX), u64::MAX);
assert_eq!(modinverse(1u64), 1u64);
assert_eq!(modinverse(3u64), 12297829382473034411u64);
}
#[test]
fn test_derive() {
use crate::check::CHECKING_TAG;
use crate::check::WANTED_SUM;
use crate::vouch::VOUCHING_TAG;
assert_eq!(
derive_parameters(0, 0),
(
WANTED_SUM.wrapping_neg(),
VOUCHING_TAG ^ 1,
(0, !CHECKING_TAG)
)
);
assert_eq!(
derive_parameters(u64::MAX, 0),
(
WANTED_SUM.wrapping_neg(),
!VOUCHING_TAG,
(0, CHECKING_TAG ^ 1)
)
);
assert_eq!(
derive_parameters(1, 1),
(
13020151265475858601,
7453010330410905431,
(1, 10993733730414794684)
)
);
assert_eq!(
derive_parameters(37, 13),
(
12023029964194261217,
7453010330410905459,
(13, 10110629933032573968)
)
);
assert_eq!(
derive_parameters(u64::MAX, u64::MAX),
(
13020151265475858601,
u64::MAX ^ VOUCHING_TAG,
(u64::MAX, 7453010343294756930)
)
);
}
#[test]
#[should_panic(expected = "failed to check voucher; parameters incorrect.")]
fn test_swap_params() {
let mut params = derive_parameters(43, 123);
std::mem::swap(&mut params.0, &mut params.2 .0);
std::mem::swap(&mut params.1, &mut params.2 .1);
check_parameters_or_die(params.0, params.1, params.2);
}
#[test]
#[should_panic(expected = "failed to check voucher; parameters incorrect.")]
fn test_swap_params_retag() {
use crate::check::CHECKING_TAG;
use crate::vouch::VOUCHING_TAG;
let mut params = derive_parameters(43, 123);
std::mem::swap(&mut params.0, &mut params.2 .0);
std::mem::swap(&mut params.1, &mut params.2 .1);
params.1 = params.1 ^ VOUCHING_TAG ^ CHECKING_TAG;
params.2 .1 = params.2 .1 ^ VOUCHING_TAG ^ CHECKING_TAG;
check_parameters_or_die(params.0, params.1, params.2);
}