use std::fmt::Display;
use std::fmt::Formatter;
use crate::auspice::Auspice;
use crate::auspice::Auspicious;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Trigram {
Qian,
Dui,
Li,
Zhen,
Xun,
Kan,
Gen,
Kun,
}
impl Trigram {
const fn index(self) -> usize {
match self {
Self::Qian => 0,
Self::Dui => 1,
Self::Li => 2,
Self::Zhen => 3,
Self::Xun => 4,
Self::Kan => 5,
Self::Gen => 6,
Self::Kun => 7,
}
}
pub const fn from_palace(palace: u8) -> Option<Trigram> {
match palace {
1 => Some(Self::Kan),
2 => Some(Self::Kun),
3 => Some(Self::Zhen),
4 => Some(Self::Xun),
6 => Some(Self::Qian),
7 => Some(Self::Dui),
8 => Some(Self::Gen),
9 => Some(Self::Li),
_ => None,
}
}
pub const fn name(self) -> &'static str {
match self {
Self::Qian => "乾",
Self::Dui => "兑",
Self::Li => "离",
Self::Zhen => "震",
Self::Xun => "巽",
Self::Kan => "坎",
Self::Gen => "艮",
Self::Kun => "坤",
}
}
pub const fn symbol(self) -> &'static str {
match self {
Self::Qian => "☰",
Self::Dui => "☱",
Self::Li => "☲",
Self::Zhen => "☳",
Self::Xun => "☴",
Self::Kan => "☵",
Self::Gen => "☶",
Self::Kun => "☷",
}
}
pub const fn element_name(self) -> &'static str {
match self {
Self::Qian => "天",
Self::Dui => "泽",
Self::Li => "火",
Self::Zhen => "雷",
Self::Xun => "风",
Self::Kan => "水",
Self::Gen => "山",
Self::Kun => "地",
}
}
}
impl Display for Trigram {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(self.name()) }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Hexagram {
upper: Trigram,
lower: Trigram,
index: u8,
}
impl Hexagram {
pub const fn new(upper: Trigram, lower: Trigram) -> Self {
let index = HEXAGRAM_INDEX[upper.index()][lower.index()];
Self { upper, lower, index }
}
pub const fn upper(&self) -> Trigram { self.upper }
pub const fn lower(&self) -> Trigram { self.lower }
pub const fn index(&self) -> u8 { self.index }
pub const fn symbol(&self) -> &'static str { HEXAGRAM_DATA[self.index as usize].1 }
}
impl Auspicious for Hexagram {
fn name(&self) -> &'static str { HEXAGRAM_DATA[self.index as usize].0 }
fn summary(&self) -> &'static str { HEXAGRAM_DATA[self.index as usize].2 }
fn auspice(&self) -> Auspice { HEXAGRAM_AUSPICE[self.index as usize] }
}
impl Display for Hexagram {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{} {}", self.symbol(), self.name()) }
}
const HEXAGRAM_INDEX: [[u8; 8]; 8] = [
[0, 9, 12, 24, 43, 5, 32, 11], [42, 57, 48, 16, 27, 46, 30, 44], [13, 37, 29, 20, 49, 63, 55, 34], [33, 53, 54, 50, 31, 39, 61, 15], [8, 60, 36, 41, 56, 58, 52, 19], [4, 59, 62, 2, 47, 28, 38, 7], [25, 40, 21, 26, 17, 3, 51, 22], [10, 18, 35, 23, 45, 6, 14, 1], ];
#[allow(clippy::type_complexity)]
const HEXAGRAM_DATA: [(&str, &str, &str); 64] = [
("乾为天", "䷀", "六十四卦之首。元亨利贞,刚健中正,自强不息。"),
("坤为地", "䷁", "六十四卦第二卦。厚德载物,柔顺利贞,包容万物。"),
("水雷屯", "䷂", "六十四卦之一。元亨利贞,初始艰难,宜守不宜进。"),
("山水蒙", "䷃", "六十四卦之一。亨通成功,童蒙求教,循循善诱。"),
("水天需", "䷄", "六十四卦之一。有孚,光亨,贞吉。待时而动,饮食宴乐。"),
("天水讼", "䷅", "六十四卦之一。有孚窒惕,中吉终凶。息事宁人,防范争端。"),
("地水师", "䷆", "六十四卦之一。师贞,丈人吉,无咎。纪律严明,统帅有道。"),
("水地比", "䷇", "六十四卦之一。吉。原筮,元永贞,无咎。团结亲比,辅佐共赢。"),
("风天小畜", "䷈", "六十四卦之一。亨,密云不雨,自我西郊。小有积蓄,力量尚微。"),
("天泽履", "䷉", "六十四卦之一。履虎尾,不咥人,亨。如履薄冰,循礼而行。"),
("地天泰", "䷊", "六十四卦之一。小往大来,吉亨,天地交泰。"),
("天地否", "䷋", "六十四卦之一。否之匪人,不利君子贞,闭塞不通。"),
("天火同人", "䷌", "六十四卦之一。同人于野,亨。志同道合,团结协作。"),
("火天大有", "䷍", "六十四卦之一。元亨。火在天上,普照万物。繁荣昌盛,大获成功。"),
("地山谦", "䷎", "六十四卦之一。亨,君子有终。卑以自牧,谦受益。"),
("雷地豫", "䷏", "六十四卦之一。利建侯行师。和乐喜悦,顺时而动。"),
("泽雷随", "䷐", "六十四卦之一。元亨利贞,无咎。随遇而安,顺应潮流。"),
("山风蛊", "䷑", "六十四卦之一。元亨,利涉大川。振疲起顿,改革弊端。"),
("地泽临", "䷒", "六十四卦之一。元亨利贞,至于八月有凶。督导推进,把握时机。"),
("风地观", "䷓", "六十四卦之一。盥而不荐,有孚颙若。观察感悟,风行地上。"),
("火雷噬嗑", "䷔", "六十四卦之一。亨。利用狱。障碍阻隔,果断铲除。"),
("山火贲", "䷕", "六十四卦之一。亨,小利有攸往。装饰与文饰,文明与礼仪。"),
("山地剥", "䷖", "六十四卦之一。不利有攸往。阴长阳消,剥落殆尽。"),
("地雷复", "䷗", "六十四卦之一。亨,出入无疾,朋来无咎。生机重现,剥极必复。"),
("天雷无妄", "䷘", "六十四卦之一。元亨利贞,匪正有眚。顺应自然,不妄求。"),
("山天大畜", "䷙", "六十四卦之一。利贞,不家食吉,利涉大川。积蓄硕大,大有作为。"),
("山雷颐", "䷚", "六十四卦之一。贞吉。观颐,自求口实。颐养天年,言行有节。"),
("泽风大过", "䷛", "六十四卦之一。栋桡。利有攸往,亨。重压之下,果断行动。"),
("坎为水", "䷜", "六十四卦之一。习坎,有孚维心,亨。险难重重,需内怀诚信。"),
("离为火", "䷝", "六十四卦之一。利贞,亨。畜牝牛吉。明亮美丽,依附守正。"),
("泽山咸", "䷞", "六十四卦之一。亨,利贞。取女吉。感应交融,心心相印。"),
("雷风恒", "䷟", "六十四卦之一。亨,无咎,利贞。持之以恒,恒久而不变。"),
("天山遁", "䷠", "六十四卦之一。遁亨,小利贞。退避守正,以退为进。"),
("雷天大壮", "䷡", "六十四卦之一。利贞。盛大强壮,需坚守正道防偏激。"),
("火地晋", "䷢", "六十四卦之一。康侯用锡马蕃庶,昼日三接。光明普照,晋升腾达。"),
("地火明夷", "䷣", "六十四卦之一。利艰贞。韬光养晦,在黑暗中保存光明。"),
("风火家人", "䷤", "六十四卦之一。利女贞。各尽其道,家和万事兴。"),
("火泽睽", "䷥", "六十四卦之一。小事吉。二女同居,其志不同。异中求同,化解对立。"),
("水山蹇", "䷦", "六十四卦之一。利西南,不利东北。利见大人,贞吉。进退维谷,宜反求诸己。"),
("雷水解", "䷧", "六十四卦之一。利西南。无所往,其来复吉。解脱困境,化解矛盾。"),
("山泽损", "䷨", "六十四卦之一。有孚,元吉,无咎,可贞。损下益上,诚信为本。"),
("风雷益", "䷩", "六十四卦之一。利有攸往,利涉大川。损上益下,获利匪浅。"),
("泽天夬", "䷪", "六十四卦之一。扬于王庭,孚号有厉。果断抉择,清除障碍。"),
("天风姤", "䷫", "六十四卦之一。天下有风,阴长阳消,防范微隐。"),
("泽地萃", "䷬", "六十四卦之一。亨。王假有庙。利见大人,亨。精英聚会,资源整合。"),
("地风升", "䷭", "六十四卦之一。元亨,用见大人,勿恤。步步高升,积微成大。"),
("泽水困", "䷮", "六十四卦之一。亨,贞,大人吉,无咎。穷困潦倒,言有不信。"),
("水风井", "䷯", "六十四卦之一。改邑不改井,无丧无得。源远流长,取之不尽。"),
("泽火革", "䷰", "六十四卦之一。己日乃孚。元亨利贞,悔亡。变革创新,面貌一新。"),
("火风鼎", "䷱", "六十四卦之一。元吉,亨。革故鼎新,稳重权柄。"),
("震为雷", "䷲", "六十四卦之一。震亨,震惊百里,不丧匕鬯。警示振作,恐惧修省。"),
("艮为山", "䷳", "六十四卦之一。艮其背,不获其身。动静不失其时,止其所当止。"),
("风山渐", "䷴", "六十四卦之一。女归吉,利贞。循序渐进,草木渐茂。"),
("雷泽归妹", "䷵", "六十四卦之一。征凶,无攸利。不当其位,需防偏离正道。"),
("雷火丰", "䷶", "六十四卦之一。亨,王假之。日中则昃,盛极之时当忧。"),
("火山旅", "䷷", "六十四卦之一。小亨,旅贞吉。身在异乡,寻求安身。"),
("巽为风", "䷸", "六十四卦之一。小亨,利攸往。随风潜入,谦逊顺服。"),
("兑为泽", "䷹", "六十四卦之一。亨,利贞。悦随其后,和悦交流。"),
("风水涣", "䷺", "六十四卦之一。亨,王假有庙。涣散离析,需重新整合。"),
("水泽节", "䷻", "六十四卦之一。亨,苦节不可贞。节制有度,不可过头。"),
("风泽中孚", "䷼", "六十四卦之一。豚鱼吉。中心诚信,感化万物。"),
("雷山小过", "䷽", "六十四卦之一。亨,利贞。可小事,不可大事。过犹不及,宜下不宜上。"),
("水火既济", "䷾", "六十四卦之一。亨,小利贞,初吉终乱。"),
("火水未济", "䷿", "六十四卦末卦。亨,但须谨慎,事尚未完成。"),
];
const HEXAGRAM_AUSPICE: [Auspice; 64] = [
Auspice::GreatAuspicious, Auspice::Auspicious, Auspice::Inauspicious, Auspice::Neutral, Auspice::Auspicious, Auspice::Inauspicious, Auspice::Neutral, Auspice::Auspicious, Auspice::Neutral, Auspice::Auspicious, Auspice::GreatAuspicious, Auspice::GreatInauspicious, Auspice::Auspicious, Auspice::GreatAuspicious, Auspice::Auspicious, Auspice::Auspicious, Auspice::Auspicious, Auspice::Neutral, Auspice::Auspicious, Auspice::Neutral, Auspice::Neutral, Auspice::Neutral, Auspice::GreatInauspicious, Auspice::Auspicious, Auspice::Neutral, Auspice::Auspicious, Auspice::Auspicious, Auspice::Inauspicious, Auspice::GreatInauspicious, Auspice::Auspicious, Auspice::Auspicious, Auspice::Neutral, Auspice::Neutral, Auspice::Auspicious, Auspice::Auspicious, Auspice::Inauspicious, Auspice::Auspicious, Auspice::Neutral, Auspice::GreatInauspicious, Auspice::Auspicious, Auspice::Neutral, Auspice::Auspicious, Auspice::Neutral, Auspice::Neutral, Auspice::Auspicious, Auspice::Auspicious, Auspice::GreatInauspicious, Auspice::Neutral, Auspice::Neutral, Auspice::Auspicious, Auspice::Neutral, Auspice::Neutral, Auspice::Auspicious, Auspice::Inauspicious, Auspice::Neutral, Auspice::Neutral, Auspice::Neutral, Auspice::Auspicious, Auspice::Neutral, Auspice::Neutral, Auspice::Auspicious, Auspice::Neutral, Auspice::Neutral, Auspice::Neutral, ];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trigram_palace_mapping() {
assert_eq!(Trigram::from_palace(1), Some(Trigram::Kan));
assert_eq!(Trigram::from_palace(4), Some(Trigram::Xun));
assert_eq!(Trigram::from_palace(5), None);
assert_eq!(Trigram::from_palace(9), Some(Trigram::Li));
}
#[test]
fn hexagram_dui_xun_is_da_guo() {
let h = Hexagram::new(Trigram::Dui, Trigram::Xun);
assert_eq!(h.name(), "泽风大过");
assert_eq!(h.symbol(), "䷛");
assert_eq!(h.index(), 27);
assert_eq!(h.auspice(), Auspice::Inauspicious);
assert!(h.summary().contains("栋桡"));
}
#[test]
fn hexagram_qian_qian_is_qian_wei_tian() {
let h = Hexagram::new(Trigram::Qian, Trigram::Qian);
assert_eq!(h.name(), "乾为天");
assert_eq!(h.symbol(), "䷀");
assert_eq!(h.auspice(), Auspice::GreatAuspicious);
}
#[test]
fn hexagram_kun_kun_is_kun_wei_di() {
let h = Hexagram::new(Trigram::Kun, Trigram::Kun);
assert_eq!(h.name(), "坤为地");
assert_eq!(h.symbol(), "䷁");
}
#[test]
fn hexagram_kan_kan_is_great_inauspicious() {
let h = Hexagram::new(Trigram::Kan, Trigram::Kan);
assert_eq!(h.name(), "坎为水");
assert_eq!(h.auspice(), Auspice::GreatInauspicious);
}
#[test]
fn all_64_combinations_have_data() {
let trigrams = [
Trigram::Qian,
Trigram::Dui,
Trigram::Li,
Trigram::Zhen,
Trigram::Xun,
Trigram::Kan,
Trigram::Gen,
Trigram::Kun,
];
let mut indices = std::collections::HashSet::new();
for u in trigrams {
for l in trigrams {
let h = Hexagram::new(u, l);
assert!(!h.name().is_empty());
assert!(!h.symbol().is_empty());
assert!(!h.summary().is_empty());
indices.insert(h.index());
}
}
assert_eq!(indices.len(), 64, "all 64 hexagrams must be unique");
}
}