From 99eeeedc658565e035b8c0bfe7cea16ea0e3aacb Mon Sep 17 00:00:00 2001 From: "Lars M. Rogne" Date: Wed, 24 Apr 2024 22:04:34 +0200 Subject: [PATCH] Improvements --- .github/workflows/run_tests.yaml | 22 ++++++++++++++ .gitignore | 1 + README | 1 - README.md | 1 + collatz.go | 10 ++++-- collatz/calculator.go | 52 ++++++++++++++++++-------------- collatz/calculator_test.go | 45 +++++++++++++++++++++++++++ {io => parsing}/cliargs.go | 2 +- parsing/cliargs_test.go | 44 +++++++++++++++++++++++++++ parsing/interface.go | 7 +++++ 10 files changed, 157 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/run_tests.yaml create mode 100644 .gitignore delete mode 100644 README create mode 100644 README.md create mode 100644 collatz/calculator_test.go rename {io => parsing}/cliargs.go (96%) create mode 100644 parsing/cliargs_test.go create mode 100644 parsing/interface.go diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml new file mode 100644 index 0000000..d34f079 --- /dev/null +++ b/.github/workflows/run_tests.yaml @@ -0,0 +1,22 @@ +name: Run Tests + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: '^1.22.1' + + - name: Run tests + run: go test ./... \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/README b/README deleted file mode 100644 index 9d76f98..0000000 --- a/README +++ /dev/null @@ -1 +0,0 @@ -Run with `go run collatz.go 1234` or download the exe and pass a number `collatz 1234`. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee0039d --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Run with `go run collatz.go 1234` or download the executable and pass a number `collatz 1234`. diff --git a/collatz.go b/collatz.go index cab9120..3c3e97b 100644 --- a/collatz.go +++ b/collatz.go @@ -2,10 +2,14 @@ package main import ( "collatz/collatz" - "collatz/io" + "collatz/parsing" + "log" ) func main() { - calculator := collatz.NewCalculator(new(io.Cliargs)) - calculator.Collatz() + calculator := collatz.NewCalculator(new(parsing.Cliargs)) + err := calculator.CalculateCollatz() + if err != nil { + log.Fatalf("Error calculating Collatz: %v", err) + } } diff --git a/collatz/calculator.go b/collatz/calculator.go index 5756d2c..3122ffe 100644 --- a/collatz/calculator.go +++ b/collatz/calculator.go @@ -1,53 +1,59 @@ package collatz import ( - "collatz/io" + "collatz/parsing" "fmt" - "log" "math/big" ) type Calculator struct { - parser *io.Cliargs - three *big.Int - two *big.Int - one *big.Int + parser parsing.Parser + three *big.Int + two *big.Int + one *big.Int + iterations int } -func NewCalculator(parser *io.Cliargs) Calculator { - r := Calculator{ - parser: parser, - three: new(big.Int).SetInt64(3), - two: new(big.Int).SetInt64(2), - one: new(big.Int).SetInt64(1), +func NewCalculator(parser parsing.Parser) *Calculator { + r := &Calculator{ + parser: parser, + three: big.NewInt(3), + two: big.NewInt(2), + one: big.NewInt(1), + iterations: 0, } return r } -func (o Calculator) Collatz() { +func (o *Calculator) CalculateCollatz() error { number, err := o.parser.Parse() if err != nil { - log.Panicf("Couldn't parse input: %s\n", err) + return fmt.Errorf("couldn't parse input: %w", err) } fmt.Print(number.Text(10)) - o.nextNumber(number) + err = o.nextNumber(*number) + if err != nil { + return err + } fmt.Println() + fmt.Println("Iterations:", o.iterations) + return nil } -func (o Calculator) nextNumber(in *big.Int) { +func (o *Calculator) nextNumber(in big.Int) error { if in.Cmp(o.one) < 0 { - log.Panicln("Must be greater than 0") + return fmt.Errorf("must be greater than 0") } if in.Cmp(o.one) == 0 { - return + return nil } var newNumber *big.Int - if new(big.Int).And(in, o.one).Cmp(o.one) == 0 { - newNumber = new(big.Int).Add(new(big.Int).Mul(in, o.three), o.one) + if new(big.Int).And(&in, o.one).Cmp(o.one) == 0 { + newNumber = new(big.Int).Add(new(big.Int).Mul(&in, o.three), o.one) } else { - newNumber = new(big.Int).Div(in, o.two) + newNumber = new(big.Int).Div(&in, o.two) } - + o.iterations++ fmt.Printf(" -> %s", newNumber.Text(10)) - o.nextNumber(newNumber) + return o.nextNumber(*newNumber) } diff --git a/collatz/calculator_test.go b/collatz/calculator_test.go new file mode 100644 index 0000000..ab4f167 --- /dev/null +++ b/collatz/calculator_test.go @@ -0,0 +1,45 @@ +package collatz + +import ( + "math/big" + "testing" +) + +type MockParser struct { + value *big.Int +} + +func (m *MockParser) Parse() (*big.Int, error) { + return m.value, nil +} + +func TestNewCalculator(t *testing.T) { + parser := &MockParser{} + calculator := NewCalculator(parser) + + if calculator.parser != parser { + t.Errorf("Expected parser to be %v, got %v", parser, calculator.parser) + } + + if calculator.one.Cmp(big.NewInt(1)) != 0 { + t.Errorf("Expected one to be 1, got %v", calculator.one) + } + + if calculator.two.Cmp(big.NewInt(2)) != 0 { + t.Errorf("Expected two to be 2, got %v", calculator.two) + } + + if calculator.three.Cmp(big.NewInt(3)) != 0 { + t.Errorf("Expected three to be 3, got %v", calculator.three) + } +} + +func TestCalculateCollatz(t *testing.T) { + parser := &MockParser{value: big.NewInt(6)} + calculator := NewCalculator(parser) + + err := calculator.CalculateCollatz() + if err != nil { + t.Errorf("Expected no error, got %v", err) + } +} diff --git a/io/cliargs.go b/parsing/cliargs.go similarity index 96% rename from io/cliargs.go rename to parsing/cliargs.go index f89abe5..aecab34 100644 --- a/io/cliargs.go +++ b/parsing/cliargs.go @@ -1,4 +1,4 @@ -package io +package parsing import ( "errors" diff --git a/parsing/cliargs_test.go b/parsing/cliargs_test.go new file mode 100644 index 0000000..72eefa7 --- /dev/null +++ b/parsing/cliargs_test.go @@ -0,0 +1,44 @@ +package parsing + +import ( + "math/big" + "os" + "testing" +) + +func TestCliargs_Parse(t *testing.T) { + tests := []struct { + name string + args []string + want *big.Int + wantErr bool + }{ + { + name: "Test with valid integer", + args: []string{"", "10"}, // the first argument is the program name + want: big.NewInt(10), + wantErr: false, + }, + { + name: "Test with non-integer", + args: []string{"", "abc"}, // the first argument is the program name + want: nil, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + os.Args = tt.args + parser := &Cliargs{} + got, err := parser.Parse() + if (err != nil) != tt.wantErr { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr && got.Cmp(tt.want) != 0 { + t.Errorf("Parse() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/parsing/interface.go b/parsing/interface.go new file mode 100644 index 0000000..48ec35e --- /dev/null +++ b/parsing/interface.go @@ -0,0 +1,7 @@ +package parsing + +import "math/big" + +type Parser interface { + Parse() (*big.Int, error) +}