aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad S Anwar <Mohammad.Anwar@yahoo.com>2022-06-21 22:13:38 +0100
committerGitHub <noreply@github.com>2022-06-21 22:13:38 +0100
commit524d19abc1aebe2bddaf77707da6f4a4d9f94b8f (patch)
tree7de2c89ed9ddb1bcb672e96d40fae3f717fe7524
parentb006cfa62bb3036a321458da2c85fdfd97b0534f (diff)
parente1930fafc54083d1d8e616a40b1b30ef2aa06829 (diff)
downloadperlweeklychallenge-club-524d19abc1aebe2bddaf77707da6f4a4d9f94b8f.tar.gz
perlweeklychallenge-club-524d19abc1aebe2bddaf77707da6f4a4d9f94b8f.tar.bz2
perlweeklychallenge-club-524d19abc1aebe2bddaf77707da6f4a4d9f94b8f.zip
Merge pull request #6304 from pokgopun/pwc170
pwc170 solution in go
-rw-r--r--challenge-170/pokgopun/go/ch-1.go30
-rw-r--r--challenge-170/pokgopun/go/ch-2.go68
-rw-r--r--challenge-170/pokgopun/go/kronecker/kronecker.go58
-rw-r--r--challenge-170/pokgopun/go/kronecker/kronecker_test.go125
-rw-r--r--challenge-170/pokgopun/go/primorial/primorial.go21
-rw-r--r--challenge-170/pokgopun/go/primorial/primorial_test.go18
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/LICENSE27
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/compare.go667
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/export_panic.go16
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/export_unsafe.go36
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go18
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go123
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go398
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go9
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/internal/function/func.go99
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/internal/value/name.go164
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go34
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go37
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go106
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go48
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/options.go552
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/path.go378
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/report.go54
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/report_compare.go435
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/report_references.go264
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/report_reflect.go407
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/report_slices.go613
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/report_text.go431
-rw-r--r--challenge-170/pokgopun/go/vendor/github.com/google/go-cmp/cmp/report_value.go121
-rw-r--r--challenge-170/pokgopun/go/vendor/modules.txt7
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.NumFie