use std::ops::{Add, BitOr, Mul};

pub use 地支::*;
pub use 天干::*;
pub use 局勢::*;
pub use 干支::*;
pub use 相合::*;

pub use 不會合 as 不会合;
pub use 局勢 as 局势;
pub use 會 as 会;

use crate::{
    五行, 北, 南, 四方, 土, 有, 有无, 有無, 木, 東, 水, 火, 無, 西, 金, 阴阳, 陰, 陰陽, 陽,
};

/// 甲乙丙丁戊己庚辛壬癸
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum 天干 {
    甲,
    乙,
    丙,
    丁,
    戊,
    己,
    庚,
    辛,
    壬,
    癸,
}

impl 天干 {
    pub const 陽干: [Self; 5] = [甲, 丙, 戊, 庚, 壬];
    pub const 陰干: [Self; 5] = [乙, 丁, 己, 辛, 癸];

    pub const 阴干: [Self; 5] = Self::陰干;
    pub const 阳干: [Self; 5] = Self::陽干;

    /// | 天干 | 陰陽 | 五行 |
    /// | --- | ---- | ---- |
    /// | 甲 | 陽 | 木 |
    /// | 乙 | 陰 | 木 |
    /// | 丙 | 陽 | 火 |
    /// | 丁 | 陰 | 火 |
    /// | 戊 | 陽 | 土 |
    /// | 己 | 陰 | 土 |
    /// | 庚 | 陽 | 金 |
    /// | 辛 | 陰 | 金 |
    /// | 壬 | 陽 | 水 |
    /// | 癸 | 陰 | 水 |
    /// ```
    /// # use taoism::*;
    /// assert_eq!(甲.陰陽五行(), (陽, 木));
    /// assert_eq!(乙.陰陽五行(), (陰, 木));
    /// assert_eq!(丙.陰陽五行(), (陽, 火));
    /// assert_eq!(丁.陰陽五行(), (陰, 火));
    /// assert_eq!(戊.陰陽五行(), (陽, 土));
    /// assert_eq!(己.陰陽五行(), (陰, 土));
    /// assert_eq!(庚.陰陽五行(), (陽, 金));
    /// assert_eq!(辛.陰陽五行(), (陰, 金));
    /// assert_eq!(壬.陰陽五行(), (陽, 水));
    /// assert_eq!(癸.陰陽五行(), (陰, 水));
    /// ```
    pub const fn 陰陽五行(self) -> (陰陽, 五行) {
        match self {
            甲 => (陽, 木),
            乙 => (陰, 木),
            丙 => (陽, 火),
            丁 => (陰, 火),
            戊 => (陽, 土),
            己 => (陰, 土),
            庚 => (陽, 金),
            辛 => (陰, 金),
            壬 => (陽, 水),
            癸 => (陰, 水),
        }
    }

    /// 見[`陰陽五行`](Self::陰陽五行)。
    pub const fn 阴阳五行(self) -> (阴阳, 五行) {
        self.陰陽五行()
    }

    /// | 天干 | 陰陽 |
    /// | ---- | ---- |
    /// | 甲、丙、戊、庚、壬 | 陽 |
    /// | 乙、丁、己、辛、癸 | 陰 |
    pub const fn 陰陽(self) -> 陰陽 {
        self.陰陽五行().0
    }

    /// 見[`陰陽`](Self::陰陽)。
    pub const fn 阴阳(self) -> 阴阳 {
        self.陰陽()
    }

    /// | 天干 | 五行 |
    /// | ---- | ---- |
    /// | 甲、乙 | 木 |
    /// | 丙、丁 | 火 |
    /// | 戊、己 | 土 |
    /// | 庚、辛 | 金 |
    /// | 壬、癸 | 水 |
    pub const fn 五行(self) -> 五行 {
        self.陰陽五行().1
    }

    /// - 甲庚相沖
    /// - 乙辛相沖
    /// - 丙壬相沖
    /// - 丁癸相沖
    /// - 戊己相沖
    /// ```
    /// # use taoism::*;
    /// assert_eq!(甲.沖(), 有(庚));
    /// assert_eq!(乙.沖(), 有(辛));
    /// assert_eq!(丙.沖(), 有(壬));
    /// assert_eq!(丁.沖(), 有(癸));
    /// assert_eq!(戊.沖(), 無);
    /// assert_eq!(己.沖(), 無);
    /// assert_eq!(庚.沖(), 有(甲));
    /// assert_eq!(辛.沖(), 有(乙));
    /// assert_eq!(壬.沖(), 有(丙));
    /// assert_eq!(癸.沖(), 有(丁));
    /// ```
    pub const fn 沖(self) -> 有無<天干> {
        match self {
            甲 => 有(庚),
            乙 => 有(辛),
            丙 => 有(壬),
            丁 => 有(癸),
            戊 | 己 => 無,
            庚 => 有(甲),
            辛 => 有(乙),
            壬 => 有(丙),
            癸 => 有(丁),
        }
    }

