diff --git a/2021/05/main.kt b/2021/05/main.kt
index a71535b..abbed9f 100644
--- a/2021/05/main.kt
+++ b/2021/05/main.kt
@@ -1,38 +1,62 @@
 import java.io.File
+import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.min
+import kotlin.system.measureTimeMillis
 
 fun main() {
 	val lineRegex = Regex("^(\\d+),(\\d+) -> (\\d+),(\\d+)$")
 	val lines = File("input/1.txt").readLines()
 		.mapNotNull(lineRegex::matchEntire)
 		.map { it.groupValues.takeLast(4).map(String::toInt) }
-		.map { Line.of(it[0], it[1], it[2], it[3]) }
+		.map { Line(it[0], it[1], it[2], it[3]) }
 	
-	val straightLines = lines.filter(Line::isStraight)
-	val straightLineFloor = Floor(straightLines)
-	
-	println("Points where at least 2 straight lines overlap: ${straightLineFloor.countPointsWithOverlap(2)}")
+	println("(Took ${measureTimeMillis { part1(lines) }} ms)")
+	println("(Took ${measureTimeMillis { part2(lines) }} ms)")
 }
 
-@Suppress("ProtectedInFinal")
-data class Line protected constructor(val x1: Int, val y1: Int, val x2: Int, val y2: Int) {
-	companion object {
-		fun of(x1: Int, y1: Int, x2: Int, y2: Int): Line {
-			return Line(min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2))
-		}
+@Suppress("ProtectedInFinal", "MemberVisibilityCanBePrivate")
+data class Line(val x1: Int, val y1: Int, val x2: Int, val y2: Int) {
+	val minX = min(x1, x2)
+	val minY = min(y1, y2)
+	val maxX = max(x1, x2)
+	val maxY = max(y1, y2)
+	
+	init {
+		require(isStraight || is45Degrees) { "Line must be straight or have a slope of 45 degrees!" }
 	}
 	
 	val isStraight
 		get() = x1 == x2 || y1 == y2
 	
+	val is45Degrees
+		get() = abs(x2 - x1) == abs(y2 - y1)
+	
+	val length = 1 + max(abs(x2 - x1), abs(y2 - y1))
+	
+	private val slopeX = Integer.signum(x2 - x1)
+	private val slopeY = Integer.signum(y2 - y1)
+	
 	fun contains(x: Int, y: Int): Boolean {
-		return if (x1 == x2)
-			x == x1 && y in y1..y2
-		else if (y1 == y2)
-			y == y1 && x in x1..x2
-		else
-			throw UnsupportedOperationException()
+		if (x1 == x2) {
+			return x == x1 && y in minY..maxY
+		}
+		
+		if (y1 == y2) {
+			return y == y1 && x in minX..maxX
+		}
+		
+		if (x !in minX..maxX || y !in minY..maxY) {
+			return false
+		}
+		
+		for (i in 0 until length) {
+			if (x == x1 + (slopeX * i) && y == y1 + (slopeY * i)) {
+				return true
+			}
+		}
+		
+		return false
 	}
 }
 
@@ -43,13 +67,22 @@ class Floor(private val lines: List<Line>) {
 	private val maxY = lines.maxOf { max(it.y1, it.y2) }
 	
 	fun countPointsWithOverlap(minOverlap: Int): Int {
-		val xs = minX..maxX
+		val xs = minX..maxY
 		val ys = minY..maxY
-		return xs.sumOf { x ->
-			ys.count { y ->
-				lines.count { it.contains(x, y) } >= minOverlap
+		val xy = xs.flatMap { x -> ys.map { y -> x to y } }
+		return xy.parallelStream().filter { (x, y) -> hasOverlapOf(x, y, minOverlap) }.count().toInt()
+	}
+	
+	private fun hasOverlapOf(x: Int, y: Int, minOverlap: Int): Boolean {
+		var count = 0
+		
+		for (line in lines) {
+			if (line.contains(x, y) && ++count >= minOverlap) {
+				return true
 			}
 		}
+		
+		return false
 	}
 	
 	@Suppress("unused")
@@ -69,3 +102,14 @@ class Floor(private val lines: List<Line>) {
 		}
 	}
 }
+
+fun part1(lines: List<Line>) {
+	val straightLines = lines.filter(Line::isStraight).ifEmpty { return }
+	val straightLineFloor = Floor(straightLines)
+	println("Points where at least 2 straight lines overlap: ${straightLineFloor.countPointsWithOverlap(2)}")
+}
+
+fun part2(lines: List<Line>) {
+	val floor = Floor(lines)
+	println("Points where at least 2 lines overlap: ${floor.countPointsWithOverlap(2)}")
+}