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

498
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. *)
Inductive step_noswap {n} : inst -> (fin n * t) -> (fin (S n) * t) -> Prop :=
| step_noswap_add : forall pc acc t,
step_noswap (add, t) (pc, acc) (FS pc, M.add acc t)
| step_noswap_nop : forall pc acc t,
step_noswap (nop, t) (pc, acc) (FS pc, acc)
| step_noswap_jmp : forall pc pc' acc t,
valid_jump_t pc t = Some pc' ->
step_noswap (jmp, t) (pc, acc) (pc', acc).
Inductive done {n} : input n -> state n -> Prop :=
| done_prog : forall inp v acc, done inp (nat_to_fin n, v, acc).
Inductive stuck {n} : input n -> state n -> Prop :=
| stuck_prog : forall inp pc' v acc,
~ set_In pc' v -> stuck inp (weaken_one pc', v, acc).
Inductive run_noswap {n} : input n -> state n -> state n -> Prop :=
| run_noswap_ok : forall inp st, done inp st -> run_noswap inp st st
| run_noswap_fail : forall inp st, stuck inp st -> run_noswap inp st st
| run_noswap_trans : forall inp pc pc' v acc acc' st',
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.
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.
Section ValidInput.
Variable n : nat.
Variable inp : input n.
Hypothesis Hv : valid_input inp.
Theorem valid_input_can_step : forall pc acc, exists pc' acc',
step_noswap (nth inp pc) (pc, acc) (pc', acc').
Proof. Proof.
intros A Aeq_dec a s Hin. intros pc acc.
destruct (nth inp pc) eqn:Hop.
destruct o.
- exists (FS pc). exists (M.add acc t0). apply step_noswap_add.
- exists (FS pc). exists acc. eapply step_noswap_nop.
- specialize (Hv pc). rewrite Hop in Hv. inversion Hv; subst.
exists f'. exists acc. eapply step_noswap_jmp. apply H0.
Qed.
(* A program is either done, stuck (at an invalid/visited address), or can step. *)
Theorem valid_input_progress : forall pc v acc,
(pc = nat_to_fin n /\ done inp (pc, v, acc)) \/
(exists pcs, pc = weaken_one pcs /\
((~ set_In pcs v /\ stuck inp (pc, v, acc)) \/
(exists pc' acc', set_In pcs v /\
step_noswap (nth inp pcs) (pcs, acc) (pc', acc')))).
Proof.
intros pc v acc.
(* Have we reached the end? *)
destruct (fin_big_or_small pc).
(* We're at the end, so we're done. *)
left. rewrite H. split. reflexivity. apply done_prog.
(* We're not at the end. *)
right. destruct H as [pcs H]. exists pcs. rewrite H. split. reflexivity.
(* We're not at the end. Is the PC valid? *)
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.
Theorem list_length_induction {X : Type} (P : list X -> Prop) :
(forall l, (forall l', length l' < length l -> P l') -> P l) ->
forall l, P l.
Proof.
intros Hrec.
assert (forall (l l' : list X), length l' <= length l -> P l').
{ induction l; intros l' Hlen; apply Hrec; intros l'0 Hlen0.
- simpl in Hlen. lia.
- apply IHl. simpl in Hlen. lia. }
intros l. apply H with l. lia.
Qed.
Theorem set_remove_length : forall (f : fin n) (s : set (fin n)),
set_In f s ->
length (set_remove Fin.eq_dec f s) < length s.
Proof.
intros f s Hin.
induction s. induction s.
- inversion Hin. - inversion Hin.
- simpl. simpl in Hin. - simpl. destruct (Fin.eq_dec f a) eqn:Heq.
destruct (Aeq_dec a a0). + unfold lt. apply le_n. (* Why couldn't lia get this one? *)
+ reflexivity. + inversion Hin; subst. exfalso. apply n0. auto.
+ simpl. rewrite IHs; auto. apply IHs in H. simpl. lia.
Qed. Qed.
Theorem set_add_append : forall {A:Type} Theorem valid_input_terminates : forall (pc : fin (S n)) (v : set (fin n)) (acc : t),
(Aeq_dec : forall x y : A, {x = y } + { x <> y }) (exists pc', run_noswap inp (pc, v, acc) pc').
(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. Proof.
induction s. intros pc v. generalize dependent pc.
- reflexivity. induction v using list_length_induction.
- intros Hnm. simpl in Hnm. intros pc acc.
destruct (Aeq_dec a a0) eqn:Heq_dec. destruct (valid_input_progress pc l acc) as [[_ Hd]|[pc' [Hw [[_ Hst]|[pc'' [acc'' [Hin Hst]]]]]]].
+ inversion Hnm. - (* We're done. *)
+ simpl. rewrite Heq_dec. rewrite IHs. eexists. apply run_noswap_ok. assumption.
reflexivity. assumption. - (* We're stuck. *)
eexists. apply run_noswap_fail. assumption.
- (* We can make a step. This will remove our current PC from the valid list, *)
edestruct (H (set_remove Fin.eq_dec pc' l)).
(* Since the PC must be in the list, removing it makes the list smaller. *)
apply (set_remove_length _ _ Hin).
(* Without the current PC, our valid set shrinks.
Since this is the inductive step, we have assumed
that programs with smaller sets of valid PCs always
terminate. Thus, after we make the step, we're done. *)
exists x. subst. eapply run_noswap_trans.
+ auto.
+ apply Hst.
+ apply H0.
Qed. Qed.
End ValidInput.
Lemma list_append_or_nil : forall {A:Type} (l : list A),
l = List.nil \/ exists l' a, l = List.app l' (List.cons a List.nil).
Proof.
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}
(P : list A -> Prop),
P List.nil -> (forall (a : A) (l : list A), P l -> P (List.app l (List.cons a (List.nil)))) ->
forall l, P l.
Proof. Admitted.
Theorem set_induction : forall {A:Type}
(Aeq_dec : forall x y : A, { x = y } + {x <> y })
(P : set A -> Prop),
P (@empty_set A) -> (forall (a : A) (s' : set A), P s' -> P (set_add Aeq_dec a s')) ->
forall (s : set A), P s.
Proof. Admitted.
Lemma weaken_one_inj : forall n (f1 f2 : fin n),
(weaken_one f1 = weaken_one f2 -> f1 = f2).
Proof.
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. *)
(* This can't be _the_ way to do this. *)
intros n.
specialize (@Fin.rect2 P H H0 H1 H2 n) as Hind.
rewrite HeqP in Hind. apply Hind.
Qed.
Lemma weaken_neq_to_fin : forall {n} (f : fin (S n)),
nat_to_fin (S n) <> weaken_one f.
Proof.
apply Fin.rectS; intros n Heq.
- inversion Heq.
- intros IH. simpl. intros Heq'.
injection Heq' as Hinj. apply inj_pair2_eq_dec in Hinj.
+ simpl in IH. apply IH. apply Hinj.
+ apply eq_nat_dec.
Qed.
Lemma add_pc_safe_step : forall {n} (inp : input n) (pc : fin (S n)) i is acc st',
step_noswap inp (pc, is, acc) st' ->
exists st'', step_noswap inp (pc, (set_add Fin.eq_dec i is), acc) st''.
Proof.
intros n inp pc' i is acc st' Hstep.
inversion Hstep.
- eexists. apply step_noswap_add. apply H4.
apply set_mem_correct2. apply set_add_intro1.
apply set_mem_correct1 with Fin.eq_dec. assumption.
- eexists. eapply step_noswap_nop. apply H4.
apply set_mem_correct2. apply set_add_intro1.
apply set_mem_correct1 with Fin.eq_dec. assumption.
- eexists. eapply step_noswap_jmp. apply H3.
apply set_mem_correct2. apply set_add_intro1.
apply set_mem_correct1 with Fin.eq_dec. assumption.
apply H6.
Qed.
Lemma remove_pc_safe_run : forall {n} (inp : input n) i pc v acc st',
run_noswap inp (pc, set_add Fin.eq_dec i v, acc) st' ->
exists st'', run_noswap inp (pc, v, acc) st''.
Proof.
intros n inp i pc v acc st' Hr.
dependent induction Hr.
- eexists. eapply run_noswap_ok.
- eexists. eapply run_noswap_fail.
apply set_mem_complete1 in H.
apply set_mem_complete2.
intros Hin. apply H. apply set_add_intro. right. apply Hin.
- 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',
run_noswap inp (pc, v, acc) st' ->
exists st'', run_noswap inp (pc, (set_add Fin.eq_dec i v), acc) st''.
Proof.
intros n inp i pc v acc st' Hr.
destruct (set_mem Fin.eq_dec i v) eqn:Hm.
(* If i is already in the set, nothing changes. *)
rewrite set_add_idempotent.
exists st'. assumption. assumption.
(* Otherwise, the behavior might have changed.. *)
destruct (fin_big_or_small pc).
- (* If we're done, we're done no matter what. *)
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).
{ apply step_noswap_nop.
- symmetry. apply Heqh.
- simpl. rewrite Heq_dec. reflexivity. }
(*
dependent induction Hr; subst.
+ (* We can't be in the OK state, since we already covered
that earlier. *)
destruct n. inversion pc'.
apply weaken_neq_to_fin in Heq as [].
+ apply weaken_one_inj in Heq as Hs. subst.
destruct (Fin.eq_dec pc'0 i) eqn:Heq_dec.
* admit.
* eexists. eapply run_noswap_fail.
assert (~set_In pc'0 v).
{ apply (set_mem_complete1 Fin.eq_dec). assumption. }
assert (~set_In pc'0 (List.cons i List.nil)).
{ simpl. intros [Heq'|[]]. apply n0. auto. }
assert (~set_In pc'0 (set_union Fin.eq_dec v (List.cons i List.nil))).
{ intros Hin. apply set_union_iff in Hin as [Hf|Hf].
- apply H0. apply Hf.
- apply H1. apply Hf. }
simpl in H2. apply set_mem_complete2. assumption.
+ apply (add_pc_safe_step _ _ i) in H as [st''' Hr'].
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.