    /// 見[`沖`](Self::沖)。
    pub const fn 冲(self) -> 有无<天干> {
        self.沖()
    }

    /// - 甲乙木克戊己土
    /// - 丙丁火克庚辛金
    /// - 戊己土克壬癸水
    /// - 庚辛金克甲乙木
    /// - 壬癸水克丙丁火
    /// ```
    /// # use taoism::*;
    /// assert_eq!(甲.克(), 戊);
    /// assert_eq!(乙.克(), 己);
    /// assert_eq!(丙.克(), 庚);
    /// assert_eq!(丁.克(), 辛);
    /// assert_eq!(戊.克(), 壬);
    /// assert_eq!(己.克(), 癸);
    /// assert_eq!(庚.克(), 甲);
    /// assert_eq!(辛.克(), 乙);
    /// assert_eq!(壬.克(), 丙);
    /// assert_eq!(癸.克(), 丁);
    /// ```
    pub const fn 克(self) -> 天干 {
        match self {
            甲 => 戊,
            乙 => 己,
            丙 => 庚,
            丁 => 辛,
            戊 => 壬,
            己 => 癸,
            庚 => 甲,
            辛 => 乙,
            壬 => 丙,
            癸 => 丁,
        }
    }
}

impl Add for 天干 {
    type Output = 相合;

    /// - 甲己合化土
    /// - 乙庚合化金
    /// - 丙辛合化水
    /// - 丁壬合化木
    /// - 戊癸合化火
    /// ```
    /// # use taoism::*;
    /// assert_eq!(甲 + 己, 合化(土));
    /// assert_eq!(己 + 甲, 合化(土));
    /// assert_eq!(乙 + 庚, 合化(金));
    /// assert_eq!(庚 + 乙, 合化(金));
    /// assert_eq!(丙 + 辛, 合化(水));
    /// assert_eq!(辛 + 丙, 合化(水));
    /// assert_eq!(丁 + 壬, 合化(木));
    /// assert_eq!(壬 + 丁, 合化(木));
    /// assert_eq!(戊 + 癸, 合化(火));
    /// assert_eq!(癸 + 戊, 合化(火));
    ///
    /// assert_eq!(甲 + 乙, 不合);
    /// assert_eq!(乙 + 甲, 不合);
    /// assert_eq!(丙 + 丁, 不合);
    /// assert_eq!(丁 + 丙, 不合);
    /// assert_eq!(戊 + 己, 不合);
    /// assert_eq!(己 + 戊, 不合);
    /// assert_eq!(庚 + 辛, 不合);
    /// ```
    fn add(self, other: Self) -> Self::Output {
        match (self, other) {
            (甲, 己) | (己, 甲) => 合化(土),
            (乙, 庚) | (庚, 乙) => 合化(金),
            (丙, 辛) | (辛, 丙) => 合化(水),
            (丁, 壬) | (壬, 丁) => 合化(木),
            (戊, 癸) | (癸, 戊) => 合化(火),
            _ => 不合,
        }
    }
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum 相合 {
    不合,
    /// 天干相合：
    /// - 甲己合化土
    /// - 乙庚合化金
    /// - 丙辛合化水
    /// - 丁壬合化木
    /// - 戊癸合化火
    ///
    /// 地支六合：
    /// - 子丑合化土
    /// - 寅亥合化木
    /// - 卯戌合化火
    /// - 辰酉合化金
    /// - 巳申合化水
    /// - 午未爲陰陽，中正合化土
    合化(五行),
}

impl 相合 {
    pub const fn 有無(self) -> 有無<五行> {
        match self {
            不合 => 無,
            合化(五行) => 有(五行),
        }
    }
}

/// 子丑寅卯辰巳午未申酉戌亥
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum 地支 {
    子,
    丑,
    寅,
    卯,
    辰,
    巳,
    午,
    未,
    申,
    酉,
    戌,
    亥,
}

impl 地支 {
    pub const 陽支: [Self; 6] = [子, 寅, 辰, 午, 申, 戌];
    pub const 陰支: [Self; 6] = [丑, 卯, 巳, 未, 酉, 亥];

