## Is a Year a Leap Year?

This program determines if the first argument passed to it is a leap year, and prints the result.

A year is a leap year if it is divisible by 4, unless it is divisible by 100.
However, years divisible by 400 *are* leap years.

The program internally uses a method that throws one of two exceptions: one
if the year is a leap year, and one if it isn't. Because these exception
classes don't do anything different from the built-in `Exception`
class, they don't need to override any methods; they can simply be declared
and used.

To convert the command-line parameter from a string to a number, the program
uses the `static` method `parseLong()` from the class
`Long`, which is a class that wraps the primitive type `long`.
Because `parseLong()` is a `static` method, it is not called on an
instance of the class.

`parseLong()` is defined to throw `NumberFormatException` if
the input string cannot be converted to a number. Because
`checkLeapYear()` throws the two user-defined exceptions and, thus,
typically is called inside a `try` block, it is not too much work to also
catch `NumberFormatException`.

`NumberFormatException` is a runtime exception and, therefore,
does not have to be listed in the `throws` clause of the declaration
of `checkLeapYear()`, but it is included because throwing that exception
is the designated way to handle an invalid input.

### Source Code

1.public class IsLeapYear {2.3.public static class LeapYearException4.extends Exception {}5.public static class NotLeapYearException6.extends Exception {}7.8.static void checkLeapYear(String year)9.throws LeapYearException, NotLeapYearException,10.NumberFormatException {11.12.long yearAsLong = Long.parseLong(year);13.14.//15.// A leap year is a multiple of 4, unless it is16.// a multiple of 100, unless it is a multiple of17.// 400.18.//19.// We calculate the three values, then make a20.// 3-bit binary value out of them and look it up21.// in results.22.//23.24.final boolean results[] =25.{ true, false, false, true,26.false, false, false, false };27.28.if (results[29.((((yearAsLong % 4) == 0) ? 1 : 0) << 2) +30.((((yearAsLong % 100) == 0) ? 1 : 0) << 1) +31.((((yearAsLong % 400) == 0) ? 1 : 0) << 0)]) {32.throw new LeapYearException();33.} else {34.throw new NotLeapYearException();35.}36.}37.38.public static void main(String[] args) {39.40.if (args.length > 0) {41.42.try {43.checkLeapYear(args[0]);44.} catch ( NumberFormatException nfe ) {45.System.out.println(46."Invalid argument: " +47.nfe.getMessage());48.} catch ( LeapYearException lye ) {49.System.out.println(50.args[0] + " is a leap year");51.} catch ( NotLeapYearException nlye ) {52.System.out.println(53.args[0] + " is not a leap year");54.}55.}56.}57.}

### Suggestions

What exactly does it mean if a bit is on in

`results`?Because the value computed on lines 29–31 is immediately used to index into

`results`, an array of size 8, is it guaranteed that this value will be properly restricted so as to not produce an invalid array index?How many possible kinds of years are there, and given that it is less than the size of

`results`, are there certain values for the index into`results`that will never occur?

### Hints

Walk through `main()` with the following values for
`args[0]`:

Multiple of 100 is not a leap year:

`"1900"`Multiple of 4 is a leap year:

`"1904"`Multiple of 400 is a leap year:

`"2000"`Any other year is not a leap year:

`"2001"`

### Explanation of the Bug

Not surprisingly, the problem is in the declaration of the `results`
array on lines 24–26. It is reversed; that is, it is declared as if the
calculation of the index on lines 29–31 had every bit flipped, which is an
**F.init** error. The proper declaration should be as follows:

private static final boolean results[] = { false, false, false, false, true, false, false, true };

Alternately, the assignment of the bits on lines 29–31 could be flipped.

With the bits assigned as-is, if a year is divisible by 400, the low bit in
the index will be on, which means that the index in binary is in the form xx1.
Because such years are also divisible by 4 and 100, the next two bits are also
on. Thus, indices 1, 3, and 5 in `results` won't ever be used. Years
divisible by 400 always wind up with an index of 7 (binary 111).

If year is not divisible by 400, but is divisible by 100, the low bit is off, but the second bit is on, so the index is in the form x10. Because such a year is also divisible by 4, the index is always 6 (binary 110), and index 2 is never used.

For the rest of the years, those not divisible by 100 (or 400), the index is in the form x00. Years divisible by 4 result in an index of 4 (binary 100), and those not divisible by 4 result in an index of 0.

Therefore, the only indices that matter are 0, 4, 6, and 7. Of those, 0 and 6 should be false (not leap years), and 4 and 7 should be true (leap years). Because of the bug, those indices had their bits flipped, so 7 and 1 were false, and 3 and 0 were true. The "unused" indices (2, 4, 5, and 6) were also set to false.

Thus, the program as written would, by chance, work if a year was divisible by 100 but not by 400 (a year such as 1900), correctly reporting, based on the value of index 6, that such a year was not a leap year. It would misrepresent the years 2000 (index 7) and 2004 (index 4) as not being leap years, and the year 2001 (index 0) as being a leap year.