Compare commits

...

5 Commits
v0.1 ... main

Author SHA1 Message Date
Logiar
61414ff5bf Write the chain on separate lines.
All checks were successful
Run Tests / test (push) Successful in 31s
release / release (push) Successful in 29s
2024-09-11 05:33:20 +00:00
713f70041b
Clean code improvements.
All checks were successful
Run Tests / test (push) Successful in 9s
2024-04-25 04:41:39 +02:00
a23e5f80f0
Smaller executables.
All checks were successful
Run Tests / test (push) Successful in 12s
release / release (push) Successful in 22s
2024-04-24 22:24:47 +02:00
99eeeedc65
Improvements
All checks were successful
Run Tests / test (push) Successful in 25s
2024-04-24 22:04:34 +02:00
c73fa4e72b
Readme 2024-04-24 20:43:38 +02:00
10 changed files with 189 additions and 40 deletions

View File

@ -17,9 +17,9 @@ jobs:
with: with:
go-version: '>=1.22.1' go-version: '>=1.22.1'
- name: release-build-windows - name: release-build-windows
run: GOOS=windows GOARCH=amd64 go build -o bin/collatz-${{ github.ref_name }}-windows-amd64.exe run: GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o bin/collatz-${{ github.ref_name }}-windows-amd64.exe
- name: release-build-linux - name: release-build-linux
run: GOOS=linux GOARCH=amd64 go build -o bin/collatz-${{ github.ref_name }}-linux-amd64 run: GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bin/collatz-${{ github.ref_name }}-linux-amd64
- name: Release - name: Release
uses: https://gitea.com/actions/release-action@main uses: https://gitea.com/actions/release-action@main
with: with:

22
.github/workflows/run_tests.yaml vendored Normal file
View File

@ -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 ./...

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

1
README.md Normal file
View File

@ -0,0 +1 @@
Run with `go run collatz.go 1234` or download the executable and pass a number `collatz 1234`.

View File

@ -2,10 +2,34 @@ package main
import ( import (
"collatz/collatz" "collatz/collatz"
"collatz/io" "collatz/parsing"
"log"
"strings"
) )
func main() { func main() {
calculator := collatz.NewCalculator(new(io.Cliargs)) sequence, err := runCollatz()
calculator.Collatz() if err != nil {
log.Fatalf("Error calculating Collatz: %v", err)
}
printSequence(sequence)
}
func printSequence(sequence collatz.Sequence) {
builder := new(strings.Builder)
log.Printf("Collatz sequence with %d iterations:", sequence.Iterations)
for i, num := range sequence.Sequence {
if i != 0 {
builder.WriteString("\n-> ")
}
builder.WriteString(num.String())
}
log.Println(builder.String())
}
func runCollatz() (collatz.Sequence, error) {
parser := new(parsing.Cliargs)
calculator := collatz.NewCalculator(parser)
sequence, err := calculator.CalculateCollatz()
return sequence, err
} }

View File

@ -1,53 +1,61 @@
package collatz package collatz
import ( import (
"collatz/io" "collatz/parsing"
"fmt" "fmt"
"log"
"math/big" "math/big"
) )
type Calculator struct { type Calculator struct {
parser *io.Cliargs parser parsing.Parser
three *big.Int three *big.Int
two *big.Int two *big.Int
one *big.Int one *big.Int
sequence Sequence
}
type Sequence struct {
Iterations int
Sequence []*big.Int
} }
func NewCalculator(parser *io.Cliargs) Calculator { func NewCalculator(parser parsing.Parser) *Calculator {
r := Calculator{ r := &Calculator{
parser: parser, parser: parser,
three: new(big.Int).SetInt64(3), three: big.NewInt(3),
two: new(big.Int).SetInt64(2), two: big.NewInt(2),
one: new(big.Int).SetInt64(1), one: big.NewInt(1),
} }
return r return r
} }
func (o Calculator) Collatz() { func (calc *Calculator) CalculateCollatz() (Sequence, error) {
number, err := o.parser.Parse() number, err := calc.parser.Parse()
if err != nil { if err != nil {
log.Panicf("Couldn't parse input: %s\n", err) return Sequence{}, fmt.Errorf("couldn't parse input: %w", err)
} }
fmt.Print(number.Text(10)) calc.sequence = Sequence{Iterations: 0}
o.nextNumber(number) calc.sequence.Sequence = append(calc.sequence.Sequence, new(big.Int).Set(number))
fmt.Println() err = calc.calculateNextCollatzNumber(*number)
if err != nil {
return Sequence{}, err
}
return calc.sequence, nil
} }
func (o Calculator) nextNumber(in *big.Int) { func (calc *Calculator) calculateNextCollatzNumber(in big.Int) error {
if in.Cmp(o.one) < 0 { if in.Cmp(calc.one) < 0 {
log.Panicln("Must be greater than 0") return fmt.Errorf("must be greater than 0")
} }
if in.Cmp(o.one) == 0 { if in.Cmp(calc.one) == 0 {
return return nil
} }
var newNumber *big.Int var newNumber *big.Int
if new(big.Int).And(in, o.one).Cmp(o.one) == 0 { if new(big.Int).And(&in, calc.one).Cmp(calc.one) == 0 {
newNumber = new(big.Int).Add(new(big.Int).Mul(in, o.three), o.one) newNumber = new(big.Int).Add(new(big.Int).Mul(&in, calc.three), calc.one)
} else { } else {
newNumber = new(big.Int).Div(in, o.two) newNumber = new(big.Int).Div(&in, calc.two)
} }
calc.sequence.Iterations++
fmt.Printf(" -> %s", newNumber.Text(10)) calc.sequence.Sequence = append(calc.sequence.Sequence, newNumber)
o.nextNumber(newNumber) return calc.calculateNextCollatzNumber(*newNumber)
} }

View File

@ -0,0 +1,48 @@
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)
sequence, err := calculator.CalculateCollatz()
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
if sequence.Iterations != 8 {
t.Errorf("Expected 8 iterations, got %v", sequence.Iterations)
}
}

View File

@ -1,4 +1,4 @@
package io package parsing
import ( import (
"errors" "errors"
@ -7,7 +7,6 @@ import (
) )
type Cliargs struct { type Cliargs struct {
value *big.Int
} }
func (arg *Cliargs) Parse() (*big.Int, error) { func (arg *Cliargs) Parse() (*big.Int, error) {
@ -19,10 +18,5 @@ func (arg *Cliargs) Parse() (*big.Int, error) {
if !success { if !success {
return nil, errors.New("argument is not an integer") return nil, errors.New("argument is not an integer")
} }
arg.value = num
return num, nil return num, nil
} }
func (arg *Cliargs) GetNumber() *big.Int {
return arg.value
}

44
parsing/cliargs_test.go Normal file
View File

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

7
parsing/interface.go Normal file
View File

@ -0,0 +1,7 @@
package parsing
import "math/big"
type Parser interface {
Parse() (*big.Int, error)
}