Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

Jump to content

Module:Calendrical Calculations

Permanently protected module
From Wikipedia, the free encyclopedia

-- This code is from Appendix D of Calendrical Calculations by Dershowitz and Reingold, 4th ed., translated from Lisp to Lua

local getArgs = require('Module:Arguments').getArgs
local p = {}
local M = {} -- This namespace is to get around Lua's local variable limit of 200

local unpack_args = table.unpack or unpack
local NIL = {} -- Lisp NIL when it is being used as the empty list.

local function _is_nil(x)
    return x == nil or x == NIL
end

local function _truth(x)
    return x ~= nil and x ~= false and x ~= NIL
end

local function _list(...)
    local n = select('#', ...)
    if n == 0 then return NIL end
    local t = {}
    for i = 1, n do
        local v = select(i, ...)
        if v == nil then v = NIL end
        t[i] = v
    end
    return t
end

local function _length(t)
    if _is_nil(t) then return 0 end
    return #t
end

local function _nth(i, t)
    if _is_nil(t) then return NIL end
    local v = t[i + 1]
    if v == nil then return NIL end
    return v
end

local function _rest(t)
    if _is_nil(t) or #t <= 1 then return NIL end
    local r = {}
    for i = 2, #t do r[#r + 1] = t[i] end
    return r
end

local function _butlast(t)
    if _is_nil(t) or #t <= 1 then return NIL end
    local r = {}
    for i = 1, #t - 1 do r[#r + 1] = t[i] end
    return r
end

local function _append(...)
    local r = {}
    for n = 1, select('#', ...) do
        local t = select(n, ...)
        if not _is_nil(t) then
            for i = 1, #t do r[#r + 1] = t[i] end
        end
    end
    if #r == 0 then return NIL end
    return r
end

local function _equal(a, b)
    if _is_nil(a) and _is_nil(b) then return true end
    if type(a) ~= type(b) then return false end
    if type(a) ~= 'table' then return a == b end
    if #a ~= #b then return false end
    for i = 1, #a do
        if not _equal(a[i], b[i]) then return false end
    end
    return true
end

local function _member(x, t)
    if _is_nil(t) then return false end
    for i = 1, #t do
        if _equal(x, t[i]) then return true end
    end
    return false
end

local function _apply(op, t)
    if op == '*' then
        local r = 1
        if not _is_nil(t) then for i = 1, #t do r = r * t[i] end end
        return r
    elseif op == '+' then
        local r = 0
        if not _is_nil(t) then for i = 1, #t do r = r + t[i] end end
        return r
    end
    error('Unsupported Lisp apply operator: ' .. tostring(op))
end

local function _round(x)
    local lo = math.floor(x)
    local frac = x - lo
    if frac < 0.5 then return lo end
    if frac > 0.5 then return lo + 1 end
    -- Common Lisp ROUND uses ties-to-even.
    if lo % 2 == 0 then return lo else return lo + 1 end
end

local function _integerp(x)
    return type(x) == 'number' and x == math.floor(x)
end

local function _format_value(v)
    if _is_nil(v) then return '' end
    if type(v) == 'boolean' then return v and '1' or '0' end -- Return 1 for true and 0 for false
    if type(v) ~= 'table' then return tostring(v) end
    local parts = {}
    for i = 1, #v do
        if type(v[i]) == 'table' then -- Handle functions that return tables
            parts[i] = '(' .. _format_value(v[i]) .. ')'
        else
            parts[i] = _format_value(v[i])
        end
    end
    return table.concat(parts, '-')
end

