SylarLong/iztro

View on GitHub
src/star/location.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { getHeavenlyStemAndEarthlyBranchBySolarDate, getTotalDaysOfLunarMonth, solar2lunar } from 'lunar-lite';
import { getConfig, getFiveElementsClass, getSoulAndBody } from '../astro';
import { EARTHLY_BRANCHES, FiveElementsClass, HEAVENLY_STEMS, PALACES } from '../data';
import {
  EarthlyBranchKey,
  EarthlyBranchName,
  FiveElementsClassKey,
  HeavenlyStemKey,
  HeavenlyStemName,
  kot,
} from '../i18n';
import { fixEarthlyBranchIndex, fixIndex, fixLunarDayIndex, fixLunarMonthIndex } from '../utils';

/**
 * 起紫微星诀算法
 *
 * - 六五四三二,酉午亥辰丑,
 * - 局数除日数,商数宫前走;
 * - 若见数无余,便要起虎口,
 * - 日数小於局,还直宫中守。
 *
 * 举例:
 * - 例一:27日出生木三局,以三除27,循环0次就可以整除,27➗3=9,从寅进9格,在戍安紫微。
 * - 例二:13日出生火六局,以六除13,最少需要加5才能整除, 18➗8=3,从寅进3格为辰,添加数为5(奇数),故要逆回五宫,在亥安紫微。
 * - 例三:6日出生土五局,以五除6,最少需要加4才能整除,10➗5=2,从寅进2格为卯,添加数为4(偶数),顺行4格为未,在未安紫微。
 *
 * @param solarDateStr 公历日期 YYYY-MM-DD
 * @param timeIndex 时辰索引【0~12】
 * @param fixLeap 是否调整农历闰月(若该月不是闰月则不会生效)
 * @returns 紫微和天府星所在宫位索引
 */
export const getStartIndex = (solarDateStr: string, timeIndex: number, fixLeap?: boolean) => {
  const { heavenlyStemOfSoul, earthlyBranchOfSoul } = getSoulAndBody(solarDateStr, timeIndex, fixLeap);
  const { lunarDay } = solar2lunar(solarDateStr);
  const fiveElements = kot<FiveElementsClassKey>(getFiveElementsClass(heavenlyStemOfSoul, earthlyBranchOfSoul));
  let remainder = -1; // 余数
  let quotient; // 商
  let offset = -1; // 循环次数

  // 获取当月最大天数
  const maxDays = getTotalDaysOfLunarMonth(solarDateStr);

  // 如果timeIndex等于12说明是晚子时,需要加一天
  let _day = timeIndex === 12 ? lunarDay + 1 : lunarDay;

  if (_day > maxDays) {
    // 假如日期超过当月最大天数,说明跨月了,需要处理为合法日期
    _day -= maxDays;
  }

  do {
    // 农历出生日(初一为1,以此类推)加上偏移量作为除数,以这个数处以五行局的数向下取整
    // 需要一直运算到余数为0为止
    offset++;

    const divisor = _day + offset;

    quotient = Math.floor(divisor / FiveElementsClass[fiveElements]);
    remainder = divisor % FiveElementsClass[fiveElements];
  } while (remainder !== 0);

  // 将商除以12取余数
  quotient %= 12;

  // 以商减一(因为需要从0开始)作为起始位置
  let ziweiIndex = quotient - 1;

  if (offset % 2 === 0) {
    // 若循环次数为偶数,则索引逆时针数到循环数
    ziweiIndex += offset;
  } else {
    // 若循环次数为偶数,则索引顺时针数到循环数
    ziweiIndex -= offset;
  }

  ziweiIndex = fixIndex(ziweiIndex);

  // 天府星位置与紫微星相对
  const tianfuIndex = fixIndex(12 - ziweiIndex);

  return { ziweiIndex, tianfuIndex };
};