    pub const 阴支: [Self; 6] = Self::陰支;
    pub const 阳支: [Self; 6] = Self::陽支;

    /// | 地支 | 陰陽 | 五行 |
    /// | ---- | ---- | ---- |
    /// | 寅 | 陽  | 木  |
    /// | 卯 | 陰  | 木  |
    /// | 午 | 陽  | 火  |
    /// | 巳 | 陰  | 火  |
    /// | 辰、戌 | 陽 | 土  |
    /// | 丑、未 | 陰 | 土  |
    /// | 申 | 陽 | 金 |
    /// | 酉 | 陰 | 金 |
    /// | 子 | 陽 | 水 |
    /// | 亥 | 陰 | 水 |
    /// ```
    /// # use taoism::*;
    /// assert_eq!(寅.陰陽五行(), (陽, 木));
    /// assert_eq!(卯.陰陽五行(), (陰, 木));
    /// assert_eq!(午.陰陽五行(), (陽, 火));
    /// assert_eq!(巳.陰陽五行(), (陰, 火));
    /// assert_eq!(辰.陰陽五行(), (陽, 土));
    /// assert_eq!(戌.陰陽五行(), (陽, 土));
    /// assert_eq!(丑.陰陽五行(), (陰, 土));
    /// assert_eq!(未.陰陽五行(), (陰, 土));
    /// assert_eq!(申.陰陽五行(), (陽, 金));
    /// assert_eq!(酉.陰陽五行(), (陰, 金));
    /// assert_eq!(子.陰陽五行(), (陽, 水));
    /// assert_eq!(亥.陰陽五行(), (陰, 水));
    /// ```
    pub const fn 陰陽五行(self) -> (陰陽, 五行) {
        match self {
            寅 => (陽, 木),
            卯 => (陰, 木),
            午 => (陽, 火),
            巳 => (陰, 火),
            辰 | 戌 => (陽, 土),
            丑 | 未 => (陰, 土),
            申 => (陽, 金),
            酉 => (陰, 金),
            子 => (陽, 水),
            亥 => (陰, 水),
        }
    }

    /// 見[`陰陽五行`](Self::陰陽五行)。
    pub const fn 阴阳五行(self) -> (阴阳, 五行) {
        self.陰陽五行()
    }

    /// | 地支 | 陰陽 |
    /// | ---- | ---- |
    /// | 子、寅、辰、午、申、戌 | 陽 |
    /// | 丑、卯、巳、未、酉、亥 | 陰 |
    pub const fn 陰陽(self) -> 陰陽 {
        self.陰陽五行().0
    }

    /// 見[`陰陽`](Self::陰陽)。
    pub const fn 阴阳(self) -> 阴阳 {
        self.陰陽()
    }

    /// | 地支 | 五行 |
    /// | ---- | ---- |
    /// | 寅、卯 | 木 |
    /// | 午、巳 | 火 |
    /// | 辰、戌、丑、未 | 土 |
    /// | 申、酉 | 金 |
    /// | 子、亥 | 水 |
    pub const fn 五行(self) -> 五行 {
        self.陰陽五行().1
    }

    /// | 地支 | 藏干 |
    /// | ---- | ---- |
    /// | 子 | 癸 |
    /// | 丑 | 己, 癸, 辛 |
    /// | 寅 | 甲, 丙, 戊 |
    /// | 卯 | 乙 |
    /// | 辰 | 戊, 乙, 癸 |
    /// | 巳 | 丙, 庚, 戊 |
    /// | 午 | 丁, 己 |
    /// | 未 | 乙, 己, 丁 |
    /// | 申 | 庚, 壬, 戊 |
    /// | 酉 | 辛 |
    /// | 戌 | 戊, 辛, 丁 |
    /// | 亥 | 壬, 甲 |
    /// ```
    /// # use taoism::*;
    /// assert_eq!(子.藏干(), &[癸]);
    /// assert_eq!(丑.藏干(), &[己, 癸, 辛]);
    /// assert_eq!(寅.藏干(), &[甲, 丙, 戊]);
    /// assert_eq!(卯.藏干(), &[乙]);
    /// assert_eq!(辰.藏干(), &[戊, 乙, 癸]);
    /// assert_eq!(巳.藏干(), &[丙, 庚, 戊]);
    /// assert_eq!(午.藏干(), &[丁, 己]);
    /// assert_eq!(未.藏干(), &[乙, 己, 丁]);
    /// assert_eq!(申.藏干(), &[庚, 壬, 戊]);
    /// assert_eq!(酉.藏干(), &[辛]);
    /// assert_eq!(戌.藏干(), &[戊, 辛, 丁]);
    /// assert_eq!(亥.藏干(), &[壬, 甲]);
    /// ```
    pub const fn 藏干(self) -> &'static [天干] {
        match self {
            子 => &[癸],
            丑 => &[己, 癸, 辛],
            寅 => &[甲, 丙, 戊],
            卯 => &[乙],
            辰 => &[戊, 乙, 癸],
            巳 => &[丙, 庚, 戊],
            午 => &[丁, 己],
            未 => &[乙, 己, 丁],
            申 => &[庚, 壬, 戊],
            酉 => &[辛],
            戌 => &[戊, 辛, 丁],
            亥 => &[壬, 甲],
        }
    }

