diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fd1f63e16..0aec70ce1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,4 +7,4 @@ # Order is important. The last matching pattern has the most precedence. -* @siriak @raklaptudirm @tjgurwara99 @yanglbme +* @raklaptudirm @yanglbme diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..be006de9a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +# Keep GitHub Actions up to date with GitHub's Dependabot... +# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + groups: + github-actions: + patterns: + - "*" # Group all Actions updates into a single larger pull request + schedule: + interval: weekly diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 29615bbe0..7a5ce1939 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,13 +15,13 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: '^1.18' - name: Run Golang CI Lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: version: latest args: -E gofmt @@ -31,7 +31,7 @@ jobs: name: Check for spelling errors runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: codespell-project/actions-codespell@master with: ignore_words_list: "actualy,nwe" diff --git a/.github/workflows/citk.yml b/.github/workflows/citk.yml new file mode 100644 index 000000000..4b675f239 --- /dev/null +++ b/.github/workflows/citk.yml @@ -0,0 +1,28 @@ +# https://github.com/golangci/golangci-lint +name: CI tool kit +on: + pull_request: + +jobs: + CITK: + name: Code style and tests + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "^1.18" + - name: Checkout branch + run: | + git fetch origin master:master + - name: Install citk tool + run: | + go install github.com/tjgurwara99/citk@latest + - name: Run citk tool + run: | + citk check -l go -b master diff --git a/.github/workflows/godocmd.yml b/.github/workflows/godocmd.yml index 2ed6aec61..7e7b306e3 100644 --- a/.github/workflows/godocmd.yml +++ b/.github/workflows/godocmd.yml @@ -1,5 +1,5 @@ name: Generate Documentation -on: +on: push: branches: - master @@ -9,10 +9,10 @@ jobs: name: Markdown Generation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v5 with: go-version: '^1.18' - name: Install GoDocMD @@ -20,8 +20,8 @@ jobs: go install github.com/tjgurwara99/godocmd@v0.1.3 - name: Configure Github Action run: | - git config --global user.name github-action - git config --global user.email '${GITHUB_ACTOR}@users.noreply.github.com' + git config --global user.name "$GITHUB_ACTOR" + git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Update README.md file run: | godocmd -r -module ./ -w diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 126bd7ad4..8118ebf78 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -6,7 +6,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v4 + - uses: actions/stale@v9 with: stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.' diff --git a/.github/workflows/upload_coverage_report.yml b/.github/workflows/upload_coverage_report.yml new file mode 100644 index 000000000..50ce54650 --- /dev/null +++ b/.github/workflows/upload_coverage_report.yml @@ -0,0 +1,39 @@ +--- +name: upload_coverage_report + +on: + workflow_dispatch: + push: + branches: + - master + pull_request: + +env: + REPORT_NAME: "coverage.out" + +jobs: + upload_coverage_report: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '^1.18' + - name: Generate code coverage + run: | + go test -coverprofile="${{ env.REPORT_NAME }}" ./... + - name: Upload coverage to codecov (tokenless) + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository + uses: codecov/codecov-action@v5 + with: + files: "${{ env.REPORT_NAME }}" + fail_ci_if_error: true + - name: Upload coverage to codecov (with token) + if: "! github.event.pull_request.head.repo.fork " + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: "${{ env.REPORT_NAME }}" + fail_ci_if_error: true +... diff --git a/.gitignore b/.gitignore index 66f8fb502..1af018398 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ .vscode/ +coverage.out diff --git a/.gitpod.dockerfile b/.gitpod.dockerfile new file mode 100644 index 000000000..84fa1774b --- /dev/null +++ b/.gitpod.dockerfile @@ -0,0 +1 @@ +FROM gitpod/workspace-go diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..f9199716f --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,7 @@ +--- +image: + file: .gitpod.dockerfile + +tasks: + - init: | + echo "Welcome to TheAlgorithms/Go" diff --git a/LICENSE b/LICENSE index f6bcf04e7..71b9656b7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 The Algorithms +Copyright (c) 2021 The Algorithms and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 776a50f4b..51f244249 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # The Algorithms - Go [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod&style=flat-square)](https://gitpod.io/#https://github.com/TheAlgorithms/Go)  [![Continuous Integration](https://github.com/TheAlgorithms/Go/actions/workflows/ci.yml/badge.svg)](https://github.com/TheAlgorithms/Go/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/TheAlgorithms/Go/graph/badge.svg?token=aSWh7N8tNx)](https://codecov.io/gh/TheAlgorithms/Go) ![godocmd](https://github.com/tjgurwara99/Go/workflows/godocmd/badge.svg) ![](https://img.shields.io/github/repo-size/TheAlgorithms/Go.svg?label=Repo%20size&style=flat-square)  ![update_directory_md](https://github.com/TheAlgorithms/Go/workflows/update_directory_md/badge.svg) @@ -70,15 +71,16 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 1. [`Abs`](./math/binary/abs.go#L10): Abs returns absolute value using binary operation Principle of operation: 1) Get the mask by right shift by the base 2) Base is the size of an integer variable in bits, for example, for int32 it will be 32, for int64 it will be 64 3) For negative numbers, above step sets mask as 1 1 1 1 1 1 1 1 and 0 0 0 0 0 0 0 0 for positive numbers. 4) Add the mask to the given number. 5) XOR of mask + n and mask gives the absolute value. 2. [`BitCounter`](./math/binary/bitcounter.go#L11): BitCounter - The function returns the number of set bits for an unsigned integer number -3. [`IsPowerOfTwo`](./math/binary/checkisnumberpoweroftwo.go#L21): IsPowerOfTwo This function uses the fact that powers of 2 are represented like 10...0 in binary, and numbers one less than the power of 2 are represented like 11...1. Therefore, using the and function: 10...0 & 01...1 00...0 -> 0 This is also true for 0, which is not a power of 2, for which we have to add and extra condition. -4. [`IsPowerOfTwoLeftShift`](./math/binary/checkisnumberpoweroftwo.go#L28): IsPowerOfTwoLeftShift This function takes advantage of the fact that left shifting a number by 1 is equivalent to multiplying by 2. For example, binary 00000001 when shifted by 3 becomes 00001000, which in decimal system is 8 or = 2 * 2 * 2 -5. [`LogBase2`](./math/binary/logarithm.go#L7): LogBase2 Finding the exponent of n = 2**x using bitwise operations (logarithm in base 2 of n) [See more](https://en.wikipedia.org/wiki/Logarithm) -6. [`MeanUsingAndXor`](./math/binary/arithmeticmean.go#L12): MeanUsingAndXor This function finds arithmetic mean using "AND" and "XOR" operations -7. [`MeanUsingRightShift`](./math/binary/arithmeticmean.go#L17): MeanUsingRightShift This function finds arithmetic mean using right shift -8. [`ReverseBits`](./math/binary/reversebits.go#L14): ReverseBits This function initialized the result by 0 (all bits 0) and process the given number starting from its least significant bit. If the current bit is 1, set the corresponding most significant bit in the result and finally move on to the next bit in the input number. Repeat this till all its bits are processed. -9. [`SequenceGrayCode`](./math/binary/rbc.go#L11): SequenceGrayCode The function generates an "Gray code" sequence of length n -10. [`Sqrt`](./math/binary/sqrt.go#L16): No description provided. -11. [`XorSearchMissingNumber`](./math/binary/xorsearch.go#L11): XorSearchMissingNumber This function finds a missing number in a sequence +3. [`FastInverseSqrt`](./math/binary/fast_inverse_sqrt.go#L15): FastInverseSqrt assumes that argument is always positive, and it does not deal with negative numbers. The "magic" number 0x5f3759df is hex for 1597463007 in decimals. The math.Float32bits is alias to *(*uint32)(unsafe.Pointer(&f)) and math.Float32frombits is to *(*float32)(unsafe.Pointer(&b)). +4. [`IsPowerOfTwo`](./math/binary/checkisnumberpoweroftwo.go#L21): IsPowerOfTwo This function uses the fact that powers of 2 are represented like 10...0 in binary, and numbers one less than the power of 2 are represented like 11...1. Therefore, using the and function: 10...0 & 01...1 00...0 -> 0 This is also true for 0, which is not a power of 2, for which we have to add and extra condition. +5. [`IsPowerOfTwoLeftShift`](./math/binary/checkisnumberpoweroftwo.go#L28): IsPowerOfTwoLeftShift This function takes advantage of the fact that left shifting a number by 1 is equivalent to multiplying by 2. For example, binary 00000001 when shifted by 3 becomes 00001000, which in decimal system is 8 or = 2 * 2 * 2 +6. [`LogBase2`](./math/binary/logarithm.go#L7): LogBase2 Finding the exponent of n = 2**x using bitwise operations (logarithm in base 2 of n) [See more](https://en.wikipedia.org/wiki/Logarithm) +7. [`MeanUsingAndXor`](./math/binary/arithmeticmean.go#L12): MeanUsingAndXor This function finds arithmetic mean using "AND" and "XOR" operations +8. [`MeanUsingRightShift`](./math/binary/arithmeticmean.go#L17): MeanUsingRightShift This function finds arithmetic mean using right shift +9. [`ReverseBits`](./math/binary/reversebits.go#L14): ReverseBits This function initialized the result by 0 (all bits 0) and process the given number starting from its least significant bit. If the current bit is 1, set the corresponding most significant bit in the result and finally move on to the next bit in the input number. Repeat this till all its bits are processed. +10. [`SequenceGrayCode`](./math/binary/rbc.go#L11): SequenceGrayCode The function generates an "Gray code" sequence of length n +11. [`Sqrt`](./math/binary/sqrt.go#L10): No description provided. +12. [`XorSearchMissingNumber`](./math/binary/xorsearch.go#L11): XorSearchMissingNumber This function finds a missing number in a sequence ---
@@ -88,12 +90,16 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. ##### Functions: -1. [`NewLRU`](./cache/lru.go#L20): NewLRU represent initiate lru cache with capacity +1. [`NewLRU`](./cache/lru.go#L22): NewLRU represent initiate lru cache with capacity +2. [`NewLFU`](./cache/lfu.go#L33): NewLFU represent initiate lfu cache with capacity +3. [`Get`](./cache/lfu.go#L52): Get the value by key from LFU cache +4. [`Put`](./cache/lfu.go#L66): Put the key and value in LFU cache --- ##### Types -1. [`LRU`](./cache/lru.go#L12): No description provided. +1. [`LRU`](./cache/lru.go#L15): Default the struct of lru cache algorithm. +2. [`LFU`](./cache/lfu.go#L19): Default the struct of lfu cache algorithm. --- @@ -223,8 +229,8 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 1. [`Base64Decode`](./conversion/base64.go#L57): Base64Decode decodes the received input base64 string into a byte slice. The implementation follows the RFC4648 standard, which is documented at https://datatracker.ietf.org/doc/html/rfc4648#section-4 2. [`Base64Encode`](./conversion/base64.go#L19): Base64Encode encodes the received input bytes slice into a base64 string. The implementation follows the RFC4648 standard, which is documented at https://datatracker.ietf.org/doc/html/rfc4648#section-4 -3. [`BinaryToDecimal`](./conversion/binarytodecimal.go#L25): BinaryToDecimal() function that will take Binary number as string, and return it's Decimal equivalent as integer. -4. [`DecimalToBinary`](./conversion/decimaltobinary.go#L32): DecimalToBinary() function that will take Decimal number as int, and return it's Binary equivalent as string. +3. [`BinaryToDecimal`](./conversion/binarytodecimal.go#L25): BinaryToDecimal() function that will take Binary number as string, and return its Decimal equivalent as integer. +4. [`DecimalToBinary`](./conversion/decimaltobinary.go#L32): DecimalToBinary() function that will take Decimal number as int, and return its Binary equivalent as string. 5. [`FuzzBase64Encode`](./conversion/base64_test.go#L113): No description provided. 6. [`HEXToRGB`](./conversion/rgbhex.go#L10): HEXToRGB splits an RGB input (e.g. a color in hex format; 0x) into the individual components: red, green and blue 7. [`IntToRoman`](./conversion/inttoroman.go#L17): IntToRoman converts an integer value to a roman numeral string. An error is returned if the integer is not between 1 and 3999. @@ -232,6 +238,38 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 9. [`Reverse`](./conversion/decimaltobinary.go#L22): Reverse() function that will take string, and returns the reverse of that string. 10. [`RomanToInt`](./conversion/romantoint.go#L40): RomanToInt converts a roman numeral string to an integer. Roman numerals for numbers outside the range 1 to 3,999 will return an error. Nil or empty string return 0 with no error thrown. +--- +
+ deque + +--- + +##### Package deque implements a Double Ended Queue data structure. + +--- +##### Functions: + +1. [`New`](./structure/deque/deque.go#L22): New returns a new DoublyEndedQueue. + +--- +##### Types + +1. [`DoublyEndedQueue`](./structure/deque/deque.go#L17): No description provided. + + +--- +
+ deque_test + +--- + +##### Types + +1. [`QueryStructure`](./structure/deque/deque_test.go#L20): No description provided. + +2. [`TestCaseData`](./structure/deque/deque_test.go#L27): No description provided. + + ---
diffiehellman @@ -276,7 +314,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 17. [`Max`](./dynamic/knapsack.go#L11): Max function - possible duplicate 18. [`NthCatalanNumber`](./dynamic/catalan.go#L13): NthCatalan returns the n-th Catalan Number Complexity: O(n²) 19. [`NthFibonacci`](./dynamic/fibonacci.go#L6): NthFibonacci returns the nth Fibonacci Number - +20. [`TrapRainWater`](./dynamic/traprainwater.go#L17): Calculate the amount of trapped rainwater between bars represented by an elevation map using dynamic programming. ---
dynamicarray @@ -316,6 +354,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 1. [`Formula`](./math/fibonacci/fibonacci.go#L42): Formula This function calculates the n-th fibonacci number using the [formula](https://en.wikipedia.org/wiki/Fibonacci_number#Relation_to_the_golden_ratio) Attention! Tests for large values fall due to rounding error of floating point numbers, works well, only on small numbers 2. [`Matrix`](./math/fibonacci/fibonacci.go#L15): Matrix This function calculates the n-th fibonacci number using the matrix method. [See](https://en.wikipedia.org/wiki/Fibonacci_number#Matrix_form) +3. [`Recursive`](./math/fibonacci/fibonacci.go#L51): Recursive calculates the n-th fibonacci number recursively by adding the previous two Fibonacci numbers. This algorithm is extremely slow for bigger numbers, but provides a simpler implementation. ---
@@ -415,34 +454,34 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 4. [`DepthFirstSearchHelper`](./graph/depthfirstsearch.go#L21): No description provided. 5. [`FloydWarshall`](./graph/floydwarshall.go#L15): FloydWarshall Returns all pair's shortest path using Floyd Warshall algorithm 6. [`GetIdx`](./graph/depthfirstsearch.go#L3): No description provided. -7. [`KruskalMST`](./graph/kruskal.go#L87): KruskalMST will return a minimum spanning tree along with its total cost to using Kruskal's algorithm. Time complexity is O(m * log (n)) where m is the number of edges in the graph and n is number of nodes in it. -8. [`LowestCommonAncestor`](./graph/lowestcommonancestor.go#L111): For each node, we will precompute its ancestor above him, its ancestor two nodes above, its ancestor four nodes above, etc. Let's call `jump[j][u]` is the `2^j`-th ancestor above the node `u` with `u` in range `[0, numbersVertex)`, `j` in range `[0,MAXLOG)`. These information allow us to jump from any node to any ancestor above it in `O(MAXLOG)` time. -9. [`New`](./graph/graph.go#L16): Constructor functions for graphs (undirected by default) -10. [`NewDSU`](./graph/kruskal.go#L34): NewDSU will return an initialised DSU using the value of n which will be treated as the number of elements out of which the DSU is being made +7. [`KruskalMST`](./graph/kruskal.go#L23): No description provided. +8. [`PrimMST`](./graph/prim.go#30): Computes the minimum spanning tree of a weighted undirected graph +9. [`LowestCommonAncestor`](./graph/lowestcommonancestor.go#L111): For each node, we will precompute its ancestor above him, its ancestor two nodes above, its ancestor four nodes above, etc. Let's call `jump[j][u]` is the `2^j`-th ancestor above the node `u` with `u` in range `[0, numbersVertex)`, `j` in range `[0,MAXLOG)`. These information allow us to jump from any node to any ancestor above it in `O(MAXLOG)` time. +10. [`New`](./graph/graph.go#L16): Constructor functions for graphs (undirected by default) 11. [`NewTree`](./graph/lowestcommonancestor.go#L84): No description provided. -12. [`NotExist`](./graph/depthfirstsearch.go#L12): No description provided. -13. [`Topological`](./graph/topological.go#L7): Assumes that graph given is valid and possible to get a topo ordering. constraints are array of []int{a, b}, representing an edge going from a to b +12. [`NewUnionFind`](./graph/unionfind.go#L24): Initialise a new union find data structure with s nodes +13. [`NotExist`](./graph/depthfirstsearch.go#L12): No description provided. +14. [`Topological`](./graph/topological.go#L7): Topological assumes that graph given is valid and that its possible to get a topological ordering. constraints are array of []int{a, b}, representing an edge going from a to b +15. [`Edmond-Karp`](./graph/edmondkarp.go#L43): Computes the maximum possible flow between a pair of s-t vertices in a weighted graph --- ##### Types -1. [`DisjointSetUnion`](./graph/kruskal.go#L29): No description provided. - -2. [`DisjointSetUnionElement`](./graph/kruskal.go#L21): No description provided. +1. [`Edge`](./graph/kruskal.go#L17): No description provided. -3. [`Edge`](./graph/kruskal.go#L14): No description provided. +2. [`Graph`](./graph/graph.go#L9): No description provided. -4. [`Graph`](./graph/graph.go#L9): No description provided. +3. [`Item`](./graph/dijkstra.go#L5): No description provided. -5. [`Item`](./graph/dijkstra.go#L5): No description provided. +4. [`Query`](./graph/lowestcommonancestor_test.go#L9): No description provided. -6. [`Query`](./graph/lowestcommonancestor_test.go#L9): No description provided. +5. [`Tree`](./graph/lowestcommonancestor.go#L25): No description provided. -7. [`Tree`](./graph/lowestcommonancestor.go#L25): No description provided. +6. [`TreeEdge`](./graph/lowestcommonancestor.go#L12): No description provided. -8. [`TreeEdge`](./graph/lowestcommonancestor.go#L12): No description provided. +7. [`UnionFind`](./graph/unionfind.go#L18): No description provided. -9. [`WeightedGraph`](./graph/floydwarshall.go#L9): No description provided. +8. [`WeightedGraph`](./graph/floydwarshall.go#L9): No description provided. --- @@ -509,6 +548,16 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. Methods: 1. [`Less`](./structure/heap/heap_test.go#L20): No description provided. +--- +
+ horspool + +--- + +##### Functions: + +1. [`Horspool`](./strings/horspool/horspool.go#L10): No description provided. + ---
kmp @@ -592,7 +641,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. --- -##### filename : krishnamurthy.go description: A program which contains the function that returns true if a given number is Krishnamurthy number or not. details: A number is a Krishnamurthy number if the sum of all the factorials of the digits is equal to the number. Ex: 1! = 1, 145 = 1! + 4! + 5! author(s): [GooMonk](https://github.com/GooMonk) see krishnamurthy_test.go Package math is a package that contains mathematical algorithms and its different implementations. +##### Package math is a package that contains mathematical algorithms and its different implementations. filename : krishnamurthy.go description: A program which contains the function that returns true if a given number is Krishnamurthy number or not. details: A number is a Krishnamurthy number if the sum of all the factorials of the digits is equal to the number. Ex: 1! = 1, 145 = 1! + 4! + 5! author(s): [GooMonk](https://github.com/GooMonk) see krishnamurthy_test.go --- ##### Functions: @@ -604,20 +653,52 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 5. [`DefaultPolynomial`](./math/pollard.go#L16): DefaultPolynomial is the commonly used polynomial g(x) = (x^2 + 1) mod n 6. [`FindKthMax`](./math/kthnumber.go#L11): FindKthMax returns the kth large element given an integer slice with nil `error` if found and returns -1 with `error` `search.ErrNotFound` if not found. NOTE: The `nums` slice gets mutated in the process. 7. [`FindKthMin`](./math/kthnumber.go#L19): FindKthMin returns kth small element given an integer slice with nil `error` if found and returns -1 with `error` `search.ErrNotFound` if not found. NOTE: The `nums` slice gets mutated in the process. -8. [`IsKrishnamurthyNumber`](./math/krishnamurthy.go#L12): IsKrishnamurthyNumber returns if the provided number n is a Krishnamurthy number or not. -9. [`IsPerfectNumber`](./math/perfectnumber.go#L34): Checks if inNumber is a perfect number -10. [`IsPowOfTwoUseLog`](./math/checkisnumberpoweroftwo.go#L10): IsPowOfTwoUseLog This function checks if a number is a power of two using the logarithm. The limiting degree can be from 0 to 63. See alternatives in the binary package. -11. [`Lerp`](./math/lerp.go#L5): Lerp or Linear interpolation This function will return new value in 't' percentage between 'v0' and 'v1' -12. [`LiouvilleLambda`](./math/liouville.go#L24): Lambda is the liouville function This function returns λ(n) for given number -13. [`Mean`](./math/mean.go#L7): No description provided. -14. [`Median`](./math/median.go#L12): No description provided. -15. [`Mode`](./math/mode.go#L19): No description provided. -16. [`Mu`](./math/mobius.go#L21): Mu is the Mobius function This function returns μ(n) for given number -17. [`Phi`](./math/eulertotient.go#L5): Phi is the Euler totient function. This function computes the number of numbers less then n that are coprime with n. -18. [`PollardsRhoFactorization`](./math/pollard.go#L29): PollardsRhoFactorization is an implementation of Pollard's rho factorization algorithm using the default parameters x = y = 2 -19. [`PronicNumber`](./math/pronicnumber.go#L15): PronicNumber returns true if argument passed to the function is pronic and false otherwise. -20. [`Sin`](./math/sin.go#L9): Sin returns the sine of the radian argument x. [See more](https://en.wikipedia.org/wiki/Sine_and_cosine) -21. [`SumOfProperDivisors`](./math/perfectnumber.go#L17): Returns the sum of proper divisors of inNumber. +8. [`IsAutomorphic`](./math/isautomorphic.go#L16): No description provided. +9. [`IsKrishnamurthyNumber`](./math/krishnamurthy.go#L12): IsKrishnamurthyNumber returns if the provided number n is a Krishnamurthy number or not. +10. [`IsPerfectNumber`](./math/perfectnumber.go#L34): Checks if inNumber is a perfect number +11. [`IsPowOfTwoUseLog`](./math/checkisnumberpoweroftwo.go#L10): IsPowOfTwoUseLog This function checks if a number is a power of two using the logarithm. The limiting degree can be from 0 to 63. See alternatives in the binary package. +12. [`Lerp`](./math/lerp.go#L5): Lerp or Linear interpolation This function will return new value in 't' percentage between 'v0' and 'v1' +13. [`LiouvilleLambda`](./math/liouville.go#L24): Lambda is the liouville function This function returns λ(n) for given number +14. [`Mean`](./math/mean.go#L7): No description provided. +15. [`Median`](./math/median.go#L12): No description provided. +16. [`Mode`](./math/mode.go#L19): No description provided. +17. [`Mu`](./math/mobius.go#L21): Mu is the Mobius function This function returns μ(n) for given number +18. [`Phi`](./math/eulertotient.go#L5): Phi is the Euler totient function. This function computes the number of numbers less then n that are coprime with n. +19. [`PollardsRhoFactorization`](./math/pollard.go#L29): PollardsRhoFactorization is an implementation of Pollard's rho factorization algorithm using the default parameters x = y = 2 +20. [`PronicNumber`](./math/pronicnumber.go#L15): PronicNumber returns true if argument passed to the function is pronic and false otherwise. +21. [`Sin`](./math/sin.go#L9): Sin returns the sine of the radian argument x. [See more](https://en.wikipedia.org/wiki/Sine_and_cosine) +22. [`SumOfProperDivisors`](./math/perfectnumber.go#L17): Returns the sum of proper divisors of inNumber. + +--- +
+ matrix + +--- + +##### filename: strassenmatrixmultiply.go description: Implements matrix multiplication using the Strassen algorithm. details: This program takes two matrices as input and performs matrix multiplication using the Strassen algorithm, which is an optimized divide-and-conquer approach. It allows for efficient multiplication of large matrices. author(s): Mohit Raghav(https://github.com/mohit07raghav19) See strassenmatrixmultiply_test.go for test cases + +--- +##### Functions: + +1. [`IsValid`](./math/matrix/isvalid.go#L6): IsValid checks if the input matrix has consistent row lengths. +2. [`New`](./math/matrix/matrix.go#L17): NewMatrix creates a new Matrix based on the provided arguments. +3. [`NewFromElements`](./math/matrix/matrix.go#L43): NewFromElements creates a new Matrix from the given elements. + +--- +##### Types + +1. [`Matrix`](./math/matrix/matrix.go#L10): No description provided. + + +--- +
+ matrix_test + +--- + +##### Functions: + +1. [`MakeRandomMatrix`](./math/matrix/strassenmatrixmultiply_test.go#L105): No description provided. ---
@@ -718,7 +799,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. ##### Functions: -1. [`Parenthesis`](./strings/parenthesis/parenthesis.go#L12): parcounter will be 0 if all open parenthesis are closed correctly +1. [`Parenthesis`](./strings/parenthesis/parenthesis.go#L8): Parenthesis algorithm checks if every opened parenthesis is closed correctly. When parcounter is less than 0 when a closing parenthesis is detected without an opening parenthesis that surrounds it and parcounter will be 0 if all open parenthesis are closed correctly. ---
@@ -753,6 +834,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 1. [`GenerateElementSet`](./math/permutation/heaps.go#L37): No description provided. 2. [`Heaps`](./math/permutation/heaps.go#L8): Heap's Algorithm for generating all permutations of n objects +3. [`NextPermutation`](./math/permutation/next_permutation.go#8): A solution to find next permutation of an integer array in constant memory ---
@@ -953,28 +1035,31 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. --- ##### Functions: -1. [`Bubble`](./sort/bubblesort.go#L9): Bubble is a simple generic definition of Bubble sort algorithm. -2. [`Bucket`](./sort/bucketsort.go#L7): Bucket sorts a slice. It is mainly useful when input is uniformly distributed over a range. -3. [`Comb`](./sort/combSort.go#L17): Comb is a simple sorting algorithm which is an improvement of the bubble sorting algorithm. -4. [`Count`](./sort/countingsort.go#L11): No description provided. -5. [`Cycle`](./sort/cyclesort.go#L10): Cycle sort is an in-place, unstable sorting algorithm that is particularly useful when sorting arrays containing elements with a small range of values. It is theoretically optimal in terms of the total number of writes to the original array. -6. [`Exchange`](./sort/exchangesort.go#L8): No description provided. -7. [`HeapSort`](./sort/heapsort.go#L116): No description provided. -8. [`ImprovedSimple`](./sort/simplesort.go#L27): ImprovedSimple is a improve SimpleSort by skipping an unnecessary comparison of the first and last. This improved version is more similar to implementation of insertion sort -9. [`Insertion`](./sort/insertionsort.go#L5): No description provided. -10. [`Merge`](./sort/mergesort.go#L41): Merge Perform merge sort on a slice -11. [`MergeIter`](./sort/mergesort.go#L55): No description provided. -12. [`Pancake`](./sort/pancakesort.go#L8): Pancake sorts a slice using flip operations, where flip refers to the idea of reversing the slice from index `0` to `i`. -13. [`ParallelMerge`](./sort/mergesort.go#L66): ParallelMerge Perform merge sort on a slice using goroutines -14. [`Partition`](./sort/quicksort.go#L12): No description provided. -15. [`Patience`](./sort/patiencesort.go#L13): No description provided. -16. [`Pigeonhole`](./sort/pigeonholesort.go#L15): Pigeonhole sorts a slice using pigeonhole sorting algorithm. NOTE: To maintain time complexity O(n + N), this is the reason for having only Integer constraint instead of Ordered. -17. [`Quicksort`](./sort/quicksort.go#L39): Quicksort Sorts the entire array -18. [`QuicksortRange`](./sort/quicksort.go#L26): QuicksortRange Sorts the specified range within the array -19. [`RadixSort`](./sort/radixsort.go#L43): No description provided. -20. [`Selection`](./sort/selectionsort.go#L5): No description provided. -21. [`Shell`](./sort/shellsort.go#L5): No description provided. -22. [`Simple`](./sort/simplesort.go#L13): No description provided. +1. [`BinaryInsertion`](./sort/binaryinsertionsort.go#L13): No description provided. +2. [`Bogo`](./sort/bogosort.go#L32): No description provided. +3. [`Bubble`](./sort/bubblesort.go#L9): Bubble is a simple generic definition of Bubble sort algorithm. +4. [`Bucket`](./sort/bucketsort.go#L7): Bucket sorts a slice. It is mainly useful when input is uniformly distributed over a range. +5. [`Cocktail`](./sort/cocktailsort.go#L9): Cocktail sort is a variation of bubble sort, operating in two directions (beginning to end, end to beginning) +6. [`Comb`](./sort/combSort.go#L17): Comb is a simple sorting algorithm which is an improvement of the bubble sorting algorithm. +7. [`Count`](./sort/countingsort.go#L11): No description provided. +8. [`Cycle`](./sort/cyclesort.go#L10): Cycle sort is an in-place, unstable sorting algorithm that is particularly useful when sorting arrays containing elements with a small range of values. It is theoretically optimal in terms of the total number of writes to the original array. +9. [`Exchange`](./sort/exchangesort.go#L8): No description provided. +10. [`HeapSort`](./sort/heapsort.go#L116): No description provided. +11. [`ImprovedSimple`](./sort/simplesort.go#L27): ImprovedSimple is a improve SimpleSort by skipping an unnecessary comparison of the first and last. This improved version is more similar to implementation of insertion sort +12. [`Insertion`](./sort/insertionsort.go#L5): No description provided. +13. [`Merge`](./sort/mergesort.go#L41): Merge Perform merge sort on a slice +14. [`MergeIter`](./sort/mergesort.go#L55): No description provided. +15. [`Pancake`](./sort/pancakesort.go#L8): Pancake sorts a slice using flip operations, where flip refers to the idea of reversing the slice from index `0` to `i`. +16. [`ParallelMerge`](./sort/mergesort.go#L66): ParallelMerge Perform merge sort on a slice using goroutines +17. [`Partition`](./sort/quicksort.go#L12): No description provided. +18. [`Patience`](./sort/patiencesort.go#L13): No description provided. +19. [`Pigeonhole`](./sort/pigeonholesort.go#L15): Pigeonhole sorts a slice using pigeonhole sorting algorithm. NOTE: To maintain time complexity O(n + N), this is the reason for having only Integer constraint instead of Ordered. +20. [`Quicksort`](./sort/quicksort.go#L39): Quicksort Sorts the entire array +21. [`QuicksortRange`](./sort/quicksort.go#L26): QuicksortRange Sorts the specified range within the array +22. [`RadixSort`](./sort/radixsort.go#L43): No description provided. +23. [`Selection`](./sort/selectionsort.go#L5): No description provided. +24. [`Shell`](./sort/shellsort.go#L5): No description provided. +25. [`Simple`](./sort/simplesort.go#L13): No description provided. --- ##### Types @@ -982,19 +1067,45 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 1. [`MaxHeap`](./sort/heapsort.go#L5): No description provided. +--- +
+ sqrt + +--- + +##### Package sqrt contains algorithms and data structures that contains a √n in their complexity + +--- +##### Functions: + +1. [`NewSqrtDecomposition`](./sqrt/sqrtdecomposition.go#L34): Create a new SqrtDecomposition instance with the parameters as specified by SqrtDecomposition comment Assumptions: - len(elements) > 0 + +--- +##### Types + +1. [`SqrtDecomposition`](./sqrt/sqrtdecomposition.go#L21): No description provided. + + ---
stack --- +##### Functions: + +1. [`NewStack`](./structure/stack/stackarray.go#L17): NewStack creates and returns a new stack. + +--- ##### Types -1. [`Node`](./structure/stack/stacklinkedlist.go#L13): No description provided. +1. [`Array`](./structure/stack/stackarray.go#L12): No description provided. + +2. [`Node`](./structure/stack/stacklinkedlist.go#L13): No description provided. -2. [`SList`](./structure/stack/stacklinkedlistwithlist.go#L18): No description provided. +3. [`SList`](./structure/stack/stacklinkedlistwithlist.go#L18): No description provided. -3. [`Stack`](./structure/stack/stacklinkedlist.go#L19): No description provided. +4. [`Stack`](./structure/stack/stacklinkedlist.go#L19): No description provided. --- @@ -1010,6 +1121,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 1. [`CountChars`](./strings/charoccurrence.go#L12): CountChars counts the number of a times a character has occurred in the provided string argument and returns a map with `rune` as keys and the count as value. 2. [`IsIsogram`](./strings/isisogram.go#L34): No description provided. +3. [`IsSubsequence`](./strings/issubsequence.go#L10): Returns true if s is subsequence of t, otherwise return false. ---
@@ -1088,6 +1200,19 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. 2. [`Encrypt`](./cipher/xor/xor.go#L10): Encrypt encrypts with Xor encryption after converting each character to byte The returned value might not be readable because there is no guarantee which is within the ASCII range If using other type such as string, []int, or some other types, add the statements for converting the type to []byte. 3. [`FuzzXOR`](./cipher/xor/xor_test.go#L108): No description provided. +--- +
+ +##### Package rail fence is a classical type of transposition cipher ref : https://en.wikipedia.org/wiki/Rail_fence_cipher + +--- +##### Functions: + +1. [`Encrypt`](.cipher/railfence/railfence.go#L7): Encrypt encrypts a message using rail fence cipher +2. [`Decrypt`](.cipher/railfence/railfence.go#L44): decrypt decrypts a message using rail fence cipher +3. [`TestEncrypt`](.cipher/railfence/railfence_test.go#L7) Test function for Encrypt +4. [`TestDecrypt`](.cipher/railfence/railfence_test.go#L50) Test function for Decrypt + --- diff --git a/cache/lfu.go b/cache/lfu.go new file mode 100644 index 000000000..e22670c68 --- /dev/null +++ b/cache/lfu.go @@ -0,0 +1,127 @@ +// lfu.go +// description: a type of cache algorithm used to manage memory within a computer. +// details: +// The standard characteristics of this method involve the system keeping track of the number of times a block is referenced in memory. +// When the cache is full and requires more room the system will purge the item with the lowest reference frequency. +// ref: (https://en.wikipedia.org/wiki/Least_frequently_used) +// time complexity: O(N) +// space complexity: O(1) +// author: [CocaineCong](https://github.com/CocaineCong) + +package cache + +import ( + "container/list" + "math" +) + +// LFU the Least Frequently Used (LFU) page-replacement algorithm +type LFU struct { + len int // length + cap int // capacity + minFreq int // The element that operates least frequently in LFU + + // key: key of element, value: value of element + itemMap map[string]*list.Element + + // key: frequency of possible occurrences of all elements in the itemMap + // value: elements with the same frequency + freqMap map[int]*list.List +} + +// NewLFU init the LFU cache with capacity +func NewLFU(capacity int) LFU { + return LFU{ + len: 0, + cap: capacity, + minFreq: math.MaxInt, + itemMap: make(map[string]*list.Element), + freqMap: make(map[int]*list.List), + } +} + +// initItem to init item for LFU +func initItem(k string, v any, f int) item { + return item{ + key: k, + value: v, + freq: f, + } +} + +// Get the key in cache by LFU +func (c *LFU) Get(key string) any { + // if existed, will return value + if e, ok := c.itemMap[key]; ok { + // the frequency of e +1 and change freqMap + c.increaseFreq(e) + obj := e.Value.(item) + return obj.value + } + + // if not existed, return nil + return nil +} + +// Put the key in LFU cache +func (c *LFU) Put(key string, value any) { + if e, ok := c.itemMap[key]; ok { + // if key existed, update the value + obj := e.Value.(item) + obj.value = value + c.increaseFreq(e) + } else { + // if key not existed + obj := initItem(key, value, 1) + // if the length of item gets to the top line + // remove the least frequently operated element + if c.len == c.cap { + c.eliminate() + c.len-- + } + // insert in freqMap and itemMap + c.insertMap(obj) + // change minFreq to 1 because insert the newest one + c.minFreq = 1 + // length++ + c.len++ + } +} + +// increaseFreq increase the frequency if element +func (c *LFU) increaseFreq(e *list.Element) { + obj := e.Value.(item) + // remove from low frequency first + oldLost := c.freqMap[obj.freq] + oldLost.Remove(e) + // change the value of minFreq + if c.minFreq == obj.freq && oldLost.Len() == 0 { + // if it is the last node of the minimum frequency that is removed + c.minFreq++ + } + // add to high frequency list + c.insertMap(obj) +} + +// insertMap insert item in map +func (c *LFU) insertMap(obj item) { + // add in freqMap + l, ok := c.freqMap[obj.freq] + if !ok { + l = list.New() + c.freqMap[obj.freq] = l + } + e := l.PushFront(obj) + // update or add the value of itemMap key to e + c.itemMap[obj.key] = e +} + +// eliminate clear the least frequently operated element +func (c *LFU) eliminate() { + l := c.freqMap[c.minFreq] + e := l.Back() + obj := e.Value.(item) + l.Remove(e) + + delete(c.itemMap, obj.key) +} diff --git a/cache/lfu_test.go b/cache/lfu_test.go new file mode 100644 index 000000000..7e891412f --- /dev/null +++ b/cache/lfu_test.go @@ -0,0 +1,58 @@ +package cache_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/cache" +) + +func TestLFU(t *testing.T) { + lfuCache := cache.NewLFU(3) + t.Run("Test 1: Put number and Get is correct", func(t *testing.T) { + key, value := "1", 1 + lfuCache.Put(key, value) + got := lfuCache.Get(key) + + if got != value { + t.Errorf("expected: %v, got: %v", value, got) + } + }) + + t.Run("Test 2: Get data not stored in cache should return nil", func(t *testing.T) { + got := lfuCache.Get("2") + if got != nil { + t.Errorf("expected: nil, got: %v", got) + } + }) + + t.Run("Test 3: Put data over capacity and Get should return nil", func(t *testing.T) { + lfuCache.Put("2", 2) + lfuCache.Put("3", 3) + lfuCache.Put("4", 4) + + got := lfuCache.Get("1") + if got != nil { + t.Errorf("expected: nil, got: %v", got) + } + }) + + t.Run("test 4: Put key over capacity but recent key exists", func(t *testing.T) { + lfuCache.Put("4", 4) + lfuCache.Put("3", 3) + lfuCache.Put("2", 2) + lfuCache.Put("1", 1) + + got := lfuCache.Get("4") + if got != nil { + t.Errorf("expected: nil, got: %v", got) + } + + expected := 3 + got = lfuCache.Get("3") + + if got != expected { + t.Errorf("expected: %v, got: %v", expected, got) + } + + }) +} diff --git a/cache/lru.go b/cache/lru.go index c6b8c734e..b9578a9fe 100644 --- a/cache/lru.go +++ b/cache/lru.go @@ -1,3 +1,10 @@ +// lru.go +// description : Least Recently Used (LRU) cache +// details : A Least Recently Used (LRU) cache is a type of cache algorithm used to manage memory within a computer. The LRU algorithm is designed to remove the least recently used items first when the cache reaches its limit. +// time complexity : O(1) +// space complexity : O(n) +// ref : https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU) + package cache import ( @@ -7,6 +14,9 @@ import ( type item struct { key string value any + + // the frequency of key + freq int } type LRU struct { diff --git a/checksum/crc8.go b/checksum/crc8.go index fc73a9a40..29c6214b5 100644 --- a/checksum/crc8.go +++ b/checksum/crc8.go @@ -3,6 +3,8 @@ // details: // A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks // and storage devices to detect accidental changes to raw data. +// time complexity: O(n) +// space complexity: O(1) // See more [CRC](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) // author(s) [red_byte](https://github.com/i-redbyte) // see crc8_test.go diff --git a/checksum/luhn.go b/checksum/luhn.go index e9f7dd6b1..b28236d95 100644 --- a/checksum/luhn.go +++ b/checksum/luhn.go @@ -1,6 +1,8 @@ // lunh.go // description: Luhn algorithm // details: is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers, IMEI numbers, etc [Lunh](https://en.wikipedia.org/wiki/Luhn_algorithm) +// time complexity: O(n) +// space complexity: O(1) // author(s) [red_byte](https://github.com/i-redbyte) // see lunh_test.go diff --git a/cipher/caesar/caesar.go b/cipher/caesar/caesar.go index 8c1cb47f7..f2f945e71 100644 --- a/cipher/caesar/caesar.go +++ b/cipher/caesar/caesar.go @@ -1,4 +1,8 @@ // Package caesar is the shift cipher +// description: Caesar cipher +// details : Caesar cipher is a type of substitution cipher in which each letter in the plaintext is shifted a certain number of places down the alphabet. +// time complexity: O(n) +// space complexity: O(n) // ref: https://en.wikipedia.org/wiki/Caesar_cipher package caesar diff --git a/cipher/diffiehellman/diffiehellmankeyexchange.go b/cipher/diffiehellman/diffiehellmankeyexchange.go index 2621e3cca..8a779efd4 100644 --- a/cipher/diffiehellman/diffiehellmankeyexchange.go +++ b/cipher/diffiehellman/diffiehellmankeyexchange.go @@ -1,4 +1,8 @@ // Package diffiehellman implements Diffie-Hellman Key Exchange Algorithm +// description: Diffie-Hellman key exchange +// details : Diffie-Hellman key exchange is a method of securely exchanging cryptographic keys over a public channel by combining private keys of two parties to generate a shared secret key. +// time complexity: O(log(n)) +// space complexity: O(1) // for more information watch : https://www.youtube.com/watch?v=NmM9HA2MQGI package diffiehellman diff --git a/cipher/dsa/dsa.go b/cipher/dsa/dsa.go new file mode 100644 index 000000000..7d4eb574a --- /dev/null +++ b/cipher/dsa/dsa.go @@ -0,0 +1,196 @@ +/* +dsa.go +description: DSA encryption and decryption including key generation +details: [DSA wiki](https://en.wikipedia.org/wiki/Digital_Signature_Algorithm) +author(s): [ddaniel27](https://github.com/ddaniel27) +*/ +package dsa + +import ( + "crypto/rand" + "io" + "math/big" +) + +const ( + numMRTests = 64 // Number of Miller-Rabin tests + L = 1024 // Number of bits in p + N = 160 // Number of bits in q +) + +type ( + // parameters represents the DSA parameters + parameters struct { + P, Q, G *big.Int + } + + // dsa represents the DSA + dsa struct { + parameters + pubKey *big.Int // public key (y) + privKey *big.Int // private key (x) + } +) + +// New creates a new DSA instance +func New() *dsa { + d := new(dsa) + d.dsaParameterGeneration() + d.keyGen() + return d +} + +// Parameter generation for DSA +// 1. FIPS 186-4 specifies that the L and N values must be (1024, 160), (2048, 224), or (3072, 256) +// 2. Choose a N-bit prime q +// 3. Choose a L-bit prime p such that p-1 is a multiple of q +// 4. Choose an integer h randomly from the range [2, p-2] +// 5. Compute g = h^((p-1)/q) mod p +// 6. Return (p, q, g) +func (dsa *dsa) dsaParameterGeneration() { + var err error + p, q, bigInt := new(big.Int), new(big.Int), new(big.Int) + one, g, h := big.NewInt(1), big.NewInt(1), big.NewInt(2) + pBytes := make([]byte, L/8) + + // GPLoop is a label for the loop + // We use this loop to change the prime q if we don't find a prime p +GPLoop: + for { + // 2. Choose a N-bit prime q + q, err = rand.Prime(rand.Reader, N) + if err != nil { + panic(err) + } + + for i := 0; i < 4*L; i++ { + // 3. Choose a L-bit prime p such that p-1 is a multiple of q + // In this case we generate a random number of L bits + if _, err := io.ReadFull(rand.Reader, pBytes); err != nil { + panic(err) + } + + // This are the minimum conditions for p being a possible prime + pBytes[len(pBytes)-1] |= 1 // p is odd + pBytes[0] |= 0x80 // p has the highest bit set + p.SetBytes(pBytes) + + // Instead of using (p-1)%q == 0 + // We ensure that p-1 is a multiple of q and validates if p is prime + bigInt.Mod(p, q) + bigInt.Sub(bigInt, one) + p.Sub(p, bigInt) + if p.BitLen() < L || !p.ProbablyPrime(numMRTests) { // Check if p is prime and has L bits + continue + } + + dsa.P = p + dsa.Q = q + break GPLoop + } + } + + // 4. Choose an integer h randomly from the range [2, p-2]. Commonly, h = 2 + // 5. Compute g = h^((p-1)/q) mod p. In case g == 1, increment h until g != 1 + pm1 := new(big.Int).Sub(p, one) + + for g.Cmp(one) == 0 { + g.Exp(h, new(big.Int).Div(pm1, q), p) + h.Add(h, one) + } + + dsa.G = g +} + +// keyGen is key generation for DSA +// 1. Choose a random integer x from the range [1, q-1] +// 2. Compute y = g^x mod p +func (dsa *dsa) keyGen() { + // 1. Choose a random integer x from the range [1, q-1] + x, err := rand.Int(rand.Reader, new(big.Int).Sub(dsa.Q, big.NewInt(1))) + if err != nil { + panic(err) + } + + dsa.privKey = x + + // 2. Compute y = g^x mod p + dsa.pubKey = new(big.Int).Exp(dsa.G, x, dsa.P) +} + +// Sign is signature generation for DSA +// 1. Choose a random integer k from the range [1, q-1] +// 2. Compute r = (g^k mod p) mod q +// 3. Compute s = (k^-1 * (H(m) + x*r)) mod q +func Sign(m []byte, p, q, g, x *big.Int) (r, s *big.Int) { + // 1. Choose a random integer k from the range [1, q-1] + k, err := rand.Int(rand.Reader, new(big.Int).Sub(q, big.NewInt(1))) + if err != nil { + panic(err) + } + + // 2. Compute r = (g^k mod p) mod q + r = new(big.Int).Exp(g, k, p) + r.Mod(r, q) + + // 3. Compute s = (k^-1 * (H(m) + x*r)) mod q + h := new(big.Int).SetBytes(m) // This should be the hash of the message + s = new(big.Int).ModInverse(k, q) // k^-1 mod q + s.Mul( + s, + new(big.Int).Add( // (H(m) + x*r) + h, + new(big.Int).Mul(x, r), + ), + ) + s.Mod(s, q) // mod q + + return r, s +} + +// Verify is signature verification for DSA +// 1. Compute w = s^-1 mod q +// 2. Compute u1 = (H(m) * w) mod q +// 3. Compute u2 = (r * w) mod q +// 4. Compute v = ((g^u1 * y^u2) mod p) mod q +// 5. If v == r, the signature is valid +func Verify(m []byte, r, s, p, q, g, y *big.Int) bool { + // 1. Compute w = s^-1 mod q + w := new(big.Int).ModInverse(s, q) + + // 2. Compute u1 = (H(m) * w) mod q + h := new(big.Int).SetBytes(m) // This should be the hash of the message + u1 := new(big.Int).Mul(h, w) + u1.Mod(u1, q) + + // 3. Compute u2 = (r * w) mod q + u2 := new(big.Int).Mul(r, w) + u2.Mod(u2, q) + + // 4. Compute v = ((g^u1 * y^u2) mod p) mod q + v := new(big.Int).Exp(g, u1, p) + v.Mul( + v, + new(big.Int).Exp(y, u2, p), + ) + v.Mod(v, p) + v.Mod(v, q) + + // 5. If v == r, the signature is valid + return v.Cmp(r) == 0 +} + +// GetPublicKey returns the public key (y) +func (dsa *dsa) GetPublicKey() *big.Int { + return dsa.pubKey +} + +// GetParameters returns the DSA parameters (p, q, g) +func (dsa *dsa) GetParameters() parameters { + return dsa.parameters +} + +// GetPrivateKey returns the private Key (x) +func (dsa *dsa) GetPrivateKey() *big.Int { + return dsa.privKey +} diff --git a/cipher/dsa/dsa_test.go b/cipher/dsa/dsa_test.go new file mode 100644 index 000000000..3ef1930e4 --- /dev/null +++ b/cipher/dsa/dsa_test.go @@ -0,0 +1,114 @@ +package dsa_test + +import ( + "math/big" + "testing" + + "github.com/TheAlgorithms/Go/cipher/dsa" +) + +func TestDSA(t *testing.T) { + tests := []struct { + name string + message string + alter bool + want bool + }{ + { + name: "valid signature", + message: "Hello, world!", + alter: false, + want: true, + }, + { + name: "invalid signature", + message: "Hello, world!", + alter: true, + want: false, + }, + } + // Generate a DSA key pair + dsaInstance := dsa.New() + pubKey := dsaInstance.GetPublicKey() + params := dsaInstance.GetParameters() + privKey := dsaInstance.GetPrivateKey() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + // Sign the message + r, s := dsa.Sign( + []byte(tt.message), + params.P, + params.Q, + params.G, + privKey, + ) + + if tt.alter { + // Alter the signature + r.Add(r, big.NewInt(1)) + } + + // Verify the signature + if got := dsa.Verify( + []byte(tt.message), + r, + s, + params.P, + params.Q, + params.G, + pubKey, + ); got != tt.want { + t.Errorf("got %v, want %v", got, tt.want) + } + }) + } +} + +/* ------------------- BENCHMARKS ------------------- */ +func BenchmarkDSANew(b *testing.B) { + for i := 0; i < b.N; i++ { + dsa.New() + } +} + +func BenchmarkDSASign(b *testing.B) { + dsaInstance := dsa.New() + params := dsaInstance.GetParameters() + privKey := dsaInstance.GetPrivateKey() + for i := 0; i < b.N; i++ { + dsa.Sign( + []byte("Hello, World!"), + params.P, + params.Q, + params.G, + privKey, + ) + } +} + +func BenchmarkDSAVerify(b *testing.B) { + dsaInstance := dsa.New() + pubKey := dsaInstance.GetPublicKey() + params := dsaInstance.GetParameters() + privKey := dsaInstance.GetPrivateKey() + r, s := dsa.Sign( + []byte("Hello, World!"), + params.P, + params.Q, + params.G, + privKey, + ) + for i := 0; i < b.N; i++ { + dsa.Verify( + []byte("Hello, World!"), + r, + s, + params.P, + params.Q, + params.G, + pubKey, + ) + } +} diff --git a/cipher/polybius/polybius.go b/cipher/polybius/polybius.go index 7b57ab344..e022dabae 100644 --- a/cipher/polybius/polybius.go +++ b/cipher/polybius/polybius.go @@ -1,4 +1,8 @@ // Package polybius is encrypting method with polybius square +// description: Polybius square +// details : The Polybius algorithm is a simple algorithm that is used to encode a message by converting each letter to a pair of numbers. +// time complexity: O(n) +// space complexity: O(n) // ref: https://en.wikipedia.org/wiki/Polybius_square#Hybrid_Polybius_Playfair_Cipher package polybius diff --git a/cipher/railfence/railfence.go b/cipher/railfence/railfence.go new file mode 100644 index 000000000..aeeaed3fa --- /dev/null +++ b/cipher/railfence/railfence.go @@ -0,0 +1,81 @@ +// railfence.go +// description: Rail Fence Cipher +// details: The rail fence cipher is a an encryption algorithm that uses a rail fence pattern to encode a message. it is a type of transposition cipher that rearranges the characters of the plaintext to form the ciphertext. +// time complexity: O(n) +// space complexity: O(n) +// ref: https://en.wikipedia.org/wiki/Rail_fence_cipher +package railfence + +import ( + "strings" +) + +func Encrypt(text string, rails int) string { + if rails == 1 { + return text + } + + // Create a matrix for the rail fence pattern + matrix := make([][]rune, rails) + for i := range matrix { + matrix[i] = make([]rune, len(text)) + } + + // Fill the matrix + dirDown := false + row, col := 0, 0 + for _, char := range text { + if row == 0 || row == rails-1 { + dirDown = !dirDown + } + matrix[row][col] = char + col++ + if dirDown { + row++ + } else { + row-- + } + } + var result strings.Builder + for _, line := range matrix { + for _, char := range line { + if char != 0 { + result.WriteRune(char) + } + } + } + + return result.String() +} +func Decrypt(cipherText string, rails int) string { + if rails == 1 || rails >= len(cipherText) { + return cipherText + } + + // Placeholder for the decrypted message + decrypted := make([]rune, len(cipherText)) + + // Calculate the zigzag pattern and place characters accordingly + index := 0 + for rail := 0; rail < rails; rail++ { + position := rail + down := true // Direction flag + for position < len(cipherText) { + decrypted[position] = rune(cipherText[index]) + index++ + + // Determine the next position based on the current rail and direction + if rail == 0 || rail == rails-1 { + position += 2 * (rails - 1) + } else if down { + position += 2 * (rails - 1 - rail) + down = false + } else { + position += 2 * rail + down = true + } + } + } + + return string(decrypted) +} diff --git a/cipher/railfence/railfence_test.go b/cipher/railfence/railfence_test.go new file mode 100644 index 000000000..7f7bfa53a --- /dev/null +++ b/cipher/railfence/railfence_test.go @@ -0,0 +1,91 @@ +package railfence + +import ( + "testing" +) + +func TestEncrypt(t *testing.T) { + var railFenceTestData = []struct { + description string + input string + rails int + expected string + }{ + { + "Encrypt with 2 rails", + "hello", + 2, + "hloel", + }, + { + "Encrypt with 3 rails", + "hello world", + 3, + "horel ollwd", + }, + { + "Encrypt with edge case: 1 rail", + "hello", + 1, + "hello", + }, + { + "Encrypt with more rails than letters", + "hi", + 100, + "hi", + }, + } + + for _, test := range railFenceTestData { + t.Run(test.description, func(t *testing.T) { + actual := Encrypt(test.input, test.rails) + if actual != test.expected { + t.Errorf("FAIL: %s - Encrypt(%s, %d) = %s, want %s", test.description, test.input, test.rails, actual, test.expected) + } + }) + } +} + +func TestDecrypt(t *testing.T) { + var railFenceTestData = []struct { + description string + input string + rails int + expected string + }{ + { + "Decrypt with 2 rails", + "hloel", + 2, + "hello", + }, + { + "Decrypt with 3 rails", + "ho l lewrdlo", + 3, + "hld olle wor", + }, + { + "Decrypt with edge case: 1 rail", + "hello", + 1, + "hello", + }, + { + "Decrypt with more rails than letters", + "hi", + 100, + "hi", + }, + } + + for _, test := range railFenceTestData { + t.Run(test.description, func(t *testing.T) { + actual := Decrypt(test.input, test.rails) + if actual != test.expected { + t.Errorf("FAIL: %s - Decrypt(%s, %d) = %s, want %s", test.description, test.input, test.rails, actual, test.expected) + } + }) + } +} diff --git a/cipher/rot13/rot13.go b/cipher/rot13/rot13.go index 9b822332b..d50d97c53 100644 --- a/cipher/rot13/rot13.go +++ b/cipher/rot13/rot13.go @@ -1,4 +1,8 @@ // Package rot13 is a simple letter substitution cipher that replaces a letter with the 13th letter after it in the alphabet. +// description: ROT13 +// details: ROT13 is a simple letter substitution cipher that replaces a letter with the 13th letter after it in the alphabet. it is a special case of the Caesar cipher +// time complexity: O(n) +// space complexity: O(n) // ref: https://en.wikipedia.org/wiki/ROT13 package rot13 diff --git a/cipher/rsa/rsa.go b/cipher/rsa/rsa.go index 44cf767b3..1eb506e2c 100644 --- a/cipher/rsa/rsa.go +++ b/cipher/rsa/rsa.go @@ -6,6 +6,8 @@ // thus both the Encrypt and Decrypt are not a production // ready implementation. The OpenSSL implementation of RSA // also adds a padding which is not present in this algorithm. +// time complexity: O(n) +// space complexity: O(n) // author(s) [Taj](https://github.com/tjgurwara99) // see rsa_test.go diff --git a/cipher/rsa/rsa2.go b/cipher/rsa/rsa2.go new file mode 100644 index 000000000..18b6e1391 --- /dev/null +++ b/cipher/rsa/rsa2.go @@ -0,0 +1,125 @@ +/* +rsa2.go +description: RSA encryption and decryption including key generation +details: [RSA wiki](https://en.wikipedia.org/wiki/RSA_(cryptosystem)) +time complexity: O(n) +space complexity: O(1) +author(s): [ddaniel27](https://github.com/ddaniel27) +*/ +package rsa + +import ( + "encoding/binary" + "fmt" + "math/big" + "math/rand" + + "github.com/TheAlgorithms/Go/math/gcd" + "github.com/TheAlgorithms/Go/math/lcm" + "github.com/TheAlgorithms/Go/math/modular" + "github.com/TheAlgorithms/Go/math/prime" +) + +// rsa struct contains the public key, private key and modulus +type rsa struct { + publicKey uint64 + privateKey uint64 + modulus uint64 +} + +// New initializes the RSA algorithm +// returns the RSA object +func New() *rsa { + // The following code generates keys for RSA encryption/decryption + // 1. Choose two large prime numbers, p and q and compute n = p * q + p, q := randomPrime() // p and q stands for prime numbers + modulus := p * q // n stands for common number + + // 2. Compute the totient of n, lcm(p-1, q-1) + totient := uint64(lcm.Lcm(int64(p-1), int64(q-1))) + + // 3. Choose an integer e such that 1 < e < totient(n) and gcd(e, totient(n)) = 1 + publicKey := uint64(2) // e stands for encryption key (public key) + for publicKey < totient { + if gcd.Recursive(int64(publicKey), int64(totient)) == 1 { + break + } + publicKey++ + } + + // 4. Compute d such that d * e ≡ 1 (mod totient(n)) + inv, _ := modular.Inverse(int64(publicKey), int64(totient)) + privateKey := uint64(inv) + + return &rsa{ + publicKey: publicKey, + privateKey: privateKey, + modulus: modulus, + } +} + +// EncryptString encrypts the data using RSA algorithm +// returns the encrypted string +func (rsa *rsa) EncryptString(data string) string { + var nums []byte + + for _, char := range data { + slice := make([]byte, 8) + binary.BigEndian.PutUint64( // convert uint64 to byte slice + slice, + encryptDecryptInt(rsa.publicKey, rsa.modulus, uint64(char)), // encrypt each character + ) + nums = append(nums, slice...) + } + + return string(nums) +} + +// DecryptString decrypts the data using RSA algorithm +// returns the decrypted string +func (rsa *rsa) DecryptString(data string) string { + result := "" + middle := []byte(data) + + for i := 0; i < len(middle); i += 8 { + if i+8 > len(middle) { + break + } + + slice := middle[i : i+8] + num := binary.BigEndian.Uint64(slice) // convert byte slice to uint64 + result += fmt.Sprintf("%c", encryptDecryptInt(rsa.privateKey, rsa.modulus, num)) + } + + return result +} + +// GetPublicKey returns the public key and modulus +func (rsa *rsa) GetPublicKey() (uint64, uint64) { + return rsa.publicKey, rsa.modulus +} + +// GetPrivateKey returns the private key +func (rsa *rsa) GetPrivateKey() uint64 { + return rsa.privateKey +} + +// encryptDecryptInt encrypts or decrypts the data using RSA algorithm +func encryptDecryptInt(e, n, data uint64) uint64 { + pow := new(big.Int).Exp(big.NewInt(int64(data)), big.NewInt(int64(e)), big.NewInt(int64(n))) + return pow.Uint64() +} + +// randomPrime returns two random prime numbers +func randomPrime() (uint64, uint64) { + sieve := prime.SieveEratosthenes(1000) + sieve = sieve[10:] // remove first 10 prime numbers (small numbers) + index1 := rand.Intn(len(sieve)) + index2 := rand.Intn(len(sieve)) + + for index1 == index2 { + index2 = rand.Intn(len(sieve)) + } + + return uint64(sieve[index1]), uint64(sieve[index2]) +} diff --git a/cipher/rsa/rsa2_test.go b/cipher/rsa/rsa2_test.go new file mode 100644 index 000000000..29e15c557 --- /dev/null +++ b/cipher/rsa/rsa2_test.go @@ -0,0 +1,49 @@ +package rsa_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/cipher/rsa" +) + +func TestRSA(t *testing.T) { + tests := []struct { + name string + message string + }{ + { + name: "Encrypt letter 'a' and decrypt it back", + message: "a", + }, + { + name: "Encrypt 'Hello, World!' and decrypt it back", + message: "Hello, World!", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rsa := rsa.New() + encrypted := rsa.EncryptString(tt.message) + decrypted := rsa.DecryptString(encrypted) + if decrypted != tt.message { + t.Errorf("expected %s, got %s", tt.message, decrypted) + } + }) + } +} + +func BenchmarkRSAEncryption(b *testing.B) { + rsa := rsa.New() + for i := 0; i < b.N; i++ { + rsa.EncryptString("Hello, World!") + } +} + +func BenchmarkRSADecryption(b *testing.B) { + rsa := rsa.New() + encrypted := rsa.EncryptString("Hello, World!") + for i := 0; i < b.N; i++ { + rsa.DecryptString(encrypted) + } +} diff --git a/cipher/transposition/transposition.go b/cipher/transposition/transposition.go index 6365ffa4a..7a18293ea 100644 --- a/cipher/transposition/transposition.go +++ b/cipher/transposition/transposition.go @@ -2,6 +2,8 @@ // description: Transposition cipher // details: // Implementation "Transposition cipher" is a method of encryption by which the positions held by units of plaintext (which are commonly characters or groups of characters) are shifted according to a regular system, so that the ciphertext constitutes a permutation of the plaintext [Transposition cipher](https://en.wikipedia.org/wiki/Transposition_cipher) +// time complexity: O(n) +// space complexity: O(n) // author(s) [red_byte](https://github.com/i-redbyte) // see transposition_test.go diff --git a/cipher/xor/xor.go b/cipher/xor/xor.go index 0296c97ce..61181b1aa 100644 --- a/cipher/xor/xor.go +++ b/cipher/xor/xor.go @@ -1,4 +1,8 @@ // Package xor is an encryption algorithm that operates the exclusive disjunction(XOR) +// description: XOR encryption +// details: The XOR encryption is an algorithm that operates the exclusive disjunction(XOR) on each character of the plaintext with a given key +// time complexity: O(n) +// space complexity: O(n) // ref: https://en.wikipedia.org/wiki/XOR_cipher package xor diff --git a/compression/huffmancoding.go b/compression/huffmancoding.go index f6515872e..ecdaa1fb2 100644 --- a/compression/huffmancoding.go +++ b/compression/huffmancoding.go @@ -3,6 +3,8 @@ // details: // We implement the linear-time 2-queue method described here https://en.wikipedia.org/wiki/Huffman_coding. // It assumes that the list of symbol-frequencies is sorted. +// time complexity: O(n) +// space complexity: O(n) // author(s) [pedromsrocha](https://github.com/pedromsrocha) // see also huffmancoding_test.go diff --git a/compression/rlecoding.go b/compression/rlecoding.go new file mode 100644 index 000000000..9aa9399bb --- /dev/null +++ b/compression/rlecoding.go @@ -0,0 +1,76 @@ +/* +rlecoding.go +description: run length encoding and decoding +details: +Run-length encoding (RLE) is a simple form of data compression in which runs of data are stored as a single data value and count, rather than as the original run. This is useful when the data contains many repeated values. For example, the data "WWWWWWWWWWWWBWWWWWWWWWWWWBBB" can be compressed to "12W1B12W3B". The algorithm is simple and can be implemented in a few lines of code. +time complexity: O(n) +space complexity: O(n) +ref: https://en.wikipedia.org/wiki/Run-length_encoding +author(s) [ddaniel27](https://github.com/ddaniel27) +*/ +package compression + +import ( + "bytes" + "fmt" + "regexp" + "strconv" + "strings" +) + +// RLEncode takes a string and returns its run-length encoding +func RLEncode(data string) string { + var result string + count := 1 + for i := 0; i < len(data); i++ { + if i+1 < len(data) && data[i] == data[i+1] { + count++ + continue + } + result += fmt.Sprintf("%d%c", count, data[i]) + count = 1 + } + return result +} + +// RLEdecode takes a run-length encoded string and returns the original string +func RLEdecode(data string) string { + var result string + regex := regexp.MustCompile(`(\d+)(\w)`) + + for _, match := range regex.FindAllStringSubmatch(data, -1) { + num, _ := strconv.Atoi(match[1]) + result += strings.Repeat(match[2], num) + } + + return result +} + +// RLEncodebytes takes a byte slice and returns its run-length encoding as a byte slice +func RLEncodebytes(data []byte) []byte { + var result []byte + var count byte = 1 + + for i := 0; i < len(data); i++ { + if i+1 < len(data) && data[i] == data[i+1] { + count++ + continue + } + result = append(result, count, data[i]) + count = 1 + } + + return result +} + +// RLEdecodebytes takes a run-length encoded byte slice and returns the original byte slice +func RLEdecodebytes(data []byte) []byte { + var result []byte + + for i := 0; i < len(data); i += 2 { + count := int(data[i]) + result = append(result, bytes.Repeat([]byte{data[i+1]}, count)...) + } + + return result +} diff --git a/compression/rlecoding_test.go b/compression/rlecoding_test.go new file mode 100644 index 000000000..bf9af6bf6 --- /dev/null +++ b/compression/rlecoding_test.go @@ -0,0 +1,161 @@ +package compression_test + +import ( + "bytes" + "testing" + + "github.com/TheAlgorithms/Go/compression" +) + +func TestCompressionRLEncode(t *testing.T) { + tests := []struct { + name string + data string + want string + }{ + { + name: "test 1", + data: "WWWWWWWWWWWWBWWWWWWWWWWWWBBB", + want: "12W1B12W3B", + }, + { + name: "test 2", + data: "AABCCCDEEEE", + want: "2A1B3C1D4E", + }, + { + name: "test 3", + data: "AAAABBBCCDA", + want: "4A3B2C1D1A", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := compression.RLEncode(tt.data); got != tt.want { + t.Errorf("RLEncode() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCompressionRLEDecode(t *testing.T) { + tests := []struct { + name string + data string + want string + }{ + { + name: "test 1", + data: "12W1B12W3B", + want: "WWWWWWWWWWWWBWWWWWWWWWWWWBBB", + }, + { + name: "test 2", + data: "2A1B3C1D4E", + want: "AABCCCDEEEE", + }, + { + name: "test 3", + data: "4A3B2C1D1A", + want: "AAAABBBCCDA", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := compression.RLEdecode(tt.data); got != tt.want { + t.Errorf("RLEdecode() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCompressionRLEncodeBytes(t *testing.T) { + tests := []struct { + name string + data []byte + want []byte + }{ + { + name: "test 1", + data: []byte("WWWWWWWWWWWWBWWWWWWWWWWWWBBB"), + want: []byte{12, 'W', 1, 'B', 12, 'W', 3, 'B'}, + }, + { + name: "test 2", + data: []byte("AABCCCDEEEE"), + want: []byte{2, 'A', 1, 'B', 3, 'C', 1, 'D', 4, 'E'}, + }, + { + name: "test 3", + data: []byte("AAAABBBCCDA"), + want: []byte{4, 'A', 3, 'B', 2, 'C', 1, 'D', 1, 'A'}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := compression.RLEncodebytes(tt.data); !bytes.Equal(got, tt.want) { + t.Errorf("RLEncodebytes() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCompressionRLEDecodeBytes(t *testing.T) { + tests := []struct { + name string + data []byte + want []byte + }{ + { + name: "test 1", + data: []byte{12, 'W', 1, 'B', 12, 'W', 3, 'B'}, + want: []byte("WWWWWWWWWWWWBWWWWWWWWWWWWBBB"), + }, + { + name: "test 2", + data: []byte{2, 'A', 1, 'B', 3, 'C', 1, 'D', 4, 'E'}, + want: []byte("AABCCCDEEEE"), + }, + { + name: "test 3", + data: []byte{4, 'A', 3, 'B', 2, 'C', 1, 'D', 1, 'A'}, + want: []byte("AAAABBBCCDA"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := compression.RLEdecodebytes(tt.data); !bytes.Equal(got, tt.want) { + t.Errorf("RLEdecodebytes() = %v, want %v", got, tt.want) + } + }) + } +} + +/* --- BENCHMARKS --- */ +func BenchmarkRLEncode(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = compression.RLEncode("WWWWWWWWWWWWBWWWWWWWWWWWWBBB") + } +} + +func BenchmarkRLEDecode(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = compression.RLEdecode("12W1B12W3B") + } +} + +func BenchmarkRLEncodeBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = compression.RLEncodebytes([]byte("WWWWWWWWWWWWBWWWWWWWWWWWWBBB")) + } +} + +func BenchmarkRLEDecodeBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = compression.RLEdecodebytes([]byte{12, 'W', 1, 'B', 12, 'W', 3, 'B'}) + } +} diff --git a/conversion/base64.go b/conversion/base64.go index 8828f6a7a..bf3a383c5 100644 --- a/conversion/base64.go +++ b/conversion/base64.go @@ -1,6 +1,8 @@ // base64.go // description: The base64 encoding algorithm as defined in the RFC4648 standard. // author: [Paul Leydier] (https://github.com/paul-leydier) +// time complexity: O(n) +// space complexity: O(n) // ref: https://datatracker.ietf.org/doc/html/rfc4648#section-4 // ref: https://en.wikipedia.org/wiki/Base64 // see base64_test.go diff --git a/conversion/binarytodecimal.go b/conversion/binarytodecimal.go index bc67745fd..aa308e6a8 100644 --- a/conversion/binarytodecimal.go +++ b/conversion/binarytodecimal.go @@ -9,6 +9,8 @@ Date: 19-Oct-2021 // https://en.wikipedia.org/wiki/Decimal // Function receives a Binary Number as string and returns the Decimal number as integer. // Supported Binary number range is 0 to 2^(31-1). +// time complexity: O(n) +// space complexity: O(1) package conversion @@ -21,7 +23,7 @@ import ( var isValid = regexp.MustCompile("^[0-1]{1,}$").MatchString // BinaryToDecimal() function that will take Binary number as string, -// and return it's Decimal equivalent as integer. +// and return its Decimal equivalent as an integer. func BinaryToDecimal(binary string) (int, error) { if !isValid(binary) { return -1, errors.New("not a valid binary string") diff --git a/conversion/decimaltobinary.go b/conversion/decimaltobinary.go index 63ef8b1c0..54153b0d2 100644 --- a/conversion/decimaltobinary.go +++ b/conversion/decimaltobinary.go @@ -8,6 +8,8 @@ Date: 14-Oct-2021 // https://en.wikipedia.org/wiki/Binary_number // Function receives a integer as a Decimal number and returns the Binary number. // Supported integer value range is 0 to 2^(31 -1). +// time complexity: O(log(n)) +// space complexity: O(1) package conversion @@ -28,7 +30,7 @@ func Reverse(str string) string { } // DecimalToBinary() function that will take Decimal number as int, -// and return it's Binary equivalent as string. +// and return its Binary equivalent as a string. func DecimalToBinary(num int) (string, error) { if num < 0 { return "", errors.New("integer must have +ve value") diff --git a/conversion/hexadecimaltobinary.go b/conversion/hexadecimaltobinary.go new file mode 100644 index 000000000..a0d262df6 --- /dev/null +++ b/conversion/hexadecimaltobinary.go @@ -0,0 +1,78 @@ +/* +Author: mapcrafter2048 +GitHub: https://github.com/mapcrafter2048 +*/ + +// This algorithm will convert any Hexadecimal number(0-9, A-F, a-f) to Binary number(0 or 1). +// https://en.wikipedia.org/wiki/Hexadecimal +// https://en.wikipedia.org/wiki/Binary_number +// Function receives a Hexadecimal Number as string and returns the Binary number as string. +// Supported Hexadecimal number range is 0 to 7FFFFFFFFFFFFFFF. + +package conversion + +import ( + "errors" + "regexp" + "strings" +) + +var isValidHex = regexp.MustCompile("^[0-9A-Fa-f]+$").MatchString + +// hexToBinary() function that will take Hexadecimal number as string, +// and return its Binary equivalent as a string. +func hexToBinary(hex string) (string, error) { + // Trim any leading or trailing whitespace + hex = strings.TrimSpace(hex) + + // Check if the hexadecimal string is empty + if hex == "" { + return "", errors.New("input string is empty") + } + + // Check if the hexadecimal string is valid + if !isValidHex(hex) { + return "", errors.New("invalid hexadecimal string: " + hex) + } + + // Parse the hexadecimal string to an integer + var decimal int64 + for i := 0; i < len(hex); i++ { + char := hex[i] + var value int64 + if char >= '0' && char <= '9' { + value = int64(char - '0') + } else if char >= 'A' && char <= 'F' { + value = int64(char - 'A' + 10) + } else if char >= 'a' && char <= 'f' { + value = int64(char - 'a' + 10) + } else { + return "", errors.New("invalid character in hexadecimal string: " + string(char)) + } + decimal = decimal*16 + value + } + + // Convert the integer to a binary string without using predefined functions + var binaryBuilder strings.Builder + if decimal == 0 { + binaryBuilder.WriteString("0") + } else { + for decimal > 0 { + bit := decimal % 2 + if bit == 0 { + binaryBuilder.WriteString("0") + } else { + binaryBuilder.WriteString("1") + } + decimal = decimal / 2 + } + } + + // Reverse the binary string since the bits are added in reverse order + binaryRunes := []rune(binaryBuilder.String()) + for i, j := 0, len(binaryRunes)-1; i < j; i, j = i+1, j-1 { + binaryRunes[i], binaryRunes[j] = binaryRunes[j], binaryRunes[i] + } + + return string(binaryRunes), nil +} diff --git a/conversion/hexadecimaltobinary_test.go b/conversion/hexadecimaltobinary_test.go new file mode 100644 index 000000000..c2dee9712 --- /dev/null +++ b/conversion/hexadecimaltobinary_test.go @@ -0,0 +1,46 @@ +package conversion + +import ( + "testing" +) + +func TestHexToBinary(t *testing.T) { + tests := []struct { + hex string + want string + wantErr bool + }{ + {"", "", true}, + {"G123", "", true}, + {"12XZ", "", true}, + {"1", "1", false}, + {"A", "1010", false}, + {"10", "10000", false}, + {"1A", "11010", false}, + {"aB", "10101011", false}, + {"0Ff", "11111111", false}, + {" 1A ", "11010", false}, + {"0001A", "11010", false}, + {"7FFFFFFFFFFFFFFF", "111111111111111111111111111111111111111111111111111111111111111", false}, + } + + for _, tt := range tests { + t.Run(tt.hex, func(t *testing.T) { + got, err := hexToBinary(tt.hex) + if (err != nil) != tt.wantErr { + t.Errorf("hexToBinary(%q) error = %v, wantErr %v", tt.hex, err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("hexToBinary(%q) = %v, want %v", tt.hex, got, tt.want) + } + }) + } +} + +func BenchmarkHexToBinary(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, _ = hexToBinary("7FFFFFFFFFFFFFFF") + } +} diff --git a/conversion/hexadecimaltodecimal.go b/conversion/hexadecimaltodecimal.go new file mode 100644 index 000000000..f0d7a81d3 --- /dev/null +++ b/conversion/hexadecimaltodecimal.go @@ -0,0 +1,57 @@ +/* +Author: mapcrafter2048 +GitHub: https://github.com/mapcrafter2048 +*/ + +// This algorithm will convert any Hexadecimal number(0-9, A-F, a-f) to Decimal number(0-9). +// https://en.wikipedia.org/wiki/Hexadecimal +// https://en.wikipedia.org/wiki/Decimal +// Function receives a Hexadecimal Number as string and returns the Decimal number as integer. +// Supported Hexadecimal number range is 0 to 7FFFFFFFFFFFFFFF. + +package conversion + +import ( + "fmt" + "regexp" + "strings" +) + +var isValidHexadecimal = regexp.MustCompile("^[0-9A-Fa-f]+$").MatchString + +// hexToDecimal converts a hexadecimal string to a decimal integer. +func hexToDecimal(hexStr string) (int64, error) { + + hexStr = strings.TrimSpace(hexStr) + + if len(hexStr) == 0 { + return 0, fmt.Errorf("input string is empty") + } + + // Check if the string has a valid hexadecimal prefix + if len(hexStr) > 2 && (hexStr[:2] == "0x" || hexStr[:2] == "0X") { + hexStr = hexStr[2:] + } + + // Validate the hexadecimal string + if !isValidHexadecimal(hexStr) { + return 0, fmt.Errorf("invalid hexadecimal string") + } + + var decimalValue int64 + for _, char := range hexStr { + var digit int64 + if char >= '0' && char <= '9' { + digit = int64(char - '0') + } else if char >= 'A' && char <= 'F' { + digit = int64(char - 'A' + 10) + } else if char >= 'a' && char <= 'f' { + digit = int64(char - 'a' + 10) + } else { + return 0, fmt.Errorf("invalid character in hexadecimal string: %c", char) + } + decimalValue = decimalValue*16 + digit + } + + return decimalValue, nil +} diff --git a/conversion/hexadecimaltodecimal_test.go b/conversion/hexadecimaltodecimal_test.go new file mode 100644 index 000000000..4f399fe46 --- /dev/null +++ b/conversion/hexadecimaltodecimal_test.go @@ -0,0 +1,52 @@ +package conversion + +import ( + "testing" +) + +func TestHexToDecimal(t *testing.T) { + tests := []struct { + hex string + want int64 + wantErr bool + }{ + {"", 0, true}, + {"G123", 0, true}, + {"123Z", 0, true}, + {"1", 1, false}, + {"A", 10, false}, + {"10", 16, false}, + {"1A", 26, false}, + {"aB", 171, false}, + {"0Ff", 255, false}, + {" 1A ", 26, false}, + {"0x1A", 26, false}, + {"0X1A", 26, false}, + {"1A", 26, false}, + {"7FFFFFFFFFFFFFFF", 9223372036854775807, false}, + {"0001A", 26, false}, + {"0000007F", 127, false}, + {"0", 0, false}, + {"0x0", 0, false}, + } + + for _, tt := range tests { + t.Run(tt.hex, func(t *testing.T) { + got, err := hexToDecimal(tt.hex) + if (err != nil) != tt.wantErr { + t.Errorf("hexToDecimal(%q) error = %v, wantErr %v", tt.hex, err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("hexToDecimal(%q) = %v, want %v", tt.hex, got, tt.want) + } + }) + } +} + +func BenchmarkHexToDecimal(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, _ = hexToDecimal("7FFFFFFFFFFFFFFF") + } +} diff --git a/conversion/inttoroman.go b/conversion/inttoroman.go index c1764db1f..1b9c39a0e 100644 --- a/conversion/inttoroman.go +++ b/conversion/inttoroman.go @@ -1,3 +1,9 @@ +// inttoroman.go +// description: Convert an integer to a roman numeral +// details: This program converts an integer to a roman numeral. The program uses a lookup array to convert the integer to a roman numeral. +// time complexity: O(1) +// space complexity: O(1) + package conversion import ( diff --git a/conversion/rgbhex.go b/conversion/rgbhex.go index d050edc8d..3d7265d2c 100644 --- a/conversion/rgbhex.go +++ b/conversion/rgbhex.go @@ -1,5 +1,7 @@ // rgbhex.go // description: convert hex input to red, green and blue and vice versa +// time complexity: O(1) +// space complexity: O(1) // author(s) [darmiel](https://github.com/darmiel) // see rgbhex_test.go diff --git a/conversion/romantoint.go b/conversion/romantoint.go index dda0000f3..629952320 100644 --- a/conversion/romantoint.go +++ b/conversion/romantoint.go @@ -3,6 +3,8 @@ // Function receives a string as a roman number and outputs an integer // Maximum output will be 3999 // Only standard form is supported +// time complexity: O(n) +// space complexity: O(1) package conversion diff --git a/dynamic/abbreviation.go b/dynamic/abbreviation.go index ebfe92171..e5b7ad6e9 100644 --- a/dynamic/abbreviation.go +++ b/dynamic/abbreviation.go @@ -11,6 +11,8 @@ // Given a = "ABcde" and b = "ABCD" // We can capitalize "c" and "d" in a to get "ABCde" then delete all the lowercase letters (which is only "e") in a to get "ABCD" which equals b. // Author: [duongoku](https://github.com/duongoku) +// Time Complexity: O(n*m) where n is the length of a and m is the length of b +// Space Complexity: O(n*m) where n is the length of a and m is the length of b // See abbreviation_test.go for test cases package dynamic diff --git a/dynamic/binomialcoefficient.go b/dynamic/binomialcoefficient.go index b26dbcbbf..fed664fd5 100644 --- a/dynamic/binomialcoefficient.go +++ b/dynamic/binomialcoefficient.go @@ -1,3 +1,8 @@ +// binomialcoefficient.go +// description: Implementation of the binomial coefficient using dynamic programming +// details: The binomial coefficient C(n, k) is the number of ways to choose a subset of k elements from a set of n elements. The binomial coefficient is calculated using the formula C(n, k) = C(n-1, k-1) + C(n-1, k) with base cases C(n, 0) = C(n, n) = 1. +// time complexity: O(n*k) where n is the number of elements and k is the number of elements to choose +// space complexity: O(n*k) where n is the number of elements and k is the number of elements to choose package dynamic import "github.com/TheAlgorithms/Go/math/min" diff --git a/dynamic/burstballoons.go b/dynamic/burstballoons.go new file mode 100644 index 000000000..492b00d8c --- /dev/null +++ b/dynamic/burstballoons.go @@ -0,0 +1,31 @@ +package dynamic + +import "github.com/TheAlgorithms/Go/math/max" + +// MaxCoins returns the maximum coins we can collect by bursting the balloons +func MaxCoins(nums []int) int { + n := len(nums) + if n == 0 { + return 0 + } + + nums = append([]int{1}, nums...) + nums = append(nums, 1) + + dp := make([][]int, n+2) + for i := range dp { + dp[i] = make([]int, n+2) + } + + for length := 1; length <= n; length++ { + for left := 1; left+length-1 <= n; left++ { + right := left + length - 1 + for k := left; k <= right; k++ { + coins := nums[left-1] * nums[k] * nums[right+1] + dp[left][right] = max.Int(dp[left][right], dp[left][k-1]+dp[k+1][right]+coins) + } + } + } + + return dp[1][n] +} diff --git a/dynamic/burstballoons_test.go b/dynamic/burstballoons_test.go new file mode 100644 index 000000000..c36e08824 --- /dev/null +++ b/dynamic/burstballoons_test.go @@ -0,0 +1,33 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +type testCaseBurstBalloons struct { + nums []int + expected int +} + +func getBurstBalloonsTestCases() []testCaseBurstBalloons { + return []testCaseBurstBalloons{ + {[]int{3, 1, 5, 8}, 167}, // Maximum coins from [3,1,5,8] + {[]int{1, 5}, 10}, // Maximum coins from [1,5] + {[]int{1}, 1}, // Single balloon + {[]int{}, 0}, // No balloons + } + +} + +func TestMaxCoins(t *testing.T) { + t.Run("Burst Balloons test cases", func(t *testing.T) { + for _, tc := range getBurstBalloonsTestCases() { + actual := dynamic.MaxCoins(tc.nums) + if actual != tc.expected { + t.Errorf("MaxCoins(%v) = %d; expected %d", tc.nums, actual, tc.expected) + } + } + }) +} diff --git a/dynamic/catalan.go b/dynamic/catalan.go index 7e0076fba..2f53c3edb 100644 --- a/dynamic/catalan.go +++ b/dynamic/catalan.go @@ -1,5 +1,7 @@ //The Catalan numbers are a sequence of positive integers that appear in many counting -//problems in combinatorics. +// problems in combinatorics. +// time complexity: O(n²) +// space complexity: O(n) //reference: https://brilliant.org/wiki/catalan-numbers/ package dynamic diff --git a/dynamic/coinchange.go b/dynamic/coinchange.go index 1098ea4e1..2ba6064ab 100644 --- a/dynamic/coinchange.go +++ b/dynamic/coinchange.go @@ -1,3 +1,9 @@ +// coinchange.go +// description: Implementation of the coin change problem using dynamic programming +// details: The coin change problem is a problem that asks for the number of ways to make change for a given amount of money using a given set of coins. The problem can be solved using dynamic programming. +// time complexity: O(n*m) where n is the number of coins and m is the amount of money +// space complexity: O(m) where m is the amount of money + package dynamic // CoinChange finds the number of possible combinations of coins diff --git a/dynamic/dicethrow.go b/dynamic/dicethrow.go new file mode 100644 index 000000000..69711ca14 --- /dev/null +++ b/dynamic/dicethrow.go @@ -0,0 +1,33 @@ +// dicethrow.go +// description: Solves the Dice Throw Problem using dynamic programming +// reference: https://www.geeksforgeeks.org/dice-throw-problem/ +// time complexity: O(m * n) +// space complexity: O(m * n) + +package dynamic + +// DiceThrow returns the number of ways to get sum `sum` using `m` dice with `n` faces +func DiceThrow(m, n, sum int) int { + dp := make([][]int, m+1) + for i := range dp { + dp[i] = make([]int, sum+1) + } + + for i := 1; i <= n; i++ { + if i <= sum { + dp[1][i] = 1 + } + } + + for i := 2; i <= m; i++ { + for j := 1; j <= sum; j++ { + for k := 1; k <= n; k++ { + if j-k >= 0 { + dp[i][j] += dp[i-1][j-k] + } + } + } + } + + return dp[m][sum] +} diff --git a/dynamic/dicethrow_test.go b/dynamic/dicethrow_test.go new file mode 100644 index 000000000..dad25e533 --- /dev/null +++ b/dynamic/dicethrow_test.go @@ -0,0 +1,42 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +type testCaseDiceThrow struct { + numDice int + numFaces int + targetSum int + expected int +} + +// getDiceThrowTestCases provides the test cases for DiceThrow +func getDiceThrowTestCases() []testCaseDiceThrow { + return []testCaseDiceThrow{ + {2, 6, 7, 6}, // Two dice, six faces each, sum = 7 + {1, 6, 3, 1}, // One die, six faces, sum = 3 + {3, 4, 5, 6}, // Three dice, four faces each, sum = 5 + {1, 6, 1, 1}, // One die, six faces, sum = 1 + {2, 6, 12, 1}, // Two dice, six faces each, sum = 12 + {3, 6, 18, 1}, // Three dice, six faces each, sum = 18 + {2, 6, 20, 0}, // Two dice, six faces each, sum = 20 (impossible) + {1, 1, 1, 1}, // One die, one face, sum = 1 + {1, 1, 2, 0}, // One die, one face, sum = 2 (impossible) + {2, 1, 2, 1}, // Two dice, one face each, sum = 2 + } +} + +// TestDiceThrow tests the DiceThrow function with basic test cases +func TestDiceThrow(t *testing.T) { + t.Run("Basic test cases", func(t *testing.T) { + for _, tc := range getDiceThrowTestCases() { + actual := dynamic.DiceThrow(tc.numDice, tc.numFaces, tc.targetSum) + if actual != tc.expected { + t.Errorf("DiceThrow(%d, %d, %d) = %d; expected %d", tc.numDice, tc.numFaces, tc.targetSum, actual, tc.expected) + } + } + }) +} diff --git a/dynamic/editdistance.go b/dynamic/editdistance.go index 92abe9583..6d4778a3e 100644 --- a/dynamic/editdistance.go +++ b/dynamic/editdistance.go @@ -1,4 +1,6 @@ // EDIT DISTANCE PROBLEM +// time complexity: O(m * n) where m and n are lengths of the strings, first and second respectively. +// space complexity: O(m * n) where m and n are lengths of the strings, first and second respectively. // https://www.geeksforgeeks.org/edit-distance-dp-5/ // https://leetcode.com/problems/edit-distance/ diff --git a/dynamic/eggdropping.go b/dynamic/eggdropping.go new file mode 100644 index 000000000..b6d379389 --- /dev/null +++ b/dynamic/eggdropping.go @@ -0,0 +1,47 @@ +package dynamic + +import ( + "github.com/TheAlgorithms/Go/math/max" + "github.com/TheAlgorithms/Go/math/min" +) + +// EggDropping finds the minimum number of attempts needed to find the critical floor +// with `eggs` number of eggs and `floors` number of floors +func EggDropping(eggs, floors int) int { + // Edge case: If there are no floors, no attempts needed + if floors == 0 { + return 0 + } + // Edge case: If there is one floor, one attempt needed + if floors == 1 { + return 1 + } + // Edge case: If there is one egg, need to test all floors one by one + if eggs == 1 { + return floors + } + + // Initialize DP table + dp := make([][]int, eggs+1) + for i := range dp { + dp[i] = make([]int, floors+1) + } + + // Fill the DP table for 1 egg + for j := 1; j <= floors; j++ { + dp[1][j] = j + } + + // Fill the DP table for more than 1 egg + for i := 2; i <= eggs; i++ { + for j := 2; j <= floors; j++ { + dp[i][j] = int(^uint(0) >> 1) // initialize with a large number + for x := 1; x <= j; x++ { + // Recurrence relation to fill the DP table + res := max.Int(dp[i-1][x-1], dp[i][j-x]) + 1 + dp[i][j] = min.Int(dp[i][j], res) + } + } + } + return dp[eggs][floors] +} diff --git a/dynamic/eggdropping_test.go b/dynamic/eggdropping_test.go new file mode 100644 index 000000000..5e3232f70 --- /dev/null +++ b/dynamic/eggdropping_test.go @@ -0,0 +1,35 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +type testCaseEggDropping struct { + eggs int + floors int + expected int +} + +func getEggDroppingTestCases() []testCaseEggDropping { + return []testCaseEggDropping{ + {1, 10, 10}, // One egg, need to test all floors + {2, 10, 4}, // Two eggs and ten floors + {3, 14, 4}, // Three eggs and fourteen floors + {2, 36, 8}, // Two eggs and thirty-six floors + {2, 0, 0}, // Two eggs, zero floors + } + +} + +func TestEggDropping(t *testing.T) { + t.Run("Egg Dropping test cases", func(t *testing.T) { + for _, tc := range getEggDroppingTestCases() { + actual := dynamic.EggDropping(tc.eggs, tc.floors) + if actual != tc.expected { + t.Errorf("EggDropping(%d, %d) = %d; expected %d", tc.eggs, tc.floors, actual, tc.expected) + } + } + }) +} diff --git a/dynamic/fibonacci.go b/dynamic/fibonacci.go index 288b82b1c..6025b59f0 100644 --- a/dynamic/fibonacci.go +++ b/dynamic/fibonacci.go @@ -1,3 +1,7 @@ +// fibonacci.go +// description: Implementation of the Fibonacci sequence using dynamic programming +// time complexity: O(n) +// space complexity: O(1) package dynamic // https://www.geeksforgeeks.org/program-for-nth-fibonacci-number/ diff --git a/dynamic/interleavingstrings.go b/dynamic/interleavingstrings.go new file mode 100644 index 000000000..da13840b2 --- /dev/null +++ b/dynamic/interleavingstrings.go @@ -0,0 +1,36 @@ +// interleavingstrings.go +// description: Solves the Interleaving Strings problem using dynamic programming +// reference: https://en.wikipedia.org/wiki/Interleaving_strings +// time complexity: O(m*n) +// space complexity: O(m*n) + +package dynamic + +// IsInterleave checks if string `s1` and `s2` can be interleaved to form string `s3` +func IsInterleave(s1, s2, s3 string) bool { + if len(s1)+len(s2) != len(s3) { + return false + } + + dp := make([][]bool, len(s1)+1) + for i := range dp { + dp[i] = make([]bool, len(s2)+1) + } + + dp[0][0] = true + for i := 1; i <= len(s1); i++ { + dp[i][0] = dp[i-1][0] && s1[i-1] == s3[i-1] + } + + for j := 1; j <= len(s2); j++ { + dp[0][j] = dp[0][j-1] && s2[j-1] == s3[j-1] + } + + for i := 1; i <= len(s1); i++ { + for j := 1; j <= len(s2); j++ { + dp[i][j] = (dp[i-1][j] && s1[i-1] == s3[i+j-1]) || (dp[i][j-1] && s2[j-1] == s3[i+j-1]) + } + } + + return dp[len(s1)][len(s2)] +} diff --git a/dynamic/interleavingstrings_test.go b/dynamic/interleavingstrings_test.go new file mode 100644 index 000000000..a1559e932 --- /dev/null +++ b/dynamic/interleavingstrings_test.go @@ -0,0 +1,38 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +type testCaseInterleaving struct { + s1, s2, s3 string + expected bool +} + +func getInterleavingTestCases() []testCaseInterleaving { + return []testCaseInterleaving{ + {"aab", "axy", "aaxaby", true}, // Valid interleaving + {"aab", "axy", "abaaxy", false}, // Invalid interleaving + {"", "", "", true}, // All empty strings + {"abc", "", "abc", true}, // Only s1 matches s3 + {"", "xyz", "xyz", true}, // Only s2 matches s3 + {"abc", "xyz", "abxcyz", true}, // Valid interleaving + {"aaa", "aaa", "aaaaaa", true}, // Identical strings + {"aaa", "aaa", "aaaaaaa", false}, // Extra character + {"abc", "def", "abcdef", true}, // Concatenation order + {"abc", "def", "adbcef", true}, // Valid mixed interleaving + } +} + +func TestIsInterleave(t *testing.T) { + t.Run("Interleaving Strings test cases", func(t *testing.T) { + for _, tc := range getInterleavingTestCases() { + actual := dynamic.IsInterleave(tc.s1, tc.s2, tc.s3) + if actual != tc.expected { + t.Errorf("IsInterleave(%q, %q, %q) = %v; expected %v", tc.s1, tc.s2, tc.s3, actual, tc.expected) + } + } + }) +} diff --git a/dynamic/knapsack.go b/dynamic/knapsack.go index b07bf6b40..6b637ca54 100644 --- a/dynamic/knapsack.go +++ b/dynamic/knapsack.go @@ -2,6 +2,9 @@ package dynamic // Knapsack Problem // https://www.geeksforgeeks.org/0-1-knapsack-problem-dp-10/ +// https://en.wikipedia.org/wiki/Knapsack_problem +// time complexity: O(n*maxWeight) +// space complexity: O(n*maxWeight) import ( "math" diff --git a/dynamic/longestarithmeticsubsequence.go b/dynamic/longestarithmeticsubsequence.go new file mode 100644 index 000000000..a187b9cea --- /dev/null +++ b/dynamic/longestarithmeticsubsequence.go @@ -0,0 +1,34 @@ +// longestarithmeticsubsequence.go +// description: Implementation of the Longest Arithmetic Subsequence problem +// reference: https://en.wikipedia.org/wiki/Longest_arithmetic_progression +// time complexity: O(n^2) +// space complexity: O(n^2) + +package dynamic + +// LongestArithmeticSubsequence returns the length of the longest arithmetic subsequence +func LongestArithmeticSubsequence(nums []int) int { + n := len(nums) + if n <= 1 { + return n + } + + dp := make([]map[int]int, n) + for i := range dp { + dp[i] = make(map[int]int) + } + + maxLength := 1 + + for i := 1; i < n; i++ { + for j := 0; j < i; j++ { + diff := nums[i] - nums[j] + dp[i][diff] = dp[j][diff] + 1 + if dp[i][diff]+1 > maxLength { + maxLength = dp[i][diff] + 1 + } + } + } + + return maxLength +} diff --git a/dynamic/longestarithmeticsubsequence_test.go b/dynamic/longestarithmeticsubsequence_test.go new file mode 100644 index 000000000..2a971fbd7 --- /dev/null +++ b/dynamic/longestarithmeticsubsequence_test.go @@ -0,0 +1,38 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +type testCaseLongestArithmeticSubsequence struct { + nums []int + expected int +} + +func getLongestArithmeticSubsequenceTestCases() []testCaseLongestArithmeticSubsequence { + return []testCaseLongestArithmeticSubsequence{ + {[]int{3, 6, 9, 12}, 4}, // Arithmetic sequence of length 4 + {[]int{9, 4, 7, 2, 10}, 3}, // Arithmetic sequence of length 3 + {[]int{20, 1, 15, 3, 10, 5, 8}, 4}, // Arithmetic sequence of length 4 + {[]int{1, 2, 3, 4, 5}, 5}, // Arithmetic sequence of length 5 + {[]int{10, 7, 4, 1}, 4}, // Arithmetic sequence of length 4 + {[]int{1, 5, 7, 8, 5, 3, 4, 3, 1, 2}, 4}, // Arithmetic sequence of length 4 + {[]int{1, 3, 5, 7, 9}, 5}, // Arithmetic sequence of length 5 + {[]int{5, 10, 15, 20}, 4}, // Arithmetic sequence of length 4 + {[]int{1}, 1}, // Single element, length is 1 + {[]int{}, 0}, // Empty array, length is 0 + } +} + +func TestLongestArithmeticSubsequence(t *testing.T) { + t.Run("Longest Arithmetic Subsequence test cases", func(t *testing.T) { + for _, tc := range getLongestArithmeticSubsequenceTestCases() { + actual := dynamic.LongestArithmeticSubsequence(tc.nums) + if actual != tc.expected { + t.Errorf("LongestArithmeticSubsequence(%v) = %v; expected %v", tc.nums, actual, tc.expected) + } + } + }) +} diff --git a/dynamic/longestcommonsubsequence.go b/dynamic/longestcommonsubsequence.go index 739aeb8c1..087d986e0 100644 --- a/dynamic/longestcommonsubsequence.go +++ b/dynamic/longestcommonsubsequence.go @@ -1,6 +1,9 @@ // LONGEST COMMON SUBSEQUENCE // DP - 4 // https://www.geeksforgeeks.org/longest-common-subsequence-dp-4/ +// https://leetcode.com/problems/longest-common-subsequence/ +// time complexity: O(m*n) where m and n are lengths of the strings +// space complexity: O(m*n) where m and n are lengths of the strings package dynamic diff --git a/dynamic/longestincreasingsubsequence.go b/dynamic/longestincreasingsubsequence.go index cce099510..a1e70e53f 100644 --- a/dynamic/longestincreasingsubsequence.go +++ b/dynamic/longestincreasingsubsequence.go @@ -1,3 +1,9 @@ +// longestincreasingsubsequence.go +// description: Implementation of the Longest Increasing Subsequence using dynamic programming +// reference: https://en.wikipedia.org/wiki/Longest_increasing_subsequence +// time complexity: O(n^2) +// space complexity: O(n) + package dynamic import ( diff --git a/dynamic/longestpalindromicsubsequence.go b/dynamic/longestpalindromicsubsequence.go index bc5df07b6..700fb8f87 100644 --- a/dynamic/longestpalindromicsubsequence.go +++ b/dynamic/longestpalindromicsubsequence.go @@ -1,4 +1,6 @@ // longest palindromic subsequence +// time complexity: O(n^2) +// space complexity: O(n^2) // http://www.geeksforgeeks.org/dynamic-programming-set-12-longest-palindromic-subsequence/ package dynamic diff --git a/dynamic/longestpalindromicsubstring.go b/dynamic/longestpalindromicsubstring.go new file mode 100644 index 000000000..01d105629 --- /dev/null +++ b/dynamic/longestpalindromicsubstring.go @@ -0,0 +1,43 @@ +// longestpalindromicsubstring.go +// description: Implementation of finding the longest palindromic substring +// reference: https://en.wikipedia.org/wiki/Longest_palindromic_substring +// time complexity: O(n^2) +// space complexity: O(n^2) + +package dynamic + +// LongestPalindromicSubstring returns the longest palindromic substring in the input string +func LongestPalindromicSubstring(s string) string { + n := len(s) + if n == 0 { + return "" + } + + dp := make([][]bool, n) + for i := range dp { + dp[i] = make([]bool, n) + } + + start := 0 + maxLength := 1 + for i := 0; i < n; i++ { + dp[i][i] = true + } + + for length := 2; length <= n; length++ { + for i := 0; i < n-length+1; i++ { + j := i + length - 1 + if length == 2 { + dp[i][j] = (s[i] == s[j]) + } else { + dp[i][j] = (s[i] == s[j]) && dp[i+1][j-1] + } + + if dp[i][j] && length > maxLength { + maxLength = length + start = i + } + } + } + return s[start : start+maxLength] +} diff --git a/dynamic/longestpalindromicsubstring_test.go b/dynamic/longestpalindromicsubstring_test.go new file mode 100644 index 000000000..e8424eabe --- /dev/null +++ b/dynamic/longestpalindromicsubstring_test.go @@ -0,0 +1,37 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +type testCaseLongestPalindromicSubstring struct { + s string + expected string +} + +func getLongestPalindromicSubstringTestCases() []testCaseLongestPalindromicSubstring { + return []testCaseLongestPalindromicSubstring{ + {"babad", "bab"}, // Example with multiple palindromes + {"cbbd", "bb"}, // Example with longest even palindrome + {"a", "a"}, // Single character, palindrome is itself + {"", ""}, // Empty string, no palindrome + {"racecar", "racecar"}, // Whole string is a palindrome + {"abcba", "abcba"}, // Palindrome in the middle + {"aabbcc", "aa"}, // Multiple substrings, longest "aa" + {"madam", "madam"}, // Full palindrome string + {"forgeeksskeegfor", "geeksskeeg"}, // Complex palindrome in the middle + } +} + +func TestLongestPalindromicSubstring(t *testing.T) { + t.Run("Longest Palindromic Substring test cases", func(t *testing.T) { + for _, tc := range getLongestPalindromicSubstringTestCases() { + actual := dynamic.LongestPalindromicSubstring(tc.s) + if actual != tc.expected { + t.Errorf("LongestPalindromicSubstring(%q) = %q; expected %q", tc.s, actual, tc.expected) + } + } + }) +} diff --git a/dynamic/matrixmultiplication.go b/dynamic/matrixmultiplication.go index 373936e62..10fccdbda 100644 --- a/dynamic/matrixmultiplication.go +++ b/dynamic/matrixmultiplication.go @@ -1,6 +1,8 @@ // matrix chain multiplication problem // https://en.wikipedia.org/wiki/Matrix_chain_multiplication // www.geeksforgeeks.org/dynamic_programming-set-8-matrix-chain-multiplication/ +// time complexity: O(n^3) +// space complexity: O(n^2) package dynamic diff --git a/dynamic/maxsubarraysum.go b/dynamic/maxsubarraysum.go new file mode 100644 index 000000000..4e283e1b9 --- /dev/null +++ b/dynamic/maxsubarraysum.go @@ -0,0 +1,22 @@ +// maxsubarraysum.go +// description: Implementation of Kadane's algorithm for Maximum Subarray Sum +// reference: https://en.wikipedia.org/wiki/Maximum_subarray_problem +// time complexity: O(n) +// space complexity: O(1) + +package dynamic + +import "github.com/TheAlgorithms/Go/math/max" + +// MaxSubArraySum returns the sum of the maximum subarray in the input array +func MaxSubArraySum(nums []int) int { + maxSum := nums[0] + currentSum := nums[0] + + for i := 1; i < len(nums); i++ { + currentSum = max.Int(nums[i], currentSum+nums[i]) + maxSum = max.Int(maxSum, currentSum) + } + + return maxSum +} diff --git a/dynamic/maxsubarraysum_test.go b/dynamic/maxsubarraysum_test.go new file mode 100644 index 000000000..20492c3f7 --- /dev/null +++ b/dynamic/maxsubarraysum_test.go @@ -0,0 +1,37 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +type testCaseMaxSubArraySum struct { + nums []int + expected int +} + +func getMaxSubArraySumTestCases() []testCaseMaxSubArraySum { + return []testCaseMaxSubArraySum{ + {[]int{-2, -3, 4, -1, -2, 1, 5, -3}, 7}, // Kadane's algorithm example + {[]int{-1, -2, -3, -4}, -1}, // All negative numbers, max single element + {[]int{5, 4, -1, 7, 8}, 23}, // Positive numbers with a large sum + {[]int{-2, 1, -3, 4, -1, 2, 1, -5, 4}, 6}, // Mixed with a maximum subarray of length 4 + {[]int{1, 2, 3, 4, 5}, 15}, // All positive numbers, sum is the entire array + {[]int{-1, -2, -3, -4, -5}, -1}, // Only negative numbers, largest single element + {[]int{0, 0, 0, 0, 0}, 0}, // Array of zeros, maximum subarray is zero + {[]int{3}, 3}, // Single positive number + {[]int{-1}, -1}, // Single negative number + } +} + +func TestMaxSubArraySum(t *testing.T) { + t.Run("Max SubArray Sum test cases", func(t *testing.T) { + for _, tc := range getMaxSubArraySumTestCases() { + actual := dynamic.MaxSubArraySum(tc.nums) + if actual != tc.expected { + t.Errorf("MaxSubArraySum(%v) = %v; expected %v", tc.nums, actual, tc.expected) + } + } + }) +} diff --git a/dynamic/optimalbst.go b/dynamic/optimalbst.go new file mode 100644 index 000000000..b80f2163c --- /dev/null +++ b/dynamic/optimalbst.go @@ -0,0 +1,61 @@ +package dynamic + +import "github.com/TheAlgorithms/Go/math/min" + +// OptimalBST returns the minimum cost of constructing a Binary Search Tree +func OptimalBST(keys []int, freq []int, n int) int { + // Initialize DP table with size n x n + dp := make([][]int, n) + for i := range dp { + dp[i] = make([]int, n) + } + + // Base case: single key cost + for i := 0; i < n; i++ { + dp[i][i] = freq[i] + } + + // Build the DP table for sequences of length 2 to n + for length := 2; length <= n; length++ { + for i := 0; i < n-length+1; i++ { + j := i + length - 1 + dp[i][j] = int(^uint(0) >> 1) // Initialize with a large value + sum := sum(freq, i, j) + + // Try every key as root and compute cost + for k := i; k <= j; k++ { + // Left cost: dp[i][k-1] is valid only if k > i + var leftCost int + if k > i { + leftCost = dp[i][k-1] + } else { + leftCost = 0 + } + + // Right cost: dp[k+1][j] is valid only if k < j + var rightCost int + if k < j { + rightCost = dp[k+1][j] + } else { + rightCost = 0 + } + + // Total cost for root k + cost := sum + leftCost + rightCost + + // Update dp[i][j] with the minimum cost + dp[i][j] = min.Int(dp[i][j], cost) + } + } + } + return dp[0][n-1] +} + +// Helper function to sum the frequencies +func sum(freq []int, i, j int) int { + total := 0 + for k := i; k <= j; k++ { + total += freq[k] + } + return total +} diff --git a/dynamic/optimalbst_test.go b/dynamic/optimalbst_test.go new file mode 100644 index 000000000..362b45449 --- /dev/null +++ b/dynamic/optimalbst_test.go @@ -0,0 +1,35 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +type testCaseOptimalBST struct { + keys []int + freq []int + n int + expected int +} + +func getOptimalBSTTestCases() []testCaseOptimalBST { + return []testCaseOptimalBST{ + {[]int{10, 12, 20}, []int{34, 8, 50}, 3, 142}, // Example with 3 keys + {[]int{10, 20, 30, 40, 50}, []int{10, 20, 30, 40, 50}, 5, 300}, // Example with 5 keys + {[]int{10}, []int{100}, 1, 100}, // Single key case + } +} + +func TestOptimalBST(t *testing.T) { + t.Run("Optimal Binary Search Tree test cases", func(t *testing.T) { + for _, tc := range getOptimalBSTTestCases() { + t.Run("testing optimal BST", func(t *testing.T) { + actual := dynamic.OptimalBST(tc.keys, tc.freq, tc.n) + if actual != tc.expected { + t.Errorf("OptimalBST(%v, %v, %d) = %d; expected %d", tc.keys, tc.freq, tc.n, actual, tc.expected) + } + }) + } + }) +} diff --git a/dynamic/partitionproblem.go b/dynamic/partitionproblem.go new file mode 100644 index 000000000..bb3ca5496 --- /dev/null +++ b/dynamic/partitionproblem.go @@ -0,0 +1,30 @@ +// partitionproblem.go +// description: Solves the Partition Problem using dynamic programming +// reference: https://en.wikipedia.org/wiki/Partition_problem +// time complexity: O(n*sum) +// space complexity: O(n*sum) + +package dynamic + +// PartitionProblem checks whether the given set can be partitioned into two subsets +// such that the sum of the elements in both subsets is the same. +func PartitionProblem(nums []int) bool { + sum := 0 + for _, num := range nums { + sum += num + } + if sum%2 != 0 { + return false + } + + target := sum / 2 + dp := make([]bool, target+1) + dp[0] = true + + for _, num := range nums { + for i := target; i >= num; i-- { + dp[i] = dp[i] || dp[i-num] + } + } + return dp[target] +} diff --git a/dynamic/partitionproblem_test.go b/dynamic/partitionproblem_test.go new file mode 100644 index 000000000..c85742ecf --- /dev/null +++ b/dynamic/partitionproblem_test.go @@ -0,0 +1,39 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +// testCasePartitionProblem holds the test cases for the Partition Problem +type testCasePartitionProblem struct { + nums []int + expected bool +} + +// getPartitionProblemTestCases returns a list of test cases for the Partition Problem +func getPartitionProblemTestCases() []testCasePartitionProblem { + return []testCasePartitionProblem{ + {[]int{1, 5, 11, 5}, true}, // Example with a partitionable set + {[]int{1, 2, 3, 5}, false}, // Example where partition is not possible + {[]int{1, 2, 5}, false}, // Set cannot be partitioned into two subsets + {[]int{2, 2, 2, 2}, true}, // Even split possible with equal elements + {[]int{7, 3, 2, 1}, false}, // Set cannot be partitioned + {[]int{}, true}, // Empty set, can be partitioned trivially + {[]int{1}, false}, // Single element, cannot be partitioned + {[]int{10, 10, 10, 10}, true}, // Equal elements, partitionable + } +} + +// TestPartitionProblem tests the PartitionProblem function with different test cases +func TestPartitionProblem(t *testing.T) { + t.Run("Partition Problem test cases", func(t *testing.T) { + for _, tc := range getPartitionProblemTestCases() { + actual := dynamic.PartitionProblem(tc.nums) + if actual != tc.expected { + t.Errorf("PartitionProblem(%v) = %v; expected %v", tc.nums, actual, tc.expected) + } + } + }) +} diff --git a/dynamic/rodcutting.go b/dynamic/rodcutting.go index ce721e18b..4d37f4ecc 100644 --- a/dynamic/rodcutting.go +++ b/dynamic/rodcutting.go @@ -1,6 +1,8 @@ // Solution to Rod cutting problem // https://en.wikipedia.org/wiki/Cutting_stock_problem // http://www.geeksforgeeks.org/dynamic-programming-set-13-cutting-a-rod/ +// time complexity: O(n^2) +// space complexity: O(n) package dynamic diff --git a/dynamic/subsetsum.go b/dynamic/subsetsum.go index 1f7da69b8..db04c32de 100644 --- a/dynamic/subsetsum.go +++ b/dynamic/subsetsum.go @@ -1,7 +1,8 @@ //Given a set of non-negative integers, and a (positive) value sum, //determine if there is a subset of the given set with sum //equal to given sum. -//Complexity: O(n*sum) +// time complexity: O(n*sum) +// space complexity: O(n*sum) //references: https://www.geeksforgeeks.org/subset-sum-problem-dp-25/ package dynamic diff --git a/dynamic/subsetsum_test.go b/dynamic/subsetsum_test.go index 2fdfb6cc5..acb24f18e 100644 --- a/dynamic/subsetsum_test.go +++ b/dynamic/subsetsum_test.go @@ -1,9 +1,6 @@ package dynamic -import ( - "fmt" - "testing" -) +import "testing" func TestSubsetSum(t *testing.T) { @@ -74,7 +71,7 @@ func TestSubsetSum(t *testing.T) { for i := range subsetSumTestData { - t.Run(fmt.Sprintf(subsetSumTestData[i].description), func(t *testing.T) { + t.Run(subsetSumTestData[i].description, func(t *testing.T) { array := subsetSumTestData[i].array sum := subsetSumTestData[i].sum diff --git a/dynamic/tilingproblem.go b/dynamic/tilingproblem.go new file mode 100644 index 000000000..0407f320d --- /dev/null +++ b/dynamic/tilingproblem.go @@ -0,0 +1,22 @@ +// tilingproblem.go +// description: Solves the Tiling Problem using dynamic programming +// reference: https://en.wikipedia.org/wiki/Tiling_problem +// time complexity: O(n) +// space complexity: O(n) + +package dynamic + +// TilingProblem returns the number of ways to tile a 2xN grid using 2x1 dominoes +func TilingProblem(n int) int { + if n <= 1 { + return 1 + } + dp := make([]int, n+1) + dp[0] = 1 + dp[1] = 1 + + for i := 2; i <= n; i++ { + dp[i] = dp[i-1] + dp[i-2] + } + return dp[n] +} diff --git a/dynamic/tilingproblem_test.go b/dynamic/tilingproblem_test.go new file mode 100644 index 000000000..4f103cd21 --- /dev/null +++ b/dynamic/tilingproblem_test.go @@ -0,0 +1,38 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +type testCaseTilingProblem struct { + n int + expected int +} + +func getTilingProblemTestCases() []testCaseTilingProblem { + return []testCaseTilingProblem{ + {1, 1}, // Base case: 1 way to tile a 2x1 grid + {2, 2}, // 2 ways to tile a 2x2 grid + {3, 3}, // 3 ways to tile a 2x3 grid + {4, 5}, // 5 ways to tile a 2x4 grid + {5, 8}, // 8 ways to tile a 2x5 grid + {6, 13}, // 13 ways to tile a 2x6 grid + {10, 89}, // 89 ways to tile a 2x10 grid + {0, 1}, // Edge case: 1 way to tile a 2x0 grid (no tiles) + {7, 21}, // 21 ways to tile a 2x7 grid + {8, 34}, // 34 ways to tile a 2x8 grid + } +} + +func TestTilingProblem(t *testing.T) { + t.Run("Tiling Problem test cases", func(t *testing.T) { + for _, tc := range getTilingProblemTestCases() { + actual := dynamic.TilingProblem(tc.n) + if actual != tc.expected { + t.Errorf("TilingProblem(%d) = %d; expected %d", tc.n, actual, tc.expected) + } + } + }) +} diff --git a/dynamic/traprainwater.go b/dynamic/traprainwater.go new file mode 100644 index 000000000..3b1c6a966 --- /dev/null +++ b/dynamic/traprainwater.go @@ -0,0 +1,43 @@ +// filename: traprainwater.go +// description: Provides a function to calculate the amount of trapped rainwater between bars represented by an elevation map using dynamic programming. +// details: +// The TrapRainWater function calculates the amount of trapped rainwater between the bars represented by the given elevation map. +// It uses dynamic programming to precompute the maximum height of bars to the left and right of each position. +// Then, it iterates through the array to calculate the amount of trapped rainwater at each position based on the minimum of the left and right maximum heights. +// Finally, it sums up the trapped rainwater for all positions and returns the total amount. +// time complexity: O(n) +// space complexity: O(n) +// author(s) [TruongNhanNguyen (SOZEL)](https://github.com/TruongNhanNguyen) +package dynamic + +import "math" + +// TrapRainWater calculates the amount of trapped rainwater between the bars represented by the given elevation map. +// It uses dynamic programming to precompute the maximum height of bars to the left and right of each position. +// Then, it iterates through the array to calculate the amount of trapped rainwater at each position based on the minimum of the left and right maximum heights. +// Finally, it sums up the trapped rainwater for all positions and returns the total amount. +func TrapRainWater(height []int) int { + if len(height) == 0 { + return 0 + } + + leftMax := make([]int, len(height)) + rightMax := make([]int, len(height)) + + leftMax[0] = height[0] + for i := 1; i < len(height); i++ { + leftMax[i] = int(math.Max(float64(leftMax[i-1]), float64(height[i]))) + } + + rightMax[len(height)-1] = height[len(height)-1] + for i := len(height) - 2; i >= 0; i-- { + rightMax[i] = int(math.Max(float64(rightMax[i+1]), float64(height[i]))) + } + + trappedWater := 0 + for i := 0; i < len(height); i++ { + trappedWater += int(math.Min(float64(leftMax[i]), float64(rightMax[i]))) - height[i] + } + + return trappedWater +} diff --git a/dynamic/traprainwater_test.go b/dynamic/traprainwater_test.go new file mode 100644 index 000000000..a031a3077 --- /dev/null +++ b/dynamic/traprainwater_test.go @@ -0,0 +1,34 @@ +package dynamic_test + +import ( + "fmt" + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +func TestTrapRainWater(t *testing.T) { + heights := [][]int{ + {}, + {0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}, + {4, 2, 0, 3, 2, 5}, + {3, 1, 2, 4, 0, 1, 3, 2, 4}, + } + + expectedResults := []int{ + 0, + 6, + 9, + 13, + } + + for i, height := range heights { + expected := expectedResults[i] + t.Run(fmt.Sprintf("Case %d", i+1), func(t *testing.T) { + result := dynamic.TrapRainWater(height) + if result != expected { + t.Errorf("Expected %d, but got %d", expected, result) + } + }) + } +} diff --git a/dynamic/uniquepaths.go b/dynamic/uniquepaths.go new file mode 100644 index 000000000..978a81c7e --- /dev/null +++ b/dynamic/uniquepaths.go @@ -0,0 +1,33 @@ +// See https://leetcode.com/problems/unique-paths/ +// time complexity: O(m*n) where m and n are the dimensions of the grid +// space complexity: O(m*n) where m and n are the dimensions of the grid +// author: Rares Mateizer (https://github.com/rares985) +package dynamic + +// UniquePaths implements the solution to the "Unique Paths" problem +func UniquePaths(m, n int) int { + if m <= 0 || n <= 0 { + return 0 + } + + grid := make([][]int, m) + for i := range grid { + grid[i] = make([]int, n) + } + + for i := 0; i < m; i++ { + grid[i][0] = 1 + } + + for j := 0; j < n; j++ { + grid[0][j] = 1 + } + + for i := 1; i < m; i++ { + for j := 1; j < n; j++ { + grid[i][j] = grid[i-1][j] + grid[i][j-1] + } + } + + return grid[m-1][n-1] +} diff --git a/dynamic/uniquepaths_test.go b/dynamic/uniquepaths_test.go new file mode 100644 index 000000000..ac5accbb4 --- /dev/null +++ b/dynamic/uniquepaths_test.go @@ -0,0 +1,28 @@ +package dynamic + +import ( + "testing" +) + +func TestUniquePaths(t *testing.T) { + testCases := map[string]struct { + m int + n int + want int + }{ + "negative sizes": {-1, -1, 0}, + "empty matrix both dimensions": {0, 0, 0}, + "empty matrix one dimension": {0, 1, 0}, + "one element": {1, 1, 1}, + "small matrix": {2, 2, 2}, + "stress test": {1000, 1000, 2874513998398909184}, + } + + for name, test := range testCases { + t.Run(name, func(t *testing.T) { + if got := UniquePaths(test.m, test.n); got != test.want { + t.Errorf("UniquePaths(%v, %v) = %v, want %v", test.m, test.n, got, test.want) + } + }) + } +} diff --git a/dynamic/wildcardmatching.go b/dynamic/wildcardmatching.go new file mode 100644 index 000000000..26b4530af --- /dev/null +++ b/dynamic/wildcardmatching.go @@ -0,0 +1,33 @@ +// wildcardmatching.go +// description: Solves the Wildcard Matching problem using dynamic programming +// reference: https://en.wikipedia.org/wiki/Wildcard_matching +// time complexity: O(m*n) +// space complexity: O(m*n) + +package dynamic + +// IsMatch checks if the string `s` matches the wildcard pattern `p` +func IsMatch(s, p string) bool { + dp := make([][]bool, len(s)+1) + for i := range dp { + dp[i] = make([]bool, len(p)+1) + } + + dp[0][0] = true + for j := 1; j <= len(p); j++ { + if p[j-1] == '*' { + dp[0][j] = dp[0][j-1] + } + } + + for i := 1; i <= len(s); i++ { + for j := 1; j <= len(p); j++ { + if p[j-1] == s[i-1] || p[j-1] == '?' { + dp[i][j] = dp[i-1][j-1] + } else if p[j-1] == '*' { + dp[i][j] = dp[i-1][j] || dp[i][j-1] + } + } + } + return dp[len(s)][len(p)] +} diff --git a/dynamic/wildcardmatching_test.go b/dynamic/wildcardmatching_test.go new file mode 100644 index 000000000..cc6cd4fde --- /dev/null +++ b/dynamic/wildcardmatching_test.go @@ -0,0 +1,44 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +// testCaseWildcardMatching holds the test cases for the Wildcard Matching problem +type testCaseWildcardMatching struct { + s string + p string + expected bool +} + +// getWildcardMatchingTestCases returns a list of test cases for the Wildcard Matching problem +func getWildcardMatchingTestCases() []testCaseWildcardMatching { + return []testCaseWildcardMatching{ + {"aa", "a*", true}, // '*' can match zero or more characters + {"aa", "a", false}, // No match due to no wildcard + {"ab", "?*", true}, // '?' matches any single character, '*' matches remaining + {"abcd", "a*d", true}, // '*' matches the characters between 'a' and 'd' + {"abcd", "a*c", false}, // No match as 'c' doesn't match the last character 'd' + {"abc", "*", true}, // '*' matches the entire string + {"abc", "a*c", true}, // '*' matches 'b' + {"abc", "a?c", true}, // '?' matches 'b' + {"abc", "a?d", false}, // '?' cannot match 'd' + {"", "", true}, // Both strings empty, so they match + {"a", "?", true}, // '?' matches any single character + {"a", "*", true}, // '*' matches any number of characters, including one + } +} + +// TestIsMatch tests the IsMatch function with various test cases +func TestIsMatch(t *testing.T) { + t.Run("Wildcard Matching test cases", func(t *testing.T) { + for _, tc := range getWildcardMatchingTestCases() { + actual := dynamic.IsMatch(tc.s, tc.p) + if actual != tc.expected { + t.Errorf("IsMatch(%q, %q) = %v; expected %v", tc.s, tc.p, actual, tc.expected) + } + } + }) +} diff --git a/dynamic/wordbreak.go b/dynamic/wordbreak.go new file mode 100644 index 000000000..6d7ad5d58 --- /dev/null +++ b/dynamic/wordbreak.go @@ -0,0 +1,28 @@ +// wordbreak.go +// description: Solves the Word Break Problem using dynamic programming +// reference: https://en.wikipedia.org/wiki/Word_break_problem +// time complexity: O(n^2) +// space complexity: O(n) + +package dynamic + +// WordBreak checks if the input string can be segmented into words from a dictionary +func WordBreak(s string, wordDict []string) bool { + wordSet := make(map[string]bool) + for _, word := range wordDict { + wordSet[word] = true + } + + dp := make([]bool, len(s)+1) + dp[0] = true + + for i := 1; i <= len(s); i++ { + for j := 0; j < i; j++ { + if dp[j] && wordSet[s[j:i]] { + dp[i] = true + break + } + } + } + return dp[len(s)] +} diff --git a/dynamic/wordbreak_test.go b/dynamic/wordbreak_test.go new file mode 100644 index 000000000..afcf62cb6 --- /dev/null +++ b/dynamic/wordbreak_test.go @@ -0,0 +1,40 @@ +package dynamic_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/dynamic" +) + +type testCaseWordBreak struct { + s string + wordDict []string + expected bool +} + +func getWordBreakTestCases() []testCaseWordBreak { + return []testCaseWordBreak{ + {"leetcode", []string{"leet", "code"}, true}, // "leetcode" can be segmented into "leet" and "code" + {"applepenapple", []string{"apple", "pen"}, true}, // "applepenapple" can be segmented into "apple", "pen", "apple" + {"catsanddog", []string{"cats", "dog", "sand", "and", "cat"}, true}, // "catsanddog" can be segmented into "cats", "and", "dog" + {"bb", []string{"a", "b", "bbb", "aaaa", "aaa"}, true}, // "bb" can be segmented into "b" and "b" + {"", []string{"cat", "dog", "sand", "and"}, true}, // Empty string can always be segmented (empty words) + {"applepie", []string{"apple", "pie"}, true}, // "applepie" can be segmented into "apple" and "pie" + {"catsandog", []string{"cats", "dog", "sand", "and", "cat"}, false}, // "catsandog" cannot be segmented + {"ilovecoding", []string{"i", "love", "coding"}, true}, // "ilovecoding" can be segmented into "i", "love", "coding" + {"cars", []string{"car", "ca", "rs"}, true}, // "cars" can be segmented into "car" and "s" + {"pen", []string{"pen", "pencil"}, true}, // "pen" is a direct match + {"apple", []string{"orange", "banana"}, false}, // "apple" is not in the word dictionary + } +} + +func TestWordBreak(t *testing.T) { + t.Run("Word Break test cases", func(t *testing.T) { + for _, tc := range getWordBreakTestCases() { + actual := dynamic.WordBreak(tc.s, tc.wordDict) + if actual != tc.expected { + t.Errorf("WordBreak(%q, %v) = %v; expected %v", tc.s, tc.wordDict, actual, tc.expected) + } + } + }) +} diff --git a/graph/articulationpoints.go b/graph/articulationpoints.go index 618107890..26b3a6b78 100644 --- a/graph/articulationpoints.go +++ b/graph/articulationpoints.go @@ -1,56 +1,48 @@ +// Package graph provides algorithms to analyze graph structures. package graph import "github.com/TheAlgorithms/Go/math/min" +// apHelper stores auxiliary data used to identify articulation points in a graph. type apHelper struct { - is_ap []bool - visited []bool - child_cnt []int - discovery_time []int - earliest_discovery []int + isAP []bool + visited []bool + childCount []int + discoveryTime []int + earliestDiscovery []int } -// ArticulationPoint is a function to identify articulation points in a graph. -// The function takes the graph as an argument and returns a boolean slice -// which indicates whether a vertex is an articulation point or not. +// ArticulationPoint identifies articulation points in a graph. It returns a boolean slice +// where each element indicates whether a vertex is an articulation point. // Worst Case Time Complexity: O(|V| + |E|) // Auxiliary Space: O(|V|) -// reference: https://en.wikipedia.org/wiki/Biconnected_component and https://cptalks.quora.com/Cut-Vertex-Articulation-point +// Reference: https://en.wikipedia.org/wiki/Biconnected_component and https://cptalks.quora.com/Cut-Vertex-Articulation-point func ArticulationPoint(graph *Graph) []bool { - // time variable to keep track of the time of discovery_time of a vertex + // Time variable to keep track of the discovery time of a vertex time := 0 - //initialize all the variables + // Initialize apHelper instance with the required data structures apHelperInstance := &apHelper{ - is_ap: make([]bool, graph.vertices), - visited: make([]bool, graph.vertices), - child_cnt: make([]int, graph.vertices), - // integer slice to store the discovery time of a vertex as we traverse - // the graph in a depth first manner - discovery_time: make([]int, graph.vertices), - // integer slice to store the earliest discovered vertex reachable from a vertex - earliest_discovery: make([]int, graph.vertices), + isAP: make([]bool, graph.vertices), + visited: make([]bool, graph.vertices), + childCount: make([]int, graph.vertices), + discoveryTime: make([]int, graph.vertices), + earliestDiscovery: make([]int, graph.vertices), } - articulationPointHelper( - apHelperInstance, - 0, - -1, - &time, - graph, - ) - if apHelperInstance.child_cnt[0] == 1 { - // if the root has only one child, it is not an articulation point - apHelperInstance.is_ap[0] = false + // Start traversal from the root (0) + articulationPointHelper(apHelperInstance, 0, -1, &time, graph) + + // Check if the root has only one child, making it non-articulate + if apHelperInstance.childCount[0] == 1 { + apHelperInstance.isAP[0] = false } - return apHelperInstance.is_ap + return apHelperInstance.isAP } -// articulationPointHelper is a recursive function to traverse the graph -// and mark articulation points. Based on the depth first search transversal -// of the graph, however modified to keep track and update the -// `child_cnt`, `discovery_time` and `earliest_discovery` slices defined above +// articulationPointHelper recursively traverses the graph using DFS and marks articulation points. +// It updates `childCount`, `discoveryTime`, and `earliestDiscovery` slices for the given vertex. func articulationPointHelper( apHelperInstance *apHelper, vertex int, @@ -60,41 +52,38 @@ func articulationPointHelper( ) { apHelperInstance.visited[vertex] = true - // Mark the time of discovery of a vertex - // set the earliest discovery time to the discovered time - // increment the time - apHelperInstance.discovery_time[vertex] = *time - apHelperInstance.earliest_discovery[vertex] = apHelperInstance.discovery_time[vertex] + // Set discovery and earliest discovery times for the vertex + apHelperInstance.discoveryTime[vertex] = *time + apHelperInstance.earliestDiscovery[vertex] = *time *time++ - for next_vertex := range graph.edges[vertex] { - if next_vertex == parent { + for nextVertex := range graph.edges[vertex] { + if nextVertex == parent { continue } - if apHelperInstance.visited[next_vertex] { - apHelperInstance.earliest_discovery[vertex] = min.Int( - apHelperInstance.earliest_discovery[vertex], - apHelperInstance.discovery_time[next_vertex], + if apHelperInstance.visited[nextVertex] { + // Update the earliest discovery time to the smallest reachable discovery time + apHelperInstance.earliestDiscovery[vertex] = min.Int( + apHelperInstance.earliestDiscovery[vertex], + apHelperInstance.discoveryTime[nextVertex], ) continue } - apHelperInstance.child_cnt[vertex]++ - articulationPointHelper( - apHelperInstance, - next_vertex, - vertex, - time, - graph, - ) - apHelperInstance.earliest_discovery[vertex] = min.Int( - apHelperInstance.earliest_discovery[vertex], - apHelperInstance.earliest_discovery[next_vertex], + // Increment child count and perform recursive traversal for DFS + apHelperInstance.childCount[vertex]++ + articulationPointHelper(apHelperInstance, nextVertex, vertex, time, graph) + + // Update the earliest discovery time post DFS + apHelperInstance.earliestDiscovery[vertex] = min.Int( + apHelperInstance.earliestDiscovery[vertex], + apHelperInstance.earliestDiscovery[nextVertex], ) - if apHelperInstance.earliest_discovery[next_vertex] >= apHelperInstance.discovery_time[vertex] { - apHelperInstance.is_ap[vertex] = true - } + // Mark vertex as articulation point if condition meets + if apHelperInstance.earliestDiscovery[nextVertex] >= apHelperInstance.discoveryTime[vertex] { + apHelperInstance.isAP[vertex] = true + } } } diff --git a/graph/bellmanford.go b/graph/bellmanford.go index c9e791f6d..d1db29c5c 100644 --- a/graph/bellmanford.go +++ b/graph/bellmanford.go @@ -1,8 +1,10 @@ // The Bellman–Ford algorithm is an algorithm that computes shortest paths from a -// single source vertex to all of the other vertices in a weighted durected graph. +// single source vertex to all of the other vertices in a weighted directed graph. // It is slower than Dijkstra but capable of handling negative edge weights. // https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm // Implementation is based on the book 'Introduction to Algorithms' (CLRS) +// time complexity: O(V*E) where V is the number of vertices and E is the number of edges in the graph +// space complexity: O(V) where V is the number of vertices in the graph package graph diff --git a/graph/bellmanford_test.go b/graph/bellmanford_test.go index 2f9d57ea6..83364b5df 100644 --- a/graph/bellmanford_test.go +++ b/graph/bellmanford_test.go @@ -125,7 +125,7 @@ func TestBellmanford(t *testing.T) { if resIsReachable != test.isReachable { t.Errorf("Reachable, Expected: %t, Computed: %t", test.isReachable, resIsReachable) } - if resError != test.err { + if !errors.Is(test.err, resError) { if resError == nil || test.err == nil { t.Errorf("Reachable, Expected: %s, Computed: %s", test.err, resError) } else if resError.Error() != test.err.Error() { diff --git a/graph/breadthfirstsearch.go b/graph/breadthfirstsearch.go index 7325c06a3..bac2f04cc 100644 --- a/graph/breadthfirstsearch.go +++ b/graph/breadthfirstsearch.go @@ -3,8 +3,8 @@ package graph // BreadthFirstSearch is an algorithm for traversing and searching graph data structures. // It starts at an arbitrary node of a graph, and explores all of the neighbor nodes // at the present depth prior to moving on to the nodes at the next depth level. -// Worst-case performance O(|V|+|E|)=O(b^{d})}O(|V|+|E|)=O(b^{d}) -// Worst-case space complexity O(|V|)=O(b^{d})}O(|V|)=O(b^{d}) +// Worst-case performance O(|V|+|E|)=O(b^{d})}O(|V|+|E|)=O(b^{d}) where |V| is the number of vertices and |E| is the number of edges in the graph and b is the branching factor of the graph (the average number of successors of a node). d is the depth of the goal node. +// Worst-case space complexity O(|V|)=O(b^{d})}O(|V|)=O(b^{d}) where |V| is the number of vertices and |E| is the number of edges in the graph and b is the branching factor of the graph (the average number of successors of a node). d is the depth of the goal node. // reference: https://en.wikipedia.org/wiki/Breadth-first_search func BreadthFirstSearch(start, end, nodes int, edges [][]int) (isConnected bool, distance int) { queue := make([]int, 0) diff --git a/graph/coloring/backtracking.go b/graph/coloring/backtracking.go index 1ae657d45..885b79e28 100644 --- a/graph/coloring/backtracking.go +++ b/graph/coloring/backtracking.go @@ -1,4 +1,6 @@ // This file contains the graph coloring implementation using backtracking +// time complexity: O(V^V) where V is the number of vertices in the graph +// space complexity: O(V) where V is the number of vertices in the graph // Author(s): [Shivam](https://github.com/Shivam010) package coloring diff --git a/graph/coloring/bfs.go b/graph/coloring/bfs.go index d73b0ed31..22f0bc899 100644 --- a/graph/coloring/bfs.go +++ b/graph/coloring/bfs.go @@ -1,4 +1,6 @@ // This file contains the graph coloring implementation using BFS +// time complexity: O(V+E) where V is the number of vertices and E is the number of edges in the graph +// space complexity: O(V) where V is the number of vertices in the graph // Author(s): [Shivam](https://github.com/Shivam010) package coloring diff --git a/graph/coloring/bipartite.go b/graph/coloring/bipartite.go index f27c7dc54..1f0e3c62c 100644 --- a/graph/coloring/bipartite.go +++ b/graph/coloring/bipartite.go @@ -1,7 +1,13 @@ package coloring +// Bipartite.go +// description: Implementation of the Bipartite graph coloring algorithm +// details: A bipartite graph is a graph whose vertices can be divided into two disjoint sets U and V such that every edge connects a vertex in U to one in V. The Bipartite graph coloring algorithm is used to determine if a graph is bipartite or not. +// time complexity: O(V+E) where V is the number of vertices and E is the number of edges in the graph +// space complexity: O(V) where V is the number of vertices in the graph + func (g *Graph) TryBipartiteColoring() map[int]Color { - // 0 is uncolored, 1/2 is colors + // 0 is uncolored, 1/2 are colors colors := make(map[int]Color) visited := make(map[int]bool) @@ -10,8 +16,8 @@ func (g *Graph) TryBipartiteColoring() map[int]Color { visited[i] = false } - var color_node func(int) - color_node = func(s int) { + var colorNode func(int) + colorNode = func(s int) { visited[s] = true coloring := []Color{0, 2, 1} @@ -20,7 +26,7 @@ func (g *Graph) TryBipartiteColoring() map[int]Color { colors[n] = coloring[colors[s]] } if !visited[n] { - color_node(n) + colorNode(n) } } } @@ -28,7 +34,7 @@ func (g *Graph) TryBipartiteColoring() map[int]Color { for i := range g.edges { if colors[i] == 0 { colors[i] = 1 - color_node(i) + colorNode(i) } } diff --git a/graph/coloring/greedy.go b/graph/coloring/greedy.go index 907395d96..07ca6af1b 100644 --- a/graph/coloring/greedy.go +++ b/graph/coloring/greedy.go @@ -1,4 +1,6 @@ // This file contains the graph coloring implementation using Greedy Approach. +// time complexity: O(V^2) where V is the number of vertices in the graph +// space complexity: O(V) where V is the number of vertices in the graph // Author(s): [Shivam](https://github.com/Shivam010) package coloring diff --git a/graph/cycle.go b/graph/cycle.go new file mode 100644 index 000000000..c55cd6bed --- /dev/null +++ b/graph/cycle.go @@ -0,0 +1,112 @@ +// cycle.go +// this file handle algorithm that related to cycle in graph +// time complexity: O(V+E) where V is the number of vertices and E is the number of edges in the graph +// space complexity: O(V) where V is the number of vertices in the graph +// reference: https://en.wikipedia.org/wiki/Cycle_(graph_theory) +// [kiarash hajian](https://github.com/kiarash8112) + +package graph + +func (g *Graph) HasCycle() bool { + //this implimetation referred as 3-color too + all := map[int]struct{}{} + visiting := map[int]struct{}{} + visited := map[int]struct{}{} + + for v := range g.edges { + all[v] = struct{}{} + } + + for current := range all { + if g.hasCycleHelper(current, all, visiting, visited) { + return true + } + } + + return false + +} + +func (g Graph) hasCycleHelper(v int, all, visiting, visited map[int]struct{}) bool { + delete(all, v) + visiting[v] = struct{}{} + + neighbors := g.edges[v] + for v := range neighbors { + if _, ok := visited[v]; ok { + continue + } else if _, ok := visiting[v]; ok { + return true + } else if g.hasCycleHelper(v, all, visiting, visited) { + return true + } + } + delete(visiting, v) + visited[v] = struct{}{} + return false +} + +// this function can do HasCycle() job but it is slower +func (g *Graph) FindAllCycles() []Graph { + all := map[int]struct{}{} + visiting := map[int]struct{}{} + visited := map[int]struct{}{} + + allCycles := []Graph{} + + for v := range g.edges { + all[v] = struct{}{} + } + + for current := range all { + foundCycle, parents := g.findAllCyclesHelper(current, all, visiting, visited) + + if foundCycle { + foundCycleFromCurrent := false + //this loop remove additional vertex from detected cycle + //using foundCycleFromCurrent bool to make sure after removing vertex we still have cycle + for i := len(parents) - 1; i > 0; i-- { + if parents[i][1] == parents[0][0] { + parents = parents[:i+1] + foundCycleFromCurrent = true + } + } + if foundCycleFromCurrent { + graph := Graph{Directed: true} + for _, edges := range parents { + graph.AddEdge(edges[1], edges[0]) + } + allCycles = append(allCycles, graph) + } + + } + + } + + return allCycles + +} + +func (g Graph) findAllCyclesHelper(current int, all, visiting, visited map[int]struct{}) (bool, [][]int) { + parents := [][]int{} + + delete(all, current) + visiting[current] = struct{}{} + + neighbors := g.edges[current] + for v := range neighbors { + if _, ok := visited[v]; ok { + continue + } else if _, ok := visiting[v]; ok { + parents = append(parents, []int{v, current}) + return true, parents + } else if ok, savedParents := g.findAllCyclesHelper(v, all, visiting, visited); ok { + parents = append(parents, savedParents...) + parents = append(parents, []int{v, current}) + return true, parents + } + } + delete(visiting, current) + visited[current] = struct{}{} + return false, parents +} diff --git a/graph/cycle_test.go b/graph/cycle_test.go new file mode 100644 index 000000000..0ac7dfdbe --- /dev/null +++ b/graph/cycle_test.go @@ -0,0 +1,53 @@ +package graph + +import ( + "testing" +) + +func TestHasCycle(t *testing.T) { + graph := Graph{Directed: true} + edges := [][]int{{0, 1}, {1, 2}, {2, 0}, {4, 0}} + for _, edge := range edges { + graph.AddEdge(edge[0], edge[1]) + } + if !graph.HasCycle() { + t.Error("answer of hasCycle is not correct") + } + + graph = Graph{Directed: true} + edges = [][]int{{0, 1}, {1, 2}, {2, 6}, {4, 0}} + for _, edge := range edges { + graph.AddEdge(edge[0], edge[1]) + } + if graph.HasCycle() { + t.Error("answer of hasCycle is not correct") + } +} + +func TestFindAllCycles(t *testing.T) { + graph := Graph{Directed: true} + edges := [][]int{{0, 4}, {1, 3}, {2, 3}, {3, 4}, {4, 7}, {5, 2}, {6, 3}, {7, 3}} + for _, edge := range edges { + graph.AddEdge(edge[0], edge[1]) + } + + res := graph.FindAllCycles() + + if len(res) != 1 { + t.Error("number of cycles is not correct") + } + + firstCycle := res[0] + if len(firstCycle.edges) != 3 { + t.Error("number of vertex in cycle is not correct") + } + if _, ok := firstCycle.edges[3][4]; !ok { + t.Error("connection in cycle is not correct") + } + if _, ok := firstCycle.edges[4][7]; !ok { + t.Error("connection in cycle is not correct") + } + if _, ok := firstCycle.edges[7][3]; !ok { + t.Error("connection in cycle is not correct") + } +} diff --git a/graph/depthfirstsearch.go b/graph/depthfirstsearch.go index c035c79f5..76bed3106 100644 --- a/graph/depthfirstsearch.go +++ b/graph/depthfirstsearch.go @@ -1,3 +1,9 @@ +// depthfirstsearch.go +// description: this file contains the implementation of the depth first search algorithm +// details: Depth-first search (DFS) is an algorithm for traversing or searching tree or graph data structures. The algorithm starts at the root node and explores as far as possible along each branch before backtracking. +// time complexity: O(n) +// space complexity: O(n) + package graph func GetIdx(target int, nodes []int) int { diff --git a/graph/dijkstra.go b/graph/dijkstra.go index 685cf02e8..ad7a8a778 100644 --- a/graph/dijkstra.go +++ b/graph/dijkstra.go @@ -1,3 +1,10 @@ +// dijkstra.go +// description: this file contains the implementation of the Dijkstra algorithm +// details: Dijkstra's algorithm is an algorithm for finding the shortest paths between nodes in a graph, which may represent, for example, road networks. It was conceived by computer scientist Edsger W. Dijkstra in 1956 and published three years later. The algorithm exists in many variants; Dijkstra's original variant found the shortest path between two nodes, but a more common variant fixes a single node as the "source" node and finds shortest paths from the source to all other nodes in the graph, producing a shortest-path tree. +// time complexity: O((V+E) log V) where V is the number of vertices and E is the number of edges in the graph +// space complexity: O(V) where V is the number of vertices in the graph +// reference: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm + package graph import "github.com/TheAlgorithms/Go/sort" diff --git a/graph/edmondkarp.go b/graph/edmondkarp.go new file mode 100644 index 000000000..f6917efcb --- /dev/null +++ b/graph/edmondkarp.go @@ -0,0 +1,93 @@ +// Edmond-Karp algorithm is an implementation of the Ford-Fulkerson method +// to compute max-flow between a pair of source-sink vertices in a weighted graph +// It uses BFS (Breadth First Search) to find the residual paths +// Time Complexity: O(V * E^2) where V is the number of vertices and E is the number of edges +// Space Complexity: O(V + E) Because we keep residual graph in size of the original graph +// Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. 2009. Introduction to Algorithms, Third Edition (3rd. ed.). The MIT Press. + +package graph + +import ( + "math" +) + +// Returns a mapping of vertices as path, if there is any from source to sink +// Otherwise, returns nil +func FindPath(rGraph WeightedGraph, source int, sink int) map[int]int { + queue := make([]int, 0) + marked := make([]bool, len(rGraph)) + marked[source] = true + queue = append(queue, source) + parent := make(map[int]int) + + // BFS loop with saving the path found + for len(queue) > 0 { + v := queue[0] + queue = queue[1:] + for i := 0; i < len(rGraph[v]); i++ { + if !marked[i] && rGraph[v][i] > 0 { + parent[i] = v + // Terminate the BFS, if we reach to sink + if i == sink { + return parent + } + marked[i] = true + queue = append(queue, i) + } + } + } + // source and sink are not in the same connected component + return nil +} + +func EdmondKarp(graph WeightedGraph, source int, sink int) float64 { + // Check graph emptiness + if len(graph) == 0 { + return 0.0 + } + + // Check correct dimensions of the graph slice + for i := 0; i < len(graph); i++ { + if len(graph[i]) != len(graph) { + return 0.0 + } + } + + rGraph := make(WeightedGraph, len(graph)) + for i := 0; i < len(graph); i++ { + rGraph[i] = make([]float64, len(graph)) + } + // Init the residual graph with the same capacities as the original graph + copy(rGraph, graph) + + maxFlow := 0.0 + + for { + parent := FindPath(rGraph, source, sink) + if parent == nil { + break + } + // Finding the max flow over the path returned by BFS + // i.e. finding minimum residual capacity amonth the path edges + pathFlow := math.MaxFloat64 + for v := sink; v != source; v = parent[v] { + u := parent[v] + if rGraph[u][v] < pathFlow { + pathFlow = rGraph[u][v] + } + } + + // update residual capacities of the edges and + // reverse edges along the path + for v := sink; v != source; v = parent[v] { + u := parent[v] + rGraph[u][v] -= pathFlow + rGraph[v][u] += pathFlow + } + + // Update the total flow found so far + maxFlow += pathFlow + } + + return maxFlow +} diff --git a/graph/edmondkarp_test.go b/graph/edmondkarp_test.go new file mode 100644 index 000000000..08e104652 --- /dev/null +++ b/graph/edmondkarp_test.go @@ -0,0 +1,79 @@ +package graph + +import ( + "testing" +) + +func TestEdmondKarp(t *testing.T) { + var edmondKarpTestData = []struct { + description string + graph WeightedGraph + source int + sink int + expected float64 + }{ + { + description: "test empty graph", + graph: nil, + source: 0, + sink: 0, + expected: 0.0, + }, + { + description: "test graph with wrong dimensions", + graph: WeightedGraph{ + {1, 2}, + {0}, + }, + source: 0, + sink: 1, + expected: 0.0, + }, + { + description: "test graph with no edges", + graph: WeightedGraph{ + {0, 0}, + {0, 0}, + }, + source: 0, + sink: 1, + expected: 0.0, + }, + { + description: "test graph with 4 vertices", + graph: WeightedGraph{ + {0, 1000000, 1000000, 0}, + {0, 0, 1, 1000000}, + {0, 0, 0, 1000000}, + {0, 0, 0, 0}, + }, + source: 0, + sink: 3, + expected: 2000000, + }, + { + description: "test graph with 6 vertices and some float64 weights", + graph: WeightedGraph{ + {0, 16, 13.8, 0, 0, 0}, + {0, 0, 10, 12.7, 0, 0}, + {0, 4.2, 0, 0, 14, 0}, + {0, 0, 9.1, 0, 0, 21.3}, + {0, 0, 0, 7.5, 0, 4}, + {0, 0, 0, 0, 0, 0}, + }, + source: 0, + sink: 5, + expected: 24.2, + }, + } + for _, test := range edmondKarpTestData { + t.Run(test.description, func(t *testing.T) { + result := EdmondKarp(test.graph, test.source, test.sink) + + if !almostEqual(test.expected, result) { + t.Logf("FAIL: %s", test.description) + t.Fatalf("Expected result:%f\nFound: %f", test.expected, result) + } + }) + } +} diff --git a/graph/floydwarshall.go b/graph/floydwarshall.go index 33b97a2c1..9544e587b 100644 --- a/graph/floydwarshall.go +++ b/graph/floydwarshall.go @@ -1,4 +1,6 @@ // Floyd-Warshall algorithm +// time complexity: O(V^3) where V is the number of vertices in the graph +// space complexity: O(V^2) where V is the number of vertices in the graph // https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm package graph diff --git a/graph/kahn.go b/graph/kahn.go new file mode 100644 index 000000000..6f9d44d71 --- /dev/null +++ b/graph/kahn.go @@ -0,0 +1,66 @@ +// Kahn's algorithm computes a topological ordering of a directed acyclic graph (DAG). +// Time Complexity: O(V + E) +// Space Complexity: O(V + E) +// Reference: https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm +// see graph.go, topological.go, kahn_test.go + +package graph + +// Kahn's algorithm computes a topological ordering of a directed acyclic graph (DAG). +// `n` is the number of vertices, +// `dependencies` is a list of directed edges, where each pair [a, b] represents +// a directed edge from a to b (i.e. b depends on a). +// Vertices are assumed to be labelled 0, 1, ..., n-1. +// If the graph is not a DAG, the function returns nil. +func Kahn(n int, dependencies [][]int) []int { + g := Graph{vertices: n, Directed: true} + // track the in-degree (number of incoming edges) of each vertex + inDegree := make([]int, n) + + // populate g with edges, increase the in-degree counts accordingly + for _, d := range dependencies { + // make sure we don't add the same edge twice + if _, ok := g.edges[d[0]][d[1]]; !ok { + g.AddEdge(d[0], d[1]) + inDegree[d[1]]++ + } + } + + // queue holds all vertices with in-degree 0 + // these vertices have no dependency and thus can be ordered first + queue := make([]int, 0, n) + + for i := 0; i < n; i++ { + if inDegree[i] == 0 { + queue = append(queue, i) + } + } + + // order holds a valid topological order + order := make([]int, 0, n) + + // process the dependency-free vertices + // every time we process a vertex, we "remove" it from the graph + for len(queue) > 0 { + // pop the first vertex from the queue + vtx := queue[0] + queue = queue[1:] + // add the vertex to the topological order + order = append(order, vtx) + // "remove" all the edges coming out of this vertex + // every time we remove an edge, the corresponding in-degree reduces by 1 + // if all dependencies on a vertex is removed, enqueue the vertex + for neighbour := range g.edges[vtx] { + inDegree[neighbour]-- + if inDegree[neighbour] == 0 { + queue = append(queue, neighbour) + } + } + } + + // if the graph is a DAG, order should contain all the certices + if len(order) != n { + return nil + } + return order +} diff --git a/graph/kahn_test.go b/graph/kahn_test.go new file mode 100644 index 000000000..71536b4f6 --- /dev/null +++ b/graph/kahn_test.go @@ -0,0 +1,115 @@ +package graph + +import ( + "testing" +) + +func TestKahn(t *testing.T) { + testCases := []struct { + name string + n int + dependencies [][]int + wantNil bool + }{ + { + "linear graph", + 3, + [][]int{{0, 1}, {1, 2}}, + false, + }, + { + "diamond graph", + 4, + [][]int{{0, 1}, {0, 2}, {1, 3}, {2, 3}}, + false, + }, + { + "star graph", + 5, + [][]int{{0, 1}, {0, 2}, {0, 3}, {0, 4}}, + false, + }, + { + "disconnected graph", + 5, + [][]int{{0, 1}, {0, 2}, {3, 4}}, + false, + }, + { + "cycle graph 1", + 4, + [][]int{{0, 1}, {1, 2}, {2, 3}, {3, 0}}, + true, + }, + { + "cycle graph 2", + 4, + [][]int{{0, 1}, {1, 2}, {2, 0}, {2, 3}}, + true, + }, + { + "single node graph", + 1, + [][]int{}, + false, + }, + { + "empty graph", + 0, + [][]int{}, + false, + }, + { + "redundant dependencies", + 4, + [][]int{{0, 1}, {1, 2}, {1, 2}, {2, 3}}, + false, + }, + { + "island vertex", + 4, + [][]int{{0, 1}, {0, 2}}, + false, + }, + { + "more complicated graph", + 14, + [][]int{{1, 9}, {2, 0}, {3, 2}, {4, 5}, {4, 6}, {4, 7}, {6, 7}, + {7, 8}, {9, 4}, {10, 0}, {10, 1}, {10, 12}, {11, 13}, + {12, 0}, {12, 11}, {13, 5}}, + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual := Kahn(tc.n, tc.dependencies) + if tc.wantNil { + if actual != nil { + t.Errorf("Kahn(%d, %v) = %v; want nil", tc.n, tc.dependencies, actual) + } + } else { + if actual == nil { + t.Errorf("Kahn(%d, %v) = nil; want valid order", tc.n, tc.dependencies) + } else { + seen := make([]bool, tc.n) + positions := make([]int, tc.n) + for i, v := range actual { + seen[v] = true + positions[v] = i + } + for i, v := range seen { + if !v { + t.Errorf("missing vertex %v", i) + } + } + for _, d := range tc.dependencies { + if positions[d[0]] > positions[d[1]] { + t.Errorf("dependency %v not satisfied", d) + } + } + } + } + }) + } +} diff --git a/graph/kosaraju.go b/graph/kosaraju.go new file mode 100644 index 000000000..d36949e5f --- /dev/null +++ b/graph/kosaraju.go @@ -0,0 +1,92 @@ +// kosaraju.go +// description: Implementation of Kosaraju's algorithm to find Strongly Connected Components (SCCs) in a directed graph. +// details: The algorithm consists of three steps: +// 1. Perform DFS and fill the stack with vertices in the order of their finish times. +// 2. Create a transposed graph by reversing all edges. +// 3. Perform DFS on the transposed graph in the order defined by the stack to find SCCs. +// time: O(V + E), where V is the number of vertices and E is the number of edges in the graph. +// space: O(V), where V is the number of vertices in the graph. +// ref link: https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm +// author: mapcrafter2048 + +package graph + +// Kosaraju returns a list of Strongly Connected Components (SCCs). +func (g *Graph) Kosaraju() [][]int { + stack := []int{} + visited := make([]bool, g.vertices) + + // Step 1: Perform DFS and fill stack based on finish times. + for i := 0; i < g.vertices; i++ { + if !visited[i] { + g.fillOrder(i, visited, &stack) + } + } + + // Step 2: Create a transposed graph. + transposed := g.transpose() + + // Step 3: Perform DFS on the transposed graph in the order defined by the stack. + visited = make([]bool, g.vertices) + var sccs [][]int + + for len(stack) > 0 { + // Pop vertex from stack + v := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + // Perform DFS if not already visited. + if !visited[v] { + scc := []int{} + transposed.dfs(v, visited, &scc) + sccs = append(sccs, scc) + } + } + + return sccs +} + +// Helper function to fill the stack with vertices in the order of their finish times. +func (g *Graph) fillOrder(v int, visited []bool, stack *[]int) { + visited[v] = true + + for neighbor := range g.edges[v] { + if !visited[neighbor] { + g.fillOrder(neighbor, visited, stack) + } + } + + // Push the current vertex to the stack after exploring all neighbors. + *stack = append(*stack, v) +} + +// Helper function to create a transposed (reversed) graph. +func (g *Graph) transpose() *Graph { + transposed := &Graph{ + vertices: g.vertices, + edges: make(map[int]map[int]int), + } + + for v, neighbors := range g.edges { + for neighbor := range neighbors { + if transposed.edges[neighbor] == nil { + transposed.edges[neighbor] = make(map[int]int) + } + transposed.edges[neighbor][v] = 1 // Add the reversed edge + } + } + + return transposed +} + +// Helper DFS function used in the transposed graph to collect SCCs. +func (g *Graph) dfs(v int, visited []bool, scc *[]int) { + visited[v] = true + *scc = append(*scc, v) + + for neighbor := range g.edges[v] { + if !visited[neighbor] { + g.dfs(neighbor, visited, scc) + } + } +} diff --git a/graph/kosaraju_test.go b/graph/kosaraju_test.go new file mode 100644 index 000000000..360b72a3e --- /dev/null +++ b/graph/kosaraju_test.go @@ -0,0 +1,106 @@ +package graph + +import ( + "reflect" + "sort" + "testing" +) + +func TestKosaraju(t *testing.T) { + tests := []struct { + name string + vertices int + edges map[int][]int + expected [][]int + }{ + { + name: "Single SCC", + vertices: 5, + edges: map[int][]int{ + 0: {1}, + 1: {2}, + 2: {0, 3}, + 3: {4}, + 4: {}, + }, + expected: [][]int{{4}, {3}, {0, 2, 1}}, + }, + { + name: "Multiple SCCs", + vertices: 8, + edges: map[int][]int{ + 0: {1}, + 1: {2}, + 2: {0, 3}, + 3: {4}, + 4: {5}, + 5: {3, 6}, + 6: {7}, + 7: {6}, + }, + expected: [][]int{{6, 7}, {3, 4, 5}, {0, 2, 1}}, + }, + { + name: "Disconnected graph", + vertices: 4, + edges: map[int][]int{ + 0: {1}, + 1: {}, + 2: {3}, + 3: {}, + }, + expected: [][]int{{1}, {0}, {3}, {2}}, + }, + { + name: "No edges", + vertices: 3, + edges: map[int][]int{ + 0: {}, + 1: {}, + 2: {}, + }, + expected: [][]int{{0}, {1}, {2}}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Initializing graph + graph := &Graph{ + vertices: tt.vertices, + edges: make(map[int]map[int]int), + } + for v, neighbors := range tt.edges { + graph.edges[v] = make(map[int]int) + for _, neighbor := range neighbors { + graph.edges[v][neighbor] = 1 + } + } + + // Running Kosaraju's algorithm to get the SCCs + result := graph.Kosaraju() + + // Sort the expected and result SCCs to ensure order doesn't matter + sortSlices(tt.expected) + sortSlices(result) + + // Compare the sorted SCCs + if !reflect.DeepEqual(result, tt.expected) { + t.Errorf("expected %v, got %v", tt.expected, result) + } + }) + } +} + +// Utility function to sort the slices and their contents +func sortSlices(s [][]int) { + for _, inner := range s { + sort.Ints(inner) + } + sort.Slice(s, func(i, j int) bool { + if len(s[i]) == 0 || len(s[j]) == 0 { + return len(s[i]) < len(s[j]) + } + return s[i][0] < s[j][0] + }) +} diff --git a/graph/kruskal.go b/graph/kruskal.go index c6f094020..1b0328da3 100644 --- a/graph/kruskal.go +++ b/graph/kruskal.go @@ -1,6 +1,10 @@ // KRUSKAL'S ALGORITHM -// https://cp-algorithms.com/data_structures/disjoint_set_union.html -// https://cp-algorithms.com/graph/mst_kruskal_with_dsu.html +// Reference: Kruskal's Algorithm: https://www.scaler.com/topics/data-structures/kruskal-algorithm/ +// Reference: Union Find Algorithm: https://www.scaler.com/topics/data-structures/disjoint-set/ +// Author: Author: Mugdha Behere[https://github.com/MugdhaBehere] +// Worst Case Time Complexity: O(E log E), where E is the number of edges. +// Worst Case Space Complexity: O(V + E), where V is the number of vertices and E is the number of edges. +// see kruskal.go, kruskal_test.go package graph @@ -10,104 +14,38 @@ import ( type Vertex int -// Edge describes the edge of a weighted graph type Edge struct { Start Vertex End Vertex Weight int } -// DisjointSetUnionElement describes what an element of DSU looks like -type DisjointSetUnionElement struct { - Parent Vertex - Rank int -} - -// DisjointSetUnion is a data structure that treats its elements as separate sets -// and provides fast operations for set creation, merging sets, and finding the parent -// of the given element of a set. -type DisjointSetUnion []DisjointSetUnionElement - -// NewDSU will return an initialised DSU using the value of n -// which will be treated as the number of elements out of which -// the DSU is being made -func NewDSU(n int) *DisjointSetUnion { - - dsu := DisjointSetUnion(make([]DisjointSetUnionElement, n)) - return &dsu -} - -// MakeSet will create a set in the DSU for the given node -func (dsu DisjointSetUnion) MakeSet(node Vertex) { - - dsu[node].Parent = node - dsu[node].Rank = 0 -} - -// FindSetRepresentative will return the parent element of the set the given node -// belongs to. Since every single element in the path from node to parent -// has the same parent, we store the parent value for each element in the -// path. This reduces consequent function calls and helps in going from O(n) -// to O(log n). This is known as path compression technique. -func (dsu DisjointSetUnion) FindSetRepresentative(node Vertex) Vertex { - - if node == dsu[node].Parent { - return node - } - - dsu[node].Parent = dsu.FindSetRepresentative(dsu[node].Parent) - return dsu[node].Parent -} - -// unionSets will merge two given sets. The naive implementation of this -// always combines the secondNode's tree with the firstNode's tree. This can lead -// to creation of trees of length O(n) so we optimize by attaching the node with -// smaller rank to the node with bigger rank. Rank represents the upper bound depth of the tree. -func (dsu DisjointSetUnion) UnionSets(firstNode Vertex, secondNode Vertex) { - - firstNode = dsu.FindSetRepresentative(firstNode) - secondNode = dsu.FindSetRepresentative(secondNode) - - if firstNode != secondNode { - - if dsu[firstNode].Rank < dsu[secondNode].Rank { - firstNode, secondNode = secondNode, firstNode - } - dsu[secondNode].Parent = firstNode - - if dsu[firstNode].Rank == dsu[secondNode].Rank { - dsu[firstNode].Rank++ - } - } -} - -// KruskalMST will return a minimum spanning tree along with its total cost -// to using Kruskal's algorithm. Time complexity is O(m * log (n)) where m is -// the number of edges in the graph and n is number of nodes in it. func KruskalMST(n int, edges []Edge) ([]Edge, int) { + // Initialize variables to store the minimum spanning tree and its total cost + var mst []Edge + var cost int - var mst []Edge // The resultant minimum spanning tree - var cost int = 0 - - dsu := NewDSU(n) - - for i := 0; i < n; i++ { - dsu.MakeSet(Vertex(i)) - } + // Create a new UnionFind data structure with 'n' nodes + u := NewUnionFind(n) + // Sort the edges in non-decreasing order based on their weights sort.SliceStable(edges, func(i, j int) bool { return edges[i].Weight < edges[j].Weight }) + // Iterate through the sorted edges for _, edge := range edges { - - if dsu.FindSetRepresentative(edge.Start) != dsu.FindSetRepresentative(edge.End) { - + // Check if adding the current edge forms a cycle or not + if u.Find(int(edge.Start)) != u.Find(int(edge.End)) { + // Add the edge to the minimum spanning tree mst = append(mst, edge) + // Add the weight of the edge to the total cost cost += edge.Weight - dsu.UnionSets(edge.Start, edge.End) + // Merge the sets containing the start and end vertices of the current edge + u.Union(int(edge.Start), int(edge.End)) } } + // Return the minimum spanning tree and its total cost return mst, cost } diff --git a/graph/kruskal_test.go b/graph/kruskal_test.go index 86f062d53..09ed6949d 100644 --- a/graph/kruskal_test.go +++ b/graph/kruskal_test.go @@ -5,157 +5,80 @@ import ( "testing" ) -func Test_KruskalMST(t *testing.T) { - +func TestKruskalMST(t *testing.T) { + // Define test cases with inputs, expected outputs, and sample graphs var testCases = []struct { n int graph []Edge cost int }{ + // Test Case 1 { n: 5, graph: []Edge{ - { - Start: 0, - End: 1, - Weight: 4, - }, - { - Start: 0, - End: 2, - Weight: 13, - }, - { - Start: 0, - End: 3, - Weight: 7, - }, - { - Start: 0, - End: 4, - Weight: 7, - }, - { - Start: 1, - End: 2, - Weight: 9, - }, - { - Start: 1, - End: 3, - Weight: 3, - }, - { - Start: 1, - End: 4, - Weight: 7, - }, - { - Start: 2, - End: 3, - Weight: 10, - }, - { - Start: 2, - End: 4, - Weight: 14, - }, - { - Start: 3, - End: 4, - Weight: 4, - }, + {Start: 0, End: 1, Weight: 4}, + {Start: 0, End: 2, Weight: 13}, + {Start: 0, End: 3, Weight: 7}, + {Start: 0, End: 4, Weight: 7}, + {Start: 1, End: 2, Weight: 9}, + {Start: 1, End: 3, Weight: 3}, + {Start: 1, End: 4, Weight: 7}, + {Start: 2, End: 3, Weight: 10}, + {Start: 2, End: 4, Weight: 14}, + {Start: 3, End: 4, Weight: 4}, }, cost: 20, }, + // Test Case 2 { n: 3, graph: []Edge{ - { - Start: 0, - End: 1, - Weight: 12, - }, - { - Start: 0, - End: 2, - Weight: 18, - }, - { - Start: 1, - End: 2, - Weight: 6, - }, + {Start: 0, End: 1, Weight: 12}, + {Start: 0, End: 2, Weight: 18}, + {Start: 1, End: 2, Weight: 6}, }, cost: 18, }, + // Test Case 3 { n: 4, graph: []Edge{ - { - Start: 0, - End: 1, - Weight: 2, - }, - { - Start: 0, - End: 2, - Weight: 1, - }, - { - Start: 0, - End: 3, - Weight: 2, - }, - { - Start: 1, - End: 2, - Weight: 1, - }, - { - Start: 1, - End: 3, - Weight: 2, - }, - { - Start: 2, - End: 3, - Weight: 3, - }, + {Start: 0, End: 1, Weight: 2}, + {Start: 0, End: 2, Weight: 1}, + {Start: 0, End: 3, Weight: 2}, + {Start: 1, End: 2, Weight: 1}, + {Start: 1, End: 3, Weight: 2}, + {Start: 2, End: 3, Weight: 3}, }, cost: 4, }, + // Test Case 4 { n: 2, graph: []Edge{ - { - Start: 0, - End: 1, - Weight: 4000000, - }, + {Start: 0, End: 1, Weight: 4000000}, }, cost: 4000000, }, + // Test Case 5 { n: 1, graph: []Edge{ - { - Start: 0, - End: 0, - Weight: 0, - }, + {Start: 0, End: 0, Weight: 0}, }, cost: 0, }, } - for i := range testCases { - + // Iterate through the test cases and run the tests + for i, testCase := range testCases { t.Run(fmt.Sprintf("Test Case %d", i), func(t *testing.T) { + // Execute KruskalMST for the current test case + _, computed := KruskalMST(testCase.n, testCase.graph) - _, computed := KruskalMST(testCases[i].n, testCases[i].graph) - if computed != testCases[i].cost { - t.Errorf("Test Case %d, Expected: %d, Computed: %d", i, testCases[i].cost, computed) + // Compare the computed result with the expected result + if computed != testCase.cost { + t.Errorf("Test Case %d, Expected: %d, Computed: %d", i, testCase.cost, computed) } }) } diff --git a/graph/lowestcommonancestor.go b/graph/lowestcommonancestor.go index 8ce575187..891bac50d 100644 --- a/graph/lowestcommonancestor.go +++ b/graph/lowestcommonancestor.go @@ -3,6 +3,8 @@ // detail: // Let `T` be a tree. The LCA of `u` and `v` in T is the shared ancestor of `u` and `v` // that is located farthest from the root. +// time complexity: O(n log n) where n is the number of vertices in the tree +// space complexity: O(n log n) where n is the number of vertices in the tree // references: [cp-algorithms](https://cp-algorithms.com/graph/lca_binary_lifting.html) // author(s) [Dat](https://github.com/datbeohbbh) // see lowestcommonancestor_test.go for a test implementation. diff --git a/graph/prim.go b/graph/prim.go new file mode 100644 index 000000000..d99dcd6e1 --- /dev/null +++ b/graph/prim.go @@ -0,0 +1,58 @@ +// The Prim's algorithm computes the minimum spanning tree for a weighted undirected graph +// Worst Case Time Complexity: O(E log V) using Binary heap, where V is the number of vertices and E is the number of edges +// Space Complexity: O(V + E) +// Implementation is based on the book 'Introduction to Algorithms' (CLRS) + +package graph + +import ( + "container/heap" +) + +type minEdge []Edge + +func (h minEdge) Len() int { return len(h) } +func (h minEdge) Less(i, j int) bool { return h[i].Weight < h[j].Weight } +func (h minEdge) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h *minEdge) Push(x interface{}) { + *h = append(*h, x.(Edge)) +} + +func (h *minEdge) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +func (g *Graph) PrimMST(start Vertex) ([]Edge, int) { + var mst []Edge + marked := make([]bool, g.vertices) + h := &minEdge{} + // Pushing neighbors of the start node to the binary heap + for neighbor, weight := range g.edges[int(start)] { + heap.Push(h, Edge{start, Vertex(neighbor), weight}) + } + marked[start] = true + cost := 0 + for h.Len() > 0 { + e := heap.Pop(h).(Edge) + end := int(e.End) + // To avoid cycles + if marked[end] { + continue + } + marked[end] = true + cost += e.Weight + mst = append(mst, e) + // Check for neighbors of the newly added edge's End vertex + for neighbor, weight := range g.edges[end] { + if !marked[neighbor] { + heap.Push(h, Edge{e.End, Vertex(neighbor), weight}) + } + } + } + return mst, cost +} diff --git a/graph/prim_test.go b/graph/prim_test.go new file mode 100644 index 000000000..8a3d42c12 --- /dev/null +++ b/graph/prim_test.go @@ -0,0 +1,143 @@ +package graph + +import ( + "fmt" + "reflect" + "testing" +) + +func TestPrimMST(t *testing.T) { + + var testCases = []struct { + edges []Edge + vertices int + start int + cost int + mst []Edge + }{ + { + edges: []Edge{ + {Start: 0, End: 1, Weight: 4}, + {Start: 0, End: 2, Weight: 13}, + {Start: 0, End: 3, Weight: 7}, + {Start: 0, End: 4, Weight: 7}, + {Start: 1, End: 2, Weight: 9}, + {Start: 1, End: 3, Weight: 3}, + {Start: 1, End: 4, Weight: 7}, + {Start: 2, End: 3, Weight: 10}, + {Start: 2, End: 4, Weight: 14}, + {Start: 3, End: 4, Weight: 4}, + }, + vertices: 5, + start: 0, + cost: 20, + mst: []Edge{ + {Start: 0, End: 1, Weight: 4}, + {Start: 1, End: 3, Weight: 3}, + {Start: 3, End: 4, Weight: 4}, + {Start: 1, End: 2, Weight: 9}, + }, + }, + { + edges: []Edge{ + {Start: 0, End: 1, Weight: 4}, + {Start: 0, End: 7, Weight: 8}, + {Start: 1, End: 2, Weight: 8}, + {Start: 1, End: 7, Weight: 11}, + {Start: 2, End: 3, Weight: 7}, + {Start: 2, End: 5, Weight: 4}, + {Start: 2, End: 8, Weight: 2}, + {Start: 3, End: 4, Weight: 9}, + {Start: 3, End: 5, Weight: 14}, + {Start: 4, End: 5, Weight: 10}, + {Start: 5, End: 6, Weight: 2}, + {Start: 6, End: 7, Weight: 1}, + {Start: 6, End: 8, Weight: 6}, + {Start: 7, End: 8, Weight: 7}, + }, + vertices: 9, + start: 3, + cost: 37, + mst: []Edge{ + {Start: 3, End: 2, Weight: 7}, + {Start: 2, End: 8, Weight: 2}, + {Start: 2, End: 5, Weight: 4}, + {Start: 5, End: 6, Weight: 2}, + {Start: 6, End: 7, Weight: 1}, + {Start: 2, End: 1, Weight: 8}, + {Start: 1, End: 0, Weight: 4}, + {Start: 3, End: 4, Weight: 9}, + }, + }, + { + edges: []Edge{ + {Start: 0, End: 1, Weight: 2}, + {Start: 0, End: 3, Weight: 6}, + {Start: 1, End: 2, Weight: 3}, + {Start: 1, End: 3, Weight: 8}, + {Start: 1, End: 4, Weight: 5}, + {Start: 2, End: 4, Weight: 7}, + {Start: 3, End: 4, Weight: 9}, + }, + vertices: 5, + start: 2, + cost: 16, + mst: []Edge{ + {Start: 2, End: 1, Weight: 3}, + {Start: 1, End: 0, Weight: 2}, + {Start: 1, End: 4, Weight: 5}, + {Start: 0, End: 3, Weight: 6}, + }, + }, + { + edges: []Edge{ + {Start: 0, End: 0, Weight: 0}, + }, + vertices: 1, + start: 0, + cost: 0, + mst: nil, + }, + { + edges: []Edge{ + {Start: 0, End: 1, Weight: 1}, + {Start: 0, End: 2, Weight: 6}, + {Start: 0, End: 3, Weight: 5}, + {Start: 1, End: 2, Weight: 2}, + {Start: 1, End: 4, Weight: 4}, + {Start: 2, End: 4, Weight: 9}, + }, + vertices: 5, + start: 4, + cost: 12, + mst: []Edge{ + {Start: 4, End: 1, Weight: 4}, + {Start: 1, End: 0, Weight: 1}, + {Start: 1, End: 2, Weight: 2}, + {Start: 0, End: 3, Weight: 5}, + }, + }, + } + + for i, testCase := range testCases { + t.Run(fmt.Sprintf("Test Case %d", i), func(t *testing.T) { + // Initializing graph, adding edges + graph := New(testCase.vertices) + graph.Directed = false + for _, edge := range testCase.edges { + graph.AddWeightedEdge(int(edge.Start), int(edge.End), edge.Weight) + } + + computedMST, computedCost := graph.PrimMST(Vertex(testCase.start)) + + // Compare the computed result with the expected result + if computedCost != testCase.cost { + t.Errorf("Test Case %d, Expected Cost: %d, Computed: %d", i, testCase.cost, computedCost) + } + + if !reflect.DeepEqual(testCase.mst, computedMST) { + t.Errorf("Test Case %d, Expected MST: %v, Computed: %v", i, testCase.mst, computedMST) + } + }) + } +} diff --git a/graph/topological.go b/graph/topological.go index feef703f1..8cd1856e0 100644 --- a/graph/topological.go +++ b/graph/topological.go @@ -1,7 +1,14 @@ +// topological.go +// description: Topological sort +// details: Topological sorting for Directed Acyclic Graph (DAG) is a linear ordering of vertices such that for every directed edge u v, vertex u comes before v in the ordering. Topological Sorting for a graph is not possible if the graph is not a DAG. +// time complexity: O(V+E) where V is the number of vertices and E is the number of edges in the graph +// space complexity: O(V) where V is the number of vertices in the graph +// reference: https://en.wikipedia.org/wiki/Topological_sorting + package graph -// Assumes that graph given is valid and possible to -// get a topo ordering. +// Topological assumes that graph given is valid and that its +// possible to get a topological ordering. // constraints are array of []int{a, b}, representing // an edge going from a to b func Topological(N int, constraints [][]int) []int { @@ -22,7 +29,7 @@ func Topological(N int, constraints [][]int) []int { edges[a][b] = true } - answer := []int{} + var answer []int for s := 0; s < N; s++ { // Only start walking from top level nodes if dependencies[s] == 0 { diff --git a/graph/unionfind.go b/graph/unionfind.go new file mode 100644 index 000000000..42714ab39 --- /dev/null +++ b/graph/unionfind.go @@ -0,0 +1,62 @@ +// Union Find Algorithm or Dynamic Connectivity algorithm, often implemented with the help +//of the union find data structure, +// is used to efficiently maintain connected components in a graph that undergoes dynamic changes, +// such as edges being added or removed over time +// Worst Case Time Complexity: The time complexity of find operation is nearly constant or +//O(α(n)), where α(n) is the inverse Ackermann function +// practically, this is a very slowly growing function making the time complexity for find +//operation nearly constant. +// The time complexity of the union operation is also nearly constant or O(α(n)) +// Worst Case Space Complexity: O(n), where n is the number of nodes or element in the structure +// Reference: https://www.scaler.com/topics/data-structures/disjoint-set/ +// https://en.wikipedia.org/wiki/Disjoint-set_data_structure +// Author: Mugdha Behere[https://github.com/MugdhaBehere] +// see: unionfind.go, unionfind_test.go + +package graph + +// Defining the union-find data structure +type UnionFind struct { + parent []int + rank []int +} + +// Initialise a new union find data structure with s nodes +func NewUnionFind(s int) UnionFind { + parent := make([]int, s) + rank := make([]int, s) + for i := 0; i < s; i++ { + parent[i] = i + rank[i] = 1 + } + return UnionFind{parent, rank} +} + +// Find finds the root of the set to which the given element belongs. +// It performs path compression to make future Find operations faster. +func (u *UnionFind) Find(q int) int { + if q != u.parent[q] { + u.parent[q] = u.Find(u.parent[q]) + } + return u.parent[q] +} + +// Union merges the sets, if not already merged, to which the given elements belong. +// It performs union by rank to keep the tree as flat as possible. +func (u *UnionFind) Union(p, q int) { + rootP := u.Find(p) + rootQ := u.Find(q) + + if rootP == rootQ { + return + } + + if u.rank[rootP] < u.rank[rootQ] { + u.parent[rootP] = rootQ + } else if u.rank[rootP] > u.rank[rootQ] { + u.parent[rootQ] = rootP + } else { + u.parent[rootQ] = rootP + u.rank[rootP]++ + } +} diff --git a/graph/unionfind_test.go b/graph/unionfind_test.go new file mode 100644 index 000000000..35eea59d4 --- /dev/null +++ b/graph/unionfind_test.go @@ -0,0 +1,41 @@ +package graph + +import ( + "testing" +) + +func TestUnionFind(t *testing.T) { + u := NewUnionFind(10) // Creating a Union-Find data structure with 10 elements + + //union operations + u.Union(0, 1) + u.Union(2, 3) + u.Union(4, 5) + u.Union(6, 7) + + // Testing the parent of specific elements + t.Run("Test Find", func(t *testing.T) { + if u.Find(0) != u.Find(1) || u.Find(2) != u.Find(3) || u.Find(4) != u.Find(5) || u.Find(6) != u.Find(7) { + t.Error("Union operation not functioning correctly") + } + }) + + u.Union(1, 5) // Additional union operation + u.Union(3, 7) // Additional union operation + + // Testing the parent of specific elements after more union operations + t.Run("Test Find after Union", func(t *testing.T) { + if u.Find(0) != u.Find(5) || u.Find(1) != u.Find(4) || u.Find(2) != u.Find(7) || u.Find(3) != u.Find(6) { + t.Error("Union operation not functioning correctly") + } + }) + + u.Union(3, 7) // Repeated union operation + + // Testing that repeated union operations are idempotent + t.Run("Test Find after repeated Union", func(t *testing.T) { + if u.Find(2) != u.Find(6) || u.Find(2) != u.Find(7) || u.Find(3) != u.Find(6) || u.Find(3) != u.Find(7) { + t.Error("Union operation not functioning correctly") + } + }) +} diff --git a/hashing/md5/md5.go b/hashing/md5/md5.go new file mode 100644 index 000000000..5fad61ef4 --- /dev/null +++ b/hashing/md5/md5.go @@ -0,0 +1,118 @@ +// md5.go +// description: The MD5 hashing function as defined in RFC 1321. +// author: Simon Waldherr +// ref: https://datatracker.ietf.org/doc/html/rfc1321 +// see md5_test.go for testing + +package md5 + +import ( + "encoding/binary" +) + +// Constants for MD5 +var ( + s = [64]uint32{ + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, + } + + K = [64]uint32{ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, + } +) + +// leftRotate rotates x left by n bits +func leftRotate(x, n uint32) uint32 { + return (x << n) | (x >> (32 - n)) +} + +// pad pads the input message so that its length is congruent to 448 modulo 512 +func pad(message []byte) []byte { + originalLength := len(message) * 8 + message = append(message, 0x80) + for (len(message)*8)%512 != 448 { + message = append(message, 0x00) + } + + lengthBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(lengthBytes, uint64(originalLength)) + message = append(message, lengthBytes...) + + return message +} + +// Hash computes the MD5 hash of the input message +func Hash(message []byte) [16]byte { + message = pad(message) + + // Initialize MD5 state variables + a0, b0, c0, d0 := uint32(0x67452301), uint32(0xefcdab89), uint32(0x98badcfe), uint32(0x10325476) + + // Process the message in successive 512-bit chunks + for i := 0; i < len(message); i += 64 { + chunk := message[i : i+64] + var M [16]uint32 + for j := 0; j < 16; j++ { + M[j] = binary.LittleEndian.Uint32(chunk[j*4 : (j+1)*4]) + } + + // Initialize hash value for this chunk + A, B, C, D := a0, b0, c0, d0 + + // Main loop + for i := 0; i < 64; i++ { + var F, g uint32 + if i < 16 { + F = (B & C) | ((^B) & D) + g = uint32(i) + } else if i < 32 { + F = (D & B) | ((^D) & C) + g = uint32((5*i + 1) % 16) + } else if i < 48 { + F = B ^ C ^ D + g = uint32((3*i + 5) % 16) + } else { + F = C ^ (B | (^D)) + g = uint32((7 * i) % 16) + } + F = F + A + K[i] + M[g] + A = D + D = C + C = B + B = B + leftRotate(F, s[i]) + } + + // Add this chunk's hash to result so far + a0 += A + b0 += B + c0 += C + d0 += D + } + + // Produce the final hash value (digest) + var digest [16]byte + binary.LittleEndian.PutUint32(digest[0:4], a0) + binary.LittleEndian.PutUint32(digest[4:8], b0) + binary.LittleEndian.PutUint32(digest[8:12], c0) + binary.LittleEndian.PutUint32(digest[12:16], d0) + + return digest +} diff --git a/hashing/md5/md5_test.go b/hashing/md5/md5_test.go new file mode 100644 index 000000000..b013c0bb3 --- /dev/null +++ b/hashing/md5/md5_test.go @@ -0,0 +1,42 @@ +// md5_test.go +// description: Tests for the MD5 hashing function as defined in RFC 1321. +// author: Simon Waldherr + +package md5 + +import ( + "encoding/hex" + "testing" +) + +// Helper function to convert hash output to hex string for comparison +func toHexString(hash [16]byte) string { + return hex.EncodeToString(hash[:]) +} + +// Test vectors for MD5 (from RFC 1321 and other known sources) +var tests = []struct { + input string + expected string +}{ + {"", "d41d8cd98f00b204e9800998ecf8427e"}, + {"a", "0cc175b9c0f1b6a831c399e269772661"}, + {"abc", "900150983cd24fb0d6963f7d28e17f72"}, + {"message digest", "f96b697d7cb7938d525a2f31aaf161d0"}, + {"abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"}, + {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "d174ab98d277d9f5a5611c2c9f419d9f"}, + {"12345678901234567890123456789012345678901234567890123456789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a"}, +} + +// TestHash verifies that the Hash function produces the correct MD5 hash values +func TestHash(t *testing.T) { + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + result := Hash([]byte(tt.input)) + resultHex := toHexString(result) + if resultHex != tt.expected { + t.Errorf("MD5(%q) = %s; want %s", tt.input, resultHex, tt.expected) + } + }) + } +} diff --git a/hashing/sha1/sha1.go b/hashing/sha1/sha1.go new file mode 100644 index 000000000..ba30fe615 --- /dev/null +++ b/hashing/sha1/sha1.go @@ -0,0 +1,110 @@ +// sha1.go +// description: The SHA-1 hashing function as defined in RFC 3174. +// author: Simon Waldherr +// ref: https://datatracker.ietf.org/doc/html/rfc3174 +// see sha1_test.go for testing + +package sha1 + +import ( + "encoding/binary" // Used for interacting with uint at the byte level +) + +// Constants for SHA-1 +const ( + h0 uint32 = 0x67452301 + h1 uint32 = 0xEFCDAB89 + h2 uint32 = 0x98BADCFE + h3 uint32 = 0x10325476 + h4 uint32 = 0xC3D2E1F0 +) + +// pad pads the input message so that its length is congruent to 448 modulo 512 +func pad(message []byte) []byte { + originalLength := len(message) * 8 + message = append(message, 0x80) + for (len(message)*8)%512 != 448 { + message = append(message, 0x00) + } + + lengthBytes := make([]byte, 8) + binary.BigEndian.PutUint64(lengthBytes, uint64(originalLength)) + message = append(message, lengthBytes...) + + return message +} + +// leftRotate rotates x left by n bits +func leftRotate(x, n uint32) uint32 { + return (x << n) | (x >> (32 - n)) +} + +// Hash computes the SHA-1 hash of the input message +func Hash(message []byte) [20]byte { + message = pad(message) + + // Initialize variables + a, b, c, d, e := h0, h1, h2, h3, h4 + + // Process the message in successive 512-bit chunks + for i := 0; i < len(message); i += 64 { + var w [80]uint32 + chunk := message[i : i+64] + + // Break chunk into sixteen 32-bit big-endian words + for j := 0; j < 16; j++ { + w[j] = binary.BigEndian.Uint32(chunk[j*4 : (j+1)*4]) + } + + // Extend the sixteen 32-bit words into eighty 32-bit words + for j := 16; j < 80; j++ { + w[j] = leftRotate(w[j-3]^w[j-8]^w[j-14]^w[j-16], 1) + } + + // Initialize hash value for this chunk + A, B, C, D, E := a, b, c, d, e + + // Main loop + for j := 0; j < 80; j++ { + var f, k uint32 + switch { + case j < 20: + f = (B & C) | ((^B) & D) + k = 0x5A827999 + case j < 40: + f = B ^ C ^ D + k = 0x6ED9EBA1 + case j < 60: + f = (B & C) | (B & D) | (C & D) + k = 0x8F1BBCDC + default: + f = B ^ C ^ D + k = 0xCA62C1D6 + } + + temp := leftRotate(A, 5) + f + E + k + w[j] + E = D + D = C + C = leftRotate(B, 30) + B = A + A = temp + } + + // Add this chunk's hash to result so far + a += A + b += B + c += C + d += D + e += E + } + + // Produce the final hash value (digest) + var digest [20]byte + binary.BigEndian.PutUint32(digest[0:4], a) + binary.BigEndian.PutUint32(digest[4:8], b) + binary.BigEndian.PutUint32(digest[8:12], c) + binary.BigEndian.PutUint32(digest[12:16], d) + binary.BigEndian.PutUint32(digest[16:20], e) + + return digest +} diff --git a/hashing/sha1/sha1_test.go b/hashing/sha1/sha1_test.go new file mode 100644 index 000000000..f7d1d2079 --- /dev/null +++ b/hashing/sha1/sha1_test.go @@ -0,0 +1,42 @@ +// sha1_test.go +// description: Tests for the SHA-1 hashing function as defined in RFC 3174. +// author: Simon Waldherr + +package sha1 + +import ( + "encoding/hex" + "testing" +) + +// Helper function to convert hash output to hex string for comparison +func toHexString(hash [20]byte) string { + return hex.EncodeToString(hash[:]) +} + +// Test vectors for SHA-1 (from RFC 3174 and other known sources) +var tests = []struct { + input string + expected string +}{ + {"", "da39a3ee5e6b4b0d3255bfef95601890afd80709"}, + {"a", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"}, + {"abc", "a9993e364706816aba3e25717850c26c9cd0d89d"}, + {"message digest", "c12252ceda8be8994d5fa0290a47231c1d16aae3"}, + {"abcdefghijklmnopqrstuvwxyz", "32d10c7b8cf96570ca04ce37f2a19d84240d3a89"}, + {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "761c457bf73b14d27e9e9265c46f4b4dda11f940"}, + {"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", "fecfd28bbc9345891a66d7c1b8ff46e60192d284"}, +} + +// TestHash verifies that the Hash function produces the correct SHA-1 hash values +func TestHash(t *testing.T) { + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + result := Hash([]byte(tt.input)) + resultHex := toHexString(result) + if resultHex != tt.expected { + t.Errorf("SHA-1(%q) = %s; want %s", tt.input, resultHex, tt.expected) + } + }) + } +} diff --git a/hashing/sha256/sha256.go b/hashing/sha256/sha256.go index 9061f3801..0dd38a557 100644 --- a/hashing/sha256/sha256.go +++ b/hashing/sha256/sha256.go @@ -1,5 +1,7 @@ // sha256.go // description: The sha256 cryptographic hash function as defined in the RFC6234 standard. +// time complexity: O(n) +// space complexity: O(n) // author: [Paul Leydier] (https://github.com/paul-leydier) // ref: https://datatracker.ietf.org/doc/html/rfc6234 // ref: https://en.wikipedia.org/wiki/SHA-2 diff --git a/math/aliquotsum.go b/math/aliquotsum.go index 6e3af6cc0..cc6bd326a 100644 --- a/math/aliquotsum.go +++ b/math/aliquotsum.go @@ -5,6 +5,8 @@ // is the sum of all proper divisors of n, // that is, all divisors of n other than n itself // wikipedia: https://en.wikipedia.org/wiki/Aliquot_sum +// time complexity: O(n) +// space complexity: O(1) // author: Akshay Dubey (https://github.com/itsAkshayDubey) // see aliquotsum_test.go diff --git a/math/armstrong/isarmstrong.go b/math/armstrong/isarmstrong.go index 68055dee8..851b9612a 100644 --- a/math/armstrong/isarmstrong.go +++ b/math/armstrong/isarmstrong.go @@ -1,6 +1,8 @@ // isarmstrong.go // description: Checks if the given number is armstrong number or not // details: An Armstrong number is a n-digit number that is equal to the sum of each of its digits taken to the nth power. +// time complexity: O(log(n)) +// space complexity: O(1) // ref: https://mathlair.allfunandgames.ca/armstrong.php // author: Kavitha J diff --git a/math/binary/abs.go b/math/binary/abs.go index 9762c7c8a..5c95fa5e8 100644 --- a/math/binary/abs.go +++ b/math/binary/abs.go @@ -1,3 +1,8 @@ +// abs.go +// description: returns absolute value using binary operation +// time complexity: O(1) +// space complexity: O(1) + package binary // Abs returns absolute value using binary operation diff --git a/math/binary/arithmeticmean.go b/math/binary/arithmeticmean.go index 9fa56560e..f7812b6be 100644 --- a/math/binary/arithmeticmean.go +++ b/math/binary/arithmeticmean.go @@ -2,6 +2,8 @@ // description: Arithmetic mean // details: // The most common type of average is the arithmetic mean. If n numbers are given, each number denoted by ai (where i = 1,2, ..., n), the arithmetic mean is the sum of the as divided by n or - [Arithmetic mean](https://en.wikipedia.org/wiki/Average#Arithmetic_mean) +// time complexity: O(1) +// space complexity: O(1) // author(s) [red_byte](https://github.com/i-redbyte) // see arithmeticmean_test.go diff --git a/math/binary/bitcounter.go b/math/binary/bitcounter.go index 33e24256d..baf8dcd7b 100644 --- a/math/binary/bitcounter.go +++ b/math/binary/bitcounter.go @@ -2,6 +2,8 @@ // description: Counts the number of set bits in a number // details: // For unsigned integer number N, return the number of bits set to 1 - [Bit numbering](https://en.wikipedia.org/wiki/Bit_numbering) +// time complexity: O(log(n)) +// space complexity: O(1) // author(s) [red_byte](https://github.com/i-redbyte) // see bitcounter_test.go diff --git a/math/binary/checkisnumberpoweroftwo.go b/math/binary/checkisnumberpoweroftwo.go index 29c023e5a..d836396f5 100644 --- a/math/binary/checkisnumberpoweroftwo.go +++ b/math/binary/checkisnumberpoweroftwo.go @@ -2,6 +2,8 @@ // description: Is the number a power of two // details: // Checks if a number is a power of two- [Power of two](https://en.wikipedia.org/wiki/Power_of_two) +// time complexity: O(1) +// space complexity: O(1) // author(s) [red_byte](https://github.com/i-redbyte) // see checkisnumberpoweroftwo_test.go diff --git a/math/binary/fast_inverse_sqrt.go b/math/binary/fast_inverse_sqrt.go new file mode 100644 index 000000000..c8689b451 --- /dev/null +++ b/math/binary/fast_inverse_sqrt.go @@ -0,0 +1,31 @@ +// Calculating the inverse square root +// time complexity: O(1) +// space complexity: O(1) +// [See more](https://en.wikipedia.org/wiki/Fast_inverse_square_root) + +package binary + +import ( + "math" +) + +// FastInverseSqrt assumes that argument is always positive, +// and it does not deal with negative numbers. +// The "magic" number 0x5f3759df is hex for 1597463007 in decimals. +// The math.Float32bits is alias to *(*uint32)(unsafe.Pointer(&f)) +// and math.Float32frombits is to *(*float32)(unsafe.Pointer(&b)). +func FastInverseSqrt(number float32) float32 { + var i uint32 + var y, x2 float32 + const threehalfs float32 = 1.5 + + x2 = number * float32(0.5) + y = number + i = math.Float32bits(y) // evil floating point bit level hacking + i = 0x5f3759df - (i >> 1) // magic number and bitshift hacking + y = math.Float32frombits(i) + + y = y * (threehalfs - (x2 * y * y)) // 1st iteration of Newton's method + y = y * (threehalfs - (x2 * y * y)) // 2nd iteration, this can be removed + return y +} diff --git a/math/binary/logarithm.go b/math/binary/logarithm.go index 205b0235b..3d73ac428 100644 --- a/math/binary/logarithm.go +++ b/math/binary/logarithm.go @@ -1,4 +1,6 @@ // author(s) [red_byte](https://github.com/i-redbyte) +// time complexity: O(1) +// space complexity: O(1) // see logarithm_test.go package binary diff --git a/math/binary/rbc.go b/math/binary/rbc.go index c0a6e78f1..b6ef45f68 100644 --- a/math/binary/rbc.go +++ b/math/binary/rbc.go @@ -2,6 +2,8 @@ // description: Reflected binary code (RBC) // details: // The reflected binary code (RBC), also known just as reflected binary (RB) or Gray code after Frank Gray, is an ordering of the binary numeral system such that two successive values differ in only one bit (binary digit). - [RBC](https://en.wikipedia.org/wiki/Gray_code) +// time complexity: O(n) +// space complexity: O(n) // author(s) [red_byte](https://github.com/i-redbyte) // see rbc_test.go diff --git a/math/binary/reversebits.go b/math/binary/reversebits.go index f4367cc94..751fbcfd5 100644 --- a/math/binary/reversebits.go +++ b/math/binary/reversebits.go @@ -2,6 +2,8 @@ // description: Reverse bits // details: // Reverse the bits of an integer number +// time complexity: O(log(n)) +// space complexity: O(1) // author(s) [red_byte](https://github.com/i-redbyte) // see reversebits_test.go diff --git a/math/binary/sqrt.go b/math/binary/sqrt.go index 111dd94a9..8c967bf91 100644 --- a/math/binary/sqrt.go +++ b/math/binary/sqrt.go @@ -3,22 +3,10 @@ // details: // Calculating the square root using binary operations and a magic number 0x5f3759df [See more](https://en.wikipedia.org/wiki/Fast_inverse_square_root) // author(s) [red_byte](https://github.com/i-redbyte) +// time complexity: O(1) +// space complexity: O(1) // see sqrt_test.go package binary -import ( - "math" -) - -const threeHalves = 1.5 - -func Sqrt(n float32) float32 { - var half, y float32 - half = n * 0.5 - z := math.Float32bits(n) - z = 0x5f3759df - (z >> 1) // floating point bit level hacking - y = math.Float32frombits(z) - y = y * (threeHalves - (half * y * y)) // Newton's approximation - return 1 / y -} +func Sqrt(n float32) float32 { return 1 / FastInverseSqrt(n) } diff --git a/math/binary/sqrt_test.go b/math/binary/sqrt_test.go index 426794fd2..fe31aefac 100644 --- a/math/binary/sqrt_test.go +++ b/math/binary/sqrt_test.go @@ -10,7 +10,7 @@ import ( "testing" ) -const epsilon = 0.2 +const epsilon = 0.001 func TestSquareRootCalculation(t *testing.T) { tests := []struct { diff --git a/math/binary/xorsearch.go b/math/binary/xorsearch.go index b16b7846b..9e914da83 100644 --- a/math/binary/xorsearch.go +++ b/math/binary/xorsearch.go @@ -2,6 +2,8 @@ // description: Find a missing number in a sequence // details: // Given an array A containing n distinct numbers in the range [0, n], return the only number in the range that is missing from the array. - [xor](https://en.wikipedia.org/wiki/Exclusive_or) +// time complexity: O(n) +// space complexity: O(1) // author(s) [red_byte](https://github.com/i-redbyte) // see xorsearch_test.go diff --git a/math/binomialcoefficient.go b/math/binomialcoefficient.go index d3c123a6f..614f4b7db 100644 --- a/math/binomialcoefficient.go +++ b/math/binomialcoefficient.go @@ -4,6 +4,8 @@ // a binomial coefficient C(n,k) gives number ways // in which k objects can be chosen from n objects. // wikipedia: https://en.wikipedia.org/wiki/Binomial_coefficient +// time complexity: O(k) or O(n-k) whichever is smaller (O(n) in worst case) +// space complexity: O(1) // author: Akshay Dubey (https://github.com/itsAkshayDubey) // see binomialcoefficient_test.go diff --git a/math/catalan/catalannumber.go b/math/catalan/catalannumber.go index 31a424347..5f81cb9f4 100644 --- a/math/catalan/catalannumber.go +++ b/math/catalan/catalannumber.go @@ -2,6 +2,8 @@ // description: Returns the Catalan number // details: // In combinatorial mathematics, the Catalan numbers are a sequence of natural numbers that occur in various counting problems, often involving recursively defined objects. - [Catalan number](https://en.wikipedia.org/wiki/Catalan_number) +// time complexity: O(n) +// space complexity: O(1) // The input is the number of the Catalan number n, at the output we get the value of the number // author(s) [red_byte](https://github.com/i-redbyte) // see catalannumber_test.go @@ -12,7 +14,15 @@ import ( f "github.com/TheAlgorithms/Go/math/factorial" ) +func factorial(n int) int { + result, error := f.Iterative(n) + if error != nil { + panic(error) + } + return result +} + // CatalanNumber This function returns the `nth` Catalan number func CatalanNumber(n int) int { - return f.Iterative(n*2) / (f.Iterative(n) * f.Iterative(n+1)) + return factorial(n*2) / (factorial(n) * factorial(n+1)) } diff --git a/math/factorial/factorial.go b/math/factorial/factorial.go index 6b7f5f516..6703e6c75 100644 --- a/math/factorial/factorial.go +++ b/math/factorial/factorial.go @@ -2,42 +2,56 @@ // description: Calculating factorial // details: // The factorial of a non-negative integer n, denoted by n!, is the product of all positive integers less than or equal to n - [Factorial](https://en.wikipedia.org/wiki/Factorial) +// time complexity: O(n) +// space complexity: O(1) // author(s) [red_byte](https://github.com/i-redbyte) // see factorial_test.go // Package factorial describes algorithms Factorials calculations. package factorial +import ( + "errors" +) + +var ErrNegativeArgument = errors.New("input argument must be non-negative integer") + // Iterative returns the iteratively brute forced factorial of n -func Iterative(n int) int { +func Iterative(n int) (int, error) { + if n < 0 { + return 0, ErrNegativeArgument + } result := 1 for i := 2; i <= n; i++ { result *= i } - return result + return result, nil } // Recursive This function recursively computes the factorial of a number -func Recursive(n int) int { - if n == 1 { - return 1 - } else { - return n * Recursive(n-1) +func Recursive(n int) (int, error) { + if n < 0 { + return 0, ErrNegativeArgument } + if n <= 1 { + return 1, nil + } + prev, _ := Recursive(n - 1) + return n * prev, nil } // UsingTree This function finds the factorial of a number using a binary tree -func UsingTree(n int) int { +func UsingTree(n int) (int, error) { if n < 0 { - return 0 + return 0, ErrNegativeArgument } if n == 0 { - return 1 + return 1, nil } if n == 1 || n == 2 { - return n + return n, nil } - return prodTree(2, n) + return prodTree(2, n), nil } func prodTree(l int, r int) int { diff --git a/math/factorial/factorial_test.go b/math/factorial/factorial_test.go index 04eceb973..1d3ed727d 100644 --- a/math/factorial/factorial_test.go +++ b/math/factorial/factorial_test.go @@ -1,81 +1,69 @@ // factorial_test.go // description: Test for calculating factorial -// author(s) [red_byte](https://github.com/i-redbyte) // see factorial.go package factorial import "testing" +import "fmt" -func getTests() []struct { - name string - n int - result int -} { - var tests = []struct { - name string - n int - result int - }{ - {"5!", 5, 120}, - {"3!", 3, 6}, - {"6!", 6, 720}, - {"12!", 12, 479001600}, - {"1!", 1, 1}, - } - return tests -} +type factorialFun func(int) (int, error) -func TestBruteForceFactorial(t *testing.T) { - tests := getTests() - for _, tv := range tests { - t.Run(tv.name, func(t *testing.T) { - result := Iterative(tv.n) - if result != tv.result { - t.Errorf("Wrong result! Expected:%d, returned:%d ", tv.result, result) - } - }) - } +var implementations = map[string]factorialFun{ + "Iterative": Iterative, + "Recursive": Recursive, + "UsingTree": UsingTree, } -func TestRecursiveFactorial(t *testing.T) { - tests := getTests() - for _, tv := range tests { - t.Run(tv.name, func(t *testing.T) { - result := Recursive(tv.n) - if result != tv.result { - t.Errorf("Wrong result! Expected:%d, returned:%d ", tv.result, result) - } - }) - } +var testCases = []struct { + n int + expected int +}{ + {0, 1}, + {1, 1}, + {2, 2}, + {3, 6}, + {4, 24}, + {5, 120}, + {6, 720}, + {7, 5040}, + {8, 40320}, + {9, 362880}, + {10, 3628800}, + {11, 39916800}, + {12, 479001600}, } -func TestCalculateFactorialUseTree(t *testing.T) { - tests := getTests() - for _, tv := range tests { - t.Run(tv.name, func(t *testing.T) { - result := UsingTree(tv.n) - if result != tv.result { - t.Errorf("Wrong result! Expected:%d, returned:%d ", tv.result, result) +func TestFactorial(t *testing.T) { + for implName, implFunction := range implementations { + t.Run(implName+" errors for negative input", func(t *testing.T) { + _, error := implFunction(-1) + if error != ErrNegativeArgument { + t.Errorf("No error captured for negative input") } }) + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s with input %d", implName, tc.n), func(t *testing.T) { + actual, err := implFunction(tc.n) + if err != nil { + t.Errorf("unexpected error captured") + } + if actual != tc.expected { + t.Errorf("Expected: %d, got: %d", tc.expected, actual) + } + }) + } } } -func BenchmarkBruteForceFactorial(b *testing.B) { - for i := 0; i < b.N; i++ { - Iterative(10) - } -} - -func BenchmarkRecursiveFactorial(b *testing.B) { - for i := 0; i < b.N; i++ { - Recursive(10) - } -} - -func BenchmarkCalculateFactorialUseTree(b *testing.B) { - for i := 0; i < b.N; i++ { - Recursive(10) +func BenchmarkFactorial(b *testing.B) { + for _, input := range []int{5, 10, 15} { + for implName, implFunction := range implementations { + b.Run(fmt.Sprintf("%s_%d", implName, input), func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = implFunction(input) + } + }) + } } } diff --git a/math/fibonacci/fibonacci.go b/math/fibonacci/fibonacci.go index 9718acbbc..c2fd1b2e2 100644 --- a/math/fibonacci/fibonacci.go +++ b/math/fibonacci/fibonacci.go @@ -2,6 +2,8 @@ // description: Get the nth Fibonacci Number // details: // In mathematics, the Fibonacci numbers, commonly denoted Fn, form a sequence, called the Fibonacci sequence, such that each number is the sum of the two preceding ones, starting from 0 and 1. [Fibonacci number](https://en.wikipedia.org/wiki/Fibonacci_number) +// time complexity: O(log n) +// space complexity: O(1) // author(s) [red_byte](https://github.com/i-redbyte) // see fibonacci_test.go @@ -45,3 +47,13 @@ func Formula(n uint) uint { powPhi := math.Pow(phi, float64(n)) return uint(powPhi/sqrt5 + 0.5) } + +// Recursive calculates the n-th fibonacci number recursively by adding the previous two Fibonacci numbers. +// This algorithm is extremely slow for bigger numbers, but provides a simpler implementation. +func Recursive(n uint) uint { + if n <= 1 { + return n + } + + return Recursive(n-1) + Recursive(n-2) +} diff --git a/math/fibonacci/fibonacci_test.go b/math/fibonacci/fibonacci_test.go index 990930251..bfdbed0bc 100644 --- a/math/fibonacci/fibonacci_test.go +++ b/math/fibonacci/fibonacci_test.go @@ -64,6 +64,19 @@ func TestFormula(t *testing.T) { } } +func TestRecursive(t *testing.T) { + tests := getTests() + for _, test := range tests { + if test.n <= 10 { + t.Run(test.name, func(t *testing.T) { + if got := Recursive(test.n); got != test.want { + t.Errorf("Return value = %v, want %v", got, test.want) + } + }) + } + } +} + func BenchmarkNthFibonacci(b *testing.B) { for i := 0; i < b.N; i++ { dynamic.NthFibonacci(90) diff --git a/math/gcd/extended.go b/math/gcd/extended.go index eea32e9e9..229630141 100644 --- a/math/gcd/extended.go +++ b/math/gcd/extended.go @@ -3,6 +3,8 @@ // details: // A simple implementation of Extended GCD algorithm, that returns GCD, a and b // which solves ax + by = gcd(a, b) +// time complexity: O(log(min(a, b))) where a and b are the two numbers +// space complexity: O(log(min(a, b))) where a and b are the two numbers // author(s) [Taj](https://github.com/tjgurwara99) // see extended_test.go diff --git a/math/gcd/extendedgcd.go b/math/gcd/extendedgcd.go index 004a91ead..0f5a750dc 100644 --- a/math/gcd/extendedgcd.go +++ b/math/gcd/extendedgcd.go @@ -1,3 +1,8 @@ +// extendedgcd.go +// description: Implementation of Extended GCD Algorithm +// time complexity: O(log(min(a, b))) where a and b are the two numbers +// space complexity: O(log(min(a, b))) where a and b are the two numbers + package gcd // ExtendedRecursive finds and returns gcd(a, b), x, y satisfying a*x + b*y = gcd(a, b). diff --git a/math/gcd/gcd.go b/math/gcd/gcd.go index e76e8396f..2045d0901 100644 --- a/math/gcd/gcd.go +++ b/math/gcd/gcd.go @@ -1,3 +1,6 @@ +// time complexity: O(log(min(a, b))) where a and b are the two numbers +// space complexity: O(1) + package gcd // Recursive finds and returns the greatest common divisor of a given integer. diff --git a/math/gcd/gcditerative.go b/math/gcd/gcditerative.go index e87dfce6f..96c217cfa 100644 --- a/math/gcd/gcditerative.go +++ b/math/gcd/gcditerative.go @@ -1,3 +1,6 @@ +// time complexity: O(log(min(a, b))) where a and b are the two numbers +// space complexity: O(1) + package gcd // Iterative Faster iterative version of GcdRecursive without holding up too much of the stack diff --git a/math/geometry/distance.go b/math/geometry/distance.go index 5b1c3c82e..0baf64562 100644 --- a/math/geometry/distance.go +++ b/math/geometry/distance.go @@ -1,5 +1,7 @@ // distance.go // Find Euclidean distance between two points +// time complexity: O(n) where n is the number of dimensions +// space complexity: O(1) // author(s) [Chetan Patil](https://github.com/Chetan07j) // Package geometry contains geometric algorithms diff --git a/math/isautomorphic.go b/math/isautomorphic.go new file mode 100644 index 000000000..855d224a1 --- /dev/null +++ b/math/isautomorphic.go @@ -0,0 +1,32 @@ +// isautomorphic.go +// description: Checks whether a whole number integer is Automorphic or not. If number < 0 then returns false. +// details: +// In mathematics, a number n is said to be a Automorphic number if the square of n ends in the same digits as n itself. +// ref: (https://en.wikipedia.org/wiki/Automorphic_number) +// time complexity: O(log10(N)) +// space complexity: O(1) +// author: [SilverDragonOfR](https://github.com/SilverDragonOfR) + +package math + +import ( + "github.com/TheAlgorithms/Go/constraints" +) + +func IsAutomorphic[T constraints.Integer](n T) bool { + // handling the negetive number case + if n < 0 { + return false + } + + n_sq := n * n + for n > 0 { + if (n % 10) != (n_sq % 10) { + return false + } + n /= 10 + n_sq /= 10 + } + + return true +} diff --git a/math/isautomorphic_test.go b/math/isautomorphic_test.go new file mode 100644 index 000000000..d8494b921 --- /dev/null +++ b/math/isautomorphic_test.go @@ -0,0 +1,58 @@ +package math + +import ( + "testing" +) + +var testCases = []struct { + name string + input int + expected bool +}{ + { + "negetive number: not Automorphic", + -1, + false, + }, + { + "negetive number: not Automorphic", + -146, + false, + }, + { + "0: is Automorphic", + 0, + true, + }, + { + "1: is Automorphic", + 1, + true, + }, + { + "7: not Automorphic", + 7, + false, + }, + { + "83: not Automorphic", + 83, + false, + }, + { + "376: is Automorphic", + 376, + true, + }, +} + +func TestIsAutomorphic(t *testing.T) { + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + funcResult := IsAutomorphic(test.input) + if test.expected != funcResult { + t.Errorf("Expected answer '%t' for the number '%d' but answer given was %t", test.expected, test.input, funcResult) + } + }) + } +} diff --git a/math/krishnamurthy.go b/math/krishnamurthy.go index 641dfbf92..63bb6dbf6 100644 --- a/math/krishnamurthy.go +++ b/math/krishnamurthy.go @@ -2,6 +2,8 @@ // description: A program which contains the function that returns true if a given number is Krishnamurthy number or not. // details: A number is a Krishnamurthy number if the sum of all the factorials of the digits is equal to the number. // Ex: 1! = 1, 145 = 1! + 4! + 5! +// time complexity: O(log n) +// space complexity: O(1) // author(s): [GooMonk](https://github.com/GooMonk) // see krishnamurthy_test.go package math diff --git a/math/liouville.go b/math/liouville.go index 908a4deac..55d2c95f4 100644 --- a/math/liouville.go +++ b/math/liouville.go @@ -6,6 +6,8 @@ // λ(n) = +1 if n is a positive integer with an even number of prime factors. // λ(n) = −1 if n is a positive integer with an odd number of prime factors. // wikipedia: https://en.wikipedia.org/wiki/Liouville_function +// time complexity: O(log n) +// space complexity: O(1) // author: Akshay Dubey (https://github.com/itsAkshayDubey) // see liouville_test.go diff --git a/math/matrix/add.go b/math/matrix/add.go new file mode 100644 index 000000000..d5d79efdd --- /dev/null +++ b/math/matrix/add.go @@ -0,0 +1,69 @@ +// add.go +// description: Add two matrices +// time complexity: O(n^2) +// space complexity: O(n^2) + +package matrix + +import ( + "context" + "errors" + "sync" +) + +// Add adds two matrices. +func (m1 Matrix[T]) Add(m2 Matrix[T]) (Matrix[T], error) { + // Check if the matrices have the same dimensions. + if !m1.MatchDimensions(m2) { + return Matrix[T]{}, errors.New("matrices are not compatible for addition") + } + + // Create a new matrix to store the result. + var zeroVal T + result := New(m1.Rows(), m1.Columns(), zeroVal) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() // Make sure it's called to release resources even if no errors + + var wg sync.WaitGroup + errCh := make(chan error, 1) + + for i := 0; i < m1.rows; i++ { + i := i // Capture the loop variable for the goroutine + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < m1.columns; j++ { + select { + case <-ctx.Done(): + return // Context canceled; return without an error + default: + } + + sum := m1.elements[i][j] + m2.elements[i][j] + err := result.Set(i, j, sum) + if err != nil { + cancel() // Cancel the context on error + select { + case errCh <- err: + default: + } + return + } + } + }() + } + + // Wait for all goroutines to finish + go func() { + wg.Wait() + close(errCh) + }() + + // Check for any errors + if err := <-errCh; err != nil { + return Matrix[T]{}, err + } + + return result, nil +} diff --git a/math/matrix/add_test.go b/math/matrix/add_test.go new file mode 100644 index 000000000..f3543965c --- /dev/null +++ b/math/matrix/add_test.go @@ -0,0 +1,58 @@ +package matrix_test + +import ( + "fmt" + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestAdd(t *testing.T) { + // Create two matrices with the same dimensions for addition + m1 := matrix.New(2, 2, 1) + m2 := matrix.New(2, 2, 2) + + // Test case 1: Valid matrix addition + addedMatrix, err := m1.Add(m2) + if err != nil { + t.Errorf("Add(m1, m2) returned an error: %v, expected no error", err) + } + expectedMatrix := matrix.New(2, 2, 3) + res := addedMatrix.CheckEqual(expectedMatrix) + if !res { + t.Errorf("Add(m1, m2) returned incorrect result:\n%v\nExpected:\n%v", addedMatrix, expectedMatrix) + } + + // Create two matrices with different dimensions for addition + m3 := matrix.New(2, 2, 1) + m4 := matrix.New(2, 3, 2) + + // Test case 2: Matrices with different dimensions + _, err2 := m3.Add(m4) + expectedError2 := fmt.Errorf("matrices are not compatible for addition") + if err2 == nil || err2.Error() != expectedError2.Error() { + t.Errorf("Add(m3, m4) returned error: %v, expected error: %v", err2, expectedError2) + } + +} + +func BenchmarkAddSmallMatrix(b *testing.B) { + m1 := matrix.New(10, 10, 0) // Create a 10x10 matrix with all zeros + m2 := matrix.New(10, 10, 1) // Create a 10x10 matrix with all ones + + for i := 0; i < b.N; i++ { + _, _ = m1.Add(m2) + } +} + +func BenchmarkAddLargeMatrix(b *testing.B) { + size := 1000 // Choose an appropriate size for your large matrix + m1 := MakeRandomMatrix[int](size, size) + m2 := MakeRandomMatrix[int](size, size) + + b.ResetTimer() // Reset the timer to exclude setup time + + for i := 0; i < b.N; i++ { + _, _ = m1.Add(m2) + } +} diff --git a/math/matrix/checkequal.go b/math/matrix/checkequal.go new file mode 100644 index 000000000..005c657af --- /dev/null +++ b/math/matrix/checkequal.go @@ -0,0 +1,35 @@ +package matrix + +// CheckEqual checks if the current matrix is equal to another matrix (m2). +// Two matrices are considered equal if they have the same dimensions and +// all their elements are equal. +// time complexity: O(n*m) where n and m are the dimensions of the matrix +// space complexity: O(1) + +func (m1 Matrix[T]) CheckEqual(m2 Matrix[T]) bool { + if !m1.MatchDimensions(m2) { + return false + } + + c := make(chan bool) + + for i := range m1.elements { + go func(i int) { + for j := range m1.elements[i] { + if m1.elements[i][j] != m2.elements[i][j] { + c <- false + return + } + } + c <- true + }(i) + } + + for range m1.elements { + if !<-c { + return false + } + } + + return true +} diff --git a/math/matrix/checkequal_test.go b/math/matrix/checkequal_test.go new file mode 100644 index 000000000..be93d82d7 --- /dev/null +++ b/math/matrix/checkequal_test.go @@ -0,0 +1,62 @@ +package matrix_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestCheckEqual(t *testing.T) { + // Create two matrices with the same dimensions and equal values + m1 := matrix.New(2, 2, 0) + m2 := matrix.New(2, 2, 0) + + // Test case 1: Matrices are equal + equal := m1.CheckEqual(m2) + + if !equal { + t.Errorf("CheckEqual(m1, m2) returned false, expected true (matrices are equal)") + } + + // Create two matrices with the same dimensions but different values + m3 := matrix.New(2, 2, 1) + m4 := matrix.New(2, 2, 0) + + // Test case 2: Matrices are not equal + equal2 := m3.CheckEqual(m4) + if equal2 { + t.Errorf("CheckEqual(m3, m4) returned true, expected false (matrices are not equal)") + } + + // Create two matrices with different dimensions + m5 := matrix.New(2, 2, 0) + m6 := matrix.New(2, 3, 0) + + // Test case 3: Matrices have different dimensions + equal3 := m5.CheckEqual(m6) + + if equal3 { + t.Errorf("CheckEqual(m5, m6) returned true, expected false (matrices are not equal)") + } +} + +func BenchmarkCheckEqualSmallMatrix(b *testing.B) { + m1 := matrix.New(10, 10, 0) // Create a 10x10 matrix with all zeros + m2 := matrix.New(10, 10, 0) // Create another 10x10 matrix with all zeros + + for i := 0; i < b.N; i++ { + _ = m1.CheckEqual(m2) + } +} + +func BenchmarkCheckEqualLargeMatrix(b *testing.B) { + size := 1000 // Choose an appropriate size for your large matrix + m1 := MakeRandomMatrix[int](size, size) + m2 := MakeRandomMatrix[int](size, size) + + b.ResetTimer() // Reset the timer to exclude setup time + + for i := 0; i < b.N; i++ { + _ = m1.CheckEqual(m2) + } +} diff --git a/math/matrix/copy.go b/math/matrix/copy.go new file mode 100644 index 000000000..11afb9237 --- /dev/null +++ b/math/matrix/copy.go @@ -0,0 +1,59 @@ +// copy.go +// description: Copy a matrix +// details: This function creates a new matrix with the same dimensions as the original matrix and copies all the elements from the original matrix to the new matrix. +// time complexity: O(n*m) where n and m are the dimensions of the matrix +// space complexity: O(n*m) where n and m are the dimensions of the matrix + +package matrix + +import "sync" + +func (m Matrix[T]) Copy() (Matrix[T], error) { + + rows := m.Rows() + columns := m.Columns() + if rows == 0 || columns == 0 { + return Matrix[T]{}, nil + } + zeroVal, err := m.Get(0, 0) // Get the zero value of the element type + if err != nil { + return Matrix[T]{}, err + } + copyMatrix := New(rows, columns, zeroVal) + var wg sync.WaitGroup + wg.Add(rows) + errChan := make(chan error, 1) + + for i := 0; i < rows; i++ { + go func(i int) { + defer wg.Done() + for j := 0; j < columns; j++ { + val, err := m.Get(i, j) + if err != nil { + select { + case errChan <- err: + default: + } + return + } + err = copyMatrix.Set(i, j, val) + if err != nil { + select { + case errChan <- err: + default: + } + return + } + } + }(i) + } + + wg.Wait() + close(errChan) + + if err, ok := <-errChan; ok { + return Matrix[T]{}, err + } + + return copyMatrix, nil +} diff --git a/math/matrix/copy_test.go b/math/matrix/copy_test.go new file mode 100644 index 000000000..027902843 --- /dev/null +++ b/math/matrix/copy_test.go @@ -0,0 +1,115 @@ +package matrix_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestMatrixCopy(t *testing.T) { + // Create a sample matrix + data := [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} + // Ensure that the copy is not the same as the original + matrix, err := matrix.NewFromElements(data) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + copyMatrix, err := matrix.Copy() + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + + // Ensure that the copy is not the same as the original + if &matrix == ©Matrix { + t.Errorf("Copy did not create a new matrix.") + } + + for i := 0; i < matrix.Rows(); i++ { + for j := 0; j < matrix.Columns(); j++ { + val1, err := matrix.Get(i, j) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + val2, err := copyMatrix.Get(i, j) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + if val1 != val2 { + t.Errorf("Copy did not correctly copy element (%d, %d).", i, j) + } + } + } + +} + +func TestMatrixCopyEmpty(t *testing.T) { + // Create an empty matrix + emptyMatrix := matrix.New(0, 0, 0) + + // Make a copy of the empty matrix + copyMatrix, err := emptyMatrix.Copy() + if err != nil { // as empty matrix + t.Fatalf("Failed to copy matrix: %v", err) + } + + // Ensure that the copy is not the same as the original by comparing their addresses + if &emptyMatrix == ©Matrix { + t.Errorf("Copy did not create a new matrix for an empty matrix.") + } + + // Check if the copy is also empty + if copyMatrix.Rows() != 0 || copyMatrix.Columns() != 0 { + t.Errorf("Copy of an empty matrix should also be empty.") + } +} + +func TestMatrixCopyWithDefaultValues(t *testing.T) { + // Create a matrix with default values (zeroes) + rows, columns := 3, 3 + defaultValue := 0 + defaultMatrix := matrix.New(rows, columns, defaultValue) + + // Make a copy of the matrix + copyMatrix, err := defaultMatrix.Copy() + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + + // Ensure that the copy is not the same as the original by comparing their addresses + if &defaultMatrix == ©Matrix { + t.Errorf("Copy did not create a new matrix for default values.") + } + + // Check if the copy has the same values as the original (all zeroes) + for i := 0; i < defaultMatrix.Rows(); i++ { + for j := 0; j < defaultMatrix.Columns(); j++ { + val1, err := defaultMatrix.Get(i, j) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + val2, err := copyMatrix.Get(i, j) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + if val1 != val2 || val1 != defaultValue || val2 != defaultValue { + t.Errorf("Copy did not preserve default values at row %d, column %d. Expected %v, got %v", i, j, defaultValue, val2) + } + } + } +} + +func BenchmarkCopyMatrix(b *testing.B) { + // Create a matrix for benchmarking + rows := 100 + columns := 100 + initialValue := 0 + matrix := matrix.New(rows, columns, initialValue) + + // Reset the benchmark timer + b.ResetTimer() + + // Run the benchmarks + for i := 0; i < b.N; i++ { + _, _ = matrix.Copy() + } +} diff --git a/math/matrix/determinant.go b/math/matrix/determinant.go new file mode 100644 index 000000000..371b1fa0a --- /dev/null +++ b/math/matrix/determinant.go @@ -0,0 +1,61 @@ +// determinant.go +// description: This method finds the determinant of a matrix. +// details: For a theoretical explanation as for what the determinant +// represents, see the [Wikipedia Article](https://en.wikipedia.org/wiki/Determinant) +// time complexity: O(n!) where n is the number of rows and columns in the matrix. +// space complexity: O(n^2) where n is the number of rows and columns in the matrix. +// author [Carter907](https://github.com/Carter907) +// see determinant_test.go + +package matrix + +import ( + "errors" +) + +// Calculates the determinant of the matrix. +// This method only works for square matrices (e.i. matrices with equal rows and columns). +func (mat Matrix[T]) Determinant() (T, error) { + + var determinant T = 0 + var elements = mat.elements + if mat.rows != mat.columns { + + return 0, errors.New("Matrix rows and columns must equal in order to find the determinant.") + } + + // Specify base cases for different sized matrices. + switch mat.rows { + case 1: + return elements[0][0], nil + case 2: + return elements[0][0]*elements[1][1] - elements[1][0]*elements[0][1], nil + default: + for i := 0; i < mat.rows; i++ { + + var initialValue T = 0 + minor := New(mat.rows-1, mat.columns-1, initialValue) + // Fill the contents of minor excluding the 0th row and the ith column. + for j, minor_i := 1, 0; j < mat.rows && minor_i < minor.rows; j, minor_i = j+1, minor_i+1 { + for k, minor_j := 0, 0; k < mat.rows && minor_j < minor.rows; k, minor_j = k+1, minor_j+1 { + if k != i { + minor.elements[minor_i][minor_j] = elements[j][k] + } else { + minor_j-- // Decrement the column of minor to account for skipping the ith column of the matrix. + } + } + } + + if i%2 == 0 { + minor_det, _ := minor.Determinant() + + determinant += elements[0][i] * minor_det + } else { + minor_det, _ := minor.Determinant() + + determinant += elements[0][i] * minor_det + } + } + return determinant, nil + } +} diff --git a/math/matrix/determinant_test.go b/math/matrix/determinant_test.go new file mode 100644 index 000000000..801058ff2 --- /dev/null +++ b/math/matrix/determinant_test.go @@ -0,0 +1,142 @@ +package matrix_test + +import ( + "errors" + "math" + "math/rand" + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +// Test different matrix contents +func TestMatrixDeterminant(t *testing.T) { + // Find Determinant of a 2 by 2 matrix. + matrix1, err := matrix.NewFromElements([][]int{ + {3, 8}, + {4, 6}, + }) + if err != nil { + t.Fatalf("Error creating 3 by 3 matrix: %v", err) + } + determinant, err := matrix1.Determinant() + if err != nil { + t.Fatalf("Error returned from 3 by 3 matrix: %v", err) + } + if determinant != -14 { + t.Fatalf("Determinant returned for a 3 by 3 matrix was %d; wanted -14", determinant) + } + + // Find Dertminant of a 1 by 1 matrix + expectedValue := rand.Intn(math.MaxInt) + matrix2, err := matrix.NewFromElements([][]int{ + {expectedValue}, + }) + if err != nil { + t.Fatalf("Error creating 1 by 1 matrix: %v", err) + } + determinant, err = matrix2.Determinant() + if err != nil { + t.Fatalf("Error returned from 1 by 1 matrix: %v", err) + } + if determinant != expectedValue { + t.Fatalf("Determinant returned for a 1 by 1 matrix was %d; wanted %d", determinant, expectedValue) + } + +} + +func TestEmptyMatrix(t *testing.T) { + emptyElements := [][]int{} + matrix, err := matrix.NewFromElements(emptyElements) + + if err != nil { + t.Fatalf("Error creating Matrix with empty elements: %v", err) + } + + determinant, err := matrix.Determinant() + + if err != nil { + t.Fatalf("Determinant returned an error for empty matrix: %v", err) + } + + // Check that 0 is returned from an empty matrix. + expectedValue := 0 + if determinant != expectedValue { + t.Errorf("Determinant returned from empty matrix was %d; wanted %d", determinant, expectedValue) + } + +} + +func TestNonSquareMatrix(t *testing.T) { + // Creating non-square matrix for testing. + initialValue := 0 + initialRows := 4 + initialCols := 2 + + nonSquareMatrix := matrix.New(initialRows, initialCols, initialValue) + + determinant, err := nonSquareMatrix.Determinant() + // Check if non square matrix returns an error. + if err == nil { + t.Fatalf("No error was returned for a non-square matrix") + } + + // Check if the correct error was returned. + expectedError := errors.New("Matrix rows and columns must equal in order to find the determinant.") + + if err.Error() != expectedError.Error() { + t.Errorf("Error returned from non-square matrix was \n\"%v\"; \nwanted \n\"%v\"", err, expectedError) + } + + // Check if the determinant of the non-square matrix is 0. + if determinant != 0 { + t.Errorf("Determinant of non-square matrix was not 0 but was %d", determinant) + } + +} + +// Test matrix returned from matrix.New +func TestDefaultMatrix(t *testing.T) { + initialValue := 0 + initialRows := 3 + initialCols := 3 + defaultMatrix := matrix.New(initialRows, initialCols, initialValue) + + determinant, err := defaultMatrix.Determinant() + + if err != nil { + t.Fatalf("Error finding the determinant of 3 by 3 default matrix: %v.", err) + } + expectedValue := 0 + if determinant != expectedValue { + t.Errorf("Determinant of the default matrix with an initial value 0 was %d; wanted %d.", initialValue, expectedValue) + } +} + +// Benchmark a 3 by 3 matrix for computational throughput +func BenchmarkSmallMatrixDeterminant(b *testing.B) { + // Create a 3 by 3 matrix for benchmarking + rows := 3 + columns := 3 + initialValue := 0 + matrix := matrix.New(rows, columns, initialValue) + + for i := 0; i < b.N; i++ { + _, _ = matrix.Determinant() + } +} + +// Benchmark a 10 by 10 matrix for computational throughput. +func BenchmarkMatrixDeterminant(b *testing.B) { + // Create a 10 by 10 matrix for benchmarking + rows := 10 + columns := 10 + initialValue := 0 + matrix := matrix.New(rows, columns, initialValue) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, _ = matrix.Determinant() + } +} diff --git a/math/matrix/isvalid.go b/math/matrix/isvalid.go new file mode 100644 index 000000000..afe8f5ed3 --- /dev/null +++ b/math/matrix/isvalid.go @@ -0,0 +1,17 @@ +package matrix + +import "github.com/TheAlgorithms/Go/constraints" + +// IsValid checks if the input matrix has consistent row lengths. +func IsValid[T constraints.Integer](elements [][]T) bool { + if len(elements) == 0 { + return true + } + columns := len(elements[0]) + for _, row := range elements { + if len(row) != columns { + return false + } + } + return true +} diff --git a/math/matrix/isvalid_test.go b/math/matrix/isvalid_test.go new file mode 100644 index 000000000..ea7dae338 --- /dev/null +++ b/math/matrix/isvalid_test.go @@ -0,0 +1,64 @@ +package matrix_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestIsValid(t *testing.T) { + // Test case 1: Valid matrix with consistent row lengths + validMatrix := [][]int{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + } + result1 := matrix.IsValid(validMatrix) + if !result1 { + t.Errorf("IsValid(validMatrix) returned false, expected true (valid matrix)") + } + + // Test case 2: Valid matrix with empty rows (no inconsistency) + validMatrixEmptyRows := [][]int{ + {}, + {}, + {}, + } + result2 := matrix.IsValid(validMatrixEmptyRows) + if !result2 { + t.Errorf("IsValid(validMatrixEmptyRows) returned false, expected true (valid matrix with empty rows)") + } + + // Test case 3: Invalid matrix with inconsistent row lengths + invalidMatrix := [][]int{ + {1, 2, 3}, + {4, 5}, + {6, 7, 8}, + } + result3 := matrix.IsValid(invalidMatrix) + if result3 { + t.Errorf("IsValid(invalidMatrix) returned true, expected false (invalid matrix with inconsistent row lengths)") + } + +} + +func BenchmarkIsValid(b *testing.B) { + // Create a sample matrix for benchmarking + rows := 100 + columns := 100 + elements := make([][]int, rows) + for i := range elements { + elements[i] = make([]int, columns) + for j := range elements[i] { + elements[i][j] = i*columns + j // Some arbitrary values + } + } + + // Reset the benchmark timer + b.ResetTimer() + + // Run the benchmark + for i := 0; i < b.N; i++ { + _ = matrix.IsValid(elements) + } +} diff --git a/math/matrix/matchdimensions.go b/math/matrix/matchdimensions.go new file mode 100644 index 000000000..982409191 --- /dev/null +++ b/math/matrix/matchdimensions.go @@ -0,0 +1,9 @@ +package matrix + +// MatchDimensions checks if two matrices have the same dimensions. +func (m Matrix[T]) MatchDimensions(m1 Matrix[T]) bool { + if m.rows == m1.rows && m.columns == m1.columns { + return true + } + return false +} diff --git a/math/matrix/matchdimensions_test.go b/math/matrix/matchdimensions_test.go new file mode 100644 index 000000000..645b4211f --- /dev/null +++ b/math/matrix/matchdimensions_test.go @@ -0,0 +1,40 @@ +package matrix_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestMatrixMatchDimensions(t *testing.T) { + // Create two matrices with the same dimensions + m1 := matrix.New(2, 3, 0) + m2 := matrix.New(2, 3, 0) + + // Test case 1: Same dimensions + if !m1.MatchDimensions(m2) { + t.Errorf("m1.MatchDimensions(m2) returned %t, expected 1 (same dimensions)", m1.MatchDimensions(m2)) + } + + // Create two matrices with different dimensions + m3 := matrix.New(2, 3, 0) + m4 := matrix.New(3, 2, 0) + + // Test case 2: Different dimensions + if m3.MatchDimensions(m4) { + t.Errorf("m3.MatchDimensions(m4) returned : %v, expected: %v", m3.MatchDimensions(m4), false) + } +} + +// BenchmarkMatchDimensions benchmarks the MatchDimensions method. +func BenchmarkMatchDimensions(b *testing.B) { + // Create sample matrices for benchmarking + rows := 100 + columns := 100 + m1 := matrix.New(rows, columns, 0) // Replace with appropriate values + m2 := matrix.New(rows, columns, 0) // Replace with appropriate values + + for i := 0; i < b.N; i++ { + _ = m1.MatchDimensions(m2) + } +} diff --git a/math/matrix/matrix.go b/math/matrix/matrix.go new file mode 100644 index 000000000..5a03e1e0b --- /dev/null +++ b/math/matrix/matrix.go @@ -0,0 +1,92 @@ +package matrix + +import ( + "errors" + "sync" + + "github.com/TheAlgorithms/Go/constraints" +) + +type Matrix[T constraints.Integer] struct { + elements [][]T + rows int + columns int +} + +// NewMatrix creates a new Matrix based on the provided arguments. +func New[T constraints.Integer](rows, columns int, initial T) Matrix[T] { + if rows < 0 || columns < 0 { + return Matrix[T]{} // Invalid dimensions, return an empty matrix + } + + // Initialize the matrix with the specified dimensions and fill it with the initial value. + elements := make([][]T, rows) + var wg sync.WaitGroup + wg.Add(rows) + + for i := range elements { + go func(i int) { + defer wg.Done() + elements[i] = make([]T, columns) + for j := range elements[i] { + elements[i][j] = initial + } + }(i) + } + + wg.Wait() + + return Matrix[T]{elements, rows, columns} +} + +// NewFromElements creates a new Matrix from the given elements. +func NewFromElements[T constraints.Integer](elements [][]T) (Matrix[T], error) { + if !IsValid(elements) { + return Matrix[T]{}, errors.New("rows have different numbers of columns") + } + rows := len(elements) + if rows == 0 { + return Matrix[T]{}, nil // Empty matrix + } + + columns := len(elements[0]) + matrix := Matrix[T]{ + elements: make([][]T, rows), + rows: rows, // Set the rows field + columns: columns, // Set the columns field + } + for i := range matrix.elements { + matrix.elements[i] = make([]T, columns) + copy(matrix.elements[i], elements[i]) + } + + return matrix, nil +} + +func (m Matrix[T]) Get(row, col int) (T, error) { + if row < 0 || row >= m.rows || col < 0 || col >= m.columns { + var zeroVal T + return zeroVal, errors.New("index out of range") + } + return m.elements[row][col], nil +} + +func (m Matrix[T]) Set(row, col int, val T) error { + if row < 0 || row >= m.rows || col < 0 || col >= m.columns { + return errors.New("index out of bounds") + } + + m.elements[row][col] = val + return nil +} + +func (m Matrix[T]) Rows() int { + return len(m.elements) +} + +func (m Matrix[T]) Columns() int { + if len(m.elements) == 0 { + return 0 + } + return len(m.elements[0]) +} diff --git a/math/matrix/matrix_test.go b/math/matrix/matrix_test.go new file mode 100644 index 000000000..b8c0f7c72 --- /dev/null +++ b/math/matrix/matrix_test.go @@ -0,0 +1,250 @@ +package matrix_test + +import ( + "errors" + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestNewMatrix(t *testing.T) { + + nullMatrix := matrix.New(0, 0, 0) + if nullMatrix.Rows() != 0 || nullMatrix.Columns() != 0 { + t.Errorf("matrix.New( 0, 0, 0) returned nil, expected a matrix") + } + // Test creating a matrix of integers + intMatrix := matrix.New(3, 4, 0) + if intMatrix.Rows() != 3 || intMatrix.Columns() != 4 { + t.Errorf("matrix.New( 3, 4, 0) returned nil, expected a matrix") + } + +} + +func TestNewFromElements(t *testing.T) { + // Test case 1: Valid matrix + validElements := [][]int{ + {1, 2, 3}, + {4, 5, 6}, + } + expectedm1 := matrix.New(2, 3, 0) + for i := 0; i < len(validElements); i++ { + for j := 0; j < len(validElements[0]); j++ { + err := expectedm1.Set(i, j, validElements[i][j]) + if err != nil { + t.Errorf("copyMatrix.Set error: %s", err.Error()) + } + } + } + + m1, err1 := matrix.NewFromElements(validElements) + if err1 != nil { + t.Errorf("NewFromElements(validElements) returned an error: %v", err1) + } + res := m1.CheckEqual(expectedm1) + if res != true { + t.Errorf("NewFromElements(validElements) returned %v, expected %v", m1, expectedm1) + } + + // Test case 2: Invalid matrix with different column counts + invalidElements := [][]int{ + {1, 2, 3}, + {4, 5}, + } + _, err2 := matrix.NewFromElements(invalidElements) + expectedError2 := errors.New("rows have different numbers of columns") + if err2 == nil || err2.Error() != expectedError2.Error() { + t.Errorf("NewFromElements(invalidElements) returned error: %v, expected error: %v", err2, expectedError2) + } + + // Test case 3: Empty matrix + emptyElements := [][]int{} + m3, err3 := matrix.NewFromElements(emptyElements) + if err3 != nil { + t.Errorf("NewFromElements(emptyElements) returned an error: %v", err3) + } + if m3.Rows() != 0 || m3.Columns() != 0 { + t.Errorf("NewFromElements(emptyElements) returned %v, expected nil", m3) + } +} + +func TestMatrixGet(t *testing.T) { + // Create a sample matrix for testing + matrix := matrix.New(3, 3, 0) + err := matrix.Set(1, 1, 42) // Set a specific value for testing + if err != nil { + t.Errorf("copyMatrix.Set error: %s", err.Error()) + } + // Test case 1: Valid Get + val1, err1 := matrix.Get(1, 1) + if err1 != nil { + t.Errorf("matrix.Get(1, 1) returned an error: %v, expected no error", err1) + } + if val1 != 42 { + t.Errorf("matrix.Get(1, 1) returned %v, expected 42", val1) + } + + // Test case 2: Get with invalid indices + _, err2 := matrix.Get(10, 10) + expectedError2 := errors.New("index out of range") + if err2 == nil || err2.Error() != expectedError2.Error() { + t.Errorf("matrix.Get(10, 10) returned error: %v, expected error: %v", err2, expectedError2) + } + // Test case 3: Get with invalid indices + _, err3 := matrix.Get(-1, -3) + expectedError3 := errors.New("index out of range") + if err3 == nil || err3.Error() != expectedError3.Error() { + t.Errorf("matrix.Get(10, 10) returned error: %v, expected error: %v", err3, expectedError3) + } +} + +func TestMatrixSet(t *testing.T) { + // Create a sample matrix for testing + matrix := matrix.New(3, 3, 0) + + // Test case 1: Valid Set + err1 := matrix.Set(1, 1, 42) + if err1 != nil { + t.Errorf("matrix.Set(1, 1, 42) returned an error: %v, expected no error", err1) + } + val1, err := matrix.Get(1, 1) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + if val1 != 42 { + t.Errorf("matrix.Set(1, 1, 42) did not set the value correctly, expected 42, got %v", val1) + } + + // Test case 2: Set with invalid indices + err2 := matrix.Set(10, 10, 100) + expectedError2 := errors.New("index out of bounds") + if err2 == nil || err2.Error() != expectedError2.Error() { + t.Errorf("matrix.Set(10, 10, 100) returned error: %v, expected error: %v", err2, expectedError2) + } + // Test case 3: Get with invalid indices + err3 := matrix.Set(-13, -1, 100) + expectedError3 := errors.New("index out of bounds") + if err3 == nil || err3.Error() != expectedError3.Error() { + t.Errorf("matrix.Get(10, 10) returned error: %v, expected error: %v", err3, expectedError3) + } +} + +func TestMatrixRows(t *testing.T) { + // Create a sample matrix + data := [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} + matrix, err := matrix.NewFromElements(data) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + // Check the number of rows + expectedRows := len(data) + rows := matrix.Rows() + if rows != expectedRows { + t.Errorf("Expected %d rows, but got %d", expectedRows, rows) + } +} + +func TestMatrixColumns(t *testing.T) { + // Create a sample matrix + data := [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} + matrix, err := matrix.NewFromElements(data) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + // Check the number of columns + expectedColumns := len(data[0]) + columns := matrix.Columns() + if columns != expectedColumns { + t.Errorf("Expected %d columns, but got %d", expectedColumns, columns) + } +} + +func TestMatrixEmptyRowsAndColumns(t *testing.T) { + // Create an empty matrix + emptyMatrix := matrix.New(0, 0, 0) + + // Check the number of rows and columns for an empty matrix + rows := emptyMatrix.Rows() + columns := emptyMatrix.Columns() + + if rows != 0 { + t.Errorf("Expected 0 rows for an empty matrix, but got %d", rows) + } + + if columns != 0 { + t.Errorf("Expected 0 columns for an empty matrix, but got %d", columns) + } +} + +// BenchmarkNew benchmarks the New function. +func BenchmarkNew(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = matrix.New(100, 100, 0) // Change the arguments to match your use case + } +} + +// BenchmarkNewFromElements benchmarks the NewFromElements function. +func BenchmarkNewFromElements(b *testing.B) { + // Create a sample matrix for benchmarking + rows := 100 + columns := 100 + elements := make([][]int, rows) + for i := range elements { + elements[i] = make([]int, columns) + for j := range elements[i] { + elements[i][j] = i*columns + j // Some arbitrary values + } + } + + for i := 0; i < b.N; i++ { + _, _ = matrix.NewFromElements(elements) + } +} + +// BenchmarkGet benchmarks the Get method. +func BenchmarkGet(b *testing.B) { + // Create a sample matrix for benchmarking + rows := 100 + columns := 100 + matrix := matrix.New(rows, columns, 0) + + for i := 0; i < b.N; i++ { + _, _ = matrix.Get(50, 50) // Change the row and column indices as needed + } +} + +// BenchmarkSet benchmarks the Set method. +func BenchmarkSet(b *testing.B) { + // Create a sample matrix for benchmarking + rows := 100 + columns := 100 + matrix := matrix.New(rows, columns, 0) + + for i := 0; i < b.N; i++ { + _ = matrix.Set(50, 50, 42) // Change the row, column, and value as needed + } +} + +// BenchmarkRows benchmarks the Rows method. +func BenchmarkRows(b *testing.B) { + // Create a sample matrix for benchmarking + rows := 100 + columns := 100 + matrix := matrix.New(rows, columns, 0) + + for i := 0; i < b.N; i++ { + _ = matrix.Rows() + } +} + +// BenchmarkColumns benchmarks the Columns method. +func BenchmarkColumns(b *testing.B) { + // Create a sample matrix for benchmarking + rows := 100 + columns := 100 + matrix := matrix.New(rows, columns, 0) + + for i := 0; i < b.N; i++ { + _ = matrix.Columns() + } +} diff --git a/math/matrix/multiply.go b/math/matrix/multiply.go new file mode 100644 index 000000000..f59213da6 --- /dev/null +++ b/math/matrix/multiply.go @@ -0,0 +1,91 @@ +// multiply.go +// description: Implementation of matrix multiplication +// time complexity: O(n^3) where n is the number of rows in the first matrix +// space complexity: O(n^2) where n is the number of rows in the first matrix + +package matrix + +import ( + "context" + "errors" + "sync" +) + +// Multiply multiplies the current matrix (m1) with another matrix (m2) and returns the result as a new matrix. +func (m1 Matrix[T]) Multiply(m2 Matrix[T]) (Matrix[T], error) { + // Check if the matrices can be multiplied. + if m1.Columns() != m2.Rows() { + return Matrix[T]{}, errors.New("matrices cannot be multiplied: column count of the first matrix must match row count of the second matrix") + } + + // Create a new matrix to store the result. + var zeroVal T + result := New(m1.Rows(), m2.Columns(), zeroVal) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() // Make sure it's called to release resources even if no errors + + var wg sync.WaitGroup + errCh := make(chan error, 1) + + for i := 0; i < m1.Rows(); i++ { + for j := 0; j < m2.Columns(); j++ { + i, j := i, j // Capture the loop variable for the goroutine + wg.Add(1) + go func() { + defer wg.Done() + // Compute the dot product of the row from the first matrix and the column from the second matrix. + dotProduct := zeroVal + for k := 0; k < m1.Columns(); k++ { + select { + case <-ctx.Done(): + return // Context canceled; return without an error + default: + } + + val1, err := m1.Get(i, k) + if err != nil { + cancel() + select { + case errCh <- err: + default: + } + return + } + val2, err := m2.Get(k, j) + if err != nil { + cancel() + select { + case errCh <- err: + default: + } + return + } + dotProduct += val1 * val2 + } + err := result.Set(i, j, dotProduct) + if err != nil { + cancel() + select { + case errCh <- err: + default: + } + return + } + }() + } + } + + // Wait for all goroutines to finish + go func() { + wg.Wait() + close(errCh) + }() + + // Check for any errors + if err := <-errCh; err != nil { + return Matrix[T]{}, err + } + + return result, nil +} diff --git a/math/matrix/multiply_test.go b/math/matrix/multiply_test.go new file mode 100644 index 000000000..1a1ca39a7 --- /dev/null +++ b/math/matrix/multiply_test.go @@ -0,0 +1,101 @@ +package matrix_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestMultiplyMatrix(t *testing.T) { + // Test case with compatible NULL matrices + t.Run("NULL Matrices", func(t *testing.T) { + mat1 := matrix.New(0, 0, 0) + mat2 := matrix.New(0, 0, 0) + + expected := matrix.New(0, 0, 0) + result, err := mat1.Multiply(mat2) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } else if !result.CheckEqual(expected) { + t.Errorf("Result matrix does not match the expected result.") + } + + }) + // Test case with compatible matrices + t.Run("Compatible Matrices", func(t *testing.T) { + mat1 := [][]int{{1, 2, 3}, {4, 5, 6}} + mat2 := [][]int{{7, 8}, {9, 10}, {11, 12}} + m1, err := matrix.NewFromElements(mat1) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + m2, err := matrix.NewFromElements(mat2) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + exp := [][]int{{58, 64}, {139, 154}} + expected, err := matrix.NewFromElements(exp) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + result, err := m1.Multiply(m2) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } else if !result.CheckEqual(expected) { + t.Errorf("Result matrix does not match the expected result.") + } + + }) + +} + +func TestMultiplyIncompatibleMatrix(t *testing.T) { + // Test case with incompatible matrices + t.Run("Incompatible Matrices", func(t *testing.T) { + mat1 := [][]int{{1, 2, 3}, {4, 5, 6}} + mat2 := [][]int{{7, 8}, {9, 10}} + m1, err := matrix.NewFromElements(mat1) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + m2, err := matrix.NewFromElements(mat2) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + + _, err = m1.Multiply(m2) + if err == nil { + t.Error("Expected an error, but got none") + } + }) + + t.Run("Incompatible Matrices", func(t *testing.T) { + mat1 := [][]int{{1, 2}} + mat2 := [][]int{{}} + m1, err := matrix.NewFromElements(mat1) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + m2, err := matrix.NewFromElements(mat2) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + _, err = m1.Multiply(m2) + + if err == nil { + t.Error("Expected an error, but got none") + } + }) +} + +func BenchmarkMatrixMultiply(b *testing.B) { + // Create sample matrices for benchmarking + rows := 10 + columns := 10 + m1 := matrix.New(rows, columns, 2) // Replace with appropriate values + m2 := matrix.New(rows, columns, 3) // Replace with appropriate values + + for i := 0; i < b.N; i++ { + _, _ = m1.Multiply(m2) + } +} diff --git a/math/matrix/strassenmatrixmultiply.go b/math/matrix/strassenmatrixmultiply.go new file mode 100644 index 000000000..9b5f01fdf --- /dev/null +++ b/math/matrix/strassenmatrixmultiply.go @@ -0,0 +1,229 @@ +// filename: strassenmatrixmultiply.go +// description: Implements matrix multiplication using the Strassen algorithm. +// details: +// This program takes two matrices as input and performs matrix multiplication +// using the Strassen algorithm, which is an optimized divide-and-conquer +// approach. It allows for efficient multiplication of large matrices. +// time complexity: O(n^2.81) +// space complexity: O(n^2) +// author(s): Mohit Raghav(https://github.com/mohit07raghav19) +// See strassenmatrixmultiply_test.go for test cases +package matrix + +// Perform matrix multiplication using Strassen's algorithm +func (A Matrix[T]) StrassenMatrixMultiply(B Matrix[T]) (Matrix[T], error) { + n := A.rows + // Check if matrices are 2x2 or smaller + if n == 1 { + a1, err := A.Get(0, 0) + if err != nil { + return Matrix[T]{}, err + } + b1, err := B.Get(0, 0) + if err != nil { + return Matrix[T]{}, err + } + result := New(1, 1, a1*b1) + return result, nil + } else { + // Calculate the size of submatrices + mid := n / 2 + + // Create submatrices + A11, err := A.SubMatrix(0, 0, mid, mid) + if err != nil { + return Matrix[T]{}, err + } + A12, err := A.SubMatrix(0, mid, mid, n-mid) + if err != nil { + return Matrix[T]{}, err + } + A21, err := A.SubMatrix(mid, 0, n-mid, mid) + if err != nil { + return Matrix[T]{}, err + } + A22, err := A.SubMatrix(mid, mid, n-mid, n-mid) + if err != nil { + return Matrix[T]{}, err + } + + B11, err := B.SubMatrix(0, 0, mid, mid) + if err != nil { + return Matrix[T]{}, err + } + B12, err := B.SubMatrix(0, mid, mid, n-mid) + if err != nil { + return Matrix[T]{}, err + } + B21, err := B.SubMatrix(mid, 0, n-mid, mid) + if err != nil { + return Matrix[T]{}, err + } + B22, err := B.SubMatrix(mid, mid, n-mid, n-mid) + if err != nil { + return Matrix[T]{}, err + } + + // Calculate result submatrices + A1, err := A11.Add(A22) + if err != nil { + return Matrix[T]{}, err + } + + A2, err := B11.Add(B22) + if err != nil { + return Matrix[T]{}, err + } + + A3, err := A21.Add(A22) + if err != nil { + return Matrix[T]{}, err + } + + A4, err := A11.Add(A12) + if err != nil { + return Matrix[T]{}, err + } + + A5, err := B11.Add(B12) + if err != nil { + return Matrix[T]{}, err + } + + A6, err := B21.Add(B22) + if err != nil { + return Matrix[T]{}, err + } + // + S1, err := B12.Subtract(B22) + if err != nil { + return Matrix[T]{}, err + } + S2, err := B21.Subtract(B11) + if err != nil { + return Matrix[T]{}, err + } + S3, err := A21.Subtract(A11) + if err != nil { + return Matrix[T]{}, err + } + S4, err := A12.Subtract(A22) + if err != nil { + return Matrix[T]{}, err + } + // Recursive steps + M1, err := A1.StrassenMatrixMultiply(A2) + if err != nil { + return Matrix[T]{}, err + } + M2, err := A3.StrassenMatrixMultiply(B11) + if err != nil { + return Matrix[T]{}, err + } + M3, err := A11.StrassenMatrixMultiply(S1) + if err != nil { + return Matrix[T]{}, err + } + M4, err := A22.StrassenMatrixMultiply(S2) + if err != nil { + return Matrix[T]{}, err + } + M5, err := A4.StrassenMatrixMultiply(B22) + if err != nil { + return Matrix[T]{}, err + } + M6, err := S3.StrassenMatrixMultiply(A5) + if err != nil { + return Matrix[T]{}, err + } + M7, err := S4.StrassenMatrixMultiply(A6) + + if err != nil { + return Matrix[T]{}, err + } // + A7, err := M1.Add(M4) + + if err != nil { + return Matrix[T]{}, err + } + A8, err := A7.Add(M7) + + if err != nil { + return Matrix[T]{}, err + } + A9, err := M1.Add(M3) + + if err != nil { + return Matrix[T]{}, err + } + A10, err := A9.Add(M6) + + if err != nil { + return Matrix[T]{}, err + } + // Calculate result submatrices + C11, err := A8.Subtract(M5) + if err != nil { + return Matrix[T]{}, err + } + C12, err := M3.Add(M5) + if err != nil { + return Matrix[T]{}, err + } + C21, err := M2.Add(M4) + if err != nil { + return Matrix[T]{}, err + } + C22, err := A10.Subtract(M2) + if err != nil { + return Matrix[T]{}, err + } + + // Combine subMatrices into the result matrix + var zeroVal T + C := New(n, n, zeroVal) + + for i := 0; i < mid; i++ { + for j := 0; j < mid; j++ { + val, err := C11.Get(i, j) + if err != nil { + return Matrix[T]{}, err + } + + err = C.Set(i, j, val) + if err != nil { + return Matrix[T]{}, err + } + + val, err = C12.Get(i, j) + if err != nil { + return Matrix[T]{}, err + } + + err1 := C.Set(i, j+mid, val) + if err1 != nil { + return Matrix[T]{}, err1 + } + val, err = C21.Get(i, j) + if err != nil { + return Matrix[T]{}, err + } + + err2 := C.Set(i+mid, j, val) + if err2 != nil { + return Matrix[T]{}, err2 + } + val, err = C22.Get(i, j) + if err != nil { + return Matrix[T]{}, err + } + + err3 := C.Set(i+mid, j+mid, val) + if err3 != nil { + return Matrix[T]{}, err3 + } + } + } + return C, nil + } +} diff --git a/math/matrix/strassenmatrixmultiply_test.go b/math/matrix/strassenmatrixmultiply_test.go new file mode 100644 index 000000000..b47b4c8fc --- /dev/null +++ b/math/matrix/strassenmatrixmultiply_test.go @@ -0,0 +1,131 @@ +package matrix_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/TheAlgorithms/Go/constraints" + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestStrassenMatrixMultiply(t *testing.T) { + // Create two sample matrices + dataA := [][]int{{1, 2}, {4, 5}} + dataB := [][]int{{9, 8}, {6, 5}} + matrixA, err := matrix.NewFromElements(dataA) + if err != nil { + t.Error("copyMatrix.Set error: " + err.Error()) + } + matrixB, err := matrix.NewFromElements(dataB) + if err != nil { + t.Error("copyMatrix.Set error: " + err.Error()) + } + + // Perform matrix multiplication using Strassen's algorithm + resultMatrix, err := matrixA.StrassenMatrixMultiply(matrixB) + if err != nil { + t.Error("copyMatrix.Set error: " + err.Error()) + } + + // Expected result + expectedData, err := matrixA.Multiply(matrixB) + if err != nil { + t.Error("copyMatrix.Set error: " + err.Error()) + } + + // Check the dimensions of the result matrix + expectedRows := expectedData.Rows() + expectedColumns := expectedData.Columns() + rows := resultMatrix.Rows() + columns := resultMatrix.Columns() + + if rows != expectedRows { + t.Errorf("Expected %d rows in result matrix, but got %d", expectedRows, rows) + } + + if columns != expectedColumns { + t.Errorf("Expected %d columns in result matrix, but got %d", expectedColumns, columns) + } + + // Check the values in the result matrix + for i := 0; i < expectedRows; i++ { + for j := 0; j < expectedColumns; j++ { + val, err := resultMatrix.Get(i, j) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + expVal, err := expectedData.Get(i, j) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + if val != expVal { + t.Errorf("Expected value %d at (%d, %d) in result matrix, but got %d", expVal, i, j, val) + } + } + } +} +func TestMatrixMultiplication(t *testing.T) { + rand.New(rand.NewSource(time.Now().UnixNano())) + + // Generate random matrices for testing + size := 1 << (rand.Intn(8) + 1) // tests for matrix with n as power of 2 + matrixA := MakeRandomMatrix[int](size, size) + matrixB := MakeRandomMatrix[int](size, size) + + // Calculate the expected result using the standard multiplication + expected, err := matrixA.Multiply(matrixB) + if err != nil { + t.Error("copyMatrix.Set error: " + err.Error()) + } + // Calculate the result using the Strassen algorithm + result, err := matrixA.StrassenMatrixMultiply(matrixB) + if err != nil { + t.Error("copyMatrix.Set error: " + err.Error()) + } + + // Check if the result matches the expected result + for i := 0; i < size; i++ { + for j := 0; j < size; j++ { + val, err := result.Get(i, j) + if err != nil { + t.Error("copyMatrix.Set error: " + err.Error()) + } + exp, err := expected.Get(i, j) + if err != nil { + t.Error("copyMatrix.Set error: " + err.Error()) + } + if val != exp { + t.Errorf("Mismatch at position (%d, %d). Expected %d, but got %d.", i, j, exp, val) + } + } + } +} + +func MakeRandomMatrix[T constraints.Integer](rows, columns int) matrix.Matrix[T] { + rand.New(rand.NewSource(time.Now().UnixNano())) + + matrixData := make([][]T, rows) + for i := 0; i < rows; i++ { + matrixData[i] = make([]T, columns) + for j := 0; j < columns; j++ { + matrixData[i][j] = T(rand.Intn(1000)) // Generate random integers between 0 and 1000 + } + } + + randomMatrix, _ := matrix.NewFromElements(matrixData) + return randomMatrix +} + +// BenchmarkStrassenMatrixMultiply benchmarks the StrassenMatrixMultiply function. +func BenchmarkStrassenMatrixMultiply(b *testing.B) { + // Create sample matrices for benchmarking + rows := 64 // it is large enough for multiplication + columns := 64 + m1 := matrix.New(rows, columns, 2) // Replace with appropriate values + m2 := matrix.New(rows, columns, 3) // Replace with appropriate values + + for i := 0; i < b.N; i++ { + _, _ = m1.StrassenMatrixMultiply(m2) + } +} diff --git a/math/matrix/string.go b/math/matrix/string.go new file mode 100644 index 000000000..b59fe8c6c --- /dev/null +++ b/math/matrix/string.go @@ -0,0 +1,15 @@ +package matrix + +import "fmt" + +// String implements the fmt.Stringer interface for Matrix. +func (m Matrix[T]) String() string { + var result string + for i := range m.elements { + for j := range m.elements[i] { + result += fmt.Sprintf("%v ", m.elements[i][j]) + } + result += "\n" + } + return result +} diff --git a/math/matrix/string_test.go b/math/matrix/string_test.go new file mode 100644 index 000000000..59ca24510 --- /dev/null +++ b/math/matrix/string_test.go @@ -0,0 +1,96 @@ +package matrix_test + +import ( + "bytes" + "fmt" + "io" + "os" + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestMatrixString(t *testing.T) { + // Create a sample matrix for testing + m1, err := matrix.NewFromElements([][]int{{1, 2}, {3, 4}}) + if err != nil { + t.Errorf("Error creating matrix: %v", err) + } + + // Redirect stdout to capture Stringed output + old := os.Stdout + r, w, err := os.Pipe() + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + os.Stdout = w + + // Call the String method + fmt.Print(m1) + + // Reset stdout + w.Close() + os.Stdout = old + + // Read the captured output + var buf bytes.Buffer + _, err = io.Copy(&buf, r) + if err != nil { + t.Errorf("Error copying output: %v", err) + } + capturedOutput := buf.String() + + // Define the expected output + expectedOutput := "1 2 \n3 4 \n" + + // Compare the captured output with the expected output + if capturedOutput != expectedOutput { + t.Errorf("Matrix.Print() produced incorrect output:\n%s\nExpected:\n%s", capturedOutput, expectedOutput) + } +} + +func TestNullMatrixString(t *testing.T) { + + m1 := matrix.New(0, 0, 0) + // Redirect stdout to capture Stringed output + old := os.Stdout + r, w, err := os.Pipe() + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + os.Stdout = w + + // Call the String method + fmt.Print(m1) + + // Reset stdout + w.Close() + os.Stdout = old + + // Read the captured output + var buf bytes.Buffer + _, err = io.Copy(&buf, r) + if err != nil { + t.Errorf("Error copying output: %v", err) + } + capturedOutput := buf.String() + + // Define the expected output + expectedOutput := "" + + // Compare the captured output with the expected output + if capturedOutput != expectedOutput { + t.Errorf("Matrix.Print() produced incorrect output:\n%s\nExpected:\n%s", capturedOutput, expectedOutput) + } +} + +func BenchmarkString(b *testing.B) { + // Create a sample matrix for benchmarking + rows := 100 + columns := 100 + m := matrix.New(rows, columns, 0) // Replace with appropriate values + + for i := 0; i < b.N; i++ { + _ = m.String() + } +} diff --git a/math/matrix/submatrix.go b/math/matrix/submatrix.go new file mode 100644 index 000000000..4d636e2c0 --- /dev/null +++ b/math/matrix/submatrix.go @@ -0,0 +1,79 @@ +package matrix + +import ( + "context" + "errors" + "sync" +) + +// SubMatrix extracts a submatrix from the current matrix. +func (m Matrix[T]) SubMatrix(rowStart, colStart, numRows, numCols int) (Matrix[T], error) { + if rowStart < 0 || colStart < 0 || numRows < 0 || numCols < 0 { + return Matrix[T]{}, errors.New("negative dimensions are not allowed") + } + + if rowStart+numRows > m.rows || colStart+numCols > m.columns { + return Matrix[T]{}, errors.New("submatrix dimensions exceed matrix bounds") + } + + var zeroVal T + if numRows == 0 || numCols == 0 { + return New(numRows, numCols, zeroVal), nil // Return an empty matrix + } + + subMatrix := New(numRows, numCols, zeroVal) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() // Make sure it's called to release resources even if no errors + + var wg sync.WaitGroup + errCh := make(chan error, 1) + + for i := 0; i < numRows; i++ { + i := i // Capture the loop variable for the goroutine + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < numCols; j++ { + select { + case <-ctx.Done(): + return // Context canceled; return without an error + default: + } + + val, err := m.Get(rowStart+i, colStart+j) + if err != nil { + cancel() + select { + case errCh <- err: + default: + } + return + } + + err = subMatrix.Set(i, j, val) + if err != nil { + cancel() + select { + case errCh <- err: + default: + } + return + } + } + }() + } + + // Wait for all goroutines to finish + go func() { + wg.Wait() + close(errCh) + }() + + // Check for any errors + if err := <-errCh; err != nil { + return Matrix[T]{}, err + } + + return subMatrix, nil +} diff --git a/math/matrix/submatrix_test.go b/math/matrix/submatrix_test.go new file mode 100644 index 000000000..09452496d --- /dev/null +++ b/math/matrix/submatrix_test.go @@ -0,0 +1,88 @@ +package matrix_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestMatrixSubMatrix(t *testing.T) { + // Create a sample matrix + data := [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} + matrix, err := matrix.NewFromElements(data) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + // Extract a submatrix + subMatrix, err := matrix.SubMatrix(1, 1, 2, 2) + if err != nil { + t.Errorf("Error extracting submatrix: %v", err) + } + + // Check the dimensions of the submatrix + expectedRows := 2 + expectedColumns := 2 + rows := subMatrix.Rows() + columns := subMatrix.Columns() + + if rows != expectedRows { + t.Errorf("Expected %d rows in submatrix, but got %d", expectedRows, rows) + } + + if columns != expectedColumns { + t.Errorf("Expected %d columns in submatrix, but got %d", expectedColumns, columns) + } + + // Check the values in the submatrix + expectedData := [][]int{{5, 6}, {8, 9}} + for i := 0; i < expectedRows; i++ { + for j := 0; j < expectedColumns; j++ { + val, err := subMatrix.Get(i, j) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + if val != expectedData[i][j] { + t.Errorf("Expected value %d at (%d, %d) in submatrix, but got %d", expectedData[i][j], i, j, val) + } + } + } +} + +func TestMatrixInvalidSubMatrix(t *testing.T) { + // Create a sample matrix + data := [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} + matrix, err := matrix.NewFromElements(data) + if err != nil { + t.Fatalf("Failed to copy matrix: %v", err) + } + // Attempt to extract an invalid submatrix + _, err = matrix.SubMatrix(1, 1, 3, 3) + + // Check if an error is returned + if err == nil { + t.Error("Expected an error for invalid submatrix dimensions, but got nil") + } + + // Check the error message + expectedErrorMessage := "submatrix dimensions exceed matrix bounds" + if err.Error() != expectedErrorMessage { + t.Errorf("Expected error message '%s', but got '%s'", expectedErrorMessage, err.Error()) + } +} + +// BenchmarkSubMatrix benchmarks the SubMatrix function. +func BenchmarkSubMatrix(b *testing.B) { + // Create a sample matrix for benchmarking + rows := 100 + columns := 100 + matrix := matrix.New(rows, columns, 2) // Replace with appropriate values + + rowStart := 10 + colStart := 10 + numRows := 20 + numCols := 20 + + for i := 0; i < b.N; i++ { + _, _ = matrix.SubMatrix(rowStart, colStart, numRows, numCols) + } +} diff --git a/math/matrix/subtract.go b/math/matrix/subtract.go new file mode 100644 index 000000000..761a7ed8c --- /dev/null +++ b/math/matrix/subtract.go @@ -0,0 +1,64 @@ +package matrix + +import ( + "context" + "errors" + "sync" +) + +// Subtract subtracts two matrices. +func (m1 Matrix[T]) Subtract(m2 Matrix[T]) (Matrix[T], error) { + // Check if the matrices have the same dimensions. + if !m1.MatchDimensions(m2) { + return Matrix[T]{}, errors.New("matrices are not compatible for subtraction") + } + + // Create a new matrix to store the result. + var zeroVal T + result := New(m1.Rows(), m1.Columns(), zeroVal) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() // Make sure it's called to release resources even if no errors + + var wg sync.WaitGroup + errCh := make(chan error, 1) + + for i := 0; i < m1.rows; i++ { + i := i // Capture the loop variable for the goroutine + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < m1.columns; j++ { + select { + case <-ctx.Done(): + return // Context canceled; return without an error + default: + } + + diff := m1.elements[i][j] - m2.elements[i][j] + err := result.Set(i, j, diff) + if err != nil { + cancel() // Cancel the context on error + select { + case errCh <- err: + default: + } + return + } + } + }() + } + + // Wait for all goroutines to finish + go func() { + wg.Wait() + close(errCh) + }() + + // Check for any errors + if err := <-errCh; err != nil { + return Matrix[T]{}, err + } + + return result, nil +} diff --git a/math/matrix/subtract_test.go b/math/matrix/subtract_test.go new file mode 100644 index 000000000..827d5c9e3 --- /dev/null +++ b/math/matrix/subtract_test.go @@ -0,0 +1,50 @@ +package matrix_test + +import ( + "fmt" + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +func TestSubtract(t *testing.T) { + // Create two matrices with the same dimensions for Subtraction + m1 := matrix.New(2, 2, 1) + m2 := matrix.New(2, 2, 2) + + // Test case 1: Valid matrix Subtraction + subMatrix, err := m1.Subtract(m2) + if err != nil { + t.Errorf("Add(m1, m2) returned an error: %v, expected no error", err) + } + expectedMatrix := matrix.New(2, 2, -1) + res := subMatrix.CheckEqual(expectedMatrix) + if !res { + t.Errorf("Add(m1, m2) returned incorrect result:\n%v\nExpected:\n%v", subMatrix, expectedMatrix) + } + + // Create two matrices with different dimensions for Subtraction + m3 := matrix.New(2, 2, 1) + m4 := matrix.New(2, 3, 2) + + // Test case 2: Matrices with different dimensions + _, err2 := m3.Subtract(m4) + expectedError2 := fmt.Errorf("matrices are not compatible for subtraction") + if err2 == nil || err2.Error() != expectedError2.Error() { + t.Errorf("m3.Subtract(m4) returned error: %v, expected error: %v", err2, expectedError2) + } + +} + +// BenchmarkSubtract benchmarks the Subtract function. +func BenchmarkSubtract(b *testing.B) { + // Create sample matrices for benchmarking + rows := 100 + columns := 100 + m1 := matrix.New(rows, columns, 2) // Replace with appropriate values + m2 := matrix.New(rows, columns, 3) // Replace with appropriate values + + for i := 0; i < b.N; i++ { + _, _ = m1.Subtract(m2) + } +} diff --git a/math/max/bitwisemax.go b/math/max/bitwisemax.go index b6c3a4623..a54f94654 100644 --- a/math/max/bitwisemax.go +++ b/math/max/bitwisemax.go @@ -3,6 +3,8 @@ // details: // implementation of finding the maximum of two numbers using only binary operations without using conditions // author(s) [red_byte](https://github.com/i-redbyte) +// time complexity: O(1) +// space complexity: O(1) // see bitwiseMax_test.go package max diff --git a/math/median.go b/math/median.go index d8cd02796..1b9dac834 100644 --- a/math/median.go +++ b/math/median.go @@ -1,5 +1,7 @@ // author(s) [jo3zeph](https://github.com/jo3zeph) // description: Find the median from a set of values +// time complexity: O(n log n) +// space complexity: O(1) // see median_test.go package math diff --git a/math/mobius.go b/math/mobius.go index 76b50da14..0f9fac639 100644 --- a/math/mobius.go +++ b/math/mobius.go @@ -7,6 +7,8 @@ // μ(n) = −1 if n is a square-free positive integer with an odd number of prime factors. // μ(n) = 0 if n has a squared prime factor. // wikipedia: https://en.wikipedia.org/wiki/M%C3%B6bius_function +// time complexity: O(n) +// space complexity: O(1) // author: Akshay Dubey (https://github.com/itsAkshayDubey) // see mobius_test.go diff --git a/math/mode.go b/math/mode.go index 164084bb6..87f233304 100644 --- a/math/mode.go +++ b/math/mode.go @@ -1,5 +1,7 @@ // mode.go // author(s): [CalvinNJK] (https://github.com/CalvinNJK) +// time complexity: O(n) +// space complexity: O(n) // description: Finding Mode Value In an Array // see mode.go diff --git a/math/modular/exponentiation.go b/math/modular/exponentiation.go index f71a8d3ff..85a397e4f 100644 --- a/math/modular/exponentiation.go +++ b/math/modular/exponentiation.go @@ -2,6 +2,8 @@ // description: Implementation of Modular Exponentiation Algorithm // details: // A simple implementation of Modular Exponentiation - [Modular Exponenetation wiki](https://en.wikipedia.org/wiki/Modular_exponentiation) +// time complexity: O(log(n)) where n is the exponent +// space complexity: O(1) // author(s) [Taj](https://github.com/tjgurwara99) // see exponentiation_test.go diff --git a/math/modular/inverse.go b/math/modular/inverse.go index 819ffda8d..f20d70b11 100644 --- a/math/modular/inverse.go +++ b/math/modular/inverse.go @@ -2,6 +2,8 @@ // description: Implementation of Modular Inverse Algorithm // details: // A simple implementation of Modular Inverse - [Modular Inverse wiki](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse) +// time complexity: O(log(min(a, b))) where a and b are the two numbers +// space complexity: O(1) // author(s) [Taj](https://github.com/tjgurwara99) // see inverse_test.go diff --git a/math/moserdebruijnsequence/sequence.go b/math/moserdebruijnsequence/sequence.go index dd846b5a1..09d2c7776 100644 --- a/math/moserdebruijnsequence/sequence.go +++ b/math/moserdebruijnsequence/sequence.go @@ -1,5 +1,7 @@ // The Moser-de Bruijn sequence is the sequence obtained by // adding up the distinct powers of the number 4 (For example 1, 4, 16, 64, etc). +// time complexity: O(n) +// space complexity: O(n) // You can get more details on https://en.wikipedia.org/wiki/Moser%E2%80%93de_Bruijn_sequence. package moserdebruijnsequence diff --git a/math/pascal/pascaltriangle.go b/math/pascal/pascaltriangle.go index 12f227fd0..742b735d7 100644 --- a/math/pascal/pascaltriangle.go +++ b/math/pascal/pascaltriangle.go @@ -16,6 +16,8 @@ //1 10 45 120 210 252 210 120 45 10 1 //... // author(s) [red_byte](https://github.com/i-redbyte) +// time complexity: O(n^2) +// space complexity: O(n^2) // see pascaltriangle_test.go package pascal diff --git a/math/perfectnumber.go b/math/perfectnumber.go index e21c89b64..37be71a2b 100644 --- a/math/perfectnumber.go +++ b/math/perfectnumber.go @@ -7,6 +7,8 @@ // A number is called perfect, if it is a sum of its proper divisors, // cf. https://en.wikipedia.org/wiki/Perfect_number, // https://mathworld.wolfram.com/PerfectNumber.html +// time complexity: O(sqrt(n)) +// space complexity: O(1) // https://oeis.org/A000396 // author(s) [Piotr Idzik](https://github.com/vil02) // see perfectnumber_test.go diff --git a/math/permutation/heaps.go b/math/permutation/heaps.go index f3d79f464..5f4ebaed3 100644 --- a/math/permutation/heaps.go +++ b/math/permutation/heaps.go @@ -1,3 +1,8 @@ +// heaps.go +// description: Implementation of Heap's Algorithm for generating all permutations of n objects +// time complexity: O(n!) +// space complexity: O(n) + package permutation import ( diff --git a/math/permutation/next_permutation.go b/math/permutation/next_permutation.go new file mode 100644 index 000000000..6aff11296 --- /dev/null +++ b/math/permutation/next_permutation.go @@ -0,0 +1,39 @@ +// A practice to find lexicographically next greater permutation of the given array of integers. +// If there does not exist any greater permutation, then print the lexicographically smallest permutation of the given array. +// The implementation below, finds the next permutation in linear time and constant memory and returns in place +// time complexity: O(n) +// space complexity: O(1) +// Useful reference: https://www.geeksforgeeks.org/next-permutation/ + +package permutation + +func NextPermutation(nums []int) { + pivot := 0 + for pivot = len(nums) - 2; pivot >= 0; pivot-- { + if nums[pivot] < nums[pivot+1] { + break + } + } + if pivot < 0 { + // current permutation is the last and must be reversed totally + for l, r := 0, len(nums)-1; l < r; l, r = l+1, r-1 { + nums[l], nums[r] = nums[r], nums[l] + } + } else { + succ := 0 + for succ = len(nums) - 1; succ > pivot; succ = succ - 1 { + if nums[succ] > nums[pivot] { + break + } + } + + // Swap the pivot and successor + nums[pivot], nums[succ] = nums[succ], nums[pivot] + + // Reverse the suffix part to minimize it + for l, r := pivot+1, len(nums)-1; l < r; l, r = l+1, r-1 { + nums[l], nums[r] = nums[r], nums[l] + } + } + +} diff --git a/math/permutation/next_permutation_test.go b/math/permutation/next_permutation_test.go new file mode 100644 index 000000000..8246876b4 --- /dev/null +++ b/math/permutation/next_permutation_test.go @@ -0,0 +1,40 @@ +package permutation + +import ( + "reflect" + "testing" +) + +func TestNextPermutation(t *testing.T) { + var nextPermutationTestData = []struct { + description string + numbers []int + next []int + }{ + { + description: "Basic case", + numbers: []int{1, 2, 3}, + next: []int{1, 3, 2}, + }, + { + description: "Should reverse the whole slice", + numbers: []int{3, 2, 1}, + next: []int{1, 2, 3}, + }, + { + description: "A more complex test", + numbers: []int{2, 4, 1, 7, 5, 0}, + next: []int{2, 4, 5, 0, 1, 7}, + }, + } + for _, test := range nextPermutationTestData { + t.Run(test.description, func(t *testing.T) { + NextPermutation(test.numbers) + + if !reflect.DeepEqual(test.numbers, test.next) { + t.Logf("FAIL: %s", test.description) + t.Fatalf("Expected result:%v\nFound: %v", test.next, test.numbers) + } + }) + } +} diff --git a/math/pi/montecarlopi.go b/math/pi/montecarlopi.go index 7bbadf01c..a2e140f08 100644 --- a/math/pi/montecarlopi.go +++ b/math/pi/montecarlopi.go @@ -2,6 +2,8 @@ // description: Calculating pi by the Monte Carlo method // details: // implementations of Monte Carlo Algorithm for the calculating of Pi - [Monte Carlo method](https://en.wikipedia.org/wiki/Monte_Carlo_method) +// time complexity: O(n) +// space complexity: O(1) // author(s): [red_byte](https://github.com/i-redbyte), [Paul Leydier] (https://github.com/paul-leydier) // see montecarlopi_test.go diff --git a/math/pi/spigotpi.go b/math/pi/spigotpi.go index 6274176e4..349602a32 100644 --- a/math/pi/spigotpi.go +++ b/math/pi/spigotpi.go @@ -2,6 +2,8 @@ // description: A Spigot Algorithm for the Digits of Pi // details: // implementation of Spigot Algorithm for the Digits of Pi - [Spigot algorithm](https://en.wikipedia.org/wiki/Spigot_algorithm) +// time complexity: O(n) +// space complexity: O(n) // author(s) [red_byte](https://github.com/i-redbyte) // see spigotpi_test.go diff --git a/math/pollard.go b/math/pollard.go index 434a0f24f..a2cc42df1 100644 --- a/math/pollard.go +++ b/math/pollard.go @@ -2,6 +2,8 @@ // description: Pollard's rho algorithm // details: // implementation of Pollard's rho algorithm for integer factorization-[Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm) +// time complexity: O(n^(1/4)) +// space complexity: O(1) // author(s) [red_byte](https://github.com/i-redbyte) // see pollard_test.go diff --git a/math/power/powvialogarithm.go b/math/power/powvialogarithm.go index df1de157f..c2a395e3b 100644 --- a/math/power/powvialogarithm.go +++ b/math/power/powvialogarithm.go @@ -2,6 +2,8 @@ // description: Powers in terms of logarithms // details: // implementation of exponentiation using exponent and logarithm, without using loops - [Powers via logarithms wiki](https://en.wikipedia.org/wiki/Exponentiation#Powers_via_logarithms) +// time complexity: O(1) +// space complexity: O(1) // author(s) [red_byte](https://github.com/i-redbyte) // see powvialogarithm_test.go diff --git a/math/prime/millerrabintest.go b/math/prime/millerrabintest.go index 1f3a2ef72..5357bc45c 100644 --- a/math/prime/millerrabintest.go +++ b/math/prime/millerrabintest.go @@ -2,7 +2,8 @@ // One of the implementations is deterministic and the other is probabilistic. // The Miller-Rabin test is one of the simplest and fastest known primality // tests and is widely used. -// +// time complexity: O(k * log(n)^3) +// space complexity: O(1) // Authors: // [Taj](https://github.com/tjgurwara99) // [Rak](https://github.com/raklaptudirm) diff --git a/math/prime/primecheck.go b/math/prime/primecheck.go index 3e2a97280..7671c9cd0 100644 --- a/math/prime/primecheck.go +++ b/math/prime/primecheck.go @@ -3,6 +3,8 @@ package prime // A primality test is an algorithm for determining whether an input number is prime. Among other // fields of mathematics, it is used for cryptography. Unlike integer factorization, primality // tests do not generally give prime factors, only stating whether the input number is prime or not. +// time complexity: O(sqrt(n)) +// space complexity: O(1) // Source - Wikipedia https://en.wikipedia.org/wiki/Primality_test // TrialDivision tests whether a number is prime by trying to divide it by the numbers less than it. diff --git a/math/prime/primefactorization.go b/math/prime/primefactorization.go index c25522871..43479e439 100644 --- a/math/prime/primefactorization.go +++ b/math/prime/primefactorization.go @@ -1,3 +1,8 @@ +// primefactorization.go +// description: Prime factorization of a number +// time complexity: O(sqrt(n)) +// space complexity: O(sqrt(n)) + package prime // Factorize is a function that computes the exponents diff --git a/math/prime/sieve2.go b/math/prime/sieve2.go new file mode 100644 index 000000000..881ca20f5 --- /dev/null +++ b/math/prime/sieve2.go @@ -0,0 +1,23 @@ +/* sieve2.go - Sieve of Eratosthenes + * Algorithm to generate prime numbers up to a limit + * time complexity: O(n log log n) + * space complexity: O(n) + * Author: ddaniel27 + */ +package prime + +func SieveEratosthenes(limit int) []int { + primes := make([]int, 0) + sieve := make([]int, limit+1) // make a slice of size limit+1 + + for i := 2; i <= limit; i++ { + if sieve[i] == 0 { // if the number is not marked as composite + primes = append(primes, i) // add it to the list of primes + for j := i * i; j <= limit; j += i { // mark all multiples of i as composite + sieve[j] = 1 + } + } + } + + return primes +} diff --git a/math/prime/sieve2_test.go b/math/prime/sieve2_test.go new file mode 100644 index 000000000..24912e3d0 --- /dev/null +++ b/math/prime/sieve2_test.go @@ -0,0 +1,43 @@ +package prime_test + +import ( + "reflect" + "testing" + + "github.com/TheAlgorithms/Go/math/prime" +) + +func TestSieveEratosthenes(t *testing.T) { + tests := []struct { + name string + limit int + want []int + }{ + { + name: "First 10 primes test", + limit: 30, + want: []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}, + }, + { + name: "First 20 primes test", + limit: 71, + want: []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := prime.SieveEratosthenes(tt.limit) + + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("SieveEratosthenes() = %v, want %v", got, tt.want) + } + }) + } +} + +func BenchmarkSieveEratosthenes(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = prime.SieveEratosthenes(10) + } +} diff --git a/math/prime/twin.go b/math/prime/twin.go index 1326746e4..dfd861510 100644 --- a/math/prime/twin.go +++ b/math/prime/twin.go @@ -4,6 +4,8 @@ // For any integer n, twin prime is (n + 2) // if and only if both n and (n + 2) both are prime // wikipedia: https://en.wikipedia.org/wiki/Twin_prime +// time complexity: O(log n) +// space complexity: O(1) // author: Akshay Dubey (https://github.com/itsAkshayDubey) // see twin_test.go diff --git a/math/pronicnumber.go b/math/pronicnumber.go index 90850d28e..47d25d17c 100644 --- a/math/pronicnumber.go +++ b/math/pronicnumber.go @@ -4,18 +4,20 @@ // Pronic number: For any integer n, if there exists integer m // such that n = m * (m + 1) then n is called a pronic number. // wikipedia: https://en.wikipedia.org/wiki/Pronic_number +// time complexity: O(1) +// space complexity: O(1) // author: Akshay Dubey (https://github.com/itsAkshayDubey) // see pronicnumber_test.go package math -import stdMath "math" +import "math" // PronicNumber returns true if argument passed to the function is pronic and false otherwise. func PronicNumber(n int) bool { if n < 0 || n%2 == 1 { return false } - x := int(stdMath.Sqrt(float64(n))) + x := int(math.Sqrt(float64(n))) return n == x*(x+1) } diff --git a/other/nested/nestedbrackets.go b/other/nested/nestedbrackets.go index ec912938c..7f220a332 100644 --- a/other/nested/nestedbrackets.go +++ b/other/nested/nestedbrackets.go @@ -17,6 +17,9 @@ package nested // **Note** Providing characters other then brackets would return false, // despite brackets sequence in the string. Make sure to filter // input before usage. +// time complexity: O(n) +// space complexity: O(n) + func IsBalanced(input string) bool { if len(input) == 0 { return true diff --git a/other/password/generator.go b/other/password/generator.go index 46237364f..71bfdbd65 100644 --- a/other/password/generator.go +++ b/other/password/generator.go @@ -3,6 +3,9 @@ // This length is not fixed if you generate multiple passwords for the same range // Package password contains functions to help generate random passwords +// time complexity: O(n) +// space complexity: O(n) + package password import ( diff --git a/project_euler/problem_1/problem1.go b/project_euler/problem_1/problem1.go new file mode 100644 index 000000000..e41fbf468 --- /dev/null +++ b/project_euler/problem_1/problem1.go @@ -0,0 +1,24 @@ +/** + * Problem 1 - Multiples of 3 and 5 + * + * @see {@link https://projecteuler.net/problem=1} + * + * If we list all the natural numbers below 10 that are multiples of 3 or 5, + * we get 3, 5, 6 and 9. The sum of these multiples is 23. + * Find the sum of all the multiples of 3 or 5 below 1000. + * + * @author ddaniel27 + */ +package problem1 + +func Problem1(n uint) uint { + sum := uint(0) + + for i := uint(1); i < n; i++ { + if i%3 == 0 || i%5 == 0 { + sum += i + } + } + + return sum +} diff --git a/project_euler/problem_1/problem1_test.go b/project_euler/problem_1/problem1_test.go new file mode 100644 index 000000000..c2d649b02 --- /dev/null +++ b/project_euler/problem_1/problem1_test.go @@ -0,0 +1,45 @@ +package problem1 + +import "testing" + +// Tests +func TestProblem1_Func(t *testing.T) { + tests := []struct { + name string + threshold uint + want uint + }{ + { + name: "Testcase 1 - threshold 10", + threshold: 10, + want: 23, + }, + { + name: "Testcase 2 - threshold 100", + threshold: 100, + want: 2318, + }, + { + name: "Testcase 3 - threshold 1000", + threshold: 1000, + want: 233168, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Problem1(tt.threshold) + + if n != tt.want { + t.Errorf("Problem1() = %v, want %v", n, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem1(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Problem1(1000) + } +} diff --git a/project_euler/problem_10/problem10.go b/project_euler/problem_10/problem10.go new file mode 100644 index 000000000..342c79d24 --- /dev/null +++ b/project_euler/problem_10/problem10.go @@ -0,0 +1,23 @@ +/** +* Problem 10 - Summation of primes +* @see {@link https://projecteuler.net/problem=10} +* +* The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. +* Find the sum of all the primes below two million. +* +* @author ddaniel27 + */ +package problem10 + +import "github.com/TheAlgorithms/Go/math/prime" + +func Problem10(n int) uint { + sum := uint(0) + sieve := prime.SieveEratosthenes(n) + + for _, v := range sieve { + sum += uint(v) + } + + return sum +} diff --git a/project_euler/problem_10/problem10_test.go b/project_euler/problem_10/problem10_test.go new file mode 100644 index 000000000..d82e6f29e --- /dev/null +++ b/project_euler/problem_10/problem10_test.go @@ -0,0 +1,40 @@ +package problem10 + +import "testing" + +// Tests +func TestProblem10_Func(t *testing.T) { + tests := []struct { + name string + input int + want uint + }{ + { + name: "Testcase 1 - input 10", + input: 10, + want: 17, + }, + { + name: "Testcase 2 - input 2000000", + input: 2000000, + want: 142913828922, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Problem10(tt.input) + + if n != tt.want { + t.Errorf("Problem10() = %v, want %v", n, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem10(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Problem10(2000000) + } +} diff --git a/project_euler/problem_11/problem11.go b/project_euler/problem_11/problem11.go new file mode 100644 index 000000000..5ddaea8e2 --- /dev/null +++ b/project_euler/problem_11/problem11.go @@ -0,0 +1,80 @@ +/** +* Problem 11 - Largest product in a grid +* @see {@link https://projecteuler.net/problem=11} +* +* In the 20×20 grid below, four numbers along a diagonal line have been marked in red. +* +* The product of these numbers is 26 × 63 × 78 × 14 = 1788696. +* +* What is the greatest product of four adjacent numbers in the same direction +* (up, down, left, right, or diagonally) in the 20×20 grid? +* +* @author ddaniel27 + */ +package problem11 + +var grid = [20][20]uint{ + {8, 2, 22, 97, 38, 15, 0, 40, 0, 75, 4, 5, 7, 78, 52, 12, 50, 77, 91, 8}, + {49, 49, 99, 40, 17, 81, 18, 57, 60, 87, 17, 40, 98, 43, 69, 48, 4, 56, 62, 0}, + {81, 49, 31, 73, 55, 79, 14, 29, 93, 71, 40, 67, 53, 88, 30, 3, 49, 13, 36, 65}, + {52, 70, 95, 23, 4, 60, 11, 42, 69, 24, 68, 56, 1, 32, 56, 71, 37, 2, 36, 91}, + {22, 31, 16, 71, 51, 67, 63, 89, 41, 92, 36, 54, 22, 40, 40, 28, 66, 33, 13, 80}, + {24, 47, 32, 60, 99, 3, 45, 2, 44, 75, 33, 53, 78, 36, 84, 20, 35, 17, 12, 50}, + {32, 98, 81, 28, 64, 23, 67, 10, 26, 38, 40, 67, 59, 54, 70, 66, 18, 38, 64, 70}, + {67, 26, 20, 68, 2, 62, 12, 20, 95, 63, 94, 39, 63, 8, 40, 91, 66, 49, 94, 21}, + {24, 55, 58, 5, 66, 73, 99, 26, 97, 17, 78, 78, 96, 83, 14, 88, 34, 89, 63, 72}, + {21, 36, 23, 9, 75, 0, 76, 44, 20, 45, 35, 14, 0, 61, 33, 97, 34, 31, 33, 95}, + {78, 17, 53, 28, 22, 75, 31, 67, 15, 94, 3, 80, 4, 62, 16, 14, 9, 53, 56, 92}, + {16, 39, 5, 42, 96, 35, 31, 47, 55, 58, 88, 24, 0, 17, 54, 24, 36, 29, 85, 57}, + {86, 56, 0, 48, 35, 71, 89, 7, 5, 44, 44, 37, 44, 60, 21, 58, 51, 54, 17, 58}, + {19, 80, 81, 68, 5, 94, 47, 69, 28, 73, 92, 13, 86, 52, 17, 77, 4, 89, 55, 40}, + {4, 52, 8, 83, 97, 35, 99, 16, 7, 97, 57, 32, 16, 26, 26, 79, 33, 27, 98, 66}, + {88, 36, 68, 87, 57, 62, 20, 72, 3, 46, 33, 67, 46, 55, 12, 32, 63, 93, 53, 69}, + {4, 42, 16, 73, 38, 25, 39, 11, 24, 94, 72, 18, 8, 46, 29, 32, 40, 62, 76, 36}, + {20, 69, 36, 41, 72, 30, 23, 88, 34, 62, 99, 69, 82, 67, 59, 85, 74, 4, 36, 16}, + {20, 73, 35, 29, 78, 31, 90, 1, 74, 31, 49, 71, 48, 86, 81, 16, 23, 57, 5, 54}, + {1, 70, 54, 71, 83, 51, 54, 69, 16, 92, 33, 48, 61, 43, 52, 1, 89, 19, 67, 48}, +} + +func Problem11() uint { + max := uint(0) + + for i := 0; i < 20; i++ { + for j := 0; j < 20; j++ { + + // Vertical + if i+3 < 20 { + product := grid[i][j] * grid[i+1][j] * grid[i+2][j] * grid[i+3][j] + if product > max { + max = product + } + } + + // Horizontal + if j+3 < 20 { + product := grid[i][j] * grid[i][j+1] * grid[i][j+2] * grid[i][j+3] + if product > max { + max = product + } + } + + if i+3 < 20 && j+3 < 20 { + // Diagonal + product := grid[i][j] * grid[i+1][j+1] * grid[i+2][j+2] * grid[i+3][j+3] + if product > max { + max = product + } + } + + if i+3 < 20 && j-3 >= 0 { + // Diagonal + product := grid[i][j] * grid[i+1][j-1] * grid[i+2][j-2] * grid[i+3][j-3] + if product > max { + max = product + } + } + } + } + + return max +} diff --git a/project_euler/problem_11/problem11_test.go b/project_euler/problem_11/problem11_test.go new file mode 100644 index 000000000..b6ae99e05 --- /dev/null +++ b/project_euler/problem_11/problem11_test.go @@ -0,0 +1,29 @@ +package problem11 + +import "testing" + +// Tests +func TestProblem11_Func(t *testing.T) { + testCases := []struct { + name string + expected uint + }{ + {"Test Case 1", 70600674}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual := Problem11() + if actual != tc.expected { + t.Errorf("Expected: %v, but got %v", tc.expected, actual) + } + }) + } +} + +// Benchmark +func BenchmarkProblem11_Func(b *testing.B) { + for i := 0; i < b.N; i++ { + Problem11() + } +} diff --git a/project_euler/problem_12/problem12.go b/project_euler/problem_12/problem12.go new file mode 100644 index 000000000..803add5b7 --- /dev/null +++ b/project_euler/problem_12/problem12.go @@ -0,0 +1,46 @@ +/** +* Problem 12 - Highly divisible triangular number +* @see {@link https://projecteuler.net/problem=12} +* +* The sequence of triangle numbers is generated by adding the natural numbers. +* So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. +* The first ten terms would be: +* +* 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ... +* +* Let us list the factors of the first seven triangle numbers: +* +* 1: 1 +* 3: 1,3 +* 6: 1,2,3,6 +* 10: 1,2,5,10 +* 15: 1,3,5,15 +* 21: 1,3,7,21 +* 28: 1,2,4,7,14,28 +* +* We can see that 28 is the first triangle number to have over five divisors. +* What is the value of the first triangle number to have over five hundred divisors? +* +* @author ddaniel27 + */ +package problem12 + +func Problem12(limit uint) uint { + triangle := uint(0) + for i := uint(1); ; i++ { + triangle += i + if numDivisors(triangle) >= limit { + return triangle + } + } +} + +func numDivisors(n uint) uint { + divisors := uint(0) + for i := uint(1); i*i <= n; i++ { + if n%i == 0 { + divisors += 2 + } + } + return divisors +} diff --git a/project_euler/problem_12/problem12_test.go b/project_euler/problem_12/problem12_test.go new file mode 100644 index 000000000..2659fb1e9 --- /dev/null +++ b/project_euler/problem_12/problem12_test.go @@ -0,0 +1,31 @@ +package problem12 + +import "testing" + +func TestProblem12_Func(t *testing.T) { + tests := []struct { + name string + input uint + want uint + }{ + {"Test Case 1", 6, 28}, + {"Test Case 2", 7, 36}, + {"Test Case 3", 11, 120}, + {"Test Case 4", 500, 76576500}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := Problem12(tt.input) + if actual != tt.want { + t.Errorf("Expected: %v, but got %v", tt.want, actual) + } + }) + } +} + +func BenchmarkProblem12_Func(b *testing.B) { + for i := 0; i < b.N; i++ { + Problem12(500) + } +} diff --git a/project_euler/problem_13/problem13.go b/project_euler/problem_13/problem13.go new file mode 100644 index 000000000..d5669d271 --- /dev/null +++ b/project_euler/problem_13/problem13.go @@ -0,0 +1,150 @@ +/** +* Problem 13 - Large sum +* @see {@link https://projecteuler.net/problem=13} +* +* Work out the first ten digits of the sum of the following one-hundred 50-digit numbers. +* +* @author ddaniel27 + */ +package problem13 + +var numbers = [100]string{ + "37107287533902102798797998220837590246510135740250", + "46376937677490009712648124896970078050417018260538", + "74324986199524741059474233309513058123726617309629", + "91942213363574161572522430563301811072406154908250", + "23067588207539346171171980310421047513778063246676", + "89261670696623633820136378418383684178734361726757", + "28112879812849979408065481931592621691275889832738", + "44274228917432520321923589422876796487670272189318", + "47451445736001306439091167216856844588711603153276", + "70386486105843025439939619828917593665686757934951", + "62176457141856560629502157223196586755079324193331", + "64906352462741904929101432445813822663347944758178", + "92575867718337217661963751590579239728245598838407", + "58203565325359399008402633568948830189458628227828", + "80181199384826282014278194139940567587151170094390", + "35398664372827112653829987240784473053190104293586", + "86515506006295864861532075273371959191420517255829", + "71693888707715466499115593487603532921714970056938", + "54370070576826684624621495650076471787294438377604", + "53282654108756828443191190634694037855217779295145", + "36123272525000296071075082563815656710885258350721", + "45876576172410976447339110607218265236877223636045", + "17423706905851860660448207621209813287860733969412", + "81142660418086830619328460811191061556940512689692", + "51934325451728388641918047049293215058642563049483", + "62467221648435076201727918039944693004732956340691", + "15732444386908125794514089057706229429197107928209", + "55037687525678773091862540744969844508330393682126", + "18336384825330154686196124348767681297534375946515", + "80386287592878490201521685554828717201219257766954", + "78182833757993103614740356856449095527097864797581", + "16726320100436897842553539920931837441497806860984", + "48403098129077791799088218795327364475675590848030", + "87086987551392711854517078544161852424320693150332", + "59959406895756536782107074926966537676326235447210", + "69793950679652694742597709739166693763042633987085", + "41052684708299085211399427365734116182760315001271", + "65378607361501080857009149939512557028198746004375", + "35829035317434717326932123578154982629742552737307", + "94953759765105305946966067683156574377167401875275", + "88902802571733229619176668713819931811048770190271", + "25267680276078003013678680992525463401061632866526", + "36270218540497705585629946580636237993140746255962", + "24074486908231174977792365466257246923322810917141", + "91430288197103288597806669760892938638285025333403", + "34413065578016127815921815005561868836468420090470", + "23053081172816430487623791969842487255036638784583", + "11487696932154902810424020138335124462181441773470", + "63783299490636259666498587618221225225512486764533", + "67720186971698544312419572409913959008952310058822", + "95548255300263520781532296796249481641953868218774", + "76085327132285723110424803456124867697064507995236", + "37774242535411291684276865538926205024910326572967", + "23701913275725675285653248258265463092207058596522", + "29798860272258331913126375147341994889534765745501", + "18495701454879288984856827726077713721403798879715", + "38298203783031473527721580348144513491373226651381", + "34829543829199918180278916522431027392251122869539", + "40957953066405232632538044100059654939159879593635", + "29746152185502371307642255121183693803580388584903", + "41698116222072977186158236678424689157993532961922", + "62467957194401269043877107275048102390895523597457", + "23189706772547915061505504953922979530901129967519", + "86188088225875314529584099251203829009407770775672", + "11306739708304724483816533873502340845647058077308", + "82959174767140363198008187129011875491310547126581", + "97623331044818386269515456334926366572897563400500", + "42846280183517070527831839425882145521227251250327", + "55121603546981200581762165212827652751691296897789", + "32238195734329339946437501907836945765883352399886", + "75506164965184775180738168837861091527357929701337", + "62177842752192623401942399639168044983993173312731", + "32924185707147349566916674687634660915035914677504", + "99518671430235219628894890102423325116913619626622", + "73267460800591547471830798392868535206946944540724", + "76841822524674417161514036427982273348055556214818", + "97142617910342598647204516893989422179826088076852", + "87783646182799346313767754307809363333018982642090", + "10848802521674670883215120185883543223812876952786", + "71329612474782464538636993009049310363619763878039", + "62184073572399794223406235393808339651327408011116", + "66627891981488087797941876876144230030984490851411", + "60661826293682836764744779239180335110989069790714", + "85786944089552990653640447425576083659976645795096", + "66024396409905389607120198219976047599490197230297", + "64913982680032973156037120041377903785566085089252", + "16730939319872750275468906903707539413042652315011", + "94809377245048795150954100921645863754710598436791", + "78639167021187492431995700641917969777599028300699", + "15368713711936614952811305876380278410754449733078", + "40789923115535562561142322423255033685442488917353", + "44889911501440648020369068063960672322193204149535", + "41503128880339536053299340368006977710650566631954", + "81234880673210146739058568557934581403627822703280", + "82616570773948327592232845941706525094512325230608", + "22918802058777319719839450180888072429661980811197", + "77158542502016545090413245809786882778948721859617", + "72107838435069186155435662884062257473692284509516", + "20849603980134001723930671666823555245252804609722", + "53503534226472524250874054075591789781264330331690", +} + +func Problem13() string { + sum := "0" + + for _, n := range numbers { + sum = add(sum, n) + } + + return sum[:10] +} + +func add(a, b string) string { + if len(a) < len(b) { + a, b = b, a + } + + carry := 0 + sum := make([]byte, len(a)+1) + + for i := 0; i < len(a); i++ { + d := int(a[len(a)-1-i] - '0') + if i < len(b) { + d += int(b[len(b)-1-i] - '0') + } + d += carry + + sum[len(sum)-1-i] = byte(d%10) + '0' + carry = d / 10 + } + + if carry > 0 { + sum[0] = byte(carry) + '0' + } else { + sum = sum[1:] + } + + return string(sum) +} diff --git a/project_euler/problem_13/problem13_test.go b/project_euler/problem_13/problem13_test.go new file mode 100644 index 000000000..e776cbb04 --- /dev/null +++ b/project_euler/problem_13/problem13_test.go @@ -0,0 +1,27 @@ +package problem13 + +import "testing" + +func TestProblem13_Func(t *testing.T) { + tests := []struct { + name string + expected string + }{ + {"Test Case 1", "5537376230"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := Problem13() + if actual != tt.expected { + t.Errorf("Expected: %v, but got %v", tt.expected, actual) + } + }) + } +} + +func BenchmarkProblem13_Func(b *testing.B) { + for i := 0; i < b.N; i++ { + Problem13() + } +} diff --git a/project_euler/problem_14/problem14.go b/project_euler/problem_14/problem14.go new file mode 100644 index 000000000..aa5702c12 --- /dev/null +++ b/project_euler/problem_14/problem14.go @@ -0,0 +1,56 @@ +/** +* Problem 14 - Longest Collatz sequence +* @see {@link https://projecteuler.net/problem=14} +* +* The following iterative sequence is defined for the set of positive integers: +* n → n/2 (n is even) +* n → 3n + 1 (n is odd) +* +* Using the rule above and starting with 13, we generate the following sequence: +* 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1 +* +* Which starting number, under one million, produces the longest chain? +* +* NOTE: Once the chain starts the terms are allowed to go above one million. +* +* @author ddaniel27 + */ +package problem14 + +type dict map[uint64]uint64 + +var dictionary = dict{ + 1: 1, +} + +func Problem14(limit uint64) uint64 { + for i := uint64(2); i <= limit; i++ { + weightNextNode(i) + } + + var max, maxWeight uint64 + for k, v := range dictionary { + if v > maxWeight { + max = k + maxWeight = v + } + } + + return max +} + +func weightNextNode(current uint64) uint64 { + var next, weight uint64 + if current%2 == 0 { + next = current / 2 + } else { + next = (3 * current) + 1 + } + if v, ok := dictionary[next]; !ok { + weight = weightNextNode(next) + 1 + } else { + weight = v + 1 + } + dictionary[current] = weight + return weight +} diff --git a/project_euler/problem_14/problem14_test.go b/project_euler/problem_14/problem14_test.go new file mode 100644 index 000000000..801ff0fdd --- /dev/null +++ b/project_euler/problem_14/problem14_test.go @@ -0,0 +1,31 @@ +package problem14 + +import "testing" + +// Tests +func TestProblem14_Func(t *testing.T) { + tests := []struct { + name string + input uint64 + want uint64 + }{ + {"Input 30", 30, 27}, + {"Input 1e6", 1e6, 837799}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Problem14(tt.input) + if got != tt.want { + t.Errorf("Problem14() = %v, want %v", got, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem14_Func(b *testing.B) { + for i := 0; i < b.N; i++ { + Problem14(1e6) + } +} diff --git a/project_euler/problem_15/problem15.go b/project_euler/problem_15/problem15.go new file mode 100644 index 000000000..6adbd6977 --- /dev/null +++ b/project_euler/problem_15/problem15.go @@ -0,0 +1,42 @@ +/** +* Problem 15 - Lattice paths +* @see {@link https://projecteuler.net/problem=15} +* +* Starting in the top left corner of a 2×2 grid, +* and only being able to move to the right and down, +* there are exactly 6 routes to the bottom right corner. +* +* How many such routes are there through a 20×20 grid? +* +* @author ddaniel27 + */ +package problem15 + +import ( + "github.com/TheAlgorithms/Go/math/factorial" +) + +func Problem15(gridSize int) int { + /** + Author note: + We can solve this problem using combinatorics. + Here is a good blog post that explains the solution: + + [link](https://stemhash.com/counting-lattice-paths/) + + Btw, I'm not related to the author of the blog post. + + After some simplification, we can see that the solution is: + (2n)! / (n!)^2 + + We can use the factorial package to calculate the factorials. + */ + + n := gridSize + + numerator, _ := factorial.Iterative(2 * n) + denominator, _ := factorial.Iterative(n) + denominator *= denominator + + return numerator / denominator +} diff --git a/project_euler/problem_15/problem15_test.go b/project_euler/problem_15/problem15_test.go new file mode 100644 index 000000000..a5b729428 --- /dev/null +++ b/project_euler/problem_15/problem15_test.go @@ -0,0 +1,34 @@ +package problem15 + +import "testing" + +// Tests +func TestProblem15_Func(t *testing.T) { + tests := []struct { + name string + input int + want int + }{ + {"Input 2", 2, 6}, + // This test case is disabled + // because it needs a big integer to run successfully + // and factorial package doesn't support it + // {"Input 20", 20, 137846528820}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Problem15(tt.input) + if got != tt.want { + t.Errorf("Problem15() = %v, want %v", got, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem15_Func(b *testing.B) { + for i := 0; i < b.N; i++ { + Problem15(20) + } +} diff --git a/project_euler/problem_16/problem16.go b/project_euler/problem_16/problem16.go new file mode 100644 index 000000000..cc0471c66 --- /dev/null +++ b/project_euler/problem_16/problem16.go @@ -0,0 +1,33 @@ +/** +* Problem 16 - Power digit sum +* @see {@link https://projecteuler.net/problem=16} +* +* 2^15 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26. +* +* What is the sum of the digits of the number 2^1000? +* +* @author ddaniel27 + */ +package problem16 + +import ( + "math/big" +) + +func Problem16(exponent int64) int64 { + var result big.Int + + bigTwo := big.NewInt(2) + bigExponent := big.NewInt(exponent) + + result.Exp(bigTwo, bigExponent, nil) + + resultStr := result.String() + + var sum int64 + for _, digit := range resultStr { + sum += int64(digit - '0') + } + + return sum +} diff --git a/project_euler/problem_16/problem16_test.go b/project_euler/problem_16/problem16_test.go new file mode 100644 index 000000000..31e3f9a02 --- /dev/null +++ b/project_euler/problem_16/problem16_test.go @@ -0,0 +1,30 @@ +package problem16 + +import "testing" + +// Tests +func TestProblem16_Func(t *testing.T) { + tests := []struct { + name string + exponent int64 + want int64 + }{ + {"2^15", 15, 26}, + {"2^1000", 1000, 1366}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Problem16(tt.exponent); got != tt.want { + t.Errorf("Problem16() = %v, want %v", got, tt.want) + } + }) + } +} + +// Benchmark +func BenchmarkProblem16_Func(b *testing.B) { + for i := 0; i < b.N; i++ { + Problem16(1000) + } +} diff --git a/project_euler/problem_17/input.go b/project_euler/problem_17/input.go new file mode 100644 index 000000000..3a16f8c3a --- /dev/null +++ b/project_euler/problem_17/input.go @@ -0,0 +1,8 @@ +/** +* I put this code in a separate file because it is too long. +* Also it took me a lot of time to parsing this input from +* a random html page, so, I don't want to lose it. + */ +package problem17 + +const INPUT = "One Two Three Four Five Six Seven Eight Nine Ten Eleven Twelve Thirteen Fourteen Fifteen Sixteen Seventeen Eighteen Nineteen Twenty Twenty one Twenty two Twenty three Twenty four Twenty five Twenty six Twenty seven Twenty eight Twenty nine Thirty Thirty one Thirty two Thirty three Thirty four Thirty five Thirty six Thirty seven Thirty eight Thirty nine Forty Forty one Forty two Forty three Forty four Forty five Forty six Forty seven Forty eight Forty nine Fifty Fifty one Fifty two Fifty three Fifty four Fifty five Fifty six Fifty seven Fifty eight Fifty nine Sixty Sixty one Sixty two Sixty three Sixty four Sixty five Sixty six Sixty seven Sixty eight Sixty nine Seventy Seventy one Seventy two Seventy three Seventy four Seventy five Seventy six Seventy seven Seventy eight Seventy nine Eighty Eighty one Eighty two Eighty three Eighty four Eighty five Eighty six Eighty seven Eighty eight Eighty nine Ninety Ninety one Ninety two Ninety three Ninety four Ninety five Ninety six Ninety seven Ninety eight Ninety nine One hundred One hundred and one One hundred and two One hundred and three One hundred and four One hundred and five One hundred and six One hundred and seven One hundred and eight One hundred and nine One hundred and ten One hundred and eleven One hundred and twelve One hundred and thirteen One hundred and fourteen One hundred and fifteen One hundred and sixteen One hundred and seventeen One hundred and eighteen One hundred and nineteen One hundred and twenty One hundred and twenty one One hundred and twenty two One hundred and twenty three One hundred and twenty four One hundred and twenty five One hundred and twenty six One hundred and twenty seven One hundred and twenty eight One hundred and twenty nine One hundred and thirty One hundred and thirty one One hundred and thirty two One hundred and thirty three One hundred and thirty four One hundred and thirty five One hundred and thirty six One hundred and thirty seven One hundred and thirty eight One hundred and thirty nine One hundred and forty One hundred and forty one One hundred and forty two One hundred and forty three One hundred and forty four One hundred and forty five One hundred and forty six One hundred and forty seven One hundred and forty eight One hundred and forty nine One hundred and fifty One hundred and fifty one One hundred and fifty two One hundred and fifty three One hundred and fifty four One hundred and fifty five One hundred and fifty six One hundred and fifty seven One hundred and fifty eight One hundred and fifty nine One hundred and sixty One hundred and sixty one One hundred and sixty two One hundred and sixty three One hundred and sixty four One hundred and sixty five One hundred and sixty six One hundred and sixty seven One hundred and sixty eight One hundred and sixty nine One hundred and seventy One hundred and seventy one One hundred and seventy two One hundred and seventy three One hundred and seventy four One hundred and seventy five One hundred and seventy six One hundred and seventy seven One hundred and seventy eight One hundred and seventy nine One hundred and eighty One hundred and eighty one One hundred and eighty two One hundred and eighty three One hundred and eighty four One hundred and eighty five One hundred and eighty six One hundred and eighty seven One hundred and eighty eight One hundred and eighty nine One hundred and ninety One hundred and ninety one One hundred and ninety two One hundred and ninety three One hundred and ninety four One hundred and ninety five One hundred and ninety six One hundred and ninety seven One hundred and ninety eight One hundred and ninety nine Two hundred Two hundred and one Two hundred and two Two hundred and three Two hundred and four Two hundred and five Two hundred and six Two hundred and seven Two hundred and eight Two hundred and nine Two hundred and ten Two hundred and eleven Two hundred and twelve Two hundred and thirteen Two hundred and fourteen Two hundred and fifteen Two hundred and sixteen Two hundred and seventeen Two hundred and eighteen Two hundred and nineteen Two hundred and twenty Two hundred and twenty one Two hundred and twenty two Two hundred and twenty three Two hundred and twenty four Two hundred and twenty five Two hundred and twenty six Two hundred and twenty seven Two hundred and twenty eight Two hundred and twenty nine Two hundred and thirty Two hundred and thirty one Two hundred and thirty two Two hundred and thirty three Two hundred and thirty four Two hundred and thirty five Two hundred and thirty six Two hundred and thirty seven Two hundred and thirty eight Two hundred and thirty nine Two hundred and forty Two hundred and forty one Two hundred and forty two Two hundred and forty three Two hundred and forty four Two hundred and forty five Two hundred and forty six Two hundred and forty seven Two hundred and forty eight Two hundred and forty nine Two hundred and fifty Two hundred and fifty one Two hundred and fifty two Two hundred and fifty three Two hundred and fifty four Two hundred and fifty five Two hundred and fifty six Two hundred and fifty seven Two hundred and fifty eight Two hundred and fifty nine Two hundred and sixty Two hundred and sixty one Two hundred and sixty two Two hundred and sixty three Two hundred and sixty four Two hundred and sixty five Two hundred and sixty six Two hundred and sixty seven Two hundred and sixty eight Two hundred and sixty nine Two hundred and seventy Two hundred and seventy one Two hundred and seventy two Two hundred and seventy three Two hundred and seventy four Two hundred and seventy five Two hundred and seventy six Two hundred and seventy seven Two hundred and seventy eight Two hundred and seventy nine Two hundred and eighty Two hundred and eighty one Two hundred and eighty two Two hundred and eighty three Two hundred and eighty four Two hundred and eighty five Two hundred and eighty six Two hundred and eighty seven Two hundred and eighty eight Two hundred and eighty nine Two hundred and ninety Two hundred and ninety one Two hundred and ninety two Two hundred and ninety three Two hundred and ninety four Two hundred and ninety five Two hundred and ninety six Two hundred and ninety seven Two hundred and ninety eight Two hundred and ninety nine Three hundred Three hundred and one Three hundred and two Three hundred and three Three hundred and four Three hundred and five Three hundred and six Three hundred and seven Three hundred and eight Three hundred and nine Three hundred and ten Three hundred and eleven Three hundred and twelve Three hundred and thirteen Three hundred and fourteen Three hundred and fifteen Three hundred and sixteen Three hundred and seventeen Three hundred and eighteen Three hundred and nineteen Three hundred and twenty Three hundred and twenty one Three hundred and twenty two Three hundred and twenty three Three hundred and twenty four Three hundred and twenty five Three hundred and twenty six Three hundred and twenty seven Three hundred and twenty eight Three hundred and twenty nine Three hundred and thirty Three hundred and thirty one Three hundred and thirty two Three hundred and thirty three Three hundred and thirty four Three hundred and thirty five Three hundred and thirty six Three hundred and thirty seven Three hundred and thirty eight Three hundred and thirty nine Three hundred and forty Three hundred and forty one Three hundred and forty two Three hundred and forty three Three hundred and forty four Three hundred and forty five Three hundred and forty six Three hundred and forty seven Three hundred and forty eight Three hundred and forty nine Three hundred and fifty Three hundred and fifty one Three hundred and fifty two Three hundred and fifty three Three hundred and fifty four Three hundred and fifty five Three hundred and fifty six Three hundred and fifty seven Three hundred and fifty eight Three hundred and fifty nine Three hundred and sixty Three hundred and sixty one Three hundred and sixty two Three hundred and sixty three Three hundred and sixty four Three hundred and sixty five Three hundred and sixty six Three hundred and sixty seven Three hundred and sixty eight Three hundred and sixty nine Three hundred and seventy Three hundred and seventy one Three hundred and seventy two Three hundred and seventy three Three hundred and seventy four Three hundred and seventy five Three hundred and seventy six Three hundred and seventy seven Three hundred and seventy eight Three hundred and seventy nine Three hundred and eighty Three hundred and eighty one Three hundred and eighty two Three hundred and eighty three Three hundred and eighty four Three hundred and eighty five Three hundred and eighty six Three hundred and eighty seven Three hundred and eighty eight Three hundred and eighty nine Three hundred and ninety Three hundred and ninety one Three hundred and ninety two Three hundred and ninety three Three hundred and ninety four Three hundred and ninety five Three hundred and ninety six Three hundred and ninety seven Three hundred and ninety eight Three hundred and ninety nine Four hundred Four hundred and one Four hundred and two Four hundred and three Four hundred and four Four hundred and five Four hundred and six Four hundred and seven Four hundred and eight Four hundred and nine Four hundred and ten Four hundred and eleven Four hundred and twelve Four hundred and thirteen Four hundred and fourteen Four hundred and fifteen Four hundred and sixteen Four hundred and seventeen Four hundred and eighteen Four hundred and nineteen Four hundred and twenty Four hundred and twenty one Four hundred and twenty two Four hundred and twenty three Four hundred and twenty four Four hundred and twenty five Four hundred and twenty six Four hundred and twenty seven Four hundred and twenty eight Four hundred and twenty nine Four hundred and thirty Four hundred and thirty one Four hundred and thirty two Four hundred and thirty three Four hundred and thirty four Four hundred and thirty five Four hundred and thirty six Four hundred and thirty seven Four hundred and thirty eight Four hundred and thirty nine Four hundred and forty Four hundred and forty one Four hundred and forty two Four hundred and forty three Four hundred and forty four Four hundred and forty five Four hundred and forty six Four hundred and forty seven Four hundred and forty eight Four hundred and forty nine Four hundred and fifty Four hundred and fifty one Four hundred and fifty two Four hundred and fifty three Four hundred and fifty four Four hundred and fifty five Four hundred and fifty six Four hundred and fifty seven Four hundred and fifty eight Four hundred and fifty nine Four hundred and sixty Four hundred and sixty one Four hundred and sixty two Four hundred and sixty three Four hundred and sixty four Four hundred and sixty five Four hundred and sixty six Four hundred and sixty seven Four hundred and sixty eight Four hundred and sixty nine Four hundred and seventy Four hundred and seventy one Four hundred and seventy two Four hundred and seventy three Four hundred and seventy four Four hundred and seventy five Four hundred and seventy six Four hundred and seventy seven Four hundred and seventy eight Four hundred and seventy nine Four hundred and eighty Four hundred and eighty one Four hundred and eighty two Four hundred and eighty three Four hundred and eighty four Four hundred and eighty five Four hundred and eighty six Four hundred and eighty seven Four hundred and eighty eight Four hundred and eighty nine Four hundred and ninety Four hundred and ninety one Four hundred and ninety two Four hundred and ninety three Four hundred and ninety four Four hundred and ninety five Four hundred and ninety six Four hundred and ninety seven Four hundred and ninety eight Four hundred and ninety nine Five hundred Five hundred and one Five hundred and two Five hundred and three Five hundred and four Five hundred and five Five hundred and six Five hundred and seven Five hundred and eight Five hundred and nine Five hundred and ten Five hundred and eleven Five hundred and twelve Five hundred and thirteen Five hundred and fourteen Five hundred and fifteen Five hundred and sixteen Five hundred and seventeen Five hundred and eighteen Five hundred and nineteen Five hundred and twenty Five hundred and twenty one Five hundred and twenty two Five hundred and twenty three Five hundred and twenty four Five hundred and twenty five Five hundred and twenty six Five hundred and twenty seven Five hundred and twenty eight Five hundred and twenty nine Five hundred and thirty Five hundred and thirty one Five hundred and thirty two Five hundred and thirty three Five hundred and thirty four Five hundred and thirty five Five hundred and thirty six Five hundred and thirty seven Five hundred and thirty eight Five hundred and thirty nine Five hundred and forty Five hundred and forty one Five hundred and forty two Five hundred and forty three Five hundred and forty four Five hundred and forty five Five hundred and forty six Five hundred and forty seven Five hundred and forty eight Five hundred and forty nine Five hundred and fifty Five hundred and fifty one Five hundred and fifty two Five hundred and fifty three Five hundred and fifty four Five hundred and fifty five Five hundred and fifty six Five hundred and fifty seven Five hundred and fifty eight Five hundred and fifty nine Five hundred and sixty Five hundred and sixty one Five hundred and sixty two Five hundred and sixty three Five hundred and sixty four Five hundred and sixty five Five hundred and sixty six Five hundred and sixty seven Five hundred and sixty eight Five hundred and sixty nine Five hundred and seventy Five hundred and seventy one Five hundred and seventy two Five hundred and seventy three Five hundred and seventy four Five hundred and seventy five Five hundred and seventy six Five hundred and seventy seven Five hundred and seventy eight Five hundred and seventy nine Five hundred and eighty Five hundred and eighty one Five hundred and eighty two Five hundred and eighty three Five hundred and eighty four Five hundred and eighty five Five hundred and eighty six Five hundred and eighty seven Five hundred and eighty eight Five hundred and eighty nine Five hundred and ninety Five hundred and ninety one Five hundred and ninety two Five hundred and ninety three Five hundred and ninety four Five hundred and ninety five Five hundred and ninety six Five hundred and ninety seven Five hundred and ninety eight Five hundred and ninety nine Six hundred Six hundred and one Six hundred and two Six hundred and three Six hundred and four Six hundred and five Six hundred and six Six hundred and seven Six hundred and eight Six hundred and nine Six hundred and ten Six hundred and eleven Six hundred and twelve Six hundred and thirteen Six hundred and fourteen Six hundred and fifteen Six hundred and sixteen Six hundred and seventeen Six hundred and eighteen Six hundred and nineteen Six hundred and twenty Six hundred and twenty one Six hundred and twenty two Six hundred and twenty three Six hundred and twenty four Six hundred and twenty five Six hundred and twenty six Six hundred and twenty seven Six hundred and twenty eight Six hundred and twenty nine Six hundred and thirty Six hundred and thirty one Six hundred and thirty two Six hundred and thirty three Six hundred and thirty four Six hundred and thirty five Six hundred and thirty six Six hundred and thirty seven Six hundred and thirty eight Six hundred and thirty nine Six hundred and forty Six hundred and forty one Six hundred and forty two Six hundred and forty three Six hundred and forty four Six hundred and forty five Six hundred and forty six Six hundred and forty seven Six hundred and forty eight Six hundred and forty nine Six hundred and fifty Six hundred and fifty one Six hundred and fifty two Six hundred and fifty three Six hundred and fifty four Six hundred and fifty five Six hundred and fifty six Six hundred and fifty seven Six hundred and fifty eight Six hundred and fifty nine Six hundred and sixty Six hundred and sixty one Six hundred and sixty two Six hundred and sixty three Six hundred and sixty four Six hundred and sixty five Six hundred and sixty six Six hundred and sixty seven Six hundred and sixty eight Six hundred and sixty nine Six hundred and seventy Six hundred and seventy one Six hundred and seventy two Six hundred and seventy three Six hundred and seventy four Six hundred and seventy five Six hundred and seventy six Six hundred and seventy seven Six hundred and seventy eight Six hundred and seventy nine Six hundred and eighty Six hundred and eighty one Six hundred and eighty two Six hundred and eighty three Six hundred and eighty four Six hundred and eighty five Six hundred and eighty six Six hundred and eighty seven Six hundred and eighty eight Six hundred and eighty nine Six hundred and ninety Six hundred and ninety one Six hundred and ninety two Six hundred and ninety three Six hundred and ninety four Six hundred and ninety five Six hundred and ninety six Six hundred and ninety seven Six hundred and ninety eight Six hundred and ninety nine Seven hundred Seven hundred and one Seven hundred and two Seven hundred and three Seven hundred and four Seven hundred and five Seven hundred and six Seven hundred and seven Seven hundred and eight Seven hundred and nine Seven hundred and ten Seven hundred and eleven Seven hundred and twelve Seven hundred and thirteen Seven hundred and fourteen Seven hundred and fifteen Seven hundred and sixteen Seven hundred and seventeen Seven hundred and eighteen Seven hundred and nineteen Seven hundred and twenty Seven hundred and twenty one Seven hundred and twenty two Seven hundred and twenty three Seven hundred and twenty four Seven hundred and twenty five Seven hundred and twenty six Seven hundred and twenty seven Seven hundred and twenty eight Seven hundred and twenty nine Seven hundred and thirty Seven hundred and thirty one Seven hundred and thirty two Seven hundred and thirty three Seven hundred and thirty four Seven hundred and thirty five Seven hundred and thirty six Seven hundred and thirty seven Seven hundred and thirty eight Seven hundred and thirty nine Seven hundred and forty Seven hundred and forty one Seven hundred and forty two Seven hundred and forty three Seven hundred and forty four Seven hundred and forty five Seven hundred and forty six Seven hundred and forty seven Seven hundred and forty eight Seven hundred and forty nine Seven hundred and fifty Seven hundred and fifty one Seven hundred and fifty two Seven hundred and fifty three Seven hundred and fifty four Seven hundred and fifty five Seven hundred and fifty six Seven hundred and fifty seven Seven hundred and fifty eight Seven hundred and fifty nine Seven hundred and sixty Seven hundred and sixty one Seven hundred and sixty two Seven hundred and sixty three Seven hundred and sixty four Seven hundred and sixty five Seven hundred and sixty six Seven hundred and sixty seven Seven hundred and sixty eight Seven hundred and sixty nine Seven hundred and seventy Seven hundred and seventy one Seven hundred and seventy two Seven hundred and seventy three Seven hundred and seventy four Seven hundred and seventy five Seven hundred and seventy six Seven hundred and seventy seven Seven hundred and seventy eight Seven hundred and seventy nine Seven hundred and eighty Seven hundred and eighty one Seven hundred and eighty two Seven hundred and eighty three Seven hundred and eighty four Seven hundred and eighty five Seven hundred and eighty six Seven hundred and eighty seven Seven hundred and eighty eight Seven hundred and eighty nine Seven hundred and ninety Seven hundred and ninety one Seven hundred and ninety two Seven hundred and ninety three Seven hundred and ninety four Seven hundred and ninety five Seven hundred and ninety six Seven hundred and ninety seven Seven hundred and ninety eight Seven hundred and ninety nine Eight hundred Eight hundred and one Eight hundred and two Eight hundred and three Eight hundred and four Eight hundred and five Eight hundred and six Eight hundred and seven Eight hundred and eight Eight hundred and nine Eight hundred and ten Eight hundred and eleven Eight hundred and twelve Eight hundred and thirteen Eight hundred and fourteen Eight hundred and fifteen Eight hundred and sixteen Eight hundred and seventeen Eight hundred and eighteen Eight hundred and nineteen Eight hundred and twenty Eight hundred and twenty one Eight hundred and twenty two Eight hundred and twenty three Eight hundred and twenty four Eight hundred and twenty five Eight hundred and twenty six Eight hundred and twenty seven Eight hundred and twenty eight Eight hundred and twenty nine Eight hundred and thirty Eight hundred and thirty one Eight hundred and thirty two Eight hundred and thirty three Eight hundred and thirty four Eight hundred and thirty five Eight hundred and thirty six Eight hundred and thirty seven Eight hundred and thirty eight Eight hundred and thirty nine Eight hundred and forty Eight hundred and forty one Eight hundred and forty two Eight hundred and forty three Eight hundred and forty four Eight hundred and forty five Eight hundred and forty six Eight hundred and forty seven Eight hundred and forty eight Eight hundred and forty nine Eight hundred and fifty Eight hundred and fifty one Eight hundred and fifty two Eight hundred and fifty three Eight hundred and fifty four Eight hundred and fifty five Eight hundred and fifty six Eight hundred and fifty seven Eight hundred and fifty eight Eight hundred and fifty nine Eight hundred and sixty Eight hundred and sixty one Eight hundred and sixty two Eight hundred and sixty three Eight hundred and sixty four Eight hundred and sixty five Eight hundred and sixty six Eight hundred and sixty seven Eight hundred and sixty eight Eight hundred and sixty nine Eight hundred and seventy Eight hundred and seventy one Eight hundred and seventy two Eight hundred and seventy three Eight hundred and seventy four Eight hundred and seventy five Eight hundred and seventy six Eight hundred and seventy seven Eight hundred and seventy eight Eight hundred and seventy nine Eight hundred and eighty Eight hundred and eighty one Eight hundred and eighty two Eight hundred and eighty three Eight hundred and eighty four Eight hundred and eighty five Eight hundred and eighty six Eight hundred and eighty seven Eight hundred and eighty eight Eight hundred and eighty nine Eight hundred and ninety Eight hundred and ninety one Eight hundred and ninety two Eight hundred and ninety three Eight hundred and ninety four Eight hundred and ninety five Eight hundred and ninety six Eight hundred and ninety seven Eight hundred and ninety eight Eight hundred and ninety nine Nine hundred Nine hundred and one Nine hundred and two Nine hundred and three Nine hundred and four Nine hundred and five Nine hundred and six Nine hundred and seven Nine hundred and eight Nine hundred and nine Nine hundred and ten Nine hundred and eleven Nine hundred and twelve Nine hundred and thirteen Nine hundred and fourteen Nine hundred and fifteen Nine hundred and sixteen Nine hundred and seventeen Nine hundred and eighteen Nine hundred and nineteen Nine hundred and twenty Nine hundred and twenty one Nine hundred and twenty two Nine hundred and twenty three Nine hundred and twenty four Nine hundred and twenty five Nine hundred and twenty six Nine hundred and twenty seven Nine hundred and twenty eight Nine hundred and twenty nine Nine hundred and thirty Nine hundred and thirty one Nine hundred and thirty two Nine hundred and thirty three Nine hundred and thirty four Nine hundred and thirty five Nine hundred and thirty six Nine hundred and thirty seven Nine hundred and thirty eight Nine hundred and thirty nine Nine hundred and forty Nine hundred and forty one Nine hundred and forty two Nine hundred and forty three Nine hundred and forty four Nine hundred and forty five Nine hundred and forty six Nine hundred and forty seven Nine hundred and forty eight Nine hundred and forty nine Nine hundred and fifty Nine hundred and fifty one Nine hundred and fifty two Nine hundred and fifty three Nine hundred and fifty four Nine hundred and fifty five Nine hundred and fifty six Nine hundred and fifty seven Nine hundred and fifty eight Nine hundred and fifty nine Nine hundred and sixty Nine hundred and sixty one Nine hundred and sixty two Nine hundred and sixty three Nine hundred and sixty four Nine hundred and sixty five Nine hundred and sixty six Nine hundred and sixty seven Nine hundred and sixty eight Nine hundred and sixty nine Nine hundred and seventy Nine hundred and seventy one Nine hundred and seventy two Nine hundred and seventy three Nine hundred and seventy four Nine hundred and seventy five Nine hundred and seventy six Nine hundred and seventy seven Nine hundred and seventy eight Nine hundred and seventy nine Nine hundred and eighty Nine hundred and eighty one Nine hundred and eighty two Nine hundred and eighty three Nine hundred and eighty four Nine hundred and eighty five Nine hundred and eighty six Nine hundred and eighty seven Nine hundred and eighty eight Nine hundred and eighty nine Nine hundred and ninety Nine hundred and ninety one Nine hundred and ninety two Nine hundred and ninety three Nine hundred and ninety four Nine hundred and ninety five Nine hundred and ninety six Nine hundred and ninety seven Nine hundred and ninety eight Nine hundred and ninety nine One thousand" diff --git a/project_euler/problem_17/problem17.go b/project_euler/problem_17/problem17.go new file mode 100644 index 000000000..a5e2beee7 --- /dev/null +++ b/project_euler/problem_17/problem17.go @@ -0,0 +1,31 @@ +/** +* Problem 17 - Number letter counts +* @see {@link https://projecteuler.net/problem=17} +* +* If the numbers 1 to 5 are written out in words: one, two, three, four, five, +* then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total. +* +* If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, +* how many letters would be used? +* +* NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) +* contains 23 letters and 115 (one hundred and fifteen) contains 20 letters. +* The use of "and" when writing out numbers is in compliance with British usage. +* +* @author ddaniel27 + */ +package problem17 + +import "strings" + +func Problem17(input string) int { + var sum int + + parsed := strings.Split(input, " ") + + for _, word := range parsed { + sum += len(word) + } + + return sum +} diff --git a/project_euler/problem_17/problem17_test.go b/project_euler/problem_17/problem17_test.go new file mode 100644 index 000000000..13cf68bf4 --- /dev/null +++ b/project_euler/problem_17/problem17_test.go @@ -0,0 +1,30 @@ +package problem17 + +import "testing" + +// Tests +func TestProblem17_Func(t *testing.T) { + tests := []struct { + name string + input string + want int + }{ + {"1 to 5", "one two three four five", 19}, + {"1 to 1000", INPUT, 21124}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Problem17(tt.input); got != tt.want { + t.Errorf("Problem17() = %v, want %v", got, tt.want) + } + }) + } +} + +// Benchmark +func BenchmarkProblem17_Func(b *testing.B) { + for i := 0; i < b.N; i++ { + Problem17(INPUT) + } +} diff --git a/project_euler/problem_18/edge.go b/project_euler/problem_18/edge.go new file mode 100644 index 000000000..90f44b082 --- /dev/null +++ b/project_euler/problem_18/edge.go @@ -0,0 +1,100 @@ +package problem18 + +type Edge struct { + ID int + NodeValue NodeValue + NodeLeft Node + NodeRight Node + Parent Node +} + +func (n *Edge) Value() NodeValue { + return n.NodeValue +} + +func (n *Edge) Left() Node { + return n.NodeLeft +} + +func (n *Edge) Right() Node { + return n.NodeRight +} + +func (n *Edge) Kind() string { + return "edge" +} + +func (n *Edge) CreateChild(value NodeValue, id int) Node { + // When the left child is nil, it's a left edge + if n.NodeLeft == nil { + return &Edge{ + ID: id, + NodeValue: value, + Parent: n, + NodeLeft: nil, + NodeRight: nil, + } + } + + // When the left child is a leaf, it's a right edge + if n.NodeLeft.Kind() == "leaf" { + return &Edge{ + ID: id, + NodeValue: value, + Parent: n, + NodeLeft: nil, + NodeRight: nil, + } + } + + return &Leaf{ + ID: id, + NodeValue: value, + Parent: n, + NodeLeft: nil, + NodeRight: nil, + } +} + +func (n *Edge) GetID() int { + return n.ID +} + +func (n *Edge) Insert(node Node) { + // If Left is nil, always simply insert the node + if n.NodeLeft == nil { + node.SetParent(n) + n.NodeLeft = node + + return + } + + // If Right is nil, insert the node + n.NodeRight = node + + // If the node to insert is an edge, set the parent + if node.Kind() == "edge" { + node.SetParent(n) + + return + } + + // If the node to insert is a leaf, send it to the sibling right + n.Parent.Right().Insert(node) +} + +func (n *Edge) HasSpace() bool { + return n.NodeLeft == nil || n.NodeRight == nil +} + +func (n *Edge) LeftIsNil() bool { + return n.NodeLeft == nil +} + +func (n *Edge) RightIsNil() bool { + return n.NodeRight == nil +} + +func (n *Edge) SetParent(node Node) { + n.Parent = node +} diff --git a/project_euler/problem_18/input.go b/project_euler/problem_18/input.go new file mode 100644 index 000000000..e11dcc41f --- /dev/null +++ b/project_euler/problem_18/input.go @@ -0,0 +1,42 @@ +package problem18 + +import "strings" + +const problem18_input_string = ` +75 +95 64 +17 47 82 +18 35 87 10 +20 04 82 47 65 +19 01 23 75 03 34 +88 02 77 73 07 63 67 +99 65 04 28 06 16 70 92 +41 41 26 56 83 40 80 70 33 +41 48 72 33 47 32 37 16 94 29 +53 71 44 65 25 43 91 52 97 51 14 +70 11 33 28 77 73 17 78 39 68 17 57 +91 71 52 38 17 14 91 43 58 50 27 29 48 +63 66 04 68 89 53 67 30 73 16 69 87 40 31 +04 62 98 27 23 09 70 98 73 93 38 53 60 04 23 +` + +var problem18_input_parsed_string []string = strings.Split( + strings.Trim( + strings.ReplaceAll(problem18_input_string, "\n", " "), + " ", + ), + " ") + +const problem18_test_string = ` +3 +7 4 +2 4 6 +8 5 9 3 +` + +var problem18_test_parsed_string []string = strings.Split( + strings.Trim( + strings.ReplaceAll(problem18_test_string, "\n", " "), + " ", + ), + " ") diff --git a/project_euler/problem_18/leaf.go b/project_euler/problem_18/leaf.go new file mode 100644 index 000000000..8dd452602 --- /dev/null +++ b/project_euler/problem_18/leaf.go @@ -0,0 +1,75 @@ +package problem18 + +type Leaf struct { + ID int + NodeValue NodeValue + NodeLeft *Leaf + NodeRight *Leaf + Parent Node +} + +func (n *Leaf) Value() NodeValue { + return n.NodeValue +} + +func (n *Leaf) Left() Node { + if n.NodeLeft != nil { + n.NodeLeft.Parent = n // Leaf is the parent of its left child always + } + + return n.NodeLeft +} + +func (n *Leaf) Right() Node { + return n.NodeRight +} + +func (n *Leaf) Kind() string { + return "leaf" +} + +func (n *Leaf) CreateChild(value NodeValue, id int) Node { + // Leafs only have leaf children + return &Leaf{ + ID: id, + NodeValue: value, + Parent: n, + NodeLeft: nil, + NodeRight: nil, + } +} + +func (n *Leaf) GetID() int { + return n.ID +} + +func (n *Leaf) Insert(node Node) { + // If Left is nil, always simply insert the node + if n.NodeLeft == nil { + node.SetParent(n) + n.NodeLeft = node.(*Leaf) + + return + } + + // If Right is nil, insert the node + n.NodeRight = node.(*Leaf) + // Send it to the sibling right + n.Parent.Right().Insert(node) +} + +func (n *Leaf) HasSpace() bool { + return n.NodeLeft == nil || n.NodeRight == nil +} + +func (n *Leaf) LeftIsNil() bool { + return n.NodeLeft == nil +} + +func (n *Leaf) RightIsNil() bool { + return n.NodeRight == nil +} + +func (n *Leaf) SetParent(node Node) { + n.Parent = node +} diff --git a/project_euler/problem_18/problem18.go b/project_euler/problem_18/problem18.go new file mode 100644 index 000000000..1775c6bd0 --- /dev/null +++ b/project_euler/problem_18/problem18.go @@ -0,0 +1,63 @@ +/** +* Problem 18 - Maximum path sum I +* @see {@link https://projecteuler.net/problem=18} +* +* By starting at the top of the triangle below and +* moving to adjacent numbers on the row below, +* the maximum total from top to bottom is 23. +* +* 3 +* 7 4 +* 2 4 6 +* 8 5 9 3 +* +* That is, 3 + 7 + 4 + 9 = 23. +* +* Find the maximum total from top to bottom of the triangle below: +* [refer to the problem link] +* +* NOTE: As there are only 16384 routes, it is possible +* to solve this problem by trying every route. +* However, Problem 67, is the same challenge with a triangle +* containing one-hundred rows; it cannot be solved by brute force, +* and requires a clever method! ;o) +* +* @author ddaniel27 + */ +package problem18 + +import "strconv" + +type ( + NodeValue int + NodeType string + + Node interface { + Value() NodeValue + GetID() int + Left() Node + Right() Node + LeftIsNil() bool + RightIsNil() bool + HasSpace() bool + Kind() string + SetParent(Node) + CreateChild(NodeValue, int) Node + Insert(Node) + } +) + +func Problem18(input []string, deep int) int { + tree := &Tree{} + + for _, num := range input { + v, err := strconv.Atoi(num) + if err != nil { + panic(err) + } + + tree.BFSInsert(NodeValue(v)) + } + + return tree.MaxPathValueSearch(deep) +} diff --git a/project_euler/problem_18/problem18_test.go b/project_euler/problem_18/problem18_test.go new file mode 100644 index 000000000..50baab2ed --- /dev/null +++ b/project_euler/problem_18/problem18_test.go @@ -0,0 +1,42 @@ +package problem18 + +import "testing" + +// Tests +func TestProblem18_Func(t *testing.T) { + tests := []struct { + name string + input []string + deep int + expected int + }{ + { + name: "Test case 1", + input: problem18_test_parsed_string, + deep: 2, + expected: 23, + }, + { + name: "Test case 2", + input: problem18_input_parsed_string, + deep: 2, + expected: 1074, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := Problem18(test.input, test.deep) + if actual != test.expected { + t.Errorf("Expected %d, but got %d", test.expected, actual) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem18_Func(b *testing.B) { + for i := 0; i < b.N; i++ { + Problem18(problem18_input_parsed_string, 2) + } +} diff --git a/project_euler/problem_18/root.go b/project_euler/problem_18/root.go new file mode 100644 index 000000000..4ee018629 --- /dev/null +++ b/project_euler/problem_18/root.go @@ -0,0 +1,62 @@ +package problem18 + +type Root struct { + ID int + NodeValue NodeValue + NodeLeft *Edge + NodeRight *Edge +} + +func (n *Root) Value() NodeValue { + return n.NodeValue +} + +func (n *Root) Left() Node { + return n.NodeLeft +} + +func (n *Root) Right() Node { + return n.NodeRight +} + +func (n *Root) Kind() string { + return "root" +} + +func (n *Root) CreateChild(value NodeValue, id int) Node { + return &Edge{ + ID: id, + NodeValue: value, + Parent: n, + NodeLeft: nil, + NodeRight: nil, + } +} + +func (n *Root) GetID() int { + return n.ID +} + +func (n *Root) Insert(node Node) { + if n.NodeLeft == nil { + n.NodeLeft = node.(*Edge) + } else { + n.NodeRight = node.(*Edge) + } +} + +func (n *Root) HasSpace() bool { + return n.NodeLeft == nil || n.NodeRight == nil +} + +func (n *Root) LeftIsNil() bool { + return n.NodeLeft == nil +} + +func (n *Root) RightIsNil() bool { + return n.NodeRight == nil +} + +func (n *Root) SetParent(node Node) { + panic("Root node cannot have a parent") +} diff --git a/project_euler/problem_18/tree.go b/project_euler/problem_18/tree.go new file mode 100644 index 000000000..6f98f3b1c --- /dev/null +++ b/project_euler/problem_18/tree.go @@ -0,0 +1,229 @@ +package problem18 + +import ( + "fmt" + "strings" +) + +type Tree struct { + Root *Root + Nodes map[int]struct{} + ID int +} + +func (t *Tree) BFSInsert(value NodeValue) { + t.Nodes = make(map[int]struct{}) // Reset the nodes map + + if t.Root == nil { + t.Root = &Root{NodeValue: value, ID: 0} + t.ID = 1 + return + } + + queue := make([]Node, 0) + queue = append(queue, t.Root) + t.isInQueue(t.Root.GetID()) + + head := 0 + + for head < len(queue) { + current := queue[head] + head++ + childNode := current.CreateChild(value, t.ID) + + if current.HasSpace() { + current.Insert(childNode) + t.ID++ + return + } + + if !t.isInQueue(current.Left().GetID()) { // Avoid duplicates + queue = append(queue, current.Left()) + } + if !t.isInQueue(current.Right().GetID()) { + queue = append(queue, current.Right()) + } + } +} + +// MaxPathValueSearch is a method that searches the maximum path value in a tree +// given a certain depth +func (r *Tree) MaxPathValueSearch(deep int) int { + if r.Root == nil { + return 0 + } + + type DFSNode struct { + Node Node + StepsLeft int + Sum int + } + + var pivot Node = r.Root + pivotEnded := false + maxPathValue := int(pivot.Value()) + + stack := make([]DFSNode, 0) + + for !pivotEnded { + stack = append(stack, DFSNode{Node: pivot, StepsLeft: deep, Sum: maxPathValue}) + + for len(stack) > 0 { + current := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + // If we run out of steps, we check the sum of the path, + // update the maxPathValue if necessary and continue + if current.StepsLeft == 0 { + if current.Sum > maxPathValue { + maxPathValue = current.Sum + pivot = current.Node + } + continue + } + + if !current.Node.RightIsNil() { + stack = append(stack, DFSNode{ + Node: current.Node.Right(), + StepsLeft: current.StepsLeft - 1, + Sum: current.Sum + int(current.Node.Right().Value()), + }) + } + + // If the left child is nil, we have reached the end of the path + if !current.Node.LeftIsNil() { + stack = append(stack, DFSNode{ + Node: current.Node.Left(), + StepsLeft: current.StepsLeft - 1, + Sum: current.Sum + int(current.Node.Left().Value()), + }) + } else { + if current.Sum > maxPathValue { + maxPathValue = current.Sum + pivot = current.Node + } + } + } + + // If we don't have reached the end of the left side of the tree, + // we continue with the search using the pivot node + // We use the left child only because how the tree is built + if pivot.LeftIsNil() { + pivotEnded = true + } + } + + return maxPathValue +} + +// PrintReport is a method that prints a report of the tree +// Node by node +func (t *Tree) PrintReport() { + t.Nodes = make(map[int]struct{}) + if t.Root == nil { + return + } + + queue := make([]Node, 0) + queue = append(queue, t.Root) + + head := 0 + + for head < len(queue) { + current := queue[head] + head++ + + print("ID:", current.GetID()) + print(", Current node:", current.Value()) + + if !current.LeftIsNil() { + print(", Left Child:", current.Left().Value()) + + if !t.isInQueue(current.Left().GetID()) { + queue = append(queue, current.Left()) + } + } + + if !current.RightIsNil() { + print(", Right Child:", current.Right().Value()) + + if !t.isInQueue(current.Right().GetID()) { + queue = append(queue, current.Right()) + } + } + + println() + } +} + +// PrintPyramid is a method that prints the tree as a pyramid +func (t *Tree) PrintPyramid() { + t.Nodes = make(map[int]struct{}) + if t.Root == nil { + return + } + + queue := []Node{t.Root} + levels := []int{0} + outputByLevel := []string{} + + head := 0 + currentLevel := 0 + + output := "" + + for head < len(queue) { + current := queue[head] + level := levels[head] + head++ + + // Level change + if level > currentLevel { + currentLevel = level + outputByLevel = append(outputByLevel, output+"\n") + output = "" + } + + // Add current node to the output + if current.Value() < 10 { + output += fmt.Sprintf("0%d ", current.Value()) + } else { + output += fmt.Sprintf("%d ", current.Value()) + } + + // Add children to the queue + if !current.LeftIsNil() { + if !t.isInQueue(current.Left().GetID()) { + queue = append(queue, current.Left()) + levels = append(levels, level+1) + } + } + + if !current.RightIsNil() { + if !t.isInQueue(current.Right().GetID()) { + queue = append(queue, current.Right()) + levels = append(levels, level+1) + } + } + } + // Add the last level + outputByLevel = append(outputByLevel, output+"\n") + + totalLevels := len(outputByLevel) + + // Print the pyramid + for i, level := range outputByLevel { + str := strings.Repeat(" ", 2*(totalLevels-i)) + level + print(str) + } +} + +// isInQueue is a method that avoids duplicates in the tree +func (n *Tree) isInQueue(nv int) bool { + if _, ok := n.Nodes[nv]; ok { + return true + } + + n.Nodes[nv] = struct{}{} + return false +} diff --git a/project_euler/problem_19/problem19.go b/project_euler/problem_19/problem19.go new file mode 100644 index 000000000..9a86f0dea --- /dev/null +++ b/project_euler/problem_19/problem19.go @@ -0,0 +1,63 @@ +package problem19 + +/** +* Problem 19 - Counting Sundays +* @see {@link https://projecteuler.net/problem=19} +* +* You are given the following information, +* but you may prefer to do some research for yourself. +* +* 1 Jan 1900 was a Monday. +* Thirty days has September, +* April, June and November. +* All the rest have thirty-one, +* Saving February alone, +* Which has twenty-eight, rain or shine. +* And on leap years, twenty-nine. +* A leap year occurs on any year evenly divisible by 4, +* but not on a century unless it is divisible by 400. +* +* How many Sundays fell on the first of the month during +* the twentieth century (1 Jan 1901 to 31 Dec 2000)? +* +* @author ddaniel27 + */ + +func Problem19() int { + count := 0 + dayOfWeek := 2 // 1 Jan 1901 was a Tuesday + + for year := 1901; year <= 2000; year++ { + for month := 1; month <= 12; month++ { + if dayOfWeek == 0 { + count++ + } + + daysInMonth := 31 + switch month { + case 4, 6, 9, 11: + daysInMonth = 30 + case 2: + if IsLeapYear(year) { + daysInMonth = 29 + } else { + daysInMonth = 28 + } + } + + dayOfWeek = (dayOfWeek + daysInMonth) % 7 + } + } + + return count +} + +func IsLeapYear(year int) bool { + if year%4 == 0 { + if year%100 == 0 { + return year%400 == 0 + } + return true + } + return false +} diff --git a/project_euler/problem_19/problem19_test.go b/project_euler/problem_19/problem19_test.go new file mode 100644 index 000000000..d26c10938 --- /dev/null +++ b/project_euler/problem_19/problem19_test.go @@ -0,0 +1,29 @@ +package problem19 + +import "testing" + +// Tests +func TestProblem19_Func(t *testing.T) { + tests := []struct { + name string + expected int + }{ + {"Problem 19 - Counting Sundays", 171}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := Problem19() + if got != test.expected { + t.Errorf("Problem19() = got %v, want %v", got, test.expected) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem19_Func(b *testing.B) { + for i := 0; i < b.N; i++ { + Problem19() + } +} diff --git a/project_euler/problem_2/problem2.go b/project_euler/problem_2/problem2.go new file mode 100644 index 000000000..e8eaeb85e --- /dev/null +++ b/project_euler/problem_2/problem2.go @@ -0,0 +1,30 @@ +/** +* Problem 2 - Even Fibonacci numbers +* @see {@link https://projecteuler.net/problem=2} +* +* Each new term in the Fibonacci sequence is generated by adding the previous two terms. +* By starting with 1 and 2, the first 10 terms will be: +* +* 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... +* +* By considering the terms in the Fibonacci sequence whose values do not exceed four million, +* find the sum of the even-valued terms. +* +* @author ddaniel27 + */ +package problem2 + +func Problem2(n uint) uint { + sum := uint(0) + a, b := uint(1), uint(2) + + for b < n { + if b%2 == 0 { + sum += b + } + + a, b = b, a+b + } + + return sum +} diff --git a/project_euler/problem_2/problem2_test.go b/project_euler/problem_2/problem2_test.go new file mode 100644 index 000000000..32c3834c6 --- /dev/null +++ b/project_euler/problem_2/problem2_test.go @@ -0,0 +1,45 @@ +package problem2 + +import "testing" + +// Tests +func TestProblem2_Func(t *testing.T) { + tests := []struct { + name string + input uint + want uint + }{ + { + name: "Testcase 1 - input 10", + input: 10, + want: 10, + }, + { + name: "Testcase 2 - input 100", + input: 100, + want: 44, + }, + { + name: "Testcase 3 - input 4e6", + input: 4e6, + want: 4613732, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Problem2(tt.input) + + if n != tt.want { + t.Errorf("Problem2() = %v, want %v", n, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem2(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Problem2(4e6) + } +} diff --git a/project_euler/problem_20/problem20.go b/project_euler/problem_20/problem20.go new file mode 100644 index 000000000..fc731da6b --- /dev/null +++ b/project_euler/problem_20/problem20.go @@ -0,0 +1,37 @@ +/** +* Problem 20 - Factorial digit sum +* @see {@link https://projecteuler.net/problem=20} +* +* n! means n × (n − 1) × ... × 3 × 2 × 1 +* +* For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800, +* and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27. +* +* Find the sum of the digits in the number 100! +* +* @author ddaniel27 + */ +package problem20 + +import "math/big" + +func Problem20(input int) int { + factorial := bigFactorial(input) + sum := 0 + for _, digit := range factorial.String() { + sum += int(digit - '0') + } + return sum +} + +// bigFactorial returns the factorial of n as a big.Int +// Use big package to handle large numbers +func bigFactorial(n int) *big.Int { + if n < 0 { + return big.NewInt(0) + } + if n == 0 { + return big.NewInt(1) + } + return big.NewInt(0).Mul(big.NewInt(int64(n)), bigFactorial(n-1)) +} diff --git a/project_euler/problem_20/problem20_test.go b/project_euler/problem_20/problem20_test.go new file mode 100644 index 000000000..f1ac574c6 --- /dev/null +++ b/project_euler/problem_20/problem20_test.go @@ -0,0 +1,31 @@ +package problem20 + +import "testing" + +// Tests +func TestProblem20_Func(t *testing.T) { + tests := []struct { + name string + input int + expected int + }{ + {"Problem 20 - Factorial digit sum", 10, 27}, + {"Problem 20 - Factorial digit sum", 100, 648}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := Problem20(test.input) + if got != test.expected { + t.Errorf("Problem20() = got %v, want %v", got, test.expected) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem20_Func(b *testing.B) { + for i := 0; i < b.N; i++ { + Problem20(100) + } +} diff --git a/project_euler/problem_3/problem3.go b/project_euler/problem_3/problem3.go new file mode 100644 index 000000000..f13525fb6 --- /dev/null +++ b/project_euler/problem_3/problem3.go @@ -0,0 +1,24 @@ +/** +* Problem 3 - Largest prime factor +* @see {@link https://projecteuler.net/problem=3} +* +* The prime factors of 13195 are 5, 7, 13 and 29. +* What is the largest prime factor of the number 600851475143 ? +* +* @author ddaniel27 + */ +package problem3 + +func Problem3(n uint) uint { + i := uint(2) + + for n > 1 { + if n%i == 0 { + n /= i + } else { + i++ + } + } + + return i +} diff --git a/project_euler/problem_3/problem3_test.go b/project_euler/problem_3/problem3_test.go new file mode 100644 index 000000000..d00379f8e --- /dev/null +++ b/project_euler/problem_3/problem3_test.go @@ -0,0 +1,40 @@ +package problem3 + +import "testing" + +// Tests +func TestProblem3_Func(t *testing.T) { + tests := []struct { + name string + input uint + want uint + }{ + { + name: "Testcase 1 - input 13195", + input: 13195, + want: 29, + }, + { + name: "Testcase 2 - input 600851475143", + input: 600851475143, + want: 6857, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Problem3(tt.input) + + if n != tt.want { + t.Errorf("Problem3() = %v, want %v", n, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem3(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Problem3(600851475143) + } +} diff --git a/project_euler/problem_4/problem4.go b/project_euler/problem_4/problem4.go new file mode 100644 index 000000000..b48d81e41 --- /dev/null +++ b/project_euler/problem_4/problem4.go @@ -0,0 +1,33 @@ +/** +* Problem 4 - Largest palindrome product +* @see {@link https://projecteuler.net/problem=4} +* +* A palindromic number reads the same both ways. +* The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99. +* Find the largest palindrome made from the product of two 3-digit numbers. +* +* @author ddaniel27 + */ +package problem4 + +import ( + "fmt" + + "github.com/TheAlgorithms/Go/strings/palindrome" +) + +func Problem4() uint { + max := uint(0) + + for i := 999; i >= 100; i-- { + for j := 999; j >= 100; j-- { + n := uint(i * j) + + if palindrome.IsPalindrome(fmt.Sprintf("%d", n)) && n > max { + max = n + } + } + } + + return max +} diff --git a/project_euler/problem_4/problem4_test.go b/project_euler/problem_4/problem4_test.go new file mode 100644 index 000000000..6b91fd2b0 --- /dev/null +++ b/project_euler/problem_4/problem4_test.go @@ -0,0 +1,33 @@ +package problem4 + +import "testing" + +// Tests +func TestProblem4_Func(t *testing.T) { + tests := []struct { + name string + want uint + }{ + { + name: "Testcase 1", + want: 906609, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Problem4() + + if n != tt.want { + t.Errorf("Problem4() = %v, want %v", n, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem4(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Problem4() + } +} diff --git a/project_euler/problem_5/problem5.go b/project_euler/problem_5/problem5.go new file mode 100644 index 000000000..dad415738 --- /dev/null +++ b/project_euler/problem_5/problem5.go @@ -0,0 +1,33 @@ +/** +* Problem 5 - Smallest multiple +* @see {@link https://projecteuler.net/problem=5} +* +* 2520 is the smallest number that can be divided by +* each of the numbers from 1 to 10 without any remainder. +* What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20? +* +* @author ddaniel27 + */ +package problem5 + +func Problem5(limit uint) uint { + n := limit * limit + + for { + if isDivisible(n, limit) { + return n + } + + n++ + } +} + +func isDivisible(n, limit uint) bool { + for i := uint(1); i <= limit; i++ { + if n%i != 0 { + return false + } + } + + return true +} diff --git a/project_euler/problem_5/problem5_test.go b/project_euler/problem_5/problem5_test.go new file mode 100644 index 000000000..43e7d8ec9 --- /dev/null +++ b/project_euler/problem_5/problem5_test.go @@ -0,0 +1,45 @@ +package problem5 + +import "testing" + +// Tests +func TestProblem5_Func(t *testing.T) { + tests := []struct { + name string + input uint + want uint + }{ + { + name: "Testcase 1 - input 10", + input: 10, + want: 2520, + }, + { + name: "Testcase 2 - input 20", + input: 20, + want: 232792560, + }, + { + name: "Testcase 3 - input 5", + input: 5, + want: 60, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Problem5(tt.input) + + if n != tt.want { + t.Errorf("Problem5() = %v, want %v", n, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem5(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Problem5(20) + } +} diff --git a/project_euler/problem_6/problem6.go b/project_euler/problem_6/problem6.go new file mode 100644 index 000000000..83287b87d --- /dev/null +++ b/project_euler/problem_6/problem6.go @@ -0,0 +1,33 @@ +/** +* Problem 6 - Sum square difference +* @see {@link https://projecteuler.net/problem=6} +* +* The sum of the squares of the first ten natural numbers is, +* 1^2 + 2^2 + ... + 10^2 = 385 +* +* The square of the sum of the first ten natural numbers is, +* (1 + 2 + ... + 10)^2 = 55^2 = 3025 +* +* Hence the difference between the sum of the squares of the first ten natural numbers +* and the square of the sum is 3025 − 385 = 2640. +* +* Find the difference between the sum of the squares of the first one hundred natural numbers +* and the square of the sum. +* +* @author ddaniel27 + */ +package problem6 + +func Problem6(n uint) uint { + sumOfSquares := uint(0) + squareOfSum := uint(0) + + for i := uint(1); i <= n; i++ { + sumOfSquares += i * i + squareOfSum += i + } + + squareOfSum *= squareOfSum + + return squareOfSum - sumOfSquares +} diff --git a/project_euler/problem_6/problem6_test.go b/project_euler/problem_6/problem6_test.go new file mode 100644 index 000000000..afa4e5fb7 --- /dev/null +++ b/project_euler/problem_6/problem6_test.go @@ -0,0 +1,40 @@ +package problem6 + +import "testing" + +// Tests +func TestProblem6_Func(t *testing.T) { + tests := []struct { + name string + input uint + want uint + }{ + { + name: "Testcase 1 - input 10", + input: 10, + want: 2640, + }, + { + name: "Testcase 2 - input 100", + input: 100, + want: 25164150, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Problem6(tt.input) + + if n != tt.want { + t.Errorf("Problem6() = %v, want %v", n, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem6(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Problem6(100) + } +} diff --git a/project_euler/problem_7/problem7.go b/project_euler/problem_7/problem7.go new file mode 100644 index 000000000..481844fed --- /dev/null +++ b/project_euler/problem_7/problem7.go @@ -0,0 +1,27 @@ +/** +* Problem 7 - 10001st prime +* @see {@link https://projecteuler.net/problem=7} +* +* By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, +* we can see that the 6th prime is 13. +* +* What is the 10 001st prime number? +* +* @author ddaniel27 + */ +package problem7 + +import "github.com/TheAlgorithms/Go/math/prime" + +func Problem7(n uint) int64 { + count, i := uint(0), int64(1) + + for count < n { + i++ + if prime.OptimizedTrialDivision(i) { + count++ + } + } + + return i +} diff --git a/project_euler/problem_7/problem7_test.go b/project_euler/problem_7/problem7_test.go new file mode 100644 index 000000000..d2ab03e74 --- /dev/null +++ b/project_euler/problem_7/problem7_test.go @@ -0,0 +1,40 @@ +package problem7 + +import "testing" + +// Tests +func TestProblem7_Func(t *testing.T) { + tests := []struct { + name string + input uint + want int64 + }{ + { + name: "Testcase 1 - input 6", + input: 6, + want: 13, + }, + { + name: "Testcase 2 - input 10001", + input: 10001, + want: 104743, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Problem7(tt.input) + + if n != tt.want { + t.Errorf("Problem7() = %v, want %v", n, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem7(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Problem7(10001) + } +} diff --git a/project_euler/problem_8/problem8.go b/project_euler/problem_8/problem8.go new file mode 100644 index 000000000..0c800dcdb --- /dev/null +++ b/project_euler/problem_8/problem8.go @@ -0,0 +1,33 @@ +/** +* Problem 8 - Largest product in a series +* @see {@link https://projecteuler.net/problem=8} +* +* The four adjacent digits in the 1000-digit number that +* have the greatest product are 9 × 9 × 8 × 9 = 5832. +* Find the thirteen adjacent digits in the 1000-digit number +* that have the greatest product. What is the value of this product? +* +* @author ddaniel27 + */ +package problem8 + +const number = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450" + +func Problem8(window int) uint { + max := uint(0) + + for i := 0; i < len(number)-window; i++ { + product := uint(1) + + for j := 0; j < window; j++ { + n := uint(number[i+j] - '0') + product *= n + } + + if product > max { + max = product + } + } + + return max +} diff --git a/project_euler/problem_8/problem8_test.go b/project_euler/problem_8/problem8_test.go new file mode 100644 index 000000000..eecde4bc6 --- /dev/null +++ b/project_euler/problem_8/problem8_test.go @@ -0,0 +1,40 @@ +package problem8 + +import "testing" + +// Tests +func TestProblem8_Func(t *testing.T) { + tests := []struct { + name string + input int + want uint + }{ + { + name: "Testcase 1 - input 4", + input: 4, + want: 5832, + }, + { + name: "Testcase 2 - input 13", + input: 13, + want: 23514624000, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Problem8(tt.input) + + if n != tt.want { + t.Errorf("Problem8() = %v, want %v", n, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem8(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Problem8(13) + } +} diff --git a/project_euler/problem_9/problem9.go b/project_euler/problem_9/problem9.go new file mode 100644 index 000000000..0f784c022 --- /dev/null +++ b/project_euler/problem_9/problem9.go @@ -0,0 +1,29 @@ +/** +* Problem 9 - Special Pythagorean triplet +* @see {@link https://projecteuler.net/problem=9} +* +* A Pythagorean triplet is a set of three natural numbers, a < b < c, for which, +* a^2 + b^2 = c^2 +* +* For example, 3^2 + 4^2 = 9 + 16 = 25 = 5^2. +* +* There exists exactly one Pythagorean triplet for which a + b + c = 1000. +* Find the product abc. +* +* @author ddaniel27 + */ +package problem9 + +func Problem9() uint { + for a := uint(1); a < 1000; a++ { + for b := a + 1; b < 1000; b++ { + c := 1000 - a - b + + if a*a+b*b == c*c { + return a * b * c + } + } + } + + return 0 +} diff --git a/project_euler/problem_9/problem9_test.go b/project_euler/problem_9/problem9_test.go new file mode 100644 index 000000000..d75226103 --- /dev/null +++ b/project_euler/problem_9/problem9_test.go @@ -0,0 +1,33 @@ +package problem9 + +import "testing" + +// Tests +func TestProblem9_Func(t *testing.T) { + tests := []struct { + name string + want uint + }{ + { + name: "Testcase 1", + want: 31875000, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Problem9() + + if n != tt.want { + t.Errorf("Problem9() = %v, want %v", n, tt.want) + } + }) + } +} + +// Benchmarks +func BenchmarkProblem9(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = Problem9() + } +} diff --git a/search/binary.go b/search/binary.go index 3b9241af6..604341edb 100644 --- a/search/binary.go +++ b/search/binary.go @@ -37,7 +37,7 @@ func BinaryIterative(array []int, target int) (int, error) { return -1, ErrNotFound } -// Returns index to the first element in the range [0, len(array)-1] that is not less than (i.e. greater or equal to) target. +// LowerBound returns index to the first element in the range [0, len(array)-1] that is not less than (i.e. greater or equal to) target. // return -1 and ErrNotFound if no such element is found. func LowerBound(array []int, target int) (int, error) { startIndex := 0 @@ -59,7 +59,7 @@ func LowerBound(array []int, target int) (int, error) { return startIndex, nil } -// Returns index to the first element in the range [lowIndex, len(array)-1] that is greater than target. +// UpperBound returns index to the first element in the range [lowIndex, len(array)-1] that is greater than target. // return -1 and ErrNotFound if no such element is found. func UpperBound(array []int, target int) (int, error) { startIndex := 0 diff --git a/search/binary_test.go b/search/binary_test.go index 24e2a0c41..99f907797 100644 --- a/search/binary_test.go +++ b/search/binary_test.go @@ -1,6 +1,9 @@ package search -import "testing" +import ( + "errors" + "testing" +) func TestBinary(t *testing.T) { for _, test := range searchTests { @@ -8,7 +11,7 @@ func TestBinary(t *testing.T) { if actualValue != test.expected { t.Errorf("test '%s' failed: input array '%v' with key '%d', expected '%d', get '%d'", test.name, test.data, test.key, test.expected, actualValue) } - if actualError != test.expectedError { + if !errors.Is(test.expectedError, actualError) { t.Errorf("test '%s' failed: input array '%v' with key '%d', expected error '%s', get error '%s'", test.name, test.data, test.key, test.expectedError, actualError) } } @@ -20,7 +23,7 @@ func TestBinaryIterative(t *testing.T) { if actualValue != test.expected { t.Errorf("test '%s' failed: input array '%v' with key '%d', expected '%d', get '%d'", test.name, test.data, test.key, test.expected, actualValue) } - if actualError != test.expectedError { + if !errors.Is(test.expectedError, actualError) { t.Errorf("test '%s' failed: input array '%v' with key '%d', expected error '%s', get error '%s'", test.name, test.data, test.key, test.expectedError, actualError) } } @@ -32,7 +35,7 @@ func TestLowerBound(t *testing.T) { if actualValue != test.expected { t.Errorf("test '%s' failed: input array '%v' with key '%d', expected '%d', get '%d'", test.name, test.data, test.key, test.expected, actualValue) } - if actualError != test.expectedError { + if !errors.Is(test.expectedError, actualError) { t.Errorf("test '%s' failed: input array '%v' with key '%d', expected error '%s', get error '%s'", test.name, test.data, test.key, test.expectedError, actualError) } } @@ -44,7 +47,7 @@ func TestUpperBound(t *testing.T) { if actualValue != test.expected { t.Errorf("test '%s' failed: input array '%v' with key '%d', expected '%d', get '%d'", test.name, test.data, test.key, test.expected, actualValue) } - if actualError != test.expectedError { + if !errors.Is(test.expectedError, actualError) { t.Errorf("test '%s' failed: input array '%v' with key '%d', expected error '%s', get error '%s'", test.name, test.data, test.key, test.expectedError, actualError) } } diff --git a/search/jump.go b/search/jump.go index 163dea55d..169620d9a 100644 --- a/search/jump.go +++ b/search/jump.go @@ -5,6 +5,8 @@ // before performing a linear search // reference: https://en.wikipedia.org/wiki/Jump_search // see jump_test.go for a test implementation, test function TestJump +// time complexity: O(sqrt(n)) +// space complexity: O(1) package search diff --git a/sort/binaryinsertionsort.go b/sort/binaryinsertionsort.go new file mode 100644 index 000000000..98b6c5a3d --- /dev/null +++ b/sort/binaryinsertionsort.go @@ -0,0 +1,35 @@ +// Binary Insertion Sort +// description: Implementation of binary insertion sort in Go +// details: Binary Insertion Sort is a variation of +// Insertion sort in which proper location to +// insert the selected element is found using the +// Binary search algorithm. +// ref: https://www.geeksforgeeks.org/binary-insertion-sort + +package sort + +import "github.com/TheAlgorithms/Go/constraints" + +func BinaryInsertion[T constraints.Ordered](arr []T) []T { + for currentIndex := 1; currentIndex < len(arr); currentIndex++ { + temporary := arr[currentIndex] + low := 0 + high := currentIndex - 1 + + for low <= high { + mid := low + (high-low)/2 + if arr[mid] > temporary { + high = mid - 1 + } else { + low = mid + 1 + } + } + + for itr := currentIndex; itr > low; itr-- { + arr[itr] = arr[itr-1] + } + + arr[low] = temporary + } + return arr +} diff --git a/sort/bogosort.go b/sort/bogosort.go new file mode 100644 index 000000000..165397155 --- /dev/null +++ b/sort/bogosort.go @@ -0,0 +1,39 @@ +// This is a pure Go implementation of the bogosort algorithm, +// also known as permutation sort, stupid sort, slowsort, shotgun sort, or monkey sort. +// Bogosort generates random permutations until it guesses the correct one. +// worst-case time complexity: O((n+1)!) +// best-case time complexity: O(n) +// More info on: https://en.wikipedia.org/wiki/Bogosort + +package sort + +import ( + "math/rand" + + "github.com/TheAlgorithms/Go/constraints" +) + +func isSorted[T constraints.Number](arr []T) bool { + for i := 0; i < len(arr)-1; i++ { + if arr[i] > arr[i+1] { + return false + } + } + + return true +} + +func shuffle[T constraints.Number](arr []T) { + for i := range arr { + j := rand.Intn(i + 1) + arr[i], arr[j] = arr[j], arr[i] + } +} + +func Bogo[T constraints.Number](arr []T) []T { + for !isSorted(arr) { + shuffle(arr) + } + + return arr +} diff --git a/sort/circlesort.go b/sort/circlesort.go new file mode 100644 index 000000000..c18b0d408 --- /dev/null +++ b/sort/circlesort.go @@ -0,0 +1,44 @@ +// Package sort implements various sorting algorithms. +package sort + +import "github.com/TheAlgorithms/Go/constraints" + +// Circle sorts an array using the circle sort algorithm. +func Circle[T constraints.Ordered](arr []T) []T { + if len(arr) == 0 { + return arr + } + for doSort(arr, 0, len(arr)-1) { + } + return arr +} + +// doSort is the recursive function that implements the circle sort algorithm. +func doSort[T constraints.Ordered](arr []T, left, right int) bool { + if left == right { + return false + } + swapped := false + low := left + high := right + + for low < high { + if arr[low] > arr[high] { + arr[low], arr[high] = arr[high], arr[low] + swapped = true + } + low++ + high-- + } + + if low == high && arr[low] > arr[high+1] { + arr[low], arr[high+1] = arr[high+1], arr[low] + swapped = true + } + + mid := left + (right-left)/2 + leftHalf := doSort(arr, left, mid) + rightHalf := doSort(arr, mid+1, right) + + return swapped || leftHalf || rightHalf +} diff --git a/sort/cocktailsort.go b/sort/cocktailsort.go new file mode 100644 index 000000000..6aee61681 --- /dev/null +++ b/sort/cocktailsort.go @@ -0,0 +1,54 @@ +// Implementation of Cocktail sorting +// reference: https://en.wikipedia.org/wiki/Cocktail_shaker_sort + +package sort + +import "github.com/TheAlgorithms/Go/constraints" + +// Cocktail sort is a variation of bubble sort, operating in two directions (beginning to end, end to beginning) +func Cocktail[T constraints.Ordered](arr []T) []T { + if len(arr) == 0 { // ignore 0 length arrays + return arr + } + + swapped := true // true if swapped two or more elements in the last loop + // if it loops through the array without swapping, the array is sorted + + // start and end indexes, this will be updated excluding already sorted elements + start := 0 + end := len(arr) - 1 + + for swapped { + swapped = false + var new_start int + var new_end int + + for i := start; i < end; i++ { // first loop, from start to end + if arr[i] > arr[i+1] { // if current and next elements are unordered + arr[i], arr[i+1] = arr[i+1], arr[i] // swap two elements + new_end = i + swapped = true + } + } + + end = new_end + + if !swapped { // early exit, skipping the second loop + break + } + + swapped = false + + for i := end; i > start; i-- { // second loop, from end to start + if arr[i] < arr[i-1] { // same process of the first loop, now going 'backwards' + arr[i], arr[i-1] = arr[i-1], arr[i] + new_start = i + swapped = true + } + } + + start = new_start + } + + return arr +} diff --git a/sort/combSort.go b/sort/combSort.go index 76e523e34..15c3e8bab 100644 --- a/sort/combSort.go +++ b/sort/combSort.go @@ -1,4 +1,7 @@ // Implementation of comb sort algorithm, an improvement of bubble sort +// average time complexity: O(n^2 / 2^p) where p is the number of increments +// worst time complexity: O(n^2) +// space complexity: O(1) // Reference: https://www.geeksforgeeks.org/comb-sort/ package sort diff --git a/sort/countingsort.go b/sort/countingsort.go index 6961efff8..55fbac7c0 100644 --- a/sort/countingsort.go +++ b/sort/countingsort.go @@ -1,6 +1,9 @@ // countingsort.go // description: Implementation of counting sort algorithm // details: A simple counting sort algorithm implementation +// worst-case time complexity: O(n + k) where n is the number of elements in the input array and k is the range of the input +// average-case time complexity: O(n + k) where n is the number of elements in the input array and k is the range of the input +// space complexity: O(n + k) // author [Phil](https://github.com/pschik) // see sort_test.go for a test implementation, test function TestQuickSort @@ -8,16 +11,27 @@ package sort import "github.com/TheAlgorithms/Go/constraints" -func Count[T constraints.Number](data []int) []int { - var aMin, aMax = -1000, 1000 +func Count[T constraints.Integer](data []T) []T { + if len(data) == 0 { + return data + } + var aMin, aMax = data[0], data[0] + for _, x := range data { + if x < aMin { + aMin = x + } + if x > aMax { + aMax = x + } + } count := make([]int, aMax-aMin+1) for _, x := range data { - count[x-aMin]++ // this is the reason for having only Number constraint instead of Ordered. + count[x-aMin]++ // this is the reason for having only Integer constraint instead of Ordered. } z := 0 for i, c := range count { for c > 0 { - data[z] = i + aMin + data[z] = T(i) + aMin z++ c-- } diff --git a/sort/exchangesort.go b/sort/exchangesort.go index cbf6b9b00..43ada90df 100644 --- a/sort/exchangesort.go +++ b/sort/exchangesort.go @@ -1,4 +1,7 @@ // Implementation of exchange sort algorithm, a variant of bubble sort +// average time complexity: O(n^2) +// worst time complexity: O(n^2) +// space complexity: O(1) // Reference: https://en.wikipedia.org/wiki/Sorting_algorithm#Exchange_sort package sort diff --git a/sort/heapsort.go b/sort/heapsort.go index 741b4e4e8..f4e581306 100644 --- a/sort/heapsort.go +++ b/sort/heapsort.go @@ -1,3 +1,9 @@ +// heapsort.go +// description: Implementation of heap sort algorithm +// worst-case time complexity: O(n log n) +// average-case time complexity: O(n log n) +// space complexity: O(1) + package sort import "github.com/TheAlgorithms/Go/constraints" diff --git a/sort/insertionsort.go b/sort/insertionsort.go index 13e45eeb9..55ac3130f 100644 --- a/sort/insertionsort.go +++ b/sort/insertionsort.go @@ -1,3 +1,9 @@ +// insertionsort.go +// description: Implementation of insertion sort algorithm +// worst-case time complexity: O(n^2) +// average-case time complexity: O(n^2) +// space complexity: O(1) + package sort import "github.com/TheAlgorithms/Go/constraints" diff --git a/sort/mergesort.go b/sort/mergesort.go index 9a875b428..550b13eea 100644 --- a/sort/mergesort.go +++ b/sort/mergesort.go @@ -1,3 +1,9 @@ +// mergesort.go +// description: Implementation of merge sort algorithm +// worst-case time complexity: O(n log n) +// average-case time complexity: O(n log n) +// space complexity: O(n) + package sort import ( diff --git a/sort/oddevensort.go b/sort/oddevensort.go new file mode 100644 index 000000000..5e98ef409 --- /dev/null +++ b/sort/oddevensort.go @@ -0,0 +1,39 @@ +// oddevensort.go +// Implementation of Odd-Even Sort (Brick Sort) +// Reference: https://en.wikipedia.org/wiki/Odd%E2%80%93even_sort + +package sort + +import "github.com/TheAlgorithms/Go/constraints" + +// OddEvenSort performs the odd-even sort algorithm on the given array. +// It is a variation of bubble sort that compares adjacent pairs, alternating +// between odd and even indexed elements in each pass until the array is sorted. +func OddEvenSort[T constraints.Ordered](arr []T) []T { + if len(arr) == 0 { // handle empty array + return arr + } + + swapped := true + for swapped { + swapped = false + + // Perform "odd" indexed pass + for i := 1; i < len(arr)-1; i += 2 { + if arr[i] > arr[i+1] { + arr[i], arr[i+1] = arr[i+1], arr[i] + swapped = true + } + } + + // Perform "even" indexed pass + for i := 0; i < len(arr)-1; i += 2 { + if arr[i] > arr[i+1] { + arr[i], arr[i+1] = arr[i+1], arr[i] + swapped = true + } + } + } + + return arr +} diff --git a/sort/patiencesort.go b/sort/patiencesort.go index 746c84931..f1b2b46f2 100644 --- a/sort/patiencesort.go +++ b/sort/patiencesort.go @@ -4,6 +4,9 @@ // GeeksForGeeks article : https://www.geeksforgeeks.org/patience-sorting/ // Wikipedia article: https://en.wikipedia.org/wiki/Patience_sorting // authors [guuzaa](https://github.com/guuzaa) +// worst-case time complexity: O(n log n) +// average time complexity: O(n log n) +// space complexity: O(n) // see patiencesort.go package sort diff --git a/sort/pigeonholesort.go b/sort/pigeonholesort.go index 12941cdc9..5e735c8fd 100644 --- a/sort/pigeonholesort.go +++ b/sort/pigeonholesort.go @@ -1,5 +1,7 @@ // Pigeonhole algorithm's working at wikipedia. // https://en.wikipedia.org/wiki/Pigeonhole_sort +// time complexity: O(n + N) where n is the number of elements in the array and N is the range of input +// space complexity: O(N) package sort diff --git a/sort/quicksort.go b/sort/quicksort.go index 576db395a..8b05fe786 100644 --- a/sort/quicksort.go +++ b/sort/quicksort.go @@ -2,6 +2,9 @@ // description: Implementation of in-place quicksort algorithm // details: // A simple in-place quicksort algorithm implementation. [Wikipedia](https://en.wikipedia.org/wiki/Quicksort) +// worst time complexity: O(n^2) +// average time complexity: O(n log n) +// space complexity: O(log n) // author(s) [Taj](https://github.com/tjgurwara99) // see sort_test.go for a test implementation, test function TestQuickSort. diff --git a/sort/radixsort.go b/sort/radixsort.go index 4dbd21d64..28ddee8bf 100644 --- a/sort/radixsort.go +++ b/sort/radixsort.go @@ -2,6 +2,9 @@ // description: Implementation of in-place radixsort algorithm // details: // A simple in-place quicksort algorithm implementation. [Wikipedia](https://en.wikipedia.org/wiki/Radix_sort) +// worst time complexity: O(n * k) where n is the number of elements in the input array and k is the number of digits in the largest number +// average time complexity: O(n * k) where n is the number of elements in the input array and k is the number of digits in the largest number +// space complexity: O(n) package sort diff --git a/sort/simplesort.go b/sort/simplesort.go index ec54c1db0..fc60f33de 100644 --- a/sort/simplesort.go +++ b/sort/simplesort.go @@ -5,6 +5,9 @@ // An improved version is included with slight changes to make the sort slightly more efficient // reference: https://arxiv.org/abs/2110.01111v1 // see sort_test.go for a test implementation, test function TestSimple and TestImprovedSimple +// worst-case time complexity: O(n^2) +// average-case time complexity: O(n^2) +// space complexity: O(1) package sort diff --git a/sort/sorts_test.go b/sort/sorts_test.go index aeb90c04e..f2394c334 100644 --- a/sort/sorts_test.go +++ b/sort/sorts_test.go @@ -1,11 +1,12 @@ package sort_test import ( - "github.com/TheAlgorithms/Go/sort" "math/rand" "reflect" "testing" "time" + + "github.com/TheAlgorithms/Go/sort" ) func testFramework(t *testing.T, sortingFunction func([]int) []int) { @@ -75,16 +76,28 @@ func testFramework(t *testing.T, sortingFunction func([]int) []int) { } } -//BEGIN TESTS +// BEGIN TESTS +func TestBinaryInsertion(t *testing.T) { + testFramework(t, sort.BinaryInsertion[int]) +} func TestBubble(t *testing.T) { testFramework(t, sort.Bubble[int]) } +func TestBogo(t *testing.T) { + t.Skip("Skipping test for Bogo Sort, as it uses a lot of resource.") + testFramework(t, sort.Bogo[int]) +} + func TestBucketSort(t *testing.T) { testFramework(t, sort.Bucket[int]) } +func TestCocktailSort(t *testing.T) { + testFramework(t, sort.Cocktail[int]) +} + func TestExchange(t *testing.T) { testFramework(t, sort.Exchange[int]) } @@ -173,7 +186,23 @@ func TestCycle(t *testing.T) { testFramework(t, sort.Cycle[int]) } -//END TESTS +func TestTimsort(t *testing.T) { + testFramework(t, sort.Timsort[int]) +} + +func TestCircle(t *testing.T) { + testFramework(t, sort.Circle[int]) +} + +func TestOddEvenSort(t *testing.T) { + testFramework(t, sort.OddEvenSort[int]) +} + +func TestStooge(t *testing.T) { + testFramework(t, sort.Stooge[int]) +} + +// END TESTS func benchmarkFramework(b *testing.B, f func(arr []int) []int) { var sortTests = []struct { @@ -214,14 +243,27 @@ func benchmarkFramework(b *testing.B, f func(arr []int) []int) { //BEGIN BENCHMARKS +func BenchmarkBinaryInsertion(b *testing.B) { + benchmarkFramework(b, sort.BinaryInsertion[int]) +} + func BenchmarkBubble(b *testing.B) { benchmarkFramework(b, sort.Bubble[int]) } +func BenchmarkBogo(b *testing.B) { + b.Skip("Skipping benchmark for Bogo Sort, as it uses a lot of resource.") + benchmarkFramework(b, sort.Bogo[int]) +} + func BenchmarkBucketSort(b *testing.B) { benchmarkFramework(b, sort.Bucket[int]) } +func BenchmarkCocktailSort(b *testing.B) { + benchmarkFramework(b, sort.Cocktail[int]) +} + func BenchmarkExchange(b *testing.B) { benchmarkFramework(b, sort.Exchange[int]) } @@ -294,3 +336,15 @@ func BenchmarkPatience(b *testing.B) { func BenchmarkCycle(b *testing.B) { benchmarkFramework(b, sort.Cycle[int]) } + +func BenchmarkTimsort(b *testing.B) { + benchmarkFramework(b, sort.Timsort[int]) +} + +func BenchmarkCircle(b *testing.B) { + benchmarkFramework(b, sort.Circle[int]) +} + +func BenchmarkStooge(b *testing.B) { + benchmarkFramework(b, sort.Stooge[int]) +} diff --git a/sort/stooge_sort.go b/sort/stooge_sort.go new file mode 100644 index 000000000..45d2206c4 --- /dev/null +++ b/sort/stooge_sort.go @@ -0,0 +1,34 @@ +// implementation of the Stooge sort +// more info at https://en.wikipedia.org/wiki/Stooge_sort +// worst-case time complexity O(n^2.709511) +// worst-case space complexity O(n) + +package sort + +import ( + "github.com/TheAlgorithms/Go/constraints" + + // math imported for floor division + "math" +) + +func innerStooge[T constraints.Ordered](arr []T, i int32, j int32) []T { + if arr[i] > arr[j] { + arr[i], arr[j] = arr[j], arr[i] + } + if (j - i + 1) > 2 { + t := int32(math.Floor(float64(j-i+1) / 3.0)) + arr = innerStooge(arr, i, j-t) + arr = innerStooge(arr, i+t, j) + arr = innerStooge(arr, i, j-t) + } + return arr +} + +func Stooge[T constraints.Ordered](arr []T) []T { + if len(arr) == 0 { + return arr + } + + return innerStooge(arr, 0, int32(len(arr)-1)) +} diff --git a/sort/timsort.go b/sort/timsort.go new file mode 100644 index 000000000..95520219a --- /dev/null +++ b/sort/timsort.go @@ -0,0 +1,71 @@ +// Implementation of Timsort algorithm +// Reference: https://en.wikipedia.org/wiki/Timsort + +package sort + +import ( + "github.com/TheAlgorithms/Go/constraints" +) + +const runSizeThreshold = 8 + +// Timsort is a simple generic implementation of Timsort algorithm. +func Timsort[T constraints.Ordered](data []T) []T { + runSize := calculateRunSize(len(data)) + insertionSortRuns(data, runSize) + mergeRuns(data, runSize) + return data +} + +// calculateRunSize returns a run size parameter that is further used +// to slice the data slice. +func calculateRunSize(dataLength int) int { + remainder := 0 + for dataLength >= runSizeThreshold { + if dataLength%2 == 1 { + remainder = 1 + } + + dataLength = dataLength / 2 + } + + return dataLength + remainder +} + +// insertionSortRuns runs insertion sort on all the data runs one by one. +func insertionSortRuns[T constraints.Ordered](data []T, runSize int) { + for lower := 0; lower < len(data); lower += runSize { + upper := lower + runSize + if upper >= len(data) { + upper = len(data) + } + + Insertion(data[lower:upper]) + } +} + +// mergeRuns merge sorts all the data runs into a single sorted data slice. +func mergeRuns[T constraints.Ordered](data []T, runSize int) { + for size := runSize; size < len(data); size *= 2 { + for lowerBound := 0; lowerBound < len(data); lowerBound += size * 2 { + middleBound := lowerBound + size - 1 + upperBound := lowerBound + 2*size - 1 + if len(data)-1 < upperBound { + upperBound = len(data) - 1 + } + + mergeRun(data, lowerBound, middleBound, upperBound) + } + } +} + +// mergeRun uses merge sort to sort adjacent data runs. +func mergeRun[T constraints.Ordered](data []T, lower, mid, upper int) { + left := data[lower : mid+1] + right := data[mid+1 : upper+1] + merged := merge(left, right) + // rewrite original data slice values with sorted values from merged slice + for i, value := range merged { + data[lower+i] = value + } +} diff --git a/sqrt/sqrtdecomposition.go b/sqrt/sqrtdecomposition.go new file mode 100644 index 000000000..34e26fc4a --- /dev/null +++ b/sqrt/sqrtdecomposition.go @@ -0,0 +1,102 @@ +// Package sqrt contains algorithms and data structures that contains a √n in their complexity +package sqrt + +import "math" + +// Sqrt (or Square Root) Decomposition is a technique used for query an array and perform updates +// Inside this package is described its most simple data structure, you can find more at: https://cp-algorithms.com/data_structures/sqrt_decomposition.html +// +// Formally, You can use SqrtDecomposition only if: +// +// Given a function $Query:E_1,...,E_n\rightarrow Q$ +// +// if $\exist unionQ:Q,Q\rightarrow Q$ +// +// s.t. +// +// - $\forall n\in \N > 1, 1\le i 0, E_1,..., E_n\in E \\ query(E_1,...,E_{new},..., E_n)=updateQ(query(E_1,...,E_{old},...,E_n), indexof(E_{old}), E_{new})$ +type SqrtDecomposition[E any, Q any] struct { + querySingleElement func(element E) Q + unionQ func(q1 Q, q2 Q) Q + updateQ func(oldQ Q, oldE E, newE E) (newQ Q) + + elements []E + blocks []Q + blockSize uint64 +} + +// Create a new SqrtDecomposition instance with the parameters as specified by SqrtDecomposition comment +// Assumptions: +// - len(elements) > 0 +func NewSqrtDecomposition[E any, Q any]( + elements []E, + querySingleElement func(element E) Q, + unionQ func(q1 Q, q2 Q) Q, + updateQ func(oldQ Q, oldE E, newE E) (newQ Q), +) *SqrtDecomposition[E, Q] { + sqrtDec := &SqrtDecomposition[E, Q]{ + querySingleElement: querySingleElement, + unionQ: unionQ, + updateQ: updateQ, + elements: elements, + } + sqrt := math.Sqrt(float64(len(sqrtDec.elements))) + blockSize := uint64(sqrt) + numBlocks := uint64(math.Ceil(float64(len(elements)) / float64(blockSize))) + sqrtDec.blocks = make([]Q, numBlocks) + for i := uint64(0); i < uint64(len(elements)); i++ { + if i%blockSize == 0 { + sqrtDec.blocks[i/blockSize] = sqrtDec.querySingleElement(elements[i]) + } else { + sqrtDec.blocks[i/blockSize] = sqrtDec.unionQ(sqrtDec.blocks[i/blockSize], sqrtDec.querySingleElement(elements[i])) + } + } + sqrtDec.blockSize = blockSize + return sqrtDec +} + +// Performs a query from index start to index end (non included) +// Assumptions: +// - start < end +// - start and end are valid +func (s *SqrtDecomposition[E, Q]) Query(start uint64, end uint64) Q { + firstIndexNextBlock := ((start / s.blockSize) + 1) * s.blockSize + q := s.querySingleElement(s.elements[start]) + if firstIndexNextBlock > end { // if in same block + start++ + for start < end { + q = s.unionQ(q, s.querySingleElement(s.elements[start])) + start++ + } + } else { + // left side + start++ + for start < firstIndexNextBlock { + q = s.unionQ(q, s.querySingleElement(s.elements[start])) + start++ + } + + //middle part + endBlock := end / s.blockSize + for i := firstIndexNextBlock / s.blockSize; i < endBlock; i++ { + q = s.unionQ(q, s.blocks[i]) + } + + // right part + for i := endBlock * s.blockSize; i < end; i++ { + q = s.unionQ(q, s.querySingleElement(s.elements[i])) + } + } + return q +} + +// Assumptions: +// - index is valid +func (s *SqrtDecomposition[E, Q]) Update(index uint64, newElement E) { + i := index / s.blockSize + s.blocks[i] = s.updateQ(s.blocks[i], s.elements[index], newElement) + s.elements[index] = newElement +} diff --git a/sqrt/sqrtdecomposition_test.go b/sqrt/sqrtdecomposition_test.go new file mode 100644 index 000000000..7a293c802 --- /dev/null +++ b/sqrt/sqrtdecomposition_test.go @@ -0,0 +1,80 @@ +package sqrt_test + +import ( + "github.com/TheAlgorithms/Go/sqrt" + "testing" +) + +// Query interval +type query struct { + firstIndex uint64 + lastIndex uint64 +} + +type update struct { + index uint64 + value int +} + +func TestSqrtDecomposition(t *testing.T) { + var sqrtDecompositionTestData = []struct { + description string + array []int + updates []update + queries []query + expected []int + }{ + { + description: "test 1-sized array", + array: []int{1}, + queries: []query{{0, 1}}, + expected: []int{1}, + }, + { + description: "test array with size 5", + array: []int{1, 2, 3, 4, 5}, + queries: []query{{0, 5}, {0, 2}, {2, 4}}, + expected: []int{15, 3, 7}, + }, + { + description: "test array with size 5 and updates", + array: []int{1, 2, 3, 4, 5}, + updates: []update{{index: 1, value: 3}, + {index: 2, value: 4}}, + queries: []query{{0, 5}, {0, 2}, {2, 4}}, + expected: []int{17, 4, 8}, + }, + { + description: "test array with size 11 and updates", + array: []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + updates: []update{{index: 2, value: 2}, + {index: 3, value: 3}, + {index: 6, value: 6}}, + queries: []query{{3, 5}, {7, 8}, {3, 7}, {0, 10}}, + expected: []int{4, 1, 11, 18}, + }, + } + for _, test := range sqrtDecompositionTestData { + t.Run(test.description, func(t *testing.T) { + s := sqrt.NewSqrtDecomposition(test.array, + func(e int) int { return e }, + func(q1, q2 int) int { return q1 + q2 }, + func(q, a, b int) int { return q - a + b }, + ) + + for i := 0; i < len(test.updates); i++ { + s.Update(test.updates[i].index, test.updates[i].value) + } + + for i := 0; i < len(test.queries); i++ { + result := s.Query(test.queries[i].firstIndex, test.queries[i].lastIndex) + + if result != test.expected[i] { + t.Logf("FAIL: %s", test.description) + t.Fatalf("Expected result: %d\nFound: %d\n", test.expected[i], result) + } + } + + }) + } +} diff --git a/strings/genetic/genetic.go b/strings/genetic/genetic.go index b385ad867..a91029433 100644 --- a/strings/genetic/genetic.go +++ b/strings/genetic/genetic.go @@ -28,7 +28,7 @@ type PopulationItem struct { Value float64 } -// Conf stands for cofigurations set provided to GeneticString function. +// Conf stands for configurations set provided to GeneticString function. type Conf struct { // Maximum size of the population. // Bigger could be faster but more memory expensive. diff --git a/strings/hamming/hammingdistance.go b/strings/hamming/hammingdistance.go new file mode 100644 index 000000000..a8c41f3fd --- /dev/null +++ b/strings/hamming/hammingdistance.go @@ -0,0 +1,31 @@ +/* +This algorithm calculates the hamming distance between two equal length strings. +The Hamming distance between two equal-length strings of symbols is the number of positions +at which the corresponding symbols are different: +https://en.wikipedia.org/wiki/Hamming_distance + +Note that we didn't consider strings as an array of bytes, therefore, we didn't use the XOR operator. +In this case, we used a simple loop to compare each character of the strings, and if they are different, +we increment the hamming distance by 1. + +Parameters: two strings to compare +Output: distance between both strings */ + +package hamming + +import "errors" + +func Distance(str1, str2 string) (int, error) { + if len(str1) != len(str2) { + return -1, errors.New("strings must have a same length") + } + + hammingDistance := 0 + for i := 0; i < len(str1); i++ { + if str1[i] != str2[i] { + hammingDistance++ + } + } + + return hammingDistance, nil +} diff --git a/strings/hamming/hammingdistance_test.go b/strings/hamming/hammingdistance_test.go new file mode 100644 index 000000000..663c4daee --- /dev/null +++ b/strings/hamming/hammingdistance_test.go @@ -0,0 +1,56 @@ +package hamming + +import "testing" + +var testCases = []struct { + name string + string1 string + string2 string + expected int +}{ + { + "empty strings", + "", + "", + 0, + }, + { + "single character strings", + "A", + "A", + 0, + }, + { + "two different strings with a same length", + "TestString 1", + "TestString 2", + 1, + }, + { + "two different strings with a different length", + "TestString1", + "TestString", + -1, + }, + { + "two same strings with a same length", + "TestString", + "TestString", + 0, + }, +} + +func TestHammingDistance(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual, err := Distance(tc.string1, tc.string2) + if err != nil { + if tc.expected != -1 { + t.Fatalf("Expected no error, but got %v", err) + } + } else if actual != tc.expected { + t.Errorf("Expected Hamming distance between strings: '%s' and '%s' is %v, but got: %v", tc.string1, tc.string2, tc.expected, actual) + } + }) + } +} diff --git a/strings/horspool/horspool.go b/strings/horspool/horspool.go index f210f9187..1611c0ce8 100644 --- a/strings/horspool/horspool.go +++ b/strings/horspool/horspool.go @@ -1,95 +1,60 @@ +// Implementation of the +// [Boyer–Moore–Horspool algorithm](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm) + package horspool -// User defined. -// Set to true to read input from two command line arguments -// Set to false to read input from two files "pattern.txt" and "text.txt" -// const commandLineInput bool = false +import "errors" + +var ErrNotFound = errors.New("pattern was not found in the input string") + +func Horspool(t, p string) (int, error) { + // in order to handle multy-byte character properly + // the input is converted into rune arrays + return horspool([]rune(t), []rune(p)) +} + +func horspool(t, p []rune) (int, error) { + shiftMap := computeShiftMap(t, p) + pos := 0 + for pos <= len(t)-len(p) { + if isMatch(pos, t, p) { + return pos, nil + } + if pos+len(p) >= len(t) { + // because the remaining length of the input string + // is the same as the length of the pattern + // and it does not match the pattern + // it is impossible to find the pattern + break + } + + // because of the check above + // t[pos+len(p)] is defined + pos += shiftMap[t[pos+len(p)]] + } -// Implementation of Boyer-Moore-Horspool algorithm (Suffix based approach). -// Requires either a two command line arguments separated by a single space, -// or two files in the same folder: "pattern.txt" containing the string to -// be searched for, "text.txt" containing the text to be searched in. -// func main() { -// if commandLineInput == true { // case of command line input -// args := os.Args -// if len(args) <= 2 { -// log.Fatal("Not enough arguments. Two string arguments separated by spaces are required!") -// } -// pattern := args[1] -// s := args[2] -// for i := 3; i < len(args); i++ { -// s = s + " " + args[i] -// } -// if len(args[1]) > len(s) { -// log.Fatal("Pattern is longer than text!") -// } -// fmt.Printf("\nRunning: Horspool algorithm.\n\n") -// fmt.Printf("Search word (%d chars long): %q.\n", len(args[1]), pattern) -// fmt.Printf("Text (%d chars long): %q.\n\n", len(s), s) -// horspool(s, pattern) -// } else if commandLineInput == false { // case of file line input -// patFile, err := ioutil.ReadFile("pattern.txt") -// if err != nil { -// log.Fatal(err) -// } -// textFile, err := ioutil.ReadFile("text.txt") -// if err != nil { -// log.Fatal(err) -// } -// if len(patFile) > len(textFile) { -// log.Fatal("Pattern is longer than text!") -// } -// fmt.Printf("\nRunning: Horspool algorithm.\n\n") -// fmt.Printf("Search word (%d chars long): %q.\n", len(patFile), patFile) -// fmt.Printf("Text (%d chars long): %q.\n\n", len(textFile), textFile) -// horspool(string(textFile), string(patFile)) -// } -// } + return -1, ErrNotFound +} -// // Function horspool performing the Horspool algorithm. -// // Prints whether the word/pattern was found and on what position in the text or not. -// func horspool(t, p string) { -// m, n, c, pos := len(p), len(t), 0, 0 -// //Perprocessing -// d := preprocess(t, p) -// //Map output -// fmt.Printf("Precomputed shifts per symbol: ") -// for key, value := range d { -// fmt.Printf("%c:%d; ", key, value) -// } -// fmt.Println() -// //Searching -// for pos <= n-m { -// j := m -// if t[pos+j-1] != p[j-1] { -// fmt.Printf("\n comparing characters %c %c at positions %d %d", t[pos+j-1], p[j-1], pos+j-1, j-1) -// c++ -// } -// for j > 0 && t[pos+j-1] == p[j-1] { -// fmt.Printf("\n comparing characters %c %c at positions %d %d", t[pos+j-1], p[j-1], pos+j-1, j-1) -// c++ -// fmt.Printf(" - match") -// j-- -// } -// if j == 0 { -// fmt.Printf("\n\nWord %q was found at position %d in %q. \n%d comparisons were done.", p, pos, t, c) -// return -// } -// pos = pos + d[t[pos+m]] -// } -// fmt.Printf("\n\nWord was not found.\n%d comparisons were done.", c) -// return -// } +// Checks if the array p matches the subarray of t starting at pos. +// Note that backward iteration. +// There are [other](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm#Tuning_the_comparison_loop) +// approaches possible. +func isMatch(pos int, t, p []rune) bool { + j := len(p) + for j > 0 && t[pos+j-1] == p[j-1] { + j-- + } + return j == 0 +} -// // Function that pre-computes map with Key: uint8 (char) Value: int. -// // Values determine safe shifting of search window. -// func preprocess(t, p string) (d map[uint8]int) { -// d = make(map[uint8]int) -// for i := 0; i < len(t); i++ { -// d[t[i]] = len(p) -// } -// for i := 0; i < len(p); i++ { -// d[p[i]] = len(p) - i -// } -// return d -// } +func computeShiftMap(t, p []rune) (res map[rune]int) { + res = make(map[rune]int) + for _, tCode := range t { + res[tCode] = len(p) + } + for i, pCode := range p { + res[pCode] = len(p) - i + } + return res +} diff --git a/strings/horspool/horspool_test.go b/strings/horspool/horspool_test.go new file mode 100644 index 000000000..6c500f3f2 --- /dev/null +++ b/strings/horspool/horspool_test.go @@ -0,0 +1,68 @@ +// horspool_test.go +// description: Tests for horspool +// see horspool.go + +package horspool + +import "testing" +import "fmt" + +func TestLHorspool(t *testing.T) { + testCases := []struct { + input string + pattern string + expected int + }{ + {"aaaaXaaa", "X", 4}, + {"aaaaXXaa", "XX", 4}, + {"Xaaab", "X", 0}, + {"XYaab", "XY", 0}, + {"abcefghXYZ", "XYZ", 7}, + {"abcefgh€YZ⌘", "€YZ", 7}, + {"⌘bcefgh€YZ⌘", "€YZ", 7}, + {"abc", "abc", 0}, + {"", "", 0}, + {"a", "", 0}, + {"a", "a", 0}, + {"aa", "a", 0}, + {"aa", "aa", 0}, + } + for _, tc := range testCases { + t.Run(fmt.Sprint("test with ", tc.input, " ", tc.pattern), func(t *testing.T) { + result, curError := Horspool(tc.input, tc.pattern) + if curError != nil { + t.Fatalf("Got unexpected error") + } + if tc.expected != result { + t.Fatalf("expected %d, got %d", tc.expected, result) + } + }) + } +} + +func TestLHorspoolNotExisintPattern(t *testing.T) { + testCases := []struct { + input string + pattern string + }{ + {"", "X"}, + {"X", "Y"}, + {"X", "XX"}, + {"aaaaaaaXaXaaaa", "XXX"}, + {"aaaaaaaXaX", "XXX"}, + {"XaX", "XXX"}, + {"XaX", "XXX"}, + {"\xe2\x8c\x98", "\x98"}, + } + for _, tc := range testCases { + t.Run(fmt.Sprint("test with ", tc.input, " ", tc.pattern), func(t *testing.T) { + result, curError := Horspool(tc.input, tc.pattern) + if curError != ErrNotFound { + t.Fatalf("Got unexpected error") + } + if result != -1 { + t.Fatalf("expected -1, got %d", result) + } + }) + } +} diff --git a/strings/issubsequence.go b/strings/issubsequence.go new file mode 100644 index 000000000..8d34177e9 --- /dev/null +++ b/strings/issubsequence.go @@ -0,0 +1,35 @@ +// Checks if a given string is a subsequence of another string. +// A subsequence of a given string is a string that can be derived from the given +// string by deleting some or no characters without changing the order of the +// remaining characters. (i.e., "dpr" is a subsequence of "depqr" while "drp" is not). +// Author: sanjibgirics + +package strings + +// Returns true if s is subsequence of t, otherwise return false. +func IsSubsequence(s string, t string) bool { + if len(s) > len(t) { + return false + } + + if s == t { + return true + } + + if len(s) == 0 { + return true + } + + sIndex := 0 + for tIndex := 0; tIndex < len(t); tIndex++ { + if s[sIndex] == t[tIndex] { + sIndex++ + } + + if sIndex == len(s) { + return true + } + } + + return false +} diff --git a/strings/issubsequence_test.go b/strings/issubsequence_test.go new file mode 100644 index 000000000..1b8c4cba4 --- /dev/null +++ b/strings/issubsequence_test.go @@ -0,0 +1,75 @@ +package strings_test + +import ( + "reflect" + "testing" + + "github.com/TheAlgorithms/Go/strings" +) + +func TestIsSubsequence(t *testing.T) { + var testCases = []struct { + name string + s string + t string + expected bool + }{ + { + "Valid case 1 ", + "ace", + "abcde", + true, + }, + { + "Invalid case 1", + "aec", + "abcde", + false, + }, + { + "Empty strings", + "", + "", + true, + }, + { + "s is more then t", + "aeccccc", + "abcde", + false, + }, + { + "s is empty", + "", + "abcde", + true, + }, + { + "Equal strings", + "aec", + "aec", + true, + }, + { + "Valid case 2", + "pyr", + "wpxqyrz", + true, + }, + { + "Invalid case 2", + "prx", + "wpxqyrz", + false, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + funcResult := strings.IsSubsequence(test.s, test.t) + if !reflect.DeepEqual(test.expected, funcResult) { + t.Errorf("expected: %v, got %v", test.expected, funcResult) + } + }) + } +} diff --git a/strings/parenthesis/parenthesis.go b/strings/parenthesis/parenthesis.go index 2ae49a641..1fb446ff4 100644 --- a/strings/parenthesis/parenthesis.go +++ b/strings/parenthesis/parenthesis.go @@ -1,14 +1,10 @@ package parenthesis // Parenthesis algorithm checks if every opened parenthesis -// is closed correctly - -// when parcounter is less than 0 is because a closing +// is closed correctly. When parcounter is less than 0 when a closing // parenthesis is detected without an opening parenthesis -// that surrounds it - -// parcounter will be 0 if all open parenthesis are closed -// correctly +// that surrounds it and parcounter will be 0 if all open +// parenthesis are closed correctly. func Parenthesis(text string) bool { parcounter := 0 diff --git a/strings/parenthesis/parenthesis_test.go b/strings/parenthesis/parenthesis_test.go index b7f5abd6c..685c761c4 100644 --- a/strings/parenthesis/parenthesis_test.go +++ b/strings/parenthesis/parenthesis_test.go @@ -15,17 +15,17 @@ var parenthesisTestCases = []struct { true, }, { - "three nested pharentesis with three deep level", + "three nested parenthesis with three deep level", "(-1*(5+2^(3-4)*(7.44-12)+6.66/(3.43-(1+2)))*(sqrt(3-4)))", true, }, { - "one opened pharentesis without be closed", + "one opened parenthesis without be closed", "(2*9-17)*((7+3/3)*2*(-1+4)", false, }, { - "one open pharentesis for each close one but not in pairs", + "one open parenthesis for each close one but not in pairs", "(4*(39.22-7.4)/6.77))(", false, }, @@ -35,7 +35,7 @@ var parenthesisTestCases = []struct { true, }, { - "inverted pharentesis", + "inverted parenthesis", ")()()()()(", false, }, diff --git a/structure/circularqueue/circularqueue_test.go b/structure/circularqueue/circularqueue_test.go new file mode 100644 index 000000000..0820b0470 --- /dev/null +++ b/structure/circularqueue/circularqueue_test.go @@ -0,0 +1,267 @@ +package circularqueue + +import "testing" + +func TestCircularQueue(t *testing.T) { + t.Run("Size Check", func(t *testing.T) { + _, err := NewCircularQueue[int](-3) + if err == nil { + t.Errorf("Expected error, got nil") + } + + queue, _ := NewCircularQueue[int](5) + expectedSize := 5 + gotSize := queue.Size() + if gotSize != expectedSize { + t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize) + } + + if err := queue.Enqueue(1); err != nil { + t.Error(err) + } + if err := queue.Enqueue(2); err != nil { + t.Error(err) + } + if err := queue.Enqueue(3); err != nil { + t.Error(err) + } + if err := queue.Enqueue(4); err != nil { + t.Error(err) + } + if err := queue.Enqueue(5); err != nil { + t.Error(err) + } + + err = queue.Enqueue(6) + if err == nil { + t.Errorf("Expected error, got nil") + } + + expectedSize = 5 + gotSize = queue.Size() + if gotSize != expectedSize { + t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize) + } + + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + + err = queue.Enqueue(6) + if err != nil { + t.Errorf("Expected nil, got error: %v\n", err.Error()) + } + + expectedSize = 5 + gotSize = queue.Size() + if gotSize != expectedSize { + t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize) + } + }) + t.Run("Enqueue", func(t *testing.T) { + queue, _ := NewCircularQueue[int](10) + + if err := queue.Enqueue(1); err != nil { + t.Error(err) + } + if err := queue.Enqueue(2); err != nil { + t.Error(err) + } + if err := queue.Enqueue(3); err != nil { + t.Error(err) + } + + expected := 1 + got, err := queue.Peek() + if err != nil { + t.Error(err.Error()) + } + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + }) + t.Run("Dequeue", func(t *testing.T) { + queue, _ := NewCircularQueue[string](10) + + if err := queue.Enqueue("one"); err != nil { + t.Error(err) + } + if err := queue.Enqueue("two"); err != nil { + t.Error(err) + } + if err := queue.Enqueue("three"); err != nil { + t.Error(err) + } + + expected := "one" + got, err := queue.Dequeue() + if err != nil { + t.Error(err.Error()) + } + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + + expected = "two" + got, err = queue.Peek() + if err != nil { + t.Error(err.Error()) + } + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + }) + t.Run("Circularity", func(t *testing.T) { + queue, _ := NewCircularQueue[int](10) + + if err := queue.Enqueue(1); err != nil { + t.Error(err) + } + if err := queue.Enqueue(2); err != nil { + t.Error(err) + } + if err := queue.Enqueue(3); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + if err := queue.Enqueue(4); err != nil { + t.Error(err) + } + if err := queue.Enqueue(5); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + + expected := 4 + got, err := queue.Peek() + if err != nil { + t.Error(err.Error()) + } + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + }) + t.Run("IsFull", func(t *testing.T) { + queue, _ := NewCircularQueue[bool](2) + if err := queue.Enqueue(false); err != nil { + t.Error(err) + } + if err := queue.Enqueue(true); err != nil { + t.Error(err) + } + + expected := true + got := queue.IsFull() + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + + expected = false + got = queue.IsFull() + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + }) + t.Run("IsEmpty", func(t *testing.T) { + queue, _ := NewCircularQueue[float64](2) + + expected := true + got := queue.IsEmpty() + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + + if err := queue.Enqueue(1.0); err != nil { + t.Error(err) + } + + expected = false + got = queue.IsEmpty() + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + + }) + t.Run("Peak", func(t *testing.T) { + queue, _ := NewCircularQueue[rune](10) + + if err := queue.Enqueue('a'); err != nil { + t.Error(err) + } + if err := queue.Enqueue('b'); err != nil { + t.Error(err) + } + if err := queue.Enqueue('c'); err != nil { + t.Error(err) + } + + expected := 'a' + got, err := queue.Peek() + if err != nil { + t.Error(err.Error()) + } + + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + }) +} + +// BenchmarkCircularQueue benchmarks the CircularQueue implementation. +func BenchmarkCircularQueue(b *testing.B) { + b.Run("Enqueue", func(b *testing.B) { + queue, _ := NewCircularQueue[int](1000) + for i := 0; i < b.N; i++ { + if err := queue.Enqueue(i); err != nil { + b.Error(err) + } + } + }) + + b.Run("Dequeue", func(b *testing.B) { + queue, _ := NewCircularQueue[int](1000) + for i := 0; i < 1000; i++ { + if err := queue.Enqueue(i); err != nil { + b.Error(err) + } + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := queue.Dequeue(); err != nil { + b.Error(err) + } + } + }) + + b.Run("Peek", func(b *testing.B) { + queue, _ := NewCircularQueue[int](1000) + for i := 0; i < 1000; i++ { + if err := queue.Enqueue(i); err != nil { + b.Error(err) + } + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := queue.Peek(); err != nil { + b.Error(err) + } + } + }) +} diff --git a/structure/circularqueue/circularqueuearray.go b/structure/circularqueue/circularqueuearray.go new file mode 100644 index 000000000..97447fa4a --- /dev/null +++ b/structure/circularqueue/circularqueuearray.go @@ -0,0 +1,96 @@ +// circularqueuearray.go +// description: Implementation of a circular queue data structure +// details: +// This file contains the implementation of a circular queue data structure +// using generics in Go. The circular queue supports basic operations such as +// enqueue, dequeue, peek, and checks for full and empty states. +// author(s): [Aram Ceballos](https://github.com/aramceballos) +// ref: https://www.programiz.com/dsa/circular-queue +// ref: https://en.wikipedia.org/wiki/Circular_buffer + +// Package queue provides an implementation of a circular queue data structure. +package circularqueue + +// errors package: Provides functions to create and manipulate error values +import ( + "errors" +) + +// CircularQueue represents a circular queue data structure. +type CircularQueue[T any] struct { + items []T + front int + rear int + size int +} + +// NewCircularQueue creates a new CircularQueue with the given size. +// Returns an error if the size is less than or equal to 0. +func NewCircularQueue[T any](size int) (*CircularQueue[T], error) { + if size <= 0 { + return nil, errors.New("size must be greater than 0") + } + return &CircularQueue[T]{ + items: make([]T, size), + front: -1, + rear: -1, + size: size, + }, nil +} + +// Enqueue adds an item to the rear of the queue. +// Returns an error if the queue is full. +func (cq *CircularQueue[T]) Enqueue(item T) error { + if cq.IsFull() { + return errors.New("queue is full") + } + if cq.IsEmpty() { + cq.front = 0 + } + cq.rear = (cq.rear + 1) % cq.size + cq.items[cq.rear] = item + return nil +} + +// Dequeue removes and returns the item from the front of the queue. +// Returns an error if the queue is empty. +func (cq *CircularQueue[T]) Dequeue() (T, error) { + if cq.IsEmpty() { + var zeroValue T + return zeroValue, errors.New("queue is empty") + } + retVal := cq.items[cq.front] + if cq.front == cq.rear { + cq.front = -1 + cq.rear = -1 + } else { + cq.front = (cq.front + 1) % cq.size + } + return retVal, nil +} + +// IsFull checks if the queue is full. +func (cq *CircularQueue[T]) IsFull() bool { + return (cq.front == 0 && cq.rear == cq.size-1) || cq.front == cq.rear+1 +} + +// IsEmpty checks if the queue is empty. +func (cq *CircularQueue[T]) IsEmpty() bool { + return cq.front == -1 && cq.rear == -1 +} + +// Peek returns the item at the front of the queue without removing it. +// Returns an error if the queue is empty. +func (cq *CircularQueue[T]) Peek() (T, error) { + if cq.IsEmpty() { + var zeroValue T + return zeroValue, errors.New("queue is empty") + } + + return cq.items[cq.front], nil +} + +// Size returns the size of the queue. +func (cq *CircularQueue[T]) Size() int { + return cq.size +} diff --git a/structure/deque/deque.go b/structure/deque/deque.go new file mode 100644 index 000000000..538703596 --- /dev/null +++ b/structure/deque/deque.go @@ -0,0 +1,84 @@ +// description: Double Ended Queue is a generalized version of Queue data structure that allows insert and delete at both ends. +// References: +// Wikipedia : https://en.wikipedia.org/wiki/Double-ended_queue +// Github: https://www.geeksforgeeks.org/deque-set-1-introduction-applications/ +// author [Sayan](https://github.com/bose-sayan) + +// Package deque implements a Double Ended Queue data structure. +package deque + +import ( + "errors" +) + +// ErrEmptyDequeue is a custom error for handling cases when some dequeuing operation is performed on an empty deque. +var ErrEmptyDequeue = errors.New("DoublyEnded queue is empty, so can't perform this operation") + +type DoublyEndedQueue[T any] struct { + deque []T +} + +// New returns a new DoublyEndedQueue. +func New[T any]() *DoublyEndedQueue[T] { + return &DoublyEndedQueue[T]{deque: make([]T, 0)} +} + +// EnqueueFront adds an item at the front of Deque. +func (dq *DoublyEndedQueue[T]) EnqueueFront(item T) { + dq.deque = append([]T{item}, dq.deque...) +} + +// EnqueueRear adds an item at the rear of Deque. +func (dq *DoublyEndedQueue[T]) EnqueueRear(item T) { + dq.deque = append(dq.deque, item) +} + +// DequeueFront deletes an item from front of Deque and returns it. +func (dq *DoublyEndedQueue[T]) DequeueFront() (T, error) { + if len(dq.deque) == 0 { + var zeroVal T + return zeroVal, ErrEmptyDequeue + } + frontElement := dq.deque[0] + dq.deque = dq.deque[1:] + return frontElement, nil +} + +// DequeueRear deletes an item from rear of Deque and returns it. +func (dq *DoublyEndedQueue[T]) DequeueRear() (T, error) { + if len(dq.deque) == 0 { + var zeroVal T + return zeroVal, ErrEmptyDequeue + } + rearElement := dq.deque[len(dq.deque)-1] + dq.deque = dq.deque[:len(dq.deque)-1] + return rearElement, nil +} + +// Front gets the front item from queue. +func (dq *DoublyEndedQueue[T]) Front() (T, error) { + if (len(dq.deque)) == 0 { + var zeroVal T + return zeroVal, ErrEmptyDequeue + } + return dq.deque[0], nil +} + +// Rear gets the last item from queue. +func (dq *DoublyEndedQueue[T]) Rear() (T, error) { + if (len(dq.deque)) == 0 { + var zeroVal T + return zeroVal, ErrEmptyDequeue + } + return dq.deque[len(dq.deque)-1], nil +} + +// IsEmpty checks whether Deque is empty or not. +func (dq *DoublyEndedQueue[T]) IsEmpty() bool { + return len(dq.deque) == 0 +} + +// Length gets the length of Deque. +func (dq *DoublyEndedQueue[T]) Length() int { + return len(dq.deque) +} diff --git a/structure/deque/deque_test.go b/structure/deque/deque_test.go new file mode 100644 index 000000000..26439e5be --- /dev/null +++ b/structure/deque/deque_test.go @@ -0,0 +1,406 @@ +// +// This file contains unit tests for the deque package. +// The tests cover the following scenarios: +// - Empty deque +// - Deque with one element +// - Deque with multiple elements +// The tests are parameterized with int and string types. +// Each test case is defined with a description and a list of queries to be executed on the deque. +// The expected results and errors are also defined for each query. +// + +package deque_test + +import ( + "testing" + + "github.com/TheAlgorithms/Go/structure/deque" +) + +type QueryStructure[T any] struct { + queryType string + parameter T + expectedResult interface{} + expectedError error +} + +type TestCaseData[T any] struct { + description string + queries []QueryStructure[T] +} + +func TestDeque(t *testing.T) { + + // Test cases with ints as params + testCasesInt := []TestCaseData[int]{ + { + description: "Test empty deque", + queries: []QueryStructure[int]{ + { + queryType: "IsEmpty", + expectedResult: true, + expectedError: nil, + }, + { + queryType: "Front", + expectedError: deque.ErrEmptyDequeue, + }, + { + queryType: "Rear", + expectedError: deque.ErrEmptyDequeue, + }, + { + queryType: "DeQueueFront", + expectedError: deque.ErrEmptyDequeue, + }, + { + queryType: "DeQueueRear", + expectedError: deque.ErrEmptyDequeue, + }, + { + queryType: "Length", + expectedResult: 0, + expectedError: nil, + }, + }, + }, + { + description: "Test deque with one element", + queries: []QueryStructure[int]{ + { + queryType: "EnQueueFront", + parameter: 1, + expectedError: nil, + }, + { + queryType: "IsEmpty", + expectedResult: false, + expectedError: nil, + }, + { + queryType: "Front", + expectedResult: 1, + expectedError: nil, + }, + { + queryType: "Rear", + expectedResult: 1, + expectedError: nil, + }, + { + queryType: "Length", + expectedResult: 1, + expectedError: nil, + }, + { + queryType: "DeQueueFront", + expectedResult: 1, + expectedError: nil, + }, + { + queryType: "IsEmpty", + expectedResult: true, + expectedError: nil, + }, + { + queryType: "Length", + expectedResult: 0, + expectedError: nil, + }, + }, + }, + { + description: "Test deque with multiple elements", + queries: []QueryStructure[int]{ + { + queryType: "EnQueueFront", + parameter: 1, + expectedError: nil, + }, + { + queryType: "EnQueueFront", + parameter: 2, + expectedError: nil, + }, + { + queryType: "EnQueueRear", + parameter: 3, + expectedError: nil, + }, + { + queryType: "EnQueueRear", + parameter: 4, + expectedError: nil, + }, + { + queryType: "IsEmpty", + expectedResult: false, + expectedError: nil, + }, + { + queryType: "Front", + expectedResult: 2, + expectedError: nil, + }, + { + queryType: "Rear", + expectedResult: 4, + expectedError: nil, + }, + { + queryType: "Length", + expectedResult: 4, + expectedError: nil, + }, + { + queryType: "DeQueueFront", + expectedResult: 2, + expectedError: nil, + }, + { + queryType: "DeQueueRear", + expectedResult: 4, + expectedError: nil, + }, + { + queryType: "IsEmpty", + expectedResult: false, + expectedError: nil, + }, + { + queryType: "Length", + expectedResult: 2, + expectedError: nil, + }, + }, + }, + } + + // Test cases with strings as params + testCasesString := []TestCaseData[string]{ + { + description: "Test one element deque", + queries: []QueryStructure[string]{ + { + queryType: "EnQueueFront", + parameter: "a", + expectedError: nil, + }, + { + queryType: "IsEmpty", + expectedResult: false, + expectedError: nil, + }, + { + queryType: "Front", + expectedResult: "a", + expectedError: nil, + }, + { + queryType: "Rear", + expectedResult: "a", + expectedError: nil, + }, + { + queryType: "Length", + expectedResult: 1, + expectedError: nil, + }, + { + queryType: "DeQueueFront", + expectedResult: "a", + expectedError: nil, + }, + { + queryType: "IsEmpty", + expectedResult: true, + expectedError: nil, + }, + { + queryType: "Length", + expectedResult: 0, + expectedError: nil, + }, + }, + }, + { + description: "Test multiple elements deque", + queries: []QueryStructure[string]{ + { + queryType: "EnQueueFront", + parameter: "a", + expectedError: nil, + }, + { + queryType: "EnQueueFront", + parameter: "b", + expectedError: nil, + }, + { + queryType: "EnQueueRear", + parameter: "c", + expectedError: nil, + }, + { + queryType: "EnQueueRear", + parameter: "d", + expectedError: nil, + }, + { + queryType: "IsEmpty", + expectedResult: false, + expectedError: nil, + }, + { + queryType: "Front", + expectedResult: "b", + expectedError: nil, + }, + { + queryType: "Rear", + expectedResult: "d", + expectedError: nil, + }, + { + queryType: "Length", + expectedResult: 4, + expectedError: nil, + }, + { + queryType: "DeQueueFront", + expectedResult: "b", + expectedError: nil, + }, + { + queryType: "DeQueueRear", + expectedResult: "d", + expectedError: nil, + }, + { + queryType: "IsEmpty", + expectedResult: false, + expectedError: nil, + }, + { + queryType: "Length", + expectedResult: 2, + expectedError: nil, + }, + }, + }, + } + + // Run tests with ints + for _, testCase := range testCasesInt { + t.Run(testCase.description, func(t *testing.T) { + dq := deque.New[int]() + for _, query := range testCase.queries { + switch query.queryType { + case "EnQueueFront": + dq.EnqueueFront(query.parameter) + case "EnQueueRear": + dq.EnqueueRear(query.parameter) + case "DeQueueFront": + result, err := dq.DequeueFront() + if err != query.expectedError { + t.Errorf("Expected error: %v, got : %v", query.expectedError, err) + } + if err == nil && result != query.expectedResult { + t.Errorf("Expected %v, got %v", query.expectedResult, result) + } + case "DeQueueRear": + result, err := dq.DequeueRear() + if err != query.expectedError { + t.Errorf("Expected error: %v, got : %v", query.expectedError, err) + } + if err == nil && result != query.expectedResult { + t.Errorf("Expected %v, got %v", query.expectedResult, result) + } + case "Front": + result, err := dq.Front() + if err != query.expectedError { + t.Errorf("Expected error: %v, got : %v", query.expectedError, err) + } + if err == nil && result != query.expectedResult { + t.Errorf("Expected %v, got %v, %v", query.expectedResult, result, testCase.description) + } + case "Rear": + result, err := dq.Rear() + if err != query.expectedError { + t.Errorf("Expected error: %v, got : %v", query.expectedError, err) + } + if err == nil && result != query.expectedResult { + t.Errorf("Expected %v, got %v", query.expectedResult, result) + } + case "IsEmpty": + result := dq.IsEmpty() + if result != query.expectedResult { + t.Errorf("Expected error: %v, got : %v", query.expectedResult, result) + } + case "Length": + result := dq.Length() + if result != query.expectedResult { + t.Errorf("Expected %v got %v", query.expectedResult, result) + } + } + } + }) + } + + // Run tests with strings + for _, testCase := range testCasesString { + t.Run(testCase.description, func(t *testing.T) { + dq := deque.New[string]() + for _, query := range testCase.queries { + switch query.queryType { + case "EnQueueFront": + dq.EnqueueFront(query.parameter) + case "EnQueueRear": + dq.EnqueueRear(query.parameter) + case "DeQueueFront": + result, err := dq.DequeueFront() + if err != query.expectedError { + t.Errorf("Expected error: %v, got : %v", query.expectedError, err) + } + if err == nil && result != query.expectedResult { + t.Errorf("Expected %v, got %v", query.expectedResult, result) + } + case "DeQueueRear": + result, err := dq.DequeueRear() + if err != query.expectedError { + t.Errorf("Expected error: %v, got : %v", query.expectedError, err) + } + if err == nil && result != query.expectedResult { + t.Errorf("Expected %v, got %v", query.expectedResult, result) + } + case "Front": + result, err := dq.Front() + if err != query.expectedError { + t.Errorf("Expected error: %v, got : %v", query.expectedError, err) + } + if err == nil && result != query.expectedResult { + t.Errorf("Expected %v, got %v, %v", query.expectedResult, result, testCase.description) + } + case "Rear": + result, err := dq.Rear() + if err != query.expectedError { + t.Errorf("Expected error: %v, got : %v", query.expectedError, err) + } + if err == nil && result != query.expectedResult { + t.Errorf("Expected %v, got %v", query.expectedResult, result) + } + case "IsEmpty": + result := dq.IsEmpty() + if result != query.expectedResult { + t.Errorf("Expected %v, got %v", query.expectedResult, result) + } + case "Length": + result := dq.Length() + if result != query.expectedResult { + t.Errorf("Expected %v got %v", query.expectedResult, result) + } + } + } + }) + } +} diff --git a/structure/fenwicktree/fenwicktree.go b/structure/fenwicktree/fenwicktree.go new file mode 100644 index 000000000..915d7b085 --- /dev/null +++ b/structure/fenwicktree/fenwicktree.go @@ -0,0 +1,60 @@ +// Fenwick Tree Data Structure for efficient range queries on an array of integers. +// Also known as Binary Indexed Tree. It can query the sum of any range of the array and +// can update the array at a specific position by adding a value to it (point update). +// Build: O(N) +// Query: O(log(N)) +// Update: O(log(N)) +// reference: https://brilliant.org/wiki/fenwick-tree/ +package fenwicktree + +// FenwickTree represents the data structure of the Fenwick Tree +type FenwickTree struct { + n int // n: Size of the input array. + array []int // array: the input array on which queries are made. + bit []int // bit: store the sum of ranges. +} + +// NewFenwickTree creates a new Fenwick tree, initializes bit with +// the values of the array. Note that the queries and updates should have +// one based indexing. +func NewFenwickTree(array []int) *FenwickTree { + newArray := []int{0} // Appending a 0 to the beginning as this implementation uses 1 based indexing + fenwickTree := &FenwickTree{ + n: len(array), + array: append(newArray, array...), + bit: append(newArray, array...), + } + for i := 1; i < fenwickTree.n; i++ { + nextPos := i + (i & -i) + if nextPos <= fenwickTree.n { + fenwickTree.bit[nextPos] += fenwickTree.bit[i] + } + } + return fenwickTree +} + +// PrefixSum returns the sum of the prefix ending at position pos. +func (f *FenwickTree) PrefixSum(pos int) int { + if pos > f.n { + return 0 + } + prefixSum := 0 + for i := pos; i > 0; i -= (i & -i) { + prefixSum += f.bit[i] + } + return prefixSum +} + +// RangeSum returns the sum of the elements in the range l to r +// both inclusive. +func (f *FenwickTree) RangeSum(l int, r int) int { + return f.PrefixSum(r) - f.PrefixSum(l-1) +} + +// Add Adds value to the element at position pos of the array +// and recomputes the range sums. +func (f *FenwickTree) Add(pos int, value int) { + for i := pos; i <= f.n; i += (i & -i) { + f.bit[i] += value + } +} diff --git a/structure/fenwicktree/fenwicktree_test.go b/structure/fenwicktree/fenwicktree_test.go new file mode 100644 index 000000000..99c9f12fc --- /dev/null +++ b/structure/fenwicktree/fenwicktree_test.go @@ -0,0 +1,81 @@ +package fenwicktree_test + +import ( + "github.com/TheAlgorithms/Go/structure/fenwicktree" + "testing" +) + +type query struct { + queryType string + firstIndex int // firstIndex and lastIndex are same for point queries + lastIndex int +} + +type update struct { + pos int + value int +} + +func TestFenwickTree(t *testing.T) { + var fenwickTreeTestData = []struct { + description string + array []int + updates []update + queries []query + expected []int + }{ + { + description: "test empty array", + array: []int{}, + queries: []query{{"point", 1, 1}}, + expected: []int{0}, + }, + { + description: "test array with size 5", + array: []int{1, 2, 3, 4, 5}, + queries: []query{{"range", 1, 5}, {"range", 1, 3}, {"range", 3, 5}}, + expected: []int{15, 6, 12}, + }, + { + description: "test array with size 5, single index updates and range queries", + array: []int{1, 2, 3, 4, 5}, + updates: []update{{pos: 2, value: 2}, {pos: 3, value: 3}}, + queries: []query{{"range", 1, 5}, {"range", 1, 3}, {"range", 3, 5}}, + expected: []int{20, 11, 15}, + }, + { + description: "test array with size 5, single index updates and point queries", + array: []int{1, 2, 3, 4, 5}, + updates: []update{{pos: 2, value: 2}, {pos: 3, value: 3}}, + queries: []query{{"point", 3, 3}, {"point", 1, 1}, {"point", 5, 5}}, + expected: []int{11, 1, 20}, + }, + } + + for _, test := range fenwickTreeTestData { + t.Run(test.description, func(t *testing.T) { + fenwickTree := fenwicktree.NewFenwickTree(test.array) + + for i := 0; i < len(test.updates); i++ { + fenwickTree.Add(test.updates[i].pos, test.updates[i].value) + } + + for i := 0; i < len(test.queries); i++ { + + var result int + + if test.queries[i].queryType == "point" { + result = fenwickTree.PrefixSum(test.queries[i].firstIndex) + } else { + result = fenwickTree.RangeSum(test.queries[i].firstIndex, test.queries[i].lastIndex) + } + + if result != test.expected[i] { + t.Logf("FAIL: %s", test.description) + t.Fatalf("Expected result: %d\nFound: %d\n", test.expected[i], result) + } + } + }) + } + +} diff --git a/structure/hashmap/hashmap.go b/structure/hashmap/hashmap.go index b0f5dbb1d..591ac8685 100644 --- a/structure/hashmap/hashmap.go +++ b/structure/hashmap/hashmap.go @@ -13,119 +13,100 @@ type node struct { next *node } -// HashMap is golang implementation of hashmap +// HashMap is a Golang implementation of a hashmap type HashMap struct { capacity uint64 size uint64 table []*node } -// New return new HashMap instance -func New() *HashMap { +// DefaultNew returns a new HashMap instance with default values +func DefaultNew() *HashMap { return &HashMap{ capacity: defaultCapacity, table: make([]*node, defaultCapacity), } } -// Make creates a new HashMap instance with input size and capacity -func Make(size, capacity uint64) HashMap { - return HashMap{ +// New creates a new HashMap instance with the specified size and capacity +func New(size, capacity uint64) *HashMap { + return &HashMap{ size: size, capacity: capacity, table: make([]*node, capacity), } } -// Get returns value associated with given key +// Get returns the value associated with the given key func (hm *HashMap) Get(key any) any { - node := hm.getNodeByHash(hm.hash(key)) - + node := hm.getNodeByKey(key) if node != nil { return node.value } - return nil } -// Put puts new key value in hashmap -func (hm *HashMap) Put(key any, value any) any { - return hm.putValue(hm.hash(key), key, value) -} - -// Contains checks if given key is stored in hashmap -func (hm *HashMap) Contains(key any) bool { - node := hm.getNodeByHash(hm.hash(key)) - return node != nil -} - -func (hm *HashMap) putValue(hash uint64, key any, value any) any { - if hm.capacity == 0 { - hm.capacity = defaultCapacity - hm.table = make([]*node, defaultCapacity) - } - - node := hm.getNodeByHash(hash) - - if node == nil { - hm.table[hash] = newNode(key, value) - - } else if node.key == key { - hm.table[hash] = newNodeWithNext(key, value, node) - return value - +// Put inserts a new key-value pair into the hashmap +func (hm *HashMap) Put(key, value any) { + index := hm.hash(key) + if hm.table[index] == nil { + hm.table[index] = &node{key: key, value: value} } else { - hm.resize() - return hm.putValue(hash, key, value) + current := hm.table[index] + for { + if current.key == key { + current.value = value + return + } + if current.next == nil { + break + } + current = current.next + } + current.next = &node{key: key, value: value} } - hm.size++ + if float64(hm.size)/float64(hm.capacity) > 0.75 { + hm.resize() + } +} - return value - +// Contains checks if the given key is stored in the hashmap +func (hm *HashMap) Contains(key any) bool { + return hm.getNodeByKey(key) != nil } -func (hm *HashMap) getNodeByHash(hash uint64) *node { - return hm.table[hash] +// getNodeByKey finds the node associated with the given key +func (hm *HashMap) getNodeByKey(key any) *node { + index := hm.hash(key) + current := hm.table[index] + for current != nil { + if current.key == key { + return current + } + current = current.next + } + return nil } +// resize doubles the capacity of the hashmap and rehashes all existing entries func (hm *HashMap) resize() { + oldTable := hm.table hm.capacity <<= 1 - - tempTable := hm.table - hm.table = make([]*node, hm.capacity) + hm.size = 0 - for i := 0; i < len(tempTable); i++ { - node := tempTable[i] - if node == nil { - continue + for _, head := range oldTable { + for current := head; current != nil; current = current.next { + hm.Put(current.key, current.value) } - - hm.table[hm.hash(node.key)] = node - } -} - -func newNode(key any, value any) *node { - return &node{ - key: key, - value: value, - } -} - -func newNodeWithNext(key any, value any, next *node) *node { - return &node{ - key: key, - value: value, - next: next, } } +// hash generates a hash value for the given key func (hm *HashMap) hash(key any) uint64 { h := fnv.New64a() _, _ = h.Write([]byte(fmt.Sprintf("%v", key))) - hashValue := h.Sum64() - return (hm.capacity - 1) & (hashValue ^ (hashValue >> 16)) } diff --git a/structure/hashmap/hashmap_test.go b/structure/hashmap/hashmap_test.go index 1699f8c25..7b55df0bb 100644 --- a/structure/hashmap/hashmap_test.go +++ b/structure/hashmap/hashmap_test.go @@ -7,8 +7,7 @@ import ( ) func TestHashMap(t *testing.T) { - - mp := hashmap.New() + mp := hashmap.DefaultNew() t.Run("Test 1: Put(10) and checking if Get() is correct", func(t *testing.T) { mp.Put("test", 10) @@ -67,7 +66,7 @@ func TestHashMap(t *testing.T) { }) t.Run("Test 8: Resizing a map", func(t *testing.T) { - mp := hashmap.Make(4, 4) + mp := hashmap.New(4, 4) for i := 0; i < 20; i++ { mp.Put(i, 40) diff --git a/structure/linkedlist/singlylinkedlist.go b/structure/linkedlist/singlylinkedlist.go index 9f951737f..57b09b104 100644 --- a/structure/linkedlist/singlylinkedlist.go +++ b/structure/linkedlist/singlylinkedlist.go @@ -125,14 +125,14 @@ func (ll *Singly[T]) Count() int { // Reverse reverses the list. func (ll *Singly[T]) Reverse() { - var prev, Next *Node[T] + var prev, next *Node[T] cur := ll.Head for cur != nil { - Next = cur.Next + next = cur.Next cur.Next = prev prev = cur - cur = Next + cur = next } ll.Head = prev diff --git a/structure/segmenttree/segmenttree.go b/structure/segmenttree/segmenttree.go index b55f69f0e..bf70f4522 100644 --- a/structure/segmenttree/segmenttree.go +++ b/structure/segmenttree/segmenttree.go @@ -1,9 +1,9 @@ -//Segment Tree Data Structure for Range Queries -//Build: O(n*log(n)) -//Query: O(log(n)) -//Update: O(log(n)) -//reference: https://cp-algorithms.com/data_structures/segment_tree.html - +// Segment Tree Data Structure for efficient range queries on an array of integers. +// It can query the sum and update the elements to a new value of any range of the array. +// Build: O(n*log(n)) +// Query: O(log(n)) +// Update: O(log(n)) +// reference: https://cp-algorithms.com/data_structures/segment_tree.html package segmenttree import ( @@ -13,14 +13,14 @@ import ( const emptyLazyNode = 0 -// SegmentTree with original Array and the Segment Tree Array +// SegmentTree represents the data structure of a segment tree with lazy propagation type SegmentTree struct { - Array []int - SegmentTree []int - LazyTree []int + Array []int // The original array + SegmentTree []int // Stores the sum of different ranges + LazyTree []int // Stores the values of lazy propagation } -// Propagate lazy tree node values +// Propagate propagates the lazy updates to the child nodes func (s *SegmentTree) Propagate(node int, leftNode int, rightNode int) { if s.LazyTree[node] != emptyLazyNode { //add lazy node value multiplied by (right-left+1), which represents all interval @@ -42,8 +42,8 @@ func (s *SegmentTree) Propagate(node int, leftNode int, rightNode int) { } } -// Query on interval [firstIndex, leftIndex] -// node, leftNode and rightNode always should start with 1, 0 and len(Array)-1 +// Query returns the sum of elements of the array in the interval [firstIndex, leftIndex]. +// node, leftNode and rightNode should always start with 1, 0 and len(Array)-1, respectively. func (s *SegmentTree) Query(node int, leftNode int, rightNode int, firstIndex int, lastIndex int) int { if (firstIndex > lastIndex) || (leftNode > rightNode) { //outside the interval @@ -67,10 +67,9 @@ func (s *SegmentTree) Query(node int, leftNode int, rightNode int, firstIndex in return leftNodeSum + rightNodeSum } -// Update Segment Tree -// node, leftNode and rightNode always should start with 1, 0 and len(Array)-1 -// index is the Array index that you want to update -// value is the value that you want to override +// Update updates the elements of the array in the range [firstIndex, lastIndex] +// with the new value provided and recomputes the sum of different ranges. +// node, leftNode and rightNode should always start with 1, 0 and len(Array)-1, respectively. func (s *SegmentTree) Update(node int, leftNode int, rightNode int, firstIndex int, lastIndex int, value int) { //propagate lazy tree s.Propagate(node, leftNode, rightNode) @@ -96,8 +95,8 @@ func (s *SegmentTree) Update(node int, leftNode int, rightNode int, firstIndex i } } -// Build Segment Tree -// node, leftNode and rightNode always should start with 1, 0 and len(Array)-1 +// Build builds the SegmentTree by computing the sum of different ranges. +// node, leftNode and rightNode should always start with 1, 0 and len(Array)-1, respectively. func (s *SegmentTree) Build(node int, left int, right int) { if left == right { //leaf node @@ -113,6 +112,8 @@ func (s *SegmentTree) Build(node int, left int, right int) { } } +// NewSegmentTree returns a new instance of a SegmentTree. It takes an input +// array of integers representing Array, initializes and builds the SegmentTree. func NewSegmentTree(Array []int) *SegmentTree { if len(Array) == 0 { return nil diff --git a/structure/set/set.go b/structure/set/set.go index 811ec717e..b8c7b51e1 100644 --- a/structure/set/set.go +++ b/structure/set/set.go @@ -4,9 +4,9 @@ package set // New gives new set. -func New(items ...any) Set { - st := set{ - elements: make(map[any]bool), +func New[T comparable](items ...T) Set[T] { + st := set[T]{ + elements: make(map[T]bool), } for _, item := range items { st.Add(item) @@ -15,73 +15,73 @@ func New(items ...any) Set { } // Set is an interface of possible methods on 'set'. -type Set interface { +type Set[T comparable] interface { // Add: adds new element to the set - Add(item any) + Add(item T) // Delete: deletes the passed element from the set if present - Delete(item any) + Delete(item T) // Len: gives the length of the set (total no. of elements in set) Len() int - // GetItems: gives the array( []any ) of elements of the set. - GetItems() []any + // GetItems: gives the array( []T ) of elements of the set. + GetItems() []T // In: checks whether item is present in set or not. - In(item any) bool + In(item T) bool // IsSubsetOf: checks whether set is subset of set2 or not. - IsSubsetOf(set2 Set) bool + IsSubsetOf(set2 Set[T]) bool // IsProperSubsetOf: checks whether set is proper subset of set2 or not. // ex: [1,2,3] proper subset of [1,2,3,4] -> true - IsProperSubsetOf(set2 Set) bool + IsProperSubsetOf(set2 Set[T]) bool // IsSupersetOf: checks whether set is superset of set2 or not. - IsSupersetOf(set2 Set) bool + IsSupersetOf(set2 Set[T]) bool // IsProperSupersetOf: checks whether set is proper superset of set2 or not. // ex: [1,2,3,4] proper superset of [1,2,3] -> true - IsProperSupersetOf(set2 Set) bool + IsProperSupersetOf(set2 Set[T]) bool // Union: gives new union set of both sets. // ex: [1,2,3] union [3,4,5] -> [1,2,3,4,5] - Union(set2 Set) Set + Union(set2 Set[T]) Set[T] // Intersection: gives new intersection set of both sets. // ex: [1,2,3] Intersection [3,4,5] -> [3] - Intersection(set2 Set) Set + Intersection(set2 Set[T]) Set[T] // Difference: gives new difference set of both sets. // ex: [1,2,3] Difference [3,4,5] -> [1,2] - Difference(set2 Set) Set + Difference(set2 Set[T]) Set[T] // SymmetricDifference: gives new symmetric difference set of both sets. // ex: [1,2,3] SymmetricDifference [3,4,5] -> [1,2,4,5] - SymmetricDifference(set2 Set) Set + SymmetricDifference(set2 Set[T]) Set[T] } -type set struct { - elements map[any]bool +type set[T comparable] struct { + elements map[T]bool } -func (st *set) Add(value any) { +func (st *set[T]) Add(value T) { st.elements[value] = true } -func (st *set) Delete(value any) { +func (st *set[T]) Delete(value T) { delete(st.elements, value) } -func (st *set) GetItems() []any { - keys := make([]any, 0, len(st.elements)) +func (st *set[T]) GetItems() []T { + keys := make([]T, 0, len(st.elements)) for k := range st.elements { keys = append(keys, k) } return keys } -func (st *set) Len() int { +func (st *set[T]) Len() int { return len(st.elements) } -func (st *set) In(value any) bool { +func (st *set[T]) In(value T) bool { if _, in := st.elements[value]; in { return true } return false } -func (st *set) IsSubsetOf(superSet Set) bool { +func (st *set[T]) IsSubsetOf(superSet Set[T]) bool { if st.Len() > superSet.Len() { return false } @@ -94,26 +94,26 @@ func (st *set) IsSubsetOf(superSet Set) bool { return true } -func (st *set) IsProperSubsetOf(superSet Set) bool { +func (st *set[T]) IsProperSubsetOf(superSet Set[T]) bool { if st.Len() == superSet.Len() { return false } return st.IsSubsetOf(superSet) } -func (st *set) IsSupersetOf(subSet Set) bool { +func (st *set[T]) IsSupersetOf(subSet Set[T]) bool { return subSet.IsSubsetOf(st) } -func (st *set) IsProperSupersetOf(subSet Set) bool { +func (st *set[T]) IsProperSupersetOf(subSet Set[T]) bool { if st.Len() == subSet.Len() { return false } return st.IsSupersetOf(subSet) } -func (st *set) Union(st2 Set) Set { - unionSet := New() +func (st *set[T]) Union(st2 Set[T]) Set[T] { + unionSet := New[T]() for _, item := range st.GetItems() { unionSet.Add(item) } @@ -123,9 +123,9 @@ func (st *set) Union(st2 Set) Set { return unionSet } -func (st *set) Intersection(st2 Set) Set { - intersectionSet := New() - var minSet, maxSet Set +func (st *set[T]) Intersection(st2 Set[T]) Set[T] { + intersectionSet := New[T]() + var minSet, maxSet Set[T] if st.Len() > st2.Len() { minSet = st2 maxSet = st @@ -141,8 +141,8 @@ func (st *set) Intersection(st2 Set) Set { return intersectionSet } -func (st *set) Difference(st2 Set) Set { - differenceSet := New() +func (st *set[T]) Difference(st2 Set[T]) Set[T] { + differenceSet := New[T]() for _, item := range st.GetItems() { if !st2.In(item) { differenceSet.Add(item) @@ -151,9 +151,9 @@ func (st *set) Difference(st2 Set) Set { return differenceSet } -func (st *set) SymmetricDifference(st2 Set) Set { - symmetricDifferenceSet := New() - dropSet := New() +func (st *set[T]) SymmetricDifference(st2 Set[T]) Set[T] { + symmetricDifferenceSet := New[T]() + dropSet := New[T]() for _, item := range st.GetItems() { if st2.In(item) { dropSet.Add(item) diff --git a/structure/set/set_test.go b/structure/set/set_test.go index 4b12a8f55..2000760ac 100644 --- a/structure/set/set_test.go +++ b/structure/set/set_test.go @@ -118,9 +118,9 @@ func TestIsProperSupersetOf(t *testing.T) { func TestUnion(t *testing.T) { td := []struct { name string - s1 Set - s2 Set - expSet Set + s1 Set[int] + s2 Set[int] + expSet Set[int] }{ {"union of different sets", New(1, 2, 3), New(4, 5, 6), New(1, 2, 3, 4, 5, 6)}, {"union of sets with elements in common", New(1, 2, 3), New(1, 2, 4), New(1, 2, 3, 4)}, @@ -144,11 +144,11 @@ func TestUnion(t *testing.T) { func TestIntersection(t *testing.T) { td := []struct { name string - s1 Set - s2 Set - expSet Set + s1 Set[int] + s2 Set[int] + expSet Set[int] }{ - {"intersection of different sets", New(0, 1, 2, 3), New(4, 5, 6), New()}, + {"intersection of different sets", New(0, 1, 2, 3), New(4, 5, 6), New[int]()}, {"intersection of sets with elements in common", New(1, 2, 3), New(1, 2, 4), New(1, 2)}, {"intersection of same sets", New(1, 2, 3), New(1, 2, 3), New(1, 2, 3)}, } @@ -170,13 +170,13 @@ func TestIntersection(t *testing.T) { func TestDifference(t *testing.T) { td := []struct { name string - s1 Set - s2 Set - expSet Set + s1 Set[int] + s2 Set[int] + expSet Set[int] }{ {"difference of different sets", New(1, 2, 3), New(4, 5, 6), New(1, 2, 3)}, {"difference of sets with elements in common", New(1, 2, 3), New(1, 2, 4), New(3)}, - {"difference of same sets", New(1, 2, 3), New(1, 2, 3), New()}, + {"difference of same sets", New(1, 2, 3), New(1, 2, 3), New[int]()}, } for _, tc := range td { t.Run(tc.name, func(t *testing.T) { @@ -196,13 +196,13 @@ func TestDifference(t *testing.T) { func TestSymmetricDifference(t *testing.T) { td := []struct { name string - s1 Set - s2 Set - expSet Set + s1 Set[int] + s2 Set[int] + expSet Set[int] }{ {"symmetric difference of different sets", New(1, 2, 3), New(4, 5, 6), New(1, 2, 3, 4, 5, 6)}, {"symmetric difference of sets with elements in common", New(1, 2, 3), New(1, 2, 4), New(3, 4)}, - {"symmetric difference of same sets", New(1, 2, 3), New(1, 2, 3), New()}, + {"symmetric difference of same sets", New(1, 2, 3), New(1, 2, 3), New[int]()}, } for _, tc := range td { t.Run(tc.name, func(t *testing.T) { diff --git a/structure/stack/stack_test.go b/structure/stack/stack_test.go index e5a77e70b..0de3dc0d6 100644 --- a/structure/stack/stack_test.go +++ b/structure/stack/stack_test.go @@ -7,23 +7,24 @@ // author [Milad](https://github.com/miraddo) // see stackarray.go, stacklinkedlist.go, stacklinkedlistwithlist.go -package stack +package stack_test import ( "container/list" + "github.com/TheAlgorithms/Go/structure/stack" "reflect" "testing" ) // TestStackLinkedList for testing Stack with LinkedList func TestStackLinkedList(t *testing.T) { - var newStack Stack + var newStack stack.Stack - newStack.push(1) - newStack.push(2) + newStack.Push(1) + newStack.Push(2) t.Run("Stack Push", func(t *testing.T) { - result := newStack.show() + result := newStack.Show() expected := []any{2, 1} for x := range result { if result[x] != expected[x] { @@ -33,20 +34,20 @@ func TestStackLinkedList(t *testing.T) { }) t.Run("Stack isEmpty", func(t *testing.T) { - if newStack.isEmpty() { - t.Error("Stack isEmpty is returned true but expected false", newStack.isEmpty()) + if newStack.IsEmpty() { + t.Error("Stack isEmpty is returned true but expected false", newStack.IsEmpty()) } }) t.Run("Stack Length", func(t *testing.T) { - if newStack.len() != 2 { - t.Error("Stack Length should be 2 but got", newStack.len()) + if newStack.Length() != 2 { + t.Error("Stack Length should be 2 but got", newStack.Length()) } }) - newStack.pop() - pop := newStack.pop() + newStack.Pop() + pop := newStack.Pop() t.Run("Stack Pop", func(t *testing.T) { if pop != 1 { @@ -55,65 +56,73 @@ func TestStackLinkedList(t *testing.T) { }) - newStack.push(52) - newStack.push(23) - newStack.push(99) - t.Run("Stack Peak", func(t *testing.T) { - if newStack.peak() != 99 { - t.Error("Stack Peak should return 99 but got ", newStack.peak()) + newStack.Push(52) + newStack.Push(23) + newStack.Push(99) + t.Run("Stack Peek", func(t *testing.T) { + if newStack.Peek() != 99 { + t.Error("Stack Peak should return 99 but got ", newStack.Peek()) } }) } // TestStackArray for testing Stack with Array func TestStackArray(t *testing.T) { + newStack := stack.NewStack[int]() t.Run("Stack With Array", func(t *testing.T) { - stackPush(2) - stackPush(3) + newStack.Push(2) + newStack.Push(3) t.Run("Stack Push", func(t *testing.T) { - if !reflect.DeepEqual([]any{3, 2}, stackArray) { - t.Errorf("Stack Push is not work we expected %v but got %v", []any{3, 2}, stackArray) + var stackElements []any + for i := 0; i < 2; i++ { + poppedElement := newStack.Pop() + stackElements = append(stackElements, poppedElement) + } + + if !reflect.DeepEqual([]any{3, 2}, stackElements) { + t.Errorf("Stack Push is not work we expected %v but got %v", []any{3, 2}, newStack) } + + newStack.Push(2) + newStack.Push(3) }) - pop := stackPop() + pop := newStack.Pop() t.Run("Stack Pop", func(t *testing.T) { - - if stackLength() == 2 && pop != 3 { + if newStack.Length() == 2 && pop != 3 { t.Errorf("Stack Pop is not work we expected %v but got %v", 3, pop) } }) - stackPush(2) - stackPush(83) + newStack.Push(2) + newStack.Push(83) t.Run("Stack Peak", func(t *testing.T) { - - if stackPeak() != 83 { - t.Errorf("Stack Peak is not work we expected %v but got %v", 83, stackPeak()) + if newStack.Peek() != 83 { + t.Errorf("Stack Peek is not work we expected %v but got %v", 83, newStack.Peek()) } }) t.Run("Stack Length", func(t *testing.T) { - if stackLength() != 3 { - t.Errorf("Stack Length is not work we expected %v but got %v", 3, stackLength()) + if newStack.Length() != 3 { + t.Errorf("Stack Length is not work we expected %v but got %v", 3, newStack.Length()) } }) t.Run("Stack Empty", func(t *testing.T) { - if stackEmpty() == true { - t.Errorf("Stack Empty is not work we expected %v but got %v", false, stackEmpty()) + if newStack.IsEmpty() == true { + t.Errorf("Stack Empty is not work we expected %v but got %v", false, newStack.IsEmpty()) } - stackPop() - stackPop() - stackPop() + newStack.Pop() + newStack.Pop() + newStack.Pop() - if stackEmpty() == false { - t.Errorf("Stack Empty is not work we expected %v but got %v", true, stackEmpty()) + if newStack.IsEmpty() == false { + t.Errorf("Stack Empty is not work we expected %v but got %v", true, newStack.IsEmpty()) } }) }) @@ -121,8 +130,8 @@ func TestStackArray(t *testing.T) { // TestStackLinkedListWithList for testing Stack with Container/List Library (STL) func TestStackLinkedListWithList(t *testing.T) { - stackList := &SList{ - stack: list.New(), + stackList := &stack.SList{ + Stack: list.New(), } t.Run("Stack Push", func(t *testing.T) { @@ -147,7 +156,7 @@ func TestStackLinkedListWithList(t *testing.T) { stackList.Push(2) stackList.Push(83) - peak, _ := stackList.Peak() + peak, _ := stackList.Peek() if peak != 83 { t.Errorf("Stack Peak is not work we expected %v but got %v", 83, peak) } @@ -160,8 +169,8 @@ func TestStackLinkedListWithList(t *testing.T) { }) t.Run("Stack Empty", func(t *testing.T) { - if stackList.Empty() == true { - t.Errorf("Stack Empty is not work we expected %v but got %v", false, stackList.Empty()) + if stackList.IsEmpty() == true { + t.Errorf("Stack Empty is not work we expected %v but got %v", false, stackList.IsEmpty()) } d1, err := stackList.Pop() @@ -172,8 +181,8 @@ func TestStackLinkedListWithList(t *testing.T) { t.Errorf("got an unexpected error %v, pop1: %v, pop2: %v, pop3: %v", err, d1, d2, d3) } - if stackList.Empty() == false { - t.Errorf("Stack Empty is not work we expected %v but got %v", true, stackList.Empty()) + if stackList.IsEmpty() == false { + t.Errorf("Stack Empty is not work we expected %v but got %v", true, stackList.IsEmpty()) } }) } diff --git a/structure/stack/stackarray.go b/structure/stack/stackarray.go index 5aba01cf4..2d0fc05c4 100644 --- a/structure/stack/stackarray.go +++ b/structure/stack/stackarray.go @@ -9,31 +9,47 @@ package stack -var stackArray []any +type Array[T any] struct { + elements []T +} + +// NewStack creates and returns a new stack. +func NewStack[T any]() *Array[T] { + return &Array[T]{} +} -// stackPush push to first index of array -func stackPush(n any) { - stackArray = append([]any{n}, stackArray...) +// Push adds an element to the top of the stack. +func (s *Array[T]) Push(value T) { + s.elements = append(s.elements, value) } -// stackLength return length of array -func stackLength() int { - return len(stackArray) +// Size returns the number of elements in the stack. +func (s *Array[T]) Length() int { + return len(s.elements) } -// stackPeak return last input of array -func stackPeak() any { - return stackArray[0] +// Peek returns the top element of the stack without removing it. +func (s *Array[T]) Peek() T { + if s.IsEmpty() { + var zeroValue T + return zeroValue // Stack is empty + } + return s.elements[len(s.elements)-1] } -// stackEmpty check array is empty or not -func stackEmpty() bool { - return len(stackArray) == 0 +// IsEmpty returns true if the stack is empty, false otherwise. +func (s *Array[T]) IsEmpty() bool { + return len(s.elements) == 0 } -// stackPop return last input and remove it in array -func stackPop() any { - pop := stackArray[0] - stackArray = stackArray[1:] - return pop +// Pop removes and returns the top element from the stack. +func (s *Array[T]) Pop() T { + if s.IsEmpty() { + var zeroValue T + return zeroValue // Stack is empty + } + index := len(s.elements) - 1 + popped := s.elements[index] + s.elements = s.elements[:index] + return popped } diff --git a/structure/stack/stacklinkedlist.go b/structure/stack/stacklinkedlist.go index 4c4973884..1328729e6 100644 --- a/structure/stack/stacklinkedlist.go +++ b/structure/stack/stacklinkedlist.go @@ -22,7 +22,7 @@ type Stack struct { } // push add value to last index -func (ll *Stack) push(n any) { +func (ll *Stack) Push(n any) { newStack := &Node{} // new node newStack.Val = n @@ -33,7 +33,7 @@ func (ll *Stack) push(n any) { } // pop remove last item as first output -func (ll *Stack) pop() any { +func (ll *Stack) Pop() any { result := ll.top.Val if ll.top.Next == nil { ll.top = nil @@ -46,22 +46,22 @@ func (ll *Stack) pop() any { } // isEmpty to check our array is empty or not -func (ll *Stack) isEmpty() bool { +func (ll *Stack) IsEmpty() bool { return ll.length == 0 } // len use to return length of our stack -func (ll *Stack) len() int { +func (ll *Stack) Length() int { return ll.length } // peak return last input value -func (ll *Stack) peak() any { +func (ll *Stack) Peek() any { return ll.top.Val } // show all value as an interface array -func (ll *Stack) show() (in []any) { +func (ll *Stack) Show() (in []any) { current := ll.top for current != nil { diff --git a/structure/stack/stacklinkedlistwithlist.go b/structure/stack/stacklinkedlistwithlist.go index ba3ce6580..ae53a499b 100644 --- a/structure/stack/stacklinkedlistwithlist.go +++ b/structure/stack/stacklinkedlistwithlist.go @@ -16,18 +16,18 @@ import ( // SList is our struct that point to stack with container/list.List library type SList struct { - stack *list.List + Stack *list.List } // Push add a value into our stack func (sl *SList) Push(val any) { - sl.stack.PushFront(val) + sl.Stack.PushFront(val) } // Peak is return last value that insert into our stack -func (sl *SList) Peak() (any, error) { - if !sl.Empty() { - element := sl.stack.Front() +func (sl *SList) Peek() (any, error) { + if !sl.IsEmpty() { + element := sl.Stack.Front() return element.Value, nil } return "", fmt.Errorf("stack list is empty") @@ -36,11 +36,11 @@ func (sl *SList) Peak() (any, error) { // Pop is return last value that insert into our stack // also it will remove it in our stack func (sl *SList) Pop() (any, error) { - if !sl.Empty() { + if !sl.IsEmpty() { // get last element that insert into stack - element := sl.stack.Front() + element := sl.Stack.Front() // remove element in stack - sl.stack.Remove(element) + sl.Stack.Remove(element) // return element value return element.Value, nil } @@ -49,12 +49,12 @@ func (sl *SList) Pop() (any, error) { // Length return length of our stack func (sl *SList) Length() int { - return sl.stack.Len() + return sl.Stack.Len() } // Empty check our stack has value or not -func (sl *SList) Empty() bool { +func (sl *SList) IsEmpty() bool { // check our stack is empty or not // if is 0 it means our stack is empty otherwise is not empty - return sl.stack.Len() == 0 + return sl.Stack.Len() == 0 } diff --git a/structure/tree/btree.go b/structure/tree/btree.go new file mode 100644 index 000000000..5b2e55eb7 --- /dev/null +++ b/structure/tree/btree.go @@ -0,0 +1,355 @@ +// B-tree is a self balancing tree that promotes data locality. +// For more details, see https://en.wikipedia.org/wiki/B-tree + +package tree + +import "github.com/TheAlgorithms/Go/constraints" + +type BTreeNode[T constraints.Ordered] struct { + keys []T + children []*BTreeNode[T] + numKeys int + isLeaf bool +} + +type BTree[T constraints.Ordered] struct { + root *BTreeNode[T] + maxKeys int +} + +func minKeys(maxKeys int) int { + return (maxKeys - 1) / 2 +} + +func NewBTreeNode[T constraints.Ordered](maxKeys int, isLeaf bool) *BTreeNode[T] { + if maxKeys <= 0 { + panic("BTree maxKeys cannot be zero") + } + return &BTreeNode[T]{ + keys: make([]T, maxKeys), + children: make([]*BTreeNode[T], maxKeys+1), + isLeaf: isLeaf, + } +} + +func NewBTree[T constraints.Ordered](maxKeys int) *BTree[T] { + if maxKeys <= 2 { + panic("Must be >= 3 keys") + } + return &BTree[T]{ + root: nil, + maxKeys: maxKeys, + } +} + +func (node *BTreeNode[T]) Verify(tree *BTree[T]) { + minKeys := minKeys(tree.maxKeys) + if node != tree.root && node.numKeys < minKeys { + panic("node has too few keys") + } else if node.numKeys > tree.maxKeys { + panic("node has too many keys") + } +} + +func (node *BTreeNode[T]) IsFull(maxKeys int) bool { + return node.numKeys == maxKeys +} + +func (node *BTreeNode[T]) Search(key T) bool { + i := 0 + for ; i < node.numKeys; i++ { + if key == node.keys[i] { + return true + } + if key < node.keys[i] { + break + } + } + if node.isLeaf { + return false + } + return node.children[i].Search(key) +} + +func (tree *BTree[T]) Search(key T) bool { + if tree.root == nil { + return false + } + return tree.root.Search(key) +} + +func (node *BTreeNode[T]) InsertKeyChild(key T, child *BTreeNode[T]) { + i := node.numKeys + node.children[i+1] = node.children[i] + for ; i > 0; i-- { + if key > node.keys[i-1] { + node.keys[i] = key + node.children[i] = child + break + } + node.keys[i] = node.keys[i-1] + node.children[i] = node.children[i-1] + } + if i == 0 { + node.keys[0] = key + node.children[0] = child + } + node.numKeys++ +} + +func (node *BTreeNode[T]) Append(key T, child *BTreeNode[T]) { + node.keys[node.numKeys] = key + node.children[node.numKeys+1] = child + node.numKeys++ +} + +// Add all of other's keys starting from idx and children starting from idx + 1 +func (node *BTreeNode[T]) Concat(other *BTreeNode[T], idx int) { + for i := 0; i < other.numKeys-idx; i++ { + node.keys[node.numKeys+i] = other.keys[i+idx] + node.children[node.numKeys+i+1] = other.children[i+idx+1] + } + node.numKeys += other.numKeys - idx +} + +// Transform: +// +// A B +// | +// +// a b c d +// +// Into: +// +// A c B +// / \ +// +// a b d +func (parent *BTreeNode[T]) Split(idx int, maxKeys int) { + child := parent.children[idx] + midKeyIndex := maxKeys / 2 + rightChild := NewBTreeNode[T](maxKeys, child.isLeaf) + rightChild.Concat(child, midKeyIndex+1) + rightChild.children[0] = child.children[midKeyIndex+1] + + // Reuse child as the left node + child.numKeys = midKeyIndex + + // Insert the child's mid index to the parent + for i := parent.numKeys; i > idx; i-- { + parent.keys[i] = parent.keys[i-1] + parent.children[i+1] = parent.children[i] + } + parent.keys[idx] = child.keys[midKeyIndex] + parent.children[idx] = child + parent.children[idx+1] = rightChild + parent.numKeys += 1 +} + +func (node *BTreeNode[T]) InsertNonFull(tree *BTree[T], key T) { + node.Verify(tree) + if node.IsFull(tree.maxKeys) { + panic("Called InsertNonFull() with a full node") + } + + if node.isLeaf { + // Node is a leaf. Directly insert the key. + node.InsertKeyChild(key, nil) + return + } + + // Find the child node to insert into + i := 0 + for ; i < node.numKeys; i++ { + if key < node.keys[i] { + break + } + } + + if node.children[i].IsFull(tree.maxKeys) { + node.Split(i, tree.maxKeys) + if key > node.keys[i] { + i++ + } + } + node.children[i].InsertNonFull(tree, key) +} + +func (tree *BTree[T]) Insert(key T) { + if tree.root == nil { + tree.root = NewBTreeNode[T](tree.maxKeys, true) + tree.root.keys[0] = key + tree.root.numKeys = 1 + return + } + + if tree.root.IsFull(tree.maxKeys) { + newRoot := NewBTreeNode[T](tree.maxKeys, false) + newRoot.numKeys = 0 + newRoot.children[0] = tree.root + newRoot.Split(0, tree.maxKeys) + tree.root = newRoot + } + tree.root.InsertNonFull(tree, key) +} + +func (node *BTreeNode[T]) DeleteIthKey(i int) { + if i >= node.numKeys { + panic("deleting out of bounds key") + } + for j := i; j < node.numKeys-1; j++ { + node.keys[j] = node.keys[j+1] + node.children[j+1] = node.children[j+2] + } + node.numKeys-- +} + +// Transform: +// +// A B C +// / \ +// a b +// +// Into: +// +// A C +// | +// +// a B c +func (node *BTreeNode[T]) Merge(idx int) { + if node.isLeaf { + panic("cannot merge when leaf node is parent") + } + left := node.children[idx] + right := node.children[idx+1] + left.Append(node.keys[idx], right.children[0]) + left.Concat(right, 0) + node.DeleteIthKey(idx) +} + +func (node *BTreeNode[T]) Min() T { + if node.isLeaf { + return node.keys[0] + } + return node.children[0].Min() +} + +func (node *BTreeNode[T]) Max() T { + if node.isLeaf { + return node.keys[node.numKeys-1] + } + return node.children[node.numKeys].Max() +} + +func (node *BTreeNode[T]) Delete(tree *BTree[T], key T) { + node.Verify(tree) + if node.isLeaf { + // Case 1: Node is a leaf. Directly delete the key. + for i := 0; i < node.numKeys; i++ { + if key == node.keys[i] { + node.DeleteIthKey(i) + return + } + } + return + } + + minKeys := minKeys(tree.maxKeys) + i := 0 + for ; i < node.numKeys; i++ { + if key == node.keys[i] { + // Case 2: key exists in a non-leaf node + left := node.children[i] + right := node.children[i+1] + if left.numKeys > minKeys { + // Replace the key we want to delete with the max key from the left + // subtree. Then delete that key from the left subtree. + // A B C + // / + // a b c + // + // If we want to delete `B`, then replace `B` with `c`, and delete `c` in the subtree. + // A c C + // / + // a b + replacementKey := left.Max() + node.keys[i] = replacementKey + left.Delete(tree, replacementKey) + } else if right.numKeys > minKeys { + // Replace the key we want to delete with the min key from the right + // subtree. Then delete that key in the right subtree. Mirrors the + // transformation above for replacing from the left subtree. + replacementKey := right.Min() + node.keys[i] = replacementKey + right.Delete(tree, replacementKey) + } else { + // Both left and right subtrees have the minimum number of keys. Merge + // the left tree, the deleted key, and the right tree together into the + // left tree. Then recursively delete the key in the left tree. + if left.numKeys != minKeys || right.numKeys != minKeys { + panic("nodes should not have less than the minimum number of keys") + } + node.Merge(i) + left.Delete(tree, key) + } + return + } + + if key < node.keys[i] { + break + } + } + + // Case 3: key may exist in a child node. + child := node.children[i] + if child.numKeys == minKeys { + // Before we recurse into the child node, make sure it has more than + // the minimum number of keys. + if i > 0 && node.children[i-1].numKeys > minKeys { + // Take a key from the left sibling + // Transform: + // A B C + // / \ + // a b c + // + // Into: + // A b C + // / \ + // a B c + left := node.children[i-1] + child.InsertKeyChild(node.keys[i-1], left.children[left.numKeys]) + node.keys[i-1] = left.keys[left.numKeys-1] + left.numKeys-- + } else if i < node.numKeys && node.children[i+1].numKeys > minKeys { + // Take a key from the right sibling. Mirrors the transformation above for taking a key from the left sibling. + right := node.children[i+1] + child.Append(node.keys[i], right.children[0]) + node.keys[i] = right.keys[0] + right.children[0] = right.children[1] + right.DeleteIthKey(0) + } else { + if i == 0 { + // Merge with right sibling + node.Merge(i) + } else { + // Merge with left sibling + node.Merge(i - 1) + child = node.children[i-1] + } + } + } + if child.numKeys == minKeys { + panic("cannot delete key from node with minimum number of keys") + } + child.Delete(tree, key) +} + +func (tree *BTree[T]) Delete(key T) { + if tree.root == nil { + return + } + tree.root.Delete(tree, key) + if tree.root.numKeys == 0 { + tree.root = tree.root.children[0] + } +} diff --git a/structure/tree/btree_test.go b/structure/tree/btree_test.go new file mode 100644 index 000000000..056461b1c --- /dev/null +++ b/structure/tree/btree_test.go @@ -0,0 +1,133 @@ +package tree_test + +import ( + bt "github.com/TheAlgorithms/Go/structure/tree" + "math/rand" + "testing" +) + +func TestBTreeIncreasing(t *testing.T) { + maxKeysCases := []int{4, 16} + sizes := []int{100, 0xBA5, 0xF00} + for _, maxKeys := range maxKeysCases { + for _, size := range sizes { + tree := bt.NewBTree[int](maxKeys) + if tree.Search(0) { + t.Errorf("Tree expected to contain 0") + } + for i := 0; i < size; i++ { + tree.Insert(i) + } + for i := 0; i < size; i++ { + if !tree.Search(i) { + t.Errorf("Tree expected to contain %d", i) + } + } + if tree.Search(size + 1) { + t.Errorf("Tree not expected to contain %d", size+1) + } + + for i := 0; i < size; i += 5 { + tree.Delete(i) + } + for i := 0; i < size; i++ { + hasKey := tree.Search(i) + if i%5 == 0 && hasKey { + t.Errorf("Tree not expected to contain %d", i) + } else if i%5 != 0 && !hasKey { + t.Errorf("Tree expected to contain %d", i) + } + } + } + } +} + +func TestBTreeDecreasing(t *testing.T) { + maxKeysCases := []int{4, 16} + sizes := []int{100, 1000} + for _, maxKeys := range maxKeysCases { + for _, size := range sizes { + tree := bt.NewBTree[int](maxKeys) + if tree.Search(0) { + t.Errorf("Tree expected to contain 0") + } + for i := size - 1; i >= 0; i-- { + tree.Insert(i) + } + for i := 0; i < size; i++ { + if !tree.Search(i) { + t.Errorf("Tree expected to contain %d", i) + } + } + if tree.Search(size + 1) { + t.Errorf("Tree not expected to contain %d", size+1) + } + + for i := 0; i < size; i += 5 { + tree.Delete(i) + } + for i := 0; i < size; i++ { + hasKey := tree.Search(i) + if i%5 == 0 && hasKey { + t.Errorf("Tree not expected to contain %d", i) + } else if i%5 != 0 && !hasKey { + t.Errorf("Tree expected to contain %d", i) + } + } + } + } +} + +func TestBTreeRandom(t *testing.T) { + maxKeysCases := []int{4, 16} + sizes := []int{100, 0xBA5, 0xF00} + for _, maxKeys := range maxKeysCases { + for _, size := range sizes { + rnd := rand.New(rand.NewSource(0)) + tree := bt.NewBTree[int](maxKeys) + nums := rnd.Perm(size) + if tree.Search(0) { + t.Errorf("Tree expected to contain 0") + } + for i := 0; i < size; i++ { + tree.Insert(nums[i]) + } + for i := 0; i < size; i++ { + if !tree.Search(nums[i]) { + t.Errorf("Tree expected to contain %d", nums[i]) + } + } + + for i := 0; i < size; i += 5 { + tree.Delete(nums[i]) + } + for i := 0; i < size; i++ { + hasKey := tree.Search(nums[i]) + if i%5 == 0 && hasKey { + t.Errorf("Tree not expected to contain %d", i) + } else if i%5 != 0 && !hasKey { + t.Errorf("Tree expected to contain %d", i) + } + } + } + } +} + +func TestBTreeDeleteEverything(t *testing.T) { + tree := bt.NewBTree[int](4) + size := 128 + for i := 0; i < size; i++ { + tree.Insert(i) + } + for i := 0; i < size; i++ { + tree.Delete(i) + } + tree.Delete(-1) + tree.Delete(1000) + + for i := 0; i < size; i++ { + if tree.Search(i) { + t.Errorf("Tree not expected to contain %d", i) + } + } +}