Compare commits

...

20 Commits

Author SHA1 Message Date
7a8503c3fe Use auto instead of apply. 2021-01-02 21:22:07 -08:00
2b69cbd391 Rewrite using induction on list length. 2021-01-02 18:16:00 -08:00
661bcbb557 Add README. 2021-01-01 15:52:53 -08:00
c6ac448ad2 Add day 25 solution. 2020-12-29 00:50:26 -08:00
3d85144065 Add J solution for day 5. 2020-12-23 22:40:42 -08:00
4e8b636d2f Add day 1 and 2 solutions in J. 2020-12-23 21:47:05 -08:00
ee819d1362 Add day 3 solution in J. 2020-12-23 21:46:46 -08:00
34cb639327 Add day 23. 2020-12-23 21:46:32 -08:00
b33e678c7e Add day24 solution. 2020-12-23 21:44:32 -08:00
94ddabc590 Add solutions to day21 and day22. 2020-12-21 22:40:37 -08:00
32895b3e17 Add updated solutions, including day 20. 2020-12-20 00:31:30 -08:00
77c91f8386 Add unclean solution to day 17. 2020-12-16 21:29:26 -08:00
8ed0896adb Add unclean day16 solution. 2020-12-15 22:05:25 -08:00
c17cf38795 Add day15 solution. 2020-12-14 21:36:25 -08:00
f53c65fb0d Add experimental formalization of (inefficient) solution. 2020-12-13 23:32:11 -08:00
6ecae2b5bf Add day 4 solution. 2020-12-13 21:53:37 -08:00
c3a12cbf59 Clean up proof for day 8. 2020-12-13 20:34:28 -08:00
8ea03a4c51 Finish first proof for day 8.
Apparently writing proof objects by hand is
easier than using tactics.
2020-12-12 22:49:52 -08:00
7757fd2b49 Add day 13 solution. 2020-12-12 22:09:28 -08:00
f0fbba722c Flail around with this goddamn proof some more. 2020-12-12 20:08:21 -08:00
19 changed files with 1074 additions and 325 deletions

55
README.md Normal file
View File

