Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
More Problems with (implementations of) Zeller's congruence
#1
If you saw my posting on a simpler method to perform Zeller's congruence (the means to compute what day of the week a particular date falls on), you'll find I discovered that the formula has errors. So, having been embarrassed, I decided to look in my archives and do some software archeology. I discover a program I wrote literally six months ago, that does the same thing, only it uses a different method to perform Zeller's congruence. I run that program, and it has errors too!

So I decided to create a test bed, where it would have all the correct dates from 1/1/1800 (which was a Wednesday), all the way to 12/31/2199 (which will be a Tuesday), already loaded into itself, then have any Zeller's congruence routine go through every single date. If it passes all of these tests, then I can consider it correct. 

Well, am I going to do this manually? Are you kidding? No serious programmer is going to do work he can shove off onto the computer. So I wrote a program to do it for me. After a few fits and starts and a rewrite of a handful (less than 10 lines to correct) I got the program to work perfectly (I had it give me snapshots of what it was doing, and I verified a few, everything was correct). At least, until I saw the results. It had generated a four megabyte file!

The program is named zeller_check.bas, and in case you're interested in a program that writes other programs, take a look here. Or download it (a copy is attached to this posting) and try it yourself.

Code: (Select All)
' ZCFC - Zeller's congruence Fact Checker
'
' Creates a table of the entire range of dates from 1800-01-01 to 2199-12-31
' Paul Robinson September 16, 2024

Option _Explicit
Dim As Long DC, Y, LC, TY, W, L
Dim As Integer Month, Day, DayStart
Dim A$

Dim YD(1800 To 2199, 12, 31)
'Print " Y    Y/4  Y\4  Mod4 /100 \100  Mod  /400 \400 Mod"
'For Y = 1899 To 2000
'    Print Using "####: #### #### #### #### #### #### #### #### ####"; Y; Y / 4; Y \ 4; Y Mod 4; Y / 100; Y \ 100; Y Mod 100; Y / 400; Y \ 400; Y Mod 400
'    If Y = 1908 Or Y = 1950 Then Input "More"; A$: Print " Y    Y/4  Y\4  Mod4 /100 \100 Mod  /400 \400 Mod"
'Next
Open "L:\yeardays.bas" For Output As #1
Print #1, "Dim YearDays(1800 To 2199) as integer"
Open "L:\datemame.bas" For Output As #2


Color 15
Const Mo = 0, Tu = 1, We = 2, Th = 3, Fr = 4, Sa = 5, Su = 6
Const Monday = 0, Tuesday = 1, Wednesday = 2, Thursday = 3
Const Friday = 4, Saturday = 5, Sunday = 6
Const January = 1, February = 2, March = 3, April = 4, May = 5, June = 6
Const July = 7, August = 8, September = 9, October = 10, November = 11, December = 12

Print #2, "Const Mo = 0, Tu = 1, We = 2, Th = 3, Fr = 4, Sa = 5, Su = 6"
Print #2, "Const Monday = 0, Tuesday = 1, Wednesday = 2, Thursday = 3"
Print #2, "Const Friday = 4, Saturday = 5, Sunday = 6"
Print #2, "Const January = 1, February = 2, March = 3, April = 4, May = 5, June = 6"
Print #2, "Const July = 7, August = 8, September = 9, October = 10, November = 11, December = 12"

Print #2, "Dim YD(1800 To 2199, December, 31)"
' these use 1-12 for convenience
Dim MonthDays(1, December) As Integer, MonthNames(December) As String
' This one uses 0
Dim DayNames(Sunday) As String
Data "January",31,"February",28,"March",31,"April",30,"May",31
Data "June",30,"July",31,"August",31,"September",30
Data "October",31,"November",30,"December",31

Data "Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"

For Month = January To December: Read MonthNames(Month), MonthDays(0, Month): Next
For Month = January To December: MonthDays(1, Month) = MonthDays(0, Month): Next
MonthDays(1, February) = 29 'Adjust for leap year

For Day = Monday To Sunday: Read DayNames(Day): Next