/**
 * 按年干支计算禄存、擎羊,陀罗、天马的索引
 *
 * 定禄存、羊、陀诀(按年干)
 *
 * - 甲禄到寅宫,乙禄居卯府。
 * - 丙戊禄在巳,丁己禄在午。
 * - 庚禄定居申,辛禄酉上补。
 * - 壬禄亥中藏,癸禄居子户。
 * - 禄前羊刃当,禄后陀罗府。
 *
 * 安天马(按年支),天马只会出现在四马地【寅申巳亥】
 *
 * - 寅午戍流马在申,申子辰流马在寅。
 * - 巳酉丑流马在亥,亥卯未流马在巳。
 *
 * @param heavenlyStemName 天干
 * @param earthlyBranchName 地支
 * @returns 禄存、擎羊,陀罗、天马的索引
 */
export const getLuYangTuoMaIndex = (heavenlyStemName: HeavenlyStemName, earthlyBranchName: EarthlyBranchName) => {
  let luIndex = -1; // 禄存索引
  let maIndex = 0; // 天马索引

  const heavenlyStem = kot<HeavenlyStemKey>(heavenlyStemName, 'Heavenly');
  const earthlyBranch = kot<EarthlyBranchKey>(earthlyBranchName, 'Earthly');

  switch (earthlyBranch) {
    case 'yinEarthly':
    case 'wuEarthly':
    case 'xuEarthly':
      maIndex = fixEarthlyBranchIndex('shen');
      break;
    case 'shenEarthly':
    case 'ziEarthly':
    case 'chenEarthly':
      maIndex = fixEarthlyBranchIndex('yin');
      break;
    case 'siEarthly':
    case 'youEarthly':
    case 'chouEarthly':
      maIndex = fixEarthlyBranchIndex('hai');
      break;
    case 'haiEarthly':
    case 'maoEarthly':
    case 'weiEarthly':
      maIndex = fixEarthlyBranchIndex('si');
      break;
  }

  switch (heavenlyStem) {
    case 'jiaHeavenly': {
      luIndex = fixEarthlyBranchIndex('yin');
      break;
    }
    case 'yiHeavenly': {
      luIndex = fixEarthlyBranchIndex('mao');
      break;
    }
    case 'bingHeavenly':
    case 'wuHeavenly': {
      luIndex = fixEarthlyBranchIndex('si');
      break;
    }
    case 'dingHeavenly':
    case 'jiHeavenly': {
      luIndex = fixEarthlyBranchIndex('woo');
      break;
    }
    case 'gengHeavenly': {
      luIndex = fixEarthlyBranchIndex('shen');
      break;
    }
    case 'xinHeavenly': {
      luIndex = fixEarthlyBranchIndex('you');
      break;
    }
    case 'renHeavenly': {
      luIndex = fixEarthlyBranchIndex('hai');
      break;
    }
    case 'guiHeavenly': {
      luIndex = fixEarthlyBranchIndex('zi');
      break;
    }
  }

  return {
    luIndex,
    maIndex,
    yangIndex: fixIndex(luIndex + 1),
    tuoIndex: fixIndex(luIndex - 1),
  };
};

/**
 * 获取天魁天钺所在宫位索引(按年干)
 *
 * - 甲戊庚之年丑未
 * - 乙己之年子申
 * - 辛年午寅
 * - 壬癸之年卯巳
 * - 丙丁之年亥酉
 *
 * @param heavenlyStemName 天干
 * @returns
 */
export const getKuiYueIndex = (heavenlyStemName: HeavenlyStemName) => {
  let kuiIndex = -1;
  let yueIndex = -1;
  const heavenlyStem = kot<HeavenlyStemKey>(heavenlyStemName, 'Heavenly');

  switch (heavenlyStem) {
    case 'jiaHeavenly':
    case 'wuHeavenly':
    case 'gengHeavenly':
      kuiIndex = fixEarthlyBranchIndex('chou');
      yueIndex = fixEarthlyBranchIndex('wei');
      break;
    case 'yiHeavenly':
    case 'jiHeavenly':
      kuiIndex = fixEarthlyBranchIndex('zi');
      yueIndex = fixEarthlyBranchIndex('shen');
      break;
    case 'xinHeavenly':
      kuiIndex = fixEarthlyBranchIndex('woo');
      yueIndex = fixEarthlyBranchIndex('yin');
      break;
    case 'bingHeavenly':
    case 'dingHeavenly':
      kuiIndex = fixEarthlyBranchIndex('hai');
      yueIndex = fixEarthlyBranchIndex('you');
      break;
    case 'renHeavenly':
    case 'guiHeavenly':
      kuiIndex = fixEarthlyBranchIndex('mao');
      yueIndex = fixEarthlyBranchIndex('si');
      break;
  }

  return { kuiIndex, yueIndex };
};

