Go Module

Let's create a module of Go or GoLang and publish it.

I introduced the Go module in Modules of the Go Crash Course (Part 1). Let's talk more about the Go module in this article. We're going to create a Go module about Fibonacci, get a Fibonacci number based on the given index with multiple methods, and publish it to pkg.go.dev.

All Fibonacci methods I covered are from the LeetCode Fibonacci Number solution. The point here is to compare all methods. We want to see the space and time complexity of all methods.

  1. Let's get started with a command go mod init your-public-repo. Mine is go mod init github.com/aristorinjuang/fibonacci. It will create a go.mod file.

    1
    2
    3
    module github.com/aristorinjuang/fibonacci
    
    go 1.16
    
  2. Write Fibonacci functions to the fibonacci.go.

    1
    2
    3
    4
    5
    6
    7
    8
    // Recursion is a Fibonacci function with recursion method.
    func Recursion(index uint) uint {
        if index <= 1 {
            return index
        }
    
        return Recursion(index-1) + Recursion(index-2)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var numbers = map[uint]uint{0: 0, 1: 1}
    
    // Memoization is a Fibonacci function with memoization method.
    func Memoization(index uint) uint {
        if _, ok := numbers[index]; ok {
            return numbers[index]
        }
    
        if index <= 2 {
            numbers[index] = 1
        } else {
            numbers[index] = Memoization(index-1) + Memoization(index-2)
        }
    
        return numbers[index]
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // Iteration is a Fibonacci function with iteration method.
    func Iteration(index uint) uint {
        if index <= 1 {
            return index
        }
    
        var i, result, prev1, prev2 uint = 2, 1, 0, 0
    
        for i <= index {
            prev2 = result
            result += prev1
            prev1 = prev2
            i++
        }
    
        return result
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    func multiply(f1 [2][2]uint, f2 [2][2]uint) [2][2]uint {
        x := f1[0][0]*f2[0][0] + f1[0][1]*f2[1][0]
        y := f1[0][0]*f2[0][1] + f1[0][1]*f2[1][1]
        z := f1[1][0]*f2[0][0] + f1[1][1]*f2[1][0]
        w := f1[1][0]*f2[0][1] + f1[1][1]*f2[1][1]
    
        f1[0][0] = x
        f1[0][1] = y
        f1[1][0] = z
        f1[1][1] = w
    
        return f1
    }
    
    func matrixPower(f1 [2][2]uint, index uint) [2][2]uint {
        if index <= 1 {
            return f1
        }
    
        f1 = matrixPower(f1, index/2)
        f1 = multiply(f1, f1)
    
        var f2 = [2][2]uint{
            {1, 1},
            {1, 0},
        }
        if index%2 != 0 {
            f1 = multiply(f1, f2)
        }
    
        return f1
    }
    
    // Matrix is a Fibonacci function with matrix method.
    func Matrix(index uint) uint {
        if index <= 1 {
            return index
        }
    
        f := [2][2]uint{
            {1, 1},
            {1, 0},
        }
    
        f = matrixPower(f, index-1)
    
        return f[0][0]
    }
    
    1
    2
    3
    4
    5
    6
    import "math"
    
    // Math is a Fibonacci function with math method.
    func Math(index uint) uint {
        return uint(math.Round(math.Pow(float64((1+math.Sqrt(5))/2), float64(index)) / math.Sqrt(5)))
    }
    

    Now, it already meets minimum requirements. But, it is not robust. Let's code more.

  3. Let's create unit tests.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    var samples = []struct {
    	index uint
    	value uint
    }{
    	{0, 0},
    	{1, 1},
    	{2, 1},
    	{3, 2},
    	{4, 3},
    	{5, 5},
    	{6, 8},
    	{7, 13},
    	{8, 21},
    	{9, 34},
    	{10, 55},
    	{11, 89},
    	{12, 144},
    	{15, 610},
    	{20, 6765},
    	{30, 832040},
    	{45, 1134903170},
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    func TestRecursion(t *testing.T) {
        for _, sample := range samples {
            value := Recursion(sample.index)
    
            if value != sample.value {
                t.Errorf("expected %d, got %d", sample.value, value)
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    func TestMemoization(t *testing.T) {
        for _, sample := range samples {
            value := Memoization(sample.index)
    
            if value != sample.value {
                t.Errorf("expected %d, got %d", sample.value, value)
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    func TestIteration(t *testing.T) {
        for _, sample := range samples {
            value := Iteration(sample.index)
    
            if value != sample.value {
                t.Errorf("expected %d, got %d", sample.value, value)
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    func TestMatrix(t *testing.T) {
        for _, sample := range samples {
            value := Matrix(sample.index)
    
            if value != sample.value {
                t.Errorf("expected %d, got %d", sample.value, value)
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    func TestMath(t *testing.T) {
        for _, sample := range samples {
            value := Math(sample.index)
    
            if value != sample.value {
                t.Errorf("expected %d, got %d", sample.value, value)
            }
        }
    }
    
  4. Let's create unit tests to benchmark. We don't need to check whether the function has an error or not in benchmark unit tests. Because we already did it on the previous step.

    1
    2
    3
    4
    5
    6
    7
    func BenchmarkRecursion(b *testing.B) {
        for i := 0; i < b.N; i++ {
            for _, sample := range samples {
                Recursion(sample.index)
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    func BenchmarkMemoization(b *testing.B) {
        for i := 0; i < b.N; i++ {
            for _, sample := range samples {
                Memoization(sample.index)
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    func BenchmarkIteration(b *testing.B) {
        for i := 0; i < b.N; i++ {
            for _, sample := range samples {
                Iteration(sample.index)
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    func BenchmarkMatrix(b *testing.B) {
        for i := 0; i < b.N; i++ {
            for _, sample := range samples {
                Matrix(sample.index)
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    func BenchmarkMath(b *testing.B) {
        for i := 0; i < b.N; i++ {
            for _, sample := range samples {
                Math(sample.index)
            }
        }
    }
    
  5. Let's integrate it with Travis CI by creating a .travis.yml file. So, it will test on the fly every time changes are pushed to the repository.

    1
    2
    3
    4
    5
    6
    language: go
    
    go:
    - 1.16.x
    
    script: go test -v ./...
    
  6. It is better to create a README.md file. Here is mine.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # Fibonacci [![Build Status](https://travis-ci.org/aristorinjuang/fibonacci.svg?branch=master)](https://travis-ci.org/aristorinjuang/fibonacci)
    The Fibonacci package with various methods that returns the Fibonacci number based on the given index as an example for the Go module.
    
    ## Install
    `go get github.com/aristorinjuang/fibonacci`
    
    ## Documentation
    [![GoDoc](https://pkg.go.dev/badge/github.com/aristorinjuang/fibonacci)](https://pkg.go.dev/github.com/aristorinjuang/fibonacci)
    
    ## References
    - [https://aristorinjuang.com/dynamic-programming.html](https://aristorinjuang.com/dynamic-programming.html)
    - [https://leetcode.com/problems/fibonacci-number/solution/](https://leetcode.com/problems/fibonacci-number/solution/)
    
  7. Put a LICENSE file on the root to let pkg.go.dev to fetch it. I suggest MIT License because it is suitable for open-source projects. Here is mine.

    1
    2
    3
    4
    5
    6
    7
    Copyright 2021 Aristo Rinjuang
    
    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    
  8. Congratulations. We made it. Let's publish it to the public repository and tag it with a semantic version. Here is mine, https://github.com/aristorinjuang/fibonacci.

  9. The final one is to access it on pkg.go.dev. The address is pkg.go.dev/<your-public-repository>. Mine is https://pkg.go.dev/github.com/aristorinjuang/fibonacci. Click Request if you see the module is not fetched yet.