#![allow(non_snake_case)]
use super::helpers::get_new_config_with_selected_table;
use super::survivals::{dx, lx};
use crate::RSLifeResult;
use crate::mt_config::MortTableConfig;
use crate::param::SingleLifeParams;
use bon::builder;
#[builder]
pub fn Cx(
mt: &MortTableConfig,
i: f64,
x: f64,
entry_age: Option<u32>,
#[builder(default = true)] validate: bool,
) -> RSLifeResult<f64> {
if validate {
let params = SingleLifeParams {
mt: mt.clone(),
i,
x,
n: 0.0, t: 0.0, m: 1, moment: 1, entry_age,
};
params
.validate_all()
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?;
}
let v = 1.0 / (1.0 + i);
let mt = get_new_config_with_selected_table(mt, entry_age)?;
let dx = dx().x(x).mt(&mt).call()?;
let result = v.powf(x + 1.0) * dx;
Ok(result)
}
#[builder]
pub fn Dx(
mt: &MortTableConfig,
i: f64,
x: f64,
entry_age: Option<u32>,
#[builder(default = true)] validate: bool,
) -> RSLifeResult<f64> {
if validate {
let params = SingleLifeParams {
mt: mt.clone(),
i,
x,
n: 0.0, t: 0.0, m: 1, moment: 1, entry_age,
};
params
.validate_all()
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?;
};
let v = 1.0 / (1.0 + i);
let mt = get_new_config_with_selected_table(mt, entry_age)?;
let lx = lx().x(x).mt(&mt).call()?;
let result = v.powf(x) * lx;
Ok(result)
}
#[builder]
pub fn Mx(
mt: &MortTableConfig,
i: f64,
x: f64,
entry_age: Option<u32>,
#[builder(default = true)] validate: bool,
) -> RSLifeResult<f64> {
if validate {
let params = SingleLifeParams {
mt: mt.clone(),
i,
x,
n: 0.0, t: 0.0, m: 1, moment: 1, entry_age,
};
params
.validate_all()
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?;
};
let mt = get_new_config_with_selected_table(mt, entry_age)?;
let max_age = mt.max_age()? as u32;
let summation = (x as u32..=max_age).fold(0.0, |acc, k| {
let Cx = Cx()
.mt(&mt)
.i(i)
.x(k as f64)
.validate(false)
.call()
.unwrap_or(0.0);
acc + Cx
});
Ok(summation)
}
#[builder]
pub fn Nx(
mt: &MortTableConfig,
i: f64,
x: f64,
entry_age: Option<u32>,
#[builder(default = true)] validate: bool,
) -> RSLifeResult<f64> {
if validate {
let params = SingleLifeParams {
mt: mt.clone(),
i,
x,
n: 0.0, t: 0.0, m: 1, moment: 1, entry_age,
};
params
.validate_all()
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?;
};
let mt = get_new_config_with_selected_table(mt, entry_age)?;
let max_age = mt.max_age()? as u32;
let summation = (x as u32..=max_age).fold(0.0, |acc, k| {
let Dx = Dx()
.mt(&mt)
.i(i)
.x(k as f64)
.validate(false)
.call()
.unwrap_or(0.0);
acc + Dx
});
Ok(summation)
}
#[builder]
pub fn Rx(
mt: &MortTableConfig,
i: f64,
x: f64,
entry_age: Option<u32>,
#[builder(default = true)] validate: bool,
) -> RSLifeResult<f64> {
if validate {
let params = SingleLifeParams {
mt: mt.clone(),
i,
x,
n: 0.0, t: 0.0, m: 1, moment: 1, entry_age,
};
params
.validate_all()
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?;
};
let mt = get_new_config_with_selected_table(mt, entry_age)?;
let max_age = mt.max_age()? as u32;
let summation = (x as u32..=max_age).fold(0.0, |acc, k| {
let Mx = Mx()
.mt(&mt)
.i(i)
.x(k as f64)
.validate(false)
.call()
.unwrap_or(0.0);
acc + Mx
});
Ok(summation)
}
#[builder]
pub fn Sx(
mt: &MortTableConfig,
i: f64,
x: f64,
entry_age: Option<u32>,
#[builder(default = true)] validate: bool,
) -> RSLifeResult<f64> {
if validate {
let params = SingleLifeParams {
mt: mt.clone(),
i,
x,
n: 0.0, t: 0.0, m: 1, moment: 1, entry_age,
};
params
.validate_all()
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?;
};
let mt = get_new_config_with_selected_table(mt, entry_age)?;
let max_age = mt.max_age()? as u32;
let summation = (x as u32..=max_age).fold(0.0, |acc, k| {
let Nx = Nx()
.mt(&mt)
.i(i)
.x(k as f64)
.validate(false)
.call()
.unwrap_or(0.0);
acc + Nx
});
Ok(summation)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mt_config::MortTableConfig;
use crate::mt_config::mt_data::MortData;
use approx::assert_abs_diff_eq;
#[test]
fn test_fn_Cx() {
let am92 = MortData::from_builtin("AM92").expect("Failed to load AM92 selected table");
let mt = MortTableConfig::builder()
.data(am92)
.radix(10_000)
.build()
.unwrap();
let ans = Cx().mt(&mt).i(0.04).x(23.0).call().unwrap();
let expected = 2.21;
assert_abs_diff_eq!(ans, expected, epsilon = 1e-2);
}
#[test]
fn test_fn_Dx() {
let am92 = MortData::from_builtin("AM92").expect("Failed to load AM92 selected table");
let mt = MortTableConfig::builder()
.data(am92)
.radix(10_000)
.build()
.unwrap();
let ans = Dx().mt(&mt).i(0.04).x(47.0).entry_age(46).call().unwrap();
let expected = 1546.49;
assert_abs_diff_eq!(ans, expected, epsilon = 1e-2);
}
#[test]
fn test_fn_Mx() {
let am92 = MortData::from_builtin("AM92").expect("Failed to load AM92 selected table");
let mt = MortTableConfig::builder()
.data(am92)
.radix(10_000)
.build()
.unwrap();
let ans = Mx().mt(&mt).i(0.04).x(62.0).entry_age(60).call().unwrap();
let expected = 388.83;
assert_abs_diff_eq!(ans, expected, epsilon = 1e-2);
}
#[test]
fn test_fn_Nx() {
let am92 = MortData::from_builtin("AM92").expect("Failed to load AM92 selected table");
let mt = MortTableConfig::builder()
.data(am92)
.radix(10_000)
.build()
.unwrap();
let ans = Nx().mt(&mt).i(0.04).x(33.0).entry_age(32).call().unwrap();
let expected = 57987.98;
assert_abs_diff_eq!(ans, expected, epsilon = 1e-2);
}
#[test]
fn test_fn_Rx() {
let am92 = MortData::from_builtin("AM92").expect("Failed to load AM92 selected table");
let mt = MortTableConfig::builder()
.data(am92)
.radix(10_000)
.build()
.unwrap();
let ans = Rx().mt(&mt).i(0.04).x(26.0).entry_age(26).call().unwrap();
let expected = 23141.58;
assert_abs_diff_eq!(ans, expected, epsilon = 1e-2);
}
#[test]
fn test_fn_Sx() {
let am92 = MortData::from_builtin("AM92").expect("Failed to load AM92 selected table");
let mt = MortTableConfig::builder()
.data(am92)
.radix(10_000)
.build()
.unwrap();
let ans = Sx().mt(&mt).i(0.04).x(65.0).entry_age(65).call().unwrap();
let expected = 78505.54;
assert_abs_diff_eq!(ans, expected, epsilon = 1e-2);
}
}