/**
 * 获取左辅右弼的索引(按生月)
 *
 * - 辰上顺正寻左辅
 * - 戌上逆正右弼当
 *
 * 解释:
 *
 * - 从辰顺数农历月份数是左辅的索引
 * - 从戌逆数农历月份数是右弼的索引
 *
 * @param lunarMonth 农历月份
 * @returns 左辅、右弼索引
 */
export const getZuoYouIndex = (lunarMonth: number) => {
  const zuoIndex = fixIndex(fixEarthlyBranchIndex('chen') + (lunarMonth - 1));
  const youIndex = fixIndex(fixEarthlyBranchIndex('xu') - (lunarMonth - 1));

  return { zuoIndex, youIndex };
};

/**
 * 获取文昌文曲的索引(按时支)
 *
 * - 辰上顺时文曲位
 * - 戌上逆时觅文昌
 *
 * 解释:
 *
 * - 从辰顺数到时辰地支索引是文曲的索引
 * - 从戌逆数到时辰地支索引是文昌的索引
 *
 * 由于时辰地支的索引即是时辰的序号,所以可以直接使用时辰的序号
 *
 * @param timeIndex 时辰索引【0~12】
 * @returns 文昌、文曲索引
 */
export const getChangQuIndex = (timeIndex: number) => {
  const changIndex = fixIndex(fixEarthlyBranchIndex('xu') - fixIndex(timeIndex));
  const quIndex = fixIndex(fixEarthlyBranchIndex('chen') + fixIndex(timeIndex));

  return { changIndex, quIndex };
};

/**
 * 获取日系星索引,包括
 *
 * 三台,八座,恩光,天贵
 *
 * - 安三台八座
 *   - 由左辅之宫位起初一,顺行至生日安三台。
 *   - 由右弼之宫位起初一,逆行至生日安八座。
 *
 * - 安恩光天贵
 *   - 由文昌之宫位起初一,顺行至生日再退一步起恩光。
 *   - 由文曲之宫位起初一,顺行至生日再退一步起天贵。
 *
 * @param solarDateStr 阳历日期
 * @param timeIndex 时辰索引【0~12】
 * @returns 三台,八座索引
 */
export const getDailyStarIndex = (solarDateStr: string, timeIndex: number) => {
  const { lunarMonth, lunarDay } = solar2lunar(solarDateStr);
  const { zuoIndex, youIndex } = getZuoYouIndex(lunarMonth);
  const { changIndex, quIndex } = getChangQuIndex(timeIndex);
  const dayIndex = fixLunarDayIndex(lunarDay, timeIndex);

  const santaiIndex = fixIndex((zuoIndex + dayIndex) % 12);
  const bazuoIndex = fixIndex((youIndex - dayIndex) % 12);
  const enguangIndex = fixIndex(((changIndex + dayIndex) % 12) - 1);
  const tianguiIndex = fixIndex(((quIndex + dayIndex) % 12) - 1);

  return { santaiIndex, bazuoIndex, enguangIndex, tianguiIndex };
};

/**
 * 获取时系星耀索引,包括台辅,封诰
 *
 * @param timeIndex 时辰序号【0~12】
 * @returns 台辅,封诰索引
 */
export const getTimelyStarIndex = (timeIndex: number) => {
  const taifuIndex = fixIndex(fixEarthlyBranchIndex('woo') + fixIndex(timeIndex));
  const fenggaoIndex = fixIndex(fixEarthlyBranchIndex('yin') + fixIndex(timeIndex));

  return { taifuIndex, fenggaoIndex };
};

