<commit_before>#! usr/bin/env python #
# -*- coding: utf-8 -*-

import datetime, calendar,operator

POSIMONTHS = ('Moses', 'Homer', 'Aristotle', 'Archimedes', 'Caesar', '<NAME>', 'Charlemagne', 'Dante', 'Gutenberg', 'Shakespeare', 'Descartes', 'Frederick', 'Bichat', 'Complementary')
REGMONTHS = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'October', 'November', 'December', 'Intercalary')

# Ordinal code stolen from stack overflow.
def ordinal(n):
	return "%d%s" % (n,"tsnrhtdd"[(n/10%10!=1)*(n%10<4)*n%10::4])

def horicat(strings):
	lines = ["".join(line) for line in list(zip(*strings))]
	return "\n".join(lines)

def design_calendar():
	"""
	Reference math from Calca:
	days = 365
	d_i_week = 7
	w_i_month = 4
	m_i_year = round(365 / (d_i_week * w_i_month)) => 13

	ic_days = days - (d_i_week * w_i_month * m_i_year) => 1
	"""

	d_i_week = int(input("How many days in a week? ") or 7)
	w_i_month = int(input("How many weeks in a month? ") or 4)
	year_1 = int(input("When is year 1? ") or 1788)
	return AlternateCal(w_i_month, d_i_week, year_1)

