Comment fixes.
[bpt/emacs.git] / lisp / calendar / cal-mayan.el
1 ;;; cal-mayan.el --- calendar functions for the Mayan calendars.
2
3 ;; Copyright (C) 1992, 1993, 1995 Free Software Foundation, Inc.
4
5 ;; Author: Stewart M. Clamen <clamen@cs.cmu.edu>
6 ;; Edward M. Reingold <reingold@cs.uiuc.edu>
7 ;; Keywords: calendar
8 ;; Human-Keywords: Mayan calendar, Maya, calendar, diary
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to
24 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25
26 ;;; Commentary:
27
28 ;; This collection of functions implements the features of calendar.el and
29 ;; diary.el that deal with the Mayan calendar. It was written jointly by
30
31 ;; Stewart M. Clamen School of Computer Science
32 ;; clamen@cs.cmu.edu Carnegie Mellon University
33 ;; 5000 Forbes Avenue
34 ;; Pittsburgh, PA 15213
35
36 ;; and
37
38 ;; Edward M. Reingold Department of Computer Science
39 ;; (217) 333-6733 University of Illinois at Urbana-Champaign
40 ;; reingold@cs.uiuc.edu 1304 West Springfield Avenue
41 ;; Urbana, Illinois 61801
42
43 ;; Comments, improvements, and bug reports should be sent to Reingold.
44
45 ;; Technical details of the Mayan calendrical calculations can be found in
46 ;; ``Calendrical Calculations, Part II: Three Historical Calendars''
47 ;; by E. M. Reingold, N. Dershowitz, and S. M. Clamen,
48 ;; Software--Practice and Experience, Volume 23, Number 4 (April, 1993),
49 ;; pages 383-404.
50
51 ;;; Code:
52
53 (require 'calendar)
54
55 (defconst calendar-mayan-days-before-absolute-zero 1137140
56 "Number of days of the Mayan calendar epoch before absolute day 0.
57 According to the Goodman-Martinez-Thompson correlation. This correlation is
58 not universally accepted, as it still a subject of astro-archeological
59 research. Using 1232041 will give you Spinden's correlation; using
60 1142840 will give you Hochleitner's correlation.")
61
62 (defconst calendar-mayan-haab-at-epoch '(8 . 18)
63 "Mayan haab date at the epoch.")
64
65 (defconst calendar-mayan-haab-month-name-array
66 ["Pop" "Uo" "Zip" "Zotz" "Tzec" "Xul" "Yaxkin" "Mol" "Chen" "Yax"
67 "Zac" "Ceh" "Mac" "Kankin" "Muan" "Pax" "Kayab" "Cumku"])
68
69 (defconst calendar-mayan-tzolkin-at-epoch '(4 . 20)
70 "Mayan tzolkin date at the epoch.")
71
72 (defconst calendar-mayan-tzolkin-names-array
73 ["Imix" "Ik" "Akbal" "Kan" "Chicchan" "Cimi" "Manik" "Lamat" "Muluc" "Oc"
74 "Chuen" "Eb" "Ben" "Ix" "Men" "Cib" "Caban" "Etznab" "Cauac" "Ahau"])
75
76 (defun calendar-mayan-long-count-from-absolute (date)
77 "Compute the Mayan long count corresponding to the absolute DATE."
78 (let ((long-count (+ date calendar-mayan-days-before-absolute-zero)))
79 (let* ((baktun (/ long-count 144000))
80 (remainder (% long-count 144000))
81 (katun (/ remainder 7200))
82 (remainder (% remainder 7200))
83 (tun (/ remainder 360))
84 (remainder (% remainder 360))
85 (uinal (/ remainder 20))
86 (kin (% remainder 20)))
87 (list baktun katun tun uinal kin))))
88
89 (defun calendar-mayan-long-count-to-string (mayan-long-count)
90 "Convert MAYAN-LONG-COUNT into traditional written form."
91 (apply 'format (cons "%s.%s.%s.%s.%s" mayan-long-count)))
92
93 (defun calendar-string-to-mayan-long-count (str)
94 "Given STR, a string of format \"%d.%d.%d.%d.%d\", return list of nums."
95 (let ((rlc nil)
96 (c (length str))
97 (cc 0))
98 (condition-case condition
99 (progn
100 (while (< cc c)
101 (let* ((start (string-match "[0-9]+" str cc))
102 (end (match-end 0))
103 datum)
104 (setq datum (read (substring str start end)))
105 (setq rlc (cons datum rlc))
106 (setq cc end)))
107 (if (not (= (length rlc) 5)) (signal 'invalid-read-syntax nil)))
108 (invalid-read-syntax nil))
109 (reverse rlc)))
110
111 (defun calendar-mayan-haab-from-absolute (date)
112 "Convert absolute DATE into a Mayan haab date (a pair)."
113 (let* ((long-count (+ date calendar-mayan-days-before-absolute-zero))
114 (day-of-haab
115 (% (+ long-count
116 (car calendar-mayan-haab-at-epoch)
117 (* 20 (1- (cdr calendar-mayan-haab-at-epoch))))
118 365))
119 (day (% day-of-haab 20))
120 (month (1+ (/ day-of-haab 20))))
121 (cons day month)))
122
123 (defun calendar-mayan-haab-difference (date1 date2)
124 "Number of days from Mayan haab DATE1 to next occurrence of haab date DATE2."
125 (mod (+ (* 20 (- (cdr date2) (cdr date1)))
126 (- (car date2) (car date1)))
127 365))
128
129 (defun calendar-mayan-haab-on-or-before (haab-date date)
130 "Absolute date of latest HAAB-DATE on or before absolute DATE."
131 (- date
132 (% (- date
133 (calendar-mayan-haab-difference
134 (calendar-mayan-haab-from-absolute 0) haab-date))
135 365)))
136
137 (defun calendar-next-haab-date (haab-date &optional noecho)
138 "Move cursor to next instance of Mayan HAAB-DATE.
139 Echo Mayan date if NOECHO is t."
140 (interactive (list (calendar-read-mayan-haab-date)))
141 (calendar-goto-date
142 (calendar-gregorian-from-absolute
143 (calendar-mayan-haab-on-or-before
144 haab-date
145 (+ 365
146 (calendar-absolute-from-gregorian (calendar-cursor-to-date))))))
147 (or noecho (calendar-print-mayan-date)))
148
149 (defun calendar-previous-haab-date (haab-date &optional noecho)
150 "Move cursor to previous instance of Mayan HAAB-DATE.
151 Echo Mayan date if NOECHO is t."
152 (interactive (list (calendar-read-mayan-haab-date)))
153 (calendar-goto-date
154 (calendar-gregorian-from-absolute
155 (calendar-mayan-haab-on-or-before
156 haab-date
157 (1- (calendar-absolute-from-gregorian (calendar-cursor-to-date))))))
158 (or noecho (calendar-print-mayan-date)))
159
160 (defun calendar-mayan-haab-to-string (haab)
161 "Convert Mayan haab date (a pair) into its traditional written form."
162 (let ((month (cdr haab))
163 (day (car haab)))
164 ;; 19th month consists of 5 special days
165 (if (= month 19)
166 (format "%d Uayeb" day)
167 (format "%d %s"
168 day
169 (aref calendar-mayan-haab-month-name-array (1- month))))))
170
171 (defun calendar-mayan-tzolkin-from-absolute (date)
172 "Convert absolute DATE into a Mayan tzolkin date (a pair)."
173 (let* ((long-count (+ date calendar-mayan-days-before-absolute-zero))
174 (day (calendar-mod
175 (+ long-count (car calendar-mayan-tzolkin-at-epoch))
176 13))
177 (name (calendar-mod
178 (+ long-count (cdr calendar-mayan-tzolkin-at-epoch))
179 20)))
180 (cons day name)))
181
182 (defun calendar-mayan-tzolkin-difference (date1 date2)
183 "Number of days from Mayan tzolkin DATE1 to next occurrence of tzolkin DATE2."
184 (let ((number-difference (- (car date2) (car date1)))
185 (name-difference (- (cdr date2) (cdr date1))))
186 (mod (+ number-difference
187 (* 13 (mod (* 3 (- number-difference name-difference))
188 20)))
189 260)))
190
191 (defun calendar-mayan-tzolkin-on-or-before (tzolkin-date date)
192 "Absolute date of latest TZOLKIN-DATE on or before absolute DATE."
193 (- date
194 (% (- date (calendar-mayan-tzolkin-difference
195 (calendar-mayan-tzolkin-from-absolute 0)
196 tzolkin-date))
197 260)))
198
199 (defun calendar-next-tzolkin-date (tzolkin-date &optional noecho)
200 "Move cursor to next instance of Mayan TZOLKIN-DATE.
201 Echo Mayan date if NOECHO is t."
202 (interactive (list (calendar-read-mayan-tzolkin-date)))
203 (calendar-goto-date
204 (calendar-gregorian-from-absolute
205 (calendar-mayan-tzolkin-on-or-before
206 tzolkin-date
207 (+ 260
208 (calendar-absolute-from-gregorian (calendar-cursor-to-date))))))
209 (or noecho (calendar-print-mayan-date)))
210
211 (defun calendar-previous-tzolkin-date (tzolkin-date &optional noecho)
212 "Move cursor to previous instance of Mayan TZOLKIN-DATE.
213 Echo Mayan date if NOECHO is t."
214 (interactive (list (calendar-read-mayan-tzolkin-date)))
215 (calendar-goto-date
216 (calendar-gregorian-from-absolute
217 (calendar-mayan-tzolkin-on-or-before
218 tzolkin-date
219 (1- (calendar-absolute-from-gregorian (calendar-cursor-to-date))))))
220 (or noecho (calendar-print-mayan-date)))
221
222 (defun calendar-mayan-tzolkin-to-string (tzolkin)
223 "Convert Mayan tzolkin date (a pair) into its traditional written form."
224 (format "%d %s"
225 (car tzolkin)
226 (aref calendar-mayan-tzolkin-names-array (1- (cdr tzolkin)))))
227
228 (defun calendar-mayan-tzolkin-haab-on-or-before (tzolkin-date haab-date date)
229 "Absolute date that is Mayan TZOLKIN-DATE and HAAB-DATE.
230 Latest such date on or before DATE.
231 Returns nil if such a tzolkin-haab combination is impossible."
232 (let* ((haab-difference
233 (calendar-mayan-haab-difference
234 (calendar-mayan-haab-from-absolute 0)
235 haab-date))
236 (tzolkin-difference
237 (calendar-mayan-tzolkin-difference
238 (calendar-mayan-tzolkin-from-absolute 0)
239 tzolkin-date))
240 (difference (- tzolkin-difference haab-difference)))
241 (if (= (% difference 5) 0)
242 (- date
243 (mod (- date
244 (+ haab-difference (* 365 difference)))
245 18980))
246 nil)))
247
248 (defun calendar-read-mayan-haab-date ()
249 "Prompt for a Mayan haab date"
250 (let* ((completion-ignore-case t)
251 (haab-day (calendar-read
252 "Haab kin (0-19): "
253 '(lambda (x) (and (>= x 0) (< x 20)))))
254 (haab-month-list (append calendar-mayan-haab-month-name-array
255 (and (< haab-day 5) '("Uayeb"))))
256 (haab-month (cdr
257 (assoc
258 (capitalize
259 (completing-read "Haab uinal: "
260 (mapcar 'list haab-month-list)
261 nil t))
262 (calendar-make-alist
263 haab-month-list 1 'capitalize)))))
264 (cons haab-day haab-month)))
265
266 (defun calendar-read-mayan-tzolkin-date ()
267 "Prompt for a Mayan tzolkin date"
268 (let* ((completion-ignore-case t)
269 (tzolkin-count (calendar-read
270 "Tzolkin kin (1-13): "
271 '(lambda (x) (and (> x 0) (< x 14)))))
272 (tzolkin-name-list (append calendar-mayan-tzolkin-names-array nil))
273 (tzolkin-name (cdr
274 (assoc
275 (capitalize
276 (completing-read "Tzolkin uinal: "
277 (mapcar 'list tzolkin-name-list)
278 nil t))
279 (calendar-make-alist
280 tzolkin-name-list 1 'capitalize)))))
281 (cons tzolkin-count tzolkin-name)))
282
283 (defun calendar-next-calendar-round-date
284 (tzolkin-date haab-date &optional noecho)
285 "Move cursor to next instance of Mayan HAAB-DATE TZOLKIN-DATE combination.
286 Echo Mayan date if NOECHO is t."
287 (interactive (list (calendar-read-mayan-tzolkin-date)
288 (calendar-read-mayan-haab-date)))
289 (let ((date (calendar-mayan-tzolkin-haab-on-or-before
290 tzolkin-date haab-date
291 (+ 18980 (calendar-absolute-from-gregorian
292 (calendar-cursor-to-date))))))
293 (if (not date)
294 (error "%s, %s does not exist in the Mayan calendar round"
295 (calendar-mayan-tzolkin-to-string tzolkin-date)
296 (calendar-mayan-haab-to-string haab-date))
297 (calendar-goto-date (calendar-gregorian-from-absolute date))
298 (or noecho (calendar-print-mayan-date)))))
299
300 (defun calendar-previous-calendar-round-date
301 (tzolkin-date haab-date &optional noecho)
302 "Move to previous instance of Mayan TZOLKIN-DATE HAAB-DATE combination.
303 Echo Mayan date if NOECHO is t."
304 (interactive (list (calendar-read-mayan-tzolkin-date)
305 (calendar-read-mayan-haab-date)))
306 (let ((date (calendar-mayan-tzolkin-haab-on-or-before
307 tzolkin-date haab-date
308 (1- (calendar-absolute-from-gregorian
309 (calendar-cursor-to-date))))))
310 (if (not date)
311 (error "%s, %s does not exist in the Mayan calendar round"
312 (calendar-mayan-tzolkin-to-string tzolkin-date)
313 (calendar-mayan-haab-to-string haab-date))
314 (calendar-goto-date (calendar-gregorian-from-absolute date))
315 (or noecho (calendar-print-mayan-date)))))
316
317 (defun calendar-absolute-from-mayan-long-count (c)
318 "Compute the absolute date corresponding to the Mayan Long Count C.
319 Long count is a list (baktun katun tun uinal kin)"
320 (+ (* (nth 0 c) 144000) ; baktun
321 (* (nth 1 c) 7200) ; katun
322 (* (nth 2 c) 360) ; tun
323 (* (nth 3 c) 20) ; uinal
324 (nth 4 c) ; kin (days)
325 (- ; days before absolute date 0
326 calendar-mayan-days-before-absolute-zero)))
327
328 (defun calendar-mayan-date-string (&optional date)
329 "String of Mayan date of Gregorian DATE.
330 Defaults to today's date if DATE is not given."
331 (let* ((d (calendar-absolute-from-gregorian
332 (or date (calendar-current-date))))
333 (tzolkin (calendar-mayan-tzolkin-from-absolute d))
334 (haab (calendar-mayan-haab-from-absolute d))
335 (long-count (calendar-mayan-long-count-from-absolute d)))
336 (format "Long count = %s; tzolkin = %s; haab = %s"
337 (calendar-mayan-long-count-to-string long-count)
338 (calendar-mayan-tzolkin-to-string tzolkin)
339 (calendar-mayan-haab-to-string haab))))
340
341 (defun calendar-print-mayan-date ()
342 "Show the Mayan long count, tzolkin, and haab equivalents of date."
343 (interactive)
344 (message "Mayan date: %s"
345 (calendar-mayan-date-string (calendar-cursor-to-date t))))
346
347 (defun calendar-goto-mayan-long-count-date (date &optional noecho)
348 "Move cursor to Mayan long count DATE. Echo Mayan date unless NOECHO is t."
349 (interactive
350 (let (lc)
351 (while (not lc)
352 (let ((datum
353 (calendar-string-to-mayan-long-count
354 (read-string "Mayan long count (baktun.katun.tun.uinal.kin): "
355 (calendar-mayan-long-count-to-string
356 (calendar-mayan-long-count-from-absolute
357 (calendar-absolute-from-gregorian
358 (calendar-current-date))))))))
359 (if (calendar-mayan-long-count-common-era datum)
360 (setq lc datum))))
361 (list lc)))
362 (calendar-goto-date
363 (calendar-gregorian-from-absolute
364 (calendar-absolute-from-mayan-long-count date)))
365 (or noecho (calendar-print-mayan-date)))
366
367 (defun calendar-mayan-long-count-common-era (lc)
368 "T if long count represents date in the Common Era."
369 (let ((base (calendar-mayan-long-count-from-absolute 1)))
370 (while (and (not (null base)) (= (car lc) (car base)))
371 (setq lc (cdr lc)
372 base (cdr base)))
373 (or (null lc) (> (car lc) (car base)))))
374
375 (defun diary-mayan-date ()
376 "Show the Mayan long count, haab, and tzolkin dates as a diary entry."
377 (format "Mayan date: %s" (calendar-mayan-date-string date)))
378
379 (provide 'cal-mayan)
380
381 ;;; cal-mayan.el ends here