/**
 * 获取地空地劫的索引(按时支)
 *
 * - 亥上子时顺安劫
 * - 逆回便是地空亡
 *
 * 解释:
 *
 * - 从亥顺数到时辰地支索引是地劫的索引
 * - 从亥逆数到时辰地支索引是地空的索引
 *
 * 由于时辰地支的索引即是时辰的序号,所以可以直接使用时辰的序号
 *
 * @param timeIndex 时辰索引【0~12】
 * @returns 地空、地劫索引
 */
export const getKongJieIndex = (timeIndex: number) => {
  const fixedTimeIndex = fixIndex(timeIndex);
  const haiIndex = fixEarthlyBranchIndex('hai');
  const kongIndex = fixIndex(haiIndex - fixedTimeIndex);
  const jieIndex = fixIndex(haiIndex + fixedTimeIndex);

  return { kongIndex, jieIndex };
};

/**
 * 获取火星铃星索引(按年支以及时支)
 *
 * - 申子辰人寅戌扬
 * - 寅午戌人丑卯方
 * - 巳酉丑人卯戌位
 * - 亥卯未人酉戌房
 *
 * 起火铃二耀先据出生年支,依口诀定火铃起子时位。
 *
 * 例如壬辰年卯时生人,据[申子辰人寅戌扬]口诀,故火星在寅宫起子时,铃星在戌宫起子时,顺数至卯时,即火星在巳,铃星在丑。
 *
 * @param earthlyBranchName 地支
 * @param timeIndex 时辰序号
 * @returns 火星、铃星索引
 */
export const getHuoLingIndex = (earthlyBranchName: EarthlyBranchName, timeIndex: number) => {
  let huoIndex = -1;
  let lingIndex = -1;
  const fixedTimeIndex = fixIndex(timeIndex);
  const earthlyBranch = kot<EarthlyBranchKey>(earthlyBranchName, 'Earthly');

  switch (earthlyBranch) {
    case 'yinEarthly':
    case 'wuEarthly':
    case 'xuEarthly': {
      huoIndex = fixEarthlyBranchIndex('chou') + fixedTimeIndex;
      lingIndex = fixEarthlyBranchIndex('mao') + fixedTimeIndex;
      break;
    }
    case 'shenEarthly':
    case 'ziEarthly':
    case 'chenEarthly': {
      huoIndex = fixEarthlyBranchIndex('yin') + fixedTimeIndex;
      lingIndex = fixEarthlyBranchIndex('xu') + fixedTimeIndex;
      break;
    }
    case 'siEarthly':
    case 'youEarthly':
    case 'chouEarthly': {
      huoIndex = fixEarthlyBranchIndex('mao') + fixedTimeIndex;
      lingIndex = fixEarthlyBranchIndex('xu') + fixedTimeIndex;
      break;
    }
    case 'haiEarthly':
    case 'weiEarthly':
    case 'maoEarthly': {
      huoIndex = fixEarthlyBranchIndex('you') + fixedTimeIndex;
      lingIndex = fixEarthlyBranchIndex('xu') + fixedTimeIndex;
      break;
    }
  }

  return {
    huoIndex: fixIndex(huoIndex),
    lingIndex: fixIndex(lingIndex),
  };
};

/**
 * 获取红鸾天喜所在宫位索引
 *
 * - 卯上起子逆数之
 * - 数到当生太岁支
 * - 坐守此宫红鸾位
 * - 对宫天喜不差移
 *
 * @param earthlyBranchName 年支
 * @returns 红鸾、天喜索引
 */
export const getLuanXiIndex = (earthlyBranchName: EarthlyBranchName) => {
  const earthlyBranch = kot<EarthlyBranchKey>(earthlyBranchName, 'Earthly');
  const hongluanIndex = fixIndex(fixEarthlyBranchIndex('mao') - EARTHLY_BRANCHES.indexOf(earthlyBranch));
  const tianxiIndex = fixIndex(hongluanIndex + 6);

  return { hongluanIndex, tianxiIndex };
};

/**
 * 安华盖
 * - 子辰申年在辰,丑巳酉年在丑
 * - 寅午戍年在戍,卯未亥年在未。
 *
 * 安咸池
 * - 子辰申年在酉,丑巳酉年在午
 * - 寅午戍年在卯,卯未亥年在子。
 *
 * @param earthlyBranchName 地支
 * @returns 华盖、咸池索引
 */
