diff options
30 files changed, 5364 insertions, 0 deletions
diff --git a/challenge-170/pokgopun/go/ch-1.go b/challenge-170/pokgopun/go/ch-1.go new file mode 100644 index 0000000000..da5a83fee2 --- /dev/null +++ b/challenge-170/pokgopun/go/ch-1.go @@ -0,0 +1,30 @@ +/* +Write a script to generate first 10 Primorial Numbers. + + +Primorial numbers are those formed by multiplying successive prime numbers. + +Usage: +go run ch-1.go 41 +[1 2 6 30 210 2310 30030 510510 9699690 223092870 6469693230 200560490130 7420738134810 304250263527210 13082761331670030 614889782588491410 32589158477190044730 1922760350154212639070 117288381359406970983270 7858321551080267055879090 557940830126698960967415390 40729680599249024150621323470 3217644767340672907899084554130 267064515689275851355624017992790 23768741896345550770650537601358310 2305567963945518424753102147331756070 232862364358497360900063316880507363070 23984823528925228172706521638692258396210 2566376117594999414479597815340071648394470 279734996817854936178276161872067809674997230 31610054640417607788145206291543662493274686990 4014476939333036189094441199026045136645885247730 525896479052627740771371797072411912900610967452630 72047817630210000485677936198920432067383702541010310 10014646650599190067509233131649940057366334653200433090 1492182350939279320058875736615841068547583863326864530410 225319534991831177328890236228992001350685163362356544091910 35375166993717494840635767087951744212057570647889977422429870 5766152219975951659023630035336134306565384015606066319856068810 962947420735983927056946215901134429196419130606213075415963491270 166589903787325219380851695350896256250980509594874862046961683989710] +*/ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/pokgopun/go/primorial" +) + +func main() { + n := uint(10) + if len(os.Args) > 1 { + i, err := fmt.Sscanf(os.Args[1], "%d", &n) + if i > 1 { + log.Fatal(err) + } + } + fmt.Println(primorial.PrimorialNums(n)) +} diff --git a/challenge-170/pokgopun/go/ch-2.go b/challenge-170/pokgopun/go/ch-2.go new file mode 100644 index 0000000000..92f64cc4dc --- /dev/null +++ b/challenge-170/pokgopun/go/ch-2.go @@ -0,0 +1,68 @@ +/* +You are given 2 matrices. + +Write a script to implement Kronecker Product on the given 2 matrices. + +Usage: +go run ch-2.go '[[1,2,3],[4,5,6],[7,8,9]]' '[[9,8,7],[6,5,4],[3,2,1]]' +A = [1 2 3] + [4 5 6] + [7 8 9] + +B = [9 8 7] + [6 5 4] + [3 2 1] + +A x B = [ 9 8 7 18 16 14 27 24 21] + [ 6 5 4 12 10 8 18 15 12] + [ 3 2 1 6 4 2 9 6 3] + [36 32 28 45 40 35 54 48 42] + [24 20 16 30 25 20 36 30 24] + [12 8 4 15 10 5 18 12 6] + [63 56 49 72 64 56 81 72 63] + [42 35 28 48 40 32 54 45 36] + [21 14 7 24 16 8 27 18 9] +*/ +package main + +import ( + "bufio" + "encoding/json" + "log" + "os" + + "github.com/pokgopun/go/kronecker" +) + +func main() { + var matrix [3][][]int + if len(os.Args) > 2 { + for i := 0; i < 2; i++ { + err := json.Unmarshal([]byte(os.Args[i+1]), &matrix[i]) + if err != nil { + log.Fatal(err) + } + } + } else { + matrix = [3][][]int{ + [][]int{ + []int{1, 2}, + []int{3, 4}, + }, + [][]int{ + []int{5, 6}, + []int{7, 8}, + }, + } + } + var err error + matrix[2], err = kronecker.Product(matrix[0], matrix[1]) + if err != nil { + log.Fatal(err) + } + w := bufio.NewWriter(os.Stdout) + for i, v := range []string{"A", "B", "A x B"} { + w.WriteString(kronecker.MatrixString(v, matrix[i]) + "\n") + } + w.Flush() +} diff --git a/challenge-170/pokgopun/go/kronecker/kronecker.go b/challenge-170/pokgopun/go/kronecker/kronecker.go new file mode 100644 index 0000000000..8fa0456dad --- /dev/null +++ b/challenge-170/pokgopun/go/kronecker/kronecker.go @@ -0,0 +1,58 @@ +package kronecker + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +func Product(a [][]int, b [][]int) ([][]int, error) { + if !isMatrix(a) || !isMatrix(b) { + return [][]int{}, errors.New("not a matrix") + } + r := make([][]int, len(a)*len(b)) + for i := 0; i < len(a); i++ { + o := i * len(b) + for j := 0; j < len(a[i]); j++ { + for p := 0; p < len(b); p++ { + for q := 0; q < len(b[p]); q++ { + r[p+o] = append(r[p+o], b[p][q]*a[i][j]) + } + } + } + } + return r, nil +} +func isMatrix(m [][]int) bool { + if len(m) < 1 { + return false + } + l := len(m[0]) + if l < 1 { + return false + } + for _, v := range m[1:] { + if l != len(v) { + return false + } + } + return true +} +func MatrixString(str string, s [][]int) string { + var max int + for i := 0; i < len(s); i++ { + for j := 0; j < len(s[i]); j++ { + if max < s[i][j] { + max = s[i][j] + } + } + } + pw := len(strconv.Itoa(max)) + var b strings.Builder + b.WriteString(fmt.Sprintf("%[1]*s = %[3]*v\n", len(str), str, pw, s[0])) + for _, v := range s[1:] { + b.WriteString(fmt.Sprintf("%[1]*s %[3]*v\n", len(str), "", pw, v)) + } + return b.String() +} diff --git a/challenge-170/pokgopun/go/kronecker/kronecker_test.go b/challenge-170/pokgopun/go/kronecker/kronecker_test.go new file mode 100644 index 0000000000..2c8863a4f7 --- /dev/null +++ b/challenge-170/pokgopun/go/kronecker/kronecker_test.go @@ -0,0 +1,125 @@ +package kronecker + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestProduct(t *testing.T) { + data := []struct { + name string + a [][]int + b [][]int + ab [][]int + errMsg string + }{ + { + "2x2 x 2x2", + [][]int{ + []int{1, 2}, + []int{3, 4}, + }, + [][]int{ + []int{5, 6}, + []int{7, 8}, + }, + [][]int{ + []int{5, 6, 10, 12}, + []int{7, 8, 14, 16}, + []int{15, 18, 20, 24}, + []int{21, 24, 28, 32}, + }, + "", + }, + { + "2x2 x 3x3", + [][]int{ + []int{1, 2}, + []int{3, 4}, + }, + [][]int{ + []int{1, 2, 3}, + []int{4, 5, 6}, + []int{7, 8, 9}, + }, + [][]int{ + []int{1, 2, 3, 2, 4, 6}, + []int{4, 5, 6, 8, 10, 12}, + []int{7, 8, 9, 14, 16, 18}, + []int{3, 6, 9, 4, 8, 12}, + []int{12, 15, 18, 16, 20, 24}, + []int{21, 24, 27, 28, 32, 36}, + }, + "", + }, + { + "3x3 x 2x2", + [][]int{ + []int{1, 2, 3}, + []int{4, 5, 6}, + []int{7, 8, 9}, + }, + [][]int{ + []int{1, 2}, + []int{3, 4}, + }, + [][]int{ + []int{1, 2, 2, 4, 3, 6}, + []int{3, 4, 6, 8, 9, 12}, + []int{4, 8, 5, 10, 6, 12}, + []int{12, 16, 15, 20, 18, 24}, + []int{7, 14, 8, 16, 9, 18}, + []int{21, 28, 24, 32, 27, 36}, + }, + "", + }, + { + "invalid x 2x2", + [][]int{ + []int{1}, + []int{1, 2}, + }, + [][]int{ + []int{1, 2}, + []int{3, 4}, + }, + [][]int{}, + "not a matrix", + }, + { + "2x2 x blank", + [][]int{ + []int{1, 2}, + []int{3, 4}, + }, + [][]int{}, + [][]int{}, + "not a matrix", + }, + { + "blank x invalid", + [][]int{}, + [][]int{ + []int{1, 2}, + []int{}, + }, + [][]int{}, + "not a matrix", + }} + for _, d := range data { + t.Run(d.name, func(t *testing.T) { + res, err := Product(d.a, d.b) + if diff := cmp.Diff(d.ab, res); diff != "" { + t.Error(diff) + } + var errMsg string + if err != nil { + errMsg = err.Error() + } + if errMsg != d.errMsg { + t.Errorf("Expected error message `%s`, got `%s`", d.errMsg, errMsg) + } + }) + } +} diff --git a/challenge-170/pokgopun/go/primorial/primorial.go b/challenge-170/pokgopun/go/primorial/primorial.go new file mode 100644 index 0000000000..4751122e72 --- /dev/null +++ b/challenge-170/pokgopun/go/primorial/primorial.go @@ -0,0 +1,21 @@ +package primorial + +import ( + "math/big" +) + +func PrimorialNums(n uint) (s []*big.Int) { + j := big.NewInt(1) + var l int + for l < int(n) { + if j.Cmp(big.NewInt(1)) == 0 || j.ProbablyPrime(0) { + s = append(s, new(big.Int).Set(j)) + l = len(s) + if l > 1 { + s[l-1].Mul(s[l-1], s[l-2]) + } + } + j.Add(j, big.NewInt(1)) + } + return s +} diff --git a/challenge-170/pokgopun/go/primorial/primorial_test.go b/challenge-170/pokgopun/go/primorial/primorial_test.go new file mode 100644 index 0000000000..c4fa165558 --- /dev/null +++ b/challenge-170/pokgopun/go/primorial/primorial_test.go @@ -0,0 +1,18 @@ +package primorial + +import ( + "strings" + "testing" +) + +const ansString = "1, 2, 6, 30, 210, 2310, 30030, 510510, 9699690, 223092870, 6469693230, 200560490130, 7420738134810, 304250263527210, 13082761331670030, 614889782588491410, 32589158477190044730, 1922760350154212639070, 117288381359406970983270, 7858321551080267055879090" + +func TestPrimorialNums(t *testing.T) { + ans := strings.Split(ansString, ", ") + for i, v := range PrimorialNums(uint(len(ans))) { + res := v.String() + if ans[i] != res { + t.Error("incorrect result, expected " + ans[i] + ", got " + res) + } + } +} diff --git a/challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/LICENSE b/challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/LICENSE new file mode 100644 index 0000000000..32017f8fa1 --- /dev/null +++ b/challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2017 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/compare.go b/challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/compare.go new file mode 100644 index 0000000000..fd2b3a42b2 --- /dev/null +++ b/challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/compare.go @@ -0,0 +1,667 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cmp determines equality of values. +// +// This package is intended to be a more powerful and safer alternative to +// reflect.DeepEqual for comparing whether two values are semantically equal. +// It is intended to only be used in tests, as performance is not a goal and +// it may panic if it cannot compare the values. Its propensity towards +// panicking means that its unsuitable for production environments where a +// spurious panic may be fatal. +// +// The primary features of cmp are: +// +// • When the default behavior of equality does not suit the needs of the test, +// custom equality functions can override the equality operation. +// For example, an equality function may report floats as equal so long as they +// are within some tolerance of each other. +// +// • Types that have an Equal method may use that method to determine equality. +// This allows package authors to determine the equality operation for the types +// that they define. +// +// • If no custom equality functions are used and no Equal method is defined, +// equality is determined by recursively comparing the primitive kinds on both +// values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported +// fields are not compared by default; they result in panics unless suppressed +// by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly +// compared using the Exporter option. +package cmp + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp/internal/diff" + "github.com/google/go-cmp/cmp/internal/function" + "github.com/google/go-cmp/cmp/internal/value" +) + +// TODO(≥go1.18): Use any instead of interface{}. + +// Equal reports whether x and y are equal by recursively applying the +// following rules in the given order to x and y and all of their sub-values: +// +// • Let S be the set of all Ignore, Transformer, and Comparer options that +// remain after applying all path filters, value filters, and type filters. +// If at least one Ignore exists in S, then the comparison is ignored. +// If the number of Transformer and Comparer options in S is greater than one, +// then Equal panics because it is ambiguous which option to use. +// If S contains a single Transformer, then use that to transform the current +// values and recursively call Equal on the output values. +// If S contains a single Comparer, then use that to compare the current values. +// Otherwise, evaluation proceeds to the next rule. +// +// • If the values have an Equal method of the form "(T) Equal(T) bool" or +// "(T) Equal(I) bool" where T is assignable to I, then use the result of +// x.Equal(y) even if x or y is nil. Otherwise, no such method exists and +// evaluation proceeds to the next rule. +// +// • Lastly, try to compare x and y based on their basic kinds. +// Simple kinds like booleans, integers, floats, complex numbers, strings, and +// channels are compared using the equivalent of the == operator in Go. +// Functions are only equal if they are both nil, otherwise they are unequal. +// +// Structs are equal if recursively calling Equal on all fields report equal. +// If a struct contains unexported fields, Equal panics unless an Ignore option +// (e.g., cmpopts.IgnoreUnexported) ignores that field or the Exporter option +// explicitly permits comparing the unexported field. +// +// Slices are equal if they are both nil or both non-nil, where recursively +// calling Equal on all non-ignored slice or array elements report equal. +// Empty non-nil slices and nil slices are not equal; to equate empty slices, +// consider using cmpopts.EquateEmpty. +// +// Maps are equal if they are both nil or both non-nil, where recursively +// calling Equal on all non-ignored map entries report equal. +// Map keys are equal according to the == operator. +// To use custom comparisons for map keys, consider using cmpopts.SortMaps. +// Empty non-nil maps and nil maps are not equal; to equate empty maps, +// consider using cmpopts.EquateEmpty. +// +// Pointers and interfaces are equal if they are both nil or both non-nil, +// where they have the same underlying concrete type and recursively +// calling Equal on the underlying values reports equal. +// +// Before recursing into a pointer, slice element, or map, the current path +// is checked to detect whether the address has already been visited. +// If there is a cycle, then the pointed at values are considered equal +// only if both addresses were previously visited in the same path step. +func Equal(x, y interface{}, opts ...Option) bool { + s := newState(opts) + s.compareAny(rootStep(x, y)) + return s.result.Equal() +} + +// Diff returns a human-readable report of the differences between two values: +// y - x. It returns an empty string if and only if Equal returns true for the +// same input values and options. +// +// The output is displayed as a literal in pseudo-Go syntax. +// At the start of each line, a "-" prefix indicates an element removed from x, +// a "+" prefix to indicates an element added from y, and the lack of a prefix +// indicates an element common to both x and y. If possible, the output +// uses fmt.Stringer.String or error.Error methods to produce more humanly +// readable outputs. In such cases, the string is prefixed with either an +// 's' or 'e' character, respectively, to indicate that the method was called. +// +// Do not depend on this output being stable. If you need the ability to +// programmatically interpret the difference, consider using a custom Reporter. +func Diff(x, y interface{}, opts ...Option) string { + s := newState(opts) + + // Optimization: If there are no other reporters, we can optimize for the + // common case where the result is equal (and thus no reported difference). + // This avoids the expensive construction of a difference tree. + if len(s.reporters) == 0 { + s.compareAny(rootStep(x, y)) + if s.result.Equal() { + return "" + } + s.result = diff.Result{} // Reset results + } + + r := new(defaultReporter) + s.reporters = append(s.reporters, reporter{r}) + s.compareAny(rootStep(x, y)) + d := r.String() + if (d == "") != s.result.Equal() { + panic("inconsistent difference and equality results") + } + return d +} + +// rootStep constructs the first path step. If x and y have differing types, +// then they are stored within an empty interface type. +func rootStep(x, y interface{}) PathStep { + vx := reflect.ValueOf(x) + vy := reflect.ValueOf(y) + + // If the inputs are different types, auto-wrap them in an empty interface + // so that they have the same parent type. + var t reflect.Type + if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() { + t = reflect.TypeOf((*interface{})(nil)).Elem() + if vx.IsValid() { + vvx := reflect.New(t).Elem() + vvx.Set(vx) + vx = vvx + } + if vy.IsValid() { + vvy := reflect.New(t).Elem() + vvy.Set(vy) + vy = vvy + } + } else { + t = vx.Type() + } + + return &pathStep{t, vx, vy} +} + +type state struct { + // These fields represent the "comparison state". + // Calling statelessCompare must not result in observable changes to these. + result diff.Result // The current result of comparison + curPath Path // The current path in the value tree + curPtrs pointerPath // The current set of visited pointers + reporters []reporter // Optional reporters + + // recChecker checks for infinite cycles applying the same set of + // transformers upon the output of itself. + recChecker recChecker + + // dynChecker triggers pseudo-random checks for option correctness. + // It is safe for statelessCompare to mutate this value. + dynChecker dynChecker + + // These fields, once set by processOption, will not change. + exporters []exporter // List of exporters for structs with unexported fields + opts Options // List of all fundamental and filter options +} + +func newState(opts []Option) *state { + // Always ensure a validator option exists to validate the inputs. + s := &state{opts: Options{validator{}}} + s.curPtrs.Init() + s.processOption(Options(opts)) + return s +} + +func (s *state) processOption(opt Option) { + switch opt := opt.(type) { + case nil: + case Options: + for _, o := range opt { + s.processOption(o) + } + case coreOption: + type filtered interface { + isFiltered() bool + } + if fopt, ok := opt.(filtered); ok && !fopt.isFiltered() { + panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt)) + } + s.opts = append(s.opts, opt) + case exporter: + s.exporters = append(s.exporters, opt) + case reporter: + s.reporters = append(s.reporters, opt) + default: + panic(fmt.Sprintf("unknown option %T", opt)) + } +} + +// statelessCompare compares two values and returns the result. +// This function is stateless in that it does not alter the current result, +// or output to any registered reporters. +func (s *state) statelessCompare(step PathStep) diff.Result { + // We do not save and restore curPath and curPtrs because all of the + // compareX methods should properly push and pop from them. + // It is an implementation bug if the contents of the paths differ from + // when calling this function to when returning from it. + + oldResult, oldReporters := s.result, s.reporters + s.result = diff.Result{} // Reset result + s.reporters = nil // Remove reporters to avoid spurious printouts + s.compareAny(step) + res := s.result + s.result, s.reporters = oldResult, oldReporters + return res +} + +func (s *state) compareAny(step PathStep) { + // Update the path stack. + s.curPath.push(step) + defer s.curPath.pop() + for _, r := range s.reporters { + r.PushStep(step) + defer r.PopStep() + } + s.recChecker.Check(s.curPath) + + // Cycle-detection for slice elements (see NOTE in compareSlice). + t := step.Type() + vx, vy := step.Values() + if si, ok := step.(SliceIndex); ok && si.isSlice && vx.IsValid() && vy.IsValid() { + px, py := vx.Addr(), vy.Addr() + if eq, visited := s.curPtrs.Push(px, py); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(px, py) + } + + // Rule 1: Check whether an option applies on this node in the value tree. + if s.tryOptions(t, vx, vy) { + return + } + + // Rule 2: Check whether the type has a valid Equal method. + if s.tryMethod(t, vx, vy) { + return + } + + // Rule 3: Compare based on the underlying kind. + switch t.Kind() { + case reflect.Bool: + s.report(vx.Bool() == vy.Bool(), 0) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s.report(vx.Int() == vy.Int(), 0) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + s.report(vx.Uint() == vy.Uint(), 0) + case reflect.Float32, reflect.Float64: + s.report(vx.Float() == vy.Float(), 0) + case reflect.Complex64, reflect.Complex128: + s.report(vx.Complex() == vy.Complex(), 0) + case reflect.String: + s.report(vx.String() == vy.String(), 0) + case reflect.Chan, reflect.UnsafePointer: + s.report(vx.Pointer() == vy.Pointer(), 0) + case reflect.Func: + s.report(vx.IsNil() && vy.IsNil(), 0) + case reflect.Struct: + s.compareStruct(t, vx, vy) + case reflect.Slice, reflect.Array: + s.compareSlice(t, vx, vy) + case reflect.Map: + s.compareMap(t, vx, vy) + case reflect.Ptr: + s.comparePtr(t, vx, vy) + case reflect.Interface: + s.compareInterface(t, vx, vy) + default: + panic(fmt.Sprintf("%v kind not handled", t.Kind())) + } +} + +func (s *state) tryOptions(t reflect.Type, vx, vy reflect.Value) bool { + // Evaluate all filters and apply the remaining options. + if opt := s.opts.filter(s, t, vx, vy); opt != nil { + opt.apply(s, vx, vy) + return true + } + return false +} + +func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool { + // Check if this type even has an Equal method. + m, ok := t.MethodByName("Equal") + if !ok || !function.IsType(m.Type, function.EqualAssignable) { + return false + } + + eq := s.callTTBFunc(m.Func, vx, vy) + s.report(eq, reportByMethod) + return true +} + +func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value { + if !s.dynChecker.Next() { + return f.Call([]reflect.Value{v})[0] + } + + // Run the function twice and ensure that we get the same results back. + // We run in goroutines so that the race detector (if enabled) can detect + // unsafe mutations to the input. + c := make(chan reflect.Value) + go detectRaces(c, f, v) + got := <-c + want := f.Call([]reflect.Value{v})[0] + if step.vx, step.vy = got, want; !s.statelessCompare(step).Equal() { + // To avoid false-positives with non-reflexive equality operations, + // we sanity check whether a value is equal to itself. + if step.vx, step.vy = want, want; !s.statelessCompare(step).Equal() { + return want + } + panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf(f))) + } + return want +} + +func (s *state) callTTBFunc(f, x, y reflect.Value) bool { + if !s.dynChecker.Next() { + return f.Call([]reflect.Value{x, y})[0].Bool() + } + + // Swapping the input arguments is sufficient to check that + // f is symmetric and deterministic. + // We run in goroutines so that the race detector (if enabled) can detect + // unsafe mutations to the input. + c := make(chan reflect.Value) + go detectRaces(c, f, y, x) + got := <-c + want := f.Call([]reflect.Value{x, y})[0].Bool() + if !got.IsValid() || got.Bool() != want { + panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf(f))) + } + return want +} + +func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { + var ret reflect.Value + defer func() { + recover() // Ignore panics, let the other call to f panic instead + c <- ret + }() + ret = f.Call(vs)[0] +} + +func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) { + var addr bool + var vax, vay reflect.Value // Addressable versions of vx and vy + + var mayForce, mayForceInit bool + step := StructField{&structField{}} + for i := 0; i < t.NumField(); i++ { + step.typ = t.Field(i).Type + step.vx = vx.Field(i) + step.vy = vy.Field(i) + step.name = t.Field(i).Name + step.idx = i + step.unexported = !isExported(step.name) + if step.unexported { + if step.name == "_" { + continue + } + // Defer checking of unexported fields until later to give an + // Ignore a chance to ignore the field. + if !vax.IsValid() || !vay.IsValid() { + // For retrieveUnexportedField to work, the parent struct must + // be addressable. Create a new copy of the values if + // necessary to make them addressable. + addr = vx.CanAddr() || vy.CanAddr() + vax = makeAddressable(vx) + vay = makeAddressable(vy) + } + if !mayForceInit { + for _, xf := range s.exporters { + mayForce = mayForce || xf(t) + } + mayForceInit = true + } + step.mayForce = mayForce + step.paddr = addr + step.pvx = vax + step.pvy = vay + step.field = t.Field(i) + } + s.compareAny(step) + } +} + +func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) { + isSlice := t.Kind() == reflect.Slice + if isSlice && (vx.IsNil() || vy.IsNil()) { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + + // NOTE: It is incorrect to call curPtrs.Push on the slice header pointer + // since slices represents a list of pointers, rather than a single pointer. |
