Write initial version of the library.
This commit is contained in:
commit
a8363ae168
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/docs/
|
||||||
|
/lib/
|
||||||
|
/bin/
|
||||||
|
/.shards/
|
||||||
|
*.dwarf
|
||||||
|
|
||||||
|
# Libraries don't need dependency lock
|
||||||
|
# Dependencies will be locked in applications that use them
|
||||||
|
/shard.lock
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2020 your-name-here
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
21
README.md
Normal file
21
README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# advent
|
||||||
|
Collection of common code that may help in solving advent of code
|
||||||
|
puzzles.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Add the dependency to your `shard.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
advent:
|
||||||
|
git: https://dev.danilafe.com/Advent-of-Code/advent
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run `shards install`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```crystal
|
||||||
|
require "advent"
|
||||||
|
```
|
9
shard.yml
Normal file
9
shard.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
name: advent
|
||||||
|
version: 0.1.0
|
||||||
|
|
||||||
|
authors:
|
||||||
|
- Danila Fedorin <danila.fedorin@gmail.com>
|
||||||
|
|
||||||
|
crystal: 0.35.1
|
||||||
|
|
||||||
|
license: MIT
|
1
spec/advent_spec.cr
Normal file
1
spec/advent_spec.cr
Normal file
@ -0,0 +1 @@
|
|||||||
|
require "./spec_helper"
|
26
spec/graph_spec.cr
Normal file
26
spec/graph_spec.cr
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
require "./spec_helper"
|
||||||
|
|
||||||
|
describe Graph do
|
||||||
|
describe "#find_path" do
|
||||||
|
it "reports no paths in a graph with no edges" do
|
||||||
|
gr = Graph(String).new
|
||||||
|
elems = ["a", "b", "c", "d"]
|
||||||
|
elems.each &->gr.add_node(String)
|
||||||
|
elems.each do |elem|
|
||||||
|
elems.each do |other|
|
||||||
|
next if elem == other
|
||||||
|
gr.find_path(elem, other).should be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "computes the shortest path" do
|
||||||
|
gr = Graph(String).new
|
||||||
|
[{"a", "b", 1}, {"a", "c", 1}, {"c", "b", 1}, {"b", "d", 1}].each do |t|
|
||||||
|
from, to, cost = t
|
||||||
|
gr.add_edge(from, to, cost)
|
||||||
|
end
|
||||||
|
gr.find_path("a", "d").should eq({["a", "b", "d"], 2})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
44
spec/heap_spec.cr
Normal file
44
spec/heap_spec.cr
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
require "./spec_helper"
|
||||||
|
|
||||||
|
describe Array do
|
||||||
|
describe "#heapify" do
|
||||||
|
it "preserves the elements in the array" do
|
||||||
|
a = Array(Int32).new(20) { rand(10) }
|
||||||
|
a.sort.should eq(a.clone.heapify!.sort)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates an array with proper ordering" do
|
||||||
|
a = Array(Int32).new(20) { rand(10) }
|
||||||
|
a.heapify!
|
||||||
|
a.is_heap?.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "maintains heap property while popping" do
|
||||||
|
a = Array(Int32).new(20) { rand(10) }
|
||||||
|
a.heapify!
|
||||||
|
20.times do |i|
|
||||||
|
a.heap_pop
|
||||||
|
a.is_heap?.should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "maintains heap property while pushing" do
|
||||||
|
a = [] of Int32
|
||||||
|
20.times do
|
||||||
|
a.heap_push(rand 10)
|
||||||
|
a.is_heap?.should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "pops numbers in descending order" do
|
||||||
|
a = Array(Int32).new(20) { rand(10) }
|
||||||
|
a.heapify!
|
||||||
|
last = Int32::MAX
|
||||||
|
20.times do
|
||||||
|
popped = a.heap_pop
|
||||||
|
popped.should be <= last
|
||||||
|
last = popped
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
18
spec/knapsack_spec.cr
Normal file
18
spec/knapsack_spec.cr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
describe Array do
|
||||||
|
describe "#knapsack" do
|
||||||
|
it "works with costs of one" do
|
||||||
|
ans = [1,2,3,4,5,6].shuffle.knapsack(3) do |i|
|
||||||
|
{1, i}
|
||||||
|
end
|
||||||
|
ans[1].sort!
|
||||||
|
ans.should eq({15, [4,5,6]})
|
||||||
|
end
|
||||||
|
|
||||||
|
it "works in the non-greedy case" do
|
||||||
|
ans = [{2, 2}, {2, 2}, {3, 3}].shuffle.knapsack(4) do |i|
|
||||||
|
i
|
||||||
|
end
|
||||||
|
ans.should eq({4, [{2,2},{2,2}]})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
2
spec/spec_helper.cr
Normal file
2
spec/spec_helper.cr
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
require "spec"
|
||||||
|
require "../src/advent"
|
1
src/advent.cr
Normal file
1
src/advent.cr
Normal file
@ -0,0 +1 @@
|
|||||||
|
require "./advent/*"
|
52
src/advent/graph.cr
Normal file
52
src/advent/graph.cr
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
class Graph(A)
|
||||||
|
def initialize
|
||||||
|
@edges = {} of A => Set({A, Int32})
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_node(n)
|
||||||
|
@edges[n] = Set({A, Int32}).new
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_edge(f, t, c = 1)
|
||||||
|
@edges[f] ||= Set({A, Int32}).new
|
||||||
|
@edges[f] << {t, c}
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_biedge(f, t, c = 1)
|
||||||
|
add_edge(f, t, c)
|
||||||
|
add_edge(t, f, c)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_path(f, t)
|
||||||
|
visited = Set(A).new
|
||||||
|
candidates = Set { f }
|
||||||
|
distances = {f => 0}
|
||||||
|
prev = {} of A => A
|
||||||
|
|
||||||
|
while !candidates.empty?
|
||||||
|
candidate = candidates.min_by { |c| distances[c] }
|
||||||
|
break if candidate == t
|
||||||
|
visited << candidate
|
||||||
|
candidates.delete candidate
|
||||||
|
dist = distances[candidate]
|
||||||
|
|
||||||
|
@edges.fetch(candidate, Set({A, Int32}).new).each do |e|
|
||||||
|
node, cost = e
|
||||||
|
new_dist = dist + cost
|
||||||
|
candidates << node unless visited.includes? node
|
||||||
|
next if (old_dist = distances[node]?) && old_dist < new_dist
|
||||||
|
distances[node] = new_dist
|
||||||
|
prev[node] = candidate
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
backtrack = t
|
||||||
|
path = [t] of A
|
||||||
|
while backtrack != f
|
||||||
|
return nil unless prev_bt = prev[backtrack]?
|
||||||
|
path << prev_bt
|
||||||
|
backtrack = prev_bt
|
||||||
|
end
|
||||||
|
{path.reverse!, distances[t]}
|
||||||
|
end
|
||||||
|
end
|
82
src/advent/heap.cr
Normal file
82
src/advent/heap.cr
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
class Array(T)
|
||||||
|
def bubble_up(i, &cmp)
|
||||||
|
return if i >= size
|
||||||
|
while i != 0
|
||||||
|
j = (i-1)//2
|
||||||
|
break if yield self[i], self[j]
|
||||||
|
self[i], self[j] = self[j], self[i]
|
||||||
|
i = j
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def percalate_down(i, &cmp)
|
||||||
|
while i*2+1 < size
|
||||||
|
j1, j2 = i*2+1, i*2+2
|
||||||
|
v1 = self[j1]
|
||||||
|
v2 = self[j2]?
|
||||||
|
if v2 && (yield v1, v2) && (yield self[i], v2)
|
||||||
|
self[j2], self[i] = self[i], v2
|
||||||
|
i = j2
|
||||||
|
elsif yield self[i], v1
|
||||||
|
self[j1], self[i] = self[i], v1
|
||||||
|
i = j1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def heapify!(&cmp : T,T -> Bool)
|
||||||
|
size.times do |i|
|
||||||
|
bubble_up(i, &cmp)
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def heapify!
|
||||||
|
heapify! do |i,j|
|
||||||
|
i < j
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def heap_push(v, &cmp : T,T -> Bool)
|
||||||
|
self << v
|
||||||
|
bubble_up(size - 1, &cmp)
|
||||||
|
end
|
||||||
|
|
||||||
|
def heap_push(v)
|
||||||
|
heap_push(v) do |i,j|
|
||||||
|
i < j
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def heap_pop(&cmp : T,T -> Bool)
|
||||||
|
self[0], self[size-1] = self[size-1], self[0]
|
||||||
|
v = pop
|
||||||
|
percalate_down(0, &cmp)
|
||||||
|
v
|
||||||
|
end
|
||||||
|
|
||||||
|
def heap_pop
|
||||||
|
heap_pop do |i, j|
|
||||||
|
i < j
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_heap?(&cmp : T,T -> Bool)
|
||||||
|
(size-1).times do |i|
|
||||||
|
i = size - i - 1
|
||||||
|
vi = self[i]
|
||||||
|
vp = self[(i-1)//2]
|
||||||
|
return false unless (yield self[i], self[(i-1)//2]) || vi == vp
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_heap?
|
||||||
|
is_heap? do |i,j|
|
||||||
|
i < j
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
39
src/advent/knapsack.cr
Normal file
39
src/advent/knapsack.cr
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
class Array(T)
|
||||||
|
def knapsack(budget, &cv : T -> {Int32,Int32})
|
||||||
|
cost_values = map &cv
|
||||||
|
|
||||||
|
memo = {} of {Int32, Int32} => Int32
|
||||||
|
bt = {} of {Int32, Int32} => Bool
|
||||||
|
compute = uninitialized Int32, Int32 -> Int32
|
||||||
|
compute = ->(size : Int32, budget : Int32) {
|
||||||
|
if m = memo[{size, budget}]?
|
||||||
|
return m
|
||||||
|
end
|
||||||
|
return memo[{size, budget}] = 0 if size == 0
|
||||||
|
|
||||||
|
cost, value = cost_values[size-1]
|
||||||
|
no_val = compute.call(size-1, budget)
|
||||||
|
yes_val = (budget < cost) ? 0 : compute.call(size-1, budget - cost) + value
|
||||||
|
|
||||||
|
if yes_val > no_val
|
||||||
|
bt[{size, budget}] = true
|
||||||
|
return yes_val
|
||||||
|
else
|
||||||
|
bt[{size, budget}] = false
|
||||||
|
return no_val
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
value = compute.call(size, budget)
|
||||||
|
i = size
|
||||||
|
items = [] of T
|
||||||
|
while i != 0
|
||||||
|
if bt[{i, budget}]
|
||||||
|
items << self[i-1]
|
||||||
|
budget -= cost_values[i-1][0]
|
||||||
|
end
|
||||||
|
i -= 1
|
||||||
|
end
|
||||||
|
{value, items}
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user