export const getHuagaiXianchiIndex = (earthlyBranchName: EarthlyBranchName) => {
  let hgIdx = -1;
  let xcIdx = -1;
  const earthlyBranch = kot<EarthlyBranchKey>(earthlyBranchName, 'Earthly');

  switch (earthlyBranch) {
    case 'yinEarthly':
    case 'wuEarthly':
    case 'xuEarthly': {
      hgIdx = fixEarthlyBranchIndex('xu');
      xcIdx = fixEarthlyBranchIndex('mao');
      break;
    }
    case 'shenEarthly':
    case 'ziEarthly':
    case 'chenEarthly': {
      hgIdx = fixEarthlyBranchIndex('chen');
      xcIdx = fixEarthlyBranchIndex('you');
      break;
    }
    case 'siEarthly':
    case 'youEarthly':
    case 'chouEarthly': {
      hgIdx = fixEarthlyBranchIndex('chou');
      xcIdx = fixEarthlyBranchIndex('woo');
      break;
    }
    case 'haiEarthly':
    case 'weiEarthly':
    case 'maoEarthly': {
      hgIdx = fixEarthlyBranchIndex('wei');
      xcIdx = fixEarthlyBranchIndex('zi');
      break;
    }
  }

  return {
    huagaiIndex: fixIndex(hgIdx),
    xianchiIndex: fixIndex(xcIdx),
  };
};

/**
 * 安孤辰寡宿
 * - 寅卯辰年安巳丑
 * - 巳午未年安申辰
 * - 申酉戍年安亥未
 * - 亥子丑年安寅戍。
 *
 * @param earthlyBranchName 地支
 * @returns 孤辰、寡宿索引
 */
export const getGuGuaIndex = (earthlyBranchName: EarthlyBranchName) => {
  let guIdx = -1;
  let guaIdx = -1;
  const earthlyBranch = kot<EarthlyBranchKey>(earthlyBranchName, 'Earthly');

  switch (earthlyBranch) {
    case 'yinEarthly':
    case 'maoEarthly':
    case 'chenEarthly': {
      guIdx = fixEarthlyBranchIndex('si');
      guaIdx = fixEarthlyBranchIndex('chou');
      break;
    }
    case 'siEarthly':
    case 'wuEarthly':
    case 'weiEarthly': {
      guIdx = fixEarthlyBranchIndex('shen');
      guaIdx = fixEarthlyBranchIndex('chen');
      break;
    }
    case 'shenEarthly':
    case 'youEarthly':
    case 'xuEarthly': {
      guIdx = fixEarthlyBranchIndex('hai');
      guaIdx = fixEarthlyBranchIndex('wei');
      break;
    }
    case 'haiEarthly':
    case 'ziEarthly':
    case 'chouEarthly': {
      guIdx = fixEarthlyBranchIndex('yin');
      guaIdx = fixEarthlyBranchIndex('xu');
      break;
    }
  }

  return {
    guchenIndex: fixIndex(guIdx),
    guasuIndex: fixIndex(guaIdx),
  };
};

