Skip to content

Latest commit

 

History

History
3051 lines (2706 loc) · 132 KB

SuperMoon.md

File metadata and controls

3051 lines (2706 loc) · 132 KB

21st Century Supermoon Estimation in R

knitr::opts_chunk$set(cache = TRUE, results = "hold") # Group output by chunk.

suppressMessages(library(extrafont))    # Extra fonts, used for graphs.
suppressMessages(library(ggplot2))      # Graph plotting library.
suppressMessages(library(lubridate))    # Date-time manipulation utilities.
suppressMessages(library(pkgcond))      # Suppress inefficiency warnings when converting mpfr values to numerics.
suppressMessages(library(Rmpfr))        # Arbitrary-precision arithmetic.
suppressMessages(library(scales))       # For formatting date axes in graphs.


AngleDegreesToRadians <- function(angle.degrees) {

   # Convert angles in degrees to radians.
   #
   # Input:
   #    angle.degrees   (an angle in degrees).
   #
   # N.B. Short-circuit order optimised for 21st century supermoon processing.

   if ((angle.degrees >= G.360.mpfr) || (angle.degrees < G.zero.mpfr)) {
      # N.B. The %% operator has higher precedence than *.
      return(angle.degrees %% G.360.mpfr * G.pi.on.180.mpfr)
   } else {
      return(angle.degrees * G.pi.on.180.mpfr)
   }
}


AngleRadiansToDegrees <- function(angle.radians) {

   # Convert angles in radians to degrees.
   #
   # Input:
   #    angle.radians   (an angle in radians).

   two.pi <- Const("pi", prec = 128) * 2

   # Convert angles to (0 <= theta < 2*Pi) radians, if necessary, by taking the angle modulo 2*Pi radians.
   if ((angle.radians < G.zero.mpfr) || (angle.radians >= two.pi)) {
      angle.radians <- angle.radians %% two.pi
   }

   return(angle.radians / Const("pi", prec = 128) * 180)
}


CalculateWithUncertainty <- function(function.name, value, uncertainty, receive.uncertainty) {

   # Call another function, propagating uncertainty.
   #    N.B. It assumes the function called also returns a value with an associated uncertainty.
   #    N.B. If time-dates are being manipulated, pass the uncertainty as a lubridate duration.
   #
   # Input:
   #    function.name         the function to invoke
   #    value                 the value to pass to the function
   #    uncertainty           the value's uncertainty to pass to the function
   #    receive.uncertainty   a boolean indicating if the invoked function itself returns a value with an uncertainty

   result.low <- function.name(value - uncertainty)
   result.mid <- function.name(value)
   result.high <- function.name(value + uncertainty)

   if (receive.uncertainty) {
      result.vector <- c(result.low$value - result.low$uncertainty, result.low$value + result.low$uncertainty,
                         result.mid$value - result.mid$uncertainty, result.mid$value + result.mid$uncertainty,
                         result.high$value - result.high$uncertainty, result.high$value + result.high$uncertainty)
   } else {
      result.vector <- c(result.low, result.mid, result.high)
   }

   result.min <- min(result.vector)
   result.max <- max(result.vector)

   return(list("value" = mean(c(result.min, result.max)), "uncertainty" = {result.max - result.min} * 0.5))
}


GetDelT <- function(date.target) {
   
   # Calculate the difference delT, in seconds, between dynamical time and universal time (UT1).
   #    See Five Millennium Canon of Solar Eclipses: -1999 to +3000 (2000 BCE to 3000 CE).
   #
   # Input:
   #    date.target   (date should be universal time (UT1).
   #                  (It's assumed the target date will be in the 21st century.)
   #
   # N.B. An exact decimal year is used here.
   #      The reference material uses a less accurate value rounded to the middle of months.
   # N.B. Where relevant, a lunar secular acceleration of -26 arcsec/cy^2 is assumed.

   # Convert input date to decimal years. E.g. 1987.25 is the end of March, 1987.
   date.target <- G.zero.mpfr + decimal_date(date.target)

   if (date.target < G.threshold.GetDelT) {
      # As known delT values exist until 31-Dec-2020 23:59:59, detT shouldn't be estimated on or prior to that.
      # Allow a 900 millisecond buffer for processing the UT1-UTC uncertainty (always <= 0.9 s).
      # So, G.threshold.GetDelT is (01-01-2021 00:00:00) - 0.9 s.

      cat("\nError in function GetDelT...")
      cat("\nInvalid date processing; date.target:\n")
      print(date.target, digits=20)
      stopifnot(FALSE)

   } else if (date.target < mpfr("2050", 128)) {
      t <- date.target - 2000
      delT <- t * {mpfr("0.005589", 128) * t + mpfr("0.32217", 128)} + mpfr("62.92", 128)

   } else {
      # delT <- -20.0 - 0.5628 * (2150.0 - date.target) + 32.0 * (((date.target - 1820.0) * 0.01) ^ 2)
      # Or more simply...
      delT <- date.target * {mpfr("0.0032", 128) * date.target - mpfr("11.0852", 128)} + mpfr("9369.66", 128)
   }

   # N.B. Here uncertainty is in seconds; algorithm's intrinsic uncertainty.
   N <- date.target - 2005
   return(list("value" = delT,
               "uncertainty" = 365.25 * N * sqrt(N * G.uncertainty.constant.GetDelT * {1 + N * mpfr("0.0004", 128)}) * mpfr("0.001", 128)))
}


GetEccentricAnomaly <- function(time.target) {

   # Get the eccentric anomaly, for a given time (in Julian centuries from Epoch J2000.0).
   #
   # Input:
   #   time.target   Target time, in Julian centuries from Epoch J2000.0.

   return(1 - time.target * {mpfr("0.002516", 128) + time.target * mpfr("0.0000074", 128)})
}


GetListEstimates <- function(function.name, date.start, date.end, interval) {

   # Get a list of dates (in JDE format) for nominated apsides or phases over a selected date range.
   #
   #   date.start   (start of the date range, in Coordinated Universal Time).
   #   date.end     (end of the date range, in Coordinated Universal Time).
   #   interval     (decimal specifying how often, in days, to check for the nearest apside or phase, e.g. 29.4.)
   #                (N.B. For apsides, ensure it's < the anomalistic month over the date range, else apsides will be missed.)
   #                (N.B. For phases, ensure it's < the synodic month over the date range, else phases will be missed.)
   #                (N.B. An mpfr value is recommended.)

   # Total list elements needed is ceiling({date.end - date.start} / ddays(interval)).
   interval.days <- ddays(interval)
   list.estimates <- lapply(0:{ceiling({date.end - date.start} / interval.days) - 1},
         function(x) {
            target.dynamical <- TimeUniversalToDynamical(date.start + x * interval.days)
            CalculateWithUncertainty(function.name, target.dynamical$value, target.dynamical$uncertainty, TRUE)
         }
   )

   # Remove duplicates.
   if (G.verbosity) {
      cat("\nList length, initial: ", length(list.estimates), sep="")
      list.estimates <- unique(list.estimates)
      cat("\nList length, final: ", length(list.estimates), sep="")
   } else {
      list.estimates <- unique(list.estimates)
   }

   return(list.estimates)
}


GetLunarApsisJDE <- function(date.target) {

   # Calculate lunar apsis dates.
   #    See Astronomical Algorithms, Meeus, chapter 50.
   #
   # Input:
   #    date.target   (date should be a Dynamical Time, as a date/time value).

   # Determine new moon coefficient "k".
   #    k = 0 corresponds to the perigee of 22nd Dec, 1999.
   #    Negative values of k give lunar apsides before that perigee.
   #    - For perigee, round to the nearest integer.
   #    - For apogee, round to the nearest integer + 0.5.
   #    Assumes target apsis is a global variable.
   #
   #    N.B. Ensure x.5 values are rounded towards zero if negative, but away from zero if positive.
   #    N.B. Ensure odd and even numbers are rounded consistently (R doesn't do this by default).
   #    N.B. Ensure target dynamical time is converted to decimal years before use.
   #
   k <- {decimal_date(date.target) - mpfr("1999.97", 128)} * mpfr("13.2555", 128)
   #
   if (G.lunar.apsis == "perigee") {
      k <- floor(0.5 + k)

   } else if (G.lunar.apsis == "apogee") {
      k <- 0.5 + floor(k)

   } else {
      cat("\nError in function GetLunarApsisJDE...")
      cat("\nInvalid lunar apsis; G.lunar.apsis:\n")
      print(G.lunar.apsis)
      stopifnot(FALSE)
   }

   # Determine the approximate time "T" in Julian centuries from epoch 2000.
   T <- k / mpfr("1325.55", 128)
   T.x.T <- T * T

   # Determine the mean perigee or apogee, in Julian Ephemeris Days "JDE".
   # Specify the calculation using Horner's Method.
   mean.apsis <- T.x.T * {mpfr("-0.0006691", 128) + T * {T * mpfr("0.0000000052", 128) - mpfr("0.000001098", 128)}} + mpfr("27.55454989", 128) * k + mpfr("2451534.6698", 128)

   # Calculate the Moon's mean elongation, in radians.
   D <- AngleDegreesToRadians(T.x.T * {T * {T * mpfr("0.000000055", 128) - mpfr("0.00001156", 128)} - mpfr("0.0100383", 128)} + mpfr("171.9179", 128) + mpfr("335.9106046", 128) * k)

   # Calculate the Sun's mean anomaly, in radians.
   M <- AngleDegreesToRadians(T.x.T * {-T * mpfr("0.0000010", 128) - mpfr("0.0008130", 128)} + mpfr("347.3477", 128) + mpfr("27.1577721", 128) * k)

   # Calculate the Moon's argument of latitude, in radians.
   F <- AngleDegreesToRadians(T.x.T * {-T * mpfr("0.0000148", 128) - mpfr("0.0125053", 128)} + mpfr("316.6109", 128) + mpfr("364.5287911", 128) * k)
   F.x.2 <- 2 * F

   # Calculate the first set of periodic corrections, in days.
   if (G.lunar.apsis == "perigee") {
      # Set apsis-specific uncertainty from Astronomical Algorithms, Meeus, chapter 50.
      # N.B. The worst-case uncertainty, not mean uncertainty, is taken here.
      # N.B. Values here are minutes converted into JDE days, using 1 day = 1440 minutes.
      apsis.uncertainty <- 31 / mpfr("1440", 128)

      # Calculate periodic corrections for perigee.

      # Perigee timing periodic calculation coefficients.
      vec.sin <-  c(        2 * D,                 4 * D,             6 * D,               8 * D,
                        2 * D - M,                     M,            10 * D,           4 * D - M,
                        6 * D - M,                12 * D,                 D,           8 * D - M,
                           14 * D,                 F.x.2,             3 * D,          10 * D - M,
                           16 * D,            12 * D - M,             5 * D,       2 * D + F.x.2,
                           18 * D,            14 * D - M,             7 * D,           2 * D + M,
                           20 * D,                 D + M,        16 * D - M,           4 * D + M,
                            9 * D,         4 * D + F.x.2,       2 * {D - M},       4 * D - 2 * M,
                    6 * D - 2 * M,                22 * D,        18 * D - M,           6 * D + M,
                           11 * D,             8 * D + M,     4 * D - F.x.2,       6 * D + F.x.2,
                        3 * D + M,             5 * D + M,            13 * D,          20 * D - M,
                    3 * D + 2 * M, 4 * D + F.x.2 - 2 * M,         D + 2 * M,          22 * D - M,
                            4 * F,         6 * D - F.x.2, 2 * D - F.x.2 + M,               2 * M,
                        F.x.2 - M,       2 * {D + F.x.2},     F.x.2 - 2 * M, 2 * {D + M} - F.x.2,
                           24 * D,           4 * {D - F},       2 * {D + M},               D - M)

      corrections.periodic <- sum({G.vec.perigee.coeff + G.vec.perigee.subtrahend * T} * sin(vec.sin))

   } else {
      # Set apsis-specific uncertainty.
      # N.B. Values here are minutes converted into JDE days, using 1 day = 1440 minutes.
      apsis.uncertainty <- 3 / mpfr("1440", 128)

      # Calculate periodic corrections for apogee.
      vec.sin <- c(        2 * D,         4 * D,                 M,     2 * D - M,
                           F.x.2,             D,             6 * D,     4 * D - M,
                   2 * D + F.x.2,         D + M,             8 * D,     6 * D - M,
                   2 * D - F.x.2,   2 * {D - M},             3 * D, 4 * D + F.x.2,
                       8 * D - M, 4 * D - 2 * M,            10 * D,     3 * D + M,
                           2 * M,     2 * D + M,       2 * {D + M}, 6 * D + F.x.2,
                   6 * D - 2 * M,    10 * D - M,             5 * D, 4 * D - F.x.2,
                       F.x.2 + M,        12 * D, 2 * D + F.x.2 - M,         D - M)

      corrections.periodic <- sum({G.vec.apogee.coeff + G.vec.apogee.subtrahend * T} * sin(vec.sin))
   }

   # Diagnostic information.
   if (G.verbosity) {
      cat("   GetLunarApsisJDE settings...\n", sep="")
      cat("\n   k:")
      print(k, digits = 20)
      cat("\n   T:")
      print(T, digits = 20)
      cat("\n   D (degrees):")
      print(AngleRadiansToDegrees(D), digits = 20)
      cat("\n   M (degrees):")
      print(AngleRadiansToDegrees(M), digits = 20)
      cat("\n   F (degrees):")
      print(AngleRadiansToDegrees(F), digits = 20)
      cat("\n   mean.apsis: ")
      print(mean.apsis, digits = 20)
      cat("\n   corrections.periodic: ")
      print(corrections.periodic, digits = 20)
   }

   return(list("value" = mean.apsis + corrections.periodic, "uncertainty" = apsis.uncertainty))
}


