#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# contrib/misc/badi_jd_tests.py
#
import os
import sys
import math
import pprint
from unittest.mock import patch
PWD = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.dirname(os.path.dirname(PWD))
sys.path.append(BASE_DIR)
from badidatetime import BahaiCalendar, GregorianCalendar
from badidatetime._timedateutils import _td_utils
from badidatetime import date as ddate
[docs]
class DateTests(BahaiCalendar):
"""
Equinox and Solstices, Perihelion, and Aphelion
https://www.timeanddate.com/sun/@112931?month=3&year=1844
The site below is where I've gotten the Vernal Equinox data that uses
the 4, 100, and 400 algorithm, so we must also. The 4 and 128 algorithm
is more accurate, but I've not found Vernal Equinox data that uses it.
| https://data.giss.nasa.gov/modelE/ar5plots/srvernal.html
| https://aa.usno.navy.mil/data/Earth_Seasons
| https://www.astropixels.com/ephemeris/soleq2001.html
| https://stellafane.org/misc/equinox.html
| Julian Period day into:
| https://aa.usno.navy.mil/data/JulianDate
| Sun rise and set info:
| https://gml.noaa.gov/grad/solcalc/
| https://aa.usno.navy.mil/data/RS_OneYear
| https://www.sunrisesunset.com/England/GreaterLondon/Greenwich.asp
| Tehran: 35.682376, 51.285817 USED
"""
# Gregorian offset to the year before the Badí' epoch.
TRAN_COFF = 1843
# Coordinents of Bahji.
# 32°56′36″N 35°05′32″E (or 32.94333°N, 35.09222°E) IST = UTC+2.0
BAHJI_COORDS = (32.94333, 35.09222, 2.0)
# The coordinates and the sunset in the city of Tehran to determine the
# yearly Badí' epochs. Below are the Gregorian dates of the Vernal Equinox.
# This needs to be done because Meeus's algorithm uses an historically
# correct algorithm which is astronomically incorrect.
VE_0001_1582 = (
( 0, 3, 20, 7, 30), ( 1, 3, 20, 13, 19), ( 2, 3, 20, 19, 8),
( 3, 3, 21, 0, 58), ( 4, 3, 20, 6, 47), ( 5, 3, 20, 12, 36),
( 6, 3, 20, 18, 25), ( 7, 3, 21, 0, 14), ( 8, 3, 20, 6, 4),
( 9, 3, 20, 11, 53), ( 10, 3, 20, 17, 42), ( 11, 3, 20, 23, 31),
( 12, 3, 20, 5, 20), ( 13, 3, 20, 11, 10), ( 14, 3, 20, 16, 59),
( 15, 3, 20, 22, 48), ( 16, 3, 20, 4, 37), ( 17, 3, 20, 10, 26),
( 18, 3, 20, 16, 16), ( 19, 3, 20, 22, 5), ( 20, 3, 20, 3, 54),
( 21, 3, 20, 9, 43), ( 22, 3, 20, 15, 32), ( 23, 3, 20, 21, 22),
( 24, 3, 20, 3, 11), ( 25, 3, 20, 9, 0), ( 26, 3, 20, 14, 49),
( 27, 3, 20, 20, 38), ( 28, 3, 20, 2, 28), ( 29, 3, 20, 8, 17),
( 30, 3, 20, 14, 6), ( 31, 3, 20, 19, 55), ( 32, 3, 20, 1, 44),
( 33, 3, 20, 7, 34), ( 34, 3, 20, 13, 23), ( 35, 3, 20, 19, 12),
( 36, 3, 20, 1, 1), ( 37, 3, 20, 6, 50), ( 38, 3, 20, 12, 40),
( 39, 3, 20, 18, 29), ( 40, 3, 20, 0, 18), ( 41, 3, 20, 6, 7),
( 42, 3, 20, 11, 56), ( 43, 3, 20, 17, 46), ( 44, 3, 19, 23, 35),
( 45, 3, 20, 5, 24), ( 46, 3, 20, 11, 13), ( 47, 3, 20, 17, 2),
( 48, 3, 19, 22, 52), ( 49, 3, 20, 4, 41), ( 50, 3, 20, 10, 30),
( 51, 3, 20, 16, 19), ( 52, 3, 19, 22, 8), ( 53, 3, 20, 3, 58),
( 54, 3, 20, 9, 47), ( 55, 3, 20, 15, 36), ( 56, 3, 19, 21, 25),
( 57, 3, 20, 3, 14), ( 58, 3, 20, 9, 4), ( 59, 3, 20, 14, 53),
( 60, 3, 19, 20, 42), ( 61, 3, 20, 2, 31), ( 62, 3, 20, 8, 20),
( 63, 3, 20, 14, 10), ( 64, 3, 19, 19, 59), ( 65, 3, 20, 1, 48),
( 66, 3, 20, 7, 37), ( 67, 3, 20, 13, 26), ( 68, 3, 19, 19, 16),
( 69, 3, 20, 1, 5), ( 70, 3, 20, 6, 54), ( 71, 3, 20, 12, 43),
( 72, 3, 19, 18, 32), ( 73, 3, 20, 0, 22), ( 74, 3, 20, 6, 11),
( 75, 3, 20, 12, 0), ( 76, 3, 19, 17, 49), ( 77, 3, 19, 23, 38),
( 78, 3, 20, 5, 28), ( 79, 3, 20, 11, 17), ( 80, 3, 19, 17, 6),
( 81, 3, 19, 22, 55), ( 82, 3, 20, 4, 44), ( 83, 3, 20, 10, 34),
( 84, 3, 19, 16, 23), ( 85, 3, 19, 22, 12), ( 86, 3, 20, 4, 1),
( 87, 3, 20, 9, 50), ( 88, 3, 19, 15, 40), ( 89, 3, 19, 21, 29),
( 90, 3, 20, 3, 18), ( 91, 3, 20, 9, 7), ( 92, 3, 19, 14, 56),
( 93, 3, 19, 20, 46), ( 94, 3, 20, 2, 35), ( 95, 3, 20, 8, 24),
( 96, 3, 19, 14, 13), ( 97, 3, 19, 20, 2), ( 98, 3, 20, 1, 52),
( 99, 3, 20, 7, 41), ( 100, 3, 20, 13, 30), ( 101, 3, 20, 19, 19),
( 102, 3, 21, 1, 8), ( 103, 3, 21, 6, 58), ( 104, 3, 20, 12, 47),
( 105, 3, 20, 18, 36), ( 106, 3, 21, 0, 25), ( 107, 3, 21, 6, 14),
( 108, 3, 20, 12, 4), ( 109, 3, 20, 17, 53), ( 110, 3, 20, 23, 42),
( 111, 3, 21, 5, 31), ( 112, 3, 20, 11, 20), ( 113, 3, 20, 17, 10),
( 114, 3, 20, 22, 59), ( 115, 3, 21, 4, 48), ( 116, 3, 20, 10, 37),
( 117, 3, 20, 16, 26), ( 118, 3, 20, 22, 16), ( 119, 3, 21, 4, 5),
( 120, 3, 20, 9, 54), ( 121, 3, 20, 15, 43), ( 122, 3, 20, 21, 32),
( 123, 3, 21, 3, 22), ( 124, 3, 20, 9, 11), ( 125, 3, 20, 15, 0),
( 126, 3, 20, 20, 49), ( 127, 3, 21, 2, 38), ( 128, 3, 20, 8, 28),
( 129, 3, 20, 14, 17), ( 130, 3, 20, 20, 6), ( 131, 3, 21, 1, 55),
( 132, 3, 20, 7, 44), ( 133, 3, 20, 13, 34), ( 134, 3, 20, 19, 23),
( 135, 3, 21, 1, 12), ( 136, 3, 20, 7, 1), ( 137, 3, 20, 12, 50),
( 138, 3, 20, 18, 40), ( 139, 3, 21, 0, 29), ( 140, 3, 20, 6, 18),
( 141, 3, 20, 12, 7), ( 142, 3, 20, 17, 56), ( 143, 3, 20, 23, 46),
( 144, 3, 20, 5, 35), ( 145, 3, 20, 11, 24), ( 146, 3, 20, 17, 13),
( 147, 3, 20, 23, 2), ( 148, 3, 20, 4, 52), ( 149, 3, 20, 10, 41),
( 150, 3, 20, 16, 30), ( 151, 3, 20, 22, 19), ( 152, 3, 20, 4, 8),
( 153, 3, 20, 9, 58), ( 154, 3, 20, 15, 47), ( 155, 3, 20, 21, 36),
( 156, 3, 20, 3, 25), ( 157, 3, 20, 9, 14), ( 158, 3, 20, 15, 4),
( 159, 3, 20, 20, 53), ( 160, 3, 20, 2, 42), ( 161, 3, 20, 8, 31),
( 162, 3, 20, 14, 20), ( 163, 3, 20, 20, 10), ( 164, 3, 20, 1, 59),
( 165, 3, 20, 7, 48), ( 166, 3, 20, 13, 37), ( 167, 3, 20, 19, 26),
( 168, 3, 20, 1, 16), ( 169, 3, 20, 7, 5), ( 170, 3, 20, 12, 54),
( 171, 3, 20, 18, 43), ( 172, 3, 20, 0, 32), ( 173, 3, 20, 6, 22),
( 174, 3, 20, 12, 11), ( 175, 3, 20, 18, 0), ( 176, 3, 19, 23, 49),
( 177, 3, 20, 5, 38), ( 178, 3, 20, 11, 28), ( 179, 3, 20, 17, 17),
( 180, 3, 19, 23, 6), ( 181, 3, 20, 4, 55), ( 182, 3, 20, 10, 44),
( 183, 3, 20, 16, 34), ( 184, 3, 19, 22, 23), ( 185, 3, 20, 4, 12),
( 186, 3, 20, 10, 1), ( 187, 3, 20, 15, 50), ( 188, 3, 19, 21, 40),
( 189, 3, 20, 3, 29), ( 190, 3, 20, 9, 18), ( 191, 3, 20, 15, 7),
( 192, 3, 19, 20, 56), ( 193, 3, 20, 2, 46), ( 194, 3, 20, 8, 35),
( 195, 3, 20, 14, 24), ( 196, 3, 19, 20, 13), ( 197, 3, 20, 2, 2),
( 198, 3, 20, 7, 52), ( 199, 3, 20, 13, 41), ( 200, 3, 20, 19, 30),
( 201, 3, 21, 1, 19), ( 202, 3, 21, 7, 8), ( 203, 3, 21, 12, 58),
( 204, 3, 20, 18, 47), ( 205, 3, 21, 0, 36), ( 206, 3, 21, 6, 25),
( 207, 3, 21, 12, 14), ( 208, 3, 20, 18, 4), ( 209, 3, 20, 23, 53),
( 210, 3, 21, 5, 42), ( 211, 3, 21, 11, 31), ( 212, 3, 20, 17, 20),
( 213, 3, 20, 23, 10), ( 214, 3, 21, 4, 59), ( 215, 3, 21, 10, 48),
( 216, 3, 20, 16, 37), ( 217, 3, 20, 22, 26), ( 218, 3, 21, 4, 16),
( 219, 3, 21, 10, 5), ( 220, 3, 20, 15, 54), ( 221, 3, 20, 21, 43),
( 222, 3, 21, 3, 32), ( 223, 3, 21, 9, 22), ( 224, 3, 20, 15, 11),
( 225, 3, 20, 21, 0), ( 226, 3, 21, 2, 49), ( 227, 3, 21, 8, 38),
( 228, 3, 20, 14, 28), ( 229, 3, 20, 20, 17), ( 230, 3, 21, 2, 6),
( 231, 3, 21, 7, 55), ( 232, 3, 20, 13, 44), ( 233, 3, 20, 19, 34),
( 234, 3, 21, 1, 23), ( 235, 3, 21, 7, 12), ( 236, 3, 20, 13, 1),
( 237, 3, 20, 18, 50), ( 238, 3, 21, 0, 40), ( 239, 3, 21, 6, 29),
( 240, 3, 20, 12, 18), ( 241, 3, 20, 18, 7), ( 242, 3, 20, 23, 56),
( 243, 3, 21, 5, 46), ( 244, 3, 20, 11, 35), ( 245, 3, 20, 17, 24),
( 246, 3, 20, 23, 13), ( 247, 3, 21, 5, 2), ( 248, 3, 20, 10, 52),
( 249, 3, 20, 16, 41), ( 250, 3, 20, 22, 30), ( 251, 3, 21, 4, 19),
( 252, 3, 20, 10, 8), ( 253, 3, 20, 15, 58), ( 254, 3, 20, 21, 47),
( 255, 3, 21, 3, 36), ( 256, 3, 20, 9, 25), ( 257, 3, 20, 15, 14),
( 258, 3, 20, 21, 4), ( 259, 3, 21, 2, 53), ( 260, 3, 20, 8, 42),
( 261, 3, 20, 14, 31), ( 262, 3, 20, 20, 20), ( 263, 3, 21, 2, 10),
( 264, 3, 20, 7, 59), ( 265, 3, 20, 13, 48), ( 266, 3, 20, 19, 37),
( 267, 3, 21, 1, 26), ( 268, 3, 20, 13, 5), ( 269, 3, 20, 7, 16),
( 270, 3, 20, 18, 54), ( 271, 3, 21, 0, 43), ( 272, 3, 20, 12, 22),
( 273, 3, 20, 12, 22), ( 274, 3, 20, 18, 11), ( 275, 3, 21, 0, 0),
( 276, 3, 20, 11, 38), ( 277, 3, 20, 11, 38), ( 278, 3, 20, 17, 28),
( 279, 3, 20, 23, 17), ( 280, 3, 20, 5, 6), ( 281, 3, 20, 10, 55),
( 282, 3, 20, 16, 44), ( 283, 3, 20, 22, 34), ( 284, 3, 20, 4, 23),
( 285, 3, 20, 10, 12), ( 286, 3, 20, 16, 1), ( 287, 3, 20, 21, 50),
( 288, 3, 20, 3, 40), ( 289, 3, 20, 9, 29), ( 290, 3, 20, 15, 18),
( 291, 3, 20, 21, 7), ( 292, 3, 20, 2, 56), ( 293, 3, 20, 8, 46),
( 294, 3, 20, 14, 35), ( 295, 3, 20, 20, 24), ( 296, 3, 20, 2, 13),
( 297, 3, 20, 8, 2), ( 298, 3, 20, 13, 52), ( 299, 3, 20, 19, 41),
( 300, 3, 21, 1, 39), ( 301, 3, 21, 7, 19), ( 302, 3, 21, 13, 8),
( 303, 3, 21, 18, 58), ( 304, 3, 21, 0, 47), ( 305, 3, 21, 6, 36),
( 306, 3, 21, 12, 25), ( 307, 3, 21, 18, 14), ( 308, 3, 21, 0, 4),
( 309, 3, 21, 5, 53), ( 310, 3, 21, 11, 42), ( 311, 3, 21, 17, 31),
( 312, 3, 20, 23, 20), ( 313, 3, 21, 5, 10), ( 314, 3, 21, 10, 59),
( 315, 3, 21, 16, 48), ( 316, 3, 20, 22, 37), ( 317, 3, 21, 4, 26),
( 318, 3, 21, 10, 16), ( 319, 3, 21, 16, 5), ( 320, 3, 20, 21, 54),
( 321, 3, 21, 3, 43), ( 322, 3, 21, 9, 32), ( 323, 3, 21, 15, 22),
( 324, 3, 20, 21, 11), ( 325, 3, 21, 3, 0), ( 326, 3, 21, 8, 49),
( 327, 3, 21, 14, 38), ( 328, 3, 20, 20, 28), ( 329, 3, 21, 2, 17),
( 330, 3, 21, 8, 6), ( 331, 3, 21, 13, 55), ( 332, 3, 20, 19, 44),
( 333, 3, 21, 1, 34), ( 334, 3, 21, 7, 23), ( 335, 3, 21, 13, 12),
( 336, 3, 20, 19, 1), ( 337, 3, 21, 0, 50), ( 338, 3, 21, 6, 40),
( 339, 3, 21, 12, 29), ( 340, 3, 20, 18, 18), ( 341, 3, 21, 0, 7),
( 342, 3, 21, 5, 56), ( 343, 3, 21, 11, 46), ( 344, 3, 20, 17, 35),
( 345, 3, 20, 23, 24), ( 346, 3, 21, 5, 13), ( 347, 3, 21, 11, 2),
( 348, 3, 20, 15, 52), ( 349, 3, 20, 22, 41), ( 350, 3, 21, 4, 30),
( 351, 3, 21, 10, 19), ( 352, 3, 20, 16, 8), ( 353, 3, 20, 21, 58),
( 354, 3, 21, 3, 47), ( 355, 3, 21, 9, 36), ( 356, 3, 20, 15, 25),
( 357, 3, 20, 21, 14), ( 358, 3, 21, 3, 4), ( 359, 3, 21, 8, 53),
( 360, 3, 20, 14, 42), ( 361, 3, 20, 20, 31), ( 362, 3, 21, 2, 20),
( 363, 3, 21, 8, 10), ( 364, 3, 20, 13, 59), ( 365, 3, 20, 19, 48),
( 366, 3, 21, 1, 37), ( 367, 3, 21, 7, 26), ( 368, 3, 20, 13, 16),
( 369, 3, 20, 19, 5), ( 370, 3, 21, 0, 54), ( 371, 3, 21, 6, 43),
( 372, 3, 20, 12, 32), ( 373, 3, 20, 18, 22), ( 374, 3, 21, 0, 11),
( 375, 3, 21, 6, 0), ( 376, 3, 20, 11, 49), ( 377, 3, 20, 17, 38),
( 378, 3, 20, 23, 28), ( 379, 3, 21, 5, 17), ( 380, 3, 20, 11, 6),
( 381, 3, 20, 16, 55), ( 382, 3, 20, 22, 44), ( 383, 3, 21, 4, 34),
( 384, 3, 20, 10, 23), ( 385, 3, 20, 16, 12), ( 386, 3, 20, 22, 1),
( 387, 3, 21, 3, 50), ( 388, 3, 20, 9, 40), ( 389, 3, 20, 15, 29),
( 390, 3, 20, 21, 18), ( 391, 3, 21, 3, 7), ( 392, 3, 20, 8, 56),
( 393, 3, 20, 14, 46), ( 394, 3, 20, 20, 35), ( 395, 3, 21, 2, 24),
( 396, 3, 20, 8, 13), ( 397, 3, 20, 14, 2), ( 398, 3, 20, 19, 52),
( 399, 3, 21, 1, 41), ( 400, 3, 20, 7, 30), ( 401, 3, 20, 13, 19),
( 402, 3, 20, 19, 8), ( 403, 3, 21, 0, 58), ( 404, 3, 20, 6, 47),
( 405, 3, 20, 12, 36), ( 406, 3, 20, 18, 25), ( 407, 3, 21, 0, 14),
( 408, 3, 20, 6, 4), ( 409, 3, 20, 11, 53), ( 410, 3, 20, 17, 42),
( 411, 3, 20, 23, 31), ( 412, 3, 20, 5, 20), ( 413, 3, 20, 11, 10),
( 414, 3, 20, 16, 59), ( 415, 3, 20, 22, 48), ( 416, 3, 20, 4, 37),
( 417, 3, 20, 10, 26), ( 418, 3, 20, 16, 16), ( 419, 3, 20, 20, 5),
( 420, 3, 20, 3, 54), ( 421, 3, 20, 9, 43), ( 422, 3, 20, 15, 32),
( 423, 3, 20, 21, 22), ( 424, 3, 20, 3, 11), ( 425, 3, 20, 9, 0),
( 426, 3, 20, 14, 49), ( 427, 3, 20, 20, 38), ( 428, 3, 20, 2, 28),
( 429, 3, 20, 8, 17), ( 430, 3, 20, 14, 6), ( 431, 3, 20, 19, 55),
( 432, 3, 20, 1, 44), ( 433, 3, 20, 7, 34), ( 434, 3, 20, 13, 23),
( 435, 3, 20, 19, 12), ( 436, 3, 20, 1, 1), ( 437, 3, 20, 6, 50),
( 438, 3, 20, 12, 40), ( 439, 3, 20, 18, 29), ( 440, 3, 20, 0, 18),
( 441, 3, 20, 6, 7), ( 442, 3, 20, 11, 56), ( 443, 3, 20, 17, 46),
( 444, 3, 19, 23, 35), ( 445, 3, 20, 5, 24), ( 446, 3, 20, 11, 13),
( 447, 3, 20, 17, 2), ( 448, 3, 19, 22, 52), ( 449, 3, 20, 4, 41),
( 450, 3, 20, 10, 30), ( 451, 3, 20, 16, 19), ( 452, 3, 19, 22, 8),
( 453, 3, 20, 3, 58), ( 454, 3, 20, 9, 47), ( 455, 3, 20, 15, 36),
( 456, 3, 19, 21, 25), ( 457, 3, 20, 3, 14), ( 458, 3, 20, 9, 4),
( 459, 3, 20, 14, 53), ( 460, 3, 19, 20, 42), ( 461, 3, 20, 2, 31),
( 462, 3, 20, 8, 20), ( 463, 3, 20, 14, 10), ( 464, 3, 19, 19, 59),
( 465, 3, 20, 1, 48), ( 466, 3, 20, 7, 37), ( 467, 3, 20, 13, 26),
( 468, 3, 19, 19, 16), ( 469, 3, 20, 1, 5), ( 470, 3, 20, 6, 54),
( 471, 3, 20, 12, 43), ( 472, 3, 19, 18, 32), ( 473, 3, 20, 0, 22),
( 474, 3, 20, 6, 11), ( 475, 3, 20, 12, 0), ( 476, 3, 19, 17, 49),
( 477, 3, 19, 23, 38), ( 478, 3, 20, 5, 28), ( 479, 3, 20, 11, 17),
( 480, 3, 19, 17, 6), ( 481, 3, 19, 22, 55), ( 482, 3, 20, 4, 44),
( 483, 3, 20, 10, 34), ( 484, 3, 19, 16, 33), ( 485, 3, 19, 22, 12),
( 486, 3, 20, 4, 1), ( 487, 3, 20, 9, 50), ( 488, 3, 19, 15, 40),
( 489, 3, 19, 21, 29), ( 490, 3, 20, 3, 18), ( 491, 3, 20, 9, 7),
( 492, 3, 19, 14, 56), ( 493, 3, 19, 20, 46), ( 494, 3, 20, 2, 35),
( 495, 3, 20, 8, 24), ( 496, 3, 19, 14, 13), ( 497, 3, 19, 20, 2),
( 498, 3, 20, 1, 52), ( 499, 3, 20, 7, 41), ( 500, 3, 20, 13, 30),
( 501, 3, 20, 19, 19), ( 502, 3, 21, 1, 8), ( 503, 3, 21, 6, 58),
( 504, 3, 20, 12, 47), ( 505, 3, 20, 18, 36), ( 506, 3, 21, 0, 25),
( 507, 3, 21, 6, 14), ( 508, 3, 20, 12, 4), ( 509, 3, 20, 17, 53),
( 510, 3, 20, 23, 42), ( 511, 3, 21, 5, 31), ( 512, 3, 20, 11, 20),
( 513, 3, 20, 17, 10), ( 514, 3, 20, 22, 59), ( 515, 3, 21, 4, 48),
( 516, 3, 20, 10, 37), ( 517, 3, 20, 16, 26), ( 518, 3, 20, 22, 16),
( 519, 3, 21, 4, 5), ( 520, 3, 20, 9, 54), ( 521, 3, 20, 15, 43),
( 522, 3, 20, 21, 32), ( 523, 3, 21, 3, 22), ( 524, 3, 20, 9, 11),
( 525, 3, 20, 15, 0), ( 526, 3, 20, 20, 49), ( 527, 3, 21, 2, 38),
( 528, 3, 20, 8, 28), ( 529, 3, 20, 14, 17), ( 530, 3, 20, 20, 6),
( 531, 3, 21, 1, 55), ( 532, 3, 20, 7, 44), ( 533, 3, 20, 13, 34),
( 534, 3, 20, 19, 23), ( 535, 3, 21, 1, 12), ( 536, 3, 20, 7, 1),
( 537, 3, 20, 12, 50), ( 538, 3, 20, 18, 40), ( 539, 3, 21, 0, 29),
( 540, 3, 20, 6, 18), ( 541, 3, 20, 12, 7), ( 542, 3, 20, 17, 56),
( 543, 3, 20, 23, 46), ( 544, 3, 20, 5, 35), ( 545, 3, 20, 11, 24),
( 546, 3, 20, 17, 13), ( 547, 3, 20, 23, 2), ( 548, 3, 20, 4, 52),
( 549, 3, 20, 10, 41), ( 550, 3, 20, 16, 30), ( 551, 3, 20, 22, 19),
( 552, 3, 20, 4, 8), ( 553, 3, 20, 9, 58), ( 554, 3, 20, 15, 47),
( 555, 3, 20, 21, 36), ( 556, 3, 20, 3, 25), ( 557, 3, 20, 9, 14),
( 558, 3, 20, 15, 4), ( 559, 3, 20, 20, 53), ( 560, 3, 20, 2, 42),
( 561, 3, 20, 8, 31), ( 562, 3, 20, 14, 20), ( 563, 3, 20, 20, 10),
( 564, 3, 20, 1, 59), ( 565, 3, 20, 7, 48), ( 566, 3, 20, 13, 37),
( 567, 3, 20, 19, 26), ( 568, 3, 20, 1, 16), ( 569, 3, 20, 7, 5),
( 570, 3, 20, 12, 54), ( 571, 3, 20, 18, 43), ( 572, 3, 20, 0, 32),
( 573, 3, 20, 6, 22), ( 574, 3, 20, 12, 11), ( 575, 3, 20, 18, 0),
( 576, 3, 19, 23, 49), ( 577, 3, 20, 5, 38), ( 578, 3, 20, 11, 28),
( 579, 3, 20, 17, 17), ( 580, 3, 19, 23, 6), ( 581, 3, 20, 4, 55),
( 582, 3, 20, 10, 44), ( 583, 3, 20, 16, 34), ( 584, 3, 19, 22, 23),
( 585, 3, 20, 4, 12), ( 586, 3, 20, 10, 1), ( 587, 3, 20, 15, 50),
( 588, 3, 19, 21, 40), ( 589, 3, 20, 3, 29), ( 590, 3, 20, 9, 18),
( 591, 3, 20, 15, 7), ( 592, 3, 19, 20, 56), ( 593, 3, 20, 2, 46),
( 594, 3, 20, 8, 35), ( 595, 3, 20, 14, 24), ( 596, 3, 19, 20, 13),
( 597, 3, 20, 2, 2), ( 598, 3, 20, 7, 52), ( 599, 3, 20, 13, 41),
( 600, 3, 20, 19, 30), ( 601, 3, 21, 1, 19), ( 602, 3, 21, 7, 8),
( 603, 3, 21, 12, 58), ( 604, 3, 20, 18, 47), ( 605, 3, 21, 0, 36),
( 606, 3, 21, 6, 25), ( 607, 3, 21, 12, 14), ( 608, 3, 20, 18, 4),
( 609, 3, 20, 23, 53), ( 610, 3, 21, 5, 42), ( 611, 3, 21, 11, 31),
( 612, 3, 20, 17, 20), ( 613, 3, 20, 23, 20), ( 614, 3, 21, 4, 59),
( 615, 3, 21, 10, 48), ( 616, 3, 20, 16, 37), ( 617, 3, 20, 22, 26),
( 618, 3, 21, 4, 16), ( 619, 3, 21, 10, 5), ( 620, 3, 20, 15, 54),
( 621, 3, 20, 21, 43), ( 622, 3, 21, 3, 32), ( 623, 3, 21, 9, 22),
( 624, 3, 20, 15, 11), ( 625, 3, 20, 21, 0), ( 626, 3, 21, 2, 49),
( 627, 3, 21, 8, 38), ( 628, 3, 20, 14, 28), ( 629, 3, 20, 20, 17),
( 630, 3, 21, 2, 6), ( 631, 3, 21, 7, 55), ( 632, 3, 20, 13, 44),
( 633, 3, 20, 19, 34), ( 634, 3, 21, 1, 23), ( 635, 3, 21, 7, 12),
( 636, 3, 20, 13, 1), ( 637, 3, 20, 18, 50), ( 638, 3, 21, 0, 40),
( 639, 3, 21, 6, 29), ( 640, 3, 20, 12, 18), ( 641, 3, 20, 18, 7),
( 642, 3, 20, 23, 56), ( 643, 3, 21, 5, 46), ( 644, 3, 20, 11, 35),
( 645, 3, 20, 17, 24), ( 646, 3, 20, 23, 13), ( 647, 3, 21, 5, 2),
( 648, 3, 20, 10, 52), ( 649, 3, 20, 16, 41), ( 650, 3, 20, 22, 30),
( 651, 3, 21, 4, 19), ( 652, 3, 20, 10, 8), ( 653, 3, 20, 15, 58),
( 654, 3, 20, 21, 47), ( 655, 3, 21, 3, 36), ( 656, 3, 20, 9, 25),
( 657, 3, 20, 15, 14), ( 658, 3, 20, 21, 4), ( 659, 3, 21, 2, 53),
( 660, 3, 20, 8, 42), ( 661, 3, 20, 14, 31), ( 662, 3, 20, 20, 20),
( 663, 3, 21, 2, 10), ( 664, 3, 20, 7, 59), ( 665, 3, 20, 13, 48),
( 666, 3, 20, 19, 37), ( 667, 3, 21, 1, 26), ( 668, 3, 20, 7, 16),
( 669, 3, 20, 13, 5), ( 670, 3, 20, 18, 54), ( 671, 3, 21, 0, 43),
( 672, 3, 20, 6, 32), ( 673, 3, 20, 12, 22), ( 674, 3, 20, 18, 11),
( 675, 3, 21, 0, 0), ( 676, 3, 20, 5, 49), ( 677, 3, 20, 11, 38),
( 678, 3, 20, 17, 28), ( 679, 3, 20, 23, 17), ( 680, 3, 20, 5, 6),
( 681, 3, 20, 10, 55), ( 682, 3, 20, 16, 44), ( 683, 3, 20, 22, 34),
( 684, 3, 20, 4, 23), ( 685, 3, 20, 10, 12), ( 686, 3, 20, 16, 1),
( 687, 3, 20, 21, 50), ( 688, 3, 20, 3, 40), ( 689, 3, 20, 9, 29),
( 690, 3, 20, 15, 18), ( 691, 3, 20, 21, 7), ( 692, 3, 20, 2, 56),
( 693, 3, 20, 8, 46), ( 694, 3, 20, 14, 35), ( 695, 3, 20, 20, 24),
( 696, 3, 20, 2, 13), ( 697, 3, 20, 8, 2), ( 698, 3, 20, 13, 52),
( 699, 3, 20, 19, 41), ( 700, 3, 21, 1, 30), ( 701, 3, 21, 7, 19),
( 702, 3, 21, 13, 8), ( 703, 3, 21, 18, 58), ( 704, 3, 21, 0, 47),
( 705, 3, 21, 6, 36), ( 706, 3, 21, 12, 25), ( 707, 3, 21, 18, 14),
( 708, 3, 21, 0, 4), ( 709, 3, 21, 5, 53), ( 710, 3, 21, 11, 42),
( 711, 3, 21, 17, 31), ( 712, 3, 20, 23, 20), ( 713, 3, 21, 5, 10),
( 714, 3, 21, 10, 59), ( 715, 3, 21, 16, 48), ( 716, 3, 20, 22, 37),
( 717, 3, 21, 4, 26), ( 718, 3, 21, 10, 16), ( 719, 3, 21, 16, 5),
( 720, 3, 20, 21, 54), ( 721, 3, 21, 3, 43), ( 722, 3, 21, 9, 32),
( 723, 3, 21, 15, 22), ( 724, 3, 20, 21, 11), ( 725, 3, 21, 3, 0),
( 726, 3, 21, 8, 49), ( 727, 3, 21, 14, 38), ( 728, 3, 20, 20, 28),
( 729, 3, 21, 2, 17), ( 730, 3, 21, 8, 6), ( 731, 3, 21, 13, 55),
( 732, 3, 20, 19, 44), ( 733, 3, 21, 1, 34), ( 734, 3, 21, 7, 23),
( 735, 3, 21, 13, 12), ( 736, 3, 20, 19, 1), ( 737, 3, 21, 0, 50),
( 738, 3, 21, 6, 40), ( 739, 3, 21, 12, 29), ( 740, 3, 20, 18, 18),
( 741, 3, 21, 0, 7), ( 742, 3, 21, 5, 56), ( 743, 3, 21, 11, 46),
( 744, 3, 20, 17, 35), ( 745, 3, 20, 23, 24), ( 746, 3, 21, 5, 13),
( 747, 3, 21, 11, 2), ( 748, 3, 20, 16, 22), ( 749, 3, 20, 22, 41),
( 750, 3, 21, 4, 30), ( 751, 3, 21, 10, 19), ( 752, 3, 20, 16, 8),
( 753, 3, 20, 21, 58), ( 754, 3, 21, 3, 47), ( 755, 3, 21, 9, 36),
( 756, 3, 20, 15, 25), ( 757, 3, 20, 21, 14), ( 758, 3, 21, 3, 4),
( 759, 3, 21, 8, 53), ( 760, 3, 20, 14, 42), ( 761, 3, 20, 20, 31),
( 762, 3, 21, 2, 20), ( 763, 3, 21, 8, 10), ( 764, 3, 20, 13, 59),
( 765, 3, 20, 19, 48), ( 766, 3, 21, 1, 37), ( 767, 3, 21, 7, 26),
( 768, 3, 20, 13, 16), ( 769, 3, 20, 19, 5), ( 770, 3, 21, 0, 54),
( 771, 3, 21, 6, 43), ( 772, 3, 20, 12, 32), ( 773, 3, 20, 18, 22),
( 774, 3, 21, 0, 11), ( 775, 3, 21, 6, 0), ( 776, 3, 20, 11, 49),
( 777, 3, 20, 17, 38), ( 778, 3, 20, 23, 28), ( 779, 3, 21, 5, 17),
( 780, 3, 20, 11, 6), ( 781, 3, 20, 16, 55), ( 782, 3, 20, 22, 44),
( 783, 3, 21, 4, 34), ( 784, 3, 20, 10, 23), ( 785, 3, 20, 16, 12),
( 786, 3, 20, 22, 1), ( 787, 3, 21, 3, 50), ( 788, 3, 20, 9, 40),
( 789, 3, 20, 15, 29), ( 790, 3, 20, 21, 18), ( 791, 3, 21, 3, 7),
( 792, 3, 20, 8, 56), ( 793, 3, 20, 14, 46), ( 794, 3, 20, 20, 35),
( 795, 3, 21, 2, 24), ( 796, 3, 20, 8, 13), ( 797, 3, 20, 14, 2),
( 798, 3, 20, 19, 52), ( 799, 3, 21, 1, 41), ( 800, 3, 20, 7, 30),
( 801, 3, 20, 13, 19), ( 802, 3, 20, 19, 8), ( 803, 3, 21, 0, 58),
( 804, 3, 20, 6, 47), ( 805, 3, 20, 12, 36), ( 806, 3, 20, 18, 25),
( 807, 3, 21, 0, 14), ( 808, 3, 20, 6, 4), ( 809, 3, 20, 11, 53),
( 810, 3, 20, 17, 42), ( 811, 3, 20, 23, 31), ( 812, 3, 20, 5, 20),
( 813, 3, 20, 11, 10), ( 814, 3, 20, 16, 59), ( 815, 3, 20, 22, 48),
( 816, 3, 20, 4, 37), ( 817, 3, 20, 10, 26), ( 818, 3, 20, 16, 16),
( 819, 3, 20, 22, 5), ( 820, 3, 20, 3, 54), ( 821, 3, 20, 9, 43),
( 822, 3, 20, 15, 32), ( 823, 3, 20, 21, 22), ( 824, 3, 20, 3, 11),
( 825, 3, 20, 9, 0), ( 826, 3, 20, 14, 49), ( 827, 3, 20, 20, 38),
( 828, 3, 20, 2, 28), ( 829, 3, 20, 8, 17), ( 830, 3, 20, 14, 6),
( 831, 3, 20, 19, 55), ( 832, 3, 20, 1, 44), ( 833, 3, 20, 7, 34),
( 834, 3, 20, 13, 23), ( 835, 3, 20, 19, 12), ( 836, 3, 20, 1, 1),
( 837, 3, 20, 6, 50), ( 838, 3, 20, 12, 40), ( 839, 3, 20, 18, 29),
( 840, 3, 20, 0, 18), ( 841, 3, 20, 6, 7), ( 842, 3, 20, 11, 56),
( 843, 3, 20, 17, 46), ( 844, 3, 19, 23, 35), ( 845, 3, 20, 5, 24),
( 846, 3, 20, 11, 13), ( 847, 3, 20, 17, 2), ( 848, 3, 19, 22, 52),
( 849, 3, 20, 4, 41), ( 850, 3, 20, 10, 30), ( 851, 3, 20, 16, 19),
( 852, 3, 19, 22, 8), ( 853, 3, 20, 3, 58), ( 854, 3, 20, 9, 47),
( 855, 3, 20, 15, 36), ( 856, 3, 19, 21, 25), ( 857, 3, 20, 3, 14),
( 858, 3, 20, 9, 4), ( 859, 3, 20, 14, 53), ( 860, 3, 19, 20, 42),
( 861, 3, 20, 2, 31), ( 862, 3, 20, 8, 20), ( 863, 3, 20, 14, 10),
( 864, 3, 19, 19, 59), ( 865, 3, 20, 1, 48), ( 866, 3, 20, 7, 37),
( 867, 3, 20, 13, 26), ( 868, 3, 19, 19, 16), ( 869, 3, 20, 1, 5),
( 870, 3, 20, 6, 54), ( 871, 3, 20, 12, 43), ( 872, 3, 19, 18, 32),
( 873, 3, 20, 0, 22), ( 874, 3, 20, 6, 11), ( 875, 3, 20, 12, 0),
( 876, 3, 19, 17, 49), ( 877, 3, 19, 23, 38), ( 878, 3, 20, 5, 28),
( 879, 3, 20, 11, 17), ( 880, 3, 19, 17, 6), ( 881, 3, 19, 22, 55),
( 882, 3, 20, 4, 44), ( 883, 3, 20, 10, 34), ( 884, 3, 19, 16, 23),
( 885, 3, 19, 22, 12), ( 886, 3, 20, 9, 50), ( 887, 3, 20, 9, 50),
( 888, 3, 19, 15, 40), ( 889, 3, 19, 21, 29), ( 890, 3, 20, 3, 18),
( 891, 3, 20, 9, 7), ( 892, 3, 19, 14, 56), ( 893, 3, 19, 20, 46),
( 894, 3, 20, 2, 35), ( 895, 3, 20, 8, 24), ( 896, 3, 19, 14, 13),
( 897, 3, 19, 20, 2), ( 898, 3, 20, 1, 52), ( 899, 3, 20, 7, 41),
( 900, 3, 20, 13, 30), ( 901, 3, 20, 19, 19), ( 902, 3, 21, 1, 8),
( 903, 3, 21, 6, 58), ( 904, 3, 20, 12, 47), ( 905, 3, 20, 18, 36),
( 906, 3, 21, 0, 25), ( 907, 3, 21, 6, 14), ( 908, 3, 20, 12, 4),
( 909, 3, 20, 17, 53), ( 910, 3, 20, 23, 42), ( 911, 3, 21, 5, 31),
( 912, 3, 20, 11, 20), ( 913, 3, 20, 17, 10), ( 914, 3, 20, 22, 59),
( 915, 3, 21, 4, 48), ( 916, 3, 20, 10, 37), ( 917, 3, 20, 16, 26),
( 918, 3, 20, 22, 16), ( 919, 3, 21, 4, 5), ( 920, 3, 20, 9, 54),
( 921, 3, 20, 15, 43), ( 922, 3, 20, 21, 32), ( 923, 3, 21, 3, 22),
( 924, 3, 20, 9, 11), ( 925, 3, 20, 15, 0), ( 926, 3, 20, 20, 49),
( 927, 3, 21, 2, 38), ( 928, 3, 20, 14, 17), ( 929, 3, 20, 14, 17),
( 930, 3, 20, 20, 6), ( 931, 3, 21, 1, 55), ( 932, 3, 20, 7, 44),
( 933, 3, 20, 13, 34), ( 934, 3, 20, 19, 23), ( 935, 3, 21, 1, 12),
( 936, 3, 20, 7, 1), ( 937, 3, 20, 12, 50), ( 938, 3, 20, 18, 40),
( 939, 3, 21, 0, 29), ( 940, 3, 20, 6, 18), ( 941, 3, 20, 12, 7),
( 942, 3, 20, 17, 56), ( 943, 3, 20, 23, 46), ( 944, 3, 20, 5, 35),
( 945, 3, 20, 11, 24), ( 946, 3, 20, 17, 13), ( 947, 3, 20, 23, 2),
( 948, 3, 20, 4, 52), ( 949, 3, 20, 10, 41), ( 950, 3, 20, 16, 30),
( 951, 3, 20, 22, 19), ( 952, 3, 20, 4, 8), ( 953, 3, 20, 9, 58),
( 954, 3, 20, 15, 47), ( 955, 3, 20, 21, 36), ( 956, 3, 20, 3, 25),
( 957, 3, 20, 9, 14), ( 958, 3, 20, 15, 4), ( 959, 3, 20, 20, 53),
( 960, 3, 20, 2, 42), ( 961, 3, 20, 8, 31), ( 962, 3, 20, 14, 20),
( 963, 3, 20, 20, 10), ( 964, 3, 20, 1, 59), ( 965, 3, 20, 7, 48),
( 966, 3, 20, 13, 37), ( 967, 3, 20, 19, 26), ( 968, 3, 20, 1, 16),
( 969, 3, 20, 7, 5), ( 970, 3, 20, 12, 54), ( 971, 3, 20, 18, 43),
( 972, 3, 20, 0, 32), ( 973, 3, 20, 6, 22), ( 974, 3, 20, 12, 11),
( 975, 3, 20, 18, 0), ( 976, 3, 19, 23, 49), ( 977, 3, 20, 5, 38),
( 978, 3, 20, 11, 28), ( 979, 3, 20, 17, 17), ( 980, 3, 19, 23, 6),
( 981, 3, 20, 4, 55), ( 982, 3, 20, 10, 44), ( 983, 3, 20, 16, 34),
( 984, 3, 19, 22, 23), ( 985, 3, 20, 2, 12), ( 986, 3, 20, 10, 1),
( 987, 3, 20, 15, 50), ( 988, 3, 19, 21, 40), ( 989, 3, 20, 3, 29),
( 990, 3, 20, 9, 18), ( 991, 3, 20, 15, 7), ( 992, 3, 19, 20, 56),
( 993, 3, 20, 2, 46), ( 994, 3, 20, 8, 35), ( 995, 3, 20, 14, 24),
( 996, 3, 19, 20, 13), ( 997, 3, 20, 2, 2), ( 998, 3, 20, 7, 52),
( 999, 3, 20, 13, 41), (1000, 3, 20, 19, 30), (1001, 3, 21, 1, 19),
(1002, 3, 21, 7, 8), (1003, 3, 21, 12, 58), (1004, 3, 20, 18, 47),
(1005, 3, 21, 0, 36), (1006, 3, 21, 6, 25), (1007, 3, 21, 12, 14),
(1008, 3, 20, 18, 4), (1009, 3, 20, 18, 4), (1010, 3, 21, 5, 42),
(1011, 3, 21, 11, 31), (1012, 3, 20, 17, 20), (1013, 3, 20, 23, 10),
(1014, 3, 21, 4, 59), (1015, 3, 21, 10, 48), (1016, 3, 20, 16, 37),
(1017, 3, 20, 22, 26), (1018, 3, 21, 4, 16), (1019, 3, 21, 10, 5),
(1020, 3, 20, 15, 54), (1021, 3, 20, 21, 43), (1022, 3, 21, 3, 32),
(1023, 3, 21, 9, 22), (1024, 3, 20, 15, 11), (1025, 3, 20, 21, 0),
(1026, 3, 21, 2, 49), (1027, 3, 21, 8, 38), (1028, 3, 20, 14, 28),
(1029, 3, 20, 20, 17), (1030, 3, 21, 2, 6), (1031, 3, 21, 7, 55),
(1032, 3, 20, 13, 44), (1033, 3, 20, 19, 34), (1034, 3, 21, 1, 23),
(1035, 3, 21, 7, 12), (1036, 3, 20, 13, 1), (1037, 3, 20, 18, 50),
(1038, 3, 21, 0, 40), (1039, 3, 21, 6, 29), (1040, 3, 20, 12, 18),
(1041, 3, 20, 18, 7), (1042, 3, 20, 23, 56), (1043, 3, 21, 5, 46),
(1044, 3, 20, 11, 35), (1045, 3, 20, 17, 24), (1046, 3, 20, 23, 13),
(1047, 3, 21, 5, 2), (1048, 3, 20, 10, 52), (1049, 3, 20, 16, 41),
(1050, 3, 20, 22, 30), (1051, 3, 21, 4, 19), (1052, 3, 20, 10, 8),
(1053, 3, 20, 15, 58), (1054, 3, 20, 21, 47), (1055, 3, 21, 3, 36),
(1056, 3, 20, 9, 25), (1057, 3, 20, 15, 14), (1058, 3, 20, 21, 4),
(1059, 3, 21, 2, 53), (1060, 3, 20, 8, 42), (1061, 3, 20, 14, 31),
(1062, 3, 20, 20, 20), (1063, 3, 21, 2, 10), (1064, 3, 20, 7, 59),
(1065, 3, 20, 13, 48), (1066, 3, 20, 19, 37), (1067, 3, 21, 1, 26),
(1068, 3, 20, 13, 5), (1069, 3, 20, 13, 5), (1070, 3, 20, 18, 54),
(1071, 3, 21, 0, 43), (1072, 3, 20, 6, 33), (1073, 3, 20, 12, 22),
(1074, 3, 20, 18, 11), (1075, 3, 21, 0, 0), (1076, 3, 20, 5, 49),
(1077, 3, 20, 11, 38), (1078, 3, 20, 17, 28), (1079, 3, 20, 23, 17),
(1080, 3, 20, 5, 6), (1081, 3, 20, 10, 55), (1082, 3, 20, 16, 44),
(1083, 3, 20, 22, 34), (1084, 3, 20, 4, 23), (1085, 3, 20, 10, 12),
(1086, 3, 20, 16, 1), (1087, 3, 20, 21, 50), (1088, 3, 20, 3, 40),
(1089, 3, 20, 9, 29), (1090, 3, 20, 15, 18), (1091, 3, 20, 21, 7),
(1092, 3, 20, 2, 56), (1093, 3, 20, 8, 46), (1094, 3, 20, 14, 35),
(1095, 3, 20, 20, 24), (1096, 3, 20, 2, 13), (1097, 3, 20, 8, 2),
(1098, 3, 20, 13, 52), (1099, 3, 20, 19, 41), (1100, 3, 21, 1, 30),
(1101, 3, 21, 7, 19), (1102, 3, 21, 13, 8), (1103, 3, 21, 18, 58),
(1104, 3, 21, 0, 47), (1105, 3, 21, 6, 36), (1106, 3, 21, 12, 25),
(1107, 3, 21, 18, 14), (1108, 3, 21, 0, 4), (1109, 3, 21, 5, 53),
(1110, 3, 21, 11, 42), (1111, 3, 21, 17, 31), (1112, 3, 20, 23, 20),
(1113, 3, 21, 5, 10), (1114, 3, 21, 10, 59), (1115, 3, 21, 16, 48),
(1116, 3, 20, 22, 37), (1117, 3, 21, 4, 26), (1118, 3, 21, 10, 16),
(1119, 3, 21, 16, 5), (1120, 3, 20, 21, 54), (1121, 3, 21, 3, 43),
(1122, 3, 21, 9, 32), (1123, 3, 21, 15, 22), (1124, 3, 20, 21, 11),
(1125, 3, 21, 3, 0), (1126, 3, 21, 8, 49), (1127, 3, 21, 14, 38),
(1128, 3, 20, 20, 28), (1129, 3, 21, 2, 17), (1130, 3, 21, 8, 6),
(1131, 3, 21, 8, 6), (1132, 3, 20, 19, 44), (1133, 3, 21, 1, 34),
(1134, 3, 21, 7, 23), (1135, 3, 21, 13, 12), (1136, 3, 20, 19, 1),
(1137, 3, 21, 0, 50), (1138, 3, 21, 6, 40), (1139, 3, 21, 12, 29),
(1140, 3, 20, 18, 18), (1141, 3, 21, 0, 7), (1142, 3, 21, 5, 56),
(1143, 3, 21, 11, 46), (1144, 3, 20, 17, 35), (1145, 3, 20, 23, 24),
(1146, 3, 21, 5, 13), (1147, 3, 21, 11, 2), (1148, 3, 20, 16, 52),
(1149, 3, 20, 22, 41), (1150, 3, 21, 4, 30), (1151, 3, 21, 10, 19),
(1152, 3, 20, 16, 8), (1153, 3, 20, 21, 58), (1154, 3, 21, 3, 47),
(1155, 3, 21, 9, 36), (1156, 3, 20, 15, 25), (1157, 3, 20, 21, 14),
(1158, 3, 21, 3, 4), (1159, 3, 21, 8, 53), (1160, 3, 20, 14, 42),
(1161, 3, 20, 20, 31), (1162, 3, 21, 2, 20), (1163, 3, 21, 8, 10),
(1164, 3, 20, 13, 59), (1165, 3, 20, 19, 48), (1166, 3, 21, 1, 37),
(1167, 3, 21, 7, 26), (1168, 3, 20, 13, 16), (1169, 3, 20, 19, 5),
(1170, 3, 21, 0, 54), (1171, 3, 21, 6, 43), (1172, 3, 20, 12, 32),
(1173, 3, 20, 18, 22), (1174, 3, 21, 0, 11), (1175, 3, 21, 6, 0),
(1176, 3, 20, 11, 49), (1177, 3, 20, 17, 38), (1178, 3, 20, 23, 28),
(1179, 3, 21, 5, 17), (1180, 3, 20, 11, 6), (1181, 3, 20, 16, 55),
(1182, 3, 20, 22, 44), (1183, 3, 21, 4, 34), (1184, 3, 20, 10, 23),
(1185, 3, 20, 16, 12), (1186, 3, 20, 22, 1), (1187, 3, 21, 3, 50),
(1188, 3, 20, 9, 40), (1189, 3, 20, 15, 29), (1190, 3, 20, 21, 18),
(1191, 3, 21, 3, 7), (1192, 3, 20, 8, 56), (1193, 3, 20, 14, 46),
(1194, 3, 20, 20, 35), (1195, 3, 21, 2, 24), (1196, 3, 20, 8, 13),
(1197, 3, 20, 14, 2), (1198, 3, 20, 19, 52), (1199, 3, 21, 1, 41),
(1200, 3, 20, 7, 30), (1201, 3, 20, 13, 19), (1202, 3, 20, 19, 8),
(1203, 3, 21, 0, 58), (1204, 3, 20, 6, 47), (1205, 3, 20, 12, 36),
(1206, 3, 20, 18, 25), (1207, 3, 21, 0, 14), (1208, 3, 20, 6, 4),
(1209, 3, 20, 11, 53), (1210, 3, 20, 17, 42), (1211, 3, 20, 23, 32),
(1212, 3, 20, 5, 20), (1213, 3, 20, 11, 10), (1214, 3, 20, 16, 59),
(1215, 3, 20, 22, 48), (1216, 3, 20, 4, 37), (1217, 3, 20, 10, 26),
(1218, 3, 20, 16, 16), (1219, 3, 20, 22, 5), (1220, 3, 20, 3, 54),
(1221, 3, 20, 9, 43), (1222, 3, 20, 15, 32), (1223, 3, 20, 21, 22),
(1224, 3, 20, 3, 11), (1225, 3, 20, 9, 0), (1226, 3, 20, 14, 49),
(1227, 3, 20, 20, 38), (1228, 3, 20, 2, 28), (1229, 3, 20, 8, 17),
(1230, 3, 20, 14, 6), (1231, 3, 20, 19, 55), (1232, 3, 20, 1, 44),
(1233, 3, 20, 7, 34), (1234, 3, 20, 13, 23), (1235, 3, 20, 19, 12),
(1236, 3, 20, 1, 1), (1237, 3, 20, 6, 50), (1238, 3, 20, 12, 40),
(1239, 3, 20, 18, 29), (1240, 3, 20, 0, 18), (1241, 3, 20, 6, 7),
(1242, 3, 20, 11, 56), (1243, 3, 20, 17, 46), (1244, 3, 19, 23, 35),
(1245, 3, 20, 5, 24), (1246, 3, 20, 11, 13), (1247, 3, 20, 17, 2),
(1248, 3, 19, 22, 52), (1249, 3, 20, 4, 41), (1250, 3, 20, 10, 30),
(1251, 3, 20, 16, 19), (1252, 3, 19, 22, 8), (1253, 3, 20, 3, 58),
(1254, 3, 20, 9, 47), (1255, 3, 20, 15, 36), (1256, 3, 19, 21, 25),
(1257, 3, 20, 3, 14), (1258, 3, 20, 9, 4), (1259, 3, 20, 14, 53),
(1260, 3, 19, 20, 42), (1261, 3, 20, 2, 31), (1262, 3, 20, 8, 20),
(1263, 3, 20, 14, 10), (1264, 3, 19, 19, 59), (1265, 3, 20, 1, 48),
(1266, 3, 20, 7, 37), (1267, 3, 20, 13, 26), (1268, 3, 19, 19, 16),
(1269, 3, 20, 1, 5), (1270, 3, 20, 6, 54), (1271, 3, 20, 12, 43),
(1272, 3, 19, 18, 32), (1273, 3, 20, 0, 22), (1274, 3, 20, 6, 11),
(1275, 3, 20, 12, 0), (1276, 3, 19, 17, 49), (1277, 3, 19, 23, 38),
(1278, 3, 20, 5, 28), (1279, 3, 20, 11, 17), (1280, 3, 19, 17, 6),
(1281, 3, 19, 22, 55), (1282, 3, 20, 4, 44), (1283, 3, 20, 10, 34),
(1284, 3, 19, 16, 23), (1285, 3, 19, 22, 12), (1286, 3, 20, 4, 1),
(1287, 3, 20, 9, 50), (1288, 3, 19, 15, 40), (1289, 3, 19, 21, 29),
(1290, 3, 20, 3, 18), (1291, 3, 20, 9, 7), (1292, 3, 19, 14, 56),
(1293, 3, 19, 20, 46), (1294, 3, 20, 2, 35), (1295, 3, 20, 8, 24),
(1296, 3, 19, 14, 13), (1297, 3, 19, 20, 2), (1298, 3, 20, 1, 52),
(1299, 3, 20, 7, 41), (1300, 3, 20, 13, 30), (1301, 3, 20, 19, 19),
(1302, 3, 21, 1, 8), (1303, 3, 21, 6, 58), (1304, 3, 20, 12, 47),
(1305, 3, 20, 18, 36), (1306, 3, 21, 0, 25), (1307, 3, 21, 6, 14),
(1308, 3, 20, 12, 4), (1309, 3, 20, 17, 53), (1310, 3, 20, 23, 42),
(1311, 3, 21, 5, 31), (1312, 3, 20, 11, 20), (1313, 3, 20, 17, 10),
(1314, 3, 20, 22, 59), (1315, 3, 21, 4, 48), (1316, 3, 20, 10, 37),
(1317, 3, 20, 16, 26), (1318, 3, 20, 22, 16), (1319, 3, 21, 4, 5),
(1320, 3, 20, 9, 54), (1321, 3, 20, 15, 43), (1322, 3, 20, 21, 32),
(1323, 3, 21, 3, 22), (1324, 3, 20, 9, 11), (1325, 3, 20, 15, 0),
(1326, 3, 20, 20, 49), (1327, 3, 21, 2, 38), (1328, 3, 20, 8, 28),
(1329, 3, 20, 14, 17), (1330, 3, 20, 20, 6), (1331, 3, 21, 1, 55),
(1332, 3, 20, 7, 44), (1333, 3, 20, 13, 34), (1334, 3, 20, 19, 23),
(1335, 3, 21, 1, 12), (1336, 3, 20, 7, 1), (1337, 3, 20, 12, 50),
(1338, 3, 20, 18, 40), (1339, 3, 21, 0, 29), (1340, 3, 20, 6, 18),
(1341, 3, 20, 12, 7), (1342, 3, 20, 17, 56), (1343, 3, 20, 23, 46),
(1344, 3, 20, 5, 35), (1345, 3, 20, 11, 24), (1346, 3, 20, 17, 13),
(1347, 3, 20, 23, 2), (1348, 3, 20, 4, 52), (1349, 3, 20, 10, 41),
(1350, 3, 20, 16, 30), (1351, 3, 20, 22, 19), (1352, 3, 20, 4, 8),
(1353, 3, 20, 9, 58), (1354, 3, 20, 15, 47), (1355, 3, 20, 21, 36),
(1356, 3, 20, 3, 25), (1357, 3, 20, 9, 14), (1358, 3, 20, 15, 4),
(1359, 3, 20, 20, 53), (1360, 3, 20, 2, 42), (1361, 3, 20, 8, 31),
(1362, 3, 20, 14, 20), (1363, 3, 20, 20, 10), (1364, 3, 20, 1, 59),
(1365, 3, 20, 7, 48), (1366, 3, 20, 13, 37), (1367, 3, 20, 19, 26),
(1368, 3, 20, 1, 16), (1369, 3, 20, 7, 5), (1370, 3, 20, 12, 54),
(1371, 3, 20, 18, 43), (1372, 3, 20, 0, 32), (1373, 3, 20, 6, 22),
(1374, 3, 20, 12, 11), (1375, 3, 20, 18, 0), (1376, 3, 19, 23, 49),
(1377, 3, 20, 5, 38), (1378, 3, 20, 11, 28), (1379, 3, 20, 17, 17),
(1380, 3, 19, 23, 6), (1381, 3, 20, 4, 55), (1382, 3, 20, 10, 44),
(1383, 3, 20, 16, 34), (1384, 3, 19, 22, 23), (1385, 3, 20, 4, 12),
(1386, 3, 20, 10, 1), (1387, 3, 20, 15, 50), (1388, 3, 19, 21, 40),
(1389, 3, 20, 3, 29), (1390, 3, 20, 9, 18), (1391, 3, 20, 15, 7),
(1392, 3, 19, 20, 56), (1393, 3, 20, 2, 46), (1394, 3, 20, 8, 35),
(1395, 3, 20, 14, 24), (1396, 3, 19, 20, 13), (1397, 3, 20, 2, 2),
(1398, 3, 20, 7, 52), (1399, 3, 20, 13, 41), (1400, 3, 20, 19, 30),
(1401, 3, 21, 1, 19), (1402, 3, 21, 1, 19), (1403, 3, 21, 12, 58),
(1404, 3, 20, 18, 47), (1405, 3, 21, 0, 36), (1406, 3, 21, 6, 25),
(1407, 3, 21, 12, 14), (1408, 3, 20, 18, 4), (1409, 3, 20, 23, 53),
(1410, 3, 21, 5, 42), (1411, 3, 21, 11, 31), (1412, 3, 20, 17, 20),
(1413, 3, 20, 23, 10), (1414, 3, 21, 4, 59), (1415, 3, 21, 10, 48),
(1416, 3, 20, 16, 37), (1417, 3, 20, 22, 26), (1418, 3, 21, 4, 16),
(1419, 3, 21, 10, 5), (1420, 3, 20, 15, 54), (1421, 3, 20, 21, 43),
(1422, 3, 21, 3, 32), (1423, 3, 21, 9, 22), (1424, 3, 20, 15, 11),
(1425, 3, 20, 21, 0), (1426, 3, 21, 2, 49), (1427, 3, 21, 8, 38),
(1428, 3, 20, 14, 28), (1429, 3, 20, 20, 17), (1430, 3, 21, 2, 6),
(1431, 3, 21, 7, 55), (1432, 3, 20, 13, 44), (1433, 3, 20, 19, 34),
(1434, 3, 21, 1, 23), (1435, 3, 21, 7, 12), (1436, 3, 20, 13, 1),
(1437, 3, 20, 18, 50), (1438, 3, 21, 0, 40), (1439, 3, 21, 6, 29),
(1440, 3, 20, 12, 18), (1441, 3, 20, 18, 7), (1442, 3, 20, 23, 56),
(1443, 3, 21, 5, 46), (1444, 3, 20, 11, 35), (1445, 3, 20, 17, 24),
(1446, 3, 20, 23, 13), (1447, 3, 21, 5, 2), (1448, 3, 20, 10, 52),
(1449, 3, 20, 16, 41), (1450, 3, 20, 22, 30), (1451, 3, 21, 4, 19),
(1452, 3, 20, 10, 8), (1453, 3, 20, 15, 58), (1454, 3, 20, 21, 47),
(1455, 3, 21, 3, 36), (1456, 3, 20, 9, 25), (1457, 3, 20, 15, 14),
(1458, 3, 20, 21, 4), (1459, 3, 21, 2, 53), (1460, 3, 20, 8, 42),
(1461, 3, 20, 14, 31), (1462, 3, 20, 20, 20), (1463, 3, 21, 2, 10),
(1464, 3, 20, 7, 59), (1465, 3, 20, 13, 48), (1466, 3, 20, 19, 37),
(1467, 3, 21, 1, 26), (1468, 3, 20, 7, 16), (1469, 3, 20, 13, 5),
(1470, 3, 20, 18, 54), (1471, 3, 21, 0, 43), (1472, 3, 20, 6, 32),
(1473, 3, 20, 12, 22), (1474, 3, 20, 18, 11), (1475, 3, 21, 0, 0),
(1476, 3, 20, 5, 49), (1477, 3, 20, 11, 38), (1478, 3, 20, 17, 28),
(1479, 3, 20, 23, 17), (1480, 3, 20, 5, 6), (1481, 3, 20, 10, 55),
(1482, 3, 20, 16, 44), (1483, 3, 20, 22, 34), (1484, 3, 20, 4, 23),
(1485, 3, 20, 10, 12), (1486, 3, 20, 16, 1), (1487, 3, 20, 21, 50),
(1488, 3, 20, 3, 40), (1489, 3, 20, 9, 29), (1490, 3, 20, 15, 18),
(1491, 3, 20, 21, 7), (1492, 3, 20, 2, 56), (1493, 3, 20, 8, 46),
(1494, 3, 20, 14, 35), (1495, 3, 20, 20, 24), (1496, 3, 20, 2, 13),
(1497, 3, 20, 8, 2), (1498, 3, 20, 13, 52), (1499, 3, 20, 19, 41),
(1500, 3, 21, 1, 30), (1501, 3, 21, 7, 19), (1502, 3, 21, 13, 8),
(1503, 3, 21, 18, 58), (1504, 3, 21, 0, 47), (1505, 3, 21, 6, 36),
(1506, 3, 21, 12, 25), (1507, 3, 21, 18, 14), (1508, 3, 21, 0, 4),
(1509, 3, 21, 5, 53), (1510, 3, 21, 11, 42), (1511, 3, 21, 17, 31),
(1512, 3, 20, 23, 20), (1513, 3, 21, 5, 10), (1514, 3, 21, 10, 59),
(1515, 3, 21, 16, 48), (1516, 3, 20, 22, 37), (1517, 3, 21, 4, 26),
(1518, 3, 21, 10, 16), (1519, 3, 21, 16, 5), (1520, 3, 20, 21, 54),
(1521, 3, 21, 3, 43), (1522, 3, 21, 9, 32), (1523, 3, 21, 15, 22),
(1524, 3, 20, 21, 11), (1525, 3, 21, 3, 0), (1526, 3, 21, 8, 49),
(1527, 3, 21, 14, 38), (1528, 3, 20, 20, 28), (1529, 3, 21, 2, 17),
(1530, 3, 21, 8, 6), (1531, 3, 21, 13, 55), (1532, 3, 20, 19, 44),
(1533, 3, 21, 1, 34), (1534, 3, 21, 7, 23), (1535, 3, 21, 13, 12),
(1536, 3, 20, 19, 1), (1537, 3, 21, 0, 50), (1538, 3, 21, 6, 40),
(1539, 3, 21, 12, 29), (1540, 3, 20, 18, 18), (1541, 3, 21, 5, 56),
(1542, 3, 21, 5, 56), (1543, 3, 21, 11, 46), (1544, 3, 20, 17, 35),
(1545, 3, 20, 23, 24), (1546, 3, 21, 5, 13), (1547, 3, 21, 11, 2),
(1548, 3, 20, 16, 52), (1549, 3, 20, 22, 41), (1550, 3, 21, 4, 30),
(1551, 3, 21, 10, 19), (1552, 3, 20, 16, 8), (1553, 3, 20, 21, 58),
(1554, 3, 21, 3, 21), (1555, 3, 21, 9, 36), (1556, 3, 20, 15, 25),
(1557, 3, 20, 21, 14), (1558, 3, 21, 3, 4), (1559, 3, 21, 8, 53),
(1560, 3, 20, 14, 42), (1561, 3, 20, 20, 31), (1562, 3, 21, 2, 20),
(1563, 3, 21, 8, 10), (1564, 3, 20, 13, 59), (1565, 3, 20, 19, 48),
(1566, 3, 21, 1, 37), (1567, 3, 21, 7, 26), (1568, 3, 20, 13, 16),
(1569, 3, 20, 19, 5), (1570, 3, 21, 0, 54), (1571, 3, 21, 6, 43),
(1572, 3, 20, 12, 32), (1573, 3, 20, 18, 22), (1574, 3, 21, 0, 11),
(1575, 3, 21, 6, 0), (1576, 3, 20, 11, 49), (1577, 3, 20, 17, 38),
(1578, 3, 20, 23, 28), (1579, 3, 21, 5, 17), (1580, 3, 20, 11, 6),
(1581, 3, 20, 16, 55), (1582, 3, 20, 22, 44),
)
"""
tuple: List of vernal equinox values before 1583-03-20.
:meta hide-value:
"""
# These are custom years, month, and days.
# *** TODO *** All need to be converted to work properly.
INJECT = (
((178, 0, 5), (2022, 3, 1, 17, 59)),
((178, 19, 1), (2022, 3, 2, 18)),
((178, 19, 2), (2022, 3, 3, 18, 1)),
((181, 1, 2), (2024, 3, 21, 18, 17)),
((181, 1, 5), (2024, 3, 24, 18, 20)),
((181, 2, 13), (2024, 4, 20, 18, 42)),
# Sunset Tehran -> 15:02:
((181, 3, 18, 20), (2024, 5, 15, 15, 3, 36.0864)),
((181, 3, 19, 20), (2024, 5, 16, 15, 4, 23.4336)),
((181, 4, 1, 17), (2024, 5, 17, 12, 4, 2.6112)),
((181, 4, 1, 20), (2024, 5, 17, 15, 5, 10.3488)),
((181, 18, 1), (2025, 2, 6, 17, 31, 3.7056)),
((181, 18, 19), (2025, 2, 24, 17, 56, 46.7232)),
((181, 0, 1), (2025, 2, 25, 17, 57, 42.4512)),
# (181, 0, 4) -> (2025, 3, 0.750321) is an error in
# GregorianCalendar._check_valid_gregorian_month_day
((181, 0, 4), (2025, 2, 28, 17, 53, 36.0384)),
((181, 19, 1), (2025, 3, 1, 18, 1, 22.1664)),
((181, 19, 19), (2025, 3, 19, 18, 16, 7.7664)),
((182, 18, 19), (2026, 2, 24, 17, 56, 33.2448)),
((182, 0, 1), (2026, 2, 25, 17, 57, 29.0592)),
((182, 0, 5), (2026, 3, 1, 18, 1, 8.9472)),
)
"""
tuple: List of dates that can be injected in the vernal equinox table
above. Currently it's not used.
:meta hide-value:
"""
# Badí' month sequence where 1 - 19 are the actual Badí' month and
# 0 is Ayyám-i-Há
MONTHS = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 0, 19)
def __init__(self):
super().__init__()
# https://qr.ae/psZONa
# https://www.someweekendreading.blog/leap-year-revised/
# 365 + 1/4 − 1/128 = 365.2421875 or 365 + 31/128
# 365.2421897
self.gc = GregorianCalendar()
self.BADI_COORDS = (35.69435, 51.288701, 3.5)
self.GMT_COORDS = (51.477928, -0.001545, 0.0)
[docs]
def analyze_date_error(self, options):
"""
Finds the the Julian Period day (yearly epoch) that corresponds to
the day of the Vernal Equinox. We display the UT Gregorian date and
time and compare the UT JD values.
-a, optional -C, -G, and -X
Also if -S and -E are used they must be used together.
"""
return self._date_range(options)
[docs]
def badi_round_trip(self, options):
"""
Test the full round trip between jd_from_badi_date() and
badi_date_from_jd().
-b and -S and -E
"""
data = []
start = options.start
end = options.end
lat = options.latitude
lon = options.longitude
zone = options.zone
for year in range(start, end):
is_leap = self._is_leap_year(year)
for month in self.MONTHS:
dm = 19 if month != 0 else 4 + is_leap
for day in range(1, dm + 1):
date = (year, month, day)
jd = self.jd_from_badi_date(date, lat, lon, zone)
b_date = self.badi_date_from_jd(jd, lat, lon, zone,
short=True)
valid = date == b_date
data.append((date, jd, b_date, valid))
return data
[docs]
def check_long_date_from_short_date(self, data):
"""
Check that long_date_from_short_date() works correctly.
-c or --ck-dates
"""
items = []
for item in data:
b_date, date = item
bd = self.long_date_from_short_date(date)
if bd != (b_date + (0, 0, 0)):
items.append((item, bd))
return items
[docs]
def find_longest_and_shortest_days(self, options):
"""
Find the longest and shortest days from year -1842 to 1161.
-d and -S and -E
The shortest and longest days are:
First occurrence
----------------
((-1842, 1, 1), (23, 59, 40.9056), (-344, 19, 1), (24, 0, 2.16))
2024-10-30 15:53:02.883745 -> 2024-10-30 16:45:24.637078
52 minutes, 21 seconds, 753333 microseconds
Last occurrence
---------------
((-1744, 19, 1), (23, 59, 40.9056), (-261, 11, 9), (24, 0, 2.16))
2024-10-30 21:22:48.037574 -> 2024-10-30 22:14:48.889369
52 minutes, 0 seconds, 851795 microseconds
"""
start = options.start
end = options.end
short_day = ()
long_day = ()
short_hms = ()
long_hms = ()
lat, lon, zone = self.BADI_COORDS
for year in range(start, end):
for month in self.MONTHS:
dm = 19 if month != 0 else 4 + self._is_leap_year(year)
for day in range(1, dm + 1):
date = (year, month, day)
jd = self.jd_from_badi_date(date)
jd = self._meeus_from_exact(jd)
ssjd = self._sun_setting(jd, lat, lon)
local_ss = self._local_zone_correction(ssjd, zone,
mod_jd=True)
ssjd = self._exact_from_meeus(local_ss)
hms = self._day_length(ssjd, lat, lon)
if short_day == () or long_day == ():
short_day = long_day = date
short_hms = long_hms = hms
elif hms <= short_hms:
short_hms = hms
short_day = date
elif hms >= long_hms:
long_hms = hms
long_day = date
if not year % 100:
print(year, file=sys.stderr)
return short_day, short_hms, long_day, long_hms
[docs]
def find_weekdays(self, options):
"""
Find weekdays between provided years.
-e and -S and -E -C to not use coefficients.
"""
data = []
start = options.start
end = options.end
lat = options.latitude
lon = options.longitude
zone = options.zone
assert -1843 < start < 1162, (
f"Start '{start}' must be from -1842 to 1161.")
assert -1842 < end < 1163, (
f"End '{end}' must be from -1841 to 1162")
for year in range(start, end):
is_leap = self._is_leap_year(year)
days = 365 + is_leap
doy = 0
for month in self.MONTHS:
dim = 19 if month != 0 else 4 + is_leap
for day in range(1, dim + 1):
date = (year, month, day)
g_date = self.gregorian_date_from_badi_date(date, lat,
lon, zone)
doy += 1
# weektuple = week, weekday, weekday name, month name
weektuple = self._day_of_week(*date, doy, options)
data.append((date, g_date, *weektuple,
is_leap, days, doy))
return data
[docs]
def find_gregorian_dates(self, options):
"""
Convert Badí' to Gregorian dates.
-g or --g-dates -XL and -S and -E
Converts Badí' to Gregorian dates for the given range.
"""
data = []
for item in self._date_range(options):
b_date, bjd, g_date, gjd, diff, offby = item
g_date = self._gregorian_date_from_badi_date(
b_date, options, *self.BADI_COORDS)
gjd = self.gc.jd_from_gregorian_date(
g_date, exact=options.exact, alt=options.alt_leap)
diff = round(bjd - gjd, 6)
offby = math.floor(bjd) - math.floor(gjd)
data.append((b_date, bjd, g_date, gjd, diff, offby))
return data
[docs]
def day_inremental_change(self, options):
"""
Check that the incremental times of a day are correct.
-i and -S (Badi years) -M (Start month) -D (Start day) -H (Start hour)
"""
data = []
year = options.start
month = options.month
day = options.start_day
hour = options.hour
#lat, lon, zone = self.GMT_COORDS
lat, lon, zone = self.BADI_COORDS
options.coff = False # This should never be set to True in this method
# The badi_date_from_jd() method below will try to use
# BahaiCalendar.jd_from_badi_date() so we need to patch it.
with patch.object(self, 'jd_from_badi_date', self._jd_from_badi_date):
for current_hour in range(hour, 24):
for minute in range(0, 60):
date = (year, month, day, current_hour, minute)
jd = self._jd_from_badi_date(date, lat, lon, zone, options)
g_date = self.gc.gregorian_date_from_jd(jd, hms=True,
exact=True)
b_date = self.badi_date_from_jd(jd, lat, lon, zone,
short=True)
data.append((date, g_date, jd, b_date))
return data
[docs]
def create_date_lists(self, options):
"""
Generate a list of Badí' dates both long and short versions.
-l or --list
Also -S and -E must both used together.
"""
data = []
for k in reversed(range(options.start, options.end)):
for v in reversed(range(1, 20)):
for y in reversed(range(1, 20)):
for m in reversed(range(0, 20)):
if m == 0:
self._calc_kvymd(5, k, v, y, m, data)
else:
self._calc_kvymd(20, k, v, y, m, data)
return data
[docs]
def find_coefficents_precursor(self, options):
"""
Dump data for determining the precursors to the coefficients. This
determines which coefficient group should be used for the years
provided. The years provided are on the Badí' Calendar - or + the
epoch.
-p or --precursor
-S and -E must be Badi dates
Arguments to the process_segment() function.
--------------------------------------------
1. First run `badi_jd_tests.py -aX > filename.txt`
This file will be long so use `less filename.txt` to look at it.
The last column will usually be -1, 0, or 1. The 0 values are
already correct, the other two values means there is a difference
between the Gregorian and Badí' Julian Period days. These are the
ones than need the coefficients to fix them.
2. The first argument is the current Badí' year being processed
subtracted from the end year argument.
3. The second argument is the 1st coefficient corresponding to the
(1, 34, 67, 100) numbers in the output from this method.
4. The third argument is the 2nd coefficient which fixes the 1 values
that were not included in the 1st coefficient and the 2 and 3 values.
If an error JD falls on a 0 (zero) value then you need to change
the start and end years so that no error JDs fall on a 0 value. The
average number of years fixed in a group is 99, but this is not a
hard and fast rule. Obvious break points are where a sequence
changes. For example where there are two consecutive already good
values where the values you need to fix had one.
Note: Zero values never get processes.
"""
data = []
for y in range(options.start, options.end):
year = options.end - y
if year in (1, 34, 67, 100):
a = year
else:
a = ''
data.append((y + self.TRAN_COFF, y, year % 4, a))
return data
[docs]
def find_coefficents(self, options):
"""
Dump data for determining coefficients.
-q or --coeff and -S and -E (Badi years)
If -X is used the more exact mode is used. This should be the
normal usage.
"""
start = options.start
end = options.end
options.start = options.start + self.TRAN_COFF
options.end = options.end + self.TRAN_COFF
data = self._date_range(options)
options.start = start
options.end = end
cp = {by: (n, a)
for gy, by, n, a in self.find_coefficents_precursor(options)}
items = []
for item in data:
b_year, month, day = item[0][:3]
h, m, s, ms = self._get_hms(item[0], short_in=True)
bjd = item[1]
msg = (f"{b_year:> 5}-{month:>02}-{day:>02}T{h:>02}:{m:>02}:"
f"{s:>02} {fmt_float(bjd, 7, 6)} ")
g_year, month, day = item[2][:3]
h, m, s, ms = self._get_hms(item[2], short_in=True)
gjd = item[3]
msg += (f"{g_year:>04}-{month:>02}-{day:>02}T{h:>02}:{m:>02}:"
f"{fmt_float(s, 2, 4, True)} {fmt_float(gjd, 7, 6)} ")
diff = item[4]
offby = item[5]
msg += f"{fmt_float(diff, 2, 12)} {offby:< 1} "
j, k = cp.get(b_year)
msg += f"{j} {k:<3}"
items.append(msg)
return items
[docs]
def twenty_four_hours(self, options):
"""
Dump the hours, minutes, and seconds of the day.
-t and -S and -E
"""
data = []
start = options.start
end = options.end
assert (self.MINYEAR-1) < start < (self.MAXYEAR+1), (
f"Start '{start}' must be from {self.MINYEAR} to {self.MAXYEAR}.")
assert self.MINYEAR < end < (self.MAXYEAR+2), (
f"End '{end}' must be from {self.MINYEAR} to {self.MAXYEAR}")
lat, lon, zone = self.BADI_COORDS
for year in range(start, end):
is_leap = self._is_leap_year(year)
for month in self.MONTHS:
dm = 19 if month != 0 else 4 + is_leap
for day in range(1, dm + 1):
date = (year, month, day)
jd = self.jd_from_badi_date(date, lat, lon, zone)
g_date = self.gc.gregorian_date_from_jd(jd, exact=True)
jd0 = math.floor(jd)
jd1 = jd0 + 1
mjd0 = self._meeus_from_exact(jd0)
mjd1 = self._meeus_from_exact(jd1)
ss0 = self._sun_setting(mjd0, lat, lon)
local_ss0 = self._local_zone_correction(ss0, zone,
mod_jd=True)
ss1 = self._sun_setting(mjd1, lat, lon)
local_ss1 = self._local_zone_correction(ss1, zone,
mod_jd=True)
b_date = self.badi_date_from_jd(jd, short=True,
fraction=True)
ss_diff = local_ss1 - local_ss0
hms = self._hms_from_decimal_day(ss_diff)
data.append((b_date, g_date, round(local_ss0 % 1, 6),
round(local_ss1 % 1, 6), round(ss_diff, 6),
hms))
return data
[docs]
def find_leap_years(self, options):
"""
Dump leap years.
-y with -S and -E
"""
data = []
start = options.start
end = options.end
assert (self.MINYEAR-1) < start < (self.MAXYEAR+1), (
f"Start '{start}' must be from {self.MINYEAR} to {self.MAXYEAR}.")
assert self.MINYEAR < end < (self.MAXYEAR+2), (
f"End '{end}' must be from {self.MINYEAR} to {self.MAXYEAR}")
last_leap_year = 0
last_5_spread = 0
for year in range(start, end):
is_leap = self._is_leap_year(year)
if is_leap:
if last_leap_year == 0:
years = 4
else:
years = year - last_leap_year
if last_5_spread == 0:
spread = ''
elif years == 5:
spread = year - last_5_spread
else:
spread = ''
data.append((year, is_leap, years, spread))
last_leap_year = year
if years == 5:
last_5_spread = year
return data
#
# Support methods
#
[docs]
def _jd_from_badi_date(self, b_date, lat=None, lon=None, zone=None,
options=None):
self._check_valid_badi_date(b_date, short_in=True)
year, month, day = b_date[:3]
hh, mm, ss, us = self._get_hms(b_date, short_in=True)
if month == 0: # Ayyam-i-Ha
days = 18 * 19
elif month < 19: # month 1 - 18
days = (month - 1) * 19
else: # month == 19
days = 18 * 19 + 4 + self._is_leap_year(year)
td = self._days_in_years(year - 1)
jd = td + math.floor(self._BADI_EPOCH + 1) + days + day
if any([True if l is None else False for l in (lat, lon, zone)]):
lat, lon, zone = self.BADI_COORDS
jd0 = self._meeus_from_exact(jd)
if not options.coff:
if options.bahji:
jd0 += self._get_bahji_day_coeff(year)
else:
jd0 += self._get_tehran_day_coeff(year)
jd_ss = self._sun_setting(jd0, lat, lon)
a_ss = self._exact_from_meeus(jd_ss)
day_frac = self._decimal_day_from_hms(hh, mm, ss, us)
jd2 = round(a_ss + day_frac, self._ROUNDING_PLACES)
#print(f"{str(b_date):<15} {day:<9} {jd:<14} {local_ss:<20}",
# file=sys.stderr)
#print(f"{year:5} {jd:18} {fmt_float(jd1, 7, 10)}")
return jd2
[docs]
def _get_tehran_day_coeff(self, year):
P1 = ((-1783, -1747), (-1651, -1615), (-1499, -1483), (-1383, -1347),
(-1251, -1215), (-1099, -1083), (-983, -947), (-851, -815),
(-699, -683), (-583, -547), (-451, -415), (-299, -283),
(-179, -143), (-47, -11), (101, 117), (217, 249), (345, 381),
(501, 513), (609, 645), (741, 777), (901, 909), (1005, 1041),
(1137, 1162), )
P1100 = ((-1699, -1683), (-1299, -1283), (-899, -883), (-499, -483),
(-99, -79), (301, 313), (701, 709), (1101, 1105), )
P1110 = ((-1799, -1783), (-1683, -1651), (-1399, -1383),
(-1283, -1251), (-999, -983), (-883, -851), (-599, -583),
(-483, -451), (-199, -179), (-79, -47), (201, 217),
(313, 345), (601, 609), (709, 741), (1001, 1005),
(1105, 1137), )
P2 = ((-1519, -1499), (-1119, -1099), (-719, -699), (-319, -299),
(85, 101), (477, 501), (873, 901), )
P2111 = ((-1747, -1715), (-1615, -1583), (-1483, -1451),
(-1347, -1315), (-1215, -1183), (-1083, -1051), (-947, -915),
(-815, -783), (-683, -651), (-547, -515), (-415, -383),
(-283, -243), (-143, -111), (-11, 21), (117, 149), (249, 281),
(381, 413), (513, 545), (645, 677), (777, 809), (909, 941),
(1041, 1073), )
P2211 = ((-1843, -1815), (-1715, -1699), (-1583, -1551),
(-1451, -1415), (-1315, -1299), (-1183, -1151),
(-1051, -1015), (-915, -899), (-783, -751), (-651, -619),
(-515, -499), (-383, -351), (-243, -211), (-111, -99),
(21, 53), (149, 185), (281, 301), (413, 445), (545, 577),
(677, 701), (809, 841), (941, 973), (1073, 1101), )
P2221 = ((-1815, -1799), (-1551, -1519), (-1415, -1399),
(-1151, -1119), (-1015, -999), (-751, -719), (-619, -599),
(-351, -319), (-211, -199), (53, 85), (185, 201), (445, 477),
(577, 601), (841, 873), (973, 1001), )
return (self._process_segments(year, P1, -1, (0, 1, 2, 3))
or self._process_segments(year, P1100, -1, (0, 3))
or self._process_segments(year, P1110, -1, (0, 2, 3))
or self._process_segments(year, P2, -2, (0, 1, 2, 3))
or self._process_segments(year, P2111, -2, (0,), -1, (1, 2, 3))
or self._process_segments(year, P2211, -2, (0, 3), -1, (1, 2))
or self._process_segments(year, P2221, -2, (0, 2, 3), -1, (1,))
or 0)
[docs]
def _get_bahji_day_coeff(self, year):
P1 = ((-1791, -1755), (-1655, -1619), (-1499, -1487), (-1391, -1355),
(-1259, -1219), (-1099, -1087), (-991, -955), (-859, -823),
(-699, -687), (-591, -555), (-459, -423), (-299, -287),
(-183, -147), (-55, -19), (101, 113), (209, 245), (341, 377),
(501, 509), (601, 604), (605, 641), (737, 773), (901, 905),
(1001, 1037), (1133, 1162),)
P1100 = ((-1699, -1691), (-1299, -1291), (-899, -891), (-499, -491),
(-99, -83), (301, 309), (701, 705), )
P1110 = ((-1799, -1791), (-1691, -1655), (-1399, -1391),
(-1291, -1259), (-999, -991), (-891, -859), (-599, -591),
(-491, -459), (-199, -183), (-83, -55), (201, 209),
(309, 341), (705, 737), (1101, 1133),)
P2 = ((-1523, -1499), (-1123, -1099), (-723, -699), (-323, -299),
(77, 101), (473, 501), (869, 901), )
P2111 = ((-1755, -1723), (-1619, -1587), (-1487, -1455),
(-1355, -1323), (-1219, -1187), (-1087, -1055), (-955, -923),
(-823, -787), (-687, -655), (-555, -523), (-423, -387),
(-287, -247), (-147, -119), (-19, 13), (113, 145), (245, 277),
(377, 409), (509, 541), (641, 669), (773, 801), (905, 933),
(1037, 1069), )
P2211 = ((-1843, -1823), (-1723, -1699), (-1587, -1555),
(-1455, -1423), (-1323, -1299), (-1187, -1155),
(-1055, -1023), (-923, -899), (-787, -755), (-655, -623),
(-523, -499), (-387, -355), (-247, -215), (-119, -99),
(13, 45), (145, 177), (277, 301), (277, 301), (409, 441),
(541, 573), (669, 701), (801, 833), (933, 969), (1069, 1101),)
P2221 = ((-1823, -1799), (-1555, -1523), (-1423, -1399),
(-1155, -1123), (-1023, -999), (-755, -723), (-623, -599),
(-355, -323), (-215, -199), (45, 77), (177, 201), (441, 473),
(573, 601), (833, 869), (969, 1001), )
return (self._process_segments(year, P1, -1, (0, 1, 2, 3))
or self._process_segments(year, P1100, -1, (0, 3))
or self._process_segments(year, P1110, -1, (0, 2, 3))
or self._process_segments(year, P2, -2, (0, 1, 2, 3))
or self._process_segments(year, P2111, -2, (0,), -1, (1, 2, 3))
or self._process_segments(year, P2211, -2, (0, 3), -1, (1, 2))
or self._process_segments(year, P2221, -2, (0, 2, 3), -1, (1,))
or 0)
[docs]
def _process_segments(self, year, pn, a=0, onoff0=(), b=0, onoff1=()):
"""
Full range is -1842 to 1161
General ranges are determined with:
./contrib/misc/badi_jd_tests.py -p -S start_year -E end_year
Where -S is the 1st year and -E is the nth year + 1 that needs to
be process.
Use the following command to test the results of each segment,
this calls the command above.
./contrib/misc/badi_jd_tests.py -qXS start_year -E end_year
"""
coeff = 0
for start, end in pn:
if year in range(start, end):
# start to end (range -S start -E end)
coeff0 = self._process_segment(end - year, a=a, onoff0=onoff0)
coeff1 = self._process_segment(end - year, b=b, onoff1=onoff1)
coeff = coeff0 if coeff0 != 0 else coeff1
return coeff
[docs]
def _process_segment(self, y, a=0, onoff0=(), b=0, onoff1=()):
func = lambda y, onoff: 0 < y < 100 and y % 4 in onoff
coeff = 0
if a and func(y, onoff0): # Whatever is passed in onoff0.
coeff = a
elif b and func(y, onoff1): # Whatever is passed in onoff1.
coeff = b
return coeff
[docs]
def _date_range(self, options):
"""
"""
data = []
if options.bahji:
lat, lon, zone = self.BAHJI_COORDS
location = 'Bahjí'
else:
lat, lon, zone = self.BADI_COORDS
location = 'Tehran'
coords = (lat, lon, zone)
print(f"{location}: {coords}")
ve_jd_0001_1582 = self._pre_process_vernal_equinoxs()
#inject = [(b_date[0], (b_date, g_date))
# for b_date, g_date in self.INJECT]
for g_year in range(options.start, options.end):
if g_year < 1583:
if g_year in ve_jd_0001_1582:
ve_jd_ut = ve_jd_0001_1582[g_year]
else:
continue
else:
g_date = (g_year, 3, 1)
# We must use the Historical correct (Meeus) algorithm not
# the Astronomically correct algorithm when finding the
# equinox and sunset. So don't use exact=options.exact here.
sjd = self.gc.jd_from_gregorian_date(g_date)
ve_jd_ut = self._find_moment_of_equinoxes_or_solstices(sjd)
jd_ss_ut = self._sun_setting(ve_jd_ut, lat, lon)
# It is allowed to have a Vernal Equinox to be up to one minute
# before sunset and still use that sunset as the beginning of
# the year. If a day == 1 then 1 minute is 0.0006944444444444444
if ve_jd_ut < (jd_ss_ut - 0.0006944444444444444):
jd_ss_ut = self._sun_setting(ve_jd_ut - 1, lat, lon)
# We now need the local zone correction and the Astromomically
# correct JD Period day.
jd_ut = self._exact_from_meeus(jd_ss_ut)
# Make the Badi' date for the beginning of the year.
b_date = (g_year - self.TRAN_COFF, 1, 1)
self._calculate_b_date(b_date, coords, jd_ut, data, options)
#for b_date, g_ss_date in self._find_dates(b_date[0], inject):
# jd_ss = self.gc.jd_from_gregorian_date(g_ss_date)
# self._calculate_b_date(b_date, coords, jd_ss, data, options)
return data
[docs]
def _calculate_b_date(self, b_date, coords, jd_ut, data, options):
# Sunset before VE
local_jd = self._local_zone_correction(jd_ut, coords[2], mod_jd=True)
g_date = self.gc.gregorian_date_from_jd(local_jd, hms=True, exact=True)
jd = self.gc.jd_from_gregorian_date(g_date, exact=True)
bjd = self._jd_from_badi_date(b_date, *coords, options)
# Be sure the comparison for both are in UT time.
diff = round(bjd - jd_ut, 12)
offby = 0 if abs(diff) < 0 else int(diff)
data.append((b_date, bjd, g_date, jd_ut, diff, offby))
[docs]
def _pre_process_vernal_equinoxs(self):
data = {}
last_year = -1
for date in self.VE_0001_1582:
year = date[0]
assert year == (last_year + 1), (
f"last_year: {last_year}, current year {year}")
jd = self.gc.jd_from_gregorian_date(date)
data[year] = jd
last_year = year
return data
[docs]
def _find_dates(self, year, inject):
items = []
for y, item in inject:
if y == year:
items.append(item)
return items
[docs]
def _gregorian_date_from_badi_date(self, b_date: tuple, options,
lat: float=0, lon: float=0,
zone: float=0) -> tuple:
"""
Get the Gregorian date from the Badí' date.
"""
jd = self._jd_from_badi_date(b_date, lat, lon, zone, options)
return self.gc.gregorian_date_from_jd(jd, hms=True,
exact=options.exact)
[docs]
def _calc_kvymd(self, days, k, v, y, m, data):
year = (k - 1) * 361 + (v - 1) * 19 + y
for d in reversed(range(1, days)):
data.append(((k, v, y, m, d), (year, m, d)))
[docs]
def _day_of_week(self, year, month, day, doy, options):
# The weekday starts at 0 and ends at 6. We need to add one below
# to make it ISO compatible.
weekday = _td_utils._day_of_week(year, month, day)
wd_name = _td_utils.DAYNAMES[weekday]
m_name = _td_utils.MONTHNAMES[month]
weekday += 1
week = (doy - (weekday - 1)) // 7 + 1
if not options.coff:
week = self._week_of_year(year, month, day, weekday, week)
return week, weekday, wd_name, m_name
_OFFSETS = {( 1, 7, False): 1, (1, 7, True): 1, # 01 & 02
( 1, 6, False): 2, (1, 6, True): 2, # 03 & 04
( 1, 5, False): 3, (1, 5, True): 3, # 05 & 06
( 2, 6, False): 2, (2, 6, True): 2, # 07 & 08
( 2, 5, False): 3, (2, 5, True): 3, # 09 & 10
( 3, 5, False): 3, (3, 5, True): 3, # 11 & 12
(17, 2, True): -3, (17, 3, False): -3, # 13 & 14
(18, 2, True): -3, (18, 3, False): -3, # 15 & 16
(18, 2, False): -2, (18, 1, True): -2, # 17 & 18
(19, 2, True): -3, (19, 3, False): -3, # 19 & 20
(19, 2, False): -2, (19, 1, True): -2, # 21 & 22
(19, 1, False): -1, (19, 7, True): -1} # 23 & 24
"""
dict: Offsets used when determining the week of year.
:meta hide-value:
"""
[docs]
def _week_of_year(self, year, month, day, weekday, week):
def _offset(year, day):
leap = self._is_leap_year(year)
# Weekday on 1st of year.
fwd = ddate(year, 1, 1).weekday() + 1
return self._OFFSETS.get((day, fwd, leap), 0)
# def _last_year_day(doy, day):
# wd = _td_utils._day_of_week(year-1, 19, day)
# return (doy - (wd - 1)) // 7 + 1
if month == 19:
if _offset(year, day) in (-1, -2, -3): # Make all the 1st week
week = 1
else: # Catch any weeks in a year where all weeks are off by one.
week += 1 if _offset(year-1, 18) in (-2, -3) else 0
elif month == 1:
if _offset(year, day) in (1, 2, 3): # Fix 52 and 53 weeks
# OK, this is confusing, what we need to do is look back 2
# years to see if the previous year was an off-by-one year,
# then set the current week correctly for this year.
week = 52 + (1 if _offset(year-2, 18) in (-2, -3) else 0)
elif week == 0: # If week is 0 make it 1
week = 1
else: # Catch any weeks in a year where all weeks are off by one.
week += 1 if _offset(year-1, 18) in (-2, -3) else 0
else: # Catch any weeks in a year where all weeks are off by one.
week += 1 if _offset(year-1, 18) in (-2, -3) else 0
return week
[docs]
def fmt_float(value, left=4, right=4, leading_zero=False):
"""
Format one float so that it is visually centered on the decimal point.
Parameters
----------
value : float | int | str
The number to format.
left : int
Width to reserve on the left of the decimal (including any minus sign).
right : int
Number of digits to show after the decimal.
"""
value = round(value, right)
s = f"{value:.{right}f}"
left_part, right_part = s.split(".")
# If leading_zero=True, pad *numerical digits* but not the sign
if leading_zero and left_part.startswith("-"):
sign = "-"
digits = left_part[1:]
padded = sign + digits.zfill(left - 1)
elif leading_zero:
padded = left_part.zfill(left)
else:
padded = left_part.rjust(left)
return f"{padded}.{right_part.ljust(right)}"
if __name__ == "__main__":
import time
import argparse
parser = argparse.ArgumentParser(
description=("Test Badí' date ranges."))
parser.add_argument(
'-a', '--analyze', action='store_true', default=False, dest='analyze',
help="Analyze Badí' date errors when converting to jd.")
parser.add_argument(
'-b', '--round-trip', action='store_true', default=False,
dest='badi_round_trip',
help=("Test the full round trip between jd_from_badi_date() and "
"badi_date_from_jd()."))
parser.add_argument(
'-c', '--ck-dates', action='store_true', default=False, dest='ck_dates',
help="Check that long_date_from_short_date() works correctly.")
parser.add_argument(
'-d', '--day', action='store_true', default=False, dest='day',
help="Find the longest and shortest day.")
parser.add_argument(
'-e', '--weekdays', action='store_true', default=False,
dest='weekdays', help="Find weekdays between provided years.")
parser.add_argument(
'-g', '--g-dates', action='store_true', default=False, dest='g_dates',
help="Convert Badí' to Gregorian dates.")
parser.add_argument(
'-i', '--increment', action='store_true', default=False,
dest='increment', help="Convert Badí' to Gregorian dates.")
parser.add_argument(
'-l', '--list', action='store_true', default=False, dest='list',
help="Generate a list of Badí' dates both long and short versions.")
parser.add_argument(
'-p', '--precursor', action='store_true', default=False,
dest='precursor',
help="Dump data for determining the precursors to the coefficients.")
parser.add_argument(
'-q', '--coeff', action='store_true', default=False, dest='coeff',
help="Dump data for determining coefficients.")
parser.add_argument(
'-t', '--twenty-four', action='store_true', default=False,
dest='twenty_four', help="Find day length.")
parser.add_argument(
'-y', '--leap-years', action='store_true', default=False,
dest='leap_years', help="Dump leap years.")
parser.add_argument(
'-A', '--latitude', type=float, default=None, dest='latitude',
help="Latitude")
parser.add_argument(
'-O', '--logitude', type=float, default=None, dest='longitude',
help="Longitude")
parser.add_argument(
'-Z', '--zone', type=float, default=None, dest='zone',
help="Time zone.")
parser.add_argument(
'-B', '--bahji', action='store_true', default=False, dest='bahji',
help="Use Bahji coordinates for orientation point.")
parser.add_argument(
'-C', '--coff', action='store_true', default=False, dest='coff',
help="Turn off all coefficients during an analysis.")
parser.add_argument(
'-D', '--start-day', type=int, default=1, dest='start_day',
help="Day to analyze.")
parser.add_argument(
'-E', '--end', type=int, default=None, dest='end',
help="End Badí' year of sequence.")
parser.add_argument(
'-G', '--graph', action='store_true', default=False, dest='graph',
help=("Turn off all coefficients and dump output appropriate for "
"graphing."))
parser.add_argument(
'-H', '--hour', type=int, default=0, dest='hour',
help="Start hour.")
parser.add_argument(
'-J', '--jd', action='store_true', default=False, dest='jd',
help=("Test for consecutive Julian Period days between start and "
"end Badí' years."))
parser.add_argument(
'-L', '--alt-leap', action='store_true', default=False,
dest='alt_leap', help=("Use the 4|100|400 (default) or the 4|128 "
"rules from Julian Calendar day one."))
parser.add_argument(
'-M', '--month', type=int, default=1, dest='month',
help="Start month.")
parser.add_argument(
'-R', '--ref-day', type=str, default='Jalál', dest='ref_day',
help="Change the reference day. Default is Jalál.")
parser.add_argument(
'-S', '--start', type=int, default=None, dest='start',
help="Start Badí' year of sequence.")
parser.add_argument(
'-X', '--exact', action='store_true', default=False, dest='exact',
help=("Use the Historically correct Meeus method (default) or the "
"Astronomically correct method for finding the Julian period "
"day."))
parser.add_argument(
'-Y', '--year', action='store_true', default=False, dest='year',
help="Test for the consecutive defined years 1 - 3004.")
options = parser.parse_args()
dt = DateTests()
ret = 0
basename = os.path.basename(__file__)
if options.analyze: # -a
if options.start is None or options.end is None:
# Set default Gregorian years.
options.start = 1 # Julian year 1
options.end = 3005 # Gregorian year 3005
G = 'G' if options.graph else ''
C = 'C' if options.coff else ''
B = 'B' if options.bahji else ''
print(f"./contrib/misc/{basename} -a{C}{B}{G}S{options.start} "
f"-E{options.end}")
if options.graph: # -G
options.coff = True
data = dt.analyze_date_error(options)
items = []
for item in data:
year, month, day = item[0][:3]
h, m, s, ms = dt._get_hms(item[0], short_in=True)
bjd = item[1]
msg = (f"{year}-{month:>02}-{day:>02}T{h:>02}:{m:>02}:"
f"{s:>02} {bjd} ")
year, month, day = item[2][:3]
h, m, s, ms = dt._get_hms(item[2], short_in=True)
gjd = item[3]
msg += (f"{year}-{month:>02}-{day:>02}T{h:>02}:{m:>02}:"
f"{s:>02} {gjd} ")
diff = item[4]
offby = item[5]
msg += f"{diff} {offby}"
items.append(msg)
[print(item) for item in items]
else:
start_time = time.time()
data = dt.analyze_date_error(options)
diff_precision = 7
underline_length = 106 + diff_precision - 12
print('-' * underline_length)
print("Badí' Date Badí' JD Gregorian Date (Sunset) "
" Gregorian JD Diff", ' ' * (diff_precision-2),
"Off By")
print('-' * underline_length)
for b_date, bjd, g_date, gjd, diff, offby in data:
print(f"{str(b_date):>13} "
f"{bjd:<18} "
f"{str(g_date):<30} "
f"{gjd:<19} "
f"{fmt_float(diff, 2, diff_precision)} "
f"{fmt_float(offby, 2, 1)}")
print('-' * underline_length)
offbys = []
diffs = []
diff_total = p = n = 0
for item in data:
year = item[2][0]
diff = round(item[4], diff_precision)
diff_total += diff
offby = item[5]
if diff != 0:
year = item[0][0]
diffs.append((year, diff))
if offby != 0:
offbys.append(offby)
if offby > 0:
p += 1
elif offby < 0:
n += 1
total_years = options.end - options.start
print(f"Total years: {len(data)}\n")
print(f"Positive Errors: {p}")
print(f"Negative Errors: {n}")
print("-" * 21)
print(f"Total Errors: {n + p}\n")
if options.coff:
coff = sum(offbys) / len(offbys)
print(f"Average off by: {coff}")
set_items = set([diff for year, diff in diffs])
if set_items:
print(f"Maximum deviation: {fmt_float(max(set_items), 2, 1)}")
print(f"Minimum deviation: {fmt_float(min(set_items), 2, 1)}")
print("Difference Average: "
f"{fmt_float(diff_total/total_years, 2, 15)}")
items = {}
for year, diff in sorted(diffs, key=lambda x: x[1]):
item = items.setdefault(diff, [])
item.append(year)
print(f"Number of sequences: {len(items)}\n")
for diff, years in items.items():
print(f"{diff} -> {years}")
end_time = time.time()
days, hours, minutes, seconds = dt._dhms_from_seconds(
end_time - start_time)
print(f"\nElapsed time: {hours:02} hours, {minutes:02} minutes, "
f"{round(seconds, 6):02.6} seconds.")
elif options.badi_round_trip: # -b
if options.start is None or options.end is None:
print("If option -q is used, -S and -E must also be used.",
file=sys.stderr)
ret = 1
else:
start_time = time.time()
data = dt.badi_round_trip(options)
print(f"./contrib/misc/{basename} -bA {options.latitude} "
f"-O {options.longitude} -Z {options.zone} "
f"-S{options.start} -E{options.end}")
print("Start Date JD End Date Valid")
underline_length = 56
print('-' * underline_length)
[print(f"{str(date):<15} "
f"{fmt_float(jd, 7, 10)} "
f"{str(b_date):<15} "
f"{valid}"
) for date, jd, b_date, valid in data]
print('-' * underline_length)
print(f"Total years processed {options.end - options.start}.\n")
true = false = 0
for date, jd, b_date, valid in data:
if valid:
true += 1
else:
false += 1
print(f" Days valid: {true}")
print(f"Days invalid: {false}")
end_time = time.time()
days, hours, minutes, seconds = dt._dhms_from_seconds(
end_time - start_time)
print(f"\nElapsed time: {hours:02} hours, {minutes:02} minutes, "
f"{round(seconds, 6):02.6} seconds.")
elif options.ck_dates: # -c
if options.start is None or options.end is None:
print("If option -c is used, -S and -E must also be used.",
file=sys.stderr)
ret = 1
else:
print(f"./contrib/misc/{basename} -cS{options.start} "
f"-E{options.end}")
data = dt.create_date_lists(options)
bad_items = dt.check_long_date_from_short_date(data)
bad_items = bad_items if bad_items else "All dates match."
pprint.pprint(bad_items)
elif options.day: # -d
if options.start is None:
options.start = -1842 # Julian Calendar year 1
if options.end is None:
options.end = 1162 # Gregorian Calendar year 3005
start_time = time.time()
print(f"./contrib/misc/{basename} -dS {options.start} "
f"-E {options.end}")
(short_day, short_hms,
long_day, long_hms) = dt.find_longest_and_shortest_days(options)
underline_length = 69
print('-' * underline_length)
print("Shortest Day Length Longest Day Length")
print('-' * underline_length)
print(f"{str(short_day):15} {str(short_hms):18} "
f"{str(long_day):15} {str(long_hms):18}")
print('-' * underline_length)
end_time = time.time()
days, hours, minutes, seconds = dt._dhms_from_seconds(
end_time - start_time)
print(f"\nElapsed time: {hours:02} hours, {minutes:02} minutes, "
f"{round(seconds, 6):02.6} seconds.")
elif options.weekdays: # -e
if options.start is None or options.end is None:
print("If option -e is used, -S and -E must also be used.",
file=sys.stderr)
ret = 1
else:
start_time = time.time()
data = dt.find_weekdays(options)
C = 'C' if options.coff else ''
print(f"./contrib/misc/{basename} -e{C}S {options.start} "
f"-E {options.end} -A {options.latitude} "
f"-O {options.longitude} -Z {options.zone}")
print("Badí' Gregorian Week "
"Week Day Month Leap Days Day in")
print("Date Date Num "
"Day Name Name Year Year Year")
underline_length = 99
print('-' * underline_length)
[print(f"{str(date):<16} "
f"{str(g_date):<34} "
f"{week:02} "
f"{weekday} "
f"{wd_name:<8} "
f"{m_name:<10} "
f"{str(leap):5s} "
f"{days:<3} "
f"{total:>03}"
) for (date, g_date, week, weekday, wd_name, m_name,
leap, days, total) in data]
print('-' * underline_length)
print(f"Years processed: {options.end - options.start}")
prev_week = 0
week_errors = []
for items in data:
woy = items[2]
dow = items[3]
if prev_week != 0 and 1 < dow <= 7 and woy != prev_week:
week_errors.append((items[0], woy, dow, items[6]))
prev_week = woy
print(f"\nWeek-of-Year errors: {len(week_errors)}")
# woy = Week of year, dow = day of week
print("\n Date WOY DOW Leap")
print('-' * 27)
pprint.pp(week_errors, width=30)
end_time = time.time()
days, hours, minutes, seconds = dt._dhms_from_seconds(
end_time - start_time)
print(f"\nElapsed time: {hours:02} hours, {minutes:02} minutes, "
f"{round(seconds, 6):02.6} seconds.")
elif options.g_dates: # -g
if options.start is None or options.end is None:
print("If option -g is used, -S and -E must also be used.",
file=sys.stderr)
ret = 1
else:
data = dt.find_gregorian_dates(options)
X = 'X' if options.exact else ''
L = 'L' if options.alt_leap else ''
print(f"./contrib/misc/{basename} -g{X}{L}S{options.start} "
f"-E{options.end}")
print("b_date", " " * 9, "bjd", " " * 14, "g_date", " " * 23,
"gjd", " " * 14, "diff offby")
[print(f"{str(b_date):<16} "
f"{bjd:<18} "
f"{str(g_date):<30} "
f"{gjd:<18} "
f"{diff:>4} "
f"{offby}"
) for b_date, bjd, g_date, gjd, diff, offby in data]
elif options.increment: # -i
if options.start is None:
print("If option -i is used, -S must also be used.",
file=sys.stderr)
ret = 1
else:
start_time = time.time()
data = dt.day_inremental_change(options)
year = options.start
month = options.month
day = options.start_day
hour = options.hour
print(f"./contrib/misc/{basename} -iS {year} -M {month} -D {day} "
f"-H {hour}")
underline_length = 102
print('-' * underline_length)
print("Reference Date", ' ' * 7, "Gregorian Date", ' ' * 16,
"JD", ' ' * 15, "Derived Date")
print('-' * underline_length)
[print(f"{str(date):22} "
f"{str(g_date):31} "
f"{fmt_float(jd, 7, 10)} "
f"{str(b_date):28}"
) for date, g_date, jd, b_date in data]
print('-' * underline_length)
print(f"Total HMS: {len(data)}")
end_time = time.time()
days, hours, minutes, seconds = dt._dhms_from_seconds(
end_time - start_time)
print(f"\nElapsed time: {hours:02} hours, {minutes:02} minutes, "
f"{round(seconds, 6):02.6} seconds.")
elif options.list: # -l
if options.start is None or options.end is None:
print("If option -l is used, -S and -E must also be used.",
file=sys.stderr)
ret = 1
else:
print(f"./contrib/misc/{basename} -lS{options.start} "
f"-E{options.end}")
data = dt.create_date_lists(options)
pprint.pprint(data)
elif options.precursor: # -p
if options.start is None or options.end is None:
print("If option -p is used, -S and -E must also be used.",
file=sys.stderr)
ret = 1
else:
print(f"./contrib/misc/{basename} -pS{options.start} "
f"-E{options.end}")
data = dt.find_coefficents_precursor(options)
[print(f"{gy:> 5} {by:> 5}, {n:<1} {a:>2}")
for gy, by, n, a in data]
print(f"Total years: {len(data)}")
elif options.coeff: # -q
if options.start is None or options.end is None:
print("If option -q is used, -S and -E must also be used.",
file=sys.stderr)
ret = 1
else:
print(f"./contrib/misc/{basename} -qS{options.start} "
f"-E{options.end}")
[print(item) for item in dt.find_coefficents(options)]
elif options.twenty_four: # -t
if options.start is None or options.end is None:
print("If option -t is used, -S and -E must also be used.",
file=sys.stderr)
ret = 1
else:
print(f"./contrib/misc/{basename} -tS{options.start} "
f"-E{options.end}")
print("Badí' Date Gregorian Date "
"SS1 Frac SS2 Frac SS2-SS1 HMS Diff")
[print(f"{str(b_date):<18} "
f"{str(g_date):<21} "
f"{fss0:<8} "
f"{fss1:<8} "
f"{ss_diff:<8} "
f"{str(hms):19}"
) for (b_date, g_date, fss0, fss1,
ss_diff, hms) in dt.twenty_four_hours(options)]
elif options.leap_years: # -y
if options.start is None or options.end is None:
print("If option -y is used, -S and -E must also be used.",
file=sys.stderr)
ret = 1
else:
start_time = time.time()
data = dt.find_leap_years(options)
print(f"./contrib/misc/{basename} -yS{options.start} "
f"-E{options.end}")
print(" 5 Year")
print("Year Leap Years Spread")
underline_length = 24
print('-' * underline_length)
[print(f"{year:+5d} "
f"{str(leap):5} "
f"{years} "
f"{spread}"
) for year, leap, years, spread in data]
print('-' * underline_length)
print(f" Total Leap years: {len(data)}")
total_5 = [l[2] for l in data]
print(f"5th year leap years: {total_5.count(5)}")
end_time = time.time()
days, hours, minutes, seconds = dt._dhms_from_seconds(
end_time - start_time)
print(f"\nElapsed time: {hours:02} hours, {minutes:02} minutes, "
f"{round(seconds, 6):02.6} seconds.")
else:
parser.print_help()
sys.exit(ret)