1
        2
        3
        4
        5
        6
        7
        8
        9
       10
       11
       12
       13
       14
       15
       16
       17
       18
       19
       20
       21
       22
       23
       24
       25
       26
       27
       28
       29
       30
       31
       32
       33
       34
       35
       36
       37
       38
       39
       40
       41
       42
       43
       44
       45
       46
       47
       48
       49
       50
       51
       52
       53
       54
       55
       56
       57
       58
       59
       60
       61
       62
       63
       64
       65
       66
       67
       68
       69
       70
       71
       72
       73
       74
       75
       76
       77
       78
       79
       80
       81
       82
       83
       84
       85
       86
       87
       88
       89
       90
       91
       92
       93
       94
       95
       96
       97
       98
       99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      184
      185
      186
      187
      188
      189
      190
      191
      192
      193
      194
      195
      196
      197
      198
      199
      200
      201
      202
      203
      204
      205
      206
      207
      208
      209
      210
      211
      212
      213
      214
      215
      216
      217
      218
      219
      220
      221
      222
      223
      224
      225
      226
      227
      228
      229
      230
      231
      232
      233
      234
      235
      236
      237
      238
      239
      240
      241
      242
      243
      244
      245
      246
      247
      248
      249
      250
      251
      252
      253
      254
      255
      256
      257
      258
      259
      260
      261
      262
      263
      264
      265
      266
      267
      268
      269
      270
      271
      272
      273
      274
      275
      276
      277
      278
      279
      280
      281
      282
      283
      284
      285
      286
      287
      288
      289
      290
      291
      292
      293
      294
      295
      296
      297
      298
      299
      300
      301
      302
      303
      304
      305
      306
      307
      308
      309
      310
      311
      312
      313
      314
      315
      316
      317
      318
      319
      320
      321
      322
      323
      324
      325
      326
      327
      328
      329
      330
      331
      332
      333
      334
      335
      336
      337
      338
      339
      340
      341
      342
      343
      344
      345
      346
      347
      348
      349
      350
      351
      352
      353
      354
      355
      356
      357
      358
      359
      360
      361
      362
      363
      364
      365
      366
      367
      368
      369
      370
      371
      372
      373
      374
      375
      376
      377
      378
      379
      380
      381
      382
      383
      384
      385
      386
      387
      388
      389
      390
      391
      392
      393
      394
      395
      396
      397
      398
      399
      400
      401
      402
      403
      404
      405
      406
      407
      408
      409
      410
      411
      412
      413
      414
      415
      416
      417
      418
      419
      420
      421
      422
      423
      424
      425
      426
      427
      428
      429
      430
      431
      432
      433
      434
      435
      436
      437
      438
      439
      440
      441
      442
      443
      444
      445
      446
      447
      448
      449
      450
      451
      452
      453
      454
      455
      456
      457
      458
      459
      460
      461
      462
      463
      464
      465
      466
      467
      468
      469
      470
      471
      472
      473
      474
      475
      476
      477
      478
      479
      480
      481
      482
      483
      484
      485
      486
      487
      488
      489
      490
      491
      492
      493
      494
      495
      496
      497
      498
      499
      500
      501
      502
      503
      504
      505
      506
      507
      508
      509
      510
      511
      512
      513
      514
      515
      516
      517
      518
      519
      520
      521
      522
      523
      524
      525
      526
      527
      528
      529
      530
      531
      532
      533
      534
      535
      536
      537
      538
      539
      540
      541
      542
      543
      544
      545
      546
      547
      548
      549
      550
      551
      552
      553
      554
      555
      556
      557
      558
      559
      560
      561
      562
      563
      564
      565
      566
      567
      568
      569
      570
      571
      572
      573
      574
      575
      576
      577
      578
      579
      580
      581
      582
      583
      584
      585
      586
      587
      588
      589
      590
      591
      592
      593
      594
      595
      596
      597
      598
      599
      600
      601
      602
      603
      604
      605
      606
      607
      608
      609
      610
      611
      612
      613
      614
      615
      616
      617
      618
      619
      620
      621
      622
      623
      624
      625
      626
      627
      628
      629
      630
      631
      632
      633
      634
      635
      636
      637
      638
      639
      640
      641
      642
      643
      644
      645
      646
      647
      648
      649
      650
      651
      652
      653
      654
      655
      656
      657
      658
      659
      660
      661
      662
      663
      664
      665
      666
      667
      668
      669
      670
      671
      672
      673
      674
      675
      676
      677
      678
      679
      680
      681
      682
      683
      684
      685
      686
      687
      688
      689
      690
      691
      692
      693
      694
      695
      696
      697
      698
      699
      700
      701
      702
      703
      704
      705
      706
      707
      708
      709
      710
      711
      712
      713
      714
      715
      716
      717
      718
      719
      720
      721
      722
      723
      724
      725
      726
      727
      728
      729
      730
      731
      732
      733
      734
      735
      736
      737
      738
      739
      740
      741
      742
      743
      744
      745
      746
      747
      748
      749
      750
      751
      752
      753
      754
      755
      756
      757
      758
      759
      760
      761
      762
      763
      764
      765
      766
      767
      768
      769
      770
      771
      772
      773
      774
      775
      776
      777
      778
      779
      780
      781
      782
      783
      784
      785
      786
      787
      788
      789
      790
      791
      792
      793
      794
      795
      796
      797
      798
      799
      800
      801
      802
      803
      804
      805
      806
      807
      808
      809
      810
      811
      812
      813
      814
      815
      816
      817
      818
      819
      820
      821
      822
      823
      824
      825
      826
      827
      828
      829
      830
      831
      832
      833
      834
      835
      836
      837
      838
      839
      840
      841
      842
      843
      844
      845
      846
      847
      848
      849
      850
      851
      852
      853
      854
      855
      856
      857
      858
      859
      860
      861
      862
      863
      864
      865
      866
      867
      868
      869
      870
      871
      872
      873
      874
      875
      876
      877
      878
      879
      880
      881
      882
      883
      884
      885
      886
      887
      888
      889
      890
      891
      892
      893
      894
      895
      896
      897
      898
      899
      900
      901
      902
      903
      904
      905
      906
      907
      908
      909
      910
      911
      912
      913
      914
      915
      916
      917
      918
      919
      920
      921
      922
      923
      924
      925
      926
      927
      928
      929
      930
      931
      932
      933
      934
      935
      936
      937
      938
      939
      940
      941
      942
      943
      944
      945
      946
      947
      948
      949
      950
      951
      952
      953
      954
      955
      956
      957
      958
      959
      960
      961
      962
      963
      964
      965
      966
      967
      968
      969
      970
      971
      972
      973
      974
      975
      976
      977
      978
      979
      980
      981
      982
      983
      984
      985
      986
      987
      988
      989
      990
      991
      992
      993
      994
      995
      996
      997
      998
      999
     1000
     1001
     1002
     1003
     1004
     1005
     1006
     1007
     1008
     1009
     1010
     1011
     1012
     1013
     1014
     1015
     1016
     1017
     1018
     1019
     1020
     1021
     1022
     1023
     1024
     1025
     1026
     1027
     1028
     1029
     1030
     1031
     1032
     1033
     1034
     1035
     1036
     1037
     1038
     1039
     1040
     1041
     1042
     1043
     1044
     1045
     1046
     1047
     1048
     1049
     1050
     1051
     1052
     1053
     1054
     1055
     1056
     1057
     1058
     1059
     1060
     1061
     1062
     1063
     1064
     1065
     1066
     1067
     1068
     1069
     1070
     1071
     1072
     1073
     1074
     1075
     1076
     1077
     1078
     1079
     1080
     1081
     1082
     1083
     1084
     1085
     1086
     1087
     1088
     1089
     1090
     1091
     1092
     1093
     1094
     1095
     1096
     1097
     1098
     1099
     1100
     1101
     1102
     1103
     1104
     1105
     1106
     1107
     1108
     1109
     1110
     1111
     1112
     1113
     1114
     1115
     1116
     1117
     1118
     1119
     1120
     1121
     1122
     1123
     1124
     1125
     1126
     1127
     1128
     1129
     1130
     1131
     1132
     1133
     1134
     1135
     1136
     1137
     1138
     1139
     1140
     1141
     1142
     1143
     1144
     1145
     1146
     1147
     1148
     1149
     1150
     1151
     1152
     1153
     1154
     1155
     1156
     1157
     1158
     1159
     1160
     1161
     1162
     1163
     1164
     1165
     1166
     1167
     1168
     1169
     1170
     1171
     1172
     1173
     1174
     1175
     1176
     1177
     1178
     1179
     1180
     1181
     1182
     1183
     1184
     1185
     1186
     1187
     1188
     1189
     1190
     1191
     1192
     1193
     1194
     1195
     1196
     1197
     1198
     1199
     1200
     1201
     1202
     1203
     1204
     1205
     1206
     1207
     1208
     1209
     1210
     1211
     1212
     1213
     1214
     1215
     1216
     1217
     1218
     1219
     1220
     1221
     1222
     1223
     1224
     1225
     1226
     1227
     1228
     1229
     1230
     1231
     1232
     1233
     1234
     1235
     1236
     1237
     1238
     1239
     1240
     1241
     1242
     1243
     1244
     1245
     1246
     1247
     1248
     1249
     1250
     1251
     1252
     1253
     1254
     1255
     1256
     1257
     1258
     1259
     1260
     1261
     1262
     1263
     1264
     1265
     1266
     1267
     1268
     1269
     1270
     1271
     1272
     1273
     1274
     1275
     1276
     1277
     1278
     1279
     1280
     1281
     1282
     1283
     1284
     1285
     1286
     1287
     1288
     1289
     1290
     1291
     1292
     1293
     1294
     1295
     1296
     1297
     1298
     1299
     1300
     1301
     1302
     1303
     1304
     1305
     1306
     1307
     1308
     1309
     1310
     1311
     1312
     1313
     1314
     1315
     1316
     1317
     1318
     1319
     1320
     1321
     1322
     1323
     1324
     1325
     1326
     1327
     1328
     1329
     1330
     1331
     1332
     1333
     1334
     1335
     1336
     1337
     1338
     1339
     1340
     1341
     1342
     1343
     1344
     1345
     1346
     1347
     1348
     1349
     1350
     1351
     1352
     1353
     1354
     1355
     1356
     1357
     1358
     1359
     1360
     1361
     1362
     1363
     1364
     1365
     1366
     1367
     1368
     1369
     1370
     1371
     1372
     1373
     1374
     1375
     1376
     1377
     1378
     1379
     1380
     1381
     1382
     1383
     1384
     1385
     1386
     1387
     1388
     1389
     1390
     1391
     1392
     1393
     1394
     1395
     1396
     1397
     1398
     1399
     1400
     1401
     1402
     1403
     1404
     1405
     1406
     1407
     1408
     1409
     1410
     1411
     1412
     1413
     1414
     1415
     1416
     1417
     1418
     1419
     1420
     1421
     1422
     1423
     1424
     1425
     1426
     1427
     1428
     1429
     1430
     1431
     1432
     1433
     1434
     1435
     1436
     1437
     1438
     1439
     1440
     1441
     1442
     1443
     1444
     1445
     1446
     1447
     1448
     1449
     1450
     1451
     1452
     1453
     1454
     1455
     1456
     1457
     1458
     1459
     1460
     1461
     1462
     1463
     1464
     1465
     1466
     1467
     1468
     1469
     1470
     1471
     1472
     1473
     1474
     1475
     1476
     1477
     1478
     1479
     1480
     1481
     1482
     1483
     1484
     1485
     1486
     1487
     1488
     1489
     1490
     1491
     1492
     1493
     1494
     1495
     1496
     1497
     1498
     1499
     1500
     1501
     1502
     1503
     1504
     1505
     1506
     1507
     1508
     1509
     1510
     1511
     1512
     1513
     1514
     1515
     1516
     1517
     1518
     1519
     1520
     1521
     1522
     1523
     1524
     1525
     1526
     1527
     1528
     1529
     1530
     1531
     1532
     1533
     1534
     1535
     1536
     1537
     1538
     1539
     1540
     1541
     1542
     1543
     1544
     1545
     1546
     1547
     1548
     1549
     1550
     1551
     1552
     1553
     1554
     1555
     1556
     1557
     1558
     1559
     1560
     1561
     1562
     1563
     1564
     1565
     1566
     1567
     1568
     1569
     1570
     1571
     1572
     1573
     1574
     1575
     1576
     1577
     1578
     1579
     1580
     1581
     1582
     1583
     1584
     1585
     1586
     1587
     1588
     1589
     1590
     1591
     1592
     1593
     1594
     1595
     1596
     1597
     1598
     1599
     1600
     1601
     1602
     1603
     1604
     1605
     1606
     1607
     1608
     1609
     1610
     1611
     1612
     1613
     1614
     1615
     1616
     1617
     1618
     1619
     1620
     1621
     1622
     1623
     1624
     1625
     1626
     1627
     1628
     1629
     1630
     1631
     1632
     1633
     1634
     1635
     1636
     1637
     1638
     1639
     1640
     1641
     1642
     1643
     1644
     1645
     1646
     1647
     1648
     1649
     1650
     1651
     1652
     1653
     1654
     1655
     1656
     1657
     1658
     1659
     1660
     1661
     1662
     1663
     1664
     1665
     1666
     1667
     1668
     1669
     1670
     1671
     1672
     1673
     1674
     1675
     1676
     1677
     1678
     1679
     1680
     1681
     1682
     1683
     1684
     1685
     1686
     1687
     1688
     1689
     1690
     1691
     1692
     1693
     1694
     1695
     1696
     1697
     1698
     1699
     1700
     1701
     1702
     1703
     1704
     1705
     1706
     1707
     1708
     1709
     1710
     1711
     1712
     1713
     1714
     1715
     1716
     1717
     1718
     1719
     1720
     1721
     1722
     1723
     1724
     1725
     1726
     1727
     1728
     1729
     1730
     1731
     1732
     1733
     1734
     1735
     1736
     1737
     1738
     1739
     1740
     1741
     1742
     1743
     1744
     1745
     1746
     1747
     1748
     1749
     1750
     1751
     1752
     1753
     1754
     1755
     1756
     1757
     1758
     1759
     1760
     1761
     1762
     1763
     1764
     1765
     1766
     1767
     1768
     1769
     1770
     1771
     1772
     1773
     1774
     1775
     1776
     1777
     1778
     1779
     1780
     1781
     1782
     1783
     1784
     1785
     1786
     1787
     1788
     1789
     1790
     1791
     1792
     1793
     1794
     1795
     1796
     1797
     1798
     1799
     1800
     1801
     1802
     1803
     1804
     1805
     1806
     1807
     1808
     1809
     1810
     1811
     1812
     1813
     1814
     1815
     1816
     1817
     1818
     1819
     1820
     1821
     1822
     1823
     1824
     1825
     1826
     1827
     1828
     1829
     1830
     1831
     1832
     1833
     1834
     1835
     1836
     1837
     1838
     1839
     1840
     1841
     1842
     1843
     1844
     1845
     1846
     1847
     1848
     1849
     1850
     1851
     1852
     1853
     1854
     1855
     1856
     1857
     1858
     1859
     1860
     1861
     1862
     1863
     1864
     1865
     1866
     1867
     1868
     1869
     1870
     1871
     1872
     1873
     1874
     1875
     1876
     1877
     1878
     1879
     1880
     1881
     1882
     1883
     1884
     1885
     1886
     1887
     1888
     1889
     1890
     1891
     1892
     1893
     1894
     1895
     1896
     1897
     1898
     1899
     1900
     1901
     1902
     1903
     1904
     1905
     1906
     1907
     1908
     1909
     1910
     1911
     1912
     1913
     1914
     1915
     1916
     1917
     1918
     1919
     1920
     1921
     1922
     1923
     1924
     1925
     1926
     1927
     1928
     1929
     1930
     1931
     1932
     1933
     1934
     1935
     1936
     1937
     1938
     1939
     1940
     1941
     1942
     1943
     1944
     1945
     1946
     1947
     1948
     1949
     1950
     1951
     1952
     1953
     1954
     1955
     1956
     1957
     1958
     1959
     1960
     1961
     1962
     1963
     1964
     1965
     1966
     1967
     1968
     1969
     1970
     1971
     1972
     1973
     1974
     1975
     1976
     1977
     1978
     1979
     1980
     1981
     1982
     1983
     1984
     1985
     1986
     1987
     1988
     1989
     1990
     1991
     1992
     1993
     1994
     1995
     1996
     1997
     1998
     1999
     2000
     2001
     2002
     2003
     2004
     2005
     2006
     2007
     2008
     2009
     2010
     2011
     2012
     2013
     2014
     2015
     2016
     2017
     2018
     2019
     2020
     2021
     2022
     2023
     2024
     2025
     2026
     2027
     2028
     2029
     2030
     2031
     2032
     2033
     2034
     2035
     2036
     2037
     2038
     2039
     2040
     2041
     2042
     2043
     2044
     2045
     2046
     2047
     2048
     2049
     2050
     2051
     2052
     2053
     2054
     2055
     2056
     2057
     2058
     2059
     2060
     2061
     2062
     2063
     2064
     2065
     2066
     2067
     2068
     2069
     2070
     2071
     2072
     2073
     2074
     2075
     2076
     2077
     2078
     2079
     2080
     2081
     2082
     2083
     2084
     2085
     2086
     2087
     2088
     2089
     2090
     2091
     2092
     2093
     2094
     2095
     2096
     2097
     2098
     2099
     2100
     2101
     2102
     2103
     2104
     2105
     2106
     2107
     2108
     2109
     2110
     2111
     2112
     2113
     2114
     2115
     2116
     2117
     2118
     2119
     2120
     2121
     2122
     2123
     2124
     2125
     2126
     2127
     2128
     2129
     2130
     2131
     2132
     2133
     2134
     2135
     2136
     2137
     2138
     2139
     2140
     2141
     2142
     2143
     2144
     2145
     2146
     2147
     2148
     2149
     2150
     2151
     2152
     2153
     2154
     2155
     2156
     2157
     2158
     2159
     2160
     2161
     2162
     2163
     2164
     2165
     2166
     2167
     2168
     2169
     2170
     2171
     2172
     2173
     2174
     2175
     2176
     2177
     2178
     2179
     2180
     2181
     2182
     2183
     2184
     2185
     2186
     2187
     2188
     2189
     2190
     2191
     2192
     2193
     2194
     2195
     2196
     2197
     2198
     2199
     2200
     2201
     2202
     2203
     2204
     2205
     2206
     2207
     2208
     2209
     2210
     2211
     2212
     2213
     2214
     2215
     2216
     2217
     2218
     2219
     2220
     2221
     2222
     2223
     2224
     2225
     2226
     2227
     2228
     2229
     2230
     2231
     2232
     2233
     2234
     2235
     2236
     2237
     2238
     2239
     2240
     2241
     2242
     2243
     2244
     2245
     2246
     2247
     2248
     2249
     2250
     2251
     2252
     2253
     2254
     2255
     2256
     2257
     2258
     2259
     2260
     2261
     2262
     2263
     2264
     2265
     2266
     2267
     2268
     2269
     2270
     2271
     2272
     2273
     2274
     2275
     2276
     2277
     2278
     2279
     2280
     2281
     2282
     2283
     2284
     2285
     2286
     2287
     2288
     2289
     2290
     2291
     2292
     2293
     2294
     2295
     2296
     2297
     2298
     2299
     2300
     2301
     2302
     2303
     2304
     2305
     2306
     2307
     2308
     2309
     2310
     2311
     2312
     2313
     2314
     2315
     2316
     2317
     2318
     2319
     2320
     2321
     2322
     2323
     2324
     2325
     2326
     2327
     2328
     2329
     2330
     2331
     2332
     2333
     2334
     2335
     2336
     2337
     2338
     2339
     2340
     2341
     2342
     2343
     2344
     2345
     2346
     2347
     2348
     2349
     2350
     2351
     2352
     2353
     2354
     2355
     2356
     2357
     2358
     2359
     2360
     2361
     2362
     2363
     2364
     2365
     2366
     2367
     2368
     2369
     2370
     2371
     2372
     2373
     2374
     2375
     2376
     2377
     2378
     2379
     2380
     2381
     2382
     2383
     2384
     2385
     2386
     2387
     2388
     2389
     2390
     2391
     2392
     2393
     2394
     2395
     2396
     2397
     2398
     2399
     2400
     2401
     2402
     2403
     2404
     2405
     2406
     2407
     2408
     2409
     2410
     2411
     2412
     2413
     2414
     2415
     2416
     2417
     2418
     2419
     2420
     2421
     2422
     2423
     2424
     2425
     2426
     2427
     2428
     2429
     2430
     2431
     2432
     2433
     2434
     2435
     2436
     2437
     2438
     2439
     2440
     2441
     2442
     2443
     2444
     2445
     2446
     2447
     2448
     2449
     2450
     2451
     2452
     2453
     2454
     2455
     2456
     2457
     2458
     2459
     2460
     2461
     2462
     2463
     2464
     2465
     2466
     2467
     2468
     2469
     2470
     2471
     2472
     2473
     2474
     2475
     2476
     2477
     2478
     2479
     2480
     2481
     2482
     2483
     2484
     2485
     2486
     2487
     2488
     2489
     2490
     2491
     2492
     2493
     2494
     2495
     2496
     2497
     2498
     2499
     2500
     2501
     2502
     2503
     2504
     2505
     2506
     2507
     2508
     2509
     2510
     2511
     2512
     2513
     2514
     2515
     2516
     2517
     2518
     2519
     2520
     2521
     2522
     2523
     2524
     2525
     2526
     2527
     2528
     2529
     2530
     2531
     2532
     2533
     2534
     2535
     2536
     2537
     2538
     2539
     2540
     2541
     2542
     2543
     2544
     2545
     2546
     2547
     2548
     2549
     2550
     2551
     2552
     2553
     2554
     2555
     2556
     2557
     2558
     2559
     2560
     2561
     2562
     2563
     2564
     2565
     2566
     2567
     2568
     2569
     2570
     2571
     2572
     2573
     2574
     2575
     2576
     2577
     2578
     2579
     2580
     2581
     2582
     2583
     2584
     2585
     2586
     2587
     2588
     2589
     2590
     2591
     2592
     2593
     2594
     2595
     2596
     2597
     2598
     2599
     2600
     2601
     2602
     2603
     2604
     2605
     2606
     2607
     2608
     2609
     2610
     2611
     2612
     2613
     2614
     2615
     2616
     2617
     2618
     2619
     2620
     2621
     2622
     2623
     2624
     2625
     2626
     2627
     2628
     2629
     2630
     2631
     2632
     2633
     2634
     2635
     2636
     2637
     2638
     2639
     2640
     2641
     2642
     2643
     2644
     2645
     2646
     2647
     2648
     2649
     2650
     2651
     2652
     2653
     2654
     2655
     2656
     2657
     2658
     2659
     2660
     2661
     2662
     2663
     2664
     2665
     2666
     2667
     2668
     2669
     2670
     2671
     2672
     2673
     2674
     2675
     2676
     2677
     2678
     2679
     2680
     2681
     2682
     2683
     2684
     2685
     2686
     2687
     2688
     2689
     2690
     2691
     2692
     2693
     2694
     2695
     2696
     2697
     2698
     2699
     2700
     2701
     2702
     2703
     2704
     2705
     2706
     2707
     2708
     2709
     2710
     2711
     2712
     2713
     2714
     2715
     2716
     2717
     2718
     2719
     2720
     2721
     2722
     2723
     2724
     2725
     2726
     2727
     2728
     2729
     2730
     2731
     2732
     2733
     2734
     2735
     2736
     2737
     2738
     2739
     2740
     2741
     2742
     2743
     2744
     2745
     2746
     2747
     2748
     2749
     2750
     2751
     2752
     2753
     2754
     2755
     2756
     2757
     2758
     2759
     2760
     2761
     2762
     2763
     2764
     2765
     2766
     2767
     2768
     2769
     2770
     2771
     2772
     2773
     2774
     2775
     2776
     2777
     2778
     2779
     2780
     2781
     2782
     2783
     2784
     2785
     2786
     2787
     2788
     2789
     2790
     2791
     2792
     2793
     2794
     2795
     2796
     2797
     2798
     2799
     2800
     2801
     2802
     2803
     2804
     2805
     2806
     2807
     2808
     2809
     2810
     2811
     2812
     2813
     2814
     2815
     2816
     2817
     2818
     2819
     2820
     2821
     2822
     2823
     2824
     2825
     2826
     2827
     2828
     2829
     2830
     2831
     2832
     2833
     2834
     2835
     2836
     2837
     2838
     2839
     2840
     2841
     2842
     2843
     2844
     2845
     2846
     2847
     2848
     2849
     2850
     2851
     2852
     2853
     2854
     2855
     2856
     2857
     2858
     2859
     2860
     2861
     2862
     2863
     2864
     2865
     2866
     2867
     2868
     2869
     2870
     2871
     2872
     2873
     2874
     2875
     2876
     2877
     2878
     2879
     2880
     2881
     2882
     2883
     2884
     2885
     2886
     2887
     2888
     2889
     2890
     2891
     2892
     2893
     2894
     2895
     2896
     2897
     2898
     2899
     2900
     2901
     2902
     2903
     2904
     2905
     2906
     2907
     2908
     2909
     2910
     2911
     2912
     2913
     2914
     2915
     2916
     2917
     2918
     2919
     2920
     2921
     2922
     2923
     2924
     2925
     2926
     2927
     2928
     2929
     2930
     2931
     2932
     2933
     2934
     2935
     2936
     2937
     2938
     2939
     2940
     2941
     2942
     2943
     2944
     2945
     2946
     2947
     2948
     2949
     2950
     2951
     2952
     2953
     2954
     2955
     2956
     2957
     2958
     2959
     2960
     2961
     2962
     2963
     2964
     2965
     2966
     2967
     2968
     2969
     2970
     2971
     2972
     2973
     2974
     2975
     2976
     2977
     2978
     2979
     2980
     2981
     2982
     2983
     2984
     2985
     2986
     2987
     2988
     2989
     2990
     2991
     2992
     2993
     2994
     2995
     2996
     2997
     2998
     2999
     3000
     3001
     3002
     3003
     3004
     3005
     3006
     3007
     3008
     3009
     3010
     3011
     3012
     3013
     3014
     3015
     3016
     3017
     3018
     3019
     3020
     3021
     3022
     3023
     3024
     3025
     3026
     3027
     3028
     3029
     3030
     3031
     3032
     3033
     3034
     3035
     3036
     3037
     3038
     3039
     3040
     3041
     3042
     3043
     3044
     3045
     3046
     3047
     3048
     3049
     3050
     3051
     3052
     3053
     3054
     3055
     3056
     3057
     3058
     3059
     3060
     3061
     3062
     3063
     3064
     3065
     3066
     3067
     3068
     3069
     3070
     3071
     3072
     3073
     3074
     3075
     3076
     3077
     3078
     3079
     3080
     3081
     3082
     3083
     3084
     3085
     3086
     3087
     3088
     3089
     3090
     3091
     3092
     3093
     3094
     3095
     3096
     3097
     3098
     3099
     3100
     3101
     3102
     3103
     3104
     3105
     3106
     3107
     3108
     3109
     3110
     3111
     3112
     3113
     3114
     3115
     3116
     3117
     3118
     3119
     3120
     3121
     3122
     3123
     3124
     3125
     3126
     3127
     3128
     3129
     3130
     3131
     3132
     3133
     3134
     3135
     3136
     3137
     3138
     3139
     3140
     3141
     3142
     3143
     3144
     3145
     3146
     3147
     3148
     3149
     3150
     3151
     3152
     3153
     3154
     3155
     3156
     3157
     3158
     3159
     3160
     3161
     3162
     3163
     3164
     3165
     3166
     3167
     3168
     3169
     3170
     3171
     3172
     3173
     3174
     3175
     3176
     3177
     3178
     3179
     3180
     3181
     3182
     3183
     3184
     3185
     3186
     3187
     3188
     3189
     3190
     3191
     3192
     3193
     3194
     3195
     3196
     3197
     3198
     3199
     3200
     3201
     3202
     3203
     3204
     3205
     3206
     3207
     3208
     3209
     3210
     3211
     3212
     3213
     3214
     3215
     3216
     3217
     3218
     3219
     3220
     3221
     3222
     3223
     3224
     3225
     3226
     3227
     3228
     3229
     3230
     3231
     3232
     3233
     3234
     3235
     3236
     3237
     3238
     3239
     3240
     3241
     3242
     3243
     3244
     3245
     3246
     3247
     3248
     3249
     3250
     3251
     3252
     3253
     3254
     3255
     3256
     3257
     3258
     3259
     3260
     3261
     3262
     3263
     3264
     3265
     3266
     3267
     3268
     3269
     3270
     3271
     3272
     3273
     3274
     3275
     3276
     3277
     3278
     3279
     3280
     3281
     3282
     3283
     3284
     3285
     3286
     3287
     3288
     3289
     3290
     3291
     3292
     3293
     3294
     3295
     3296
     3297
     3298
     3299
     3300
     3301
     3302
     3303
     3304
     3305
     3306
     3307
     3308
     3309
     3310
     3311
     3312
     3313
     3314
     3315
     3316
     3317
     3318
     3319
     3320
     3321
     3322
     3323
     3324
     3325
     3326
     3327
     3328
     3329
     3330
     3331
     3332
     3333
     3334
     3335
     3336
     3337
     3338
     3339
     3340
     3341
     3342
     3343
     3344
     3345
     3346
     3347
     3348
     3349
     3350
     3351
     3352
     3353
     3354
     3355
     3356
     3357
     3358
     3359
     3360
     3361
     3362
     3363
     3364
     3365
     3366
     3367
     3368
     3369
     3370
     3371
     3372
     3373
     3374
     3375
     3376
     3377
     3378
     3379
     3380
     3381
     3382
     3383
     3384
     3385
     3386
     3387
     3388
     3389
     3390
     3391
     3392
     3393
     3394
     3395
     3396
     3397
     3398
     3399
     3400
     3401
     3402
     3403
     3404
     3405
     3406
     3407
     3408
     3409
     3410
     3411
     3412
     3413
     3414
     3415
     3416
     3417
     3418
     3419
     3420
     3421
     3422
     3423
     3424
     3425
     3426
     3427
     3428
     3429
     3430
     3431
     3432
     3433
     3434
     3435
     3436
     3437
     3438
     3439
     3440
     3441
     3442
     3443
     3444
     3445
     3446
     3447
     3448
     3449
     3450
     3451
     3452
     3453
     3454
     3455
     3456
     3457
     3458
     3459
     3460
     3461
     3462
     3463
     3464
     3465
     3466
     3467
     3468
     3469
     3470
     3471
     3472
     3473
     3474
     3475
     3476
     3477
     3478
     3479
     3480
     3481
     3482
     3483
     3484
     3485
     3486
     3487
     3488
     3489
     3490
     3491
     3492
     3493
     3494
     3495
     3496
     3497
     3498
     3499
     3500
     3501
     3502
     3503
     3504
     3505
     3506
     3507
     3508
     3509
     3510
     3511
     3512
     3513
     3514
     3515
     3516
     3517
     3518
     3519
     3520
     3521
     3522
     3523
     3524
     3525
     3526
     3527
     3528
     3529
     3530
     3531
     3532
     3533
     3534
     3535
     3536
     3537
     3538
     3539
     3540
     3541
     3542
     3543
     3544
     3545
     3546
     3547
     3548
     3549
     3550
     3551
     3552
     3553
     3554
     3555
     3556
     3557
     3558
     3559
     3560
     3561
     3562
     3563
     3564
     3565
     3566
     3567
     3568
     3569
     3570
     3571
     3572
     3573
     3574
     3575
     3576
     3577
     3578
     3579
     3580
     3581
     3582
     3583
     3584
     3585
     3586
     3587
     3588
     3589
     3590
     3591
     3592
     3593
     3594
     3595
     3596
     3597
     3598
     3599
     3600
     3601
     3602
     3603
     3604
     3605
     3606
     3607
     3608
     3609
     3610
     3611
     3612
     3613
     3614
     3615
     3616
     3617
     3618
     3619
     3620
     3621
     3622
     3623
     3624
     3625
     3626
     3627
     3628
     3629
     3630
     3631
     3632
     3633
     3634
     3635
     3636
     3637
     3638
     3639
     3640
     3641
     3642
     3643
     3644
     3645
     3646
     3647
     3648
     3649
     3650
     3651
     3652
     3653
     3654
     3655
     3656
     3657
     3658
     3659
     3660
     3661
     3662
     3663
     3664
     3665
     3666
     3667
     3668
     3669
     3670
     3671
     3672
     3673
     3674
     3675
     3676
     3677
     3678
     3679
     3680
     3681
     3682
     3683
     3684
     3685
     3686
     3687
     3688
     3689
     3690
     3691
     3692
     3693
     3694
     3695
     3696
     3697
     3698
     3699
     3700
     3701
     3702
     3703
     3704
     3705
     3706
     3707
     3708
     3709
     3710
     3711
     3712
     3713
     3714
     3715
     3716
     3717
     3718
     3719
     3720
     3721
     3722
     3723
     3724
     3725
     3726
     3727
     3728
     3729
     3730
     3731
     3732
     3733
     3734
     3735
     3736
     3737
     3738
     3739
     3740
     3741
     3742
     3743
     3744
     3745
     3746
     3747
     3748
     3749
     3750
     3751
     3752
     3753
     3754
     3755
     3756
     3757
     3758
     3759
     3760
     3761
     3762
     3763
     3764
     3765
     3766
     3767
     3768
     3769
     3770
     3771
     3772
     3773
     3774
     3775
     3776
     3777
     3778
     3779
     3780
     3781
     3782
     3783
     3784
     3785
     3786
     3787
     3788
     3789
     3790
     3791
     3792
     3793
     3794
     3795
     3796
     3797
     3798
     3799
     3800
     3801
     3802
     3803
     3804
     3805
     3806
     3807
     3808
     3809
     3810
     3811
     3812
     3813
     3814
     3815
     3816
     3817
     3818
     3819
     3820
     3821
     3822
     3823
     3824
     3825
     3826
     3827
     3828
     3829
     3830
     3831
     3832
     3833
     3834
     3835
     3836
     3837
     3838
     3839
     3840
     3841
     3842
     3843
     3844
     3845
     3846
     3847
     3848
     3849
     3850
     3851
     3852
     3853
     3854
     3855
     3856
     3857
     3858
     3859
     3860
     3861
     3862
     3863
     3864
     3865
     3866
     3867
     3868
     3869
     3870
     3871
     3872
     3873
     3874
     3875
     3876
     3877
     3878
     3879
     3880
     3881
     3882
     3883
     3884
     3885
     3886
     3887
     3888
     3889
     3890
     3891
     3892
     3893
     3894
     3895
     3896
     3897
     3898
     3899
     3900
     3901
     3902
     3903
     3904
     3905
     3906
     3907
     3908
     3909
     3910
     3911
     3912
     3913
     3914
     3915
     3916
     3917
     3918
     3919
     3920
     3921
     3922
     3923
     3924
     3925
     3926
     3927
     3928
     3929
     3930
     3931
     3932
     3933
     3934
     3935
     3936
     3937
     3938
     3939
     3940
     3941
     3942
     3943
     3944
     3945
     3946
     3947
     3948
     3949
     3950
     3951
     3952
     3953
     3954
     3955
     3956
     3957
     3958
     3959
     3960
     3961
     3962
     3963
     3964
     3965
     3966
     3967
     3968
     3969
     3970
     3971
     3972
     3973
     3974
     3975
     3976
     3977
     3978
     3979
     3980
     3981
     3982
     3983
     3984
     3985
     3986
     3987
     3988
     3989
     3990
     3991
     3992
     3993
     3994
     3995
     3996
     3997
     3998
     3999
     4000
     4001
     4002
     4003
     4004
     4005
     4006
     4007
     4008
     4009
     4010
     4011
     4012
     4013
     4014
     4015
     4016
     4017
     4018
     4019
     4020
     4021
     4022
     4023
     4024
     4025
     4026
     4027
     4028
     4029
     4030
     4031
     4032
     4033
     4034
     4035
     4036
     4037
     4038
     4039
     4040
     4041
     4042
     4043
     4044
     4045
     4046
     4047
     4048
     4049
     4050
     4051
     4052
     4053
     4054
     4055
     4056
     4057
     4058
     4059
     4060
     4061
     4062
     4063
     4064
     4065
     4066
     4067
     4068
     4069
     4070
     4071
     4072
     4073
     4074
     4075
     4076
     4077
     4078
     4079
     4080
     4081
     4082
     4083
     4084
     4085
     4086
     4087
     4088
     4089
     4090
     4091
     4092
     4093
     4094
     4095
     4096
     4097
     4098
     4099
     4100
     4101
     4102
     4103
     4104
     4105
     4106
     4107
     4108
     4109
     4110
     4111
     4112
     4113
     4114
     4115
     4116
     4117
     4118
     4119
     4120
     4121
     4122
     4123
     4124
     4125
     4126
     4127
     4128
     4129
     4130
     4131
     4132
     4133
     4134
     4135
     4136
     4137
     4138
     4139
     4140
     4141
     4142
     4143
     4144
     4145
     4146
     4147
     4148
     4149
     4150
     4151
     4152
     4153
     4154
     4155
     4156
     4157
     4158
     4159
     4160
     4161
     4162
     4163
     4164
     4165
     4166
     4167
     4168
     4169
     4170
     4171
     4172
     4173
     4174
     4175
     4176
     4177
     4178
     4179
     4180
     4181
     4182
     4183
     4184
     4185
     4186
     4187
     4188
     4189
     4190
     4191
     4192
     4193
     4194
     4195
     4196
     4197
     4198
     4199
     4200
     4201
     4202
     4203
     4204
     4205
     4206
     4207
     4208
     4209
     4210
     4211
     4212
     4213
     4214
     4215
     4216
     4217
     4218
     4219
     4220
     4221
     4222
     4223
     4224
     4225
     4226
     4227
     4228
     4229
     4230
     4231
     4232
     4233
     4234
     4235
     4236
     4237
     4238
     4239
     4240
     4241
     4242
     4243
     4244
     4245
     4246
     4247
     4248
     4249
     4250
     4251
     4252
     4253
     4254
     4255
     4256
     4257
     4258
     4259
     4260
     4261
     4262
     4263
     4264
     4265
     4266
     4267
     4268
     4269
     4270
     4271
     4272
     4273
     4274
     4275
     4276
     4277
     4278
     4279
     4280
     4281
     4282
     4283
     4284
     4285
     4286
     4287
     4288
     4289
     4290
     4291
     4292
     4293
     4294
     4295
     4296
     4297
     4298
     4299
     4300
     4301
     4302
     4303
     4304
     4305
     4306
     4307
     4308
     4309
     4310
     4311
     4312
     4313
     4314
     4315
     4316
     4317
     4318
     4319
     4320
     4321
     4322
     4323
     4324
     4325
     4326
     4327
     4328
     4329
     4330
     4331
     4332
     4333
     4334
     4335
     4336
     4337
     4338
     4339
     4340
     4341
     4342
     4343
     4344
     4345
     4346
     4347
     4348
     4349
     4350
     4351
     4352
     4353
     4354
     4355
     4356
     4357
     4358
     4359
     4360
     4361
     4362
     4363
     4364
     4365
     4366
     4367
     4368
     4369
     4370
     4371
     4372
     4373
     4374
     4375
     4376
     4377
     4378
     4379
     4380
     4381
     4382
     4383
     4384
     4385
     4386
     4387
     4388
     4389
     4390
     4391
     4392
     4393
     4394
     4395
     4396
     4397
     4398
     4399
     4400
     4401
     4402
     4403
     4404
     4405
     4406
     4407
     4408
     4409
     4410
     4411
     4412
     4413
     4414
     4415
     4416
     4417
     4418
     4419
     4420
     4421
     4422
     4423
     4424
     4425
     4426
     4427
     4428
     4429
     4430
     4431
     4432
     4433
     4434
     4435
     4436
     4437
     4438
     4439
     4440
     4441
     4442
     4443
     4444
     4445
     4446
     4447
     4448
     4449
     4450
     4451
     4452
     4453
     4454
     4455
     4456
     4457
     4458
     4459
     4460
     4461
     4462
     4463
     4464
     4465
     4466
     4467
     4468
     4469
     4470
     4471
     4472
     4473
     4474
     4475
     4476
     4477
     4478
     4479
     4480
     4481
     4482
     4483
     4484
     4485
     4486
     4487
     4488
     4489
     4490
     4491
     4492
     4493
     4494
     4495
     4496
     4497
     4498
     4499
     4500
     4501
     4502
     4503
     4504
     4505
     4506
     4507
     4508
     4509
     4510
     4511
     4512
     4513
     4514
     4515
     4516
     4517
     4518
     4519
     4520
     4521
     4522
     4523
     4524
     4525
     4526
     4527
     4528
     4529
     4530
     4531
     4532
     4533
     4534
     4535
     4536
     4537
     4538
     4539
     4540
     4541
     4542
     4543
     4544
     4545
     4546
     4547
     4548
     4549
     4550
     4551
     4552
     4553
     4554
     4555
     4556
     4557
     4558
     4559
     4560
     4561
     4562
     4563
     4564
     4565
     4566
     4567
     4568
     4569
     4570
     4571
     4572
     4573
     4574
     4575
     4576
     4577
     4578
     4579
     4580
     4581
     4582
     4583
     4584
     4585
     4586
     4587
     4588
     4589
     4590
     4591
     4592
     4593
     4594
     4595
     4596
     4597
     4598
     4599
     4600
     4601
     4602
     4603
     4604
     4605
     4606
     4607
     4608
     4609
     4610
     4611
     4612
     4613
     4614
     4615
     4616
     4617
     4618
     4619
     4620
     4621
     4622
     4623
     4624
     4625
     4626
     4627
     4628
     4629
     4630
     4631
     4632
     4633
     4634
     4635
     4636
     4637
     4638
     4639
     4640
     4641
     4642
     4643
     4644
     4645
     4646
     4647
     4648
     4649
     4650
     4651
     4652
     4653
     4654
     4655
     4656
     4657
     4658
     4659
     4660
     4661
     4662
     4663
     4664
     4665
     4666
     4667
     4668
     4669
     4670
     4671
     4672
     4673
     4674
     4675
     4676
     4677
     4678
     4679
     4680
     4681
     4682
     4683
     4684
     4685
     4686
     4687
     4688
     4689
     4690
     4691
     4692
     4693
     4694
     4695
     4696
     4697
     4698
     4699
     4700
     4701
     4702
     4703
     4704
     4705
     4706
     4707
     4708
     4709
     4710
     4711
     4712
     4713
     4714
     4715
     4716
     4717
     4718
     4719
     4720
     4721
     4722
     4723
     4724
     4725
     4726
     4727
     4728
     4729
     4730
     4731
     4732
     4733
     4734
     4735
     4736
     4737
     4738
     4739
     4740
     4741
     4742
     4743
     4744
     4745
     4746
     4747
     4748
     4749
     4750
     4751
     4752
     4753
     4754
     4755
     4756
     4757
     4758
     4759
     4760
     4761
     4762
     4763
     4764
     4765
     4766
     4767
     4768
     4769
     4770
     4771
     4772
     4773
     4774
     4775
     4776
     4777
     4778
     4779
     4780
     4781
     4782
     4783
     4784
     4785
     4786
     4787
     4788
     4789
     4790
     4791
     4792
     4793
     4794
     4795
     4796
     4797
     4798
     4799
     4800
     4801
     4802
     4803
     4804
     4805
     4806
     4807
     4808
     4809
     4810
     4811
     4812
     4813
     4814
     4815
     4816
     4817
     4818
     4819
     4820
     4821
     4822
     4823
     4824
     4825
     4826
     4827
     4828
     4829
     4830
     4831
     4832
     4833
     4834
     4835
     4836
     4837
     4838
     4839
     4840
     4841
     4842
     4843
     4844
     4845
     4846
     4847
     4848
     4849
     4850
     4851
     4852
     4853
     4854
     4855
     4856
     4857
     4858
     4859
     4860
     4861
     4862
     4863
     4864
     4865
     4866
     4867
     4868
     4869
     4870
     4871
     4872
     4873
     4874
     4875
     4876
     4877
     4878
     4879
     4880
     4881
     4882
     4883
     4884
     4885
     4886
     4887
     4888
     4889
     4890
     4891
     4892
     4893
     4894
     4895
     4896
     4897
     4898
     4899
     4900
     4901
     4902
     4903
     4904
     4905
     4906
     4907
     4908
     4909
     4910
     4911
     4912
     4913
     4914
     4915
     4916
     4917
     4918
     4919
     4920
     4921
     4922
     4923
     4924
     4925
     4926
     4927
     4928
     4929
     4930
     4931
     4932
     4933
     4934
     4935
     4936
     4937
     4938
     4939
     4940
     4941
     4942
     4943
     4944
     4945
     4946
     4947
     4948
     4949
     4950
     4951
     4952
     4953
     4954
     4955
     4956
     4957
     4958
     4959
     4960
     4961
     4962
     4963
     4964
     4965
     4966
     4967
     4968
     4969
     4970
     4971
     4972
     4973
     4974
     4975
     4976
     4977
     4978
     4979
     4980
     4981
     4982
     4983
     4984
     4985
     4986
     4987
     4988
     4989
     4990
     4991
     4992
     4993
     4994
     4995
     4996
     4997
     4998
     4999
     5000
     5001
     5002
     5003
     5004
     5005
     5006
     5007
     5008
     5009
     5010
     5011
     5012
     5013
     5014
     5015
     5016
     5017
     5018
     5019
     5020
     5021
     5022
     5023
     5024
     5025
     5026
     5027
     5028
     5029
     5030
     5031
     5032
     5033
     5034
     5035
     5036
     5037
     5038
     5039
     5040
     5041
     5042
     5043
     5044
     5045
     5046
     5047
     5048
     5049
     5050
     5051
     5052
     5053
     5054
     5055
     5056
     5057
     5058
     5059
     5060
     5061
     5062
     5063
     5064
     5065
     5066
     5067
     5068
     5069
     5070
     5071
     5072
     5073
     5074
     5075
     5076
     5077
     5078
     5079
     5080
     5081
     5082
     5083
     5084
     5085
     5086
     5087
     5088
     5089
     5090
     5091
     5092
     5093
     5094
     5095
     5096
     5097
     5098
     5099
     5100
     5101
     5102
     5103
     5104
     5105
     5106
     5107
     5108
     5109
     5110
     5111
     5112
     5113
     5114
     5115
     5116
     5117
     5118
     5119
     5120
     5121
     5122
     5123
     5124
     5125
     5126
     5127
     5128
     5129
     5130
     5131
     5132
     5133
     5134
     5135
     5136
     5137
     5138
     5139
     5140
     5141
     5142
     5143
     5144
     5145
     5146
     5147
     5148
     5149
     5150
     5151
     5152
     5153
     5154
     5155
     5156
     5157
     5158
     5159
     5160
     5161
     5162
     5163
     5164
     5165
     5166
     5167
     5168
     5169
     5170
     5171
     5172
     5173
     5174
     5175
     5176
     5177
     5178
     5179
     5180
     5181
     5182
     5183
     5184
     5185
     5186
     5187
     5188
     5189
     5190
     5191
     5192
     5193
     5194
     5195
     5196
     5197
     5198
     5199
     5200
     5201
     5202
     5203
     5204
     5205
     5206
     5207
     5208
     5209
     5210
     5211
     5212
     5213
     5214
     5215
     5216
     5217
     5218
     5219
     5220
     5221
     5222
     5223
     5224
     5225
     5226
     5227
     5228
     5229
     5230
     5231
     5232
     5233
     5234
     5235
     5236
     5237
     5238
     5239
     5240
     5241
     5242
     5243
     5244
     5245
     5246
     5247
     5248
     5249
     5250
     5251
     5252
     5253
     5254
     5255
     5256
     5257
     5258
     5259
     5260
     5261
     5262
     5263
     5264
     5265
     5266
     5267
     5268
     5269
     5270
     5271
     5272
     5273
     5274
     5275
     5276
     5277
     5278
     5279
     5280
     5281
     5282
     5283
     5284
     5285
     5286
     5287
     5288
     5289
     5290
     5291
     5292
     5293
     5294
     5295
     5296
     5297
     5298
     5299
     5300
     5301
     5302
     5303
     5304
     5305
     5306
     5307
     5308
     5309
     5310
     5311
     5312
     5313
     5314
     5315
     5316
     5317
     5318
     5319
     5320
     5321
     5322
     5323
     5324
     5325
     5326
     5327
     5328
     5329
     5330
     5331
     5332
     5333
     5334
     5335
     5336
     5337
     5338
     5339
     5340
     5341
     5342
     5343
     5344
     5345
     5346
     5347
     5348
     5349
     5350
     5351
     5352
     5353
     5354
     5355
     5356
     5357
     5358
     5359
     5360
     5361
     5362
     5363
     5364
     5365
     5366
     5367
     5368
     5369
     5370
     5371
     5372
     5373
     5374
     5375
     5376
     5377
     5378
     5379
     5380
     5381
     5382
     5383
     5384
     5385
     5386
     5387
     5388
     5389
     5390
     5391
     5392
     5393
     5394
     5395
     5396
     5397
     5398
     5399
     5400
     5401
     5402
     5403
     5404
     5405
     5406
     5407
     5408
     5409
     5410
     5411
     5412
     5413
     5414
     5415
     5416
     5417
     5418
     5419
     5420
     5421
     5422
     5423
     5424
     5425
     5426
     5427
     5428
     5429
     5430
     5431
     5432
     5433
     5434
     5435
     5436
     5437
     5438
     5439
     5440
     5441
     5442
     5443
     5444
     5445
     5446
     // Input: ~1000 signed integer; -17 +14 +10 -2 -1 +6 +6 +7 +1 +9 +8 -13 -7...
     //
     // Problem 1: What is resulting frequency af    ter applying input frequencies?
     // Solution 1: Sum all inputs
     //
     // Problem 2: What is the first frequency encountered twice?
     // Solution 2: Iterate, sum, look for duplicate
     pub mod day01 {
         use hashbrown::HashSet;
         use lazy_static::lazy_static;
         use std::fmt::Write;
     
         pub fn run() -> String {
             let mut result: String = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day1.txt").unwrap();
             }
     
             let lines = file_string.lines();
             let values = lines.map(|line| line.parse::<i32>().unwrap());
     
             // Day 1; Problem 1
             let sum = values.clone().sum::<i32>();
             writeln!(&mut result, "Day 1, Problem 1 - [{}]", sum).unwrap(); // 522
     
             // Day 2; Problem 2
             let mut sum = 0;
             let mut set = HashSet::<i32>::default();
     
             for v in values.clone().cycle() {
                 sum += v;
                 let inserted = set.insert(sum);
                 if !inserted {
                     break;
                 }
             }
     
             writeln!(&mut result, "Day 1, Problem 2 - [{}]", sum).unwrap(); // 73364
     
             result
         }
     }
     
     // Input: ~250 short strings; efhyxuxckqldtwjzvisbpargko
     //
     // Problem 1: Count letters that appear 2 or 3+ times
     // Solution 1: Iterate strings, iterate characters, count
     //
     // Problem 2: Common letters between two strings which differ by only 1 letter
     // Solution 2: Iterate all pairs and compare
     pub mod day02 {
         use lazy_static::lazy_static;
         use std::fmt::Write;
     
         pub fn run() -> String {
             let mut result: String = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day2.txt").unwrap();
             }
     
             let lines: Vec<&str> = file_string.lines().collect();
     
             // Day 2; Problem 1
             let mut total_two_count = 0;
             let mut total_three_count = 0;
     
             // Iterate input strings
             for line in &lines {
                 let mut count: [i32; 26] = [0; 26];
                 let mut line_two_count = 0;
                 let mut line_three_count = 0;
     
                 // Iterate chars in string
                 for letter in line.chars() {
                     let index: usize = (letter as usize) - ('a' as usize);
                     let new_count = count[index] + 1;
     
                     // Update counts
                     match new_count {
                         2 => line_two_count += 1,
                         3 => {
                             line_two_count -= 1;
                             line_three_count += 1;
                         }
                         4 => line_three_count -= 1,
                         _ => (),
                     }
                     count[index] = new_count;
                 }
     
                 if line_two_count > 0 {
                     total_two_count += 1;
                 }
                 if line_three_count > 0 {
                     total_three_count += 1;
                 }
             }
     
             writeln!(
                 &mut result,
                 "Day 2, Problem 1 - [{}]",
                 total_two_count * total_three_count
             )
             .unwrap(); // 7808
     
             // Day 2; Problem 2
             let mut answer: Option<(&str, usize)> = None;
             for line0 in &lines {
                 let chars0 = line0.chars().map(|v| v as i32);
     
                 for line1 in &lines {
                     let chars1 = line1.chars().map(|v| v as i32);
     
                     let mut diff_index: Option<usize> = None;
                     for (i, (a, b)) in chars0.clone().zip(chars1).enumerate() {
                         if a == b {
                             continue;
                         } else if diff_index == None {
                             diff_index = Some(i);
                         } else {
                             diff_index = None;
                             break;
                         }
                     }
     
                     if diff_index != None {
                         answer = Some((line0, diff_index.unwrap()));
                         break;
                     }
                 }
     
                 if answer != None {
                     break;
                 }
             }
     
             match answer {
                 Some(answer) => {
                     let s = answer.0.to_string();
                     let n = answer.1;
                     let final_answer = format!("{}{}", &s[..n], &s[n + 1..]);
     
                     writeln!(&mut result, "Day 2, Problem 2 - [{}]", final_answer).unwrap(); // efmyhuckqldtwjyvisipargno
                 }
                 None => println!("Day 2, Problem 2 - Failed to find an answer"),
             }
     
             result
         }
     }
     
     // Input: ~1200 rectangles; #1 @ 338,764: 20x24
     //
     // Problem 1: How many positions covered by 2+ rectangles
     // Solution 1: Accumulate buffer, count how many 2+
     //
     // Problem 2: Which rect isn't overlapped with any other rect?
     // Solution 2: Iterate all rects, check accumulation buffer
     pub mod day03 {
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::cmp::max;
         use std::fmt::Write;
     
         #[derive(Copy, Clone)]
         struct Box {
             min_x: u16,
             max_x: u16,
             min_y: u16,
             max_y: u16,
         }
     
         impl Box {
             fn new(x: u16, y: u16, width: u16, height: u16) -> Box {
                 Box {
                     min_x: x,
                     min_y: y,
                     max_x: x + width - 1,
                     max_y: y + height - 1,
                 }
             }
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day3.txt").unwrap();
             }
     
             lazy_static! {
                 static ref re: Regex =
                     Regex::new(r"#\d+ @ (?P<x>\d+),(?P<y>\d+): (?P<width>\d+)x(?P<height>\d+)")
                         .unwrap();
             }
     
             let mut boxes: Vec<Box> = Vec::new();
             let mut width = 0;
             let mut height = 0;
     
             for line in file_string.lines() {
                 let caps = re.captures(line).unwrap();
                 let x = caps["x"].parse().unwrap();
                 let y = caps["y"].parse().unwrap();
                 let w = caps["width"].parse().unwrap();
                 let h = caps["height"].parse().unwrap();
     
                 let new_box = Box::new(x, y, w, h);
                 boxes.push(new_box);
     
                 width = max(width, (new_box.max_x + 1) as usize);
                 height = max(height, (new_box.max_y + 1) as usize);
             }
     
             // Problem 1
             let mut overlaps: Vec<u8> = vec![0; width * height];
     
             // Write boxes into overlaps buffer
             for b in &boxes {
                 for row in b.min_y..=b.max_y {
                     for col in b.min_x..=b.max_x {
                         let idx = row as usize * width + col as usize;
                         unsafe {
                             let value = overlaps.get_unchecked_mut(idx);
                             *value = value.saturating_add(1);
                         }
                     }
                 }
             }
     
             let overlapped = overlaps.iter().filter(|c| **c >= 2).count();
             writeln!(&mut result, "Day 3, Problem 1 - [{}]", overlapped).unwrap(); // 100595
     
             // Problem 2
             let mut non_overlapped: Option<usize> = None;
     
             // Iterate boxes
             for (i, b) in boxes.iter().enumerate() {
                 let mut any_overlaps = false;
     
                 // Iterate tiles overlapped by box
                 'outer: for row in b.min_y..=b.max_y {
                     for col in b.min_x..=b.max_x {
                         let idx = row as usize * width + col as usize;
     
                         // Check if tile has multiple overlaps
                         let count = unsafe { overlaps.get_unchecked(idx) };
                         if *count >= 2 {
                             any_overlaps = true;
                             break 'outer;
                         }
                     }
                 }
     
                 // Check if this box had zero overlaps
                 if !any_overlaps {
                     non_overlapped = Some(i + 1);
                     break;
                 }
             }
     
             writeln!(
                 &mut result,
                 "Day 3, Problem 2 - [{}]",
                 non_overlapped.unwrap()
             )
             .unwrap(); // 415
     
             result
         }
     }
     
     // Input: Guard log entries; [1518-05-14 23:58] Guard #1559 begins shift
     //
     // Problem 1: Who is the sleepiest guard and at what minute does he sleep?
     // Solution 1: Process logbook, find sleepiest guard, pick sleepiest minute
     //
     // Problem 2: What guard sleeps the most on a particular minute?
     // Solution 2: Iterate guards and minutes slept to find maximum
     //
     // Note: Solution mildly overcomplicated because problem was constrained
     //       further than I realized. Oops!
     pub mod day04 {
         use hashbrown::HashMap;
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::fmt::Write;
     
         #[derive(PartialEq)]
         enum GuardAction {
             BeginsShift(i32),
             FallsAsleep,
             WakesUp,
         }
     
         struct GuardLog {
             year: i32,
             month: i32,
             day: i32,
             hour: i32,
             minute: i32,
             action: GuardAction,
         }
     
         struct GuardInfo {
             total_sleep_time: i32,
             sleep_time_per_minute: [i32; 60],
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day4.txt").unwrap();
             }
     
             lazy_static! {
                 // Regex to parse "[year-month-day hour:minte] remainder"
                 static ref date_regex: Regex = Regex::new(
                     r"(?x)                      # Ignore regex whitespace + enable comments
                     \[(?P<year>\d+)-            # OpenBracket (Year) Dash
                     (?P<month>\d+)-             # (Month) Dash
                     (?P<day>\d+)\s*             # (Day) whitespace
                     (?P<hour>\d+):              # (Hour) colon
                     (?P<minute>\d+)\]\s*        # (Minute) CloseBracket
                     (?P<remainder>.*)           # (EverythingLeft)
                 "
                 ).unwrap();
             }
     
             lazy_static! {
                 // Regex to parse guard id from "Guard #1559 begins shift"
                 static ref guard_regex: Regex = Regex::new(
                     r"(?x)              # Ignore regex whitespace + enable comments
                     .+                  # Read everything
                     \#                  # Up to the first '#'
                     (?P<id>\d+)         # Then parse an (ID) number
                 "
                 ).unwrap();
             }
     
             let mut logbook: Vec<GuardLog> = Vec::new();
     
             // Parse GuardLog structs from input file
             for line in file_string.lines() {
                 let caps = date_regex.captures(line);
                 if caps.is_some() {
                     let caps = caps.unwrap();
                     let y: i32 = caps["year"].parse().unwrap();
                     let m: i32 = caps["month"].parse().unwrap();
                     let d: i32 = caps["day"].parse().unwrap();
                     let h: i32 = caps["hour"].parse().unwrap();
                     let min: i32 = caps["minute"].parse().unwrap();
                     let rem: String = caps["remainder"].parse().unwrap();
     
                     let action = match rem.as_ref() {
                         "wakes up" => GuardAction::WakesUp,
                         "falls asleep" => GuardAction::FallsAsleep,
                         _ => GuardAction::BeginsShift(
                             guard_regex.captures(&rem).unwrap()["id"].parse().unwrap(),
                         ),
                     };
     
                     logbook.push(GuardLog {
                         year: y,
                         month: m,
                         day: d,
                         hour: h,
                         minute: min,
                         action,
                     });
                 }
             }
     
             // Sort log book
             logbook.sort_by(|a, b| {
                 a.year
                     .cmp(&b.year)
                     .then_with(|| a.month.cmp(&b.month))
                     .then_with(|| a.day.cmp(&b.day))
                     .then_with(|| a.hour.cmp(&b.hour))
                     .then_with(|| a.minute.cmp(&b.minute))
             });
     
             // Accumulate guard sleep
             let mut guard_infos: HashMap<i32, GuardInfo> = HashMap::default(); // id -> sleeptime
             let mut curr_guard: i32 = -1;
             let mut sleep_start: (i32, i32) = (-1, -1);
     
             for entry in logbook {
                 match entry.action {
                     GuardAction::BeginsShift(id) => {
                         curr_guard = id;
                     }
                     GuardAction::FallsAsleep => {
                         sleep_start = (entry.hour, entry.minute);
                     }
                     GuardAction::WakesUp => {
                         let sleep_end = (entry.hour, entry.minute);
                         let sleep_time = if sleep_end.0 == sleep_start.0 {
                             sleep_end.1 - sleep_start.1
                         } else {
                             (sleep_end.1 + 60) - sleep_start.1
                         };
     
                         let guard_info = guard_infos.entry(curr_guard).or_insert(GuardInfo {
                             total_sleep_time: 0,
                             sleep_time_per_minute: [0; 60],
                         });
     
                         guard_info.total_sleep_time += sleep_time;
     
                         let mut t = sleep_start.1;
                         loop {
                             guard_info.sleep_time_per_minute[t as usize] += 1;
     
                             t += 1;
                             if t >= 60 {
                                 t = 0;
                             }
     
                             if t == sleep_end.1 {
                                 break;
                             }
                         }
                     }
                 };
             }
     
             let mut sleepiest_guard_id: i32 = -1;
             let mut sleepiest_time: i32 = -1;
     
             for (guard_id, guard_info) in &guard_infos {
                 if guard_info.total_sleep_time > sleepiest_time {
                     sleepiest_guard_id = *guard_id;
                     sleepiest_time = guard_info.total_sleep_time;
                 }
             }
     
             let mut sleepiest_minute: i32 = -1;
             let mut sleepiest_minute_time: i32 = -1;
             let sleepiest_guard_info = &guard_infos[&sleepiest_guard_id];
             for m in 0..60 {
                 let t = sleepiest_guard_info.sleep_time_per_minute[m];
                 if t > sleepiest_minute_time {
                     sleepiest_minute = m as i32;
                     sleepiest_minute_time = t;
                 }
             }
             let answer = sleepiest_guard_id * sleepiest_minute;
             writeln!(&mut result, "Day 4, Problem 1 - [{}]", answer).unwrap(); // 146622
     
             // Part 2
             let mut sleepiest_guard_id: i32 = -1;
             let mut sleepiest_minute: i32 = -1;
             let mut sleepiest_minute_time: i32 = -1;
             for (guard_id, guard_info) in &guard_infos {
                 for m in 0..60 {
                     let t = guard_info.sleep_time_per_minute[m];
                     if t > sleepiest_minute_time {
                         sleepiest_minute_time = t;
                         sleepiest_minute = m as i32;
                         sleepiest_guard_id = *guard_id;
                     }
                 }
             }
             let answer = sleepiest_guard_id * sleepiest_minute;
             writeln!(&mut result, "Day 4, Problem 2 - [{}]", answer).unwrap(); // 31848
     
             result
         }
     }
     
     // Input: 50,000 random upper and lowercase characters
     //
     // Problem 1: Recursively remove pairs of characters the meet certain critera. What is new length?
     // Solution 1: Linearly process input and count
     //
     // Problem 2: What is shortest output if all values of a certain letter are ignored?
     // Solution 2: Iterate for a..z and pick shortest
     pub mod day05 {
         use lazy_static::lazy_static;
         use std::fmt::Write;
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Read input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day5.txt").unwrap();
             }
     
             let source_polymer = file_string.as_bytes();
     
             // Problem 1 + Problem 2
             let mut new_polymer = Vec::<u8>::with_capacity(source_polymer.len());
     
             let mut problem_1_solution: usize = 0;
             let mut problem_2_solution = std::usize::MAX;
     
             // -1 -> Problem 1
             // [0..26] -> Problem 2
             for i in -1i32..=26 {
                 let lower: u8 = if i >= 0 { b'a' + i as u8 } else { 0 };
                 let upper: u8 = if i >= 0 { b'A' + i as u8 } else { 0 };
     
                 // Iterate each character in source polymer
                 for curr in source_polymer {
                     // Ignore all values of a particular character for part2
                     if i >= 0 && (*curr == lower || *curr == upper) {
                         continue;
                     }
     
                     // Check if we should push this character or pop the last
                     let should_push = match new_polymer.last() {
                         None => true,
                         Some(last) => {
                             !curr.eq_ignore_ascii_case(last)
                                 || curr.is_ascii_lowercase() == last.is_ascii_lowercase()
                         }
                     };
     
                     if should_push {
                         new_polymer.push(*curr);
                     } else {
                         new_polymer.pop();
                     }
                 }
     
                 // Update answers
                 let len = new_polymer.len();
                 if i == -1 {
                     problem_1_solution = len;
                 } else {
                     problem_2_solution = std::cmp::min(problem_2_solution, len);
                 }
     
                 new_polymer.clear();
             }
     
             writeln!(&mut result, "Day 5, Problem 1 - [{}]", problem_1_solution).unwrap(); // 9704
             writeln!(&mut result, "Day 5, Problem 2 - [{}]", problem_2_solution).unwrap(); // 6942
     
             result
         }
     }
     
     // Input: Series of coordinates; 156, 193
     //
     // Problem 1: Points are dangerous, find non-infinite area furthest from other points
     // Solution 1: Flood-fill out from each point. Pick largest area not against border.
     //
     // Problem 2: Points are safe. Find region within 10,000 of all points
     // Solution 2: Calculate distance to points. Flood-fill.
     pub mod day06 {
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::cmp::max;
         use std::collections::VecDeque;
         use std::fmt::Write;
     
         const EMPTY: i32 = -1;
         const FULL: i32 = -2;
     
         #[derive(Copy, Clone)]
         struct Coordinate {
             x: i32,
             y: i32,
         }
     
         #[derive(Copy, Clone)]
         struct BestDistance {
             id: i32,
             distance: i32,
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day6.txt").unwrap();
             }
     
             lazy_static! {
                 // Regex to parse: x, y
                 static ref xy_regex: Regex = Regex::new(
                     r"(?x)          # Ignore regex whitespace + enable comments
                     (?P<x>\d+)      # (x) coordinate
                     ,\s*            # comma whitespace
                     (?P<y>\d+)      # (y) coordinate
                 "
                 )
                 .unwrap();
             }
     
             let mut width = 0;
             let mut height = 0;
             let mut coordinates: Vec<Coordinate> = Vec::new();
     
             for line in file_string.lines() {
                 let caps = xy_regex.captures(line);
                 if caps.is_some() {
                     let caps = caps.unwrap();
                     let x: i32 = caps["x"].parse().unwrap();
                     let y: i32 = caps["y"].parse().unwrap();
     
                     let coordinate = Coordinate {
                         x: x as i32,
                         y: y as i32,
                     };
     
                     coordinates.push(coordinate);
                     width = max(width, x + 1);
                     height = max(height, y + 1);
                 }
             }
     
             // Problem 1
             let default = BestDistance {
                 id: EMPTY,
                 distance: std::i32::MAX,
             };
     
             let mut distances: Vec<BestDistance> = vec![default; (width * height) as usize];
             let mut is_finite: Vec<bool> = vec![true; coordinates.len()];
     
             // Calculate each distance
             for row in 0..height {
                 for col in 0..width {
                     let entry = &mut distances[(row * width + col) as usize];
     
                     for (id, coordinate) in coordinates.iter().enumerate() {
                         let dist = (coordinate.x - col).abs() + (coordinate.y - row).abs();
     
                         if dist < entry.distance {
                             entry.id = id as i32;
                             entry.distance = dist;
                         } else if dist == entry.distance {
                             entry.id = FULL;
                         }
                     }
     
                     if (row == 0 || row == height - 1 || col == 0 || col == width - 1)
                         && entry.id != FULL
                     {
                         let idx = entry.id as usize;
                         is_finite[idx] = false;
                     }
                 }
             }
     
             // Perform count
             let mut counts: Vec<i32> = vec![0; coordinates.len()];
             for entry in &distances {
                 if entry.id == FULL {
                     continue;
                 }
     
                 let idx = entry.id as usize;
                 counts[idx] += 1;
             }
     
             // Pick largest finite
             let mut max_finite = 0;
             for i in 0..counts.len() {
                 if is_finite[i] && counts[i] > max_finite {
                     max_finite = counts[i];
                 }
             }
     
             writeln!(&mut result, "Day 6, Problem 1 - [{}]", max_finite).unwrap(); // 4342
     
             // Problem 2
             const MAX_DISTANCE: i32 = 10000;
     
             let mut distances: Vec<i32> = vec![0; (width * height) as usize];
             for row in 0..height {
                 for col in 0..width {
                     let entry = &mut distances[(row * width + col) as usize];
     
                     for coordinate in &coordinates {
                         let dist = (coordinate.x - col).abs() + (coordinate.y - row).abs();
                         let new_dist = *entry + dist;
                         *entry = new_dist;
     
                         if new_dist > MAX_DISTANCE {
                             break;
                         }
                     }
                 }
             }
     
             // Flood fill
             let mut nodes: VecDeque<Coordinate> = VecDeque::new();
             let mut tested: Vec<bool> = vec![false; (width * height) as usize];
     
             let push = |nodes: &mut VecDeque<Coordinate>, tested: &mut Vec<bool>, x, y| {
                 if x >= 0 && x < width && y >= 0 && y < height {
                     let idx = (y * width + x) as usize;
                     if tested[idx] {
                         return;
                     }
     
                     tested[idx] = true;
                     if distances[idx] < MAX_DISTANCE {
                         nodes.push_back(Coordinate { x, y });
                     }
                 }
             };
     
             let mut max_count = 0;
             for row in 0..height {
                 for col in 0..width {
                     let idx = (row * width + col) as usize;
     
                     if tested[idx] {
                         continue;
                     }
     
                     let distance = distances[(row * width + col) as usize];
                     if distance >= MAX_DISTANCE {
                         continue;
                     }
     
                     let mut count = 0;
                     push(&mut nodes, &mut tested, col, row);
     
                     while !nodes.is_empty() {
                         let node = nodes.pop_front().unwrap();
                         count += 1;
     
                         push(&mut nodes, &mut tested, node.x - 1, node.y);
                         push(&mut nodes, &mut tested, node.x + 1, node.y);
                         push(&mut nodes, &mut tested, node.x, node.y - 1);
                         push(&mut nodes, &mut tested, node.x, node.y + 1);
                     }
     
                     max_count = max(max_count, count);
                 }
             }
     
             writeln!(&mut result, "Day 6, Problem 2 - [{}]", max_count).unwrap(); // 42966
     
             result
         }
     }
     
     // Input: 100 steps; Step Y must be finished before step J can begin.
     //
     // Problem 1: Given steps, what order should the be completed it
     // Solution 1: Build dependencies. Complete steps whose dependencies have been completed.
     //
     // Problem 2: Steps must be completed by elves and take time. How long to finish?
     // Solution 2: Assign workers to steps and increment time.
     pub mod day07 {
         use hashbrown::{HashMap, HashSet};
         use lazy_static::lazy_static;
         use std::fmt::Write;
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Process input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day7.txt").unwrap();
             }
     
             let mut dependencies: HashMap<u8, HashSet<u8>> = HashMap::default();
     
             for line in file_string.lines() {
                 let chars = line.as_bytes();
                 let depends_on = chars[5] - b'A';
                 let step = chars[36] - b'A';
     
                 dependencies
                     .entry(step)
                     .or_insert_with(HashSet::default)
                     .insert(depends_on);
     
                 dependencies
                     .entry(depends_on)
                     .or_insert_with(HashSet::default);
             }
     
             let mut num_completed = 0;
             let mut completed: Vec<bool> = vec![false; dependencies.len()];
             let mut completeable: Vec<u8> = Vec::new();
             let mut order: Vec<u8> = Vec::new();
     
             while num_completed < dependencies.len() {
                 for (step, deps) in &dependencies {
                     if completed[*step as usize] {
                         continue;
                     }
     
                     let mut all_completed = true;
                     for dep in deps {
                         if !completed[*dep as usize] {
                             all_completed = false;
                             break;
                         }
                     }
     
                     if all_completed {
                         completeable.push(*step);
                     }
                 }
     
                 completeable.sort();
                 order.push(completeable[0]);
                 completed[completeable[0] as usize] = true;
                 num_completed += 1;
     
                 completeable.clear();
             }
     
             let mut answer: String = String::with_capacity(order.len());
             for step in order {
                 answer.push((b'A' + step) as char);
             }
     
             writeln!(&mut result, "Day 7, Problem 1 - [{}]", answer).unwrap(); // CHILFNMORYKGAQXUVBZPSJWDET
     
             // Problem 2
             let mut num_completed = 0;
             let mut completed: Vec<bool> = vec![false; dependencies.len()];
             let mut in_progress: HashSet<u8> = HashSet::default();
             let mut workable: Vec<u8> = Vec::new();
     
             #[derive(Copy, Clone)]
             struct Worker {
                 step: u8,
                 time_left: u8,
             };
     
             const NO_STEP: u8 = 255;
             const NUM_WORKERS: usize = 5;
             const BASE_TIME: u8 = 60;
     
             let mut workers: [Worker; NUM_WORKERS] = [Worker {
                 step: NO_STEP,
                 time_left: 0,
             }; NUM_WORKERS];
             let mut free_workers = NUM_WORKERS;
             let mut time = 0;
     
             while num_completed < dependencies.len() {
                 let mut work_str = String::new();
                 writeln!(&mut work_str, "{}", time).unwrap();
     
                 // Advance time
                 let mut did_work = false;
                 for worker in &mut workers {
                     if worker.step != NO_STEP {
                         writeln!(&mut work_str, " {}", (worker.step + b'A') as char).unwrap();
     
                         worker.time_left -= 1;
                         did_work = true;
                         if worker.time_left == 0 {
                             completed[worker.step as usize] = true;
                             num_completed += 1;
                             in_progress.remove(&worker.step);
     
                             worker.step = NO_STEP;
                             free_workers += 1;
                         }
                     } else {
                         writeln!(&mut work_str, " .").unwrap();
                     }
                 }
     
                 if did_work {
                     time += 1;
                 }
     
                 // All workers busy, advance time!
                 if free_workers == 0 {
                     continue;
                 }
     
                 // Look for steps that are workable but not in_progress
                 for (step, deps) in &dependencies {
                     if completed[*step as usize] {
                         continue;
                     }
     
                     if in_progress.contains(step) {
                         continue;
                     }
     
                     let mut all_completed = true;
                     for dep in deps {
                         if !completed[*dep as usize] {
                             all_completed = false;
                             break;
                         }
                     }
     
                     if all_completed {
                         workable.push(*step);
                     }
                 }
     
                 // Assign workable steps to workers
                 workable.sort();
                 let mut i = 0;
                 while free_workers > 0 && i < workable.len() {
                     for worker in &mut workers {
                         if worker.step == NO_STEP {
                             worker.step = workable[i];
                             worker.time_left = BASE_TIME + worker.step + 1;
                             i += 1;
                             in_progress.insert(worker.step);
                             free_workers -= 1;
                         }
     
                         if i >= workable.len() {
                             break;
                         }
                     }
                 }
                 workable.clear();
             }
     
             writeln!(&mut result, "Day 7, Problem 2 - [{}]", time).unwrap(); // 891
     
             result
         }
     }
     
     // Input: Long sequence of positive integers
     //
     // Problem 1: Parse data structure tree from integer stream
     // Solution 1: Recursively parse stream
     //
     // Problem 2: Extract value from the tree root that depends on children
     // Solution 2: Traverse tree to obtain value
     pub mod day08 {
         use lazy_static::lazy_static;
         use std::fmt::Write;
     
         #[derive(Clone)]
         struct Node {
             metadata: Vec<i32>,
             children: Vec<Node>,
         }
     
         impl Node {
             pub fn new() -> Node {
                 Node {
                     metadata: Vec::new(),
                     children: Vec::new(),
                 }
             }
     
             pub fn parse(&mut self, stream: &[i32]) -> usize {
                 let mut num_read = 0;
     
                 let num_children = stream[0];
                 let num_metadata = stream[1] as usize;
                 num_read += 2;
     
                 let mut stream = &stream[2..];
                 for _ in 0..num_children {
                     let mut child = Node::new();
                     let child_num_read = child.parse(stream);
                     num_read += child_num_read;
     
                     self.children.push(child);
                     stream = &stream[child_num_read..];
                 }
     
                 for i in 0..num_metadata {
                     self.metadata.push(stream[i]);
                 }
                 num_read += num_metadata;
     
                 num_read
             }
     
             pub fn sum_metadata(&self) -> i32 {
                 let mut sum = self.metadata.iter().sum();
     
                 sum += self.children.iter().map(|c| c.sum_metadata()).sum::<i32>();
     
                 sum
             }
     
             pub fn value(&self) -> i32 {
                 let mut value = 0;
     
                 if self.children.is_empty() {
                     return self.metadata.iter().sum();
                 }
     
                 for i in &self.metadata {
                     let idx = (i - 1) as usize;
                     if idx < self.children.len() {
                         value += self.children[idx].value();
                     }
                 }
     
                 value
             }
         }
     
         pub fn run() -> String {
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day8.txt").unwrap();
             }
     
             let nums: Vec<i32> = file_string
                 .split(' ')
                 .map(|s| s.parse::<i32>().unwrap())
                 .collect();
     
             let mut root = Node::new();
             root.parse(&nums[..]);
     
             let mut result = String::with_capacity(128);
             writeln!(&mut result, "Day 8, Problem 1 - [{}]", root.sum_metadata()).unwrap(); // 38567
             writeln!(&mut result, "Day 8, Problem 2 - [{}]", root.value()).unwrap(); // 24453
     
             result
         }
     }
     
     // Input: 462 players; last marble is worth 71938 points
     //
     // Problem 1: Given a circle of marbles and specific rules, what is the winning score?
     // Solution 1: Build a double-linked list to represent circle. Follow game rules.
     //
     // Problem 2: Play the game, but with 100x larger end score
     // Solution 2: Optimize to run fast.
     //
     // Notes: Double-linked implemented implemented via Vec + indices for performance
     pub mod day09 {
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::fmt::Write;
     
         const INVALID: u32 = std::u32::MAX;
     
         // A data-less double-linked-list node
         #[derive(Copy, Clone)]
         struct Marble {
             prev: u32,
             next: u32,
         }
     
         // Container for nodes with utilities for insertion, removal, and iteration
         struct MarbleCircle {
             marbles: Vec<Marble>,
         }
     
         impl MarbleCircle {
             pub fn new(last_marble: usize) -> Self {
                 let default_marble = Marble {
                     prev: INVALID,
                     next: INVALID,
                 };
     
                 let mut marbles: Vec<Marble> = vec![default_marble; last_marble + 1];
                 marbles[0] = Marble { prev: 0, next: 0 };
     
                 MarbleCircle { marbles }
             }
     
             pub fn remove(&mut self, idx: usize) -> usize {
                 let (prev_idx, next_idx) = (self.marbles[idx].prev, self.marbles[idx].next);
     
                 self.marbles[prev_idx as usize].next = next_idx;
                 self.marbles[next_idx as usize].prev = prev_idx;
                 self.marbles[idx].prev = INVALID;
                 self.marbles[idx].next = INVALID;
     
                 next_idx as usize
             }
     
             pub fn insert_after(&mut self, idx: usize, marble: usize) {
                 let old_next = self.marbles[idx].next;
                 self.marbles[idx].next = marble as u32;
                 self.marbles[marble].prev = idx as u32;
                 self.marbles[marble].next = old_next as u32;
                 self.marbles[old_next as usize].prev = marble as u32;
             }
     
             pub fn clockwise(&self, marble: usize, count: usize) -> usize {
                 let mut result = marble;
                 for _ in 0..count {
                     result = self.marbles[result].next as usize;
                 }
                 result
             }
     
             pub fn counter_clockwise(&self, marble: usize, count: usize) -> usize {
                 let mut result = marble;
                 for _ in 0..count {
                     result = self.marbles[result].prev as usize;
                 }
                 result
             }
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day9.txt").unwrap();
             }
     
             lazy_static! {
                 static ref game_regex: Regex = Regex::new(
                     r"(?x)
                     (?P<num_players>\d+)    # Digits
                     \D+                     # Not Digits
                     (?P<last_marble>\d+)    # Digits
                 "
                 )
                 .unwrap();
             }
     
             let mut num_players: usize = 0;
             let mut last_marble: usize = 0;
     
             let caps = game_regex.captures(&file_string);
             if caps.is_some() {
                 let caps = caps.unwrap();
                 num_players = caps["num_players"].parse().unwrap();
                 last_marble = caps["last_marble"].parse().unwrap();
             }
     
             // Closure to play a single game
             let play_game = |num_players, last_marble| -> usize {
                 let mut player_scores: Vec<usize> = vec![0; num_players as usize];
     
                 let mut circle = MarbleCircle::new(last_marble);
     
                 let mut cur_player: usize = 0;
                 let mut cur_marble: usize = 0;
                 let mut next_marble: usize = 1;
     
                 while next_marble <= last_marble {
                     if next_marble % 23 != 0 {
                         let insert = circle.clockwise(cur_marble, 1);
                         circle.insert_after(insert, next_marble);
                         cur_marble = next_marble;
                     } else {
                         player_scores[cur_player] += next_marble;
     
                         let to_remove = circle.counter_clockwise(cur_marble, 7);
                         cur_marble = circle.remove(to_remove);
                         player_scores[cur_player] += to_remove;
                     }
     
                     next_marble += 1;
                     cur_player = (cur_player + 1) % player_scores.len();
                 }
     
                 let high_score: usize = *player_scores.iter().max().unwrap();
                 high_score
             };
     
             let answer = play_game(num_players, last_marble);
             writeln!(&mut result, "Day 9, Problem 1 - [{}]", answer).unwrap(); // 398371
     
             let answer = play_game(num_players, last_marble * 100);
             writeln!(&mut result, "Day 9, Problem 2 - [{}]", answer).unwrap(); // 3212830280
             result
         }
     }
     
     // Input: List of positions; position=< 52672,  52690> velocity=<-5, -5>
     //
     // Problem 1: What message is printed when the stars align?
     // Solution 1: Print sky when bounds reaches it's smallest point
     //
     // Problem 2: How long did it take?
     // Solution 2: Count iterations to reach smallest bounds
     pub mod day10 {
         use hashbrown::HashSet;
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::cmp::{max, min};
         use std::fmt::Write;
     
         #[derive(Copy, Clone)]
         struct Bounds {
             min_x: i32,
             min_y: i32,
             max_x: i32,
             max_y: i32,
         }
     
         impl Bounds {
             pub fn new() -> Bounds {
                 Bounds {
                     min_x: std::i32::MAX,
                     min_y: std::i32::MAX,
                     max_x: std::i32::MIN,
                     max_y: std::i32::MIN,
                 }
             }
     
             pub fn min_x(&self) -> i32 {
                 self.min_x
             }
     
             pub fn min_y(&self) -> i32 {
                 self.min_y
             }
     
             pub fn add_point(&mut self, pt_x: i32, pt_y: i32) {
                 self.min_x = min(self.min_x, pt_x);
                 self.max_x = max(self.max_x, pt_x);
                 self.min_y = min(self.min_y, pt_y);
                 self.max_y = max(self.max_y, pt_y);
             }
     
             pub fn width(&self) -> i32 {
                 if self.max_x > self.min_x {
                     self.max_x - self.min_x
                 } else {
                     0
                 }
             }
     
             pub fn height(&self) -> i32 {
                 if self.max_y > self.min_y {
                     self.max_y - self.min_y
                 } else {
                     0
                 }
             }
     
             pub fn area(&self) -> i64 {
                 i64::from(self.width()) * i64::from(self.height())
             }
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(1024);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day10.txt").unwrap();
             }
     
             lazy_static! {
                 static ref re: Regex = Regex::new(
                     r"(?x)
                     [^\-\d]+                # Not SignDigit
                     (?P<pos_x>\-*\d+)       # (SignDigit)
                     [^\-\d]+                # Not SignDigit
                     (?P<pos_y>\-*\d+)       # (SignDigit)
                     [^\-\d]+                # Not SignDigit
                     (?P<vel_x>\-*\d+)       # (SignDigit)  
                     [^\-\d]+                # Not SignDigit
                     (?P<vel_y>\-*\d+)       # (SignDigit)
                 "
                 )
                 .unwrap();
             }
     
             let mut moving_points: Vec<((i32, i32), (i32, i32))> = Vec::new();
             let mut curr_bounds = Bounds::new();
     
             for line in file_string.lines() {
                 let caps = re.captures(line);
                 if caps.is_some() {
                     let caps = caps.unwrap();
                     let px: i32 = caps["pos_x"].parse().unwrap();
                     let py: i32 = caps["pos_y"].parse().unwrap();
                     let vx: i32 = caps["vel_x"].parse().unwrap();
                     let vy: i32 = caps["vel_y"].parse().unwrap();
     
                     curr_bounds.add_point(px, py);
     
                     moving_points.push(((px, py), (vx, vy)));
                 }
             }
     
             // Problem 1 + Problem 2
             let mut iteration = 0;
             loop {
                 let mut new_points = moving_points.clone();
                 let mut new_bounds = Bounds::new();
     
                 // Update
                 for moving_point in &mut new_points {
                     (moving_point.0).0 += (moving_point.1).0;
                     (moving_point.0).1 += (moving_point.1).1;
     
                     let (x, y) = moving_point.0;
                     new_bounds.add_point(x, y);
                 }
     
                 // curr_bounds is the "focal" length
                 if new_bounds.area() > curr_bounds.area() {
                     writeln!(&mut result, "Day 10, Problem 1:").unwrap(); // FNRGPBHR
     
                     let mut positions: HashSet<(i32, i32)> = HashSet::default();
                     for pos in moving_points.iter().map(|mp| mp.0) {
                         positions.insert(pos);
                     }
     
                     let width = curr_bounds.width();
                     let height = curr_bounds.height();
                     for row in 0..=height {
                         let y = curr_bounds.min_y() + row;
                         let mut row_string = String::with_capacity(width as usize + 4);
                         row_string.push_str("    ");
                         for col in 0..=width {
                             let x = curr_bounds.min_x() + col;
                             let c = if positions.contains(&(x, y)) {
                                 '#'
                             } else {
                                 '.'
                             };
                             row_string.push(c);
                         }
                         writeln!(&mut result, "{}", row_string).unwrap();
                     }
     
                     writeln!(&mut result, "Day 10, Problem 2 - [{}]", iteration).unwrap(); // 10511
                     break;
                 }
     
                 moving_points = new_points;
                 curr_bounds = new_bounds;
                 iteration += 1;
             }
     
             result
         } // fn run()
     } // mod day10
     
     // Input: Single integer; No parsing required
     //
     // Problem 1: Follow rules to generate 300x300 grid. Find 3x3 sub-grid with largest sum
     // Solution 1: Follow rules. Test all possible 3x3 grids.
     //
     // Problem 2: Find best NxN sub-grid where N ∃ [1..300]
     // Solution 2a: Brute force. Takes ~1.5 seconds on high-end PC.
     // Solution 2b: Implement summed-area table. Takes ~13 milliseconds.
     //              https://en.wikipedia.org/wiki/Summed-area_table
     pub mod day11 {
         use std::fmt::Write;
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             let serial_num = 8772; // puzzle input
             const WIDTH: usize = 300;
             const HEIGHT: usize = 300;
     
             // Generate 300x300 table
             let mut power_levels: Vec<i32> = vec![0; WIDTH * HEIGHT];
     
             for row in 1..=HEIGHT {
                 for col in 1..=WIDTH {
                     let rack_id = col as i32 + 10;
                     let mut power_level = rack_id * row as i32;
                     power_level += serial_num;
                     power_level *= rack_id;
                     power_level = (power_level / 100) % 10;
                     power_level -= 5;
     
                     power_levels[(row - 1) * WIDTH + (col - 1)] = power_level;
                 }
             }
     
             // Compute summed area table
             // https://en.wikipedia.org/wiki/Summed-area_table
             let mut summed_area: Vec<i32> = vec![0; WIDTH * HEIGHT];
             for row in 0..HEIGHT {
                 for col in 0..WIDTH {
                     let idx = row * WIDTH + col;
                     let a = power_levels[idx];
                     let b = if col > 0 { summed_area[idx - 1] } else { 0 };
                     let c = if row > 0 { summed_area[idx - WIDTH] } else { 0 };
                     let d = if row > 0 && col > 0 {
                         summed_area[idx - WIDTH - 1]
                     } else {
                         0
                     };
     
                     let summed_power = a + b + c - d;
                     summed_area[idx] = summed_power;
                 }
             }
     
             // Check all sub areas
             let mut best_cell_overall = (0, 0, 0);
             let mut best_power_overall = 0;
     
             let k_max = WIDTH;
             for k in 1..=k_max {
                 let mut best_power = std::i32::MIN;
                 let mut best_cell = (0, 0);
                 let offset = k - 1;
     
                 for row in 0..HEIGHT - offset {
                     for col in 0..WIDTH - offset {
                         // Rust + rustfmt makes this annoyingly verbose. C++ ternary would be so clean.
                         let a = if row > 0 && col > 0 {
                             summed_area[(row - 1) * WIDTH + (col - 1)]
                         } else {
                             0
                         };
                         let b = if row > 0 {
                             summed_area[(row - 1) * WIDTH + (col + offset)]
                         } else {
                             0
                         };
                         let c = if col > 0 {
                             summed_area[(row + offset) * WIDTH + (col - 1)]
                         } else {
                             0
                         };
                         let d = summed_area[(row + offset) * WIDTH + (col + offset)];
     
                         let sum = a - b - c + d;
     
                         if sum > best_power {
                             best_power = sum;
                             best_cell = (col, row);
                         }
                     }
                 }
     
                 if k == 3 {
                     // Answer: 235, 31
                     writeln!(
                         &mut result,
                         "Day 11, Problem 1 - [{}, {}]",
                         best_cell.0 + 1,
                         best_cell.1 + 1
                     )
                     .unwrap();
                 }
     
                 if best_power > best_power_overall {
                     best_power_overall = best_power;
                     best_cell_overall = (best_cell.0, best_cell.1, k);
                 }
             }
     
             // Answer: 241, 65, 10
             writeln!(
                 &mut result,
                 "Day 11, Problem 2 - [{}, {}, {}]",
                 best_cell_overall.0 + 1,
                 best_cell_overall.1 + 1,
                 best_cell_overall.2
             )
             .unwrap();
     
             result
         } // fn run()
     } // mod day11
     
     // Input: Initial state + rules; ..#.# => .
     //
     // Problem 1: Given an infinite row of pots and generational rules, what is the state after 20 gens?
     // Solution 1: Implement rules. Run 20 generations.
     //
     // Problem 2: What does it look like after 50,000,000,000 generations?
     // Solution 2: Detect pattern isn't changing, only moving. Compute state after 50 billion gens.
     //
     // Note: This probably should use a VecDeque and enum
     pub mod day12 {
         use hashbrown::HashSet;
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::cmp::{max, min};
         use std::fmt::Write;
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day12.txt").unwrap();
             }
     
             lazy_static! {
                 static ref first_line_re: Regex =
                     Regex::new(r"initial state: (?P<initial_state>.+)").unwrap();
             }
     
             lazy_static! {
                 static ref entry_re: Regex = Regex::new(
                     r"(?x)
                     (?P<pattern>[\.\#]+)
                     \s=>\s
                     (?P<output>[\.\#]+)
                 "
                 )
                 .unwrap();
             }
     
             // Helper functions
             let char_to_bool = |c: char| c == '#';
     
             let mut pots = String::new();
             let mut zero_index = 0;
     
             // Parse input
             let mut lines = file_string.lines();
     
             let first_line = lines.next().unwrap();
             let caps = first_line_re.captures(first_line);
             if caps.is_some() {
                 let caps = caps.unwrap();
                 let initial_state: String = caps["initial_state"].parse().unwrap();
                 pots = initial_state.chars().collect();
             }
     
             let mut pattern_results: HashSet<String> = HashSet::default();
     
             for line in lines {
                 let caps = entry_re.captures(line);
                 if caps.is_some() {
                     let caps = caps.unwrap();
                     let pattern: String = caps["pattern"].parse().unwrap();
                     let result: bool = char_to_bool(caps["output"].parse::<char>().unwrap());
     
                     if result {
                         pattern_results.insert(pattern);
                     }
                 }
             }
     
             // Run generations
             let mut pattern: String = String::with_capacity(5);
             let mut gen: i64 = 0;
     
             let mut extra = 0;
     
             loop {
                 let mut new_pots = String::with_capacity(pots.len());
                 let mut prepended = false;
                 let mut appended = false;
     
                 // Iterate over pots
                 for j in -4..(pots.len() + 4) as i32 {
                     let k = j + 2;
                     pattern.clear();
     
                     // Build pattern of 5 pots
                     for _ in j..min(j + 5, 0) {
                         pattern.push('.');
                     }
     
                     let start = max(j, 0);
                     let end = min(j + 5, pots.len() as i32);
                     let mut iter = pots.chars().skip(start as usize);
                     for _ in start..end {
                         pattern.push(iter.next().unwrap());
                     }
     
                     for _ in pots.len() as i32..j + 5 {
                         pattern.push('.');
                     }
     
                     // Check pattern
                     if pattern_results.contains(&pattern) {
                         new_pots.push('#');
                         if k < 0 {
                             prepended = true;
                             zero_index += 1;
                         } else if k >= pots.len() as i32 {
                             appended = true;
                         }
                     } else if k < 0 {
                         if prepended {
                             new_pots.push('.');
                             zero_index += 1;
                         }
                     } else if k >= pots.len() as i32 {
                         if appended {
                             new_pots.push('.');
                         }
                     } else {
                         new_pots.push('.');
                     }
                 }
     
                 // Increment gen count
                 gen += 1;
     
                 //println!("{}", pots);
                 let pots_sum = |s: &String| -> i32 {
                     s.chars()
                         .enumerate()
                         .map(|(i, c)| if c == '#' { i as i32 - zero_index } else { 0 })
                         .sum()
                 };
     
                 // Check for convergence
                 let converged = pots
                     .chars()
                     .skip_while(|c| *c == '.')
                     .zip(new_pots.chars().skip_while(|c| *c == '.'))
                     .all(|(a, b)| a == b);
     
                 if converged {
                     if extra == 0 {
                         let old_sum = i64::from(pots_sum(&pots));
                         let new_sum = i64::from(pots_sum(&new_pots));
                         let gens_to_go = 50_000_000_000 - gen;
     
                         let final_sum = new_sum + gens_to_go * (new_sum - old_sum);
                         writeln!(&mut result, "Day 12, Problem 2 - [{}]", final_sum).unwrap();
                         break;
                     }
     
                     extra += 1;
                 }
     
                 // Set old pots to new pots
                 pots = new_pots;
     
                 // Output day 12, problem 1
                 if gen == 20 {
                     writeln!(&mut result, "Day 12, Problem 1 - [{}]", pots_sum(&pots)).unwrap(); // 2045
                 }
             }
     
             result
         } // fn run()
     } // mod day 12
     
     // Input: Ascii map of: \ / | - + ^ > v <
     //
     // Problem 1: Given minecart map, where is the first crash?
     // Solution 1: Build map. Implement rules to follow. Simulate. Detect crash.
     //
     // Problem 2: Run simulation until only 1 cart remains.
     // Solution 1: Change end condition. Code is fast.
     pub mod day13 {
         use hashbrown::{HashMap, HashSet};
         use lazy_static::lazy_static;
         use std::cmp::Ordering;
         use std::fmt::Write;
     
         #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
         struct Pos {
             row: i16,
             col: i16,
         }
     
         #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
         enum Dir {
             North,
             South,
             East,
             West,
         }
     
         #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
         enum Turn {
             Left,
             Straight,
             Right,
         }
     
         #[derive(Debug)]
         struct Cart {
             id: u8,
             pos: Pos,
             dir: Dir,
             turn: Turn,
             crashed: bool,
         }
     
         #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
         enum TrackPiece {
             Vertical,
             Horizontal,
             Intersection,
             Slash,
             Backslash,
         }
     
         fn cart_sorter(a: &Cart, b: &Cart) -> Ordering {
             if a.pos.row != b.pos.row {
                 a.pos.row.cmp(&b.pos.row)
             } else {
                 a.pos.col.cmp(&b.pos.col)
             }
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day13.txt").unwrap();
             }
     
             let mut carts: Vec<Cart> = Vec::new();
             let mut tiles: HashMap<Pos, TrackPiece> = HashMap::default();
             let mut cart_locations: HashSet<Pos> = HashSet::default();
     
             let mut next_cart_id = 0;
             for (row, line) in file_string.lines().enumerate() {
                 for (col, c) in line.chars().enumerate() {
                     let pos = Pos {
                         row: row as i16,
                         col: col as i16,
                     };
     
                     if c == '|' {
                         tiles.insert(pos, TrackPiece::Vertical);
                     } else if c == '-' {
                         tiles.insert(pos, TrackPiece::Horizontal);
                     } else if c == '+' {
                         tiles.insert(pos, TrackPiece::Intersection);
                     } else if c == '/' {
                         tiles.insert(pos, TrackPiece::Slash);
                     } else if c == '\\' {
                         tiles.insert(pos, TrackPiece::Backslash);
                     } else if c == '<' || c == '>' {
                         carts.push(Cart {
                             id: next_cart_id,
                             pos,
                             dir: if c == '<' { Dir::West } else { Dir::East },
                             turn: Turn::Left,
                             crashed: false,
                         });
                         next_cart_id += 1;
                         cart_locations.insert(pos);
                         tiles.insert(pos, TrackPiece::Horizontal);
                     } else if c == '^' || c == 'v' {
                         carts.push(Cart {
                             id: next_cart_id,
                             pos,
                             dir: if c == '^' { Dir::North } else { Dir::South },
                             turn: Turn::Left,
                             crashed: false,
                         });
                         next_cart_id += 1;
                         cart_locations.insert(pos);
                         tiles.insert(pos, TrackPiece::Vertical);
                     } else if c == ' ' {
                         // skip
                     } else {
                         panic!("Unexpected character");
                     }
                 }
             }
     
             // Helper functions
             let inc_turn = |turn: Turn| match turn {
                 Turn::Left => Turn::Straight,
                 Turn::Straight => Turn::Right,
                 Turn::Right => Turn::Left,
             };
     
             let inc_pos = |pos: Pos, dir: Dir| -> Pos {
                 match dir {
                     Dir::North => Pos {
                         row: pos.row - 1,
                         col: pos.col,
                     },
                     Dir::South => Pos {
                         row: pos.row + 1,
                         col: pos.col,
                     },
                     Dir::East => Pos {
                         row: pos.row,
                         col: pos.col + 1,
                     },
                     Dir::West => Pos {
                         row: pos.row,
                         col: pos.col - 1,
                     },
                 }
             };
     
             // (Dir, TrackPiece) -> Dir
             let mut new_dir: HashMap<(Dir, TrackPiece), Dir> = HashMap::default();
             new_dir.insert((Dir::North, TrackPiece::Vertical), Dir::North);
             new_dir.insert((Dir::North, TrackPiece::Slash), Dir::East);
             new_dir.insert((Dir::North, TrackPiece::Backslash), Dir::West);
     
             new_dir.insert((Dir::South, TrackPiece::Vertical), Dir::South);
             new_dir.insert((Dir::South, TrackPiece::Slash), Dir::West);
             new_dir.insert((Dir::South, TrackPiece::Backslash), Dir::East);
     
             new_dir.insert((Dir::East, TrackPiece::Horizontal), Dir::East);
             new_dir.insert((Dir::East, TrackPiece::Slash), Dir::North);
             new_dir.insert((Dir::East, TrackPiece::Backslash), Dir::South);
     
             new_dir.insert((Dir::West, TrackPiece::Horizontal), Dir::West);
             new_dir.insert((Dir::West, TrackPiece::Slash), Dir::South);
             new_dir.insert((Dir::West, TrackPiece::Backslash), Dir::North);
     
             // (Dir, Turn) -> Dir
             let mut turn_dir: HashMap<(Dir, Turn), Dir> = HashMap::default();
             turn_dir.insert((Dir::North, Turn::Left), Dir::West);
             turn_dir.insert((Dir::North, Turn::Straight), Dir::North);
             turn_dir.insert((Dir::North, Turn::Right), Dir::East);
     
             turn_dir.insert((Dir::South, Turn::Left), Dir::East);
             turn_dir.insert((Dir::South, Turn::Straight), Dir::South);
             turn_dir.insert((Dir::South, Turn::Right), Dir::West);
     
             turn_dir.insert((Dir::East, Turn::Left), Dir::North);
             turn_dir.insert((Dir::East, Turn::Straight), Dir::East);
             turn_dir.insert((Dir::East, Turn::Right), Dir::South);
     
             turn_dir.insert((Dir::West, Turn::Left), Dir::South);
             turn_dir.insert((Dir::West, Turn::Straight), Dir::West);
             turn_dir.insert((Dir::West, Turn::Right), Dir::North);
     
             let mut num_crashed = 0;
     
             // Run simulation
             loop {
                 // Sort because carts update in specific order
                 carts.sort_by(cart_sorter);
     
                 for i in 0..carts.len() {
                     let cart: &mut Cart = &mut carts[i];
     
                     // Ignore crashed carts
                     if cart.crashed {
                         continue;
                     }
     
                     cart_locations.remove(&cart.pos);
     
                     // Move cart position
                     let new_pos = inc_pos(cart.pos, cart.dir);
                     assert!(tiles.contains_key(&new_pos));
                     cart.pos = new_pos;
     
                     // Check for crash
                     let inserted = cart_locations.insert(new_pos);
                     if !inserted {
                         if num_crashed == 0 {
                             writeln!(
                                 &mut result,
                                 "Day 13, Problem 1 - [({}, {})]",
                                 new_pos.col, new_pos.row
                             )
                             .unwrap(); // 40,90
                         }
                         num_crashed += 2;
     
                         for cart in carts.iter_mut().filter(|c| c.pos == new_pos) {
                             cart.crashed = true;
                         }
     
                         cart_locations.remove(&new_pos);
                         continue;
                     }
     
                     // Update dir
                     let new_tile = tiles[&new_pos];
                     if new_tile == TrackPiece::Intersection {
                         cart.dir = turn_dir[&(cart.dir, cart.turn)];
                         cart.turn = inc_turn(cart.turn);
                     } else {
                         cart.dir = new_dir[&(cart.dir, new_tile)];
                     }
                 }
     
                 if carts.len() - num_crashed == 1 {
                     let survivor = carts.iter().find(|c| !c.crashed).unwrap();
                     writeln!(
                         &mut result,
                         "Day 13, Problem 2 - [({}, {})]",
                         survivor.pos.col, survivor.pos.row
                     )
                     .unwrap(); // 65,81
     
                     break;
                 }
             }
     
             result
         } // fn run()
     } // mod day 13
     
     // Input: A couple of integers
     //
     // Problem 1: Given recipe building rules what is the score after 10 new recipes?
     // Solution 1: Implement arbitrary rules. Run until there are 10 new recipes.
     //
     // Problem 2: Run until 6-digit sequence is detected.
     // Solution 2: Optimize code. Run until sequence is detected.
     //
     // Notes: Contains safe and unsafe solutions.
     //        Safe solution is simple and runs in ~270 milliseconds
     //        Unsafe is unrolled, caches sequences information, and runs in ~170 milliseconds
     pub mod day14 {
         use std::fmt::Write;
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
             let mut recipes: Vec<u8> = vec![3, 7];
     
             let start = 327_901; // puzzle input
             let count = 10;
             recipes.reserve(start + count);
     
             let digits = [3, 2, 7, 9, 0, 1]; // puzzle input
             let mut matched_digits = 0;
             let mut next_digit = digits[0];
     
             // Unused due to unsafe block. But keeping so safe path can be enabled.
             // let mut elves: [usize; 2] = [0, 1];
     
             let mut index_sequence: [Vec<u32>; 9] = [
                 vec![0],
                 vec![1],
                 vec![],
                 vec![],
                 vec![],
                 vec![],
                 vec![],
                 vec![],
                 vec![],
             ];
     
             let mut recipe_sequence: [Vec<u8>; 9] = [
                 vec![3],
                 vec![7],
                 vec![],
                 vec![],
                 vec![],
                 vec![],
                 vec![],
                 vec![],
                 vec![],
             ];
     
             let n = 30_000_000; // conservative estimate of how long recipe may be
             recipes.reserve(n);
             for i in 0..9 {
                 index_sequence[i].reserve(n / 9);
                 recipe_sequence[i].reserve(n / 9);
             }
     
             let mut elf_source_sequence: [usize; 2] = [0, 1];
             let mut elf_current: [usize; 2] = [0, 0];
     
             macro_rules! push_digit {
                 ($d:expr) => {
                     recipes.push($d);
                     if $d == next_digit {
                         matched_digits += 1;
                         if matched_digits == digits.len() {
                             writeln!(
                                 &mut result,
                                 "Day 14, Problem 2 - [{}]", // 20229822
                                 recipes.len() - digits.len()
                             )
                             .unwrap();
                             return result;
                         } else {
                             next_digit = digits[matched_digits];
                         }
                     } else {
                         matched_digits = if $d == digits[0] { 1 } else { 0 };
                         next_digit = digits[matched_digits];
                     }
                 };
             }
     
             loop {
                 unsafe {
                     let s0 = elf_source_sequence[0];
                     let s1 = elf_source_sequence[1];
     
                     let c0 = elf_current[0];
                     let c1 = elf_current[1];
     
                     let r0 = *recipe_sequence.get_unchecked(s0).get_unchecked(c0);
                     let r1 = *recipe_sequence.get_unchecked(s1).get_unchecked(c1);
     
                     let sum = r0 + r1;
                     if sum >= 10 {
                         push_digit!(1);
                         push_digit!(sum % 10);
                     } else {
                         push_digit!(sum);
                     }
     
                     elf_current[0] += 1;
                     if elf_current[0] >= index_sequence.get_unchecked(s0).len() {
                         let e0 = *index_sequence.get_unchecked(s0).get_unchecked(c0);
                         let next_index = (e0 as usize + 1 + r0 as usize) % recipes.len();
                         if next_index <= 8 {
                             elf_current[0] = 0;
                             elf_source_sequence[0] = next_index as usize;
                             let idx_seq = index_sequence.get_unchecked_mut(next_index as usize);
                             if idx_seq.is_empty() {
                                 idx_seq.push(next_index as u32);
                                 recipe_sequence
                                     .get_unchecked_mut(next_index)
                                     .push(*recipes.get_unchecked(next_index));
                             }
                         } else {
                             index_sequence.get_unchecked_mut(s0).push(next_index as u32);
                             recipe_sequence
                                 .get_unchecked_mut(s0)
                                 .push(*recipes.get_unchecked(next_index));
                         }
                     }
     
                     elf_current[1] += 1;
                     if elf_current[1] >= index_sequence.get_unchecked(s1).len() {
                         let e1 = *index_sequence.get_unchecked(s1).get_unchecked(c1);
                         let next_index = (e1 as usize + 1 + r1 as usize) % recipes.len();
                         if next_index <= 8 {
                             elf_current[1] = 0;
                             elf_source_sequence[1] = next_index as usize;
                             let idx_seq = index_sequence.get_unchecked_mut(next_index as usize);
                             if idx_seq.is_empty() {
                                 idx_seq.push(next_index as u32);
                                 recipe_sequence
                                     .get_unchecked_mut(next_index)
                                     .push(*recipes.get_unchecked(next_index));
                             }
                         } else {
                             index_sequence.get_unchecked_mut(s1).push(next_index as u32);
                             recipe_sequence
                                 .get_unchecked_mut(s1)
                                 .push(*recipes.get_unchecked(next_index));
                         }
                     }
                 } // unsafe
     
                 // The unsafe block is an unrolled, unchecked version of the follwoing:
                 /*
                     let e0 = elves[0];
                     let e1 = elves[1];
     
                     // This unsafe plus the one below saves ~7 milliseconds
                     let r0 = recipes[e0];
                     let r1 = recipes[e1];
                     let sum = r0 + r1;
     
                     if sum >= 10 {
                         push_digit!(1);
                         push_digit!(sum % 10);
                     } else {
                         push_digit!(sum);
                     }
     
                     elves[0] = (e0 + 1 + r0 as usize) % recipes.len();
                     elves[1] = (e1 + 1 + r1 as usize) % recipes.len();
     
                     if recipes.len() == start + count {
                         let mut answer: String = String::with_capacity(count);
                         for i in start..start + count {
                             answer.push_str(&recipes[i].to_string());
                         }
     
                         writeln!(&mut result, "Day 14, Problem 1 - [{}]", answer).unwrap(); // 1115317115
                     }
                 }
                 */
     
                 if recipes.len() == start + count {
                     let mut answer: String = String::with_capacity(count);
                     for i in start..start + count {
                         answer.push_str(&recipes[i].to_string());
                     }
     
                     writeln!(&mut result, "Day 14, Problem 1 - [{}]", answer).unwrap(); // 1115317115
                 }
             } // loop
         } // fn run()
     } // mod day 14
     
     // Input ascii map
     //
     // Problem 1: Given map of cave with goblins and elves, who wins the fight?
     // Solution 1: Implement very nuanced rules. Simulate until end.
     //
     // Problem 2: How much bonus attack do elves need to win with zero deaths?
     // Solution 2: Increment bonus and re-simulate until condition met.
     //
     // Notes: This probably was one of the longest to solve. The rules aren't complicated
     //        but they are subtle with lots of rules. Very easy to get wrong.
     //
     //        Also requires pathfinding. My implementation is a simple
     //        breadth-first search.
     pub mod day15 {
         use derive_more::*;
         use hashbrown::{HashMap, HashSet};
         use lazy_static::lazy_static;
         use std::cmp::Ordering;
         use std::collections::VecDeque;
         use std::fmt::Write;
     
         // Simple struct to store row/col
         #[derive(Copy, Clone, Eq, PartialEq, Hash, Add, Debug)]
         struct Pos {
             row: i8,
             col: i8,
         }
     
         impl Pos {
             pub fn new(row: i8, col: i8) -> Pos {
                 Pos { row, col }
             }
     
             pub fn get_index(self, width: usize) -> usize {
                 self.row as usize * width + self.col as usize
             }
     
             pub fn adjacency_iter(self, width: usize, height: usize) -> AdjacencyIterator {
                 AdjacencyIterator {
                     pos: self,
                     count: 0,
                     width: width as i8,
                     height: height as i8,
                 }
             }
         }
     
         impl PartialOrd for Pos {
             fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
                 if self.row < other.row {
                     Some(Ordering::Less)
                 } else if self.row > other.row {
                     Some(Ordering::Greater)
                 } else if self.col < other.col {
                     Some(Ordering::Less)
                 } else if self.col > other.col {
                     Some(Ordering::Greater)
                 } else {
                     Some(Ordering::Equal)
                 }
             }
         }
     
         // Per-unit information
         #[derive(Copy, Clone, Eq, PartialEq, Hash)]
         struct Unit {
             id: u8,
             is_elf: bool,
             health: u8,
             turn: u16,
             pos: Pos,
         }
     
         impl Unit {
             pub fn get_key(self, width: usize, height: usize) -> usize {
                 let r = self.pos.row as usize;
                 let c = self.pos.col as usize;
                 let t = self.turn as usize;
     
                 (width * height * t) + r * width + c
             }
         }
     
         // Utility iterator to iterate through adjacent tiles on the board
         // Handle border checks
         struct AdjacencyIterator {
             pos: Pos,
             width: i8,
             height: i8,
             count: usize,
         }
     
         impl Iterator for AdjacencyIterator {
             type Item = Pos;
     
             fn next(&mut self) -> Option<Pos> {
                 // NWES; reading order
                 let offsets = [
                     Pos::new(-1, 0),
                     Pos::new(0, -1),
                     Pos::new(0, 1),
                     Pos::new(1, 0),
                 ];
     
                 let mut result: Option<Pos> = None;
                 while result.is_none() && self.count < offsets.len() {
                     let p = self.pos + offsets[self.count];
                     if p.row >= 0 && p.row < self.height && p.col >= 0 && p.col < self.width {
                         result = Some(p);
                     }
                     self.count += 1;
                 }
     
                 result
             }
         }
     
         // Helper to performing breadth-first pathfind
         #[derive(Copy, Clone, Eq, PartialEq)]
         struct PathfindNode {
             pos: Pos,
             parent: Pos,
             cost: u16,
         }
     
         impl PathfindNode {
             fn new(pos: Pos, parent: Pos, cost: u16) -> Self {
                 PathfindNode { pos, parent, cost }
             }
         }
     
         impl PartialOrd for PathfindNode {
             fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
                 if other.cost != self.cost {
                     other.cost.partial_cmp(&self.cost)
                 } else {
                     other.pos.partial_cmp(&self.pos)
                 }
             }
         }
     
         impl Ord for PathfindNode {
             fn cmp(&self, other: &Self) -> Ordering {
                 self.partial_cmp(other).unwrap()
             }
         }
     
         #[allow(dead_code)]
             fn print_board(
             walls: &[bool],
             units: &HashMap<Pos, Unit>,
             width: usize,
             height: usize,
         ) {
             for row in 0..height {
                 let mut row_str = String::with_capacity(width * 2);
                 for col in 0..width {
                     let pos = Pos::new(row as i8, col as i8);
                     let unit = units.get(&pos);
                     let c: char = match unit {
                         Some(unit) => match unit.is_elf {
                             true => 'E',
                             false => 'G',
                         },
                         None => match walls[pos.get_index(width)] {
                             true => '#',
                             false => '.',
                         },
                     };
                     row_str.push(c);
                 }
     
                 row_str.push_str(&"   ".to_string());
                 let mut row_units: Vec<&Unit> = units
                     .iter()
                     .map(|(_, unit)| unit)
                     .filter(|unit| (**unit).pos.row == row as i8)
                     .collect();
                 row_units.sort_by_key(|u| u.pos.col);
                 for unit in row_units {
                     let s = format!("{}({}), ", if unit.is_elf { 'E' } else { 'G' }, unit.health);
                     row_str.push_str(&s);
                 }
     
                 row_str.push('\n');
                 print!("{}", row_str);
             }
     
             println!();
         }
     
         fn attack_nearest(
             source_pos: Pos,
             source_is_elf: bool,
             units: &mut HashMap<Pos, Unit>,
             unit_occupancy: &mut Vec<bool>,
             width: usize,
             height: usize,
             elf_damage: u8,
             goblin_damage: u8,
             dead_elves: &mut u8,
         ) -> bool {
             // ------------------------------------------------------
             // Check for adjacenct enemy with lowest health
             // ------------------------------------------------------
             let mut best_enemy: Option<(Pos, u8)> = None;
     
             for adjacent_pos in source_pos.adjacency_iter(width, height) {
                 // Get unit in adjacent position
                 let target = units.get_mut(&adjacent_pos);
                 if target.is_none() {
                     continue;
                 }
                 let target = target.unwrap();
     
                 // Check teams
                 if target.is_elf == source_is_elf {
                     continue;
                 }
     
                 // Check health
                 if best_enemy.is_none() {
                     best_enemy = Some((target.pos, target.health));
                 } else {
                     let (enemy_pos, enemy_health) = best_enemy.unwrap();
                     if target.health < enemy_health
                         || (target.health == enemy_health
                             && target.pos.get_index(width) < enemy_pos.get_index(width))
                     {
                         best_enemy = Some((target.pos, target.health));
                     }
                 }
             }
     
             if best_enemy.is_some() {
                 let (enemy_pos, _) = best_enemy.unwrap();
                 let enemy = units.get_mut(&enemy_pos).unwrap();
                 let attack_damage = match source_is_elf {
                     true => elf_damage,
                     false => goblin_damage,
                 };
     
                 if enemy.health > attack_damage {
                     enemy.health -= attack_damage;
                 } else {
                     if enemy.is_elf {
                         *dead_elves += 1;
                     }
                     units.remove(&enemy_pos);
                     unit_occupancy[enemy_pos.get_index(width)] = false;
                 }
     
                 true
             } else {
                 false
             }
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day15.txt").unwrap();
             }
     
             let width = file_string.lines().next().unwrap().len();
             let height = file_string.lines().count();
     
             let mut walls: Vec<bool> = Vec::with_capacity(width * height);
             let mut initial_units: HashMap<Pos, Unit> = HashMap::default();
     
             let default_health: u8 = 200;
             let goblin_damage: u8 = 3;
             let mut elf_damage: u8 = 3;
     
             let mut next_id = 0;
     
             for (row, line) in file_string.lines().enumerate() {
                 for (col, c) in line.chars().enumerate() {
                     match c {
                         '.' => walls.push(false),
                         '#' => walls.push(true),
                         'E' | 'G' => {
                             walls.push(false);
                             let pos = Pos {
                                 row: row as i8,
                                 col: col as i8,
                             };
                             let new_unit = Unit {
                                 id: next_id,
                                 is_elf: c == 'E',
                                 health: default_health,
                                 turn: 0,
                                 pos,
                             };
                             initial_units.insert(pos, new_unit);
                             next_id += 1;
                         }
                         _ => panic!("Unexpected character [{}]", c),
                     };
                 }
             }
     
             let mut initial_unit_occupancy: Vec<bool> = vec![false; width * height];
             initial_units
                 .keys()
                 .for_each(|p| initial_unit_occupancy[p.get_index(width)] = true);
     
             // ---------------------------
             // Loop until elves win without loss
             // ---------------------------
             let mut game_count = 0;
             loop {
                 let mut units = initial_units.clone();
                 let mut unit_occupancy = initial_unit_occupancy.clone();
                 //print_board(&walls, &units, width, height);
     
                 let mut current_turn = 0;
                 let mut dead_elves = 0;
     
                 // ---------------------------
                 // Loop until one team wins
                 // ---------------------------
                 loop {
                     // Early out when looking for winning elf game
                     if game_count > 0 && dead_elves > 0 {
                         break;
                     }
     
                     // ---------------------------
                     // Pick unit to take turn
                     // ---------------------------
                     let first_unit_pos = units
                         .values()
                         .min_by_key(|u| u.get_key(width, height))
                         .unwrap()
                         .pos;
     
                     // Update turn counter
                     if units[&first_unit_pos].turn > current_turn {
                         assert!(units[&first_unit_pos].turn == current_turn + 1);
                         current_turn += 1;
     
                         //println!("After {} rounds: ", current_turn);
                         //print_board(&walls, &units, width, height);
                     }
     
                     // ---------------------------
                     // Check for winner
                     // ---------------------------
                     let mut elves: u8 = 0;
                     let mut goblins: u8 = 0;
                     for unit in units.values() {
                         match unit.is_elf {
                             true => elves += 1,
                             false => goblins += 1,
                         };
                     }
     
                     if elves == 0 || goblins == 0 {
                         break;
                     }
     
                     let current_unit = units.get_mut(&first_unit_pos).unwrap();
                     current_unit.turn += 1;
                     let current_is_elf = current_unit.is_elf;
                     let mut current_pos = current_unit.pos;
     
                     // ------------------------------------------------------
                     // Attack adjacent enemy, if possible
                     // ------------------------------------------------------
                     let performed_attack = attack_nearest(
                         current_pos,
                         current_is_elf,
                         &mut units,
                         &mut unit_occupancy,
                         width,
                         height,
                         elf_damage,
                         goblin_damage,
                         &mut dead_elves,
                     );
     
                     if performed_attack {
                         continue;
                     }
     
                     // ---------------------------
                     // Move towards an enemy
                     // ---------------------------
     
                     // slots are valid positions adjacent to an enemy
                     let slots: HashSet<Pos> = units
                         .iter()
                         .filter(|(_, unit)| unit.is_elf != current_is_elf)
                         .flat_map(|(_, unit)| unit.pos.adjacency_iter(width, height))
                         .filter(|pos| {
                             !walls[pos.get_index(width)] && !unit_occupancy[pos.get_index(width)]
                         })
                         .collect();
     
                     let mut parents: Vec<Pos> = vec![Pos::new(-1, -1); width * height];
                     let mut best_distance = std::u16::MAX;
                     let mut closest_nodes: Vec<PathfindNode> = Vec::new();
                     let mut visited: Vec<bool> = vec![false; width * height];
     
                     // node structure to perform breadth-first search
                     let mut nodes: VecDeque<PathfindNode> = current_pos
                         .adjacency_iter(width, height)
                         .filter(|pos| {
                             !walls[pos.get_index(width)] && !unit_occupancy[pos.get_index(width)]
                         })
                         .map(|pos| {
                             let idx = pos.get_index(width);
                             parents[idx] = current_pos;
                             visited[idx] = true;
                             PathfindNode::new(pos, current_pos, 1)
                         })
                         .collect();
     
                     // perform BFS
                     while !nodes.is_empty() {
                         // Get cheapest node from heap
                         let node = nodes.pop_front().unwrap();
     
                         // Check if this node costs more than our best_distance
                         if node.cost > best_distance {
                             break;
                         }
     
                         if slots.contains(&node.pos) {
                             // node is a destination slot, add it to closest nodes
                             best_distance = node.cost;
                             closest_nodes.push(node);
                         } else {
                             // push valid adjacent nodes
                             for pos in node.pos.adjacency_iter(width, height) {
                                 let idx = pos.get_index(width);
     
                                 // Skip position if it contains a unit, contains a wall, or has been visited
                                 if walls[idx] || visited[idx] || units.contains_key(&pos) {
                                     continue;
                                 }
     
                                 visited[idx] = true;
                                 parents[idx] = node.pos;
     
                                 nodes.push_back(PathfindNode::new(pos, node.pos, node.cost + 1));
                             }
                         }
                     }
     
                     // pick node to move towards; sort by index for tiebreaker
                     let mut path: Vec<Pos> = Vec::new();
     
                     if !closest_nodes.is_empty() {
                         let mut new_pos = closest_nodes
                             .iter()
                             .min_by_key(|node| node.pos.get_index(width))
                             .unwrap()
                             .pos;
     
                         loop {
                             path.push(new_pos);
                             let parent_pos = parents[new_pos.get_index(width)];
                             if parent_pos == current_pos {
                                 break;
                             } else {
                                 assert!(new_pos != parent_pos);
                                 new_pos = parent_pos;
                             }
                         }
     
                         // Move unit from old pos to new pos
                         let mut unit = units.remove(&current_pos).unwrap();
                         unit_occupancy[current_pos.get_index(width)] = false;
                         unit.pos = new_pos;
                         units.insert(new_pos, unit);
                         current_pos = new_pos;
                         unit_occupancy[current_pos.get_index(width)] = true;
                     }
     
                     // ------------------------------------------------------
                     // Attack adjacent enemy, if possible
                     // ------------------------------------------------------
                     attack_nearest(
                         current_pos,
                         current_is_elf,
                         &mut units,
                         &mut unit_occupancy,
                         width,
                         height,
                         elf_damage,
                         goblin_damage,
                         &mut dead_elves,
                     );
                 }
     
                 //print_board(&walls, &units, width, height);
     
                 // ---------------------------
                 // Compute final score
                 // ---------------------------
                 let health_sum: i32 = units
                     .iter()
                     .map(|(_, unit)| i32::from(unit.health))
                     .sum::<i32>();
                 let final_score = health_sum * i32::from(current_turn);
     
                 if game_count == 0 {
                     writeln!(&mut result, "Day 15, Problem 1 - [{}]", final_score).unwrap(); // 226688
                 }
     
                 if dead_elves == 0 {
                     writeln!(&mut result, "Day 15, Problem 2 - [{}]", final_score).unwrap(); // 62958
                     break;
                 }
     
                 elf_damage += 1;
                 game_count += 1;
             } // game loop
     
             result
         } // fn run
     } // day15
     
     // Input: Commands with before/after state
     //
     // Problem 1: How many commands behave like three or more opcodes?
     // Solution 1: Implement all opcodes. Run all commands. Check behavior.
     //
     // Problem 2: What are registers after program execution?
     // Solution 2: Determine int -> opcode mapping. Run program.
     pub mod day16 {
         use hashbrown::HashSet;
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::fmt::Write;
     
         type Registers = [usize; 4];
     
         #[allow(non_camel_case_types)]
         #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
         enum OpCode {
             addr, // rC = rA + rB
             addi, // rC = rA + b
     
             mulr, // rC = rA * rB
             muli, // rC = rA * b
     
             banr, // rC = rA & rB
             bani, // rC = rA & b
     
             borr, // rC = rA | rB
             bori, // rC = rA | b
     
             setr, // rC = rA
             seti, // rC = a
     
             gtir, // rC = a > rB ? 1 : 0
             gtri, // rC = rA > b ? 1 : 0
             gtrr, // rC = rA > rB ? 1 : 0
     
             eqir, // rC = a == rB ? 1 : 0
             eqri, // rC = rA == b ? 1 : 0
             eqrr, // rC = rA == rB ? 1 : 0
         }
     
         fn op_from_usize(u: usize) -> OpCode {
             match u {
                 0 => OpCode::addr,
                 1 => OpCode::addi,
                 2 => OpCode::mulr,
                 3 => OpCode::muli,
                 4 => OpCode::banr,
                 5 => OpCode::bani,
                 6 => OpCode::borr,
                 7 => OpCode::bori,
                 8 => OpCode::setr,
                 9 => OpCode::seti,
                 10 => OpCode::gtir,
                 11 => OpCode::gtri,
                 12 => OpCode::gtrr,
                 13 => OpCode::eqir,
                 14 => OpCode::eqri,
                 15 => OpCode::eqrr,
                 _ => panic!("op_from_usize failed"),
             }
         }
     
         #[derive(Copy, Clone)]
         struct Instruction {
             op_num: usize,
             a: usize,
             b: usize,
             c: usize,
         }
     
         impl Instruction {
             fn new(op_num: usize, a: usize, b: usize, c: usize) -> Instruction {
                 Instruction { op_num, a, b, c }
             }
         }
     
         fn execute(op: OpCode, a: usize, b: usize, c: usize, r: &mut Registers) {
             match op {
                 OpCode::addr => r[c] = r[a] + r[b],
                 OpCode::addi => r[c] = r[a] + b,
                 OpCode::mulr => r[c] = r[a] * r[b],
                 OpCode::muli => r[c] = r[a] * b,
                 OpCode::banr => r[c] = r[a] & r[b],
                 OpCode::bani => r[c] = r[a] & b,
                 OpCode::borr => r[c] = r[a] | r[b],
                 OpCode::bori => r[c] = r[a] | b,
                 OpCode::setr => r[c] = r[a],
                 OpCode::seti => r[c] = a,
                 OpCode::gtir => r[c] = if a > r[b] { 1 } else { 0 },
                 OpCode::gtri => r[c] = if r[a] > b { 1 } else { 0 },
                 OpCode::gtrr => r[c] = if r[a] > r[b] { 1 } else { 0 },
                 OpCode::eqir => r[c] = if a == r[b] { 1 } else { 0 },
                 OpCode::eqri => r[c] = if r[a] == b { 1 } else { 0 },
                 OpCode::eqrr => r[c] = if r[a] == r[b] { 1 } else { 0 },
             };
         }
     
         #[derive(Copy, Clone)]
         struct Entry {
             before: Registers,
             inst: Instruction,
             after: Registers,
         }
     
         impl Entry {
             pub fn new() -> Entry {
                 Entry {
                     before: [0, 0, 0, 0],
                     inst: Instruction {
                         op_num: 0,
                         a: 0,
                         b: 0,
                         c: 0,
                     },
                     after: [0, 0, 0, 0],
                 }
             }
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day16.txt").unwrap();
             }
     
             lazy_static! {
                 static ref file_string_pt2: String =
                     std::fs::read_to_string("data/input_day16_part2.txt").unwrap();
             }
     
             lazy_static! {
                 static ref re: Regex = Regex::new(
                     r"(?x)
                         Before:\s*\[(?P<B0>\d+),\s*(?P<B1>\d+),\s*(?P<B2>\d+),\s*(?P<B3>\d+)\]\s*
                         (?P<I0>\d+)\s*(?P<I1>\d+)\s*(?P<I2>\d+)\s*(?P<I3>\d+)\s*
                         After:\s*\[(?P<A0>\d+),\s*(?P<A1>\d+),\s*(?P<A2>\d+),\s*(?P<A3>\d+)\]\s*"
                 )
                 .unwrap();
             }
     
             lazy_static! {
                 static ref re2: Regex =
                     Regex::new(r"(?P<I0>\d+) (?P<I1>\d+) (?P<I2>\d+) (?P<I3>\d+)").unwrap();
             }
     
             let mut entries: Vec<Entry> = Vec::new();
     
             let mut entry_str: String = String::new();
             for (i, line) in file_string.lines().enumerate().take(3228) {
                 if i % 4 == 3 {
                     entry_str.clear();
                 } else {
                     entry_str.push_str(line);
                     if i % 4 == 2 {
                         entry_str.push_str(line);
     
                         let caps = re.captures(&entry_str).unwrap();
     
                         let mut new_entry = Entry::new();
                         new_entry.before[0] = caps["B0"].parse().unwrap();
                         new_entry.before[1] = caps["B1"].parse().unwrap();
                         new_entry.before[2] = caps["B2"].parse().unwrap();
                         new_entry.before[3] = caps["B3"].parse().unwrap();
     
                         new_entry.inst.op_num = caps["I0"].parse().unwrap();
                         new_entry.inst.a = caps["I1"].parse().unwrap();
                         new_entry.inst.b = caps["I2"].parse().unwrap();
                         new_entry.inst.c = caps["I3"].parse().unwrap();
     
                         new_entry.after[0] = caps["A0"].parse().unwrap();
                         new_entry.after[1] = caps["A1"].parse().unwrap();
                         new_entry.after[2] = caps["A2"].parse().unwrap();
                         new_entry.after[3] = caps["A3"].parse().unwrap();
     
                         entries.push(new_entry);
                     }
                 }
             }
     
             // ------------------------------------------------------
             // Check entry to see how many opcodes it behaves likes
             // ------------------------------------------------------
             let mut three_or_more = 0;
             let num_ops: usize = 16;
             let mut initial_set: HashSet<OpCode> =
                 HashSet::with_capacity_and_hasher(num_ops, Default::default());
             for i in 0..num_ops {
                 initial_set.insert(op_from_usize(i));
             }
     
             let mut maybe_ops: Vec<HashSet<OpCode>> = vec![initial_set; num_ops];
             let mut entry_ops: HashSet<OpCode> =
                 HashSet::with_capacity_and_hasher(num_ops, Default::default());
     
             for entry in entries {
                 let mut behaves_like = 0;
                 let op_num = entry.inst.op_num;
     
                 for i in 0..=15 {
                     let op = op_from_usize(i);
                     let mut r = entry.before;
                     execute(op, entry.inst.a, entry.inst.b, entry.inst.c, &mut r);
     
                     if r == entry.after {
                         behaves_like += 1;
                         entry_ops.insert(op);
                     }
                 }
     
                 let intersection: HashSet<OpCode> = maybe_ops[op_num]
                     .intersection(&entry_ops)
                     .cloned()
                     .collect();
                 maybe_ops[op_num] = intersection;
     
                 if behaves_like >= 3 {
                     three_or_more += 1;
                 }
     
                 entry_ops.clear();
             }
     
             writeln!(&mut result, "Day 16, Problem 1 - [{}]", three_or_more).unwrap(); // 563
     
             // ------------------------------------------------------
             // Reduce maybe_ops until each op_num has one opcode
             // ------------------------------------------------------
             let mut removed: Vec<bool> = vec![false; num_ops];
             loop {
                 let mut to_remove: Option<(usize, OpCode)> = None;
     
                 // Find a new entry in maybe_ops that has been reduced to a single OpCode
                 for (i, set) in (&maybe_ops).iter().enumerate() {
                     if set.len() == 1 && !removed[i] {
                         let op: OpCode = *set.iter().next().unwrap();
                         removed[i] = true;
                         to_remove = Some((i, op));
                         break;
                     }
                 }
     
                 // Remove that single OpCode from all other
                 match to_remove {
                     Some((src_idx, op_to_remove)) => {
                         maybe_ops
                             .iter_mut()
                             .enumerate()
                             .filter(|(i, _)| *i != src_idx)
                             .for_each(|(_, set)| {
                                 set.remove(&op_to_remove);
                             });
                     }
                     None => break,
                 };
             }
     
             // Build vec to map instruction.op_num -> OpCode
             let opcode_from_opnum: Vec<OpCode> = maybe_ops
                 .iter()
                 .map(|set| {
                     assert!(set.len() == 1);
                     *set.iter().next().unwrap()
                 })
                 .collect();
     
             // ------------------------------------------------------
             // Parse instructions from part2
             // ------------------------------------------------------
             let mut instructions: Vec<Instruction> = Vec::new();
             for line in file_string_pt2.lines() {
                 let caps = re2.captures(line).unwrap();
                 instructions.push(Instruction::new(
                     caps["I0"].parse().unwrap(),
                     caps["I1"].parse().unwrap(),
                     caps["I2"].parse().unwrap(),
                     caps["I3"].parse().unwrap(),
                 ));
             }
     
             // ------------------------------------------------------
             // Execute part2 instructions
             // ------------------------------------------------------
             let mut registers = [0, 0, 0, 0];
             for inst in instructions {
                 execute(
                     opcode_from_opnum[inst.op_num],
                     inst.a,
                     inst.b,
                     inst.c,
                     &mut registers,
                 );
             }
     
             writeln!(&mut result, "Day 16, Problem 2 - [{}]", registers[0]).unwrap(); // 629
     
             result
         } // fn run
     } // mod day16
     
     // Input: brush strokes to paint a grid; x=331, y=565..585
     //
     // Problem 1: Water falls into buckets drawn onto grid. How many tiles does it reach?
     // Solution 1: Implement simple water simulation. Count tiles.
     //
     // Problem 2: How much water is retained?
     // Solution 2: Simulate water flow until all buckets are full. Count final tiles.
     pub mod day17 {
         use derive_more::*;
         use hashbrown::HashMap;
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::fmt::Write;
     
         type Tiles = HashMap<Pos, u8>;
         const CLAY: u8 = 10;
         const WATER_FLOW: u8 = 20;
         const WATER_REST: u8 = 30;
         const WATER_INFINITE: u8 = 40;
     
         #[derive(Copy, Clone, Eq, PartialEq, Hash, Add, Sub, Debug)]
         struct Pos {
             row: i16,
             col: i16,
         }
     
         impl Pos {
             fn from_xy(x: i16, y: i16) -> Pos {
                 Pos { row: y, col: x }
             }
     
             fn left(self) -> Pos {
                 self + Pos::from_xy(-1, 0)
             }
             fn right(self) -> Pos {
                 self + Pos::from_xy(1, 0)
             }
             fn down(self) -> Pos {
                 self + Pos::from_xy(0, 1)
             }
         }
     
         #[derive(Copy, Clone, Debug)]
         struct Bounds {
             min_row: i16,
             min_col: i16,
             max_row: i16,
             max_col: i16,
         }
     
         impl Bounds {
             fn new() -> Bounds {
                 Bounds {
                     min_row: std::i16::MAX,
                     min_col: std::i16::MAX,
                     max_row: std::i16::MIN,
                     max_col: std::i16::MIN,
                 }
             }
     
             fn add(&mut self, pos: Pos) {
                 self.min_row = std::cmp::min(self.min_row, pos.row);
                 self.min_col = std::cmp::min(self.min_col, pos.col);
                 self.max_row = std::cmp::max(self.max_row, pos.row);
                 self.max_col = std::cmp::max(self.max_col, pos.col);
             }
         }
     
         #[allow(dead_code)]
         fn print_board(bounds: Bounds, tiles: &Tiles) {
             for row in bounds.min_row..=bounds.max_row {
                 let mut row_string =
                     String::with_capacity((bounds.max_col - bounds.min_col + 1) as usize);
                 for col in bounds.min_col..=bounds.max_col {
                     let pos = Pos { row, col };
                     let tile = tiles.get(&pos);
     
                     let c = match tile {
                         Some(tile) => match *tile {
                             CLAY => '#',
                             WATER_FLOW => '|',
                             WATER_REST => '~',
                             WATER_INFINITE => 'v',
                             _ => panic!("Unexpected value"),
                         },
                         None => '.',
                     };
     
                     row_string.push(c);
                 }
                 println!("{}", row_string);
             }
         }
     
         pub fn run() -> String {
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day17.txt").unwrap();
             }
     
             lazy_static! {
                 static ref xy_re: Regex =
                     Regex::new(r"x=(?P<X>\d+), y=(?P<Y0>\d+)..(?P<Y1>\d+)").unwrap();
             }
     
             lazy_static! {
                 static ref yx_re: Regex =
                     Regex::new(r"y=(?P<Y>\d+), x=(?P<X0>\d+)..(?P<X1>\d+)").unwrap();
             }
     
             let mut result = String::with_capacity(128);
     
             let mut tiles: Tiles = Tiles::default();
             let mut input_bounds = Bounds::new();
     
             let mut water_flow: Vec<Pos> = Vec::new();
             tiles.insert(Pos::from_xy(500, 0), WATER_FLOW);
     
             // ------------------------------------------------------
             // Parse input
             // ------------------------------------------------------
             for line in file_string.lines() {
                 let x0: i16;
                 let x1: i16;
                 let y0: i16;
                 let y1: i16;
     
                 let caps = xy_re.captures(&line);
                 match caps {
                     Some(caps) => {
                         x0 = caps["X"].parse().unwrap();
                         x1 = x0;
                         y0 = caps["Y0"].parse().unwrap();
                         y1 = caps["Y1"].parse().unwrap();
                     }
                     None => {
                         let caps = yx_re.captures(&line).unwrap();
                         x0 = caps["X0"].parse().unwrap();
                         x1 = caps["X1"].parse().unwrap();
                         y0 = caps["Y"].parse().unwrap();
                         y1 = y0;
                     }
                 }
     
                 for row in y0..=y1 {
                     for col in x0..=x1 {
                         let pos = Pos { row, col };
                         tiles.insert(pos, CLAY);
                         input_bounds.add(pos);
                     }
                 }
             }
     
             let mut render_bounds = input_bounds;
     
             let spout_pos = Pos::from_xy(500, 0);
             render_bounds.add(spout_pos);
             water_flow.push(spout_pos);
     
             while !water_flow.is_empty() {
                 let idx = water_flow.len() - 1;
                 let flow_pos = water_flow[idx];
     
                 let down_pos = flow_pos.down();
                 match tiles.get(&down_pos) {
                     Some(tile) => match *tile {
                         CLAY | WATER_REST => {
                             // Scan left and right
                             let mut span: Vec<Pos> = Vec::with_capacity(32);
                             water_flow.pop();
                             span.push(flow_pos);
     
                             let mut overflow = false;
     
                             for left in &[true, false] {
                                 let mut next = flow_pos;
                                 loop {
                                     next = if *left { next.left() } else { next.right() };
     
                                     let stop = match tiles.get(&next) {
                                         Some(tile) => match *tile {
                                             CLAY => true,
                                             _ => false,
                                         },
                                         None => false,
                                     };
     
                                     if stop {
                                         break;
                                     }
     
                                     span.push(next);
     
                                     let next_down = next.down();
                                     match tiles.get(&next_down) {
                                         Some(tile) => match *tile {
                                             CLAY | WATER_REST => (),
                                             WATER_FLOW => {
                                                 water_flow.push(next);
                                                 overflow = true;
                                                 break;
                                             }
                                             _ => panic!("Unexpected behavior"),
                                         },
                                         None => {
                                             water_flow.push(next);
                                             overflow = true;
                                             break;
                                         }
                                     };
                                 }
                             }
     
                             let span_tile = if overflow { WATER_FLOW } else { WATER_REST };
                             for pos in span {
                                 tiles.insert(pos, span_tile);
                             }
                         }
                         WATER_FLOW => {
                             water_flow.pop();
                         }
                         _ => panic!("Unexpected behavior"),
                     },
                     None => {
                         if down_pos.row <= input_bounds.max_row {
                             tiles.insert(down_pos, WATER_FLOW);
                             water_flow.push(down_pos);
                         } else {
                             water_flow.pop();
                         }
                     }
                 };
             }
     
             // Recompute bounds because it may have grown
             tiles.iter().for_each(|(pos, _)| render_bounds.add(*pos));
     
             // Count number of water tiles
             let water_tiles = tiles
                 .iter()
                 .filter(|(pos, v)| {
                     pos.row >= input_bounds.min_row
                         && pos.row <= input_bounds.max_row
                         && (**v == WATER_FLOW || **v == WATER_REST || **v == WATER_INFINITE)
                 })
                 .count();
     
             writeln!(&mut result, "Day 17, Problem 1 - [{}]", water_tiles).unwrap();
     
             let rest_tiles = tiles.iter().filter(|(_, v)| **v == WATER_REST).count();
     
             writeln!(&mut result, "Day 17, Problem 2 - [{}]", rest_tiles).unwrap();
     
             result
         } // fn run()
     } // day 17
     
     // Input: ASCII map
     //
     // Problem 1: Given rules of evolution, what is board state after 10 minutes?
     // Solution 1: Implement rules. Simulate 10 ticks.
     //
     // Problem 2: What is value after one billion ticks?
     // Solution 2: Simulate board. Detect cycle. Compute final state.
     //
     // Notes: This is basically Conway's game of life.
     pub mod day18 {
         use derive_more::*;
         use hashbrown::HashMap;
         use lazy_static::lazy_static;
         use std::collections::hash_map::DefaultHasher;
         use std::fmt::Write;
         use std::hash::{Hash, Hasher};
     
         const TREE: u8 = 1;
         const LUMBERYARD: u8 = 2;
         const FIELD: u8 = 3;
     
         #[derive(Copy, Clone, Debug, Add)]
         struct Pos {
             row: i8,
             col: i8,
         }
     
         impl std::ops::Add<(i8, i8)> for Pos {
             type Output = Pos;
     
             fn add(self, other: (i8, i8)) -> Pos {
                 Pos::new(self.row + other.0, self.col + other.1)
             }
         }
     
         impl Pos {
             pub fn new(row: i8, col: i8) -> Pos {
                 Pos { row, col }
             }
     
             pub fn get_index(self, width: usize) -> usize {
                 self.row as usize * width + self.col as usize
             }
     
             pub fn adjacency_iter(
                 self,
                 width: usize,
                 height: usize,
             ) -> impl Iterator<Item = Pos> {
                 const OFFSETS: [(i8, i8); 8] = [
                     (-1, -1),
                     (-1, 0),
                     (-1, 1),
                     (0, -1),
                     (0, 1),
                     (1, -1),
                     (1, 0),
                     (1, 1),
                 ];
     
                 let width = width as i8;
                 let height = height as i8;
     
                 OFFSETS
                     .iter()
                     .map(move |offset| self + *offset)
                     .filter(move |pos| {
                         pos.row >= 0 && pos.row < height && pos.col >= 0 && pos.col < width
                     })
             }
         }
     
         struct Neighbors {
             trees: u8,
             lumberyards: u8,
             fields: u8,
         }
     
         impl Neighbors {
             fn new() -> Neighbors {
                 Neighbors {
                     trees: 0,
                     lumberyards: 0,
                     fields: 0,
                 }
             }
         }
     
         pub fn print_board(
             board: &[u8],
             width: usize,
             height: usize,
             gen: u64
         ) {
             for row in 0..height {
                 let mut row_string = String::with_capacity(width);
                 for col in 0..width {
                     let idx = row * width + col;
                     let c = match board[idx] {
                         TREE => '|',
                         FIELD => '.',
                         LUMBERYARD => '#',
                         _ => panic!("Unexpected value"),
                     };
                     row_string.push(c);
                 }
                 println!("{}", row_string);
             }
     
             let long = 250;
             let short = 30;
             let a = 12;
             let b = 45;
     
             let sleep_time = if gen == 1 {
                 500
             } else if gen < a {
                 long
             } else if gen < b {
                 let frac = (gen - a) as f32 / (b - a) as f32;
                 short + ((1.0 - frac) * ((long - short) as f32)) as u64
             } else {
                 short
             };
     
             std::thread::sleep(std::time::Duration::from_millis(sleep_time));
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day18.txt").unwrap();
             }
     
             let width = file_string.lines().next().unwrap().chars().count();
             let height = file_string.lines().count();
     
             let mut curr = 0;
             let mut next = 1;
             let mut areas: Vec<Vec<u8>> = vec![vec![0; width * height]; 2];
             let mut hashes: HashMap<u64, u64> = HashMap::default();
     
             let problem_one_minutes = 10;
             let problem_two_minutes = 1_000_000_000;
             let mut problem_two_target = std::u64::MAX;
             let mut cycle_found = false;
             let mut cycle_length;
     
             let mut idx = 0;
             for line in file_string.lines() {
                 for c in line.chars() {
                     match c {
                         '.' => areas[curr][idx] = FIELD,
                         '|' => areas[curr][idx] = TREE,
                         '#' => areas[curr][idx] = LUMBERYARD,
                         _ => panic!("Unexpected input"),
                     };
                     idx += 1;
                 }
             }
     
             // Loop for the desired number of minutes
             let mut minute = 1;
             loop {
                 // Loop over rows and cols
                 for row in 0..height {
                     for col in 0..width {
                         let pos = Pos {
                             row: row as i8,
                             col: col as i8,
                         };
     
                         let idx2 = pos.get_index(width);
                         let curr_tile = areas[curr][idx2];
     
                         // Count neighbors
                         let neighbors = pos
                             .adjacency_iter(width, height)
                             .map(|p| areas[curr][p.get_index(width)])
                             .fold(Neighbors::new(), |accum, v| {
                                 let mut accum = accum;
                                 match v {
                                     TREE => accum.trees += 1,
                                     LUMBERYARD => accum.lumberyards += 1,
                                     FIELD => accum.fields += 1,
                                     _ => panic!("Unexpected value"),
                                 };
                                 accum
                             });
     
                         // Determine what tile turns into
                         let next_tile = match curr_tile {
                             FIELD => {
                                 if neighbors.trees >= 3 {
                                     TREE
                                 } else {
                                     FIELD
                                 }
                             }
                             TREE => {
                                 if neighbors.lumberyards >= 3 {
                                     LUMBERYARD
                                 } else {
                                     TREE
                                 }
                             }
                             LUMBERYARD => {
                                 if neighbors.lumberyards >= 1 && neighbors.trees >= 1 {
                                     LUMBERYARD
                                 } else {
                                     FIELD
                                 }
                             }
                             _ => panic!("Unexpected value"),
                         };
     
                         // Set new tile
                         areas[next][idx2] = next_tile;
                     }
                 }
     
                 // Swap curr/next
                 curr = next;
                 next = (next + 1) % 2;
     
                 //print_board(&areas[curr], width, height, minute, cycle_length);
     
                 // Hash new board
                 if !cycle_found {
                     let mut hasher = DefaultHasher::new();
                     areas[curr].hash(&mut hasher);
                     let hash = hasher.finish();
     
                     cycle_found = match hashes.get(&hash) {
                         Some(old_minute) => {
                             cycle_length = minute - *old_minute;
                             let to_go = problem_two_minutes - minute;
                             problem_two_target = minute + (to_go % cycle_length);
                             true
                         }
                         None => {
                             hashes.insert(hash, minute);
                             false
                         }
                     };
                 }
     
                 // Check for answer
                 if minute == problem_one_minutes || minute == problem_two_target {
                     let counts: (usize, usize) = areas[curr].iter().fold((0, 0), |accum, v| match *v {
                         LUMBERYARD => (accum.0 + 1, accum.1),
                         TREE => (accum.0, accum.1 + 1),
                         _ => accum,
                     });
                     let answer = counts.0 * counts.1; // num lumberyards * num trees
     
                     if minute == 10 {
                         writeln!(&mut result, "Day 18, Problem 1 - [{}]", answer).unwrap(); // 589931
                     }
     
                     if minute == problem_two_target {
                         writeln!(&mut result, "Day 18, Problem 2 - [{}]", answer).unwrap(); // 222332
                         break;
                     }
                 }
     
                 minute += 1;
             }
     
             result
         } // fn run()
     } // mod day18
     
     // Input: ASM Instructions; addi 5 16 5
     //
     // Problem 1: Given instruction pointer, when does program halt?
     // Solution 1: Implement instruction pointer rules. Run until halt.
     //
     // Problem 2: What is final value if starting value changes from 0 to 1?
     // Solution 2: Write asm instructions on a piece of paper.
     //             Figure out what the mildly obfuscated program is slowly doing.
     //             That's the answer.
     //
     //             This problem is like being a compiler and optimizing away
     //             literally the entire program.
     //
     //             answer = sum_of_factors_of(10551373)
     //                    = 1 + 7 + 17 + 119+ 88667+ 620669+ 1507339 + 10551373
     //                    = 12768192
     pub mod day19 {
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::fmt::Write;
     
         type Registers = [usize; 6];
     
         #[allow(non_camel_case_types)]
         #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
         enum OpCode {
             addr, // rC = rA + rB
             addi, // rC = rA + b
     
             mulr, // rC = rA * rB
             muli, // rC = rA * b
     
             banr, // rC = rA & rB
             bani, // rC = rA & b
     
             borr, // rC = rA | rB
             bori, // rC = rA | b
     
             setr, // rC = rA
             seti, // rC = a
     
             gtir, // rC = a > rB ? 1 : 0
             gtri, // rC = rA > b ? 1 : 0
             gtrr, // rC = rA > rB ? 1 : 0
     
             eqir, // rC = a == rB ? 1 : 0
             eqri, // rC = rA == b ? 1 : 0
             eqrr, // rC = rA == rB ? 1 : 0
         }
     
         fn op_from_str(s: &str) -> OpCode {
             match s {
                 "addr" => OpCode::addr,
                 "addi" => OpCode::addi,
                 "mulr" => OpCode::mulr,
                 "muli" => OpCode::muli,
                 "banr" => OpCode::banr,
                 "bani" => OpCode::bani,
                 "borr" => OpCode::borr,
                 "bori" => OpCode::bori,
                 "setr" => OpCode::setr,
                 "seti" => OpCode::seti,
                 "gtir" => OpCode::gtir,
                 "gtri" => OpCode::gtri,
                 "gtrr" => OpCode::gtrr,
                 "eqir" => OpCode::eqir,
                 "eqri" => OpCode::eqri,
                 "eqrr" => OpCode::eqrr,
                 _ => panic!("Unexpected input"),
             }
         }
     
         #[derive(Copy, Clone)]
         struct Instruction {
             op: OpCode,
             a: usize,
             b: usize,
             c: usize,
         }
     
         impl Instruction {
             fn new(op: OpCode, a: usize, b: usize, c: usize) -> Instruction {
                 Instruction { op, a, b, c }
             }
         }
     
         impl std::fmt::Display for Instruction {
             fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                 writeln!(f, "({:?} {} {} {})", self.op, self.a, self.b, self.c)
             }
         }
     
         fn execute(inst: &Instruction, r: &mut Registers) {
             let op = inst.op;
             let a = inst.a;
             let b = inst.b;
             let c = inst.c;
     
             match op {
                 OpCode::addr => r[c] = r[a] + r[b],
                 OpCode::addi => r[c] = r[a] + b,
                 OpCode::mulr => r[c] = r[a] * r[b],
                 OpCode::muli => r[c] = r[a] * b,
                 OpCode::banr => r[c] = r[a] & r[b],
                 OpCode::bani => r[c] = r[a] & b,
                 OpCode::borr => r[c] = r[a] | r[b],
                 OpCode::bori => r[c] = r[a] | b,
                 OpCode::setr => r[c] = r[a],
                 OpCode::seti => r[c] = a,
                 OpCode::gtir => r[c] = if a > r[b] { 1 } else { 0 },
                 OpCode::gtri => r[c] = if r[a] > b { 1 } else { 0 },
                 OpCode::gtrr => r[c] = if r[a] > r[b] { 1 } else { 0 },
                 OpCode::eqir => r[c] = if a == r[b] { 1 } else { 0 },
                 OpCode::eqri => r[c] = if r[a] == b { 1 } else { 0 },
                 OpCode::eqrr => r[c] = if r[a] == r[b] { 1 } else { 0 },
             };
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string : String = std::fs::read_to_string("data/input_day19.txt").unwrap();
                 //static ref file_string : String = std::fs::read_to_string("data/test.txt").unwrap();
             }
     
             lazy_static! {
                 static ref re: Regex =
                     Regex::new(r"(?P<inst>[[:alpha:]]+) (?P<a>\d+) (?P<b>\d+) (?P<c>\d+)").unwrap();
             }
     
             let mut instructions: Vec<Instruction> = Vec::new();
     
             let mut lines = file_string.lines();
     
             let first_line = lines.next();
             let test = first_line.unwrap().as_bytes();
             let instruction_ptr_register: usize = (test[4] - b'0') as usize; // parse number from: '#ip 5'
     
             for line in lines {
                 let caps = re.captures(line).unwrap();
                 instructions.push(Instruction::new(
                     op_from_str(&caps["inst"]),
                     caps["a"].parse().unwrap(),
                     caps["b"].parse().unwrap(),
                     caps["c"].parse().unwrap(),
                 ));
             }
     
             // ----------------------------------
             // Execute
             // ----------------------------------
             let mut instruction_ptr = 0;
             let mut registers = [0, 0, 0, 0, 0, 0];
     
             loop {
                 // Halt execution
                 if instruction_ptr >= instructions.len() {
                     break;
                 }
     
                 // Write instruction ptr to register
                 registers[instruction_ptr_register] = instruction_ptr;
     
                 let inst = &instructions[instruction_ptr];
                 execute(&inst, &mut registers);
     
                 // Write register to instruction ptr
                 instruction_ptr = registers[instruction_ptr_register];
                 instruction_ptr += 1;
             }
     
             writeln!(&mut result, "Day 19, Problem 1 - [{}]", registers[0]).unwrap(); // 1120
     
             // Whole program optimization!!!
             // sum_of_factors_of(10551373)
             // = 1 + 7 + 17 + 119+ 88667+ 620669+ 1507339 + 10551373
             // = 12768192
             writeln!(&mut result, "Day 19, Problem 2 - [12768192]").unwrap();
     
             result
         } // fn run()
     } // mod day19
     
     // Input: Regex of NSWE ascii characters
     //
     // Problem 1: What is the furthest room from the starting point
     // Solution 1: Recursively parse input stream to build map. Traverse with BFS to get answer.
     //
     // Problem 2: How many rooms are at least 1000 steps away?
     // Solution 2: Traverse full map. Count rooms at least 1000 away from start.
     //
     // Note: All the work in this puzzle is in parsing the input stream.
     pub mod day20 {
         use hashbrown::{HashMap, HashSet};
         use lazy_static::lazy_static;
         use std::cmp::Ordering;
         use std::collections::BinaryHeap;
         use std::fmt::Write;
     
         #[derive(Copy, Clone, Debug)]
         #[allow(dead_code)]
         enum Dir {
             North = 0b1000,
             South = 0b0100,
             East = 0b0010,
             West = 0b0001,
         }
     
         fn inverse_dir(dir: Dir) -> Dir {
             match dir {
                 Dir::North => Dir::South,
                 Dir::South => Dir::North,
                 Dir::East => Dir::West,
                 Dir::West => Dir::East,
             }
         }
     
         fn dir_from_char(c: char) -> Dir {
             match c {
                 'N' => Dir::North,
                 'S' => Dir::South,
                 'E' => Dir::East,
                 'W' => Dir::West,
                 _ => panic!("Unexpected input"),
             }
         }
     
         #[derive(Copy, Clone, Eq, Hash, PartialEq)]
         struct Room {
             doors: u8,
         }
     
         impl Room {
             pub fn from_dir(dir: Dir) -> Self {
                 Room {
                     doors: inverse_dir(dir) as u8,
                 }
             }
     
             pub fn add_door(&mut self, dir: Dir) {
                 self.doors |= dir as u8;
             }
     
             pub fn has_door(self, dir: Dir) -> bool {
                 (self.doors & (dir as u8)) > 0
             }
         }
     
         #[derive(Copy, Clone, Eq, Debug, Hash, PartialEq)]
         struct Pos {
             row: i16,
             col: i16,
         }
     
         impl std::ops::Add<(i16, i16)> for Pos {
             type Output = Pos;
     
             fn add(self, other: (i16, i16)) -> Pos {
                 Pos::new(self.row + other.0, self.col + other.1)
             }
         }
     
         impl Pos {
             pub fn new(row: i16, col: i16) -> Pos {
                 Pos { row, col }
             }
     
             pub fn travel(self, dir: Dir) -> Pos {
                 match dir {
                     Dir::North => self + (-1, 0),
                     Dir::South => self + (1, 0),
                     Dir::East => self + (0, 1),
                     Dir::West => self + (0, -1),
                 }
             }
         }
     
         #[derive(Copy, Clone, Debug)]
         struct Bounds {
             min_row: i16,
             min_col: i16,
             max_row: i16,
             max_col: i16,
         }
     
         impl Bounds {
             fn new() -> Bounds {
                 Bounds {
                     min_row: std::i16::MAX,
                     min_col: std::i16::MAX,
                     max_row: std::i16::MIN,
                     max_col: std::i16::MIN,
                 }
             }
         }
     
         impl std::ops::Add<Pos> for Bounds {
             type Output = Bounds;
     
             fn add(self, pos: Pos) -> Bounds {
                 Bounds {
                     min_row: std::cmp::min(self.min_row, pos.row),
                     min_col: std::cmp::min(self.min_col, pos.col),
                     max_row: std::cmp::max(self.max_row, pos.row),
                     max_col: std::cmp::max(self.max_col, pos.col),
                 }
             }
         }
     
         struct Map {
             rooms: HashMap<Pos, Room>,
         }
     
         // Helper to performing breadth-first pathfind
         #[derive(Copy, Clone, Eq, PartialEq)]
         struct PathfindNode {
             pos: Pos,
             room: Room,
             cost: u16,
         }
     
         impl PathfindNode {
             fn new(pos: Pos, room: Room, cost: u16) -> Self {
                 PathfindNode { pos, room, cost }
             }
         }
     
         impl PartialOrd for PathfindNode {
             fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
                 other.cost.partial_cmp(&self.cost)
             }
         }
     
         impl Ord for PathfindNode {
             fn cmp(&self, other: &Self) -> Ordering {
                 self.partial_cmp(other).unwrap()
             }
         }
     
         impl Map {
             #[allow(dead_code)]
             fn print(&self) {
                 println!("\n\nNum Rooms: {}\n", self.rooms.keys().count());
     
                 // Calculate bounds of known rooms
                 let bounds: Bounds = self
                     .rooms
                     .iter()
                     .fold(Bounds::new(), |b, (room_pos, _)| b + *room_pos);
     
                 // Calculate width; add padding for exterior walls
                 let width = (bounds.max_col - bounds.min_col + 1) * 2 + 1;
     
                 // Build and print northern exterior wall
                 let wall = 'â–ˆ';
     
                 let all_walls: String = vec![wall; width as usize].into_iter().collect();
                 println!("{}", all_walls);
     
                 // Loop over iterior
                 for row in bounds.min_row..=bounds.max_row {
                     let mut curr_row = String::with_capacity(width as usize);
                     let mut next_row = String::with_capacity(width as usize);
     
                     curr_row.push(wall);
                     next_row.push(wall);
     
                     for col in bounds.min_col..=bounds.max_col {
                         let pos = Pos { row, col };
                         match self.rooms.get(&pos) {
                             Some(room) => {
                                 curr_row.push(if pos == Pos::new(0, 0) { 'X' } else { ' ' });
                                 curr_row.push(if room.has_door(Dir::East) { ' ' } else { wall });
                                 next_row.push(if room.has_door(Dir::South) { ' ' } else { wall });
                                 next_row.push(wall);
                             }
                             None => {
                                 curr_row.push(wall);
                                 curr_row.push(wall);
                                 next_row.push(wall);
                                 next_row.push(wall);
                             }
                         }
                     }
     
                     println!("{}\n{}", curr_row, next_row);
                 }
             }
     
             fn explore(
                 &mut self,
                 src_pos: Pos,
                 dir: Dir
             ) -> Pos {
                 // Add door to current room
                 self.rooms.get_mut(&src_pos).unwrap().add_door(dir);
     
                 // Get pos for new room
                 let new_pos = src_pos.travel(dir);
     
                 // Add door to new room
                 self.rooms
                     .entry(new_pos)
                     .and_modify(|r| r.add_door(inverse_dir(dir)))
                     .or_insert_with(|| Room::from_dir(dir));
     
                 new_pos
             }
         }
     
         fn dfs(
             src_pos: Pos,
             path: &[u8],
             start: usize,
             map: &mut Map,
         ) -> usize {
             let mut curr_pos = src_pos;
     
             let mut i = start;
             while i < path.len() {
                 let c = path[i];
                 match c as char {
                     'N' | 'S' | 'E' | 'W' => {
                         curr_pos =
                             map.explore(curr_pos, dir_from_char(c as char));
                     }
                     ')' | '$' => return i,
                     '|' => {
                         i = dfs(src_pos, &path, i + 1, map);
                         return i;
                     }
                     '(' => {
                         i = dfs(curr_pos, &path, i + 1, map);
                     }
                     _ => panic!("Unexpected value"),
                 };
     
                 i += 1;
             }
     
             path.len()
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day20.txt").unwrap();
             }
     
             let bytes = file_string.as_bytes();
             assert!(bytes[0] == b'^');
             assert!(bytes[bytes.len() - 1] == b'$');
     
             let mut map = Map {
                 rooms: HashMap::default(),
             };
     
             // Build map from path stream
             let origin = Pos::new(0, 0);
             map.rooms.insert(origin, Room { doors: 0 });
     
             dfs(origin, &bytes[1..], 0, &mut map);
     
             // BFS to find furthest room
             let mut visited: HashSet<Pos> = HashSet::default();
     
             let mut nodes: BinaryHeap<PathfindNode> = BinaryHeap::new();
             let mut max_cost = 0;
             let mut thousand_plus = 0;
     
             let all_dirs: [Dir; 4] = [Dir::North, Dir::South, Dir::East, Dir::West];
     
             nodes.push(PathfindNode::new(origin, map.rooms[&origin], 0));
             while !nodes.is_empty() {
                 let node = nodes.pop().unwrap();
                 max_cost = std::cmp::max(max_cost, node.cost);
                 if node.cost >= 1000 {
                     thousand_plus += 1;
                 }
     
                 let iter = all_dirs
                     .iter()
                     .filter(|dir| node.room.has_door(**dir))
                     .map(|dir| node.pos.travel(*dir));
     
                 for pos in iter {
                     if let Some(room) = map.rooms.get(&pos) {
                         if visited.insert(pos) {
                             nodes.push(PathfindNode::new(pos, *room, node.cost + 1));
                         }
                     };
                 }
             }
     
             writeln!(&mut result, "Day 20, Problem 1 - [{}]", max_cost).unwrap(); // 4721
             writeln!(&mut result, "Day 20, Problem 2 - [{}]", thousand_plus).unwrap(); // 8281
     
             result
         } // fn run()
     } // mod day20
     
     // Input: Another asm program
     //
     // Problem 1: What is smallest positive integer to halt program in fewest instructions?
     // Solution 1: Write asm on piece of paper. Simulate by hand to find answer. Very easy.
     //
     // Problem 2: What is lowest integer to cause halt in maximum number of instructions?
     // Solution 2: Hand annotate elfcode. See how register is used.
     //             Slowly simulate looking for cycle. Last value before cycle is answer
     //
     // Optimization: This solution has no elfcode. I converted elfcode to Rust. It ran in
     //               ~120 milliseconds. Then I hand optimized that code by removing a
     //               very slow, incremental way to do a basic divide by 256. Final code
     //               runs in less than half a millisecond.
     pub mod day21 {
         use hashbrown::HashSet;
         use std::fmt::Write;
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Part 1 solved by hand
             writeln!(&mut result, "Day 21, Problem 1 - [1797184]").unwrap();
     
             // Part 2
             let mut encountered: HashSet<usize> = HashSet::default();
             let mut last = 0;
     
             // Prelude
             let mut r1: usize = 0;
             let mut r4: usize = 0;
             let mut r5;
     
             let mut skip_to_eight = false;
             loop {
                 if !skip_to_eight {
                     r4 = r1 | 65536; // 6
                     r1 = 3_798_839; // 7
                 }
                 skip_to_eight = false;
                 r5 = r4 & 255; // 8
                 r1 += r5; // 9
                 r1 &= 16_777_215; //10
                 r1 *= 65899; // 11
                 r1 &= 16_777_215; // 12
                 if 256 > r4 {
                     let v = r1;
                     let inserted = encountered.insert(v);
                     if !inserted {
                         writeln!(&mut result, "Day 21, Problem 2 - [{}]", last).unwrap(); // 11011493
                         return result;
                     }
     
                     last = v;
     
                     continue; // 30
                 }
     
                 r4 /= 256;
                 skip_to_eight = true;
             }
         } // fn run
     } // mod day21
     
     // Input: depth: 6084\ntarget: 14,709
     //
     // Problem 1: What is risk level for cave?
     // Solution 1: Follow arbitrary rules to generate cave tiles. Calculate risk.
     //
     // Problem 2: How long is fastest time to reach target?
     // Solution 2: Implement A*
     //
     // Notes: Assorted optimizations took this from ~260 milliseconds to ~85. A huge
     //        win would be converted HashSet to Vec<bool>. But I wanted to be general
     //        and the map doesn't have a known size.
     pub mod day22 {
         use hashbrown::{HashMap, HashSet};
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::cmp::Ordering;
         use std::collections::BinaryHeap;
         use std::fmt::Write;
     
         #[derive(Copy, Clone, Eq, Debug, Hash, PartialEq)]
         enum TileType {
             Mouth,
             Rocky,
             Wet,
             Narrow,
             Target,
         }
     
         #[derive(Copy, Clone, Eq, Debug, Hash, PartialEq)]
         enum Gear {
             Torch,
             Climbing,
             Neither,
         }
     
         #[derive(Copy, Clone, Eq, Debug, Hash, PartialEq)]
         struct Pos {
             row: i16,
             col: i16,
         }
     
         impl Pos {
             fn new(row: i16, col: i16) -> Pos {
                 Pos { row, col }
             }
         }
     
         impl std::ops::Add<(i16, i16)> for Pos {
             type Output = Pos;
     
             fn add(self, other: (i16, i16)) -> Pos {
                 Pos::new(self.row + other.0, self.col + other.1)
             }
         }
     
         // Helper to performing A*
         #[derive(Copy, Clone, Eq, PartialEq)]
         struct PathfindNode {
             pos: Pos,
             gear: Gear,
             tile: TileType,
             cost: u16,
             estimate: u16,
         }
     
         impl PathfindNode {
             fn new(pos: Pos, gear: Gear, tile: TileType, cost: u16, estimate: u16) -> Self {
                 PathfindNode {
                     pos,
                     gear,
                     tile,
                     cost,
                     estimate,
                 }
             }
         }
     
         impl PartialOrd for PathfindNode {
             fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
                 let a = other.cost + other.estimate;
                 let b = self.cost + self.estimate;
                 a.partial_cmp(&b)
             }
         }
     
         impl Ord for PathfindNode {
             fn cmp(&self, other: &Self) -> Ordering {
                 self.partial_cmp(other).unwrap()
             }
         }
     
         struct Tile {
             _geo_index: u32,
             erosion_level: u32,
             tile_type: TileType,
         }
     
         struct Map {
             tiles: HashMap<Pos, Tile>,
             cave_depth: u32,
             magic_modulo: u32,
         }
     
         impl Map {
             pub fn get_tile(&mut self, pos: Pos) -> &mut Tile {
                 if !self.tiles.contains_key(&pos) {
                     let row = pos.row;
                     let col = pos.col;
     
                     // Compute geological index
                     let geo_index = if col == 0 {
                         row as u32 * 48271
                     } else if row == 0 {
                         col as u32 * 16807
                     } else {
                         let p0 = pos + (-1, 0);
                         let p1 = pos + (0, -1);
     
                         // Recursively query erosion level of previos nodes
                         self.get_tile(p0).erosion_level * self.get_tile(p1).erosion_level
                     };
     
                     let erosion_level = (geo_index + self.cave_depth) % self.magic_modulo;
     
                     let tile_type = match erosion_level % 3 {
                         0 => TileType::Rocky,
                         1 => TileType::Wet,
                         2 => TileType::Narrow,
                         _ => panic!("Impossible to reach code"),
                     };
     
                     let new_tile = Tile {
                         _geo_index: geo_index,
                         erosion_level,
                         tile_type,
                     };
     
                     self.tiles.insert(pos, new_tile);
                 }
     
                 self.tiles.get_mut(&pos).unwrap()
             }
     
             #[allow(dead_code)]
             pub fn print(&self) {
                 let mut width = 0;
                 let mut height = 0;
     
                 for pos in self.tiles.keys() {
                     width = std::cmp::max(width, pos.col);
                     height = std::cmp::max(height, pos.row);
                 }
     
                 for row in 0..=height {
                     let mut row_string = String::with_capacity(width as usize);
                     for col in 0..=width {
                         let pos = Pos { row, col };
                         let c = match self.tiles.get(&pos) {
                             Some(tile) => match tile.tile_type {
                                 TileType::Mouth => 'M',
                                 TileType::Rocky => '.',
                                 TileType::Wet => '=',
                                 TileType::Narrow => '|',
                                 TileType::Target => 'T',
                             },
                             None => '?',
                         };
                         row_string.push(c);
                     }
                     println!("{}", row_string);
                 }
             }
         }
     
         pub fn run() -> String {
             use crate::day22::Gear::*;
             use crate::day22::TileType::*;
     
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day22.txt").unwrap();
             }
     
             lazy_static! {
                 static ref re: Regex =
                     Regex::new(r"(?m)depth: (?P<depth>\d+)\s*target: (?P<x>\d+),(?P<y>\d+)").unwrap();
             }
     
             let caps = re.captures(&file_string).unwrap();
             let cave_depth: u32 = caps["depth"].parse().unwrap();
             let target_col: usize = caps["x"].parse().unwrap();
             let target_row: usize = caps["y"].parse().unwrap();
     
             let magic_modulo = 20183;
     
             let mouth_pos = Pos::new(0, 0);
             let target_pos = Pos::new(target_row as i16, target_col as i16);
     
             // Create map
             let mut map = Map {
                 tiles: HashMap::default(),
                 cave_depth,
                 magic_modulo,
             };
     
             // Manual enter tiles for cave mouth and target
             map.tiles.insert(
                 mouth_pos,
                 Tile {
                     _geo_index: 0,
                     erosion_level: cave_depth % magic_modulo,
                     tile_type: TileType::Mouth,
                 },
             );
     
             map.tiles.insert(
                 target_pos,
                 Tile {
                     _geo_index: 0,
                     erosion_level: cave_depth % magic_modulo,
                     tile_type: TileType::Target,
                 },
             );
     
             // Calculate risk
             let mut risk: u32 = 0;
             for row in 0..=target_row {
                 for col in 0..=target_col {
                     let tile = map.get_tile(Pos::new(row as i16, col as i16));
                     risk += match tile.tile_type {
                         TileType::Wet => 1,
                         TileType::Narrow => 2,
                         _ => 0,
                     };
                 }
             }
     
             writeln!(&mut result, "Day 22, Problem 1 - [{}]", risk).unwrap(); // 10603
     
             // Part 2
             let gear_change_penalty = 7;
     
             let starting_pos = Pos { row: 0, col: 0 };
             let target_gear = Gear::Torch;
             let offsets = [(-1, 0), (1, 0), (0, 1), (0, -1)];
     
             let mut nodes: BinaryHeap<PathfindNode> = BinaryHeap::new();
             nodes.push(PathfindNode::new(
                 starting_pos,
                 Gear::Torch,
                 TileType::Mouth,
                 0,
                 (target_row + target_col) as u16,
             ));
     
             let mut visited: HashSet<(Pos, Gear)> = HashSet::default();
             let mut best_cost: HashMap<(Pos, Gear), u16> = HashMap::default();
     
             // There has got to be a better way to initialize this
     
             let gear_intersections = [
                 [Climbing, Torch, Neither].iter(), // 0 mouth
                 [Climbing, Torch].iter(),          // 1 rocky
                 [Climbing, Neither].iter(),        // 2 wet
                 [Torch, Neither].iter(),           // 3 narrow
                 [Climbing].iter(),                 // 4 rocky + wet
                 [Torch].iter(),                    // 5 rocky + narrow
                 [Neither].iter(),                  // 6 wet + narrow
             ];
     
             const ROCKY: usize = 1;
             const WET: usize = 2;
             const NARROW: usize = 3;
             const ROCKY_WET: usize = 4;
             const ROCKY_NARROW: usize = 5;
             const WET_NARROW: usize = 6;
     
             // Precompute allowable gear
             let get_allowable_gear = |a: TileType, b: TileType| match a {
                 Mouth => match b {
                     Rocky | Target | Mouth => gear_intersections[ROCKY].clone(),
                     Wet => gear_intersections[WET].clone(),
                     Narrow => gear_intersections[NARROW].clone(),
                 },
                 Rocky | Target => match b {
                     Rocky | Target | Mouth => gear_intersections[ROCKY].clone(),
                     Wet => gear_intersections[ROCKY_WET].clone(),
                     Narrow => gear_intersections[ROCKY_NARROW].clone(),
                 },
                 Wet => match b {
                     Rocky | Target => gear_intersections[ROCKY_WET].clone(),
                     Wet | Mouth => gear_intersections[WET].clone(),
                     Narrow => gear_intersections[WET_NARROW].clone(),
                 },
                 Narrow => match b {
                     Rocky | Target => gear_intersections[ROCKY_NARROW].clone(),
                     Wet => gear_intersections[WET_NARROW].clone(),
                     Narrow | Mouth => gear_intersections[NARROW].clone(),
                 },
             };
     
             // Perform A* search
             let fastest_time = loop {
                 let node = nodes.pop().unwrap();
     
                 let inserted = visited.insert((node.pos, node.gear));
                 if !inserted {
                     continue;
                 }
     
                 if node.tile == TileType::Target {
                     break node.cost;
                 }
     
                 let neighbors = offsets
                     .iter()
                     .map(|offset| node.pos + *offset)
                     .filter(|p| p.row >= 0 && p.col >= 0);
     
                 for neighbor_pos in neighbors {
                     let neighbor_tile = map.get_tile(neighbor_pos);
                     let neighbor_type = neighbor_tile.tile_type;
     
                     let allowable_gear = get_allowable_gear(node.tile, neighbor_type);
     
                     for travel_gear in allowable_gear {
                         let dest = (neighbor_pos, *travel_gear);
     
                         // Skip if we've already visited this node with this gear
                         if visited.contains(&dest) {
                             continue;
                         }
     
                         // Calculate new cost
                         let mut new_cost = node.cost + 1;
                         if *travel_gear != node.gear {
                             new_cost += gear_change_penalty;
                         }
     
                         if neighbor_type == TileType::Target && *travel_gear != target_gear {
                             new_cost += gear_change_penalty;
                         }
     
                         // Skip if this cost is more expensive than a pre-existing cost
                         if let Some(cost) = best_cost.get(&dest) {
                             if *cost < new_cost {
                                 continue;
                             }
                         }
                         best_cost.insert(dest, new_cost);
     
                         // Estimate distance to target (very conservative!)
                         let estimate = ((target_row as i16 - neighbor_pos.row).abs()
                             + (target_col as i16 - neighbor_pos.col).abs())
                             as u16;
     
                         nodes.push(PathfindNode::new(
                             neighbor_pos,
                             *travel_gear,
                             neighbor_tile.tile_type,
                             new_cost,
                             estimate,
                         ));
                     }
                 }
             };
     
             writeln!(&mut result, "Day 22, Problem 2 - [{}]", fastest_time).unwrap(); // 952
     
             result
         } // fn run()
     } // mod day22
     
     // Input: 3d position with radius; pos=<19753426,69715835,25404341>, r=74542975
     //
     // Problem 1: How many nanobots overlap nanobot with largest radius?
     // Solution 1: Identify largest bot. Perform overlap test with other bots.
     //
     // Problem 2: What coordinate is in range of largest number of nanobots?
     // Solution 2: Octree. The trick is the traversal is neither BFS nor DFS. Instead
     //             You're searching the node with highest potential.
     pub mod day23 {
         use derive_more::*;
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::cell::RefCell;
         use std::fmt::Write;
         use std::rc::Rc;
     
         #[derive(Copy, Clone, Debug, Eq, PartialEq, Add, Sub)]
         struct Pos {
             x: i32,
             y: i32,
             z: i32,
         }
     
         impl Pos {
             fn new(x: i32, y: i32, z: i32) -> Pos {
                 Pos { x, y, z }
             }
     
             fn with_value(v: i32) -> Pos {
                 Pos::new(v, v, v)
             }
     
             fn min(&self, p: Pos) -> Pos {
                 Pos {
                     x: std::cmp::min(self.x, p.x),
                     y: std::cmp::min(self.y, p.y),
                     z: std::cmp::min(self.z, p.z),
                 }
             }
     
             fn max(&self, p: Pos) -> Pos {
                 Pos {
                     x: std::cmp::max(self.x, p.x),
                     y: std::cmp::max(self.y, p.y),
                     z: std::cmp::max(self.z, p.z),
                 }
             }
         }
     
         impl std::ops::Index<usize> for Pos {
             type Output = i32;
     
             fn index(&self, i: usize) -> &i32 {
                 match i {
                     0 => &self.x,
                     1 => &self.y,
                     2 => &self.z,
                     _ => panic!("Invalid index"),
                 }
             }
         }
     
         impl std::ops::Add<(i32, i32, i32)> for Pos {
             type Output = Pos;
     
             fn add(self, other: (i32, i32, i32)) -> Pos {
                 Pos::new(self.x + other.0, self.y + other.1, self.z + other.2)
             }
         }
     
         impl std::ops::IndexMut<usize> for Pos {
             fn index_mut(&mut self, i: usize) -> &mut i32 {
                 match i {
                     0 => &mut self.x,
                     1 => &mut self.y,
                     2 => &mut self.z,
                     _ => panic!("Invalid index"),
                 }
             }
         }
     
         // min/max are both INCLUSIVE
         #[derive(Copy, Clone, Debug)]
         struct AABB {
             min: Pos,
             max: Pos,
         }
     
         impl AABB {
             fn new() -> AABB {
                 let min = Pos::with_value(std::i32::MAX);
                 let max = Pos::with_value(std::i32::MIN);
                 AABB { min, max }
             }
     
             fn with_corners(min: Pos, max: Pos) -> AABB {
                 AABB { min, max }
             }
     
             fn add_point(&mut self, pos: Pos) {
                 self.min = self.min.min(pos);
                 self.max = self.max.max(pos);
             }
     
             fn add_sphere(&mut self, center: Pos, radius: i32) {
                 self.add_point(center - Pos::with_value(radius));
                 self.add_point(center + Pos::with_value(radius));
             }
     
             fn overlaps(&self, bot: &Nanobot) -> bool {
                 let dist = self.distance_from(bot.pos);
                 dist <= bot.radius
             }
     
             fn num_overlaps(&self, bots: &[Nanobot]) -> usize {
                 bots.iter().filter(|bot| self.overlaps(bot)).count()
             }
     
             fn distance_from(&self, point: Pos) -> i32 {
                 let mut closest: Pos = point;
     
                 for i in 0..3 {
                     if closest[i] > self.max[i] {
                         closest[i] = self.max[i];
                     } else if closest[i] < self.min[i] {
                         closest[i] = self.min[i];
                     }
                 }
     
                 // Manhattan distance
                 let diff = point - closest;
                 diff.x.abs() + diff.y.abs() + diff.z.abs()
             }
     
             fn width(&self) -> i32 {
                 self.max.x - self.min.x + 1
             }
     
             fn height(&self) -> i32 {
                 self.max.y - self.min.y + 1
             }
     
             fn depth(&self) -> i32 {
                 self.max.z - self.min.z + 1
             }
     
             fn volume(&self) -> usize {
                 let w = self.width() as usize;
                 let h = self.height() as usize;
                 let d = self.depth() as usize;
     
                 w.saturating_mul(h).saturating_mul(d)
             }
     
             #[allow(dead_code)]
             fn contains(&self, pt: Pos) -> bool {
                 pt.x >= self.min.x
                     && pt.x <= self.max.x
                     && pt.y >= self.min.y
                     && pt.y <= self.max.y
                     && pt.z >= self.min.z
                     && pt.z <= self.max.z
             }
         }
     
         impl Pos {
             pub fn distance_between(&self, other: Pos) -> i32 {
                 (self.x - other.x).abs() + (self.y - other.y).abs() + (self.z - other.z).abs()
             }
         }
     
         #[derive(Copy, Clone, Debug)]
         struct Nanobot {
             pos: Pos,
             radius: i32,
         }
     
         #[derive(Debug)]
         struct OctreeNode {
             level: u8,
             max_possible: usize,
             bounds: AABB,
             children: Vec<Rc<RefCell<OctreeNode>>>,
         }
     
         impl OctreeNode {
             fn subdivide(&mut self, tree: &mut Octree, bots: &[Nanobot]) {
                 let min = self.bounds.min;
     
                 let w = self.bounds.width() - 1;
                 let h = self.bounds.height() - 1;
                 let d = self.bounds.depth() - 1;
     
                 let hw = w / 2;
                 let hh = w / 2;
                 let hd = w / 2;
     
                 let new_bounds = [
                     AABB::with_corners(min + (0, 0, 0), min + (hw, hh, hd)),
                     AABB::with_corners(min + (0, 0, hd + 1), min + (hw, hh, d)),
                     AABB::with_corners(min + (hw + 1, 0, 0), min + (w, hh, hd)),
                     AABB::with_corners(min + (hw + 1, 0, hd + 1), min + (w, hh, d)),
                     AABB::with_corners(min + (0, hh + 1, 0), min + (hw, h, hd)),
                     AABB::with_corners(min + (0, hh + 1, hd + 1), min + (hw, h, d)),
                     AABB::with_corners(min + (hw + 1, hh + 1, 0), min + (w, h, hd)),
                     AABB::with_corners(min + (hw + 1, hh + 1, hd + 1), min + (w, h, d)),
                 ];
     
                 for bounds in new_bounds.iter() {
                     let new_node = Rc::new(RefCell::new(OctreeNode {
                         level: self.level + 1,
                         max_possible: bounds.num_overlaps(bots),
                         bounds: *bounds,
                         children: Vec::new(),
                     }));
     
                     self.children.push(new_node.clone());
                     tree.leaves.push(new_node);
                 }
             }
         }
     
         struct Octree {
             _root: Rc<RefCell<OctreeNode>>,
             leaves: Vec<Rc<RefCell<OctreeNode>>>,
         }
     
         impl Octree {
             fn new(bots: &[Nanobot]) -> Octree {
                 let bounds = bots.iter().fold(AABB::new(), |aabb, bot| {
                     let mut result = aabb;
                     result.add_sphere(bot.pos, bot.radius);
                     result
                 });
     
                 let root = Rc::new(RefCell::new(OctreeNode {
                     level: 0,
                     max_possible: bots.len(),
                     bounds,
                     children: Vec::new(),
                 }));
     
                 Octree {
                     _root: root.clone(),
                     leaves: vec![root],
                 }
             }
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             // Parse input
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day23.txt").unwrap();
             }
     
             lazy_static! {
                 static ref re: Regex =
                     Regex::new(r"pos=<(?P<x>-*\d+),(?P<y>-*\d+),(?P<z>-*\d+)>, r=(?P<r>\d+)").unwrap();
             }
     
             let mut nanobots: Vec<Nanobot> = Vec::new();
     
             // Parse input
             for line in file_string.lines() {
                 let caps = re.captures(&line).unwrap();
                 let x: i32 = caps["x"].parse().unwrap();
                 let y: i32 = caps["y"].parse().unwrap();
                 let z: i32 = caps["z"].parse().unwrap();
                 let r: i32 = caps["r"].parse().unwrap();
     
                 nanobots.push(Nanobot {
                     pos: Pos { x, y, z },
                     radius: r,
                 });
             }
     
             // Find strongest bot
             let mut strongest_bot_index = 0;
             let mut strongest_bot_radius = 0;
             for (i, bot) in nanobots.iter().enumerate() {
                 if bot.radius > strongest_bot_radius {
                     strongest_bot_index = i;
                     strongest_bot_radius = bot.radius;
                 }
             }
     
             // Find bots in range
             let strongest_pos = nanobots[strongest_bot_index].pos;
             let in_range: usize = nanobots
                 .iter()
                 .map(|bot| strongest_pos.distance_between(bot.pos))
                 .filter(|dist| *dist <= strongest_bot_radius)
                 .count();
     
             writeln!(&mut result, "Day 23, Problem 1 - [{}]", in_range).unwrap(); // 410
     
             // Day 2
             let mut octree = Octree::new(&nanobots);
             let origin = Pos::new(0, 0, 0);
             let mut best_leaf: Option<Rc<RefCell<OctreeNode>>> = None;
     
             while !octree.leaves.is_empty() {
                 assert!(!octree.leaves.is_empty());
     
                 // Get next leaf
                 let leaf = octree.leaves.pop().unwrap();
                 let mut inner_leaf = leaf.borrow_mut();
                 assert!(inner_leaf.children.is_empty());
     
                 match best_leaf.clone() {
                     Some(old_best) => {
                         let old_best = old_best.borrow();
                         if inner_leaf.max_possible < old_best.max_possible {
                             continue;
                         }
     
                         if inner_leaf.bounds.min == inner_leaf.bounds.max {
                             if inner_leaf.max_possible > old_best.max_possible
                                 || inner_leaf.bounds.distance_from(origin)
                                     < old_best.bounds.distance_from(origin)
                             {
                                 // New best!
                                 best_leaf = Some(leaf.clone());
                             }
                             continue;
                         }
                     }
                     None => {
                         // Found our first candidate!
                         if inner_leaf.bounds.min == inner_leaf.bounds.max {
                             best_leaf = Some(leaf.clone());
                             continue;
                         }
                     }
                 };
     
                 if inner_leaf.max_possible > 1 {
                     assert!(inner_leaf.bounds.min != inner_leaf.bounds.max);
                     inner_leaf.subdivide(&mut octree, &nanobots);
     
                     // This could be faster
                     octree.leaves.sort_by(|a, b| {
                         let a = a.borrow();
                         let b = b.borrow();
     
                         // Sort by leaf with max possible overlaps
                         if a.max_possible != b.max_possible {
                             // Put leaves with larger max_possible values at the end
                             a.max_possible.cmp(&b.max_possible)
                         } else {
                             let av = a.bounds.volume();
                             let bv = b.bounds.volume();
     
                             if av != bv {
                                 // Put larger volumes at the end
                                 av.cmp(&bv)
                             } else {
                                 // Put volumes closer to the origin at the end
                                 let dist_a = a.bounds.distance_from(origin);
                                 let dist_b = b.bounds.distance_from(origin);
                                 dist_b.cmp(&dist_a)
                             }
                         }
                     });
                 }
             }
     
             let temp = best_leaf.unwrap();
             let best_leaf = temp.borrow();
             assert!(best_leaf.bounds.min == best_leaf.bounds.max); // down to a single point
     
             let pt = best_leaf.bounds.min;
             let dist = pt.x.abs() + pt.y.abs() + pt.z.abs();
             writeln!(&mut result, "Day 23, Problem 2 - [{}]", dist).unwrap(); //Count: [980] Pos: [(59182453, 49186430, 10819933)]  Dist: [119188816]
     
             result
         } // fn run
     } // mod day23
     
     // Input: Manually transcribed
     //
     // Problem 1: Given a bunch of complicated rules, who wins?
     // Solution 1: Implement arbitrary rules. Run to completion.
     //
     // Problem 2: What is smallest boost needed for other team to win?
     // Solution 2: Re-run simulating increment boost until other team wins.
     pub mod day24 {
         use std::fmt::Write;
     
         #[derive(Copy, Clone, Eq, Hash, PartialEq)]
         enum AttackType {
             Bludgeoning,
             Cold,
             Fire,
             Radiation,
             Slashing,
         }
     
         #[derive(Clone, Eq, PartialEq)]
         struct Group {
             immune_team: bool,
             num_units: u32,
             unit_health: u32,
             attack_damage: u32,
             attack_type: AttackType,
             initiative: u32,
             immune_to: Vec<AttackType>,
             weak_to: Vec<AttackType>,
         }
     
         impl Group {
             fn effective_power(&self, boost: u32) -> u32 {
                 // Only immune team gets to use the boost
                 match self.immune_team {
                     true => self.num_units * (self.attack_damage + boost),
                     false => self.num_units * self.attack_damage,
                 }
             }
     
             fn damage_against(&self, target: &Group, boost: u32) -> u32 {
                 match target.immune_to(self.attack_type) {
                     true => 0,
                     false => match target.weak_to(self.attack_type) {
                         true => self.effective_power(boost) * 2,
                         false => self.effective_power(boost),
                     },
                 }
             }
     
             #[inline]
             fn immune_to(&self, t: AttackType) -> bool {
                 for v in &self.immune_to {
                     if *v == t {
                         return true;
                     }
                 }
                 false
             }
     
             #[inline]
             fn weak_to(&self, t: AttackType) -> bool {
                 for v in &self.weak_to {
                     if *v == t {
                         return true;
                     }
                 }
                 false
             }
         }
     
         pub fn run() -> String {
             let mut result = String::with_capacity(128);
     
             let initial_groups = input_data();
     
             let mut indices: Vec<usize> = (0..initial_groups.len()).collect();
             let mut targeted = vec![false; indices.len()];
             let mut targets: Vec<Option<usize>> = vec![None; indices.len()];
     
             let mut boost = 0;
             loop {
                 let mut groups = initial_groups.clone();
                 let living_groups = loop {
                     // Sort indices by effective power; initiative breaks ties
                     indices.sort_by(|a: &usize, b: &usize| {
                         let a = &groups[*a];
                         let b = &groups[*b];
                         let ep_a = a.effective_power(boost);
                         let ep_b = b.effective_power(boost);
     
                         if ep_a != ep_b {
                             ep_b.cmp(&ep_a)
                         } else {
                             b.initiative.cmp(&a.initiative)
                         }
                     });
     
                     for i in 0..indices.len() {
                         targeted[i] = false;
                         targets[i] = None;
                     }
     
                     // Determine target for each attacker
                     indices
                         .iter()
                         .filter(|i| groups[**i].num_units > 0) // skip groups that are dead
                         .for_each(|attacker_idx| {
                             let attacker_group = &groups[*attacker_idx];
                             let target_idx = groups
                                 .iter()
                                 .enumerate()
                                 .filter(|(idx, group)| {
                                     group.num_units > 0 // ignore dead teams
                                     && group.immune_team != attacker_group.immune_team // ignore teams on the same team
                                     && !targeted[*idx] // ignore teams already targeted
                                 })
                                 .max_by(|(_, a), (_, b)| {
                                     // Find group to which attacker deals the most damage
                                     // Initiative breaks ties
                                     let dmg_a = attacker_group.damage_against(&a, boost);
                                     let dmg_b = attacker_group.damage_against(&b, boost);
                                     if dmg_a != dmg_b {
                                         dmg_a.cmp(&dmg_b)
                                     } else {
                                         let pow_a = a.effective_power(boost);
                                         let pow_b = b.effective_power(boost);
     
                                         if pow_a != pow_b {
                                             pow_a.cmp(&pow_b)
                                         } else {
                                             a.initiative.cmp(&b.initiative)
                                         }
                                     }
                                 })
                                 .map(|(idx, _)| idx);
     
                             // Store target
                             if let Some(target_idx) = target_idx {
                                 let a = &groups[*attacker_idx];
                                 let b = &groups[target_idx];
     
                                 if a.damage_against(&b, boost) > 0 {
                                     targets[*attacker_idx] = Some(target_idx);
                                     targeted[target_idx] = true;
                                 }
                             }
                         });
     
                     // Sort indices by initiative;
                     indices.sort_by(|a: &usize, b: &usize| {
                         groups[*b].initiative.cmp(&groups[*a].initiative)
                     });
     
                     // Perform attacks
                     let mut units_killed = 0;
                     indices.iter().for_each(|attacker_idx| {
                         let attacker_group = &groups[*attacker_idx];
     
                         // Attacker may have been killed earlier in this round
                         if attacker_group.num_units > 0 {
                             if let Some(target_idx) = targets[*attacker_idx] {
                                 let target_group = &groups[target_idx];
                                 let damage = attacker_group.damage_against(&target_group, boost); // damage to deal (handles AttackType)
                                 let units_lost = damage / target_group.unit_health; // integer number of units lost
     
                                 let new_units = target_group.num_units.saturating_sub(units_lost); // don't underflow
                                 let units_lost = target_group.num_units - new_units;
     
                                 groups[target_idx].num_units = new_units; // update units count
     
                                 units_killed += units_lost; // tally total units killed to detect draws
                             }
                         }
                     });
     
                     // Calculate number of living groups
                     let living_groups = groups
                         .iter()
                         .fold((0, 0), |accum, g| match g.num_units > 0 {
                             true => match g.immune_team {
                                 true => (accum.0 + 1, accum.1),
                                 false => (accum.0, accum.1 + 1),
                             },
                             false => accum,
                         });
     
                     // Game over if one team dead OR no units killed (draw)
                     if living_groups.0 == 0 || living_groups.1 == 0 || units_killed == 0 {
                         break living_groups;
                     }
                 };
     
                 // Check for draw
                 if living_groups.0 == 0 || living_groups.1 == 0 {
                     let winner_units: u32 = groups.iter().map(|g| g.num_units).sum();
     
                     if boost == 0 {
                         writeln!(&mut result, "Day 24, Problem 1 - [{}]", winner_units).unwrap(); // 23701
                     } else if living_groups.0 > 0 {
                         // Immune (finally) team wins!
                         writeln!(&mut result, "Day 24, Problem 2 - [{}]", winner_units).unwrap(); // 779
                         break;
                     }
                 }
     
                 boost += 1;
             } // simulation loop
     
             result
         } // fn run
     
         #[allow(dead_code)]
         fn test_data() -> Vec<Group> {
             vec![
                 Group {
                     immune_team: true,
                     num_units: 17,
                     unit_health: 5390,
                     attack_damage: 4507,
                     attack_type: AttackType::Fire,
                     initiative: 2,
                     immune_to: vec![],
                     weak_to: vec![AttackType::Radiation, AttackType::Bludgeoning],
                 },
                 Group {
                     immune_team: true,
                     num_units: 989,
                     unit_health: 1274,
                     attack_damage: 25,
                     attack_type: AttackType::Slashing,
                     initiative: 3,
                     immune_to: vec![AttackType::Fire],
                     weak_to: vec![AttackType::Bludgeoning, AttackType::Slashing],
                 },
                 Group {
                     immune_team: false,
                     num_units: 801,
                     unit_health: 4706,
                     attack_damage: 116,
                     attack_type: AttackType::Bludgeoning,
                     initiative: 1,
                     immune_to: vec![],
                     weak_to: vec![AttackType::Radiation],
                 },
                 Group {
                     immune_team: false,
                     num_units: 4485,
                     unit_health: 2961,
                     attack_damage: 12,
                     attack_type: AttackType::Slashing,
                     initiative: 4,
                     immune_to: vec![AttackType::Radiation],
                     weak_to: vec![AttackType::Fire, AttackType::Cold],
                 },
             ]
         }
     
         #[allow(dead_code)]
         fn input_data() -> Vec<Group> {
             vec![
                 Group {
                     immune_team: true,
                     num_units: 1614,
                     unit_health: 8016,
                     immune_to: vec![AttackType::Slashing],
                     weak_to: vec![AttackType::Radiation],
                     attack_damage: 48,
                     attack_type: AttackType::Fire,
                     initiative: 9,
                 },
                 Group {
                     immune_team: true,
                     num_units: 3730,
                     unit_health: 5611,
                     immune_to: vec![AttackType::Bludgeoning],
                     weak_to: vec![AttackType::Fire],
                     attack_damage: 14,
                     attack_type: AttackType::Radiation,
                     initiative: 16,
                 },
                 Group {
                     immune_team: true,
                     num_units: 1627,
                     unit_health: 9770,
                     immune_to: vec![],
                     weak_to: vec![AttackType::Cold],
                     attack_damage: 55,
                     attack_type: AttackType::Fire,
                     initiative: 3,
                 },
                 Group {
                     immune_team: true,
                     num_units: 4665,
                     unit_health: 9782,
                     immune_to: vec![],
                     weak_to: vec![AttackType::Fire],
                     attack_damage: 18,
                     attack_type: AttackType::Radiation,
                     initiative: 10,
                 },
                 Group {
                     immune_team: true,
                     num_units: 281,
                     unit_health: 5764,
                     immune_to: vec![AttackType::Fire],
                     weak_to: vec![AttackType::Radiation],
                     attack_damage: 187,
                     attack_type: AttackType::Slashing,
                     initiative: 19,
                 },
                 Group {
                     immune_team: true,
                     num_units: 524,
                     unit_health: 9344,
                     immune_to: vec![],
                     weak_to: vec![],
                     attack_damage: 158,
                     attack_type: AttackType::Cold,
                     initiative: 15,
                 },
                 Group {
                     immune_team: true,
                     num_units: 5013,
                     unit_health: 9768,
                     immune_to: vec![],
                     weak_to: vec![],
                     attack_damage: 15,
                     attack_type: AttackType::Cold,
                     initiative: 14,
                 },
                 Group {
                     immune_team: true,
                     num_units: 1143,
                     unit_health: 1822,
                     immune_to: vec![],
                     weak_to: vec![AttackType::Radiation],
                     attack_damage: 15,
                     attack_type: AttackType::Fire,
                     initiative: 18,
                 },
                 Group {
                     immune_team: true,
                     num_units: 136,
                     unit_health: 6830,
                     immune_to: vec![],
                     weak_to: vec![AttackType::Radiation],
                     attack_damage: 420,
                     attack_type: AttackType::Slashing,
                     initiative: 7,
                 },
                 Group {
                     immune_team: true,
                     num_units: 665,
                     unit_health: 7973,
                     immune_to: vec![AttackType::Slashing],
                     weak_to: vec![AttackType::Bludgeoning],
                     attack_damage: 119,
                     attack_type: AttackType::Fire,
                     initiative: 11,
                 },
                 Group {
                     immune_team: false,
                     num_units: 515,
                     unit_health: 8712,
                     immune_to: vec![AttackType::Radiation],
                     weak_to: vec![AttackType::Slashing, AttackType::Fire],
                     attack_damage: 30,
                     attack_type: AttackType::Cold,
                     initiative: 1,
                 },
                 Group {
                     immune_team: false,
                     num_units: 5542,
                     unit_health: 56769,
                     immune_to: vec![],
                     weak_to: vec![],
                     attack_damage: 16,
                     attack_type: AttackType::Bludgeoning,
                     initiative: 4,
                 },
                 Group {
                     immune_team: false,
                     num_units: 1663,
                     unit_health: 10437,
                     immune_to: vec![
                         AttackType::Slashing,
                         AttackType::Fire,
                         AttackType::Radiation,
                     ],
                     weak_to: vec![],
                     attack_damage: 12,
                     attack_type: AttackType::Radiation,
                     initiative: 12,
                 },
                 Group {
                     immune_team: false,
                     num_units: 574,
                     unit_health: 50124,
                     immune_to: vec![],
                     weak_to: vec![AttackType::Slashing, AttackType::Radiation],
                     attack_damage: 171,
                     attack_type: AttackType::Fire,
                     initiative: 8,
                 },
                 Group {
                     immune_team: false,
                     num_units: 1190,
                     unit_health: 10652,
                     immune_to: vec![],
                     weak_to: vec![],
                     attack_damage: 16,
                     attack_type: AttackType::Cold,
                     initiative: 17,
                 },
                 Group {
                     immune_team: false,
                     num_units: 3446,
                     unit_health: 23450,
                     immune_to: vec![],
                     weak_to: vec![],
                     attack_damage: 12,
                     attack_type: AttackType::Fire,
                     initiative: 5,
                 },
                 Group {
                     immune_team: false,
                     num_units: 5887,
                     unit_health: 14556,
                     immune_to: vec![],
                     weak_to: vec![AttackType::Slashing],
                     attack_damage: 4,
                     attack_type: AttackType::Radiation,
                     initiative: 2,
                 },
                 Group {
                     immune_team: false,
                     num_units: 1761,
                     unit_health: 41839,
                     immune_to: vec![],
                     weak_to: vec![AttackType::Cold],
                     attack_damage: 35,
                     attack_type: AttackType::Cold,
                     initiative: 20,
                 },
                 Group {
                     immune_team: false,
                     num_units: 4194,
                     unit_health: 16090,
                     immune_to: vec![AttackType::Fire],
                     weak_to: vec![AttackType::Slashing],
                     attack_damage: 6,
                     attack_type: AttackType::Fire,
                     initiative: 6,
                 },
                 Group {
                     immune_team: false,
                     num_units: 2127,
                     unit_health: 27065,
                     immune_to: vec![],
                     weak_to: vec![AttackType::Cold, AttackType::Slashing],
                     attack_damage: 24,
                     attack_type: AttackType::Slashing,
                     initiative: 13,
                 },
             ]
         } // fn input_data()
     } // mod day24
     
     // Input: 4D positions; -3,-4,0,-6
     //
     // Problem 1: Close points form a constellation. How many constellations are there?
     // Solution 1: Naively test points for closeness and merge until no more constellations merge.
     //
     // Problem 2: There is no problem 2. Solve all 49 problems to earn 50th and final star.
     pub mod day25 {
         use derive_more::*;
         use itertools::iproduct;
         use lazy_static::lazy_static;
         use regex::Regex;
         use std::cmp::{max, min};
         use std::fmt::Write;
     
         #[derive(Copy, Clone, Add, Sub)]
         struct Pos {
             x: i32,
             y: i32,
             z: i32,
             w: i32,
         }
     
         impl Pos {
             fn new(x: i32, y: i32, z: i32, w: i32) -> Pos {
                 Pos { x, y, z, w }
             }
     
             #[inline]
             fn distance_to(&self, pt: Pos) -> i32 {
                 (pt.x - self.x).abs()
                     + (pt.y - self.y).abs()
                     + (pt.z - self.z).abs()
                     + (pt.w - self.w).abs()
             }
         }
     
         impl std::ops::Index<usize> for Pos {
             type Output = i32;
     
             fn index(&self, i: usize) -> &i32 {
                 match i {
                     0 => &self.x,
                     1 => &self.y,
                     2 => &self.z,
                     3 => &self.w,
                     _ => panic!("Invalid index"),
                 }
             }
         }
     
         struct AABB {
             min: Pos,
             max: Pos,
         }
     
         impl AABB {
             fn from_pos(pos: Pos) -> AABB {
                 AABB { min: pos, max: pos }
             }
     
             fn merged(&self, other: &AABB) -> AABB {
                 AABB {
                     min: Pos::new(
                         min(self.min.x, other.min.x),
                         min(self.min.y, other.min.y),
                         min(self.min.z, other.min.z),
                         min(self.min.w, other.min.w),
                     ),
                     max: Pos::new(
                         max(self.max.x, other.max.x),
                         max(self.max.y, other.max.y),
                         max(self.max.z, other.max.z),
                         max(self.max.w, other.max.w),
                     ),
                 }
             }
     
             #[inline]
             fn overlaps(&self, other: &AABB, buffer: i32) -> bool {
                 let b = buffer;
                 for i in 0..4 {
                     if self.max[i] + b < other.min[i] - b || self.min[i] - b > other.max[i] + b {
                         return false;
                     }
                 }
     
                 true
             }
         }
     
         struct Constellation {
             bounds: AABB,
             points: Vec<Pos>,
         }
     
         impl Constellation {
             fn with_point(pt: Pos) -> Constellation {
                 Constellation {
                     bounds: AABB::from_pos(pt),
                     points: vec![pt],
                 }
             }
     
             fn mergeable(&self, other: &Constellation, dist: i32) -> bool {
                 if !self.bounds.overlaps(&other.bounds, dist) {
                     return false;
                 }
     
                 iproduct!(self.points.iter(), other.points.iter())
                     .map(|(a, b)| a.distance_to(*b))
                     .any(|d| d <= dist)
             }
     
             fn merge(&mut self, other: &mut Constellation) {
                 self.bounds = self.bounds.merged(&other.bounds);
                 self.points.append(&mut other.points);
             }
         }
     
         pub fn run() -> String {
             lazy_static! {
                 static ref file_string: String =
                     std::fs::read_to_string("data/input_day25.txt").unwrap();
             }
     
             lazy_static! {
                 static ref re: Regex =
                     Regex::new(r"(?P<x>-*\d+),(?P<y>-*\d+),(?P<z>-*\d+),(?P<w>-*\d+)").unwrap();
             }
     
             let mut result = String::with_capacity(128);
     
             let merge_dist = 3;
     
             // Parse input
             let mut constellations: Vec<Constellation> = Vec::new();
             for line in file_string.lines() {
                 let caps = re.captures(&line).unwrap();
                 let x: i32 = caps["x"].parse().unwrap();
                 let y: i32 = caps["y"].parse().unwrap();
                 let z: i32 = caps["z"].parse().unwrap();
                 let w: i32 = caps["w"].parse().unwrap();
                 constellations.push(Constellation::with_point(Pos { x, y, z, w }));
             }
     
             loop {
                 let mut i = 0;
                 let mut merged_any = false;
     
                 while i < constellations.len() {
                     let mut j = i + 1;
                     let merged = loop {
                         if j >= constellations.len() {
                             break false;
                         }
     
                         if constellations[i].mergeable(&constellations[j], merge_dist) {
                             let mut other = constellations.swap_remove(j);
                             constellations[i].merge(&mut other);
                             break true;
                         }
                         j += 1;
                     };
     
                     match merged {
                         true => merged_any = true,
                         false => i += 1,
                     }
                 }
     
                 if !merged_any {
                     break;
                 }
             }
     
             writeln!(
                 &mut result,
                 "Day 25, Problem 1 - [{}]",
                 constellations.len()
             )
             .unwrap(); // 377
             result
         } // fn run()
     } // mod day25