Project Euler Problem 19

http://projecteuler.net/index.php?section=problems&id=19
20世紀の月初めの日曜日の数を求める。

0を日曜、6を土曜とする。

main = print $ euler19

euler19 :: Int
euler19 = euler19' 1900 1 1
            where euler19' y m w = if y == 2001 then 0 else
                                   let w' = (firstOfNextMonth y m w)
                                       n  = nextMonth (y,m)
                                   in (if ((w' == 0) && (y /= 1900)) then 1 else 0) + euler19' (fst n) (snd n) w'

nextMonth :: (Int, Int) -> (Int,Int)
nextMonth (y,m) = if m == 12 then (y+1,1) else (y,m+1)

--年->月->月初めの曜日->次の月の月初めの曜日
firstOfNextMonth :: Int -> Int -> Int -> Int
firstOfNextMonth y m w
         | elem m [4,6,9,11]        = ((w+30) `mod` 7)
         | elem m [1,3,5,7,8,10,12] = ((w+31) `mod` 7)
         | m == 2                   = if isLeapYear y
                                      then ((w+29) `mod` 7)
                                      else ((w+28) `mod` 7)
         | otherwise                = -1


isLeapYear :: Int -> Bool
isLeapYear y = ((y `mod` 4) == 0) && not (((y `mod` 400) /= 0) && ((y `mod` 100) == 0))

not関数

ツェラーの公式使用ばーじょん

main = print $ length $ filter (\(x,y) -> (zeller x y 1) == 0) [(y,m) | y <- [1901..2000], m <- [1..12]]

zeller :: Int -> Int -> Int -> Int
zeller y m d = if m < 3
               then zeller (y-1) (m+12) d
               else (y + (y `div` 4) - (y `div` 100) + (y `div` 400) + ((13*m + 8) `div` 5) + d) `mod` 7

1200を7で割っても出るとは不思議ですね。