/**
 * 获取年系星的索引,包括
 * 咸池,华盖,孤辰,寡宿, 天厨,破碎,天才,天寿,蜚蠊, 龙池,凤阁,天哭,天虚,
 * 天官,天福
 *
 * - 安天才天寿
 *   - 天才由命宫起子,顺行至本生年支安之。天寿由身宫起子,顺行至本生年支安之。
 *
 * - 安破碎
 *   - 子午卯酉年安巳宫,寅申巳亥年安酉宫,辰戍丑未年安丑宫。
 *
 * - 安天厨
 *   - 甲丁食蛇口,乙戊辛马方。丙从鼠口得,己食于猴房。庚食虎头上,壬鸡癸猪堂。
 *
 * - 安蜚蠊
 *   - 子丑寅年在申酉戍,卯辰巳年在巳午未,午未申年在寅卯辰,酉戍亥年在亥子丑。
 *
 * - 安龙池凤阁
 *   - 龙池从辰宫起子,顺至本生年支安之。凤阁从戍宫起子,逆行至本生年支安之。
 *
 * - 安天哭天虚
 *   - 天哭天虚起午宫,午宫起子两分踪,哭逆行兮虚顺转,数到生年便停留。
 *
 * - 安天官天福
 *   - 甲喜羊鸡乙龙猴,丙年蛇鼠一窝谋。丁虎擒猪戊玉兔,
 *   - 己鸡居然与虎俦。庚猪马辛鸡蛇走,壬犬马癸马蛇游。
 *
 * - 安截路空亡(截空)
 *   - 甲己之年申酉,乙庚之年午未,
 *   - 丙辛之年辰巳,丁壬之年寅卯,
 *   - 戊癸之年子丑。
 *
 * - 安天空
 *   - 生年支顺数的前一位就是。
 * @param solarDate 阳历日期
 * @param timeIndex 时辰序号
 * @param fixLeap 是否修复闰月,假如当月不是闰月则不生效
 */
export const getYearlyStarIndex = (solarDate: string, timeIndex: number, fixLeap?: boolean) => {
  const { yearly } = getHeavenlyStemAndEarthlyBranchBySolarDate(solarDate, timeIndex, {
    // 流耀应该用立春为界,但为了满足不同流派的需求允许配置
    year: getConfig().horoscopeDivide,
  });
  const { soulIndex, bodyIndex } = getSoulAndBody(solarDate, timeIndex, fixLeap);
  const heavenlyStem = kot<HeavenlyStemKey>(yearly[0], 'Heavenly');
  const earthlyBranch = kot<EarthlyBranchKey>(yearly[1], 'Earthly');

  const { huagaiIndex, xianchiIndex } = getHuagaiXianchiIndex(yearly[1]);
  const { guchenIndex, guasuIndex } = getGuGuaIndex(yearly[1]);
  const tiancaiIndex = fixIndex(soulIndex + EARTHLY_BRANCHES.indexOf(earthlyBranch));
  const tianshouIndex = fixIndex(bodyIndex + EARTHLY_BRANCHES.indexOf(earthlyBranch));
  const tianchuIndex = fixIndex(
    fixEarthlyBranchIndex(
      ['si', 'woo', 'zi', 'si', 'woo', 'shen', 'yin', 'woo', 'you', 'hai'][
        HEAVENLY_STEMS.indexOf(heavenlyStem)
      ] as EarthlyBranchName,
    ),
  );
  const posuiIndex = fixIndex(
    fixEarthlyBranchIndex(['si', 'chou', 'you'][EARTHLY_BRANCHES.indexOf(earthlyBranch) % 3] as EarthlyBranchName),
  );
  const feilianIndex = fixIndex(
    fixEarthlyBranchIndex(
      ['shen', 'you', 'xu', 'si', 'woo', 'wei', 'yin', 'mao', 'chen', 'hai', 'zi', 'chou'][
        EARTHLY_BRANCHES.indexOf(earthlyBranch)
      ] as EarthlyBranchName,
    ),
  );
  const longchiIndex = fixIndex(fixEarthlyBranchIndex('chen') + EARTHLY_BRANCHES.indexOf(earthlyBranch));
  const fenggeIndex = fixIndex(fixEarthlyBranchIndex('xu') - EARTHLY_BRANCHES.indexOf(earthlyBranch));
  const tiankuIndex = fixIndex(fixEarthlyBranchIndex('woo') - EARTHLY_BRANCHES.indexOf(earthlyBranch));
  const tianxuIndex = fixIndex(fixEarthlyBranchIndex('woo') + EARTHLY_BRANCHES.indexOf(earthlyBranch));
  const tianguanIndex = fixIndex(
    fixEarthlyBranchIndex(
      ['wei', 'chen', 'si', 'yin', 'mao', 'you', 'hai', 'you', 'xu', 'woo'][
        HEAVENLY_STEMS.indexOf(heavenlyStem)
      ] as EarthlyBranchName,
    ),
  );
  const tianfuIndex = fixIndex(
    fixEarthlyBranchIndex(
      ['you', 'shen', 'zi', 'hai', 'mao', 'yin', 'woo', 'si', 'woo', 'si'][
        HEAVENLY_STEMS.indexOf(heavenlyStem)
      ] as EarthlyBranchName,
    ),
  );
  const tiandeIndex = fixIndex(fixEarthlyBranchIndex('you') + EARTHLY_BRANCHES.indexOf(earthlyBranch));
  const yuedeIndex = fixIndex(fixEarthlyBranchIndex('si') + EARTHLY_BRANCHES.indexOf(earthlyBranch));
  const tiankongIndex = fixIndex(fixEarthlyBranchIndex(yearly[1]) + 1);
  const jieluIndex = fixIndex(
    fixEarthlyBranchIndex(
      ['shen', 'woo', 'chen', 'yin', 'zi'][HEAVENLY_STEMS.indexOf(heavenlyStem) % 5] as EarthlyBranchName,
    ),
  );
  const kongwangIndex = fixIndex(
    fixEarthlyBranchIndex(
      ['you', 'wei', 'si', 'mao', 'chou'][HEAVENLY_STEMS.indexOf(heavenlyStem) % 5] as EarthlyBranchName,
    ),
  );
  const xunkongIndex = fixIndex(
    fixEarthlyBranchIndex(yearly[1]) + HEAVENLY_STEMS.indexOf('guiHeavenly') - HEAVENLY_STEMS.indexOf(heavenlyStem) + 1,
  );
  const tianshangIndex = fixIndex(PALACES.indexOf('friendsPalace') + soulIndex);
  const tianshiIndex = fixIndex(PALACES.indexOf('healthPalace') + soulIndex);

  return {
    xianchiIndex,
    huagaiIndex,
    guchenIndex,
    guasuIndex,
    tiancaiIndex,
    tianshouIndex,
    tianchuIndex,
    posuiIndex,
    feilianIndex,
    longchiIndex,
    fenggeIndex,
    tiankuIndex,
    tianxuIndex,
    tianguanIndex,
    tianfuIndex,
    tiandeIndex,
    yuedeIndex,
    tiankongIndex,
    jieluIndex,
    kongwangIndex,
    xunkongIndex,
    tianshangIndex,
    tianshiIndex,
  };
};

