{-# Language QuasiQuotes #-}
{-|
Module      : Main
Description : Day 6 solution
Copyright   : (c) Eric Mertens, 2023
License     : ISC
Maintainer  : emertens@gmail.com

<https://adventofcode.com/2023/day/6>

This problem asks us to consider the time we should spend
charging up a toy car to beat a target distance. The distance
the car will travel is a quadratic equation. What we end up
doing is finding the distance between the roots of the function.

-- >>> :{
:main +
"Time:      7  15   30
Distance:  9  40  200
"
:}
288
71503

-}
module Main (main) where

import Advent (format, binSearchLargest)

-- |
--
-- >>> :main
-- 281600
-- 33875953
main :: IO ()
IO ()
main =
 do (times, distances) <- [format|2023 6 Time:( +%d!)*%nDistance:( +%d!)*%n|]
    let input1 = [Int] -> [Int] -> [(Int, Int)]
forall a b. [a] -> [b] -> [(a, b)]
zip (((String, Int) -> Int) -> [(String, Int)] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (String, Int) -> Int
forall a b. (a, b) -> b
snd [(String, Int)]
times) (((String, Int) -> Int) -> [(String, Int)] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (String, Int) -> Int
forall a b. (a, b) -> b
snd [(String, Int)]
distances)
        input2 = (String -> Int
forall a. Read a => String -> a
read (((String, Int) -> String) -> [(String, Int)] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (String, Int) -> String
forall a b. (a, b) -> a
fst [(String, Int)]
times), String -> Int
forall a. Read a => String -> a
read (((String, Int) -> String) -> [(String, Int)] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (String, Int) -> String
forall a b. (a, b) -> a
fst [(String, Int)]
distances))
    print (product (map ways input1))
    print (ways input2)

ways :: (Int, Int) -> Int
ways :: (Int, Int) -> Int
ways (Int
t, Int
d)
  | Int -> Bool
valid Int
mid = Int
hi Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
tooLo
  | Bool
otherwise = Int
0
  where
    valid :: Int -> Bool
valid Int
hold = (Int
t Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
hold) Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
hold Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
d
    mid :: Int
mid = Int
t Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2 -- the midpoint is the best we can get
    tooLo :: Int
tooLo = (Int -> Bool) -> Int -> Int -> Int
binSearchLargest (Bool -> Bool
not (Bool -> Bool) -> (Int -> Bool) -> Int -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Bool
valid)   Int
0 Int
mid
    hi :: Int
hi    = (Int -> Bool) -> Int -> Int -> Int
binSearchLargest        Int -> Bool
valid  Int
mid   Int
t