Functions for working with the idealized calendar of Planet Xhilr
Revisión | bc537f1f710d466134764828000b7a88aa9fe7e7 (tree) |
---|---|
Tiempo | 2017-06-13 17:28:36 |
Autor | Joel Matthew Rees <joel.rees@gmai...> |
Commiter | Joel Matthew Rees |
pursuing two separate paths
@@ -39,7 +39,7 @@ | ||
39 | 39 | ( For ancient, especially pre-1983 fig, Forths: ) |
40 | 40 | ( Do not use these in modern Forths like gforth. ) |
41 | 41 | : UM* U* ; |
42 | -: FM/MOD M/MOD DROP ; ( Cheat! Behavior is not well defined for negative numbers. ) | |
42 | +: FM/MOD M/MOD DROP ; ( Cheat! fm/mod is supposed to be floored, not unsigned. ) | |
43 | 43 | ( This is just renaming in a sloppy fashion, ) |
44 | 44 | ( to accomodate the difference between ancient fig-Forths and modern Forths. ) |
45 | 45 | ( Showing it in infix won't help. ) |
@@ -0,0 +1,363 @@ | ||
1 | +( | |
2 | +// Forth programs for calculating idealized lengths of months | |
3 | +// relative to skip years | |
4 | +// for the world of Bobbie, Karel, Dan, and Kristie, | |
5 | +// | |
6 | +// by Joel Matthew Rees, winter/spring 2017. | |
7 | +// Copyright 2017, Joel Matthew Rees | |
8 | +// | |
9 | +// Permission granted to use for personal entertainment only. | |
10 | +// -- You really shouldn't write programs like this on modern computers. | |
11 | +// | |
12 | +// http://joel-rees-economics.blogspot.com/2017/03/soc500-03-08-calendar-math.html | |
13 | +// http://joel-rees-economics.blogspot.com/2017/03/soc500-03-09-calculating-skip-years.html | |
14 | +// | |
15 | +// Novel here: | |
16 | +// http://joel-rees-economics.blogspot.com/2017/01/soc500-00-00-toc.html | |
17 | +// | |
18 | +// | |
19 | +// Save as "econcalcmonth.fs" | |
20 | +// | |
21 | +// In gforth and most modern or emulated environments, | |
22 | +// just paste it into the terminal of a running Forth session. | |
23 | +// | |
24 | +// Run it with | |
25 | +// 7 SHOWMONTHS | |
26 | +// for seven years, etc. | |
27 | +) | |
28 | + | |
29 | +( Using integer math. ) | |
30 | +( Forth expression syntax is mostly postfix. ) | |
31 | +( Only the definition syntax is prefix or infix. ) | |
32 | +( I've added comments with equivalent infix expressions to help those unfamiliar with Forth. ) | |
33 | + | |
34 | + | |
35 | +( Using baroque identifiers for ancient Forths. ) | |
36 | +( fig-Forth used first three character significant symbol tables. ) | |
37 | + | |
38 | + | |
39 | +( For ancient, especially pre-1983 fig, Forths: ) | |
40 | +( Do not use these in modern Forths like gforth. ) | |
41 | +: UM* U* ; | |
42 | +: FM/MOD M/MOD DROP ; ( Cheat! Behavior is not well defined for negative numbers. ) | |
43 | +( This is just renaming in a sloppy fashion, ) | |
44 | +( to accomodate the difference between ancient fig-Forths and modern Forths. ) | |
45 | +( Showing it in infix won't help. ) | |
46 | +: S>D S->D ; ( Old fashioned name for single-to-double. ) | |
47 | + | |
48 | +( Semi-simulate local variables. ) | |
49 | +SP@ SP@ - ABS CONSTANT CELLWIDTH | |
50 | +( Infix won't help here, either, but I can try to explain: ) | |
51 | +( CELLWIDTH = absolute-value-of difference-between SP-without-pointer and SP-with-pointer. ) | |
52 | + | |
53 | +( Infix will be confusing here, too. ) | |
54 | +: LC@ ( index -- sp[ix] ) ( 0 is top. PICK is available on many modern Forths. ) | |
55 | + 1 + CELLWIDTH * ( Skip over the stack address on stack. ) | |
56 | + SP@ + @ ( Assumes push-down stack. Will fail on push-up. ) | |
57 | +; | |
58 | + | |
59 | +( Infix will be confusing here, too. ) | |
60 | +: LC! ( n index -- ) ( 0 is top. Just store. This is not ROLL. ) | |
61 | + 2 + CELLWIDTH * ( Index and stack address are extra on stack during calculation. ) | |
62 | + SP@ + ( Assumes push-down stack. ) | |
63 | + ! ( *** Will fail in MISERABLE ways on push-up stacks! *** ) | |
64 | +; | |
65 | + | |
66 | +( Make things easier to read. ) | |
67 | +( Infix will be confusing here, too. ) | |
68 | + | |
69 | +: PRCH EMIT ; | |
70 | + | |
71 | +: COMMA 44 PRCH ; | |
72 | +: COLON 58 PRCH ; | |
73 | +: POINT 46 PRCH ; | |
74 | +: LPAREN 40 PRCH ; | |
75 | +: RPAREN 41 PRCH ; | |
76 | + | |
77 | +( No trailing space. ) | |
78 | +: PSNUM ( number -- ) | |
79 | + 0 .R ; | |
80 | + | |
81 | + | |
82 | +: NUMERATORS ( count -- ) | |
83 | +DUP 1+ 0 DO | |
84 | + I PSNUM COLON SPACE | |
85 | + I 1000 * OVER / PSNUM COMMA ( 1000 times I, divided by count ) | |
86 | + SPACE LOOP | |
87 | +DROP ; | |
88 | + | |
89 | +: FRACTIONS ( count -- ) | |
90 | +1 DO | |
91 | + I NUMERATORS CR | |
92 | +LOOP ; | |
93 | + | |
94 | + | |
95 | +( Watch limits on 16 bit processors. ) | |
96 | +( Do it in integers! ) | |
97 | + | |
98 | +7 CONSTANT SCYCLE ( years in short cycle ) | |
99 | +( SCYCLE = 7 ) | |
100 | + | |
101 | +7 2 * CONSTANT SPMCYC ( short cycles in medium cycle ) | |
102 | +( SPMCYC = 7 × 2 ) | |
103 | + | |
104 | +SCYCLE SPMCYC * CONSTANT MCYCLE ( years in medium cycle, should be 98 ) | |
105 | +( MCYCLE = SCYCLE × SPMCYC ) | |
106 | + | |
107 | +7 7 * CONSTANT SPLCYC ( short cycles in single long cycle ) | |
108 | +( SPLCYC = 7 × 7 ) | |
109 | + | |
110 | +SCYCLE SPLCYC * CONSTANT LCYCLE ( years in single long cycle, should be 343 ) | |
111 | +( LCYCLE = SCYCLE × SPLCYC ) | |
112 | + | |
113 | +7 CONSTANT MP2LCYC ( medium cycles in double long cycle ) | |
114 | +( MP2LCYC = 7 ) | |
115 | +( MPLCYC would not be an integer: 3 1/2 ) | |
116 | + | |
117 | +MCYCLE MP2LCYC * CONSTANT 2LCYCLE ( years in double long cycle, should be 686 ) | |
118 | +( 2LCYCLE = MCYCLE × MP2LCYC ) | |
119 | + | |
120 | +352 CONSTANT DPSKIPYEAR ( floor of days per year ) | |
121 | + | |
122 | + | |
123 | +5 CONSTANT RDSCYCLE ( remainder days in short cycle ) | |
124 | + | |
125 | +DPSKIPYEAR SCYCLE * RDSCYCLE + CONSTANT DPSCYCLE ( whole days per 7 year cycle ) | |
126 | +( DPSCYCLE = DPSKIPYEAR × SCYCLE + RDSCYCLE ) | |
127 | +( DPMCYCLE and DP2LCYCLE would overflow on 16 bit math CPUs. ) | |
128 | + | |
129 | +RDSCYCLE SPMCYC * 1 - CONSTANT RDMCYCLE ( remainder days in medium cycle ) | |
130 | +( RDMCYCLE = RDSCYCLE × SPMCYC - 1 ) | |
131 | + | |
132 | +RDMCYCLE MP2LCYC * 2 + CONSTANT RD2LCYCLE ( remainder days in double long cycle -- odd number ) | |
133 | +( RD2LCYCLE = RDMCYCLE × MP2LCYC + 2 ) | |
134 | +( RD2LCYCLE / 2LCYCLE is fractional part of year. ) | |
135 | +( Ergo, length of year is DPSKIPYEAR + RD2LCYCLE / 2LCYCLE, ) | |
136 | +( or 352 485/686 days. ) | |
137 | + | |
138 | +12 CONSTANT MPYEAR ( months per year ) | |
139 | + | |
140 | +DPSKIPYEAR MPYEAR /MOD CONSTANT FDMONTH ( floor of days per month ) | |
141 | +( FDMONTH = DPSKIPYEAR / MPYEAR ) | |
142 | +CONSTANT FRMONTH ( floored minimum remainder days per month ) | |
143 | +( FRMONTH = DPSKIPYEAR MOD MPYEAR ) | |
144 | + | |
145 | +2LCYCLE MPYEAR * CONSTANT MDENOMINATOR ( denominator of month fractional part ) | |
146 | +( MDENOMINATOR = 2LCYCLE × MPYEAR ) | |
147 | + | |
148 | +FRMONTH 2LCYCLE * RD2LCYCLE + CONSTANT MNUMERATOR ( numerator of month fractional part ) | |
149 | +( MNUMERATOR = FRMONTH × 2LCYCLE + RD2LCYCLE ) | |
150 | +( Ergo, length of month is FDMONTH + MNUMERATOR / MDENOMINATOR, ) | |
151 | +( or 29 3229/8232 days. ) | |
152 | + | |
153 | +MDENOMINATOR 2 / CONSTANT MROUNDFUDGE | |
154 | + | |
155 | +( Infix will be confusing below here, as well. ) | |
156 | +( Hopefully, the comments and explanations will provide enough clues. ) | |
157 | + | |
158 | +( Sum up the days of the months in a year. ) | |
159 | +: SU1MONTH ( startfractional startdays -- endfractional enddays ) | |
160 | + 29 + ( Add the whole part. ) | |
161 | + SWAP ( Make the fractional part available to work on. ) | |
162 | + MNUMERATOR + ( Add the fractional part. ) | |
163 | + DUP MDENOMINATOR < ( Have we got a whole day yet? ) | |
164 | + IF | |
165 | + SWAP ( No, restore stack order for next pass. ) | |
166 | + ELSE | |
167 | + MDENOMINATOR - ( Take one whole day from the fractional part. ) | |
168 | + SWAP 1+ ( Restore stack and add the day carried in. ) | |
169 | + ENDIF | |
170 | +; | |
171 | + | |
172 | +: PRMONTH ( fractional days -- fractional days ) | |
173 | + SPACE DUP PSNUM POINT ( whole days ) | |
174 | + OVER 1000 UM* ( Fake three digits of decimal precision. ) | |
175 | + MROUNDFUDGE 0 D+ ( Round the bottom digit. ) | |
176 | + MDENOMINATOR FM/MOD ( Divide, or evaluate the fraction. ) | |
177 | + S>D <# # # # #> ( Formatting puts most significant digits in buffer first. ) | |
178 | + TYPE ( Fake decimal output. ) | |
179 | + DROP SPACE | |
180 | +; | |
181 | + | |
182 | +: SH1IDEALYEAR ( year daysmemory fractional days -- year daysmemory fractional days ) | |
183 | + CR | |
184 | + 12 0 DO | |
185 | + 3 LC@ PSNUM SPACE ( year ) | |
186 | + I PSNUM COLON SPACE | |
187 | + SU1MONTH | |
188 | + DUP 3 LC@ - ( difference in days ) | |
189 | + 2 LC@ ( ceiling ) IF 1+ ENDIF | |
190 | + DUP PSNUM SPACE ( show theoretical days in month ) | |
191 | + 3 LC@ + ( sum of days ) | |
192 | + LPAREN DUP PSNUM COMMA SPACE | |
193 | + 2 LC! ( update ) | |
194 | + PRMONTH RPAREN CR | |
195 | + LOOP | |
196 | +; | |
197 | + | |
198 | +: SHOWIDEALMONTHS ( years -- ) | |
199 | + >R | |
200 | + 0 0 0 0 ( year, daysmemory, fractional, days ) | |
201 | + R> 0 DO | |
202 | + CR | |
203 | + SH1IDEALYEAR | |
204 | + 3 LC@ 1+ 3 LC! | |
205 | + LOOP | |
206 | + DROP DROP DROP DROP | |
207 | +; | |
208 | + | |
209 | + 0 CONSTANT SKMONTH | |
210 | + 1 CONSTANT SK1SHORTCYC | |
211 | + 4 CONSTANT SK2SHORTCYC | |
212 | + 48 CONSTANT SKMEDIUMCYC | |
213 | +186 CONSTANT LPLONGCYC ( Must be short1 or short2 within the seven year cycle. ) | |
214 | + | |
215 | +( Since skipyears are the exception, ) | |
216 | +( we test for skipyears instead of leapyears. ) | |
217 | +( Calendar system starts with year 0, not year 1. ) | |
218 | +( Would need to check and adjust if the calendar started with year ) | |
219 | +: ISKIPYEAR ( year -- flag ) | |
220 | + DUP MCYCLE MOD SKMEDIUMCYC = | |
221 | + IF DROP -1 ( One specified extra skip year in medium cycle. ) | |
222 | + ELSE | |
223 | + DUP SCYCLE MOD DUP | |
224 | + SK1SHORTCYC = | |
225 | + SWAP SK2SHORTCYC = OR ( Two specified skip years in short cycle, but ... ) | |
226 | + SWAP LCYCLE MOD LPLONGCYC = 0= AND ( not the specified exception in the long cycle. ) | |
227 | + ENDIF | |
228 | +; | |
229 | + | |
230 | + | |
231 | +( At this point, I hit a condundrum. ) | |
232 | +( Modern "standard" Forths want uninitialized variables, ) | |
233 | +( but ancient, especially fig-Forths want initialized variables. ) | |
234 | +( The lower-level <BUILDS DOES> for fig is only partially part of the modern standard. ) | |
235 | +( And CREATE is initialized as a CONSTANT in the fig-Forth, ) | |
236 | +( but has no initial characteristic code or value in modern standards. ) | |
237 | +( So. ) | |
238 | +( On ancient Forths, VARIABLE wants an initial value. We give it one. ) | |
239 | +( It stays around forever. ) | |
240 | +0 VARIABLE DIMARRAY ( Days In Months array ) | |
241 | + 30 DIMARRAY ! ( 1st month ) | |
242 | + 29 , | |
243 | + 30 , | |
244 | + 29 , | |
245 | + 29 , | |
246 | + 30 , | |
247 | + 29 , | |
248 | + 30 , | |
249 | + 29 , | |
250 | + 29 , | |
251 | + 30 , | |
252 | + 29 , | |
253 | + 0 , | |
254 | + | |
255 | +: DIMONTH ( year month -- days ) | |
256 | + DUP 0 < 0= | |
257 | + OVER MPYEAR < AND 0= | |
258 | + IF | |
259 | + DROP DROP 0 ( Out of range. No days. ) | |
260 | + ELSE | |
261 | + DUP CELLWIDTH * DIMARRAY + @ ( Get the basic days. ) | |
262 | + SWAP SKMONTH = ( true if skip month ) | |
263 | + ROT ISKIPYEAR AND ( true if skip month of skip year ) | |
264 | + 1 AND - ( Subtrahend is 1 only if skip month of skip year. ) | |
265 | + ENDIF | |
266 | +; | |
267 | + | |
268 | +: SH1YEAR ( year daysmemory fractional days -- year daysmemory fractional days ) | |
269 | + CR | |
270 | + 12 0 DO | |
271 | + 3 LC@ PSNUM SPACE ( year ) | |
272 | + I PSNUM COLON SPACE | |
273 | + SU1MONTH ( ideal month ) | |
274 | + 3 LC@ I DIMONTH ( real month ) | |
275 | + DUP PSNUM SPACE ( show days in month ) | |
276 | + 3 LC@ + ( sum of days ) | |
277 | + LPAREN DUP PSNUM COMMA SPACE | |
278 | + 2 LC! ( update ) | |
279 | + PRMONTH RPAREN CR | |
280 | + LOOP | |
281 | +; | |
282 | + | |
283 | +: SHOWMONTHS ( years -- ) | |
284 | + >R | |
285 | + 0 0 0 0 ( year, daysmemory, fractional, days ) | |
286 | + R> 0 DO | |
287 | + CR | |
288 | + SH1YEAR | |
289 | + 3 LC@ 1+ 3 LC! | |
290 | + LOOP | |
291 | + DROP DROP DROP DROP | |
292 | +; | |
293 | + | |
294 | + | |
295 | + | |
296 | +( Below here is scratch work I'm leaving for my notes. ) | |
297 | +( It isn't necessary to the above, and can be deleted. ) | |
298 | + | |
299 | +: V2-SHOWIDEALMONTHS ( years -- ) | |
300 | + >R | |
301 | + 0 0 0 ( daysmemory, fractional, days ) | |
302 | + R> 0 DO | |
303 | + CR | |
304 | + 12 0 DO | |
305 | + J PSNUM SPACE ( year ) | |
306 | + I PSNUM COLON SPACE | |
307 | + SU1MONTH | |
308 | + DUP 3 LC@ - ( difference in days ) | |
309 | + 2 LC@ ( ceiling ) IF 1+ ENDIF | |
310 | + DUP PSNUM SPACE ( show theoretical days in month ) | |
311 | + 3 LC@ + ( sum of days ) | |
312 | + LPAREN DUP PSNUM COMMA SPACE | |
313 | + 2 LC! ( update ) | |
314 | + PRMONTH RPAREN CR | |
315 | + LOOP | |
316 | + LOOP | |
317 | + DROP DROP DROP | |
318 | +; | |
319 | + | |
320 | + | |
321 | +( : ABS number -- absolute-value *** built in! *** | |
322 | +DUP 0< IF NEGATE THEN ; ) | |
323 | + | |
324 | +: WITHIN1 ( n1 n2 -- flag ) | |
325 | + - ABS 1 <= ; ( n1 and n2 are within 1 of each other ) | |
326 | + | |
327 | +( Negatives end in division by zero or infinite loop. ) | |
328 | +: SQRT ( number -- square-root ) | |
329 | +DUP IF ( square root of zero is zero. ) | |
330 | + ABS | |
331 | + 2 ( initial guess ) | |
332 | + BEGIN | |
333 | + OVER OVER / ( test guess by divide ) | |
334 | + OVER OVER - ABS 1 <= ( number guess quotient flag ) | |
335 | + IF ( number guess quotient ) | |
336 | + MIN -1 ( number result flag ) | |
337 | + ELSE | |
338 | + OVER + 2 / ( number guess avg ) | |
339 | + SWAP OVER ( number avg guess avg ) | |
340 | + - 1 <= ( number avg flag ) ( Integer average will always be floored. ) | |
341 | + ENDIF | |
342 | + UNTIL ( number result ) | |
343 | + SWAP DROP | |
344 | +ENDIF ; | |
345 | + | |
346 | + | |
347 | +353 CONSTANT DPYEAR ( nominal days per year ) | |
348 | + | |
349 | +7 CONSTANT 7YEARS | |
350 | + | |
351 | +2 CONSTANT DS7CYCLE ( days short in seven year cycle ) | |
352 | + | |
353 | +DPYEAR 7YEARS * DS7CYCLE - CONSTANT DP7YEAR ( whole days per 7 year cycle ) | |
354 | + | |
355 | +7YEARS 7 2 * * CONSTANT 98YEARS | |
356 | + | |
357 | +98YEARS 7YEARS / DS7CYCLE * 1 + CONSTANT DS98CYCLE ( days short in 98 year cycle ) | |
358 | + | |
359 | +98YEARS 7 * CONSTANT 686YEARS | |
360 | + | |
361 | +686YEARS 98YEARS / DS98CYCLE * 2 - CONSTANT DS686CYCLE ( days short in 686 year cycle ) | |
362 | + | |
363 | + |