/**
 * 获取年解的索引
 *
 * - 年解(按年支)
 *   - 解神从戌上起子,逆数至当生年太岁上是也
 *
 * @param earthlyBranch 地支(年)
 * @returns 年解索引
 */
export const getNianjieIndex = (earthlyBranchName: EarthlyBranchName) => {
  const earthlyBranch = kot<EarthlyBranchKey>(earthlyBranchName, 'Earthly');

  return fixIndex(
    fixEarthlyBranchIndex(
      ['xu', 'you', 'shen', 'wei', 'woo', 'si', 'chen', 'mao', 'yin', 'chou', 'zi', 'hai'][
        EARTHLY_BRANCHES.indexOf(earthlyBranch)
      ] as EarthlyBranchName,
    ),
  );
};
/**
 * 获取以月份索引为基准的星耀索引,包括解神,天姚,天刑,阴煞,天月,天巫
 * 解神分为年解和月解,月解作用更加直接快速,年解稍迟钝,且作用力没有月解那么大
 *
 * - 月解(按生月)
 *   - 正二在申三四在戍,五六在子七八在寅,九十月坐於辰宫,十一十二在午宫。
 *
 * - 安天刑天姚(三合必见)
 *   - 天刑从酉起正月,顺至生月便安之。天姚丑宫起正月,顺到生月即停留。
 *
 * - 安阴煞
 *   - 正七月在寅,二八月在子,三九月在戍,四十月在申,五十一在午,六十二在辰。
 *
 * - 安天月
 *   - 一犬二蛇三在龙,四虎五羊六兔宫。七猪八羊九在虎,十马冬犬腊寅中。
 *
 * - 安天巫
 *   - 正五九月在巳,二六十月在申,三七十一在寅,四八十二在亥。
 *
 * @param solarDate 阳历日期
 * @param timeIndex 时辰序号
 * @param fixLeap 是否修复闰月,假如当月不是闰月则不生效
 * @returns
 */
