use crate::math::{ceil, ln, powf};
use alloc::{vec, vec::Vec};
struct Information {
a: Vec<f64>,
bv: Vec<f64>,
ab: Vec<f64>,
best_stat: f64,
best_loc: i32,
best_t2: i32,
min_size: usize,
b: i32,
}
impl Information {
fn new(bb: i32, m: usize) -> Self {
Self {
a: vec![0.0; 1 << (bb + 1)],
bv: vec![0.0; 1 << (bb + 1)],
ab: vec![0.0; 1 << (bb + 1)],
b: bb,
best_stat: -3.0,
best_loc: -3,
best_t2: -3,
min_size: m,
}
}
}
fn get_index(b: i32, x: f64) -> usize {
(ceil(x.abs() * (1 << b) as f64) + (1 << b) as f64 - 1.0) as usize
}
fn get_quantile(x: &[f64], quant: f64) -> f64 {
let n = x.len();
let mut k = ceil(x[1] * quant);
let mut l = 0.0;
let mut u = 1.0;
let mut i = 1;
let mut j;
while i < n {
j = i << 1;
if j >= n {
break;
}
if x[i] == k {
let l_weight = x[j] / (x[j] + x[j + 1]);
let r_weight = 1.0 - l_weight;
let lu = (u + l) / 2.0;
let rl = (u + lu) / 2.0;
return l_weight * (quant * (lu - l) + l) + r_weight * (quant * (u - rl) + rl);
}
else if x[j] >= k {
i = j;
u = (l + u) / 2.0;
}
else if x[j] < k {
k -= x[j];
i = j + 1;
l = (l + u) / 2.0;
}
}
quant * (u - l) + l
}
pub fn edm_tail(z: &[f64], min_size: usize, alpha: f64) -> (usize, f64) {
let quant = 0.5;
let n = z.len();
let mut eps = ceil(ln(n as f64)) as i32;
eps = eps.max(10);
let mut info = Information::new(eps, min_size);
let mut tau1 = info.min_size;
let mut tau2 = tau1 * 2;
for i in 0..tau1 {
for j in i + 1..tau1 {
let mut index = get_index(info.b, z[i] - z[j]);
while index != 0 {
info.a[index] += 1.0;
index /= 2;
}
}
}
for i in tau1..tau2 {
for j in i + 1..tau2 {
let mut index = get_index(info.b, z[i] - z[j]);
while index != 0 {
info.bv[index] += 1.0;
index /= 2;
}
}
}
for i in 0..tau1 {
for j in tau1..tau2 {
let mut index = get_index(info.b, z[i] - z[j]);
while index != 0 {
info.ab[index] += 1.0;
index /= 2;
}
}
}
let qa = powf(get_quantile(&info.a, quant), alpha);
let mut qb = powf(get_quantile(&info.bv, quant), alpha);
let qc = powf(get_quantile(&info.ab, quant), alpha);
let mut stat = 2.0 * qc - qa - qb;
stat *= (tau1 * (tau2 - tau1) / tau2) as f64;
info.best_stat = stat;
info.best_loc = tau1 as i32;
info.best_t2 = tau2 as i32;
tau2 += 1;
while tau2 < n + 1 {
let mut index = get_index(info.b, z[tau2 - 1] - z[tau2 - 2]);
while index != 0 {
info.bv[index] += 1.0;
index /= 2;
}
qb = powf(get_quantile(&info.bv, quant), alpha);
stat = 2.0 * qc - qa - qb;
stat *= ((tau2 - tau1) * tau1 / tau2) as f64;
if stat > info.best_stat {
info.best_stat = stat;
info.best_loc = tau1 as i32;
info.best_t2 = tau2 as i32;
}
tau2 += 1;
}
let mut forward_move = false;
while tau1 < n - min_size {
if forward_move {
tau1 = forward_update(z, &mut info, tau1, quant, alpha);
} else {
tau1 = backward_update(z, &mut info, tau1, quant, alpha);
}
forward_move = !forward_move;
}
(info.best_loc as usize, info.best_stat)
}
fn forward_update(z: &[f64], info: &mut Information, tau1: usize, quant: f64, alpha: f64) -> usize {
let min_size = info.min_size;
let mut tau2 = tau1 + min_size;
let mut tau1 = tau1;
tau1 += 1;
let n = z.len();
let mut index;
for i in tau1 - min_size..tau1 - 1 {
index = get_index(info.b, z[i] - z[tau1 - 1]);
while index != 0 {
info.a[index] += 1.0;
index /= 2;
}
}
for i in tau1 - min_size..tau1 {
index = get_index(info.b, z[i] - z[tau1 - min_size - 1]);
while index != 0 {
info.a[index] -= 1.0;
index /= 2;
}
}
index = get_index(info.b, z[tau1 - min_size - 1] - z[tau1 - min_size]);
while index != 0 {
info.a[index] += 1.0;
index /= 2;
}
let qa = powf(get_quantile(&info.a, quant), alpha);
index = get_index(info.b, z[tau1 - 1] - z[tau1 - min_size - 1]);
while index != 0 {
info.ab[index] -= 1.0;
index /= 2;
}
for i in tau1..tau2 {
index = get_index(info.b, z[i] - z[tau1 - min_size - 1]);
while index != 0 {
info.ab[index] -= 1.0;
index /= 2;
}
index = get_index(info.b, z[i] - z[tau1 - 1]);
while index != 0 {
info.ab[index] += 1.0;
index /= 2;
}
}
for i in tau1 - min_size..tau1 - 1 {
index = get_index(info.b, z[i] - z[tau1 - 1]);
while index != 0 {
info.ab[index] -= 1.0;
index /= 2;
}
index = get_index(info.b, z[i] - z[tau2]);
while index != 0 {
info.ab[index] += 1.0;
index /= 2;
}
}
index = get_index(info.b, z[tau1 - 1] - z[tau2]);
while index != 0 {
info.ab[index] += 1.0;
index /= 2;
}
let qc = powf(get_quantile(&info.ab, quant), alpha);
for i in tau1..tau2 {
index = get_index(info.b, z[i] - z[tau1 - 1]);
while index != 0 {
info.bv[index] += 1.0;
index /= 2;
}
index = get_index(info.b, z[i] - z[tau2]);
while index != 0 {
info.bv[index] += 1.0;
index /= 2;
}
}
tau2 += 1;
while tau2 < n + 1 {
index = get_index(info.b, z[tau2 - 1] - z[tau2 - 2]);
while index != 0 {
info.bv[index] += 1.0;
index /= 2;
}
let qb = powf(get_quantile(&info.bv, quant), alpha);
let mut stat = 2.0 * qc - qa - qb;
stat *= ((tau2 - tau1) * tau1 / tau2) as f64;
if stat > info.best_stat {
info.best_stat = stat;
info.best_loc = tau1 as i32;
info.best_t2 = tau2 as i32;
}
tau2 += 1;
}
tau1
}
fn backward_update(
z: &[f64],
info: &mut Information,
tau1: usize,
quant: f64,
alpha: f64,
) -> usize {
let min_size = info.min_size;
let mut tau2 = tau1 + min_size;
let mut tau1 = tau1;
tau1 += 1;
let n = z.len();
let mut index;
for i in tau1 - min_size..tau1 - 1 {
index = get_index(info.b, z[i] - z[tau1 - 1]);
while index != 0 {
info.a[index] += 1.0;
index /= 2;
}
}
for i in tau1 - min_size..tau1 {
index = get_index(info.b, z[i] - z[tau1 - min_size - 1]);
while index != 0 {
info.a[index] -= 1.0;
index /= 2;
}
}
index = get_index(info.b, z[tau1 - min_size - 1] - z[tau1 - min_size]);
while index != 0 {
info.a[index] += 1.0;
index /= 2;
}
let qa = powf(get_quantile(&info.a, quant), alpha);
index = get_index(info.b, z[tau1 - 1] - z[tau1 - min_size - 1]);
while index != 0 {
info.ab[index] -= 1.0;
index /= 2;
}
for i in tau1..tau2 {
index = get_index(info.b, z[i] - z[tau1 - min_size - 1]);
while index != 0 {
info.ab[index] -= 1.0;
index /= 2;
}
index = get_index(info.b, z[i] - z[tau1 - 1]);
while index != 0 {
info.ab[index] += 1.0;
index /= 2;
}
}
for i in tau1 - min_size..tau1 - 1 {
index = get_index(info.b, z[i] - z[tau1 - 1]);
while index != 0 {
info.ab[index] -= 1.0;
index /= 2;
}
index = get_index(info.b, z[i] - z[tau2]);
while index != 0 {
info.ab[index] += 1.0;
index /= 2;
}
}
index = get_index(info.b, z[tau1 - 1] - z[tau2]);
while index != 0 {
info.ab[index] += 1.0;
index /= 2;
}
let qc = powf(get_quantile(&info.ab, quant), alpha);
for i in tau1..tau1 + min_size - 1 {
index = get_index(info.b, z[tau1 + min_size - 1] - z[i]);
while index != 0 {
info.bv[index] += 1.0;
index /= 2;
}
index = get_index(info.b, z[i] - z[tau1 - 1]);
while index != 0 {
info.bv[index] -= 1.0;
index /= 2;
}
}
tau2 = n;
while tau2 >= tau1 + min_size {
index = get_index(info.b, z[tau2 - 1] - z[tau2 - 2]);
while index != 0 {
info.bv[index] += 1.0;
index /= 2;
}
let qb = powf(get_quantile(&info.bv, quant), alpha);
let mut stat = 2.0 * qc - qa - qb;
stat *= ((tau2 - tau1) * tau1 / tau2) as f64;
if stat > info.best_stat {
info.best_stat = stat;
info.best_loc = tau1 as i32;
info.best_t2 = tau2 as i32;
}
tau2 -= 1;
}
tau1
}