use clock::ChainEpoch;
use fil_types::StoragePower;
use forest_actor::math::{poly_parse, PRECISION};
use forest_actor::smooth::extrapolated_cum_sum_of_ratio as ecsor;
use forest_actor::smooth::*;
use forest_actor::EPOCHS_IN_DAY;
use num_bigint::{BigInt, Integer};
use num_traits::sign::Signed;
const ERR_BOUND: u64 = 350;
fn per_million_error(val_1: &BigInt, val_2: &BigInt) -> BigInt {
let diff = (val_1 - val_2) << PRECISION;
let ratio = diff.div_floor(&val_1);
let million = BigInt::from(1_000_000) << PRECISION;
let diff_per_million = (ratio * million).abs();
diff_per_million >> 2 * PRECISION
}
fn iterative_cum_sum_of_ratio(
num: &FilterEstimate,
denom: &FilterEstimate,
t0: ChainEpoch,
delta: ChainEpoch,
) -> BigInt {
let mut ratio = BigInt::from(0u8);
for i in 0..delta {
let num_epsilon = num.extrapolate(t0 + i); let denom_epsilon = denom.extrapolate(t0 + i) >> PRECISION; let mut epsilon = num_epsilon.div_floor(&denom_epsilon);
if i != 0 && i != delta - 1 {
epsilon *= 2; }
ratio += epsilon;
}
ratio.div_floor(&BigInt::from(2))
}
fn assert_err_bound(
num: &FilterEstimate,
denom: &FilterEstimate,
delta: ChainEpoch,
t0: ChainEpoch,
err_bound: BigInt,
) {
let analytic = ecsor(delta, t0, num, denom);
let iterative = iterative_cum_sum_of_ratio(num, denom, t0, delta);
let actual_err = per_million_error(&analytic, &iterative);
assert!(
actual_err < err_bound,
"Values are {} and {}",
actual_err,
err_bound
);
}
pub fn testing_constant_estimate(val: BigInt) -> FilterEstimate {
FilterEstimate::new(val, BigInt::from(0u8))
}
pub fn testing_estimate(x: BigInt, v: BigInt) -> FilterEstimate {
FilterEstimate::new(x, v)
}
#[test]
fn test_natural_log() {
let ln_inputs: Vec<BigInt> = poly_parse(&[
"340282366920938463463374607431768211456", "924990000000000000000000000000000000000", "34028236692093846346337460743176821145600000000000000000000", "6805647338418769269267492148635364229120000000000000000000000", "204169000000000000000000000000000000", "34028236692093846346337460743", ])
.unwrap();
let expected_ln_outputs: Vec<BigInt> = poly_parse(&[
"0", "340282366920938463463374607431768211456", "15670582109617661336106769654068947397831", "17473506083804940763855390762239996622013", "-2524410000000000000000000000000000000000", "-7835291054808830668053384827034473698915", ])
.unwrap();
assert_eq!(ln_inputs.len(), expected_ln_outputs.len());
let num_inputs = ln_inputs.len();
for i in 0..num_inputs {
let z = &ln_inputs[i];
let ln_of_z = ln(z);
let expected_z = &expected_ln_outputs[i];
assert_eq!(expected_z >> PRECISION, ln_of_z >> PRECISION);
}
}
#[test]
fn constant_estimate() {
let num_estimate = testing_constant_estimate(BigInt::from(4_000_000));
let denom_estimate = testing_constant_estimate(BigInt::from(1));
let csr_1 = ecsor(1000, 0, &num_estimate, &denom_estimate) >> PRECISION;
assert_eq!(BigInt::from(4 * 10_i64.pow(9)), csr_1);
let csr_2 = ecsor(1000, 10_i64.pow(15), &num_estimate, &denom_estimate) >> PRECISION;
assert_eq!(csr_1, csr_2);
let num_estimate = testing_constant_estimate(BigInt::from(10_i64.pow(12)));
let denom_estimate = testing_constant_estimate(BigInt::from(200 * 10_i64.pow(12)));
let csr_frac = ecsor(100, 0, &num_estimate, &denom_estimate);
assert_eq!(BigInt::from(0u8), &csr_frac >> PRECISION);
let product = csr_frac * (BigInt::from(10_000) << PRECISION); assert_eq!(BigInt::from(5000), product >> 2 * PRECISION);
}
#[test]
fn both_positive_velocity() {
let num_estimate = testing_estimate(BigInt::from(111), BigInt::from(12));
let denom_estimate = testing_estimate(BigInt::from(3456), BigInt::from(8));
assert_err_bound(
&num_estimate,
&denom_estimate,
10_000,
0,
BigInt::from(ERR_BOUND),
);
}
#[test]
fn flipped_signs() {
let num_estimate = testing_estimate(BigInt::from(1_000_000), BigInt::from(-100));
let denom_estimate = testing_estimate(BigInt::from(70_000), BigInt::from(1000));
assert_err_bound(
&num_estimate,
&denom_estimate,
100_000,
0,
BigInt::from(ERR_BOUND),
);
}
#[test]
fn values_in_range() {
let tens_of_fil = BigInt::from(50 * 10_i128.pow(18));
let one_fil_per_sec = BigInt::from(25);
let four_fil_per_second = BigInt::from(100);
let slow_money = testing_estimate(tens_of_fil.clone(), one_fil_per_sec);
let fast_money = testing_estimate(tens_of_fil.clone(), four_fil_per_second);
let tens_of_ei_bs = StoragePower::from(10_i128.pow(19));
let thousands_of_ei_bs = StoragePower::from(2 * 10_i128.pow(22));
let one_byte_per_epoch_velocity = BigInt::from(1);
let ten_pi_bs_per_day_velocity =
BigInt::from(10 * 2_i128.pow(50)) / BigInt::from(EPOCHS_IN_DAY);
let one_ei_bs_per_day_velocity = BigInt::from(2_i128.pow(60)) / BigInt::from(EPOCHS_IN_DAY);
let delta = EPOCHS_IN_DAY;
let t0 = 0;
let err_bound = BigInt::from(ERR_BOUND);
let test_cases: Vec<(StoragePower, BigInt)> = vec![
(tens_of_ei_bs.clone(), one_byte_per_epoch_velocity.clone()),
(tens_of_ei_bs.clone(), ten_pi_bs_per_day_velocity.clone()),
(tens_of_ei_bs.clone(), one_ei_bs_per_day_velocity.clone()),
(
thousands_of_ei_bs.clone(),
one_byte_per_epoch_velocity.clone(),
),
(
thousands_of_ei_bs.clone(),
ten_pi_bs_per_day_velocity.clone(),
),
(
thousands_of_ei_bs.clone(),
one_ei_bs_per_day_velocity.clone(),
),
];
for test_case in test_cases {
let power = testing_estimate(test_case.0, test_case.1);
assert_err_bound(&slow_money, &power, delta, t0, err_bound.clone());
assert_err_bound(&fast_money, &power, delta, t0, err_bound.clone());
}
}