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.

// request.cf · coarse context

A page that knows where it met you.

Only coarse request metadata is shown. This demo does not display or persist visitor IP addresses.

Country
US
Cloudflare location
CMH
Connection
HTTP/2
Language
Not provided

Ray ID: a401e8d50ab27929

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