program LeapYear ; {$I VERSION.PAS} {$IFNDEF PASCAL}{$IFNDEF DELPHI} Fail! ; {$ENDIF}{$ENDIF} { Detailed speed order depends on whether run-time checks are on; and probably on processor, language &c. A table lookup - Tbl - is only quickest of the full methods when run-time checks are off. Z15 is, IMHO, the nicest rule; but not the most efficient full one, which, so far, is Z00 or Z01. For speed, obviously one should test the 4-year rule first; if the year passes that, the next test should be the 100, then if needed the 400. OTOH, within 1901-2099, just use the first, as in An3. In Pascal, "Y and 3" always beats "Y mod 4" for code size and speed. Cobol : FUNCTION MOD (YEAR, 4) = 0 or ??? } {$IFDEF DELPHI} uses Windows ; {$ENDIF} {$IFDEF WINDOWS} uses WinCrt ; {$ENDIF} const YMin = 1980 ; YMax = 2107 ; type YrT = YMin..YMax ; var LArr : array [YrT] of boolean ; function Nul(Y : word) : boolean ; FAR ; { Dummy } begin Nul := false end ; function UGH(Y : word) : boolean ; FAR ; { Just 1901-2099 } begin UGH := boolean(Y and 3) end ; function An3(Y : word) : boolean ; FAR ; { Just 1901-2099 } begin An3 := (Y and 3) = 0 end ; function Md4(Y : word) : boolean ; FAR ; { Just 1901-2099 } begin Md4 := (Y mod 4) = 0 end ; function Tbl(Y : word) : boolean ; FAR ; { Lookup } begin Tbl := LArr[Y] end ; function Cen(Y : word) : boolean ; FAR ; { Just 1901-2199 } begin Cen := ((Y and 3) = 0) and (Y<>2100) end ; function Ass(Y : word) : boolean ; FAR ; assembler ; asm ; {$IFDEF PASCAL} {$IFDEF SLOWER} mov ax,[Y] ; and ax,3 ; jne @1 ; mov ax,[Y] ; xor dx,dx ; mov cx,100 ; div cx ; or dx,dx ; ja @2 ; and ax,3 ; je @2 ; @1 : mov al,0 ; jmp @3 ; @2 : mov al,1 ; @3 : ; {$ELSE after Osmo} xor bx,bx ; mov ax,[Y] ; test ax,3 ; jne @out ; inc bx ; xor dx,dx ; mov cx,100 ; div cx ; or dx,dx ; jne @out ; test ax,3 ; je @out ; xor bx,bx ; @out : mov ax,bx ; {$ENDIF} {$ENDIF} {$IFDEF DELPHI} { WRONG mov ax,Y ; and ax,3 ; or ax,ax ; jne @1 ; mov ax,Y ; xor dx,dx ; mov cx,100 ; div cx ; xchg dx,ax ; or ax,ax ; ja @2 ; mov ax,Y ; xor dx,dx ; mov cx,400 ; div cx ; xchg dx,ax ; or ax,ax ; je @2 ; @1 : mov al,0 ; jmp @3 ; @2 : mov al,1 ; @3 : ; } push ebx ; xor bx,bx ; mov ax,Y ; test ax,3 ; jne @out ; inc bx ; xor dx,dx ; mov cx,100 ; div cx ; or dx,dx ; jne @out ; test ax,3 ; je @out ; xor bx,bx ; @out : mov ax,bx ; pop ebx ; {$ENDIF} end ; function Z00(Y : word) : boolean ; FAR ; begin Z00 := ((Y and 3) = 0) and ((Y mod 100 > 0) or (Y mod 400 = 0)) end ; function Z01(Y : word) : boolean ; FAR ; begin Z01 := ((Y and 3) = 0) and ((Y mod 100 <> 0) or (Y mod 400 = 0)) end ; function Z02(Y : word) : boolean ; FAR ; begin Z02 := False ; if (Y and 3)<>0 then EXIT ; if ((Y mod 400)=0) or ((Y mod 100)<>0) then Z02 := True; end {Olaf van der Spek} ; function Z03(Y : word) : boolean ; FAR ; begin Z03 := ((Y and 3) = 0) and ((Y mod 400 = 0) or (Y mod 100 > 0)) end ; function Z04(Y : word) : boolean ; FAR ; begin Z04 := (Y and 3) = 0 ; if Y mod 100 = 0 then if Y div 100 in [19,21,22] then Z04 := FALSE ; end ; function Z05(Y : word) : boolean ; FAR ; begin Z05 := (Y mod 4 = 0) and not ((Y mod 100 = 0) and (Y mod 400 <> 0)) end ; function Z06(Y : word) : boolean ; FAR ; begin if Y mod 4 <> 0 then Z06 := false else if Y mod 100 <> 0 then Z06 := true else if Y mod 400 <> 0 then Z06 := false else Z06 := true ; end ; function Z07(Y : word) : boolean ; FAR ; begin if Y mod 4 <> 0 then Z07 := false else if Y mod 100 <> 0 then Z07 := true else Z07 := Y mod 400 = 0 ; end ; function Z08(Y : word) : boolean ; FAR ; begin Z08 := (Y mod 4 = 0) and ((Y mod 100 <> 0) or (Y mod 400 = 0)) end ; function TSF(Y : word) : boolean ; FAR ; { from TSFAQP #91 2000-01-08 } begin TSF := (Y mod 4 = 0) and not ((Y mod 100 = 0) and not (Y mod 400 = 0)) end ; function Z09(Y : word) : boolean ; FAR ; begin if Y mod 4 <> 0 then Z09 := false else Z09 := (Y mod 100 <> 0) or (Y mod 400 = 0) ; end ; function Z10(Y : word) : boolean ; FAR ; begin if (Y mod 4 <> 0) then Z10 := false else if Y mod 400 = 0 then Z10 := true else Z10 := Y mod 100 <> 0 ; end ; function Z11(Y : word) : boolean ; FAR ; begin Z11 := (Y mod 4 = 0) and ((Y mod 100 = 0) <= (Y mod 400 = 0)) end ; function ESB(Year : word) : boolean ; FAR ; begin ESB := false; if Year mod 400 = 0 then ESB := true else if (Year mod 4 = 0) and (Year mod 100 <> 0) then ESB := true ; end ; function Z12(Y : word) : boolean ; FAR ; (* suggested, but not recommended, by {R}, rearranged *) const divide : array [boolean] of word = (4, 400) ; begin Z12 := Y mod divide[Y mod 100 = 0] = 0 end ; function Z13(Y : word) : boolean ; FAR ; begin if Y mod 400 = 0 then Z13 := true else if Y mod 100 = 0 then Z13 := false else if Y mod 4 = 0 then Z13 := true else Z13 := false ; end ; function Z14(Y : word) : boolean ; FAR ; begin Z14 := not (Y mod 4 <> 0) xor (Y mod 100 <> 0) xor (Y mod 400 <> 0) end ; function Z15(Y : word) : boolean ; FAR ; begin Z15 := (Y mod 4 = 0) xor (Y mod 100 = 0) xor (Y mod 400 = 0) end ; function Z16(Y : word) : boolean ; FAR ; begin Z16 := (Y mod 4 = 0) <> ((Y mod 100 = 0) <> (Y mod 400 = 0)) end ; function Z17(Y : word) : boolean ; FAR ; begin Z17 := ((Y mod 4 = 0) = (Y mod 100 = 0)) = (Y mod 400 = 0) end ; function Z18(Y : word) : boolean ; FAR ; begin Z18 := (Y mod 4 = 0) = ((Y mod 100 = 0) = (Y mod 400 = 0)) end ; function Z19(Y : word) : boolean ; FAR ; begin Z19 := ((Y mod 4 = 0) <> (Y mod 100 = 0)) <> (Y mod 400 = 0) end ; function Z20(Y : word) : boolean ; FAR ; begin Z20 := {suggested, but not recommended, by Roy Brown} Odd(Ord(Y mod 4 = 0) + Ord(Y mod 100 = 0) + Ord(Y mod 400 = 0)) end ; type LeapFn = function(Y : word) : boolean ; const MinF = -5 ; MaxF = 24 ; FnAr : array [MinF..MaxF] of record F : LeapFn ; S : string [3] end = ( (F:Nul; S:'Nul'), (F:UGH; S:'UGH'), (F:Tbl; S:'Tbl'), (F:An3; S:'An3'), (F:Md4; S:'Md4'), (F:Ass; S:'Ass'), (F:Cen; S:'Cen'), (F:Z00; S:'Z00'), (F:Z01; S:'Z01'), (F:Z02; S:'Z02'), (F:Z03; S:'Z03'), (F:Z04; S:'Z04'), (F:Z05; S:'Z05'), (F:Z06; S:'Z06'), (F:Z07; S:'Z07'), (F:Z08; S:'Z08'), (F:TSF; S:'TSF'), (F:Z09; S:'Z09'), (F:Z10; S:'Z10'), (F:Z11; S:'Z11'), (F:ESB; S:'ESB'), (F:Z12; S:'Z12'), (F:Z13; S:'Z13'), (F:Z14; S:'Z14'), (F:Z15; S:'Z15'), (F:Z16; S:'Z16'), (F:Z17; S:'Z17'), (F:Z18; S:'Z18'), (F:Z19; S:'Z19'), (F:Z20; S:'Z20') ) ; procedure Test(Y : word) ; const Z : array [boolean] of char = 'ny' ; ZZ : array [0..1] of char = '-~' ; var C : char ; J : shortint ; L, L1 : boolean ; begin Write(Y:5) ; L1 := LArr[Y] ; for J := Succ(MinF) to MaxF do begin L := FnAr[J].F(Y) ; C := Z[L] ; if L<>L1 then C := UpCase(C) ; Write(C:2) end ; Writeln end {Test} ; procedure Perp ; const Yrs : array [1..12] of word = (1980, 1990, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2010, 2020, 2100) ; const Z : array [boolean] of char = 'ny' ; var J, Y : word ; K : integer ; L : boolean ; C : char ; begin Write('Function ') ; for J := Low(Yrs) to High(Yrs) do Write(Yrs[J]:5) ; Writeln ; for K := Succ(MinF) to MaxF do with FnAr[K] do begin Write(K:3, S:5) ; for J := Low(Yrs) to High(Yrs) do begin Y := Yrs[J] ; L := F(Y) ; C := Z[L] ; if L<>LArr[Y] then C := UpCase(C) ; Write(C:5) end ; Writeln end ; end {Perp} ; procedure TestAll ; const Z : array [boolean] of char = 'ny' ; var J : shortint ; L, L1 : boolean ; Y : word ; begin Writeln('TestAll:') ; for Y := YMin to YMax do begin Write(#13, Y:5) ; L1 := LArr[Y] ; for J := 0 to MaxF do with FnAr[J] do begin L := F(Y) ; if (L<>L1) xor (@F=@UGH) then begin Write(' ERROR! ', S:4, ' gave ', L:5, ' ') ; Readln end ; end ; end ; Writeln(#13, 'TestAll Done.') end {TestAll} ; function Timer : longint { P: 55ms; D: 1ms } ; {$IFDEF PASCAL} assembler ; {$ENDIF} {$IFDEF BORPAS} asm mov es,[Seg0040] ; @1: mov ax,[es:$6c] ; mov dx,[es:$6e] ; mov cl,[es:$70] cmp ax,[es:$6c] ; jne @1 cmp cl,0 ; je @2 add ax,$B0 ; adc dx,$18 ; @2: end {$ENDIF} {$IFDEF __TMT__} asm mov eax,[$046C] end {$ENDIF} {$IFDEF DELPHI} begin Timer := GetTickCount end {$ENDIF} {Timer} ; var Overhead : longint ; procedure Speed(J : shortint ; Ticks : longint) ; var L : boolean ; N, T : longint ; Y : YrT ; (* {$IFDEF PASCAL} Tix : longint absolute $40:$6C ; {$ENDIF not hour-safe} {$IFDEF DELPHI} function Tix : longint ; assembler ; { not midnight-safe } asm xor ax,ax ; Int $1A ; mov ax,dx ; mov dx,cx end {Tix} ; {$ENDIF} *) begin with FnAr[J] do begin Write(' Func ', S, '':2) ; repeat T := Timer until T<>Timer ; Inc(T, Ticks) ; N := 0 ; while Timer<=T do begin for Y := YMin to YMax do L := F(Y) ; Inc(N) end ; end ; Write('did', N:8, ' loops of ', YMin, -YMax) ; if J=MinF then Overhead := N ; if N>0 then Write(', relative Func time ', (Overhead/N-1.0):1:3) ; end {Speed} ; var Ticks : longint ; YY : word ; J : shortint ; K : byte ; BEGIN ; Writeln('LEAPYEAR.PAS >= 2000-06-03 www.merlyn.demon.co.uk ', {$IFDEF PASCAL} 'Pascal' {$ENDIF} {$IFDEF DELPHI} 'Delphi' {$ENDIF} , ^M^J' N.B. UGH is inverse & malformed!') ; for YY := YMin to YMax do LArr[YY] := Z15(YY) ; (* Write('':31) ; for J := 10 to MaxF do Write(J div 10:2) ; Writeln ; Write('Fn #':5) ; for J := Succ(MinF) to 9 do Write(J:2) ; {} for J := 10 to MaxF do Write(J mod 10:2) ; Writeln ; *) for K := 1 to 3 do begin Write('':5) ; for J := Succ(MinF) to MaxF do Write(FnAr[J].S[K]:2) ; Writeln end ; Test(1980) ; Test(1990) ; for YY := 1999 to 2005 do Test(YY) ; Test(2010) ; Test(2020) ; Test(2100) ; { Perp ; } TestAll ; repeat Write(' (0=Next) Year (', Ymin, '-', Ymax, ') ? ') ; Readln(YY) ; if YY=0 then BREAK ; Test(YY) until false ; Writeln(' OK') ; Write('Test Time in Ticks (P:55ms, D:1ms) ? ') ; Readln(Ticks) ; Writeln(' Preloading any cache:') ; for J := MinF to MaxF do begin Speed(J, 1) ; Write(#13) end ; Speed(MinF, Ticks) ; Writeln(#13' Done.', '':73) ; for J := MinF to MaxF do begin Speed(J, Ticks) ; Writeln end ; Write(' Done') ; Readln ; END.