Functions for working with the idealized calendar of Planet Xhilr
Revisión | 27fcb962ebed1ab6f8c7122001175fd7cf3d926b (tree) |
---|---|
Tiempo | 2017-06-13 17:05:27 |
Autor | Joel Matthew Rees <joel.rees@gmai...> |
Commiter | Joel Matthew Rees |
starting in C
@@ -0,0 +1,535 @@ | ||
1 | +/* This is a program to generate calendars | |
2 | +// for novels about Bobbie, Karel, Dan, and Kristie's world, | |
3 | +// specifically those in the Economics 101 trilogy. | |
4 | +// | |
5 | +// By Joel Matthew Rees, March 2017, | |
6 | +// Copyright 2017, all rights reserved. | |
7 | +// | |
8 | +// Fair use acknowledged. | |
9 | +// If you want to use it for purposes other than personal entertainment, | |
10 | +// rewriting it yourself is not that hard, | |
11 | +// you'll understand the math and the science much better that way, | |
12 | +// and the result will satisfy your needs much more effectively. | |
13 | +// | |
14 | +// See | |
15 | +// http://joel-rees-economics.blogspot.com/2017/03/soc500-03-08-calendar-math.html | |
16 | +// http://joel-rees-economics.blogspot.com/2017/03/soc500-03-09-computer-calendar.html | |
17 | +// for more explanation of the code. | |
18 | +// | |
19 | +// Novel here: | |
20 | +// http://joel-rees-economics.blogspot.com/2017/01/soc500-00-00-toc.html | |
21 | +// | |
22 | +// From the intermediate text of the second draft of Sociology 500, a Novel, | |
23 | +// chapter 3 part 7, Family Games: | |
24 | +// ************************* | |
25 | +// Uhm, lemmesee. Where did I put those notes? Oh, here they are. | |
26 | +// 353 days per year in about five years out of seven | |
27 | +// and 352 in the other two. | |
28 | +// So they have two skip years out of seven | |
29 | +// where we have one leap year out of four. | |
30 | +// | |
31 | +// But it isn't exact, of course. | |
32 | +// They have to adjust that again every 98 years | |
33 | +// and then once again every 343 years. | |
34 | +// Already too much detail, and you're falling asleep? | |
35 | +// | |
36 | +// But they have two moons. Did I mention that? | |
37 | +// The smaller moon orbits their earth | |
38 | +// in just under seven and an eighth days, | |
39 | +// and their larger moon orbits it | |
40 | +// in about twenty-eight and seven eighths days. | |
41 | +// About forty-nine and a half lunar weeks a year | |
42 | +// and about twelve and two fifths lunar months each year. | |
43 | +// ************************* | |
44 | +// | |
45 | +// From the tool, econmonths.bc: | |
46 | +// ************************* | |
47 | +// # If you want to do something similar, | |
48 | +// # for looking at how leap years in your world | |
49 | +// # match the actual orbits and revolutions | |
50 | +// # of your world and its moon, | |
51 | +// # replace isskip() with an appropriate isleap(). | |
52 | +// # Left as an exercise for the reader. | |
53 | +// | |
54 | +// define isskip( y, m ) { | |
55 | +// if ( m != 0 ) { | |
56 | +// return 0; | |
57 | +// } | |
58 | +// mem = scale; | |
59 | +// scale = 0; | |
60 | +// diff7 = y % 7; | |
61 | +// diff98 = y % 98; | |
62 | +// diff343 = y % 343; | |
63 | +// scale = mem; | |
64 | +// if ( diff98 == 48 ) { | |
65 | +// return 1; | |
66 | +// } | |
67 | +// if ( ( diff7 != 1 ) && ( diff7 != 4 ) ) { | |
68 | +// return 0; | |
69 | +// } | |
70 | +// if ( diff343 == 186 ) { | |
71 | +// return 0; | |
72 | +// } | |
73 | +// return 1; | |
74 | +// } | |
75 | +// | |
76 | +// | |
77 | +// # Note that we are treating the first year as year 0 | |
78 | +// # and the first month as month 0. | |
79 | +// # For your earth, you will need to adjust the year and month input and output. | |
80 | +// ************************* | |
81 | +// | |
82 | +// Note also that, in this calendar system, | |
83 | +// the day in the month and the day in the year start with day 0. | |
84 | +// | |
85 | +// | |
86 | +// Their two moons were at near zenith on the night their Christ was born, | |
87 | +// in addition to the new star, but that's all I'm telling for now. | |
88 | +// | |
89 | +*/ | |
90 | + | |
91 | + | |
92 | +#include <stdio.h> | |
93 | +#include <stdlib.h> | |
94 | +#include <string.h> | |
95 | + | |
96 | +#include <limits.h> | |
97 | +#if INT_MAX < 0x8000L /* sloppy 8 bit thinking */ | |
98 | +# define INT_BITS 16 | |
99 | +#elif INT_MAX < 0x80000000L | |
100 | +# define INT_BITS 32 | |
101 | +#elif INT_MAX < 0x8000000000000000L | |
102 | +# define INT_BITS 64 | |
103 | +#else | |
104 | +# warning "INT_BITS greater than 64!!" | |
105 | +#endif | |
106 | + | |
107 | + | |
108 | +typedef unsigned char ubyte_t; | |
109 | +typedef signed char sbyte_t; | |
110 | + | |
111 | + | |
112 | +#define SPACE ' ' | |
113 | + | |
114 | + | |
115 | +#define SKIPyears_per_short 2 | |
116 | +#define FULLyears_per_short 5 | |
117 | +#define YEARcycle_short 7 | |
118 | +#define YEARcycle_medium 98 /* 7^2 * 2 */ | |
119 | +#define YEARcycle_long 343 /* 7^3 */ | |
120 | +#define YEARcycle_dbllong 686 /* 7^3 * 2 */ | |
121 | + | |
122 | +#define FULLYEARS_dbllongcycle \ | |
123 | +( ( YEARcycle_dbllong / YEARcycle_short ) * FULLyears_per_short \ | |
124 | +- ( YEARcycle_dbllong / YEARcycle_medium ) * 1 + ( YEARcycle_dbllong / YEARcycle_long ) * 1 ) | |
125 | + | |
126 | +#define SKIPYEARS_dbllongcycle ( YEARcycle_dbllong - FULLYEARS_dbllongcycle ) | |
127 | + | |
128 | + | |
129 | +/* C double is (barely) sufficient resolution to avoid most of the tricks. */ | |
130 | +#define YEARfraction ( (double) FULLYEARS_dbllongcycle / YEARcycle_dbllong ) | |
131 | +#define DAYSperYEARint 352 | |
132 | +#define DAYSperYEAR ( (double) DAYSperYEARint + YEARfraction ) | |
133 | +#define DAYSperWEEK 7 | |
134 | +#define MONTHSperYEAR 12 | |
135 | + | |
136 | +/* Do it in integers. | |
137 | +*/ | |
138 | +#define daysUPTOyear( year ) \ | |
139 | +( tdays = (year) * DAYSperYEARint + ( ( year ) * FULLYEARS_dbllongcycle ) / YEARcycle_dbllong ) | |
140 | + | |
141 | +#define remainderUPTOyear( year ) \ | |
142 | +( ( (year) * FULLYEARS_dbllongcycle ) % YEARcycle_dbllong ) | |
143 | + | |
144 | + | |
145 | +#define SKIPmonth 0 | |
146 | +#define SKIPyear_short_1 1 | |
147 | +#define SKIPyear_short_2 4 | |
148 | +#define SKIPyear_medium 48 | |
149 | +#define SKIPyear_long 186 /* Must be short1 or short2 within the seven year cycle. */ | |
150 | + | |
151 | + | |
152 | +/* Since skipyears are the exception, we test for skipyears instead of leapyears. | |
153 | +// Calendar system starts with year 0, not year 1. | |
154 | +// Would need to check and adjust if the calendar started with year 1. | |
155 | +*/ | |
156 | +int isskipyr( long year ) | |
157 | +{ int rem = year % YEARcycle_medium; | |
158 | + if ( rem == SKIPyear_medium ) | |
159 | + { return 1; | |
160 | + } | |
161 | + rem = year % YEARcycle_short; | |
162 | + if ( ( rem != SKIPyear_small_1 ) && ( rem != SKIPyear_small_2 ) ) | |
163 | + { return 0; | |
164 | + } | |
165 | + rem = year % YEARcycle_long; | |
166 | + if ( rem == SKIPyear_long ) /* Must be short1 or short2 within the seven year cycle. */ | |
167 | + { return 0; | |
168 | + } | |
169 | + return 1; | |
170 | +} | |
171 | + | |
172 | + | |
173 | +ubyte_t daysInMonths[ MONTHSperYEAR ] = | |
174 | +{ 30, | |
175 | + 29, | |
176 | + 30, | |
177 | + 29, | |
178 | + 29, | |
179 | + 30, | |
180 | + 29, | |
181 | + 30, | |
182 | + 29, | |
183 | + 29, | |
184 | + 30, | |
185 | + 29 | |
186 | +}; | |
187 | + | |
188 | +#define daysInMonth( month, year ) \ | |
189 | +( ( month != SKIPmonth ) ? daysInMonths[ (month) ] : daysInMonths[ (month) ] - ( isskipyr( (year) ) ? 1 : 0 ) ) | |
190 | + | |
191 | +/* months are 0 to MONTHSperYEAR - 1 here. | |
192 | +// If months were 1 to MONTHSperYEAR, would need to adjust here. | |
193 | +*/ | |
194 | +int yearDaysToMonth( int month, long year ) | |
195 | +{ unsigned total; | |
196 | + int i; | |
197 | +/* */ | |
198 | + if ( month >= MONTHSperYEAR ) | |
199 | + { month = MONTHSperYEAR - 1; | |
200 | + } | |
201 | + for ( total = 0, i = 0; i < month; ++i ) | |
202 | + { total += daysInMonth( i, year ); | |
203 | + } | |
204 | + return total; | |
205 | +} | |
206 | + | |
207 | + | |
208 | +/* day in month is 0 to max - 1 here. | |
209 | +// If day in month were 1 to max, would need to adjust here. | |
210 | +*/ | |
211 | +unsigned long daysUpTo( long year, int month, int day, unsigned * remainderp ) | |
212 | +{ unsigned long days = daysUPTOyear( year ); | |
213 | + unsigned partial = remainderUPTOyear( year ); | |
214 | +/* */ | |
215 | + days += | |
216 | + * remainderp = partial; | |
217 | + return days; | |
218 | +} | |
219 | + | |
220 | + | |
221 | +#define PLANETname "Bokadakr" | |
222 | + | |
223 | +typedef char * str0_t; /* It looks like the pedants have attacked again. */ | |
224 | +typedef str0_t str0array_t[]; /* Or I'm losing my memory again. */ | |
225 | +typedef str0array_t * str0arrayptr_t; /* Wonder which. */ | |
226 | + | |
227 | +str0array_t weekDayNames = | |
228 | +{ "Sunday", | |
229 | + "Slowmoonday", | |
230 | + "Aegisday", | |
231 | + "Gefnday", | |
232 | + "Freyday", | |
233 | + "Tewesday", | |
234 | + "Vensday" | |
235 | +}; | |
236 | + | |
237 | +str0array_t fakeDayNames = | |
238 | +{ "Sunday", | |
239 | + "Monday", | |
240 | + "Tuesday", | |
241 | + "Wednesday", | |
242 | + "Thursday", | |
243 | + "Friday", | |
244 | + "Saturday" | |
245 | +}; | |
246 | + | |
247 | +str0array_t fakeMonthNames = | |
248 | +{ "January", | |
249 | + "February", | |
250 | + "March", | |
251 | + "April", | |
252 | + "May", | |
253 | + "June", | |
254 | + "July", | |
255 | + "August", | |
256 | + "September", | |
257 | + "October", | |
258 | + "November", | |
259 | + "December", | |
260 | +}; | |
261 | + | |
262 | +str0array_t realMonthNames = | |
263 | +{ "Time-division", | |
264 | + "Deep-winter", | |
265 | + "War-time", | |
266 | + "Thaw-time", | |
267 | + "Rebirth", | |
268 | + "Brides-month", | |
269 | + "Imperious", | |
270 | + "Senatorious", | |
271 | + "False-summer", | |
272 | + "Harvest", | |
273 | + "Gratitude", | |
274 | + "Winter-month", | |
275 | +}; | |
276 | + | |
277 | + | |
278 | +void centeredName( char name[], int field ) | |
279 | +{ int i; | |
280 | + size_t length = strlen( name ); | |
281 | + int partial; | |
282 | +/* */ | |
283 | + if ( field < 1 ) | |
284 | + { return; | |
285 | + } | |
286 | + if ( length > field ) | |
287 | + { length = field; | |
288 | + } | |
289 | + for ( partial = field; partial > length; partial -= 2 ) | |
290 | + { putchar( SPACE ); | |
291 | + } | |
292 | + for ( i = 0; i < length; ++i ) | |
293 | + { putchar( name[ i ] ); | |
294 | + } | |
295 | + for ( partial = field - 1; partial > length; partial -= 2 ) /* weight right */ | |
296 | + { putchar( SPACE ); | |
297 | + } | |
298 | +} | |
299 | + | |
300 | +void headerNames( str0array_t * daynames, int offset, int field ) | |
301 | +{ int tracer; | |
302 | +/* */ | |
303 | + if ( offset >= DAYSperWEEK ) | |
304 | + { offset = 0; | |
305 | + } | |
306 | + tracer = offset; | |
307 | + putchar( '|' ); | |
308 | + do | |
309 | + { centeredName( (* daynames )[ tracer++ ], field ); | |
310 | + putchar( '|' ); | |
311 | + if ( tracer >= DAYSperWEEK ) | |
312 | + { tracer = 0; | |
313 | + } | |
314 | + } while ( tracer != offset ); | |
315 | +} | |
316 | + | |
317 | +int intlog10( int number ) | |
318 | +{ int result = 1; | |
319 | + int temp = number; | |
320 | +/* */ | |
321 | + while ( temp > 0 ) | |
322 | + { temp /= 10; | |
323 | + ++result; | |
324 | + } | |
325 | + return result; | |
326 | +} | |
327 | + | |
328 | +void header( long year, int month, str0array_t * daynames, str0array_t * monthNames, int dayoffset, int terminalwidth ) | |
329 | +{ int rightfield = ( terminalwidth - intlog10( year ) - intlog10( month ) - 4 - strlen( PLANETname ) ) / 2; | |
330 | +/* */ | |
331 | + while ( rightfield-- > 0 ) | |
332 | + { putchar ( SPACE ); | |
333 | + } | |
334 | + printf( "%s: %ld %s", PLANETname, year, (* monthNames )[ month ] ); | |
335 | + putchar( '\n' ); | |
336 | + headerNames( daynames, dayoffset, ( ( terminalwidth - 1 ) / DAYSperWEEK ) - 1 ); | |
337 | + putchar( '\n' ); | |
338 | +} | |
339 | + | |
340 | +/* buffer must have room for field plus trailing NUL! | |
341 | +// ** That's one more than field! ** | |
342 | +// Oh, for a dedicated parameter stack and returnable ad-hoc vectors. | |
343 | +// Returns the starting point for the conversion | |
344 | +*/ | |
345 | +char * ulongtostring( char buffer[], unsigned long * numberp, int field, unsigned base ) | |
346 | +{ | |
347 | + char * inpoint = buffer + field; /* Going to store a NUL here! */ | |
348 | + unsigned long number = * numberp; | |
349 | +/* */ | |
350 | + * inpoint = '\0'; | |
351 | + * --inpoint = '0'; | |
352 | + if ( number > 0 ) | |
353 | + { ++inpoint; | |
354 | + while ( ( number > 0 ) && ( inpoint > buffer ) ) | |
355 | + { * --inpoint = '0' + number % base; | |
356 | + number /= base; | |
357 | + } | |
358 | + } | |
359 | + * numberp = number; /* leave any leftover. */ | |
360 | + return inpoint; | |
361 | +} | |
362 | + | |
363 | +#define LPAREN '(' | |
364 | +#define RPAREN ')' /* Balance to avoid confusing balancing editors, as well. */ | |
365 | + | |
366 | +char * longtostring( char buffer[], signed long * numberp, int field, unsigned base, char sign ) | |
367 | +{ signed long number = * numberp; | |
368 | + unsigned long value = ( number < 0 ) ? -number : number; | |
369 | + char * converted = ulongtostring( buffer, &value, ( ( number < 0 ) && ( sign == LPAREN ) ) ? field - 1 : field, base ); | |
370 | +/* */ | |
371 | + if ( ( number < 0 ) && ( converted > buffer ) ) | |
372 | + { * --converted = sign; | |
373 | + if ( sign == LPAREN ) /* sign is parenthesis */ | |
374 | + { buffer[ field - 1 ] = RPAREN; | |
375 | + buffer[ field ] = '\0'; | |
376 | + } | |
377 | + } | |
378 | + * numberp = ( number < 0 ) ? -value : value; /* leave any leftover. */ | |
379 | + return converted; | |
380 | +} | |
381 | + | |
382 | +void centeredNumber( int number, int field, int base, char sign, char overfill ) | |
383 | +{ int rightfield; | |
384 | + int i; | |
385 | + signed long value = number; | |
386 | + char buffer[ field + 1 ]; /* one more for NUL */ | |
387 | + char * converted = longtostring( buffer, &value, field, base, sign ); | |
388 | + int numberwidth = field - ( converted - buffer ); | |
389 | +/* */ | |
390 | +/* printf( "\nnumber: %d, final value: %lu, converted: %s, field: %d\n", number, value, converted, field ); */ | |
391 | + if ( ( value > 0 ) || ( ( number < 0 ) && !( converted > buffer ) ) ) | |
392 | + { char * p; | |
393 | + for ( p = buffer + field; p > buffer; * --p = overfill ) | |
394 | + { /* empty loop */ | |
395 | + } | |
396 | + } | |
397 | + rightfield = ( field - numberwidth ) / 2; /* bias left */ | |
398 | + for ( i = 0; i < rightfield; ++i ) | |
399 | + { putchar( SPACE ); | |
400 | + } | |
401 | + fputs ( converted, stdout ); | |
402 | + for ( i = rightfield + numberwidth; i < field; ++i ) | |
403 | + { putchar( SPACE ); | |
404 | + } | |
405 | +} | |
406 | + | |
407 | +int printOneWeek( int month, int day, int field, int offset ) | |
408 | +{ int column; | |
409 | + int endday = daysInMonths[ ( month > 0 ) ? month - 1 : MONTHSperYEAR - 1 ] - offset + 1; | |
410 | +/* */ | |
411 | + for ( column = 0; column < offset; ++column ) | |
412 | + { putchar( '|' ); | |
413 | + centeredNumber( -( endday++ ), field - 1, 10, LPAREN, '*' ); | |
414 | + } | |
415 | +/* */ | |
416 | + for ( /* as is */; ( column < DAYSperWEEK ) && ( day < daysInMonths[ month ] ); ++column ) | |
417 | + { putchar( '|' ); | |
418 | + centeredNumber( day++, field - 1, 10, LPAREN, '*' ); | |
419 | + } | |
420 | +/* */ | |
421 | + endday = 0; | |
422 | + for ( /* as is */; column < DAYSperWEEK; ++column ) | |
423 | + { putchar( '|' ); | |
424 | + centeredNumber( -( endday++ ), field - 1, 10, LPAREN, '*' ); | |
425 | + } | |
426 | +/* */ | |
427 | + putchar( '|' ); | |
428 | + putchar( '\n' ); | |
429 | + return ( day < daysInMonths[ month ] ) ? day : -1; | |
430 | +} | |
431 | + | |
432 | +void printOneMonth( long year, int month, int day, char fake, int wkstart, int terminalWidth ) | |
433 | +{ long daysfromzero = | |
434 | +/* */ | |
435 | + str0array_t * daynames = ( fake = 'f' ) ? &fakeDayNames : &weekDayNames; | |
436 | + str0array_t * monthNames = ( fake = 'f' ) ? &fakeMonthNames : &realMonthNames; | |
437 | +/* */ | |
438 | + header( year, month, daynames, monthNames, wkstart, terminalWidth ); | |
439 | + day = printOneWeek( month, day, terminalWidth / 7, 2 ); | |
440 | + day = printOneWeek( month, day, terminalWidth / 7, 0 ); | |
441 | + day = printOneWeek( month, day, terminalWidth / 7, 0 ); | |
442 | + day = printOneWeek( month, day, terminalWidth / 7, 0 ); | |
443 | + day = printOneWeek( month, day, terminalWidth / 7, 0 ); | |
444 | + putchar( '\n' ); | |
445 | +} | |
446 | + | |
447 | + | |
448 | +int setlong( long * target, char * source ) | |
449 | +{ int validity = 0; | |
450 | + char * parsept = source; | |
451 | + long temp = strtol( source, &parsept, 0 ); | |
452 | +/* */ | |
453 | + if ( parsept > source ) | |
454 | + { validity = 1; | |
455 | + * target = temp; | |
456 | + } | |
457 | +/* */ | |
458 | +/* printf( "setlong: %s:%s %ld: %d\n", source, parsept, *target, validity ); */ | |
459 | + return validity; | |
460 | +} | |
461 | + | |
462 | + | |
463 | +int setlongLimited( long * target, char * source, long low, long high ) | |
464 | +{ long temp; | |
465 | + int validity = setlong( &temp, source ); | |
466 | +/* */ | |
467 | + validity = validity && ( temp >= low ) && ( temp <= high ); | |
468 | + if ( validity ) | |
469 | + { * target = temp; | |
470 | + } | |
471 | +/* printf( "setlongLimited: %s %ld %ld %ld %ld: %d\n", source, low, temp, high, *target, validity ); */ | |
472 | + return validity; | |
473 | +} | |
474 | + | |
475 | + | |
476 | +int main( int argc, char * argv[] ) | |
477 | +{ | |
478 | + long year = -1; | |
479 | + long month = -1; | |
480 | + int day = 0; | |
481 | + long offset = 0; | |
482 | + int startDay = 0; | |
483 | + int argx = 1; | |
484 | + char fake = 'f'; | |
485 | +/* */ | |
486 | + long terminalWidth = 80; | |
487 | +/* */ | |
488 | + int valid = 1; | |
489 | +/* */ | |
490 | + while ( valid && ( argx < argc ) ) | |
491 | + { | |
492 | + if ( argv[ argx ][ 0 ] == '-' ) | |
493 | + { switch ( argv[ argx ][ 1 ] ) | |
494 | + { | |
495 | + case 'y': | |
496 | + valid = setlong( &year, argv[ ++argx ] ); | |
497 | + break; | |
498 | + case 'm': | |
499 | + valid = setlongLimited( &month, argv[ ++argx ], 0, MONTHSperYEAR - 1 ); | |
500 | + break; | |
501 | + case 'o': | |
502 | + valid = setlongLimited( &offset, argv[ ++argx ], 0, DAYSperWEEK - 1 ); | |
503 | + break; | |
504 | + case 'w': | |
505 | + valid = setlongLimited( &terminalWidth, argv[ ++argx ], 0, LONG_MAX ); | |
506 | + break; | |
507 | + case 'f': | |
508 | + case 'r': | |
509 | + fake = argv[ argx ][ 1 ]; | |
510 | + break; | |
511 | + } | |
512 | + } | |
513 | + else | |
514 | + { if ( year < 0 ) | |
515 | + { valid = setlong( &year, argv[ argx ] ); | |
516 | + } | |
517 | + else if ( month < 0 ) | |
518 | + { valid = setlongLimited( &month, argv[ argx ], 0, MONTHSperYEAR - 1 ); | |
519 | + } | |
520 | + } | |
521 | + ++argx; | |
522 | + } | |
523 | + | |
524 | + if ( !valid ) /* Not really quite accurate summary. */ | |
525 | + { printf( "%s { [ <year> [ <month> ] ] }\n", | |
526 | + argv[ 0 ] ); | |
527 | + puts( "\t | { [ -f(ake) | -r(eal) ] [ -y <year> ] [ -m <month> ] [ -o <weekday-offset> ] [ -w <terminal-width> ] }" ); | |
528 | + return EXIT_FAILURE; | |
529 | + } | |
530 | + | |
531 | + | |
532 | + printOneMonth( year, (int) month, day, fake, offset, terminalWidth ); | |
533 | + | |
534 | + return EXIT_SUCCESS; | |
535 | +} |