cue: add Value.Equals method

Also changed typos for != support for numbers.

Change-Id: I8bc34b50b7c87b31146cd89ea683a4182039b4e1
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2641
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/types.go b/cue/types.go
index 84dcd3e..2074fe1 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -1165,6 +1165,18 @@
 	return u
 }
 
+// Equals reports whether two values are equal.
+// The result is undefined for incomplete values.
+func (v Value) Equals(other Value) bool {
+	if v.path == nil || other.path == nil {
+		return false
+	}
+	x := v.path.val()
+	y := other.path.val()
+	// TODO: improve upon this highly inefficient implementation.
+	return subsumes(v.ctx(), x, y, 0) && subsumes(v.ctx(), y, x, 0)
+}
+
 // Format prints a debug version of a value.
 func (v Value) Format(state fmt.State, verb rune) {
 	ctx := v.ctx()
diff --git a/cue/types_test.go b/cue/types_test.go
index 50a7e76..326a70c 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -921,6 +921,59 @@
 	}
 }
 
+func TestEquals(t *testing.T) {
+	testCases := []struct {
+		a, b string
+		want bool
+	}{{
+		`4`, `4`, true,
+	}, {
+		`"str"`, `2`, false,
+	}, {
+		`2`, `3`, false,
+	}, {
+		`[1]`, `[3]`, false,
+	}, {
+		`[]`, `[]`, true,
+	}, {
+		`{
+			a: b,
+			b: a,
+		}`,
+		`{
+			a: b,
+			b: a,
+		}`,
+		true,
+	}, {
+		`{
+			a: "foo",
+			b: "bar",
+		}`,
+		`{
+			a: "foo",
+		}`,
+		false,
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			var r Runtime
+			a, err := r.Compile("a", tc.a)
+			if err != nil {
+				t.Fatal(err)
+			}
+			b, err := r.Compile("b", tc.b)
+			if err != nil {
+				t.Fatal(err)
+			}
+			got := a.Value().Equals(b.Value())
+			if got != tc.want {
+				t.Errorf("got %v; want %v", got, tc.want)
+			}
+		})
+	}
+}
+
 func TestDecode(t *testing.T) {
 	type fields struct {
 		A int `json:"A"`
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index d2315fa..568d643 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -576,10 +576,6 @@
 	// Type may be number of float.
 
 	switch op, a := v.Expr(); op {
-	// TODO: support the following JSON schema constraints
-	// - multipleOf
-	// setIntConstraint(t, "multipleOf", a)
-
 	case cue.LessThanOp:
 		b.setFilter("Schema", "exclusiveMaximum", b.big(a[0]))
 
@@ -595,8 +591,8 @@
 	case cue.NotEqualOp:
 		i := b.big(a[0])
 		b.setNot("allOff", []*oaSchema{
-			b.kv("minItems", i),
-			b.kv("maxItems", i),
+			b.kv("minimum", i),
+			b.kv("maximum", i),
 		})
 
 	case cue.CallOp:
diff --git a/encoding/openapi/testdata/nums.cue b/encoding/openapi/testdata/nums.cue
index a20b36b..d5b19c6 100644
--- a/encoding/openapi/testdata/nums.cue
+++ b/encoding/openapi/testdata/nums.cue
@@ -1,3 +1,5 @@
 import "math"
 
 mul: math.MultipleOf(5)
+
+neq: !=4
diff --git a/encoding/openapi/testdata/nums.json b/encoding/openapi/testdata/nums.json
index 819d5e8..65c5564 100644
--- a/encoding/openapi/testdata/nums.json
+++ b/encoding/openapi/testdata/nums.json
@@ -6,6 +6,22 @@
          "mul": {
             "type": "number",
             "multipleOf": 5
+         },
+         "neq": {
+            "not": {
+               "type": "number",
+               "allOff": [
+                  {
+                     "type": "number",
+                     "minimum": 4
+                  },
+                  {
+                     "type": "number",
+                     "maximum": 4
+                  }
+               ]
+            },
+            "type": "number"
          }
       }
    }