-- SPDX-License-Identifier: Apache-2.0
-- Copyright 2025 Greeting Project
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

-- Haskell solution for LeetCode 2 Sum problem
-- Given an array of integers nums and an integer target, 
-- return indices of the two numbers such that they add up to target.

import qualified Data.Map as Map
import Data.Maybe (fromJust)

-- Brute force solution - O(n^2) time complexity
twoSumBruteForce :: [Int] -> Int -> Maybe (Int, Int)
twoSumBruteForce nums target = findPair (zip nums [0..]) target
  where
    findPair [] _ = Nothing
    findPair ((x, i):xs) t = 
      case findComplement xs (t - x) of
        Just j -> Just (i, j)
        Nothing -> findPair xs t
    
    findComplement [] _ = Nothing
    findComplement ((y, j):ys) complement
      | y == complement = Just j
      | otherwise = findComplement ys complement

-- Optimized solution using HashMap - O(n) time complexity
twoSumOptimized :: [Int] -> Int -> Maybe (Int, Int)
twoSumOptimized nums target = twoSumHelper nums target Map.empty 0
  where
    twoSumHelper [] _ _ _ = Nothing
    twoSumHelper (x:xs) t seen i =
      let complement = t - x
      in case Map.lookup complement seen of
           Just j -> Just (j, i)
           Nothing -> twoSumHelper xs t (Map.insert x i seen) (i + 1)

-- Helper function to format output
formatResult :: Maybe (Int, Int) -> String
formatResult Nothing = "No solution found"
formatResult (Just (i, j)) = "Indices: [" ++ show i ++ ", " ++ show j ++ "]"

-- Test cases
testCases :: [([Int], Int)]
testCases = 
  [ ([2, 7, 11, 15], 9)    -- Expected: [0, 1]
  , ([3, 2, 4], 6)         -- Expected: [1, 2]
  , ([3, 3], 6)            -- Expected: [0, 1]
  , ([1, 2, 3, 4, 5], 8)   -- Expected: [2, 4]
  , ([1, 2, 3], 7)         -- Expected: No solution
  ]

main :: IO ()
main = do
    putStrLn "=== LeetCode 2 Sum Problem Solution ==="
    putStrLn ""
    putStrLn "Problem: Given an array of integers and a target sum,"
    putStrLn "find two numbers that add up to the target and return their indices."
    putStrLn ""
    
    mapM_ runTest (zip [1..] testCases)
    
    putStrLn ""
    putStrLn "=== Performance Comparison ==="
    putStrLn "Brute Force: O(n²) time, O(1) space"
    putStrLn "Optimized:   O(n) time, O(n) space"
  where
    runTest (testNum, (nums, target)) = do
      putStrLn $ "Test Case " ++ show testNum ++ ":"
      putStrLn $ "  Array: " ++ show nums
      putStrLn $ "  Target: " ++ show target
      
      let bruteForcResult = twoSumBruteForce nums target
      let optimizedResult = twoSumOptimized nums target
      
      putStrLn $ "  Brute Force Result: " ++ formatResult bruteForcResult
      putStrLn $ "  Optimized Result:   " ++ formatResult optimizedResult
      putStrLn ""