Advent of Languages

Solutions for AOC 2022 in (hopefully) 25 different languages.

The repository containing all the code is on my Github profile.

DISCLAIMER: THIS PAGE IS NOT A DISPLAY OF MY WEBDEV SKILLS :^)

Table of contents

  1. Javascript
  2. Python
  3. Ruby
  4. Lua
  5. Crystal
  6. C
  7. Go
  8. Cpp
  9. Clojure
  10. Nim
  11. Janet
  12. Java
  13. Julia
  14. Dart
  15. Rust

Day01 - Javascript

const fs = require("fs");

const input = fs.readFileSync(0, "utf8");

const elves = input.split("\n\n").map((elf) =>
  elf.split("\n").map(Number).reduce((a, b) => a + b)
).sort((a, b) => b - a);

console.log("Part 1:", elves[0]);
console.log("Part 2:", elves.slice(0, 3).reduce((a, b) => a + b, 0));

Output:

v19.2.0

Part 1: 71502
Part 2: 208191
Back to top

Day02 - Python

import sys

text = sys.stdin.read().splitlines()

p_map = {"X": 1, "Y": 2, "Z": 3}

d_map = {"A": "X", "B": "Y", "C": "Z"}
w_map = {"A": "Y", "B": "Z", "C": "X"}
l_map = {"A": "Z", "B": "X", "C": "Y"}


def part1():
    points = 0
    for line in text:
        o, m = line.split()
        if w_map[o] == m:
            points += 6

        if d_map[o] == m:
            points += 3

        points += p_map[m]

    print("Part 1:", points)


def part2():
    points = 0
    for line in text:
        o, m = line.split()
        if m == "X":
            points += p_map[l_map[o]]
        if m == "Y":
            points += 3 + p_map[d_map[o]]
        if m == "Z":
            points += 6 + p_map[w_map[o]]

    print("Part 2:", points)


if __name__ == "__main__":
    part1()
    part2()

Output:

Python 3.11.0

Part 1: 11767
Part 2: 13886
Back to top

Day03 - Ruby

require 'set'

def calculate_priority(letter)
    if letter >= 'a' and letter <= 'z' 
        return letter.ord - 'a'.ord + 1
    end

    return letter.ord - 'A'.ord + 27
end

def part1(input)
    s = 0
    input.each do |line|
        h1, h2 = line.chars.each_slice(line.length / 2).map(&:join)
        a = Set.new(h1.chars)
        b = Set.new(h2.chars)

        common = a & b
        s += calculate_priority common.to_a[0]
    end

    puts "Part 1: #{s}"
end

def part2(input)
    s = 0
    input.each_slice(3) do |group|
        a, b, c = group.map { |g| Set.new(g.chomp.chars) }

        common = a & b & c
        s += calculate_priority common.to_a[0]
    end

    puts "Part 2: #{s}"
end

input = ARGF.readlines

part1 input
part2 input

Output:

ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [x86_64-linux]

Part 1: 8039
Part 2: 2510
Back to top

Day04 - Lua

pattern = "(%d+)-(%d+),(%d+)-(%d+)"

function is_subrange(a, b, c, d)
  return (a <= c and b >= d) or (c <= a and d >= b)
end

function overlaps(a, b, c, d)
  return (a >= c and a <= d) or (b >= c and b <= d) or (c >= a and c <= b) or (d >= a and d <= b)
end

p1_sum = 0
p2_sum = 0

for line in io.lines() do
  local _, _, a, b, c, d = string.find(line, pattern)
  
  if is_subrange(tonumber(a), tonumber(b), tonumber(c), tonumber(d)) then 
    p1_sum = p1_sum + 1 
  end
  
  if overlaps(tonumber(a), tonumber(b), tonumber(c), tonumber(d)) then
    p2_sum = p2_sum + 1
  end
end

print("Part 1:", p1_sum)
print("Part 2:", p2_sum)

Output:

Lua 5.4.4  Copyright (C) 1994-2022 Lua.org, PUC-Rio

Part 1:	453
Part 2:	919
Back to top

Day05 - Crystal