GetLunarDistance <- function(JDE.mpfr) {

   # Get the lunar distance, in kilometres, for a given JDE (Julian Ephemeris Day).
   #    See Astronomical Algorithms, Meeus, chapter 47.
   #
   # Input:
   #   JDE.mpfr   Target date, in Julian Ephemeris Days, best passed in mpfr format with, e.g. 128-bit accuracy.
   #              E.g. R stores a passed value of 2443259.9 as 2443259.8999999999 by default.
   #                   This causes errors of +- 1 second easily.
   
   # Input must be >= 0.
   if (JDE.mpfr < 0) {
      cat("\nError in function GetLunarDistance...")
      cat("\nInput dynamical time; JDE.mpfr:\n")
      print(JDE.mpfr, digits=20)
      cat("\nNeed dynamical time >= 0.\n")
      stopifnot(FALSE)
   }

   # Express the target date as "T" in Julian centuries from epoch 2000.
   # N.B. See Astronomical Algorithms, Meeus, chapter 22, p. 143.
   T <- {JDE.mpfr - 2451545} / 36525

   # Calculate E, to correct for the (changing) eccentricity of the Earth's orbit around the Sun.
   E <- GetEccentricAnomaly(T)
   E.x.E <- E * E

   # Calculate the Moon's mean elongation, in radians.
   D <- AngleDegreesToRadians(mpfr("297.8501921", 128) + T * {mpfr("445267.1114034", 128) - T * {mpfr("0.0018819", 128) - T * {G.lunar.mean.elongation.constant - T / 113065000}}})

   # Calculate the Sun's mean anomaly, in radians.
   M <- AngleDegreesToRadians(mpfr("357.5291092", 128) + T * {mpfr("35999.0502909", 128) - T * {mpfr("0.0001536", 128) - T / 24490000}})

   # Calculate the Moon's mean anomaly, in radians.
   M.prime <- AngleDegreesToRadians(mpfr("134.9633964", 128) + T * {mpfr("477198.8675055", 128) + T * {mpfr("0.0087414", 128) + T * {G.lunar.mean.anomaly.constant - T / 14712000}}})

   # Calculate the Moon's argument of latitude, in radians.
   F.x.2 <- 2 * AngleDegreesToRadians(mpfr("93.272095", 128) + T * {mpfr("483202.0175233", 128) - T * {mpfr("0.0036539", 128) + T * {G.lunar.argument.latitude.constant - T / 863310000}}})

   # Calculate periodic distance terms.
   vec.E <-     c( G.1.mpfr,                G.1.mpfr,                G.1.mpfr,                G.1.mpfr,
                          E,                G.1.mpfr,                G.1.mpfr,                       E,
                   G.1.mpfr,                       E,                       E,                G.1.mpfr,
                          E,                G.1.mpfr,                G.1.mpfr,                G.1.mpfr,
                   G.1.mpfr,                G.1.mpfr,                       E,                       E,
                   G.1.mpfr,                       E,                       E,                G.1.mpfr,
                   G.1.mpfr,                G.1.mpfr,                       E,                       E,
                   G.1.mpfr,                   E.x.E,                       E,                   E.x.E,
                   G.1.mpfr,                       E,                G.1.mpfr,                       E,
                          E,                   E.x.E,                   E.x.E,                G.1.mpfr,
                   G.1.mpfr,                       E,                G.1.mpfr,                G.1.mpfr,               
                      E.x.E,                G.1.mpfr)

   distance.value <- mpfr("385000.56", 128) +
                     sum(G.vec.ld.coeff * vec.E *
                        cos(D * G.mask.distance.D +
                            F.x.2 * G.mask.distance.F.x.2 +
                            M * G.mask.distance.M +
                            M.prime * G.mask.distance.M.prime)) *
                     mpfr("0.001", 128)

   # Diagnostic information.
   if (G.verbosity) {
      cat("   GetLunarDistance...\n", sep="")
      cat("\n   T:")
      print(T, digits = 20)
      cat("\n   E:")
      print(E, digits = 20)
      cat("\n   D (degrees):")
      print(AngleRadiansToDegrees(D), digits = 20)
      cat("\n   M (degrees):")
      print(AngleRadiansToDegrees(M), digits = 20)
      cat("\n   M.prime (degrees):")
      print(AngleRadiansToDegrees(M.prime), digits = 20)
      cat("\n   F (degrees):")
      print(AngleRadiansToDegrees(F.x.2 * 0.5), digits = 20)
      cat("\n   distance.periodic: ")
      print({distance.value - mpfr("385000.56", 128)} * mpfr("1000", 128), digits = 20)
   }

   # Determine uncertainty by comparison to the complete ELP-2000/82 theory.
   # Load a Fortran shared library for ELP-2000/82.
   # N.B. Ideally, this shouldn't be loaded/unloaded each time it's needed, but implementation is unclear.
   dyn.load(G.library.path)
   stopifnot(is.loaded("elp82b_2"))
   result <- .Fortran("ELP82B_2", as.double(JDE.mpfr), 0, as.integer(1), double(3), as.integer(-1))
   dyn.unload(G.library.path)

   # ELP-2000/82 distance is the length derived from its three Cartesian coordinate vectors.
   return(list("value" = distance.value,
               "uncertainty" = abs(sqrt(result[[4]][1] * result[[4]][1] +
                                        result[[4]][2] * result[[4]][2] +
                                        result[[4]][3] * result[[4]][3]) - distance.value)))
}


GetLunarPhaseJDE <- function(date.target) {

   # Calculate lunar phase dates.
   #    See Astronomical Algorithms, Meeus, chapter 49.
   #
   # Input:
   #    date.target   (date should be a Dynamical Time, as a date/time value).

   # Determine new moon coefficient "k".
   #    k = 0 corresponds to the New Moon of 6th Jan, 2000.
   #    Negative values of k give lunar phases before the year 2000.
   #    - If new moon, round to the nearest integer.
   #    - If first quarter moon, round to the nearest integer + 0.25.
   #    - If full moon, round to the nearest integer + 0.5.
   #    - If last quarter moon, round to the nearest integer + 0.75.
   #    Assumes target phase is a global variable.
   #
   #    N.B. Ensure x.5 values are rounded towards zero if negative, but away from zero if positive.
   #    N.B. Ensure odd and even numbers are rounded consistently (R doesn't do this by default).
   #    N.B. Ensure target dynamical time is converted to decimal years before use.
   #
   k <- {decimal_date(date.target) - 2000} * mpfr("12.3685", 128)
   #
   if (G.lunar.phase == "full moon") {
      k <- mpfr("0.5", 128) + floor(k)
      
      # Set phase-specific uncertainty from Astronomical Algorithms, Meeus, chapter 49, p. 354.
      # N.B. The worst-case uncertainty, not mean uncertainty, is taken here.
      # N.B. Values here are seconds converted into JDE days, using 1 day = 86,400 seconds.
      phase.uncertainty <- mpfr("17.4", 128) / 86400

   } else if (G.lunar.phase == "last quarter") {
      k <- sign(k) * mpfr("0.75", 128) + floor(mpfr("0.5", 128) - sign(k) * mpfr("0.75", 128) + k)
      phase.uncertainty <- mpfr("13", 128) / 86400

   } else if (G.lunar.phase == "new moon") {
      k <- floor(mpfr("0.5", 128) + k)
      phase.uncertainty <- mpfr("16.4", 128) / 86400

   } else if (G.lunar.phase == "first quarter") {
      k <- sign(k) * mpfr("0.25", 128) + floor(mpfr("0.5", 128) - sign(k) * mpfr("0.25", 128) + k)
      phase.uncertainty <- mpfr("15.3", 128) / 86400

   } else {
      cat("\nError in function GetLunarPhaseJDE...")
      cat("\nInvalid lunar phase; G.lunar.phase:\n")
      print(G.lunar.phase)
      stopifnot(FALSE)
   }

   # Determine the approximate time "T" in Julian centuries from epoch 2000.
   T <- k / mpfr("1236.85", 128)
   T.x.T <- T * T

   # Determine the mean lunar phase, in Julian Ephemeris Days "JDE".
   # Specify the calculation using Horner's Method.
   mean.phase <- T.x.T * {0.00015437 + T * {T * 0.00000000073 - 0.00000015}} + 29.530588861 * k + 2451550.09766

   # Calculate E, to correct for the (changing) eccentricity of the Earth's orbit around the Sun.
   E <- GetEccentricAnomaly(T)
   E.x.E <- E * E

   # Calculate the Sun's mean anomaly, in radians.
   M <- AngleDegreesToRadians(T.x.T * {-T * 0.00000011 - 0.0000014} + 2.5534 + 29.1053567 * k)

   # Calculate the Moon's mean anomaly, in radians.
   M.prime <- AngleDegreesToRadians(T.x.T * {-T.x.T * 0.000000058 + T * 0.00001238 + 0.0107582} + 201.5643 + 385.81693528 * k)

   # Calculate the Moon's argument of latitude, in radians.
   F.x.2 <- 2 * AngleDegreesToRadians(T.x.T * {T * {T * 0.000000011 - 0.00000227} - 0.0016118} + 160.7108 + 390.67050284 * k)

   # Calculate the longitude of ascending node of the lunar orbit, in radians.
   omega <- AngleDegreesToRadians(T.x.T * {T * 0.00000215 + 0.0020672} + 124.7746 - 1.56375588 * k)

   # Calculate the first set of periodic corrections, in days.
   if (G.lunar.phase == "full moon" || G.lunar.phase == "new moon") {

      vec.ph.E <- c(G.1.mpfr,
                    E,
                    G.1.mpfr,
                    G.1.mpfr,
                    E,
                    E,
                    E.x.E,
                    G.1.mpfr,
                    G.1.mpfr,
                    E,
                    G.1.mpfr,
                    E,
                    E,
                    E,
                    G.1.mpfr,
                    G.1.mpfr,
                    G.1.mpfr,
                    G.1.mpfr,
                    G.1.mpfr,
                    G.1.mpfr,
                    G.1.mpfr,
                    G.1.mpfr,
                    G.1.mpfr,
                    G.1.mpfr,
                    G.1.mpfr)

      vec.ph.qrt.sin <- c(M.prime,
                          M,
                          2 * M.prime,
                          F.x.2,
                          M.prime - M,
                          M.prime + M,
                          2 * M,
                          M.prime - F.x.2,
                          M.prime + F.x.2,
                          2 * M.prime + M,
                          3 * M.prime,
                          M + F.x.2,
                          M - F.x.2,
                          2 * M.prime - M,
                          omega,
                          M.prime + 2 * M,
                          2 * M.prime - F.x.2,
                          3 * M,
                          M.prime + M - F.x.2,
                          2 * M.prime + F.x.2,
                          M.prime + M + F.x.2,
                          M.prime - M + F.x.2,
                          M.prime - M - F.x.2,
                          3 * M.prime + M,
                          4 * M.prime)

      if (G.lunar.phase == "full moon") {
         # A full moon is being processed.
         corrections.periodic <- sum(G.vec.ph.full.coeff * vec.ph.E * sin(vec.ph.qrt.sin))
      } else {
         # Assume a new moon is being processed.
         corrections.periodic <- sum(G.vec.ph.new.coeff * vec.ph.E * sin(vec.ph.qrt.sin))
      }
      
   } else {
      # Assume a quarter phase is being processed.

      vec.ph.qrt.E <- c(G.1.mpfr,
                        E,
                        E,
                        G.1.mpfr,
                        G.1.mpfr,
                        E,
                        E.x.E,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr,
                        E,
                        E,
                        E,
                        E.x.E,
                        E,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr)

      vec.ph.qrt.sin <- c(M.prime,
                          M,
                          M.prime + M,
                          2 * M.prime,
                          F.x.2,
                          M.prime - M,
                          2 * M,
                          M.prime - F.x.2,
                          M.prime + F.x.2,
                          3 * M.prime,
                          2 * M.prime - M,
                          M + F.x.2,
                          M - F.x.2,
                          M.prime + 2 * M,
                          2 * M.prime + M,
                          omega,
                          M.prime - M - F.x.2,
                          2 * M.prime + F.x.2,
                          M.prime + M + F.x.2,
                          M.prime - 2 * M,
                          M.prime + M - F.x.2,
                          3 * M,
                          2 * M.prime - F.x.2,
                          M.prime - M + F.x.2,
                          3 * M.prime + M)

      corrections.periodic <- sum(G.vec.ph.qrt.coeff * vec.ph.qrt.E * sin(vec.ph.qrt.sin))

      # Supplemental quarter phase corrections.
      vec.ph.sup.E <- c(E,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr,
                        G.1.mpfr)

      vec.ph.sup.cos <- c(M,
                          M.prime,
                          M.prime - M,
                          M.prime + M,
                          F.x.2)

      W <- 0.00306 + sum(G.vec.ph.sup.coeff * vec.ph.sup.E  * cos(vec.ph.sup.cos))

      if (G.lunar.phase == "first quarter") {
         # Apply supplemental first quarter corrections.
         corrections.periodic <- corrections.periodic + W

      } else {
         # Apply supplemental last quarter corrections.
         corrections.periodic <- corrections.periodic - W
      }

      # Diagnostic information.
      if (G.verbosity) {
         cat("   GetLunarPhaseJDE settings (quarter-specific)...\n", sep="")
         cat("\n   W:")
         print(W, digits = 20)
      }
   }

   # Calculate additional corrections, based on "planetary arguments" in days.
   corrections.additional <- sum(G.vec.ph.add.coeff.outer * sin(AngleDegreesToRadians(G.vec.ph.add.addend + G.vec.ph.add.coeff.inner * k - G.vec.ph.add.subtrahend * T.x.T)))

   # Diagnostic information.
   if (G.verbosity) {
      cat("   GetLunarPhaseJDE settings...\n", sep="")
      cat("\n   k:")
      print(k, digits = 20)
      cat("\n   T:")
      print(T, digits = 20)
      cat("\n   E:")
      print(E, digits = 20)
      cat("\n   mean.phase: ")
      print(mean.phase, digits = 20)
      cat("\n   corrections.periodic: ")
      print(corrections.periodic, digits = 20)
      cat("\n   corrections.additional: ")
      print(corrections.additional, digits = 20)
   }

   return(list("value" = corrections.additional + corrections.periodic + mean.phase,
               "uncertainty" = phase.uncertainty))
}


GetUTC <- function(DT) {

   # Estimate UTC from an input Dynamical Time.
   # Effectively reverses the operation in GetDelT (except it returns UTC, not UT1).
   #
   # Input:
   #    date.target   (date should be a Dynamical Time).
   #                  (It's assumed the recovered UTC date will be in the 21st century.)

   # Ensure a valid date range has been entered.
   # Known delT values exist until 31-Dec-2020 23:59:59.
   # Thus, UTC should be determined exactly on or prior to that.
   # N.B. Assume 01-Jan-2021 00:00:00.000 UTC = 01-01-2021 00:01:12.150 DT.
   #
   if (DT < dmy_hms("01-01-2021 00:01:12.150", tz="UTC")) {
      cat("\nError in function GetUTC...")
      cat("\nInput date:  ", format(DT, "%d-%b-%Y %H:%M:%OS"), " (assumed Dynamical Time)")
      cat("\nNeed date >= 01-01-2021 00:01:12.150 Dynamical Time", sep="")
      stopifnot(FALSE)
   }

   # Determine how many seconds were in the UTC year, get a inverse scaling factor.
   # N.B. Remember, delT is a function of the UTC year (not DT year).
   if (IsUTCLeapYear(DT)) {
      # 31622400 seconds = 366 days.
      scaling <- G.366.day.scaling
   } else {
      # 31536000 seconds = 365 days.
      scaling <- G.365.day.scaling
   }

   # Estimate value.
   # Remember, 0 = UTC (decimal year) + (ay^2 + by + c) (seconds) - DT (decimal year).
   DT.decimal <- G.zero.mpfr + decimal_date(DT)
   small.diff <- G.zero.mpfr + decimal_date(DT - G.5.minutes)

   if (DT < dmy_hms("01-01-2050 00:01:33.000", tz="UTC")) {
      # Assume 01-Jan-2050 00:00:00.000 UTC = 01-Jan-2050 00:01:33.000 DT.
      f1 <- function (x) {
               t <- x - 2000
               x + scaling * {t * {0.005589 * t + 0.32217} + 62.92} - DT.decimal
            }

   } else {
      f1 <- function (x) {
               x + scaling * {x * {0.0032 * x - 11.0852} + 9369.66} - DT.decimal
            }
   }
   
   # Return the UTC value as a date-time.
   #    N.B. By default unirootR warns if the root finding calculation fails to converge.
   #    N.B. Using tol = 1e-20 is generally fine, but we use the R numerical mantissa for added rigor.
   UTC <- unirootR(f1, interval = c(small.diff, DT.decimal), verbose = FALSE, tol = 1e-36)

   UTC.decimal <- date_decimal(asNumeric(UTC$root), tz = "UTC")

   return(list("value" = UTC.decimal,
               "uncertainty" = difftime(date_decimal(asNumeric(UTC$root + UTC$estim.prec * 0.5), tz = "UTC"),
                                        UTC.decimal, tz = "UTC", units = c("auto"))))
}


IsUTCLeapYear <- function(date.target) {

   # Test if a Dynamical Time is equivalent to a UTC leap year.
   # This information is used to conversion of DT to UTC date-times > 31-Dec-2020 23:59:59.999 (UTC).
   #
   # Input:
   #   date.target   (date should be a UTC time).
   #   N.B. An input date equivalent to a 21st century UTC date is assumed.

   # Convert input date to decimal format.
   date.target.decimal <- G.zero.mpfr + decimal_date(date.target)

   # Ensure only dates >= 01-Jan-2021 UTC are being processed.
   # N.B. Assume 01-Jan-2021 00:00:00.000 UTC = 01-Jan-2021 00:01:12.150 Dynamical Time.
   #                                           ~2021.000002287871666339920000000000000000.
   #      Technically, here, 01-Jan-2021 00:00:00.000 UTC = 01-Jan-2021 00:01:12.150 +- 00:00:03.261 Dynamical Time.
   #
   date.threshold.decimal <- mpfr("2021.000002287871666339920000000000000000", 128)
   if (date.target.decimal < date.threshold.decimal) {
      cat("\nWarning in function IsUTCLeapYear...")
      cat("\n   Input date: ", formatMpfr(date.target.decimal, NULL), sep="")
      cat("\n   Expected date >= ", formatMpfr(date.threshold.decimal, NULL), sep="")
   }

   # Get the year of the input Dynamical Time.
   year.target <- year(date.target)

   # Allow for Dynamical Times falling shortly after New Year's Eve of a UTC leap year.
   #
   #    E.g. 2024 is a leap year.
   #    Assume 01-Jan-2025 00:00:00.000 UTC --> 01-Jan-2025 00:01:14.467 +- 4.561s Dynamical Time.
   #                                           ~2025.000002361344968448980000000000000003.
   #    So 2025 Dynamical Time dates < 2025.000002361344968448980000000000000003 actually started in 2024 UTC.
   #

   # Get the index of the input year in the vector vec.post.leap.years, if it's there.
   # N.B. R indicies start from 1, not 0.
   x <- match(year.target, G.seq.leap.year.check, nomatch = 0)
   if (x > 0) {
      if (date.target.decimal < G.vec.DT.transition[x]) {
         year.target <- year.target - 1
      }
   }

   return(leap_year(year.target))
}


