import Test.Hspec

-- Dummy implementation for testing
slow_sqrs_sum :: [Int] -> Int
slow_sqrs_sum a = foldr (+) 0 (map (^2) a)

slow_sum_sqr :: [Int] -> Int
slow_sum_sqr a = (sum a) ^ 2

slow_sum_sqr_diff :: [Int] -> Int
slow_sum_sqr_diff a = (slow_sum_sqr a) - (slow_sqrs_sum a)

-- Fast emplementation using math
-- We need to calculate (1 + 2 + ... + n)^2 - (1^2 + 2^2 + ... + n^2)
-- If we open first square we'll get:
-- x1^2 + x2^2 + ... + xn^2 + 2*(x1[x2..xn] + x2[x3..xn] + xn-1 * xn
-- So diff will get rid of first sum of squares and well have only 2*(...) part
fast_sum_sqr_diff :: [Int] -> Int
fast_sum_sqr_diff a = 2 * foldl (+) 0 (
        foldr (++) [] [map (*x) (filter (>x) a) | x <- a]
    )

-- Tests
run_test = hspec $ do
    describe "Dummy" $ do
        it "dummy test" $ do
            True `shouldBe` True
    describe "Euler test" $ do
        it "sqrs sum" $ do
            slow_sqrs_sum [1..10] `shouldBe` 385
            slow_sum_sqr [1..10] `shouldBe` 3025
            slow_sum_sqr_diff [1..10] `shouldBe` 2640
            fast_sum_sqr_diff [1..10] `shouldBe` 2640
    describe "Unit" $ do
        it "Making sure fast implementation has same results" $ do
            fast_sum_sqr_diff [1..4]   `shouldBe` slow_sum_sqr_diff [1..4]
            fast_sum_sqr_diff [1..5]   `shouldBe` slow_sum_sqr_diff [1..5]
            fast_sum_sqr_diff [1..6]   `shouldBe` slow_sum_sqr_diff [1..6]
            fast_sum_sqr_diff [1..7]   `shouldBe` slow_sum_sqr_diff [1..7]
            fast_sum_sqr_diff [1..8]   `shouldBe` slow_sum_sqr_diff [1..8]
            fast_sum_sqr_diff [1..9]   `shouldBe` slow_sum_sqr_diff [1..9]
            fast_sum_sqr_diff [1..10]  `shouldBe` slow_sum_sqr_diff [1..10]
            fast_sum_sqr_diff [1..50]  `shouldBe` slow_sum_sqr_diff [1..50]
            fast_sum_sqr_diff [1..100] `shouldBe` slow_sum_sqr_diff [1..100]

-- res = slow_sum_sqr_diff [1..100]
res = fast_sum_sqr_diff [1..100]

-- Main
main = do
    run_test
    putStrLn ("res = " ++ show res)
