diff --git a/go/atbash-cipher/README.md b/go/atbash-cipher/README.md new file mode 100644 index 0000000..939c038 --- /dev/null +++ b/go/atbash-cipher/README.md @@ -0,0 +1,46 @@ +# Atbash Cipher + +Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East. + +The Atbash cipher is a simple substitution cipher that relies on +transposing all the letters in the alphabet such that the resulting +alphabet is backwards. The first letter is replaced with the last +letter, the second with the second-last, and so on. + +An Atbash cipher for the Latin alphabet would be as follows: + +```plain +Plain: abcdefghijklmnopqrstuvwxyz +Cipher: zyxwvutsrqponmlkjihgfedcba +``` + +It is a very weak cipher because it only has one possible key, and it is +a simple monoalphabetic substitution cipher. However, this may not have +been an issue in the cipher's time. + +Ciphertext is written out in groups of fixed length, the traditional group size +being 5 letters, and punctuation is excluded. This is to make it harder to guess +things based on word boundaries. + +## Examples +- Encoding `test` gives `gvhg` +- Decoding `gvhg` gives `test` +- Decoding `gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt` gives `The quick brown fox jumps over the lazy dog.` + +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/Atbash](http://en.wikipedia.org/wiki/Atbash) + +## Submitting Incomplete Problems +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + diff --git a/go/atbash-cipher/atbash.go b/go/atbash-cipher/atbash.go new file mode 100644 index 0000000..6d81291 --- /dev/null +++ b/go/atbash-cipher/atbash.go @@ -0,0 +1,36 @@ +package atbash + +import "strings" + +func Atbash(s string) string { + s = strings.ToLower(s) + var v []byte + var b, iter int + for i := range s { + sv := int(s[i]) + switch { + case sv < 48: + continue + case sv >= 48 && sv <= 57: + b = sv + case sv < 109: + b = 110 + (109 - sv) + case sv == 109: + b = 110 + case sv == 110: + b = 109 + case sv > 110: + b = 109 - (sv - 110) + } + v = append(v, byte(b)) + iter++ + if iter == 5 { + v = append(v, ' ') + iter = 0 + } + } + if iter == 0 { + return string(v)[:len(string(v))-1] + } + return string(v) +} diff --git a/go/atbash-cipher/atbash_cipher_test.go b/go/atbash-cipher/atbash_cipher_test.go new file mode 100644 index 0000000..08f274e --- /dev/null +++ b/go/atbash-cipher/atbash_cipher_test.go @@ -0,0 +1,40 @@ +package atbash + +import "testing" + +var tests = []struct { + expected string + s string +}{ + {"ml", "no"}, + {"ml", "no"}, + {"bvh", "yes"}, + {"lnt", "OMG"}, + {"lnt", "O M G"}, + {"nrmwy oldrm tob", "mindblowingly"}, + {"gvhgr mt123 gvhgr mt", "Testing, 1 2 3, testing."}, + {"gifgs rhurx grlm", "Truth is fiction."}, + {"gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt", "The quick brown fox jumps over the lazy dog."}, +} + +func TestAtbash(t *testing.T) { + for _, test := range tests { + actual := Atbash(test.s) + if actual != test.expected { + t.Errorf("Atbash(%s): expected %s, actual %s", test.s, test.expected, actual) + } + } +} + +func BenchmarkAtbash(b *testing.B) { + b.StopTimer() + for _, test := range tests { + b.StartTimer() + + for i := 0; i < b.N; i++ { + Atbash(test.s) + } + + b.StopTimer() + } +} diff --git a/go/atbash-cipher/cmd/cmd b/go/atbash-cipher/cmd/cmd new file mode 100755 index 0000000..7dbaafd Binary files /dev/null and b/go/atbash-cipher/cmd/cmd differ diff --git a/go/atbash-cipher/cmd/main.go b/go/atbash-cipher/cmd/main.go new file mode 100644 index 0000000..2790129 --- /dev/null +++ b/go/atbash-cipher/cmd/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + + "../../atbash-cipher" +) + +func main() { + fmt.Println(atbash.Atbash("mindblowingly")) +} diff --git a/go/current b/go/current index bcc6b93..4e19c2a 120000 --- a/go/current +++ b/go/current @@ -1 +1 @@ -custom-set \ No newline at end of file +prime-factors \ No newline at end of file diff --git a/go/custom-set/cmd/cmd b/go/custom-set/cmd/cmd index 23eb61c..983108e 100755 Binary files a/go/custom-set/cmd/cmd and b/go/custom-set/cmd/cmd differ diff --git a/go/custom-set/cmd/main.go b/go/custom-set/cmd/main.go index 88c867e..2260c70 100644 --- a/go/custom-set/cmd/main.go +++ b/go/custom-set/cmd/main.go @@ -11,9 +11,9 @@ func main() { s1 := stringset.NewFromSlice([]string{"a"}) addAndOutput(s1, "b") addAndOutput(s1, "d") + addAndOutput(s1, "c") s1.PrettyPrint() return - addAndOutput(s1, "c") addAndOutput(s1, "0") addAndOutput(s1, "aa") addAndOutput(s1, "aaa") diff --git a/go/custom-set/custom_set_test.go b/go/custom-set/custom_set_test.go index c2cacb5..7b8d0ba 100644 --- a/go/custom-set/custom_set_test.go +++ b/go/custom-set/custom_set_test.go @@ -27,7 +27,6 @@ package stringset // Format the empty set as {}. import ( - "fmt" "math/rand" "reflect" "strconv" @@ -167,17 +166,13 @@ func TestEqual(t *testing.T) { // helper for testing Add, Delete func testEleOp(name string, op func(Set, string), cases []eleOpCase, t *testing.T) { for _, tc := range cases { - fmt.Print("Running Test Case: ") - fmt.Println(tc) s := NewFromSlice(tc.set) op(s, tc.ele) want := NewFromSlice(tc.want) if !Equal(s, want) { - fmt.Println(s.String()) t.Fatalf("%v %s %q = %v, want %v", NewFromSlice(tc.set), name, tc.ele, s, want) } - fmt.Println("=== Done ===") } } diff --git a/go/custom-set/stringset.go b/go/custom-set/stringset.go index f99a4c6..2f3867b 100644 --- a/go/custom-set/stringset.go +++ b/go/custom-set/stringset.go @@ -3,7 +3,6 @@ package stringset import ( "errors" "fmt" - "strconv" "strings" ) @@ -45,20 +44,12 @@ func (s Set) Add(v string) { return } if !s.Has(v) { - bef := "Current Top Node: " + s.top.value + ";L:" + s.top.getLeftValue() + ";R:" + s.top.getRightValue() newNode := s.top.Add(&Node{value: v}) - fmt.Println(bef) - fmt.Println("Current New Node: " + newNode.value + ";L:" + newNode.getLeftValue() + ";R:" + newNode.getRightValue()) - fmt.Printf("newNode.left: %v (%v)\n", &newNode.left, newNode.left) + s.top.value = newNode.value s.top.left = newNode.left s.top.right = newNode.right - fmt.Println("Current Top Node: " + s.top.value + ";L:" + s.top.getLeftValue() + ";R:" + s.top.getRightValue()) - fmt.Printf("s.top.left: %v (%v)\n", &s.top.left, s.top.left) - fmt.Println("Add Done") - //s.top = s.top.Add(newNode) - //fmt.Println("Current Top Node: " + s.top.value + ";L:" + s.top.getLeftValue() + ";R:" + s.top.getRightValue()) } } @@ -274,63 +265,43 @@ func (sv *Node) findHome(v *Node) { } func (sv *Node) Add(n *Node) *Node { - fmt.Println("1 Adding at Node " + sv.value) cmp := strings.Compare(n.value, sv.value) if cmp < 0 { - fmt.Println("2 checking to left") if sv.left == nil { - fmt.Println("3 left is nil, add it") sv.left = n } else { - fmt.Println("--> 4 recurse left") sv.left = sv.left.Add(n) - fmt.Println("<-- 5 added to left") } } else { - fmt.Println("6 checking to right") if sv.right == nil { - fmt.Println("7 right is nil, add it") sv.right = n } else { - fmt.Println("--> 8 recurse right") sv.right = sv.right.Add(n) - fmt.Println("<-- 9 added to right") } } - fmt.Println("10 let's balance now (sv: " + sv.value + ")") + newVal := sv + /* TODO: Fix the tree balancing balance := GetBalance(sv) // left left unbalance - fmt.Println("11 balance: " + strconv.Itoa(balance)) - newVal := sv - if balance > 1 && strings.Compare(n.value, sv.getLeftValue()) < 0 { // < sv.left.value { - fmt.Println("12 Left Left") + if balance > 1 && strings.Compare(n.value, sv.getLeftValue()) < 0 { newVal = RightRotate(sv) - fmt.Println("13") } // right right unbalance - if balance < -1 && strings.Compare(n.value, sv.getRightValue()) > 0 { //n.value > sv.right.value { - fmt.Println("14 Right Right") + if balance < -1 && strings.Compare(n.value, sv.getRightValue()) > 0 { newVal = LeftRotate(sv) - fmt.Println("15") } // left right unbalance - if balance > 1 && strings.Compare(n.value, sv.getLeftValue()) > 0 { //n.value > sv.left.value { - fmt.Println("16 Left Right") + if balance > 1 && strings.Compare(n.value, sv.getLeftValue()) > 0 { sv.left = LeftRotate(sv.left) - fmt.Println("17") newVal = RightRotate(sv) - fmt.Println("18") } // right left unbalance - if balance < -1 && strings.Compare(n.value, sv.getRightValue()) < 0 { //n.value < sv.right.value { - fmt.Println("19 Right Left") + if balance < -1 && strings.Compare(n.value, sv.getRightValue()) < 0 { sv.right = RightRotate(sv.right) - fmt.Println("20") newVal = LeftRotate(sv) - fmt.Println("21") } - fmt.Println("22 end add") + */ return newVal } @@ -476,9 +447,6 @@ func RightRotate(y *Node) *Node { // Perform rotation x.right = y y.left = t2 - fmt.Println("RightRotate: return=" + x.value + "; L:" + x.left.value + "; R:" + x.right.value) - fmt.Println("RightRotate: leftNode=" + x.getLeftValue() + "; L:" + x.left.getLeftValue() + "; R:" + x.left.getRightValue()) - fmt.Println("RightRotate: rightNode=" + x.getRightValue() + "; L:" + x.right.getLeftValue() + "; R:" + x.right.getRightValue()) // Return new root return x } @@ -490,9 +458,6 @@ func LeftRotate(x *Node) *Node { y.left = x x.right = t2 // Return new root - fmt.Println("LeftRotate: return=" + y.value + "; L:" + y.left.value + "; R:" + y.right.value) - fmt.Println("LeftRotate: leftNode=" + y.getLeftValue() + "; L:" + y.left.getLeftValue() + "; R:" + y.left.getRightValue()) - fmt.Println("LeftRotate: rightNode=" + y.getRightValue() + "; L:" + y.right.getLeftValue() + "; R:" + y.right.getRightValue()) return y } diff --git a/go/hello-world/README.md b/go/hello-world/README.md new file mode 100644 index 0000000..42aa266 --- /dev/null +++ b/go/hello-world/README.md @@ -0,0 +1,58 @@ +# Hello World + +Write a function that greets the user by name, or by saying "Hello, World!" if no name is given. + +["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is the traditional first program for beginning programming in a new language. + +**Note:** You can skip this exercise by running: + + exercism skip $LANGUAGE hello-world + +## Specification + +Write a `Hello World!` function that can greet someone given their name. +The function should return the appropriate greeting. + +For an input of "Alice", the response should be "Hello, Alice!". + +If a name is not given, the response should be "Hello, World!" + +## Test-Driven Development + +As programmers mature, they eventually want to test their code. + +Here at Exercism we simulate [Test-Driven Development](http://en.wikipedia.org/wiki/Test-driven_development) (TDD), where you write your tests before writing any functionality. The simulation comes in the form of a pre-written test suite, which will signal that you have solved the problem. + +It will also provide you with a safety net to explore other solutions without breaking the functionality. + +### A typical TDD workflow on Exercism: + +1. Run the test file and pick one test that's failing. +2. Write some code to fix the test you picked. +3. Re-run the tests to confirm the test is now passing. +4. Repeat from step 1. +5. Submit your solution (`exercism submit /path/to/file`) + +## Instructions + +Submissions are encouraged to be general, within reason. Having said that, it's also important not to over-engineer a solution. + +It's important to remember that the goal is to make code as expressive and readable as we can. However, solutions to the hello-world exercise will not be reviewed by a person, but by rikki- the robot, who will offer an encouraging word. + +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 + +This is an exercise to introduce users to using Exercism [http://en.wikipedia.org/wiki/%22Hello,_world!%22_program](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) + +## Submitting Incomplete Problems +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + diff --git a/go/hello-world/hello_test.go b/go/hello-world/hello_test.go new file mode 100644 index 0000000..3099d41 --- /dev/null +++ b/go/hello-world/hello_test.go @@ -0,0 +1,29 @@ +package hello + +import "testing" + +// Define a function HelloWorld(string) string. +// +// Also define a testVersion with a value that matches +// the targetTestVersion here. + +const targetTestVersion = 2 + +func TestHelloWorld(t *testing.T) { + tests := []struct { + name, expected string + }{ + {"", "Hello, World!"}, + {"Gopher", "Hello, Gopher!"}, + } + for _, test := range tests { + observed := HelloWorld(test.name) + if observed != test.expected { + t.Fatalf("HelloWorld(%s) = %v, want %v", test.name, observed, test.expected) + } + } + + if testVersion != targetTestVersion { + t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion) + } +} diff --git a/go/hello-world/hello_world.go b/go/hello-world/hello_world.go new file mode 100644 index 0000000..aceb1dc --- /dev/null +++ b/go/hello-world/hello_world.go @@ -0,0 +1,11 @@ +package hello + +const testVersion = 2 + +// HelloWorld takes a string (optionally empty) and says hello to it! +func HelloWorld(s string) string { + if s == "" { + s = "World" + } + return "Hello, " + s + "!" +} diff --git a/go/phone-number/README.md b/go/phone-number/README.md new file mode 100644 index 0000000..ce88c4e --- /dev/null +++ b/go/phone-number/README.md @@ -0,0 +1,38 @@ +# Phone Number + +Write a program that cleans up user-entered phone numbers so that they can be sent SMS messages. + +The rules are as follows: + +- If the phone number is less than 10 digits assume that it is bad + number +- If the phone number is 10 digits assume that it is good +- If the phone number is 11 digits and the first number is 1, trim the 1 + and use the last 10 digits +- If the phone number is 11 digits and the first number is not 1, then + it is a bad number +- If the phone number is more than 11 digits assume that it is a bad + number + +We've provided tests, now make them pass. + +Hint: Only make one test pass at a time. Disable the others, then flip +each on in turn after you get the current failing one to pass. + +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 + +Event Manager by JumpstartLab [http://tutorials.jumpstartlab.com/projects/eventmanager.html](http://tutorials.jumpstartlab.com/projects/eventmanager.html) + +## Submitting Incomplete Problems +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + diff --git a/go/phone-number/phone_number_test.go b/go/phone-number/phone_number_test.go new file mode 100644 index 0000000..2effa00 --- /dev/null +++ b/go/phone-number/phone_number_test.go @@ -0,0 +1,118 @@ +package phonenumber + +import ( + "testing" +) + +type testCase struct { + input string + expected string + expectErr bool +} + +var numberTests = []testCase{ + {"(123) 456-7890", "1234567890", false}, + {"123.456.7890", "1234567890", false}, + {"1234567890", "1234567890", false}, + {"12345678901234567", "", true}, + {"21234567890", "", true}, + {"123456789", "", true}, +} + +func TestNumber(t *testing.T) { + for _, test := range numberTests { + actual, actualErr := Number(test.input) + if actual != test.expected { + t.Errorf("Number(%s): expected [%s], actual: [%s]", test.input, test.expected, actual) + } + // if we expect an error and there isn't one + if test.expectErr && actualErr == nil { + t.Errorf("Number(%s): expected an error, but error is nil", test.input) + } + // if we don't expect an error and there is one + if !test.expectErr && actualErr != nil { + t.Errorf("Number(%s): expected no error, but error is: %s", test.input, actualErr) + } + } +} + +func BenchmarkNumber(b *testing.B) { + b.StopTimer() + for _, test := range numberTests { + b.StartTimer() + for i := 0; i < b.N; i++ { + Number(test.input) + } + b.StopTimer() + } +} + +var areaCodeTests = []testCase{ + {"1234567890", "123", false}, + {"213.456.7890", "213", false}, + {"213.456.7890.2345", "", true}, + {"213.456", "", true}, +} + +func TestAreaCode(t *testing.T) { + for _, test := range areaCodeTests { + actual, actualErr := AreaCode(test.input) + if actual != test.expected { + t.Errorf("AreaCode(%s): expected [%s], actual: [%s]", test.input, test.expected, actual) + } + // if we expect an error and there isn't one + if test.expectErr && actualErr == nil { + t.Errorf("AreaCode(%s): expected an error, but error is nil", test.input) + } + // if we don't expect an error and there is one + if !test.expectErr && actualErr != nil { + t.Errorf("AreaCode(%s): expected no error, but error is: %s", test.input, actualErr) + } + } +} + +func BenchmarkAreaCode(b *testing.B) { + b.StopTimer() + for _, test := range areaCodeTests { + b.StartTimer() + for i := 0; i < b.N; i++ { + AreaCode(test.input) + } + b.StopTimer() + } +} + +var formatTests = []testCase{ + {"1234567890", "(123) 456-7890", false}, + {"11234567890", "(123) 456-7890", false}, + {"112345", "", true}, + {"11234590870986", "", true}, +} + +func TestFormat(t *testing.T) { + for _, test := range formatTests { + actual, actualErr := Format(test.input) + if actual != test.expected { + t.Errorf("Format(%s): expected [%s], actual: [%s]", test.input, test.expected, actual) + } + // if we expect an error and there isn't one + if test.expectErr && actualErr == nil { + t.Errorf("Format(%s): expected an error, but error is nil", test.input) + } + // if we don't expect an error and there is one + if !test.expectErr && actualErr != nil { + t.Errorf("Format(%s): expected no error, but error is: %s", test.input, actualErr) + } + } +} + +func BenchmarkFormat(b *testing.B) { + b.StopTimer() + for _, test := range areaCodeTests { + b.StartTimer() + for i := 0; i < b.N; i++ { + Format(test.input) + } + b.StopTimer() + } +} diff --git a/go/phone-number/phonenumber.go b/go/phone-number/phonenumber.go new file mode 100644 index 0000000..bb3fcb5 --- /dev/null +++ b/go/phone-number/phonenumber.go @@ -0,0 +1,44 @@ +package phonenumber + +import ( + "errors" + "strings" +) + +// Number takes a number and validates it +func Number(num string) (string, error) { + num = strings.Map(func(r rune) rune { + if r >= '0' && r <= '9' { + return r + } + return -1 + }, num) + // A number can be 11 digits long, if it starts with a 1 + // but ignore that one. + if len(num) == 11 && num[0] == '1' { + num = num[1:] + } + if len(num) != 10 { + return "", errors.New("Invalid Number") + } + return num, nil +} + +// AreaCode takes a number, validates it, and returns the area code +func AreaCode(num string) (string, error) { + num, err := Number(num) + if err != nil { + return "", err + } + return num[:3], nil +} + +// Format takes a number, validates it and returns it in the format +// (nnn) nnn-nnnn +func Format(num string) (string, error) { + num, err := Number(num) + if err != nil { + return "", err + } + return "(" + num[:3] + ") " + num[3:6] + "-" + num[6:], nil +} diff --git a/go/pig-latin/README.md b/go/pig-latin/README.md new file mode 100644 index 0000000..e4def4c --- /dev/null +++ b/go/pig-latin/README.md @@ -0,0 +1,36 @@ +# Pig Latin + +Implement a program that translates from English to Pig Latin + +Pig Latin is a made-up children's language that's intended to be +confusing. It obeys a few simple rules (below), but when it's spoken +quickly it's really difficult for non-children (and non-native speakers) +to understand. + +- **Rule 1**: If a word begins with a vowel sound, add an "ay" sound to + the end of the word. +- **Rule 2**: If a word begins with a consonant sound, move it to the + end of the word, and then add an "ay" sound to the end of the word. + +There are a few more rules for edge cases, and there are regional +variants too. + +See for more details. + +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 + +The Pig Latin exercise at Test First Teaching by Ultrasaurus [https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/](https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/) + +## Submitting Incomplete Problems +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + diff --git a/go/pig-latin/igpay.go b/go/pig-latin/igpay.go new file mode 100644 index 0000000..b6e1bef --- /dev/null +++ b/go/pig-latin/igpay.go @@ -0,0 +1,41 @@ +package igpay + +import "strings" + +// PigLatin takes some words and converts them to pig latin +// 1) Move starting consonant sounds to the end of the word +// 2) add 'ay' +func PigLatin(in string) string { + var ret string + words := strings.Split(in, " ") + for i := range words { + ret += pigLatinWord(words[i]) + if i < len(words)-1 { + ret += " " + } + } + return ret +} + +// pigLatinWord actually does the heavy lifting +func pigLatinWord(in string) string { + var ret string + // First move all consonant sounds to the end + for i := 0; i < len(in); i++ { + // Edge consonant sound cases + if in[0] == 'q' && in[1] == 'u' { + in = in[2:] + string(in[0]) + string(in[1]) + } + + vowelPos := strings.IndexAny(in, "aeiou") + + // If we have a y and another vowel, then y is a consonant + // also, x + if vowelPos == 0 || (vowelPos > 1 && (in[0] == 'y' || in[0] == 'x')) { + return in + "ay" + } + in = in[1:] + string(in[0]) + } + + return ret +} diff --git a/go/pig-latin/pig_latin_test.go b/go/pig-latin/pig_latin_test.go new file mode 100644 index 0000000..a8397a6 --- /dev/null +++ b/go/pig-latin/pig_latin_test.go @@ -0,0 +1,29 @@ +package igpay + +import "testing" + +var tests = []struct{ pl, in string }{ + {"appleay", "apple"}, + {"earay", "ear"}, + {"igpay", "pig"}, + {"oalakay", "koala"}, + {"airchay", "chair"}, + {"eenquay", "queen"}, + {"aresquay", "square"}, + {"erapythay", "therapy"}, + {"ushthray", "thrush"}, + {"oolschay", "school"}, + {"ickquay astfay unray", "quick fast run"}, + {"ellowyay", "yellow"}, + {"yttriaay", "yttria"}, + {"enonxay", "xenon"}, + {"xrayay", "xray"}, +} + +func TestPigLatin(t *testing.T) { + for _, test := range tests { + if pl := PigLatin(test.in); pl != test.pl { + t.Fatalf("PigLatin(%q) = %q, want %q.", test.in, pl, test.pl) + } + } +} diff --git a/go/prime-factors/README.md b/go/prime-factors/README.md new file mode 100644 index 0000000..c9002fb --- /dev/null +++ b/go/prime-factors/README.md @@ -0,0 +1,48 @@ +# Prime Factors + +Compute the prime factors of a given natural number. + +A prime number is only evenly divisible by itself and 1. + +Note that 1 is not a prime number. + +## Example + +What are the prime factors of 60? + +- Our first divisor is 2. 2 goes into 60, leaving 30. +- 2 goes into 30, leaving 15. + - 2 doesn't go cleanly into 15. So let's move on to our next divisor, 3. +- 3 goes cleanly into 15, leaving 5. + - 3 does not go cleanly into 5. The next possible factor is 4. + - 4 does not go cleanly into 5. The next possible factor is 5. +- 5 does go cleanly into 5. +- We're left only with 1, so now, we're done. + +Our successful divisors in that computation represent the list of prime +factors of 60: 2, 2, 3, and 5. + +You can check this yourself: + +- 2 * 2 * 3 * 5 +- = 4 * 15 +- = 60 +- Success! + +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 + +The Prime Factors Kata by Uncle Bob [http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata](http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata) + +## Submitting Incomplete Problems +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + diff --git a/go/prime-factors/prime.go b/go/prime-factors/prime.go new file mode 100644 index 0000000..8db9c0a --- /dev/null +++ b/go/prime-factors/prime.go @@ -0,0 +1,21 @@ +package prime + +const testVersion = 2 + +func Factors(fnd int64) []int64 { + var ret []int64 + var i int64 + foundFactor := true + for i = 2; i < fnd; i++ { + for j := range ret { + if i%ret[j] == 0 { + // Already covered this case, move on + foundFactor = false + } + if !foundFactor { + break + } + } + } + return ret +} diff --git a/go/prime-factors/primefactors_test.go b/go/prime-factors/primefactors_test.go new file mode 100644 index 0000000..40bbed8 --- /dev/null +++ b/go/prime-factors/primefactors_test.go @@ -0,0 +1,48 @@ +package prime + +// Return prime factors in increasing order + +import ( + "reflect" + "testing" +) + +const targetTestVersion = 2 + +var tests = []struct { + input int64 + expected []int64 +}{ + {1, []int64{}}, + {2, []int64{2}}, + {3, []int64{3}}, + {4, []int64{2, 2}}, + {6, []int64{2, 3}}, + {8, []int64{2, 2, 2}}, + {9, []int64{3, 3}}, + {27, []int64{3, 3, 3}}, + {625, []int64{5, 5, 5, 5}}, + {901255, []int64{5, 17, 23, 461}}, + {93819012551, []int64{11, 9539, 894119}}, +} + +func TestPrimeFactors(t *testing.T) { + if testVersion != targetTestVersion { + t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion) + } + for _, test := range tests { + actual := Factors(test.input) + if !reflect.DeepEqual(actual, test.expected) { + t.Errorf("prime.Factors(%d) = %v; expected %v", + test.input, actual, test.expected) + } + } +} + +func BenchmarkPrimeFactors(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, test := range tests { + Factors(test.input) + } + } +} diff --git a/go/strain/README.md b/go/strain/README.md new file mode 100644 index 0000000..26c9d2f --- /dev/null +++ b/go/strain/README.md @@ -0,0 +1,53 @@ +# Strain + +Implement the `keep` and `discard` operation on collections. Given a collection and a predicate on the collection's elements, `keep` returns a new collection containing those elements where the predicate is true, while `discard` returns a new collection containing those elements where the predicate is false. + +Write two functions that each take a function and a list. One of them will +return the list of items for which the passed in function is true, and the +other will return the items for which it is false. + +For example, given the collection of numbers: + +- 1, 2, 3, 4, 5 + +And the predicate: + +- is the number even? + +Then your keep operation should produce: + +- 2, 4 + +While your discard operation should produce: + +- 1, 3, 5 + +Note that the union of keep and discard is all the elements. + +The functions may be called `keep` and `discard`, or they may need different +names in order to not clash with existing functions or concepts in your +language. + +## Restrictions + +Keep your hands off that filter/reject/whatchamacallit functionality +provided by your standard library! Solve this one yourself using other +basic tools instead. + +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 + +Conversation with James Edward Gray II [https://twitter.com/jeg2](https://twitter.com/jeg2) + +## Submitting Incomplete Problems +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + diff --git a/go/strain/strain.go b/go/strain/strain.go new file mode 100644 index 0000000..48dd31d --- /dev/null +++ b/go/strain/strain.go @@ -0,0 +1,54 @@ +package strain + +// Ints is a slice of int +type Ints []int + +// Lists is a slice of int slices +type Lists [][]int + +// Strings is a slice of strings +type Strings []string + +// Keep runs the Ints through a function and returns the ones that succeed +func (i Ints) Keep(kf func(int) bool) Ints { + var ret Ints + for idx := range i { + if kf(i[idx]) { + ret = append(ret, i[idx]) + } + } + return ret +} + +// Discard runs the Ints through a function and returns the ones that fail +func (i Ints) Discard(df func(int) bool) Ints { + var ret Ints + for idx := range i { + if !df(i[idx]) { + ret = append(ret, i[idx]) + } + } + return ret +} + +// Keep runs the Lists through a function and returns the ones that succeed +func (l Lists) Keep(kf func([]int) bool) Lists { + var ret Lists + for idx := range l { + if kf(l[idx]) { + ret = append(ret, l[idx]) + } + } + return ret +} + +// Keep runs the Strings through a function and returns the ones that succeed +func (s Strings) Keep(kf func(string) bool) Strings { + var ret Strings + for idx := range s { + if kf(s[idx]) { + ret = append(ret, s[idx]) + } + } + return ret +} diff --git a/go/strain/strain_test.go b/go/strain/strain_test.go new file mode 100644 index 0000000..dd3da5a --- /dev/null +++ b/go/strain/strain_test.go @@ -0,0 +1,170 @@ +// Collections, hm? For this exercise in Go you'll work with slices as +// collections. Define the following in your solution: +// +// type Ints []int +// type Lists [][]int +// type Strings []string +// +// Then complete the exercise by implementing these methods: +// +// (Ints) Keep(func(int) bool) Ints +// (Ints) Discard(func(int) bool) Ints +// (Lists) Keep(func([]int) bool) Lists +// (Strings) Keep(func(string) bool) Strings + +package strain + +import ( + "reflect" + "testing" +) + +func lt10(x int) bool { return x < 10 } +func gt10(x int) bool { return x > 10 } +func odd(x int) bool { return x&1 == 1 } +func even(x int) bool { return x&1 == 0 } + +var keepTests = []struct { + pred func(int) bool + list Ints + want Ints +}{ + {lt10, + nil, + nil}, + {lt10, + Ints{1, 2, 3}, + Ints{1, 2, 3}}, + {odd, + Ints{1, 2, 3}, + Ints{1, 3}}, + {even, + Ints{1, 2, 3, 4, 5}, + Ints{2, 4}}, +} + +func TestKeepInts(t *testing.T) { + for _, test := range keepTests { + // setup here copies test.list, preserving the nil value if it is nil + // and making a fresh copy of the underlying array otherwise. + cp := test.list + if cp != nil { + cp = append(Ints{}, cp...) + } + switch res := cp.Keep(test.pred); { + case !reflect.DeepEqual(cp, test.list): + t.Fatalf("Ints%v.Keep() should not modify its reciever. "+ + "Found %v, reciever should stay %v", + test.list, cp, test.list) + case !reflect.DeepEqual(res, test.want): + t.Fatalf("Ints%v.Keep() = %v, want %v", + test.list, res, test.want) + } + } +} + +var discardTests = []struct { + pred func(int) bool + list Ints + want Ints +}{ + {lt10, + nil, + nil}, + {gt10, + Ints{1, 2, 3}, + Ints{1, 2, 3}}, + {odd, + Ints{1, 2, 3}, + Ints{2}}, + {even, + Ints{1, 2, 3, 4, 5}, + Ints{1, 3, 5}}, +} + +func TestDiscardInts(t *testing.T) { + for _, test := range discardTests { + cp := test.list + if cp != nil { + cp = append(Ints{}, cp...) // dup underlying array + } + switch res := cp.Discard(test.pred); { + case !reflect.DeepEqual(cp, test.list): + t.Fatalf("Ints%v.Discard() should not modify its reciever. "+ + "Found %v, reciever should stay %v", + test.list, cp, test.list) + case !reflect.DeepEqual(res, test.want): + t.Fatalf("Ints%v.Discard() = %v, want %v", + test.list, res, test.want) + } + } +} + +func TestKeepStrings(t *testing.T) { + zword := func(s string) bool { return len(s) > 0 && s[0] == 'z' } + list := Strings{"apple", "zebra", "banana", "zombies", "cherimoya", "zelot"} + want := Strings{"zebra", "zombies", "zelot"} + + cp := append(Strings{}, list...) // make copy, as with TestInts + switch res := cp.Keep(zword); { + case !reflect.DeepEqual(cp, list): + t.Fatalf("Strings%v.Keep() should not modify its reciever. "+ + "Found %v, reciever should stay %v", + list, cp, list) + case !reflect.DeepEqual(res, want): + t.Fatalf("Strings%v.Keep() = %v, want %v", + list, res, want) + } +} + +func TestKeepLists(t *testing.T) { + has5 := func(l []int) bool { + for _, e := range l { + if e == 5 { + return true + } + } + return false + } + list := Lists{ + {1, 2, 3}, + {5, 5, 5}, + {5, 1, 2}, + {2, 1, 2}, + {1, 5, 2}, + {2, 2, 1}, + {1, 2, 5}, + } + want := Lists{ + {5, 5, 5}, + {5, 1, 2}, + {1, 5, 2}, + {1, 2, 5}, + } + cp := append(Lists{}, list...) + switch res := cp.Keep(has5); { + case !reflect.DeepEqual(cp, list): + t.Fatalf("Lists%v.Keep() should not modify its reciever. "+ + "Found %v, reciever should stay %v", + list, cp, list) + case !reflect.DeepEqual(res, want): + t.Fatalf("Lists%v.Keep() = %v, want %v", + list, res, want) + } +} + +func BenchmarkKeepInts(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, test := range keepTests { + test.list.Keep(test.pred) + } + } +} + +func BenchmarkDiscardInts(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, test := range discardTests { + test.list.Discard(test.pred) + } + } +}