@@ -0,0 +1,55 @@
# Advent of Code 2020 Solutions
Here's to my first 50 star year since 2017! The goal
was to make it to top 100 at least once this year,
and it finally came true on day 22 (and again in day 25
part 2, but that hardly counts). Honestly,
I am not at all happy with myself, though.
## Kinds of Solutions
I "raced" in [Crystal](https://crystal-lang.org), but also tried my hand at
formal verification in [Coq](https://coq.inria.fr/), and tried out
an APL dialect called [J](https://jsoftware.com/) for fun.
I didn't always clean my race day solutions, particularly the ones to the
"hard" days. I will write about my Coq solutions on [my site](https://danilafe.com).
## Rankings
Here's the (rather embarassing) table with my times.
```
-------Part 1-------- -------Part 2--------
Day Time Rank Score Time Rank Score
25 00:07:18 105 0 00:07:24 94 7
24 00:25:27 1496 0 00:43:46 1165 0
23 00:27:30 887 0 01:35:00 1057 0
22 00:03:44 42 59 01:16:52 2242 0
21 00:21:57 612 0 00:26:05 439 0
20 00:27:52 465 0 03:00:50 845 0
19 00:14:56 123 0 00:48:50 439 0
18 00:26:17 1209 0 00:28:01 574 0
17 00:17:23 400 0 00:19:14 281 0
16 00:11:50 627 0 01:03:40 2040 0
15 00:18:11 1772 0 00:30:38 1924 0
14 00:18:05 1220 0 00:39:57 1173 0
13 00:04:59 198 0 01:05:11 1606 0
12 00:06:19 206 0 00:14:17 245 0
11 00:13:23 414 0 00:19:11 261 0
10 00:05:04 293 0 00:31:04 1493 0
9 00:08:55 1721 0 00:26:50 3540 0
8 00:04:23 275 0 00:09:56 256 0
7 00:17:46 832 0 00:24:22 615 0
6 00:03:31 385 0 00:09:38 999 0
5 00:08:21 839 0 00:27:23 3404 0
4 00:07:40 786 0 00:20:26 440 0
3 00:02:46 119 0 00:07:40 448 0
2 00:03:48 341 0 00:06:01 272 0
1 00:07:52 1072 0 00:09:00 748 0
```
## Crystal for Competitive Programming
I really enjoyed writing Crystal for the Advent of Code, but there were a few reasons
why it wasn't perfect for the task.
* When you're not banking on brute force speed, you lose some time to the compiler.
* Numbers are _always_ 32-bit by default, and require constant `_i64` suffixes everywhere
in the code when they're involved.
* Type annotations (however necessary they are) for hashes and arrays make refactoring
a little bit slower.

3
day1.ijs Normal file
View File

@@ -0,0 +1,3 @@
c =. > 0 ". each cutopen 1!:1 < jpath '~/projects/AoC/2020/year2020day1.txt.cache'
>./,(c*/c)*2020=c+/c
>./,(c*/c*/c)*2020=c+/c+/c

34
day13.cr Normal file
View File

@@ -0,0 +1,34 @@
require "advent"
INPU = input(2020, 13).lines
INPUT = {INPU[0].to_i32, INPU[1].split(",")}
def part1(input)
early, busses = input
busses.reject! &.==("x")
busses = busses.map &.to_i32
bbus = busses.min_by do |b|
(early / b).ceil * b
end
diff = bbus * (((early/bbus).ceil * bbus).to_i32 - early)
end
def part2(input)
_, busses = input
busses = busses.map_with_index do |x, i|
x.to_i32?.try { |n| { n, i } }
end
busses = busses.compact
n = 0_i64
iter = 1_i64
busses.each do |m, i|
while (n + i) % m != 0
n += iter
end
iter *= m
end
puts n
puts busses
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

60
day14.cr Normal file
View File

@@ -0,0 +1,60 @@
require "advent"
raw = input(2020, 14).lines
input = [] of {UInt64, UInt64, Array(Int32), UInt64, UInt64}
and_mask = (2_u64**63) + (2_u64**63-1)
or_mask = 0_u64
floats = [] of Int32
raw.each do |line|
if data = line.match(/^mask = (.+)$/)
new_mask = data[1]
and_mask = (2_u64**63) + (2_u64**63-1)
or_mask = 0_u64
floats = [] of Int32
new_mask.reverse.chars.each_with_index do |c, i|
floats << i if c == 'X'
or_mask = or_mask | (1_u64 << i) if c == '1'
and_mask = and_mask & ~(1_u64 << i) if c == '0'
end
elsif data = line.match(/^mem\[(\d+)\] = (\d+)$/)
input << {and_mask, or_mask, floats, data[1].to_u64, data[2].to_u64}
else
raise "Invalid line"
end
end
def part1(input)
memory = {} of UInt64 => UInt64
input.each do |i|
and_mask, or_mask, floats, addr, v = i
memory[addr] = (v & and_mask) | or_mask
end
memory.values.sum
end
def generate_possible(indices, index, n, &block : UInt64 -> _)
if index == indices.size
yield n
return
end
float_addr = indices[index]
no = n | (1_u64 << float_addr)
generate_possible(indices, index+1, no, &block)
nz = n & ~(1_u64 << float_addr)
generate_possible(indices, index+1, nz, &block)
end
def part2(input)
memory = {} of UInt64 => UInt64
input.each do |i|
_, or_mask, floats, addr, v = i
generate_possible(floats, 0, addr | or_mask) do |addr|
memory[addr] = v
end
end
memory.values.sum
end
puts part1(input)
puts part2(input)

24
day15.cr Normal file
View File

@@ -0,0 +1,24 @@
require "advent"
INPUT = input(2020, 15).split(",").map(&.to_i32)
def run(input, times)
ls = {} of Int32 => Int32
temp = 0
(times-1).times do |i|
n = input[i]? || temp
temp = i - (ls[n]? || i)
ls[n] = i
end
return temp
end
def part1(input)
run(input, 2020)
end
def part2(input)
run(input, 30000000)
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

65
day16.cr Normal file
View File

@@ -0,0 +1,65 @@
require "advent"
def parse_range(str)
data = str.match(/([a-z ]+): (\d+)-(\d+) or (\d+)-(\d+)/).not_nil!
return {data[1], (data[2].to_i32..data[3].to_i32), (data[4].to_i32.. data[5].to_i32)}
end
fields, your, nearby = input(2020, 16).split("\n\n")
fields = fields.lines.map { |l| parse_range(l) }
your = your.lines[1].split(",").map(&.to_i32)
nearby = nearby.lines[1..].map(&.split(",").map(&.to_i32))
def part1(input)
fields, your, nearby = input
all_ranges = [] of Range(Int32, Int32)
fields.each do |a|
all_ranges << a[1] << a[2]
end
nearby.select! do |nb|
nb.all? do |n|
all_ranges.any? &.includes?(n)
end
end
nearby << your
field_map = {} of String => Set(Int32)
fields.each do |f|
field_map[f[0]] = Set(Int32).new
nearby[0].size.times do |i|
next if field_map.values.includes? i
all_match = nearby.all? do |nb|
f[1].includes?(nb[i]) || f[2].includes?(nb[i])
end
if all_match
field_map[f[0]] << i
end
end
end
sum = 1_u64
numbers = (0...nearby[0].size).to_a
solved = {} of String => Int32
while solved.size != fields.size
cleared = [] of {String, Int32}
field_map.each do |k, v|
next unless v.size == 1
cleared << {k, v.to_a[0]}
end
cleared.each do |f,n|
solved[f] = n
field_map.each do |k, v|
v.delete n
end
end
end
fields.each do |f|
next unless f[0].starts_with? "departure"
sum *= your[solved[f[0]]]
end
sum
end
def part2(input)
end
puts part1({fields, your, nearby})
puts part2({fields, your, nearby})

47
day17.cr Normal file
View File

@@ -0,0 +1,47 @@
require "advent"
require "benchmark"
INPUT = input(2020, 17).lines.map(&.chars)
def solve(input, dim)
step = input.clone
cubes = Set(Array(Int32)).new
new_cubes = Set(Array(Int32)).new
input.each_with_index do |row, y|
row.each_with_index do |c, x|
cubes << [x,y].concat([0] * (dim-2)) if c == '#'
end
end
8.times do |i|
print '.'
neighbor_count = Hash(Array(Int32), Int32).new(0)
Array.product([[-1,0,1]] * dim).each do |diff|
next if diff.all? &.==(0)
cubes.each do |c|
neighbor_count[c.zip_with(diff) { |a,b| a+b }] += 1
end
end
new_cubes.clear
neighbor_count.each do |n, i|
new_cubes << n if i == 3 || (cubes.includes?(n) && i == 2)
end
new_cubes, cubes = cubes, new_cubes
end
cubes.size
end
def part1(input)
solve(input, 3)
end
def part2(input)
solve(input, 4)
end
(3..).each do |i|
print "Dim #{i} "
bm = Benchmark.measure { puts " #{solve(INPUT, i)}" }
puts bm.real * 1000
end

54
day18.cr Normal file
View File

@@ -0,0 +1,54 @@
require "advent"
INPUT = input(2020, 18).lines
class Array(T)
def push_op(op)
r = pop
l = pop
self << ((op == '*') ? (l*r) : (l+r))
end
def has_op?
!empty? && last != '('
end
end
def translate(toks, prec)
output = [] of Int64
stack = [] of Char
toks.each do |tok|
case tok
when .number? then output << tok.to_i64
when '(' then stack << '('
when ')'
while stack.has_op?
output.push_op(stack.pop)
end
stack.pop
else
while stack.has_op? && prec[stack.last] < prec[tok]
output.push_op(stack.pop)
end
stack << tok
end
end
while stack.has_op?
output.push_op(stack.pop)
end
output.last
end
def part1(input)
input.sum do |line|
translate(line.chars.reject &.==(' '), {'*' => 0, '+' => 0})
end
end
def part2(input)
input.sum do |line|
translate(line.chars.reject &.==(' '), {'*' => 1, '+' => 0})
end
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

68
day19.cr Normal file
View File

@@ -0,0 +1,68 @@
require "advent"
rlines, _, strings = input(2020, 19).partition("\n\n")
strings = strings.lines
rules = {} of String => String
rlines.lines.map do |l|
rule, _, text = l.partition(": ")
rules[rule] = text
end
alias Matcher = Proc(String,Int32,Array(Int32))
def char(c)
->(str : String, i : Int32) { str[i]? == c ? [i+1] : [] of Int32 }
end
def any(ps)
->(str : String, i : Int32) { ps.flat_map { |p| p.call(str, i) } }
end
def seq(ps)
->(str : String, i : Int32) {
base = [i]
ps.each do |p|
base = base.flat_map { |i| p.call(str, i) }
end
base
}
end
def to_regex(rules, rule)
end
def build_rule(rules, built, rule) : Matcher
if exists = built[rule]?
return exists
end
body = rules[rule]
return built[rule] = char body[1] if body.matches? /"."/
branches = [] of Matcher
top = any(branches)
built[rule] = top
branches.concat(body.split(" | ").map { |b| seq(b.split(" ").map { |subrule| build_rule(rules, built, subrule) }) })
top
end
def part1(input)
rules, lines = input
matcher = build_rule(rules, {} of String => Matcher, "0")
lines.count do |l|
matcher.call(l, 0).includes? l.size
end
end
def part2(input)
rules, lines = input
rules["8"] = "42 | 42 8"
rules["11"] = "42 31 | 42 11 31"
matcher = build_rule(rules, {} of String => Matcher, "0")
lines.count do |l|
matcher.call(l, 0).includes? l.size
end
end
puts part1({rules,strings})
puts part2({rules,strings})

6
day2.ijs Normal file
View File

@@ -0,0 +1,6 @@
r =: >"1 cut each cutopen 1!:1 < jpath '~/projects/AoC/2020/year2020day2.txt.cache'
rs =: >0".each'-'cut"1>(0}"1 r)
cs =: 0{"1>1{"1 r
ss =: 2{"1 r
+/1>**/|:rs-+/"1 cs=>ss
+/1=+/|:(rs-1){"1 cs=>ss

166
day20.cr Normal file
View File

@@ -0,0 +1,166 @@
require "advent"
tiles = input(2020, 20).split "\n\n"
tiles.pop
thash = {} of Int32 => Array(Array(Char))
tiles = tiles.map do |t|
tl = t.lines
tid = tl[0].match(/Tile (\d+):/).not_nil![1].to_i32
tls = tl[1..]
thash[tid] = tls.map &.chars
end
class Array(T)
def matches?(other)
zip_with(other) { |l,r| l == r }.all?
end
def rotate
reverse.transpose
end
end
def check(side_a, side_b)
return :normal if side_a.matches?(side_b)
return :flip if side_a.matches?(side_b.reverse)
return nil
end
def check_all(side_a, other, other_t)
check(side_a, other.first) || check(side_a, other.last) || check(side_a, other_t.first) || check(side_a, other_t.last)
end
MONSTER = [ {0, 1}, {1, 0}, {4, 0}, {5, 1}, {6, 1}, {7, 0}, {10, 0}, {11,1}, {12, 1}, {13, 0}, {16, 0},
{17, 1}, {18, 1}, {18, 2}, {19, 1} ]
def stitch(m, thash, corner)
image = Array(Array(Char)).new(12*8) do |y|
Array(Char).new(12*8) do |x|
'.'
end
end
tile = thash[corner]
tile_id = corner
tile.reverse! if m[corner].has_key? :top
tile.each &.reverse! if m[corner].has_key? :left
12.times do |row|
tile = tile.not_nil!
row_tile = tile
row_id = tile_id
12.times do |col|
row_tile = row_tile.not_nil!
(0..7).each do |y|
(0..7).each do |x|
image[col*8+y][row*8+x] = row_tile[y+1][x+1]
end
end
matches = nil
thash.each do |other_id, other_tile|
next if matches
next if other_id == row_id
4.times do
if row_tile.last.matches? other_tile.first
row_id = other_id
matches = other_tile
elsif row_tile.last.matches? other_tile.first.reverse
row_id = other_id
matches = other_tile.map &.reverse
end
other_tile = other_tile.rotate
end
end
row_tile = matches
end
rot = tile.rotate
matches = nil
thash.each do |other_id, other_tile|
next if matches
next if other_id == tile_id
4.times do
if rot.last.matches? other_tile.first
tile_id = other_id
matches = other_tile.rotate.rotate.rotate
elsif rot.last.matches? other_tile.first.reverse
tile_id = other_id
matches = other_tile.map(&.reverse).rotate.rotate.rotate
end
other_tile = other_tile.rotate
end
end
tile = matches
end
image
end
def find_dragons(stitch)
dragons = [] of {Int32,Int32}
stitch.each_with_index do |row, y|
row.each_with_index do |c, x|
is_dragon = MONSTER.all? do |dx, dy|
(stitch[y+dy]?.try &.[x+dx]?) == '#'
end
dragons << {x,y} if is_dragon
end
end
dragons
end
def find_all_dragons(stitch)
dragons = [] of {Int32,Int32}
4.times do
dragons.concat find_dragons(stitch)
dragons.concat find_dragons(stitch.reverse)
stitch = stitch.rotate
end
dragons
end
def match(thash, tile)
matches = {} of Symbol => {Int32, Symbol}
cs = thash[tile]
tcs = cs.transpose
thash.each do |t, ocs|
next if t == tile
tocs = ocs.transpose
top = check_all(cs.first, ocs, tocs)
bottom = check_all(cs.last, ocs, tocs)
left = check_all(tcs.first, ocs, tocs)
right = check_all(tcs.last, ocs, tocs)
matches[:top] = {t, top} if top
matches[:left] = {t, left} if left
matches[:bottom] = {t, bottom} if bottom
matches[:right] = {t, right} if right
end
matches
end
def part1(input)
corners = input.select do |t, i|
match(input, t).size == 2
end
corners.keys.map(&.to_i64).product
end
def part2(input)
matches = {} of Int32 => Hash(Symbol, {Int32, Symbol})
corners = input.select do |t, i|
matches[t] = match(input, t)
match(input, t).size == 2
end
stitched = stitch(matches, input, corners.first[0])
dragons = find_all_dragons(stitched)
stitched.sum(&.count(&.==('#'))) - (dragons.size * MONSTER.size)
end
puts part1(thash.clone)
puts part2(thash.clone)

31
day21.cr Normal file
View File

@@ -0,0 +1,31 @@
require "advent"
input = input(2020, 21).lines.map do |line|
data = line.match(/^([a-z ]+) \(contains (.+)\)$/).not_nil!
{data[1].split(" ").to_set, data[2].split(", ").to_set}
end
allergens = input.flat_map(&.last.to_a).to_set
ingredients = input.flat_map(&.first.to_a).to_set
allergen_sets = {} of String => Set(String)
allergens.each do |a|
input.each do |ings, als|
next unless als.includes? a
allergen_sets[a] ||= ings
allergen_sets[a] &= ings
end
end
safe = ingredients.reject { |i| allergen_sets.any? &.last.includes?(i) }
puts "Part 1: #{input.sum &.first.count { |i| safe.includes? i }}"
known_allergens = {} of String => String
while allergen_sets.size > known_allergens.size
allergen_sets.each do |a, s|
next unless s.size == 1
new_known = s.first
known_allergens[a] = new_known
allergen_sets.each &.last.delete(new_known)
end
end
puts "Part 2: #{known_allergens.to_a.sort_by(&.first).map(&.last).join(",")}"

66
day22.cr Normal file
View File

@@ -0,0 +1,66 @@
require "advent"
first, second = input(2020, 22).split("\n\n")
first = first.lines[1..].map &.to_i32
second = second.lines[1..].map &.to_i32
class Array(T)
def pop_left
delete_at(0)
end
def score
total = 0
s = size
each_with_index do |n, i|
total += n * (s - i)
end
total
end
end
def no_rec(deck1, deck2)
while !deck1.empty? && !deck2.empty?
f, s = deck1.delete_at(0), deck2.delete_at(0)
if f > s
deck1 << f << s
else
deck2 << s << f
end
end
return !deck1.empty?
end
def rec(deck1, deck2)
seen = Set({Int32, Int32}).new
while !deck1.empty? && !deck2.empty?
key = {deck1.score, deck2.score}
return true if seen.includes?(key)
seen << key
f = deck1.delete_at(0)
s = deck2.delete_at(0)
p1wins = (f <= deck1.size) && (s <= deck2.size) ? rec(deck1[0..f-1], deck2[0..s-1]) : f > s
if p1wins
deck1 << f << s
else
deck2 << s << f
end
end
return !deck1.empty?
end
def run(first, second, proc)
((proc.call(first, second)) ? first : second).score
end
def part1(input)
run(input[0], input[1], ->no_rec(Array(Int32), Array(Int32)))
end
def part2(input)
run(input[0], input[1], ->rec(Array(Int32), Array(Int32)))
end
puts part1({first, second}.clone)
puts part2({first, second}.clone)

117
day23.cr Normal file
View File

@@ -0,0 +1,117 @@
require "advent"
input = input(2020, 23).lines[0].chars.map &.to_i32
class Array(T)
def get(i)
self[i % size]
end
def del(i)
delete_at(i % size)
end
end
class Node
property next : Node
property int : Int32
def initialize(@int, @next)
end
def insert(other : Node, n = 1)
set_next = other
(n-1).times { set_next = set_next.@next }
set_next.next = @next
@next = other
end
def remove(n = 1)
curr = self
to_return = self.next
n.times { curr = curr.@next }
@next = curr.@next
to_return
end
def find(n)
start = self
while start.int != n
start = start.next
end
return start
end
def to_s(n)
return "" if n < 0
return "#{@int} -> #{@next.to_s(n-1)}"
end
def includes?(n, count)
return false if count <= 0
return @int == n || @next.includes?(n, count-1)
end
end
class Cups
getter size : Int32
getter head : Node
def initialize(list : Array(Int32))
@cache = {} of Int32 => Node
h = list.delete_at(0)
temp = uninitialized Node
@cache[h] = @head = Node.new(h, temp)
@size = list.size
@local_min = list.min.as(Int32)
@local_max = list.max.as(Int32)
@head.next = @head
curr = @head
list.each do |n|
@cache[n] = new = Node.new(n, curr.next)
curr.insert(new)
curr = new
end
end
def to_s
@head.to_s(@size)
end
def step
first = @head
after = first.remove(3)
m = first.int - 1
while after.includes?(m, 3) || m < @local_min
m = (m < @local_min) ? @local_max : (m - 1)
end
@cache[m].insert(after, 3)
@head = @head.next
end
end
def play(input, count)
cups = Cups.new input
count.times { |n| cups.step }
cups
end
def part1(input)
cups = play(input.clone, 100)
cups.head.find(1).next.to_s(cups.size - 1).gsub(" -> ", "")
end
def part2(input)
list = input.clone
max = input.max
(1000000 - input.size).times do
max += 1
list << max
end
cups = play(list, 10000000)
one = cups.head.find(1)
one.next.int.to_i64 * one.next.next.int
end
puts "Part 1: #{part1(input)}"
puts "Part 2: #{part2(input)}"

78
day24.cr Normal file
View File

@@ -0,0 +1,78 @@
require "advent"
INPUT = input(2020, 24).lines.map &.tiles.map { |t| MATCHES[t] }
MATCHES = { "se" => {0.5, -1.0}, "sw" => {-0.5, -1.0}, "ne" => {0.5, 1.0}, "nw" => {-0.5, 1.0}, "e" => {1.0, 0.0}, "w" => {-1.0, 0.0} }
class String
def tiles
i = 0
tiles = [] of String
while i < size
if self[i] == 's' || self[i] == 'n'
tiles << self[i..i+1]
i += 2
else
tiles << self[i..i]
i += 1
end
end
tiles
end
end
class Array(T)
def pos
curr = {0.0, 0.0}
each do |c|
curr = curr.add c
end
curr
end
end
def part1(input)
counts = Hash({Float64,Float64}, Int32).new(0)
input.each do |i|
counts[i.pos] += 1
end
counts.count &.[1].%(2).==(1)
end
struct Tuple(*T)
def neighbors
MATCHES.values.map &.add(self)
end
end
def part2(input)
state = Hash({Float64,Float64}, Bool).new(false)
counts = Hash({Float64,Float64}, Int32).new(0)
input.each do |i|
state[i.pos] ^= true
end
100.times do |i|
counts.clear
state.each do |t, f|
next unless f
t.neighbors.each do |n|
counts[n] += 1
end
end
new_state = state.clone.clear
state.each do |k, v|
next unless v
new_state[k] = !(counts[k] == 0 || (counts[k] > 2))
end
counts.each do |k, v|
new_state[k] = true if (!state[k]) && v == 2
end
state = new_state
end
state.count &.[1]
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

28
day25.cr Normal file
View File

@@ -0,0 +1,28 @@
require "advent"
INPUT = input(2020, 25).lines.map(&.to_i64)
def transform(s)
i = 1_i64
c = 0
loop do
c += 1
i = (i * s) % 20201227
yield i, c
end
end
def find_size(s, goal)
transform(s) do |n, c|
return c if n == goal
end
end
def part1(input)
goal = find_size(7, input[0])
puts goal
transform(input[1]) do |n, c|
return n if c == goal
end
end
puts part1(INPUT.clone)

5
day3.ijs Normal file
View File

@@ -0,0 +1,5 @@
r =: > cutopen 1!:1 < jpath '~/projects/AoC/2020/year2020day3.txt.cache'
rn =: 4 : '+/''#''=((#|:y)|x*i.#y){"0 1 y'
rnx =: 3 : '(1{y) rn 0{"2((0{-y)]\r)'
rnx 1 3
*/ rnx"1 (_2 [\ 1 1 1 3 1 5 1 7 2 1)

4
day5.ijs Normal file
View File

@@ -0,0 +1,4 @@
bs2n =: 3 : '+/ "1(|.*/\(1,(_1+#y)$2)) *"(1) 0 "."0 y'
ns =: bs2n "1 'F0B1L0R1' charsub > cutopen 1!:1 < jpath '~/projects/AoC/2020/year2020day5.txt.cache'
>./ns
(<./ns)+I.-.((<./ns)+i.#ns)e.ns

488
day8.v
View File

@@ -5,6 +5,8 @@ Require Import Coq.Vectors.Fin.
Require Import Coq.Program.Equality. Require Import Coq.Program.Equality.
Require Import Coq.Logic.Eqdep_dec. Require Import Coq.Logic.Eqdep_dec.
Require Import Coq.Arith.Peano_dec. Require Import Coq.Arith.Peano_dec.
Require Import Coq.Program.Wf.
Require Import Lia.
Module DayEight (Import M:Int). Module DayEight (Import M:Int).
(* We need to coerce natural numbers into integers to add them. *) (* We need to coerce natural numbers into integers to add them. *)
@@ -27,6 +29,7 @@ Module DayEight (Import M:Int).
| Ok : t -> run_result | Ok : t -> run_result
| Fail : set (fin n) -> run_result. | Fail : set (fin n) -> run_result.
(* A single program state .*)
Definition state n : Type := (fin (S n) * set (fin n) * t). Definition state n : Type := (fin (S n) * set (fin n) * t).
(* An instruction is a pair of an opcode and an argument. *) (* An instruction is a pair of an opcode and an argument. *)
@@ -37,6 +40,18 @@ Module DayEight (Import M:Int).
addresses, which are used for calculating jumps. *) addresses, which are used for calculating jumps. *)
Definition indices (n : nat) := VectorDef.t (fin n) n. Definition indices (n : nat) := VectorDef.t (fin n) n.
(* Change a jump to a nop, or a nop to a jump. *)
Definition swap (i : inst) : inst :=
match i with
| (add, t) => (add, t)
| (nop, t) => (jmp, t)
| (jmp, t) => (nop, t)
end.
Inductive swappable : inst -> Prop :=
| swap_nop : forall t, swappable (nop, t)
| swap_jmp : forall t, swappable (jmp, t).
(* Compute the destination jump index, an integer. *) (* Compute the destination jump index, an integer. *)
Definition jump_t {n} (pc : fin n) (off : t) : t := Definition jump_t {n} (pc : fin n) (off : t) : t :=
M.add (nat_to_t (proj1_sig (to_nat pc))) off. M.add (nat_to_t (proj1_sig (to_nat pc))) off.
@@ -45,56 +60,23 @@ Module DayEight (Import M:Int).
Not all inputs are valid, so this may fail. *) Not all inputs are valid, so this may fail. *)
Definition valid_jump_t {n} (pc : fin n) (off : t) : option (fin (S n)) := @clamp (S n) (jump_t pc off). Definition valid_jump_t {n} (pc : fin n) (off : t) : option (fin (S n)) := @clamp (S n) (jump_t pc off).
(* Cast a fin n to a fin (S n). *)
Fixpoint weaken_one {n} (f : fin n) : fin (S n) := Fixpoint weaken_one {n} (f : fin n) : fin (S n) :=
match f with match f with
| F1 => F1 | F1 => F1
| FS f' => FS (weaken_one f') | FS f' => FS (weaken_one f')
end. end.
(* One modification: we really want to use 'allowed' addresses, (* Convert a nat to fin. *)
a set that shrinks as the program continues, rather than 'visited'
addresses, a set that increases as the program continues. *)
Inductive step_noswap {n} : input n -> state n -> state n -> Prop :=
| step_noswap_add : forall inp pc' v acc t,
nth inp pc' = (add, t) ->
set_mem Fin.eq_dec pc' v = true ->
step_noswap inp (weaken_one pc', v, acc) (FS pc', set_remove Fin.eq_dec pc' v, M.add acc t)
| step_noswap_nop : forall inp pc' v acc t,
nth inp pc' = (nop, t) ->
set_mem Fin.eq_dec pc' v = true ->
step_noswap inp (weaken_one pc', v, acc) (FS pc', set_remove Fin.eq_dec pc' v, acc)
| step_noswap_jmp : forall inp pc' pc'' v acc t,
nth inp pc' = (jmp, t) ->
set_mem Fin.eq_dec pc' v = true ->
valid_jump_t pc' t = Some pc'' ->
step_noswap inp (weaken_one pc', v, acc) (pc'', set_remove Fin.eq_dec pc' v, acc).
Fixpoint nat_to_fin (n : nat) : fin (S n) := Fixpoint nat_to_fin (n : nat) : fin (S n) :=
match n with match n with
| O => F1 | O => F1
| S n' => FS (nat_to_fin n') | S n' => FS (nat_to_fin n')
end. end.
Inductive run_noswap {n} : input n -> state n -> state n -> Prop := (* A finite natural is either its maximum value (aka nat_to_fin n),
| run_noswap_ok : forall inp v acc, or it's not thatbig, which means it can be cast down to
run_noswap inp (nat_to_fin n, v, acc) (nat_to_fin n, v, acc) a fin (pred n). *)
| run_noswap_fail : forall inp pc' v acc,
set_mem Fin.eq_dec pc' v = false ->
run_noswap inp (weaken_one pc', v, acc) (weaken_one pc', v, acc)
| run_noswap_trans : forall inp st st' st'',
step_noswap inp st st' -> run_noswap inp st' st'' -> run_noswap inp st st''.
Inductive valid_inst {n} : inst -> fin n -> Prop :=
| valid_inst_add : forall t f, valid_inst (add, t) f
| valid_inst_nop : forall t f f',
valid_jump_t f t = Some f' -> valid_inst (nop, t) f
| valid_inst_jmp : forall t f f',
valid_jump_t f t = Some f' -> valid_inst (jmp, t) f.
(* An input is valid if all its instructions are valid. *)
Definition valid_input {n} (inp : input n) : Prop := forall (pc : fin n),
valid_inst (nth inp pc) pc.
Lemma fin_big_or_small : forall {n} (f : fin (S n)), Lemma fin_big_or_small : forall {n} (f : fin (S n)),
(f = nat_to_fin n) \/ (exists (f' : fin n), f = weaken_one f'). (f = nat_to_fin n) \/ (exists (f' : fin n), f = weaken_one f').
Proof. Proof.
@@ -115,303 +97,159 @@ Module DayEight (Import M:Int).
reflexivity. reflexivity.
Qed. Qed.
Lemma set_add_idempotent : forall {A:Type} (* One modification: we really want to use 'allowed' addresses,
(Aeq_dec : forall x y : A, { x = y } + { x <> y }) a set that shrinks as the program continues, rather than 'visited'
(a : A) (s : set A), set_mem Aeq_dec a s = true -> set_add Aeq_dec a s = s. addresses, a set that increases as the program continues. *)
Proof. Inductive step_noswap {n} : inst -> (fin n * t) -> (fin (S n) * t) -> Prop :=
intros A Aeq_dec a s Hin. | step_noswap_add : forall pc acc t,
induction s. step_noswap (add, t) (pc, acc) (FS pc, M.add acc t)
- inversion Hin. | step_noswap_nop : forall pc acc t,
- simpl. simpl in Hin. step_noswap (nop, t) (pc, acc) (FS pc, acc)
destruct (Aeq_dec a a0). | step_noswap_jmp : forall pc pc' acc t,
+ reflexivity. valid_jump_t pc t = Some pc' ->
+ simpl. rewrite IHs; auto. step_noswap (jmp, t) (pc, acc) (pc', acc).
Qed.
Theorem set_add_append : forall {A:Type} Inductive done {n} : input n -> state n -> Prop :=
(Aeq_dec : forall x y : A, {x = y } + { x <> y }) | done_prog : forall inp v acc, done inp (nat_to_fin n, v, acc).
(a : A) (s : set A), set_mem Aeq_dec a s = false ->
set_add Aeq_dec a s = List.app s (List.cons a List.nil).
Proof.
induction s.
- reflexivity.
- intros Hnm. simpl in Hnm.
destruct (Aeq_dec a a0) eqn:Heq_dec.
+ inversion Hnm.
+ simpl. rewrite Heq_dec. rewrite IHs.
reflexivity. assumption.
Qed.
Lemma list_append_or_nil : forall {A:Type} (l : list A), Inductive stuck {n} : input n -> state n -> Prop :=
l = List.nil \/ exists l' a, l = List.app l' (List.cons a List.nil). | stuck_prog : forall inp pc' v acc,
Proof. ~ set_In pc' v -> stuck inp (weaken_one pc', v, acc).
induction l.
- left. reflexivity.
- right. destruct IHl.
+ exists List.nil. exists a.
rewrite H. reflexivity.
+ destruct H as [l' [a' H]].
exists (List.cons a l'). exists a'.
rewrite H. reflexivity.
Qed.
Theorem list_append_induction : forall {A:Type} Inductive run_noswap {n} : input n -> state n -> state n -> Prop :=
(P : list A -> Prop), | run_noswap_ok : forall inp st, done inp st -> run_noswap inp st st
P List.nil -> (forall (a : A) (l : list A), P l -> P (List.app l (List.cons a (List.nil)))) -> | run_noswap_fail : forall inp st, stuck inp st -> run_noswap inp st st
forall l, P l. | run_noswap_trans : forall inp pc pc' v acc acc' st',
Proof. Admitted. set_In pc v ->
step_noswap (nth inp pc) (pc, acc) (pc', acc') ->
run_noswap inp (pc', set_remove Fin.eq_dec pc v, acc') st' ->
run_noswap inp (weaken_one pc, v, acc) st'.
Inductive run_swap {n} : input n -> state n -> state n -> Prop :=
| run_swap_normal : forall inp pc pc' v acc acc' st',
set_In pc v ->
~ swappable (nth inp pc) ->
step_noswap (nth inp pc) (pc, acc) (pc', acc') ->
run_swap inp (pc', set_remove Fin.eq_dec pc v, acc') st' ->
run_swap inp (weaken_one pc, v, acc) st'
| run_swap_swapped_ok : forall inp pc pc' v acc acc' st',
set_In pc v ->
swappable (nth inp pc) ->
step_noswap (swap (nth inp pc)) (pc, acc) (pc', acc') ->
run_noswap inp (pc', set_remove Fin.eq_dec pc v, acc') st' ->
done inp st' ->
run_swap inp (weaken_one pc, v, acc) st'
| run_swap_swapped_next : forall inp pc pc'w pc'n v acc acc'w acc'n st'w st'n,
set_In pc v ->
swappable (nth inp pc) ->
step_noswap (swap (nth inp pc)) (pc, acc) (pc'w, acc'w) ->
run_noswap inp (pc'w, set_remove Fin.eq_dec pc v, acc'w) st'w ->
stuck inp st'w ->
step_noswap (nth inp pc) (pc, acc) (pc'n, acc'n) ->
run_swap inp (pc'n, set_remove Fin.eq_dec pc v, acc'n) st'n ->
run_swap inp (weaken_one pc, v, acc) st'n.
Theorem set_induction : forall {A:Type} Inductive valid_inst {n} : inst -> fin n -> Prop :=
(Aeq_dec : forall x y : A, { x = y } + {x <> y }) | valid_inst_add : forall t f, valid_inst (add, t) f
(P : set A -> Prop), | valid_inst_nop : forall t f f',
P (@empty_set A) -> (forall (a : A) (s' : set A), P s' -> P (set_add Aeq_dec a s')) -> valid_jump_t f t = Some f' -> valid_inst (nop, t) f
forall (s : set A), P s. | valid_inst_jmp : forall t f f',
Proof. Admitted. valid_jump_t f t = Some f' -> valid_inst (jmp, t) f.
Lemma weaken_one_inj : forall n (f1 f2 : fin n), (* An input is valid if all its instructions are valid. *)
(weaken_one f1 = weaken_one f2 -> f1 = f2). Definition valid_input {n} (inp : input n) : Prop := forall (pc : fin n),
Proof. valid_inst (nth inp pc) pc.
remember (fun {n} (a b : fin n) => weaken_one a = weaken_one b -> a = b) as P.
(* Base case for rect2 *)
assert (forall n, @P (S n) F1 F1).
{rewrite HeqP. intros n Heq. reflexivity. }
(* 'Impossible' cases for rect2. *)
assert (forall {n} (f : fin n), P (S n) F1 (FS f)).
{rewrite HeqP. intros n f Heq. simpl in Heq. inversion Heq. }
assert (forall {n} (f : fin n), P (S n) (FS f) F1).
{rewrite HeqP. intros n f Heq. simpl in Heq. inversion Heq. }
(* Recursive case for rect2. *)
assert (forall {n} (f g : fin n), P n f g -> P (S n) (FS f) (FS g)).
{rewrite HeqP. intros n f g IH Heq.
simpl in Heq. injection Heq as Heq'.
apply inj_pair2_eq_dec in Heq'.
- rewrite IH. reflexivity. assumption.
- apply eq_nat_dec. }
(* Actually apply recursion. *) Section ValidInput.
(* This can't be _the_ way to do this. *) Variable n : nat.
intros n. Variable inp : input n.
specialize (@Fin.rect2 P H H0 H1 H2 n) as Hind. Hypothesis Hv : valid_input inp.
rewrite HeqP in Hind. apply Hind.
Qed.
Lemma weaken_neq_to_fin : forall {n} (f : fin (S n)), Theorem valid_input_can_step : forall pc acc, exists pc' acc',
nat_to_fin (S n) <> weaken_one f. step_noswap (nth inp pc) (pc, acc) (pc', acc').
Proof. Proof.
apply Fin.rectS; intros n Heq. intros pc acc.
- inversion Heq. destruct (nth inp pc) eqn:Hop.
- intros IH. simpl. intros Heq'. destruct o.
injection Heq' as Hinj. apply inj_pair2_eq_dec in Hinj. - exists (FS pc). exists (M.add acc t0). apply step_noswap_add.
+ simpl in IH. apply IH. apply Hinj. - exists (FS pc). exists acc. eapply step_noswap_nop.
+ apply eq_nat_dec. - specialize (Hv pc). rewrite Hop in Hv. inversion Hv; subst.
Qed. exists f'. exists acc. eapply step_noswap_jmp. apply H0.
Qed.
Lemma add_pc_safe_step : forall {n} (inp : input n) (pc : fin (S n)) i is acc st', (* A program is either done, stuck (at an invalid/visited address), or can step. *)
step_noswap inp (pc, is, acc) st' -> Theorem valid_input_progress : forall pc v acc,
exists st'', step_noswap inp (pc, (set_add Fin.eq_dec i is), acc) st''. (pc = nat_to_fin n /\ done inp (pc, v, acc)) \/
Proof. (exists pcs, pc = weaken_one pcs /\
intros n inp pc' i is acc st' Hstep. ((~ set_In pcs v /\ stuck inp (pc, v, acc)) \/
inversion Hstep. (exists pc' acc', set_In pcs v /\
- eexists. apply step_noswap_add. apply H4. step_noswap (nth inp pcs) (pcs, acc) (pc', acc')))).
apply set_mem_correct2. apply set_add_intro1. Proof.
apply set_mem_correct1 with Fin.eq_dec. assumption. intros pc v acc.
- eexists. eapply step_noswap_nop. apply H4. (* Have we reached the end? *)
apply set_mem_correct2. apply set_add_intro1. destruct (fin_big_or_small pc).
apply set_mem_correct1 with Fin.eq_dec. assumption. (* We're at the end, so we're done. *)
- eexists. eapply step_noswap_jmp. apply H3. left. rewrite H. split. reflexivity. apply done_prog.
apply set_mem_correct2. apply set_add_intro1. (* We're not at the end. *)
apply set_mem_correct1 with Fin.eq_dec. assumption. right. destruct H as [pcs H]. exists pcs. rewrite H. split. reflexivity.
apply H6. (* We're not at the end. Is the PC valid? *)
Qed. destruct (set_In_dec Fin.eq_dec pcs v).
- (* It is. *)
right.
destruct (valid_input_can_step pcs acc) as [pc' [acc' Hstep]].
exists pc'; exists acc'; auto.
- (* It is not. *)
left. split; auto. apply stuck_prog; auto.
Qed.
Lemma remove_pc_safe_run : forall {n} (inp : input n) i pc v acc st', Theorem list_length_induction {X : Type} (P : list X -> Prop) :
run_noswap inp (pc, set_add Fin.eq_dec i v, acc) st' -> (forall l, (forall l', length l' < length l -> P l') -> P l) ->
exists st'', run_noswap inp (pc, v, acc) st''. forall l, P l.
Proof. Proof.
intros n inp i pc v acc st' Hr. intros Hrec.
dependent induction Hr. assert (forall (l l' : list X), length l' <= length l -> P l').
- eexists. eapply run_noswap_ok. { induction l; intros l' Hlen; apply Hrec; intros l'0 Hlen0.
- eexists. eapply run_noswap_fail. - simpl in Hlen. lia.
apply set_mem_complete1 in H. - apply IHl. simpl in Hlen. lia. }
apply set_mem_complete2. intros l. apply H with l. lia.
intros Hin. apply H. apply set_add_intro. right. apply Hin. Qed.
- inversion H; subst; destruct (set_mem Fin.eq_dec pc' v) eqn:Hm.
Admitted.
Lemma add_pc_safe_run : forall {n} (inp : input n) i pc v acc st', Theorem set_remove_length : forall (f : fin n) (s : set (fin n)),
run_noswap inp (pc, v, acc) st' -> set_In f s ->
exists st'', run_noswap inp (pc, (set_add Fin.eq_dec i v), acc) st''. length (set_remove Fin.eq_dec f s) < length s.
Proof. Proof.
intros n inp i pc v acc st' Hr. intros f s Hin.
destruct (set_mem Fin.eq_dec i v) eqn:Hm. induction s.
(* If i is already in the set, nothing changes. *) - inversion Hin.
rewrite set_add_idempotent. - simpl. destruct (Fin.eq_dec f a) eqn:Heq.
exists st'. assumption. assumption. + unfold lt. apply le_n. (* Why couldn't lia get this one? *)
(* Otherwise, the behavior might have changed.. *) + inversion Hin; subst. exfalso. apply n0. auto.
destruct (fin_big_or_small pc). apply IHs in H. simpl. lia.
- (* If we're done, we're done no matter what. *) Qed.
eexists. rewrite H. eapply run_noswap_ok.
- (* The PC points somewhere inside. We tried (and maybe failed)
to execute and instruction. The challenging part
is that adding i may change the outcome from 'fail' to 'ok' *)
destruct H as [pc' Heq].
generalize dependent st'.
induction v using (@set_induction (fin n) Fin.eq_dec);
intros st' Hr.
+ (* Our set of valid states is nearly empty. One step,
and it runs dry. *)
simpl. destruct (Fin.eq_dec pc' i) eqn:Heq_dec.
* (* The PC is the one allowed state. *)
remember (nth inp pc') as h. destruct h. destruct o.
{ (* Addition. *)
destruct (fin_big_or_small (FS pc')).
- (* The additional step puts as at the end. *)
eexists. eapply run_noswap_trans.
+ rewrite Heq. apply step_noswap_add.
symmetry. apply Heqh. simpl. rewrite Heq_dec. reflexivity.
+ rewrite H. apply run_noswap_ok.
- (* The additional step puts us somewhere else. *)
destruct H as [f' H].
eexists. eapply run_noswap_trans.
+ rewrite Heq. apply step_noswap_add.
symmetry. apply Heqh. simpl. rewrite Heq_dec. reflexivity.
+ rewrite H. apply run_noswap_fail.
simpl. rewrite Heq_dec. reflexivity. }
{ (* No-op *) admit. }
{ (* Jump*) admit. }
* (* The PC is not. We're done. *)
eexists. rewrite Heq. eapply run_noswap_fail.
simpl. rewrite Heq_dec. reflexivity.
+ destruct (set_mem Fin.eq_dec a v) eqn:Hm'.
* unfold fin. rewrite (set_add_idempotent Fin.eq_dec a).
Theorem valid_input_terminates : forall (pc : fin (S n)) (v : set (fin n)) (acc : t),
{ apply step_noswap_nop. (exists pc', run_noswap inp (pc, v, acc) pc').
- symmetry. apply Heqh. Proof.
- simpl. rewrite Heq_dec. reflexivity. } intros pc v. generalize dependent pc.
induction v using list_length_induction.
(* intros pc acc.
dependent induction Hr; subst. destruct (valid_input_progress pc l acc) as [[_ Hd]|[pc' [Hw [[_ Hst]|[pc'' [acc'' [Hin Hst]]]]]]].
+ (* We can't be in the OK state, since we already covered - (* We're done. *)
that earlier. *) eexists. apply run_noswap_ok. assumption.
destruct n. inversion pc'. - (* We're stuck. *)
apply weaken_neq_to_fin in Heq as []. eexists. apply run_noswap_fail. assumption.
+ apply weaken_one_inj in Heq as Hs. subst. - (* We can make a step. This will remove our current PC from the valid list, *)
destruct (Fin.eq_dec pc'0 i) eqn:Heq_dec. edestruct (H (set_remove Fin.eq_dec pc' l)).
* admit. (* Since the PC must be in the list, removing it makes the list smaller. *)
* eexists. eapply run_noswap_fail. apply (set_remove_length _ _ Hin).
assert (~set_In pc'0 v). (* Without the current PC, our valid set shrinks.
{ apply (set_mem_complete1 Fin.eq_dec). assumption. } Since this is the inductive step, we have assumed
assert (~set_In pc'0 (List.cons i List.nil)). that programs with smaller sets of valid PCs always
{ simpl. intros [Heq'|[]]. apply n0. auto. } terminate. Thus, after we make the step, we're done. *)
assert (~set_In pc'0 (set_union Fin.eq_dec v (List.cons i List.nil))). exists x. subst. eapply run_noswap_trans.
{ intros Hin. apply set_union_iff in Hin as [Hf|Hf]. + auto.
- apply H0. apply Hf. + apply Hst.
- apply H1. apply Hf. } + apply H0.
simpl in H2. apply set_mem_complete2. assumption. Qed.
+ apply (add_pc_safe_step _ _ i) in H as [st''' Hr']. End ValidInput.
eexists. eapply run_noswap_trans.
apply Hr'. destruct st' as [[pc'' v''] acc''].
specialize (IHHr i pc'' v'' acc'').*)
(* intros n inp i pc v.
generalize dependent i.
generalize dependent pc.
induction v; intros pc i acc st Hr.
- inversion Hr; subst.
+ eexists. apply run_noswap_ok.
+ destruct (Fin.eq_dec pc' i) eqn:Heq_dec.
* admit.
* eexists. apply run_noswap_fail.
simpl. rewrite Heq_dec. reflexivity.
+ inversion H; subst; simpl in H7; inversion H7.
- inversion Hr; subst.
+ eexists. apply run_noswap_ok.
+ destruct (Fin.eq_dec pc' i) eqn:Heq_dec.
* admit.
* eexists. apply run_noswap_fail.
simpl. rewrite Heq_dec. simpl in H4.
apply H4.
+
destruct (nth inp pc') as [op t]. *)
Admitted.
Theorem valid_input_terminates : forall n (inp : input n) st,
valid_input inp -> exists st', run_noswap inp st st'.
Proof.
intros n inp st.
destruct st as [[pc is] acc].
generalize dependent inp.
generalize dependent pc.
generalize dependent acc.
induction is using (@set_induction (fin n) Fin.eq_dec); intros acc pc inp Hv;
(* The PC may point past the end of the
array, or it may not. *)
destruct (fin_big_or_small pc);
(* No matter what, if it's past the end
of the array, we're done, *)
try (eexists (pc, _, acc); rewrite H; apply run_noswap_ok).
- (* It's not past the end of the array,
and the 'allowed' list is empty.
Evaluation fails. *)
destruct H as [f' Heq].
exists (pc, Datatypes.nil, acc).
rewrite Heq. apply run_noswap_fail. reflexivity.
- (* We're not past the end of the array. However,
adding a new valid index still guarantees
evaluation terminates. *)
specialize (IHis acc pc inp Hv) as [st' Hr].
apply add_pc_safe_run with st'. assumption.
Qed.
(*
(* It's not past the end of the array,
and we're in the inductive case on is. *)
destruct H as [pc' Heq].
destruct (Fin.eq_dec pc' a) eqn:Heq_dec.
+ (* This PC is allowed. *)
(* That must mean we have a non-empty list. *)
remember (nth inp pc') as h. destruct h as [op t].
(* Unfortunately, we can't do eexists at the top
level, since that will mean the final state
has to be the same for every op. *)
destruct op.
(* Addition. *)
{ destruct (IHis (M.add acc t) (FS pc') inp Hv) as [st' Htrans].
eexists. eapply run_noswap_trans.
rewrite Heq. apply step_noswap_add.
- symmetry. apply Heqh.
- simpl. rewrite Heq_dec. reflexivity.
- simpl. rewrite Heq_dec. apply Htrans. }
(* No-ops *)
{ destruct (IHis acc (FS pc') inp Hv) as [st' Htrans].
eexists. eapply run_noswap_trans.
rewrite Heq. apply step_noswap_nop with t.
- symmetry. apply Heqh.
- simpl. rewrite Heq_dec. reflexivity.
- simpl. rewrite Heq_dec. apply Htrans. }
(* Jump. *)
{ (* A little more interesting. We need to know that the jump is valid. *)
assert (Hv' : valid_inst (jmp, t) pc').
{ specialize (Hv pc'). rewrite <- Heqh in Hv. assumption. }
inversion Hv'.
(* Now, proceed as usual. *)
destruct (IHis acc f' inp Hv) as [st' Htrans].
eexists. eapply run_noswap_trans.
rewrite Heq. apply step_noswap_jmp with t.
- symmetry. apply Heqh.
- simpl. rewrite Heq_dec. reflexivity.
- apply H0.
- simpl. rewrite Heq_dec. apply Htrans. }
+ (* The top PC is not allowed. *)
specialize (IHis acc pc inp Hv) as [st' Hr].
apply add_pc_safe_run with st'. assumption. *)
Qed.
(* Stoppped here. *)
Admitted.
End DayEight. End DayEight.