use crate::data::stars::StarKey;
use crate::data::types::EarthlyBranch;
use crate::models::star::Star;
use crate::pattern::view::JYTL;
use crate::pattern::{ChartView, PatternHit, PatternKey, Rule};
pub static RULES: &[Rule] = &[
Rule {
key: PatternKey::JunChenQingHui,
detect: jun_chen_qing_hui,
},
Rule {
key: PatternKey::ZiFuTongGong,
detect: zi_fu_tong_gong,
},
Rule {
key: PatternKey::JinYuFuJia,
detect: jin_yu_fu_jia,
},
Rule {
key: PatternKey::ZiFuJiaMing,
detect: zi_fu_jia_ming,
},
Rule {
key: PatternKey::JiXiangLiMing,
detect: ji_xiang_li_ming,
},
Rule {
key: PatternKey::JiJuMaoYou,
detect: ji_ju_mao_you,
},
Rule {
key: PatternKey::JiYueTongLiang,
detect: ji_yue_tong_liang,
},
Rule {
key: PatternKey::ShanYinChaoGang,
detect: shan_yin_chao_gang,
},
Rule {
key: PatternKey::JiJuTongLin,
detect: ji_ju_tong_lin,
},
Rule {
key: PatternKey::JiJuJuMao,
detect: ji_ju_ju_mao,
},
];
const JI_YIN: [StarKey; 2] = [StarKey::TianjiMaj, StarKey::TaiyinMaj];
const TONG_LIANG: [StarKey; 2] = [StarKey::TiantongMaj, StarKey::TianliangMaj];
pub fn jun_chen_qing_hui(v: &ChartView) -> Vec<PatternHit> {
let s = v.soul();
let clean = v.no_sha(s);
let key = PatternKey::JunChenQingHui;
let mut hits = Vec::new();
let push = |hits: &mut Vec<PatternHit>, variant, stars: Vec<(usize, &Star)>| {
let mut hit = v.hit(key, s, stars);
hit.variant = Some(variant);
hits.push(hit);
};
if clean
&& let (Some(zi), Some(po), Some((pz, py))) = (
v.find(s, StarKey::ZiweiMaj),
v.find(s, StarKey::PojunMaj),
v.jia(s, &[StarKey::ZuofuMin], &[StarKey::YoubiMin]),
)
&& let (Some(zuo), Some(you)) =
(v.find(pz, StarKey::ZuofuMin), v.find(py, StarKey::YoubiMin))
{
push(
&mut hits,
"zi_po_zuo_you_jia",
vec![(s, zi), (s, po), (pz, zuo), (py, you)],
);
}
let o = v.opposite(s);
if clean
&& let (Some(zi), Some(xiang)) = (
v.find(s, StarKey::ZiweiMaj),
v.find(s, StarKey::TianxiangMaj),
)
&& let Some((chang, qu)) = v
.find(s, StarKey::WenchangMin)
.zip(v.find(o, StarKey::WenquMin))
.map(|(c, q)| ((s, c), (o, q)))
.or_else(|| {
v.find(s, StarKey::WenquMin)
.zip(v.find(o, StarKey::WenchangMin))
.map(|(q, c)| ((o, c), (s, q)))
})
{
push(
&mut hits,
"zi_xiang_chang_qu_axis",
vec![(s, zi), (s, xiang), chang, qu],
);
}
if clean && let Some(fu) = v.find(s, StarKey::TianfuMaj) {
let (prev, next) = (v.prev(s), v.next(s));
let four: Option<Vec<_>> = JYTL
.iter()
.map(|k| {
v.find_major_at(prev, *k)
.or_else(|| v.find_major_at(next, *k))
})
.collect();
if let Some(four) = four {
let mut stars = vec![(s, fu)];
stars.extend(four);
push(&mut hits, "tian_fu_ji_yin_tong_liang_jia", stars);
}
}
if let (Some(zi), Some(zuo), Some(you)) = (
v.find(s, StarKey::ZiweiMaj),
v.find(s, StarKey::ZuofuMin),
v.find(s, StarKey::YoubiMin),
) {
let mut hit = v.hit(key, s, vec![(s, zi), (s, zuo), (s, you)]);
hit.variant = Some("zi_zuo_you_tong_gong");
hit.broken = !clean;
hits.push(hit);
}
hits
}
pub fn zi_fu_tong_gong(v: &ChartView) -> Vec<PatternHit> {
let s = v.soul();
match (v.find(s, StarKey::ZiweiMaj), v.find(s, StarKey::TianfuMaj)) {
(Some(zi), Some(fu)) => vec![v.hit(PatternKey::ZiFuTongGong, s, vec![(s, zi), (s, fu)])],
_ => vec![],
}
}
pub fn jin_yu_fu_jia(v: &ChartView) -> Vec<PatternHit> {
let s = v.soul();
if !matches!(v.branch(s), EarthlyBranch::Chou | EarthlyBranch::Wei) {
return vec![];
}
let (Some(fu), Some((pr, pi))) = (
v.find(s, StarKey::TianfuMaj),
v.jia(s, &[StarKey::TaiyangMaj], &[StarKey::TaiyinMaj]),
) else {
return vec![];
};
let (Some(sun), Some(moon)) = (
v.find(pr, StarKey::TaiyangMaj),
v.find(pi, StarKey::TaiyinMaj),
) else {
return vec![];
};
vec![v.hit(
PatternKey::JinYuFuJia,
s,
vec![(s, fu), (pr, sun), (pi, moon)],
)]
}
pub fn zi_fu_jia_ming(v: &ChartView) -> Vec<PatternHit> {
let s = v.soul();
let (Some(ji), Some(yin), Some((pz, pf))) = (
v.find(s, StarKey::TianjiMaj),
v.find(s, StarKey::TaiyinMaj),
v.jia(s, &[StarKey::ZiweiMaj], &[StarKey::TianfuMaj]),
) else {
return vec![];
};
let (Some(zi), Some(fu)) = (
v.find(pz, StarKey::ZiweiMaj),
v.find(pf, StarKey::TianfuMaj),
) else {
return vec![];
};
vec![v.hit(
PatternKey::ZiFuJiaMing,
s,
vec![(s, ji), (s, yin), (pz, zi), (pf, fu)],
)]
}
pub fn ji_xiang_li_ming(v: &ChartView) -> Vec<PatternHit> {
let s = v.soul();
match v.find(s, StarKey::ZiweiMaj) {
Some(zi) if v.branch(s) == EarthlyBranch::Wu && v.no_sha(s) => {
vec![v.hit(PatternKey::JiXiangLiMing, s, vec![(s, zi)])]
}
_ => vec![],
}
}
pub fn ji_ju_mao_you(v: &ChartView) -> Vec<PatternHit> {
let s = v.soul();
if !matches!(v.branch(s), EarthlyBranch::Mao | EarthlyBranch::You) {
return vec![];
}
match (v.find(s, StarKey::ZiweiMaj), v.find(s, StarKey::TanlangMaj)) {
(Some(zi), Some(tan)) => vec![v.hit(PatternKey::JiJuMaoYou, s, vec![(s, zi), (s, tan)])],
_ => vec![],
}
}
pub fn ji_yue_tong_liang(v: &ChartView) -> Vec<PatternHit> {
let s = v.soul();
let mut hits = Vec::new();
if matches!(v.branch(s), EarthlyBranch::Yin | EarthlyBranch::Shen)
&& let Some(stars) = [TONG_LIANG, JI_YIN].into_iter().find_map(|pair| {
pair.iter()
.map(|k| v.find_major_at(s, *k))
.collect::<Option<Vec<_>>>()
})
{
hits.push(v.hit(PatternKey::JiYueTongLiang, s, stars));
}
let wide: Option<Vec<_>> = JYTL
.iter()
.map(|k| {
v.surround(s)
.into_iter()
.find_map(|p| v.find_major_at(p, *k))
})
.collect();
if let Some(stars) = wide {
let mut hit = v.hit(PatternKey::JiYueTongLiang, s, stars);
hit.variant = Some("surround");
hits.push(hit);
}
hits
}
pub fn shan_yin_chao_gang(v: &ChartView) -> Vec<PatternHit> {
v.soul_and_body()
.into_iter()
.filter_map(|p| {
let (ji, liang) = (
v.find(p, StarKey::TianjiMaj)?,
v.find(p, StarKey::TianliangMaj)?,
);
Some(v.hit(PatternKey::ShanYinChaoGang, p, vec![(p, ji), (p, liang)]))
})
.collect()
}
pub fn ji_ju_tong_lin(v: &ChartView) -> Vec<PatternHit> {
let s = v.soul();
let (Some(ji), Some(ju)) = (v.find(s, StarKey::TianjiMaj), v.find(s, StarKey::JumenMaj)) else {
return vec![];
};
let mut hit = v.hit(PatternKey::JiJuTongLin, s, vec![(s, ji), (s, ju)]);
if v.branch(s) == EarthlyBranch::You {
hit.variant = Some("you");
}
vec![hit]
}
pub fn ji_ju_ju_mao(v: &ChartView) -> Vec<PatternHit> {
let s = v.soul();
if v.branch(s) != EarthlyBranch::Mao {
return vec![];
}
match (v.find(s, StarKey::TianjiMaj), v.find(s, StarKey::JumenMaj)) {
(Some(ji), Some(ju)) => vec![v.hit(PatternKey::JiJuJuMao, s, vec![(s, ji), (s, ju)])],
_ => vec![],
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data::types::{Palace, Scope};
use crate::models::astrolabe::Astrolabe;
use crate::pattern::PatternConfig;
use crate::pattern::testutil::{find_chart, find_charts};
fn view(a: &Astrolabe) -> ChartView<'_> {
ChartView::natal(a, &PatternConfig::default())
}
fn any_major(v: &ChartView, i: usize, keys: &[StarKey]) -> bool {
keys.iter().any(|k| v.find_major_at(i, *k).is_some())
}
fn tian_fu_four(v: &ChartView, s: usize) -> bool {
let (p, n) = (v.prev(s), v.next(s));
JYTL.iter().all(|k| {
v.find_major_at(p, *k)
.or_else(|| v.find_major_at(n, *k))
.is_some()
})
}
fn soul_index(a: &Astrolabe) -> usize {
a.palaces
.iter()
.position(|p| p.name == Palace::Soul)
.unwrap()
}
fn variants(a: &Astrolabe, key: PatternKey) -> Vec<Option<&'static str>> {
a.patterns()
.iter()
.filter(|h| h.key == key)
.map(|h| h.variant)
.collect()
}
#[test]
fn jun_chen_qing_hui_zi_po_zuo_you_jia() {
let a = find_chart(|a| {
let v = view(a);
let s = v.soul();
v.no_sha(s)
&& v.has(s, StarKey::ZiweiMaj)
&& v.has(s, StarKey::PojunMaj)
&& v.jia(s, &[StarKey::ZuofuMin], &[StarKey::YoubiMin])
.is_some()
});
let v = view(&a);
let s = v.soul();
let hits = a.patterns();
let hit = hits
.iter()
.find(|h| h.key == PatternKey::JunChenQingHui && h.variant == Some("zi_po_zuo_you_jia"))
.expect("hit");
assert_eq!(hit.palace, s);
assert_eq!(hit.scope, Scope::Origin);
assert_eq!(hit.stars.len(), 4);
let sides: Vec<_> = hit
.stars
.iter()
.filter(|st| matches!(st.star, StarKey::ZuofuMin | StarKey::YoubiMin))
.map(|st| st.palace)
.collect();
assert!(sides.contains(&v.prev(s)) && sides.contains(&v.next(s)));
}
#[test]
fn jun_chen_qing_hui_zi_xiang_chang_qu_axis() {
let a = find_chart(|a| {
let v = view(a);
let (s, o) = (v.soul(), v.opposite(v.soul()));
v.no_sha(s)
&& v.has(s, StarKey::ZiweiMaj)
&& v.has(s, StarKey::TianxiangMaj)
&& ((v.has(s, StarKey::WenchangMin) && v.has(o, StarKey::WenquMin))
|| (v.has(s, StarKey::WenquMin) && v.has(o, StarKey::WenchangMin)))
});
let v = view(&a);
let hit = a
.patterns()
.into_iter()
.find(|h| {
h.key == PatternKey::JunChenQingHui && h.variant == Some("zi_xiang_chang_qu_axis")
})
.expect("hit");
let chang = hit
.stars
.iter()
.find(|st| st.star == StarKey::WenchangMin)
.unwrap();
let qu = hit
.stars
.iter()
.find(|st| st.star == StarKey::WenquMin)
.unwrap();
assert_eq!(qu.palace, v.opposite(chang.palace));
}
#[test]
fn jun_chen_qing_hui_tian_fu_needs_all_four_across_flanks() {
let a = find_chart(|a| {
let v = view(a);
let s = v.soul();
v.no_sha(s) && v.has(s, StarKey::TianfuMaj) && tian_fu_four(&v, s)
});
let v = view(&a);
let s = v.soul();
let hit = a
.patterns()
.into_iter()
.find(|h| {
h.key == PatternKey::JunChenQingHui
&& h.variant == Some("tian_fu_ji_yin_tong_liang_jia")
})
.expect("hit");
assert!(matches!(
v.branch(s),
EarthlyBranch::Si | EarthlyBranch::Hai
));
assert_eq!(hit.stars.len(), 5);
assert_eq!(hit.stars[0].star, StarKey::TianfuMaj);
assert!(hit.stars[1..].iter().all(|st| {
let d = (st.palace + 12 - s) % 12;
[1, 11, 5, 7].contains(&d)
}));
}
#[test]
fn jun_chen_qing_hui_tian_fu_rejects_partial_pairs() {
let a = find_chart(|a| {
let v = view(a);
let s = v.soul();
let (p, n) = (v.prev(s), v.next(s));
v.no_sha(s)
&& v.has(s, StarKey::TianfuMaj)
&& (any_major(&v, p, &JI_YIN) && any_major(&v, n, &TONG_LIANG)
|| any_major(&v, p, &TONG_LIANG) && any_major(&v, n, &JI_YIN))
&& !tian_fu_four(&v, s)
});
assert!(!a.patterns().iter().any(|h| {
h.key == PatternKey::JunChenQingHui
&& h.variant == Some("tian_fu_ji_yin_tong_liang_jia")
}));
}
#[test]
fn jun_chen_qing_hui_tian_fu_hits_only_si_hai() {
let hits = find_charts(8, |a| {
a.patterns().iter().any(|h| {
h.key == PatternKey::JunChenQingHui
&& h.variant == Some("tian_fu_ji_yin_tong_liang_jia")
})
});
for a in &hits {
let v = view(a);
assert!(matches!(
v.branch(v.soul()),
EarthlyBranch::Si | EarthlyBranch::Hai
));
assert!(v.has(v.soul(), StarKey::TianfuMaj));
}
}
#[test]
fn jun_chen_qing_hui_zi_zuo_you_tong_gong() {
let a = find_chart(|a| {
let v = view(a);
v.has_all(
v.soul(),
&[StarKey::ZiweiMaj, StarKey::ZuofuMin, StarKey::YoubiMin],
)
});
let v = view(&a);
let s = v.soul();
let hit = a
.patterns()
.into_iter()
.find(|h| {
h.key == PatternKey::JunChenQingHui && h.variant == Some("zi_zuo_you_tong_gong")
})
.expect("hit");
assert!(hit.stars.iter().all(|st| st.palace == s));
assert_eq!(hit.broken, !v.no_sha(s));
assert!(matches!(
v.branch(s),
EarthlyBranch::Chou | EarthlyBranch::Wei
));
}
#[test]
fn jun_chen_qing_hui_needs_clean_surround() {
let a = find_chart(|a| {
let v = view(a);
let s = v.soul();
!v.no_sha(s)
&& v.has(s, StarKey::ZiweiMaj)
&& v.has(s, StarKey::PojunMaj)
&& v.jia(s, &[StarKey::ZuofuMin], &[StarKey::YoubiMin])
.is_some()
});
assert!(!a.patterns().iter().any(|h| {
h.key == PatternKey::JunChenQingHui && h.variant != Some("zi_zuo_you_tong_gong")
}));
}
#[test]
fn zi_fu_tong_gong_hits_only_when_both_in_soul() {
let a = find_chart(|a| {
let v = view(a);
v.has_all(v.soul(), &[StarKey::ZiweiMaj, StarKey::TianfuMaj])
});
let hit = a
.patterns()
.into_iter()
.find(|h| h.key == PatternKey::ZiFuTongGong)
.expect("hit");
assert_eq!(hit.palace, soul_index(&a));
assert!(matches!(
view(&a).branch(hit.palace),
EarthlyBranch::Yin | EarthlyBranch::Shen
));
let b = find_chart(|a| {
let v = view(a);
v.has(v.soul(), StarKey::ZiweiMaj) && !v.has(v.soul(), StarKey::TianfuMaj)
});
assert!(
!b.patterns()
.iter()
.any(|h| h.key == PatternKey::ZiFuTongGong)
);
}
#[test]
fn jin_yu_fu_jia_needs_tianfu_in_chou_or_wei_jia_by_sun_moon() {
let a = find_chart(|a| {
let v = view(a);
let s = v.soul();
matches!(v.branch(s), EarthlyBranch::Chou | EarthlyBranch::Wei)
&& v.has(s, StarKey::TianfuMaj)
&& v.jia(s, &[StarKey::TaiyangMaj], &[StarKey::TaiyinMaj])
.is_some()
});
let v = view(&a);
let s = v.soul();
let hit = a
.patterns()
.into_iter()
.find(|h| h.key == PatternKey::JinYuFuJia)
.expect("hit");
assert_eq!(hit.palace, s);
let mut sides: Vec<_> = hit
.stars
.iter()
.filter(|st| st.star != StarKey::TianfuMaj)
.map(|st| st.palace)
.collect();
sides.sort_unstable();
let mut want = [v.prev(s), v.next(s)];
want.sort_unstable();
assert_eq!(sides, want);
let b = find_chart(|a| {
let v = view(a);
let s = v.soul();
matches!(v.branch(s), EarthlyBranch::Chou | EarthlyBranch::Wei)
&& !v.has(s, StarKey::TianfuMaj)
&& v.jia(s, &[StarKey::TaiyangMaj], &[StarKey::TaiyinMaj])
.is_some()
});
assert!(!b.patterns().iter().any(|h| h.key == PatternKey::JinYuFuJia));
}
#[test]
fn zi_fu_jia_ming_requires_ji_yin_in_soul() {
let a = find_chart(|a| {
let v = view(a);
v.has_all(v.soul(), &[StarKey::TianjiMaj, StarKey::TaiyinMaj])
});
let v = view(&a);
let s = v.soul();
let hit = a
.patterns()
.into_iter()
.find(|h| h.key == PatternKey::ZiFuJiaMing)
.expect("hit");
assert_eq!(hit.palace, s);
let zi = hit
.stars
.iter()
.find(|st| st.star == StarKey::ZiweiMaj)
.unwrap();
let fu = hit
.stars
.iter()
.find(|st| st.star == StarKey::TianfuMaj)
.unwrap();
assert!([v.prev(s), v.next(s)].contains(&zi.palace));
assert!([v.prev(s), v.next(s)].contains(&fu.palace));
let b = find_chart(|a| {
let v = view(a);
let s = v.soul();
v.jia(s, &[StarKey::ZiweiMaj], &[StarKey::TianfuMaj])
.is_some()
&& !v.has(s, StarKey::TianjiMaj)
});
assert!(
!b.patterns()
.iter()
.any(|h| h.key == PatternKey::ZiFuJiaMing)
);
}
#[test]
fn ji_xiang_li_ming_requires_wu_palace_without_sha() {
let a = find_chart(|a| {
let v = view(a);
let s = v.soul();
v.branch(s) == EarthlyBranch::Wu && v.has(s, StarKey::ZiweiMaj) && v.no_sha(s)
});
let hit = a
.patterns()
.into_iter()
.find(|h| h.key == PatternKey::JiXiangLiMing)
.expect("hit");
assert_eq!(hit.stars.len(), 1);
assert_eq!(hit.stars[0].star, StarKey::ZiweiMaj);
let b = find_chart(|a| {
let v = view(a);
let s = v.soul();
v.branch(s) == EarthlyBranch::Wu && v.has(s, StarKey::ZiweiMaj) && !v.no_sha(s)
});
assert!(
!b.patterns()
.iter()
.any(|h| h.key == PatternKey::JiXiangLiMing)
);
}
#[test]
fn ji_ju_mao_you_requires_zi_tan_in_mao_or_you() {
let a = find_chart(|a| {
let v = view(a);
v.has_all(v.soul(), &[StarKey::ZiweiMaj, StarKey::TanlangMaj])
});
let v = view(&a);
let hit = a
.patterns()
.into_iter()
.find(|h| h.key == PatternKey::JiJuMaoYou)
.expect("hit");
assert!(matches!(
v.branch(hit.palace),
EarthlyBranch::Mao | EarthlyBranch::You
));
let b = find_chart(|a| {
let v = view(a);
v.has(v.soul(), StarKey::TanlangMaj) && !v.has(v.soul(), StarKey::ZiweiMaj)
});
assert!(!b.patterns().iter().any(|h| h.key == PatternKey::JiJuMaoYou));
}
#[test]
fn ji_yue_tong_liang_hits_yin_shen_soul() {
let a = find_chart(|a| {
let v = view(a);
let s = v.soul();
matches!(v.branch(s), EarthlyBranch::Yin | EarthlyBranch::Shen)
&& !v.is_empty(s)
&& v.has_major(s, StarKey::TiantongMaj)
&& v.has_major(s, StarKey::TianliangMaj)
});
let hit = a
.patterns()
.into_iter()
.find(|h| h.key == PatternKey::JiYueTongLiang && h.variant.is_none())
.expect("hit");
assert_eq!(hit.palace, soul_index(&a));
assert_eq!(hit.stars.len(), 2);
assert_eq!(
variants(&a, PatternKey::JiYueTongLiang),
vec![None, Some("surround")]
);
let b = find_chart(|a| {
let v = view(a);
let s = v.soul();
matches!(v.branch(s), EarthlyBranch::Yin | EarthlyBranch::Shen)
&& v.has_major(s, StarKey::QishaMaj)
&& !JYTL
.iter()
.all(|k| v.surround(s).iter().any(|p| v.has_major(*p, *k)))
});
assert!(
!b.patterns()
.iter()
.any(|h| h.key == PatternKey::JiYueTongLiang)
);
}
#[test]
fn ji_yue_tong_liang_borrows_from_opposite() {
let a = find_chart(|a| {
let v = view(a);
let s = v.soul();
matches!(v.branch(s), EarthlyBranch::Yin | EarthlyBranch::Shen)
&& v.is_empty(s)
&& v.has_major(s, StarKey::TianjiMaj)
&& v.has_major(s, StarKey::TaiyinMaj)
});
let v = view(&a);
let s = v.soul();
let hit = a
.patterns()
.into_iter()
.find(|h| h.key == PatternKey::JiYueTongLiang && h.variant.is_none())
.expect("hit");
assert!(hit.stars.iter().all(|st| st.palace == v.opposite(s)));
}
#[test]
fn ji_yue_tong_liang_surround_variant_outside_yin_shen() {
let a = find_chart(|a| {
let v = view(a);
let s = v.soul();
!matches!(v.branch(s), EarthlyBranch::Yin | EarthlyBranch::Shen)
&& JYTL
.iter()
.all(|k| v.surround(s).iter().any(|p| v.has_major(*p, *k)))
});
assert_eq!(
variants(&a, PatternKey::JiYueTongLiang),
vec![Some("surround")]
);
let v = view(&a);
let hit = a
.patterns()
.into_iter()
.find(|h| h.key == PatternKey::JiYueTongLiang)
.expect("hit");
assert_eq!(hit.palace, v.soul());
assert_eq!(hit.stars.len(), 4);
}
#[test]
fn shan_yin_chao_gang_covers_soul_and_body() {
let a = find_chart(|a| {
let v = view(a);
v.soul_and_body()
.iter()
.any(|p| v.has_all(*p, &[StarKey::TianjiMaj, StarKey::TianliangMaj]))
});
let v = view(&a);
let hits: Vec<_> = a
.patterns()
.into_iter()
.filter(|h| h.key == PatternKey::ShanYinChaoGang)
.collect();
assert!(!hits.is_empty());
for h in &hits {
assert!(v.soul_and_body().contains(&h.palace));
assert!(matches!(
v.branch(h.palace),
EarthlyBranch::Chen | EarthlyBranch::Xu
));
assert!(h.stars.iter().all(|st| st.palace == h.palace));
}
let b = find_chart(|a| {
let v = view(a);
v.has(v.soul(), StarKey::TianjiMaj)
&& v.soul_and_body()
.iter()
.all(|p| !v.has(*p, StarKey::TianliangMaj))
});
assert!(
!b.patterns()
.iter()
.any(|h| h.key == PatternKey::ShanYinChaoGang)
);
}
#[test]
fn ji_ju_tong_lin_marks_you_variant() {
let mao = find_chart(|a| {
let v = view(a);
let s = v.soul();
v.branch(s) == EarthlyBranch::Mao
&& v.has_all(s, &[StarKey::TianjiMaj, StarKey::JumenMaj])
});
assert_eq!(variants(&mao, PatternKey::JiJuTongLin), vec![None]);
assert_eq!(variants(&mao, PatternKey::JiJuJuMao), vec![None]);
let you = find_chart(|a| {
let v = view(a);
let s = v.soul();
v.branch(s) == EarthlyBranch::You
&& v.has_all(s, &[StarKey::TianjiMaj, StarKey::JumenMaj])
});
assert_eq!(variants(&you, PatternKey::JiJuTongLin), vec![Some("you")]);
assert!(variants(&you, PatternKey::JiJuJuMao).is_empty());
let b = find_chart(|a| {
let v = view(a);
v.has(v.soul(), StarKey::JumenMaj) && !v.has(v.soul(), StarKey::TianjiMaj)
});
assert!(variants(&b, PatternKey::JiJuTongLin).is_empty());
assert!(variants(&b, PatternKey::JiJuJuMao).is_empty());
}
}