local function make_public(func, do_unpack)
    -- This is not in the book, but is necessary to make the functions work with #invoke on Wikipedia
    if do_unpack == nil then do_unpack = true end
    return function(frame)
        local args = getArgs(frame)
        -- Convert arguments from strings to numbers, because it breaks otherwise
        local numArgs = {}
        for i, v in ipairs(args) do numArgs[i] = tonumber(v) or v end
        local result
        if do_unpack then result = func(unpack_args(numArgs)) -- Pass arguments individually: func(arg1, arg2, ...)
        else result = func(numArgs) end -- Pass argumemts as a single table: func({arg1, arg2, ...})
        if type(result) == 'table' and args.index then
            -- If the user provided an 'index' parameter in the template, like {{#invoke:Calendrical Calculations|clock_from_moment|739737.12|index=1}}, then use it
            local val = result[tonumber(args.index) or args.index]
            return _format_value(val)
        end
        return _format_value(result)
    end
end

local function normalize_calendar_name(value)
	local name = mw.ustring.lower(mw.text.trim(tostring(value or '')))
	name = mw.ustring.gsub(name, '[%s%-]+', '_')
	name = mw.ustring.gsub(name, '_calendar$', '')

	if name == '' then
		error('A source and output calendar must both be specified', 0)
	end
	if not mw.ustring.match(name, '^[a-z][a-z0-9_]*$') then
		error(string.format('Invalid calendar name: %s', tostring(value)), 0)
	end
	return name
end

local function convert_calendar_date(source_name, output_name, date)
	local source = normalize_calendar_name(source_name)
	local output = normalize_calendar_name(output_name)
	local fixed_function_name = 'fixed_from_' .. source
	local output_function_name = output .. '_from_fixed'
	local fixed_function = M[fixed_function_name .. '_for_wikipedia'] or M[fixed_function_name]
	local output_function = M[output_function_name .. '_for_wikipedia'] or M[output_function_name]

	if type(fixed_function) ~= 'function' then
		error(string.format('The calendar %q does not provide %s', source_name, fixed_function_name), 0)
	end
	if type(output_function) ~= 'function' then
		error(string.format('The calendar %q does not provide %s', output_name, output_function_name), 0)
	end
	if #date == 0 then
		error('At least one date component must be supplied', 0)
	end

	return output_function(fixed_function(date))
end

function M.convert(source_name, output_name, ...)
	-- This is also not in the book, but makes it easier to convert calendars from Wikipedia
	local date = {}
	for i = 1, select('#', ...) do
		date[i] = select(i, ...)
	end
	return convert_calendar_date(source_name, output_name, date)
end

function M.sum(f, i, predicate)
    local s = 0
    while predicate(i) do s = s + f(i); i = i + 1 end
    return s
end

function M.prod(f, i, predicate)
    local product = 1
    while predicate(i) do product = product * f(i); i = i + 1 end
    return product
end

function M.sigma(lists, f)
    if _is_nil(lists) or #lists == 0 then return 0 end
    local n = _length(lists[1])
    local s = 0
    for i = 1, n do
        local args = {}
        for j = 1, #lists do args[j] = lists[j][i] end
        s = s + f(unpack_args(args))
    end
    return s
end

function M.next(i, predicate)
    while not predicate(i) do i = i + 1 end
    return i
end

function M.final(i, predicate)
    while predicate(i) do i = i + 1 end
    return i - 1
end

function M.binary_search(lo, hi, test, finished)
    local l, h = lo, hi
    while not finished(l, h) do
        local x = (l + h) / 2
        if test(x) then h = x else l = x end
    end
    return (l + h) / 2
end

function M.invert_angular(f, y, range)
    local varepsilon = 1 / 100000
    return M.binary_search(range[1], range[2],
        function(x) return ((f(x) - y) % 360) < 180 end,
        function(l, h) return (h - l) < varepsilon end)
end

----- D.1 Basics -----

-- TYPE day-of-week
-- Residue class for Sunday.
M.SUNDAY = 0

-- TYPE day-of-week
-- Residue class for Monday.
M.MONDAY = 1

-- TYPE day-of-week
-- Residue class for Tuesday.
M.TUESDAY = 2

-- TYPE day-of-week
-- Residue class for Wednesday.
M.WEDNESDAY = 3

-- TYPE day-of-week
-- Residue class for Thursday.
M.THURSDAY = 4

-- TYPE day-of-week
-- Residue class for Friday.
M.FRIDAY = 5

-- TYPE day-of-week
-- Residue class for Saturday.
M.SATURDAY = 6

-- TYPE boolean
-- Constant representing false.
M.FALSE = false

-- TYPE boolean
-- Constant representing true.
M.TRUE = true

-- TYPE string
-- Used to denote nonexistent dates.
M.BOGUS = "bogus"

-- TYPE fixed-date -> day-of-week
-- The residue class of the day of the week of date.
function M.day_of_week_from_fixed(date)
    return ((date - M.rd(0) - M.SUNDAY) % 7)
end

-- TYPE moment -> moment
-- Identity function for fixed dates/moments. If internal
-- timekeeping is shifted, change epoch to be RD date of
-- origin of internal count. epoch should be an integer.
function M.rd(tee)
    local epoch = 0
    return (tee - epoch)
end

-- TYPE moment
-- Fixed time of start of the julian day number.
M.JD_EPOCH = M.rd(-1721424.5)

-- TYPE julian-day-number -> moment
-- Moment of julian day number jd.
function M.moment_from_jd(jd)
    return (jd + M.JD_EPOCH)
end

-- TYPE moment -> julian-day-number
-- Julian day number of moment tee.
function M.jd_from_moment(tee)
    return (tee - M.JD_EPOCH)
end

-- TYPE fixed-date
-- Fixed time of start of the modified julian day number.
M.MJD_EPOCH = M.rd(678576)

-- TYPE julian-day-number -> fixed-date
-- Fixed date of modified julian day number mjd.
function M.fixed_from_mjd(mjd)
    return (mjd + M.MJD_EPOCH)
end

-- TYPE fixed-date -> julian-day-number
-- Modified julian day number of fixed date.
function M.mjd_from_fixed(date)
    return (date - M.MJD_EPOCH)
end

-- TYPE fixed-date
-- Fixed date of the start of the Unix second count.
M.UNIX_EPOCH = M.rd(719163)

-- TYPE second -> moment
-- Fixed date from Unix second count s
function M.moment_from_unix(s)
    return (M.UNIX_EPOCH + (s / 24 / 60 / 60))
end

-- TYPE moment -> second
-- Unix second count from moment tee
function M.unix_from_moment(tee)
    return (24 * 60 * 60 * (tee - M.UNIX_EPOCH))
end

-- TYPE julian-day-number -> fixed-date
-- Fixed date of julian day number jd.
function M.fixed_from_jd(jd)
    return math.floor(M.moment_from_jd(jd))
end

-- TYPE fixed-date -> julian-day-number
-- Julian day number of fixed date.
function M.jd_from_fixed(date)
    return M.jd_from_moment(date)
end

-- TYPE (real nonzero-real) -> integer
-- Whole part of m/n.
function M.quotient(m, n)
    return math.floor((m) / (n))
end

-- TYPE (integer nonzero-integer) -> integer
-- The value of (x mod y) with y instead of 0.
function M.amod(x, y)
    return (y + (x % (-y)))
end

-- TYPE (real real real) -> real
-- The value of x shifted into the range
-- [a..b). Returns x if a=b.
function M.mod3(x, a, b)
    if _truth(((a == b))) then
        return x
    else
        return (a + ((x - a) % (b - a)))
    end
end

-- TYPE (real list-of-reals) -> real
-- Sum powers of x with coefficients (from order 0 up)
-- in list a.
function M.poly(x, a)
    if _truth(_equal(a, NIL)) then
        return 0
    else
        return (_nth(0, a) + (x * M.poly(x, _rest(a))))
    end
end

-- TYPE standard-date -> standard-month
-- Month field of date = (year month day).
function M.standard_month(date)
    return _nth(1, date)
end

-- TYPE standard-date -> standard-day
-- Day field of date = (year month day).
function M.standard_day(date)
    return _nth(2, date)
end

-- TYPE standard-date -> standard-year
-- Year field of date = (year month day).
function M.standard_year(date)
    return _nth(0, date)
end

-- TYPE clock-time -> hour
function M.hour(clock)
    return _nth(0, clock)
end

-- TYPE clock-time -> minute
function M.minute(clock)
    return _nth(1, clock)
end

-- TYPE clock-time -> second
function M.seconds(clock)
    return _nth(2, clock)
end

-- TYPE (hour minute second) -> clock-time
function M.time_of_day(hour, minute, second)
    return _list(hour, minute, second)
end

-- TYPE moment -> fixed-date
-- Fixed-date from moment tee.
function M.fixed_from_moment(tee)
    return math.floor(tee)
end

-- TYPE real -> {-1,0,+1}
-- Sign of y.
function M.sign(y)
    if _truth(((y < 0))) then
        return -1
    elseif _truth(((y > 0))) then
        return 1
    else
        return 0
    end
end

-- TYPE moment -> time
-- Time from moment tee.
function M.time_from_moment(tee)
    return (tee % 1)
end

-- TYPE list-of-moments -> list-of-fixed-dates
-- List of fixed dates corresponding to list ell
-- of moments.
function M.list_of_fixed_from_moments(ell)
    if _truth(_equal(ell, NIL)) then
        return NIL
    else
        return _append(_list(M.fixed_from_moment(_nth(0, ell))), M.list_of_fixed_from_moments(_rest(ell)))
    end
end

-- TYPE (moment moment) -> interval
-- Half-open interval [t0..t1).
function M.interval(t0, t1)
    return _list(t0, t1)
end

-- TYPE (moment moment) -> interval
-- Closed interval [t0..t1].
function M.interval_closed(t0, t1)
    return _list(t0, t1)
end

-- TYPE interval -> moment
-- Start t0 of range [t0..t1) or [t0..t1].
function M.begin(range)
    return _nth(0, range)
end

-- TYPE interval -> moment
-- End t1 of range [t0..t1) or [t0..t1].
function M.end_(range)
    return _nth(1, range)
end

-- TYPE (moment interval) -> boolean
-- True if tee is in half-open range.
function M.in_range_p(tee, range)
    return (_truth(((M.begin(range) <= tee))) and _truth(((tee < M.end_(range)))))
end

-- TYPE (list-of-moments interval) -> list-of-moments
-- Those moments in list ell that occur in range.
function M.list_range(ell, range)
    if _truth(_equal(ell, NIL)) then
        return NIL
    else
        local r = M.list_range(_rest(ell), range)
        if _truth(M.in_range_p(_nth(0, ell), range)) then
            return _append(_list(_nth(0, ell)), r)
        else
            return r
        end
    end
end

-- TYPE (nonegative-real positive-real
-- TYPE nonegative-real interval) -> list-of-moments
-- List of occurrences of moment p of c-day cycle
-- within range.
-- cap-Delta is position in cycle of RD moment 0.
function M.positions_in_range(p, c, cap_Delta, range)
    local a = M.begin(range)
    local b = M.end_(range)
    local date = M.mod3((p - cap_Delta), a, (a + c))
    if _truth(((date >= b))) then
        return NIL
    else
        return _append(_list(date), M.positions_in_range(p, c, cap_Delta, M.interval((a + c), b)))
    end
end

-- TYPE (list-of-reals list-of-rationals list-of-rationals)
-- TYPE -> real
-- The number corresponding to a in radix notation
-- with base b for whole part and c for fraction.
function M.from_radix(a, b, c)
    return (M.sum(function(i) return (_nth(i, a) * M.prod(function(j) return _nth(j, _append(b, c)) end, i, function(j) return _truth(((j < (_length(b) + _length(c))))) end)) end, 0, function(i) return _truth(((i < _length(a)))) end) / _apply("*", c))
end

-- TYPE (real list-of-rationals list-of-rationals)
-- TYPE -> list-of-reals
-- The radix notation corresponding to x
-- with base b for whole part and c for fraction.
function M.to_radix(x, b, c)
    if _truth(_is_nil(c)) then
        if _truth(_is_nil(b)) then
            return _list(x)
        else
            return _append(M.to_radix(M.quotient(x, _nth((_length(b) - 1), b)), _butlast(b), NIL), _list((x % _nth((_length(b) - 1), b))))
        end
    else
        return M.to_radix((x * _apply("*", c)), _append(b, c))
    end
end

-- TYPE clock-time -> time
-- Time of day from hms = hour:minute:second.
function M.time_from_clock(hms)
    return (M.from_radix(hms, NIL, _list(24, 60, 60)) / 24)
end

-- TYPE moment -> clock-time
-- Clock time hour:minute:second from moment tee.
function M.clock_from_moment(tee)
    return _rest(M.to_radix(tee, NIL, _list(24, 60, 60)))
end

-- TYPE angle -> list-of-reals
-- List of degrees-arcminutes-arcseconds from angle alpha
-- in degrees.
function M.angle_from_degrees(alpha)
    local dms = M.to_radix(math.abs(alpha), NIL, _list(60, 60))
    if _truth(((alpha >= 0))) then
        return dms
    else
        return _list((-_nth(0, dms)), (-_nth(1, dms)), (-_nth(2, dms)))
    end
end

-- TYPE (egyptian-year egyptian-month egyptian-day)
-- TYPE -> egyptian-date
function M.egyptian_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE fixed-date
-- Fixed date of start of the Egyptian (Nabonasser)
-- calendar.
-- JD 1448638 = February 26, 747 BCE (Julian).
M.EGYPTIAN_EPOCH = M.fixed_from_jd(1448638)

-- TYPE egyptian-date -> fixed-date
-- Fixed date of Egyptian date e-date.
function M.fixed_from_egyptian(e_date)
    local month = M.standard_month(e_date)
    local day = M.standard_day(e_date)
    local year = M.standard_year(e_date)
    return (M.EGYPTIAN_EPOCH + (365 * (year - 1)) + (30 * (month - 1)) + day + -1)
end

-- TYPE egyptian-date -> fixed-date
-- Fixed date of Egyptian date e-date.
function M.alt_fixed_from_egyptian(e_date)
    return (M.EGYPTIAN_EPOCH + M.sigma(_list(_list(365, 30, 1), e_date), function(a, e_date) return (a * (e_date - 1)) end))
end

-- TYPE fixed-date -> egyptian-date
-- Egyptian equivalent of fixed date.
function M.egyptian_from_fixed(date)
    local days = (date - M.EGYPTIAN_EPOCH)
    local year = (M.quotient(days, 365) + 1)
    local month = (M.quotient((days % 365), 30) + 1)
    local day = (days - (365 * (year - 1)) - (30 * (month - 1)) - -1)
    return M.egyptian_date(year, month, day)
end

-- TYPE (armenian-year armenian-month armenian-day)
-- TYPE -> armenian-date
function M.armenian_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE fixed-date
-- Fixed date of start of the Armenian calendar.
-- = July 11, 552 CE (Julian).
M.ARMENIAN_EPOCH = M.rd(201443)

-- TYPE armenian-date -> fixed-date
-- Fixed date of Armenian date a-date.
function M.fixed_from_armenian(a_date)
    local month = M.standard_month(a_date)
    local day = M.standard_day(a_date)
    local year = M.standard_year(a_date)
    return (M.ARMENIAN_EPOCH + (M.fixed_from_egyptian(M.egyptian_date(year, month, day)) - M.EGYPTIAN_EPOCH))
end

-- TYPE fixed-date -> armenian-date
-- Armenian equivalent of fixed date.
function M.armenian_from_fixed(date)
    return M.egyptian_from_fixed((date + (M.EGYPTIAN_EPOCH - M.ARMENIAN_EPOCH)))
end

-- TYPE (day-of-week fixed-date) -> fixed-date
-- Fixed date of the k-day on or before fixed date.
-- k=0 means Sunday, k=1 means Monday, and so on.
function M.kday_on_or_before(k, date)
    return (date - M.day_of_week_from_fixed((date - k)))
end

-- TYPE (day-of-week fixed-date) -> fixed-date
-- Fixed date of the k-day on or after fixed date.
-- k=0 means Sunday, k=1 means Monday, and so on.
function M.kday_on_or_after(k, date)
    return M.kday_on_or_before(k, (date + 6))
end

-- TYPE (day-of-week fixed-date) -> fixed-date
-- Fixed date of the k-day nearest fixed date.
-- k=0 means Sunday, k=1 means Monday, and so on.
function M.kday_nearest(k, date)
    return M.kday_on_or_before(k, (date + 3))
end

-- TYPE (day-of-week fixed-date) -> fixed-date
-- Fixed date of the k-day before fixed date.
-- k=0 means Sunday, k=1 means Monday, and so on.
function M.kday_before(k, date)
    return M.kday_on_or_before(k, (date - 1))
end

-- TYPE (day-of-week fixed-date) -> fixed-date
-- Fixed date of the k-day after fixed date.
-- k=0 means Sunday, k=1 means Monday, and so on.
function M.kday_after(k, date)
    return M.kday_on_or_before(k, (date + 7))
end

-- TYPE integer -> akan-name
-- The n-th name of the Akan cycle.
function M.akan_day_name(n)
    return M.akan_name(M.amod(n, 6), M.amod(n, 7))
end

-- TYPE (akan-prefix akan-stem) -> akan-name
function M.akan_name(prefix, stem)
    return _list(prefix, stem)
end

-- TYPE akan-name -> akan-prefix
function M.akan_prefix(name)
    return _nth(0, name)
end

-- TYPE akan-name -> akan-stem
function M.akan_stem(name)
    return _nth(1, name)
end

-- TYPE (akan-name akan-name) -> nonnegative-integer
-- Number of names from Akan name a-name1 to the
-- next occurrence of Akan name a-name2.
function M.akan_name_difference(a_name1, a_name2)
    local prefix1 = M.akan_prefix(a_name1)
    local prefix2 = M.akan_prefix(a_name2)
    local stem1 = M.akan_stem(a_name1)
    local stem2 = M.akan_stem(a_name2)
    local prefix_difference = (prefix2 - prefix1)
    local stem_difference = (stem2 - stem1)
    return M.amod((prefix_difference + (36 * (stem_difference - prefix_difference))), 42)
end

-- TYPE fixed-date
-- RD date of an epoch (day 0)   of Akan day cycle.
M.AKAN_DAY_NAME_EPOCH = M.rd(37)

-- TYPE fixed-date -> akan-name
-- Akan name for date.
function M.akan_name_from_fixed(date)
    return M.akan_day_name((date - M.AKAN_DAY_NAME_EPOCH))
end

-- TYPE (akan-name fixed-date) -> fixed-date
-- Fixed date of latest date on or before fixed date
-- that has Akan name.
function M.akan_day_name_on_or_before(name, date)
    return M.mod3(M.akan_name_difference(M.akan_name_from_fixed(0), name), date, (date - 42))
end


----- D.2 The Gregorian Calendar -----

-- TYPE (gregorian-year gregorian-month gregorian-day)
-- TYPE -> gregorian-date
function M.gregorian_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE fixed-date
-- Fixed date of start of the (proleptic) Gregorian
-- calendar.
M.GREGORIAN_EPOCH = M.rd(1)

-- TYPE standard-month
-- January on Julian/Gregorian calendar.
M.JANUARY = 1

-- TYPE standard-month
-- February on Julian/Gregorian calendar.
M.FEBRUARY = 2

-- TYPE standard-month
-- March on Julian/Gregorian calendar.
M.MARCH = 3

-- TYPE standard-month
-- April on Julian/Gregorian calendar.
M.APRIL = 4

-- TYPE standard-month
-- May on Julian/Gregorian calendar.
M.MAY = 5

-- TYPE standard-month
-- June on Julian/Gregorian calendar.
M.JUNE = 6

-- TYPE standard-month
-- July on Julian/Gregorian calendar.
M.JULY = 7

-- TYPE standard-month
-- August on Julian/Gregorian calendar.
M.AUGUST = 8

-- TYPE standard-month
-- September on Julian/Gregorian calendar.
M.SEPTEMBER = 9

-- TYPE standard-month
-- October on Julian/Gregorian calendar.
M.OCTOBER = 10

-- TYPE standard-month
-- November on Julian/Gregorian calendar.
M.NOVEMBER = 11

-- TYPE standard-month
-- December on Julian/Gregorian calendar.
M.DECEMBER = 12

-- TYPE gregorian-year -> boolean
-- True if g-year is a leap year on the Gregorian
-- calendar.
function M.gregorian_leap_year_p(g_year)
    return (_truth((((g_year % 4) == 0))) and _truth((not _truth(_member((g_year % 400), _list(100, 200, 300))))))
end

-- TYPE gregorian-date -> fixed-date
-- Fixed date equivalent to the Gregorian date g-date.
function M.fixed_from_gregorian(g_date)
    local month = M.standard_month(g_date)
    local day = M.standard_day(g_date)
    local year = M.standard_year(g_date)
    return ((M.GREGORIAN_EPOCH - 1) + (365 * (year - 1)) + M.quotient((year - 1), 4) + (-M.quotient((year - 1), 100)) + M.quotient((year - 1), 400) + M.quotient(((367 * month) - 362), 12) + (function() if _truth(((month <= 2))) then return 0 else return (function() if _truth(M.gregorian_leap_year_p(year)) then return -1 else return -2 end end)() end end)() + day)
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of January 1 in g-year.
function M.gregorian_new_year(g_year)
    return M.fixed_from_gregorian(M.gregorian_date(g_year, M.JANUARY, 1))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of December 31 in g-year.
function M.gregorian_year_end(g_year)
    return M.fixed_from_gregorian(M.gregorian_date(g_year, M.DECEMBER, 31))
end

-- TYPE gregorian-year -> range
-- The range of moments in Gregorian year g-year.
function M.gregorian_year_range(g_year)
    return M.interval(M.gregorian_new_year(g_year), M.gregorian_new_year((g_year + 1)))
end

-- TYPE fixed-date -> gregorian-year
-- Gregorian year corresponding to the fixed date.
function M.gregorian_year_from_fixed(date)
    local d0 = (date - M.GREGORIAN_EPOCH)
    local n400 = M.quotient(d0, 146097)
    local d1 = (d0 % 146097)
    local n100 = M.quotient(d1, 36524)
    local d2 = (d1 % 36524)
    local n4 = M.quotient(d2, 1461)
    local d3 = (d2 % 1461)
    local n1 = M.quotient(d3, 365)
    local year = ((400 * n400) + (100 * n100) + (4 * n4) + n1)
    if _truth((_truth(((n100 == 4))) or _truth(((n1 == 4))))) then
        return year
    else
        return (year + 1)
    end
end

-- TYPE fixed-date -> gregorian-date
-- Gregorian (year month day) corresponding to fixed date.
function M.gregorian_from_fixed(date)
    local year = M.gregorian_year_from_fixed(date)
    local prior_days = (date - M.gregorian_new_year(year))
    local correction = (function() if _truth(((date < M.fixed_from_gregorian(M.gregorian_date(year, M.MARCH, 1))))) then return 0 else return (function() if _truth(M.gregorian_leap_year_p(year)) then return 1 else return 2 end end)() end end)()
    local month = M.quotient(((12 * (prior_days + correction)) + 373), 367)
    local day = ((date - M.fixed_from_gregorian(M.gregorian_date(year, month, 1))) + 1)
    return M.gregorian_date(year, month, day)
end

-- TYPE (gregorian-date gregorian-date) -> integer
-- Number of days from Gregorian date g-date1 until
-- g-date2.
function M.gregorian_date_difference(g_date1, g_date2)
    return (M.fixed_from_gregorian(g_date2) - M.fixed_from_gregorian(g_date1))
end

-- TYPE gregorian-date -> positive-integer
-- Day number in year of Gregorian date g-date.
function M.day_number(g_date)
    return M.gregorian_date_difference(M.gregorian_date((M.standard_year(g_date) - 1), M.DECEMBER, 31), g_date)
end

-- TYPE gregorian-date -> nonnegative-integer
-- Days remaining in year after Gregorian date g-date.
function M.days_remaining(g_date)
    return M.gregorian_date_difference(g_date, M.gregorian_date(M.standard_year(g_date), M.DECEMBER, 31))
end

-- TYPE (gregorian-year gregorian-month) -> gregorian-day
-- Last day of month g-month in Gregorian year g-year.
function M.last_day_of_gregorian_month(g_year, g_month)
    return M.gregorian_date_difference(M.gregorian_date(g_year, g_month, 1), M.gregorian_date((function() if _truth(((g_month == 12))) then return (g_year + 1) else return g_year end end)(), M.amod((g_month + 1), 12), 1))
end

-- TYPE gregorian-date -> fixed-date
-- Alternative calculation of fixed date equivalent to the
-- Gregorian date g-date.
function M.alt_fixed_from_gregorian(g_date)
    local month = M.standard_month(g_date)
    local day = M.standard_day(g_date)
    local year = M.standard_year(g_date)
    local m_prime = ((month - 3) % 12)
    local y_prime = (year - M.quotient(m_prime, 10))
    return ((M.GREGORIAN_EPOCH - 1) + -306 + (365 * y_prime) + M.sigma(_list(M.to_radix(y_prime, _list(4, 25, 4)), _list(97, 24, 1, 0)), function(y, a) return (y * a) end) + M.quotient(((3 * m_prime) + 2), 5) + (30 * m_prime) + day)
end

-- TYPE fixed-date -> gregorian-date
-- Alternative calculation of Gregorian (year month day)
-- corresponding to fixed date.
function M.alt_gregorian_from_fixed(date)
    local y = M.gregorian_year_from_fixed(((M.GREGORIAN_EPOCH - 1) + date + 306))
    local prior_days = (date - M.fixed_from_gregorian(M.gregorian_date((y - 1), M.MARCH, 1)))
    local month = M.amod((M.quotient(((5 * prior_days) + 2), 153) + 3), 12)
    local year = (y - M.quotient((month + 9), 12))
    local day = ((date - M.fixed_from_gregorian(M.gregorian_date(year, month, 1))) + 1)
    return M.gregorian_date(year, month, day)
end

-- TYPE fixed-date -> gregorian-year
-- Gregorian year corresponding to the fixed date.
function M.alt_gregorian_year_from_fixed(date)
    local approx = M.quotient((date - M.GREGORIAN_EPOCH - -2), (146097 / 400))
    local start = (M.GREGORIAN_EPOCH + (365 * approx) + M.sigma(_list(M.to_radix(approx, _list(4, 25, 4)), _list(97, 24, 1, 0)), function(y, a) return (y * a) end))
    if _truth(((date < start))) then
        return approx
    else
        return (approx + 1)
    end
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of United States Independence Day in
-- Gregorian year g-yaer.
function M.independence_day(g_year)
    return M.fixed_from_gregorian(M.gregorian_date(g_year, M.JULY, 4))
end

-- TYPE (integer day-of-week gregorian-date) -> fixed-date
-- If n>0, return the n-th k-day on or after
-- g-date. If n<0, return the n-th k-day on or
-- before g-date. If n=0 return bogus. A k-day of
-- 0 means Sunday, 1 means Monday, and so on.
function M.nth_kday(n, k, g_date)
    if _truth(((n > 0))) then
        return ((7 * n) + M.kday_before(k, M.fixed_from_gregorian(g_date)))
    elseif _truth(((n < 0))) then
        return ((7 * n) + M.kday_after(k, M.fixed_from_gregorian(g_date)))
    else
        return M.BOGUS
    end
end

-- TYPE (day-of-week gregorian-date) -> fixed-date
-- Fixed date of first k-day on or after Gregorian date
-- g-date. A k-day of 0 means Sunday, 1 means Monday,
-- and so on.
function M.first_kday(k, g_date)
    return M.nth_kday(1, k, g_date)
end

-- TYPE (day-of-week gregorian-date) -> fixed-date
-- Fixed date of last k-day on or before Gregorian date
-- g-date. A k-day of 0 means Sunday, 1 means Monday,
-- and so on.
function M.last_kday(k, g_date)
    return M.nth_kday(-1, k, g_date)
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of United States Labor Day in Gregorian
-- year g-year (the first Monday in September).
function M.labor_day(g_year)
    return M.first_kday(M.MONDAY, M.gregorian_date(g_year, M.SEPTEMBER, 1))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of United States Memorial Day in Gregorian
-- year g-year (the last Monday in May).
function M.memorial_day(g_year)
    return M.last_kday(M.MONDAY, M.gregorian_date(g_year, M.MAY, 31))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of United States Election Day in Gregorian
-- year g-year (the Tuesday after the first Monday in
-- November).
function M.election_day(g_year)
    return M.first_kday(M.TUESDAY, M.gregorian_date(g_year, M.NOVEMBER, 2))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of the start of United States daylight
-- saving time in Gregorian year g-year (the second
-- Sunday in March).
function M.daylight_saving_start(g_year)
    return M.nth_kday(2, M.SUNDAY, M.gregorian_date(g_year, M.MARCH, 1))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of the end of United States daylight saving
-- time in Gregorian year g-year (the first Sunday in
-- November).
function M.daylight_saving_end(g_year)
    return M.first_kday(M.SUNDAY, M.gregorian_date(g_year, M.NOVEMBER, 1))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Christmas in Gregorian year g-year.
function M.christmas(g_year)
    return M.fixed_from_gregorian(M.gregorian_date(g_year, M.DECEMBER, 25))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Advent in Gregorian year g-year
-- (the Sunday closest to November 30).
function M.advent(g_year)
    return M.kday_nearest(M.SUNDAY, M.fixed_from_gregorian(M.gregorian_date(g_year, M.NOVEMBER, 30)))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Epiphany in U.S. in Gregorian year
-- g-year (the first Sunday after January 1).
function M.epiphany(g_year)
    return M.first_kday(M.SUNDAY, M.gregorian_date(g_year, M.JANUARY, 2))
end

-- TYPE range -> list-of-fixed-dates
-- List of Fridays within range of dates
-- that are day 13 of Gregorian months.
function M.unlucky_fridays_in_range(range)
    local a = M.begin(range)
    local b = M.end_(range)
    local fri = M.kday_on_or_after(M.FRIDAY, a)
    local date = M.gregorian_from_fixed(fri)
    if _truth(M.in_range_p(fri, range)) then
        return _append((function() if _truth(((M.standard_day(date) == 13))) then return _list(fri) else return NIL end end)(), M.unlucky_fridays_in_range(M.interval((fri + 1), b)))
    else
        return NIL
    end
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of Fridays within Gregorian year g-year
-- that are day 13 of Gregorian months.
function M.unlucky_fridays(g_year)
    return M.unlucky_fridays_in_range(M.gregorian_year_range(g_year))
end


----- D.3 The Julian Calendar -----

-- TYPE standard-year -> julian-year
-- Negative value to indicate a BCE Julian year.
function M.bce(n)
    return (-n)
end

-- TYPE standard-year -> julian-year
-- Positive value to indicate a CE Julian year.
function M.ce(n)
    return n
end

-- TYPE (julian-year julian-month julian-day)
-- TYPE -> julian-date
function M.julian_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE julian-year -> boolean
-- True if j-year is a leap year on the Julian calendar.
function M.julian_leap_year_p(j_year)
    return (((j_year % 4) == (function() if _truth(((j_year > 0))) then return 0 else return 3 end end)()))
end

-- TYPE fixed-date
-- Fixed date of start of the Julian calendar.
M.JULIAN_EPOCH = M.fixed_from_gregorian(M.gregorian_date(0, M.DECEMBER, 30))

-- TYPE julian-date -> fixed-date
-- Fixed date equivalent to the Julian date j-date.
function M.fixed_from_julian(j_date)
    local month = M.standard_month(j_date)
    local day = M.standard_day(j_date)
    local year = M.standard_year(j_date)
    local y = (function() if _truth(((year < 0))) then return (year + 1) else return year end end)()
    return ((M.JULIAN_EPOCH - 1) + (365 * (y - 1)) + M.quotient((y - 1), 4) + M.quotient(((367 * month) - 362), 12) + (function() if _truth(((month <= 2))) then return 0 else return (function() if _truth(M.julian_leap_year_p(year)) then return -1 else return -2 end end)() end end)() + day)
end

-- TYPE fixed-date -> julian-date
-- Julian (year month day) corresponding to fixed date.
function M.julian_from_fixed(date)
    local approx = M.quotient(((4 * (date - M.JULIAN_EPOCH)) + 1464), 1461)
    local year = (function() if _truth(((approx <= 0))) then return (approx - 1) else return approx end end)()
    local prior_days = (date - M.fixed_from_julian(M.julian_date(year, M.JANUARY, 1)))
    local correction = (function() if _truth(((date < M.fixed_from_julian(M.julian_date(year, M.MARCH, 1))))) then return 0 else return (function() if _truth(M.julian_leap_year_p(year)) then return 1 else return 2 end end)() end end)()
    local month = M.quotient(((12 * (prior_days + correction)) + 373), 367)
    local day = ((date - M.fixed_from_julian(M.julian_date(year, month, 1))) + 1)
    return M.julian_date(year, month, day)
end

-- TYPE roman-event
-- Class of Kalends.
M.KALENDS = 1

-- TYPE roman-event
-- Class of Nones.
M.NONES = 2

-- TYPE roman-event
-- Class of Ides.
M.IDES = 3

-- TYPE (roman-year roman-month roman-event roman-count
-- TYPE roman-leap) -> roman-date
function M.roman_date(year, month, event, count, leap)
    return _list(year, month, event, count, leap)
end

-- TYPE roman-date -> roman-year
function M.roman_year(date)
    return _nth(0, date)
end

-- TYPE roman-date -> roman-month
function M.roman_month(date)
    return _nth(1, date)
end

-- TYPE roman-date -> roman-event
function M.roman_event(date)
    return _nth(2, date)
end

-- TYPE roman-date -> roman-count
function M.roman_count(date)
    return _nth(3, date)
end

-- TYPE roman-date -> roman-leap
function M.roman_leap(date)
    return _nth(4, date)
end

-- TYPE roman-month -> ides
-- Date of Ides in Roman month.
function M.ides_of_month(month)
    if _truth(_member(month, _list(M.MARCH, M.MAY, M.JULY, M.OCTOBER))) then
        return 15
    else
        return 13
    end
end

-- TYPE roman-month -> nones
-- Date of Nones in Roman month.
function M.nones_of_month(month)
    return (M.ides_of_month(month) - 8)
end

-- TYPE roman-date -> fixed-date
-- Fixed date for Roman name r-date.
function M.fixed_from_roman(r_date)
    local leap = M.roman_leap(r_date)
    local count = M.roman_count(r_date)
    local event = M.roman_event(r_date)
    local month = M.roman_month(r_date)
    local year = M.roman_year(r_date)
    return ((function() if _truth(((event == M.KALENDS))) then return M.fixed_from_julian(M.julian_date(year, month, 1)) elseif _truth(((event == M.NONES))) then return M.fixed_from_julian(M.julian_date(year, month, M.nones_of_month(month))) elseif _truth(((event == M.IDES))) then return M.fixed_from_julian(M.julian_date(year, month, M.ides_of_month(month))) else return NIL end end)() + (-count) + (function() if _truth((_truth(M.julian_leap_year_p(year)) and _truth(((month == M.MARCH))) and _truth(((event == M.KALENDS))) and _truth(((16 >= count) and (count >= 6))))) then return 0 else return 1 end end)() + (function() if _truth(leap) then return 1 else return 0 end end)())
end

-- TYPE fixed-date -> roman-date
-- Roman name for fixed date.
function M.roman_from_fixed(date)
    local j_date = M.julian_from_fixed(date)
    local month = M.standard_month(j_date)
    local day = M.standard_day(j_date)
    local year = M.standard_year(j_date)
    local month_prime = M.amod((month + 1), 12)
    local year_prime = (function() if _truth(((month_prime ~= 1))) then return year else return (function() if _truth(((year ~= -1))) then return (year + 1) else return 1 end end)() end end)()
    local kalends1 = M.fixed_from_roman(M.roman_date(year_prime, month_prime, M.KALENDS, 1, false))
    if _truth(((day == 1))) then
        return M.roman_date(year, month, M.KALENDS, 1, false)
    elseif _truth(((day <= M.nones_of_month(month)))) then
        return M.roman_date(year, month, M.NONES, ((M.nones_of_month(month) - day) + 1), false)
    elseif _truth(((day <= M.ides_of_month(month)))) then
        return M.roman_date(year, month, M.IDES, ((M.ides_of_month(month) - day) + 1), false)
    elseif _truth((_truth(((month ~= M.FEBRUARY))) or _truth((not _truth(M.julian_leap_year_p(year)))))) then
        return M.roman_date(year_prime, month_prime, M.KALENDS, ((kalends1 - date) + 1), false)
    elseif _truth(((day < 25))) then
        return M.roman_date(year, M.MARCH, M.KALENDS, (30 - day), false)
    else
        return M.roman_date(year, M.MARCH, M.KALENDS, (31 - day), ((day == 25)))
    end
end

-- TYPE julian-year
-- Year on the Julian calendar of the founding of Rome.
M.YEAR_ROME_FOUNDED = M.bce(753)

-- TYPE auc-year -> julian-year
-- Julian year equivalent to AUC year
function M.julian_year_from_auc(year)
    if _truth(((1 <= year) and (year <= (-M.YEAR_ROME_FOUNDED)))) then
        return (year + M.YEAR_ROME_FOUNDED + -1)
    else
        return (year + M.YEAR_ROME_FOUNDED)
    end
end

-- TYPE julian-year -> auc-year
-- Year AUC equivalent to Julian year
function M.auc_year_from_julian(year)
    if _truth(((M.YEAR_ROME_FOUNDED <= year) and (year <= -1))) then
        return (year - M.YEAR_ROME_FOUNDED - -1)
    else
        return (year - M.YEAR_ROME_FOUNDED)
    end
end

-- TYPE (olympiad-cycle olympiad-year) -> olympiad
function M.olympiad(cycle, year)
    return _list(cycle, year)
end

-- TYPE olympiad -> olympiad-cycle
function M.olympiad_cycle(o_date)
    return _nth(0, o_date)
end

-- TYPE olympiad -> olympiad-year
function M.olympiad_year(o_date)
    return _nth(1, o_date)
end

-- TYPE julian-year
-- Start of the Olympiads.
M.OLYMPIAD_START = M.bce(776)

-- TYPE olympiad -> julian-year
-- Julian year corresponding to Olympian o-date.
function M.julian_year_from_olympiad(o_date)
    local cycle = M.olympiad_cycle(o_date)
    local year = M.olympiad_year(o_date)
    local years = (M.OLYMPIAD_START + (4 * (cycle - 1)) + year + -1)
    if _truth(((years < 0))) then
        return years
    else
        return (years + 1)
    end
end

-- TYPE julian-year -> olympiad
-- Olympiad corresponding to Julian year j-year.
function M.olympiad_from_julian_year(j_year)
    local years = (j_year - M.OLYMPIAD_START - (function() if _truth(((j_year < 0))) then return 0 else return 1 end end)())
    return M.olympiad((M.quotient(years, 4) + 1), ((years % 4) + 1))
end

-- TYPE real -> angle
-- TYPE list-of-reals -> list-of-angles
-- x degrees.
-- For typesetting purposes.
function M.deg(x)
    return x
end

-- TYPE season
-- Longitude of sun at vernal equinox.
M.SPRING = M.deg(0)

-- TYPE season
-- Longitude of sun at summer solstice.
M.SUMMER = M.deg(90)

-- TYPE season
-- Longitude of sun at autumnal equinox.
M.AUTUMN = M.deg(180)

-- TYPE season
-- Longitude of sun at winter solstice.
M.WINTER = M.deg(270)

-- TYPE (season gregorian-year positive-real moment)
-- TYPE -> list-of-moments
-- Moments of season in Gregorian year g-year.
-- Seasonal year is cap-L days, seasons are given as
-- longitudes and are of equal length,
-- and a seasonal year started at moment start.
function M.cycle_in_gregorian(season, g_year, cap_L, start)
    local year = M.gregorian_year_range(g_year)
    local pos = ((season / M.deg(360)) * cap_L)
    local cap_Delta = (pos - (start % cap_L))
    return M.positions_in_range(pos, cap_L, cap_Delta, year)
end

-- TYPE (season gregorian-year) -> list-of-moments
-- Moment(s) of Julian season in Gregorian year g-year.
function M.julian_season_in_gregorian(season, g_year)
    local cap_Y = (365 + M.hr(6))
    local offset = ((season / M.deg(360)) * cap_Y)
    return M.cycle_in_gregorian(season, g_year, cap_Y, (M.fixed_from_julian(M.julian_date(M.bce(1), M.MARCH, 23)) + offset))
end

-- TYPE (julian-month julian-day gregorian-year)
-- TYPE -> list-of-fixed-dates
-- List of the fixed dates of Julian month j-month, day
-- j-day that occur in Gregorian year g-year.
function M.julian_in_gregorian(j_month, j_day, g_year)
    local jan1 = M.gregorian_new_year(g_year)
    local y = M.standard_year(M.julian_from_fixed(jan1))
    local y_prime = (function() if _truth(((y == -1))) then return 1 else return (y + 1) end end)()
    local date0 = M.fixed_from_julian(M.julian_date(y, j_month, j_day))
    local date1 = M.fixed_from_julian(M.julian_date(y_prime, j_month, j_day))
    return M.list_range(_list(date0, date1), M.gregorian_year_range(g_year))
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of zero or one fixed dates of Eastern Orthodox
-- Christmas in Gregorian year g-year.
function M.eastern_orthodox_christmas(g_year)
    return M.julian_in_gregorian(M.DECEMBER, 25, g_year)
end


----- D.4 The Coptic and Ethiopic Calendars -----

-- TYPE (coptic-year coptic-month coptic-day) -> coptic-date
function M.coptic_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE fixed-date
-- Fixed date of start of the Coptic calendar.
M.COPTIC_EPOCH = M.fixed_from_julian(M.julian_date(M.ce(284), M.AUGUST, 29))

-- TYPE coptic-year -> boolean
-- True if c-year is a leap year on the Coptic calendar.
function M.coptic_leap_year_p(c_year)
    return (((c_year % 4) == 3))
end

-- TYPE coptic-date -> fixed-date
-- Fixed date of Coptic date c-date.
function M.fixed_from_coptic(c_date)
    local month = M.standard_month(c_date)
    local day = M.standard_day(c_date)
    local year = M.standard_year(c_date)
    return (M.COPTIC_EPOCH + -1 + (365 * (year - 1)) + M.quotient(year, 4) + (30 * (month - 1)) + day)
end

-- TYPE fixed-date -> coptic-date
-- Coptic equivalent of fixed date.
function M.coptic_from_fixed(date)
    local year = M.quotient(((4 * (date - M.COPTIC_EPOCH)) + 1463), 1461)
    local month = (M.quotient((date - M.fixed_from_coptic(M.coptic_date(year, 1, 1))), 30) + 1)
    local day = (date - -1 - M.fixed_from_coptic(M.coptic_date(year, month, 1)))
    return M.coptic_date(year, month, day)
end

-- TYPE (ethiopic-year ethiopic-month ethiopic-day)
-- TYPE -> ethiopic-date
function M.ethiopic_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE fixed-date
-- Fixed date of start of the Ethiopic calendar.
M.ETHIOPIC_EPOCH = M.fixed_from_julian(M.julian_date(M.ce(8), M.AUGUST, 29))

-- TYPE ethiopic-date -> fixed-date
-- Fixed date of Ethiopic date e-date.
function M.fixed_from_ethiopic(e_date)
    local month = M.standard_month(e_date)
    local day = M.standard_day(e_date)
    local year = M.standard_year(e_date)
    return (M.ETHIOPIC_EPOCH + (M.fixed_from_coptic(M.coptic_date(year, month, day)) - M.COPTIC_EPOCH))
end

-- TYPE fixed-date -> ethiopic-date
-- Ethiopic equivalent of fixed date.
function M.ethiopic_from_fixed(date)
    return M.coptic_from_fixed((date + (M.COPTIC_EPOCH - M.ETHIOPIC_EPOCH)))
end

-- TYPE (coptic-month coptic-day gregorian-year)
-- TYPE -> list-of-fixed-dates
-- List of the fixed dates of Coptic month c-month, day
-- c-day that occur in Gregorian year g-year.
function M.coptic_in_gregorian(c_month, c_day, g_year)
    local jan1 = M.gregorian_new_year(g_year)
    local y = M.standard_year(M.coptic_from_fixed(jan1))
    local date0 = M.fixed_from_coptic(M.coptic_date(y, c_month, c_day))
    local date1 = M.fixed_from_coptic(M.coptic_date((y + 1), c_month, c_day))
    return M.list_range(_list(date0, date1), M.gregorian_year_range(g_year))
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of zero or one fixed dates of Coptic Christmas
-- in Gregorian year g-year.
function M.coptic_christmas(g_year)
    return M.coptic_in_gregorian(4, 29, g_year)
end


----- D.5 The ISO Calendar -----

-- TYPE (iso-year iso-week iso-day) -> iso-date
function M.iso_date(year, week, day)
    return _list(year, week, day)
end

-- TYPE iso-date -> iso-week
function M.iso_week(date)
    return _nth(1, date)
end

-- TYPE iso-date -> day-of-week
function M.iso_day(date)
    return _nth(2, date)
end

-- TYPE iso-date -> iso-year
function M.iso_year(date)
    return _nth(0, date)
end

-- TYPE iso-date -> fixed-date
-- Fixed date equivalent to ISO i-date.
function M.fixed_from_iso(i_date)
    local week = M.iso_week(i_date)
    local day = M.iso_day(i_date)
    local year = M.iso_year(i_date)
    return (M.nth_kday(week, M.SUNDAY, M.gregorian_date((year - 1), M.DECEMBER, 28)) + day)
end

-- TYPE fixed-date -> iso-date
-- ISO (year week day) corresponding to the fixed date.
function M.iso_from_fixed(date)
    local approx = M.gregorian_year_from_fixed((date - 3))
    local year = (function() if _truth(((date >= M.fixed_from_iso(M.iso_date((approx + 1), 1, 1))))) then return (approx + 1) else return approx end end)()
    local week = (M.quotient((date - M.fixed_from_iso(M.iso_date(year, 1, 1))), 7) + 1)
    local day = M.amod((date - M.rd(0)), 7)
    return M.iso_date(year, week, day)
end

-- TYPE iso-year -> boolean
-- True if i-year is a long (53-week) year.
function M.iso_long_year_p(i_year)
    local jan1 = M.day_of_week_from_fixed(M.gregorian_new_year(i_year))
    local dec31 = M.day_of_week_from_fixed(M.gregorian_year_end(i_year))
    return (_truth(((jan1 == M.THURSDAY))) or _truth(((dec31 == M.THURSDAY))))
end


----- D.6 The Icelandic Calendar -----

-- TYPE (icelandic-year icelandic-season
-- TYPE icelandic-week icelandic-weekday) -> icelandic-date
function M.icelandic_date(year, season, week, weekday)
    return _list(year, season, week, weekday)
end

-- TYPE icelandic-date -> icelandic-year
function M.icelandic_year(i_date)
    return _nth(0, i_date)
end

-- TYPE icelandic-date -> icelandic-season
function M.icelandic_season(i_date)
    return _nth(1, i_date)
end

-- TYPE icelandic-date -> icelandic-week
function M.icelandic_week(i_date)
    return _nth(2, i_date)
end

-- TYPE icelandic-date -> icelandic-weekday
function M.icelandic_weekday(i_date)
    return _nth(3, i_date)
end

-- TYPE fixed-date
-- Fixed date of start of the Icelandic calendar.
M.ICELANDIC_EPOCH = M.fixed_from_gregorian(M.gregorian_date(1, M.APRIL, 19))

-- TYPE icelandic-year -> fixed-date
-- Fixed date of start of Icelandic year i-year.
function M.icelandic_summer(i_year)
    local apr19 = (M.ICELANDIC_EPOCH + (365 * (i_year - 1)) + M.sigma(_list(M.to_radix(i_year, _list(4, 25, 4)), _list(97, 24, 1, 0)), function(y, a) return (y * a) end))
    return M.kday_on_or_after(M.THURSDAY, apr19)
end

-- TYPE icelandic-year -> fixed-date
-- Fixed date of start of Icelandic winter season
-- in Icelandic year i-year.
function M.icelandic_winter(i_year)
    return (M.icelandic_summer((i_year + 1)) - 180)
end

-- TYPE icelandic-date -> fixed-date
-- Fixed date equivalent to Icelandic i-date.
function M.fixed_from_icelandic(i_date)
    local year = M.icelandic_year(i_date)
    local season = M.icelandic_season(i_date)
    local week = M.icelandic_week(i_date)
    local weekday = M.icelandic_weekday(i_date)
    local start = (function() if _truth(((season == M.SUMMER))) then return M.icelandic_summer(year) else return M.icelandic_winter(year) end end)()
    local shift = (function() if _truth(((season == M.SUMMER))) then return M.THURSDAY else return M.SATURDAY end end)()
    return (start + (7 * (week - 1)) + ((weekday - shift) % 7))
end

-- TYPE fixed-date -> icelandic-date
-- Icelandic (year season week weekday) corresponding to
-- the fixed date.
function M.icelandic_from_fixed(date)
    local approx = M.quotient((date - M.ICELANDIC_EPOCH - -369), (146097 / 400))
    local year = (function() if _truth(((date >= M.icelandic_summer(approx)))) then return approx else return (approx - 1) end end)()
    local season = (function() if _truth(((date < M.icelandic_winter(year)))) then return M.SUMMER else return M.WINTER end end)()
    local start = (function() if _truth(((season == M.SUMMER))) then return M.icelandic_summer(year) else return M.icelandic_winter(year) end end)()
    local week = (M.quotient((date - start), 7) + 1)
    local weekday = M.day_of_week_from_fixed(date)
    return M.icelandic_date(year, season, week, weekday)
end

-- TYPE icelandic-year -> boolean
-- True if Icelandic i-year is a leap year (53 weeks)
-- on the Icelandic calendar.
function M.icelandic_leap_year_p(i_year)
    return (((M.icelandic_summer((i_year + 1)) - M.icelandic_summer(i_year)) ~= 364))
end

-- TYPE icelandic-date -> icelandic-month
-- Month of i-date on the Icelandic calendar.
-- Epagomenae are ~month~ 0.
function M.icelandic_month(i_date)
    local date = M.fixed_from_icelandic(i_date)
    local year = M.icelandic_year(i_date)
    local season = M.icelandic_season(i_date)
	local summer = M.icelandic_summer(year)
    local midsummer = (M.icelandic_winter(year) - 90)
    local start = (function() if _truth(((season == M.WINTER))) then return M.icelandic_winter(year) elseif _truth(((date >= midsummer))) then return (midsummer - 90) elseif _truth(((date < (summer + 90)))) then return summer else return midsummer end end)()
    local month = (M.quotient((date - start), 30) + 1)
	-- This part isn't in the book, but I added it so I can put the Icelandic day of the month on Wikipedia
    local day = (function() if _truth(((month == 0))) then return (date - (summer + 90) + 1) else return (((date - start) % 30) + 1) end end)()
    return _list(month, day)
end

-- TYPE fixed-date -> (icelandic-month icelandic-day)
-- This isn't in the book but I need it for Wikipedia
function M.icelandic_month_from_fixed(date)
    return M.icelandic_month(M.icelandic_from_fixed(date))
end

----- D.7 The Islamic Calendar -----

-- TYPE (islamic-year islamic-month islamic-day)
-- TYPE -> islamic-date
function M.islamic_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE fixed-date
-- Fixed date of start of the Islamic calendar.
M.ISLAMIC_EPOCH = M.fixed_from_julian(M.julian_date(M.ce(622), M.JULY, 16))

-- TYPE islamic-year -> boolean
-- True if i-year is an Islamic leap year.
function M.islamic_leap_year_p(i_year)
    return ((((14 + (11 * i_year)) % 30) < 11))
end

-- TYPE islamic-date -> fixed-date
-- Fixed date equivalent to Islamic date i-date.
function M.fixed_from_islamic(i_date)
    local month = M.standard_month(i_date)
    local day = M.standard_day(i_date)
    local year = M.standard_year(i_date)
    return ((M.ISLAMIC_EPOCH - 1) + ((year - 1) * 354) + M.quotient((3 + (11 * year)), 30) + (29 * (month - 1)) + M.quotient(month, 2) + day)
end

-- TYPE fixed-date -> islamic-date
-- Islamic date (year month day) corresponding to fixed
-- date.
function M.islamic_from_fixed(date)
    local year = M.quotient(((30 * (date - M.ISLAMIC_EPOCH)) + 10646), 10631)
    local prior_days = (date - M.fixed_from_islamic(M.islamic_date(year, 1, 1)))
    local month = M.quotient(((11 * prior_days) + 330), 325)
    local day = ((date - M.fixed_from_islamic(M.islamic_date(year, month, 1))) + 1)
    return M.islamic_date(year, month, day)
end

-- TYPE (islamic-month islamic-day gregorian-year)
-- TYPE -> list-of-fixed-dates
-- List of the fixed dates of Islamic month i-month, day
-- i-day that occur in Gregorian year g-year.
function M.islamic_in_gregorian(i_month, i_day, g_year)
    local jan1 = M.gregorian_new_year(g_year)
    local y = M.standard_year(M.islamic_from_fixed(jan1))
    local date0 = M.fixed_from_islamic(M.islamic_date(y, i_month, i_day))
    local date1 = M.fixed_from_islamic(M.islamic_date((y + 1), i_month, i_day))
    local date2 = M.fixed_from_islamic(M.islamic_date((y + 2), i_month, i_day))
    return M.list_range(_list(date0, date1, date2), M.gregorian_year_range(g_year))
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of fixed dates of Mawlid an-Nabi occurring in
-- Gregorian year g-year.
function M.mawlid(g_year)
    return M.islamic_in_gregorian(3, 12, g_year)
end


----- D.8 The Hebrew Calendar -----

-- TYPE (hebrew-year hebrew-month hebrew-day) -> hebrew-date
function M.hebrew_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE hebrew-month
-- Nisan is month number 1.
M.NISAN = 1

-- TYPE hebrew-month
-- Iyyar is month number 2.
M.IYYAR = 2

-- TYPE hebrew-month
-- Sivan is month number 3.
M.SIVAN = 3

-- TYPE hebrew-month
-- Tammuz is month number 4.
M.TAMMUZ = 4

-- TYPE hebrew-month
-- Av is month number 5.
M.AV = 5

-- TYPE hebrew-month
-- Elul is month number 6.
M.ELUL = 6

-- TYPE hebrew-month
-- Tishri is month number 7.
M.TISHRI = 7

-- TYPE hebrew-month
-- Marheshvan is month number 8.
M.MARHESHVAN = 8

-- TYPE hebrew-month
-- Kislev is month number 9.
M.KISLEV = 9

-- TYPE hebrew-month
-- Tevet is month number 10.
M.TEVET = 10

-- TYPE hebrew-month
-- Shevat is month number 11.
M.SHEVAT = 11

-- TYPE hebrew-month
-- Adar is month number 12.
M.ADAR = 12

-- TYPE hebrew-month
-- Adar II is month number 13.
M.ADARII = 13

-- TYPE hebrew-year -> boolean
-- True if h-year is a leap year on Hebrew calendar.
function M.hebrew_leap_year_p(h_year)
    return (((((7 * h_year) + 1) % 19) < 7))
end

-- TYPE hebrew-year -> hebrew-month
-- Last month of Hebrew year h-year.
function M.last_month_of_hebrew_year(h_year)
    if _truth(M.hebrew_leap_year_p(h_year)) then
        return M.ADARII
    else
        return M.ADAR
    end
end

-- TYPE hebrew-year -> boolean
-- True if h-year is a sabbatical year on the Hebrew
-- calendar.
function M.hebrew_sabbatical_year_p(h_year)
    return (((h_year % 7) == 0))
end

-- TYPE fixed-date
-- Fixed date of start of the Hebrew calendar, that is,
-- Tishri 1, 1 AM.
M.HEBREW_EPOCH = M.fixed_from_julian(M.julian_date(M.bce(3761), M.OCTOBER, 7))

-- TYPE (hebrew-year hebrew-month) -> rational-moment
-- Moment of mean conjunction of h-month in Hebrew
-- h-year.
function M.molad(h_year, h_month)
    local y = (function() if _truth(((h_month < M.TISHRI))) then return (h_year + 1) else return h_year end end)()
    local months_elapsed = ((h_month - M.TISHRI) + M.quotient(((235 * y) - 234), 19))
    return (M.HEBREW_EPOCH + (-876 / 25920) + (months_elapsed * (29 + M.hr(12) + (793 / 25920))))
end

-- TYPE hebrew-year -> integer
-- Number of days elapsed from the (Sunday) noon prior
-- to the epoch of the Hebrew calendar to the mean
-- conjunction (molad) of Tishri of Hebrew year h-year,
-- or one day later.
function M.hebrew_calendar_elapsed_days(h_year)
    local months_elapsed = M.quotient(((235 * h_year) - 234), 19)
    local parts_elapsed = (12084 + (13753 * months_elapsed))
    local days = ((29 * months_elapsed) + M.quotient(parts_elapsed, 25920))
    if _truth(((((3 * (days + 1)) % 7) < 3))) then
        return (days + 1)
    else
        return days
    end
end

-- TYPE hebrew-year -> 0-2
-- Delays to start of Hebrew year h-year to keep ordinary
-- year in range 353-
function M.hebrew_year_length_correction(h_year)
    local ny0 = M.hebrew_calendar_elapsed_days((h_year - 1))
    local ny1 = M.hebrew_calendar_elapsed_days(h_year)
    local ny2 = M.hebrew_calendar_elapsed_days((h_year + 1))
    if _truth((((ny2 - ny1) == 356))) then
        return 2
    elseif _truth((((ny1 - ny0) == 382))) then
        return 1
    else
        return 0
    end
end

-- TYPE hebrew-year -> fixed-date
-- Fixed date of Hebrew new year h-year.
function M.hebrew_new_year(h_year)
    return (M.HEBREW_EPOCH + M.hebrew_calendar_elapsed_days(h_year) + M.hebrew_year_length_correction(h_year))
end

-- TYPE (hebrew-year hebrew-month) -> hebrew-day
-- Last day of month h-month in Hebrew year h-year.
function M.last_day_of_hebrew_month(h_year, h_month)
    if _truth((_truth(_member(h_month, _list(M.IYYAR, M.TAMMUZ, M.ELUL, M.TEVET, M.ADARII))) or _truth((_truth(((h_month == M.ADAR))) and _truth((not _truth(M.hebrew_leap_year_p(h_year)))))) or _truth((_truth(((h_month == M.MARHESHVAN))) and _truth((not _truth(M.long_marheshvan_p(h_year)))))) or _truth((_truth(((h_month == M.KISLEV))) and _truth(M.short_kislev_p(h_year)))))) then
        return 29
    else
        return 30
    end
end

-- TYPE hebrew-year -> boolean
-- True if Marheshvan is long in Hebrew year h-year.
function M.long_marheshvan_p(h_year)
    return _member(M.days_in_hebrew_year(h_year), _list(355, 385))
end

-- TYPE hebrew-year -> boolean
-- True if Kislev is short in Hebrew year h-year.
function M.short_kislev_p(h_year)
    return _member(M.days_in_hebrew_year(h_year), _list(353, 383))
end

-- TYPE hebrew-year -> {353,354,355,383,384,385}
-- Number of days in Hebrew year h-year.
function M.days_in_hebrew_year(h_year)
    return (M.hebrew_new_year((h_year + 1)) - M.hebrew_new_year(h_year))
end

-- TYPE hebrew-date -> fixed-date
-- Fixed date of Hebrew date h-date.
function M.fixed_from_hebrew(h_date)
    local month = M.standard_month(h_date)
    local day = M.standard_day(h_date)
    local year = M.standard_year(h_date)
    return (M.hebrew_new_year(year) + day + -1 + (function() if _truth(((month < M.TISHRI))) then return (M.sum(function(m) return M.last_day_of_hebrew_month(year, m) end, M.TISHRI, function(m) return _truth(((m <= M.last_month_of_hebrew_year(year)))) end) + M.sum(function(m) return M.last_day_of_hebrew_month(year, m) end, M.NISAN, function(m) return _truth(((m < month))) end)) else return M.sum(function(m) return M.last_day_of_hebrew_month(year, m) end, M.TISHRI, function(m) return _truth(((m < month))) end) end end)())
end

-- TYPE fixed-date -> hebrew-date
-- Hebrew (year month day) corresponding to fixed date.
-- The fraction can be approximated by 365.25.
function M.hebrew_from_fixed(date)
    local approx = (M.quotient((date - M.HEBREW_EPOCH), (35975351 / 98496)) + 1)
    local year = M.final((approx - 1), function(y) return _truth(((M.hebrew_new_year(y) <= date))) end)
    local start = (function() if _truth(((date < M.fixed_from_hebrew(M.hebrew_date(year, M.NISAN, 1))))) then return M.TISHRI else return M.NISAN end end)()
    local month = M.next(start, function(m) return _truth(((date <= M.fixed_from_hebrew(M.hebrew_date(year, m, M.last_day_of_hebrew_month(year, m)))))) end)
    local day = ((date - M.fixed_from_hebrew(M.hebrew_date(year, month, 1))) + 1)
    return M.hebrew_date(year, month, day)
end

-- TYPE duration -> fixed-date
-- Fixed date of the molad that occurs moon days
-- and fractional days into the week.
function M.fixed_from_molad(moon)
    local r = (((74377 * moon) - (2879 / 2160)) % 7)
    return M.fixed_from_moment((M.molad(1, M.TISHRI) + (r * 765433)))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Yom Kippur occurring in Gregorian year
-- g-year.
function M.yom_kippur(g_year)
    local h_year = ((g_year - M.gregorian_year_from_fixed(M.HEBREW_EPOCH)) + 1)
    return M.fixed_from_hebrew(M.hebrew_date(h_year, M.TISHRI, 10))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Passover occurring in Gregorian year
-- g-year.
function M.passover(g_year)
    local h_year = (g_year - M.gregorian_year_from_fixed(M.HEBREW_EPOCH))
    return M.fixed_from_hebrew(M.hebrew_date(h_year, M.NISAN, 15))
end

-- TYPE fixed-date -> omer-count
-- Number of elapsed weeks and days in the omer at date.
-- Returns bogus if that date does not fall during the
-- omer.
function M.omer(date)
    local c = (date - M.passover(M.gregorian_year_from_fixed(date)))
    if _truth(((1 <= c) and (c <= 49))) then
        return _list(M.quotient(c, 7), (c % 7))
    else
        return M.BOGUS
    end
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Purim occurring in Gregorian year g-year.
function M.purim(g_year)
    local h_year = (g_year - M.gregorian_year_from_fixed(M.HEBREW_EPOCH))
    local last_month = M.last_month_of_hebrew_year(h_year)
    return M.fixed_from_hebrew(M.hebrew_date(h_year, last_month, 14))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Ta’anit Esther occurring in
-- Gregorian year g-year.
function M.ta_anit_esther(g_year)
    local purim_date = M.purim(g_year)
    if _truth(((M.day_of_week_from_fixed(purim_date) == M.SUNDAY))) then
        return (purim_date - 3)
    else
        return (purim_date - 1)
    end
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Tishah be-Av occurring in
-- Gregorian year g-year.
function M.tishah_be_av(g_year)
    local h_year = (g_year - M.gregorian_year_from_fixed(M.HEBREW_EPOCH))
    local av9 = M.fixed_from_hebrew(M.hebrew_date(h_year, M.AV, 9))
    if _truth(((M.day_of_week_from_fixed(av9) == M.SATURDAY))) then
        return (av9 + 1)
    else
        return av9
    end
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Yom ha-Zikkaron occurring in Gregorian
-- year g-year.
function M.yom_ha_zikkaron(g_year)
    local h_year = (g_year - M.gregorian_year_from_fixed(M.HEBREW_EPOCH))
    local iyyar4 = M.fixed_from_hebrew(M.hebrew_date(h_year, M.IYYAR, 4))
    if _truth(_member(M.day_of_week_from_fixed(iyyar4), _list(M.THURSDAY, M.FRIDAY))) then
        return M.kday_before(M.WEDNESDAY, iyyar4)
    elseif _truth(((M.SUNDAY == M.day_of_week_from_fixed(iyyar4)))) then
        return (iyyar4 + 1)
    else
        return iyyar4
    end
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of fixed dates of Sh’ela occurring in
-- Gregorian year g-year.
function M.sh_ela(g_year)
    return M.coptic_in_gregorian(3, 26, g_year)
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of fixed date of Birkath ha-Hama occurring in
-- Gregorian year g-year, if it occurs.
function M.birkath_ha_hama(g_year)
    local dates = M.coptic_in_gregorian(7, 30, g_year)
    if _truth((_truth((not _truth(_equal(dates, NIL)))) and _truth((((M.standard_year(M.coptic_from_fixed(_nth(0, dates))) % 28) == 17))))) then
        return dates
    else
        return NIL
    end
end

-- TYPE (season gregorian-year) -> list-of-moments
-- Moment(s) of season in Gregorian year g-year
-- per Samuel.
function M.samuel_season_in_gregorian(season, g_year)
    local cap_Y = (365 + M.hr(6))
    local offset = ((season / M.deg(360)) * cap_Y)
    return M.cycle_in_gregorian(season, g_year, cap_Y, (M.fixed_from_hebrew(M.hebrew_date(1, M.ADAR, 21)) + M.hr(18) + offset))
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of fixed date of Birkath ha-Hama occurring in
-- Gregorian year g-year, if it occurs.
function M.alt_birkath_ha_hama(g_year)
    local cap_Y = (365 + M.hr(6))
    local season = (M.SPRING + (M.hr(6) * (M.deg(360) / cap_Y)))
    local moments = M.samuel_season_in_gregorian(season, g_year)
    if _truth((_truth((not _truth(_equal(moments, NIL)))) and _truth(((M.day_of_week_from_fixed(_nth(0, moments)) == M.WEDNESDAY))) and _truth(((M.time_from_moment(_nth(0, moments)) == M.hr(0)))))) then
        return _list(M.fixed_from_moment(_nth(0, moments)))
    else
        return NIL
    end
end

-- TYPE (season gregorian-year) -> list-of-moments
-- Moment(s) of season in Gregorian year g-year
-- per R. Adda bar Ahava.
function M.adda_season_in_gregorian(season, g_year)
    local cap_Y = (365 + M.hr((5 + (3791 / 4104))))
    local offset = ((season / M.deg(360)) * cap_Y)
    return M.cycle_in_gregorian(season, g_year, cap_Y, (M.fixed_from_hebrew(M.hebrew_date(1, M.ADAR, 28)) + M.hr(18) + offset))
end

-- TYPE (hebrew-month hebrew-day gregorian-year)
-- TYPE -> list-of-fixed-dates
-- List of the fixed dates of Hebrew month h-month, day
-- h-day that occur in Gregorian year g-year.
function M.hebrew_in_gregorian(h_month, h_day, g_year)
    local jan1 = M.gregorian_new_year(g_year)
    local y = M.standard_year(M.hebrew_from_fixed(jan1))
    local date0 = M.fixed_from_hebrew(M.hebrew_date(y, h_month, h_day))
    local date1 = M.fixed_from_hebrew(M.hebrew_date((y + 1), h_month, h_day))
    local date2 = M.fixed_from_hebrew(M.hebrew_date((y + 2), h_month, h_day))
    return M.list_range(_list(date0, date1, date2), M.gregorian_year_range(g_year))
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- Fixed date(s) of first day of Hanukkah
-- occurring in Gregorian year g-year.
function M.hanukkah(g_year)
    return M.hebrew_in_gregorian(M.KISLEV, 25, g_year)
end

-- TYPE (hebrew-date hebrew-year) -> fixed-date
-- Fixed date of the anniversary of Hebrew birthdate
-- occurring in Hebrew h-year.
function M.hebrew_birthday(birthdate, h_year)
    local birth_day = M.standard_day(birthdate)
    local birth_month = M.standard_month(birthdate)
    local birth_year = M.standard_year(birthdate)
    if _truth(((birth_month == M.last_month_of_hebrew_year(birth_year)))) then
        return M.fixed_from_hebrew(M.hebrew_date(h_year, M.last_month_of_hebrew_year(h_year), birth_day))
    else
        return (M.fixed_from_hebrew(M.hebrew_date(h_year, birth_month, 1)) + birth_day + -1)
    end
end

-- TYPE (hebrew-date gregorian-year)
-- TYPE -> list-of-fixed-dates
-- List of the fixed dates of Hebrew birthday
-- that occur in Gregorian g-year.
function M.hebrew_birthday_in_gregorian(birthdate, g_year)
    local jan1 = M.gregorian_new_year(g_year)
    local y = M.standard_year(M.hebrew_from_fixed(jan1))
    local date0 = M.hebrew_birthday(birthdate, y)
    local date1 = M.hebrew_birthday(birthdate, (y + 1))
    local date2 = M.hebrew_birthday(birthdate, (y + 2))
    return M.list_range(_list(date0, date1, date2), M.gregorian_year_range(g_year))
end

-- TYPE (hebrew-date hebrew-year) -> fixed-date
-- Fixed date of the anniversary of Hebrew death-date
-- occurring in Hebrew h-year.
function M.yahrzeit(death_date, h_year)
    local death_day = M.standard_day(death_date)
    local death_month = M.standard_month(death_date)
    local death_year = M.standard_year(death_date)
    if _truth((_truth(((death_month == M.MARHESHVAN))) and _truth(((death_day == 30))) and _truth((not _truth(M.long_marheshvan_p((death_year + 1))))))) then
        return (M.fixed_from_hebrew(M.hebrew_date(h_year, M.KISLEV, 1)) - 1)
    elseif _truth((_truth(((death_month == M.KISLEV))) and _truth(((death_day == 30))) and _truth(M.short_kislev_p((death_year + 1))))) then
        return (M.fixed_from_hebrew(M.hebrew_date(h_year, M.TEVET, 1)) - 1)
    elseif _truth(((death_month == M.ADARII))) then
        return M.fixed_from_hebrew(M.hebrew_date(h_year, M.last_month_of_hebrew_year(h_year), death_day))
    elseif _truth((_truth(((death_day == 30))) and _truth(((death_month == M.ADAR))) and _truth((not _truth(M.hebrew_leap_year_p(h_year)))))) then
        return M.fixed_from_hebrew(M.hebrew_date(h_year, M.SHEVAT, 30))
    else
        return (M.fixed_from_hebrew(M.hebrew_date(h_year, death_month, 1)) + death_day + -1)
    end
end

-- TYPE (hebrew-date gregorian-year)
-- TYPE -> list-of-fixed-dates
-- List of the fixed dates of death-date (yahrzeit)
-- that occur in Gregorian year g-year.
function M.yahrzeit_in_gregorian(death_date, g_year)
    local jan1 = M.gregorian_new_year(g_year)
    local y = M.standard_year(M.hebrew_from_fixed(jan1))
    local date0 = M.yahrzeit(death_date, y)
    local date1 = M.yahrzeit(death_date, (y + 1))
    local date2 = M.yahrzeit(death_date, (y + 2))
    return M.list_range(_list(date0, date1, date2), M.gregorian_year_range(g_year))
end

-- TYPE (list-of-weekdays integer) -> list-of-weekdays
-- Shift each weekday on list l by cap-Delta days
function M.shift_days(l, cap_Delta)
    if _truth(_equal(l, NIL)) then
        return NIL
    else
        return _append(_list(((_nth(0, l) + cap_Delta) % 7)), M.shift_days(_rest(l), cap_Delta))
    end
end

-- TYPE (hebrew-month hebrew-day) -> list-of-weekdays
-- Possible days of week
function M.possible_hebrew_days(h_month, h_day)
    local h_date0 = M.hebrew_date(5, M.NISAN, 1)
    local h_year = (function() if _truth(((h_month > M.ELUL))) then return 6 else return 5 end end)()
    local h_date = M.hebrew_date(h_year, h_month, h_day)
    local n = (M.fixed_from_hebrew(h_date) - M.fixed_from_hebrew(h_date0))
    local basic = _list(M.TUESDAY, M.THURSDAY, M.SATURDAY)
    local extra = (function() if _truth((_truth(((h_month == M.MARHESHVAN))) and _truth(((h_day == 30))))) then return NIL elseif _truth((_truth(((h_month == M.KISLEV))) and _truth(((h_day < 30))))) then return _list(M.MONDAY, M.WEDNESDAY, M.FRIDAY) elseif _truth((_truth(((h_month == M.KISLEV))) and _truth(((h_day == 30))))) then return _list(M.MONDAY) elseif _truth(_member(h_month, _list(M.TEVET, M.SHEVAT))) then return _list(M.SUNDAY, M.MONDAY) elseif _truth((_truth(((h_month == M.ADAR))) and _truth(((h_day < 30))))) then return _list(M.SUNDAY, M.MONDAY) else return _list(M.SUNDAY) end end)()
    return M.shift_days(_append(basic, extra), n)
end


----- D.9 The Ecclesiastical Calendars -----

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Orthodox Easter in Gregorian year g-year.
function M.orthodox_easter(g_year)
    local shifted_epact = ((14 + (11 * (g_year % 19))) % 30)
    local j_year = (function() if _truth(((g_year > 0))) then return g_year else return (g_year - 1) end end)()
    local paschal_moon = (M.fixed_from_julian(M.julian_date(j_year, M.APRIL, 19)) - shifted_epact)
    return M.kday_after(M.SUNDAY, paschal_moon)
end

-- TYPE gregorian-year -> fixed-date
-- Alternative calculation of fixed date of Orthodox Easter
-- in Gregorian year g-year.
function M.alt_orthodox_easter(g_year)
    local paschal_moon = ((354 * g_year) + (30 * M.quotient(((7 * g_year) + 8), 19)) + M.quotient(g_year, 4) + (-M.quotient(g_year, 19)) + -273 + M.GREGORIAN_EPOCH)
    return M.kday_after(M.SUNDAY, paschal_moon)
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Easter in Gregorian year g-year.
function M.easter(g_year)
    local century = (M.quotient(g_year, 100) + 1)
    local shifted_epact = ((14 + (11 * (g_year % 19)) + (-M.quotient((3 * century), 4)) + M.quotient((5 + (8 * century)), 25)) % 30)
    local adjusted_epact = (function() if _truth((_truth(((shifted_epact == 0))) or _truth((_truth(((shifted_epact == 1))) and _truth(((10 < (g_year % 19)))))))) then return (shifted_epact + 1) else return shifted_epact end end)()
    local paschal_moon = (M.fixed_from_gregorian(M.gregorian_date(g_year, M.APRIL, 19)) - adjusted_epact)
    return M.kday_after(M.SUNDAY, paschal_moon)
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Pentecost in Gregorian year g-year.
function M.pentecost(g_year)
    return (M.easter(g_year) + 49)
end


----- D.10 The Old Hindu Calendars -----

-- TYPE fixed-date
-- Fixed date of start of the Hindu calendar (Kali Yuga).
M.HINDU_EPOCH = M.fixed_from_julian(M.julian_date(M.bce(3102), M.FEBRUARY, 18))

-- TYPE fixed-date -> integer
-- Elapsed days (Ahargana) to date since Hindu epoch (KY).
function M.hindu_day_count(date)
    return (date - M.HINDU_EPOCH)
end

-- TYPE rational
-- Length of Old Hindu solar year.
M.ARYA_SOLAR_YEAR = (1577917500 / 4320000)

-- TYPE rational
-- Number of days in one revolution of Jupiter around the
-- Sun.
M.ARYA_JOVIAN_PERIOD = (1577917500 / 364224)

-- TYPE fixed-date -> 1-60
-- Year of Jupiter cycle at fixed date.
function M.jovian_year(date)
    return M.amod((27 + M.quotient(M.hindu_day_count(date), (M.ARYA_JOVIAN_PERIOD / 12))), 60)
end

-- TYPE rational
-- Length of Old Hindu solar month.
M.ARYA_SOLAR_MONTH = (M.ARYA_SOLAR_YEAR / 12)

-- TYPE hindu-solar-date -> fixed-date
-- Fixed date corresponding to Old Hindu solar date s-date.
function M.fixed_from_old_hindu_solar(s_date)
    local month = M.standard_month(s_date)
    local day = M.standard_day(s_date)
    local year = M.standard_year(s_date)
    return math.ceil((M.HINDU_EPOCH + (year * M.ARYA_SOLAR_YEAR) + ((month - 1) * M.ARYA_SOLAR_MONTH) + day + M.hr(-30)))
end

-- TYPE fixed-date -> hindu-solar-date
-- Old Hindu solar date equivalent to fixed date.
function M.old_hindu_solar_from_fixed(date)
    local sun = (M.hindu_day_count(date) + M.hr(6))
    local year = M.quotient(sun, M.ARYA_SOLAR_YEAR)
    local month = ((M.quotient(sun, M.ARYA_SOLAR_MONTH) % 12) + 1)
    local day = (math.floor((sun % M.ARYA_SOLAR_MONTH)) + 1)
    return M.hindu_solar_date(year, month, day)
end

-- TYPE (old-hindu-lunar-year old-hindu-lunar-month
-- TYPE old-hindu-lunar-leap old-hindu-lunar-day)
-- TYPE -> old-hindu-lunar-date
function M.old_hindu_lunar_date(year, month, leap, day)
    return _list(year, month, leap, day)
end

-- TYPE old-hindu-lunar-date -> old-hindu-lunar-month
function M.old_hindu_lunar_month(date)
    return _nth(1, date)
end

-- TYPE old-hindu-lunar-date -> old-hindu-lunar-leap
function M.old_hindu_lunar_leap(date)
    return _nth(2, date)
end

-- TYPE old-hindu-lunar-date -> old-hindu-lunar-day
function M.old_hindu_lunar_day(date)
    return _nth(3, date)
end

-- TYPE old-hindu-lunar-date -> old-hindu-lunar-year
function M.old_hindu_lunar_year(date)
    return _nth(0, date)
end

-- TYPE rational
-- Length of Old Hindu lunar month.
M.ARYA_LUNAR_MONTH = (1577917500 / 53433336)

-- TYPE rational
-- Length of Old Hindu lunar day.
M.ARYA_LUNAR_DAY = (M.ARYA_LUNAR_MONTH / 30)

-- TYPE old-hindu-lunar-year -> boolean
-- True if l-year is a leap year on the
-- old Hindu calendar.
function M.old_hindu_lunar_leap_year_p(l_year)
    return (((((l_year * M.ARYA_SOLAR_YEAR) - M.ARYA_SOLAR_MONTH) % M.ARYA_LUNAR_MONTH) >= (23902504679 / 1282400064)))
end

-- TYPE fixed-date -> old-hindu-lunar-date
-- Old Hindu lunar date equivalent to fixed date.
function M.old_hindu_lunar_from_fixed(date)
    local sun = (M.hindu_day_count(date) + M.hr(6))
    local new_moon = (sun - (sun % M.ARYA_LUNAR_MONTH))
    local leap = (_truth((((M.ARYA_SOLAR_MONTH - M.ARYA_LUNAR_MONTH) >= (new_moon % M.ARYA_SOLAR_MONTH)))) and _truth((((new_moon % M.ARYA_SOLAR_MONTH) > 0))))
    local month = ((math.ceil((new_moon / M.ARYA_SOLAR_MONTH)) % 12) + 1)
    local day = ((M.quotient(sun, M.ARYA_LUNAR_DAY) % 30) + 1)
    local year = (math.ceil(((new_moon + M.ARYA_SOLAR_MONTH) / M.ARYA_SOLAR_YEAR)) - 1)
    return M.old_hindu_lunar_date(year, month, leap, day)
end

-- TYPE old-hindu-lunar-date -> fixed-date
-- Fixed date corresponding to Old Hindu lunar date
-- l-date.
function M.fixed_from_old_hindu_lunar(l_date)
    local year = M.old_hindu_lunar_year(l_date)
    local month = M.old_hindu_lunar_month(l_date)
    local leap = M.old_hindu_lunar_leap(l_date)
    local day = M.old_hindu_lunar_day(l_date)
    local mina = (((12 * year) - 1) * M.ARYA_SOLAR_MONTH)
    local lunar_new_year = (M.ARYA_LUNAR_MONTH * (M.quotient(mina, M.ARYA_LUNAR_MONTH) + 1))
    return math.ceil((M.HINDU_EPOCH + lunar_new_year + (M.ARYA_LUNAR_MONTH * (function() if _truth((_truth((not _truth(leap))) and _truth(((math.ceil(((lunar_new_year - mina) / (M.ARYA_SOLAR_MONTH - M.ARYA_LUNAR_MONTH))) <= month))))) then return month else return (month - 1) end end)()) + ((day - 1) * M.ARYA_LUNAR_DAY) + M.hr(-6)))
end


----- D.11 The Mayan Calendars -----

-- TYPE (mayan-baktun mayan-katun mayan-tun mayan-uinal
-- TYPE mayan-kin) -> mayan-long-count-date
function M.mayan_long_count_date(baktun, katun, tun, uinal, kin)
    return _list(baktun, katun, tun, uinal, kin)
end

-- TYPE mayan-long-count-date -> mayan-baktun
function M.mayan_baktun(date)
    return _nth(0, date)
end

-- TYPE mayan-long-count-date -> mayan-katun
function M.mayan_katun(date)
    return _nth(1, date)
end

-- TYPE mayan-long-count-date -> mayan-tun
function M.mayan_tun(date)
    return _nth(2, date)
end

-- TYPE mayan-long-count-date -> mayan-uinal
function M.mayan_uinal(date)
    return _nth(3, date)
end

-- TYPE mayan-long-count-date -> mayan-kin
function M.mayan_kin(date)
    return _nth(4, date)
end

-- TYPE fixed-date
-- Fixed date of start of the Mayan calendar, according
-- to the Goodman-Martinez-Thompson correlation.
-- That is, August 11, -3113.
M.MAYAN_EPOCH = M.fixed_from_jd(584283)

-- TYPE mayan-long-count-date -> fixed-date
-- Fixed date corresponding to the Mayan long count,
-- which is a list (baktun katun tun uinal kin).
function M.fixed_from_mayan_long_count(count)
    return (M.MAYAN_EPOCH + M.from_radix(count, _list(20, 20, 18, 20)))
end

-- TYPE fixed-date -> mayan-long-count-date
-- Mayan long count date of fixed date.
function M.mayan_long_count_from_fixed(date)
    return M.to_radix((date - M.MAYAN_EPOCH), _list(20, 20, 18, 20))
end

-- TYPE (mayan-haab-month mayan-haab-day) -> mayan-haab-date
function M.mayan_haab_date(month, day)
    return _list(month, day)
end

-- TYPE mayan-haab-date -> mayan-haab-day
function M.mayan_haab_day(date)
    return _nth(1, date)
end

-- TYPE mayan-haab-date -> mayan-haab-month
function M.mayan_haab_month(date)
    return _nth(0, date)
end

-- TYPE mayan-haab-date -> nonnegative-integer
-- Number of days into cycle of Mayan haab date h-date.
function M.mayan_haab_ordinal(h_date)
    local day = M.mayan_haab_day(h_date)
    local month = M.mayan_haab_month(h_date)
    return (((month - 1) * 20) + day)
end

-- TYPE fixed-date
-- Fixed date of start of haab cycle.
M.MAYAN_HAAB_EPOCH = (M.MAYAN_EPOCH - M.mayan_haab_ordinal(M.mayan_haab_date(18, 8)))

-- TYPE fixed-date -> mayan-haab-date
-- Mayan haab date of fixed date.
function M.mayan_haab_from_fixed(date)
    local count = ((date - M.MAYAN_HAAB_EPOCH) % 365)
    local day = (count % 20)
    local month = (M.quotient(count, 20) + 1)
    return M.mayan_haab_date(month, day)
end

-- TYPE (mayan-haab-date fixed-date) -> fixed-date
-- Fixed date of latest date on or before fixed date
-- that is Mayan haab date haab.
function M.mayan_haab_on_or_before(haab, date)
    return M.mod3((M.mayan_haab_ordinal(haab) + M.MAYAN_HAAB_EPOCH), date, (date - 365))
end

-- TYPE (mayan-tzolkin-number mayan-tzolkin-name)
-- TYPE -> mayan-tzolkin-date
function M.mayan_tzolkin_date(number, name)
    return _list(number, name)
end

-- TYPE mayan-tzolkin-date -> mayan-tzolkin-number
function M.mayan_tzolkin_number(date)
    return _nth(0, date)
end

-- TYPE mayan-tzolkin-date -> mayan-tzolkin-name
function M.mayan_tzolkin_name(date)
    return _nth(1, date)
end

-- TYPE mayan-tzolkin-date -> nonnegative-integer
-- Number of days into Mayan tzolkin cycle of t-date.
function M.mayan_tzolkin_ordinal(t_date)
    local number = M.mayan_tzolkin_number(t_date)
    local name = M.mayan_tzolkin_name(t_date)
    return ((number + -1 + (39 * (number - name))) % 260)
end

-- TYPE fixed-date
-- Start of tzolkin date cycle.
M.MAYAN_TZOLKIN_EPOCH = (M.MAYAN_EPOCH - M.mayan_tzolkin_ordinal(M.mayan_tzolkin_date(4, 20)))

-- TYPE fixed-date -> mayan-tzolkin-date
-- Mayan tzolkin date of fixed date.
function M.mayan_tzolkin_from_fixed(date)
    local count = (date - M.MAYAN_TZOLKIN_EPOCH - -1)
    local number = M.amod(count, 13)
    local name = M.amod(count, 20)
    return M.mayan_tzolkin_date(number, name)
end

-- TYPE (mayan-tzolkin-date fixed-date) -> fixed-date
-- Fixed date of latest date on or before fixed date
-- that is Mayan tzolkin date tzolkin.
function M.mayan_tzolkin_on_or_before(tzolkin, date)
    return M.mod3((M.mayan_tzolkin_ordinal(tzolkin) + M.MAYAN_TZOLKIN_EPOCH), date, (date - 260))
end

-- TYPE fixed-date -> mayan-tzolkin-name
-- Year bearer of year containing fixed date.
-- Returns bogus for uayeb.
function M.mayan_year_bearer_from_fixed(date)
    local x = M.mayan_haab_on_or_before(M.mayan_haab_date(1, 0), date)
    if _truth(((M.mayan_haab_month(M.mayan_haab_from_fixed(date)) == 19))) then
        return M.BOGUS
    else
        return M.mayan_tzolkin_name(M.mayan_tzolkin_from_fixed(x))
    end
end

-- TYPE (mayan-haab-date mayan-tzolkin-date fixed-date)
-- TYPE -> fixed-date
-- Fixed date of latest date on or before date, that is
-- Mayan haab date haab and tzolkin date tzolkin.
-- Returns bogus for impossible combinations.
function M.mayan_calendar_round_on_or_before(haab, tzolkin, date)
    local haab_count = (M.mayan_haab_ordinal(haab) + M.MAYAN_HAAB_EPOCH)
    local tzolkin_count = (M.mayan_tzolkin_ordinal(tzolkin) + M.MAYAN_TZOLKIN_EPOCH)
    local diff = (tzolkin_count - haab_count)
    if _truth((((diff % 5) == 0))) then
        return M.mod3((haab_count + (365 * diff)), date, (date - 18980))
    else
        return M.BOGUS
    end
end

-- TYPE fixed-date
-- Known date of Aztec cycles (Caso’s correlation)
M.AZTEC_CORRELATION = M.fixed_from_julian(M.julian_date(1521, M.AUGUST, 13))

-- TYPE (aztec-xihuitl-month aztec-xihuitl-day) ->
-- TYPE aztec-xihuitl-date
function M.aztec_xihuitl_date(month, day)
    return _list(month, day)
end

-- TYPE aztec-xihuitl-date -> aztec-xihuitl-month
function M.aztec_xihuitl_month(date)
    return _nth(0, date)
end

-- TYPE aztec-xihuitl-date -> aztec-xihuitl-day
function M.aztec_xihuitl_day(date)
    return _nth(1, date)
end

-- TYPE aztec-xihuitl-date -> nonnegative-integer
-- Number of elapsed days into cycle of Aztec xihuitl x-date.
function M.aztec_xihuitl_ordinal(x_date)
    local day = M.aztec_xihuitl_day(x_date)
    local month = M.aztec_xihuitl_month(x_date)
    return (((month - 1) * 20) + (day - 1))
end

-- TYPE fixed-date
-- Start of a xihuitl cycle.
M.AZTEC_XIHUITL_CORRELATION = (M.AZTEC_CORRELATION - M.aztec_xihuitl_ordinal(M.aztec_xihuitl_date(11, 2)))

-- TYPE fixed-date -> aztec-xihuitl-date
-- Aztec xihuitl date of fixed date.
function M.aztec_xihuitl_from_fixed(date)
    local count = ((date - M.AZTEC_XIHUITL_CORRELATION) % 365)
    local day = ((count % 20) + 1)
    local month = (M.quotient(count, 20) + 1)
    return M.aztec_xihuitl_date(month, day)
end

-- TYPE (aztec-xihuitl-date fixed-date) -> fixed-date
-- Fixed date of latest date on or before fixed date
-- that is Aztec xihuitl date xihuitl.
function M.aztec_xihuitl_on_or_before(xihuitl, date)
    return M.mod3((M.AZTEC_XIHUITL_CORRELATION + M.aztec_xihuitl_ordinal(xihuitl)), date, (date - 365))
end

-- TYPE (aztec-tonalpohualli-number aztec-tonalpohualli-name)
-- TYPE -> aztec-tonalpohualli-date
function M.aztec_tonalpohualli_date(number, name)
    return _list(number, name)
end

-- TYPE aztec-tonalpohualli-date -> aztec-tonalpohualli-number
function M.aztec_tonalpohualli_number(date)
    return _nth(0, date)
end

-- TYPE aztec-tonalpohualli-date -> aztec-tonalpohualli-name
function M.aztec_tonalpohualli_name(date)
    return _nth(1, date)
end

-- TYPE aztec-tonalpohualli-date -> nonnegative-integer
-- Number of days into Aztec tonalpohualli cycle of t-date.
function M.aztec_tonalpohualli_ordinal(t_date)
    local number = M.aztec_tonalpohualli_number(t_date)
    local name = M.aztec_tonalpohualli_name(t_date)
    return ((number + -1 + (39 * (number - name))) % 260)
end

-- TYPE fixed-date
-- Start of a tonalpohualli date cycle.
M.AZTEC_TONALPOHUALLI_CORRELATION = (M.AZTEC_CORRELATION - M.aztec_tonalpohualli_ordinal(M.aztec_tonalpohualli_date(1, 5)))

-- TYPE fixed-date -> aztec-tonalpohualli-date
-- Aztec tonalpohualli date of fixed date.
function M.aztec_tonalpohualli_from_fixed(date)
    local count = (date - M.AZTEC_TONALPOHUALLI_CORRELATION - -1)
    local number = M.amod(count, 13)
    local name = M.amod(count, 20)
    return M.aztec_tonalpohualli_date(number, name)
end

-- TYPE (aztec-tonalpohualli-date fixed-date) -> fixed-date
-- Fixed date of latest date on or before fixed date
-- that is Aztec tonalpohualli date tonalpohualli.
function M.aztec_tonalpohualli_on_or_before(tonalpohualli, date)
    return M.mod3((M.AZTEC_TONALPOHUALLI_CORRELATION + M.aztec_tonalpohualli_ordinal(tonalpohualli)), date, (date - 260))
end

-- TYPE (aztec-xiuhmolpilli-number aztec-xiuhmolpilli-name)
-- TYPE -> aztec-xiuhmolpilli-designation
function M.aztec_xiuhmolpilli_designation(number, name)
    return _list(number, name)
end

-- TYPE aztec-xiuhmolpilli-designation -> aztec-xiuhmolpilli-number
function M.aztec_xiuhmolpilli_number(date)
    return _nth(0, date)
end

-- TYPE aztec-xiuhmolpilli-designation -> aztec-xiuhmolpilli-name
function M.aztec_xiuhmolpilli_name(date)
    return _nth(1, date)
end

-- TYPE fixed-date -> aztec-xiuhmolpilli-designation
-- Designation of year containing fixed date.
-- Returns bogus for nemontemi.
function M.aztec_xiuhmolpilli_from_fixed(date)
    local x = M.aztec_xihuitl_on_or_before(M.aztec_xihuitl_date(18, 20), (date + 364))
    local month = M.aztec_xihuitl_month(M.aztec_xihuitl_from_fixed(date))
    if _truth(((month == 19))) then
        return M.BOGUS
    else
        return M.aztec_tonalpohualli_from_fixed(x)
    end
end

-- TYPE (aztec-xihuitl-date aztec-tonalpohualli-date
-- TYPE fixed-date) -> fixed-date
-- Fixed date of latest xihuitl-tonalpohualli combination
-- on or before date. That is the date on or before
-- date that is Aztec xihuitl date xihuitl and
-- tonalpohualli date tonalpohualli.
-- Returns bogus for impossible combinations.
function M.aztec_xihuitl_tonalpohualli_on_or_before(xihuitl, tonalpohualli, date)
    local xihuitl_count = (M.aztec_xihuitl_ordinal(xihuitl) + M.AZTEC_XIHUITL_CORRELATION)
    local tonalpohualli_count = (M.aztec_tonalpohualli_ordinal(tonalpohualli) + M.AZTEC_TONALPOHUALLI_CORRELATION)
    local diff = (tonalpohualli_count - xihuitl_count)
    if _truth((((diff % 5) == 0))) then
        return M.mod3((xihuitl_count + (365 * diff)), date, (date - 18980))
    else
        return M.BOGUS
    end
end


----- D.12 The Balinese Pawukon Calendar -----

-- TYPE (boolean 1-2 1-3 1-4 1-5 1-6 1-7 1-8 1-9 0-9)
-- TYPE -> balinese-date
function M.balinese_date(b1, b2, b3, b4, b5, b6, b7, b8, b9, b0)
    return _list(b1, b2, b3, b4, b5, b6, b7, b8, b9, b0)
end

-- TYPE balinese-date -> boolean
function M.bali_luang(b_date)
    return _nth(0, b_date)
end

-- TYPE balinese-date -> 1-2
function M.bali_dwiwara(b_date)
    return _nth(1, b_date)
end

-- TYPE balinese-date -> 1-3
function M.bali_triwara(b_date)
    return _nth(2, b_date)
end

-- TYPE balinese-date -> 1-4
function M.bali_caturwara(b_date)
    return _nth(3, b_date)
end

-- TYPE balinese-date -> 1-5
function M.bali_pancawara(b_date)
    return _nth(4, b_date)
end

-- TYPE balinese-date -> 1-6
function M.bali_sadwara(b_date)
    return _nth(5, b_date)
end

-- TYPE balinese-date -> 1-7
function M.bali_saptawara(b_date)
    return _nth(6, b_date)
end

-- TYPE balinese-date -> 1-8
function M.bali_asatawara(b_date)
    return _nth(7, b_date)
end

-- TYPE balinese-date -> 1-9
function M.bali_sangawara(b_date)
    return _nth(8, b_date)
end

-- TYPE balinese-date -> 0-9
function M.bali_dasawara(b_date)
    return _nth(9, b_date)
end

-- TYPE fixed-date -> balinese-date
-- Positions of date in ten cycles of Balinese Pawukon
-- calendar.
function M.bali_pawukon_from_fixed(date)
    return M.balinese_date(M.bali_luang_from_fixed(date), M.bali_dwiwara_from_fixed(date), M.bali_triwara_from_fixed(date), M.bali_caturwara_from_fixed(date), M.bali_pancawara_from_fixed(date), M.bali_sadwara_from_fixed(date), M.bali_saptawara_from_fixed(date), M.bali_asatawara_from_fixed(date), M.bali_sangawara_from_fixed(date), M.bali_dasawara_from_fixed(date))
end

-- TYPE fixed-date
-- Fixed date of start of a Balinese Pawukon cycle.
M.BALI_EPOCH = M.fixed_from_jd(146)

-- TYPE fixed-date -> 0-209
-- Position of date in 210-day Pawukon cycle.
function M.bali_day_from_fixed(date)
    return ((date - M.BALI_EPOCH) % 210)
end

-- TYPE fixed-date -> 1-3
-- Position of date in 3-day Balinese cycle.
function M.bali_triwara_from_fixed(date)
    return ((M.bali_day_from_fixed(date) % 3) + 1)
end

-- TYPE fixed-date -> 1-6
-- Position of date in 6-day Balinese cycle.
function M.bali_sadwara_from_fixed(date)
    return ((M.bali_day_from_fixed(date) % 6) + 1)
end

-- TYPE fixed-date -> 1-7
-- Position of date in Balinese week.
function M.bali_saptawara_from_fixed(date)
    return ((M.bali_day_from_fixed(date) % 7) + 1)
end

-- TYPE fixed-date -> 1-5
-- Position of date in 5-day Balinese cycle.
function M.bali_pancawara_from_fixed(date)
    return M.amod((M.bali_day_from_fixed(date) + 2), 5)
end

-- TYPE fixed-date -> 1-30
-- Week number of date in Balinese cycle.
function M.bali_week_from_fixed(date)
    return (M.quotient(M.bali_day_from_fixed(date), 7) + 1)
end

-- TYPE fixed-date -> 0-9
-- Position of date in 10-day Balinese cycle.
function M.bali_dasawara_from_fixed(date)
    local i = (M.bali_pancawara_from_fixed(date) - 1)
    local j = (M.bali_saptawara_from_fixed(date) - 1)
    return ((1 + _nth(i, _list(5, 9, 7, 4, 8)) + _nth(j, _list(5, 4, 3, 7, 8, 6, 9))) % 10)
end

-- TYPE fixed-date -> 1-2
-- Position of date in 2-day Balinese cycle.
function M.bali_dwiwara_from_fixed(date)
    return M.amod(M.bali_dasawara_from_fixed(date), 2)
end

-- TYPE fixed-date -> boolean
-- Membership of date in "1-day" Balinese cycle.
function M.bali_luang_from_fixed(date)
    return ((M.bali_dasawara_from_fixed(date)) % 2 == 0)
end

-- TYPE fixed-date -> 1-9
-- Position of date in 9-day Balinese cycle.
function M.bali_sangawara_from_fixed(date)
    return ((math.max(0, (M.bali_day_from_fixed(date) - 3)) % 9) + 1)
end

-- TYPE fixed-date -> 1-8
-- Position of date in 8-day Balinese cycle.
function M.bali_asatawara_from_fixed(date)
    local day = M.bali_day_from_fixed(date)
    return ((math.max(6, (4 + ((day - 70) % 210))) % 8) + 1)
end

-- TYPE fixed-date -> 1-4
-- Position of date in 4-day Balinese cycle.
function M.bali_caturwara_from_fixed(date)
    return M.amod(M.bali_asatawara_from_fixed(date), 4)
end

-- TYPE (balinese-date fixed-date) -> fixed-date
-- Last fixed date on or before date with Pawukon b-date.
function M.bali_on_or_before(b_date, date)
    local luang = M.bali_luang(b_date)
    local dwiwara = M.bali_dwiwara(b_date)
    local triwara = M.bali_triwara(b_date)
    local caturwara = M.bali_caturwara(b_date)
    local pancawara = M.bali_pancawara(b_date)
    local sadwara = M.bali_sadwara(b_date)
    local saptawara = M.bali_saptawara(b_date)
    local asatawara = M.bali_asatawara(b_date)
    local sangawara = M.bali_sangawara(b_date)
    local dasawara = M.bali_dasawara(b_date)
    local a5 = (pancawara - 1)
    local a6 = (sadwara - 1)
    local b7 = (saptawara - 1)
    local b35 = ((a5 + 14 + (15 * (b7 - a5))) % 35)
    local days = (a6 + (36 * (b35 - a6)))
    local cap_Delta = M.bali_day_from_fixed(M.rd(0))
    return (date - (((date + cap_Delta) - days) % 210))
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- Occurrences of Kajeng Keliwon (9th day of each
-- 15-day subcycle of Pawukon) in Gregorian year g-year.
function M.kajeng_keliwon(g_year)
    local year = M.gregorian_year_range(g_year)
    local cap_Delta = M.bali_day_from_fixed(M.rd(0))
    return M.positions_in_range(8, 15, cap_Delta, year)
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- Occurrences of Tumpek (14th day of Pawukon and every
-- 35th subsequent day) within Gregorian year g-year.
function M.tumpek(g_year)
    local year = M.gregorian_year_range(g_year)
    local cap_Delta = M.bali_day_from_fixed(M.rd(0))
    return M.positions_in_range(13, 35, cap_Delta, year)
end


----- D.14 Time and Astronomy -----

-- TYPE real -> radian
-- Convert angle theta from degrees to radians.
function M.radians_from_degrees(theta)
    return ((theta % 360) * math.pi * (1 / 180))
end

-- TYPE radian -> angle
-- Convert angle theta from radians to degrees.
function M.degrees_from_radians(theta)
    return ((theta / math.pi / (1 / 180)) % 360)
end

-- TYPE angle -> amplitude
-- Sine of theta (given in degrees).
function M.sin_degrees(theta)
    return math.sin(M.radians_from_degrees(theta))
end

-- TYPE angle -> amplitude
-- Cosine of theta (given in degrees).
function M.cos_degrees(theta)
    return math.cos(M.radians_from_degrees(theta))
end

-- TYPE angle -> real
-- Tangent of theta (given in degrees).
function M.tan_degrees(theta)
    return math.tan(M.radians_from_degrees(theta))
end

-- TYPE (real real) -> angle
-- Arctangent of y/x in degrees.
-- Returns bogus if x and y are both 0.
function M.arctan_degrees(y, x)
    if _truth((_truth(((x == y) and (y == 0))))) then
        return M.BOGUS
    else
        return ((function() if _truth(((x == 0))) then return (M.sign(y) * M.deg(90)) else return (function() local alpha = M.degrees_from_radians(math.atan((y / x))); return (function() if _truth(((x >= 0))) then return alpha else return (alpha + M.deg(180)) end end)() end)() end end)() % 360)
    end
end

-- TYPE amplitude -> angle
-- Arcsine of x in degrees.
function M.arcsin_degrees(x)
    return M.degrees_from_radians(math.asin(x))
end

-- TYPE amplitude -> angle
-- Arccosine of x in degrees.
function M.arccos_degrees(x)
    return M.degrees_from_radians(math.acos(x))
end

-- TYPE real -> duration
-- x hours.
function M.hr(x)
    return (x / 24)
end

-- TYPE real -> duration
-- x minutes.
function M.mn(x)
    return (x / 24 / 60)
end

-- TYPE real -> duration
-- x seconds.
function M.sec(x)
    return (x / 24 / 60 / 60)
end

-- TYPE real -> distance
-- x meters.
-- For typesetting purposes.
function M.mt(x)
    return x
end

-- TYPE real -> angle
-- x arcminutes
function M.mins(x)
    return (x / 60)
end

-- TYPE real -> angle
-- x arcseconds
function M.secs(x)
    return (x / 3600)
end

-- TYPE (integer integer real) -> angle
-- d degrees, m arcminutes, s arcseconds.
function M.angle(d, m, s)
    return (d + ((m + (s / 60)) / 60))
end

-- TYPE (degree minute real) -> angle
function M.degrees_minutes_seconds(d, m, s)
    return _list(d, m, s)
end

-- TYPE (half-circle circle distance real) -> location
function M.location(latitude, longitude, elevation, zone)
    return _list(latitude, longitude, elevation, zone)
end

-- TYPE location -> half-circle
function M.latitude(location)
    return _nth(0, location)
end

-- TYPE location -> circle
function M.longitude(location)
    return _nth(1, location)
end

-- TYPE location -> distance
function M.elevation(location)
    return _nth(2, location)
end

-- TYPE location -> real
function M.zone(location)
    return _nth(3, location)
end

-- TYPE location
-- Location of Mecca.
M.MECCA = M.location(M.angle(21, 25, 24), M.angle(39, 49, 24), M.mt(298), M.hr(3))

-- TYPE location
-- Location of Jerusalem.
M.JERUSALEM = M.location(M.deg(31.78), M.deg(35.24), M.mt(740), M.hr(2))

-- TYPE location
-- Location of Acre.
M.ACRE = M.location(M.deg(32.94), M.deg(35.09), M.mt(22), M.hr(2))

-- TYPE (location location) -> angle
-- Angle (clockwise from North) to face focus when
-- standing in location. Subject to errors near focus and
-- its antipode.
function M.direction(location, focus)
    local phi = M.latitude(location)
    local phi_prime = M.latitude(focus)
    local psi = M.longitude(location)
    local psi_prime = M.longitude(focus)
    local y = M.sin_degrees((psi_prime - psi))
    local x = ((M.cos_degrees(phi) * M.tan_degrees(phi_prime)) - (M.sin_degrees(phi) * M.cos_degrees((psi - psi_prime))))
    if _truth((_truth(((x == y) and (y == 0))) or _truth(((phi_prime == M.deg(90)))))) then
        return M.deg(0)
    elseif _truth(((phi_prime == M.deg(-90)))) then
        return M.deg(180)
    else
        return M.arctan_degrees(y, x)
    end
end

-- TYPE circle -> duration
-- Difference between UT and local mean time at longitude
-- phi as a fraction of a day.
function M.zone_from_longitude(phi)
    return (phi / M.deg(360))
end

-- TYPE (moment location) -> moment
-- Universal time from local tee_ell at location.
function M.universal_from_local(tee_ell, location)
    return (tee_ell - M.zone_from_longitude(M.longitude(location)))
end

-- TYPE (moment location) -> moment
-- Local time from universal tee_rom-u at location.
function M.local_from_universal(tee_rom_u, location)
    return (tee_rom_u + M.zone_from_longitude(M.longitude(location)))
end

-- TYPE (moment location) -> moment
-- Standard time from tee_rom-u in universal time at
-- location.
function M.standard_from_universal(tee_rom_u, location)
    return (tee_rom_u + M.zone(location))
end

-- TYPE (moment location) -> moment
-- Universal time from tee_rom-s in standard time at
-- location.
function M.universal_from_standard(tee_rom_s, location)
    return (tee_rom_s - M.zone(location))
end

-- TYPE (moment location) -> moment
-- Standard time from local tee_ell at location.
function M.standard_from_local(tee_ell, location)
    return M.standard_from_universal(M.universal_from_local(tee_ell, location), location)
end

-- TYPE (moment location) -> moment
-- Local time from standard tee_rom-s at location.
function M.local_from_standard(tee_rom_s, location)
    return M.local_from_universal(M.universal_from_standard(tee_rom_s, location), location)
end

-- TYPE moment -> fraction-of-day
-- Dynamical Time minus Universal Time (in days) for
-- moment tee. Adapted from "Astronomical Algorithms"
-- by Jean Meeus, Willmann-Bell (1991) for years
-- 1600-1986 and from polynomials on the NASA
-- Eclipse web site for other years.
function M.ephemeris_correction(tee)
    local year = M.gregorian_year_from_fixed(math.floor(tee))
    local c = (M.gregorian_date_difference(M.gregorian_date(1900, M.JANUARY, 1), M.gregorian_date(year, M.JULY, 1)) / 36525)
    local c2051 = ((1 / 86400) * (-20 + (32 * (((year - 1820) / 100) ^ 2)) - (0.5628 * (2150 - year))))
    local y2000 = (year - 2000)
    local c2006 = ((1 / 86400) * M.poly(y2000, _list(62.92, 0.32217, 0.005589)))
    local c1987 = ((1 / 86400) * M.poly(y2000, _list(63.86, 0.3345, -0.060374, 0.0017275, 0.000651814, 0.00002373599)))
    local c1900 = M.poly(c, _list(-0.00002, 0.000297, 0.025184, -0.181133, 0.553040, -0.861938, 0.677066, -0.212591))
    local c1800 = M.poly(c, _list(-0.000009, 0.003844, 0.083563, 0.865736, 4.867575, 15.845535, 31.332267, 38.291999, 28.316289, 11.636204, 2.043794))
    local y1700 = (year - 1700)
    local c1700 = ((1 / 86400) * M.poly(y1700, _list(8.118780842, -0.005092142, 0.003336121, -0.0000266484)))
    local y1600 = (year - 1600)
    local c1600 = ((1 / 86400) * M.poly(y1600, _list(120, -0.9808, -0.01532, 0.000140272128)))
    local y1000 = ((year - 1000) / 100)
    local c500 = ((1 / 86400) * M.poly(y1000, _list(1574.2, -556.01, 71.23472, 0.319781, -0.8503463, -0.005050998, 0.0083572073)))
    local y0 = (year / 100)
    local c0 = ((1 / 86400) * M.poly(y0, _list(10583.6, -1014.41, 33.78311, -5.952053, -0.1798452, 0.022174192, 0.0090316521)))
    local y1820 = ((year - 1820) / 100)
    local other = ((1 / 86400) * M.poly(y1820, _list(-20, 0, 32)))
    if _truth(((2051 <= year) and (year <= 2150))) then
        return c2051
    elseif _truth(((2006 <= year) and (year <= 2050))) then
        return c2006
    elseif _truth(((1987 <= year) and (year <= 2005))) then
        return c1987
    elseif _truth(((1900 <= year) and (year <= 1986))) then
        return c1900
    elseif _truth(((1800 <= year) and (year <= 1899))) then
        return c1800
    elseif _truth(((1700 <= year) and (year <= 1799))) then
        return c1700
    elseif _truth(((1600 <= year) and (year <= 1699))) then
        return c1600
    elseif _truth(((500 <= year) and (year <= 1599))) then
        return c500
    elseif _truth(((-500 < year) and (year < 500))) then
        return c0
    else
        return other
    end
end

-- TYPE moment -> moment
-- Dynamical time at Universal moment tee_rom-u.
function M.dynamical_from_universal(tee_rom_u)
    return (tee_rom_u + M.ephemeris_correction(tee_rom_u))
end

-- TYPE moment -> moment
-- Universal moment from Dynamical time tee.
function M.universal_from_dynamical(tee)
    return (tee - M.ephemeris_correction(tee))
end

-- TYPE moment -> century
-- Julian centuries since 2000 at moment tee.
function M.julian_centuries(tee)
    return ((M.dynamical_from_universal(tee) - M.J2000) / 36525)
end

-- TYPE moment
-- Noon at start of Gregorian year 2000.
M.J2000 = (M.hr(12) + M.gregorian_new_year(2000))

-- TYPE moment -> fraction-of-day
-- Equation of time (as fraction of day) for moment tee.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998, p. 185.
function M.equation_of_time(tee)
    local c = M.julian_centuries(tee)
    local lambda = M.poly(c, M.deg(_list(280.46645, 36000.76983, 0.0003032)))
    local anomaly = M.poly(c, M.deg(_list(357.52910, 35999.05030, -0.0001559, -0.00000048)))
    local eccentricity = M.poly(c, _list(0.016708617, -0.000042037, -0.0000001236))
    local varepsilon = M.obliquity(tee)
    local y = (M.tan_degrees((varepsilon / 2)) ^ 2)
    local equation = ((1 / 2 / math.pi) * ((y * M.sin_degrees((2 * lambda))) + (-2 * eccentricity * M.sin_degrees(anomaly)) + (4 * eccentricity * y * M.sin_degrees(anomaly) * M.cos_degrees((2 * lambda))) + (-0.5 * y * y * M.sin_degrees((4 * lambda))) + (-1.25 * eccentricity * eccentricity * M.sin_degrees((2 * anomaly)))))
    return (M.sign(equation) * math.min(math.abs(equation), M.hr(12)))
end

-- TYPE (moment location) -> moment
-- Sundial time from local time tee_ell at location.
function M.apparent_from_local(tee_ell, location)
    return (tee_ell + M.equation_of_time(M.universal_from_local(tee_ell, location)))
end

-- TYPE (moment location) -> moment
-- Local time from sundial time tee at location.
function M.local_from_apparent(tee, location)
    return (tee - M.equation_of_time(M.universal_from_local(tee, location)))
end

-- TYPE (moment location) -> moment
-- True (apparent) time at universal time tee at location.
function M.apparent_from_universal(tee_rom_u, location)
    return M.apparent_from_local(M.local_from_universal(tee_rom_u, location), location)
end

-- TYPE (moment location) -> moment
-- Universal time from sundial time tee at location.
function M.universal_from_apparent(tee, location)
    return M.universal_from_local(M.local_from_apparent(tee, location), location)
end

-- TYPE (fixed-date location) -> moment
-- Universal time of true (apparent)
-- midnight of fixed date at location.
function M.midnight(date, location)
    return M.universal_from_apparent(date, location)
end

-- TYPE (fixed-date location) -> moment
-- Universal time on fixed date of midday at location.
function M.midday(date, location)
    return M.universal_from_apparent((date + M.hr(12)), location)
end

-- TYPE moment -> angle
-- Mean sidereal time of day from moment tee expressed
-- as hour angle. Adapted from "Astronomical Algorithms"
-- by Jean Meeus, Willmann-Bell, Inc., 2nd edn., 1998, p. 88.
function M.sidereal_from_moment(tee)
    local c = ((tee - M.J2000) / 36525)
    return (M.poly(c, M.deg(_list(280.46061837, (36525 * 360.98564736629), 0.000387933, (-1 / 38710000)))) % 360)
end

-- TYPE moment -> angle
-- Obliquity of ecliptic at moment tee.
function M.obliquity(tee)
    local c = M.julian_centuries(tee)
    return (M.angle(23, 26, 21.448) + M.poly(c, _list(0, M.angle(0, 0, -46.8150), M.angle(0, 0, -0.00059), M.angle(0, 0, 0.001813))))
end

-- TYPE (moment half-circle circle) -> angle
-- Declination at moment UT tee of object at
-- latitude beta and longitude lambda.
function M.declination(tee, beta, lambda)
    local varepsilon = M.obliquity(tee)
    return M.arcsin_degrees(((M.sin_degrees(beta) * M.cos_degrees(varepsilon)) + (M.cos_degrees(beta) * M.sin_degrees(varepsilon) * M.sin_degrees(lambda))))
end

-- TYPE (moment half-circle circle) -> angle
-- Right ascension at moment UT tee of object at
-- latitude beta and longitude lambda.
function M.right_ascension(tee, beta, lambda)
    local varepsilon = M.obliquity(tee)
    return M.arctan_degrees(((M.sin_degrees(lambda) * M.cos_degrees(varepsilon)) - (M.tan_degrees(beta) * M.sin_degrees(varepsilon))), M.cos_degrees(lambda))
end

-- TYPE duration
M.MEAN_TROPICAL_YEAR = 365.242189

-- TYPE duration
M.MEAN_SIDEREAL_YEAR = 365.25636

-- TYPE moment -> season
-- Longitude of sun at moment tee.
-- Adapted from "Planetary Programs and Tables from -4000
-- to +2800" by Pierre Bretagnon and Jean-Louis Simon,
-- Willmann-Bell, 1986.
function M.solar_longitude(tee)
    local c = M.julian_centuries(tee)
    local coefficients = _list(403406, 195207, 119433, 112392, 3891, 2819, 1721, 660, 350, 334, 314, 268, 242, 234, 158, 132, 129, 114, 99, 93, 86, 78, 72, 68, 64, 46, 38, 37, 32, 29, 28, 27, 27, 25, 24, 21, 21, 20, 18, 17, 14, 13, 13, 13, 12, 10, 10, 10, 10)
    local multipliers = _list(0.9287892, 35999.1376958, 35999.4089666, 35998.7287385, 71998.20261, 71998.4403, 36000.35726, 71997.4812, 32964.4678, -19.4410, 445267.1117, 45036.8840, 3.1008, 22518.4434, -19.9739, 65928.9345, 9038.0293, 3034.7684, 33718.148, 3034.448, -2280.773, 29929.992, 31556.493, 149.588, 9037.750, 107997.405, -4444.176, 151.771, 67555.316, 31556.080, -4561.540, 107996.706, 1221.655, 62894.167, 31437.369, 14578.298, -31931.757, 34777.243, 1221.999, 62894.511, -4442.039, 107997.909, 119.066, 16859.071, -4.578, 26895.292, -39.127, 12297.536, 90073.778)
    local addends = _list(270.54861, 340.19128, 63.91854, 331.26220, 317.843, 86.631, 240.052, 310.26, 247.23, 260.87, 297.82, 343.14, 166.79, 81.53, 3.50, 132.75, 182.95, 162.03, 29.8, 266.4, 249.2, 157.6, 257.8, 185.1, 69.9, 8.0, 197.1, 250.4, 65.3, 162.7, 341.5, 291.6, 98.5, 146.7, 110.0, 5.2, 342.6, 230.9, 256.1, 45.3, 242.9, 115.2, 151.8, 285.3, 53.3, 126.6, 205.7, 85.9, 146.1)
    local lambda = (M.deg(282.7771834) + (M.deg(36000.76953744) * c) + (M.deg(0.000005729577951308232) * M.sigma(_list(coefficients, addends, multipliers), function(x, y, z) return (x * M.sin_degrees((y + (z * c)))) end)))
    return ((lambda + M.aberration(tee) + M.nutation(tee)) % 360)
end

-- TYPE moment -> circle
-- Longitudinal nutation at moment tee.
function M.nutation(tee)
    local c = M.julian_centuries(tee)
    local cap_A = M.poly(c, M.deg(_list(124.90, -1934.134, 0.002063)))
    local cap_B = M.poly(c, M.deg(_list(201.11, 72001.5377, 0.00057)))
    return ((M.deg(-0.004778) * M.sin_degrees(cap_A)) + (M.deg(-0.0003667) * M.sin_degrees(cap_B)))
end

-- TYPE moment -> circle
-- Aberration at moment tee.
function M.aberration(tee)
    local c = M.julian_centuries(tee)
    return ((M.deg(0.0000974) * M.cos_degrees((M.deg(177.63) + (M.deg(35999.01848) * c)))) - M.deg(0.005575))
end

-- TYPE (season moment) -> moment
-- Moment UT of the first time at or after tee
-- when the solar longitude will be lambda degrees.
function M.solar_longitude_after(lambda, tee)
    local rate = (M.MEAN_TROPICAL_YEAR / M.deg(360))
    local tau = (tee + (rate * ((lambda - M.solar_longitude(tee)) % 360)))
    local a = math.max(tee, (tau - 5))
    local b = (tau + 5)
    return M.invert_angular(M.solar_longitude, lambda, M.interval_closed(a, b))
end

-- TYPE (season gregorian-year) -> moment
-- Moment UT of season in Gregorian year g-year.
function M.season_in_gregorian(season, g_year)
    local jan1 = M.gregorian_new_year(g_year)
    return M.solar_longitude_after(season, jan1)
end

-- TYPE moment -> angle
-- Precession at moment tee using 0,0 as J2000 coordinates.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998, pp. 136-137.
function M.precession(tee)
    local c = M.julian_centuries(tee)
    local eta = (M.poly(c, _list(0, M.secs(47.0029), M.secs(-0.03302), M.secs(0.000060))) % 360)
    local cap_P = (M.poly(c, _list(M.deg(174.876384), M.secs(-869.8089), M.secs(0.03536))) % 360)
    local p = (M.poly(c, _list(0, M.secs(5029.0966), M.secs(1.11113), M.secs(0.000006))) % 360)
    local cap_A = (M.cos_degrees(eta) * M.sin_degrees(cap_P))
    local cap_B = M.cos_degrees(cap_P)
    local arg = M.arctan_degrees(cap_A, cap_B)
    return (((p + cap_P) - arg) % 360)
end

-- TYPE moment -> angle
-- Sidereal solar longitude at moment tee
function M.sidereal_solar_longitude(tee)
    return ((M.solar_longitude(tee) + (-M.precession(tee)) + M.SIDEREAL_START) % 360)
end

-- TYPE (moment location) -> half-circle
-- Geocentric altitude of sun at tee at location,
-- as a positive/negative angle in degrees, ignoring
-- parallax and refraction.
function M.solar_altitude(tee, location)
    local phi = M.latitude(location)
    local psi = M.longitude(location)
    local lambda = M.solar_longitude(tee)
    local alpha = M.right_ascension(tee, 0, lambda)
    local delta = M.declination(tee, 0, lambda)
    local theta0 = M.sidereal_from_moment(tee)
    local cap_H = ((theta0 - (-psi) - alpha) % 360)
    local altitude = M.arcsin_degrees(((M.sin_degrees(phi) * M.sin_degrees(delta)) + (M.cos_degrees(phi) * M.cos_degrees(delta) * M.cos_degrees(cap_H))))
    return M.mod3(altitude, -180, 180)
end

-- TYPE (season moment) -> moment
-- Approximate moment at or before tee
-- when solar longitude just exceeded lambda degrees.
function M.estimate_prior_solar_longitude(lambda, tee)
    local rate = (M.MEAN_TROPICAL_YEAR / M.deg(360))
    local tau = (tee - (rate * ((M.solar_longitude(tee) - lambda) % 360)))
    local cap_Delta = M.mod3((M.solar_longitude(tau) - lambda), -180, 180)
    return math.min(tee, (tau - (rate * cap_Delta)))
end

-- TYPE duration
M.MEAN_SYNODIC_MONTH = 29.530588861

-- TYPE integer -> moment
-- Moment of n-th new moon after (or before) the new moon
-- of January 11, 1. Adapted from "Astronomical Algorithms"
-- by Jean Meeus, Willmann-Bell, corrected 2nd edn., 2005.
function M.nth_new_moon(n)
    local n0 = 24724
    local k = (n - n0)
    local c = (k / 1236.85)
    local approx = (M.J2000 + M.poly(c, _list(5.09766, (M.MEAN_SYNODIC_MONTH * 1236.85), 0.00015437, -0.000000150, 0.00000000073)))
    local cap_E = M.poly(c, _list(1, -0.002516, -0.0000074))
    local solar_anomaly = M.poly(c, M.deg(_list(2.5534, (1236.85 * 29.10535670), -0.0000014, -0.00000011)))
    local lunar_anomaly = M.poly(c, M.deg(_list(201.5643, (385.81693528 * 1236.85), 0.0107582, 0.00001238, -0.000000058)))
    local moon_argument = M.poly(c, M.deg(_list(160.7108, (390.67050284 * 1236.85), -0.0016118, -0.00000227, 0.000000011)))
    local cap_omega = M.poly(c, M.deg(_list(124.7746, (-1.56375588 * 1236.85), 0.0020672, 0.00000215)))
    local E_factor = _list(0, 1, 0, 0, 1, 1, 2, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
    local solar_coeff = _list(0, 1, 0, 0, -1, 1, 2, 0, 0, 1, 0, 1, 1, -1, 2, 0, 3, 1, 0, 1, -1, -1, 1, 0)
    local lunar_coeff = _list(1, 0, 2, 0, 1, 1, 0, 1, 1, 2, 3, 0, 0, 2, 1, 2, 0, 1, 2, 1, 1, 1, 3, 4)
    local moon_coeff = _list(0, 0, 0, 2, 0, 0, 0, -2, 2, 0, 0, 2, -2, 0, 0, -2, 0, -2, 2, 2, 2, -2, 0, 0)
    local sine_coeff = _list(-0.40720, 0.17241, 0.01608, 0.01039, 0.00739, -0.00514, 0.00208, -0.00111, -0.00057, 0.00056, -0.00042, 0.00042, 0.00038, -0.00024, -0.00007, 0.00004, 0.00004, 0.00003, 0.00003, -0.00003, 0.00003, -0.00002, -0.00002, 0.00002)
    local correction = ((-0.00017 * M.sin_degrees(cap_omega)) + M.sigma(_list(sine_coeff, E_factor, solar_coeff, lunar_coeff, moon_coeff), function(v, w, x, y, z) return (v * (cap_E ^ w) * M.sin_degrees(((x * solar_anomaly) + (y * lunar_anomaly) + (z * moon_argument)))) end))
    local add_const = _list(251.88, 251.83, 349.42, 84.66, 141.74, 207.14, 154.84, 34.52, 207.19, 291.34, 161.72, 239.56, 331.55)
    local add_coeff = _list(0.016321, 26.651886, 36.412478, 18.206239, 53.303771, 2.453732, 7.306860, 27.261239, 0.121824, 1.844379, 24.198154, 25.513099, 3.592518)
    local add_factor = _list(0.000165, 0.000164, 0.000126, 0.000110, 0.000062, 0.000060, 0.000056, 0.000047, 0.000042, 0.000040, 0.000037, 0.000035, 0.000023)
    local extra = (0.000325 * M.sin_degrees(M.poly(c, M.deg(_list(299.77, 132.8475848, -0.009173)))))
    local additional = M.sigma(_list(add_const, add_coeff, add_factor), function(i, j, l) return (l * M.sin_degrees((i + (j * k)))) end)
    return M.universal_from_dynamical((approx + correction + extra + additional))
end

-- TYPE moment -> moment
-- Moment UT of last new moon before tee.
function M.new_moon_before(tee)
    local t0 = M.nth_new_moon(0)
    local phi = M.lunar_phase(tee)
    local n = _round((((tee - t0) / M.MEAN_SYNODIC_MONTH) - (phi / M.deg(360))))
    return M.nth_new_moon(M.final((n - 1), function(k) return _truth(((M.nth_new_moon(k) < tee))) end))
end

-- TYPE moment -> moment
-- Moment UT of first new moon at or after tee.
function M.new_moon_at_or_after(tee)
    local t0 = M.nth_new_moon(0)
    local phi = M.lunar_phase(tee)
    local n = _round((((tee - t0) / M.MEAN_SYNODIC_MONTH) - (phi / M.deg(360))))
    return M.nth_new_moon(M.next(n, function(k) return _truth(((M.nth_new_moon(k) >= tee))) end))
end

-- TYPE moment -> angle
-- Longitude of moon (in degrees) at moment tee.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998, pp. 338-342.
function M.lunar_longitude(tee)
    local c = M.julian_centuries(tee)
    local cap_L_prime = M.mean_lunar_longitude(c)
    local cap_D = M.lunar_elongation(c)
    local cap_M = M.solar_anomaly(c)
    local cap_M_prime = M.lunar_anomaly(c)
    local cap_F = M.moon_node(c)
    local cap_E = M.poly(c, _list(1, -0.002516, -0.0000074))
    local args_lunar_elongation = _list(0, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 1, 0, 2, 0, 0, 4, 0, 4, 2, 2, 1, 1, 2, 2, 4, 2, 0, 2, 2, 1, 2, 0, 0, 2, 2, 2, 4, 0, 3, 2, 4, 0, 2, 2, 2, 4, 0, 4, 1, 2, 0, 1, 3, 4, 2, 0, 1, 2)
    local args_solar_anomaly = _list(0, 0, 0, 0, 1, 0, 0, -1, 0, -1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, -1, 0, 0, 0, 1, 0, -1, 0, -2, 1, 2, -2, 0, 0, -1, 0, 0, 1, -1, 2, 2, 1, -1, 0, 0, -1, 0, 1, 0, 1, 0, 0, -1, 2, 1, 0)
    local args_lunar_anomaly = _list(1, -1, 0, 2, 0, 0, -2, -1, 1, 0, -1, 0, 1, 0, 1, 1, -1, 3, -2, -1, 0, -1, 0, 1, 2, 0, -3, -2, -1, -2, 1, 0, 2, 0, -1, 1, 0, -1, 2, -1, 1, -2, -1, -1, -2, 0, 1, 4, 0, -2, 0, 2, 1, -2, -3, 2, 1, -1, 3)
    local args_moon_node = _list(0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, -2, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, -2, 2, 0, 2, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0)
    local sine_coeff = _list(6288774, 1274027, 658314, 213618, -185116, -114332, 58793, 57066, 53322, 45758, -40923, -34720, -30383, 15327, -12528, 10980, 10675, 10034, 8548, -7888, -6766, -5163, 4987, 4036, 3994, 3861, 3665, -2689, -2602, 2390, -2348, 2236, -2120, -2069, 2048, -1773, -1595, 1215, -1110, -892, -810, 759, -713, -700, 691, 596, 549, 537, 520, -487, -399, -381, 351, -340, 330, 327, -323, 299, 294)
    local correction = (M.deg((1 / 1000000)) * M.sigma(_list(sine_coeff, args_lunar_elongation, args_solar_anomaly, args_lunar_anomaly, args_moon_node), function(v, w, x, y, z) return (v * (cap_E ^ math.abs(x)) * M.sin_degrees(((w * cap_D) + (x * cap_M) + (y * cap_M_prime) + (z * cap_F)))) end))
    local venus = (M.deg((3958 / 1000000)) * M.sin_degrees((M.deg(119.75) + (c * M.deg(131.849)))))
    local jupiter = (M.deg((318 / 1000000)) * M.sin_degrees((M.deg(53.09) + (c * M.deg(479264.29)))))
    local flat_earth = (M.deg((1962 / 1000000)) * M.sin_degrees((cap_L_prime - cap_F)))
    return ((cap_L_prime + correction + venus + jupiter + flat_earth + M.nutation(tee)) % 360)
end

-- TYPE century -> angle
-- Mean longitude of moon (in degrees) at moment
-- given in Julian centuries c.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998, pp. 337-340.
function M.mean_lunar_longitude(c)
    return (M.poly(c, M.deg(_list(218.3164477, 481267.88123421, -0.0015786, (1 / 538841), (-1 / 65194000)))) % 360)
end

-- TYPE century -> angle
-- Elongation of moon (in degrees) at moment
-- given in Julian centuries c.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998, p. 338.
function M.lunar_elongation(c)
    return (M.poly(c, M.deg(_list(297.8501921, 445267.1114034, -0.0018819, (1 / 545868), (-1 / 113065000)))) % 360)
end

-- TYPE century -> angle
-- Mean anomaly of sun (in degrees) at moment
-- given in Julian centuries c.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998, p. 338.
function M.solar_anomaly(c)
    return (M.poly(c, M.deg(_list(357.5291092, 35999.0502909, -0.0001536, (1 / 24490000)))) % 360)
end

-- TYPE century -> angle
-- Mean anomaly of moon (in degrees) at moment
-- given in Julian centuries c.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998, p. 338.
function M.lunar_anomaly(c)
    return (M.poly(c, M.deg(_list(134.9633964, 477198.8675055, 0.0087414, (1 / 69699), (-1 / 14712000)))) % 360)
end

-- TYPE century -> angle
-- Moon’s argument of latitude (in degrees) at moment
-- given in Julian centuries c.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998, p. 338.
function M.moon_node(c)
    return (M.poly(c, M.deg(_list(93.2720950, 483202.0175233, -0.0036539, (-1 / 3526000), (1 / 863310000)))) % 360)
end

-- TYPE fixed-date -> angle
-- Angular distance of the lunar node from the equinoctial
-- point on fixed date.
function M.lunar_node(date)
    return M.mod3((M.moon_node(M.julian_centuries(date))), -90, 90)
end

-- TYPE moment -> angle
-- Sidereal lunar longitude at moment tee.
function M.sidereal_lunar_longitude(tee)
    return ((M.lunar_longitude(tee) + (-M.precession(tee)) + M.SIDEREAL_START) % 360)
end

-- TYPE moment -> phase
-- Lunar phase, as an angle in degrees, at moment tee.
-- An angle of 0 means a new moon, 90 degrees means the
-- first quarter, 180 means a full moon, and 270 degrees
-- means the last quarter.
function M.lunar_phase(tee)
    local phi = ((M.lunar_longitude(tee) - M.solar_longitude(tee)) % 360)
    local t0 = M.nth_new_moon(0)
    local n = _round(((tee - t0) / M.MEAN_SYNODIC_MONTH))
    local phi_prime = (M.deg(360) * (((tee - M.nth_new_moon(n)) / M.MEAN_SYNODIC_MONTH) % 1))
    if _truth(((math.abs((phi - phi_prime)) > M.deg(180)))) then
        return phi_prime
    else
        return phi
    end
end

-- TYPE (phase moment) -> moment
-- Moment UT of the last time at or before tee
-- when the lunar-phase was phi degrees.
function M.lunar_phase_at_or_before(phi, tee)
    local tau = (tee - (M.MEAN_SYNODIC_MONTH * (1 / M.deg(360)) * ((M.lunar_phase(tee) - phi) % 360)))
    local a = (tau - 2)
    local b = math.min(tee, (tau + 2))
    return M.invert_angular(M.lunar_phase, phi, M.interval_closed(a, b))
end

-- TYPE (phase moment) -> moment
-- Moment UT of the next time at or after tee
-- when the lunar-phase is phi degrees.
function M.lunar_phase_at_or_after(phi, tee)
    local tau = (tee + (M.MEAN_SYNODIC_MONTH * (1 / M.deg(360)) * ((phi - M.lunar_phase(tee)) % 360)))
    local a = math.max(tee, (tau - 2))
    local b = (tau + 2)
    return M.invert_angular(M.lunar_phase, phi, M.interval_closed(a, b))
end

-- TYPE phase
-- Excess of lunar longitude over solar longitude at new
-- moon.
M.NEW = M.deg(0)

-- TYPE phase
-- Excess of lunar longitude over solar longitude at full
-- moon.
M.FULL = M.deg(180)

-- TYPE phase
-- Excess of lunar longitude over solar longitude at first
-- quarter moon.
M.FIRST_QUARTER = M.deg(90)

-- TYPE phase
-- Excess of lunar longitude over solar longitude at last
-- quarter moon.
M.LAST_QUARTER = M.deg(270)

-- TYPE moment -> half-circle
-- Latitude of moon (in degrees) at moment tee.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998, pp. 338-342.
function M.lunar_latitude(tee)
    local c = M.julian_centuries(tee)
    local cap_L_prime = M.mean_lunar_longitude(c)
    local cap_D = M.lunar_elongation(c)
    local cap_M = M.solar_anomaly(c)
    local cap_M_prime = M.lunar_anomaly(c)
    local cap_F = M.moon_node(c)
    local cap_E = M.poly(c, _list(1, -0.002516, -0.0000074))
    local args_lunar_elongation = _list(0, 0, 0, 2, 2, 2, 2, 0, 2, 0, 2, 2, 2, 2, 2, 2, 2, 0, 4, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 4, 0, 4, 2, 2, 2, 2, 0, 2, 2, 2, 2, 4, 2, 2, 0, 2, 1, 1, 0, 2, 1, 2, 0, 4, 4, 1, 4, 1, 4, 2)
    local args_solar_anomaly = _list(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 1, -1, -1, -1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 1, 0, -1, -2, 0, 1, 1, 1, 1, 1, 0, -1, 1, 0, -1, 0, 0, 0, -1, -2)
    local args_lunar_anomaly = _list(0, 1, 1, 0, -1, -1, 0, 2, 1, 2, 0, -2, 1, 0, -1, 0, -1, -1, -1, 0, 0, -1, 0, 1, 1, 0, 0, 3, 0, -1, 1, -2, 0, 2, 1, -2, 3, 2, -3, -1, 0, 0, 1, 0, 1, 1, 0, 0, -2, -1, 1, -2, 2, -2, -1, 1, 1, -1, 0, 0)
    local args_moon_node = _list(1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 3, 1, 1, 1, -1, -1, -1, 1, -1, 1, -3, 1, -3, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 3, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1)
    local sine_coeff = _list(5128122, 280602, 277693, 173237, 55413, 46271, 32573, 17198, 9266, 8822, 8216, 4324, 4200, -3359, 2463, 2211, 2065, -1870, 1828, -1794, -1749, -1565, -1491, -1475, -1410, -1344, -1335, 1107, 1021, 833, 777, 671, 607, 596, 491, -451, 439, 422, 421, -366, -351, 331, 315, 302, -283, -229, 223, 223, -220, -220, -185, 181, -177, 176, 166, -164, 132, -119, 115, 107)
    local beta = (M.deg((1 / 1000000)) * M.sigma(_list(sine_coeff, args_lunar_elongation, args_solar_anomaly, args_lunar_anomaly, args_moon_node), function(v, w, x, y, z) return (v * (cap_E ^ math.abs(x)) * M.sin_degrees(((w * cap_D) + (x * cap_M) + (y * cap_M_prime) + (z * cap_F)))) end))
    local venus = (M.deg((175 / 1000000)) * (M.sin_degrees((M.deg(119.75) + (c * M.deg(131.849)) + cap_F)) + M.sin_degrees((M.deg(119.75) + (c * M.deg(131.849)) + (-cap_F)))))
    local flat_earth = ((M.deg((-2235 / 1000000)) * M.sin_degrees(cap_L_prime)) + (M.deg((127 / 1000000)) * M.sin_degrees((cap_L_prime - cap_M_prime))) + (M.deg((-115 / 1000000)) * M.sin_degrees((cap_L_prime + cap_M_prime))))
    local extra = (M.deg((382 / 1000000)) * M.sin_degrees((M.deg(313.45) + (c * M.deg(481266.484)))))
    return (beta + venus + flat_earth + extra)
end

-- TYPE (moment location) -> half-circle
-- Geocentric altitude of moon at tee at location,
-- as a small positive/negative angle in degrees, ignoring
-- parallax and refraction. Adapted from "Astronomical
-- Algorithms" by Jean Meeus, Willmann-Bell, 2nd edn.,
-- 1998.
function M.lunar_altitude(tee, location)
    local phi = M.latitude(location)
    local psi = M.longitude(location)
    local lambda = M.lunar_longitude(tee)
    local beta = M.lunar_latitude(tee)
    local alpha = M.right_ascension(tee, beta, lambda)
    local delta = M.declination(tee, beta, lambda)
    local theta0 = M.sidereal_from_moment(tee)
    local cap_H = ((theta0 - (-psi) - alpha) % 360)
    local altitude = M.arcsin_degrees(((M.sin_degrees(phi) * M.sin_degrees(delta)) + (M.cos_degrees(phi) * M.cos_degrees(delta) * M.cos_degrees(cap_H))))
    return M.mod3(altitude, -180, 180)
end

-- TYPE moment -> distance
-- Distance to moon (in meters) at moment tee.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998, pp. 338-342.
function M.lunar_distance(tee)
    local c = M.julian_centuries(tee)
    local cap_D = M.lunar_elongation(c)
    local cap_M = M.solar_anomaly(c)
    local cap_M_prime = M.lunar_anomaly(c)
    local cap_F = M.moon_node(c)
    local cap_E = M.poly(c, _list(1, -0.002516, -0.0000074))
    local args_lunar_elongation = _list(0, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 1, 0, 2, 0, 0, 4, 0, 4, 2, 2, 1, 1, 2, 2, 4, 2, 0, 2, 2, 1, 2, 0, 0, 2, 2, 2, 4, 0, 3, 2, 4, 0, 2, 2, 2, 4, 0, 4, 1, 2, 0, 1, 3, 4, 2, 0, 1, 2, 2)
    local args_solar_anomaly = _list(0, 0, 0, 0, 1, 0, 0, -1, 0, -1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, -1, 0, 0, 0, 1, 0, -1, 0, -2, 1, 2, -2, 0, 0, -1, 0, 0, 1, -1, 2, 2, 1, -1, 0, 0, -1, 0, 1, 0, 1, 0, 0, -1, 2, 1, 0, 0)
    local args_lunar_anomaly = _list(1, -1, 0, 2, 0, 0, -2, -1, 1, 0, -1, 0, 1, 0, 1, 1, -1, 3, -2, -1, 0, -1, 0, 1, 2, 0, -3, -2, -1, -2, 1, 0, 2, 0, -1, 1, 0, -1, 2, -1, 1, -2, -1, -1, -2, 0, 1, 4, 0, -2, 0, 2, 1, -2, -3, 2, 1, -1, 3, -1)
    local args_moon_node = _list(0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, -2, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, -2, 2, 0, 2, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, -2)
    local cosine_coeff = _list(-20905355, -3699111, -2955968, -569925, 48888, -3149, 246158, -152138, -170733, -204586, -129620, 108743, 104755, 10321, 0, 79661, -34782, -23210, -21636, 24208, 30824, -8379, -16675, -12831, -10445, -11650, 14403, -7003, 0, 10056, 6322, -9884, 5751, 0, -4950, 4130, 0, -3958, 0, 3258, 2616, -1897, -2117, 2354, 0, 0, -1423, -1117, -1571, -1739, 0, -4421, 0, 0, 0, 0, 1165, 0, 0, 8752)
    local correction = M.sigma(_list(cosine_coeff, args_lunar_elongation, args_solar_anomaly, args_lunar_anomaly, args_moon_node), function(v, w, x, y, z) return (v * (cap_E ^ math.abs(x)) * M.cos_degrees(((w * cap_D) + (x * cap_M) + (y * cap_M_prime) + (z * cap_F)))) end)
    return (M.mt(385000560) + correction)
end

-- TYPE (moment location) -> angle
-- Parallax of moon at tee at location.
-- Adapted from "Astronomical Algorithms" by Jean Meeus,
-- Willmann-Bell, 2nd edn., 1998.
function M.lunar_parallax(tee, location)
    local geo = M.lunar_altitude(tee, location)
    local cap_Delta = M.lunar_distance(tee)
    local alt = (M.mt(6378140) / cap_Delta)
    local arg = (alt * M.cos_degrees(geo))
    return M.arcsin_degrees(arg)
end

-- TYPE (moment location) -> half-circle
-- Topocentric altitude of moon at tee at location,
-- as a small positive/negative angle in degrees,
-- ignoring refraction.
function M.topocentric_lunar_altitude(tee, location)
    return (M.lunar_altitude(tee, location) - M.lunar_parallax(tee, location))
end

-- TYPE (moment location half-circle boolean) -> moment
-- Moment in local time near tee when depression angle
-- of sun is alpha (negative if above horizon) at
-- location; early? is true when morning event is sought
-- and false for evening. Returns bogus if depression
-- angle is not reached.
function M.approx_moment_of_depression(tee, location, alpha, early_p)
    local try = M.sine_offset(tee, location, alpha)
    local date = M.fixed_from_moment(tee)
    local alt = (function() if _truth(((alpha >= 0))) then return (function() if _truth(early_p) then return date else return (date + 1) end end)() else return (date + M.hr(12)) end end)()
    local value = (function() if _truth(((math.abs(try) > 1))) then return M.sine_offset(alt, location, alpha) else return try end end)()
    if _truth(((math.abs(value) <= 1))) then
        local offset = M.mod3((M.arcsin_degrees(value) / M.deg(360)), M.hr(-12), M.hr(12))
        return M.local_from_apparent((date + (function() if _truth(early_p) then return (M.hr(6) - offset) else return (M.hr(18) + offset) end end)()), location)
    else
        return M.BOGUS
    end
end

-- TYPE (moment location half-circle) -> real
-- Sine of angle between position of sun at
-- local time tee and
-- when its depression is alpha at location.
-- Out of range when it does not occur.
function M.sine_offset(tee, location, alpha)
    local phi = M.latitude(location)
    local tee_prime = M.universal_from_local(tee, location)
    local delta = M.declination(tee_prime, M.deg(0), M.solar_longitude(tee_prime))
    return ((M.tan_degrees(phi) * M.tan_degrees(delta)) + (M.sin_degrees(alpha) / (M.cos_degrees(delta) * M.cos_degrees(phi))))
end

-- TYPE (moment location half-circle boolean) -> moment
-- Moment in local time near approx when depression
-- angle of sun is alpha (negative if above horizon) at
-- location; early? is true when morning event is
-- sought, and false for evening.
-- Returns bogus if depression angle is not reached.
function M.moment_of_depression(approx, location, alpha, early_p)
    local tee = M.approx_moment_of_depression(approx, location, alpha, early_p)
    if _truth(_equal(tee, M.BOGUS)) then
        return M.BOGUS
    else
        if _truth(((math.abs((approx - tee)) < M.sec(30)))) then
            return tee
        else
            return M.moment_of_depression(tee, location, alpha, early_p)
        end
    end
end

-- TYPE boolean
-- Signifies morning.
M.MORNING = true

-- TYPE (fixed-date location half-circle) -> moment
-- Standard time in morning on fixed date at
-- location when depression angle of sun is alpha.
-- Returns bogus if there is no dawn on date.
function M.dawn(date, location, alpha)
    local result = M.moment_of_depression((date + M.hr(6)), location, alpha, M.MORNING)
    if _truth(_equal(result, M.BOGUS)) then
        return M.BOGUS
    else
        return M.standard_from_local(result, location)
    end
end

-- TYPE boolean
-- Signifies evening.
M.EVENING = false

-- TYPE (fixed-date location half-circle) -> moment
-- Standard time in evening on fixed date at
-- location when depression angle of sun is alpha.
-- Returns bogus if there is no dusk on date.
function M.dusk(date, location, alpha)
    local result = M.moment_of_depression((date + M.hr(18)), location, alpha, M.EVENING)
    if _truth(_equal(result, M.BOGUS)) then
        return M.BOGUS
    else
        return M.standard_from_local(result, location)
    end
end

-- TYPE (moment location) -> half-circle
-- Refraction angle at moment tee at location.
-- The moment is not used.
function M.refraction(tee, location)
    local h = math.max(M.mt(0), M.elevation(location))
    local cap_R = M.mt(6.372e6)
    local dip = M.arccos_degrees((cap_R / (cap_R + h)))
    return (M.mins(34) + dip + (M.secs(19) * math.sqrt(h)))
end

-- TYPE (fixed-date location) -> moment
-- Standard time of sunrise on fixed date at
-- location.
function M.sunrise(date, location)
    local alpha = (M.refraction((date + M.hr(6)), location) + M.mins(16))
    return M.dawn(date, location, alpha)
end

-- TYPE (fixed-date location) -> moment
-- Standard time of sunset on fixed date at
-- location.
function M.sunset(date, location)
    local alpha = (M.refraction((date + M.hr(18)), location) + M.mins(16))
    return M.dusk(date, location, alpha)
end

-- TYPE (fixed-date location) -> moment
-- Standard time of end of Jewish sabbath on fixed date
-- at location (as per Berthold Cohn).
function M.jewish_sabbath_ends(date, location)
    return M.dusk(date, location, M.angle(7, 5, 0))
end

-- TYPE (fixed-date location) -> moment
-- Standard time of Jewish dusk on fixed date
-- at location (as per Vilna Gaon).
function M.jewish_dusk(date, location)
    return M.dusk(date, location, M.angle(4, 40, 0))
end

-- TYPE (moment location) -> half-circle
-- Observed altitude of upper limb of moon at tee at location
-- as a small positive/negative angle in degrees, including
-- refraction and elevation.
function M.observed_lunar_altitude(tee, location)
    return (M.topocentric_lunar_altitude(tee, location) + M.refraction(tee, location) + M.mins(16))
end

-- TYPE (fixed-date location) -> moment
-- Standard time of moonrise on fixed date at location.
-- Returns bogus if there is no moonrise on date.
function M.moonrise(date, location)
    local tee = M.universal_from_standard(date, location)
    local waning = ((M.lunar_phase(tee) > M.deg(180)))
    local alt = M.observed_lunar_altitude(tee, location)
    local lat = M.latitude(location)
    local offset = (alt / (4 * (M.deg(90) - math.abs(lat))))
    local approx = (function() if _truth(waning) then return (function() if _truth(((offset > 0))) then return (tee - -1 - offset) else return (tee - offset) end end)() else return (tee + (1 / 2) + offset) end end)()
    local rise = M.binary_search((approx - M.hr(6)), (approx + M.hr(6)), function(x) local l, h = 0, 0; return _truth(((M.observed_lunar_altitude(x, location) > M.deg(0)))) end, function(l, h) local x = (l + h) / 2; return _truth((((h - l) < M.mn(1)))) end)
    if _truth(((rise < (tee + 1)))) then
        return math.max(M.standard_from_universal(rise, location), date)
    else
        return M.BOGUS
    end
end

-- TYPE (fixed-date location) -> moment
-- Standard time of moonset on fixed date at location.
-- Returns bogus if there is no moonset on date.
function M.moonset(date, location)
    local tee = M.universal_from_standard(date, location)
    local waxing = ((M.lunar_phase(tee) < M.deg(180)))
    local alt = M.observed_lunar_altitude(tee, location)
    local lat = M.latitude(location)
    local offset = (alt / (4 * (M.deg(90) - math.abs(lat))))
    local approx = (function() if _truth(waxing) then return (function() if _truth(((offset > 0))) then return (tee + offset) else return (tee + 1 + offset) end end)() else return (tee - offset - (-1 / 2)) end end)()
    local set = M.binary_search((approx - M.hr(6)), (approx + M.hr(6)), function(x) local l, h = 0, 0; return _truth(((M.observed_lunar_altitude(x, location) < M.deg(0)))) end, function(l, h) local x = (l + h) / 2; return _truth((((h - l) < M.mn(1)))) end)
    if _truth(((set < (tee + 1)))) then
        return math.max(M.standard_from_universal(set, location), date)
    else
        return M.BOGUS
    end
end

-- TYPE location
-- Location of Padua, Italy.
M.PADUA = M.location(M.angle(45, 24, 28), M.angle(11, 53, 9), M.mt(18), M.hr(1))

-- TYPE moment -> moment
-- Local time of dusk in Padua, Italy on date of moment tee.
function M.local_zero_hour(tee)
    local date = M.fixed_from_moment(tee)
    return M.local_from_standard((M.dusk(date, M.PADUA, M.angle(0, 16, 0)) + M.mn(30)), M.PADUA)
end

-- TYPE moment -> moment
-- Local time corresponding to Italian time tee.
function M.local_from_italian(tee)
    local date = M.fixed_from_moment(tee)
    local z = M.local_zero_hour((tee - 1))
    return (tee - (date - z))
end

-- TYPE moment -> moment
-- Italian time corresponding to local time tee_ell.
function M.italian_from_local(tee_ell)
    local date = M.fixed_from_moment(tee_ell)
    local z0 = M.local_zero_hour((tee_ell - 1))
    local z = M.local_zero_hour(tee_ell)
    if _truth(((tee_ell > z))) then
        return (tee_ell + (date - -1 - z))
    else
        return (tee_ell + (date - z0))
    end
end

-- TYPE (fixed-date location) -> real
-- Length of daytime temporal hour on fixed date at location
-- Returns bogus if there no sunrise or sunset on date.
function M.daytime_temporal_hour(date, location)
    if _truth((_truth(_equal(M.sunrise(date, location), M.BOGUS)) or _truth(_equal(M.sunset(date, location), M.BOGUS)))) then
        return M.BOGUS
    else
        return ((M.sunset(date, location) - M.sunrise(date, location)) / 12)
    end
end

-- TYPE (fixed-date location) -> real
-- Length of nighttime temporal hour on fixed date at location
-- Returns bogus if there no sunrise or sunset on date.
function M.nighttime_temporal_hour(date, location)
    if _truth((_truth(_equal(M.sunrise((date + 1), location), M.BOGUS)) or _truth(_equal(M.sunset(date, location), M.BOGUS)))) then
        return M.BOGUS
    else
        return ((M.sunrise((date + 1), location) - M.sunset(date, location)) / 12)
    end
end

-- TYPE (moment location) -> moment
-- Standard time of temporal moment tee at location.
-- Returns bogus if temporal hour is undefined that day.
function M.standard_from_sundial(tee, location)
    local date = M.fixed_from_moment(tee)
    local hour = (24 * M.time_from_moment(tee))
    local h = (function() if _truth(((6 <= hour) and (hour <= 18))) then return M.daytime_temporal_hour(date, location) elseif _truth(((hour < 6))) then return M.nighttime_temporal_hour((date - 1), location) else return M.nighttime_temporal_hour(date, location) end end)()
    if _truth(_equal(h, M.BOGUS)) then
        return M.BOGUS
    elseif _truth(((6 <= hour) and (hour <= 18))) then
        return (M.sunrise(date, location) + ((hour - 6) * h))
    elseif _truth(((hour < 6))) then
        return (M.sunset((date - 1), location) + ((hour + 6) * h))
    else
        return (M.sunset(date, location) + ((hour - 18) * h))
    end
end

-- TYPE (fixed-date location) -> moment
-- Standard time on fixed date at location of end of
-- morning according to Jewish ritual.
function M.jewish_morning_end(date, location)
    return M.standard_from_sundial((date + M.hr(10)), location)
end

-- TYPE (fixed-date location) -> moment
-- Standard time of asr on fixed date at location.
-- According to Hanafi rule.
-- Returns bogus is no asr occurs.
function M.asr(date, location)
    local noon = M.midday(date, location)
    local phi = M.latitude(location)
    local delta = M.declination(noon, M.deg(0), M.solar_longitude(noon))
    local altitude = M.arcsin_degrees(((M.cos_degrees(delta) * M.cos_degrees(phi)) + (M.sin_degrees(delta) * M.sin_degrees(phi))))
    local h = M.mod3(M.arctan_degrees(M.tan_degrees(altitude), ((2 * M.tan_degrees(altitude)) + 1)), -90, 90)
    if _truth(((altitude <= M.deg(0)))) then
        return M.BOGUS
    else
        return M.dusk(date, location, (-h))
    end
end

-- TYPE (fixed-date location) -> moment
-- Standard time of asr on fixed date at location.
-- According to Shafi’i rule.
-- Returns bogus is no asr occurs.
function M.alt_asr(date, location)
    local noon = M.midday(date, location)
    local phi = M.latitude(location)
    local delta = M.declination(noon, M.deg(0), M.solar_longitude(noon))
    local altitude = M.arcsin_degrees(((M.cos_degrees(delta) * M.cos_degrees(phi)) + (M.sin_degrees(delta) * M.sin_degrees(phi))))
    local h = M.mod3(M.arctan_degrees(M.tan_degrees(altitude), (M.tan_degrees(altitude) + 1)), -90, 90)
    if _truth(((altitude <= M.deg(0)))) then
        return M.BOGUS
    else
        return M.dusk(date, location, (-h))
    end
end

-- TYPE moment -> half-circle
-- Angular separation of sun and moon
-- at moment tee.
function M.arc_of_light(tee)
    return M.arccos_degrees((M.cos_degrees(M.lunar_latitude(tee)) * M.cos_degrees(M.lunar_phase(tee))))
end

-- TYPE (fixed-date location) -> moment
-- Best viewing time (UT) in the evening.
-- Simple version.
function M.simple_best_view(date, location)
    local dark = M.dusk(date, location, M.deg(4.5))
    local best = (function() if _truth(_equal(dark, M.BOGUS)) then return (date + 1) else return dark end end)()
    return M.universal_from_standard(best, location)
end

-- TYPE (fixed-date location) -> boolean
-- S. K. Shaukat’s criterion for likely
-- visibility of crescent moon on eve of date at location.
-- Not intended for high altitudes or polar regions.
function M.shaukat_criterion(date, location)
    local tee = M.simple_best_view((date - 1), location)
    local phase = M.lunar_phase(tee)
    local h = M.lunar_altitude(tee, location)
    local cap_ARCL = M.arc_of_light(tee)
    return (_truth(((M.NEW < phase) and (phase < M.FIRST_QUARTER))) and _truth(((M.deg(10.6) <= cap_ARCL) and (cap_ARCL <= M.deg(90)))) and _truth(((h > M.deg(4.1)))))
end

-- TYPE (moment location) -> half-circle
-- Angular difference in altitudes of sun and moon
-- at moment tee at location.
function M.arc_of_vision(tee, location)
    return (M.lunar_altitude(tee, location) - M.solar_altitude(tee, location))
end

-- TYPE (fixed-date location) -> moment
-- Best viewing time (UT) in the evening.
-- Yallop version, per Bruin (1977).
function M.bruin_best_view(date, location)
    local sun = M.sunset(date, location)
    local moon = M.moonset(date, location)
    local best = (function() if _truth((_truth(_equal(sun, M.BOGUS)) or _truth(_equal(moon, M.BOGUS)))) then return (date + 1) else return (((5 / 9) * sun) + ((4 / 9) * moon)) end end)()
    return M.universal_from_standard(best, location)
end

-- TYPE (fixed-date location) -> boolean
-- B. D. Yallop’s criterion for possible
-- visibility of crescent moon on eve of date at location.
-- Not intended for high altitudes or polar regions.
function M.yallop_criterion(date, location)
    local tee = M.bruin_best_view((date - 1), location)
    local phase = M.lunar_phase(tee)
    local cap_D = M.lunar_semi_diameter(tee, location)
    local cap_ARCL = M.arc_of_light(tee)
    local cap_W = (cap_D * (1 - M.cos_degrees(cap_ARCL)))
    local cap_ARCV = M.arc_of_vision(tee, location)
    local e = -0.14
    local q1 = M.poly(cap_W, _list(11.8371, -6.3226, 0.7319, -0.1018))
    return (_truth(((M.NEW < phase) and (phase < M.FIRST_QUARTER))) and _truth(((cap_ARCV > (q1 + e)))))
end

-- TYPE (moment location) -> half-circle
-- Topocentric lunar semi-diameter at moment tee and location.
function M.lunar_semi_diameter(tee, location)
    local h = M.lunar_altitude(tee, location)
    local p = M.lunar_parallax(tee, location)
    return (0.27245 * p * ((M.sin_degrees(h) * M.sin_degrees(p)) + 1))
end

-- TYPE moment -> angle
-- Geocentric apparent lunar diameter of the moon (in
-- degrees) at moment tee. Adapted from "Astronomical
-- Algorithms" by Jean Meeus, Willmann-Bell, 2nd edn.,
-- 1998.
function M.lunar_diameter(tee)
    return (M.deg((1792367000 / 9)) / M.lunar_distance(tee))
end

-- TYPE (fixed-date location) -> boolean
-- Criterion for possible visibility of crescent moon
-- on eve of date at location.
-- Shaukat’s criterion may be replaced with another.
function M.visible_crescent(date, location)
    return M.shaukat_criterion(date, location)
end

-- TYPE (fixed-date location) -> fixed-date
-- Closest fixed date on or before date when crescent
-- moon first became visible at location.
function M.phasis_on_or_before(date, location)
    local moon = M.fixed_from_moment(M.lunar_phase_at_or_before(M.NEW, date))
    local age = (date - moon)
    local tau = (function() if _truth((_truth(((age <= 3))) and _truth((not _truth(M.visible_crescent(date, location)))))) then return (moon - 30) else return moon end end)()
    return M.next(tau, function(d) return _truth(M.visible_crescent(d, location)) end)
end

-- TYPE (fixed-date location) -> fixed-date
-- Closest fixed date on or after date on the eve
-- of which crescent moon first became visible at location.
function M.phasis_on_or_after(date, location)
    local moon = M.fixed_from_moment(M.lunar_phase_at_or_before(M.NEW, date))
    local age = (date - moon)
    local tau = (function() if _truth((_truth(((4 <= age))) or _truth(M.visible_crescent((date - 1), location)))) then return (moon + 29) else return date end end)()
    return M.next(tau, function(d) return _truth(M.visible_crescent(d, location)) end)
end


----- D.15 The Persian Calendar -----

-- TYPE (persian-year persian-month persian-day)
-- TYPE -> persian-date
function M.persian_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE fixed-date
-- Fixed date of start of the Persian calendar.
M.PERSIAN_EPOCH = M.fixed_from_julian(M.julian_date(M.ce(622), M.MARCH, 19))

-- TYPE location
-- Location of Tehran, Iran.
M.TEHRAN = M.location(M.deg(35.68), M.deg(51.42), M.mt(1100), M.hr((3 + (1 / 2))))

-- TYPE fixed-date -> moment
-- Universal time of true noon on fixed date in Tehran.
function M.midday_in_tehran(date)
    return M.midday(date, M.TEHRAN)
end

-- TYPE fixed-date -> fixed-date
-- Fixed date of Astronomical Persian New Year on or
-- before fixed date.
function M.persian_new_year_on_or_before(date)
    local approx = M.estimate_prior_solar_longitude(M.SPRING, M.midday_in_tehran(date))
    return M.next((math.floor(approx) - 1), function(day) return _truth(((M.solar_longitude(M.midday_in_tehran(day)) <= (M.SPRING + M.deg(2))))) end)
end

-- TYPE persian-date -> fixed-date
-- Fixed date of Astronomical Persian date p-date.
function M.fixed_from_persian(p_date)
    local month = M.standard_month(p_date)
    local day = M.standard_day(p_date)
    local year = M.standard_year(p_date)
    local new_year = M.persian_new_year_on_or_before((M.PERSIAN_EPOCH + 180 + math.floor((M.MEAN_TROPICAL_YEAR * (function() if _truth(((0 < year))) then return (year - 1) else return year end end)()))))
    return ((new_year - 1) + (function() if _truth(((month <= 7))) then return (31 * (month - 1)) else return ((30 * (month - 1)) + 6) end end)() + day)
end

-- TYPE fixed-date -> persian-date
-- Astronomical Persian date (year month day)
-- corresponding to fixed date.
function M.persian_from_fixed(date)
    local new_year = M.persian_new_year_on_or_before(date)
    local y = (_round(((new_year - M.PERSIAN_EPOCH) / M.MEAN_TROPICAL_YEAR)) + 1)
    local year = (function() if _truth(((0 < y))) then return y else return (y - 1) end end)()
    local day_of_year = ((date - M.fixed_from_persian(M.persian_date(year, 1, 1))) + 1)
    local month = (function() if _truth(((day_of_year <= 186))) then return math.ceil((day_of_year / 31)) else return math.ceil(((day_of_year - 6) / 30)) end end)()
    local day = (date - (M.fixed_from_persian(M.persian_date(year, month, 1)) - 1))
    return M.persian_date(year, month, day)
end

-- TYPE persian-year -> boolean
-- True if p-year is a leap year on the Persian calendar.
function M.arithmetic_persian_leap_year_p(p_year)
    local y = (function() if _truth(((0 < p_year))) then return (p_year - 474) else return (p_year - 473) end end)()
    local year = ((y % 2820) + 474)
    return (((((year + 38) * 31) % 128) < 31))
end

-- TYPE persian-date -> fixed-date
-- Fixed date equivalent to Persian date p-date.
function M.fixed_from_arithmetic_persian(p_date)
    local day = M.standard_day(p_date)
    local month = M.standard_month(p_date)
    local p_year = M.standard_year(p_date)
    local y = (function() if _truth(((0 < p_year))) then return (p_year - 474) else return (p_year - 473) end end)()
    local year = ((y % 2820) + 474)
    return ((M.PERSIAN_EPOCH - 1) + (1029983 * M.quotient(y, 2820)) + (365 * (year - 1)) + M.quotient(((31 * year) - 5), 128) + (function() if _truth(((month <= 7))) then return (31 * (month - 1)) else return ((30 * (month - 1)) + 6) end end)() + day)
end

-- TYPE fixed-date -> persian-year
-- Persian year corresponding to the fixed date.
function M.arithmetic_persian_year_from_fixed(date)
    local d0 = (date - M.fixed_from_arithmetic_persian(M.persian_date(475, 1, 1)))
    local n2820 = M.quotient(d0, 1029983)
    local d1 = (d0 % 1029983)
    local y2820 = (function() if _truth(((d1 == 1029982))) then return 2820 else return M.quotient(((128 * d1) + 46878), 46751) end end)()
    local year = (474 + (2820 * n2820) + y2820)
    if _truth(((0 < year))) then
        return year
    else
        return (year - 1)
    end
end

-- TYPE fixed-date -> persian-date
-- Persian date corresponding to fixed date.
function M.arithmetic_persian_from_fixed(date)
    local year = M.arithmetic_persian_year_from_fixed(date)
    local day_of_year = ((date - M.fixed_from_arithmetic_persian(M.persian_date(year, 1, 1))) + 1)
    local month = (function() if _truth(((day_of_year <= 186))) then return math.ceil((day_of_year / 31)) else return math.ceil(((day_of_year - 6) / 30)) end end)()
    local day = (date - (M.fixed_from_arithmetic_persian(M.persian_date(year, month, 1)) - 1))
    return M.persian_date(year, month, day)
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Persian New Year (Nowruz) in Gregorian
-- year g-year.
function M.nowruz(g_year)
    local persian_year = ((g_year - M.gregorian_year_from_fixed(M.PERSIAN_EPOCH)) + 1)
    local y = (function() if _truth(((persian_year <= 0))) then return (persian_year - 1) else return persian_year end end)()
    return M.fixed_from_persian(M.persian_date(y, 1, 1))
end


----- D.16 The Baha'i Calendar -----

-- TYPE (bahai-major bahai-cycle bahai-year
-- TYPE bahai-month bahai-day) -> bahai-date
function M.bahai_date(major, cycle, year, month, day)
    return _list(major, cycle, year, month, day)
end

-- TYPE bahai-date -> bahai-major
function M.bahai_major(date)
    return _nth(0, date)
end

-- TYPE bahai-date -> bahai-cycle
function M.bahai_cycle(date)
    return _nth(1, date)
end

-- TYPE bahai-date -> bahai-year
function M.bahai_year(date)
    return _nth(2, date)
end

-- TYPE bahai-date -> bahai-month
function M.bahai_month(date)
    return _nth(3, date)
end

-- TYPE bahai-date -> bahai-day
function M.bahai_day(date)
    return _nth(4, date)
end

-- TYPE bahai-month
-- Signifies intercalary period of 4 or 5 days.
M.AYYAM_I_HA = 0

-- TYPE fixed-date
-- Fixed date of start of Baha’i calendar.
M.BAHAI_EPOCH = M.fixed_from_gregorian(M.gregorian_date(1844, M.MARCH, 21))

-- TYPE bahai-date -> fixed-date
-- Fixed date equivalent to the Baha’i date b-date.
function M.fixed_from_bahai(b_date)
    local major = M.bahai_major(b_date)
    local cycle = M.bahai_cycle(b_date)
    local year = M.bahai_year(b_date)
    local month = M.bahai_month(b_date)
    local day = M.bahai_day(b_date)
    local g_year = ((361 * (major - 1)) + (19 * (cycle - 1)) + year + -1 + M.gregorian_year_from_fixed(M.BAHAI_EPOCH))
    return (M.fixed_from_gregorian(M.gregorian_date(g_year, M.MARCH, 20)) + (function() if _truth(((month == M.AYYAM_I_HA))) then return 342 elseif _truth(((month == 19))) then return (function() if _truth(M.gregorian_leap_year_p((g_year + 1))) then return 347 else return 346 end end)() else return (19 * (month - 1)) end end)() + day)
end

-- TYPE fixed-date -> bahai-date
-- Baha’i (major cycle year month day) corresponding to fixed
-- date.
function M.bahai_from_fixed(date)
    local g_year = M.gregorian_year_from_fixed(date)
    local start = M.gregorian_year_from_fixed(M.BAHAI_EPOCH)
    local years = (g_year - start - (function() if _truth(((date <= M.fixed_from_gregorian(M.gregorian_date(g_year, M.MARCH, 20))))) then return 1 else return 0 end end)())
    local major = (M.quotient(years, 361) + 1)
    local cycle = (M.quotient((years % 361), 19) + 1)
    local year = ((years % 19) + 1)
    local days = (date - M.fixed_from_bahai(M.bahai_date(major, cycle, year, 1, 1)))
    local month = (function() if _truth(((date >= M.fixed_from_bahai(M.bahai_date(major, cycle, year, 19, 1))))) then return 19 elseif _truth(((date >= M.fixed_from_bahai(M.bahai_date(major, cycle, year, M.AYYAM_I_HA, 1))))) then return M.AYYAM_I_HA else return (M.quotient(days, 19) + 1) end end)()
    local day = (date - -1 - M.fixed_from_bahai(M.bahai_date(major, cycle, year, month, 1)))
    return M.bahai_date(major, cycle, year, month, day)
end

-- TYPE location
-- Location of Tehran for astronomical Baha’i calendar.
M.BAHAI_LOCATION = M.location(M.deg(35.696111), M.deg(51.423056), M.mt(0), M.hr((3 + (1 / 2))))

-- TYPE fixed-date -> moment
-- Universal time of sunset on fixed date
-- in Bahai-Location.
function M.bahai_sunset(date)
    return M.universal_from_standard(M.sunset(date, M.BAHAI_LOCATION), M.BAHAI_LOCATION)
end

-- TYPE fixed-date -> fixed-date
-- Fixed date of astronomical Bahai New Year on or before fixed
-- date.
function M.astro_bahai_new_year_on_or_before(date)
    local approx = M.estimate_prior_solar_longitude(M.SPRING, M.bahai_sunset(date))
    return M.next((math.floor(approx) - 1), function(day) return _truth(((M.solar_longitude(M.bahai_sunset(day)) <= (M.SPRING + M.deg(2))))) end)
end

-- TYPE bahai-date -> fixed-date
-- Fixed date of Baha’i date b-date.
function M.fixed_from_astro_bahai(b_date)
    local major = M.bahai_major(b_date)
    local cycle = M.bahai_cycle(b_date)
    local year = M.bahai_year(b_date)
    local month = M.bahai_month(b_date)
    local day = M.bahai_day(b_date)
    local years = ((361 * (major - 1)) + (19 * (cycle - 1)) + year)
    if _truth(((month == 19))) then
        return (M.astro_bahai_new_year_on_or_before((M.BAHAI_EPOCH + math.floor((M.MEAN_TROPICAL_YEAR * (years + (1 / 2)))))) + -20 + day)
    elseif _truth(((month == M.AYYAM_I_HA))) then
        return (M.astro_bahai_new_year_on_or_before((M.BAHAI_EPOCH + math.floor((M.MEAN_TROPICAL_YEAR * (years - (1 / 2)))))) + 341 + day)
    else
        return (M.astro_bahai_new_year_on_or_before((M.BAHAI_EPOCH + math.floor((M.MEAN_TROPICAL_YEAR * (years - (1 / 2)))))) + ((month - 1) * 19) + day + -1)
    end
end

-- TYPE fixed-date -> bahai-date
-- Astronomical Baha’i date corresponding to fixed date.
function M.astro_bahai_from_fixed(date)
    local new_year = M.astro_bahai_new_year_on_or_before(date)
    local years = _round(((new_year - M.BAHAI_EPOCH) / M.MEAN_TROPICAL_YEAR))
    local major = (M.quotient(years, 361) + 1)
    local cycle = (M.quotient((years % 361), 19) + 1)
    local year = ((years % 19) + 1)
    local days = (date - new_year)
    local month = (function() if _truth(((date >= M.fixed_from_astro_bahai(M.bahai_date(major, cycle, year, 19, 1))))) then return 19 elseif _truth(((date >= M.fixed_from_astro_bahai(M.bahai_date(major, cycle, year, M.AYYAM_I_HA, 1))))) then return M.AYYAM_I_HA else return (M.quotient(days, 19) + 1) end end)()
    local day = (date - -1 - M.fixed_from_astro_bahai(M.bahai_date(major, cycle, year, month, 1)))
    return M.bahai_date(major, cycle, year, month, day)
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Baha’i New Year in Gregorian year g-year.
function M.bahai_new_year(g_year)
    return M.fixed_from_gregorian(M.gregorian_date(g_year, M.MARCH, 21))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Baha’i New Year (Naw-Ruz) in Gregorian
-- year g-year.
function M.naw_ruz(g_year)
    return M.astro_bahai_new_year_on_or_before(M.gregorian_new_year((g_year + 1)))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Feast of Ridvan in Gregorian year g-year.
function M.feast_of_ridvan(g_year)
    return (M.naw_ruz(g_year) + 31)
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of the Birthday of the Bab
-- in Gregorian year g-year.
function M.birth_of_the_bab(g_year)
    local ny = M.naw_ruz(g_year)
    local set1 = M.bahai_sunset(ny)
    local m1 = M.new_moon_at_or_after(set1)
    local m8 = M.new_moon_at_or_after((m1 + 190))
    local day = M.fixed_from_moment(m8)
    local set8 = M.bahai_sunset(day)
    if _truth(((m8 < set8))) then
        return (day + 1)
    else
        return (day + 2)
    end
end


----- D.17 The French Revolutionary Calendar -----

-- TYPE (french-year french-month french-day) -> french-date
function M.french_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE location
-- Location of Paris Observatory. Longitude corresponds
-- to difference of 9m 21s between Paris time zone and
-- Universal Time.
M.PARIS = M.location(M.angle(48, 50, 11), M.angle(2, 20, 15), M.mt(27), M.hr(1))

-- TYPE fixed-date -> moment
-- Universal time of true midnight at end of fixed date
-- in Paris.
function M.midnight_in_paris(date)
    return M.midnight((date + 1), M.PARIS)
end

-- TYPE fixed-date -> fixed-date
-- Fixed date of French Revolutionary New Year on or
-- before fixed date.
function M.french_new_year_on_or_before(date)
    local approx = M.estimate_prior_solar_longitude(M.AUTUMN, M.midnight_in_paris(date))
    return M.next((math.floor(approx) - 1), function(day) return _truth(((M.AUTUMN <= M.solar_longitude(M.midnight_in_paris(day))))) end)
end

-- TYPE fixed-date
-- Fixed date of start of the French Revolutionary
-- calendar.
M.FRENCH_EPOCH = M.fixed_from_gregorian(M.gregorian_date(1792, M.SEPTEMBER, 22))

-- TYPE french-date -> fixed-date
-- Fixed date of French Revolutionary date.
function M.fixed_from_french(f_date)
    local month = M.standard_month(f_date)
    local day = M.standard_day(f_date)
    local year = M.standard_year(f_date)
    local new_year = M.french_new_year_on_or_before(math.floor((M.FRENCH_EPOCH + 180 + (M.MEAN_TROPICAL_YEAR * (year - 1)))))
    return (new_year + -1 + (30 * (month - 1)) + day)
end

-- TYPE fixed-date -> french-date
-- French Revolutionary date of fixed date.
function M.french_from_fixed(date)
    local new_year = M.french_new_year_on_or_before(date)
    local year = (_round(((new_year - M.FRENCH_EPOCH) / M.MEAN_TROPICAL_YEAR)) + 1)
    local month = (M.quotient((date - new_year), 30) + 1)
    local day = (((date - new_year) % 30) + 1)
	local decade = M.quotient(day - 1, 10) + 1 -- I added decade and weekday, which are not in the book, for use on Wikipedia
    local weekday = M.amod(day, 10)
    return _list(year, month, decade, weekday)
end

-- TYPE french-year -> boolean
-- True if f-year is a leap year on the
-- French Revolutionary calendar.
function M.french_leap_year_p(f_year)
    return (((M.fixed_from_french(M.french_date((f_year + 1), 1, 1)) - M.fixed_from_french(M.french_date(f_year, 1, 1))) > 365))
end

-- TYPE french-year -> boolean
-- True if f-year is a leap year on the
-- Arithmetic French Revolutionary calendar.
function M.arithmetic_french_leap_year_p(f_year)
    return (_truth((((f_year % 4) == 0))) and _truth((not _truth(_member((f_year % 400), _list(100, 200, 300))))) and _truth((not _truth((((f_year % 4000) == 0))))))
end

-- TYPE french-date -> fixed-date
-- Fixed date of Arithmetic French Revolutionary
-- date f-date.
function M.fixed_from_arithmetic_french(f_date)
    local month = M.standard_month(f_date)
    local day = M.standard_day(f_date)
    local year = M.standard_year(f_date)
    return (M.FRENCH_EPOCH + -1 + (365 * (year - 1)) + M.quotient((year - 1), 4) + (-M.quotient((year - 1), 100)) + M.quotient((year - 1), 400) + (-M.quotient((year - 1), 4000)) + (30 * (month - 1)) + day)
end

-- TYPE fixed-date -> french-date
-- Arithmetic French Revolutionary date (year month day)
-- of fixed date.
function M.arithmetic_french_from_fixed(date)
    local approx = (M.quotient((date - M.FRENCH_EPOCH - -2), (1460969 / 4000)) + 1)
    local year = (function() if _truth(((date < M.fixed_from_arithmetic_french(M.french_date(approx, 1, 1))))) then return (approx - 1) else return approx end end)()
    local month = (M.quotient((date - M.fixed_from_arithmetic_french(M.french_date(year, 1, 1))), 30) + 1)
    local day = ((date - M.fixed_from_arithmetic_french(M.french_date(year, month, 1))) + 1)
    return M.french_date(year, month, day)
end


----- D.18 Astronomical Lunar Calendars -----

-- TYPE (babylonian-year babylonian-month
-- TYPE babylonian-leap babylonian-day)
-- TYPE -> babylonian-date
function M.babylonian_date(year, month, leap, day)
    return _list(year, month, leap, day)
end

-- TYPE babylonian-date -> babylonian-year
function M.babylonian_year(date)
    return _nth(0, date)
end

-- TYPE babylonian-date -> babylonian-month
function M.babylonian_month(date)
    return _nth(1, date)
end

-- TYPE babylonian-date -> babylonian-leap
function M.babylonian_leap(date)
    return _nth(2, date)
end

-- TYPE babylonian-date -> babylonian-day
function M.babylonian_day(date)
    return _nth(3, date)
end

-- TYPE (fixed-date location) -> duration
-- Time between sunset and moonset on date at location.
-- Returns bogus if there is no sunset on date.
function M.moonlag(date, location)
    local sun = M.sunset(date, location)
    local moon = M.moonset(date, location)
    if _truth(_equal(sun, M.BOGUS)) then
        return M.BOGUS
    elseif _truth(_equal(moon, M.BOGUS)) then
        return M.hr(24)
    else
        return (moon - sun)
    end
end

-- TYPE location
-- Location of Babylon.
M.BABYLON = M.location(M.deg(32.4794), M.deg(44.4328), M.mt(26), M.hr((3 + (1 / 2))))

-- TYPE (fixed-date location) -> boolean
-- Moonlag criterion for visibility of crescent moon on
-- eve of date in Babylon.
function M.babylonian_criterion(date)
    local set = M.sunset((date - 1), M.BABYLON)
    local tee = M.universal_from_standard(set, M.BABYLON)
    local phase = M.lunar_phase(tee)
    return (_truth(((M.NEW < phase) and (phase < M.FIRST_QUARTER))) and _truth(((M.new_moon_before(tee) <= (tee - M.hr(24))))) and _truth(((M.moonlag((date - 1), M.BABYLON) > M.mn(48)))))
end

-- TYPE fixed-date -> fixed-date
-- Fixed date of start of Babylonian month on or before
-- Babylonian date. Using lag of moonset criterion.
function M.babylonian_new_month_on_or_before(date)
    local moon = M.fixed_from_moment(M.lunar_phase_at_or_before(M.NEW, date))
    local age = (date - moon)
    local tau = (function() if _truth((_truth(((age <= 3))) and _truth((not _truth(M.babylonian_criterion(date)))))) then return (moon - 30) else return moon end end)()
    return M.next(tau, function(d) return _truth(M.babylonian_criterion(d)) end)
end

-- TYPE fixed-date
-- Fixed date of start of the Babylonian calendar
-- (Seleucid era). April 3, 311 BCE (Julian).
M.BABYLONIAN_EPOCH = M.fixed_from_julian(M.julian_date(M.bce(311), M.APRIL, 3))

-- TYPE babylonian-year -> boolean
-- True if b-year is a leap year on Babylonian calendar.
function M.babylonian_leap_year_p(b_year)
    return (((((7 * b_year) + 13) % 19) < 7))
end

-- TYPE babylonian-date -> fixed-date
-- Fixed date equivalent to Babylonian date.
function M.fixed_from_babylonian(b_date)
    local month = M.babylonian_month(b_date)
    local leap = M.babylonian_leap(b_date)
    local day = M.babylonian_day(b_date)
    local year = M.babylonian_year(b_date)
    local month1 = (function() if _truth((_truth(leap) or _truth((_truth((((year % 19) == 18))) and _truth(((month > 6))))))) then return month else return (month - 1) end end)()
    local months = (M.quotient((((year - 1) * 235) + 13), 19) + month1)
    local midmonth = (M.BABYLONIAN_EPOCH + _round((M.MEAN_SYNODIC_MONTH * months)) + 15)
    return (M.babylonian_new_month_on_or_before(midmonth) + day + -1)
end

-- TYPE fixed-date -> babylonian-date
-- Babylonian date corresponding to fixed date.
function M.babylonian_from_fixed(date)
    local crescent = M.babylonian_new_month_on_or_before(date)
    local months = _round(((crescent - M.BABYLONIAN_EPOCH) / M.MEAN_SYNODIC_MONTH))
    local year = (M.quotient(((19 * months) + 5), 235) + 1)
    local approx = (M.BABYLONIAN_EPOCH + _round((M.quotient((((year - 1) * 235) + 13), 19) * M.MEAN_SYNODIC_MONTH)))
    local new_year = M.babylonian_new_month_on_or_before((approx + 15))
    local month1 = (_round(((crescent - new_year) / 29.5)) + 1)
    local special = (((year % 19) == 18))
    local leap = (function() if _truth(special) then return ((month1 == 7)) else return ((month1 == 13)) end end)()
    local month = (function() if _truth((_truth(leap) or _truth((_truth(special) and _truth(((month1 > 6))))))) then return (month1 - 1) else return month1 end end)()
    local day = (date - crescent - -1)
    return M.babylonian_date(year, month, leap, day)
end

-- TYPE gregorian-year -> fixed-date
-- Date of (proposed) astronomical Easter in Gregorian
-- year g-year.
function M.astronomical_easter(g_year)
    local equinox = M.season_in_gregorian(M.SPRING, g_year)
    local paschal_moon = math.floor(M.apparent_from_universal(M.lunar_phase_at_or_after(M.FULL, equinox), M.JERUSALEM))
    return M.kday_after(M.SUNDAY, paschal_moon)
end

-- TYPE location
-- Sample location for Observational Islamic calendar
-- (Cairo, Egypt).
M.ISLAMIC_LOCATION = M.location(M.deg(30.1), M.deg(31.3), M.mt(200), M.hr(2))

-- TYPE islamic-date -> fixed-date
-- Fixed date equivalent to Observational Islamic date
-- i-date.
function M.fixed_from_observational_islamic(i_date)
    local month = M.standard_month(i_date)
    local day = M.standard_day(i_date)
    local year = M.standard_year(i_date)
    local midmonth = (M.ISLAMIC_EPOCH + math.floor(((((year - 1) * 12) + month + (-1 / 2)) * M.MEAN_SYNODIC_MONTH)))
    return (M.phasis_on_or_before(midmonth, M.ISLAMIC_LOCATION) + day + -1)
end

-- TYPE fixed-date -> islamic-date
-- Observational Islamic date (year month day)
-- corresponding to fixed date.
function M.observational_islamic_from_fixed(date)
    local crescent = M.phasis_on_or_before(date, M.ISLAMIC_LOCATION)
    local elapsed_months = _round(((crescent - M.ISLAMIC_EPOCH) / M.MEAN_SYNODIC_MONTH))
    local year = (M.quotient(elapsed_months, 12) + 1)
    local month = ((elapsed_months % 12) + 1)
    local day = ((date - crescent) + 1)
    return M.islamic_date(year, month, day)
end

-- TYPE (fixed-date location) -> 1..31
-- Length of lunar month based on observability at location,
-- which includes date.
function M.month_length(date, location)
    local moon = M.phasis_on_or_after((date + 1), location)
    local prev = M.phasis_on_or_before(date, location)
    return (moon - prev)
end

-- TYPE (fixed-date location) -> boolean
-- Fixed date in location is in a month that was forced to
-- start early.
function M.early_month_p(date, location)
    local start = M.phasis_on_or_before(date, location)
    local prev = (start - 15)
    return (_truth((((date - start) >= 30))) or _truth(((M.month_length(prev, location) > 30))) or _truth((_truth(((M.month_length(prev, location) == 30))) and _truth(M.early_month_p(prev, location)))))
end

-- TYPE islamic-date -> fixed-date
-- Fixed date equivalent to Observational Islamic i-date.
-- Months are never longer than 30 days.
function M.alt_fixed_from_observational_islamic(i_date)
    local month = M.standard_month(i_date)
    local day = M.standard_day(i_date)
    local year = M.standard_year(i_date)
    local midmonth = (M.ISLAMIC_EPOCH + math.floor(((((year - 1) * 12) + month + (-1 / 2)) * M.MEAN_SYNODIC_MONTH)))
    local moon = M.phasis_on_or_before(midmonth, M.ISLAMIC_LOCATION)
    local date = (moon + day + -1)
    if _truth(M.early_month_p(midmonth, M.ISLAMIC_LOCATION)) then
        return (date - 1)
    else
        return date
    end
end

-- TYPE fixed-date -> islamic-date
-- Observational Islamic date (year month day)
-- corresponding to fixed date.
-- Months are never longer than 30 days.
function M.alt_observational_islamic_from_fixed(date)
    local early = M.early_month_p(date, M.ISLAMIC_LOCATION)
    local long = (_truth(early) and _truth(((M.month_length(date, M.ISLAMIC_LOCATION) > 29))))
    local date_prime = (function() if _truth(long) then return (date + 1) else return date end end)()
    local moon = M.phasis_on_or_before(date_prime, M.ISLAMIC_LOCATION)
    local elapsed_months = _round(((moon - M.ISLAMIC_EPOCH) / M.MEAN_SYNODIC_MONTH))
    local year = (M.quotient(elapsed_months, 12) + 1)
    local month = ((elapsed_months % 12) + 1)
    local day = (date_prime - moon - (function() if _truth((_truth(early) and _truth((not _truth(long))))) then return -2 else return -1 end end)())
    return M.islamic_date(year, month, day)
end

-- TYPE fixed-date -> boolean
-- Saudi visibility criterion on eve of fixed date in Mecca.
function M.saudi_criterion(date)
    local set = M.sunset((date - 1), M.MECCA)
    local tee = M.universal_from_standard(set, M.MECCA)
    local phase = M.lunar_phase(tee)
    return (_truth(((M.NEW < phase) and (phase < M.FIRST_QUARTER))) and _truth(((M.moonlag((date - 1), M.MECCA) > 0))))
end

-- TYPE fixed-date -> fixed-date
-- Closest fixed date on or before date when Saudi
-- visibility criterion held.
function M.saudi_new_month_on_or_before(date)
    local moon = M.fixed_from_moment(M.lunar_phase_at_or_before(M.NEW, date))
    local age = (date - moon)
    local tau = (function() if _truth((_truth(((age <= 3))) and _truth((not _truth(M.saudi_criterion(date)))))) then return (moon - 30) else return moon end end)()
    return M.next(tau, function(d) return _truth(M.saudi_criterion(d)) end)
end

-- TYPE islamic-date -> fixed-date
-- Fixed date equivalent to Saudi Islamic date s-date.
function M.fixed_from_saudi_islamic(s_date)
    local month = M.standard_month(s_date)
    local day = M.standard_day(s_date)
    local year = M.standard_year(s_date)
    local midmonth = (M.ISLAMIC_EPOCH + math.floor(((((year - 1) * 12) + month + (-1 / 2)) * M.MEAN_SYNODIC_MONTH)))
    return (M.saudi_new_month_on_or_before(midmonth) + day + -1)
end

-- TYPE fixed-date -> islamic-date
-- Saudi Islamic date (year month day) corresponding to
-- fixed date.
function M.saudi_islamic_from_fixed(date)
    local crescent = M.saudi_new_month_on_or_before(date)
    local elapsed_months = _round(((crescent - M.ISLAMIC_EPOCH) / M.MEAN_SYNODIC_MONTH))
    local year = (M.quotient(elapsed_months, 12) + 1)
    local month = ((elapsed_months % 12) + 1)
    local day = ((date - crescent) + 1)
    return M.islamic_date(year, month, day)
end

-- TYPE location
-- Sample location for Observational Hebrew calendar
-- (Haifa, Israel).
M.HEBREW_LOCATION = M.location(M.deg(32.82), M.deg(35), M.mt(0), M.hr(2))

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Observational (classical)
-- Nisan 1 occurring in Gregorian year g-year.
function M.observational_hebrew_first_of_nisan(g_year)
    local equinox = M.season_in_gregorian(M.SPRING, g_year)
    local set = M.universal_from_standard(M.sunset(math.floor(equinox), M.HEBREW_LOCATION), M.HEBREW_LOCATION)
    return M.phasis_on_or_after((math.floor(equinox) - (function() if _truth(((equinox < set))) then return 14 else return 13 end end)()), M.HEBREW_LOCATION)
end

-- TYPE fixed-date -> hebrew-date
-- Observational Hebrew date (year month day)
-- corresponding to fixed date.
function M.observational_hebrew_from_fixed(date)
    local crescent = M.phasis_on_or_before(date, M.HEBREW_LOCATION)
    local g_year = M.gregorian_year_from_fixed(date)
    local ny = M.observational_hebrew_first_of_nisan(g_year)
    local new_year = (function() if _truth(((date < ny))) then return M.observational_hebrew_first_of_nisan((g_year - 1)) else return ny end end)()
    local month = (_round(((crescent - new_year) / 29.5)) + 1)
    local year = (M.standard_year(M.hebrew_from_fixed(new_year)) + (function() if _truth(((month >= M.TISHRI))) then return 1 else return 0 end end)())
    local day = (date - crescent - -1)
    return M.hebrew_date(year, month, day)
end

-- TYPE hebrew-date -> fixed-date
-- Fixed date equivalent to Observational Hebrew date.
function M.fixed_from_observational_hebrew(h_date)
    local month = M.standard_month(h_date)
    local day = M.standard_day(h_date)
    local year = M.standard_year(h_date)
    local year1 = (function() if _truth(((month >= M.TISHRI))) then return (year - 1) else return year end end)()
    local start = M.fixed_from_hebrew(M.hebrew_date(year1, M.NISAN, 1))
    local g_year = M.gregorian_year_from_fixed((start + 60))
    local new_year = M.observational_hebrew_first_of_nisan(g_year)
    local midmonth = (new_year + _round((29.5 * (month - 1))) + 15)
    return (M.phasis_on_or_before(midmonth, M.HEBREW_LOCATION) + day + -1)
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Classical (observational) Passover Eve
-- (Nisan 14) occurring in Gregorian year g-year.
function M.classical_passover_eve(g_year)
    return (M.observational_hebrew_first_of_nisan(g_year) + 13)
end

-- TYPE fixed-date -> hebrew-date
-- Observational Hebrew date (year month day)
-- corresponding to fixed date.
-- Months are never longer than 30 days.
function M.alt_observational_hebrew_from_fixed(date)
    local early = M.early_month_p(date, M.HEBREW_LOCATION)
    local long = (_truth(early) and _truth(((M.month_length(date, M.HEBREW_LOCATION) > 29))))
    local date_prime = (function() if _truth(long) then return (date + 1) else return date end end)()
    local moon = M.phasis_on_or_before(date_prime, M.HEBREW_LOCATION)
    local g_year = M.gregorian_year_from_fixed(date_prime)
    local ny = M.observational_hebrew_first_of_nisan(g_year)
    local new_year = (function() if _truth(((date_prime < ny))) then return M.observational_hebrew_first_of_nisan((g_year - 1)) else return ny end end)()
    local month = (_round(((moon - new_year) / 29.5)) + 1)
    local year = (M.standard_year(M.hebrew_from_fixed(new_year)) + (function() if _truth(((month >= M.TISHRI))) then return 1 else return 0 end end)())
    local day = (date_prime - moon - (function() if _truth((_truth(early) and _truth((not _truth(long))))) then return -2 else return -1 end end)())
    return M.hebrew_date(year, month, day)
end

-- TYPE hebrew-date -> fixed-date
-- Fixed date equivalent to Observational Hebrew h-date.
-- Months are never longer than 30 days.
function M.alt_fixed_from_observational_hebrew(h_date)
    local month = M.standard_month(h_date)
    local day = M.standard_day(h_date)
    local year = M.standard_year(h_date)
    local year1 = (function() if _truth(((month >= M.TISHRI))) then return (year - 1) else return year end end)()
    local start = M.fixed_from_hebrew(M.hebrew_date(year1, M.NISAN, 1))
    local g_year = M.gregorian_year_from_fixed((start + 60))
    local new_year = M.observational_hebrew_first_of_nisan(g_year)
    local midmonth = (new_year + _round((29.5 * (month - 1))) + 15)
    local moon = M.phasis_on_or_before(midmonth, M.HEBREW_LOCATION)
    local date = (moon + day + -1)
    if _truth(M.early_month_p(midmonth, M.HEBREW_LOCATION)) then
        return (date - 1)
    else
        return date
    end
end

-- TYPE location
-- Location of Mt. Gerizim.
M.SAMARITAN_LOCATION = M.location(M.deg(32.1994), M.deg(35.2728), M.mt(881), M.hr(2))

-- TYPE fixed-date -> moment
-- Universal time of true noon on date at Samaritan location.
function M.samaritan_noon(date)
    return M.midday(date, M.SAMARITAN_LOCATION)
end

-- TYPE moment -> fixed-date
-- Fixed date of first new moon after UT moment tee.
-- Modern calculation.
function M.samaritan_new_moon_after(tee)
    return math.ceil((M.apparent_from_universal(M.new_moon_at_or_after(tee), M.SAMARITAN_LOCATION) - M.hr(12)))
end

-- TYPE moment -> fixed-date
-- Fixed-date of last new moon before UT moment tee.
-- Modern calculation.
function M.samaritan_new_moon_at_or_before(tee)
    return math.ceil((M.apparent_from_universal(M.new_moon_before(tee), M.SAMARITAN_LOCATION) - M.hr(12)))
end

-- TYPE fixed-date
-- Fixed date of start of the Samaritan Entry Era.
M.SAMARITAN_EPOCH = M.fixed_from_julian(M.julian_date(M.bce(1639), M.MARCH, 15))

-- TYPE fixed-date -> fixed-date
-- Fixed date of Samaritan New Year on or before fixed
-- date.
function M.samaritan_new_year_on_or_before(date)
    local g_year = M.gregorian_year_from_fixed(date)
    local dates = _append(M.julian_in_gregorian(M.MARCH, 11, (g_year - 1)), M.julian_in_gregorian(M.MARCH, 11, g_year), _list((date + 1)))
    local n = M.final(0, function(i) return _truth(((M.samaritan_new_moon_after(M.samaritan_noon(_nth(i, dates))) <= date))) end)
    return M.samaritan_new_moon_after(M.samaritan_noon(_nth(n, dates)))
end

-- TYPE hebrew-date -> fixed-date
-- Fixed date of Samaritan date h-date.
function M.fixed_from_samaritan(s_date)
    local month = M.standard_month(s_date)
    local day = M.standard_day(s_date)
    local year = M.standard_year(s_date)
    local ny = M.samaritan_new_year_on_or_before(math.floor((M.SAMARITAN_EPOCH + 50 + (365.25 * (year - math.ceil(((month - 5)) / (8)))))))
    local nm = M.samaritan_new_moon_at_or_before((ny + (29.5 * (month - 1)) + 15))
    return (nm + day + -1)
end

-- TYPE fixed-date -> hebrew-date
-- Samaritan date corresponding to fixed date.
function M.samaritan_from_fixed(date)
    local moon = M.samaritan_new_moon_at_or_before(M.samaritan_noon(date))
    local new_year = M.samaritan_new_year_on_or_before(moon)
    local month = (_round(((moon - new_year) / 29.5)) + 1)
    local year = (_round(((new_year - M.SAMARITAN_EPOCH) / 365.25)) + math.ceil(((month - 5)) / (8)))
    local day = (date - moon - -1)
    return M.hebrew_date(year, month, day)
end


----- D.19 The Chinese Calendar -----

-- TYPE (chinese-cycle chinese-year chinese-month
-- TYPE chinese-leap chinese-day) -> chinese-date
function M.chinese_date(cycle, year, month, leap, day)
    return _list(cycle, year, month, leap, day)
end

-- TYPE chinese-date -> chinese-cycle
function M.chinese_cycle(date)
    return _nth(0, date)
end

-- TYPE chinese-date -> chinese-year
function M.chinese_year(date)
    return _nth(1, date)
end

-- TYPE chinese-date -> chinese-month
function M.chinese_month(date)
    return _nth(2, date)
end

-- TYPE chinese-date -> chinese-leap
function M.chinese_leap(date)
    return _nth(3, date)
end

-- TYPE chinese-date -> chinese-day
function M.chinese_day(date)
    return _nth(4, date)
end

-- TYPE fixed-date -> integer
-- Last Chinese major solar term (zhongqi) before fixed
-- date.
function M.current_major_solar_term(date)
    local s = M.solar_longitude(M.universal_from_standard(date, M.chinese_location(date)))
    return M.amod((2 + M.quotient(s, M.deg(30))), 12)
end

-- TYPE moment -> location
-- Location of Beijing; time zone varies with tee.
function M.chinese_location(tee)
    local year = M.gregorian_year_from_fixed(math.floor(tee))
    if _truth(((year < 1929))) then
        return M.location(M.angle(39, 55, 0), M.angle(116, 25, 0), M.mt(43.5), M.hr((1397 / 180)))
    else
        return M.location(M.angle(39, 55, 0), M.angle(116, 25, 0), M.mt(43.5), M.hr(8))
    end
end

-- TYPE (season moment) -> moment
-- Moment (Beijing time) of the first time at or after
-- tee (Beijing time) when the solar longitude
-- will be lambda degrees.
function M.chinese_solar_longitude_on_or_after(lambda, tee)
    local sun = M.solar_longitude_after(lambda, M.universal_from_standard(tee, M.chinese_location(tee)))
    return M.standard_from_universal(sun, M.chinese_location(sun))
end

-- TYPE fixed-date -> moment
-- Moment (in Beijing) of the first Chinese major
-- solar term (zhongqi) on or after fixed date. The
-- major terms begin when the sun’s longitude is a
-- multiple of 30 degrees.
function M.major_solar_term_on_or_after(date)
    local s = M.solar_longitude(M.midnight_in_china(date))
    local l = ((30 * math.ceil((s / 30))) % 360)
    return M.chinese_solar_longitude_on_or_after(l, date)
end

-- TYPE fixed-date -> integer
-- Last Chinese minor solar term (jieqi) before date.
function M.current_minor_solar_term(date)
    local s = M.solar_longitude(M.universal_from_standard(date, M.chinese_location(date)))
    return M.amod((3 + M.quotient((s - M.deg(15)), M.deg(30))), 12)
end

-- TYPE fixed-date -> moment
-- Moment (in Beijing) of the first Chinese minor solar
-- term (jieqi) on or after fixed date. The minor terms
-- begin when the sun’s longitude is an odd multiple of 15
-- degrees.
function M.minor_solar_term_on_or_after(date)
    local s = M.solar_longitude(M.midnight_in_china(date))
    local l = (((30 * math.ceil(((s - M.deg(15)) / 30))) + M.deg(15)) % 360)
    return M.chinese_solar_longitude_on_or_after(l, date)
end

-- TYPE fixed-date -> moment
-- Universal time of (clock) midnight at start of fixed
-- date in China.
function M.midnight_in_china(date)
    return M.universal_from_standard(date, M.chinese_location(date))
end

-- TYPE fixed-date -> fixed-date
-- Fixed date, in the Chinese zone, of winter solstice
-- on or before fixed date.
function M.chinese_winter_solstice_on_or_before(date)
    local approx = M.estimate_prior_solar_longitude(M.WINTER, M.midnight_in_china((date + 1)))
    return M.next((math.floor(approx) - 1), function(day) return _truth(((M.WINTER < M.solar_longitude(M.midnight_in_china((day + 1)))))) end)
end

-- TYPE fixed-date -> fixed-date
-- Fixed date (Beijing) of first new moon on or after
-- fixed date.
function M.chinese_new_moon_on_or_after(date)
    local tee = M.new_moon_at_or_after(M.midnight_in_china(date))
    return math.floor(M.standard_from_universal(tee, M.chinese_location(tee)))
end

-- TYPE fixed-date -> fixed-date
-- Fixed date (Beijing) of first new moon before fixed
-- date.
function M.chinese_new_moon_before(date)
    local tee = M.new_moon_before(M.midnight_in_china(date))
    return math.floor(M.standard_from_universal(tee, M.chinese_location(tee)))
end

-- TYPE fixed-date -> boolean
-- True if Chinese lunar month starting on date
-- has no major solar term.
function M.chinese_no_major_solar_term_p(date)
    return ((M.current_major_solar_term(date) == M.current_major_solar_term(M.chinese_new_moon_on_or_after((date + 1)))))
end

-- TYPE (fixed-date fixed-date) -> boolean
-- True if there is a Chinese leap month on or after lunar
-- month starting on fixed day m-prime and at or before
-- lunar month starting at fixed date m.
function M.chinese_prior_leap_month_p(m_prime, m)
    return (_truth(((m >= m_prime))) and _truth((_truth(M.chinese_no_major_solar_term_p(m)) or _truth(M.chinese_prior_leap_month_p(m_prime, M.chinese_new_moon_before(m))))))
end

-- TYPE fixed-date -> fixed-date
-- Fixed date of Chinese New Year in sui (period from
-- solstice to solstice) containing date.
function M.chinese_new_year_in_sui(date)
    local s1 = M.chinese_winter_solstice_on_or_before(date)
    local s2 = M.chinese_winter_solstice_on_or_before((s1 + 370))
    local m12 = M.chinese_new_moon_on_or_after((s1 + 1))
    local m13 = M.chinese_new_moon_on_or_after((m12 + 1))
    local next_m11 = M.chinese_new_moon_before((s2 + 1))
    if _truth((_truth(((_round(((next_m11 - m12) / M.MEAN_SYNODIC_MONTH)) == 12))) and _truth((_truth(M.chinese_no_major_solar_term_p(m12)) or _truth(M.chinese_no_major_solar_term_p(m13)))))) then
        return M.chinese_new_moon_on_or_after((m13 + 1))
    else
        return m13
    end
end

-- TYPE fixed-date -> fixed-date
-- Fixed date of Chinese New Year on or before fixed date.
function M.chinese_new_year_on_or_before(date)
    local new_year = M.chinese_new_year_in_sui(date)
    if _truth(((date >= new_year))) then
        return new_year
    else
        return M.chinese_new_year_in_sui((date - 180))
    end
end

-- TYPE fixed-date
-- Fixed date of start of the Chinese calendar.
M.CHINESE_EPOCH = M.fixed_from_gregorian(M.gregorian_date(-2636, M.FEBRUARY, 15))

-- TYPE fixed-date -> chinese-date
-- Chinese date (cycle year month leap day) of fixed date.
function M.chinese_from_fixed(date)
    local s1 = M.chinese_winter_solstice_on_or_before(date)
    local s2 = M.chinese_winter_solstice_on_or_before((s1 + 370))
    local m12 = M.chinese_new_moon_on_or_after((s1 + 1))
    local next_m11 = M.chinese_new_moon_before((s2 + 1))
    local m = M.chinese_new_moon_before((date + 1))
    local leap_year = ((_round(((next_m11 - m12) / M.MEAN_SYNODIC_MONTH)) == 12))
    local month = M.amod((_round(((m - m12) / M.MEAN_SYNODIC_MONTH)) - (function() if _truth((_truth(leap_year) and _truth(M.chinese_prior_leap_month_p(m12, m)))) then return 1 else return 0 end end)()), 12)
    local leap_month = (_truth(leap_year) and _truth(M.chinese_no_major_solar_term_p(m)) and _truth((not _truth(M.chinese_prior_leap_month_p(m12, M.chinese_new_moon_before(m))))))
    local elapsed_years = math.floor((1.5 + (-(month / 12)) + ((date - M.CHINESE_EPOCH) / M.MEAN_TROPICAL_YEAR)))
    local cycle = (M.quotient((elapsed_years - 1), 60) + 1)
    local year = M.amod(elapsed_years, 60)
    local day = ((date - m) + 1)
    return M.chinese_date(cycle, year, month, leap_month, day)
end

-- TYPE chinese-date -> fixed-date
-- Fixed date of Chinese date c-date.
function M.fixed_from_chinese(c_date)
    local cycle = M.chinese_cycle(c_date)
    local year = M.chinese_year(c_date)
    local month = M.chinese_month(c_date)
    local leap = M.chinese_leap(c_date)
    local day = M.chinese_day(c_date)
    local mid_year = math.floor((M.CHINESE_EPOCH + ((((cycle - 1) * 60) + (year - 1) + (1 / 2)) * M.MEAN_TROPICAL_YEAR)))
    local new_year = M.chinese_new_year_on_or_before(mid_year)
    local p = M.chinese_new_moon_on_or_after((new_year + ((month - 1) * 29)))
    local d = M.chinese_from_fixed(p)
    local prior_new_moon = (function() if _truth((_truth(((month == M.chinese_month(d)))) and _truth(_equal(leap, M.chinese_leap(d))))) then return p else return M.chinese_new_moon_on_or_after((p + 1)) end end)()
    return (prior_new_moon + day + -1)
end

-- TYPE (chinese-stem chinese-branch) -> chinese-name
-- Combination is impossible if stem and branch
-- are not the equal mod 2.
function M.chinese_name(stem, branch)
    return _list(stem, branch)
end

-- TYPE chinese-name -> chinese-stem
function M.chinese_stem(name)
    return _nth(0, name)
end

-- TYPE chinese-name -> chinese-branch
function M.chinese_branch(name)
    return _nth(1, name)
end

-- TYPE integer -> chinese-name
-- The n-th name of the Chinese sexagesimal cycle.
function M.chinese_sexagesimal_name(n)
    return M.chinese_name(M.amod(n, 10), M.amod(n, 12))
end

-- TYPE (chinese-name chinese-name) -> nonnegative-integer
-- Number of names from Chinese name c-name1 to the
-- next occurrence of Chinese name c-name2.
function M.chinese_name_difference(c_name1, c_name2)
    local stem1 = M.chinese_stem(c_name1)
    local stem2 = M.chinese_stem(c_name2)
    local branch1 = M.chinese_branch(c_name1)
    local branch2 = M.chinese_branch(c_name2)
    local stem_difference = (stem2 - stem1)
    local branch_difference = (branch2 - branch1)
    return M.amod((stem_difference + (25 * (branch_difference - stem_difference))), 60)
end

-- TYPE chinese-year -> chinese-name
-- Sexagesimal name for Chinese year of any cycle.
function M.chinese_year_name(year)
    return M.chinese_sexagesimal_name(year)
end

-- TYPE integer
-- Elapsed months at start of Chinese sexagesimal month
-- cycle.
M.CHINESE_MONTH_NAME_EPOCH = 57

-- TYPE (chinese-month chinese-year) -> chinese-name
-- Sexagesimal name for month month of Chinese year
-- year.
function M.chinese_month_name(month, year)
    local elapsed_months = ((12 * (year - 1)) + (month - 1))
    return M.chinese_sexagesimal_name((elapsed_months - M.CHINESE_MONTH_NAME_EPOCH))
end

-- TYPE integer
-- RD date of a start of Chinese sexagesimal day cycle.
M.CHINESE_DAY_NAME_EPOCH = M.rd(45)

-- TYPE fixed-date -> chinese-name
-- Chinese sexagesimal name for date.
function M.chinese_day_name(date)
    return M.chinese_sexagesimal_name((date - M.CHINESE_DAY_NAME_EPOCH))
end

-- TYPE (chinese-name fixed-date) -> fixed-date
-- Fixed date of latest date on or before fixed date
-- that has Chinese name.
function M.chinese_day_name_on_or_before(name, date)
    return M.mod3(M.chinese_name_difference(M.chinese_day_name(0), name), date, (date - 60))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Chinese New Year in Gregorian year g-year.
function M.chinese_new_year(g_year)
    return M.chinese_new_year_on_or_before(M.fixed_from_gregorian(M.gregorian_date(g_year, M.JULY, 1)))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of the Dragon Festival occurring in
-- Gregorian year g-year.
function M.dragon_festival(g_year)
    local elapsed_years = ((g_year - M.gregorian_year_from_fixed(M.CHINESE_EPOCH)) + 1)
    local cycle = (M.quotient((elapsed_years - 1), 60) + 1)
    local year = M.amod(elapsed_years, 60)
    return M.fixed_from_chinese(M.chinese_date(cycle, year, 5, false, 5))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Qingming occurring in Gregorian year
-- g-year.
function M.qing_ming(g_year)
    return math.floor(M.minor_solar_term_on_or_after(M.fixed_from_gregorian(M.gregorian_date(g_year, M.MARCH, 30))))
end

-- TYPE (chinese-date fixed-date) -> nonnegative-integer
-- Age at fixed date, given Chinese birthdate,
-- according to the Chinese custom. Returns bogus if
-- date is before birthdate.
function M.chinese_age(birthdate, date)
    local today = M.chinese_from_fixed(date)
    if _truth(((date >= M.fixed_from_chinese(birthdate)))) then
        return ((60 * (M.chinese_cycle(today) - M.chinese_cycle(birthdate))) + (M.chinese_year(today) - M.chinese_year(birthdate)) + 1)
    else
        return M.BOGUS
    end
end

-- TYPE augury
-- Lichun occurs twice (double-happiness).
M.DOUBLE_BRIGHT = 3

-- TYPE augury
-- Lichun occurs once at the start.
M.BRIGHT = 2

-- TYPE augury
-- Lichun occurs once at the end.
M.BLIND = 1

-- TYPE augury
-- Lichun does not occur (double-blind year).
M.WIDOW = 0

-- TYPE (chinese-cycle chinese-year) -> augury
-- The marriage augury type of Chinese year in cycle.
function M.chinese_year_marriage_augury(cycle, year)
    local new_year = M.fixed_from_chinese(M.chinese_date(cycle, year, 1, false, 1))
    local c = (function() if _truth(((year == 60))) then return (cycle + 1) else return cycle end end)()
    local y = (function() if _truth(((year == 60))) then return 1 else return (year + 1) end end)()
    local next_new_year = M.fixed_from_chinese(M.chinese_date(c, y, 1, false, 1))
    local first_minor_term = M.current_minor_solar_term(new_year)
    local next_first_minor_term = M.current_minor_solar_term(next_new_year)
    if _truth((_truth(((first_minor_term == 1))) and _truth(((next_first_minor_term == 12))))) then
        return M.WIDOW
    elseif _truth((_truth(((first_minor_term == 1))) and _truth(((next_first_minor_term ~= 12))))) then
        return M.BLIND
    elseif _truth((_truth(((first_minor_term ~= 1))) and _truth(((next_first_minor_term == 12))))) then
        return M.BRIGHT
    else
        return M.DOUBLE_BRIGHT
    end
end

function M.chinese_solar_term_from_fixed(fixed)
    -- This one is not in the book, but I added it so that Template:Currect Sekki can use it
    local s = M.solar_longitude(
        M.universal_from_standard(fixed + 1, M.chinese_location(fixed)))
    local l = 15 * (M.quotient(s, 15) % 24) -- Last solar term
    local next_l = (l + 15) % 360 -- Next solar term
    local next_moment = math.floor(
        M.chinese_solar_longitude_on_or_after(next_l, fixed))
    local days_until = next_moment - fixed
    return {l, next_l, days_until}
end

-- TYPE moment -> location
-- Location for Japanese calendar; varies with tee.
function M.japanese_location(tee)
    local year = M.gregorian_year_from_fixed(math.floor(tee))
    if _truth(((year < 1888))) then
        return M.location(M.deg(35.7), M.angle(139, 46, 0), M.mt(24), M.hr((9 + (143 / 450))))
    else
        return M.location(M.deg(35), M.deg(135), M.mt(0), M.hr(9))
    end
end


----- Japanese calendar (not in the book, but I added them because I wanted to) -----

function M.midnight_in_japan(date)
    -- Universal time of (clock) midnight at start of fixed date in Japan
    return M.universal_from_standard(date, M.japanese_location(date))
end

function M.japanese_solar_longitude_on_or_after(lambda, date)
    -- Moment (Tokyo time) of the first date on or after fixed date (Tokyo time)
    -- when the solar longitude will be lambda degrees.
    local tee = M.solar_longitude_after(lambda,
        M.universal_from_standard(date, M.japanese_location(date)))
    return M.standard_from_universal(tee, M.japanese_location(tee))
end

function M.japanese_current_major_solar_term(date)
    -- Last Japanese major solar term (chuki) before fixed date
    local s = M.solar_longitude(
        M.universal_from_standard(date, M.japanese_location(date)))
    return M.amod(2 + M.quotient(s, 30), 12)
end

function M.japanese_major_solar_term_on_or_after(date)
    -- Moment (in Tokyo) of first Japanese major solar term (chuki) on or after
    -- fixed date. The major terms begin when the sun's longitude is a multiple
    -- of 30 degrees.
    local s = M.solar_longitude(M.midnight_in_japan(date))
    local l = (30 * math.ceil(s / 30)) % 360
    return M.japanese_solar_longitude_on_or_after(l, date)
end

function M.japanese_current_minor_solar_term(date)
    -- Last Japanese minor solar term (sekki) before date
    local s = M.solar_longitude(
        M.universal_from_standard(date, M.japanese_location(date)))
    return M.amod(3 + M.quotient(s - 15, 30), 12)
end

function M.japanese_minor_solar_term_on_or_after(date)
    -- Moment (in Tokyo) of the first Japanese minor solar term (sekki) on or
    -- after fixed date. The minor terms begin when the sun's longitude is an
    -- odd multiple of 15 degrees.
    local s = M.solar_longitude(M.midnight_in_japan(date))
    local l = (30 * math.ceil((s - 15) / 30) + 15) % 360
    return M.japanese_solar_longitude_on_or_after(l, date)
end

function M.japanese_solar_term_from_fixed(fixed)
    -- This one is not in the book, but I added it so that Template:Currect Sekki can use it
    local s = M.solar_longitude(
        M.universal_from_standard(fixed + 1, M.japanese_location(fixed)))
    local l = 15 * (M.quotient(s, 15) % 24) -- Last solar term
    local next_l = (l + 15) % 360 -- Next solar term
    local next_moment = math.floor(
        M.japanese_solar_longitude_on_or_after(next_l, fixed))
    local days_until = next_moment - fixed
    return {l, next_l, days_until}
end

function M.japanese_new_moon_before(date)
    -- Fixed date (Tokyo) of first new moon before fixed date
    local tee = M.new_moon_before(M.midnight_in_japan(date))
    return math.floor(M.standard_from_universal(tee, M.japanese_location(tee)))
end

function M.japanese_new_moon_on_or_after(date)
    -- Fixed date (Tokyo) of first new moon on or after fixed date
    local tee = M.new_moon_at_or_after(M.midnight_in_japan(date))
    return math.floor(M.standard_from_universal(tee, M.japanese_location(tee)))
end

function M.japanese_no_major_solar_term(date)
    -- True if Japanese lunar month starting on date has no major solar term
    return M.japanese_current_major_solar_term(date)
        == M.japanese_current_major_solar_term(
            M.japanese_new_moon_on_or_after(date + 1))
end

function M.japanese_winter_solstice_on_or_before(date)
    -- Fixed date, in the Japanese zone, of winter solstice on or before fixed date
    local approx = M.estimate_prior_solar_longitude(
        M.WINTER, M.midnight_in_japan(date + 1))
    local day = math.floor(approx) - 1
    while not (M.WINTER < M.solar_longitude(M.midnight_in_japan(day + 1))) do
        day = day + 1
    end
    return day
end

function M.japanese_new_year_in_sui(date)
    -- Fixed date of Japanese New Year in sui (period from solstice to solstice)
    -- containing date.
    local s1 = M.japanese_winter_solstice_on_or_before(date)
    local s2 = M.japanese_winter_solstice_on_or_before(s1 + 370)
    local m12 = M.japanese_new_moon_on_or_after(s1 + 1)
    local m13 = M.japanese_new_moon_on_or_after(m12 + 1)
    local next_m11 = M.japanese_new_moon_before(s2 + 1)
    if math.floor(0.5 + (next_m11 - m12) / M.MEAN_SYNODIC_MONTH) == 12
       and (M.japanese_no_major_solar_term(m12)
            or M.japanese_no_major_solar_term(m13)) then
        return M.japanese_new_moon_on_or_after(m13 + 1)
    else
        return m13
    end
end

function M.japanese_new_year_on_or_before(date)
    -- Fixed date of Japanese New Year on or before fixed date
    local new_year = M.japanese_new_year_in_sui(date)
    if date >= new_year then
        return new_year
    else
        -- Got the New Year after; this happens if date is after the solstice
        -- but before the new year. Go back half a year.
        return M.japanese_new_year_in_sui(date - 180)
    end
end

M.japanese_prior_leap_month = function(m_prime, m)
    -- True if there is a Japanese leap month on or after lunar month starting
    -- m-prime and at or before lunar month starting m.
    return m >= m_prime
       and (M.japanese_no_major_solar_term(m)
            or M.japanese_prior_leap_month(
                m_prime, M.japanese_new_moon_before(m)))
end

function M.japanese_from_fixed(date)
    -- Japanese date (cycle year month leap day) of fixed date
    local s1 = M.japanese_winter_solstice_on_or_before(date)
    local s2 = M.japanese_winter_solstice_on_or_before(s1 + 370)
    local m12 = M.japanese_new_moon_on_or_after(s1 + 1)
    local next_m11 = M.japanese_new_moon_before(s2 + 1)
    local m = M.japanese_new_moon_before(date + 1)
    local leap_year = math.floor(
        0.5 + (next_m11 - m12) / M.MEAN_SYNODIC_MONTH) == 12
    local prior = (leap_year and M.japanese_prior_leap_month(m12, m)) and 1 or 0
    local month = M.amod(
        math.floor(0.5 + (m - m12) / M.MEAN_SYNODIC_MONTH) - prior, 12)
    local leap_month = leap_year
        and M.japanese_no_major_solar_term(m)
        and (not M.japanese_prior_leap_month(
            m12, M.japanese_new_moon_before(m)))
    local elapsed_years = math.floor(
        1.5 - month / 12 + (date - M.CHINESE_EPOCH) / M.MEAN_TROPICAL_YEAR)
    local cycle = M.quotient(elapsed_years - 1, 60) + 1
    local year = M.amod(elapsed_years, 60)
    local day = date - m + 1
    return {cycle, year, month, leap_month, day}
end

function M.fixed_from_japanese(j_date)
    -- Fixed date of Japanese date j-date.
    local cycle, year, month, leap, day =
        j_date[1], j_date[2], j_date[3], j_date[4], j_date[5]
    local mid_year = math.floor(M.CHINESE_EPOCH
        + ((cycle - 1) * 60 + (year - 1) + 1 / 2) * M.MEAN_TROPICAL_YEAR)
    local new_year = M.japanese_new_year_on_or_before(mid_year)
    local moon = M.japanese_new_moon_on_or_after(new_year + (month - 1) * 29)
    local d = M.japanese_from_fixed(moon)
    local prior_new_moon
    if month == d[3] and leap == d[4] then
        prior_new_moon = moon
    else
        prior_new_moon = M.japanese_new_moon_on_or_after(moon + 1)
    end
    return prior_new_moon + day - 1
end

function M.fixed_from_japanese_for_wikipedia(args)
    -- Not in the book: adapts the leap-month field to Wikipedia #invoke, where
    -- 1 means true and 0 means false.
    return M.fixed_from_japanese(
        {args[1], args[2], args[3], args[4] == 1, args[5]})
end

-- TYPE moment -> location
-- Location for Korean calendar; varies with tee.
-- Seoul city hall at a varying time zone.
function M.korean_location(tee)
    local z = (function() if _truth(((tee < M.fixed_from_gregorian(M.gregorian_date(1908, M.APRIL, 1))))) then return (3809 / 450) elseif _truth(((tee < M.fixed_from_gregorian(M.gregorian_date(1912, M.JANUARY, 1))))) then return 8.5 elseif _truth(((tee < M.fixed_from_gregorian(M.gregorian_date(1954, M.MARCH, 21))))) then return 9 elseif _truth(((tee < M.fixed_from_gregorian(M.gregorian_date(1961, M.AUGUST, 10))))) then return 8.5 else return 9 end end)()
    return M.location(M.angle(37, 34, 0), M.angle(126, 58, 0), M.mt(0), M.hr(z))
end

-- TYPE (chinese-cycle chinese-year) -> integer
-- Equivalent Korean year to Chinese cycle and year
function M.korean_year(cycle, year)
    return ((60 * cycle) + year + -364)
end

----- Korean calendar (not in the book, but I added them because I wanted to) -----

function M.midnight_in_korea(date)
    -- Universal time of (clock) midnight at start of fixed date in Korea
    return M.universal_from_standard(date, M.korean_location(date))
end

function M.korean_solar_longitude_on_or_after(lambda, date)
    -- Moment (Seoul time) of the first date on or after fixed date (Seoul time)
    -- when the solar longitude will be lambda degrees.
    local tee = M.solar_longitude_after(lambda,
        M.universal_from_standard(date, M.korean_location(date)))
    return M.standard_from_universal(tee, M.korean_location(tee))
end

function M.korean_current_major_solar_term(date)
    -- Last Korean major solar term before fixed date
    local s = M.solar_longitude(
        M.universal_from_standard(date, M.korean_location(date)))
    return M.amod(2 + M.quotient(s, 30), 12)
end

function M.korean_major_solar_term_on_or_after(date)
    -- Moment (in Seoul) of first Korean major solar term on or after
    -- fixed date. The major terms begin when the sun's longitude is a multiple
    -- of 30 degrees.
    local s = M.solar_longitude(M.midnight_in_korea(date))
    local l = (30 * math.ceil(s / 30)) % 360
    return M.korean_solar_longitude_on_or_after(l, date)
end

function M.korean_current_minor_solar_term(date)
    -- Last Korean minor solar term before date
    local s = M.solar_longitude(
        M.universal_from_standard(date, M.korean_location(date)))
    return M.amod(3 + M.quotient(s - 15, 30), 12)
end

function M.korean_minor_solar_term_on_or_after(date)
    -- Moment (in Seoul) of the first Korean minor solar term on or
    -- after fixed date. The minor terms begin when the sun's longitude is an
    -- odd multiple of 15 degrees.
    local s = M.solar_longitude(M.midnight_in_japan(date))
    local l = (30 * math.ceil((s - 15) / 30) + 15) % 360
    return M.korean_solar_longitude_on_or_after(l, date)
end

function M.korean_solar_term_from_fixed(fixed)
    -- This one is not in the book, but I added it so that Template:Currect Sekki can use it
    local s = M.solar_longitude(
        M.universal_from_standard(fixed + 1, M.korean_location(fixed)))
    local l = 15 * (M.quotient(s, 15) % 24) -- Last solar term
    local next_l = (l + 15) % 360 -- Next solar term
    local next_moment = math.floor(
        M.korean_solar_longitude_on_or_after(next_l, fixed))
    local days_until = next_moment - fixed
    return {l, next_l, days_until}
end

function M.korean_new_moon_before(date)
    -- Fixed date (Seoul) of first new moon before fixed date
    local tee = M.new_moon_before(M.midnight_in_japan(date))
    return math.floor(M.standard_from_universal(tee, M.korean_location(tee)))
end

function M.korean_new_moon_on_or_after(date)
    -- Fixed date (Seoul) of first new moon on or after fixed date
    local tee = M.new_moon_at_or_after(M.midnight_in_korea(date))
    return math.floor(M.standard_from_universal(tee, M.korean_location(tee)))
end

function M.korean_no_major_solar_term(date)
    -- True if Korean lunar month starting on date has no major solar term
    return M.korean_current_major_solar_term(date)
        == M.korean_current_major_solar_term(
            M.korean_new_moon_on_or_after(date + 1))
end

function M.korean_winter_solstice_on_or_before(date)
    -- Fixed date, in the Korean zone, of winter solstice on or before fixed date
    local approx = M.estimate_prior_solar_longitude(
        M.WINTER, M.midnight_in_korea(date + 1))
    local day = math.floor(approx) - 1
    while not (M.WINTER < M.solar_longitude(M.midnight_in_korea(day + 1))) do
        day = day + 1
    end
    return day
end

function M.korean_new_year_in_sui(date)
    -- Fixed date of Korean New Year in sui (period from solstice to solstice)
    -- containing date.
    local s1 = M.korean_winter_solstice_on_or_before(date)
    local s2 = M.korean_winter_solstice_on_or_before(s1 + 370)
    local m12 = M.korean_new_moon_on_or_after(s1 + 1)
    local m13 = M.korean_new_moon_on_or_after(m12 + 1)
    local next_m11 = M.korean_new_moon_before(s2 + 1)
    if math.floor(0.5 + (next_m11 - m12) / M.MEAN_SYNODIC_MONTH) == 12
       and (M.korean_no_major_solar_term(m12)
            or M.korean_no_major_solar_term(m13)) then
        return M.korean_new_moon_on_or_after(m13 + 1)
    else
        return m13
    end
end

function M.korean_new_year_on_or_before(date)
    -- Fixed date of Korean New Year on or before fixed date
    local new_year = M.korean_new_year_in_sui(date)
    if date >= new_year then
        return new_year
    else
        -- Got the New Year after; this happens if date is after the solstice
        -- but before the new year. Go back half a year.
        return M.korean_new_year_in_sui(date - 180)
    end
end

M.korean_prior_leap_month = function(m_prime, m)
    -- True if there is a Korean leap month on or after lunar month starting
    -- m-prime and at or before lunar month starting m.
    return m >= m_prime
       and (M.korean_no_major_solar_term(m)
            or M.korean_prior_leap_month(
                m_prime, M.korean_new_moon_before(m)))
end

function M.korean_from_fixed(date)
    -- Korean date (year month leap day) of fixed date
    local s1 = M.korean_winter_solstice_on_or_before(date)
    local s2 = M.korean_winter_solstice_on_or_before(s1 + 370)
    local m12 = M.korean_new_moon_on_or_after(s1 + 1)
    local next_m11 = M.korean_new_moon_before(s2 + 1)
    local m = M.korean_new_moon_before(date + 1)
    local leap_year = math.floor(
        0.5 + (next_m11 - m12) / M.MEAN_SYNODIC_MONTH) == 12
    local prior = (leap_year and M.korean_prior_leap_month(m12, m)) and 1 or 0
    local month = M.amod(
        math.floor(0.5 + (m - m12) / M.MEAN_SYNODIC_MONTH) - prior, 12)
    local leap_month = leap_year
        and M.korean_no_major_solar_term(m)
        and (not M.korean_prior_leap_month(
            m12, M.korean_new_moon_before(m)))
    local elapsed_years = math.floor(
        1.5 - month / 12 + (date - M.CHINESE_EPOCH) / M.MEAN_TROPICAL_YEAR)
    local cycle = M.quotient(elapsed_years - 1, 60) + 1
    local year = M.amod(elapsed_years, 60)
    local day = date - m + 1
    return {cycle, year, month, leap_month, day}
end

function M.korean_from_fixed_for_wikipedia(fixed)
    -- Not in the book
	local k_date = M.korean_from_fixed(fixed)
    return {M.korean_year(k_date[1], k_date[2]), k_date[3], k_date[4], k_date[5]}
end

function M.fixed_from_korean(k_date)
    -- Fixed date of korean date k-date.
    local cycle, year, month, leap, day =
        k_date[1], k_date[2], k_date[3], k_date[4], k_date[5]
    local mid_year = math.floor(M.CHINESE_EPOCH
        + ((cycle - 1) * 60 + (year - 1) + 1 / 2) * M.MEAN_TROPICAL_YEAR)
    local new_year = M.korean_new_year_on_or_before(mid_year)
    local moon = M.korean_new_moon_on_or_after(new_year + (month - 1) * 29)
    local d = M.korean_from_fixed(moon)
    local prior_new_moon
    if month == d[3] and leap == d[4] then
        prior_new_moon = moon
    else
        prior_new_moon = M.korean_new_moon_on_or_after(moon + 1)
    end
    return prior_new_moon + day - 1
end

function M.fixed_from_korean_for_wikipedia(args)
    -- Not in the book: adapts the leap-month field to Wikipedia #invoke, where
    -- 1 means true and 0 means false.
    return M.fixed_from_korean(
        {args[1], args[2], args[3], args[4] == 1, args[5]})
end

-- TYPE moment -> location
-- Location for Vietnamese calendar is Hanoi; varies with
-- tee. Time zone has changed over the years.
function M.vietnamese_location(tee)
    local z = (function() if _truth(((tee < M.gregorian_new_year(1968)))) then return 8 else return 7 end end)()
    return M.location(M.angle(21, 2, 0), M.angle(105, 51, 0), M.mt(12), M.hr(z))
end


----- D.20 The Modern Hindu Calendars -----

-- TYPE rational
-- Mean length of Hindu sidereal year.
M.HINDU_SIDEREAL_YEAR = (365 + (279457 / 1080000))

-- TYPE rational
-- Mean length of Hindu sidereal month.
M.HINDU_SIDEREAL_MONTH = (27 + (4644439 / 14438334))

-- TYPE rational
-- Mean time from new moon to new moon.
M.HINDU_SYNODIC_MONTH = (29 + (7087771 / 13358334))

-- TYPE integer -> rational-amplitude
-- This simulates the Hindu sine table.
-- entry is an angle given as a multiplier of 225’.
function M.hindu_sine_table(entry)
    local exact = (3438 * M.sin_degrees((entry * M.angle(0, 225, 0))))
    local error = (0.215 * M.sign(exact) * M.sign((math.abs(exact) - 1716)))
    return (_round((exact + error)) / 3438)
end

-- TYPE rational-angle -> rational-amplitude
-- Linear interpolation for theta in Hindu table.
function M.hindu_sine(theta)
    local entry = (theta / M.angle(0, 225, 0))
    local fraction = (entry % 1)
    return ((fraction * M.hindu_sine_table(math.ceil(entry))) + ((1 - fraction) * M.hindu_sine_table(math.floor(entry))))
end

-- TYPE rational-amplitude -> rational-angle
-- Inverse of Hindu sine function of amp.
function M.hindu_arcsin(amp)
    if _truth(((amp < 0))) then
        return (-M.hindu_arcsin((-amp)))
    else
        local pos = M.next(0, function(k) return _truth(((amp <= M.hindu_sine_table(k)))) end)
        local below = M.hindu_sine_table((pos - 1))
        return (M.angle(0, 225, 0) * (pos + -1 + ((amp - below) / (M.hindu_sine_table(pos) - below))))
    end
end

-- TYPE (rational-moment rational) -> rational-angle
-- Position in degrees at moment tee in uniform circular
-- orbit of period days.
function M.hindu_mean_position(tee, period)
    return (M.deg(360) * (((tee - M.HINDU_CREATION) / period) % 1))
end

-- TYPE fixed-date
-- Fixed date of Hindu creation.
M.HINDU_CREATION = (M.HINDU_EPOCH - (1955880000 * M.HINDU_SIDEREAL_YEAR))

-- TYPE rational
-- Time from aphelion to aphelion.
M.HINDU_ANOMALISTIC_YEAR = (1577917828000 / (4320000000 - 387))

-- TYPE rational
-- Time from apogee to apogee, with bija correction.
M.HINDU_ANOMALISTIC_MONTH = (1577917828 / (57753336 - 488199))

-- TYPE (rational-moment rational rational rational
-- TYPE rational) -> rational-angle
-- Longitudinal position at moment tee. period is
-- period of mean motion in days. size is ratio of
-- radii of epicycle and deferent. anomalistic is the
-- period of retrograde revolution about epicycle.
-- change is maximum decrease in epicycle size.
function M.hindu_true_position(tee, period, size, anomalistic, change)
    local lambda = M.hindu_mean_position(tee, period)
    local offset = M.hindu_sine(M.hindu_mean_position(tee, anomalistic))
    local contraction = (math.abs(offset) * change * size)
    local equation = M.hindu_arcsin((offset * (size - contraction)))
    return ((lambda - equation) % 360)
end

-- TYPE rational-moment -> rational-angle
-- Solar longitude at moment tee.
function M.hindu_solar_longitude(tee)
    return M.hindu_true_position(tee, M.HINDU_SIDEREAL_YEAR, (14 / 360), M.HINDU_ANOMALISTIC_YEAR, (1 / 42))
end

-- TYPE rational-moment -> hindu-solar-month
-- Zodiacal sign of the sun, as integer in range 1..12,
-- at moment tee.
function M.hindu_zodiac(tee)
    return (M.quotient(M.hindu_solar_longitude(tee), M.deg(30)) + 1)
end

-- TYPE rational-moment -> rational-angle
-- Lunar longitude at moment tee.
function M.hindu_lunar_longitude(tee)
    return M.hindu_true_position(tee, M.HINDU_SIDEREAL_MONTH, (32 / 360), M.HINDU_ANOMALISTIC_MONTH, (1 / 96))
end

-- TYPE rational-moment -> rational-angle
-- Longitudinal distance between the sun and moon
-- at moment tee.
function M.hindu_lunar_phase(tee)
    return ((M.hindu_lunar_longitude(tee) - M.hindu_solar_longitude(tee)) % 360)
end

-- TYPE rational-moment -> hindu-lunar-day
-- Phase of moon (tithi) at moment tee, as an integer in
-- the range 1..30.
function M.hindu_lunar_day_from_moment(tee)
    return (M.quotient(M.hindu_lunar_phase(tee), M.deg(12)) + 1)
end

-- TYPE rational-moment -> rational-moment
-- Approximate moment of last new moon preceding moment
-- tee, close enough to determine zodiacal sign.
function M.hindu_new_moon_before(tee)
    local varepsilon = (2 ^ -1000)
    local tau = (tee - ((1 / M.deg(360)) * M.hindu_lunar_phase(tee) * M.HINDU_SYNODIC_MONTH))
    return M.binary_search((tau - 1), math.min(tee, (tau + 1)), function(x) local l, h = 0, 0; return _truth(((M.hindu_lunar_phase(x) < M.deg(180)))) end, function(l, h) local x = (l + h) / 2; return _truth((_truth(((M.hindu_zodiac(l) == M.hindu_zodiac(h)))) or _truth((((h - l) < varepsilon))))) end)
end

-- TYPE (hindu-solar-year hindu-solar-month hindu-solar-day)
-- TYPE -> hindu-solar-date
function M.hindu_solar_date(year, month, day)
    return _list(year, month, day)
end

-- TYPE rational-moment -> hindu-solar-year
-- Determine solar year at given moment tee.
function M.hindu_calendar_year(tee)
    return _round((((tee - M.HINDU_EPOCH) / M.HINDU_SIDEREAL_YEAR) - (M.hindu_solar_longitude(tee) / M.deg(360))))
end

-- TYPE standard-year
-- Years from Kali Yuga until Saka era.
M.HINDU_SOLAR_ERA = 3179

-- TYPE fixed-date -> hindu-solar-date
-- Hindu (Orissa) solar date equivalent to fixed date.
function M.hindu_solar_from_fixed(date)
    local critical = M.hindu_sunrise((date + 1))
    local month = M.hindu_zodiac(critical)
    local year = (M.hindu_calendar_year(critical) - M.HINDU_SOLAR_ERA)
    local approx = (date - 3 - (math.floor(M.hindu_solar_longitude(critical)) % M.deg(30)))
    local start = M.next(approx, function(i) return _truth(((M.hindu_zodiac(M.hindu_sunrise((i + 1))) == month))) end)
    local day = (date - start - -1)
    return M.hindu_solar_date(year, month, day)
end

-- TYPE hindu-solar-date -> fixed-date
-- Fixed date corresponding to Hindu solar date s-date
-- (Saka era; Orissa rule.)
function M.fixed_from_hindu_solar(s_date)
    local month = M.standard_month(s_date)
    local day = M.standard_day(s_date)
    local year = M.standard_year(s_date)
    local start = (math.floor(((year + M.HINDU_SOLAR_ERA + ((month - 1) / 12)) * M.HINDU_SIDEREAL_YEAR)) + M.HINDU_EPOCH)
    return (day + -1 + M.next((start - 3), function(d) return _truth(((M.hindu_zodiac(M.hindu_sunrise((d + 1))) == month))) end))
end

-- TYPE (hindu-lunar-year hindu-lunar-month
-- TYPE hindu-lunar-leap-month hindu-lunar-day
-- TYPE hindu-lunar-leap-day) -> hindu-lunar-date
function M.hindu_lunar_date(year, month, leap_month, day, leap_day)
    return _list(year, month, leap_month, day, leap_day)
end

-- TYPE hindu-lunar-date -> hindu-lunar-month
function M.hindu_lunar_month(date)
    return _nth(1, date)
end

-- TYPE hindu-lunar-date -> hindu-lunar-leap-month
function M.hindu_lunar_leap_month(date)
    return _nth(2, date)
end

-- TYPE hindu-lunar-date -> hindu-lunar-day
function M.hindu_lunar_day(date)
    return _nth(3, date)
end

-- TYPE hindu-lunar-date -> hindu-lunar-leap-day
function M.hindu_lunar_leap_day(date)
    return _nth(4, date)
end

-- TYPE hindu-lunar-date -> hindu-lunar-year
function M.hindu_lunar_year(date)
    return _nth(0, date)
end

-- TYPE standard-year
-- Years from Kali Yuga until Vikrama era.
M.HINDU_LUNAR_ERA = 3044

-- TYPE fixed-date -> hindu-lunar-date
-- Hindu lunar date, new-moon scheme,
-- equivalent to fixed date.
function M.hindu_lunar_from_fixed(date)
    local critical = M.hindu_sunrise(date)
    local day = M.hindu_lunar_day_from_moment(critical)
    local leap_day = ((day == M.hindu_lunar_day_from_moment(M.hindu_sunrise((date - 1)))))
    local last_new_moon = M.hindu_new_moon_before(critical)
    local next_new_moon = M.hindu_new_moon_before((math.floor(last_new_moon) + 35))
    local solar_month = M.hindu_zodiac(last_new_moon)
    local leap_month = ((solar_month == M.hindu_zodiac(next_new_moon)))
    local month = M.amod((solar_month + 1), 12)
    local year = (M.hindu_calendar_year((function() if _truth(((month <= 2))) then return (date + 180) else return date end end)()) - M.HINDU_LUNAR_ERA)
    return M.hindu_lunar_date(year, month, leap_month, day, leap_day)
end

-- TYPE hindu-lunar-date -> fixed-date
-- Fixed date corresponding to Hindu lunar date l-date.
function M.fixed_from_hindu_lunar(l_date)
    local year = M.hindu_lunar_year(l_date)
    local month = M.hindu_lunar_month(l_date)
    local leap_month = M.hindu_lunar_leap_month(l_date)
    local day = M.hindu_lunar_day(l_date)
    local leap_day = M.hindu_lunar_leap_day(l_date)
    local approx = (M.HINDU_EPOCH + (M.HINDU_SIDEREAL_YEAR * (year + M.HINDU_LUNAR_ERA + ((month - 1) / 12))))
    local s = math.floor((approx - (M.HINDU_SIDEREAL_YEAR * M.mod3(((M.hindu_solar_longitude(approx) / M.deg(360)) - ((month - 1) / 12)), (-1 / 2), (1 / 2)))))
    local k = M.hindu_lunar_day_from_moment((s + M.hr(6)))
    local est = (s - (-day) - (function() if _truth(((3 < k) and (k < 27))) then return k elseif _truth((function() local mid = M.hindu_lunar_from_fixed((s - 15)); return (_truth(((M.hindu_lunar_month(mid) ~= month))) or _truth((_truth(M.hindu_lunar_leap_month(mid)) and _truth((not _truth(leap_month)))))) end)()) then return M.mod3(k, -15, 15) else return M.mod3(k, 15, 45) end end)())
    local tau = (est - M.mod3((M.hindu_lunar_day_from_moment((est + M.hr(6))) - day), -15, 15))
    local date = M.next((tau - 1), function(d) return _truth(_member(M.hindu_lunar_day_from_moment(M.hindu_sunrise(d)), _list(day, M.amod((day + 1), 30)))) end)
    if _truth(leap_day) then
        return (date + 1)
    else
        return date
    end
end

-- TYPE location
-- Location of Ujjain.
M.UJJAIN = M.location(M.angle(23, 9, 0), M.angle(75, 46, 6), M.mt(0), M.hr((5 + (461 / 9000))))

-- TYPE location
-- Location (Ujjain) for determining Hindu calendar.
M.HINDU_LOCATION = M.UJJAIN

-- TYPE (fixed-date location) -> rational-angle
-- Difference between right and oblique ascension
-- of sun on date at location.
function M.hindu_ascensional_difference(date, location)
    local sin_delta = ((1397 / 3438) * M.hindu_sine(M.hindu_tropical_longitude(date)))
    local phi = M.latitude(location)
    local diurnal_radius = M.hindu_sine((M.deg(90) + M.hindu_arcsin(sin_delta)))
    local tan_phi = (M.hindu_sine(phi) / M.hindu_sine((M.deg(90) + phi)))
    local earth_sine = (sin_delta * tan_phi)
    return M.hindu_arcsin((-(earth_sine / diurnal_radius)))
end

-- TYPE fixed-date -> rational-angle
-- Hindu tropical longitude on fixed date.
-- Assumes precession with maximum of 27 degrees
-- and period of 7200 sidereal years
-- (= 1577917828/600 days).
function M.hindu_tropical_longitude(date)
    local days = (date - M.HINDU_EPOCH)
    local precession = (M.deg(27) - math.abs((M.deg(108) * M.mod3((((600 / 1577917828) * days) - (1 / 4)), (-1 / 2), (1 / 2)))))
    return ((M.hindu_solar_longitude(date) - precession) % 360)
end

-- TYPE fixed-date -> rational-angle
-- Difference between solar and sidereal day on date.
function M.hindu_solar_sidereal_difference(date)
    return (M.hindu_daily_motion(date) * M.hindu_rising_sign(date))
end

-- TYPE fixed-date -> rational-angle
-- Sidereal daily motion of sun on date.
function M.hindu_daily_motion(date)
    local mean_motion = (M.deg(360) / M.HINDU_SIDEREAL_YEAR)
    local anomaly = M.hindu_mean_position(date, M.HINDU_ANOMALISTIC_YEAR)
    local epicycle = ((14 / 360) - (math.abs(M.hindu_sine(anomaly)) / 1080))
    local entry = M.quotient(anomaly, M.angle(0, 225, 0))
    local sine_table_step = (M.hindu_sine_table((entry + 1)) - M.hindu_sine_table(entry))
    local factor = ((-3438 / 225) * sine_table_step * epicycle)
    return (mean_motion * (factor + 1))
end

-- TYPE fixed-date -> rational-amplitude
-- Tabulated speed of rising of current zodiacal sign on
-- date.
function M.hindu_rising_sign(date)
    local i = M.quotient(M.hindu_tropical_longitude(date), M.deg(30))
    return _nth((i % 6), _list((1670 / 1800), (1795 / 1800), (1935 / 1800), (1935 / 1800), (1795 / 1800), (1670 / 1800)))
end

-- TYPE fixed-date -> rational-moment
-- Time from true to mean midnight of date.
-- (This is a gross approximation to the correct value.)
function M.hindu_equation_of_time(date)
    local offset = M.hindu_sine(M.hindu_mean_position(date, M.HINDU_ANOMALISTIC_YEAR))
    local equation_sun = (offset * M.angle(57, 18, 0) * ((14 / 360) - (math.abs(offset) / 1080)))
    return ((M.hindu_daily_motion(date) / M.deg(360)) * (equation_sun / M.deg(360)) * M.HINDU_SIDEREAL_YEAR)
end

-- TYPE fixed-date -> rational-moment
-- Sunrise at hindu-location on date.
function M.hindu_sunrise(date)
    return (date + M.hr(6) + ((M.longitude(M.UJJAIN) - M.longitude(M.HINDU_LOCATION)) / M.deg(360)) + (-M.hindu_equation_of_time(date)) + (((1577917828 / 1582237828) / M.deg(360)) * (M.hindu_ascensional_difference(date, M.HINDU_LOCATION) + ((1 / 4) * M.hindu_solar_sidereal_difference(date)))))
end

-- TYPE fixed-date -> rational-moment
-- Sunset at hindu-location on date.
function M.hindu_sunset(date)
    return (date + M.hr(18) + ((M.longitude(M.UJJAIN) - M.longitude(M.HINDU_LOCATION)) / M.deg(360)) + (-M.hindu_equation_of_time(date)) + (((1577917828 / 1582237828) / M.deg(360)) * ((-M.hindu_ascensional_difference(date, M.HINDU_LOCATION)) + ((3 / 4) * M.hindu_solar_sidereal_difference(date)))))
end

-- TYPE rational-moment -> rational-moment
-- Hindu local time of temporal moment tee.
function M.hindu_standard_from_sundial(tee)
    local date = M.fixed_from_moment(tee)
    local time = M.time_from_moment(tee)
    local q = math.floor((4 * time))
    local a = (function() if _truth(((q == 0))) then return M.hindu_sunset((date - 1)) elseif _truth(((q == 3))) then return M.hindu_sunset(date) else return M.hindu_sunrise(date) end end)()
    local b = (function() if _truth(((q == 0))) then return M.hindu_sunrise(date) elseif _truth(((q == 3))) then return M.hindu_sunrise((date + 1)) else return M.hindu_sunset(date) end end)()
    return (a + (2 * (b - a) * (time - (function() if _truth(((q == 3))) then return M.hr(18) elseif _truth(((q == 0))) then return M.hr(-6) else return M.hr(6) end end)())))
end

-- TYPE fixed-date -> hindu-lunar-date
-- Hindu lunar date, full-moon scheme,
-- equivalent to fixed date.
function M.hindu_fullmoon_from_fixed(date)
    local l_date = M.hindu_lunar_from_fixed(date)
    local year = M.hindu_lunar_year(l_date)
    local month = M.hindu_lunar_month(l_date)
    local leap_month = M.hindu_lunar_leap_month(l_date)
    local day = M.hindu_lunar_day(l_date)
    local leap_day = M.hindu_lunar_leap_day(l_date)
    local m = (function() if _truth(((day >= 16))) then return M.hindu_lunar_month(M.hindu_lunar_from_fixed((date + 20))) else return month end end)()
    return M.hindu_lunar_date(year, m, leap_month, day, leap_day)
end

-- TYPE hindu-lunar-date -> fixed-date
-- Fixed date equivalent to Hindu lunar l-date
-- in full-moon scheme.
function M.fixed_from_hindu_fullmoon(l_date)
    local year = M.hindu_lunar_year(l_date)
    local month = M.hindu_lunar_month(l_date)
    local leap_month = M.hindu_lunar_leap_month(l_date)
    local day = M.hindu_lunar_day(l_date)
    local leap_day = M.hindu_lunar_leap_day(l_date)
    local m = (function() if _truth((_truth(leap_month) or _truth(((day <= 15))))) then return month elseif _truth(M.hindu_expunged_p(year, M.amod((month - 1), 12))) then return M.amod((month - 2), 12) else return M.amod((month - 1), 12) end end)()
    return M.fixed_from_hindu_lunar(M.hindu_lunar_date(year, m, leap_month, day, leap_day))
end

-- TYPE (hindu-lunar-year hindu-lunar-month) ->
-- TYPE boolean
-- True of Hindu lunar month l-month in l-year
-- is expunged.
function M.hindu_expunged_p(l_year, l_month)
    return ((l_month ~= M.hindu_lunar_month(M.hindu_lunar_from_fixed(M.fixed_from_hindu_lunar(_list(l_year, l_month, false, 15, false))))))
end

-- TYPE fixed-date -> rational-moment
-- Astronomical sunrise at Hindu location on date,
-- per Lahiri,
-- rounded to nearest minute, as a rational number.
function M.alt_hindu_sunrise(date)
    local rise = M.dawn(date, M.HINDU_LOCATION, M.angle(0, 47, 0))
    return ((1 / 24) * (1 / 60) * _round((rise * 24 * 60)))
end

-- TYPE moment -> angle
-- Difference between tropical and sidereal solar longitude.
function M.ayanamsha(tee)
    return (M.solar_longitude(tee) - M.sidereal_solar_longitude(tee))
end

-- TYPE (season moment) -> moment
-- Moment of the first time at or after tee
-- when Hindu solar longitude will be lambda degrees.
function M.hindu_solar_longitude_at_or_after(lambda, tee)
    local tau = (tee + (M.HINDU_SIDEREAL_YEAR * (1 / M.deg(360)) * ((lambda - M.hindu_solar_longitude(tee)) % 360)))
    local a = math.max(tee, (tau - 5))
    local b = (tau + 5)
    return M.invert_angular(M.hindu_solar_longitude, lambda, M.interval_closed(a, b))
end

-- TYPE gregorian-year -> rational-moment
-- Fixed moment of Mesha samkranti (Vernal equinox)
-- in Gregorian g-year.
function M.mesha_samkranti(g_year)
    local jan1 = M.gregorian_new_year(g_year)
    return M.hindu_solar_longitude_at_or_after(M.deg(0), jan1)
end

-- TYPE angle
M.SIDEREAL_START = M.precession(M.universal_from_local(M.mesha_samkranti(M.ce(285)), M.HINDU_LOCATION))

-- TYPE fixed-date -> moment
-- Geometrical sunset at Hindu location on date.
function M.astro_hindu_sunset(date)
    return M.dusk(date, M.HINDU_LOCATION, M.deg(0))
end

-- TYPE moment -> hindu-solar-month
-- Sidereal zodiacal sign of the sun, as integer in range
-- 1..12, at moment tee.
function M.sidereal_zodiac(tee)
    return (M.quotient(M.sidereal_solar_longitude(tee), M.deg(30)) + 1)
end

-- TYPE moment -> hindu-solar-year
-- Astronomical Hindu solar year KY at given moment tee.
function M.astro_hindu_calendar_year(tee)
    return _round((((tee - M.HINDU_EPOCH) / M.MEAN_SIDEREAL_YEAR) - (M.sidereal_solar_longitude(tee) / M.deg(360))))
end

-- TYPE fixed-date -> hindu-solar-date
-- Astronomical Hindu (Tamil) solar date equivalent to
-- fixed date.
function M.astro_hindu_solar_from_fixed(date)
    local critical = M.astro_hindu_sunset(date)
    local month = M.sidereal_zodiac(critical)
    local year = (M.astro_hindu_calendar_year(critical) - M.HINDU_SOLAR_ERA)
    local approx = (date - 3 - (math.floor(M.sidereal_solar_longitude(critical)) % M.deg(30)))
    local start = M.next(approx, function(i) return _truth(((M.sidereal_zodiac(M.astro_hindu_sunset(i)) == month))) end)
    local day = (date - start - -1)
    return M.hindu_solar_date(year, month, day)
end

-- TYPE hindu-solar-date -> fixed-date
-- Fixed date corresponding to Astronomical
-- Hindu solar date (Tamil rule; Saka era).
function M.fixed_from_astro_hindu_solar(s_date)
    local month = M.standard_month(s_date)
    local day = M.standard_day(s_date)
    local year = M.standard_year(s_date)
    local approx = (M.HINDU_EPOCH + -3 + math.floor((((year + M.HINDU_SOLAR_ERA) + ((month - 1) / 12)) * M.MEAN_SIDEREAL_YEAR)))
    local start = M.next(approx, function(i) return _truth(((M.sidereal_zodiac(M.astro_hindu_sunset(i)) == month))) end)
    return (start + day + -1)
end

-- TYPE moment -> hindu-lunar-day
-- Phase of moon (tithi) at moment tee, as an integer in
-- the range 1..30.
function M.astro_lunar_day_from_moment(tee)
    return (M.quotient(M.lunar_phase(tee), M.deg(12)) + 1)
end

-- TYPE fixed-date -> hindu-lunar-date
-- Astronomical Hindu lunar date equivalent to fixed date.
function M.astro_hindu_lunar_from_fixed(date)
    local critical = M.alt_hindu_sunrise(date)
    local day = M.astro_lunar_day_from_moment(critical)
    local leap_day = ((day == M.astro_lunar_day_from_moment(M.alt_hindu_sunrise((date - 1)))))
    local last_new_moon = M.new_moon_before(critical)
    local next_new_moon = M.new_moon_at_or_after(critical)
    local solar_month = M.sidereal_zodiac(last_new_moon)
    local leap_month = ((solar_month == M.sidereal_zodiac(next_new_moon)))
    local month = M.amod((solar_month + 1), 12)
    local year = (M.astro_hindu_calendar_year((function() if _truth(((month <= 2))) then return (date + 180) else return date end end)()) - M.HINDU_LUNAR_ERA)
    return M.hindu_lunar_date(year, month, leap_month, day, leap_day)
end

-- TYPE hindu-lunar-date -> fixed-date
-- Fixed date corresponding to Hindu lunar date l-date.
function M.fixed_from_astro_hindu_lunar(l_date)
    local year = M.hindu_lunar_year(l_date)
    local month = M.hindu_lunar_month(l_date)
    local leap_month = M.hindu_lunar_leap_month(l_date)
    local day = M.hindu_lunar_day(l_date)
    local leap_day = M.hindu_lunar_leap_day(l_date)
    local approx = (M.HINDU_EPOCH + (M.MEAN_SIDEREAL_YEAR * (year + M.HINDU_LUNAR_ERA + ((month - 1) / 12))))
    local s = math.floor((approx - (M.HINDU_SIDEREAL_YEAR * M.mod3(((M.sidereal_solar_longitude(approx) / M.deg(360)) - ((month - 1) / 12)), (-1 / 2), (1 / 2)))))
    local k = M.astro_lunar_day_from_moment((s + M.hr(6)))
    local est = (s - (-day) - (function() if _truth(((3 < k) and (k < 27))) then return k elseif _truth((function() local mid = M.astro_hindu_lunar_from_fixed((s - 15)); return (_truth(((M.hindu_lunar_month(mid) ~= month))) or _truth((_truth(M.hindu_lunar_leap_month(mid)) and _truth((not _truth(leap_month)))))) end)()) then return M.mod3(k, -15, 15) else return M.mod3(k, 15, 45) end end)())
    local tau = (est - M.mod3((M.astro_lunar_day_from_moment((est + M.hr(6))) - day), -15, 15))
    local date = M.next((tau - 1), function(d) return _truth(_member(M.astro_lunar_day_from_moment(M.alt_hindu_sunrise(d)), _list(day, M.amod((day + 1), 30)))) end)
    if _truth(leap_day) then
        return (date + 1)
    else
        return date
    end
end

-- TYPE (rational rational-moment) -> rational-moment
-- Time lunar-day (tithi) number k begins at or after
-- moment tee. k can be fractional (for karanas).
function M.hindu_lunar_day_at_or_after(k, tee)
    local phase = ((k - 1) * M.deg(12))
    local tau = (tee + ((1 / M.deg(360)) * ((phase - M.hindu_lunar_phase(tee)) % 360) * M.HINDU_SYNODIC_MONTH))
    local a = math.max(tee, (tau - 2))
    local b = (tau + 2)
    return M.invert_angular(M.hindu_lunar_phase, phase, M.interval_closed(a, b))
end

-- TYPE gregorian-year -> fixed-date
-- Fixed date of Hindu lunisolar new year in Gregorian
-- g-year.
function M.hindu_lunar_new_year(g_year)
    local jan1 = M.gregorian_new_year(g_year)
    local mina = M.hindu_solar_longitude_at_or_after(M.deg(330), jan1)
    local new_moon = M.hindu_lunar_day_at_or_after(1, mina)
    local h_day = math.floor(new_moon)
    local critical = M.hindu_sunrise(h_day)
    return (h_day + (function() if _truth((_truth(((new_moon < critical))) or _truth(((M.hindu_lunar_day_from_moment(M.hindu_sunrise((h_day + 1))) == 2))))) then return 0 else return 1 end end)())
end

-- TYPE (hindu-lunar-date hindu-lunar-date) -> boolean
-- True if Hindu lunar date l-date1 is on or before
-- Hindu lunar date l-date2.
function M.hindu_lunar_on_or_before_p(l_date1, l_date2)
    local month1 = M.hindu_lunar_month(l_date1)
    local month2 = M.hindu_lunar_month(l_date2)
    local leap1 = M.hindu_lunar_leap_month(l_date1)
    local leap2 = M.hindu_lunar_leap_month(l_date2)
    local day1 = M.hindu_lunar_day(l_date1)
    local day2 = M.hindu_lunar_day(l_date2)
    local leap_day1 = M.hindu_lunar_leap_day(l_date1)
    local leap_day2 = M.hindu_lunar_leap_day(l_date2)
    local year1 = M.hindu_lunar_year(l_date1)
    local year2 = M.hindu_lunar_year(l_date2)
    return (_truth(((year1 < year2))) or _truth((_truth(((year1 == year2))) and _truth((_truth(((month1 < month2))) or _truth((_truth(((month1 == month2))) and _truth((_truth((_truth(leap1) and _truth((not _truth(leap2))))) or _truth((_truth(_equal(leap1, leap2)) and _truth((_truth(((day1 < day2))) or _truth((_truth(((day1 == day2))) and _truth((_truth((not _truth(leap_day1))) or _truth(leap_day2))))))))))))))))))
end

-- TYPE (hindu-lunar-year hindu-lunar-month
-- TYPE hindu-lunar-day) -> fixed-date
-- Fixed date of occurrence of Hindu lunar l-month,
-- l-day in Hindu lunar year l-year, taking leap and
-- expunged days into account. When the month is
-- expunged, then the following month is used.
function M.hindu_date_occur(l_year, l_month, l_day)
    local lunar = M.hindu_lunar_date(l_year, l_month, false, l_day, false)
    local try = M.fixed_from_hindu_lunar(lunar)
    local mid = M.hindu_lunar_from_fixed((function() if _truth(((l_day > 15))) then return (try - 5) else return try end end)())
    local expunged_p = ((l_month ~= M.hindu_lunar_month(mid)))
    local l_date = M.hindu_lunar_date(M.hindu_lunar_year(mid), M.hindu_lunar_month(mid), M.hindu_lunar_leap_month(mid), l_day, false)
    if _truth(expunged_p) then
        return (M.next(try, function(d) return _truth((not _truth(M.hindu_lunar_on_or_before_p(M.hindu_lunar_from_fixed(d), l_date)))) end) - 1)
    elseif _truth(((l_day ~= M.hindu_lunar_day(M.hindu_lunar_from_fixed(try))))) then
        return (try - 1)
    else
        return try
    end
end

-- TYPE (hindu-lunar-month hindu-lunar-day
-- TYPE gregorian-year) -> list-of-fixed-dates
-- List of fixed dates of occurrences of Hindu lunar
-- month, day in Gregorian year g-year.
function M.hindu_lunar_holiday(l_month, l_day, g_year)
    local l_year = M.hindu_lunar_year(M.hindu_lunar_from_fixed(M.gregorian_new_year(g_year)))
    local date0 = M.hindu_date_occur(l_year, l_month, l_day)
    local date1 = M.hindu_date_occur((l_year + 1), l_month, l_day)
    return M.list_range(_list(date0, date1), M.gregorian_year_range(g_year))
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of fixed date(s) of Diwali in Gregorian year
-- g-year.
function M.diwali(g_year)
    return M.hindu_lunar_holiday(8, 1, g_year)
end

-- TYPE (hindu-lunar-month rational rational
-- TYPE hindu-lunar-year) -> fixed-date
-- Fixed date of occurrence of Hindu lunar tithi prior
-- to sundial time tee, in Hindu lunar l-month, l-year.
function M.hindu_tithi_occur(l_month, tithi, tee, l_year)
    local approx = M.hindu_date_occur(l_year, l_month, math.floor(tithi))
    local lunar = M.hindu_lunar_day_at_or_after(tithi, (approx - 2))
    local try = M.fixed_from_moment(lunar)
    local tee_h = M.standard_from_sundial((try + tee), M.UJJAIN)
    if _truth((_truth(((lunar <= tee_h))) or _truth(((M.hindu_lunar_phase(M.standard_from_sundial((try + 1 + tee), M.UJJAIN)) > (12 * tithi)))))) then
        return try
    else
        return (try + 1)
    end
end

-- TYPE (hindu-lunar-month rational rational
-- TYPE gregorian-year) -> list-of-fixed-dates
-- List of fixed dates of occurrences of Hindu lunar tithi
-- prior to sundial time tee, in Hindu lunar l-month,
-- in Gregorian year g-year.
function M.hindu_lunar_event(l_month, tithi, tee, g_year)
    local l_year = M.hindu_lunar_year(M.hindu_lunar_from_fixed(M.gregorian_new_year(g_year)))
    local date0 = M.hindu_tithi_occur(l_month, tithi, tee, l_year)
    local date1 = M.hindu_tithi_occur(l_month, tithi, tee, (l_year + 1))
    return M.list_range(_list(date0, date1), M.gregorian_year_range(g_year))
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of fixed date(s) of Night of Shiva in Gregorian
-- year g-year.
function M.shiva(g_year)
    return M.hindu_lunar_event(11, 29, M.hr(24), g_year)
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of fixed date(s) of Rama’s Birthday in Gregorian
-- year g-year.
function M.rama(g_year)
    return M.hindu_lunar_event(1, 9, M.hr(12), g_year)
end

-- TYPE fixed-date -> nakshatra
-- Hindu lunar station (nakshatra) at sunrise on date.
function M.hindu_lunar_station(date)
    local critical = M.hindu_sunrise(date)
    return (M.quotient(M.hindu_lunar_longitude(critical), M.angle(0, 800, 0)) + 1)
end

-- TYPE 1-60 -> 0-10
-- Number (0-10) of the name of the n-th (1-60) Hindu
-- karana.
function M.karana(n)
    if _truth(((n == 1))) then
        return 0
    elseif _truth(((n > 57))) then
        return (n - 50)
    else
        return M.amod((n - 1), 7)
    end
end

-- TYPE fixed-date -> 1-27
-- Hindu yoga on date.
function M.yoga(date)
    return (math.floor((((M.hindu_solar_longitude(date) + M.hindu_lunar_longitude(date)) / M.angle(0, 800, 0)) % 27)) + 1)
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of Wednesdays in Gregorian year g-year
-- that are day 8 of Hindu lunar months.
function M.sacred_wednesdays(g_year)
    return M.sacred_wednesdays_in_range(M.gregorian_year_range(g_year))
end

-- TYPE range -> list-of-fixed-dates
-- List of Wednesdays within range of dates
-- that are day 8 of Hindu lunar months.
function M.sacred_wednesdays_in_range(range)
    local a = M.begin(range)
    local b = M.end_(range)
    local wed = M.kday_on_or_after(M.WEDNESDAY, a)
    local h_date = M.hindu_lunar_from_fixed(wed)
    if _truth(M.in_range_p(wed, range)) then
        return _append((function() if _truth(((M.hindu_lunar_day(h_date) == 8))) then return _list(wed) else return NIL end end)(), M.sacred_wednesdays_in_range(M.interval((wed + 1), b)))
    else
        return NIL
    end
end


----- D.21 The Tibetan Calendar -----

-- TYPE (tibetan-year tibetan-month
-- TYPE tibetan-leap-month tibetan-day
-- TYPE tibetan-leap-day) -> tibetan-date
function M.tibetan_date(year, month, leap_month, day, leap_day)
    return _list(year, month, leap_month, day, leap_day)
end

-- TYPE tibetan-date -> tibetan-year
function M.tibetan_year(date)
    return _nth(0, date)
end

-- TYPE tibetan-date -> tibetan-month
function M.tibetan_month(date)
    return _nth(1, date)
end

-- TYPE tibetan-date -> tibetan-leap-month
function M.tibetan_leap_month(date)
    return _nth(2, date)
end

-- TYPE tibetan-date -> tibetan-day
function M.tibetan_day(date)
    return _nth(3, date)
end

-- TYPE tibetan-date -> tibetan-leap-day
function M.tibetan_leap_day(date)
    return _nth(4, date)
end

-- TYPE fixed-date
M.TIBETAN_EPOCH = M.fixed_from_gregorian(M.gregorian_date(-127, M.DECEMBER, 7))

-- TYPE rational-angle -> rational
-- Interpolated tabular sine of solar anomaly alpha.
function M.tibetan_sun_equation(alpha)
    if _truth(((alpha > 6))) then
        return (-M.tibetan_sun_equation((alpha - 6)))
    elseif _truth(((alpha > 3))) then
        return M.tibetan_sun_equation((6 - alpha))
    elseif _truth(_integerp(alpha)) then
        return _nth(alpha, _list(M.mins(0), M.mins(6), M.mins(10), M.mins(11)))
    else
        return (((alpha % 1) * M.tibetan_sun_equation(math.ceil(alpha))) + (((-alpha) % 1) * M.tibetan_sun_equation(math.floor(alpha))))
    end
end

-- TYPE rational-angle -> rational
-- Interpolated tabular sine of lunar anomaly alpha.
function M.tibetan_moon_equation(alpha)
    if _truth(((alpha > 14))) then
        return (-M.tibetan_moon_equation((alpha - 14)))
    elseif _truth(((alpha > 7))) then
        return M.tibetan_moon_equation((14 - alpha))
    elseif _truth(_integerp(alpha)) then
        return _nth(alpha, _list(M.mins(0), M.mins(5), M.mins(10), M.mins(15), M.mins(19), M.mins(22), M.mins(24), M.mins(25)))
    else
        return (((alpha % 1) * M.tibetan_moon_equation(math.ceil(alpha))) + (((-alpha) % 1) * M.tibetan_moon_equation(math.floor(alpha))))
    end
end

-- TYPE tibetan-date -> fixed-date
-- Fixed date corresponding to Tibetan lunar date t-date.
function M.fixed_from_tibetan(t_date)
    local year = M.tibetan_year(t_date)
    local month = M.tibetan_month(t_date)
    local leap_month = M.tibetan_leap_month(t_date)
    local day = M.tibetan_day(t_date)
    local leap_day = M.tibetan_leap_day(t_date)
    local months = math.floor((((804 / 65) * (year - 1)) + ((67 / 65) * month) + (function() if _truth(leap_month) then return -1 else return 0 end end)() + (64 / 65)))
    local days = ((30 * months) + day)
    local mean = ((days * (11135 / 11312)) + -30 + (function() if _truth(leap_day) then return 0 else return -1 end end)() + (1071 / 1616))
    local solar_anomaly = (((days * (13 / 4824)) + (2117 / 4824)) % 1)
    local lunar_anomaly = (((days * (3781 / 105840)) + (2837 / 15120)) % 1)
    local sun = (-M.tibetan_sun_equation((12 * solar_anomaly)))
    local moon = M.tibetan_moon_equation((28 * lunar_anomaly))
    return math.floor((M.TIBETAN_EPOCH + mean + sun + moon))
end

-- TYPE fixed-date -> tibetan-date
-- Tibetan lunar date corresponding to fixed date.
function M.tibetan_from_fixed(date)
    local cap_Y = (365 + (4975 / 18382))
    local years = math.ceil(((date - M.TIBETAN_EPOCH) / cap_Y))
    local year0 = M.final(years, function(y) return _truth(((date >= M.fixed_from_tibetan(M.tibetan_date(y, 1, false, 1, false))))) end)
    local month0 = M.final(1, function(m) return _truth(((date >= M.fixed_from_tibetan(M.tibetan_date(year0, m, false, 1, false))))) end)
    local est = (date - M.fixed_from_tibetan(M.tibetan_date(year0, month0, false, 1, false)))
    local day0 = M.final((est - 2), function(d) return _truth(((date >= M.fixed_from_tibetan(M.tibetan_date(year0, month0, false, d, false))))) end)
    local leap_month = ((day0 > 30))
    local day = M.amod(day0, 30)
    local month = M.amod((function() if _truth(((day > day0))) then return (month0 - 1) elseif _truth(leap_month) then return (month0 + 1) else return month0 end end)(), 12)
    local year = (function() if _truth((_truth(((day > day0))) and _truth(((month0 == 1))))) then return (year0 - 1) elseif _truth((_truth(leap_month) and _truth(((month0 == 12))))) then return (year0 + 1) else return year0 end end)()
    local leap_day = ((date == M.fixed_from_tibetan(M.tibetan_date(year, month, leap_month, day, true))))
    return M.tibetan_date(year, month, leap_month, day, leap_day)
end

-- TYPE (tibetan-year tibetan-month) -> boolean
-- True if t-month is leap in Tibetan year t-year.
function M.tibetan_leap_month_p(t_year, t_month)
    return ((t_month == M.tibetan_month(M.tibetan_from_fixed(M.fixed_from_tibetan(M.tibetan_date(t_year, t_month, true, 2, false))))))
end

-- TYPE (tibetan-year tibetan-month tibetan-day) -> boolean
-- True if t-day is leap in Tibetan
-- month t-month and year t-year.
function M.tibetan_leap_day_p(t_year, t_month, t_day)
    return (_truth(((t_day == M.tibetan_day(M.tibetan_from_fixed(M.fixed_from_tibetan(M.tibetan_date(t_year, t_month, false, t_day, true))))))) or _truth(((t_day == M.tibetan_day(M.tibetan_from_fixed(M.fixed_from_tibetan(M.tibetan_date(t_year, t_month, M.tibetan_leap_month_p(t_year, t_month), t_day, true))))))))
end

-- TYPE tibetan-year -> fixed-date
-- Fixed date of Tibetan New Year (Losar)
-- in Tibetan year t-year.
function M.losar(t_year)
    local t_leap = M.tibetan_leap_month_p(t_year, 1)
    return M.fixed_from_tibetan(M.tibetan_date(t_year, 1, t_leap, 1, false))
end

-- TYPE gregorian-year -> list-of-fixed-dates
-- List of fixed dates of Tibetan New Year in
-- Gregorian year g-year.
function M.tibetan_new_year(g_year)
    local dec31 = M.gregorian_year_end(g_year)
    local t_year = M.tibetan_year(M.tibetan_from_fixed(dec31))
    return M.list_range(_list(M.losar((t_year - 1)), M.losar(t_year)), M.gregorian_year_range(g_year))
end


----- MediaWiki public interface -----

p.convert = make_public(M.convert)
p.day_of_week_from_fixed = make_public(M.day_of_week_from_fixed)
p.rd = make_public(M.rd)
p.moment_from_jd = make_public(M.moment_from_jd)
p.jd_from_moment = make_public(M.jd_from_moment)
p.fixed_from_mjd = make_public(M.fixed_from_mjd)
p.mjd_from_fixed = make_public(M.mjd_from_fixed)
p.moment_from_unix = make_public(M.moment_from_unix)
p.unix_from_moment = make_public(M.unix_from_moment)
p.fixed_from_jd = make_public(M.fixed_from_jd)
p.jd_from_fixed = make_public(M.jd_from_fixed)
p.quotient = make_public(M.quotient)
p.amod = make_public(M.amod)
p.mod3 = make_public(M.mod3)
p.poly = make_public(M.poly)
p.standard_month = make_public(M.standard_month)
p.standard_day = make_public(M.standard_day)
p.standard_year = make_public(M.standard_year)
p.hour = make_public(M.hour)
p.minute = make_public(M.minute)
p.seconds = make_public(M.seconds)
p.time_of_day = make_public(M.time_of_day)
p.fixed_from_moment = make_public(M.fixed_from_moment)
p.sign = make_public(M.sign)
p.time_from_moment = make_public(M.time_from_moment)
p.list_of_fixed_from_moments = make_public(M.list_of_fixed_from_moments)
p.interval = make_public(M.interval)
p.interval_closed = make_public(M.interval_closed)
p.begin = make_public(M.begin)
p.end_ = make_public(M.end_)
p.in_range_p = make_public(M.in_range_p)
p.list_range = make_public(M.list_range)
p.positions_in_range = make_public(M.positions_in_range)
p.from_radix = make_public(M.from_radix)
p.to_radix = make_public(M.to_radix)
p.time_from_clock = make_public(M.time_from_clock, false)
p.clock_from_moment = make_public(M.clock_from_moment)
p.angle_from_degrees = make_public(M.angle_from_degrees, false)
p.egyptian_date = make_public(M.egyptian_date)
p.fixed_from_egyptian = make_public(M.fixed_from_egyptian, false)
p.alt_fixed_from_egyptian = make_public(M.alt_fixed_from_egyptian)
p.egyptian_from_fixed = make_public(M.egyptian_from_fixed)
p.armenian_date = make_public(M.armenian_date)
p.fixed_from_armenian = make_public(M.fixed_from_armenian, false)
p.armenian_from_fixed = make_public(M.armenian_from_fixed)
p.kday_on_or_before = make_public(M.kday_on_or_before)
p.kday_on_or_after = make_public(M.kday_on_or_after)
p.kday_nearest = make_public(M.kday_nearest)
p.kday_before = make_public(M.kday_before)
p.kday_after = make_public(M.kday_after)
p.akan_day_name = make_public(M.akan_day_name)
p.akan_name = make_public(M.akan_name)
p.akan_prefix = make_public(M.akan_prefix)
p.akan_stem = make_public(M.akan_stem)
p.akan_name_difference = make_public(M.akan_name_difference)
p.akan_name_from_fixed = make_public(M.akan_name_from_fixed)
p.akan_day_name_on_or_before = make_public(M.akan_day_name_on_or_before)
p.gregorian_date = make_public(M.gregorian_date)
p.gregorian_leap_year_p = make_public(M.gregorian_leap_year_p)
p.fixed_from_gregorian = make_public(M.fixed_from_gregorian, false)
p.gregorian_new_year = make_public(M.gregorian_new_year)
p.gregorian_year_end = make_public(M.gregorian_year_end)
p.gregorian_year_range = make_public(M.gregorian_year_range)
p.gregorian_year_from_fixed = make_public(M.gregorian_year_from_fixed)
p.gregorian_from_fixed = make_public(M.gregorian_from_fixed)
p.gregorian_date_difference = make_public(M.gregorian_date_difference)
p.day_number = make_public(M.day_number, false)
p.days_remaining = make_public(M.days_remaining, false)
p.last_day_of_gregorian_month = make_public(M.last_day_of_gregorian_month)
p.alt_fixed_from_gregorian = make_public(M.alt_fixed_from_gregorian, false)
p.alt_gregorian_from_fixed = make_public(M.alt_gregorian_from_fixed)
p.alt_gregorian_year_from_fixed = make_public(M.alt_gregorian_year_from_fixed)
p.independence_day = make_public(M.independence_day)
p.nth_kday = make_public(M.nth_kday)
p.first_kday = make_public(M.first_kday)
p.last_kday = make_public(M.last_kday)
p.labor_day = make_public(M.labor_day)
p.memorial_day = make_public(M.memorial_day)
p.election_day = make_public(M.election_day)
p.daylight_saving_start = make_public(M.daylight_saving_start)
p.daylight_saving_end = make_public(M.daylight_saving_end)
p.christmas = make_public(M.christmas)
p.advent = make_public(M.advent)
p.epiphany = make_public(M.epiphany)
p.unlucky_fridays_in_range = make_public(M.unlucky_fridays_in_range)
p.unlucky_fridays = make_public(M.unlucky_fridays)
p.bce = make_public(M.bce)
p.ce = make_public(M.ce)
p.julian_date = make_public(M.julian_date)
p.julian_leap_year_p = make_public(M.julian_leap_year_p)
p.fixed_from_julian = make_public(M.fixed_from_julian, false)
p.julian_from_fixed = make_public(M.julian_from_fixed)
p.roman_date = make_public(M.roman_date)
p.roman_year = make_public(M.roman_year)
p.roman_month = make_public(M.roman_month)
p.roman_event = make_public(M.roman_event)
p.roman_count = make_public(M.roman_count)
p.roman_leap = make_public(M.roman_leap)
p.ides_of_month = make_public(M.ides_of_month)
p.nones_of_month = make_public(M.nones_of_month)
p.fixed_from_roman = make_public(M.fixed_from_roman, false)
p.roman_from_fixed = make_public(M.roman_from_fixed)
p.julian_year_from_auc = make_public(M.julian_year_from_auc)
p.auc_year_from_julian = make_public(M.auc_year_from_julian)
p.olympiad = make_public(M.olympiad)
p.olympiad_cycle = make_public(M.olympiad_cycle)
p.olympiad_year = make_public(M.olympiad_year)
p.julian_year_from_olympiad = make_public(M.julian_year_from_olympiad)
p.olympiad_from_julian_year = make_public(M.olympiad_from_julian_year)
p.cycle_in_gregorian = make_public(M.cycle_in_gregorian)
p.julian_season_in_gregorian = make_public(M.julian_season_in_gregorian)
p.julian_in_gregorian = make_public(M.julian_in_gregorian)
p.eastern_orthodox_christmas = make_public(M.eastern_orthodox_christmas)
p.coptic_date = make_public(M.coptic_date)
p.coptic_leap_year_p = make_public(M.coptic_leap_year_p)
p.fixed_from_coptic = make_public(M.fixed_from_coptic, false)
p.coptic_from_fixed = make_public(M.coptic_from_fixed)
p.ethiopic_date = make_public(M.ethiopic_date)
p.fixed_from_ethiopic = make_public(M.fixed_from_ethiopic, false)
p.ethiopic_from_fixed = make_public(M.ethiopic_from_fixed)
p.coptic_in_gregorian = make_public(M.coptic_in_gregorian)
p.coptic_christmas = make_public(M.coptic_christmas)
p.iso_date = make_public(M.iso_date)
p.iso_week = make_public(M.iso_week)
p.iso_day = make_public(M.iso_day)
p.iso_year = make_public(M.iso_year)
p.fixed_from_iso = make_public(M.fixed_from_iso, false)
p.iso_from_fixed = make_public(M.iso_from_fixed)
p.iso_long_year_p = make_public(M.iso_long_year_p)
p.icelandic_date = make_public(M.icelandic_date)
p.icelandic_year = make_public(M.icelandic_year)
p.icelandic_season = make_public(M.icelandic_season)
p.icelandic_week = make_public(M.icelandic_week)
p.icelandic_weekday = make_public(M.icelandic_weekday)
p.icelandic_summer = make_public(M.icelandic_summer)
p.icelandic_winter = make_public(M.icelandic_winter)
p.fixed_from_icelandic = make_public(M.fixed_from_icelandic, false)
p.icelandic_from_fixed = make_public(M.icelandic_from_fixed)
p.icelandic_leap_year_p = make_public(M.icelandic_leap_year_p)
p.icelandic_month = make_public(M.icelandic_month)
p.icelandic_month_from_fixed = make_public(M.icelandic_month_from_fixed)
p.islamic_date = make_public(M.islamic_date)
p.islamic_leap_year_p = make_public(M.islamic_leap_year_p)
p.fixed_from_islamic = make_public(M.fixed_from_islamic, false)
p.islamic_from_fixed = make_public(M.islamic_from_fixed)
p.islamic_in_gregorian = make_public(M.islamic_in_gregorian)
p.mawlid = make_public(M.mawlid)
p.hebrew_date = make_public(M.hebrew_date)
p.hebrew_leap_year_p = make_public(M.hebrew_leap_year_p)
p.last_month_of_hebrew_year = make_public(M.last_month_of_hebrew_year)
p.hebrew_sabbatical_year_p = make_public(M.hebrew_sabbatical_year_p)
p.molad = make_public(M.molad)
p.hebrew_calendar_elapsed_days = make_public(M.hebrew_calendar_elapsed_days)
p.hebrew_year_length_correction = make_public(M.hebrew_year_length_correction)
p.hebrew_new_year = make_public(M.hebrew_new_year)
p.last_day_of_hebrew_month = make_public(M.last_day_of_hebrew_month)
p.long_marheshvan_p = make_public(M.long_marheshvan_p)
p.short_kislev_p = make_public(M.short_kislev_p)
p.days_in_hebrew_year = make_public(M.days_in_hebrew_year)
p.fixed_from_hebrew = make_public(M.fixed_from_hebrew, false)
p.hebrew_from_fixed = make_public(M.hebrew_from_fixed)
p.fixed_from_molad = make_public(M.fixed_from_molad)
p.yom_kippur = make_public(M.yom_kippur)
p.passover = make_public(M.passover)
p.omer = make_public(M.omer)
p.purim = make_public(M.purim)
p.ta_anit_esther = make_public(M.ta_anit_esther)
p.tishah_be_av = make_public(M.tishah_be_av)
p.yom_ha_zikkaron = make_public(M.yom_ha_zikkaron)
p.sh_ela = make_public(M.sh_ela)
p.birkath_ha_hama = make_public(M.birkath_ha_hama)
p.samuel_season_in_gregorian = make_public(M.samuel_season_in_gregorian)
p.alt_birkath_ha_hama = make_public(M.alt_birkath_ha_hama)
p.adda_season_in_gregorian = make_public(M.adda_season_in_gregorian)
p.hebrew_in_gregorian = make_public(M.hebrew_in_gregorian)
p.hanukkah = make_public(M.hanukkah)
p.hebrew_birthday = make_public(M.hebrew_birthday)
p.hebrew_birthday_in_gregorian = make_public(M.hebrew_birthday_in_gregorian)
p.yahrzeit = make_public(M.yahrzeit)
p.yahrzeit_in_gregorian = make_public(M.yahrzeit_in_gregorian)
p.shift_days = make_public(M.shift_days)
p.possible_hebrew_days = make_public(M.possible_hebrew_days)
p.orthodox_easter = make_public(M.orthodox_easter)
p.alt_orthodox_easter = make_public(M.alt_orthodox_easter)
p.easter = make_public(M.easter)
p.pentecost = make_public(M.pentecost)
p.hindu_day_count = make_public(M.hindu_day_count)
p.jovian_year = make_public(M.jovian_year)
p.fixed_from_old_hindu_solar = make_public(M.fixed_from_old_hindu_solar, false)
p.old_hindu_solar_from_fixed = make_public(M.old_hindu_solar_from_fixed)
p.old_hindu_lunar_date = make_public(M.old_hindu_lunar_date)
p.old_hindu_lunar_month = make_public(M.old_hindu_lunar_month)
p.old_hindu_lunar_leap = make_public(M.old_hindu_lunar_leap)
p.old_hindu_lunar_day = make_public(M.old_hindu_lunar_day)
p.old_hindu_lunar_year = make_public(M.old_hindu_lunar_year)
p.old_hindu_lunar_leap_year_p = make_public(M.old_hindu_lunar_leap_year_p)
p.old_hindu_lunar_from_fixed = make_public(M.old_hindu_lunar_from_fixed)
p.fixed_from_old_hindu_lunar = make_public(M.fixed_from_old_hindu_lunar, false)
p.mayan_long_count_date = make_public(M.mayan_long_count_date)
p.mayan_baktun = make_public(M.mayan_baktun)
p.mayan_katun = make_public(M.mayan_katun)
p.mayan_tun = make_public(M.mayan_tun)
p.mayan_uinal = make_public(M.mayan_uinal)
p.mayan_kin = make_public(M.mayan_kin)
p.fixed_from_mayan_long_count = make_public(M.fixed_from_mayan_long_count, false)
p.mayan_long_count_from_fixed = make_public(M.mayan_long_count_from_fixed)
p.mayan_haab_date = make_public(M.mayan_haab_date)
p.mayan_haab_day = make_public(M.mayan_haab_day)
p.mayan_haab_month = make_public(M.mayan_haab_month)
p.mayan_haab_ordinal = make_public(M.mayan_haab_ordinal)
p.mayan_haab_from_fixed = make_public(M.mayan_haab_from_fixed)
p.mayan_haab_on_or_before = make_public(M.mayan_haab_on_or_before)
p.mayan_tzolkin_date = make_public(M.mayan_tzolkin_date)
p.mayan_tzolkin_number = make_public(M.mayan_tzolkin_number)
p.mayan_tzolkin_name = make_public(M.mayan_tzolkin_name)
p.mayan_tzolkin_from_fixed = make_public(M.mayan_tzolkin_from_fixed)
p.mayan_tzolkin_ordinal = make_public(M.mayan_tzolkin_ordinal)
p.mayan_tzolkin_on_or_before = make_public(M.mayan_tzolkin_on_or_before)
p.mayan_year_bearer_from_fixed = make_public(M.mayan_year_bearer_from_fixed)
p.mayan_calendar_round_on_or_before = make_public(M.mayan_calendar_round_on_or_before)
p.aztec_xihuitl_date = make_public(M.aztec_xihuitl_date)
p.aztec_xihuitl_month = make_public(M.aztec_xihuitl_month)
p.aztec_xihuitl_day = make_public(M.aztec_xihuitl_day)
p.aztec_xihuitl_ordinal = make_public(M.aztec_xihuitl_ordinal)
p.aztec_xihuitl_from_fixed = make_public(M.aztec_xihuitl_from_fixed)
p.aztec_xihuitl_on_or_before = make_public(M.aztec_xihuitl_on_or_before)
p.aztec_tonalpohualli_date = make_public(M.aztec_tonalpohualli_date)
p.aztec_tonalpohualli_number = make_public(M.aztec_tonalpohualli_number)
p.aztec_tonalpohualli_name = make_public(M.aztec_tonalpohualli_name)
p.aztec_tonalpohualli_ordinal = make_public(M.aztec_tonalpohualli_ordinal)
p.aztec_tonalpohualli_from_fixed = make_public(M.aztec_tonalpohualli_from_fixed)
p.aztec_tonalpohualli_on_or_before = make_public(M.aztec_tonalpohualli_on_or_before)
p.aztec_xiuhmolpilli_designation = make_public(M.aztec_xiuhmolpilli_designation)
p.aztec_xiuhmolpilli_number = make_public(M.aztec_xiuhmolpilli_number)
p.aztec_xiuhmolpilli_name = make_public(M.aztec_xiuhmolpilli_name)
p.aztec_xiuhmolpilli_from_fixed = make_public(M.aztec_xiuhmolpilli_from_fixed)
p.aztec_xihuitl_tonalpohualli_on_or_before = make_public(M.aztec_xihuitl_tonalpohualli_on_or_before)
p.balinese_date = make_public(M.balinese_date)
p.bali_luang = make_public(M.bali_luang)
p.bali_dwiwara = make_public(M.bali_dwiwara)
p.bali_triwara = make_public(M.bali_triwara)
p.bali_caturwara = make_public(M.bali_caturwara)
p.bali_pancawara = make_public(M.bali_pancawara)
p.bali_sadwara = make_public(M.bali_sadwara)
p.bali_saptawara = make_public(M.bali_saptawara)
p.bali_asatawara = make_public(M.bali_asatawara)
p.bali_sangawara = make_public(M.bali_sangawara)
p.bali_dasawara = make_public(M.bali_dasawara)
p.bali_pawukon_from_fixed = make_public(M.bali_pawukon_from_fixed)
p.bali_day_from_fixed = make_public(M.bali_day_from_fixed)
p.bali_triwara_from_fixed = make_public(M.bali_triwara_from_fixed)
p.bali_sadwara_from_fixed = make_public(M.bali_sadwara_from_fixed)
p.bali_saptawara_from_fixed = make_public(M.bali_saptawara_from_fixed)
p.bali_pancawara_from_fixed = make_public(M.bali_pancawara_from_fixed)
p.bali_week_from_fixed = make_public(M.bali_week_from_fixed)
p.bali_dasawara_from_fixed = make_public(M.bali_dasawara_from_fixed)
p.bali_dwiwara_from_fixed = make_public(M.bali_dwiwara_from_fixed)
p.bali_luang_from_fixed = make_public(M.bali_luang_from_fixed)
p.bali_sangawara_from_fixed = make_public(M.bali_sangawara_from_fixed)
p.bali_asatawara_from_fixed = make_public(M.bali_asatawara_from_fixed)
p.bali_caturwara_from_fixed = make_public(M.bali_caturwara_from_fixed)
p.bali_on_or_before = make_public(M.bali_on_or_before)
p.kajeng_keliwon = make_public(M.kajeng_keliwon)
p.tumpek = make_public(M.tumpek)
p.radians_from_degrees = make_public(M.radians_from_degrees)
p.degrees_from_radians = make_public(M.degrees_from_radians)
p.sin_degrees = make_public(M.sin_degrees)
p.cos_degrees = make_public(M.cos_degrees)
p.tan_degrees = make_public(M.tan_degrees)
p.arctan_degrees = make_public(M.arctan_degrees)
p.arcsin_degrees = make_public(M.arcsin_degrees)
p.arccos_degrees = make_public(M.arccos_degrees)
p.hr = make_public(M.hr)
p.mn = make_public(M.mn)
p.sec = make_public(M.sec)
p.mt = make_public(M.mt)
p.deg = make_public(M.deg)
p.mins = make_public(M.mins)
p.secs = make_public(M.secs)
p.angle = make_public(M.angle)
p.degrees_minutes_seconds = make_public(M.degrees_minutes_seconds)
p.location = make_public(M.location)
p.latitude = make_public(M.latitude)
p.longitude = make_public(M.longitude)
p.elevation = make_public(M.elevation)
p.zone = make_public(M.zone)
p.direction = make_public(M.direction)
p.zone_from_longitude = make_public(M.zone_from_longitude)
p.universal_from_local = make_public(M.universal_from_local)
p.local_from_universal = make_public(M.local_from_universal)
p.standard_from_universal = make_public(M.standard_from_universal)
p.universal_from_standard = make_public(M.universal_from_standard)
p.standard_from_local = make_public(M.standard_from_local)
p.local_from_standard = make_public(M.local_from_standard)
p.ephemeris_correction = make_public(M.ephemeris_correction)
p.dynamical_from_universal = make_public(M.dynamical_from_universal)
p.universal_from_dynamical = make_public(M.universal_from_dynamical)
p.julian_centuries = make_public(M.julian_centuries)
p.equation_of_time = make_public(M.equation_of_time)
p.apparent_from_local = make_public(M.apparent_from_local)
p.local_from_apparent = make_public(M.local_from_apparent)
p.apparent_from_universal = make_public(M.apparent_from_universal)
p.universal_from_apparent = make_public(M.universal_from_apparent)
p.midnight = make_public(M.midnight)
p.midday = make_public(M.midday)
p.sidereal_from_moment = make_public(M.sidereal_from_moment)
p.obliquity = make_public(M.obliquity)
p.declination = make_public(M.declination)
p.right_ascension = make_public(M.right_ascension)
p.solar_longitude = make_public(M.solar_longitude)
p.nutation = make_public(M.nutation)
p.aberration = make_public(M.aberration)
p.solar_longitude_after = make_public(M.solar_longitude_after)
p.season_in_gregorian = make_public(M.season_in_gregorian)
p.precession = make_public(M.precession)
p.sidereal_solar_longitude = make_public(M.sidereal_solar_longitude)
p.solar_altitude = make_public(M.solar_altitude)
p.estimate_prior_solar_longitude = make_public(M.estimate_prior_solar_longitude)
p.nth_new_moon = make_public(M.nth_new_moon)
p.new_moon_before = make_public(M.new_moon_before)
p.new_moon_at_or_after = make_public(M.new_moon_at_or_after)
p.lunar_longitude = make_public(M.lunar_longitude)
p.mean_lunar_longitude = make_public(M.mean_lunar_longitude)
p.lunar_elongation = make_public(M.lunar_elongation)
p.solar_anomaly = make_public(M.solar_anomaly)
p.lunar_anomaly = make_public(M.lunar_anomaly)
p.moon_node = make_public(M.moon_node)
p.lunar_node = make_public(M.lunar_node)
p.sidereal_lunar_longitude = make_public(M.sidereal_lunar_longitude)
p.lunar_phase = make_public(M.lunar_phase)
p.lunar_phase_at_or_before = make_public(M.lunar_phase_at_or_before)
p.lunar_phase_at_or_after = make_public(M.lunar_phase_at_or_after)
p.lunar_latitude = make_public(M.lunar_latitude)
p.lunar_altitude = make_public(M.lunar_altitude)
p.lunar_distance = make_public(M.lunar_distance)
p.lunar_parallax = make_public(M.lunar_parallax)
p.topocentric_lunar_altitude = make_public(M.topocentric_lunar_altitude)
p.approx_moment_of_depression = make_public(M.approx_moment_of_depression)
p.sine_offset = make_public(M.sine_offset)
p.moment_of_depression = make_public(M.moment_of_depression)
p.dawn = make_public(M.dawn)
p.dusk = make_public(M.dusk)
p.refraction = make_public(M.refraction)
p.sunrise = make_public(M.sunrise)
p.sunset = make_public(M.sunset)
p.jewish_sabbath_ends = make_public(M.jewish_sabbath_ends)
p.jewish_dusk = make_public(M.jewish_dusk)
p.observed_lunar_altitude = make_public(M.observed_lunar_altitude)
p.moonrise = make_public(M.moonrise)
p.moonset = make_public(M.moonset)
p.local_zero_hour = make_public(M.local_zero_hour)
p.local_from_italian = make_public(M.local_from_italian)
p.italian_from_local = make_public(M.italian_from_local)
p.daytime_temporal_hour = make_public(M.daytime_temporal_hour)
p.nighttime_temporal_hour = make_public(M.nighttime_temporal_hour)
p.standard_from_sundial = make_public(M.standard_from_sundial)
p.jewish_morning_end = make_public(M.jewish_morning_end)
p.asr = make_public(M.asr)
p.alt_asr = make_public(M.alt_asr)
p.arc_of_light = make_public(M.arc_of_light)
p.simple_best_view = make_public(M.simple_best_view)
p.shaukat_criterion = make_public(M.shaukat_criterion)
p.arc_of_vision = make_public(M.arc_of_vision)
p.bruin_best_view = make_public(M.bruin_best_view)
p.yallop_criterion = make_public(M.yallop_criterion)
p.lunar_semi_diameter = make_public(M.lunar_semi_diameter)
p.lunar_diameter = make_public(M.lunar_diameter)
p.visible_crescent = make_public(M.visible_crescent)
p.phasis_on_or_before = make_public(M.phasis_on_or_before)
p.phasis_on_or_after = make_public(M.phasis_on_or_after)
p.persian_date = make_public(M.persian_date)
p.midday_in_tehran = make_public(M.midday_in_tehran)
p.persian_new_year_on_or_before = make_public(M.persian_new_year_on_or_before)
p.fixed_from_persian = make_public(M.fixed_from_persian, false)
p.persian_from_fixed = make_public(M.persian_from_fixed)
p.arithmetic_persian_leap_year_p = make_public(M.arithmetic_persian_leap_year_p)
p.fixed_from_arithmetic_persian = make_public(M.fixed_from_arithmetic_persian, false)
p.arithmetic_persian_year_from_fixed = make_public(M.arithmetic_persian_year_from_fixed)
p.arithmetic_persian_from_fixed = make_public(M.arithmetic_persian_from_fixed)
p.nowruz = make_public(M.nowruz)
p.bahai_date = make_public(M.bahai_date)
p.bahai_major = make_public(M.bahai_major)
p.bahai_cycle = make_public(M.bahai_cycle)
p.bahai_year = make_public(M.bahai_year)
p.bahai_month = make_public(M.bahai_month)
p.bahai_day = make_public(M.bahai_day)
p.fixed_from_bahai = make_public(M.fixed_from_bahai, false)
p.bahai_from_fixed = make_public(M.bahai_from_fixed)
p.bahai_sunset = make_public(M.bahai_sunset)
p.astro_bahai_new_year_on_or_before = make_public(M.astro_bahai_new_year_on_or_before)
p.fixed_from_astro_bahai = make_public(M.fixed_from_astro_bahai, false)
p.astro_bahai_from_fixed = make_public(M.astro_bahai_from_fixed)
p.bahai_new_year = make_public(M.bahai_new_year)
p.naw_ruz = make_public(M.naw_ruz)
p.feast_of_ridvan = make_public(M.feast_of_ridvan)
p.birth_of_the_bab = make_public(M.birth_of_the_bab)
p.french_date = make_public(M.french_date)
p.midnight_in_paris = make_public(M.midnight_in_paris)
p.french_new_year_on_or_before = make_public(M.french_new_year_on_or_before)
p.fixed_from_french = make_public(M.fixed_from_french, false)
p.french_from_fixed = make_public(M.french_from_fixed)
p.french_leap_year_p = make_public(M.french_leap_year_p)
p.arithmetic_french_leap_year_p = make_public(M.arithmetic_french_leap_year_p)
p.fixed_from_arithmetic_french = make_public(M.fixed_from_arithmetic_french, false)
p.arithmetic_french_from_fixed = make_public(M.arithmetic_french_from_fixed)
p.babylonian_date = make_public(M.babylonian_date)
p.babylonian_year = make_public(M.babylonian_year)
p.babylonian_month = make_public(M.babylonian_month)
p.babylonian_leap = make_public(M.babylonian_leap)
p.babylonian_day = make_public(M.babylonian_day)
p.moonlag = make_public(M.moonlag)
p.babylonian_criterion = make_public(M.babylonian_criterion)
p.babylonian_new_month_on_or_before = make_public(M.babylonian_new_month_on_or_before)
p.babylonian_leap_year_p = make_public(M.babylonian_leap_year_p)
p.fixed_from_babylonian = make_public(M.fixed_from_babylonian, false)
p.babylonian_from_fixed = make_public(M.babylonian_from_fixed)
p.astronomical_easter = make_public(M.astronomical_easter)
p.fixed_from_observational_islamic = make_public(M.fixed_from_observational_islamic, false)
p.observational_islamic_from_fixed = make_public(M.observational_islamic_from_fixed)
p.month_length = make_public(M.month_length)
p.early_month_p = make_public(M.early_month_p)
p.alt_fixed_from_observational_islamic = make_public(M.alt_fixed_from_observational_islamic)
p.alt_observational_islamic_from_fixed = make_public(M.alt_observational_islamic_from_fixed)
p.saudi_criterion = make_public(M.saudi_criterion)
p.saudi_new_month_on_or_before = make_public(M.saudi_new_month_on_or_before)
p.fixed_from_saudi_islamic = make_public(M.fixed_from_saudi_islamic, false)
p.saudi_islamic_from_fixed = make_public(M.saudi_islamic_from_fixed)
p.observational_hebrew_first_of_nisan = make_public(M.observational_hebrew_first_of_nisan)
p.observational_hebrew_from_fixed = make_public(M.observational_hebrew_from_fixed)
p.fixed_from_observational_hebrew = make_public(M.fixed_from_observational_hebrew, false)
p.classical_passover_eve = make_public(M.classical_passover_eve)
p.alt_observational_hebrew_from_fixed = make_public(M.alt_observational_hebrew_from_fixed)
p.alt_fixed_from_observational_hebrew = make_public(M.alt_fixed_from_observational_hebrew)
p.samaritan_noon = make_public(M.samaritan_noon)
p.samaritan_new_moon_after = make_public(M.samaritan_new_moon_after)
p.samaritan_new_moon_at_or_before = make_public(M.samaritan_new_moon_at_or_before)
p.samaritan_new_year_on_or_before = make_public(M.samaritan_new_year_on_or_before)
p.fixed_from_samaritan = make_public(M.fixed_from_samaritan, false)
p.samaritan_from_fixed = make_public(M.samaritan_from_fixed)
p.chinese_date = make_public(M.chinese_date)
p.chinese_cycle = make_public(M.chinese_cycle)
p.chinese_year = make_public(M.chinese_year)
p.chinese_month = make_public(M.chinese_month)
p.chinese_leap = make_public(M.chinese_leap)
p.chinese_day = make_public(M.chinese_day)
p.current_major_solar_term = make_public(M.current_major_solar_term)
p.chinese_location = make_public(M.chinese_location)
p.chinese_solar_longitude_on_or_after = make_public(M.chinese_solar_longitude_on_or_after)
p.major_solar_term_on_or_after = make_public(M.major_solar_term_on_or_after)
p.current_minor_solar_term = make_public(M.current_minor_solar_term)
p.minor_solar_term_on_or_after = make_public(M.minor_solar_term_on_or_after)
p.midnight_in_china = make_public(M.midnight_in_china)
p.chinese_winter_solstice_on_or_before = make_public(M.chinese_winter_solstice_on_or_before)
p.chinese_new_moon_on_or_after = make_public(M.chinese_new_moon_on_or_after)
p.chinese_new_moon_before = make_public(M.chinese_new_moon_before)
p.chinese_no_major_solar_term_p = make_public(M.chinese_no_major_solar_term_p)
p.chinese_prior_leap_month_p = make_public(M.chinese_prior_leap_month_p)
p.chinese_new_year_in_sui = make_public(M.chinese_new_year_in_sui)
p.chinese_new_year_on_or_before = make_public(M.chinese_new_year_on_or_before)
p.chinese_from_fixed = make_public(M.chinese_from_fixed)
p.fixed_from_chinese = make_public(M.fixed_from_chinese, false)
p.chinese_name = make_public(M.chinese_name)
p.chinese_stem = make_public(M.chinese_stem)
p.chinese_branch = make_public(M.chinese_branch)
p.chinese_sexagesimal_name = make_public(M.chinese_sexagesimal_name)
p.chinese_name_difference = make_public(M.chinese_name_difference)
p.chinese_year_name = make_public(M.chinese_year_name)
p.chinese_month_name = make_public(M.chinese_month_name)
p.chinese_day_name = make_public(M.chinese_day_name)
p.chinese_day_name_on_or_before = make_public(M.chinese_day_name_on_or_before)
p.chinese_new_year = make_public(M.chinese_new_year)
p.dragon_festival = make_public(M.dragon_festival)
p.qing_ming = make_public(M.qing_ming)
p.chinese_age = make_public(M.chinese_age)
p.chinese_year_marriage_augury = make_public(M.chinese_year_marriage_augury)
p.chinese_solar_term_from_fixed = make_public(M.chinese_solar_term_from_fixed)
p.japanese_location = make_public(M.japanese_location)
p.japanese_solar_term_from_fixed = make_public(M.japanese_solar_term_from_fixed)
p.japanese_from_fixed = make_public(M.japanese_from_fixed)
p.fixed_from_japanese = make_public(M.fixed_from_japanese_for_wikipedia, false)
p.korean_location = make_public(M.korean_location)
p.korean_year = make_public(M.korean_year)
p.korean_solar_term_from_fixed = make_public(M.korean_solar_term_from_fixed)
p.korean_from_fixed = make_public(M.korean_from_fixed_for_wikipedia)
p.fixed_from_korean = make_public(M.fixed_from_korean_for_wikipedia, false)
p.vietnamese_location = make_public(M.vietnamese_location)
p.hindu_sine_table = make_public(M.hindu_sine_table)
p.hindu_sine = make_public(M.hindu_sine)
p.hindu_arcsin = make_public(M.hindu_arcsin)
p.hindu_mean_position = make_public(M.hindu_mean_position)
p.hindu_true_position = make_public(M.hindu_true_position)
p.hindu_solar_longitude = make_public(M.hindu_solar_longitude)
p.hindu_zodiac = make_public(M.hindu_zodiac)
p.hindu_lunar_longitude = make_public(M.hindu_lunar_longitude)
p.hindu_lunar_phase = make_public(M.hindu_lunar_phase)
p.hindu_lunar_day_from_moment = make_public(M.hindu_lunar_day_from_moment)
p.hindu_new_moon_before = make_public(M.hindu_new_moon_before)
p.hindu_solar_date = make_public(M.hindu_solar_date)
p.hindu_calendar_year = make_public(M.hindu_calendar_year)
p.hindu_solar_from_fixed = make_public(M.hindu_solar_from_fixed)
p.fixed_from_hindu_solar = make_public(M.fixed_from_hindu_solar, false)
p.hindu_lunar_date = make_public(M.hindu_lunar_date)
p.hindu_lunar_month = make_public(M.hindu_lunar_month)
p.hindu_lunar_leap_month = make_public(M.hindu_lunar_leap_month)
p.hindu_lunar_day = make_public(M.hindu_lunar_day)
p.hindu_lunar_leap_day = make_public(M.hindu_lunar_leap_day)
p.hindu_lunar_year = make_public(M.hindu_lunar_year)
p.hindu_lunar_from_fixed = make_public(M.hindu_lunar_from_fixed)
p.fixed_from_hindu_lunar = make_public(M.fixed_from_hindu_lunar, false)
p.hindu_ascensional_difference = make_public(M.hindu_ascensional_difference)
p.hindu_tropical_longitude = make_public(M.hindu_tropical_longitude)
p.hindu_solar_sidereal_difference = make_public(M.hindu_solar_sidereal_difference)
p.hindu_daily_motion = make_public(M.hindu_daily_motion)
p.hindu_rising_sign = make_public(M.hindu_rising_sign)
p.hindu_equation_of_time = make_public(M.hindu_equation_of_time)
p.hindu_sunrise = make_public(M.hindu_sunrise)
p.hindu_sunset = make_public(M.hindu_sunset)
p.hindu_standard_from_sundial = make_public(M.hindu_standard_from_sundial)
p.hindu_fullmoon_from_fixed = make_public(M.hindu_fullmoon_from_fixed)
p.fixed_from_hindu_fullmoon = make_public(M.fixed_from_hindu_fullmoon, false)
p.hindu_expunged_p = make_public(M.hindu_expunged_p)
p.alt_hindu_sunrise = make_public(M.alt_hindu_sunrise)
p.ayanamsha = make_public(M.ayanamsha)
p.astro_hindu_sunset = make_public(M.astro_hindu_sunset)
p.sidereal_zodiac = make_public(M.sidereal_zodiac)
p.astro_hindu_calendar_year = make_public(M.astro_hindu_calendar_year)
p.astro_hindu_solar_from_fixed = make_public(M.astro_hindu_solar_from_fixed)
p.fixed_from_astro_hindu_solar = make_public(M.fixed_from_astro_hindu_solar, false)
p.astro_lunar_day_from_moment = make_public(M.astro_lunar_day_from_moment)
p.astro_hindu_lunar_from_fixed = make_public(M.astro_hindu_lunar_from_fixed)
p.fixed_from_astro_hindu_lunar = make_public(M.fixed_from_astro_hindu_lunar, false)
p.hindu_solar_longitude_at_or_after = make_public(M.hindu_solar_longitude_at_or_after)
p.mesha_samkranti = make_public(M.mesha_samkranti)
p.hindu_lunar_day_at_or_after = make_public(M.hindu_lunar_day_at_or_after)
p.hindu_lunar_new_year = make_public(M.hindu_lunar_new_year)
p.hindu_lunar_on_or_before_p = make_public(M.hindu_lunar_on_or_before_p)
p.hindu_date_occur = make_public(M.hindu_date_occur)
p.hindu_lunar_holiday = make_public(M.hindu_lunar_holiday)
p.diwali = make_public(M.diwali)
p.hindu_tithi_occur = make_public(M.hindu_tithi_occur)
p.hindu_lunar_event = make_public(M.hindu_lunar_event)
p.shiva = make_public(M.shiva)
p.rama = make_public(M.rama)
p.hindu_lunar_station = make_public(M.hindu_lunar_station)
p.karana = make_public(M.karana)
p.yoga = make_public(M.yoga)
p.sacred_wednesdays = make_public(M.sacred_wednesdays)
p.sacred_wednesdays_in_range = make_public(M.sacred_wednesdays_in_range)
p.tibetan_date = make_public(M.tibetan_date)
p.tibetan_year = make_public(M.tibetan_year)
p.tibetan_month = make_public(M.tibetan_month)
p.tibetan_leap_month = make_public(M.tibetan_leap_month)
p.tibetan_day = make_public(M.tibetan_day)
p.tibetan_leap_day = make_public(M.tibetan_leap_day)
p.tibetan_sun_equation = make_public(M.tibetan_sun_equation)
p.tibetan_moon_equation = make_public(M.tibetan_moon_equation)
p.fixed_from_tibetan = make_public(M.fixed_from_tibetan, false)
p.tibetan_from_fixed = make_public(M.tibetan_from_fixed)
p.tibetan_leap_month_p = make_public(M.tibetan_leap_month_p)
p.tibetan_leap_day_p = make_public(M.tibetan_leap_day_p)
p.losar = make_public(M.losar)
p.tibetan_new_year = make_public(M.tibetan_new_year)
p.julian_year_from_auc_year = p.julian_year_from_auc
p.auc_year_from_julian_year = p.auc_year_from_julian

return p