Write initial version of the library.

This commit is contained in:
Danila Fedorin 2020-12-05 15:32:50 -08:00
commit a8363ae168
13 changed files with 325 additions and 0 deletions

9
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
require "./spec_helper"

26
spec/graph_spec.cr Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
require "spec"
require "../src/advent"

1
src/advent.cr Normal file
View File

@ -0,0 +1 @@
require "./advent/*"

52
src/advent/graph.cr Normal file
View 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
View 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
View 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