    /// - 子午相沖
    /// - 丑未相沖
    /// - 寅申相沖
    /// - 卯酉相沖
    /// - 辰戌相沖
    /// - 巳亥相沖
    /// ```
    /// # use taoism::*;
    /// assert_eq!(子.沖(), 午);
    /// assert_eq!(午.沖(), 子);
    /// assert_eq!(丑.沖(), 未);
    /// assert_eq!(未.沖(), 丑);
    /// assert_eq!(寅.沖(), 申);
    /// assert_eq!(申.沖(), 寅);
    /// assert_eq!(卯.沖(), 酉);
    /// assert_eq!(酉.沖(), 卯);
    /// assert_eq!(戌.沖(), 辰);
    /// assert_eq!(辰.沖(), 戌);
    /// assert_eq!(巳.沖(), 亥);
    /// assert_eq!(亥.沖(), 巳);
    /// ```
    pub const fn 沖(self) -> Self {
        match self {
            子 => 午,
            丑 => 未,
            寅 => 申,
            卯 => 酉,
            辰 => 戌,
            巳 => 亥,
            午 => 子,
            未 => 丑,
            申 => 寅,
            酉 => 卯,
            戌 => 辰,
            亥 => 巳,
        }
    }

    /// 見[`沖`](Self::沖)。
    pub const fn 冲(self) -> Self {
        self.沖()
    }

    /// - 子酉相破
    /// - 午卯相破
    /// - 巳申相破
    /// - 寅亥相破
    /// - 辰丑相破
    /// - 戌未相破
    /// ```
    /// # use taoism::*;
    /// assert_eq!(子.破(), 酉);
    /// assert_eq!(酉.破(), 子);
    /// assert_eq!(午.破(), 卯);
    /// assert_eq!(卯.破(), 午);
    /// assert_eq!(巳.破(), 申);
    /// assert_eq!(申.破(), 巳);
    /// assert_eq!(寅.破(), 亥);
    /// assert_eq!(亥.破(), 寅);
    /// assert_eq!(辰.破(), 丑);
    /// assert_eq!(丑.破(), 辰);
    /// assert_eq!(戌.破(), 未);
    /// assert_eq!(未.破(), 戌);
    /// ```
    pub const fn 破(self) -> Self {
        match self {
            子 => 酉,
            丑 => 辰,
            寅 => 亥,
            卯 => 午,
            辰 => 丑,
            巳 => 申,
            午 => 卯,
            未 => 戌,
            申 => 巳,
            酉 => 子,
            戌 => 未,
            亥 => 寅,
        }
    }

    /// - 子未相害
    /// - 丑午相害
    /// - 寅巳相害
    /// - 卯辰相害
    /// - 申亥相害
    /// - 酉戌相害
    /// ```
    /// # use taoism::*;
    /// assert_eq!(子.害(), 未);
    /// assert_eq!(未.害(), 子);
    /// assert_eq!(丑.害(), 午);
    /// assert_eq!(午.害(), 丑);
    /// assert_eq!(寅.害(), 巳);
    /// assert_eq!(巳.害(), 寅);
    /// assert_eq!(卯.害(), 辰);
    /// assert_eq!(辰.害(), 卯);
    /// assert_eq!(申.害(), 亥);
    /// assert_eq!(亥.害(), 申);
    /// assert_eq!(酉.害(), 戌);
    /// assert_eq!(戌.害(), 酉);
    /// ```
    pub const fn 害(self) -> Self {
        match self {
            子 => 未,
            丑 => 午,
            寅 => 巳,
            卯 => 辰,
            辰 => 卯,
            巳 => 寅,
            午 => 丑,
            未 => 子,
            申 => 亥,
            酉 => 戌,
            戌 => 酉,
            亥 => 申,
        }
    }