' Jan 1 1800 was a  Wednesday
DayStart = Wednesday
For Y = 1800 To 2199
    L = 0 ' not determined if leap year; presume it is not

    ' Count the number of days in a year
    TY = 365
    ' Year mod 4 =0 for a leap year
    If Y Mod 4 = 0 Then
        If Y Mod 100 <> 0 Then
            TY = TY + 1 ' Regular leap year
            L = 1
            Color 4
        Else
            If Y Mod 400 = 0 Then TY = TY + 1: L = 1: Color 4 ' Century leap year

        End If
    End If
    DC = DC + TY
    Print Using "#### ###  "; Y; TY;
    Print #1, "YearDays("; Y; ") = "; TY
    Color 15
    LC = LC + 1
    If LC = 6 Then LC = 0: Print: W = W + 1
    If W = 10 Then Input "More"; A$: W = 0
    For Month = January To December
        On Error GoTo Catch
        For Day = 1 To MonthDays(L, Month) ' if leap year, uses alternate
            Print #2, " YD("; Y; ","; MonthNames(Month); ","; Day; ") = ";
            Print #2, DayNames(DayStart)

            If Day = 1 And Month = 1 Then Print MonthNames(Month); " 1,"; Y; "="; DayNames(DayStart)

            DayStart = DayStart + 1
            If DayStart = 7 Then DayStart = Monday
        Next
    Next
Next
Print DC; "days."
Close
End
Catch:
Print: Print " ERROR "; Err; "  DayStart="; DayStart

Now, a couple of things about the program. "Monthdays" was originally a 1-dimensional array, but I was getting errors - it was not tracking the day of the week correctly. Then it hit me: I wasn't accounting for leap years! So, I got thinking, I already have L to determine if it is a leap year. So I changed Monthdays to a two-dimensional array, load the number of days in a month into dimension 0, i.e. Monthdays(0,1) to Monthdays (0,12). Now copy the array into dimension, then change Monthdays(1,2) to 29, which I do on line 48. Then use Monthdays(0,1 to 12) for common years, and Monthdays(1, 1 to 12) for leap years.

Now it worked perfectly. Here's a sample, top of the file:
Code: (Select All)
Const Mo = 0, Tu = 1, We = 2, Th = 3, Fr = 4, Sa = 5, Su = 6
Const Monday = 0, Tuesday = 1, Wednesday = 2, Thursday = 3
Const Friday = 4, Saturday = 5, Sunday = 6
Const January = 1, February = 2, March = 3, April = 4, May = 5, June = 6
Const July = 7, August = 8, September = 9, October = 10, November = 11, December = 12
Dim YD(1800 To 2199, December, 31)
YD( 1800 ,January, 1 ) = Wednesday
YD( 1800 ,January, 2 ) = Thursday
YD( 1800 ,January, 3 ) = Friday
YD( 1800 ,January, 4 ) = Saturday
Now, this continues on for, get this, 146,083 lines! Here's the bottom 10 lines, lines 196,094 through 146,103:
Code: (Select All)

YD( 2199 ,December, 19 ) = Thursday
YD( 2199 ,December, 20 ) = Friday
YD( 2199 ,December, 21 ) = Saturday
YD( 2199 ,December, 22 ) = Sunday
YD( 2199 ,December, 23 ) = Monday
YD( 2199 ,December, 24 ) = Tuesday
YD( 2199 ,December, 25 ) = Wednesday
YD( 2199 ,December, 26 ) = Thursday
YD( 2199 ,December, 27 ) = Friday
YD( 2199 ,December, 28 ) = Saturday
YD( 2199 ,December, 29 ) = Sunday
YD( 2199 ,December, 30 ) = Monday
YD( 2199 ,December, 31 ) = Tuesday
So while the program worked, I could just imagine trying to give the QB64PE compiler a behemoth 5,113,223 byte, 146,103 line, file. If it could even handle it, how long it would take to compile? And then, at run time, how long it would take for the program to initialize all these arrays? So, I got thinking, I'm already creating these initialization records, how long would it take to just have the program initialize itself? Let's find out!

And since this is a forum on "work in progress", I'll be back with part 2 of this story, after I take this program apart and have it initialize itself. We'll find out the answer to that question, in the next episode.


Attached Files
.bas   zeller_check.bas (Size: 3.45 KB / Downloads: 5)
While 1
   Fix Bugs
   report all bugs fixed
   receive bug report
end while
Reply


Messages In This Thread
More Problems with (implementations of) Zeller's congruence - by TDarcos - 09-17-2024, 07:07 AM



Users browsing this thread: 2 Guest(s)