> Your function must be self-contained, such that someone could copy-paste it into their code and not get an error. It also must not effect the environment outside it if at all possible (no #define or mutating globals). It must be as "pure" and self-contained as the language will allow.
This is an interesting restriction, actually, with respect to style. The “natural” way I would program such an algorithm certainly doesn’t adhere to this requirement.
Producing functions that are still correct, idiomatic and legible while producing minimal changes to environment or state requires a non-beginners understanding of the runtime nature of the language.
This is also interesting for speed, The algorithms ran by all the impls are identical, but sacrificing speed for cleanliness and adherence to spirit is done regularly.
Without having actually measured, the rust impl probably runs faster than the C impl, but that's not because rust is "faster" than C. That's because I used a closure with one call to floor() for modulus in rust, whilst in C I did 2 fmod()'s to get it because the alternative would've been to make the code unreadable or add a second function/macro (although thinking about it now, undef does exist...)
danparsonson · 1d ago
Could you elaborate on this? Maybe an example of what you mean?
blululu · 1d ago
This is really nice. My initial thought was "why just divide the time interval since the Unix Epoch by the synodic period of the moon". Turns out the moon's phase is a bit more complicated than that.
https://en.wikipedia.org/wiki/Lunar_phase#Calculating_phase
madcaptenor · 1d ago
That said, this is basically what the Hebrew calendar does (although of course they use a much earlier epoch)
o11c · 1d ago
`pom(6)` from `bsdgames` might already be installed on your system to do this. Source comments indicate it was originally based on prior versions of the same book but updated to the Third Edition in 1998.
A PostScript program to visualize a calendar of moon phases (skip down to "LCAL PostScript Calendar Examples" for just that). Did some nice PS prints recently for the next 10 years, adapted to fit in a frame I had laying around.
sub fixangle($a) { $a mod 360 } # raku has built-in support for Euclidean modulo
pi # raku has pi
# Solve equation of Kepler
my $e = $M;
my $delta;
repeat { # raku's repeat loop allows initialisation of delta to be folded in
$delta = $e - $eccent * sin($e) - $M;
$e -= $delta / (1 - $eccent * cos($e));
} while abs($delta) > 1e-6;
eddyg · 1d ago
Thanks! (I'm a big fan of Raku; it doesn't get nearly the respect it deserves!)
I adapted the same moonphase logic for the status line program I use with dwm. Did you run into any discrepancies with using January 0 vs December 31st? Some of the translations of Walker's as noted in my comment at https://github.com/ericpruitt/emus/blob/ea059239845ee6d57614... seem to produce differing results.
oliverkwebb · 1d ago
This discrepancy with time is explained in the book the algorithm is based on (Practical Astronomy with Your Calculator, which also does a good job explaining a ton of other ideas and models in astronomical calculation).
January 0th makes sense because a year starts on January 1st. You count days of the year from 1 instead of from 0. So if we are going by the day of the year, day 1 of the year should be Jan1, the consequence of this being that Jan0 exists and is Dec31 of the prev year.
I originally got into this because of my status bar on i3, hacking together C code from moontool into https://github.com/oliverkwebb/moontool about a year back.
lifthrasiir · 1d ago
This code consistently converts angles to the [0, 360) range, which is IMO overkill. A better approach is to normalize everything to [0, 2) range so that you can simply multiply or divide by pi to get the radians. If your math library has `sinpi` or `cospi` it will be even more accurate, as it no longer has to divide by 2pi to do the actual calculation.
> Your function must be self-contained, such that someone could copy-paste it into their code and not get an error. It also must not effect the environment outside it if at all possible (no #define or mutating globals). It must be as "pure" and self-contained as the language will allow.
This is an interesting restriction, actually, with respect to style. The “natural” way I would program such an algorithm certainly doesn’t adhere to this requirement.
Producing functions that are still correct, idiomatic and legible while producing minimal changes to environment or state requires a non-beginners understanding of the runtime nature of the language.
[0]: https://github.com/oliverkwebb/moonphase?tab=readme-ov-file#...
Without having actually measured, the rust impl probably runs faster than the C impl, but that's not because rust is "faster" than C. That's because I used a closure with one call to floor() for modulus in rust, whilst in C I did 2 fmod()'s to get it because the alternative would've been to make the code unreadable or add a second function/macro (although thinking about it now, undef does exist...)
For "implement simple task in many languages" you should probably think of Rosetta Code, now at https://rosettacode.miraheze.org/wiki/Rosetta_Code
A PostScript program to visualize a calendar of moon phases (skip down to "LCAL PostScript Calendar Examples" for just that). Did some nice PS prints recently for the next 10 years, adapted to fit in a frame I had laying around.
[0] https://pcal.sourceforge.net/
some cool things stood out:
January 0th makes sense because a year starts on January 1st. You count days of the year from 1 instead of from 0. So if we are going by the day of the year, day 1 of the year should be Jan1, the consequence of this being that Jan0 exists and is Dec31 of the prev year.
I originally got into this because of my status bar on i3, hacking together C code from moontool into https://github.com/oliverkwebb/moontool about a year back.