diff --git a/.github/workflows/backend-sanity-checks.yml b/.github/workflows/backend-sanity-checks.yml index 34337a0..33f9065 100644 --- a/.github/workflows/backend-sanity-checks.yml +++ b/.github/workflows/backend-sanity-checks.yml @@ -14,7 +14,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v4 with: - go-version: "1.20.x" + go-version: "1.21.x" - name: Run go vet run: go vet ./... > govetresult.json - name: Upload Go test results @@ -30,7 +30,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v4 with: - go-version: "1.20.x" + go-version: "1.21.x" - name: Run go fmt id: run-go-fmt run: | @@ -51,7 +51,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v4 with: - go-version: "1.20.x" + go-version: "1.21.x" - name: Install dependencies run: | go install gotest.tools/gotestsum@latest diff --git a/.gitignore b/.gitignore index 531d484..f017602 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ measurements.txt +measurements.out trace* -.DS_store +.DS_store \ No newline at end of file diff --git a/go.mod b/go.mod index f80d1e2..461c3d5 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,10 @@ module github.com/shraddhaag/1brc-go -go 1.20 +go 1.21 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.8.4 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8cf6655 --- /dev/null +++ b/go.sum @@ -0,0 +1,9 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 762e46a..3f6c86d 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "cmp" "errors" "flag" "fmt" @@ -11,15 +12,17 @@ import ( "runtime" "runtime/pprof" "runtime/trace" - "sort" "strconv" "strings" "sync" + + "slices" ) var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") var memprofile = flag.String("memprofile", "", "write memory profile to `file`") var executionprofile = flag.String("execprofile", "", "write tarce execution to `file`") +var input = flag.String("input", "", "path to the input file to evaluate") func main() { @@ -47,7 +50,7 @@ func main() { defer pprof.StopCPUProfile() } - fmt.Println(evaluate()) + fmt.Println(evaluate(*input)) if *memprofile != "" { f, err := os.Create("./profiles/" + *memprofile) @@ -62,22 +65,28 @@ func main() { } } -func evaluate() string { +type result struct { + city string + temp string +} + +func evaluate(input string) string { // mapOfTemp, err := readFileLineByLineIntoAMap("./test_cases/measurements-rounding.txt") - mapOfTemp, err := readFileLineByLineIntoAMap("measurements.txt") + // mapOfTemp, err := readFileLineByLineIntoAMap("measurements.txt") + mapOfTemp, err := readFileLineByLineIntoAMap(input) if err != nil { panic(err) } - var result []string + var resultArr []result var wg sync.WaitGroup var mx sync.Mutex - updateResult := func(input string) { + updateResult := func(city, temp string) { mx.Lock() defer mx.Unlock() - result = append(result, input) + resultArr = append(resultArr, result{city, temp}) } for city, temps := range mapOfTemp { @@ -85,7 +94,7 @@ func evaluate() string { go func(city string, temps []float64) { defer wg.Done() var min, max, avg float64 - min, max = math.MaxFloat64, 0 + min, max = math.MaxFloat64, math.MinInt64 for _, temp := range temps { if temp < min { @@ -101,14 +110,21 @@ func evaluate() string { avg = avg / float64(len(temps)) avg = math.Ceil(avg*10) / 10 - updateResult(fmt.Sprintf("%s=%.1f/%.1f/%.1f", city, min, avg, max)) + updateResult(city, fmt.Sprintf("%.1f/%.1f/%.1f", min, avg, max)) }(city, temps) } wg.Wait() - sort.Strings(result) - return strings.Join(result, ", ") + slices.SortFunc(resultArr, func(i, j result) int { + return cmp.Compare(i.city, j.city) + }) + + var stringsBuilder strings.Builder + for _, i := range resultArr { + stringsBuilder.WriteString(fmt.Sprintf("%s=%s, ", i.city, i.temp)) + } + return stringsBuilder.String()[:stringsBuilder.Len()-2] } func readFileLineByLineIntoAMap(filepath string) (map[string][]float64, error) { diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..d75846a --- /dev/null +++ b/main_test.go @@ -0,0 +1,43 @@ +package main + +import ( + "io/fs" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMain(t *testing.T) { + + inputFiles := find("./test_cases", ".txt") + for _, test := range inputFiles { + t.Run(test, func(t *testing.T) { + output := evaluate(test + ".txt") + assert.Equal(t, readFile(test+".out"), "{"+output+"}\n") + }) + } +} + +func readFile(input string) string { + fileContent, err := os.ReadFile(input) + if err != nil { + panic(err) + } + return string(fileContent) +} + +func find(root, ext string) []string { + var a []string + filepath.WalkDir(root, func(s string, d fs.DirEntry, e error) error { + if e != nil { + return e + } + if filepath.Ext(d.Name()) == ext { + a = append(a, s[:len(s)-len(ext)]) + } + return nil + }) + return a +}