    /// - 寅刑巳、巳刑申、申刑寅，爲無恩之刑
    /// - 未刑丑、丑刑戌、戌刑未，爲持勢之刑
    /// - 子刑卯、卯刑子、爲無禮之刑
    /// - 辰刑辰、午刑午、酉刑酉、亥刑亥，爲自刑之刑
    /// ```
    /// # use taoism::*;
    /// assert_eq!(寅.刑(), 巳);
    /// assert_eq!(巳.刑(), 申);
    /// assert_eq!(申.刑(), 寅);
    ///
    /// assert_eq!(未.刑(), 丑);
    /// assert_eq!(丑.刑(), 戌);
    /// assert_eq!(戌.刑(), 未);
    ///
    /// assert_eq!(子.刑(), 卯);
    /// assert_eq!(卯.刑(), 子);
    ///
    /// assert_eq!(辰.刑(), 辰);
    /// assert_eq!(午.刑(), 午);
    /// assert_eq!(酉.刑(), 酉);
    /// assert_eq!(亥.刑(), 亥);
    /// ```
    pub const fn 刑(self) -> Self {
        match self {
            子 => 卯,
            丑 => 戌,
            寅 => 巳,
            卯 => 子,
            辰 => 辰,
            巳 => 申,
            午 => 午,
            未 => 丑,
            申 => 寅,
            酉 => 酉,
            戌 => 未,
            亥 => 亥,
        }
    }
}

impl Add for 地支 {
    type Output = 相合;
    /// - 子丑合化土
    /// - 寅亥合化木
    /// - 卯戌合化火
    /// - 辰酉合化金
    /// - 巳申合化水
    /// - 午未為陰陽，中正合化土
    fn add(self, other: Self) -> Self::Output {
        match (self, other) {
            (子, 丑) | (丑, 子) => 合化(土),
            (寅, 亥) | (亥, 寅) => 合化(木),
            (卯, 戌) | (戌, 卯) => 合化(火),
            (辰, 酉) | (酉, 辰) => 合化(金),
            (巳, 申) | (申, 巳) => 合化(水),
            (午, 未) | (未, 午) => 合化(土),
            _ => 不合,
        }
    }
}

#[doc(hidden)]
pub struct 地支組合(u16);

impl BitOr for 地支 {
    type Output = 地支組合;
    /// 見[`局勢`]。
    /// ```
    /// # use taoism::*;
    /// assert_eq!(申 | 子 | 辰, 合成(水));
    /// assert_eq!(巳 | 酉 | 丑, 合成(金));
    /// assert_eq!(寅 | 午 | 戌, 合成(火));
    /// assert_eq!(亥 | 卯 | 未, 合成(木));
    /// assert_eq!(亥 | 子 | 丑, 會(北, 水));
    /// assert_eq!(寅 | 卯 | 辰, 會(東, 木));
    /// assert_eq!(巳 | 午 | 未, 會(南, 火));
    /// assert_eq!(申 | 酉 | 戌, 會(西, 金));
    ///
    /// assert_eq!(申 | 子 | 丑, 不會合);
    /// assert_eq!(寅 | 卯 | 巳, 不會合);
    /// ```
    fn bitor(self, other: Self) -> Self::Output {
        地支組合((1 << self as u16) | (1 << other as u16))
    }
}