class AlternateCal(object):
	"""
	>>> cal = AlternateCal()
	>>> bad_cal = AlternateCal(w_i_month=3, d_i_week=8, year_1=1788)
	>>> cal.from_date(2014, 2, 22)
	positivist date(226, 2, 25)
	>>> cal.from_date(datetime.date(2014, 2, 22))
	positivist date(226, 2, 25)
	>>> print(cal.from_date(2001, 1, 1))
	Monday, 1st of Moses, 213: Prometheus
	>>> print(cal.from_date(2001, 5, 10))
	Thursday, 18th of Caesar, 213: Hannibal
	>>> print(cal.from_date(2001, 12, 31))
	Festival of All the Dead, 213
	>>> print(cal.from_date(2000, 2, 29))
	Thursday, 4th of Aristotle, 212: Anaxagoras
	>>> print(cal.from_date(2000, 12, 31))
	Festival of Holy Women, 212
	>>> print(cal.from_date(2014, 2, 22).to_gregorian())
	2014-02-22
	>>> class New_cal(AlternateCal):
	...  days_in_a_month = 26
	>>> cal2 = New_cal()
	>>> cal.from_date(2014, 2, 22)
	positivist date(226, 2, 25)
	>>> print(cal)
	The Positivist calendar, consisting of 7-day weeks, 4-week months, and 13-month years, with 1 intercalary day(s).
	>>> print(bad_cal)
	The Crepuscular calendar, consisting of 8-day weeks, 3-week months, and 15-month years, with 5 intercalary day(s).
	>>> print(bad_cal.from_date(2014, 3, 5))
	8th Day, 16th of March, 226: Solon
	>>> next_day = datetime.timedelta(days = 1)
	>>> print(bad_cal.from_date(2011,3,11) + next_day)
	Sunday, 23rd of March, 223: Aristippus
	"""
	
	def __init__(calendar, w_i_month=4, d_i_week=7, year_1=1788):
		calendar.days_in_a_month = d_i_week * w_i_month
		calendar.days_in_a_week = d_i_week
		calendar.weeks_in_a_month = w_i_month
		calendar.months_in_a_year = 365 // calendar.days_in_a_month
		calendar.intercalary_days = 365 % calendar.days_in_a_month
		name_choices = ('New Adjusted', 'Utilitarian', 'Lycurgian', 'Multi-Manifold', 'Crepuscular', 'Urquhart', 'Adamantine', 'Organic Non-Repeating', 'Antediluvian', 'Re-Corresponding', 'Positivist')
		calendar.name = name_choices[hash((w_i_month, d_i_week, year_1)) % 11]
		calendar.year_offset = year_1
		if calendar.name is 'Positivist':
			calendar.MONTHS = POSIMONTHS
		else:
			calendar.MONTHS = REGMONTHS
		calendar.DAYS = ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')
		calendar.SAINTS = ('Prometheus', 'Hercules', 'Orpheus', 'Ulysses', 'Lycurgus', 'Romulus', 'NUMA', 'Belus', 'Sesostris', 'Menu', 'Cyrus', 'Zoroaster', 'The Druids', 'BUDDHA', 'Fo-Hi', 'Lao-Tseu', 'Meng-Tseu', 'Theocrats of Tibet', 'Theocrats of Japan', 'Mano-Capac', 'CONFUCIUS', 'Abraham', 'Samuel', 'Solomon', 'Isaiah', 'St. John  the Baptist', 'Haroun-al-Raschid', 'MUHAMMAD', 'Hesiod', 'Tyrt\u00E6us', 'Anacreon', 'Pindar', 'Sophocles', 'Theocritus', '\u00C6SCHYLUS', 'Scopas', 'Zeuxis', 'Ictinus', 'Praxiteles', 'Lysippus', 'Apelles', 'PHIDIAS', '\u00C6sop', 'Plautus', 'Terence', 'Phaedrus', 'Juvenal', 'Lucian', 'ARISTOPHANES', 'Ennius', 'Lucretius', 'Horace', 'Tibullus', 'Ovid', 'Lucan', 'VIRGIL', 'Anaximander', 'Anaximenes', 'Heraclitus', 'Anaxagoras', 'Democritus', 'Herodotus', 'THALES', 'Solon', 'Xenophanes', 'Empedocles', 'Thucydides', 'Archytas', 'Apollonius of Tyana', 'PYTHAGORAS', 'Aristippus', 'Antisthenes', 'Zeno', 'Cicero', 'Epictetus', 'Tacitus', 'SOCRATES', 'Xenocrates', 'Philo of Alexandria', 'St. John the Evangelist', 'St. Justin', ' St.Clement of Alexandria', 'Origen', 'PLATO', 'Theophrastus', 'Herophilus', 'Erasistratus', 'Celsus', 'Galen', 'Avicenna', 'HIPPOCRATES', 'Euclid', 'Arist\u0230us', 'Theodisius of Bithynia', 'Hero', 'Pappus', 'Diophantus', 'APOLLONIUS', 'Eudoxus', 'Pytheas', 'Aristarchus', 'Eratosthenes', 'Ptolemy', 'Albategnius', 'HIPPARCHUS', 'Varro', 'Columella', 'Vitruvius', 'Strabo', 'Frontinus', 'Plutarch', 'PLINY THE ELDER', 'Miltiades', 'Leonidas', 'Aristides', 'Cimon', 'Xenophon', 'Phocion', 'THEMISTOCLES', 'Pericles', 'Philip', 'Demos- thenes', 'Ptolemy Lagus', 'Philopoemen', 'Polybius', 'ALEXANDER', 'Junius Brutus', 'Camillus', 'Fabricius', 'Hannibal', '<NAME>', 'Marius', 'SCIPIO', 'Augustus', 'Vespasian', 'Hadrian', 'Antonius', 'Papinian', '<NAME>', 'TRAJAN', 'St. Luke', 'St. Cyprian', ' St.Athanasius', 'St. Jerome', 'St. Ambrose', 'St. Monica', ' St.AUGUSTINE', 'Constantine', 'Theodosius', 'St. Chrysostom', ' St.Genevieve of Paris', 'St. Pulcheria', 'St. Gregory the Great', 'HILDEBRAND', 'St. Benedict', 'St. Boniface', 'St. Isidore of Seville', 'Lanfranc', 'Heloise', "Arch'ts of Middle Ages", ' St.BERNARD', 'St. <NAME>', 'St. <NAME>', 'St. Theresa', 'St. Vincent de Paul', 'Bourdaloue', '<NAME>', 'BOSSUET', 'Theodoric the Great', 'Pelayo', 'Otho the Great', 'St. Henry', 'Villers', 'Don John of Austria', 'ALFRED', '<NAME>', 'The Cid', '<NAME>', 'Joan of Arc', 'Albuquerque', 'Bayard', 'GODFREY', 'St. Leo the Great', 'Gerbert', '<NAME>', 'Suger', '<NAME>', 'St. Francis of Assisi', 'INNOCENT III', 'St. Clotilde', 'St. Bathilda', 'St. Stephen of Hungary', 'St. Elizabeth of Hungary', 'Blanche of Castille', 'St. Ferdinand III', 'ST. LOUIS', 'The Troubadours', 'Boccaccio', 'Rabelais', 'Cervantes', '<NAME>', '<NAME>', 'ARISTO', '<NAME>', '<NAME>', 'Holbein', 'Poussin', 'Velásquez', 'Teniers', 'RAPHAEL', 'Froissart', 'Camoens', 'The Spanish Romancers', 'Chateaubriand', '<NAME>', 'Manzoni', 'TASSO', 'Petrarca', '<NAME>', 'Mme. <NAME>', 'F\u00E9n\u00E9lon', 'Klopstock', 'Byron', 'MILTON', '<NAME>', '<NAME>', '<NAME>', 'Napier', 'Lacaille', 'Cook', 'COLUMBUS', '<NAME>', 'Amontons', 'Harrison', 'Dollond', 'Arkwright', 'Cont\u00e9', 'VAUCANSON', 'Stevin', 'Mariotte', 'Papin', 'Black', 'Jouffroy', 'Dalton', 'WATT', '<NAME>', 'Guglielmini', 'Duhamel (du Monceau)', 'Saussure', 'Coulomb', 'Carnot', 'MONTGOLFIER', '<NAME>', 'Moreto', 'Rojas', 'Otway', 'Lessing', 'Goethe', 'CALDERON', 'Tirso', 'Vondel', 'Racine', 'Voltaire', 'Metastasio', 'Schiller', 'CORNEILLE', 'Almarcon', 'Mme. de Motteville', 'Mme. de S\u00E9vign\u00E9', 'Lesage', 'Mme. de Staal', 'Fielding', 'MOLIERE', 'Pergolese', 'Sacchini', 'Gluck', 'Beethoven', 'Rossini', 'Bellini', 'MOZART', '<NAME>', '<NAME>', 'St. Bonaven- tura', 'Ramus', 'Montaigne', 'Campanella', 'ST. THOMAS AQUINAS', 'Hobbes', 'Pascal', 'Locke', 'Vauvenargues', 'Diderot', 'Cabanis', '<NAME>', 'Grotius', 'Fontenelle', 'Vico', 'Fr\u00E9ret', 'Montesquieu', 'Buffon', 'LEIBNITZ', 'Robertson', '<NAME>', 'Kant', 'Condercet', '<NAME>', 'Hegel', 'HUME', '<NAME>', '<NAME>', '<NAME>', '<NAME>', '<NAME>', '<NAME>', 'LOUIS XI', "L'H\u0244pital", 'Barneveldt', '<NAME>', '<NAME>', 'Ruyter', '<NAME>', 'WILLIAM THE SILENT', 'Ximenes', 'Sully', 'Mazarin', 'Colbert', "D'Aranda", 'Turgot', 'RICHELIEU', 'Sidney', 'Franklin', 'Washington', 'Jefferson', 'Bolivar', 'Francia', 'CROMWELL', 'Copernicus', 'Kepler', 'Huyghens', '<NAME>', 'Bradley', 'Volta', 'GALILEO', 'Vieta', 'Wallis', 'Clairaut', 'Euler', "D'Alembert", 'Lagrange', 'NEWTON', 'Bergmann', 'Priestley', 'Cavendish', '<NAME>', 'Berthollet', 'Berzelius', 'LAVOISIER', 'Harvey', 'Bo\u0235rhaave', 'Linn\u0230us', 'Haller', 'Lamarck', 'Broussais', 'GALL', 'Festival of All the Dead', 'Festival of Holy Women')
		calendar.LEAPSAINTS = ('Cadmus', 'Theseus', 'Tiresias', '', '', '', '', 'Semiramus', '', '', '', '', 'Ossian', '', '', '', '', '', '', 'Tameha-meha', '', 'Joseph', '', 'David', '', '', 'Abderrah-man', '', '', 'Sappho', '', '', 'Euripides', 'Longus', '', '', '', '', '', '', '', '', 'Pilpay', '', 'Menander', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'Leucippus', '', '', '', '', '', '', 'Philolaus', '', '', '', '', '', 'Pliny the Younger', 'Arrian', '', '', '', '', '', 'St. Iren&#230us', '', 'Tertullian', '', '', '', '', '', '', 'Averrhoes', '', '', '', '', 'Ctesibius', '', '', '', 'Aratus', 'Nearchus', 'Berosus', 'Sosigenes', '', 'Nasir-Eddin', '', '', '', '', '', '', '', '', '', '', '', '', '', 'Epaminon-das', '', '', '', '', '', '', '', '', '', 'Cincinnatus', 'Regulus', '', '', 'The Gracchi', '', 'M&#230cenas', 'Titus', 'Nerva', '<NAME>', 'Ulpian', 'Aetius', '', 'St. James', '', '', '', '', '', '', '', '', 'St. Basil', '', 'Marcian', '', '', 'St. Anthony', 'St. Austin', 'St. Bruno', 'St. Anselm', 'Beatrice', 'St. Benezet', '', '<NAME>', '<NAME>', 'St.Catharineof Siena', "Abb\u00E9 de l'Ep\u00E9e", 'ClaudeFleury', '<NAME>', '', '', '', '<NAME>', '', 'La Valette', 'JohnSobieski', '', '', 'Tancred', 'Saladin', 'Marina', '<NAME>', '', '', '<NAME>', 'PeterDamian', '', 'St. Eligius', 'Becket', 'St. Dominic', '', '', 'St. Mathilda of Tuscany', '<NAME>', '', '', '<NAME>', '', '', 'Chaucer', 'Swift', '', 'Burns', 'Goldsmith', '', 'Titian', '<NAME>', 'Rembrandt', 'Lesueuer', 'Murillo', 'Rubens', '', 'Joinville', 'Spenser', '', '', '<NAME>', '', '', '', 'L. of Grana-<NAME>', 'Mme. de Sta\u00EBl', 'St. Francisof Sales', 'Gessner', '\u00E9. Mercoeur & Shelly', '', 'Chardin', 'Gresham', 'Magellan', 'Briggs', 'Delambre', 'Tasman', '', '', 'Wheatstone', '<NAME>', 'Graham', 'Jacquard', '', '', 'Torricelli', 'Boyle', 'Worcester', '', 'Fulton', 'Thilorier', '', '', 'Riquet', 'Bourgelat', 'Bouguer', 'Borda', 'Vauban', '', 'Montalvan', '<NAME>', 'Guevara', '', '', '', '', '', '', '', '', 'Alfieri', '', '', '', 'Mme. Roland', '<NAME>', 'Sterne', '<NAME>', 'Richardson', '', 'Palestrina', 'Gr\u00E9try', 'Lully', 'Handel', 'Weber', 'Donizeti', '', '<NAME>', '<NAME>', 'Joachim', 'The Cardinal of Cusa', 'Erasmus', '<NAME>', '', 'Spinoza', '<NAME>', 'Male-branche', '<NAME>', 'Duclos', 'GeorgeLeroy', '', 'Cujas', 'Maupertuis', 'Herder', 'Winckle-mann', "D'Aguesseau", 'Oken', '', 'Gibbon', 'Dunoyer', 'Fichte', 'Ferguson', 'Bonald', '<NAME>', '', '', '', 'Guicciardini', '', 'Sixtus V', '', '', '', '', '', '', '', '', '', '', 'Oxenstiern', 'Walpole', 'Louis XIV', 'Pombal', 'Campo-manes', '', 'Lambert', 'Hampden', 'Kosciusko', 'Madison', "<NAME>", '', '', '<NAME>\u00E9', 'Halley', 'Varignon', '<NAME>', 'R\u00F5mer', 'Sauveur', '', 'Harriot', 'Fermat', 'Poinsot', 'Monge', '<NAME>', '<NAME>', '', 'Scheele', 'Davy', '', 'Geoffroy', '', 'Ritter', '', '<NAME>', '<NAME>', '<NAME>', "Vicq-d'Azyr", 'Blainville', 'Morgagni', '', '', 'Festival of Holy Women')



		class AlternateDate(object):
			def __init__(self, year, month, day, calendar):
				self.year = year
				self.month = month
				self.day = day
				self.day_of_year = (month - 1) * calendar.weeks_in_a_month * calendar.days_in_a_week + day
				if self.day_of_year > 366:
					raise ValueError("This day cannot exist.")

				self.calendar = calendar
				self.is_leap = calendar.is_leap(self.to_gregorian().year)
								
				self.weekday = calendar.get_weekday(self.day)
				self.month_name = calendar.get_month_name(self.month)
				self.day_name = calendar.get_day_name(self.day_of_year, self.is_leap)
				self.weekday_name = calendar.get_weekday_name(self.weekday)
				
			def total_days(self):
				year = self.year + calendar.year_offset
				return datetime.date(year, 1, 1).toordinal() + self.day_of_year - 1

			def to_gregorian(self):
				return datetime.date.fromordinal(self.total_days())
			
			def __str__(self):
				if self.month is calendar.months_in_a_year + 1:
					return "{}, {}".format(self.day_name, self.year)
				else:
					return "{}, {} of {}, {}: {}".format(self.weekday_name, ordinal(self.day), self.month_name, 
												    self.year, self.day_name)
				
			def __repr__(self):
				return '%s date(%d, %d, %d)' % (calendar.name.lower(), self.year, self.month, self.day)
			
			def __add__(self, timedelta):
				return self.calendar.from_date((self.to_gregorian() + timedelta))
			
			def __sub__(self, arg):
				if isinstance(arg, datetime.timedelta):
					return self.calendar.from_date(self.to_gregorian() - arg)
				elif "AlternateDate" in str(type(arg)):
					return datetime.timedelta(days=self.total_days() - arg.total_days())

			def __eq__(self, other_date):
				othercast = (other_date.to_gregorian())
				return self.to_gregorian() == othercast
			
			def __gt__(self, other_date):
				othercast = (other_date.to_gregorian())
				return self.to_gregorian() > othercast
			
			def __lt__(self, other_date):
				othercast = (other_date.to_gregorian())
				return self.to_gregorian() < othercast	
			
			def __ge__(self, other_date):
				othercast = (other_date.to_gregorian())
				return self.to_gregorian() >= othercast
				
			def __le__(self, other_date):
				othercast = (other_date.to_gregorian())
				return self.to_gregorian() <= othercast

		calendar.date_class = AlternateDate

	# create a date from gregorian date
	def from_date(self, *args):
		if not args:
			gregorian = datetime.date.today()
		else:
			try:
				year, month, day = args
				gregorian = datetime.date(year, month, day)
				# return self.date_class(datetime.date(year, month, day), self)
			except Exception:
				try:
					gregorian = args[0]
					# return self.date_class(args[0], self)
				except Exception:
					raise
		year = gregorian.year - self.year_offset
		day_of_year = gregorian.timetuple().tm_yday
		month = ((day_of_year - 1) // self.days_in_a_month) + 1
		day = day_of_year % self.days_in_a_month or self.days_in_a_month
		return self.date_class(year, month, day, self)

	# create "native" date
	def date(self, *args):
		if not args:
			return self.from_date()
		else:
			year, month, day = args
			return self.date_class(year, month, day, self)
	
	def get_weekday(self, day):
		return day % self.days_in_a_week or self.days_in_a_week
	def get_weekday_name(self, weekday):
		if weekday > len(self.DAYS):
			return ordinal(weekday) + " Weekday"
		else:
			return self.DAYS[weekday - 1]				
	def get_month_name(self, month):
		if month > len(self.MONTHS):
			return ordinal(month) + " Month"
		else:
			return self.MONTHS[(month - 1)]
	def get_day_name(self, day, is_leap):
		if day > len(self.SAINTS):
			return ""
		elif is_leap and self.LEAPSAINTS[day - 1]:
			return self.LEAPSAINTS[day - 1]
		else:
			return self.SAINTS[day - 1]
	def is_leap(self, year):
		return calendar.isleap(year + self.year_offset)

	def print_month(self, year=None, month=None):

		maxwidth = max(max(map(len, self.SAINTS)),(max(map(len, self.LEAPSAINTS))))
		if not year or not month:
			today = self.date()
			year = today.year
			month = today.month
		if month == self.months_in_a_year + 1:
			intercal = True
			print("THE {} INTERCALARY DAYS OF THE YEAR {}".format(self.name.upper(), year))
		else:
			intercal = False
			print("THE {} MONTH OF {}, {}".format(self.name.upper(), self.get_month_name(month).upper(), year))
	
		month_offset = (month - 1) * self.weeks_in_a_month * self.days_in_a_week	
		for week in range(self.weeks_in_a_month):		
			calbox = []
			week_offset = (week * self.days_in_a_week)
			for date in range(1 + week_offset, week_offset + self.days_in_a_week + 1):
				day_of_year = month_offset + date
				if day_of_year > len(self.SAINTS):
					break
				if not intercal:
					weekday = self.get_weekday(date)
					wkd_name = self.get_weekday_name(weekday)
					datestr = str(date)
				else:
					weekday = 0
					wkd_name = ""
					datestr = ""
				saint = self.get_day_name(day_of_year, self.is_leap(year))
				
				height = 4
				box = []
				box.append("+".ljust(maxwidth + 1, '-'))
				if wkd_name:
					box.append("|" + wkd_name.ljust(maxwidth - len(datestr)) + datestr)
				else:
					box.append("|".ljust(maxwidth + 1))
				for i in range(height):
					box.append("|".ljust(maxwidth + 1))
				box.append("|" + saint.rjust(maxwidth))

				calbox.append(box)
			
			cal_layout = horicat(calbox)
			print(cal_layout)

	def __str__(self):
		return "The %s calendar, consisting of %d-day weeks, %d-week months, and %d-month years, with %d intercalary day(s)." % (self.name, self.days_in_a_week, self.days_in_a_month // self.days_in_a_week, self.months_in_a_year, self.intercalary_days)

if __name__ == "__main__":	
	import doctest
	doctest.testmod()
	
<commit_msg>More responsible error handling<commit_after>#! usr/bin/env python #
# -*- coding: utf-8 -*-

import datetime, calendar,operator

POSIMONTHS = ('Moses', 'Homer', 'Aristotle', 'Archimedes', 'Caesar', '<NAME>', 'Charlemagne', 'Dante', 'Gutenberg', 'Shakespeare', 'Descartes', 'Frederick', 'Bichat', 'Complementary')
REGMONTHS = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'October', 'November', 'December', 'Intercalary')

# Ordinal code stolen from stack overflow.
def ordinal(n):
	return "%d%s" % (n,"tsnrhtdd"[(n/10%10!=1)*(n%10<4)*n%10::4])

def horicat(strings):
	lines = ["".join(line) for line in list(zip(*strings))]
	return "\n".join(lines)

def design_calendar():
	"""
	Reference math from Calca:
	days = 365
	d_i_week = 7
	w_i_month = 4
	m_i_year = round(365 / (d_i_week * w_i_month)) => 13

	ic_days = days - (d_i_week * w_i_month * m_i_year) => 1
	"""

	d_i_week = int(input("How many days in a week? ") or 7)
	w_i_month = int(input("How many weeks in a month? ") or 4)
	year_1 = int(input("When is year 1? ") or 1788)
	return AlternateCal(w_i_month, d_i_week, year_1)

class AlternateCal(object):
	"""
	>>> cal = AlternateCal()
	>>> bad_cal = AlternateCal(w_i_month=3, d_i_week=8, year_1=1788)
	>>> cal.from_date(2014, 2, 22)
	positivist date(226, 2, 25)
	>>> cal.from_date(datetime.date(2014, 2, 22))
	positivist date(226, 2, 25)
	>>> print(cal.from_date(2001, 1, 1))
	Monday, 1st of Moses, 213: Prometheus
	>>> print(cal.from_date(2001, 5, 10))
	Thursday, 18th of Caesar, 213: Hannibal
	>>> print(cal.from_date(2001, 12, 31))
	Festival of All the Dead, 213
	>>> print(cal.from_date(2000, 2, 29))
	Thursday, 4th of Aristotle, 212: Anaxagoras
	>>> print(cal.from_date(2000, 12, 31))
	Festival of Holy Women, 212
	>>> print(cal.from_date(2014, 2, 22).to_gregorian())
	2014-02-22
	>>> class New_cal(AlternateCal):
	...  days_in_a_month = 26
	>>> cal2 = New_cal()
	>>> cal.from_date(2014, 2, 22)
	positivist date(226, 2, 25)
	>>> print(cal)
	The Positivist calendar, consisting of 7-day weeks, 4-week months, and 13-month years, with 1 intercalary day(s).
	>>> print(bad_cal)
	The Crepuscular calendar, consisting of 8-day weeks, 3-week months, and 15-month years, with 5 intercalary day(s).
	>>> print(bad_cal.from_date(2014, 3, 5))
	8th Day, 16th of March, 226: Solon
	>>> next_day = datetime.timedelta(days = 1)
	>>> print(bad_cal.from_date(2011,3,11) + next_day)
	Sunday, 23rd of March, 223: Aristippus
	"""
	
	def __init__(calendar, w_i_month=4, d_i_week=7, year_1=1788):
		calendar.days_in_a_month = d_i_week * w_i_month
		calendar.days_in_a_week = d_i_week
		calendar.weeks_in_a_month = w_i_month
		calendar.months_in_a_year = 365 // calendar.days_in_a_month
		calendar.intercalary_days = 365 % calendar.days_in_a_month
		name_choices = ('New Adjusted', 'Utilitarian', 'Lycurgian', 'Multi-Manifold', 'Crepuscular', 'Urquhart', 'Adamantine', 'Organic Non-Repeating', 'Antediluvian', 'Re-Corresponding', 'Positivist')
		calendar.name = name_choices[hash((w_i_month, d_i_week, year_1)) % 11]
		calendar.year_offset = year_1
		if calendar.name is 'Positivist':
			calendar.MONTHS = POSIMONTHS
		else:
			calendar.MONTHS = REGMONTHS
		calendar.DAYS = ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')
		calendar.SAINTS = ('Prometheus', 'Hercules', 'Orpheus', 'Ulysses', 'Lycurgus', 'Romulus', 'NUMA', 'Belus', 'Sesostris', 'Menu', 'Cyrus', 'Zoroaster', 'The Druids', 'BUDDHA', 'Fo-Hi', 'Lao-Tseu', 'Meng-Tseu', 'Theocrats of Tibet', 'Theocrats of Japan', 'Mano-Capac', 'CONFUCIUS', 'Abraham', 'Samuel', 'Solomon', 'Isaiah', 'St. John  the Baptist', 'Haroun-al-Raschid', 'MUHAMMAD', 'Hesiod', 'Tyrt\u00E6us', 'Anacreon', 'Pindar', 'Sophocles', 'Theocritus', '\u00C6SCHYLUS', 'Scopas', 'Zeuxis', 'Ictinus', 'Praxiteles', 'Lysippus', 'Apelles', 'PHIDIAS', '\u00C6sop', 'Plautus', 'Terence', 'Phaedrus', 'Juvenal', 'Lucian', 'ARISTOPHANES', 'Ennius', 'Lucretius', 'Horace', 'Tibullus', 'Ovid', 'Lucan', 'VIRGIL', 'Anaximander', 'Anaximenes', 'Heraclitus', 'Anaxagoras', 'Democritus', 'Herodotus', 'THALES', 'Solon', 'Xenophanes', 'Empedocles', 'Thucydides', 'Archytas', 'Apollonius of Tyana', 'PYTHAGORAS', 'Aristippus', 'Antisthenes', 'Zeno', 'Cicero', 'Epictetus', 'Tacitus', 'SOCRATES', 'Xenocrates', 'Philo of Alexandria', 'St. John the Evangelist', 'St. Justin', ' St.Clement of Alexandria', 'Origen', 'PLATO', 'Theophrastus', 'Herophilus', 'Erasistratus', 'Celsus', 'Galen', 'Avicenna', 'HIPPOCRATES', 'Euclid', 'Arist\u0230us', 'Theodisius of Bithynia', 'Hero', 'Pappus', 'Diophantus', 'APOLLONIUS', 'Eudoxus', 'Pytheas', 'Aristarchus', 'Eratosthenes', 'Ptolemy', 'Albategnius', 'HIPPARCHUS', 'Varro', 'Columella', 'Vitruvius', 'Strabo', 'Frontinus', 'Plutarch', 'PLINY THE ELDER', 'Miltiades', 'Leonidas', 'Aristides', 'Cimon', 'Xenophon', 'Phocion', 'THEMISTOCLES', 'Pericles', 'Philip', 'Demos- thenes', 'Ptolemy Lagus', 'Philopoemen', 'Polybius', 'ALEXANDER', '<NAME>', 'Camillus', 'Fabricius', 'Hannibal', '<NAME>', 'Marius', 'SCIPIO', 'Augustus', 'Vespasian', 'Hadrian', 'Antonius', 'Papinian', '<NAME>', 'TRAJAN', 'St. Luke', 'St. Cyprian', ' St.Athanasius', 'St. Jerome', 'St. Ambrose', 'St. Monica', ' St.AUGUSTINE', 'Constantine', 'Theodosius', 'St. Chrysostom', ' St.Genevieve of Paris', 'St. Pulcheria', 'St. Gregory the Great', 'HILDEBRAND', 'St. Benedict', 'St. Boniface', 'St. Isidore of Seville', 'Lanfranc', 'Heloise', "Arch'ts of Middle Ages", ' St.BERNARD', 'St. Francis Xavier', 'St. Charles Borromeo', 'St. Theresa', 'St. V<NAME>', 'Bourdaloue', '<NAME>', 'BOSSUET', 'Theodoric the Great', 'Pelayo', 'Otho the Great', 'St. Henry', 'Villers', 'Don John of Austria', 'ALFRED', '<NAME>', 'The Cid', '<NAME>', 'Joan of Arc', 'Albuquerque', 'Bayard', 'GODFREY', 'St. Leo the Great', 'Gerbert', '<NAME>mit', 'Suger', 'Alexander III', 'St. Francis of Assisi', 'INNOCENT III', 'St. Clotilde', 'St. Bathilda', 'St. Stephen of Hungary', 'St. Elizabeth of Hungary', 'Blanche of Castille', 'St. Ferdinand III', 'ST. LOUIS', 'The Troubadours', 'Boccaccio', 'Rabelais', 'Cervantes', 'La Fontain', 'De Foe', 'ARISTO', '<NAME>', 'Michael Angelo', 'Holbein', 'Poussin', 'Velásquez', 'Teniers', 'RAPHAEL', 'Froissart', 'Camoens', 'The Spanish Romancers', 'Chateaubriand', '<NAME>', 'Manzoni', 'TASSO', 'Petrarca', '<NAME>', 'Mme. de Lafayette', 'F\u00E9n\u00E9lon', 'Klopstock', 'Byron', 'MILTON', '<NAME>', '<NAME>eur', '<NAME>', 'Napier', 'Lacaille', 'Cook', 'COLUMBUS', '<NAME>', 'Amontons', 'Harrison', 'Dollond', 'Arkwright', 'Cont\u00e9', 'VAUCANSON', 'Stevin', 'Mariotte', 'Papin', 'Black', 'Jouffroy', 'Dalton', 'WATT', '<NAME>', 'Guglielmini', 'Duhamel (du Monceau)', 'Saussure', 'Coulomb', 'Carnot', 'MONTGOLFIER', '<NAME>', 'Moreto', 'Rojas', 'Otway', 'Lessing', 'Goethe', 'CALDERON', 'Tirso', 'Vondel', 'Racine', 'Voltaire', 'Metastasio', 'Schiller', 'CORNEILLE', 'Almarcon', 'Mme. de Motteville', 'Mme. de S\u00E9vign\u00E9', 'Lesage', 'Mme. de Staal', 'Fielding', 'MOLIERE', 'Pergolese', 'Sacchini', 'Gluck', 'Beethoven', 'Rossini', 'Bellini', 'MOZART', '<NAME>', '<NAME>', 'St. Bonaven- tura', 'Ramus', 'Montaigne', 'Campanella', 'ST. THOMAS AQUINAS', 'Hobbes', 'Pascal', 'Locke', 'Vauvenargues', 'Diderot', 'Cabanis', '<NAME>', 'Grotius', 'Fontenelle', 'Vico', 'Fr\u00E9ret', 'Montesquieu', 'Buffon', 'LEIBNITZ', 'Robertson', '<NAME>', 'Kant', 'Condercet', '<NAME>', 'Hegel', 'HUME', '<NAME>', 'Cosmo de Medici the Elder', '<NAME>', 'Isabella of Castille', '<NAME>', '<NAME>', '<NAME>I', "L'H\u0244pital", 'Barneveldt', '<NAME>', '<NAME>', 'Ruyter', '<NAME>', 'WILLIAM THE SILENT', 'Ximenes', 'Sully', 'Mazarin', 'Colbert', "D'Aranda", 'Turgot', 'RICHELIEU', 'Sidney', 'Franklin', 'Washington', 'Jefferson', 'Bolivar', 'Francia', 'CROMWELL', 'Copernicus', 'Kepler', 'Huyghens', '<NAME>', 'Bradley', 'Volta', 'GALILEO', 'Vieta', 'Wallis', 'Clairaut', 'Euler', "D'Alembert", 'Lagrange', 'NEWTON', 'Bergmann', 'Priestley', 'Cavendish', '<NAME>', 'Berthollet', 'Berzelius', 'LAVOISIER', 'Harvey', 'Bo\u0235rhaave', 'Linn\u0230us', 'Haller', 'Lamarck', 'Broussais', 'GALL', 'Festival of All the Dead', 'Festival of Holy Women')
		calendar.LEAPSAINTS = ('Cadmus', 'Theseus', 'Tiresias', '', '', '', '', 'Semiramus', '', '', '', '', 'Ossian', '', '', '', '', '', '', 'Tameha-meha', '', 'Joseph', '', 'David', '', '', 'Abderrah-man', '', '', 'Sappho', '', '', 'Euripides', 'Longus', '', '', '', '', '', '', '', '', 'Pilpay', '', 'Menander', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'Leucippus', '', '', '', '', '', '', 'Philolaus', '', '', '', '', '', '<NAME>', 'Arrian', '', '', '', '', '', 'St. Iren&#230us', '', 'Tertullian', '', '', '', '', '', '', 'Averrhoes', '', '', '', '', 'Ctesibius', '', '', '', 'Aratus', 'Nearchus', 'Berosus', 'Sosigenes', '', 'Nasir-Eddin', '', '', '', '', '', '', '', '', '', '', '', '', '', 'Epaminon-das', '', '', '', '', '', '', '', '', '', 'Cincinnatus', 'Regulus', '', '', 'The Gracchi', '', 'M&#230cenas', 'Titus', 'Nerva', '<NAME>', 'Ulpian', 'Aetius', '', 'St. James', '', '', '', '', '', '', '', '', 'St. Basil', '', 'Marcian', '', '', 'St. Anthony', 'St. Austin', 'St. Bruno', 'St. Anselm', 'Beatrice', 'St. Benezet', '', '<NAME>', '<NAME>', 'St.Catharineof Siena', "Abb\u00E9 de l'Ep\u00E9e", 'ClaudeFleury', '<NAME>', '', '', '', '<NAME>', '', '<NAME>', 'JohnSobieski', '', '', 'Tancred', 'Saladin', 'Marina', 'Sir <NAME>', '', '', '<NAME>', 'PeterDamian', '', 'St. Eligius', 'Becket', 'St. Dominic', '', '', 'St. Mathilda of Tuscany', '<NAME>', '', '', '<NAME>', '', '', 'Chaucer', 'Swift', '', 'Burns', 'Goldsmith', '', 'Titian', '<NAME>', 'Rembrandt', 'Lesueuer', 'Murillo', 'Rubens', '', 'Joinville', 'Spenser', '', '', '<NAME>', '', '', '', 'L. of Grana-da & Bunyan', 'Mme. de Sta\u00EBl', 'St. Francisof Sales', 'Gessner', '\u00E9. Mercoeur & Shelly', '', 'Chardin', 'Gresham', 'Magellan', 'Briggs', 'Delambre', 'Tasman', '', '', 'Wheatstone', '<NAME>', 'Graham', 'Jacquard', '', '', 'Torricelli', 'Boyle', 'Worcester', '', 'Fulton', 'Thilorier', '', '', 'Riquet', 'Bourgelat', 'Bouguer', 'Borda', 'Vauban', '', 'Montalvan', '<NAME>', 'Guevara', '', '', '', '', '', '', '', '', 'Alfieri', '', '', '', 'Mme. Roland', '<NAME>', 'Sterne', '<NAME>', 'Richardson', '', 'Palestrina', 'Gr\u00E9try', 'Lully', 'Handel', 'Weber', 'Donizeti', '', '<NAME>', '<NAME>', 'Joachim', 'The Cardinal of Cusa', 'Erasmus', '<NAME>', '', 'Spinoza', '<NAME>', 'Male-branche', 'Mme. de Lambert', 'Duclos', 'GeorgeLeroy', '', 'Cujas', 'Maupertuis', 'Herder', 'Winckle-mann', "D'Aguesseau", 'Oken', '', 'Gibbon', 'Dunoyer', 'Fichte', 'Ferguson', 'Bonald', '<NAME>', '', '', '', 'Guicciardini', '', 'Sixtus V', '', '', '', '', '', '', '', '', '', '', 'Oxenstiern', 'Walpole', 'Louis XIV', 'Pombal', 'Campo-manes', '', 'Lambert', 'Hampden', 'Kosciusko', 'Madison', "<NAME>", '', '', '<NAME>\u00E9', 'Halley', 'Varignon', '<NAME>', 'R\u00F5mer', 'Sauveur', '', 'Harriot', 'Fermat', 'Poinsot', 'Monge', '<NAME>', '<NAME>', '', 'Scheele', 'Davy', '', 'Geoffroy', '', 'Ritter', '', '<NAME>', '<NAME>', '<NAME>', "Vicq-d'Azyr", 'Blainville', 'Morgagni', '', '', 'Festival of Holy Women')



		class AlternateDate(object):
			def __init__(self, year, month, day, calendar):
				self.year = year
				self.month = month
				self.day = day
				self.day_of_year = (month - 1) * calendar.weeks_in_a_month * calendar.days_in_a_week + day
				if self.day_of_year > 366:
					raise ValueError("This day cannot exist.")

				self.calendar = calendar
				self.is_leap = calendar.is_leap(self.to_gregorian().year)
								
				self.weekday = calendar.get_weekday(self.day)
				self.month_name = calendar.get_month_name(self.month)
				self.day_name = calendar.get_day_name(self.day_of_year, self.is_leap)
				self.weekday_name = calendar.get_weekday_name(self.weekday)
				
			def total_days(self):
				year = self.year + calendar.year_offset
				return datetime.date(year, 1, 1).toordinal() + self.day_of_year - 1

			def to_gregorian(self):
				return datetime.date.fromordinal(self.total_days())
			
			def __str__(self):
				if self.month is calendar.months_in_a_year + 1:
					return "{}, {}".format(self.day_name, self.year)
				else:
					return "{}, {} of {}, {}: {}".format(self.weekday_name, ordinal(self.day), self.month_name, 
												    self.year, self.day_name)
				
			def __repr__(self):
				return '%s date(%d, %d, %d)' % (calendar.name.lower(), self.year, self.month, self.day)
			
			def __add__(self, timedelta):
				return self.calendar.from_date((self.to_gregorian() + timedelta))
			
			def __sub__(self, arg):
				if isinstance(arg, datetime.timedelta):
					return self.calendar.from_date(self.to_gregorian() - arg)
				elif "AlternateDate" in str(type(arg)):
					return datetime.timedelta(days=self.total_days() - arg.total_days())

			def __eq__(self, other_date):
				othercast = (other_date.to_gregorian())
				return self.to_gregorian() == othercast
			
			def __gt__(self, other_date):
				othercast = (other_date.to_gregorian())
				return self.to_gregorian() > othercast
			
			def __lt__(self, other_date):
				othercast = (other_date.to_gregorian())
				return self.to_gregorian() < othercast	
			
			def __ge__(self, other_date):
				othercast = (other_date.to_gregorian())
				return self.to_gregorian() >= othercast
				
			def __le__(self, other_date):
				othercast = (other_date.to_gregorian())
				return self.to_gregorian() <= othercast

		calendar.date_class = AlternateDate

	# create a date from gregorian date
	def from_date(self, *args):
		if not args:
			gregorian = datetime.date.today()
		else:
			try:
				year, month, day = args
				gregorian = datetime.date(year, month, day)
				# return self.date_class(datetime.date(year, month, day), self)
			except ValueError:
				gregorian = args[0]
					# return self.date_class(args[0], self)
		year = gregorian.year - self.year_offset
		day_of_year = gregorian.timetuple().tm_yday
		month = ((day_of_year - 1) // self.days_in_a_month) + 1
		day = day_of_year % self.days_in_a_month or self.days_in_a_month
		return self.date_class(year, month, day, self)

	# create "native" date
	def date(self, *args):
		if not args:
			return self.from_date()
		else:
			year, month, day = args
			return self.date_class(year, month, day, self)
	
	def get_weekday(self, day):
		return day % self.days_in_a_week or self.days_in_a_week
	def get_weekday_name(self, weekday):
		if weekday > len(self.DAYS):
			return ordinal(weekday) + " Weekday"
		else:
			return self.DAYS[weekday - 1]				
	def get_month_name(self, month):
		if month > len(self.MONTHS):
			return ordinal(month) + " Month"
		else:
			return self.MONTHS[(month - 1)]
	def get_day_name(self, day, is_leap):
		if day > len(self.SAINTS):
			return ""
		elif is_leap and self.LEAPSAINTS[day - 1]:
			return self.LEAPSAINTS[day - 1]
		else:
			return self.SAINTS[day - 1]
	def is_leap(self, year):
		return calendar.isleap(year + self.year_offset)

	def print_month(self, year=None, month=None):

		maxwidth = max(max(map(len, self.SAINTS)),(max(map(len, self.LEAPSAINTS))))
		if not year or not month:
			today = self.date()
			year = today.year
			month = today.month
		if month == self.months_in_a_year + 1:
			intercal = True
			print("THE {} INTERCALARY DAYS OF THE YEAR {}".format(self.name.upper(), year))
		else:
			intercal = False
			print("THE {} MONTH OF {}, {}".format(self.name.upper(), self.get_month_name(month).upper(), year))
	
		month_offset = (month - 1) * self.weeks_in_a_month * self.days_in_a_week	
		for week in range(self.weeks_in_a_month):		
			calbox = []
			week_offset = (week * self.days_in_a_week)
			for date in range(1 + week_offset, week_offset + self.days_in_a_week + 1):
				day_of_year = month_offset + date
				if day_of_year > len(self.SAINTS):
					break
				if not intercal:
					weekday = self.get_weekday(date)
					wkd_name = self.get_weekday_name(weekday)
					datestr = str(date)
				else:
					weekday = 0
					wkd_name = ""
					datestr = ""
				saint = self.get_day_name(day_of_year, self.is_leap(year))
				
				height = 4
				box = []
				box.append("+".ljust(maxwidth + 1, '-'))
				if wkd_name:
					box.append("|" + wkd_name.ljust(maxwidth - len(datestr)) + datestr)
				else:
					box.append("|".ljust(maxwidth + 1))
				for i in range(height):
					box.append("|".ljust(maxwidth + 1))
				box.append("|" + saint.rjust(maxwidth))

				calbox.append(box)
			
			cal_layout = horicat(calbox)
			print(cal_layout)

	def __str__(self):
		return "The %s calendar, consisting of %d-day weeks, %d-week months, and %d-month years, with %d intercalary day(s)." % (self.name, self.days_in_a_week, self.days_in_a_month // self.days_in_a_week, self.months_in_a_year, self.intercalary_days)

if __name__ == "__main__":	
	import doctest
	doctest.testmod()
	

{'fraction_non_alphanumeric': 0.22265403126961825, 'fraction_numerical': 0.017817105617171387, 'mean_word_length': 4.994982078853047, 'pattern_counts': {'":': 2, '<': 141, '<?xml version=': 0, '>': 261, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 54, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '163772', 'n_tokens_mistral': 14560, 'n_tokens_neox': 13023, 'n_words': 2869}