IsValidDate <- function(date.target) {

   # This program is designed to process 21st century dates.
   #    I.e. 01-Jan-2001 to 31-Dec-2100.
   #
   # Input:
   #    date.target   (date should be a UTC time).

   if (date.target >= dmy_hms("01-01-2001 00:00:00", tz="UTC")) {
      if (date.target < dmy_hms("01-01-2101 00:00:00", tz="UTC")) {
         return(TRUE)
      } else {
         return(FALSE)
      }
   } else {
      return(FALSE)
   }
}


TimeDynamicalToJDE <- function(DT) {

   # Convert Dynamical Time to JDE (Julian Ephemeris Day).
   #    See Astronomical Algorithms, Meeus, chapter 7.
   #    N.B. Assumes a Gregorian calendar.
   #
   # Input:
   #   DT   (a date-time, in Dynamical Time format).

   Y <- G.zero.mpfr + year(DT)
   M <- G.zero.mpfr + month(DT)
   D <- G.zero.mpfr + day(DT)

   # Add the date's fractional part to D.
   D <- D + {DT - dmy_hms(paste(sprintf("%02d", as.integer(D)), "-", sprintf("%02d", as.integer(M)), "-", as.integer(Y), " 00:00:00", sep=""), tz="UTC")} / G.1.day

   if (M < 3) {
      Y <- Y - 1
      M <- M + 12
   }

   A <- floor(Y * 0.01)
   B <- 2 - A + floor(A * 0.25)

   return(floor(365.25 * {Y + 4716}) + floor(30.6001 * {M + 1}) + D + B - 1524.5)
}


