Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automate formatting #9

Merged
merged 1 commit into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/hs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,26 @@ jobs:
name: aoc2024-hs
path: ${{ steps.build.outputs.exe }}

lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: haskell-actions/hlint-setup@v2
- uses: haskell-actions/hlint-run@v2
with:
fail-on: warning
path: hs
- uses: haskell-actions/run-ormolu@v16
with:
pattern: |
hs/**/*.hs
hs/**/*.hs-boot
- uses: tfausak/cabal-gild-setup-action@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
- run: cabal-gild --input hs/aoc2024.cabal --mode check

run:
needs: [ get-inputs, build ]
runs-on: ubuntu-latest
Expand Down
14 changes: 14 additions & 0 deletions hs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,17 @@ Run [hlint](https://github.com/ndmitchell/hlint) source code suggestions:
cabal install hlint
hlint src test bench
```

Run [ormolu](https://github.com/tweag/ormolu) formatting:

```sh
cabal install ormolu
git ls-files -coz '*.hs' | xargs -0 ormolu --mode inplace
```

Run [cabal-gild](https://github.com/tfausak/cabal-gild) formatting:

```sh
cabal install cabal-gild
cabal-gild -i aoc2024.cabal -o aoc2024.cabal
```
144 changes: 82 additions & 62 deletions hs/aoc2024.cabal
Original file line number Diff line number Diff line change
@@ -1,74 +1,94 @@
cabal-version: 3.0

name: aoc2024
version: 0.1.0.0
cabal-version: 3.0
name: aoc2024
version: 0.1.0.0
synopsis:
Please see the README on GitHub at <https://github.com/ephemient/aoc2024/blob/main/hs/README.md>
homepage: https://github.com/ephemient/aoc2024/tree/main/hs
license: BSD-3-Clause
license-file: LICENSE
author: Daniel Lin
maintainer: [email protected]
category: None
build-type: Simple
Please see the README on GitHub at <https://github.com/ephemient/aoc2024/blob/main/hs/README.md>

homepage: https://github.com/ephemient/aoc2024/tree/main/hs
license: BSD-3-Clause
license-file: LICENSE
author: Daniel Lin
maintainer: [email protected]
category: None
build-type: Simple
extra-source-files: README.md

source-repository head
type: git
location: https://github.com/ephemient/aoc2024.git
subdir: hs
type: git
location: https://github.com/ephemient/aoc2024.git
subdir: hs

library
hs-source-dirs: src
exposed-modules:
Day1
other-modules:
Common
build-depends:
base ^>=4.20.0.0,
containers ^>=0.7,
text ^>=2.1.1
ghc-options: -Wall
default-language: GHC2024
hs-source-dirs: src
exposed-modules:
Day1

other-modules:
Common

build-depends:
base ^>=4.20.0.0,
containers ^>=0.7,
text ^>=2.1.1,

ghc-options: -Wall
default-language: GHC2024

executable aoc2024
hs-source-dirs: app
main-is: Main.hs
c-sources: app/cbits/main.c
build-depends:
aoc2024,
base ^>=4.20.0.0,
filepath ^>=1.5.2.0,
text ^>=2.1.1
ghc-options: -no-hs-main -threaded -Wall
default-language: GHC2024
hs-source-dirs: app
main-is: Main.hs
c-sources: app/cbits/main.c
build-depends:
aoc2024,
base ^>=4.20.0.0,
filepath ^>=1.5.2.0,
text ^>=2.1.1,

ghc-options:
-no-hs-main
-threaded
-Wall

default-language: GHC2024

test-suite aoc2024-test
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Main.hs
other-modules:
Day1Spec
build-depends:
aoc2024,
base ^>=4.20.0.0,
hspec ^>=2.11.10,
text ^>=2.1.1
build-tool-depends:
hspec-discover:hspec-discover ^>=2.11.10
ghc-options: -threaded -rtsopts "-with-rtsopts=-N" -Wall
default-language: GHC2024
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Main.hs
other-modules:
Day1Spec

build-depends:
aoc2024,
base ^>=4.20.0.0,
hspec ^>=2.11.10,
text ^>=2.1.1,

build-tool-depends:
hspec-discover:hspec-discover ^>=2.11.10

ghc-options:
-threaded
-rtsopts
-with-rtsopts=-N
-Wall

default-language: GHC2024

benchmark aoc2024-bench
type: exitcode-stdio-1.0
hs-source-dirs: bench
main-is: Main.hs
c-sources: bench/cbits/main.c
build-depends:
aoc2024,
base ^>=4.20.0.0,
criterion ^>=1.6.4.0,
filepath ^>=1.5.2.0,
text ^>=2.1.1
ghc-options: -no-hs-main -threaded
default-language: GHC2024
type: exitcode-stdio-1.0
hs-source-dirs: bench
main-is: Main.hs
c-sources: bench/cbits/main.c
build-depends:
aoc2024,
base ^>=4.20.0.0,
criterion ^>=1.6.4.0,
filepath ^>=1.5.2.0,
text ^>=2.1.1,

ghc-options:
-no-hs-main
-threaded

default-language: GHC2024
17 changes: 8 additions & 9 deletions hs/app/Main.hs
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
{-# LANGUAGE NondecreasingIndentation #-}
module Main (main) where

import qualified Day1 (part1, part2)
module Main (main) where

import Control.Monad (ap, when)
import Data.Foldable (find)
import Data.Function (on)
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text.IO as TIO (putStrLn, readFile)
import Data.Text.IO qualified as TIO (readFile)
import Day1 qualified (part1, part2)
import System.Environment (getArgs, lookupEnv)
import System.FilePath (combine)

getDayInput :: Int -> IO Text
getDayInput i = do
dataDir <- fromMaybe "." . find (not . null) <$> lookupEnv "AOC2024_DATADIR"
TIO.readFile . combine dataDir $ "day" ++ show i ++ ".txt"
dataDir <- fromMaybe "." . find (not . null) <$> lookupEnv "AOC2024_DATADIR"
TIO.readFile . combine dataDir $ "day" ++ show i ++ ".txt"

run :: Int -> (a -> IO ()) -> [Text -> a] -> IO ()
run = run' `ap` show

run' :: Int -> String -> (a -> IO ()) -> [Text -> a] -> IO ()
run' day name showIO funcs = do
args <- getArgs
when (null args || name `elem` args) $ do
args <- getArgs
when (null args || name `elem` args) $ do
putStrLn $ "Day " ++ name
contents <- getDayInput day
mapM_ (showIO . ($ contents)) funcs
putStrLn ""

main :: IO ()
main = do
run 1 (either fail print) [Day1.part1, Day1.part2]
run 1 (either fail print) [Day1.part1, Day1.part2]
23 changes: 13 additions & 10 deletions hs/bench/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Criterion.Main (bench, bgroup, defaultMain, env, envWithCleanup, nf)
import Data.Foldable (find)
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text.IO as TIO (readFile)
import qualified Day1 (part1, part2)
import Data.Text.IO qualified as TIO (readFile)
import Day1 qualified (part1, part2)
import System.Environment.Blank (getEnv, setEnv, unsetEnv)
import System.FilePath (combine)

Expand All @@ -18,13 +18,16 @@ unsetTrace = maybe (unsetEnv "TRACE") (setEnv "TRACE" `flip` True)

getDayInput :: Int -> IO Text
getDayInput i = do
dataDir <- fromMaybe "." . find (not . null) <$> getEnv "AOC2024_DATADIR"
TIO.readFile . combine dataDir $ "day" ++ show i ++ ".txt"
dataDir <- fromMaybe "." . find (not . null) <$> getEnv "AOC2024_DATADIR"
TIO.readFile . combine dataDir $ "day" ++ show i ++ ".txt"

main :: IO ()
main = defaultMain
[ env (getDayInput 1) $ \input -> bgroup "Day 1"
[ bench "part 1" $ nf Day1.part1 input
, bench "part 2" $ nf Day1.part2 input
]
]
main =
defaultMain
[ env (getDayInput 1) $ \input ->
bgroup
"Day 1"
[ bench "part 1" $ nf Day1.part1 input,
bench "part 2" $ nf Day1.part2 input
]
]
17 changes: 9 additions & 8 deletions hs/src/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@ module Common (readEntire, readMany, readSome) where

import Control.Arrow (first)
import Data.Char (isSpace)
import Data.List.NonEmpty (NonEmpty((:|)))
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Text (Text)
import qualified Data.Text as T (dropWhile, null)
import Data.Text qualified as T (dropWhile, null)
import Data.Text.Read (Reader)

readEntire :: Reader a -> Text -> Either String a
readEntire reader input = do
(a, t) <- reader input
if T.null t then Right a else Left "incomplete read"
(a, t) <- reader input
if T.null t then Right a else Left "incomplete read"

readMany :: Reader a -> Reader [a]
readMany reader = pure . readMany' id where
readMany reader = pure . readMany' id
where
readMany' k input =
either (const (k [], input)) (uncurry $ readMany' . (.) k . (:)) . reader $
either (const (k [], input)) (uncurry $ readMany' . (.) k . (:)) . reader $
T.dropWhile isSpace input

readSome :: Reader a -> Reader (NonEmpty a)
readSome reader input = do
(a, input') <- reader input
first (a :|) <$> readMany reader input'
(a, input') <- reader input
first (a :|) <$> readMany reader input'
29 changes: 14 additions & 15 deletions hs/src/Day1.hs
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
{-|
Module: Day1
Description: <https://adventofcode.com/2024/day/1 Day 1: Historian Hysteria>
-}
-- |
-- Module: Day1
-- Description: <https://adventofcode.com/2024/day/1 Day 1: Historian Hysteria>
module Day1 (part1, part2) where

import Common (readEntire)
import Data.Either (fromLeft)
import Data.Function (on)
import qualified Data.IntMap as IntMap (fromListWith, findWithDefault)
import Data.IntMap qualified as IntMap (findWithDefault)
import Data.IntMap.Strict qualified as IntMap (fromListWith)
import Data.List (sort, transpose)
import Data.Text (Text)
import qualified Data.Text as T (lines, words)
import qualified Data.Text.Read as T (decimal)
import Data.Text qualified as T (lines, words)
import Data.Text.Read qualified as T (decimal)

parse :: Text -> Either String [[Int]]
parse = fmap transpose . mapM (mapM (readEntire T.decimal) . T.words) . T.lines

part1 :: Text -> Either String Int
part1 input = case parse input of
Left err -> Left err
Right [as, bs] -> pure $ sum $ abs <$> (zipWith (-) `on` sort) as bs
_ -> Left "no parse"
Right [as, bs] -> pure $ sum $ abs <$> (zipWith (-) `on` sort) as bs
other -> Left $ fromLeft "no parse" other

part2 :: Text -> Either String Int
part2 input = case parse input of
Left err -> Left err
Right [as, bs] ->
let cs = IntMap.fromListWith (($!) . (+)) [(b, 1) | b <- bs]
in pure $ sum [a * IntMap.findWithDefault 0 a cs | a <- as]
_ -> Left "no parse"
Right [as, bs] ->
let cs = IntMap.fromListWith (+) [(b, 1) | b <- bs]
in pure $ sum [a * IntMap.findWithDefault 0 a cs | a <- as]
other -> Left $ fromLeft "no parse" other
Loading