diff --git a/2024/day02/.gitignore b/2024/day02/.gitignore
new file mode 100644
index 0000000..599be4e
--- /dev/null
+++ b/2024/day02/.gitignore
@@ -0,0 +1,4 @@
+*.beam
+*.ez
+/build
+erl_crash.dump
diff --git a/2024/day02/gleam.toml b/2024/day02/gleam.toml
new file mode 100644
index 0000000..448d48d
--- /dev/null
+++ b/2024/day02/gleam.toml
@@ -0,0 +1,20 @@
+name = "day02"
+version = "1.0.0"
+
+# Fill out these fields if you intend to generate HTML documentation or publish
+# your project to the Hex package manager.
+#
+# description = ""
+# licences = ["Apache-2.0"]
+# repository = { type = "github", user = "", repo = "" }
+# links = [{ title = "Website", href = "" }]
+#
+# For a full reference of all the available options, you can have a look at
+# https://gleam.run/writing-gleam/gleam-toml/.
+
+[dependencies]
+gleam_stdlib = ">= 0.34.0 and < 2.0.0"
+gleam_yielder = ">= 1.1.0 and < 2.0.0"
+
+[dev-dependencies]
+gleeunit = ">= 1.0.0 and < 2.0.0"
diff --git a/2024/day02/manifest.toml b/2024/day02/manifest.toml
new file mode 100644
index 0000000..0e33798
--- /dev/null
+++ b/2024/day02/manifest.toml
@@ -0,0 +1,13 @@
+# This file was generated by Gleam
+# You typically do not need to edit this file
+
+packages = [
+  { name = "gleam_stdlib", version = "0.45.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "206FCE1A76974AECFC55AEBCD0217D59EDE4E408C016E2CFCCC8FF51278F186E" },
+  { name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" },
+  { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
+]
+
+[requirements]
+gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
+gleam_yielder = { version = ">= 1.1.0 and < 2.0.0" }
+gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
diff --git a/2024/day02/src/day02.gleam b/2024/day02/src/day02.gleam
new file mode 100644
index 0000000..c87b60b
--- /dev/null
+++ b/2024/day02/src/day02.gleam
@@ -0,0 +1,74 @@
+import gleam/bool
+import gleam/int
+import gleam/io
+import gleam/list
+import gleam/result
+import gleam/string
+import gleam/yielder
+import stdin.{stdin}
+
+pub fn main() {
+  let reports =
+    stdin()
+    |> yielder.map(fn(line) {
+      let assert #(ints, []) =
+        string.trim_end(line)
+        |> string.split(" ")
+        |> list.map(int.parse)
+        |> result.partition
+      ints
+    })
+    |> yielder.to_list
+
+  part1(reports)
+  |> int.to_string
+  |> io.println
+
+  part2(reports)
+  |> int.to_string
+  |> io.println
+}
+
+fn part1(reports: List(List(Int))) -> Int {
+  list.map(reports, is_safe)
+  |> list.count(fn(x) { x })
+}
+
+fn part2(reports: List(List(Int))) {
+  let #(safe, unsafe) = list.partition(reports, is_safe)
+  let non_unsafe =
+    list.map(unsafe, fn(report) {
+      list.combinations(report, list.length(report) - 1)
+      |> until_true(is_safe)
+    })
+  list.length(safe) + list.count(non_unsafe, fn(x) { x })
+}
+
+fn until_true(over list: List(a), with fun: fn(a) -> Bool) -> Bool {
+  case list {
+    [] -> False
+    [head] -> fun(head)
+    [head, ..tail] ->
+      case fun(head) {
+        True -> True
+        False -> until_true(tail, fun)
+      }
+  }
+}
+
+fn is_safe(report: List(Int)) -> Bool {
+  list.window_by_2(report)
+  |> list.try_fold(0, fn(sum, window) {
+    let diff = window.1 - window.0
+    case int.absolute_value(diff) {
+      x if x >= 1 && x <= 3 ->
+        case bool.exclusive_nor(sum > 0, diff > 0) {
+          True -> Ok(sum + diff)
+          False if sum == 0 -> Ok(sum + diff)
+          False -> Error(Nil)
+        }
+      _ -> Error(Nil)
+    }
+  })
+  |> result.is_ok
+}
diff --git a/2024/day02/src/stdin.gleam b/2024/day02/src/stdin.gleam
new file mode 100644
index 0000000..560c4ec
--- /dev/null
+++ b/2024/day02/src/stdin.gleam
@@ -0,0 +1,24 @@
+import gleam/dynamic
+import gleam/result
+import gleam/yielder
+
+@external(erlang, "io", "get_line")
+fn ffi_read_line(prompt: String) -> dynamic.Dynamic
+
+fn read_line() -> Result(String, Nil) {
+  ffi_read_line("")
+  |> dynamic.from()
+  |> dynamic.string()
+  |> result.replace_error(Nil)
+}
+
+fn assert_upwrap(res: Result(a, _)) -> a {
+  let assert Ok(a) = res
+  a
+}
+
+pub fn stdin() -> yielder.Yielder(String) {
+  yielder.repeatedly(read_line)
+  |> yielder.take_while(result.is_ok)
+  |> yielder.map(assert_upwrap)
+}