export const getMonthlyStarIndex = (solarDate: string, timeIndex: number, fixLeap?: boolean) => {
  const monthIndex = fixLunarMonthIndex(solarDate, timeIndex, fixLeap);

  const jieshenIndex = fixIndex(
    fixEarthlyBranchIndex(['shen', 'xu', 'zi', 'yin', 'chen', 'woo'][Math.floor(monthIndex / 2)] as EarthlyBranchName),
  );
  const tianyaoIndex = fixIndex(fixEarthlyBranchIndex('chou') + monthIndex);
  const tianxingIndex = fixIndex(fixEarthlyBranchIndex('you') + monthIndex);
  const yinshaIndex = fixIndex(
    fixEarthlyBranchIndex(['yin', 'zi', 'xu', 'shen', 'woo', 'chen'][monthIndex % 6] as EarthlyBranchName),
  );
  const tianyueIndex = fixIndex(
    fixEarthlyBranchIndex(
      ['xu', 'si', 'chen', 'yin', 'wei', 'mao', 'hai', 'wei', 'yin', 'woo', 'xu', 'yin'][
        monthIndex
      ] as EarthlyBranchName,
    ),
  );
  const tianwuIndex = fixIndex(
    fixEarthlyBranchIndex(['si', 'shen', 'yin', 'hai'][monthIndex % 4] as EarthlyBranchName),
  );

  return {
    yuejieIndex: jieshenIndex,
    tianyaoIndex,
    tianxingIndex,
    yinshaIndex,
    tianyueIndex,
    tianwuIndex,
  };
};

/**
 * 通过 大限/流年 天干获取流昌流曲
 *
 * - 流昌起巳位    甲乙顺流去
 * - 不用四墓宫    日月同年岁
 * - 流曲起酉位    甲乙逆行踪
 * - 亦不用四墓    年日月相同
 *
 * @param heavenlyStemName 天干
 * @returns 文昌、文曲索引
 */
export const getChangQuIndexByHeavenlyStem = (heavenlyStemName: HeavenlyStemName) => {
  let changIndex = -1;
  let quIndex = -1;
  const heavenlyStem = kot<HeavenlyStemKey>(heavenlyStemName, 'Heavenly');

  switch (heavenlyStem) {
    case 'jiaHeavenly':
      changIndex = fixIndex(fixEarthlyBranchIndex('si'));
      quIndex = fixIndex(fixEarthlyBranchIndex('you'));
      break;
    case 'yiHeavenly':
      changIndex = fixIndex(fixEarthlyBranchIndex('woo'));
      quIndex = fixIndex(fixEarthlyBranchIndex('shen'));
      break;
    case 'bingHeavenly':
    case 'wuHeavenly':
      changIndex = fixIndex(fixEarthlyBranchIndex('shen'));
      quIndex = fixIndex(fixEarthlyBranchIndex('woo'));
      break;
    case 'dingHeavenly':
    case 'jiHeavenly':
      changIndex = fixIndex(fixEarthlyBranchIndex('you'));
      quIndex = fixIndex(fixEarthlyBranchIndex('si'));
      break;
    case 'gengHeavenly':
      changIndex = fixIndex(fixEarthlyBranchIndex('hai'));
      quIndex = fixIndex(fixEarthlyBranchIndex('mao'));
      break;
    case 'xinHeavenly':
      changIndex = fixIndex(fixEarthlyBranchIndex('zi'));
      quIndex = fixIndex(fixEarthlyBranchIndex('yin'));
      break;
    case 'renHeavenly':
      changIndex = fixIndex(fixEarthlyBranchIndex('yin'));
      quIndex = fixIndex(fixEarthlyBranchIndex('zi'));
      break;
    case 'guiHeavenly':
      changIndex = fixIndex(fixEarthlyBranchIndex('mao'));
      quIndex = fixIndex(fixEarthlyBranchIndex('hai'));
      break;
  }

  return { changIndex, quIndex };
};