impl BitOr<地支> for 地支組合 {
    type Output = 局勢;
    fn bitor(self, other: 地支) -> Self::Output {
        let 組合 = self.0 | (1 << other as u16);
        let [
            子支,
            丑支,
            寅支,
            卯支,
            辰支,
            巳支,
            午支,
            未支,
            申支,
            酉支,
            戌支,
            亥支,
        ] = [子, 丑, 寅, 卯, 辰, 巳, 午, 未, 申, 酉, 戌, 亥].map(|地支| 1 << (地支 as u16));
        match 組合 {
            _ if 組合 == (申支 | 子支 | 辰支) => 合成(水),
            _ if 組合 == (巳支 | 酉支 | 丑支) => 合成(金),
            _ if 組合 == (寅支 | 午支 | 戌支) => 合成(火),
            _ if 組合 == (亥支 | 卯支 | 未支) => 合成(木),
            _ if 組合 == (亥支 | 子支 | 丑支) => 會(北, 水),
            _ if 組合 == (寅支 | 卯支 | 辰支) => 會(東, 木),
            _ if 組合 == (巳支 | 午支 | 未支) => 會(南, 火),
            _ if 組合 == (申支 | 酉支 | 戌支) => 會(西, 金),
            _ => return 不會合,
        }
    }
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum 局勢 {
    不會合,
    /// 地支三合：
    /// - 申子辰合成水局
    /// - 巳酉丑合成金局
    /// - 寅午戌合成火局
    /// - 亥卯未合成木局
    合成(五行),
    /// 地支三會：
    /// - 亥子丑會北方水局
    /// - 寅卯辰會東方木局
    /// - 巳午未會南方火局
    /// - 申酉戌會西方金局
    會(四方, 五行),
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[rustfmt::skip]
pub enum 干支 {
    甲子, 乙丑, 丙寅, 丁卯, 戊辰, 己巳, 庚午, 辛未, 壬申, 癸酉,
    甲戌, 乙亥, 丙子, 丁丑, 戊寅, 己卯, 庚辰, 辛巳, 壬午, 癸未,
    甲申, 乙酉, 丙戌, 丁亥, 戊子, 己丑, 庚寅, 辛卯, 壬辰, 癸巳,
    甲午, 乙未, 丙申, 丁酉, 戊戌, 己亥, 庚子, 辛丑, 壬寅, 癸卯,
    甲辰, 乙巳, 丙午, 丁未, 戊申, 己酉, 庚戌, 辛亥, 壬子, 癸丑,
    甲寅, 乙卯, 丙辰, 丁巳, 戊午, 己未, 庚申, 辛酉, 壬戌, 癸亥,
}

impl 干支 {
    pub const fn 天干(self) -> 天干 {
        match self {
            甲子 | 甲戌 | 甲申 | 甲午 | 甲辰 | 甲寅 => 甲,
            乙丑 | 乙亥 | 乙酉 | 乙未 | 乙巳 | 乙卯 => 乙,
            丙寅 | 丙子 | 丙戌 | 丙申 | 丙午 | 丙辰 => 丙,
            丁卯 | 丁丑 | 丁亥 | 丁酉 | 丁未 | 丁巳 => 丁,
            戊辰 | 戊寅 | 戊子 | 戊戌 | 戊申 | 戊午 => 戊,
            己巳 | 己卯 | 己丑 | 己亥 | 己酉 | 己未 => 己,
            庚午 | 庚辰 | 庚寅 | 庚子 | 庚戌 | 庚申 => 庚,
            辛未 | 辛巳 | 辛卯 | 辛丑 | 辛亥 | 辛酉 => 辛,
            壬申 | 壬午 | 壬辰 | 壬寅 | 壬子 | 壬戌 => 壬,
            癸酉 | 癸未 | 癸巳 | 癸卯 | 癸丑 | 癸亥 => 癸,
        }
    }

    pub const fn 地支(self) -> 地支 {
        match self {
            甲子 | 丙子 | 戊子 | 庚子 | 壬子 => 子,
            乙丑 | 丁丑 | 己丑 | 辛丑 | 癸丑 => 丑,
            丙寅 | 戊寅 | 庚寅 | 壬寅 | 甲寅 => 寅,
            丁卯 | 己卯 | 辛卯 | 癸卯 | 乙卯 => 卯,
            戊辰 | 庚辰 | 壬辰 | 甲辰 | 丙辰 => 辰,
            己巳 | 辛巳 | 癸巳 | 乙巳 | 丁巳 => 巳,
            庚午 | 壬午 | 甲午 | 丙午 | 戊午 => 午,
            辛未 | 癸未 | 乙未 | 丁未 | 己未 => 未,
            壬申 | 甲申 | 丙申 | 戊申 | 庚申 => 申,
            癸酉 | 乙酉 | 丁酉 | 己酉 | 辛酉 => 酉,
            甲戌 | 丙戌 | 戊戌 | 庚戌 | 壬戌 => 戌,
            乙亥 | 丁亥 | 己亥 | 辛亥 | 癸亥 => 亥,
        }
    }

    pub const fn 陰陽(self) -> 陰陽 {
        self.天干().陰陽()
    }
}

impl Mul<地支> for 天干 {
    type Output = 干支;

    fn mul(self, 地支: 地支) -> Self::Output {
        match (self, 地支) {
            (甲, 子) => 甲子,
            (乙, 丑) => 乙丑,
            (丙, 寅) => 丙寅,
            (丁, 卯) => 丁卯,
            (戊, 辰) => 戊辰,
            (己, 巳) => 己巳,
            (庚, 午) => 庚午,
            (辛, 未) => 辛未,
            (壬, 申) => 壬申,
            (癸, 酉) => 癸酉,
            (甲, 戌) => 甲戌,
            (乙, 亥) => 乙亥,
            (丙, 子) => 丙子,
            (丁, 丑) => 丁丑,
            (戊, 寅) => 戊寅,
            (己, 卯) => 己卯,
            (庚, 辰) => 庚辰,
            (辛, 巳) => 辛巳,
            (壬, 午) => 壬午,
            (癸, 未) => 癸未,
            (甲, 申) => 甲申,
            (乙, 酉) => 乙酉,
            (丙, 戌) => 丙戌,
            (丁, 亥) => 丁亥,
            (戊, 子) => 戊子,
            (己, 丑) => 己丑,
            (庚, 寅) => 庚寅,
            (辛, 卯) => 辛卯,
            (壬, 辰) => 壬辰,
            (癸, 巳) => 癸巳,
            (甲, 午) => 甲午,
            (乙, 未) => 乙未,
            (丙, 申) => 丙申,
            (丁, 酉) => 丁酉,
            (戊, 戌) => 戊戌,
            (己, 亥) => 己亥,
            (庚, 子) => 庚子,
            (辛, 丑) => 辛丑,
            (壬, 寅) => 壬寅,
            (癸, 卯) => 癸卯,
            (甲, 辰) => 甲辰,
            (乙, 巳) => 乙巳,
            (丙, 午) => 丙午,
            (丁, 未) => 丁未,
            (戊, 申) => 戊申,
            (己, 酉) => 己酉,
            (庚, 戌) => 庚戌,
            (辛, 亥) => 辛亥,
            (壬, 子) => 壬子,
            (癸, 丑) => 癸丑,
            (甲, 寅) => 甲寅,
            (乙, 卯) => 乙卯,
            (丙, 辰) => 丙辰,
            (丁, 巳) => 丁巳,
            (戊, 午) => 戊午,
            (己, 未) => 己未,
            (庚, 申) => 庚申,
            (辛, 酉) => 辛酉,
            (壬, 戌) => 壬戌,
            (癸, 亥) => 癸亥,
            (甲 | 丙 | 戊 | 庚 | 壬, 丑 | 卯 | 巳 | 未 | 酉 | 亥) => {
                panic!("陽天干{self:?}與陰地支{地支:?}不合")
            }
            (乙 | 丁 | 己 | 辛 | 癸, 子 | 寅 | 辰 | 午 | 申 | 戌) => {
                panic!("陰天干{self:?}與陽地支{地支:?}不合")
            }
        }
    }
}

impl Mul<天干> for 地支 {
    type Output = 干支;
    fn mul(self, 天干: 天干) -> Self::Output {
        天干 * self
    }
}

#[cfg(test)]
mod tests {

    use std::panic::catch_unwind;

    use super::*;

    #[test]
    fn 測試_干支() {
        for 天干 in 天干::陽干 {
            for 地支 in 地支::陽支 {
                let 干支 = 天干 * 地支;
                assert_eq!(干支.天干(), 天干);
                assert_eq!(干支.地支(), 地支);
                let 干支 = 地支 * 天干;
                assert_eq!(干支.天干(), 天干);
                assert_eq!(干支.地支(), 地支);
            }

            for 地支 in 地支::陰支 {
                catch_unwind(|| 天干 * 地支).unwrap_err();
                catch_unwind(|| 地支 * 天干).unwrap_err();
            }
        }

        for 天干 in 天干::陰干 {
            for 地支 in 地支::陰支 {
                let 干支 = 天干 * 地支;
                assert_eq!(干支.天干(), 天干);
                assert_eq!(干支.地支(), 地支);
                let 干支 = 地支 * 天干;
                assert_eq!(干支.天干(), 天干);
                assert_eq!(干支.地支(), 地支);
            }
            for 地支 in 地支::陽支 {
                catch_unwind(|| 天干 * 地支).unwrap_err();
                catch_unwind(|| 地支 * 天干).unwrap_err();
            }
        }
    }

    #[test]
    fn 測試_天干_組合() {
        assert_eq!(甲 + 己, 合化(土));
        assert_eq!(己 + 甲, 合化(土));
        assert_eq!(乙 + 庚, 合化(金));
        assert_eq!(庚 + 乙, 合化(金));
        assert_eq!(丙 + 辛, 合化(水));
        assert_eq!(辛 + 丙, 合化(水));
        assert_eq!(丁 + 壬, 合化(木));
        assert_eq!(壬 + 丁, 合化(木));
        assert_eq!(戊 + 癸, 合化(火));
        assert_eq!(癸 + 戊, 合化(火));
        assert_eq!(甲 + 乙, 不合);
        assert_eq!(甲 + 丙, 不合);
        assert_eq!(甲 + 丁, 不合);
        assert_eq!(甲 + 戊, 不合);
    }

    #[test]
    fn 測試_地支_組合() {
        assert_eq!(子 + 丑, 合化(土));
        assert_eq!(寅 + 亥, 合化(木));
        assert_eq!(卯 + 戌, 合化(火));
        assert_eq!(辰 + 酉, 合化(金));
        assert_eq!(巳 + 申, 合化(水));
        assert_eq!(午 + 未, 合化(土));

        assert_eq!(子 | 丑 | 亥, 會(北, 水));
        assert_eq!(寅 | 卯 | 辰, 會(東, 木));
        assert_eq!(巳 | 午 | 未, 會(南, 火));
        assert_eq!(申 | 酉 | 戌, 會(西, 金));

        assert_eq!(申 | 子 | 辰, 合成(水));
        assert_eq!(巳 | 酉 | 丑, 合成(金));
        assert_eq!(寅 | 午 | 戌, 合成(火));
        assert_eq!(亥 | 卯 | 未, 合成(木));

        assert_eq!(子 | 丑 | 寅, 不會合);
        assert_eq!(寅 | 卯 | 亥, 不會合);
        assert_eq!(巳 | 午 | 申, 不會合);
        assert_eq!(申 | 酉 | 寅, 不會合);

        assert_eq!(子.沖(), 午);
        assert_eq!(午.沖(), 子);
        assert_eq!(丑.沖(), 未);
        assert_eq!(未.沖(), 丑);
        assert_eq!(寅.沖(), 申);
        assert_eq!(申.沖(), 寅);
        assert_eq!(卯.沖(), 酉);
        assert_eq!(酉.沖(), 卯);
        assert_eq!(辰.沖(), 戌);
        assert_eq!(戌.沖(), 辰);
        assert_eq!(巳.沖(), 亥);
        assert_eq!(亥.沖(), 巳);

        assert_eq!(子.破(), 酉);
        assert_eq!(酉.破(), 子);
        assert_eq!(午.破(), 卯);
        assert_eq!(卯.破(), 午);
        assert_eq!(巳.破(), 申);
        assert_eq!(申.破(), 巳);
        assert_eq!(寅.破(), 亥);
        assert_eq!(亥.破(), 寅);
        assert_eq!(辰.破(), 丑);
        assert_eq!(丑.破(), 辰);
        assert_eq!(戌.破(), 未);
        assert_eq!(未.破(), 戌);

        assert_eq!(子.害(), 未);
        assert_eq!(未.害(), 子);
        assert_eq!(丑.害(), 午);
        assert_eq!(午.害(), 丑);
        assert_eq!(寅.害(), 巳);
        assert_eq!(巳.害(), 寅);
        assert_eq!(卯.害(), 辰);
        assert_eq!(辰.害(), 卯);
        assert_eq!(申.害(), 亥);
        assert_eq!(亥.害(), 申);
        assert_eq!(酉.害(), 戌);
        assert_eq!(戌.害(), 酉);

        assert_eq!(寅.刑(), 巳);
        assert_eq!(巳.刑(), 申);
        assert_eq!(申.刑(), 寅);

        assert_eq!(未.刑(), 丑);
        assert_eq!(丑.刑(), 戌);
        assert_eq!(戌.刑(), 未);

        assert_eq!(子.刑(), 卯);
        assert_eq!(卯.刑(), 子);

        assert_eq!(辰.刑(), 辰);
        assert_eq!(午.刑(), 午);
        assert_eq!(酉.刑(), 酉);
        assert_eq!(亥.刑(), 亥);
    }
}
