Syncing Up

This commit is contained in:
Brian Buller 2016-09-09 11:24:29 -05:00
parent 148fad68a9
commit 2f99878b09
14 changed files with 1352 additions and 24 deletions

View 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.

View 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
}

View 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)
}
}

View 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.

View 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)
}

View 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)
}
}

View File

@ -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
View 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
View 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
View 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
View 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)
}
}
}

View 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.

View 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
}
}

View 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)
}
}