diff --git a/.gitignore b/.gitignore index e620e5a..117bc10 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .DS_Store +settings.json + # ---> Go # If you prefer the allow list template instead of the deny list, see community template: # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore diff --git a/container/queue.go b/container/queue.go new file mode 100644 index 0000000..5e9f20c --- /dev/null +++ b/container/queue.go @@ -0,0 +1,50 @@ +package container + +type Queue[T any] struct { + data []T + len int +} + +// New creates an empty queue +func NewQueue[T any]() Queue[T] { + q := Queue[T]{ + []T{}, + 0, + } + + return q +} + +// Enqueue adds an element to the end of the queue. +// It appends the given value to the queue's data slice and increments the length of the queue. +func (q *Queue[T]) Enqueue(v T) { + q.data = append(q.data, v) + q.len++ +} + +// Dequeue removes and returns the element at the front of the queue. +// It panics if the queue is empty. +func (q *Queue[T]) Dequeue() T { + if !q.HasElement() { + panic("called Dequeue() on an empty queue") + } + + value := q.data[0] + var zeroValue T + q.data[0] = zeroValue + q.data = q.data[1:] + + q.len-- + + return value +} + +// HasElement returns true if the queue has at least one element, otherwise false. +func (q *Queue[T]) HasElement() bool { + return q.len > 0 +} + +// GetData returns the data stored in the queue. +func (q *Queue[T]) GetData() []T { + return q.data +} diff --git a/container/queue_test.go b/container/queue_test.go new file mode 100644 index 0000000..83a6de5 --- /dev/null +++ b/container/queue_test.go @@ -0,0 +1,66 @@ +package container + +import ( + "testing" +) + +func TestQueueDequeue(t *testing.T) { + // Create a new queue + q := NewQueue[int]() + + // Enqueue some elements + q.Enqueue(1) + q.Enqueue(2) + q.Enqueue(3) + + // Dequeue the elements and check if they match the expected values + value := q.Dequeue() + if value != 1 { + t.Errorf("Expected dequeued value to be 1, but got %v", value) + } + + value = q.Dequeue() + if value != 2 { + t.Errorf("Expected dequeued value to be 2, but got %v", value) + } + + value = q.Dequeue() + if value != 3 { + t.Errorf("Expected dequeued value to be 3, but got %v", value) + } + + // Try to dequeue from an empty queue and expect a panic + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected Dequeue() to panic, but it didn't") + } + }() + + q.Dequeue() +} + +func TestQueueHasElement(t *testing.T) { + // Create a new queue + q := NewQueue[int]() + + // Check if the queue has elements, expect false + if q.HasElement() { + t.Errorf("Expected HasElement() to return false, but got true") + } + + // Enqueue an element + q.Enqueue(1) + + // Check if the queue has elements, expect true + if !q.HasElement() { + t.Errorf("Expected HasElement() to return true, but got false") + } + + // Dequeue the element + q.Dequeue() + + // Check if the queue has elements, expect false + if q.HasElement() { + t.Errorf("Expected HasElement() to return false, but got true") + } +} diff --git a/container/stack.go b/container/stack.go new file mode 100644 index 0000000..1b803b9 --- /dev/null +++ b/container/stack.go @@ -0,0 +1,76 @@ +/* +Copyright 2022 Olivier Abrivard + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +*/ + +package container + +type Stack[T any] struct { + data []T +} + +// New creates an empty stack +func NewStack[T any]() Stack[T] { + result := Stack[T]{ + []T{}, + } + + return result +} + +// Push pushes the value v on top of stack s. +func (s *Stack[T]) Push(v T) { + s.data = append(s.data, v) +} + +// Pop removes the value v from the top of stack s and returns it. +func (s *Stack[T]) Pop() T { + if !s.HasElement() { + panic("called Pop() on an empty stack") + } + + value := s.data[len(s.data)-1] + s.data = s.data[0 : len(s.data)-1] + return value +} + +// HasElement returns true if the stack s contains at least one element. +func (s *Stack[T]) HasElement() bool { + return len(s.data) > 0 +} + +// Count returns the number of elements contained in s. +func (s *Stack[T]) Count() int { + return len(s.data) +} + +// Reversed returns a new stack built from stack s, with its elements in reverse order. +// s will be emptied by this operation. +func (s *Stack[T]) Reversed() Stack[T] { + result := Stack[T]{ + make([]T, len(s.data)), + } + + for s.HasElement() { + result.Push(s.Pop()) + } + + return result +} diff --git a/container/stack_test.go b/container/stack_test.go new file mode 100644 index 0000000..8ae56d7 --- /dev/null +++ b/container/stack_test.go @@ -0,0 +1,103 @@ +/* +Copyright 2022 Olivier Abrivard + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +*/ + +package container + +import "testing" + +func TestEmpty(t *testing.T) { + defer func() { _ = recover() }() // turn off the panic + + s := NewStack[int]() + s.Pop() + + // Never reaches here if Pop() panics. + t.Errorf("Pop() on empty stack should panic") +} + +func TestHasElements(t *testing.T) { + s := NewStack[int]() + + if s.HasElement() { + t.Errorf("HasElement() should return false for empty stack") + } + + s.Push(1) + + if !s.HasElement() { + t.Errorf("HasElement() should return true for non empty stack") + } +} + +func TestCount(t *testing.T) { + s := NewStack[int]() + + if s.Count() != 0 { + t.Errorf("Count() should be 0 for empty stack") + } + + s.Push(1) + + if s.Count() != 1 { + t.Errorf("Count() should be 1 for stack with as single element") + } +} + +func TestPushPop(t *testing.T) { + s1 := NewStack[int]() + s1.Push(1) + s1.Push(2) + s1.Push(3) + + var expectedValues = []int{3, 2, 1} + + for _, expected := range expectedValues { + got := s1.Pop() + + if expected != got { + t.Errorf("got %d, want %d", got, expected) + } + } +} + +func TestReversed(t *testing.T) { + s1 := NewStack[int]() + s1.Push(1) + s1.Push(2) + s1.Push(3) + + s2 := s1.Reversed() + + if s1.Count() != 0 { + t.Errorf("Reversed() should emty the stack it is called upon") + } + + var expectedValues = []int{1, 2, 3} + + for _, expected := range expectedValues { + got := s2.Pop() + + if expected != got { + t.Errorf("got %d, want %d", got, expected) + } + } +} diff --git a/container/vector.go b/container/vector.go new file mode 100644 index 0000000..4f9f638 --- /dev/null +++ b/container/vector.go @@ -0,0 +1,58 @@ +package container + +type Vector[T any] []T + +// PushBack adds an element to the end of the vector. +// Complexity O(1) + array extesnion +func (v *Vector[T]) PushBack(e T) { + *v = append(*v, e) +} + +// PopBack removes and returns the last element from the vector. +// If the vector is empty, it will panic. +// Complexity O(1) +func (v *Vector[T]) PopBack() T { + if !v.HasElement() { + panic("called PopBack() on an empty vector") + } + + value := (*v)[len(*v)-1] + *v = (*v)[0 : len(*v)-1] + return value +} + +// Len returns the length of the vector. +// It returns an integer representing the number of elements in the vector. +func (v Vector[T]) Len() int { + return len(v) +} + +// HasElement returns true if the vector v contains at least one element. +func (v Vector[T]) HasElement() bool { + return v.Len() > 0 +} + +// PushFront adds an element to the front of the vector. +// Complexity O(n) +func (v *Vector[T]) PushFront(e T) { + newVect := make([]T, v.Len()+1) + copy(newVect[1:], *v) + newVect[0] = e + *v = newVect +} + +// PopFront removes and returns the element at the front of the vector. +// It panics if the vector is empty. +// Complexity O(1) +func (v *Vector[T]) PopFront() T { + if !v.HasElement() { + panic("called PopFront() on an empty vector") + } + + value := (*v)[0] + var zeroValue T + (*v)[0] = zeroValue + *v = (*v)[1:] + + return value +} diff --git a/container/vector_test.go b/container/vector_test.go new file mode 100644 index 0000000..5103655 --- /dev/null +++ b/container/vector_test.go @@ -0,0 +1,118 @@ +package container + +import ( + "reflect" + "testing" +) + +func TestVectorPushBack(t *testing.T) { + v := Vector[int]{1, 2, 3} + e := 4 + + v.PushBack(e) + + expected := Vector[int]{1, 2, 3, 4} + if !reflect.DeepEqual(v, expected) { + t.Errorf("PushBack() failed, expected %v, got %v", expected, v) + } + + if v.Len() != 4 { + t.Errorf("expected a length of 4, got %v", v.Len()) + } +} + +func TestVectorPopBack(t *testing.T) { + v := Vector[int]{1, 2, 3, 4} + expectedValue := 4 + + value := v.PopBack() + + if value != expectedValue { + t.Errorf("PopBack() failed, expected %v, got %v", expectedValue, value) + } + + expectedVector := Vector[int]{1, 2, 3} + if !reflect.DeepEqual(v, expectedVector) { + t.Errorf("PopBack() failed, expected vector %v, got %v", expectedVector, v) + } + + if v.Len() != 3 { + t.Errorf("expected a length of 3, got %v", v.Len()) + } + + // Test panic on empty vector + emptyVector := Vector[int]{} + defer func() { + if r := recover(); r == nil { + t.Errorf("PopBack() did not panic on empty vector") + } + }() + emptyVector.PopBack() +} + +func TestVectorLen(t *testing.T) { + v := Vector[int]{1, 2, 3} + + expected := 3 + result := v.Len() + + if result != expected { + t.Errorf("Len() failed, expected %d, got %d", expected, result) + } +} + +func TestVectorHasElement(t *testing.T) { + v := Vector[int]{1, 2, 3} + + hasElement := v.HasElement() + + expected := true + if hasElement != expected { + t.Errorf("HasElement() failed, expected %v, got %v", expected, hasElement) + } + + v = Vector[int]{} + + hasElement = v.HasElement() + + expected = false + if hasElement != expected { + t.Errorf("HasElement() failed, expected %v, got %v", expected, hasElement) + } +} + +func TestVectorPushFront(t *testing.T) { + v := Vector[int]{1, 2, 3} + e := 4 + + v.PushFront(e) + + expected := Vector[int]{4, 1, 2, 3} + if !reflect.DeepEqual(v, expected) { + t.Errorf("PushFront() failed, expected %v, got %v", expected, v) + } + + if v.Len() != 4 { + t.Errorf("expected a length of 4, got %v", v.Len()) + } +} + +func TestVectorPopFront(t *testing.T) { + v := Vector[int]{1, 2, 3, 4} + expectedValue := 1 + expectedVector := Vector[int]{2, 3, 4} + + value := v.PopFront() + + if value != expectedValue { + t.Errorf("PopFront() failed, expected value %v, got %v", expectedValue, value) + } + + if !reflect.DeepEqual(v, expectedVector) { + t.Errorf("PopFront() failed, expected vector %v, got %v", expectedVector, v) + } + + if v.Len() != 3 { + t.Errorf("expected a length of 3, got %v", v.Len()) + } +} diff --git a/fp/fp.go b/fp/fp.go new file mode 100644 index 0000000..f2d8f76 --- /dev/null +++ b/fp/fp.go @@ -0,0 +1,40 @@ +package fp + +// Fold applies a function to each element in the slice and accumulates the result. +// It takes a slice of type T, an initial value of type A, and a function that combines the accumulator value with each element of the slice. +// The function returns the final accumulated value. +func Fold[A any, T any](s []T, a A, f func(A, T) A) A { + for i := range s { + a = f(a, s[i]) + } + return a +} + +// Map applies the function f to each element of the slice s and returns a new slice +// containing the results. +// +// The returned slice will contain elements of type M, which is the result +// type of the function f. +// +// Example usage: +// +// numbers := []int{1, 2, 3, 4, 5} +// doubled := Map(numbers, func(n int) int { +// return n * 2 +// }) +// // doubled is now []int{2, 4, 6, 8, 10} +// +// names := []string{"Alice", "Bob", "Charlie"} +// lengths := Map(names, func(name string) int { +// return len(name) +// }) +// // lengths is now []int{5, 3, 7} +// +// Note: The input slice s is not modified by this function. +func Map[T, M any](s []T, f func(T) M) []M { + res := []M{} + for i := range s { + res = append(res, f(s[i])) + } + return res +} diff --git a/fp/fp_test.go b/fp/fp_test.go new file mode 100644 index 0000000..9241613 --- /dev/null +++ b/fp/fp_test.go @@ -0,0 +1,89 @@ +package fp + +import ( + "reflect" + "testing" +) + +func TestMap(t *testing.T) { + tests := []struct { + name string + input []int + function func(int) int + want []int + }{ + { + name: "multiply by 2", + input: []int{1, 2, 3}, + function: func(x int) int { return x * 2 }, + want: []int{2, 4, 6}, + }, + { + name: "add 1", + input: []int{1, 2, 3}, + function: func(x int) int { return x + 1 }, + want: []int{2, 3, 4}, + }, + { + name: "subtract 5", + input: []int{10, 15, 20}, + function: func(x int) int { return x - 5 }, + want: []int{5, 10, 15}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Map(tt.input, tt.function) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Map() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFold(t *testing.T) { + tests := []struct { + name string + input []int + accumulator int + function func(int, int) int + want int + }{ + { + name: "sum", + input: []int{1, 2, 3}, + accumulator: 0, + function: func(a, b int) int { return a + b }, + want: 6, + }, + { + name: "product", + input: []int{1, 2, 3}, + accumulator: 1, + function: func(a, b int) int { return a * b }, + want: 6, + }, + { + name: "maximum", + input: []int{1, 2, 3}, + accumulator: 0, + function: func(a, b int) int { + if a > b { + return a + } + return b + }, + want: 3, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Fold(tt.input, tt.accumulator, tt.function) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Fold() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c9a02c8 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module gitea.paas.celticinfo.fr/oabrivard/abrolgo + +go 1.21.6 + +require golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3707af3 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= diff --git a/math/geometry.go b/math/geometry.go new file mode 100644 index 0000000..4a18933 --- /dev/null +++ b/math/geometry.go @@ -0,0 +1,50 @@ +package math + +import "math" + +type Point struct { + X, Y float64 +} + +// ShoelaceArea calculates the area of a polygon using the Shoelace formula. +// It takes a slice of Point structs representing the vertices of the polygon. +// If the number of vertices is less than 3, it returns 0.0 indicating that it is not a valid polygon. +func ShoelaceArea(polygon []Point) float64 { + if len(polygon) < 3 { + return 0.0 // Not a polygon + } + + var area float64 = 0.0 + j := len(polygon) - 1 // The last vertex is the 'previous' one to the first + + for i := 0; i < len(polygon); i++ { + area += (polygon[j].X + polygon[i].X) * (polygon[j].Y - polygon[i].Y) + j = i // j is previous vertex to i + } + + return 0.5 * math.Abs(area) +} + +// floodFill performs a flood fill algorithm on the given matrix starting from the specified row and column. +// It replaces all occurrences of the previous color with the new color. +// The height and width parameters define the dimensions of the matrix. +func floodFill(matrix Matrix[int64], row int, col int, prevColor int64, newColor int64, height int, width int) { + if row < 0 || row >= height || col < 0 || col >= width { + return + } + + if matrix[row][col] != prevColor { + return + } + + if matrix[row][col] == newColor { + return + } + + matrix[row][col] = newColor + + floodFill(matrix, row+1, col, prevColor, newColor, height, width) + floodFill(matrix, row-1, col, prevColor, newColor, height, width) + floodFill(matrix, row, col+1, prevColor, newColor, height, width) + floodFill(matrix, row, col-1, prevColor, newColor, height, width) +} diff --git a/math/geometry_test.go b/math/geometry_test.go new file mode 100644 index 0000000..2865c30 --- /dev/null +++ b/math/geometry_test.go @@ -0,0 +1,76 @@ +package math + +import "testing" + +func TestShoelaceArea(t *testing.T) { + tests := []struct { + name string + polygon []Point + want float64 + }{ + { + name: "Triangle", + polygon: []Point{ + {X: 0, Y: 0}, + {X: 0, Y: 4}, + {X: 3, Y: 0}, + }, + want: 6.0, + }, + { + name: "Quadrilateral", + polygon: []Point{ + {X: 0, Y: 0}, + {X: 0, Y: 4}, + {X: 3, Y: 4}, + {X: 3, Y: 0}, + }, + want: 12.0, + }, + { + name: "Invalid Polygon", + polygon: []Point{ + {X: 0, Y: 0}, + {X: 0, Y: 4}, + }, + want: 0.0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ShoelaceArea(tt.polygon) + if got != tt.want { + t.Errorf("ShoelaceArea() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFloodFill(t *testing.T) { + matrix := Matrix[int64]{ + {1, 1, 1, 1}, + {1, 0, 0, 1}, + {1, 0, 0, 1}, + {1, 1, 1, 1}, + } + row := 1 + col := 1 + prevColor := int64(0) + newColor := int64(2) + height := 4 + width := 4 + + floodFill(matrix, row, col, prevColor, newColor, height, width) + + expectedMatrix := Matrix[int64]{ + {1, 1, 1, 1}, + {1, 2, 2, 1}, + {1, 2, 2, 1}, + {1, 1, 1, 1}, + } + + if !matrix.Equal(expectedMatrix) { + t.Errorf("FloodFill() failed, expected matrix: %v, got: %v", expectedMatrix, matrix) + } +} diff --git a/math/math.go b/math/math.go new file mode 100644 index 0000000..5f21411 --- /dev/null +++ b/math/math.go @@ -0,0 +1,113 @@ +package math + +import ( + "golang.org/x/exp/constraints" +) + +// IsPrime checks if the given number is prime. +// It returns true if the number is prime, and false otherwise. +func IsPrime[T constraints.Integer](n T) bool { + if n <= 1 { + return false + } + if n <= 3 { + return true + } + if n%2 == 0 || n%3 == 0 { + return false + } + + for i := T(5); i*i <= n; i += 6 { + if n%i == 0 || n%(i+2) == 0 { + return false + } + } + + return true +} + +// IntPow calculates the power of an integer. +// It takes two parameters, x and y, and returns the result of x raised to the power of y. +// The type of x and y must be an integer type. +func IntPow[T constraints.Integer](x, y T) T { + result := T(1) + + for i := T(0); i < y; i++ { + result *= x + } + + return result +} + +// Pow2 calculates the power of 2 for the given integer value. +// It performs a bitwise left shift operation on the number 1 by the value of x. +// The result is returned as an integer of the same type as the input. +func Pow2[T constraints.Integer](x T) T { + return 1 << x +} + +// GCD calculates the greatest common divisor (GCD) of two integers. +// It uses the Euclidean algorithm to find the GCD. +// The function takes two parameters, 'a' and 'b', which represent the integers. +// It returns the GCD as the result. +func GCD[T constraints.Integer](a, b T) T { + for b != 0 { + t := b + b = a % b + a = t + } + return a +} + +// LCM calculates the least common multiple (LCM) of two or more integers. +// It takes two integers, a and b, as the first two arguments, and an optional variadic parameter integers for additional integers. +// The function uses the GCD (greatest common divisor) function to calculate the LCM. +// It returns the LCM as the result. +func LCM[T constraints.Integer](a, b T, integers ...T) T { + result := a * b / GCD(a, b) + + for i := 0; i < len(integers); i++ { + result = LCM(result, integers[i]) + } + + return result +} + +// AbsInt returns the absolute value of an integer. +// It takes an integer value x as input and returns the absolute value of x. +func AbsInt[T constraints.Integer](x T) T { + if x >= 0 { + return x + } else { + return -x + } +} + +// gaussianElimination performs Gaussian elimination on the given coefficients matrix and right-hand side vector. +// It solves a system of linear equations by transforming the coefficients matrix into row-echelon form. +// The function takes a square matrix of type Matrix[float64] and a vector of type []float64 as input. +// The size of the matrix and the length of the vector should be the same. +// The function modifies the coefficients matrix and the right-hand side vector in place. +// The solution is stored into the rhs vector. +func gaussianElimination(coefficients Matrix[float64], rhs []float64) { + size := len(coefficients) + for i := 0; i < size; i++ { + // Select pivot + pivot := coefficients[i][i] + // Normalize row i + for j := 0; j < size; j++ { + coefficients[i][j] = coefficients[i][j] / pivot + } + rhs[i] = rhs[i] / pivot + // Sweep using row i + for k := 0; k < size; k++ { + if k != i { + factor := coefficients[k][i] + for j := 0; j < size; j++ { + coefficients[k][j] = coefficients[k][j] - factor*coefficients[i][j] + } + rhs[k] = rhs[k] - factor*rhs[i] + } + } + } +} diff --git a/math/math_test.go b/math/math_test.go new file mode 100644 index 0000000..3a9a3ee --- /dev/null +++ b/math/math_test.go @@ -0,0 +1,271 @@ +package math + +import ( + "testing" +) + +func TestIsPrime(t *testing.T) { + tests := []struct { + n int + expected bool + }{ + { + n: 0, + expected: false, + }, + { + n: 1, + expected: false, + }, + { + n: 2, + expected: true, + }, + { + n: 3, + expected: true, + }, + { + n: 4, + expected: false, + }, + { + n: 5, + expected: true, + }, + { + n: 6, + expected: false, + }, + // Add more test cases as needed + } + + for _, tt := range tests { + result := IsPrime(tt.n) + if result != tt.expected { + t.Errorf("IsPrime(%d) = %v, expected %v", tt.n, result, tt.expected) + } + } +} + +func TestIntPow(t *testing.T) { + tests := []struct { + x int + y int + expected int + }{ + { + x: 2, + y: 3, + expected: 8, + }, + { + x: 5, + y: 0, + expected: 1, + }, + { + x: 10, + y: 1, + expected: 10, + }, + { + x: 3, + y: 4, + expected: 81, + }, + { + x: 2, + y: 0, + expected: 1, + }, + { + x: 0, + y: 5, + expected: 0, + }, + { + x: -2, + y: 3, + expected: -8, + }, + } + + for _, tt := range tests { + result := IntPow(tt.x, tt.y) + if result != tt.expected { + t.Errorf("IntPow(%d, %d) = %d, expected %d", tt.x, tt.y, result, tt.expected) + } + } +} + +func TestPow2(t *testing.T) { + tests := []struct { + x int + expected int + }{ + { + x: 0, + expected: 1, + }, + { + x: 1, + expected: 2, + }, + { + x: 2, + expected: 4, + }, + { + x: 3, + expected: 8, + }, + } + + for _, tt := range tests { + result := Pow2(tt.x) + if result != tt.expected { + t.Errorf("Pow2(%d) = %d, expected %d", tt.x, result, tt.expected) + } + } +} + +func TestGCD(t *testing.T) { + tests := []struct { + a int + b int + expected int + }{ + { + a: 0, + b: 0, + expected: 0, + }, + { + a: 0, + b: 5, + expected: 5, + }, + { + a: 10, + b: 5, + expected: 5, + }, + { + a: 12, + b: 8, + expected: 4, + }, + { + a: 15, + b: 25, + expected: 5, + }, + { + a: 21, + b: 14, + expected: 7, + }, + { + a: 100, + b: 75, + expected: 25, + }, + } + + for _, tt := range tests { + result := GCD(tt.a, tt.b) + if result != tt.expected { + t.Errorf("GCD(%d, %d) = %d, expected %d", tt.a, tt.b, result, tt.expected) + } + } +} +func TestLCM(t *testing.T) { + tests := []struct { + a int + b int + integers []int + expected int + }{ + { + a: 2, + b: 3, + integers: []int{4, 5}, + expected: 60, + }, + { + a: 5, + b: 7, + integers: []int{10, 15}, + expected: 210, + }, + { + a: 12, + b: 18, + integers: []int{24, 36}, + expected: 72, + }, + } + + for _, tt := range tests { + result := LCM(tt.a, tt.b, tt.integers...) + if result != tt.expected { + t.Errorf("LCM(%d, %d, %v) = %d, expected %d", tt.a, tt.b, tt.integers, result, tt.expected) + } + } +} + +func TestAbsInt(t *testing.T) { + tests := []struct { + x int + expected int + }{ + { + x: 0, + expected: 0, + }, + { + x: 5, + expected: 5, + }, + { + x: -10, + expected: 10, + }, + { + x: 100, + expected: 100, + }, + { + x: -50, + expected: 50, + }, + } + + for _, tt := range tests { + result := AbsInt(tt.x) + if result != tt.expected { + t.Errorf("AbsInt(%d) = %d, expected %d", tt.x, result, tt.expected) + } + } +} + +func TestGaussianElimination(t *testing.T) { + coefficients := [][]float64{ + {2, 1, -1}, + {-3, -1, 2}, + {-2, 1, 2}, + } + rhs := []float64{8, -11, -3} + + expected := []float64{2, 3, -1} + + gaussianElimination(coefficients, rhs) + + for i := 0; i < len(rhs); i++ { + if rhs[i] != expected[i] { + t.Errorf("GaussianElimination failed. Expected %v, got %v", expected, rhs) + break + } + } +} diff --git a/math/matrix.go b/math/matrix.go new file mode 100644 index 0000000..033064b --- /dev/null +++ b/math/matrix.go @@ -0,0 +1,66 @@ +package math + +type Matrix[T comparable] [][]T + +// Transpose returns the transpose of the matrix. +func (m Matrix[T]) Transpose() Matrix[T] { + xl := len(m[0]) + yl := len(m) + result := make([][]T, xl) + for i := range result { + result[i] = make([]T, yl) + } + for i := 0; i < xl; i++ { + for j := 0; j < yl; j++ { + result[i][j] = m[j][i] + } + } + return result +} + +// RotateClockwise rotates the matrix clockwise by 90 degrees. +// It returns a new matrix with the rotated values. +func (m Matrix[T]) RotateClockwise() Matrix[T] { + xl := len(m[0]) + yl := len(m) + result := make([][]T, xl) + for i := range result { + result[i] = make([]T, yl) + } + + for row := 0; row < yl; row++ { + for col := 0; col < xl; col++ { + result[col][yl-1-row] = m[row][col] + } + } + return result +} + +// Duplicate creates a duplicate of the matrix. +// It returns a new matrix with the same values as the original matrix. +func (m Matrix[T]) Duplicate() Matrix[T] { + duplicate := make([][]T, len(m)) + for i := range m { + duplicate[i] = make([]T, len(m[i])) + copy(duplicate[i], m[i]) + } + return duplicate +} + +// Equal checks if the current matrix is equal to the given matrix. +// It returns true if the matrices are equal, and false otherwise. +func (matrix1 Matrix[T]) Equal(matrix2 Matrix[T]) bool { + if len(matrix1) != len(matrix2) || len(matrix1[0]) != len(matrix2[0]) { + return false + } + + for i := 0; i < len(matrix1); i++ { + for j := 0; j < len(matrix1[0]); j++ { + if matrix1[i][j] != matrix2[i][j] { + return false + } + } + } + + return true +} diff --git a/math/matrix_test.go b/math/matrix_test.go new file mode 100644 index 0000000..52eb402 --- /dev/null +++ b/math/matrix_test.go @@ -0,0 +1,113 @@ +package math + +import ( + "reflect" + "testing" +) + +func TestRotateClockwise(t *testing.T) { + matrix := Matrix[int]{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + } + + expected := Matrix[int]{ + {7, 4, 1}, + {8, 5, 2}, + {9, 6, 3}, + } + + result := matrix.RotateClockwise() + + if !reflect.DeepEqual(result, expected) { + t.Errorf("RotateClockwise() = %v, want %v", result, expected) + } +} + +func TestTranspose(t *testing.T) { + matrix := Matrix[int]{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + } + + expected := Matrix[int]{ + {1, 4, 7}, + {2, 5, 8}, + {3, 6, 9}, + } + + result := matrix.Transpose() + + if !reflect.DeepEqual(result, expected) { + t.Errorf("Transpose() = %v, want %v", result, expected) + } +} + +func TestDuplicate(t *testing.T) { + matrix := Matrix[int]{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + } + + expected := Matrix[int]{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + } + + result := matrix.Duplicate() + + if !reflect.DeepEqual(result, expected) { + t.Errorf("Duplicate() = %v, want %v", result, expected) + } +} +func TestEqual(t *testing.T) { + matrix1 := Matrix[int]{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + } + + matrix2 := Matrix[int]{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + } + + if !matrix1.Equal(matrix2) { + t.Errorf("Equal() = false, want true") + } + + matrix3 := Matrix[int]{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + } + + matrix4 := Matrix[int]{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 0}, + } + + if matrix3.Equal(matrix4) { + t.Errorf("Equal() = true, want false") + } + + matrix5 := Matrix[int]{ + {1, 2}, + {3, 4}, + } + + matrix6 := Matrix[int]{ + {1, 2, 3}, + {4, 5, 6}, + } + + if matrix5.Equal(matrix6) { + t.Errorf("Equal() = true, want false") + } +} diff --git a/parse/parse.go b/parse/parse.go new file mode 100644 index 0000000..280fd73 --- /dev/null +++ b/parse/parse.go @@ -0,0 +1,71 @@ +// Package parse provides utility functions for parsing and reading data from files. +package parse + +import ( + "bufio" + "log" + "os" + "strconv" + "strings" + + "golang.org/x/exp/constraints" +) + +// ReadLines reads all lines from a file specified by fileName and returns them as a slice of strings. +func ReadLines(fileName string) []string { + result := []string{} + file, err := os.Open(fileName) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + result = append(result, scanner.Text()) + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + + return result +} + +// ParseIntArray parses a string containing integer values separated by a specified separator and returns a slice of integers. +func ParseIntArray[T constraints.Integer](s string, sep string) []T { + result := []T{} + + var vals []string + if sep == " " { + vals = strings.Fields(strings.TrimSpace(s)) + } else { + vals = strings.Split(strings.TrimSpace(s), sep) + } + + for _, val := range vals { + n, e := strconv.ParseInt(strings.TrimSpace(val), 10, 64) + if e != nil { + log.Fatal(e) + } + + result = append(result, T(n)) + } + + return result +} + +// SplitNoBlank splits a string by a specified separator and returns a slice of the non-empty parts. +func SplitNoBlank(s string, sep string) []string { + splitted_line := strings.Split(s, sep) + + result := []string{} + + for _, part := range splitted_line { + if part != "" { + result = append(result, part) + } + } + + return result +} diff --git a/parse/parse_test.go b/parse/parse_test.go new file mode 100644 index 0000000..74802dc --- /dev/null +++ b/parse/parse_test.go @@ -0,0 +1,136 @@ +package parse + +import ( + "os" + "reflect" + "testing" +) + +func TestReadLines(t *testing.T) { + // Create a temporary file and write some lines to it + tmpFile, err := os.CreateTemp("", "testfile") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpFile.Name()) + + lines := []string{ + "Line 1", + "Line 2", + "Line 3", + } + + for _, line := range lines { + _, err := tmpFile.WriteString(line + "\n") + if err != nil { + t.Fatal(err) + } + } + + // Close the file before reading from it + err = tmpFile.Close() + if err != nil { + t.Fatal(err) + } + + fileName := tmpFile.Name() + + // Call the ReadLines function + result := ReadLines(fileName) + + // Check if the lines read from file match the expected lines + for i, line := range result { + if line != lines[i] { + t.Errorf("Expected line %d to be '%s', but got '%s'", i+1, lines[i], line) + } + } +} + +func TestParseIntArray(t *testing.T) { + tests := []struct { + name string + input string + separator string + want []int64 + expectFail bool + }{ + { + name: "space separated", + input: "1 2 3", + separator: " ", + want: []int64{1, 2, 3}, + }, + { + name: "comma separated", + input: "1,2,3", + separator: ",", + want: []int64{1, 2, 3}, + }, + { + name: "with leading/trailing spaces", + input: " 1, 2, 3 ", + separator: ",", + want: []int64{1, 2, 3}, + }, + { + name: "empty string", + input: "", + separator: ",", + want: []int64{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ParseIntArray[int64](tt.input, tt.separator) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ParseIntArray() = %v, want %v", + got, tt.want) + } + }) + } +} + +func TestSplitNoBlank(t *testing.T) { + tests := []struct { + name string + input string + sep string + want []string + }{ + { + name: "no blank", + input: "abc", + sep: "", + want: []string{"a", "b", "c"}, + }, + { + name: "with blanks", + input: "a b c", + sep: " ", + want: []string{"a", "b", "c"}, + }, + { + name: "leading/trailing blanks", + input: " a b c ", + sep: " ", + want: []string{"a", "b", "c"}, + }, + { + name: "empty string", + input: "", + sep: " ", + want: []string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := SplitNoBlank(tt.input, tt.sep) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("SplitNoBlank() = %v, want %v", + got, tt.want) + } + }) + } +}