Initial Commit
This commit is contained in:
20
go/palindrome-products/README.md
Normal file
20
go/palindrome-products/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Palindrome Products
|
||||
|
||||
Write a program that can detect palindrome products in a given range.
|
||||
|
||||
A palindromic number reads the same both ways. The largest palindrome
|
||||
made from the product of two 2-digit numbers is 9009 = 91 x 99.
|
||||
|
||||
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://help.exercism.io/getting-started-with-go.html).
|
||||
|
||||
## Source
|
||||
|
||||
Problem 4 at Project Euler [view source](http://projecteuler.net/problem=4)
|
91
go/palindrome-products/palindrome/palindrome_products.go
Normal file
91
go/palindrome-products/palindrome/palindrome_products.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package palindrome
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Product is a palindromic product and all of its
|
||||
// factorizations
|
||||
type Product struct {
|
||||
Product int
|
||||
Factorizations [][2]int
|
||||
}
|
||||
|
||||
func createProduct(fac1, fac2 int) *Product {
|
||||
p := Product{Product: (fac1 * fac2)}
|
||||
p.addFactors(fac1, fac2)
|
||||
return &p
|
||||
}
|
||||
|
||||
func (p *Product) hasFactors(fac1, fac2 int) bool {
|
||||
for i := 0; i < len(p.Factorizations); i++ {
|
||||
if p.Factorizations[i][0] == fac1 && p.Factorizations[i][1] == fac2 {
|
||||
return true
|
||||
}
|
||||
if p.Factorizations[i][1] == fac1 && p.Factorizations[i][0] == fac2 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Product) addFactors(fac1, fac2 int) {
|
||||
if (fac1 * fac2) == p.Product {
|
||||
if !p.hasFactors(fac1, fac2) {
|
||||
p.Factorizations = append(p.Factorizations, [2]int{fac1, fac2})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Products takes a min and a max and finds
|
||||
func Products(fmin, fmax int) (Product, Product, error) {
|
||||
if fmin > fmax {
|
||||
return Product{}, Product{}, fmt.Errorf("fmin > fmax")
|
||||
}
|
||||
var pMin, pMax Product
|
||||
var err error
|
||||
var allProducts []Product
|
||||
|
||||
for i := fmin; i <= fmax; i++ {
|
||||
for j := fmin; j <= fmax; j++ {
|
||||
if isPalindrome(strconv.Itoa(i * j)) {
|
||||
var found bool
|
||||
for k := 0; k < len(allProducts); k++ {
|
||||
if allProducts[k].Product == (i * j) {
|
||||
allProducts[k].addFactors(i, j)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
allProducts = append(allProducts, *createProduct(i, j))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range allProducts {
|
||||
if allProducts[i].Product > pMax.Product {
|
||||
// is the old pMax the new pMin?
|
||||
if len(pMin.Factorizations) == 0 {
|
||||
pMin = pMax
|
||||
}
|
||||
pMax = allProducts[i]
|
||||
} else {
|
||||
if allProducts[i].Product < pMin.Product {
|
||||
pMin = allProducts[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println(allProducts)
|
||||
return pMin, pMax, err
|
||||
}
|
||||
|
||||
func isPalindrome(s string) bool {
|
||||
for i := 0; i < (len(s) / 2); i++ {
|
||||
if s[i] != s[len(s)-1-i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
92
go/palindrome-products/palindrome_products.go
Normal file
92
go/palindrome-products/palindrome_products.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package palindrome
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Product is a palindromic product and all of its
|
||||
// factorizations
|
||||
type Product struct {
|
||||
Product int
|
||||
Factorizations [][2]int
|
||||
}
|
||||
|
||||
func createProduct(fac1, fac2 int) *Product {
|
||||
p := Product{Product: (fac1 * fac2)}
|
||||
p.addFactors(fac1, fac2)
|
||||
return &p
|
||||
}
|
||||
|
||||
func (p *Product) hasFactors(fac1, fac2 int) bool {
|
||||
for i := 0; i < len(p.Factorizations); i++ {
|
||||
if p.Factorizations[i][0] == fac1 && p.Factorizations[i][1] == fac2 {
|
||||
return true
|
||||
}
|
||||
if p.Factorizations[i][1] == fac1 && p.Factorizations[i][0] == fac2 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Product) addFactors(fac1, fac2 int) {
|
||||
if (fac1 * fac2) == p.Product {
|
||||
if !p.hasFactors(fac1, fac2) {
|
||||
p.Factorizations = append(p.Factorizations, [2]int{fac1, fac2})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Products takes a min and a max and finds
|
||||
func Products(fmin, fmax int) (Product, Product, error) {
|
||||
if fmin > fmax {
|
||||
return Product{}, Product{}, fmt.Errorf("fmin > fmax")
|
||||
}
|
||||
var pMin, pMax Product
|
||||
var allProducts []Product
|
||||
|
||||
for i := fmin; i <= fmax; i++ {
|
||||
for j := fmin; j <= fmax; j++ {
|
||||
if isPalindrome(strconv.Itoa(i * j)) {
|
||||
var found bool
|
||||
for k := 0; k < len(allProducts); k++ {
|
||||
if allProducts[k].Product == (i * j) {
|
||||
allProducts[k].addFactors(i, j)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
allProducts = append(allProducts, *createProduct(i, j))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range allProducts {
|
||||
if allProducts[i].Product > pMax.Product {
|
||||
// is the old pMax the new pMin?
|
||||
if len(pMin.Factorizations) == 0 {
|
||||
pMin = pMax
|
||||
}
|
||||
pMax = allProducts[i]
|
||||
} else {
|
||||
if allProducts[i].Product < pMin.Product {
|
||||
pMin = allProducts[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(allProducts) == 0 {
|
||||
return pMin, pMax, fmt.Errorf("No palindromes")
|
||||
}
|
||||
return pMin, pMax, nil
|
||||
}
|
||||
|
||||
func isPalindrome(s string) bool {
|
||||
for i := 0; i < (len(s) / 2); i++ {
|
||||
if s[i] != s[len(s)-1-i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
98
go/palindrome-products/palindrome_products_test.go
Normal file
98
go/palindrome-products/palindrome_products_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package palindrome
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// API to impliment:
|
||||
// type Product struct {
|
||||
// Product int // palindromic, of course
|
||||
// // list of all possible two-factor factorizations of Product, within
|
||||
// // given limits, in order
|
||||
// Factorizations [][2]int
|
||||
// }
|
||||
// func Products(fmin, fmax int) (pmin, pmax Product, error)
|
||||
|
||||
var testData = []struct {
|
||||
// input to Products(): range limits for factors of the palindrome
|
||||
fmin, fmax int
|
||||
// output from Products():
|
||||
pmin, pmax Product // min and max palandromic products
|
||||
errPrefix string // start of text if there is an error, "" otherwise
|
||||
}{
|
||||
{1, 9,
|
||||
Product{}, // zero value means don't bother to test it
|
||||
Product{9, [][2]int{{1, 9}, {3, 3}}},
|
||||
""},
|
||||
{10, 99,
|
||||
Product{121, [][2]int{{11, 11}}},
|
||||
Product{9009, [][2]int{{91, 99}}},
|
||||
""},
|
||||
{100, 999,
|
||||
Product{10201, [][2]int{{101, 101}}},
|
||||
Product{906609, [][2]int{{913, 993}}},
|
||||
""},
|
||||
{4, 10, Product{}, Product{}, "No palindromes"},
|
||||
{10, 4, Product{}, Product{}, "fmin > fmax"},
|
||||
/* bonus curiosities. (can a negative number be a palindrome?
|
||||
// most say no
|
||||
{-99, -10, Product{}, Product{}, "Negative limits"},
|
||||
// but you can still get non-negative products from negative factors.
|
||||
{-99, -10,
|
||||
Product{121, [][2]int{{-11, -11}}},
|
||||
Product{9009, [][2]int{{-99, -91}}},
|
||||
""},
|
||||
{-2, 2,
|
||||
Product{0, [][2]int{{-2, 0}, {-1, 0}, {0, 0}, {0, 1}, {0, 2}}},
|
||||
Product{4, [][2]int{{-2, -2}, {2, 2}}},
|
||||
""},
|
||||
// or you could reverse the *digits*, keeping the minus sign in place.
|
||||
{-2, 2,
|
||||
Product{-4, [][2]int{{-2, 2}}},
|
||||
Product{4, [][2]int{{-2, -2}, {2, 2}}},
|
||||
""},
|
||||
{
|
||||
{0, (^uint(0))>>1, Product{}, Product{}, "This one's gonna overflow"},
|
||||
*/
|
||||
}
|
||||
|
||||
func TestPalindromeProducts(t *testing.T) {
|
||||
for _, test := range testData {
|
||||
// common preamble for test failures
|
||||
ret := fmt.Sprintf("Products(%d, %d) returned",
|
||||
test.fmin, test.fmax)
|
||||
// test
|
||||
pmin, pmax, err := Products(test.fmin, test.fmax)
|
||||
switch {
|
||||
case err == nil:
|
||||
if test.errPrefix > "" {
|
||||
t.Fatalf(ret+" err = nil, want %q", test.errPrefix+"...")
|
||||
}
|
||||
case test.errPrefix == "":
|
||||
t.Fatalf(ret+" err = %q, want nil", err)
|
||||
case !strings.HasPrefix(err.Error(), test.errPrefix):
|
||||
t.Fatalf(ret+" err = %q, want %q", err, test.errPrefix+"...")
|
||||
default:
|
||||
continue // correct error, no further tests for this test case
|
||||
}
|
||||
matchProd := func(ww string, rp, wp Product) {
|
||||
if len(wp.Factorizations) > 0 && // option to skip test
|
||||
!reflect.DeepEqual(rp, wp) {
|
||||
t.Fatal(ret, ww, "=", rp, "want", wp)
|
||||
}
|
||||
}
|
||||
matchProd("pmin", pmin, test.pmin)
|
||||
matchProd("pmax", pmax, test.pmax)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPalindromeProducts(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, test := range testData {
|
||||
Products(test.fmin, test.fmax)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user