|
3 | 3 | -- Description: <https://adventofcode.com/2024/day/5 Day 5: Print Queue>
|
4 | 4 | module Day5 (part1, part2) where
|
5 | 5 |
|
6 |
| -import Control.Arrow (second) |
7 |
| -import Data.IntMap qualified as IntMap (findWithDefault, fromList, (!?)) |
8 |
| -import Data.IntMap.Strict qualified as IntMap (fromListWith) |
9 |
| -import Data.IntSet qualified as IntSet (empty, member, singleton, toList, union) |
10 |
| -import Data.List (sortBy) |
11 |
| -import Data.Maybe (fromJust, fromMaybe) |
| 6 | +import Data.List (tails) |
| 7 | +import Data.Set (Set) |
| 8 | +import Data.Set qualified as Set (fromList, notMember) |
12 | 9 | import Data.String (IsString)
|
13 | 10 | import Data.Text (Text)
|
| 11 | +import Data.Vector.Generic (Vector) |
| 12 | +import Data.Vector.Generic qualified as V (fromList, length, modify, (!)) |
| 13 | +import Data.Vector.Generic.Mutable qualified as MV (length, read, write) |
| 14 | +import Data.Vector.Unboxed qualified as UV (Vector) |
14 | 15 | import Data.Void (Void)
|
15 | 16 | import Text.Megaparsec (MonadParsec, ParseErrorBundle, Stream (Token, Tokens), parse, sepEndBy, sepEndBy1, skipMany)
|
16 | 17 | import Text.Megaparsec.Char (char, newline)
|
17 | 18 | import Text.Megaparsec.Char.Lexer (decimal)
|
18 | 19 |
|
19 |
| -parser :: (MonadParsec e s m, IsString (Tokens s), Token s ~ Char, Num a) => m ([(a, a)], [[a]]) |
| 20 | +parser :: (MonadParsec e s m, IsString (Tokens s), Token s ~ Char, Num a, Ord a) => m (Set (a, a), [[a]]) |
20 | 21 | parser =
|
21 |
| - (,) |
| 22 | + (,) . Set.fromList |
22 | 23 | <$> ((,) <$> decimal <* char '|' <*> decimal) `sepEndBy` newline
|
23 | 24 | <* skipMany newline
|
24 | 25 | <*> (decimal `sepEndBy1` char ',') `sepEndBy` newline
|
25 | 26 |
|
26 | 27 | part1 :: Text -> Either (ParseErrorBundle Text Void) Int
|
27 | 28 | part1 input = do
|
28 | 29 | (deps, updates) <- parse parser "" input
|
29 |
| - pure $ |
30 |
| - sum |
31 |
| - [ update !! (length update `div` 2) |
32 |
| - | update <- updates, |
33 |
| - let order = IntMap.fromList @Int $ zip update [0 ..], |
34 |
| - and |
35 |
| - [ fromMaybe True $ (<) <$> order IntMap.!? a <*> order IntMap.!? b |
36 |
| - | (a, b) <- deps |
37 |
| - ] |
38 |
| - ] |
| 30 | + pure . sum $ |
| 31 | + [ update !! (length update `div` 2) |
| 32 | + | update <- updates, |
| 33 | + and [(b, a) `Set.notMember` deps | a : rest <- tails update, b <- rest] |
| 34 | + ] |
39 | 35 |
|
40 | 36 | part2 :: Text -> Either (ParseErrorBundle Text Void) Int
|
41 | 37 | part2 input = do
|
42 | 38 | (deps, updates) <- parse parser "" input
|
43 |
| - let deps' = IntMap.fromListWith IntSet.union $ second IntSet.singleton <$> deps |
44 |
| - tryCompare a b |
45 |
| - | a == b = Just EQ |
46 |
| - | b `IntSet.member` (IntMap.findWithDefault IntSet.empty a deps') = Just LT |
47 |
| - | a `IntSet.member` (IntMap.findWithDefault IntSet.empty b deps') = Just GT |
48 |
| - | otherwise = |
49 |
| - mconcat (tryCompare a <$> maybe [] IntSet.toList (deps' IntMap.!? b)) |
50 |
| - <> fmap (compare EQ) (mconcat (tryCompare b <$> maybe [] IntSet.toList (deps' IntMap.!? a))) |
51 |
| - compare' a b = fromJust $ tryCompare a b |
52 |
| - pure $ |
53 |
| - sum |
54 |
| - [ update' !! (length update `div` 2) |
55 |
| - | update <- updates, |
56 |
| - let update' = sortBy compare' update, |
57 |
| - update /= update' |
58 |
| - ] |
| 39 | + let ok a b = (b, a) `Set.notMember` deps |
| 40 | + pure . sum $ |
| 41 | + [ pages V.! (V.length pages `div` 2) |
| 42 | + | update <- V.fromList @UV.Vector @Int <$> updates, |
| 43 | + let pages = sort' ok update, |
| 44 | + update /= pages |
| 45 | + ] |
| 46 | + |
| 47 | +sort' :: (Vector v a) => (a -> a -> Bool) -> v a -> v a |
| 48 | +sort' ok = V.modify $ \v -> |
| 49 | + let go i j |
| 50 | + | j < MV.length v = do |
| 51 | + x <- MV.read v i |
| 52 | + y <- MV.read v j |
| 53 | + if ok x y |
| 54 | + then go i (j + 1) |
| 55 | + else do |
| 56 | + MV.write v i y |
| 57 | + MV.write v j x |
| 58 | + go i (i + 1) |
| 59 | + | i < MV.length v = go (i + 1) (i + 2) |
| 60 | + | otherwise = pure () |
| 61 | + in go 0 1 |
0 commit comments