stacks = [
    ['P', 'F', 'M', 'Q', 'W', 'G', 'R', 'T'], 
    ['H', 'F', 'R'], 
    ['P', 'Z', 'R', 'V', 'G', 'H', 'S', 'D'],
    ['Q', 'H', 'P', 'B', 'F', 'W', 'G'],
    ['P', 'S', 'M', 'J', 'H'],
    ['M', 'Z', 'T', 'H', 'S', 'R', 'P', 'L'],
    ['P', 'T', 'H', 'N', 'M', 'L'],
    ['F', 'D', 'Q', 'R'],
    ['D', 'S', 'C', 'N', 'L', 'P', 'H'],
]

input = ARGF.gets_to_end
_, moves = input.split "\n\n"

moves.each_line do |line|
    /move (\d+) from (\d+) to (\d+)/.match line
    amount, from, to = $1.to_i, $2.to_i, $3.to_i
    stacks[to - 1] = stacks[to - 1] + (stacks[from - 1].pop amount).reverse
end

puts "Part 1: #{stacks.map { |stack| stack.last }.join}"

stacks = [
    ['P', 'F', 'M', 'Q', 'W', 'G', 'R', 'T'], 
    ['H', 'F', 'R'], 
    ['P', 'Z', 'R', 'V', 'G', 'H', 'S', 'D'],
    ['Q', 'H', 'P', 'B', 'F', 'W', 'G'],
    ['P', 'S', 'M', 'J', 'H'],
    ['M', 'Z', 'T', 'H', 'S', 'R', 'P', 'L'],
    ['P', 'T', 'H', 'N', 'M', 'L'],
    ['F', 'D', 'Q', 'R'],
    ['D', 'S', 'C', 'N', 'L', 'P', 'H'],
]

moves.each_line do |line|
    /move (\d+) from (\d+) to (\d+)/.match line
    amount, from, to = $1.to_i, $2.to_i, $3.to_i
    stacks[to - 1] = stacks[to - 1] + stacks[from - 1].pop amount
end

puts "Part 2: #{stacks.map { |stack| stack.last }.join}"

Output:

Crystal 1.6.2 [879691b2e] (2022-11-03)

LLVM: 13.0.1
Default target: x86_64-unknown-linux-gnu

Part 1: TPGVQPFDH
Part 2: DMRDFRHHH
Back to top

Day06 - C

#include <stdio.h>
#include <stdbool.h>

bool are_all_different(char *recent, int n) {
  for (int i = 0; i < n; ++i) {
    for (int j = i+1; j < n; ++j) {
      if (recent[i] == recent[j]) return false;
    }
  }
  
  return true;
}

int main(void) {
  char recent4[4], recent14[14];
  
  bool p1_done = false;
  
  for (int i = 0; ;i++) {
    char c = getchar();
    
    recent4[i % 4] = c;
    recent14[i % 14] = c;
    
    if (i > 3 && !p1_done && are_all_different(recent4, 4)) {
      printf("Part 1: %d\n", i + 1);
      p1_done = true;
    }
    
    if (i > 13 && are_all_different(recent14, 14)) {
      printf("Part 2: %d\n", i + 1);
      break;
    }
  }
  
  return 0;
}

Output:

cc (GCC) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Part 1: 1965
Part 2: 2773
Back to top

Day07 - Go

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	dirs := make(map[string]int64)
	var path []string

	scanner := bufio.NewScanner(os.Stdin)

	for scanner.Scan() {
		line := scanner.Text()

		if strings.HasPrefix(line, "$ cd") {
			dir := strings.Split(line, " ")[2]

			if dir == "/" {
				path = append(path, "/")
			} else if dir == ".." {
				path = path[:len(path)-1]
			} else {
				path = append(path, path[len(path)-1]+dir+"/")
			}
		}

		if line[0] >= '0' && line[0] <= '9' {
			for _, p := range path {
				i, _ := strconv.ParseInt(strings.Split(line, " ")[0], 10, 64)
				dirs[p] += i
			}
		}
	}

	var s1 int64
	var s2 []int64
	for _, v := range dirs {
		if v <= 100_000 {
			s1 += v
		}

		if v >= 30_000_000-(70_000_000-dirs["/"]) {
			s2 = append(s2, v)
		}
	}

	min := s2[0]

	for _, v := range s2 {
		if v < min {
			min = v
		}
	}

	fmt.Println("Part 1:", s1)
	fmt.Println("Part 2:", min)
}

