diff --git a/2020/16-Ticket_Translation/example2 b/2020/16-Ticket_Translation/example2
new file mode 100644
index 0000000..3245817
--- /dev/null
+++ b/2020/16-Ticket_Translation/example2
@@ -0,0 +1,11 @@
+departure class: 0-1 or 4-19
+departure row: 0-5 or 8-19
+seat: 0-13 or 16-19
+
+your ticket:
+11,12,13
+
+nearby tickets:
+3,9,18
+15,1,5
+5,14,9
diff --git a/2020/16-Ticket_Translation/second.hs b/2020/16-Ticket_Translation/second.hs
new file mode 100644
index 0000000..12b55fe
--- /dev/null
+++ b/2020/16-Ticket_Translation/second.hs
@@ -0,0 +1,113 @@
+-- requires cabal install --lib megaparsec parser-combinators
+module Main (main) where
+import Control.Monad (void, when)
+import Data.List (foldl', isPrefixOf, transpose)
+import Data.Map qualified as M
+import Data.Void (Void)
+import Text.Megaparsec
+import Text.Megaparsec.Char
+import System.Exit (die)
+
+exampleExpectedOutput = 132
+
+data Range = Range { field :: String
+                   , ll :: Int
+                   , lh :: Int
+                   , hl :: Int
+                   , hh :: Int
+                   } deriving (Show)
+type Ticket = [Int]
+data Input = Input { ranges :: [Range]
+                   , myTicket :: Ticket
+                   , nearbyTickets :: [Ticket]
+                   } deriving (Show)
+
+type Parser = Parsec Void String
+
+parseInt :: Parser Int
+parseInt = do
+  n <- some digitChar
+  void $ optional (char ',')
+  return $ read n
+
+parseRange :: Parser Range
+parseRange = do
+  f <- some (letterChar <|> char ' ')
+  void $ string ": "
+  ll <- parseInt
+  void $ char '-'
+  lh <- parseInt
+  void $ string " or "
+  hl <- parseInt
+  void $ char '-'
+  hh <- parseInt
+  void $ char '\n'
+  return $ Range f ll lh hl hh
+
+parseTicket :: Parser Ticket
+parseTicket = some parseInt <* char '\n'
+
+parseInput' :: Parser Input
+parseInput' = do
+  r <- some parseRange
+  void $ string "\nyour ticket:\n"
+  t <- parseTicket
+  void $ string "\nnearby tickets:\n"
+  nts <- some parseTicket
+  void eof
+  return $ Input r t nts
+
+parseInput :: String -> IO Input
+parseInput filename = do
+  input <- readFile filename
+  case runParser parseInput' filename input of
+    Left bundle -> die $ errorBundlePretty bundle
+    Right ops -> return ops
+
+filterInvalid :: Input -> Input
+filterInvalid (Input ranges ticket nearbyTickets) = Input ranges ticket (foldl' compute' [] nearbyTickets)
+  where
+    compute' :: [Ticket] -> Ticket -> [Ticket]
+    compute' acc nt
+      | and $ map isValid nt = nt : acc
+      | otherwise = acc
+    isValid :: Int -> Bool
+    isValid n = or $ map (isValid' n) ranges
+    isValid' :: Int -> Range -> Bool
+    isValid' n (Range{ll, lh, hl, hh}) = (n >= ll && n <= lh) || (n >= hl && n <= hh)
+
+compute' :: Input -> Int
+compute' Input{ranges, myTicket, nearbyTickets} = product . map (myTicket !!) . map snd . filter importantRange . snd $ iterate (potentialFieldIDsByRange, [])
+  where
+    importantRange :: (Range, Int) -> Bool
+    importantRange (Range{field}, _) = isPrefixOf "departure" field
+    nearbyTicketsByField = transpose nearbyTickets
+    iterate :: ([(Range, [Int])], [(Range, Int)]) -> ([(Range, [Int])], [(Range, Int)])
+    iterate ([], res) = ([], res)
+    iterate ((r, [i]):ps, res) = iterate (map (removeID i) ps, (r, i):res)
+    iterate (p:ps, res) = iterate (ps ++ [p], res)
+    removeID :: Int -> (Range, [Int]) -> (Range, [Int])
+    removeID i (r, l) = (r, filter (/= i) l)
+    potentialFieldIDsByRange :: [(Range, [Int])]
+    potentialFieldIDsByRange = zip ranges $ map (getMatchingFieldIDs myTicket nearbyTicketsByField) ranges
+    getMatchingFieldIDs :: Ticket -> [Ticket] -> Range -> [Int]
+    getMatchingFieldIDs [] [] _ = []
+    getMatchingFieldIDs (_:ts) (fields:fieldss) range
+      | and (map (match range) fields) = myId : getMatchingFieldIDs ts fieldss range
+      | otherwise = getMatchingFieldIDs ts fieldss range
+      where
+        myId :: Int
+        myId = (length myTicket) - (length ts) - 1
+    match :: Range -> Int -> Bool
+    match Range{ll, lh, hl, hh} n = (n >= ll && n <= lh) || (n >= hl && n <= hh)
+
+compute :: Input -> Int
+compute input = compute' $ filterInvalid input
+
+main :: IO ()
+main = do
+  example <- parseInput "example2"
+  let exampleOutput = compute example
+  when  (exampleOutput /= exampleExpectedOutput)  (die $ "example failed: got " ++ show exampleOutput ++ " instead of " ++ show exampleExpectedOutput)
+  input <- parseInput "input"
+  print $ compute input