use crate::data::constants::{PALACES, TIGER_RULE};
use crate::data::earthly_branches::get_earthly_branch_info;
use crate::data::types::*;
use crate::models::palace::Decadal;
use crate::utils::{fix_index, get_age_index};
#[derive(Debug, Clone)]
pub struct SoulAndBody {
pub soul_index: usize,
pub body_index: usize,
pub heavenly_stem_of_soul: HeavenlyStem,
pub earthly_branch_of_soul: EarthlyBranch,
}
pub fn get_soul_and_body(
month_index: usize,
time_index: u8,
yearly_stem: HeavenlyStem,
) -> SoulAndBody {
let time_earthly_branch_index = fix_index(time_index as i32, 12);
let soul_index = fix_index(month_index as i32 - time_earthly_branch_index as i32, 12);
let body_index = fix_index(month_index as i32 + time_earthly_branch_index as i32, 12);
let start_stem = TIGER_RULE[yearly_stem.index()];
let heavenly_stem_of_soul_index = fix_index(start_stem.index() as i32 + soul_index as i32, 10);
let heavenly_stem_of_soul = HeavenlyStem::from_index(heavenly_stem_of_soul_index);
let earthly_branch_of_soul = EarthlyBranch::from_index(fix_index(soul_index as i32 + 2, 12));
SoulAndBody {
soul_index,
body_index,
heavenly_stem_of_soul,
earthly_branch_of_soul,
}
}
pub fn get_five_elements_class(stem: HeavenlyStem, branch: EarthlyBranch) -> FiveElementsClass {
let stem_number = stem.index() / 2 + 1;
let branch_number = fix_index(branch.index() as i32, 6) / 2 + 1;
let mut sum = stem_number + branch_number;
while sum > 5 {
sum -= 5;
}
match sum {
1 => FiveElementsClass::Wood3rd,
2 => FiveElementsClass::Metal4th,
3 => FiveElementsClass::Water2nd,
4 => FiveElementsClass::Fire6th,
5 => FiveElementsClass::Earth5th,
_ => unreachable!(),
}
}
pub fn get_palace_names(soul_index: usize) -> [Palace; 12] {
let mut names = [Palace::Soul; 12];
for i in 0..12 {
names[i] = PALACES[fix_index(i as i32 - soul_index as i32, 12)];
}
names
}
pub fn get_decadals_and_ages(
soul_index: usize,
five_elements_class: FiveElementsClass,
gender: Gender,
yearly_stem: HeavenlyStem,
yearly_branch: EarthlyBranch,
) -> ([Decadal; 12], [Vec<u32>; 12]) {
let gender_yinyang = match gender {
Gender::Male => YinYang::Yang,
Gender::Female => YinYang::Yin,
};
let branch_yinyang = get_earthly_branch_info(yearly_branch).yin_yang;
let forward = gender_yinyang == branch_yinyang;
let start_stem = TIGER_RULE[yearly_stem.index()];
let age_idx = get_age_index(yearly_branch);
let mut final_ages: [Vec<u32>; 12] = std::array::from_fn(|_| Vec::new());
for i in 0..12 {
let ages_for_palace: Vec<u32> = (0..10).map(|j| (12 * j + i + 1) as u32).collect();
let idx = match gender {
Gender::Male => fix_index(age_idx as i32 + i, 12),
Gender::Female => fix_index(age_idx as i32 - i, 12),
};
final_ages[idx] = ages_for_palace;
}
let mut final_decadals: [Option<Decadal>; 12] = std::array::from_fn(|_| None);
for i in 0..12 {
let idx = if forward {
fix_index(soul_index as i32 + i as i32, 12)
} else {
fix_index(soul_index as i32 - i as i32, 12)
};
let start_age = five_elements_class.value() + 10 * i;
let end_age = start_age + 9;
let stem_index = fix_index(start_stem.index() as i32 + idx as i32, 10);
let branch_index = fix_index(2 + idx as i32, 12);
final_decadals[idx] = Some(Decadal {
range: (start_age as u32, end_age as u32),
heavenly_stem: HeavenlyStem::from_index(stem_index),
earthly_branch: EarthlyBranch::from_index(branch_index),
});
}
let final_decadals: [Decadal; 12] =
final_decadals.map(|d| d.expect("all decadals should be filled"));
(final_decadals, final_ages)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_soul_and_body() {
let result = get_soul_and_body(0, 0, HeavenlyStem::Jia);
assert_eq!(result.soul_index, 0);
assert_eq!(result.body_index, 0);
assert_eq!(result.heavenly_stem_of_soul, HeavenlyStem::Bing);
assert_eq!(result.earthly_branch_of_soul, EarthlyBranch::Yin);
}
#[test]
fn test_get_soul_and_body_with_offset() {
let result = get_soul_and_body(2, 3, HeavenlyStem::Jia);
assert_eq!(result.soul_index, 11);
assert_eq!(result.body_index, 5);
}
#[test]
fn test_get_five_elements_class() {
let result = get_five_elements_class(HeavenlyStem::Bing, EarthlyBranch::Yin);
assert_eq!(result, FiveElementsClass::Fire6th);
let result = get_five_elements_class(HeavenlyStem::Jia, EarthlyBranch::Zi);
assert_eq!(result, FiveElementsClass::Metal4th);
}
#[test]
fn test_get_palace_names() {
let names = get_palace_names(0);
assert_eq!(names[0], Palace::Soul);
assert_eq!(names[1], Palace::Parents);
assert_eq!(names[11], Palace::Siblings);
let names = get_palace_names(1);
assert_eq!(names[0], Palace::Siblings);
assert_eq!(names[1], Palace::Soul);
}
#[test]
fn test_get_decadals_and_ages() {
let (decadals, ages) = get_decadals_and_ages(
0,
FiveElementsClass::Metal4th,
Gender::Male,
HeavenlyStem::Jia,
EarthlyBranch::Zi, );
assert_eq!(decadals[0].range, (4, 13));
for age_vec in &ages {
assert_eq!(age_vec.len(), 10);
}
}
}