diff --git a/day10/part1/input b/day10/part1/input
new file mode 100644
index 0000000..de682dd
--- /dev/null
+++ b/day10/part1/input
@@ -0,0 +1,107 @@
+48
+171
+156
+51
+26
+6
+80
+62
+65
+82
+130
+97
+49
+31
+142
+83
+75
+20
+154
+119
+56
+114
+92
+33
+140
+74
+118
+1
+96
+44
+128
+134
+121
+64
+158
+27
+17
+101
+59
+12
+89
+88
+145
+167
+11
+3
+39
+43
+105
+16
+170
+63
+111
+2
+108
+21
+146
+77
+45
+52
+32
+127
+147
+76
+58
+37
+86
+129
+57
+133
+120
+163
+138
+161
+139
+71
+9
+141
+168
+164
+124
+157
+95
+25
+38
+69
+87
+155
+135
+15
+102
+70
+34
+42
+24
+50
+68
+169
+10
+55
+117
+30
+81
+151
+100
+162
+148
diff --git a/day10/part1/main.d b/day10/part1/main.d
new file mode 100644
index 0000000..5b78de3
--- /dev/null
+++ b/day10/part1/main.d
@@ -0,0 +1,40 @@
+import std;
+
+void main()
+{
+    File("input", "r").byLine.map!(to!int)
+        .array.calculateJoltDifferences.multiply1And3Differences.writeln;
+}
+
+auto multiply1And3Differences(int[int] differences)
+{
+    return differences[1] * differences[3];
+}
+
+int[int] calculateJoltDifferences(int[] input)
+{
+    input.sort();
+
+    int[int] differenceCounts;
+    chain([0], input, [input.back + 3]).slide(2).map!(pair => pair[1] - pair[0])
+        .each!(difference => differenceCounts[difference]++);
+
+    return differenceCounts;
+}
+
+unittest
+{
+    auto input = [16, 10, 15, 5, 1, 11, 7, 19, 6, 12, 4];
+    auto differences = input.calculateJoltDifferences;
+    assert(differences[1] == 7 && differences[3] == 5);
+}
+
+unittest
+{
+    auto input = [
+        28, 33, 18, 42, 31, 14, 46, 20, 48, 47, 24, 23, 49, 45, 19, 38, 39, 11,
+        1, 32, 25, 35, 8, 17, 7, 9, 4, 2, 34, 10, 3
+    ];
+    auto differences = input.calculateJoltDifferences;
+    assert(differences[1] == 22 && differences[3] == 10);
+}
diff --git a/day10/part2/input b/day10/part2/input
new file mode 100644
index 0000000..de682dd
--- /dev/null
+++ b/day10/part2/input
@@ -0,0 +1,107 @@
+48
+171
+156
+51
+26
+6
+80
+62
+65
+82
+130
+97
+49
+31
+142
+83
+75
+20
+154
+119
+56
+114
+92
+33
+140
+74
+118
+1
+96
+44
+128
+134
+121
+64
+158
+27
+17
+101
+59
+12
+89
+88
+145
+167
+11
+3
+39
+43
+105
+16
+170
+63
+111
+2
+108
+21
+146
+77
+45
+52
+32
+127
+147
+76
+58
+37
+86
+129
+57
+133
+120
+163
+138
+161
+139
+71
+9
+141
+168
+164
+124
+157
+95
+25
+38
+69
+87
+155
+135
+15
+102
+70
+34
+42
+24
+50
+68
+169
+10
+55
+117
+30
+81
+151
+100
+162
+148
diff --git a/day10/part2/main.d b/day10/part2/main.d
new file mode 100644
index 0000000..43c9a06
--- /dev/null
+++ b/day10/part2/main.d
@@ -0,0 +1,41 @@
+import std;
+
+void main()
+{
+    auto nodes = File("input", "r").byLine.map!(to!int).chain([0])
+        .map!(it => tuple(it, cast(void[0])[])).assocArray;
+    nodes.calculateNumberOfPathsLeadingFromTo(0, nodes.byKey.maxElement).writeln;
+}
+
+ulong calculateNumberOfPathsLeadingFromTo(void[0][int] nodes, int start, int end)
+{
+    if (end == start)
+    {
+        return 1;
+    }
+    return iota(end - 3, end).filter!(potentialAncestor => potentialAncestor in nodes)
+        .map!(ancestor => nodes.memoize!calculateNumberOfPathsLeadingFromTo(start, ancestor))
+        .sum;
+}
+
+unittest
+{
+    void[0][int] input = [0 : [], 1 : []];
+    assert(input.calculateNumberOfPathsLeadingFromTo(0, 1) == 1);
+}
+
+unittest
+{
+    auto input = [16, 10, 15, 5, 1, 11, 7, 19, 6, 12, 4].chain([0])
+        .map!(it => tuple(it, cast(void[0])[])).assocArray;
+    assert(input.calculateNumberOfPathsLeadingFromTo(0, input.byKey.maxElement) == 8);
+}
+
+unittest
+{
+    auto input = [
+        28, 33, 18, 42, 31, 14, 46, 20, 48, 47, 24, 23, 49, 45, 19, 38, 39, 11,
+        1, 32, 25, 35, 8, 17, 7, 9, 4, 2, 34, 10, 3
+    ].chain([0]).map!(it => tuple(it, cast(void[0])[])).assocArray;
+    assert(input.calculateNumberOfPathsLeadingFromTo(0, input.byKey.maxElement) == 19_208);
+}