Syncing Up
This commit is contained in:
parent
148fad68a9
commit
2f99878b09
72
go/binary-search-tree/README.md
Normal file
72
go/binary-search-tree/README.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# Binary Search Tree
|
||||||
|
|
||||||
|
Write a program that inserts numbers and searches in a binary tree.
|
||||||
|
|
||||||
|
When we need to represent sorted data, an array does not make a good
|
||||||
|
data structure.
|
||||||
|
|
||||||
|
Say we have the array `[1, 3, 4, 5]`, and we add 2 to it so it becomes
|
||||||
|
`[1, 3, 4, 5, 2]` now we must sort the entire array again! We can
|
||||||
|
improve on this by realizing that we only need to make space for the new
|
||||||
|
item `[1, nil, 3, 4, 5]`, and then adding the item in the space we
|
||||||
|
added. But this still requires us to shift many elements down by one.
|
||||||
|
|
||||||
|
Binary Search Trees, however, can operate on sorted data much more
|
||||||
|
efficiently.
|
||||||
|
|
||||||
|
A binary search tree consists of a series of connected nodes. Each node
|
||||||
|
contains a piece of data (e.g. the number 3), a variable named `left`,
|
||||||
|
and a variable named `right`. The `left` and `right` variables point at
|
||||||
|
`nil`, or other nodes. Since these other nodes in turn have other nodes
|
||||||
|
beneath them, we say that the left and right variables are pointing at
|
||||||
|
subtrees. All data in the left subtree is less than or equal to the
|
||||||
|
current node's data, and all data in the right subtree is greater than
|
||||||
|
the current node's data.
|
||||||
|
|
||||||
|
For example, if we had a node containing the data 4, and we added the
|
||||||
|
data 2, our tree would look like this:
|
||||||
|
|
||||||
|
4
|
||||||
|
/
|
||||||
|
2
|
||||||
|
|
||||||
|
If we then added 6, it would look like this:
|
||||||
|
|
||||||
|
4
|
||||||
|
/ \
|
||||||
|
2 6
|
||||||
|
|
||||||
|
If we then added 3, it would look like this
|
||||||
|
|
||||||
|
4
|
||||||
|
/ \
|
||||||
|
2 6
|
||||||
|
\
|
||||||
|
3
|
||||||
|
|
||||||
|
And if we then added 1, 5, and 7, it would look like this
|
||||||
|
|
||||||
|
4
|
||||||
|
/ \
|
||||||
|
/ \
|
||||||
|
2 6
|
||||||
|
/ \ / \
|
||||||
|
1 3 5 7
|
||||||
|
|
||||||
|
To run the tests simply run the command `go test` in the exercise directory.
|
||||||
|
|
||||||
|
If the test suite contains benchmarks, you can run these with the `-bench`
|
||||||
|
flag:
|
||||||
|
|
||||||
|
go test -bench .
|
||||||
|
|
||||||
|
For more detailed info about the Go track see the [help
|
||||||
|
page](http://exercism.io/languages/go).
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Josh Cheek [https://twitter.com/josh_cheek](https://twitter.com/josh_cheek)
|
||||||
|
|
||||||
|
## Submitting Incomplete Problems
|
||||||
|
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
||||||
|
|
50
go/binary-search-tree/binary_search_tree.go
Normal file
50
go/binary-search-tree/binary_search_tree.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package binarysearchtree
|
||||||
|
|
||||||
|
// SearchTreeData is a Binary Search Tree node
|
||||||
|
type SearchTreeData struct {
|
||||||
|
data int
|
||||||
|
left *SearchTreeData
|
||||||
|
right *SearchTreeData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bst creates a node from an int
|
||||||
|
func Bst(n int) *SearchTreeData {
|
||||||
|
return &SearchTreeData{data: n}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert finds the correct location for a new node and inserts it there.
|
||||||
|
func (s *SearchTreeData) Insert(n int) *SearchTreeData {
|
||||||
|
if s == nil {
|
||||||
|
return Bst(n)
|
||||||
|
}
|
||||||
|
if s.data < n {
|
||||||
|
s.right = s.right.Insert(n)
|
||||||
|
} else {
|
||||||
|
s.left = s.left.Insert(n)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapString maps a function to every node in the search tree and returns
|
||||||
|
// the []string result of it.
|
||||||
|
func (s *SearchTreeData) MapString(f func(int) string) []string {
|
||||||
|
var ret []string
|
||||||
|
if s != nil {
|
||||||
|
ret = append(ret, s.left.MapString(f)...)
|
||||||
|
ret = append(ret, f(s.data))
|
||||||
|
ret = append(ret, s.right.MapString(f)...)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapInt maps a function to every node in the search tree and returns
|
||||||
|
// the []int result of it.
|
||||||
|
func (s *SearchTreeData) MapInt(f func(int) int) []int {
|
||||||
|
var ret []int
|
||||||
|
if s != nil {
|
||||||
|
ret = append(ret, s.left.MapInt(f)...)
|
||||||
|
ret = append(ret, f(s.data))
|
||||||
|
ret = append(ret, s.right.MapInt(f)...)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
199
go/binary-search-tree/binary_search_tree_test.go
Normal file
199
go/binary-search-tree/binary_search_tree_test.go
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
// API:
|
||||||
|
//
|
||||||
|
// type SearchTreeData struct {
|
||||||
|
// left *SearchTreeData
|
||||||
|
// data int
|
||||||
|
// right *SearchTreeData
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func Bst(int) *SearchTreeData
|
||||||
|
// func (*SearchTreeData) Insert(int)
|
||||||
|
// func (*SearchTreeData) MapString(func(int) string) []string
|
||||||
|
// func (*SearchTreeData) MapInt(func(int) int) []int
|
||||||
|
|
||||||
|
package binarysearchtree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDataIsRetained(t *testing.T) {
|
||||||
|
actual := Bst(4).data
|
||||||
|
expected := 4
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("Bst(4).data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertingLess(t *testing.T) {
|
||||||
|
bst := SearchTreeData{data: 4}
|
||||||
|
bst.Insert(2)
|
||||||
|
|
||||||
|
actual := bst.data
|
||||||
|
expected := 4
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = bst.left.data
|
||||||
|
expected = 2
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.left.data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertingSame(t *testing.T) {
|
||||||
|
bst := SearchTreeData{data: 4}
|
||||||
|
bst.Insert(4)
|
||||||
|
|
||||||
|
actual := bst.data
|
||||||
|
expected := 4
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = bst.left.data
|
||||||
|
expected = 4
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.left.data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertingMore(t *testing.T) {
|
||||||
|
bst := SearchTreeData{data: 4}
|
||||||
|
bst.Insert(5)
|
||||||
|
|
||||||
|
actual := bst.data
|
||||||
|
expected := 4
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = bst.right.data
|
||||||
|
expected = 5
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComplexTree(t *testing.T) {
|
||||||
|
bst := SearchTreeData{data: 4}
|
||||||
|
bst.Insert(2)
|
||||||
|
bst.Insert(6)
|
||||||
|
bst.Insert(1)
|
||||||
|
bst.Insert(3)
|
||||||
|
bst.Insert(7)
|
||||||
|
bst.Insert(5)
|
||||||
|
|
||||||
|
actual := bst.data
|
||||||
|
expected := 4
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = bst.left.data
|
||||||
|
expected = 2
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.left.data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = bst.left.left.data
|
||||||
|
expected = 1
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.left.left.data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = bst.left.right.data
|
||||||
|
expected = 3
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.left.right.data: %d, want %d.", actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = bst.right.data
|
||||||
|
expected = 6
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.right.data: %d, want %d", actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = bst.right.left.data
|
||||||
|
expected = 5
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.right.left.data: %d, want %d", actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = bst.right.right.data
|
||||||
|
expected = 7
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("bst.right.right.data: %d, want %d", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapStringWithOneElement(t *testing.T) {
|
||||||
|
bst := SearchTreeData{data: 4}
|
||||||
|
|
||||||
|
actual := bst.MapString(strconv.Itoa)
|
||||||
|
expected := []string{"4"}
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Errorf("bst.MapString(): %q, want %q.", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapStringWithSmallElement(t *testing.T) {
|
||||||
|
bst := SearchTreeData{data: 4}
|
||||||
|
bst.Insert(2)
|
||||||
|
|
||||||
|
actual := bst.MapString(strconv.Itoa)
|
||||||
|
expected := []string{"2", "4"}
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Errorf("bst.MapString(): %q, want %q.", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapStringWithLargeElement(t *testing.T) {
|
||||||
|
bst := SearchTreeData{data: 4}
|
||||||
|
bst.Insert(5)
|
||||||
|
|
||||||
|
actual := bst.MapString(strconv.Itoa)
|
||||||
|
expected := []string{"4", "5"}
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Errorf("bst.MapString(): %q, want %q.", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapStringWithComplexStructure(t *testing.T) {
|
||||||
|
bst := SearchTreeData{data: 4}
|
||||||
|
bst.Insert(2)
|
||||||
|
bst.Insert(1)
|
||||||
|
bst.Insert(3)
|
||||||
|
bst.Insert(6)
|
||||||
|
bst.Insert(7)
|
||||||
|
bst.Insert(5)
|
||||||
|
|
||||||
|
actual := bst.MapString(strconv.Itoa)
|
||||||
|
expected := []string{"1", "2", "3", "4", "5", "6", "7"}
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Errorf("bst.MapString(): %q, want %q.", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapIntWithComplexStructure(t *testing.T) {
|
||||||
|
bst := SearchTreeData{data: 4}
|
||||||
|
bst.Insert(2)
|
||||||
|
bst.Insert(1)
|
||||||
|
bst.Insert(3)
|
||||||
|
bst.Insert(6)
|
||||||
|
bst.Insert(7)
|
||||||
|
bst.Insert(5)
|
||||||
|
|
||||||
|
f := func(i int) int {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := bst.MapInt(f)
|
||||||
|
expected := []int{1, 2, 3, 4, 5, 6, 7}
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Errorf("bst.MapString(): %v, want %v.", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
53
go/binary-search/README.md
Normal file
53
go/binary-search/README.md
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Binary Search
|
||||||
|
|
||||||
|
Write a program that implements a binary search algorithm.
|
||||||
|
|
||||||
|
Searching a sorted collection is a common task. A dictionary is a sorted
|
||||||
|
list of word definitions. Given a word, one can find its definition. A
|
||||||
|
telephone book is a sorted list of people's names, addresses, and
|
||||||
|
telephone numbers. Knowing someone's name allows one to quickly find
|
||||||
|
their telephone number and address.
|
||||||
|
|
||||||
|
If the list to be searched contains more than a few items (a dozen, say)
|
||||||
|
a binary search will require far fewer comparisons than a linear search,
|
||||||
|
but it imposes the requirement that the list be sorted.
|
||||||
|
|
||||||
|
In computer science, a binary search or half-interval search algorithm
|
||||||
|
finds the position of a specified input value (the search "key") within
|
||||||
|
an array sorted by key value.
|
||||||
|
|
||||||
|
In each step, the algorithm compares the search key value with the key
|
||||||
|
value of the middle element of the array.
|
||||||
|
|
||||||
|
If the keys match, then a matching element has been found and its index,
|
||||||
|
or position, is returned.
|
||||||
|
|
||||||
|
Otherwise, if the search key is less than the middle element's key, then
|
||||||
|
the algorithm repeats its action on the sub-array to the left of the
|
||||||
|
middle element or, if the search key is greater, on the sub-array to the
|
||||||
|
right.
|
||||||
|
|
||||||
|
If the remaining array to be searched is empty, then the key cannot be
|
||||||
|
found in the array and a special "not found" indication is returned.
|
||||||
|
|
||||||
|
A binary search halves the number of items to check with each iteration,
|
||||||
|
so locating an item (or determining its absence) takes logarithmic time.
|
||||||
|
A binary search is a dichotomic divide and conquer search algorithm.
|
||||||
|
|
||||||
|
To run the tests simply run the command `go test` in the exercise directory.
|
||||||
|
|
||||||
|
If the test suite contains benchmarks, you can run these with the `-bench`
|
||||||
|
flag:
|
||||||
|
|
||||||
|
go test -bench .
|
||||||
|
|
||||||
|
For more detailed info about the Go track see the [help
|
||||||
|
page](http://exercism.io/languages/go).
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Wikipedia [http://en.wikipedia.org/wiki/Binary_search_algorithm](http://en.wikipedia.org/wiki/Binary_search_algorithm)
|
||||||
|
|
||||||
|
## Submitting Incomplete Problems
|
||||||
|
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
||||||
|
|
58
go/binary-search/binary_search.go
Normal file
58
go/binary-search/binary_search.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package binarysearch
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// SearchInts searches a slice of ints for the first position that 'n' should
|
||||||
|
// be inserted at and keep 'hay' sorted
|
||||||
|
func SearchInts(hay []int, n int) int {
|
||||||
|
if len(hay) == 0 || n < hay[0] {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if n > hay[len(hay)-1] {
|
||||||
|
return len(hay)
|
||||||
|
}
|
||||||
|
|
||||||
|
mid := len(hay) / 2
|
||||||
|
if hay[mid] < n {
|
||||||
|
return mid + 1 + SearchInts(hay[mid+1:], n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < hay[mid] {
|
||||||
|
return SearchInts(hay[:mid], n)
|
||||||
|
}
|
||||||
|
|
||||||
|
for mid > 0 && hay[mid-1] == n {
|
||||||
|
mid--
|
||||||
|
}
|
||||||
|
return mid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message does the binary search and returns an english string representation
|
||||||
|
// of the result
|
||||||
|
func Message(hay []int, n int) string {
|
||||||
|
if len(hay) == 0 {
|
||||||
|
return "slice has no values"
|
||||||
|
}
|
||||||
|
v := SearchInts(hay, n)
|
||||||
|
if v < len(hay) && hay[v] == n {
|
||||||
|
switch v {
|
||||||
|
case 0:
|
||||||
|
return itoa(n) + " found at beginning of slice"
|
||||||
|
case len(hay) - 1:
|
||||||
|
return itoa(n) + " found at end of slice"
|
||||||
|
}
|
||||||
|
return itoa(n) + " found at index " + itoa(v)
|
||||||
|
}
|
||||||
|
switch v {
|
||||||
|
case 0:
|
||||||
|
return itoa(n) + " < all values"
|
||||||
|
case len(hay):
|
||||||
|
return itoa(n) + " > all " + itoa(len(hay)) + " values"
|
||||||
|
}
|
||||||
|
return itoa(n) + " > " + itoa(hay[v-1]) + " at index " + itoa(v-1) + ", < " + itoa(hay[v]) + " at index " + itoa(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// itoa is because I'm tired of writing strconv.Itoa
|
||||||
|
func itoa(v int) string {
|
||||||
|
return strconv.Itoa(v)
|
||||||
|
}
|
193
go/binary-search/binary_search_test.go
Normal file
193
go/binary-search/binary_search_test.go
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
// Go has binary search in the standard library. Let's reimplement
|
||||||
|
// sort.SearchInts with the same API as documented in the standard library
|
||||||
|
// at http://golang.org/pkg/sort/#Search. Note that there are some differences
|
||||||
|
// with the exercise README.
|
||||||
|
//
|
||||||
|
// * If there are duplicate values of the key you are searching for, you can't
|
||||||
|
// just stop at the first one you find; you must find the first occurance in
|
||||||
|
// the slice.
|
||||||
|
//
|
||||||
|
// * There is no special "not found" indication. If the search key is not
|
||||||
|
// present, SearchInts returns the index of the first value greater than the
|
||||||
|
// search key. If the key is greater than all values in the slice, SearchInts
|
||||||
|
// returns the length of the slice.
|
||||||
|
//
|
||||||
|
// * You can assume the slice is sorted in increasing order. There is no need
|
||||||
|
// to check that it is sorted. (That would wreck the performance.)
|
||||||
|
//
|
||||||
|
// Try it on your own without peeking at the standard library code.
|
||||||
|
|
||||||
|
package binarysearch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testData = []struct {
|
||||||
|
ref string
|
||||||
|
slice []int
|
||||||
|
key int
|
||||||
|
x int // expected result
|
||||||
|
}{
|
||||||
|
{"6 found at index 3",
|
||||||
|
[]int{1, 3, 4, 6, 8, 9, 11},
|
||||||
|
6, 3},
|
||||||
|
{"9 found at index 5",
|
||||||
|
[]int{1, 3, 4, 6, 8, 9, 11},
|
||||||
|
9, 5},
|
||||||
|
{"3 found at index 1",
|
||||||
|
[]int{1, 3, 5, 8, 13, 21, 34, 55, 89, 144},
|
||||||
|
3, 1},
|
||||||
|
{"55 found at index 7",
|
||||||
|
[]int{1, 3, 5, 8, 13, 21, 34, 55, 89, 144},
|
||||||
|
55, 7},
|
||||||
|
{"21 found at index 5",
|
||||||
|
[]int{1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377},
|
||||||
|
21, 5},
|
||||||
|
{"34 found at index 6",
|
||||||
|
[]int{1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377},
|
||||||
|
34, 6},
|
||||||
|
|
||||||
|
{"1 found at beginning of slice",
|
||||||
|
[]int{1, 3, 6},
|
||||||
|
1, 0},
|
||||||
|
{"6 found at end of slice",
|
||||||
|
[]int{1, 3, 6},
|
||||||
|
6, 2},
|
||||||
|
{"2 > 1 at index 0, < 3 at index 1",
|
||||||
|
[]int{1, 3, 6},
|
||||||
|
2, 1},
|
||||||
|
{"2 < all values",
|
||||||
|
[]int{11, 13, 16},
|
||||||
|
2, 0},
|
||||||
|
{"21 > all 3 values",
|
||||||
|
[]int{11, 13, 16},
|
||||||
|
21, 3},
|
||||||
|
{"1 found at beginning of slice",
|
||||||
|
[]int{1, 1, 1, 1, 1, 3, 6}, // duplicates
|
||||||
|
1, 0},
|
||||||
|
{"3 found at index 1",
|
||||||
|
[]int{1, 3, 3, 3, 3, 3, 6},
|
||||||
|
3, 1},
|
||||||
|
{"6 found at index 4",
|
||||||
|
[]int{1, 3, 3, 3, 6, 6, 6},
|
||||||
|
6, 4},
|
||||||
|
{"-2 > -3 at index 1, < -1 at index 2",
|
||||||
|
[]int{-6, -3, -1}, // negatives
|
||||||
|
-2, 2},
|
||||||
|
{"0 > -7 at index 4, < 1 at index 5",
|
||||||
|
[]int{-19, -17, -15, -12, -7, 1, 14, 35, 69, 124},
|
||||||
|
0, 5},
|
||||||
|
{"slice has no values",
|
||||||
|
nil, 0, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearchInts(t *testing.T) {
|
||||||
|
for _, test := range testData {
|
||||||
|
if !sort.IntsAreSorted(test.slice) {
|
||||||
|
t.Skip("Invalid test data")
|
||||||
|
}
|
||||||
|
if x := SearchInts(test.slice, test.key); x != test.x {
|
||||||
|
t.Fatalf("SearchInts(%v, %d) = %d, want %d",
|
||||||
|
test.slice, test.key, x, test.x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did you get it? Did you cut and paste from the standard library?
|
||||||
|
// Whatever. Now show you can work it.
|
||||||
|
//
|
||||||
|
// The test program will supply slices and keys and you will write a function
|
||||||
|
// that searches and returns one of the following messages:
|
||||||
|
//
|
||||||
|
// k found at beginning of slice.
|
||||||
|
// k found at end of slice.
|
||||||
|
// k found at index fx.
|
||||||
|
// k < all values.
|
||||||
|
// k > all n values.
|
||||||
|
// k > lv at lx, < gv at gx.
|
||||||
|
// slice has no values.
|
||||||
|
//
|
||||||
|
// In your messages, substitute appropritate values for k, lv, and gv;
|
||||||
|
// substitute indexes for fx, lx, and gx; substitute a number for n.
|
||||||
|
//
|
||||||
|
// In the function Message you will demonstrate a number of different ways
|
||||||
|
// to test the result of SearchInts. Note that you would probably never need
|
||||||
|
// all of these different tests in a real program. The point of the exercise
|
||||||
|
// is just to show that it is possible to identify a number of different
|
||||||
|
// conditions from the return value.
|
||||||
|
func TestMessage(t *testing.T) {
|
||||||
|
for _, test := range testData {
|
||||||
|
if !sort.IntsAreSorted(test.slice) {
|
||||||
|
t.Skip("Invalid test data")
|
||||||
|
}
|
||||||
|
if res := Message(test.slice, test.key); res != test.ref {
|
||||||
|
t.Fatalf("Message(%v, %d) =\n%q\nwant:\n%q",
|
||||||
|
test.slice, test.key, res, test.ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmarks also test searching larger random slices
|
||||||
|
|
||||||
|
func Benchmark1e2(b *testing.B) {
|
||||||
|
s := make([]int, 1e2)
|
||||||
|
for i := range s {
|
||||||
|
s[i] = rand.Intn(len(s))
|
||||||
|
}
|
||||||
|
sort.Ints(s)
|
||||||
|
k := rand.Intn(len(s))
|
||||||
|
ref := sort.SearchInts(s, k)
|
||||||
|
res := SearchInts(s, k)
|
||||||
|
if ref != res {
|
||||||
|
b.Fatalf(
|
||||||
|
"Search of %d values gave different answer than sort.SearchInts",
|
||||||
|
len(s))
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
SearchInts(s, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark1e4(b *testing.B) {
|
||||||
|
s := make([]int, 1e4)
|
||||||
|
for i := range s {
|
||||||
|
s[i] = rand.Intn(len(s))
|
||||||
|
}
|
||||||
|
sort.Ints(s)
|
||||||
|
k := rand.Intn(len(s))
|
||||||
|
ref := sort.SearchInts(s, k)
|
||||||
|
res := SearchInts(s, k)
|
||||||
|
if ref != res {
|
||||||
|
b.Fatalf(
|
||||||
|
"Search of %d values gave different answer than sort.SearchInts",
|
||||||
|
len(s))
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
SearchInts(s, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark1e6(b *testing.B) {
|
||||||
|
s := make([]int, 1e6)
|
||||||
|
for i := range s {
|
||||||
|
s[i] = rand.Intn(len(s))
|
||||||
|
}
|
||||||
|
sort.Ints(s)
|
||||||
|
k := rand.Intn(len(s))
|
||||||
|
ref := sort.SearchInts(s, k)
|
||||||
|
res := SearchInts(s, k)
|
||||||
|
if ref != res {
|
||||||
|
b.Fatalf(
|
||||||
|
"Search of %d values gave different answer than sort.SearchInts",
|
||||||
|
len(s))
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
SearchInts(s, k)
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,6 @@ package hexadecimal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,43 +12,42 @@ func ParseHex(in string) (int64, error) {
|
|||||||
func HandleErrors(in []string) []string {
|
func HandleErrors(in []string) []string {
|
||||||
var ret []string
|
var ret []string
|
||||||
for i := range in {
|
for i := range in {
|
||||||
|
errStr := "none"
|
||||||
_, err := convertNumStringBase(in[i], 16)
|
_, err := convertNumStringBase(in[i], 16)
|
||||||
ret = append(ret, err.Error())
|
if err != nil {
|
||||||
|
errStr = err.Error()
|
||||||
|
}
|
||||||
|
ret = append(ret, errStr)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertNumStringBase takes a numeric string and the base of that string
|
// convertNumStringBase takes a numeric string and the base of that string
|
||||||
// and returns an int64 of the decimal representation
|
// and returns an int64 of the decimal representation
|
||||||
func convertNumStringBase(in string, oldBase int64) (int64, error) {
|
func convertNumStringBase(in string, base int64) (int64, error) {
|
||||||
if in == "" {
|
if in == "" {
|
||||||
return 0, errors.New("syntax")
|
return 0, errors.New("syntax")
|
||||||
}
|
}
|
||||||
in = strings.ToLower(in)
|
in = strings.ToLower(in)
|
||||||
base := big.NewInt(oldBase)
|
var a, n int64
|
||||||
var sum big.Int
|
|
||||||
for i := range in {
|
for i := range in {
|
||||||
var x *big.Int
|
n *= base
|
||||||
if in[i] >= '0' && in[i] <= '9' {
|
switch {
|
||||||
x = big.NewInt(int64(in[i] - '0'))
|
case in[i] >= '0' && in[i] <= '9':
|
||||||
} else if in[i] >= 'a' && in[i] <= 'z' {
|
a = int64(in[i] - '0')
|
||||||
x = big.NewInt(int64(in[i]-'a') + 10)
|
case in[i] >= 'a' && in[i] <= 'f':
|
||||||
}
|
a = int64(in[i]-'a') + 10
|
||||||
fmt.Println("Comparing Converted Value ", x, "<", oldBase)
|
default:
|
||||||
if x.Int64() > oldBase {
|
|
||||||
return 0, errors.New("syntax")
|
return 0, errors.New("syntax")
|
||||||
}
|
}
|
||||||
|
if a > base {
|
||||||
|
return 0, errors.New("syntax")
|
||||||
|
}
|
||||||
|
n += a
|
||||||
|
|
||||||
pow := big.NewInt(int64(len(in) - i - 1))
|
if n < 0 {
|
||||||
|
return 0, errors.New("range")
|
||||||
var n big.Int
|
|
||||||
n.Exp(base, pow, nil)
|
|
||||||
n.Mul(x, &n)
|
|
||||||
|
|
||||||
sum.Add(&sum, &n)
|
|
||||||
if sum.Int64() < 0 {
|
|
||||||
return 0, errors.New("Integer Overflow")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sum.Int64(), nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
36
go/meetup/README.md
Normal file
36
go/meetup/README.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Meetup
|
||||||
|
|
||||||
|
Calculate the date of meetups.
|
||||||
|
|
||||||
|
Typically meetups happen on the same day of the week.
|
||||||
|
|
||||||
|
Examples are
|
||||||
|
|
||||||
|
- the first Monday
|
||||||
|
- the third Tuesday
|
||||||
|
- the Wednesteenth
|
||||||
|
- the last Thursday
|
||||||
|
|
||||||
|
Note that "Monteenth", "Tuesteenth", etc are all made up words. There
|
||||||
|
was a meetup whose members realised that there are exactly 7 days that
|
||||||
|
end in '-teenth'. Therefore, one is guaranteed that each day of the week
|
||||||
|
(Monday, Tuesday, ...) will have exactly one date that is named with '-teenth'
|
||||||
|
in every month.
|
||||||
|
|
||||||
|
To run the tests simply run the command `go test` in the exercise directory.
|
||||||
|
|
||||||
|
If the test suite contains benchmarks, you can run these with the `-bench`
|
||||||
|
flag:
|
||||||
|
|
||||||
|
go test -bench .
|
||||||
|
|
||||||
|
For more detailed info about the Go track see the [help
|
||||||
|
page](http://exercism.io/languages/go).
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Jeremy Hinegardner mentioned a Boulder meetup that happens on the Wednesteenth of every month [https://twitter.com/copiousfreetime](https://twitter.com/copiousfreetime)
|
||||||
|
|
||||||
|
## Submitting Incomplete Problems
|
||||||
|
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
||||||
|
|
110
go/meetup/cases_test.go
Normal file
110
go/meetup/cases_test.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package meetup
|
||||||
|
|
||||||
|
// Source: exercism/x-common
|
||||||
|
// Commit: b237b7b Merge pull request #124 from soniakeys/meetup-common-tests
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
var testCases = []struct {
|
||||||
|
year int
|
||||||
|
month time.Month
|
||||||
|
week WeekSchedule
|
||||||
|
weekday time.Weekday
|
||||||
|
expDay int
|
||||||
|
}{
|
||||||
|
{2013, 5, Teenth, time.Monday, 13}, // monteenth of May 2013
|
||||||
|
{2013, 8, Teenth, time.Monday, 19}, // monteenth of August 2013
|
||||||
|
{2013, 9, Teenth, time.Monday, 16}, // monteenth of September 2013
|
||||||
|
{2013, 3, Teenth, time.Tuesday, 19}, // tuesteenth of March 2013
|
||||||
|
{2013, 4, Teenth, time.Tuesday, 16}, // tuesteenth of April 2013
|
||||||
|
{2013, 8, Teenth, time.Tuesday, 13}, // tuesteenth of August 2013
|
||||||
|
{2013, 1, Teenth, time.Wednesday, 16}, // wednesteenth of January 2013
|
||||||
|
{2013, 2, Teenth, time.Wednesday, 13}, // wednesteenth of February 2013
|
||||||
|
{2013, 6, Teenth, time.Wednesday, 19}, // wednesteenth of June 2013
|
||||||
|
{2013, 5, Teenth, time.Thursday, 16}, // thursteenth of May 2013
|
||||||
|
{2013, 6, Teenth, time.Thursday, 13}, // thursteenth of June 2013
|
||||||
|
{2013, 9, Teenth, time.Thursday, 19}, // thursteenth of September 2013
|
||||||
|
{2013, 4, Teenth, time.Friday, 19}, // friteenth of April 2013
|
||||||
|
{2013, 8, Teenth, time.Friday, 16}, // friteenth of August 2013
|
||||||
|
{2013, 9, Teenth, time.Friday, 13}, // friteenth of September 2013
|
||||||
|
{2013, 2, Teenth, time.Saturday, 16}, // saturteenth of February 2013
|
||||||
|
{2013, 4, Teenth, time.Saturday, 13}, // saturteenth of April 2013
|
||||||
|
{2013, 10, Teenth, time.Saturday, 19}, // saturteenth of October 2013
|
||||||
|
{2013, 5, Teenth, time.Sunday, 19}, // sunteenth of May 2013
|
||||||
|
{2013, 6, Teenth, time.Sunday, 16}, // sunteenth of June 2013
|
||||||
|
{2013, 10, Teenth, time.Sunday, 13}, // sunteenth of October 2013
|
||||||
|
{2013, 3, First, time.Monday, 4}, // first Monday of March 2013
|
||||||
|
{2013, 4, First, time.Monday, 1}, // first Monday of April 2013
|
||||||
|
{2013, 5, First, time.Tuesday, 7}, // first Tuesday of May 2013
|
||||||
|
{2013, 6, First, time.Tuesday, 4}, // first Tuesday of June 2013
|
||||||
|
{2013, 7, First, time.Wednesday, 3}, // first Wednesday of July 2013
|
||||||
|
{2013, 8, First, time.Wednesday, 7}, // first Wednesday of August 2013
|
||||||
|
{2013, 9, First, time.Thursday, 5}, // first Thursday of September 2013
|
||||||
|
{2013, 10, First, time.Thursday, 3}, // first Thursday of October 2013
|
||||||
|
{2013, 11, First, time.Friday, 1}, // first Friday of November 2013
|
||||||
|
{2013, 12, First, time.Friday, 6}, // first Friday of December 2013
|
||||||
|
{2013, 1, First, time.Saturday, 5}, // first Saturday of January 2013
|
||||||
|
{2013, 2, First, time.Saturday, 2}, // first Saturday of February 2013
|
||||||
|
{2013, 3, First, time.Sunday, 3}, // first Sunday of March 2013
|
||||||
|
{2013, 4, First, time.Sunday, 7}, // first Sunday of April 2013
|
||||||
|
{2013, 3, Second, time.Monday, 11}, // second Monday of March 2013
|
||||||
|
{2013, 4, Second, time.Monday, 8}, // second Monday of April 2013
|
||||||
|
{2013, 5, Second, time.Tuesday, 14}, // second Tuesday of May 2013
|
||||||
|
{2013, 6, Second, time.Tuesday, 11}, // second Tuesday of June 2013
|
||||||
|
{2013, 7, Second, time.Wednesday, 10}, // second Wednesday of July 2013
|
||||||
|
{2013, 8, Second, time.Wednesday, 14}, // second Wednesday of August 2013
|
||||||
|
{2013, 9, Second, time.Thursday, 12}, // second Thursday of September 2013
|
||||||
|
{2013, 10, Second, time.Thursday, 10}, // second Thursday of October 2013
|
||||||
|
{2013, 11, Second, time.Friday, 8}, // second Friday of November 2013
|
||||||
|
{2013, 12, Second, time.Friday, 13}, // second Friday of December 2013
|
||||||
|
{2013, 1, Second, time.Saturday, 12}, // second Saturday of January 2013
|
||||||
|
{2013, 2, Second, time.Saturday, 9}, // second Saturday of February 2013
|
||||||
|
{2013, 3, Second, time.Sunday, 10}, // second Sunday of March 2013
|
||||||
|
{2013, 4, Second, time.Sunday, 14}, // second Sunday of April 2013
|
||||||
|
{2013, 3, Third, time.Monday, 18}, // third Monday of March 2013
|
||||||
|
{2013, 4, Third, time.Monday, 15}, // third Monday of April 2013
|
||||||
|
{2013, 5, Third, time.Tuesday, 21}, // third Tuesday of May 2013
|
||||||
|
{2013, 6, Third, time.Tuesday, 18}, // third Tuesday of June 2013
|
||||||
|
{2013, 7, Third, time.Wednesday, 17}, // third Wednesday of July 2013
|
||||||
|
{2013, 8, Third, time.Wednesday, 21}, // third Wednesday of August 2013
|
||||||
|
{2013, 9, Third, time.Thursday, 19}, // third Thursday of September 2013
|
||||||
|
{2013, 10, Third, time.Thursday, 17}, // third Thursday of October 2013
|
||||||
|
{2013, 11, Third, time.Friday, 15}, // third Friday of November 2013
|
||||||
|
{2013, 12, Third, time.Friday, 20}, // third Friday of December 2013
|
||||||
|
{2013, 1, Third, time.Saturday, 19}, // third Saturday of January 2013
|
||||||
|
{2013, 2, Third, time.Saturday, 16}, // third Saturday of February 2013
|
||||||
|
{2013, 3, Third, time.Sunday, 17}, // third Sunday of March 2013
|
||||||
|
{2013, 4, Third, time.Sunday, 21}, // third Sunday of April 2013
|
||||||
|
{2013, 3, Fourth, time.Monday, 25}, // fourth Monday of March 2013
|
||||||
|
{2013, 4, Fourth, time.Monday, 22}, // fourth Monday of April 2013
|
||||||
|
{2013, 5, Fourth, time.Tuesday, 28}, // fourth Tuesday of May 2013
|
||||||
|
{2013, 6, Fourth, time.Tuesday, 25}, // fourth Tuesday of June 2013
|
||||||
|
{2013, 7, Fourth, time.Wednesday, 24}, // fourth Wednesday of July 2013
|
||||||
|
{2013, 8, Fourth, time.Wednesday, 28}, // fourth Wednesday of August 2013
|
||||||
|
{2013, 9, Fourth, time.Thursday, 26}, // fourth Thursday of September 2013
|
||||||
|
{2013, 10, Fourth, time.Thursday, 24}, // fourth Thursday of October 2013
|
||||||
|
{2013, 11, Fourth, time.Friday, 22}, // fourth Friday of November 2013
|
||||||
|
{2013, 12, Fourth, time.Friday, 27}, // fourth Friday of December 2013
|
||||||
|
{2013, 1, Fourth, time.Saturday, 26}, // fourth Saturday of January 2013
|
||||||
|
{2013, 2, Fourth, time.Saturday, 23}, // fourth Saturday of February 2013
|
||||||
|
{2013, 3, Fourth, time.Sunday, 24}, // fourth Sunday of March 2013
|
||||||
|
{2013, 4, Fourth, time.Sunday, 28}, // fourth Sunday of April 2013
|
||||||
|
{2013, 3, Last, time.Monday, 25}, // last Monday of March 2013
|
||||||
|
{2013, 4, Last, time.Monday, 29}, // last Monday of April 2013
|
||||||
|
{2013, 5, Last, time.Tuesday, 28}, // last Tuesday of May 2013
|
||||||
|
{2013, 6, Last, time.Tuesday, 25}, // last Tuesday of June 2013
|
||||||
|
{2013, 7, Last, time.Wednesday, 31}, // last Wednesday of July 2013
|
||||||
|
{2013, 8, Last, time.Wednesday, 28}, // last Wednesday of August 2013
|
||||||
|
{2013, 9, Last, time.Thursday, 26}, // last Thursday of September 2013
|
||||||
|
{2013, 10, Last, time.Thursday, 31}, // last Thursday of October 2013
|
||||||
|
{2013, 11, Last, time.Friday, 29}, // last Friday of November 2013
|
||||||
|
{2013, 12, Last, time.Friday, 27}, // last Friday of December 2013
|
||||||
|
{2013, 1, Last, time.Saturday, 26}, // last Saturday of January 2013
|
||||||
|
{2013, 2, Last, time.Saturday, 23}, // last Saturday of February 2013
|
||||||
|
{2013, 3, Last, time.Sunday, 31}, // last Sunday of March 2013
|
||||||
|
{2013, 4, Last, time.Sunday, 28}, // last Sunday of April 2013
|
||||||
|
{2012, 2, Last, time.Wednesday, 29}, // last Wednesday of February 2012
|
||||||
|
{2014, 12, Last, time.Wednesday, 31}, // last Wednesday of December 2014
|
||||||
|
{2015, 2, Last, time.Sunday, 22}, // last Sunday of February 2015
|
||||||
|
{2012, 12, First, time.Friday, 7}, // first Friday of December 2012
|
||||||
|
}
|
76
go/meetup/meetup.go
Normal file
76
go/meetup/meetup.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package meetup
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const testVersion = 3
|
||||||
|
|
||||||
|
type WeekSchedule int
|
||||||
|
|
||||||
|
const (
|
||||||
|
First WeekSchedule = iota
|
||||||
|
Second
|
||||||
|
Third
|
||||||
|
Fourth
|
||||||
|
Last
|
||||||
|
Teenth
|
||||||
|
)
|
||||||
|
|
||||||
|
// Day calculates which day of the month the requested day falls on
|
||||||
|
func Day(week WeekSchedule, weekday time.Weekday, month time.Month, year int) int {
|
||||||
|
var t time.Time
|
||||||
|
switch week {
|
||||||
|
case First:
|
||||||
|
t = time.Date(year, month, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
if t.Weekday() == weekday {
|
||||||
|
return t.Day()
|
||||||
|
}
|
||||||
|
case Second:
|
||||||
|
t = time.Date(year, month, 8, 0, 0, 0, 0, time.UTC)
|
||||||
|
if t.Weekday() == weekday {
|
||||||
|
return t.Day()
|
||||||
|
}
|
||||||
|
case Third:
|
||||||
|
t = time.Date(year, month, 15, 0, 0, 0, 0, time.UTC)
|
||||||
|
if t.Weekday() == weekday {
|
||||||
|
return t.Day()
|
||||||
|
}
|
||||||
|
case Fourth:
|
||||||
|
t = time.Date(year, month, 22, 0, 0, 0, 0, time.UTC)
|
||||||
|
if t.Weekday() == weekday {
|
||||||
|
return t.Day()
|
||||||
|
}
|
||||||
|
case Last:
|
||||||
|
t = time.Date(year, month+1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
t = t.AddDate(0, 0, -1)
|
||||||
|
if t.Weekday() == weekday {
|
||||||
|
return t.Day()
|
||||||
|
}
|
||||||
|
return findPrevDay(weekday, t).Day()
|
||||||
|
case Teenth:
|
||||||
|
t = time.Date(year, month, 13, 0, 0, 0, 0, time.UTC)
|
||||||
|
if t.Weekday() == weekday {
|
||||||
|
return t.Day()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return findNextDay(weekday, t).Day()
|
||||||
|
}
|
||||||
|
|
||||||
|
// findNextDay finds the next 'weekday' after t
|
||||||
|
func findNextDay(weekday time.Weekday, t time.Time) time.Time {
|
||||||
|
t = t.AddDate(0, 0, 1)
|
||||||
|
dow := t.Weekday()
|
||||||
|
for ; dow != weekday; dow = t.Weekday() {
|
||||||
|
t = t.AddDate(0, 0, 1)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// findPrevDay finds the previous 'weekday' before t
|
||||||
|
func findPrevDay(weekday time.Weekday, t time.Time) time.Time {
|
||||||
|
t = t.AddDate(0, 0, -1)
|
||||||
|
dow := t.Weekday()
|
||||||
|
for ; dow != weekday; dow = t.Weekday() {
|
||||||
|
t = t.AddDate(0, 0, -1)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
39
go/meetup/meetup_test.go
Normal file
39
go/meetup/meetup_test.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package meetup
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
/* API
|
||||||
|
package meetup
|
||||||
|
type Weekschedule
|
||||||
|
WeekSchedule First
|
||||||
|
WeekSchedule Second
|
||||||
|
WeekSchedule Third
|
||||||
|
WeekSchedule Fourth
|
||||||
|
WeekSchedule Last
|
||||||
|
WeekSchedule Teenth
|
||||||
|
func Day(WeekSchedule, time.Weekday, time.Month, int) int
|
||||||
|
*/
|
||||||
|
|
||||||
|
const targetTestVersion = 3
|
||||||
|
|
||||||
|
var weekName = map[WeekSchedule]string{
|
||||||
|
First: "first",
|
||||||
|
Second: "second",
|
||||||
|
Third: "third",
|
||||||
|
Fourth: "fourth",
|
||||||
|
Teenth: "teenth",
|
||||||
|
Last: "last",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDay(t *testing.T) {
|
||||||
|
if testVersion != targetTestVersion {
|
||||||
|
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
res := Day(test.week, test.weekday, test.month, test.year)
|
||||||
|
if res != test.expDay {
|
||||||
|
t.Fatalf("For %s %s of %s 2013 got date of %d, want %d",
|
||||||
|
weekName[test.week], test.weekday, test.month, res, test.expDay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
go/tree-building/README.md
Normal file
31
go/tree-building/README.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Tree Building
|
||||||
|
|
||||||
|
Refactor a tree building algorithm.
|
||||||
|
|
||||||
|
Some web-forums have a tree layout, so posts are presented as a tree. However
|
||||||
|
the posts are typically stored in a database as an unsorted set of records. Thus
|
||||||
|
when presenting the posts to the user the tree structure has to be
|
||||||
|
reconstructed.
|
||||||
|
|
||||||
|
Your job will be to refactor a working but slow and ugly piece of code that
|
||||||
|
implements the tree building logic for highly abstracted records. The records
|
||||||
|
only contain an ID number and a parent ID number. The ID number is always
|
||||||
|
between 0 (inclusive) and the length of the record list (exclusive). No record
|
||||||
|
has a parent ID lower than its own ID and no record, except the root record,
|
||||||
|
has a parent ID that's equal to its own ID.
|
||||||
|
|
||||||
|
To run the tests simply run the command `go test` in the exercise directory.
|
||||||
|
|
||||||
|
If the test suite contains benchmarks, you can run these with the `-bench`
|
||||||
|
flag:
|
||||||
|
|
||||||
|
go test -bench .
|
||||||
|
|
||||||
|
For more detailed info about the Go track see the [help
|
||||||
|
page](http://exercism.io/languages/go).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Submitting Incomplete Problems
|
||||||
|
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
||||||
|
|
109
go/tree-building/tree_building.go
Normal file
109
go/tree-building/tree_building.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package tree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testVersion = 3
|
||||||
|
|
||||||
|
type Record struct {
|
||||||
|
ID, Parent int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
ID int
|
||||||
|
Children []*Node
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mismatch struct{}
|
||||||
|
|
||||||
|
func (m Mismatch) Error() string {
|
||||||
|
return "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
func Build(records []Record) (*Node, error) {
|
||||||
|
if len(records) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
root := &Node{}
|
||||||
|
todo := []*Node{root}
|
||||||
|
n := 1
|
||||||
|
for {
|
||||||
|
if len(todo) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
newTodo := []*Node(nil)
|
||||||
|
for _, c := range todo {
|
||||||
|
for _, r := range records {
|
||||||
|
if r.Parent == c.ID {
|
||||||
|
if r.ID < c.ID {
|
||||||
|
return nil, errors.New("a")
|
||||||
|
} else if r.ID == c.ID {
|
||||||
|
if r.ID != 0 {
|
||||||
|
return nil, fmt.Errorf("b")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n++
|
||||||
|
switch len(c.Children) {
|
||||||
|
case 0:
|
||||||
|
nn := &Node{ID: r.ID}
|
||||||
|
c.Children = []*Node{nn}
|
||||||
|
newTodo = append(newTodo, nn)
|
||||||
|
case 1:
|
||||||
|
nn := &Node{ID: r.ID}
|
||||||
|
if c.Children[0].ID < r.ID {
|
||||||
|
c.Children = []*Node{c.Children[0], nn}
|
||||||
|
newTodo = append(newTodo, nn)
|
||||||
|
} else {
|
||||||
|
c.Children = []*Node{nn, c.Children[0]}
|
||||||
|
newTodo = append(newTodo, nn)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
nn := &Node{ID: r.ID}
|
||||||
|
newTodo = append(newTodo, nn)
|
||||||
|
breakpoint:
|
||||||
|
for _ = range []bool{false} {
|
||||||
|
for i, cc := range c.Children {
|
||||||
|
if cc.ID > r.ID {
|
||||||
|
a := make([]*Node, len(c.Children)+1)
|
||||||
|
copy(a, c.Children[:i])
|
||||||
|
copy(a[i+1:], c.Children[i:])
|
||||||
|
copy(a[i:i+1], []*Node{nn})
|
||||||
|
c.Children = a
|
||||||
|
break breakpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Children = append(c.Children, nn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
todo = newTodo
|
||||||
|
}
|
||||||
|
if n != len(records) {
|
||||||
|
return nil, Mismatch{}
|
||||||
|
}
|
||||||
|
if err := chk(root, len(records)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return root, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func chk(n *Node, m int) (err error) {
|
||||||
|
if n.ID > m {
|
||||||
|
return fmt.Errorf("z")
|
||||||
|
} else if n.ID == m {
|
||||||
|
return fmt.Errorf("y")
|
||||||
|
} else {
|
||||||
|
for i := 0; i < len(n.Children); i++ {
|
||||||
|
err = chk(n.Children[i], m)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
305
go/tree-building/tree_test.go
Normal file
305
go/tree-building/tree_test.go
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
package tree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Define a function Build(records []Record) (*Node, error)
|
||||||
|
// where Record is a struct containing int fields ID and Parent
|
||||||
|
// and Node is a struct containing int field ID and []*Node field Children.
|
||||||
|
//
|
||||||
|
// Also define a testVersion with a value that matches
|
||||||
|
// the targetTestVersion here.
|
||||||
|
|
||||||
|
const targetTestVersion = 3
|
||||||
|
|
||||||
|
var successTestCases = []struct {
|
||||||
|
name string
|
||||||
|
input []Record
|
||||||
|
expected *Node
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty input",
|
||||||
|
input: []Record{},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one node",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 0},
|
||||||
|
},
|
||||||
|
expected: &Node{
|
||||||
|
ID: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "three nodes in order",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 0},
|
||||||
|
{ID: 1, Parent: 0},
|
||||||
|
{ID: 2, Parent: 0},
|
||||||
|
},
|
||||||
|
expected: &Node{
|
||||||
|
ID: 0,
|
||||||
|
Children: []*Node{
|
||||||
|
{ID: 1},
|
||||||
|
{ID: 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "three nodes in reverse order",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 2, Parent: 0},
|
||||||
|
{ID: 1, Parent: 0},
|
||||||
|
{ID: 0},
|
||||||
|
},
|
||||||
|
expected: &Node{
|
||||||
|
ID: 0,
|
||||||
|
Children: []*Node{
|
||||||
|
{ID: 1},
|
||||||
|
{ID: 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "more than two children",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 3, Parent: 0},
|
||||||
|
{ID: 2, Parent: 0},
|
||||||
|
{ID: 1, Parent: 0},
|
||||||
|
{ID: 0},
|
||||||
|
},
|
||||||
|
expected: &Node{
|
||||||
|
ID: 0,
|
||||||
|
Children: []*Node{
|
||||||
|
{ID: 1},
|
||||||
|
{ID: 2},
|
||||||
|
{ID: 3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "binary tree",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 5, Parent: 1},
|
||||||
|
{ID: 3, Parent: 2},
|
||||||
|
{ID: 2, Parent: 0},
|
||||||
|
{ID: 4, Parent: 1},
|
||||||
|
{ID: 1, Parent: 0},
|
||||||
|
{ID: 0},
|
||||||
|
{ID: 6, Parent: 2},
|
||||||
|
},
|
||||||
|
expected: &Node{
|
||||||
|
ID: 0,
|
||||||
|
Children: []*Node{
|
||||||
|
{
|
||||||
|
ID: 1,
|
||||||
|
Children: []*Node{
|
||||||
|
{ID: 4},
|
||||||
|
{ID: 5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 2,
|
||||||
|
Children: []*Node{
|
||||||
|
{ID: 3},
|
||||||
|
{ID: 6},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unbalanced tree",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 5, Parent: 2},
|
||||||
|
{ID: 3, Parent: 2},
|
||||||
|
{ID: 2, Parent: 0},
|
||||||
|
{ID: 4, Parent: 1},
|
||||||
|
{ID: 1, Parent: 0},
|
||||||
|
{ID: 0},
|
||||||
|
{ID: 6, Parent: 2},
|
||||||
|
},
|
||||||
|
expected: &Node{
|
||||||
|
ID: 0,
|
||||||
|
Children: []*Node{
|
||||||
|
{
|
||||||
|
ID: 1,
|
||||||
|
Children: []*Node{
|
||||||
|
{ID: 4},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 2,
|
||||||
|
Children: []*Node{
|
||||||
|
{ID: 3},
|
||||||
|
{ID: 5},
|
||||||
|
{ID: 6},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var failureTestCases = []struct {
|
||||||
|
name string
|
||||||
|
input []Record
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "root node has parent",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 0, Parent: 1},
|
||||||
|
{ID: 1, Parent: 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no root node",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 1, Parent: 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-continuous",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 2, Parent: 0},
|
||||||
|
{ID: 4, Parent: 2},
|
||||||
|
{ID: 1, Parent: 0},
|
||||||
|
{ID: 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cycle directly",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 5, Parent: 2},
|
||||||
|
{ID: 3, Parent: 2},
|
||||||
|
{ID: 2, Parent: 2},
|
||||||
|
{ID: 4, Parent: 1},
|
||||||
|
{ID: 1, Parent: 0},
|
||||||
|
{ID: 0},
|
||||||
|
{ID: 6, Parent: 3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cycle indirectly",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 5, Parent: 2},
|
||||||
|
{ID: 3, Parent: 2},
|
||||||
|
{ID: 2, Parent: 6},
|
||||||
|
{ID: 4, Parent: 1},
|
||||||
|
{ID: 1, Parent: 0},
|
||||||
|
{ID: 0},
|
||||||
|
{ID: 6, Parent: 3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "higher id parent of lower id",
|
||||||
|
input: []Record{
|
||||||
|
{ID: 0},
|
||||||
|
{ID: 2, Parent: 0},
|
||||||
|
{ID: 1, Parent: 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n Node) String() string {
|
||||||
|
return fmt.Sprintf("%d:%s", n.ID, n.Children)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMakeTreeSuccess(t *testing.T) {
|
||||||
|
if testVersion != targetTestVersion {
|
||||||
|
t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
|
||||||
|
}
|
||||||
|
for _, tt := range successTestCases {
|
||||||
|
actual, err := Build(tt.input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Build for test case %q returned error %q. Error not expected.",
|
||||||
|
tt.name, err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actual, tt.expected) {
|
||||||
|
t.Fatalf("Build for test case %q returned %s but was expected to return %s.",
|
||||||
|
tt.name, tt.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMakeTreeFailure(t *testing.T) {
|
||||||
|
for _, tt := range failureTestCases {
|
||||||
|
actual, err := Build(tt.input)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Build for test case %q returned %s but was expected to fail.",
|
||||||
|
tt.name, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func shuffleRecords(records []Record) []Record {
|
||||||
|
rand := rand.New(rand.NewSource(42))
|
||||||
|
newRecords := make([]Record, len(records))
|
||||||
|
for i, idx := range rand.Perm(len(records)) {
|
||||||
|
newRecords[i] = records[idx]
|
||||||
|
}
|
||||||
|
return newRecords
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary tree
|
||||||
|
func makeTwoTreeRecords() []Record {
|
||||||
|
records := make([]Record, 1<<16)
|
||||||
|
for i := range records {
|
||||||
|
if i == 0 {
|
||||||
|
records[i] = Record{ID: 0}
|
||||||
|
} else {
|
||||||
|
records[i] = Record{ID: i, Parent: i >> 1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return shuffleRecords(records)
|
||||||
|
}
|
||||||
|
|
||||||
|
var twoTreeRecords = makeTwoTreeRecords()
|
||||||
|
|
||||||
|
func BenchmarkTwoTree(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Build(twoTreeRecords)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each node but the root node and leaf nodes has ten children.
|
||||||
|
func makeTenTreeRecords() []Record {
|
||||||
|
records := make([]Record, 10000)
|
||||||
|
for i := range records {
|
||||||
|
if i == 0 {
|
||||||
|
records[i] = Record{ID: 0}
|
||||||
|
} else {
|
||||||
|
records[i] = Record{ID: i, Parent: i / 10}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return shuffleRecords(records)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tenTreeRecords = makeTenTreeRecords()
|
||||||
|
|
||||||
|
func BenchmarkTenTree(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Build(tenTreeRecords)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeShallowRecords() []Record {
|
||||||
|
records := make([]Record, 10000)
|
||||||
|
for i := range records {
|
||||||
|
records[i] = Record{ID: i, Parent: 0}
|
||||||
|
}
|
||||||
|
return shuffleRecords(records)
|
||||||
|
}
|
||||||
|
|
||||||
|
var shallowRecords = makeShallowRecords()
|
||||||
|
|
||||||
|
func BenchmarkShallowTree(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Build(shallowRecords)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user