TimeDynamicalToUniversal <- function(DT) {

   # Convert Dynamical Time to Coordinated Universal Time.
   #
   #    N.B. This function reverses the process in TimeUniversalToDynamical.
   #    N.B. It's assumed a 21st century date will be processed.
   #         I.e. 01-Jan-2001 to 31-Dec-2100.
   #         Limited support for other dates, for testing purposes, has been provided.
   #
   # Input:
   #   DT   (a date-time, in Dynamical Time format).
   # Output:
   #   UTC = DT - offset.

   # These values are given in UTC, to avoid accidental assignment to other time zones.
   # If the UTC label was omitted, they would be valid dynamical time values.
   #
   if (DT >= dmy_hms("01-01-2021 00:01:09.18", tz="UTC")) {
      # Get UTC via algorithmic estimation.
      #    N.B. GetUTC returns a date-time.
      #         GetUTC returns uncertainty as a time difference.
      return(GetUTC(DT))

   } else if (DT >= dmy_hms("01-01-2017 00:01:08.18", tz="UTC")) {
      # Offset = 42.18 + 27 seconds.
      return(list("value" = DT - G.69.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-2015 00:01:07.18", tz="UTC")) {
      # Offset = 42.18 + 26 seconds.
      return(list("value" = DT - G.68.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-2012 00:01:06.18", tz="UTC")) {
      # Offset = 42.18 + 25 seconds.
      return(list("value" = DT - G.67.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-2009 00:01:05.18", tz="UTC")) {
      # Offset = 42.18 + 24 seconds.
      return(list("value" = DT - G.66.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-2006 00:01:04.18", tz="UTC")) {
      # Offset = 42.18 + 23 seconds.
      return(list("value" = DT - G.65.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1999 00:01:03.18", tz="UTC")) {
      # Offset = 42.18 + 22 seconds.
      return(list("value" = DT - G.64.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-1997 00:01:02.18", tz="UTC")) {
      # Offset = 42.18 + 21 seconds.
      return(list("value" = DT - G.63.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1996 00:01:01.18", tz="UTC")) {
      # Offset = 42.18 + 20 seconds.
      return(list("value" = DT - G.62.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-1994 00:01:00.18", tz="UTC")) {
      # Offset = 42.18 + 19 seconds.
      return(list("value" = DT - G.61.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-1993 00:00:59.18", tz="UTC")) {
      # Offset = 42.18 + 18 seconds.
      return(list("value" = DT - G.60.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-1992 00:00:58.18", tz="UTC")) {
      # Offset = 42.18 + 17 seconds.
      return(list("value" = DT - G.59.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1991 00:00:57.18", tz="UTC")) {
      # Offset = 42.18 + 16 seconds.
      return(list("value" = DT - G.58.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1990 00:00:56.18", tz="UTC")) {
      # Offset = 42.18 + 15 seconds.
      return(list("value" = DT - G.57.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1988 00:00:55.18", tz="UTC")) {
      # Offset = 42.18 + 14 seconds.
      return(list("value" = DT - G.56.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-1985 00:00:54.18", tz="UTC")) {
      # Offset = 42.18 + 13 seconds.
      return(list("value" = DT - G.55.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-1983 00:00:53.18", tz="UTC")) {
      # Offset = 42.18 + 12 seconds.
      return(list("value" = DT - G.54.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-1982 00:00:52.18", tz="UTC")) {
      # Offset = 42.18 + 11 seconds.
      return(list("value" = DT - G.53.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-1981 00:00:51.18", tz="UTC")) {
      # Offset = 42.18 + 10 seconds.
      return(list("value" = DT - G.52.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1980 00:00:50.18", tz="UTC")) {
      # Offset = 42.18 + 9 seconds.
      return(list("value" = DT - G.51.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1979 00:00:49.18", tz="UTC")) {
      # Offset = 42.18 + 8 seconds.
      return(list("value" = DT - G.50.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1978 00:00:48.18", tz="UTC")) {
      # Offset = 42.18 + 7 seconds.
      return(list("value" = DT - G.49.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1977 00:00:47.18", tz="UTC")) {
      # Offset = 42.18 + 6 seconds.
      return(list("value" = DT - G.48.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1976 00:00:46.18", tz="UTC")) {
      # Offset = 42.18 + 5 seconds.
      return(list("value" = DT - G.47.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1975 00:00:45.18", tz="UTC")) {
      # Offset = 42.18 + 4 seconds.
      return(list("value" = DT - G.46.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1974 00:00:44.18", tz="UTC")) {
      # Offset = 42.18 + 3 seconds.
      return(list("value" = DT - G.45.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-01-1973 00:00:43.18", tz="UTC")) {
      # Offset = 42.18 + 2 seconds.
      return(list("value" = DT - G.44.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (DT >= dmy_hms("01-07-1972 00:00:42.18", tz="UTC")) {
      # Offset = 42.18 + 1 seconds.
      return(list("value" = DT - G.43.18.seconds, "uncertainty" = G.zero.seconds))

   } else {
      return(list("value" = DT - G.42.18.seconds, "uncertainty" = G.zero.seconds))
   }
}


TimeJDEToDynamical <- function(JDE.mpfr) {

   # Convert JDE (Julian Ephemeris Day) to Dynamical Time.
   #    See Astronomical Algorithms, Meeus, chapter 7.
   #    N.B. Error on p. 63: "2291 161" should be "2299 161".
   #
   # Input:
   #   JDE.mpfr   Target date, in Julian Ephemeris Days, best passed in mpfr format with, e.g. 128-bit accuracy.
   #              E.g. R stores a passed value of 2443259.9 as 2443259.8999999999 by default.
   #                   This causes errors of +- 1 second easily.

   # Input must be >= 0.
   if (JDE.mpfr < 0) {
      cat("\nError in function TimeJDEToDynamical...")
      cat("\nInput dynamical time; JDE.mpfr:\n")
      print(JDE.mpfr, digits=20)
      cat("\nNeed dynamical time >= 0.\n")
      stopifnot(FALSE)
   }

   JDE <- 0.5 + JDE.mpfr
   Z <- trunc(JDE)   # Integer part.
   F <- JDE - Z      # Fractional part.

   if (Z < 2299161) {
      A <- Z
   } else {
      alpha <- floor({Z - 1867216.25} / 36524.25)
      A <- Z + 1 + alpha - floor(alpha * 0.25)
   }

   B <- A + 1524
   C <- floor({B - 122.1} / 365.25)
   D <- floor(365.25 * C)
   E <- floor({B - D} / 30.6001)

   # Day, as a decimal.
   day <- B - D - floor(30.6001 * E) + F

   # Month, as an integer.
   if (E < 14) {
      month <- E - 1
   } else {
      month <- E - 13
   }

   # Year, as an integer.
   if (month > 2) {
      year <- C - 4716
   } else {
      year <- C - 4715
   }

   # The Dynamical Time returned here is stored as a UTC date-time format.
   # If the time zone label is neglected however, the date and time are the correct Dynamical Time.
   # N.B. Don't forget to add any fractional part of the day to the output.

   return(dmy_hms(paste(sprintf("%02d", as.integer(trunc(day))), "-", sprintf("%02d", as.integer(month)), "-", as.integer(year), " 00:00:00", sep=""), tz="UTC") + ddays(day - trunc(day)))
}


TimeUniversalToDynamical <- function(UTC) {

   # Convert Coordinated Universal Time to Dynamical Time.
   #    N.B. It's assumed a 21st century date will be processed.
   #         I.e. 01-Jan-2001 to 31-Dec-2100.
   #         Limited support for other dates, for testing purposes, has been provided.
   #
   # Input:
   #   UTC   (target date, in Coordinated Universal Time).
   #
   # N.B. The output value is stored as a UTC date-time format.
   #      If the time zone label is neglected however, the date and time are the correct Dynamical Time.
   #
   #    DT = UTC + 42.18 s + # leap seconds
   #
   # Ref: Polynomial approximations to Delta T, 1620-2000 AD (Meeus and Simons).
   #
   # N.B. Dynamical time can be determined exactly for dates from 1972 to the present, estimated after.
   # N.B. Number of leap seconds added from 1972-2020 = 27.
   # N.B. Timings are available from Wikipedia, the Astronomical Almanac, etc.

   if (UTC >= G.2021.start) {
      # Get delta T via algorithmic estimation.
      # N.B. The algorithm finds delta T for a UT (or UT1).
      # N.B. Thus, allow for uncertainty in UTC vs UT1 (always <= 0.9 s; as maintained by leap seconds).
      DelT <- CalculateWithUncertainty(GetDelT, UTC, G.900.milliseconds, TRUE)
      return(list("value" = UTC + dseconds(DelT$value), "uncertainty" = dseconds(DelT$uncertainty)))

   } else if (UTC >= G.2017.start) {
      # offset = 42.18 seconds + 27 leap seconds.
      # No forecast change to the end of 2021; keep leap seconds unchanged.
      return(list("value" = UTC + G.69.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.2015.mid) {
      # offset = 42.18 seconds + 26 leap seconds.
      return(list("value" = UTC + G.68.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.2012.mid) {
      # offset = 42.18 seconds + 25 leap seconds.
      return(list("value" = UTC + G.67.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.2009.start) {
      # offset = 42.18 seconds + 24 leap seconds.
      return(list("value" = UTC + G.66.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.2006.start) {
      # offset = 42.18 seconds + 23 leap seconds.
      return(list("value" = UTC + G.65.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1999.start) {
      # offset = 42.18 seconds + 22 leap seconds.
      return(list("value" = UTC + G.64.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1997.mid) {
      # offset = 42.18 seconds + 21 leap seconds.
      return(list("value" = UTC + G.63.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1996.start) {
      # offset = 42.18 seconds + 20 leap seconds.
      return(list("value" = UTC + G.62.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1994.mid) {
      # offset = 42.18 seconds + 19 leap seconds.
      return(list("value" = UTC + G.61.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1993.mid) {
      # offset = 42.18 seconds + 18 leap seconds.
      return(list("value" = UTC + G.60.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1992.mid) {
      # offset = 42.18 seconds + 17 leap seconds.
      return(list("value" = UTC + G.59.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1991.start) {
      # offset = 42.18 seconds + 16 leap seconds.
      return(list("value" = UTC + G.58.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1990.start) {
      # offset = 42.18 seconds + 15 leap seconds.
      return(list("value" = UTC + G.57.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1988.start) {
      # offset = 42.18 seconds + 14 leap seconds.
      return(list("value" = UTC + G.56.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1985.mid) {
      # offset = 42.18 seconds + 13 leap seconds.
      return(list("value" = UTC + G.55.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1983.mid) {
      # offset = 42.18 seconds + 12 leap seconds.
      return(list("value" = UTC + G.54.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1982.mid) {
      # offset = 42.18 seconds + 11 leap seconds.
      return(list("value" = UTC + G.53.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1981.mid) {
      # offset = 42.18 seconds + 10 leap seconds.
      return(list("value" = UTC + G.52.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1980.start) {
      # offset = 42.18 seconds + 9 leap seconds.
      return(list("value" = UTC + G.51.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1979.start) {
      # offset = 42.18 seconds + 8 leap seconds.
      return(list("value" = UTC + G.50.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1978.start) {
      # offset = 42.18 seconds + 7 leap seconds.
      return(list("value" = UTC + G.49.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1977.start) {
      # offset = 42.18 seconds + 6 leap seconds.
      return(list("value" = UTC + G.48.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1976.start) {
      # offset = 42.18 seconds + 5 leap seconds.
      return(list("value" = UTC + G.47.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1975.start) {
      # offset = 42.18 seconds + 4 leap seconds.
      return(list("value" = UTC + G.46.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1974.start) {
      # offset = 42.18 seconds + 3 leap seconds.
      return(list("value" = UTC + G.45.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1973.start) {
      # offset = 42.18 seconds + 2 leap seconds.
      return(list("value" = UTC + G.44.18.seconds, "uncertainty" = G.zero.seconds))

   } else if (UTC >= G.1972.mid) {
      # offset = 42.18 seconds + 1 leap seconds.
      return(list("value" = UTC + G.43.18.seconds, "uncertainty" = G.zero.seconds))

   } else {
      # offset = 42.18 seconds + 0 leap seconds.
      return(list("value" = UTC + G.42.18.seconds, "uncertainty" = G.zero.seconds))
   }
}


#######################
# Set global variables.

# Set output verbosity.
G.verbosity <- FALSE

# Set lunar apsis.
# G.lunar.apsis <- "apogee"
G.lunar.apsis <- "perigee"

# Set lunar phase.
# G.lunar.phase <- "new moon"
# G.lunar.phase <- "first quarter"
G.lunar.phase <- "full moon"
# G.lunar.phase <- "last quarter"

# Set the path to a Fortran shared library for ELP-2000/82.
# Designate the shared library extension in platform-independent manner.
G.library.path <- file.path(paste("elp82b_2", .Platform$dynlib.ext, sep=""))

# Control extra fonts installation.
G.extra.font.installed <- TRUE

# Set general purpose numeric globals.
G.max.indent <- 27
G.pi.on.180.mpfr <- Const("pi", prec = 128) / mpfr("180", 128)
G.360.mpfr <- mpfr("360", 128)
G.1.mpfr <- mpfr("1", 128)
G.zero.mpfr <- mpfr("0", 128)

# Set durations.
G.zero.seconds <- dseconds(G.zero.mpfr)
G.900.milliseconds <- dmilliseconds(mpfr("900", 128))
G.42.18.seconds <- dseconds(mpfr("42.18", 128))
G.43.18.seconds <- dseconds(mpfr("43.18", 128))
G.44.18.seconds <- dseconds(mpfr("44.18", 128))
G.45.18.seconds <- dseconds(mpfr("45.18", 128))
G.46.18.seconds <- dseconds(mpfr("46.18", 128))
G.47.18.seconds <- dseconds(mpfr("47.18", 128))
G.48.18.seconds <- dseconds(mpfr("48.18", 128))
G.49.18.seconds <- dseconds(mpfr("49.18", 128))
G.50.18.seconds <- dseconds(mpfr("50.18", 128))
G.51.18.seconds <- dseconds(mpfr("51.18", 128))
G.52.18.seconds <- dseconds(mpfr("52.18", 128))
G.53.18.seconds <- dseconds(mpfr("53.18", 128))
G.54.18.seconds <- dseconds(mpfr("54.18", 128))
G.55.18.seconds <- dseconds(mpfr("55.18", 128))
G.56.18.seconds <- dseconds(mpfr("56.18", 128))
G.57.18.seconds <- dseconds(mpfr("57.18", 128))
G.58.18.seconds <- dseconds(mpfr("58.18", 128))
G.59.18.seconds <- dseconds(mpfr("59.18", 128))
G.60.18.seconds <- dseconds(mpfr("60.18", 128))
G.61.18.seconds <- dseconds(mpfr("61.18", 128))
G.62.18.seconds <- dseconds(mpfr("62.18", 128))
G.63.18.seconds <- dseconds(mpfr("63.18", 128))
G.64.18.seconds <- dseconds(mpfr("64.18", 128))
G.65.18.seconds <- dseconds(mpfr("65.18", 128))
G.66.18.seconds <- dseconds(mpfr("66.18", 128))
G.67.18.seconds <- dseconds(mpfr("67.18", 128))
G.68.18.seconds <- dseconds(mpfr("68.18", 128))
G.69.18.seconds <- dseconds(mpfr("69.18", 128))
G.5.minutes <- dminutes(mpfr("5", 128))
G.1.day <- ddays(G.1.mpfr)

# Set time conversion constants.
G.2021.start <- dmy_hms("01-01-2021 00:00:00", tz="UTC")
G.2017.start <- dmy_hms("01-01-2017 00:00:00", tz="UTC")
G.2015.mid <- dmy_hms("01-07-2015 00:00:00", tz="UTC")
G.2012.mid <- dmy_hms("01-07-2012 00:00:00", tz="UTC")
G.2009.start <- dmy_hms("01-01-2009 00:00:00", tz="UTC")
G.2006.start <- dmy_hms("01-01-2006 00:00:00", tz="UTC")
G.1999.start <- dmy_hms("01-01-1999 00:00:00", tz="UTC")
G.1997.mid <- dmy_hms("01-07-1997 00:00:00", tz="UTC")
G.1996.start <- dmy_hms("01-01-1996 00:00:00", tz="UTC")
G.1994.mid <- dmy_hms("01-07-1994 00:00:00", tz="UTC")
G.1993.mid <- dmy_hms("01-07-1993 00:00:00", tz="UTC")
G.1992.mid <- dmy_hms("01-07-1992 00:00:00", tz="UTC")
G.1991.start <- dmy_hms("01-01-1991 00:00:00", tz="UTC")
G.1990.start <- dmy_hms("01-01-1990 00:00:00", tz="UTC")
G.1988.start <- dmy_hms("01-01-1988 00:00:00", tz="UTC")
G.1985.mid <- dmy_hms("01-07-1985 00:00:00", tz="UTC")
G.1983.mid <- dmy_hms("01-07-1983 00:00:00", tz="UTC")
G.1982.mid <- dmy_hms("01-07-1982 00:00:00", tz="UTC")
G.1981.mid <- dmy_hms("01-07-1981 00:00:00", tz="UTC")
G.1980.start <- dmy_hms("01-01-1980 00:00:00", tz="UTC")
G.1979.start <- dmy_hms("01-01-1979 00:00:00", tz="UTC")
G.1978.start <- dmy_hms("01-01-1978 00:00:00", tz="UTC")
G.1977.start <- dmy_hms("01-01-1977 00:00:00", tz="UTC")
G.1976.start <- dmy_hms("01-01-1976 00:00:00", tz="UTC")
G.1975.start <- dmy_hms("01-01-1975 00:00:00", tz="UTC")
G.1974.start <- dmy_hms("01-01-1974 00:00:00", tz="UTC")
G.1973.start <- dmy_hms("01-01-1973 00:00:00", tz="UTC")
G.1972.mid <- dmy_hms("01-07-1972 00:00:00", tz="UTC")

# Set thresholds and miscellaneous constants.
G.365.day.scaling <- 1 / mpfr("31536000", 128)
G.366.day.scaling <- 1 / mpfr("31622400", 128)
G.lunar.argument.latitude.constant <- 1 / mpfr("3526000", 128)
G.lunar.mean.anomaly.constant <- 1 / mpfr("69699", 128)
G.lunar.mean.elongation.constant <- 1 / mpfr("545868", 128)
G.threshold.GetDelT <- G.zero.mpfr + decimal_date(dmy_hms("01-01-2021 00:00:00", tz="UTC") - G.900.milliseconds)
G.uncertainty.constant.GetDelT <- mpfr("0.058", 128) / 3

# Set leap year check transition constants.
G.vec.DT.transition <- c(mpfr("2025.000002361344968448980000000000000003", 128),
                         mpfr("2029.000002440489652144610000000000000000", 128),
                         mpfr("2033.000002525305490053139999999999999999", 128),
                         mpfr("2037.000002615792482174580000000000000000", 128),
                         mpfr("2041.000002711950855882609999999999999999", 128),
                         mpfr("2045.000002813780383803530000000000000003", 128),
                         mpfr("2049.000002921281065937360000000000000003", 128),
                         mpfr("2053.000003143493358948040000000000000003", 128),
                         mpfr("2057.000003405644292797659999999999999996", 128),
                         mpfr("2061.000003671042577479970000000000000000", 128),
                         mpfr("2065.000003939687758247599999999999999994", 128),
                         mpfr("2069.000004211580289847920000000000000000", 128),
                         mpfr("2073.000004486719717533559999999999999995", 128),
                         mpfr("2077.000004765106496051890000000000000002", 128),
                         mpfr("2081.000005046740170655539999999999999999", 128),
                         mpfr("2085.000005331621196091869999999999999999", 128),
                         mpfr("2089.000005619748662866189999999999999998", 128),
                         mpfr("2093.000005911123935220530000000000000000", 128),
                         mpfr("2097.000006205745648912850000000000000004", 128))

# Set leap year check sequence.
G.seq.leap.year.check <- seq(2025, 2097, by=4)

# Lunar apogee calculation coefficients.
G.vec.apogee.coeff <- c(mpfr( "0.4392", 128),
                        mpfr( "0.0684", 128),
                        mpfr( "0.0456", 128),
                        mpfr( "0.0426", 128),
                        mpfr( "0.0212", 128),
                        mpfr("-0.0189", 128),
                        mpfr( "0.0144", 128),
                        mpfr( "0.0113", 128),
                        mpfr( "0.0047", 128),
                        mpfr( "0.0036", 128),
                        mpfr( "0.0035", 128),
                        mpfr( "0.0034", 128),
                        mpfr("-0.0034", 128),
                        mpfr( "0.0022", 128),
                        mpfr("-0.0017", 128),
                        mpfr( "0.0013", 128),
                        mpfr( "0.0011", 128),
                        mpfr( "0.0010", 128),
                        mpfr( "0.0009", 128),
                        mpfr( "0.0007", 128),
                        mpfr( "0.0006", 128),
                        mpfr( "0.0005", 128),
                        mpfr( "0.0005", 128),
                        mpfr( "0.0004", 128),
                        mpfr( "0.0004", 128),
                        mpfr( "0.0004", 128),
                        mpfr("-0.0004", 128),
                        mpfr("-0.0004", 128),
                        mpfr( "0.0003", 128),
                        mpfr( "0.0003", 128),
                        mpfr( "0.0003", 128),
                        mpfr("-0.0003", 128))
#
G.vec.apogee.subtrahend <- c(G.zero.mpfr,
                             G.zero.mpfr,
                             mpfr("-0.00011", 128),
                             mpfr("-0.00011", 128),
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr,
                             G.zero.mpfr)

# Lunar perigee calculation coefficients.
G.vec.perigee.coeff <- c(mpfr("-1.6769", 128),
                         mpfr( "0.4589", 128),
                         mpfr("-0.1856", 128),
                         mpfr( "0.0883", 128),
                         mpfr("-0.0773", 128),
                         mpfr( "0.0502", 128),
                         mpfr("-0.0460", 128),
                         mpfr( "0.0422", 128),
                         mpfr("-0.0256", 128),
                         mpfr( "0.0253", 128),
                         mpfr( "0.0237", 128),
                         mpfr( "0.0162", 128),
                         mpfr("-0.0145", 128),
                         mpfr( "0.0129", 128),
                         mpfr("-0.0112", 128),
                         mpfr("-0.0104", 128),
                         mpfr( "0.0086", 128),
                         mpfr( "0.0069", 128),
                         mpfr( "0.0066", 128),
                         mpfr("-0.0053", 128),
                         mpfr("-0.0052", 128),
                         mpfr("-0.0046", 128),
                         mpfr("-0.0041", 128),
                         mpfr( "0.0040", 128),
                         mpfr( "0.0032", 128),
                         mpfr("-0.0032", 128),
                         mpfr( "0.0031", 128),
                         mpfr("-0.0029", 128),
                         mpfr( "0.0027", 128),
                         mpfr( "0.0027", 128),
                         mpfr("-0.0027", 128),
                         mpfr( "0.0024", 128),
                         mpfr("-0.0021", 128),
                         mpfr("-0.0021", 128),
                         mpfr("-0.0021", 128),
                         mpfr( "0.0019", 128),
                         mpfr("-0.0018", 128),
                         mpfr("-0.0014", 128),
                         mpfr("-0.0014", 128),
                         mpfr("-0.0014", 128),
                         mpfr( "0.0014", 128),
                         mpfr("-0.0014", 128),
                         mpfr( "0.0013", 128),
                         mpfr( "0.0013", 128),
                         mpfr( "0.0011", 128),
                         mpfr("-0.0011", 128),
                         mpfr("-0.0010", 128),
                         mpfr("-0.0009", 128),
                         mpfr("-0.0008", 128),
                         mpfr( "0.0008", 128),
                         mpfr( "0.0008", 128),
                         mpfr( "0.0007", 128),
                         mpfr( "0.0007", 128),
                         mpfr( "0.0007", 128),
                         mpfr("-0.0006", 128),
                         mpfr("-0.0006", 128),
                         mpfr( "0.0006", 128),
                         mpfr( "0.0005", 128),
                         mpfr( "0.0005", 128),
                         mpfr("-0.0004", 128))
#
G.vec.perigee.subtrahend <- c(G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              mpfr( "0.00019", 128),
                              mpfr("-0.00013", 128),
                              G.zero.mpfr,
                              mpfr("-0.00011", 128),
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr)

# Lunar distance periodic calculation coefficients.
G.vec.ld.coeff <- c(-20905355, -3699111, -2955968, -569925,
                        48888,    -3149,   246158, -152138,
                      -170733,  -204586,  -129620,  108743,
                       104755,    10321,    79661,  -34782,
                       -23210,   -21636,    24208,   30824,
                        -8379,   -16675,   -12831,  -10445,
                       -11650,    14403,    -7003,   10056,
                         6322,    -9884,     5751,   -4950,
                         4130,    -3958,     3258,    2616,
                        -1897,    -2117,     2354,   -1423,
                        -1117,    -1571,    -1739,   -4421,
                         1165,     8752)

# Set lunar distance masks for vectorised operations.
G.mask.distance.D <- c(0,
                       2,
                       2,
                       0,
                       0,
                       0,
                       2,
                       2,
                       2,
                       2,
                       0,
                       1,
                       0,
                       2,
                       0,
                       4,
                       0,
                       4,
                       2,
                       2,
                       1,
                       1,
                       2,
                       2,
                       4,
                       2,
                       0,
                       2,
                       1,
                       2,
                       0,
                       2,
                       2,
                       4,
                       3,
                       2,
                       4,
                       0,
                       2,
                       4,
                       0,
                       4,
                       1,
                       0,
                       0,
                       2)
#
G.mask.distance.F.x.2 <- c( 0,
                            0,
                            0,
                            0,
                            0,
                            1,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                           -1,
                           -1,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                           -1,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                           -1,
                            0,
                           -1)
#
G.mask.distance.M <- c( 0,
                        0,
                        0,
                        0,
                        1,
                        0,
                        0,
                       -1,
                        0,
                       -1,
                        1,
                        0,
                        1,
                        0,
                        0,
                        0,
                        0,
                        0,
                        1,
                        1,
                        0,
                        1,
                       -1,
                        0,
                        0,
                        0,
                        1,
                       -1,
                        0,
                       -2,
                        1,
                       -2,
                        0,
                       -1,
                        0,
                        1,
                       -1,
                        2,
                        2,
                        0,
                        0,
                       -1,
                        0,
                        0,
                        2,
                        0)
#
G.mask.distance.M.prime <- c( 1,
                             -1,
                              0,
                              2,
                              0,
                              0,
                             -2,
                             -1,
                              1,
                              0,
                             -1,
                              0,
                              1,
                              0,
                              1,
                             -1,
                              3,
                             -2,
                             -1,
                              0,
                             -1,
                              0,
                              1,
                              2,
                              0,
                             -3,
                             -2,
                             -2,
                              1,
                              0,
                              2,
                             -1,
                              1,
                             -1,
                             -1,
                              1,
                             -2,
                             -1,
                             -1,
                              1,
                              4,
                              0,
                             -2,
                              2,
                              1,
                             -1)

# Lunar phase (full moon) calculation coefficients.
G.vec.ph.full.coeff <- c(mpfr("-0.40614", 128),
                         mpfr( "0.17302", 128),
                         mpfr( "0.01614", 128),
                         mpfr( "0.01043", 128),
                         mpfr( "0.00734", 128),
                         mpfr("-0.00515", 128),
                         mpfr( "0.00209", 128),
                         mpfr("-0.00111", 128),
                         mpfr("-0.00057", 128),
                         mpfr( "0.00056", 128),
                         mpfr("-0.00042", 128),
                         mpfr( "0.00042", 128),
                         mpfr( "0.00038", 128),
                         mpfr("-0.00024", 128),
                         mpfr("-0.00017", 128),
                         mpfr("-0.00007", 128),
                         mpfr( "0.00004", 128),
                         mpfr( "0.00004", 128),
                         mpfr( "0.00003", 128),
                         mpfr( "0.00003", 128),
                         mpfr("-0.00003", 128),
                         mpfr( "0.00003", 128),
                         mpfr("-0.00002", 128),
                         mpfr("-0.00002", 128),
                         mpfr( "0.00002", 128))

# Lunar phase (new moon) calculation coefficients.
G.vec.ph.new.coeff <- c(mpfr("-0.40720", 128),
                        mpfr( "0.17241", 128),
                        mpfr( "0.01608", 128),
                        mpfr( "0.01039", 128),
                        mpfr( "0.00739", 128),
                        mpfr("-0.00514", 128),
                        mpfr( "0.00208", 128),
                        mpfr("-0.00111", 128),
                        mpfr("-0.00057", 128),
                        mpfr( "0.00056", 128),
                        mpfr("-0.00042", 128),
                        mpfr( "0.00042", 128),
                        mpfr( "0.00038", 128),
                        mpfr("-0.00024", 128),
                        mpfr("-0.00017", 128),
                        mpfr("-0.00007", 128),
                        mpfr( "0.00004", 128),
                        mpfr( "0.00004", 128),
                        mpfr( "0.00003", 128),
                        mpfr( "0.00003", 128),
                        mpfr("-0.00003", 128),
                        mpfr( "0.00003", 128),
                        mpfr("-0.00002", 128),
                        mpfr("-0.00002", 128),
                        mpfr( "0.00002", 128))

# Lunar phase (quarter phase) calculation coefficients.
G.vec.ph.qrt.coeff <- c(mpfr("-0.62801", 128),
                        mpfr( "0.17172", 128),
                        mpfr("-0.01183", 128),
                        mpfr( "0.00862", 128),
                        mpfr( "0.00804", 128),
                        mpfr( "0.00454", 128),
                        mpfr( "0.00204", 128),
                        mpfr("-0.00180", 128),
                        mpfr("-0.00070", 128),
                        mpfr("-0.00040", 128),
                        mpfr("-0.00034", 128),
                        mpfr( "0.00032", 128),
                        mpfr( "0.00032", 128),
                        mpfr("-0.00028", 128),
                        mpfr( "0.00027", 128),
                        mpfr("-0.00017", 128),
                        mpfr("-0.00005", 128),
                        mpfr( "0.00004", 128),
                        mpfr("-0.00004", 128),
                        mpfr( "0.00004", 128),
                        mpfr( "0.00003", 128),
                        mpfr( "0.00003", 128),
                        mpfr( "0.00002", 128),
                        mpfr( "0.00002", 128),
                        mpfr("-0.00002", 128))

# Lunar phase (quarter phase) supplemental corrections.
G.vec.ph.sup.coeff <- c(mpfr("-0.00038", 128),
                        mpfr(" 0.00026", 128),
                        mpfr("-0.00002", 128),
                        mpfr(" 0.00002", 128),
                        mpfr(" 0.00002", 128))

# Lunar phase additional corrections coefficients.
G.vec.ph.add.coeff.outer <- c(mpfr("0.000325", 128),
                              mpfr("0.000165", 128),
                              mpfr("0.000164", 128),
                              mpfr("0.000126", 128),
                              mpfr("0.000110", 128),
                              mpfr("0.000062", 128),
                              mpfr("0.000060", 128),
                              mpfr("0.000056", 128),
                              mpfr("0.000047", 128),
                              mpfr("0.000042", 128),
                              mpfr("0.000040", 128),
                              mpfr("0.000037", 128),
                              mpfr("0.000035", 128),
                              mpfr("0.000023", 128))
#
G.vec.ph.add.addend      <- c(mpfr("299.77", 128),
                              mpfr("251.88", 128),
                              mpfr("251.83", 128),
                              mpfr("349.42", 128),
                              mpfr( "84.66", 128),
                              mpfr("141.74", 128),
                              mpfr("207.14", 128),
                              mpfr("154.84", 128),
                              mpfr( "34.52", 128),
                              mpfr("207.19", 128),
                              mpfr("291.34", 128),
                              mpfr("161.72", 128),
                              mpfr("239.56", 128),
                              mpfr("331.55", 128))
#
G.vec.ph.add.coeff.inner <- c(mpfr( "0.107408", 128),
                              mpfr( "0.016321", 128),
                              mpfr("26.651886", 128),
                              mpfr("36.412478", 128),
                              mpfr("18.206239", 128),
                              mpfr("53.303771", 128),
                              mpfr( "2.453732", 128),
                              mpfr( "7.306860", 128),
                              mpfr("27.261239", 128),
                              mpfr( "0.121824", 128),
                              mpfr( "1.844379", 128),
                              mpfr("24.198154", 128),
                              mpfr("25.513099", 128),
                              mpfr( "3.592518", 128))
#
G.vec.ph.add.subtrahend  <- c(mpfr("0.009173", 128),
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr,
                              G.zero.mpfr)
# Precision Testing
# -----------------
#
# Quantify the effect of using R's default double (53-bit) precision vs mpfr 128-bit precision.
# Here, converting a date-time in JDE (Julian Ephemeris Day) to Dynamical Time is the test case.

G.lunar.apsis <- "perigee"
G.lunar.phase <- "full moon"

JDE.A <- 2443259.9
JDE.B <- mpfr("2443259.9", 128)

result.A <- TimeJDEToDynamical(JDE.A)
result.B <- TimeJDEToDynamical(JDE.B)

cat("Precision Testing\n-----------------\n\n", sep="")

cat("JDE, low precision...\n", sep="")
print(JDE.A, digits = 17)
cat("\nJDE, high precision...\n", sep="")
print(JDE.B, digits = 17)

cat("\n\nJDE --> Dynamical time, low precision (top) vs high precision (bottom)...\n", sep="")
print(result.A, digits = 17)
print(result.B, digits = 17)

cat("\nWhat's the corresponding difference in lunar distance?")
lunar.distance.A <- GetLunarDistance(JDE.A)
lunar.distance.B <- GetLunarDistance(JDE.B)
difference <- abs(lunar.distance.A$value - lunar.distance.B$value) * mpfr("1000000", 128)
cat("\n\U2248", formatMpfr(difference, 8), " mm\n", sep="")
## Precision Testing
## -----------------
## 
## JDE, low precision...
## [1] 2443259.8999999999
## 
## JDE, high precision...
## 1 'mpfr' number of precision  128   bits 
## [1] 2443259.9
## 
## 
## JDE --> Dynamical time, low precision (top) vs high precision (bottom)...
## [1] "1977-04-26 09:35:59 UTC"
## [1] "1977-04-26 09:36:00 UTC"
## 
## What's the corresponding difference in lunar distance?
## ˜0.46645042 mm
# Testing
# -------

cat("Testing\n-------\n\n", sep="")
cat("Test the system's functions against examples in 'Astronomical Algorithms'.\n")
cat("Ref: Meeus J., Astronomical Algorithms, 2nd Ed, Willmann-Bell, Richmond, VA, 1998.\n")

######
cat("\n-------\nTesting: New Moon Feb 1977 (page 353)\n\n")
G.lunar.phase <- "new moon"
date.target.UTC <- date_decimal(1977.13)
date.target.dynamical <- TimeUniversalToDynamical(date.target.UTC)
phase.date <- CalculateWithUncertainty(GetLunarPhaseJDE, date.target.dynamical$value, date.target.dynamical$uncertainty, TRUE)
label <- paste("Next ", G.lunar.phase, ":", sep="")
cat(label, replicate(G.max.indent - nchar(label), " "), formatMpfr(phase.date$value, 11), " +- ", formatMpfr(phase.date$uncertainty, 1), " JDE\n", sep="")
phase.date.dynamical <- CalculateWithUncertainty(TimeJDEToDynamical, phase.date$value, phase.date$uncertainty, FALSE)
cat(replicate(G.max.indent - 2, " "), "= ", format(round_date(phase.date.dynamical$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ", format(hms::as_hms(signif(as.numeric(phase.date.dynamical$uncertainty, units = "secs"), 2)), "%H:%M:%S"), " Dynamical Time\n", sep="")
phase.date.UTC <- CalculateWithUncertainty(TimeDynamicalToUniversal, phase.date.dynamical$value, phase.date.dynamical$uncertainty, TRUE)
cat(replicate(G.max.indent - 2, " "), "= ", format(round_date(phase.date.UTC$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ", format(hms::as_hms(signif(as.numeric(phase.date.UTC$uncertainty, units = "secs"), 2)), "%H:%M:%S"), " UTC\n\n", sep="")

label <- paste("Expected:")
cat(label, replicate(G.max.indent - nchar(label), " "), "18-Feb-1977 03:37 UT.\n", sep="")
cat(replicate(G.max.indent, " "), "N.B. UT = UTC +- 0.9 s.\n", sep="")
######


######
cat("\n-------\nTesting: Last Quarter Moon Jan 2044 (page 353)\n\n")
G.lunar.phase <- "last quarter"
date.target.UTC <- date_decimal(2044.026)
date.target.dynamical <- TimeUniversalToDynamical(date.target.UTC)
phase.date <- CalculateWithUncertainty(GetLunarPhaseJDE, date.target.dynamical$value, date.target.dynamical$uncertainty, TRUE)
label <- paste("Next ", G.lunar.phase, ":", sep="")
cat(label, replicate(G.max.indent - nchar(label), " "), formatMpfr(phase.date$value, 11), " +- ", formatMpfr(phase.date$uncertainty, 1), " JDE\n", sep="")
phase.date.dynamical <- CalculateWithUncertainty(TimeJDEToDynamical, phase.date$value, phase.date$uncertainty, FALSE)
cat(replicate(G.max.indent - 2, " "), "= ", format(round_date(phase.date.dynamical$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ", format(hms::as_hms(signif(as.numeric(phase.date.dynamical$uncertainty, units = "secs"), 2)), "%H:%M:%S"), " Dynamical Time\n\n", sep="")

label <- paste("Expected:")
cat(label, replicate(G.max.indent - nchar(label), " "), "21-Jan-2044 23:48:17 Dynamical Time.\n", sep="")
######


######
cat("\n-------\nTesting: Lunar Apogee Oct 1988 (page 357)\n\n")
G.lunar.apsis <- "apogee"
date.target.UTC <- date_decimal(1988.75)
date.target.dynamical <- TimeUniversalToDynamical(date.target.UTC)
apsis.date <- CalculateWithUncertainty(GetLunarApsisJDE, date.target.dynamical$value, date.target.dynamical$uncertainty, TRUE)
label <- paste("Next ", G.lunar.apsis, ":", sep="")
cat(label, replicate(G.max.indent - nchar(label), " "), formatMpfr(apsis.date$value, 10), " +- ", formatMpfr(apsis.date$uncertainty, 1), " JDE\n", sep="")
apsis.date.dynamical <- CalculateWithUncertainty(TimeJDEToDynamical, apsis.date$value, apsis.date$uncertainty, FALSE)
cat(replicate(G.max.indent - 2, " "), "= ", format(round_date(apsis.date.dynamical$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ", format(hms::as_hms(signif(as.numeric(apsis.date.dynamical$uncertainty, units = "secs"), 3)), "%H:%M:%S"), " Dynamical Time\n\n", sep="")

label <- paste("Expected:")
cat(label, replicate(G.max.indent - nchar(label), " "), "07-Oct-1988 20:29 Dynamical Time.\n", sep="")
######


######
cat("\n-------\nTesting: Lunar Distance for 12 April 1992, at 00:00:00 Dynamical Time (pages 342-343).\n")
lunar.distance <- CalculateWithUncertainty(GetLunarDistance, mpfr("2448724.5", 128), G.zero.mpfr, TRUE)
label <- paste("Estimated lunar distance:", sep="")
cat("\n", label, replicate(G.max.indent - nchar(label), " "), signif(asNumeric(lunar.distance$value), 6), " +- ", signif(asNumeric(lunar.distance$uncertainty), 1), " km\n\n", sep="")

label <- paste("Expected:")
cat(label, replicate(G.max.indent - nchar(label), " "), "368409.7 km.\n", sep="")
######
## Testing
## -------
## 
## Test the system's functions against examples in 'Astronomical Algorithms'.
## Ref: Meeus J., Astronomical Algorithms, 2nd Ed, Willmann-Bell, Richmond, VA, 1998.
## 
## -------
## Testing: New Moon Feb 1977 (page 353)
## 
## Next new moon:             2443192.6512 +- 0.0002 JDE
##                          = 18-Feb-1977 03:37:42 +- 00:00:16 Dynamical Time
##                          = 18-Feb-1977 03:36:54 +- 00:00:16 UTC
## 
## Expected:                  18-Feb-1977 03:37 UT.
##                            N.B. UT = UTC +- 0.9 s.
## 
## -------
## Testing: Last Quarter Moon Jan 2044 (page 353)
## 
## Next last quarter:         2467636.4919 +- 0.0002 JDE
##                          = 21-Jan-2044 23:48:17 +- 00:00:13 Dynamical Time
## 
## Expected:                  21-Jan-2044 23:48:17 Dynamical Time.
## 
## -------
## Testing: Lunar Apogee Oct 1988 (page 357)
## 
## Next apogee:               2447442.354 +- 0.002 JDE
##                          = 07-Oct-1988 20:30:12 +- 00:03:00 Dynamical Time
## 
## Expected:                  07-Oct-1988 20:29 Dynamical Time.
## 
## -------
## Testing: Lunar Distance for 12 April 1992, at 00:00:00 Dynamical Time (pages 342-343).
## 
## Estimated lunar distance:  368410 +- 4 km
## 
## Expected:                  368409.7 km.
# Testing against grey literature examples.
# -----------------------------------------
#
# Compare estimated lunar distance estimations.
# Compare this system vs astropixels.com and fourmilab.com.

G.lunar.apsis <- "perigee"
G.lunar.phase <- "full moon"

label.date <- "Target date:"
chars.label.date <- nchar(label.date)
label.distance <- "Lunar distance"

vec.dates.astropixels <- c(dmy_hms("09-01-2001 20:24:00", tz="UTC"),
                           dmy_hms("04-07-2050 18:51:00", tz="UTC"),
                           dmy_hms("23-05-2100 17:25:00", tz="UTC"))

vec.dates.fourmilab   <- c(dmy_hms("10-01-2001 09:00:00", tz="UTC"),
                           dmy_hms("07-07-2050 02:26:00", tz="UTC"),
                           dmy_hms("22-05-2100 11:08:00", tz="UTC"))

vec.distances.astropixels <- c(357406, 367058, 360904)
vec.distances.fourmilab   <- c(357131, 363255, 359497)

# Get lunar distance at target dates, for astropixels dates.
# N.B. Convert dates from UTC --> Dynamical Time--> JDE.
vec.calculated.distances <- lapply(
                               lapply(
                                  lapply(
                                     vec.dates.astropixels, TimeUniversalToDynamical),
                                  function(x) {CalculateWithUncertainty(TimeDynamicalToJDE, x$value, x$uncertainty, FALSE)}
                               ),
                               function(x) {CalculateWithUncertainty(GetLunarDistance, x$value, x$uncertainty, TRUE)}
                             )

# Display astropixels.com data.
cat("Lunar distance benchmarking: astropixels.com\n\n")
for (i in 1:length(vec.dates.astropixels)) {
   
   # Display target date.
   cat("------\n", label.date, replicate(G.max.indent - chars.label.date, " "), format(vec.dates.astropixels[i], "%d-%b-%Y %H:%M:%S %Z"), "\n", sep="")

   # Display astropixels.com's estimate.
   cat("\n", label.distance, sep="")
   cat("\nEstimate, astropixels.com:", replicate(G.max.indent - nchar("Estimate, astropixels.com:"), " "), vec.distances.astropixels[i], " +- <unspecified> km", sep="")

   # Display lunar distance at target date.
   cat("\nEstimate, R:", replicate(G.max.indent - nchar("Estimate, R:"), " "), signif(asNumeric(vec.calculated.distances[[i]]$value), 6), " +- ", signif(asNumeric(vec.calculated.distances[[i]]$uncertainty), 1), " km\n\n", sep="")
}


# Get lunar distance at target dates, for fourmilab dates.
# N.B. That source states that its times are only accurate to +- 2 minutes.
#      That error is incorporated, below.
vec.calculated.distances <- lapply(
                               lapply(
                                  lapply(
                                     vec.dates.fourmilab, function(x) {CalculateWithUncertainty(TimeUniversalToDynamical, x, dminutes(mpfr("2", 128)), TRUE)}),
                                  function(x) {CalculateWithUncertainty(TimeDynamicalToJDE, x$value, x$uncertainty, FALSE)}
                               ),
                               function(x) {CalculateWithUncertainty(GetLunarDistance, x$value, x$uncertainty, TRUE)}
                             )

# Display fourmilab.com data.
# N.B. The first test distance needs a different number of decimal places than the rest.
cat(replicate(G.max.indent, "-"), sep="")
cat("\n\nLunar distance benchmarking: www.fourmilab.ch\n\n")
#
# Display target date.
cat("------\n", label.date, replicate(G.max.indent - chars.label.date, " "), format(vec.dates.fourmilab[1], "%d-%b-%Y %H:%M:%S"), " +- 00:00:02 UTC\n", sep="")

# Display fourmilab.com's estimate.
cat("\n", label.distance, sep="")
cat("\nEstimate, fourmilab.com:", replicate(G.max.indent - nchar("Estimate, fourmilab.com:"), " "), format(vec.distances.fourmilab[1], nsmall=1), " +- ", signif(asNumeric(vec.calculated.distances[[1]]$uncertainty), 1), " km (assumed uncertainty, from ELP 2000-82)", sep="")

# Display lunar distance at target date.
cat("\nEstimate, R:", replicate(G.max.indent - nchar("Estimate, R:"), " "), format(signif(asNumeric(vec.calculated.distances[[1]]$value), 7), nsmall=1), " +- ", signif(asNumeric(vec.calculated.distances[[1]]$uncertainty), 1), " km\n\n", sep="")
#
for (i in 2:length(vec.dates.fourmilab)) {

   # Display target date.
   cat("------\n", label.date, replicate(G.max.indent - chars.label.date, " "), format(vec.dates.fourmilab[i], "%d-%b-%Y %H:%M:%S"), " +- 00:00:02 UTC\n", sep="")

   # Display fourmilab.com's estimate.
   cat("\n", label.distance, sep="")
   cat("\nEstimate, fourmilab.com:", replicate(G.max.indent - nchar("Estimate, fourmilab.com:"), " "), vec.distances.fourmilab[i], " +- ", signif(asNumeric(vec.calculated.distances[[i]]$uncertainty), 1), " km (assumed uncertainty, from ELP 2000-82)", sep="")

   # Display lunar distance at target date.
   cat("\nEstimate, R:", replicate(G.max.indent - nchar("Estimate, R:"), " "), signif(asNumeric(vec.calculated.distances[[i]]$value), 6), " +- ", signif(asNumeric(vec.calculated.distances[[i]]$uncertainty), 1), " km\n\n", sep="")
}
## Lunar distance benchmarking: astropixels.com
## 
## ------
## Target date:               09-Jan-2001 20:24:00 UTC
## 
## Lunar distance
## Estimate, astropixels.com: 357406 +- <unspecified> km
## Estimate, R:               357407 +- 1 km
## 
## ------
## Target date:               04-Jul-2050 18:51:00 UTC
## 
## Lunar distance
## Estimate, astropixels.com: 367058 +- <unspecified> km
## Estimate, R:               367057 +- 4 km
## 
## ------
## Target date:               23-May-2100 17:25:00 UTC
## 
## Lunar distance
## Estimate, astropixels.com: 360904 +- <unspecified> km
## Estimate, R:               360904 +- 1 km
## 
## ---------------------------
## 
## Lunar distance benchmarking: www.fourmilab.ch
## 
## ------
## Target date:               10-Jan-2001 09:00:00 +- 00:00:02 UTC
## 
## Lunar distance
## Estimate, fourmilab.com:   357131.0 +- 0.8 km (assumed uncertainty, from ELP 2000-82)
## Estimate, R:               357131.0 +- 0.8 km
## 
## ------
## Target date:               07-Jul-2050 02:26:00 +- 00:00:02 UTC
## 
## Lunar distance
## Estimate, fourmilab.com:   363255 +- 2 km (assumed uncertainty, from ELP 2000-82)
## Estimate, R:               363255 +- 2 km
## 
## ------
## Target date:               22-May-2100 11:08:00 +- 00:00:02 UTC
## 
## Lunar distance
## Estimate, fourmilab.com:   359497 +- 3 km (assumed uncertainty, from ELP 2000-82)
## Estimate, R:               359493 +- 3 km
# Example 1
# ---------
#
# For a given date, determine the time of the next...
# a) full moon,
# b) perigee and associated lunar distance.

cat("Example 1\n---------\n\n", sep="")
cat("For a target date, determine the time of the next....\n")
cat("a) full moon,\n")
cat("b) perigee and associated lunar distance.\n\n")

G.lunar.apsis <- "perigee"
G.lunar.phase <- "full moon"

# Set target date.
# Assume format is dd-mm-yyyy hh:mm:ss, with UTC time zone.
date.target.UTC <- dmy_hms("18-02-2029 00:00:00", tz="UTC")
label <- "Target date:"
cat(label, replicate(G.max.indent - nchar(label), " "), format(date.target.UTC, "%d-%b-%Y %H:%M:%S %Z"), "\n", sep="")

# Ensure a 21st century date is being processed.
if (!IsValidDate(date.target.UTC)) {
   cat("\nInvalid date; date.target.UTC:\n")
   print(date.target.UTC, digits=20)
   cat("\nNeed 21st century dates.")
   stopifnot(FALSE)
}

# Get the dynamical date equivalent of the target date.
date.target.dynamical <- TimeUniversalToDynamical(date.target.UTC)
cat(replicate(G.max.indent - 2, " "), "= ", format(round_date(date.target.dynamical$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ", format(signif(date.target.dynamical$uncertainty, 1), "%H:%M:%S"), " Dynamical Time\n\n", sep="")

# Get the date of the next nominated lunar phase (set to full moon by default).
phase.date <- CalculateWithUncertainty(GetLunarPhaseJDE, date.target.dynamical$value, date.target.dynamical$uncertainty, TRUE)
label <- paste("Next ", G.lunar.phase, ":", sep="")
cat(label, replicate(G.max.indent - nchar(label), " "), format(signif(asNumeric(phase.date$value), 11), nsmall=4), " +- ", signif(asNumeric(phase.date$uncertainty), 1), " JDE\n", sep="")

# Convert JDE to dynamical time.
# N.B. Handle uncertainty differently than for target date; it used a decimal uncertainty, this uses a difftime value.
phase.date.dynamical <- CalculateWithUncertainty(TimeJDEToDynamical, phase.date$value, phase.date$uncertainty, FALSE)
cat(replicate(G.max.indent - 2, " "), "= ", format(round_date(phase.date.dynamical$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ", format(hms::as_hms(signif(as.numeric(phase.date.dynamical$uncertainty, units = "secs"), 2)), "%H:%M:%S"), " Dynamical Time\n", sep="")

# Convert dynamical time to UTC.
# N.B. As for the JDE to dynamical time conversion, the uncertainty here uses a difftime value.
phase.date.UTC <- CalculateWithUncertainty(TimeDynamicalToUniversal, phase.date.dynamical$value, phase.date.dynamical$uncertainty, TRUE)
cat(replicate(G.max.indent - 2, " "), "= ", format(round_date(phase.date.UTC$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ", format(hms::as_hms(signif(as.numeric(phase.date.UTC$uncertainty, units = "secs"), 2)), "%H:%M:%S"), " UTC\n", sep="")


# Get the perigee estimation.
apsis.date <- CalculateWithUncertainty(GetLunarApsisJDE, date.target.dynamical$value, date.target.dynamical$uncertainty, TRUE)
label <- paste("Next ", G.lunar.apsis, ":", sep="")
cat("\n", label, replicate(G.max.indent - nchar(label), " "), format(signif(asNumeric(apsis.date$value), 9), nsmall=2), " +- ", signif(asNumeric(apsis.date$uncertainty), 1), " JDE\n", sep="")

# Convert JDE to dynamical time.
# N.B. Handle uncertainty differently than for target date; it used a decimal uncertainty, this uses a difftime value.
apsis.date.dynamical <- CalculateWithUncertainty(TimeJDEToDynamical, apsis.date$value, apsis.date$uncertainty, FALSE)
cat(replicate(G.max.indent - 2, " "), "= ", format(round_date(apsis.date.dynamical$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ", format(hms::as_hms(signif(as.numeric(apsis.date.dynamical$uncertainty, units = "secs"), 4)), "%H:%M:%S"), " Dynamical Time\n", sep="")

# Convert dynamical time to UTC.
# N.B. As for the JDE to dynamical time conversion, the uncertainty here uses a difftime value.
apsis.date.UTC <- CalculateWithUncertainty(TimeDynamicalToUniversal, apsis.date.dynamical$value, apsis.date.dynamical$uncertainty, TRUE)
cat(replicate(G.max.indent - 2, " "), "= ", format(round_date(apsis.date.UTC$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ", format(hms::as_hms(signif(as.numeric(apsis.date.UTC$uncertainty, units = "secs"), 4)), "%H:%M:%S"), " UTC\n", sep="")


# Get lunar distance at target apsis.
lunar.distance <- CalculateWithUncertainty(GetLunarDistance, apsis.date$value, apsis.date$uncertainty, TRUE)
label <- paste("Lunar distance at ", G.lunar.apsis, ":", sep="")
cat("\n", label, replicate(G.max.indent - nchar(label), " "), signif(asNumeric(lunar.distance$value), 6), " +- ", signif(asNumeric(lunar.distance$uncertainty), 1), " km\n", sep="")
## Example 1
## ---------
## 
## For a target date, determine the time of the next....
## a) full moon,
## b) perigee and associated lunar distance.
## 
## Target date:               18-Feb-2029 00:00:00 UTC
##                          = 18-Feb-2029 00:01:17 +- 6s Dynamical Time
## 
## Next full moon:            2462196.2163 +- 2e-04 JDE
##                          = 28-Feb-2029 17:11:27 +- 00:00:17 Dynamical Time
##                          = 28-Feb-2029 17:10:10 +- 00:00:56 UTC
## 
## Next perigee:              2462197.27 +- 0.02 JDE
##                          = 01-Mar-2029 18:30:52 +- 00:31:00 Dynamical Time
##                          = 01-Mar-2029 18:29:55 +- 00:31:19 UTC
## 
## Lunar distance at perigee: 358630 +- 3 km
# Example 2a
# ----------
#
# Graph variation in lunar distance for 2019-2020.
# Overlay full and new moons.
# First step: generate required data.

cat("Example 2\n---------\n\n", sep="")
cat("Graph variation in lunar distance for 2019-2020.\n")
cat("Overlay full and new moons.\n")

# Prepare poster data.
date.start <- dmy_hms("01-01-2019 00:00:00", tz="UTC")
date.end <- dmy_hms("31-12-2020 00:00:00", tz="UTC")

# Series 1: Get all full moons and their distances.
G.lunar.phase <- "full moon"
list.full.moon <- GetListEstimates(GetLunarPhaseJDE, date.start, date.end, mpfr("29.4", 128))
#
list.full.moon.distances <- lapply(list.full.moon,
   function(x) {CalculateWithUncertainty(GetLunarDistance, x$value, x$uncertainty, TRUE)})
#
# Convert full moon JDE dates to UTC.
list.full.moon <- lapply(list.full.moon,
   function(x) {
      target.Dynamical <- CalculateWithUncertainty(TimeJDEToDynamical, x$value, x$uncertainty, FALSE)
      CalculateWithUncertainty(TimeDynamicalToUniversal, target.Dynamical$value, target.Dynamical$uncertainty, TRUE)
})


# Series 2: Get all new moons and their distances.
G.lunar.phase <- "new moon"
list.new.moon <- GetListEstimates(GetLunarPhaseJDE, date.start, date.end, mpfr("29.4", 128))
#
list.new.moon.distances <- lapply(list.new.moon,
   function(x) {CalculateWithUncertainty(GetLunarDistance, x$value, x$uncertainty, TRUE)})
#
# Convert new moon JDE dates to UTC.
list.new.moon <- lapply(list.new.moon,
   function(x) {
      target.Dynamical <- CalculateWithUncertainty(TimeJDEToDynamical, x$value, x$uncertainty, FALSE)
      CalculateWithUncertainty(TimeDynamicalToUniversal, target.Dynamical$value, target.Dynamical$uncertainty, TRUE)
})


# Series 3: Get enough other lunar distances to graph. Try a point every 0.5 days.
interval.days <- ddays(mpfr("0.5", 128))
list.filler.dates <- 0:{ceiling({date.end - date.start} / interval.days) - 1}
list.filler.dates <- date.start + list.filler.dates * interval.days
list.filler.distances <- lapply(list.filler.dates,
   function(x) {
      target.Dynamical <- TimeUniversalToDynamical(x)
      target.JDE <- CalculateWithUncertainty(TimeDynamicalToJDE, target.Dynamical$value, target.Dynamical$uncertainty, FALSE)
      CalculateWithUncertainty(GetLunarDistance, target.JDE$value, target.JDE$uncertainty, TRUE)
})


# Convert lists to dataframes.
#
# Full moon UTC dates (values and uncertainties).
list.full.moon.dates <- lapply(list.full.moon, function(x) {x$value})
list.full.moon.dates.uncertainties <- lapply(list.full.moon, function(x) {x$uncertainty})
#
# Full moon distances (values and uncertainties).
list.full.moon.distances.approx <- lapply(list.full.moon.distances, function(x) {suppress_warnings(asNumeric(x$value), "*")})
list.full.moon.distances.uncertainties.approx <- lapply(list.full.moon.distances, function(x) {suppress_warnings(asNumeric(x$uncertainty), "*")})
#
# New moon UTC dates (values and uncertainties).
list.new.moon.dates <- lapply(list.new.moon, function(x) {x$value})
list.new.moon.dates.uncertainties <- lapply(list.new.moon, function(x) {x$uncertainty})
#
# New moon distances (values and uncertainties).
list.new.moon.distances.approx <- lapply(list.new.moon.distances, function(x) {suppress_warnings(asNumeric(x$value), "*")})
list.new.moon.distances.uncertainties.approx <- lapply(list.new.moon.distances, function(x) {suppress_warnings(asNumeric(x$uncertainty), "*")})
#
# Filler distances (values and uncertainties).
list.filler.distances.approx <- lapply(list.filler.distances, function(x) {suppress_warnings(asNumeric(x$value), "*")})
list.filler.distances.uncertainties.approx <- lapply(list.filler.distances, function(x) {suppress_warnings(asNumeric(x$uncertainty), "*")})
#
lunar.data <- cbind(data.frame(day = as_datetime(unlist(list.filler.dates)),
                          distance = unlist(list.filler.distances.approx),
              distance.uncertainty = unlist(list.filler.distances.uncertainties.approx))
                   )

full.moon.data <- cbind(data.frame(day.full.moon = as_datetime(unlist(list.full.moon.dates)),
                       day.full.moon.uncertainty = as_datetime(unlist(list.full.moon.dates.uncertainties)),
                              distance.full.moon = unlist(list.full.moon.distances.approx),
                  distance.full.moon.uncertainty = unlist(list.full.moon.distances.uncertainties.approx))
                       )

new.moon.data <- cbind(data.frame(day.new.moon = as_datetime(unlist(list.new.moon.dates)),
                      day.new.moon.uncertainty = as_datetime(unlist(list.new.moon.dates.uncertainties)),
                             distance.new.moon = unlist(list.new.moon.distances.approx),
                 distance.new.moon.uncertainty = unlist(list.new.moon.distances.uncertainties.approx))
                      )

cat("\nData prepared.")
## Example 2
## ---------
## 
## Graph variation in lunar distance for 2019-2020.
## Overlay full and new moons.
## 
## Data prepared.
# Example 2b
# ----------
#
# Graph variation in lunar distance for 2019-2020.
# Overlay full and new moons.
# Second step: display poster-style graph.

# Load needed font, if needed, but only once.
# N.B. Toggle G.extra.font.installed as needed.
# N.B. It may need console-based confirmation.

# Import Candara font, if not already available.
G.extra.font.installed <- TRUE
if (!G.extra.font.installed) {
   font_import(pattern = "Candara.ttf")
   loadfonts(device = "win")
   fonts()

   G.extra.font.installed <- TRUE
}

# Compose graph.
p <- ggplot() +
        geom_line(data = lunar.data, aes(x = day, y = distance, color = "distance")) +
        geom_point(data = full.moon.data, aes(x = day.full.moon, y = distance.full.moon, color = "full moon"), shape = 16, size = 4) +
        geom_point(data = new.moon.data, aes(x = day.new.moon, y = distance.new.moon, color = "new moon"), shape = 4, size = 4, stroke = 2) +
        ggtitle("Lunar Distance vs. Date", subtitle = "(with full and new moons overlaid)") + 
        guides(color = guide_legend(override.aes = list(shape = c(NA, 16, 4)))) + 
        scale_color_manual(values = c("#18677E", "#18677E", "#18677E"), name = "Legend") + 
        scale_x_datetime(labels = date_format("%d-%b-%Y")) + 
        scale_y_continuous(labels = comma) +
        theme_bw() + 
        theme_classic() + 
        theme(axis.text.x = element_text(color="#000000"),
              axis.text.y = element_text(color="#000000"),
              legend.background = element_rect(color = NA, fill = "transparent"),
              legend.box.background = element_rect(color = NA, fill = "transparent"),
              panel.background = element_rect(fill = "transparent"),
              plot.background = element_rect(fill = "transparent", color = NA),
              plot.title = element_text(hjust = 0.5),
              plot.subtitle = element_text(hjust = 0.5),
              text = element_text(color = "#717171", family = "Candara", size = 23.76097441)) +
        xlab("\ndate (UTC)") +
        ylab("distance (km)\n")

# Save graph, large format for poster use.
ggsave("supermoon_graph.png", bg = "transparent", width = 11.9, height = 8.499, units = "in")
# Example 2c
# ----------
#
# Graph variation in lunar distance for 2019-2020.
# Overlay full and new moons.
# Third step: display research paper-style graph.

# Compose graph.
p <- ggplot() +
        geom_line(data = lunar.data, aes(x = day, y = distance, color = "distance")) +
        geom_point(data = full.moon.data, aes(x = day.full.moon, y = distance.full.moon, color = "full moon"), shape = 16) +
        geom_point(data = new.moon.data, aes(x = day.new.moon, y = distance.new.moon, color = "new moon"), shape = 4) + 
        ggtitle("Lunar Distance vs. Date", subtitle = "(with full and new moons overlaid)") + 
        guides(color = guide_legend(override.aes = list(shape = c(NA, 16, 4)))) + 
        scale_color_manual(values = c("#000000", "#000000", "#000000"), name = "Legend") + 
        scale_x_datetime(labels = date_format("%d-%b-%Y")) + 
        scale_y_continuous(labels = comma) +
        theme_bw() + 
        theme_classic() + 
        theme(axis.text.x = element_text(color="#000000"),
          axis.text.y = element_text(color="#000000"),
              legend.background = element_rect(color = NA, fill = "transparent"),
              legend.box.background = element_rect(color = NA, fill = "transparent"),
              panel.background = element_rect(fill = "transparent"),
              plot.background = element_rect(fill = "transparent", color = NA),
              plot.title = element_text(hjust = 0.5),
              plot.subtitle = element_text(hjust = 0.5),
              text = element_text(color = "#000000", family = "Times New Roman")) +
        xlab("\ndate (UTC)") +
        ylab("distance (km)\n")

# Display graph.
p

# Save graph, small format for research paper use.
ggsave(file = "supermoon_graph.eps", device = cairo_ps, family = "Times", scale = 0.7)
## Saving 4.9 x 3.5 in image
# Example 3
# ---------
#
# Get all 21st century supermoons.
# Define "supermoon" as a full moon and lunar perigee separated by <= 12 h.

cat("Example 3\n---------\n\n", sep="")
cat("Get all 21st century supermoons.\n")
cat("Define 'supermoon' as a full moon and lunar perigee separated by <= 12 h.\n")

G.lunar.apsis <- "perigee"
G.lunar.phase <- "full moon"

# Get the 21st century full moons.
list.full.moons <- GetListEstimates(GetLunarPhaseJDE, dmy_hms("01-01-2001 00:00:00", tz="UTC"), dmy_hms("31-12-2100 00:00:00", tz="UTC"), mpfr("29.4", 128))

# Get the 21st century lunar perigees.
list.lunar.perigees <- GetListEstimates(GetLunarApsisJDE, dmy_hms("01-01-2001 00:00:00", tz="UTC"), dmy_hms("31-12-2100 00:00:00", tz="UTC"), mpfr("27.4", 128))

# Build the supermoons list.
# Each list element will hold: (date-time of full moon, date-time of perigee, distance).
# N.B. A 12 h UTC or DT time difference = 0.5 JDE.
list.supermoons <- list()
num.full.moons <- length(list.full.moons)
num.lunar.perigees <- length(list.lunar.perigees)
match.found <- FALSE
#
for (x in 1:num.full.moons) {
   for (y in 1:num.lunar.perigees) {

      # Only assess, in detail, relatively close data points.
      if (abs(list.full.moons[[x]]$value - list.lunar.perigees[[y]]$value) <= 2) {

         if (list.full.moons[[x]]$value < list.lunar.perigees[[y]]$value) {
            if ({list.lunar.perigees[[y]]$value - list.lunar.perigees[[y]]$uncertainty} - {list.full.moons[[x]]$value + list.full.moons[[x]]$uncertainty} <= 0.5) {
               match.found <- TRUE
               break
            }

         } else{
            if ({list.full.moons[[x]]$value - list.full.moons[[x]]$uncertainty} - {list.lunar.perigees[[y]]$value + list.lunar.perigees[[y]]$uncertainty} <= 0.5) {
               match.found <- TRUE
               break
            }
         }

      }
   }

   if (match.found) {
      # a) Convert time of full moon from JDE to UTC.
      # b) Convert time of perigee from JDE to UTC.
      # c) Get lunar distance at perigee.

      phase.date.dynamical <- CalculateWithUncertainty(TimeJDEToDynamical, list.full.moons[[x]]$value, list.full.moons[[x]]$uncertainty, FALSE)

      apsis.date.dynamical <- CalculateWithUncertainty(TimeJDEToDynamical, list.lunar.perigees[[y]]$value, list.lunar.perigees[[y]]$uncertainty, FALSE)

      list.supermoons[[length(list.supermoons) + 1]] <- list(
         "full.moon" = CalculateWithUncertainty(TimeDynamicalToUniversal, phase.date.dynamical$value, phase.date.dynamical$uncertainty, TRUE),
         "perigee" = CalculateWithUncertainty(TimeDynamicalToUniversal, apsis.date.dynamical$value, apsis.date.dynamical$uncertainty, TRUE),
         "distance" = CalculateWithUncertainty(GetLunarDistance, list.lunar.perigees[[y]]$value, list.lunar.perigees[[y]]$uncertainty, TRUE))

      # Delete the perigee from the perigee list.
      list.lunar.perigees[- y]
      num.lunar.perigees <- length(list.lunar.perigees)

      match.found <- FALSE
   }
}


# Display results.
cat("\n---------------------------------------------\n")
#
for (x in 1:length(list.supermoons)) {

   cat("supermoon #", x, "\n", sep="")

   label <- paste(" ", G.lunar.phase, ":", sep="")
   if (list.supermoons[[x]]$full.moon$uncertainty < 60) {
      significant.figures <- 2
   } else {
      significant.figures <- 3
   }
   cat(label, replicate(G.max.indent - nchar(label), " "), format(round_date(list.supermoons[[x]]$full.moon$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ",
       format(hms::as_hms(signif(as.numeric(list.supermoons[[x]]$full.moon$uncertainty, units = "secs"), significant.figures)), "%H:%M:%S"), " UTC\n", sep="")

   label <- paste(" ", G.lunar.apsis, ":", sep="")
   cat(label, replicate(G.max.indent - nchar(label), " "), format(round_date(list.supermoons[[x]]$perigee$value, unit = "seconds"), "%d-%b-%Y %H:%M:%S"), " +- ",  
       format(hms::as_hms(signif(as.numeric(list.supermoons[[x]]$perigee$uncertainty, units = "secs"), 4)), "%H:%M:%S"), " UTC\n", sep="")

   label <- paste(" distance at ", G.lunar.apsis, ":", sep="")
   if (list.supermoons[[x]]$distance$uncertainty < 1) {
      significant.figures <- 7
   } else {
      significant.figures <- 6
   }
   cat(label, replicate(G.max.indent - nchar(label), " "), signif(asNumeric(list.supermoons[[x]]$distance$value), significant.figures), " +- ", signif(asNumeric(list.supermoons[[x]]$distance$uncertainty), 1), " km\n\n", sep="")
}

cat("---------------------------------------------")
## Example 3
## ---------
## 
## Get all 21st century supermoons.
## Define 'supermoon' as a full moon and lunar perigee separated by <= 12 h.
## 
## ---------------------------------------------
## supermoon #1
##  full moon:                08-Feb-2001 07:11:33 +- 00:00:17 UTC
##  perigee:                  07-Feb-2001 22:18:46 +- 00:31:00 UTC
##  distance at perigee:      356854 +- 2 km
## 
## supermoon #2
##  full moon:                27-Feb-2002 09:16:38 +- 00:00:17 UTC
##  perigee:                  27-Feb-2002 19:47:12 +- 00:31:00 UTC
##  distance at perigee:      356903 +- 5 km
## 
## supermoon #3
##  full moon:                28-Mar-2002 18:24:50 +- 00:00:17 UTC
##  perigee:                  28-Mar-2002 07:41:41 +- 00:31:00 UTC
##  distance at perigee:      357014 +- 4 km
## 
## supermoon #4
##  full moon:                16-Apr-2003 19:35:34 +- 00:00:17 UTC
##  perigee:                  17-Apr-2003 04:57:37 +- 00:31:00 UTC
##  distance at perigee:      357156.6 +- 0.5 km
## 
## supermoon #5
##  full moon:                16-May-2003 03:35:56 +- 00:00:17 UTC
##  perigee:                  15-May-2003 15:39:16 +- 00:31:00 UTC
##  distance at perigee:      357451 +- 1 km
## 
## supermoon #6
##  full moon:                03-Jun-2004 04:19:33 +- 00:00:17 UTC
##  perigee:                  03-Jun-2004 13:09:47 +- 00:31:00 UTC
##  distance at perigee:      357250 +- 3 km
## 
## supermoon #7
##  full moon:                02-Jul-2004 11:08:51 +- 00:00:17 UTC
##  perigee:                  01-Jul-2004 22:59:39 +- 00:31:00 UTC
##  distance at perigee:      357448.8 +- 0.8 km
## 
## supermoon #8
##  full moon:                21-Jul-2005 11:00:08 +- 00:00:17 UTC
##  perigee:                  21-Jul-2005 19:44:29 +- 00:31:00 UTC
##  distance at perigee:      357161 +- 2 km
## 
## supermoon #9
##  full moon:                19-Aug-2005 17:52:54 +- 00:00:17 UTC
##  perigee:                  19-Aug-2005 05:32:04 +- 00:31:00 UTC
##  distance at perigee:      357399 +- 6 km
## 
## supermoon #10
##  full moon:                07-Sep-2006 18:41:56 +- 00:00:17 UTC
##  perigee:                  08-Sep-2006 03:07:14 +- 00:31:00 UTC
##  distance at perigee:      357178 +- 2 km
## 
## supermoon #11
##  full moon:                26-Oct-2007 04:51:34 +- 00:00:17 UTC
##  perigee:                  26-Oct-2007 11:51:14 +- 00:31:00 UTC
##  distance at perigee:      356755 +- 3 km
## 
## supermoon #12
##  full moon:                12-Dec-2008 16:37:12 +- 00:00:17 UTC
##  perigee:                  12-Dec-2008 21:36:59 +- 00:31:00 UTC
##  distance at perigee:      356569 +- 4 km
## 
## supermoon #13
##  full moon:                30-Jan-2010 06:17:31 +- 00:00:17 UTC
##  perigee:                  30-Jan-2010 09:02:39 +- 00:31:00 UTC
##  distance at perigee:      356594 +- 1 km
## 
## supermoon #14
##  full moon:                19-Mar-2011 18:10:02 +- 00:00:17 UTC
##  perigee:                  19-Mar-2011 19:09:04 +- 00:31:00 UTC
##  distance at perigee:      356579 +- 4 km
## 
## supermoon #15
##  full moon:                06-May-2012 03:35:07 +- 00:00:17 UTC
##  perigee:                  06-May-2012 03:32:45 +- 00:31:00 UTC
##  distance at perigee:      356954 +- 1 km
## 
## supermoon #16
##  full moon:                23-Jun-2013 11:32:13 +- 00:00:17 UTC
##  perigee:                  23-Jun-2013 11:09:24 +- 00:31:00 UTC
##  distance at perigee:      356991.5 +- 0.3 km
## 
## supermoon #17
##  full moon:                10-Aug-2014 18:09:17 +- 00:00:17 UTC
##  perigee:                  10-Aug-2014 17:43:01 +- 00:31:00 UTC
##  distance at perigee:      356898 +- 3 km
## 
## supermoon #18
##  full moon:                28-Sep-2015 02:50:29 +- 00:00:17 UTC
##  perigee:                  28-Sep-2015 01:45:55 +- 00:31:00 UTC
##  distance at perigee:      356876.7 +- 0.6 km
## 
## supermoon #19
##  full moon:                14-Nov-2016 13:52:08 +- 00:00:17 UTC
##  perigee:                  14-Nov-2016 11:22:36 +- 00:31:00 UTC
##  distance at perigee:      356512 +- 3 km
## 
## supermoon #20
##  full moon:                02-Jan-2018 02:24:04 +- 00:00:17 UTC
##  perigee:                  01-Jan-2018 21:54:30 +- 00:31:00 UTC
##  distance at perigee:      356568 +- 3 km
## 
## supermoon #21
##  full moon:                19-Feb-2019 15:53:29 +- 00:00:17 UTC
##  perigee:                  19-Feb-2019 09:05:45 +- 00:31:00 UTC
##  distance at perigee:      356763 +- 2 km
## 
## supermoon #22
##  full moon:                08-Apr-2020 02:34:57 +- 00:00:17 UTC
##  perigee:                  07-Apr-2020 18:08:21 +- 00:31:00 UTC
##  distance at perigee:      356911 +- 4 km
## 
## supermoon #23
##  full moon:                27-Apr-2021 03:31:44 +- 00:00:36 UTC
##  perigee:                  27-Apr-2021 15:24:11 +- 00:31:36 UTC
##  distance at perigee:      357377.5 +- 1 km
## 
## supermoon #24
##  full moon:                26-May-2021 11:14:01 +- 00:00:45 UTC
##  perigee:                  26-May-2021 01:51:53 +- 00:31:00 UTC
##  distance at perigee:      357312 +- 1 km
## 
## supermoon #25
##  full moon:                14-Jun-2022 11:51:17 +- 00:00:36 UTC
##  perigee:                  14-Jun-2022 23:21:22 +- 00:31:36 UTC
##  distance at perigee:      357435 +- 2 km
## 
## supermoon #26
##  full moon:                13-Jul-2022 18:37:36 +- 00:00:45 UTC
##  perigee:                  13-Jul-2022 09:07:41 +- 00:31:18 UTC
##  distance at perigee:      357263.4 +- 0.8 km
## 
## supermoon #27
##  full moon:                01-Aug-2023 18:31:28 +- 00:00:54 UTC
##  perigee:                  02-Aug-2023 05:52:04 +- 00:31:18 UTC
##  distance at perigee:      357312 +- 1 km
## 
## supermoon #28
##  full moon:                31-Aug-2023 01:35:27 +- 00:00:17 UTC
##  perigee:                  30-Aug-2023 15:51:02 +- 00:31:00 UTC
##  distance at perigee:      357185 +- 4 km
## 
## supermoon #29
##  full moon:                18-Sep-2024 02:34:19 +- 00:00:55 UTC
##  perigee:                  18-Sep-2024 13:26:06 +- 00:31:19 UTC
##  distance at perigee:      357286 +- 0.5 km
## 
## supermoon #30
##  full moon:                17-Oct-2024 11:26:24 +- 00:00:55 UTC
##  perigee:                  17-Oct-2024 00:45:12 +- 00:31:19 UTC
##  distance at perigee:      357173 +- 2 km
## 
## supermoon #31
##  full moon:                05-Nov-2025 13:19:15 +- 00:00:55 UTC
##  perigee:                  05-Nov-2025 22:28:52 +- 00:31:37 UTC
##  distance at perigee:      356833.2 +- 0.4 km
## 
## supermoon #32
##  full moon:                04-Dec-2025 23:14:01 +- 00:00:38 UTC
##  perigee:                  04-Dec-2025 11:05:36 +- 00:31:19 UTC
##  distance at perigee:      356965 +- 3 km
## 
## supermoon #33
##  full moon:                24-Dec-2026 01:28:17 +- 00:00:47 UTC
##  perigee:                  24-Dec-2026 08:29:51 +- 00:31:19 UTC
##  distance at perigee:      356652 +- 1 km
## 
## supermoon #34
##  full moon:                10-Feb-2028 15:03:27 +- 00:00:47 UTC
##  perigee:                  10-Feb-2028 19:53:16 +- 00:31:38 UTC
##  distance at perigee:      356679.7 +- 0.6 km
## 
## supermoon #35
##  full moon:                30-Mar-2029 02:26:20 +- 00:00:39 UTC
##  perigee:                  30-Mar-2029 05:39:48 +- 00:31:39 UTC
##  distance at perigee:      356666 +- 2 km
## 
## supermoon #36
##  full moon:                17-May-2030 11:19:12 +- 00:00:48 UTC
##  perigee:                  17-May-2030 13:45:20 +- 00:31:39 UTC
##  distance at perigee:      357017 +- 2 km
## 
## supermoon #37
##  full moon:                04-Jul-2031 19:01:11 +- 00:00:57 UTC
##  perigee:                  04-Jul-2031 21:13:53 +- 00:31:20 UTC
##  distance at perigee:      357009.2 +- 0.9 km
## 
## supermoon #38
##  full moon:                21-Aug-2032 01:46:37 +- 00:00:57 UTC
##  perigee:                  21-Aug-2032 03:51:38 +- 00:31:00 UTC
##  distance at perigee:      356881 +- 2 km
## 
## supermoon #39
##  full moon:                08-Oct-2033 10:58:10 +- 00:00:49 UTC
##  perigee:                  08-Oct-2033 12:11:20 +- 00:31:40 UTC
##  distance at perigee:      356824 +- 2 km
## 
## supermoon #40
##  full moon:                25-Nov-2034 22:31:51 +- 00:00:49 UTC
##  perigee:                  25-Nov-2034 22:06:15 +- 00:31:40 UTC
##  distance at perigee:      356448 +- 3 km
## 
## supermoon #41
##  full moon:                13-Jan-2036 11:15:50 +- 00:00:50 UTC
##  perigee:                  13-Jan-2036 08:46:57 +- 00:31:20 UTC
##  distance at perigee:      356519 +- 2 km
## 
## supermoon #42
##  full moon:                02-Mar-2037 00:27:56 +- 00:00:59 UTC
##  perigee:                  01-Mar-2037 19:47:36 +- 00:31:21 UTC
##  distance at perigee:      356711 +- 3 km
## 
## supermoon #43
##  full moon:                19-Apr-2038 10:35:49 +- 00:00:59 UTC
##  perigee:                  19-Apr-2038 04:30:40 +- 00:31:21 UTC
##  distance at perigee:      356843 +- 6 km
## 
## supermoon #44
##  full moon:                06-Jun-2039 18:47:44 +- 00:00:51 UTC
##  perigee:                  06-Jun-2039 12:00:54 +- 00:31:21 UTC
##  distance at perigee:      357207 +- 2 km
## 
## supermoon #45
##  full moon:                24-Jul-2040 02:05:14 +- 00:00:51 UTC
##  perigee:                  23-Jul-2040 19:15:19 +- 00:31:43 UTC
##  distance at perigee:      357112 +- 2 km
## 
## supermoon #46
##  full moon:                10-Sep-2041 09:23:36 +- 00:01:00 UTC
##  perigee:                  10-Sep-2041 02:11:53 +- 00:31:43 UTC
##  distance at perigee:      357006 +- 6 km
## 
## supermoon #47
##  full moon:                28-Oct-2042 19:48:32 +- 00:00:43 UTC
##  perigee:                  28-Oct-2042 11:27:14 +- 00:31:43 UTC
##  distance at perigee:      356973 +- 2 km
## 
## supermoon #48
##  full moon:                16-Nov-2043 21:52:28 +- 00:00:53 UTC
##  perigee:                  17-Nov-2043 09:11:26 +- 00:31:22 UTC
##  distance at perigee:      356948 +- 1 km
## 
## supermoon #49
##  full moon:                16-Dec-2043 08:01:57 +- 00:00:53 UTC
##  perigee:                  15-Dec-2043 22:00:39 +- 00:31:00 UTC
##  distance at perigee:      356772 +- 4 km
## 
## supermoon #50
##  full moon:                03-Jan-2045 10:20:13 +- 00:00:17 UTC
##  perigee:                  03-Jan-2045 19:24:35 +- 00:31:00 UTC
##  distance at perigee:      356774 +- 1 km
## 
## supermoon #51
##  full moon:                01-Feb-2045 21:05:13 +- 00:01:02 UTC
##  perigee:                  01-Feb-2045 08:42:32 +- 00:31:22 UTC
##  distance at perigee:      357105 +- 1 km
## 
## supermoon #52
##  full moon:                20-Feb-2046 23:43:59 +- 00:01:02 UTC
##  perigee:                  21-Feb-2046 06:43:17 +- 00:31:45 UTC
##  distance at perigee:      356806 +- 2 km
## 
## supermoon #53
##  full moon:                10-Apr-2047 10:35:03 +- 00:01:03 UTC
##  perigee:                  10-Apr-2047 16:08:00 +- 00:31:45 UTC
##  distance at perigee:      356790 +- 4 km
## 
## supermoon #54
##  full moon:                27-May-2048 18:56:46 +- 00:00:46 UTC
##  perigee:                  27-May-2048 23:56:00 +- 00:31:46 UTC
##  distance at perigee:      357115 +- 1 km
## 
## supermoon #55
##  full moon:                15-Jul-2049 02:29:15 +- 00:01:04 UTC
##  perigee:                  15-Jul-2049 07:17:56 +- 00:31:46 UTC
##  distance at perigee:      357062 +- 1 km
## 
## supermoon #56
##  full moon:                01-Sep-2050 09:30:26 +- 00:00:47 UTC
##  perigee:                  01-Sep-2050 14:02:31 +- 00:31:24 UTC
##  distance at perigee:      356899 +- 4 km
## 
## supermoon #57
##  full moon:                19-Oct-2051 19:12:50 +- 00:01:06 UTC
##  perigee:                  19-Oct-2051 22:40:51 +- 00:31:48 UTC
##  distance at perigee:      356809 +- 2 km
## 
## supermoon #58
##  full moon:                06-Dec-2052 07:17:32 +- 00:00:58 UTC
##  perigee:                  06-Dec-2052 08:52:07 +- 00:31:49 UTC
##  distance at perigee:      356425 +- 4 km
## 
## supermoon #59
##  full moon:                23-Jan-2054 20:07:38 +- 00:01:08 UTC
##  perigee:                  23-Jan-2054 19:37:41 +- 00:31:51 UTC
##  distance at perigee:      356512 +- 1 km
## 
## supermoon #60
##  full moon:                13-Mar-2055 08:56:47 +- 00:01:09 UTC
##  perigee:                  13-Mar-2055 06:25:07 +- 00:31:52 UTC
##  distance at perigee:      356698 +- 4 km
## 
## supermoon #61
##  full moon:                29-Apr-2056 18:30:49 +- 00:01:10 UTC
##  perigee:                  29-Apr-2056 14:47:49 +- 00:31:53 UTC
##  distance at perigee:      356811 +- 5 km
## 
## supermoon #62
##  full moon:                17-Jun-2057 02:18:27 +- 00:01:03 UTC
##  perigee:                  16-Jun-2057 22:08:24 +- 00:31:27 UTC
##  distance at perigee:      357136 +- 2 km
## 
## supermoon #63
##  full moon:                04-Aug-2058 09:37:28 +- 00:01:13 UTC
##  perigee:                  04-Aug-2058 05:22:10 +- 00:31:55 UTC
##  distance at perigee:      356996 +- 2 km
## 
## supermoon #64
##  full moon:                21-Sep-2059 17:18:17 +- 00:01:14 UTC
##  perigee:                  21-Sep-2059 12:34:20 +- 00:31:57 UTC
##  distance at perigee:      356863 +- 5 km
## 
## supermoon #65
##  full moon:                08-Nov-2060 04:17:14 +- 00:00:58 UTC
##  perigee:                  07-Nov-2060 22:10:49 +- 00:31:29 UTC
##  distance at perigee:      356812 +- 3 km
## 
## supermoon #66
##  full moon:                26-Dec-2061 16:52:34 +- 00:01:16 UTC
##  perigee:                  26-Dec-2061 08:55:32 +- 00:31:59 UTC
##  distance at perigee:      356619 +- 3 km
## 
## supermoon #67
##  full moon:                14-Jan-2063 19:11:13 +- 00:01:00 UTC
##  perigee:                  15-Jan-2063 06:21:14 +- 00:31:30 UTC
##  distance at perigee:      356937 +- 2 km
## 
## supermoon #68
##  full moon:                13-Feb-2063 05:48:26 +- 00:01:18 UTC
##  perigee:                  12-Feb-2063 19:32:29 +- 00:32:00 UTC
##  distance at perigee:      356964 +- 3 km
## 
## supermoon #69
##  full moon:                03-Mar-2064 08:18:35 +- 00:01:19 UTC
##  perigee:                  03-Mar-2064 17:31:27 +- 00:32:01 UTC
##  distance at perigee:      356971.3 +- 0.4 km
## 
## supermoon #70
##  full moon:                01-Apr-2064 17:40:07 +- 00:01:10 UTC
##  perigee:                  01-Apr-2064 05:28:34 +- 00:32:01 UTC
##  distance at perigee:      357235.9 +- 0.7 km
## 
## supermoon #71
##  full moon:                20-Apr-2065 18:35:47 +- 00:01:20 UTC
##  perigee:                  21-Apr-2065 02:33:38 +- 00:32:02 UTC
##  distance at perigee:      356952 +- 2 km
## 
## supermoon #72
##  full moon:                08-Jun-2066 02:30:39 +- 00:01:21 UTC
##  perigee:                  08-Jun-2066 10:05:32 +- 00:32:04 UTC
##  distance at perigee:      357248 +- 2 km
## 
## supermoon #73
##  full moon:                26-Jul-2067 09:58:15 +- 00:01:22 UTC
##  perigee:                  26-Jul-2067 17:23:09 +- 00:32:05 UTC
##  distance at perigee:      357148.8 +- 0.4 km
## 
## supermoon #74
##  full moon:                11-Sep-2068 17:18:56 +- 00:01:23 UTC
##  perigee:                  12-Sep-2068 00:17:10 +- 00:32:06 UTC
##  distance at perigee:      356953 +- 4 km
## 
## supermoon #75
##  full moon:                30-Oct-2069 03:35:07 +- 00:01:25 UTC
##  perigee:                  30-Oct-2069 09:14:21 +- 00:31:34 UTC
##  distance at perigee:      356831 +- 1 km
## 
## supermoon #76
##  full moon:                17-Dec-2070 16:05:22 +- 00:01:26 UTC
##  perigee:                  17-Dec-2070 19:40:29 +- 00:32:09 UTC
##  distance at perigee:      356443 +- 4 km
## 
## supermoon #77
##  full moon:                04-Feb-2072 04:55:16 +- 00:01:27 UTC
##  perigee:                  04-Feb-2072 06:25:57 +- 00:32:10 UTC
##  distance at perigee:      356545.9 +- 0.6 km
## 
## supermoon #78
##  full moon:                23-Mar-2073 17:16:51 +- 00:01:28 UTC
##  perigee:                  23-Mar-2073 16:57:26 +- 00:32:11 UTC
##  distance at perigee:      356722 +- 3 km
## 
## supermoon #79
##  full moon:                11-May-2074 02:17:19 +- 00:01:21 UTC
##  perigee:                  11-May-2074 01:01:08 +- 00:32:12 UTC
##  distance at perigee:      356815 +- 4 km
## 
## supermoon #80
##  full moon:                28-Jun-2075 09:46:14 +- 00:01:31 UTC
##  perigee:                  28-Jun-2075 08:12:49 +- 00:32:13 UTC
##  distance at perigee:      357100 +- 1 km
## 
## supermoon #81
##  full moon:                14-Aug-2076 17:11:36 +- 00:01:23 UTC
##  perigee:                  14-Aug-2076 15:29:27 +- 00:32:15 UTC
##  distance at perigee:      356914 +- 3 km
## 
## supermoon #82
##  full moon:                02-Oct-2077 01:20:21 +- 00:01:33 UTC
##  perigee:                  01-Oct-2077 22:58:31 +- 00:32:16 UTC
##  distance at perigee:      356757 +- 7 km
## 
## supermoon #83
##  full moon:                19-Nov-2078 12:52:16 +- 00:01:35 UTC
##  perigee:                  19-Nov-2078 08:56:51 +- 00:32:17 UTC
##  distance at perigee:      356690 +- 1 km
## 
## supermoon #84
##  full moon:                07-Jan-2080 01:44:33 +- 00:01:36 UTC
##  perigee:                  06-Jan-2080 19:50:10 +- 00:31:39 UTC
##  distance at perigee:      356508 +- 4 km
## 
## supermoon #85
##  full moon:                23-Feb-2081 14:26:57 +- 00:01:37 UTC
##  perigee:                  23-Feb-2081 06:17:54 +- 00:32:20 UTC
##  distance at perigee:      356861 +- 2 km
## 
## supermoon #86
##  full moon:                14-Mar-2082 16:44:37 +- 00:01:21 UTC
##  perigee:                  15-Mar-2082 04:16:12 +- 00:31:40 UTC
##  distance at perigee:      357176 +- 1 km
## 
## supermoon #87
##  full moon:                13-Apr-2082 01:45:08 +- 00:01:38 UTC
##  perigee:                  12-Apr-2082 15:53:20 +- 00:32:21 UTC
##  distance at perigee:      357106 +- 2 km
## 
## supermoon #88
##  full moon:                02-May-2083 02:29:28 +- 00:01:40 UTC
##  perigee:                  02-May-2083 12:57:27 +- 00:31:41 UTC
##  distance at perigee:      357150 +- 3 km
## 
## supermoon #89
##  full moon:                31-May-2083 09:41:53 +- 00:01:40 UTC
##  perigee:                  30-May-2083 23:06:39 +- 00:31:41 UTC
##  distance at perigee:      357247 +- 1 km
## 
## supermoon #90
##  full moon:                18-Jun-2084 10:00:15 +- 00:01:32 UTC
##  perigee:                  18-Jun-2084 20:15:01 +- 00:32:23 UTC
##  distance at perigee:      357415 +- 2 km
## 
## supermoon #91
##  full moon:                17-Jul-2084 17:01:24 +- 00:01:32 UTC
##  perigee:                  17-Jul-2084 06:12:13 +- 00:32:24 UTC
##  distance at perigee:      357471.7 +- 0.7 km
## 
## supermoon #92
##  full moon:                05-Aug-2085 17:28:51 +- 00:01:33 UTC
##  perigee:                  06-Aug-2085 03:30:21 +- 00:32:25 UTC
##  distance at perigee:      357270 +- 2 km
## 
## supermoon #93
##  full moon:                04-Sep-2085 00:40:57 +- 00:01:40 UTC
##  perigee:                  03-Sep-2085 13:42:42 +- 00:32:25 UTC
##  distance at perigee:      357232 +- 2 km
## 
## supermoon #94
##  full moon:                23-Sep-2086 01:14:37 +- 00:01:40 UTC
##  perigee:                  23-Sep-2086 10:35:14 +- 00:32:26 UTC
##  distance at perigee:      357041 +- 5 km
## 
## supermoon #95
##  full moon:                22-Oct-2086 09:55:40 +- 00:01:40 UTC
##  perigee:                  21-Oct-2086 22:00:07 +- 00:32:26 UTC
##  distance at perigee:      357178 +- 5 km
## 
## supermoon #96
##  full moon:                10-Nov-2087 12:04:45 +- 00:01:40 UTC
##  perigee:                  10-Nov-2087 19:53:15 +- 00:31:44 UTC
##  distance at perigee:      356889.7 +- 0.5 km
## 
## supermoon #97
##  full moon:                28-Dec-2088 00:57:03 +- 00:01:50 UTC
##  perigee:                  28-Dec-2088 06:32:34 +- 00:31:44 UTC
##  distance at perigee:      356502 +- 6 km
## 
## supermoon #98
##  full moon:                14-Feb-2090 13:39:02 +- 00:01:50 UTC
##  perigee:                  14-Feb-2090 17:12:55 +- 00:32:30 UTC
##  distance at perigee:      356620.4 +- 0.6 km
## 
## supermoon #99
##  full moon:                04-Apr-2091 01:31:11 +- 00:01:40 UTC
##  perigee:                  04-Apr-2091 03:25:59 +- 00:32:31 UTC
##  distance at perigee:      356784 +- 4 km
## 
## supermoon #100
##  full moon:                21-May-2092 09:59:48 +- 00:01:50 UTC
##  perigee:                  21-May-2092 11:10:29 +- 00:31:46 UTC
##  distance at perigee:      356854 +- 3 km
## 
## supermoon #101
##  full moon:                08-Jul-2093 17:13:31 +- 00:01:50 UTC
##  perigee:                  08-Jul-2093 18:17:45 +- 00:31:47 UTC
##  distance at perigee:      357098 +- 0.5 km
## 
## supermoon #102
##  full moon:                26-Aug-2094 00:51:25 +- 00:01:50 UTC
##  perigee:                  26-Aug-2094 01:38:31 +- 00:32:35 UTC
##  distance at perigee:      356867 +- 2 km
## 
## supermoon #103
##  full moon:                13-Oct-2095 09:30:32 +- 00:01:36 UTC
##  perigee:                  13-Oct-2095 09:25:59 +- 00:31:48 UTC
##  distance at perigee:      356687 +- 5 km
## 
## supermoon #104
##  full moon:                29-Nov-2096 21:33:39 +- 00:01:50 UTC
##  perigee:                  29-Nov-2096 19:44:15 +- 00:31:49 UTC
##  distance at perigee:      356609 +- 2 km
## 
## supermoon #105
##  full moon:                17-Jan-2098 10:35:40 +- 00:02:00 UTC
##  perigee:                  17-Jan-2098 06:41:26 +- 00:32:39 UTC
##  distance at perigee:      356437 +- 3 km
## 
## supermoon #106
##  full moon:                06-Mar-2099 22:59:22 +- 00:02:00 UTC
##  perigee:                  06-Mar-2099 16:58:42 +- 00:32:40 UTC
##  distance at perigee:      356797 +- 3 km
## 
## supermoon #107
##  full moon:                24-Apr-2100 09:43:26 +- 00:02:00 UTC
##  perigee:                  24-Apr-2100 02:12:39 +- 00:31:51 UTC
##  distance at perigee:      357012 +- 1 km
## 
## ---------------------------------------------