Output:

go version go1.19.4 linux/amd64

Part 1: 1232307
Part 2: 7268994
Back to top

Day08 - Cpp

#include <iostream>
#include <string>
#include <vector>

using grid = std::vector<std::string>;

bool see_right(const grid& g, int row, int col) {
    auto c = g[row][col];

    for (auto i = 0; i < col; ++i)
        if (g[row][i] >= c) return false;
    
    return true;
}

bool see_left(const grid& g, int row, int col) {
    auto c = g[row][col];

    for (auto i = col+1; i < g[row].size(); ++i)
        if (g[row][i] >= c) return false;

    return true;
}

bool see_up(const grid& g, int row, int col) { 
    auto c = g[row][col];

    for (auto i = 0; i < row; ++i)
        if (g[i][col] >= c) return false;

    return true; 
}

bool see_down(const grid& g, int row, int col) {
    auto c = g[row][col];

    for (auto i = row+1; i < g.size(); ++i)
        if (g[i][col] >= c) return false;

    return true;
}

int v_left(const grid& g, int row, int col) {
    auto c = g[row][col];
    auto d = 0;

    for (auto i = col-1; i >= 0; --i) {
        d++;
        if (g[row][i] >= c) break;
    }

    return d;
}

int v_right(const grid& g, int row, int col) {
    auto c = g[row][col];
    auto d = 0;

    for (auto i = col+1; i < g[row].size(); ++i) {
        d++;
        if (g[row][i] >= c) break;
    }

    return d;
}

int v_up(const grid& g, int row, int col) {
    auto c = g[row][col];
    auto d = 0;

    for (auto i = row-1; i >= 0; --i) {
        d++;
        if (g[i][col] >= c) break;
    }

    return d;
}

int v_down(const grid& g, int row, int col) {
    auto c = g[row][col];
    auto d = 0;

    for (auto i = row+1; i < g.size(); ++i) {
        d++;
        if (g[i][col] >= c) break;
    }

    return d;
}

int calc_v(const grid& g, int row, int col) {
    return v_up(g, row, col)
        * v_left(g, row, col)
        * v_down(g, row, col)
        * v_right(g, row, col);
}

int main() {
    std::string s;
    grid g;

    while (std::getline(std::cin, s))
        g.push_back(s);

    auto p1 = 0, p2 = 0;

    for (auto row = 0; row < g.size(); ++row) {
        for (auto col = 0; col < g[row].size(); ++col) {
            if (see_up(g, row, col) || see_left(g, row, col) || see_down(g, row, col) || see_right(g, row, col))
                p1++;
        }
    }


    for (auto row = 1; row < g.size(); ++row) {
        for (auto col = 1; col < g[row].size(); ++col) {
            auto v = calc_v(g, row, col);
            p2 = v > p2 ? v : p2;
        }
    }

    
    std::cout << "Part 1: " << p1 << '\n';
    std::cout << "Part 2: " << p2 << '\n';
    return 0;
}

Output:

g++ (GCC) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Part 1: 1711
Part 2: 301392
Back to top

Day09 - Clojure

(ns solution
  (:require [clojure.string :as str]
            [clojure.java.io :as io]))

(def directions
  {"U" [0 -1] "D" [0 1] "L" [-1 0] "R" [1 0]})

