--- Mayan and Aztec calendar conversions. -- Ported from "Calendrical Calculations" (4th edition) -- by Nachum Dershowitz and Edward M. Reingold. -- Original Lisp code (CALENDRICA 4.0) is Apache 2.0 licensed. -- @module calendrica-mayan -- @release 0.1 2026-07-19 local M = {} local basic = require("calendrica-basic") local julian = require("calendrica-julian") -- === Mayan Long Count === -- Fixed date of start of Mayan calendar (Goodman-Martinez-Thompson). local MAYAN_EPOCH = basic.fixed_from_jd(584283) -- Mayan long count date constructor. local function mayan_long_count_date(baktun, katun, tun, uinal, kin) -- luacheck: ignore return {baktun, katun, tun, uinal, kin} end -- Mayan long count component accessors. local function mayan_baktun(date) return date[1] end -- luacheck: ignore local function mayan_katun(date) return date[2] end -- luacheck: ignore local function mayan_tun(date) return date[3] end -- luacheck: ignore local function mayan_uinal(date) return date[4] end -- luacheck: ignore local function mayan_kin(date) return date[5] end -- luacheck: ignore --- Fixed date corresponding to Mayan long count `count`. -- @tparam table count Mayan long count date {baktun,katun,tun,uinal,kin}. -- @treturn number Fixed date. function M.fixed_from_mayan_long_count(count) return MAYAN_EPOCH + basic.from_radix(count, {20, 20, 18, 20}) end --- Mayan long count date {baktun,katun,tun,uinal,kin} of fixed `date`. -- @tparam number date Fixed date. -- @treturn table {baktun, katun, tun, uinal, kin} function M.mayan_long_count_from_fixed(date) return basic.to_radix(date - MAYAN_EPOCH, {20, 20, 18, 20}) end -- === Mayan Haab === -- Mayan haab date constructor. local function mayan_haab_date(month, day) return {month, day} end -- Mayan haab accessors. local function mayan_haab_month(date) return date[1] end local function mayan_haab_day(date) return date[2] end -- Number of days into haab cycle of h_date. local function mayan_haab_ordinal(h_date) return (mayan_haab_month(h_date) - 1) * 20 + mayan_haab_day(h_date) end -- Fixed date of start of haab cycle. local MAYAN_HAAB_EPOCH = MAYAN_EPOCH - mayan_haab_ordinal(mayan_haab_date(18, 8)) --- Mayan haab date {month, day} of fixed `date`. -- @tparam number date Fixed date. -- @treturn table {month, day} function M.mayan_haab_from_fixed(date) local count = (date - MAYAN_HAAB_EPOCH) % 365 local day = count % 20 local month = 1 + basic.quotient(count, 20) return mayan_haab_date(month, day) end local mayan_haab_from_fixed = M.mayan_haab_from_fixed -- Latest haab date on or before fixed date. local function mayan_haab_on_or_before(haab, date) return basic.mod3( mayan_haab_ordinal(haab) + MAYAN_HAAB_EPOCH, date, date - 365 ) end -- === Mayan Tzolkin === -- Mayan tzolkin date constructor. local function mayan_tzolkin_date(number, name) return {number, name} end -- Mayan tzolkin accessors. local function mayan_tzolkin_number(date) return date[1] end local function mayan_tzolkin_name(date) return date[2] end -- Number of days into tzolkin cycle of t_date. local function mayan_tzolkin_ordinal(t_date) local number = mayan_tzolkin_number(t_date) local name = mayan_tzolkin_name(t_date) return (number - 1 + 39 * (number - name)) % 260 end -- Start of tzolkin date cycle. local MAYAN_TZOLKIN_EPOCH = MAYAN_EPOCH - mayan_tzolkin_ordinal(mayan_tzolkin_date(4, 20)) --- Mayan tzolkin date {number, name} of fixed `date`. -- @tparam number date Fixed date. -- @treturn table {number, name} function M.mayan_tzolkin_from_fixed(date) local count = date - MAYAN_TZOLKIN_EPOCH + 1 local number = basic.amod(count, 13) local name = basic.amod(count, 20) return mayan_tzolkin_date(number, name) end local mayan_tzolkin_from_fixed = M.mayan_tzolkin_from_fixed -- Latest tzolkin date on or before fixed date. local function mayan_tzolkin_on_or_before(tzolkin, date) return basic.mod3( mayan_tzolkin_ordinal(tzolkin) + MAYAN_TZOLKIN_EPOCH, date, date - 260 ) end -- Year bearer of year containing fixed date; bogus for uayeb. local function mayan_year_bearer_from_fixed(date) local x = mayan_haab_on_or_before(mayan_haab_date(1, 0), date) if mayan_haab_month(mayan_haab_from_fixed(date)) == 19 then return basic.BOGUS else return mayan_tzolkin_name(mayan_tzolkin_from_fixed(x)) end end -- Latest haab-tzolkin combination on or before date; bogus if impossible. local function mayan_calendar_round_on_or_before(haab, tzolkin, date) local haab_count = mayan_haab_ordinal(haab) + MAYAN_HAAB_EPOCH local tzolkin_count = mayan_tzolkin_ordinal(tzolkin) + MAYAN_TZOLKIN_EPOCH local diff = tzolkin_count - haab_count if diff % 5 == 0 then return basic.mod3(haab_count + 365 * diff, date, date - 18980) else return basic.BOGUS end end -- === Aztec Xihuitl === -- Known date of Aztec cycles (Caso's correlation). local AZTEC_CORRELATION = julian.fixed_from_julian( julian.julian_date(1521, julian.AUGUST, 13) ) -- Aztec xihuitl date constructor. local function aztec_xihuitl_date(month, day) return {month, day} end -- Aztec xihuitl accessors. local function aztec_xihuitl_month(date) return date[1] end local function aztec_xihuitl_day(date) return date[2] end -- Number of elapsed days into xihuitl cycle of x_date. local function aztec_xihuitl_ordinal(x_date) return (aztec_xihuitl_month(x_date) - 1) * 20 + (aztec_xihuitl_day(x_date) - 1) end -- Start of a xihuitl cycle. local AZTEC_XIHUITL_CORRELATION = AZTEC_CORRELATION - aztec_xihuitl_ordinal(aztec_xihuitl_date(11, 2)) --- Aztec xihuitl date {month, day} of fixed `date`. -- @tparam number date Fixed date. -- @treturn table {month, day} function M.aztec_xihuitl_from_fixed(date) local count = (date - AZTEC_XIHUITL_CORRELATION) % 365 local day = 1 + (count % 20) local month = 1 + basic.quotient(count, 20) return aztec_xihuitl_date(month, day) end local aztec_xihuitl_from_fixed = M.aztec_xihuitl_from_fixed -- Latest xihuitl date on or before fixed date. local function aztec_xihuitl_on_or_before(xihuitl, date) return basic.mod3( AZTEC_XIHUITL_CORRELATION + aztec_xihuitl_ordinal(xihuitl), date, date - 365 ) end -- === Aztec Tonalpohualli === -- Aztec tonalpohualli date constructor. local function aztec_tonalpohualli_date(number, name) return {number, name} end -- Aztec tonalpohualli accessors. local function aztec_tonalpohualli_number(date) return date[1] end local function aztec_tonalpohualli_name(date) return date[2] end -- Number of days into tonalpohualli cycle of t_date. local function aztec_tonalpohualli_ordinal(t_date) local number = aztec_tonalpohualli_number(t_date) local name = aztec_tonalpohualli_name(t_date) return (number - 1 + 39 * (number - name)) % 260 end -- Start of a tonalpohualli date cycle. local AZTEC_TONALPOHUALLI_CORRELATION = AZTEC_CORRELATION - aztec_tonalpohualli_ordinal(aztec_tonalpohualli_date(1, 5)) --- Aztec tonalpohualli date {number, name} of fixed `date`. -- @tparam number date Fixed date. -- @treturn table {number, name} function M.aztec_tonalpohualli_from_fixed(date) local count = date - AZTEC_TONALPOHUALLI_CORRELATION + 1 local number = basic.amod(count, 13) local name = basic.amod(count, 20) return aztec_tonalpohualli_date(number, name) end local aztec_tonalpohualli_from_fixed = M.aztec_tonalpohualli_from_fixed -- Latest tonalpohualli date on or before fixed date. local function aztec_tonalpohualli_on_or_before(tonalpohualli, date) return basic.mod3( AZTEC_TONALPOHUALLI_CORRELATION + aztec_tonalpohualli_ordinal(tonalpohualli), date, date - 260 ) end -- === Aztec Xiuhmolpilli === -- Aztec xiuhmolpilli designation constructor. local function aztec_xiuhmolpilli_designation(number, name) -- luacheck: ignore return {number, name} end -- Aztec xiuhmolpilli accessors. local function aztec_xiuhmolpilli_number(date) return date[1] end -- luacheck: ignore local function aztec_xiuhmolpilli_name(date) return date[2] end -- luacheck: ignore -- Latest xihuitl-tonalpohualli combination on or before date; bogus if impossible. local function aztec_xihuitl_tonalpohualli_on_or_before(xihuitl, tonalpohualli, date) -- luacheck: ignore local xihuitl_count = aztec_xihuitl_ordinal(xihuitl) + AZTEC_XIHUITL_CORRELATION local tonalpohualli_count = aztec_tonalpohualli_ordinal(tonalpohualli) + AZTEC_TONALPOHUALLI_CORRELATION local diff = tonalpohualli_count - xihuitl_count if diff % 5 == 0 then return basic.mod3(xihuitl_count + 365 * diff, date, date - 18980) else return basic.BOGUS end end -- Designation of year containing fixed date; bogus for nemontemi. local function aztec_xiuhmolpilli_from_fixed(date) local x = aztec_xihuitl_on_or_before( aztec_xihuitl_date(18, 20), date + 364 ) local month = aztec_xihuitl_month(aztec_xihuitl_from_fixed(date)) if month == 19 then return basic.BOGUS -- nemontemi else return aztec_tonalpohualli_from_fixed(x) end end -- === Exports === --- Latest fixed date on or before `date` with Mayan Haab date `haab`. -- @function mayan_haab_on_or_before -- @tparam table haab Mayan Haab date {month, day}. -- @tparam number date Fixed date. -- @treturn number Fixed date. M.mayan_haab_on_or_before = mayan_haab_on_or_before --- Latest fixed date on or before `date` with Mayan Tzolkin date `tzolkin`. -- @function mayan_tzolkin_on_or_before -- @tparam table tzolkin Mayan Tzolkin date {number, name}. -- @tparam number date Fixed date. -- @treturn number Fixed date. M.mayan_tzolkin_on_or_before = mayan_tzolkin_on_or_before --- Latest fixed date on or before `date` with Mayan Calendar Round `haab`+`tzolkin`. -- Returns BOGUS if the combination is impossible. -- @function mayan_calendar_round_on_or_before -- @tparam table haab Mayan Haab date {month, day}. -- @tparam table tzolkin Mayan Tzolkin date {number, name}. -- @tparam number date Fixed date. -- @treturn number Fixed date or BOGUS. M.mayan_calendar_round_on_or_before = mayan_calendar_round_on_or_before --- Year bearer of fixed `date` in the Mayan calendar. -- @function mayan_year_bearer_from_fixed -- @tparam number date Fixed date. -- @treturn number Year bearer. M.mayan_year_bearer_from_fixed = mayan_year_bearer_from_fixed --- Latest fixed date on or before `date` with Aztec Xihuitl date `xihuitl`. -- @function aztec_xihuitl_on_or_before -- @tparam table xihuitl Aztec Xihuitl date {month, day}. -- @tparam number date Fixed date. -- @treturn number Fixed date. M.aztec_xihuitl_on_or_before = aztec_xihuitl_on_or_before --- Latest fixed date on or before `date` with Aztec Tonalpohualli date `tonalpohualli`. -- @function aztec_tonalpohualli_on_or_before -- @tparam table tonalpohualli Aztec Tonalpohualli date {number, name}. -- @tparam number date Fixed date. -- @treturn number Fixed date. M.aztec_tonalpohualli_on_or_before = aztec_tonalpohualli_on_or_before --- Aztec Xiuhmolpilli (52-year cycle) designation for fixed `date`. -- Returns BOGUS during Nemontemi (the 5 unlucky days). -- @function aztec_xiuhmolpilli_from_fixed -- @tparam number date Fixed date. -- @treturn table|number Tonalpohualli date {number, name} or BOGUS. M.aztec_xiuhmolpilli_from_fixed = aztec_xiuhmolpilli_from_fixed return M