(defn parse-move [move]
  (let [[dir amount] (str/split move #" ")]
    (repeat (Integer/parseInt amount) dir)))

(def moves
  (->> (line-seq (io/reader *in*))
       (mapcat parse-move)))

(defn vec-add [[u1 u2] [v1 v2]]
  [(+ u1 v1) (+ u2 v2)])

(defn vec-sub [[u1 u2] [v1 v2]]
  [(- u1 v1) (- u2 v2)])

(defn vec-abs [v]
  (vec (map abs v)))

(defn dist [a b]
  (reduce max (vec-abs (vec-sub a b))))

(defn clamp [n min max]
  (if (< n min) min (if (> n max) max n)))>

(defn normalize [v]
  (vec (map #(clamp % -1 1) v)))

(defn move-tail [head tail]
  (if (> (dist head tail) 1)
    (vec-add tail (normalize (vec-sub head tail)))
    tail))

(defn move-knot [rope knot]
  (conj rope (move-tail (last rope) knot)))

(defn move [history dir]
  (let [[head & rest] (first history)
        next-head (vec-add head (directions dir))
        rope (seq (reduce move-knot [next-head] rest))]
    (conj history rope)))

(defn solve [length]
  (->> (reduce move (seq [(repeat length [0 0])]) moves)
       (map last)
       (set)
       (count)))

(println "Part 1:" (solve 2))
(println "Part 2:" (solve 10))

Output:

babashka v1.0.168

Part 1: 6642
Part 2: 2765
Back to top

Day10 - Nim

import std/strutils
import std/sugar

type
    Clock = tuple
        cycle: int
        signalStrength: int
        crt: seq[char]

proc inc(clock: var Clock; x: int) = 
    if clock.cycle mod 40 in @[x - 1, x, x + 1]:
        clock.crt[clock.cycle] = '#'

    clock.cycle += 1

    if clock.cycle in @[20, 60, 100, 140, 180, 220]:
        clock.signalStrength += x * clock.cycle

proc display(clock: Clock) = 
    for row in 0 ..< 6:
        let s = row * 40
        let e = s + 40
        echo join(clock.crt[s ..< e])

let lines = readAll(io.stdin).strip().splitLines()

var x = 1

let initialCrt = collect(newSeq):
    for _ in 0 ..< 240: ' '
var clock: Clock = (cycle: 0, signalStrength: 0, crt: initialCrt)

for line in lines:
    if line == "noop":
        clock.inc(x)
    else:
        let n = line.split(' ')[1].parseInt()
        clock.inc(x)
        clock.inc(x)
        x += n

echo "Part 1: ", clock.signalStrength
echo "Part 2:"
clock.display()

Output:

Nim Compiler Version 1.6.10 [Linux: amd64]
Compiled at 2022-12-06
Copyright (c) 2006-2021 by Andreas Rumpf

active boot switches: -d:release

Part 1: 15680
Part 2:
#### #### ###  #### #  #  ##  #  # ###  
   # #    #  # #    #  # #  # #  # #  # 
  #  ###  ###  ###  #### #    #  # #  # 
 #   #    #  # #    #  # # ## #  # ###  
#    #    #  # #    #  # #  # #  # #    
#### #    ###  #    #  #  ###  ##  #    
Back to top

Day11 - Janet

# Hand parsed :^)
(defn get-monkeys 
  []
  @[{:items @[77 69 76 77 50 58]
     :op |(* $ 11)
     :test 5 :true 1 :false 5}

    {:items @[75 70 82 83 96 64 62]
     :op |(+ $ 8)
     :test 17 :true 5 :false 6}

    {:items @[53]
     :op |(* $ 3)
     :test 2 :true 0 :false 7}

    {:items @[85 64 93 64 99]
     :op |(+ $ 4)
     :test 7 :true 7 :false 2}

    {:items @[61 92 71]
     :op |(* $ $)
     :test 3 :true 2 :false 3}

    {:items @[79 73 50 90]
     :op |(+ $ 2)
     :test 11 :true 4 :false 6}

    {:items @[50 89]
     :op |(+ $ 3)
     :test 13 :true 4 :false 3}

    {:items @[83 56 64 58 93 91 56 65]
     :op |(+ $ 5)
     :test 19 :true 1 :false 0}])

(defn dividers-lcm
  [monkeys]
  (let [dividers (map |($ :test) monkeys)]
    (reduce math/lcm 1 dividers)))

(defn execute-round
  [monkeys post inspected items]
  (loop [[monkey-index monkey] :pairs monkeys]
    (+= (inspected monkey-index) (length (monkey-index items)))
    (each item (monkey-index items)
      (let [u (post ((monkey :op) item))
            dst (if (= 0 (% u (monkey :test))) :true :false)]
              (array/push ((monkey dst) items) u)))
    (set (items monkey-index) @[])))

(defn solve
  [monkeys rounds post]
  (let [inspected (array/new-filled (length monkeys) 0)
        items (map |($ :items) monkeys)]
          (loop [i :range [0 rounds]] (execute-round monkeys post inspected items))
          (sort inspected >)
          (* (0 inspected) (1 inspected))))


(print "Part 1: " (solve (get-monkeys) 20 |(math/floor (/ $ 3))))

(def lcm (dividers-lcm (get-monkeys)))
(print "Part 2: " (solve (get-monkeys) 10000 |(% $ lcm)))

Output:

1.24.1-c81c57f

Part 1: 57838
Part 2: 15050382231
Back to top

Day12 - Java

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.HashSet;

record Tuple(int i, int j, int dist, char c) {}
record Visited(int a, int b) {}

public class solution {
    static String[] grid;
    static ArrayDeque<Tuple> queue;
    static HashSet<Visited> visited;
    
    static void push(int i, int j, int d, char a) {
        if (i < 0 || i >= grid.length || j < 0 || j >= grid[i].length()) return;
        if (visited.contains(new Visited(i, j))) return;

        var b = grid[i].charAt(j) == 'E' ? 'z' : grid[i].charAt(j);
        if (b > a + 1) return;

        visited.add(new Visited(i, j));
        queue.add(new Tuple(i, j, d+1, b));
    }
    
    static int solve(String[] grid, char start, char start2) {
        queue = new ArrayDeque<Tuple>();
        visited = new HashSet<Visited>();

        for (var i = 0; i < grid.length; i++) {
            for (var j = 0; j < grid[i].length(); j++) {
                if (grid[i].charAt(j) == start || grid[i].charAt(j) == start2) {
                    queue.add(new Tuple(i, j, 0, 'a'));
                    visited.add(new Visited(i, j));
                }
            }
        }

        while (!queue.isEmpty()) {
            var cur = queue.poll(); 
            if (grid[cur.i()].charAt(cur.j()) == 'E') return cur.dist();
            push(cur.i() + 1, cur.j(), cur.dist(), cur.c());
            push(cur.i() - 1, cur.j(), cur.dist(), cur.c());
            push(cur.i(), cur.j() + 1, cur.dist(), cur.c());
            push(cur.i(), cur.j() - 1, cur.dist(), cur.c());

        }

        return -1;
    }

    static int solve(String[] grid, char start) {
        return solve(grid, start, start);
    }
    
    static String[] getInput() {
        var reader = new BufferedReader(new InputStreamReader(System.in));
        return reader.lines().toArray(String[]::new);
    }
    
    public static void main(String[] args) {
        grid = getInput();
        System.out.println("Part 1: " + solve(grid, 'S'));
        System.out.println("Part 2: " + solve(grid, 'S', 'a'));
    }
}

Output:

javac 19.0.1
openjdk 19.0.1 2022-10-18
OpenJDK Runtime Environment (build 19.0.1+10)
OpenJDK 64-Bit Server VM (build 19.0.1+10, mixed mode)

Part 1: 520
Part 2: 508
Back to top

Day13 - Julia

using JSON

less(a::Int, b::Int) = a < b
less(a::Vector, b::Int) = less(a, [b])
less(a::Int, b::Vector) = less([a], b)
less(a::Vector, b::Vector) = 
    !isempty(b) && (isempty(a)
    || less(a[1], b[1]) 
    || (!less(b[1], a[1]) && less(a[2:end], b[2:end])))

function read_input()::Vector{Tuple{Vector, Vector}}
    pairs = []

    while true
        p1 = readline()
        p2 = readline()

        if p1 == "" || p2 == "" break end

        push!(pairs, (JSON.parse(p1), JSON.parse(p2)))
        readline()
    end
    
    pairs
end

input = read_input()

function part1() 
    sum([i for (i, (p1, p2)) in enumerate(input) if less(p1, p2)])
end

function part2()
    v = collect(Iterators.flatten([[a, b] for (a, b) in input]))
    push!(v, [[2]], [[6]])
    sort!(v, lt=less)

    findfirst(x -> x == [[2]], v) * findfirst(x -> x == [[6]], v)
end


println("Part 1: ", part1())
println("Part 2: ", part2())

Output:

julia version 1.8.3

Part 1: 5196
Part 2: 22134
Back to top

Day14 - Dart

import 'dart:io';
import 'dart:math';

Set<Point<int>> parse(String input) {
  final grid = <Point<int>>{};

  for (var line in input.split("\n")) {
    final coords = line.split(" -> ");
    for (var i = 1; i < coords.length; i++) {
      final o = coords[i - 1].split(",").map(int.parse);
      final d = coords[i].split(",").map(int.parse);

      final p1 = Point(o.first, o.last);
      final p2 = Point(d.first, d.last);

      final v = Point((p2.x - p1.x).sign, (p2.y - p1.y).sign);

      for (var p = p1; p != p2; p += v) {
        grid.add(p);
      }
      grid.add(p2);
    }
  }

  return grid;
}

void main() async {
  final input = (await stdin.first).map(String.fromCharCode).join().trim();
  final grid = parse(input);

  final maxY = grid
      .reduce((value, element) => (value.y > element.y) ? value : element)
      .y;

  for (int i = -2000; i < 2000; i++) {
    grid.add(Point(i, maxY + 2));
  }

  final source = Point(500, 0);
  final path = [source];

  final order = [Point(0, 1), Point(-1, 1), Point(1, 1)];

  var count = 0;
  int part1 = 0;

  while (path.isNotEmpty) {
    final current = path.last;

    if (grid.contains(source)) {
      break;
    }

    if (current.y > maxY && part1 == 0) {
      part1 = count;
    }

    var couldPlace = false;
    for (var p in order) {
      if (!grid.contains(current + p)) {
        path.add(current + p);
        couldPlace = true;
        break;
      }
    }

    if (!couldPlace) {
      grid.add(current);
      path.removeLast();
      count++;
    }
  }

  print("Part 1: $part1");
  print("Part 2: $count");
}

Output:

Dart SDK version: 2.18.5 (stable) (Tue Nov 22 15:47:29 2022 +0000) on "linux_x64"

Part 1: 862
Part 2: 28744
Back to top

Day15 - Rust

use std::io;

#[derive(Debug, Clone, Copy)]
struct Sensor {
    pos: (isize, isize),
    close: (isize, isize),
    dist: isize,
}

impl Sensor {
    fn inside_p(&self, p: (isize, isize)) -> bool {
        self.close != p && self.dist as usize >= self.pos.0.abs_diff(p.0) + self.pos.1.abs_diff(p.1)
    }

    fn parse(line: &str) -> Self {
        let nums = line
            .split_whitespace()
            .into_iter()
            .filter(|x| x.contains(|a: char| a.is_digit(10)))
            .map(|x| x.chars().filter(|a| a == &'-' || a.is_digit(10)).collect())
            .filter_map(|x: String| x.parse::<isize>().ok())
            .collect::<Vec<_>>();

        Self {
            pos: (nums[0], nums[1]),
            close: (nums[2], nums[3]),
            dist: (nums[0].abs_diff(nums[2]) + nums[1].abs_diff(nums[3])) as isize,
        }
    }
}

fn main() {
    let input = io::stdin()
        .lines()
        .map(|line| Sensor::parse(&line.unwrap()))
        .collect::<Vec<_>>();

    let left_b = input.iter().map(|s| s.pos.0 - s.dist).min().unwrap();
    let right_b = input.iter().map(|s| s.pos.0 + s.dist).max().unwrap();

    let part1 = (left_b..=right_b)
        .filter(|&i| input.iter().any(|s| s.inside_p((i, 2_000_000))))
        .collect::<Vec<_>>()
        .len();

    println!("Part 1: {}", part1);

    let part2 = input
        .iter()
        .find_map(|s| {
            ((s.pos.0 - s.dist - 1).max(0)..=s.pos.0.min(4_000_000))
                .zip(s.pos.1..=4_000_000)
                .find_map(|p| {
                    input
                        .iter()
                        .all(|s| !s.inside_p(p))
                        .then(|| p.0 * 4_000_000 + p.1)
                })
        })
        .unwrap();

    println!("Part 2: {}", part2);
}

Output:

rustc 1.65.0 (897e37553 2022-11-02)

Part 1: 5100463
Part 2: 11557863040754
Back to top