internal/filetypes: add auto interpretation

- automatically detect OpenAPI and JSONSchema
  based on strict criteria.
- json+schema no longer magically maps to jsonschema

For inputs, auto mode is now automatically enabled for .json
and .yaml/yml files. Any explicit tag, like json: or data: disables
auto mode.

Change-Id: I391179c4542b823c428e4989e31381e00caa4a45
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5410
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/internal/encoding/detect.go b/internal/encoding/detect.go
new file mode 100644
index 0000000..b125a3f
--- /dev/null
+++ b/internal/encoding/detect.go
@@ -0,0 +1,67 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package encoding
+
+import (
+	"net/url"
+	"path"
+	"strings"
+
+	"cuelang.org/go/cue"
+	"cuelang.org/go/cue/build"
+)
+
+// Detect detects the interpretation.
+func Detect(v cue.Value) (i build.Interpretation) {
+	switch {
+	case isOpenAPI(v):
+		return build.OpenAPI
+	case isJSONSchema(v):
+		return build.JSONSchema
+	}
+	return i
+}
+
+func isOpenAPI(v cue.Value) bool {
+	s, _ := v.Lookup("openapi").String()
+	if !strings.HasPrefix(s, "3.") {
+		return false
+	}
+	if _, err := v.Lookup("info", "title").String(); err != nil {
+		return false
+	}
+	if _, err := v.Lookup("info", "version").String(); err != nil {
+		return false
+	}
+	return true
+}
+
+func isJSONSchema(v cue.Value) bool {
+	s, err := v.Lookup("$schema").String()
+	if err != nil {
+		return false
+	}
+	u, err := url.Parse(s)
+	if err != nil {
+		return false
+	}
+	if u.Hostname() != "json-schema.org" {
+		return false
+	}
+	if _, base := path.Split(u.EscapedPath()); base != "schema" {
+		return false
+	}
+	return true
+}
diff --git a/internal/encoding/detect_test.go b/internal/encoding/detect_test.go
new file mode 100644
index 0000000..bdb1b9b
--- /dev/null
+++ b/internal/encoding/detect_test.go
@@ -0,0 +1,101 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package encoding
+
+import (
+	"testing"
+
+	"cuelang.org/go/cue"
+	"cuelang.org/go/cue/build"
+)
+
+func TestDetect(t *testing.T) {
+	testCases := []struct {
+		name string
+		in   string
+		out  build.Interpretation
+	}{{
+		name: "validOpenAPI",
+		in: `
+		openapi: "3.0.0"
+		info: title: "Foo"
+		info: version: "v1alpha1"
+		`,
+		out: build.OpenAPI,
+	}, {
+		name: "noOpenAPI",
+		in: `
+		info: title: "Foo"
+		info: version: "v1alpha1"
+		`,
+	}, {
+		name: "noTitle",
+		in: `
+		openapi: "3.0.0"
+		info: version: "v1alpha1"
+		`,
+	}, {
+		name: "noVersion",
+		in: `
+		openapi: "3.0.0"
+		info: title: "Foo"
+		`,
+	}, {
+		name: "validJSONSchema",
+		in: `
+		$schema: "https://json-schema.org/schema#"
+		`,
+		out: build.JSONSchema,
+	}, {
+		name: "validJSONSchema",
+		in: `
+		$schema: "https://json-schema.org/draft-07/schema#"
+		`,
+		out: build.JSONSchema,
+	}, {
+		name: "noSchema",
+		in: `
+		$id: "https://acme.com/schema#"
+		`,
+	}, {
+		name: "wrongHost",
+		in: `
+		$schema: "https://acme.com/schema#"
+		`,
+	}, {
+		name: "invalidURL",
+		in: `
+		$schema: "://json-schema.org/draft-07"
+		`,
+	}, {
+		name: "invalidPath",
+		in: `
+		$schema: "https://json-schema.org/draft-07"
+		`,
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var r cue.Runtime
+			inst, err := r.Compile(tc.name, tc.in)
+			if err != nil {
+				t.Fatal(err)
+			}
+			got := Detect(inst.Value())
+			if got != tc.out {
+				t.Errorf("got %v; want %v", got, tc.out)
+			}
+		})
+	}
+}
diff --git a/internal/encoding/encoding.go b/internal/encoding/encoding.go
index 83de8a3..14d579b 100644
--- a/internal/encoding/encoding.go
+++ b/internal/encoding/encoding.go
@@ -45,7 +45,7 @@
 	cfg       *Config
 	closer    io.Closer
 	next      func() (ast.Expr, error)
-	interpret func(*cue.Instance) (file *ast.File, id string, err error)
+	interpret interpretFunc
 	expr      ast.Expr
 	file      *ast.File
 	filename  string // may change on iteration for some formats
@@ -54,6 +54,8 @@
 	err       error
 }
 
+type interpretFunc func(*cue.Instance) (file *ast.File, id string, err error)
+
 // ID returns a canonical identifier for the decoded object or "" if no such
 // identifier could be found.
 func (i *Decoder) ID() string {
@@ -69,6 +71,7 @@
 		return
 	}
 	// Decoder level
+	i.file = nil
 	i.expr, i.err = i.next()
 	i.index++
 	if i.err != nil {
@@ -77,7 +80,8 @@
 	// Interpretations
 	if i.interpret != nil {
 		var r cue.Runtime
-		inst, err := r.CompileFile(i.File())
+		i.file = i.File()
+		inst, err := r.CompileFile(i.file)
 		if err != nil {
 			i.err = err
 			return
@@ -176,33 +180,22 @@
 
 	switch f.Interpretation {
 	case "":
+	case build.Auto:
+		openAPI := openAPIFunc(cfg, f)
+		jsonSchema := jsonSchemaFunc(cfg, f)
+		i.interpret = func(inst *cue.Instance) (file *ast.File, id string, err error) {
+			switch Detect(inst.Value()) {
+			case build.JSONSchema:
+				return jsonSchema(inst)
+			case build.OpenAPI:
+				return openAPI(inst)
+			}
+			return i.file, "", i.err
+		}
 	case build.OpenAPI:
-		i.interpret = func(i *cue.Instance) (file *ast.File, id string, err error) {
-			cfg := &openapi.Config{PkgName: cfg.PkgName}
-			file, err = simplify(openapi.Extract(i, cfg))
-			return file, "", err
-		}
+		i.interpret = openAPIFunc(cfg, f)
 	case build.JSONSchema:
-		i.interpret = func(i *cue.Instance) (file *ast.File, id string, err error) {
-			id = f.Tags["id"]
-			if id == "" {
-				id, _ = i.Lookup("$id").String()
-			}
-			if id != "" {
-				u, err := url.Parse(id)
-				if err != nil {
-					return nil, "", errors.Wrapf(err, token.NoPos, "invalid id")
-				}
-				u.Scheme = ""
-				id = strings.TrimPrefix(u.String(), "//")
-			}
-			cfg := &jsonschema.Config{
-				ID:      id,
-				PkgName: cfg.PkgName,
-			}
-			file, err = simplify(jsonschema.Extract(i, cfg))
-			return file, id, err
-		}
+		i.interpret = jsonSchemaFunc(cfg, f)
 	default:
 		i.err = fmt.Errorf("unsupported interpretation %q", f.Interpretation)
 	}
@@ -237,6 +230,37 @@
 	return i
 }
 
+func jsonSchemaFunc(cfg *Config, f *build.File) interpretFunc {
+	return func(i *cue.Instance) (file *ast.File, id string, err error) {
+		id = f.Tags["id"]
+		if id == "" {
+			id, _ = i.Lookup("$id").String()
+		}
+		if id != "" {
+			u, err := url.Parse(id)
+			if err != nil {
+				return nil, "", errors.Wrapf(err, token.NoPos, "invalid id")
+			}
+			u.Scheme = ""
+			id = strings.TrimPrefix(u.String(), "//")
+		}
+		cfg := &jsonschema.Config{
+			ID:      id,
+			PkgName: cfg.PkgName,
+		}
+		file, err = simplify(jsonschema.Extract(i, cfg))
+		return file, id, err
+	}
+}
+
+func openAPIFunc(c *Config, f *build.File) interpretFunc {
+	cfg := &openapi.Config{PkgName: c.PkgName}
+	return func(i *cue.Instance) (file *ast.File, id string, err error) {
+		file, err = simplify(openapi.Extract(i, cfg))
+		return file, "", err
+	}
+}
+
 func reader(f *build.File, stdin io.Reader) (io.ReadCloser, error) {
 	switch s := f.Source.(type) {
 	case nil:
diff --git a/internal/filetypes/filetypes.go b/internal/filetypes/filetypes.go
index c265070..4e291af 100644
--- a/internal/filetypes/filetypes.go
+++ b/internal/filetypes/filetypes.go
@@ -84,18 +84,20 @@
 		}
 	}
 
-	if s, _ := v.Lookup("interpretation").String(); s != "" {
-		v = v.Unify(i.Lookup("interpretations", s))
-	} else {
+	interpretation, _ := v.Lookup("interpretation").String()
+	if b.Form != "" {
+		v = v.Unify(i.Lookup("forms", string(b.Form)))
+		// may leave some encoding-dependent options open in data mode.
+	} else if interpretation != "" {
+		// always sets schema form.
+		v = v.Unify(i.Lookup("interpretations", interpretation))
+	}
+	if interpretation == "" {
 		s, err := v.Lookup("encoding").String()
 		if err != nil {
 			return nil, err
 		}
 		v = v.Unify(i.Lookup("encodings", s))
-
-	}
-	if b.Form != "" {
-		v = v.Unify(i.Lookup("forms", string(b.Form)))
 	}
 
 	fi := &FileInfo{}
@@ -189,20 +191,30 @@
 	return toFile(inst, val, file)
 }
 
+func hasEncoding(v cue.Value) (concrete, hasDefault bool) {
+	enc := v.Lookup("encoding")
+	d, _ := enc.Default()
+	return enc.IsConcrete(), d.IsConcrete()
+}
+
 func toFile(i, v cue.Value, filename string) (*build.File, error) {
 	v = v.Fill(filename, "filename")
-	if s, _ := v.Lookup("encoding").String(); s == "" {
-		if filename != "-" {
-			ext := filepath.Ext(filename)
-			if ext == "" {
-				return nil, errors.Newf(token.NoPos,
-					"no encoding specified for file %q", filename)
+
+	if concrete, hasDefault := hasEncoding(v); !concrete {
+		if filename == "-" {
+			if !hasDefault {
+				v = v.Unify(i.LookupDef("Default"))
 			}
-			v = v.Unify(i.Lookup("extensions", ext))
-		} else {
-			v = v.Unify(i.LookupDef("Default"))
+		} else if ext := filepath.Ext(filename); ext != "" {
+			if x := i.Lookup("extensions", ext); x.Exists() || !hasDefault {
+				v = v.Unify(x)
+			}
+		} else if !hasDefault {
+			return nil, errors.Newf(token.NoPos,
+				"no encoding specified for file %q", filename)
 		}
 	}
+
 	f := &build.File{}
 	if err := v.Decode(&f); err != nil {
 		return nil, err
diff --git a/internal/filetypes/filetypes_test.go b/internal/filetypes/filetypes_test.go
index 29bc9fc..90a0ce8 100644
--- a/internal/filetypes/filetypes_test.go
+++ b/internal/filetypes/filetypes_test.go
@@ -70,6 +70,7 @@
 		},
 	}, {
 		name: "yaml",
+		mode: Def,
 		in: build.File{
 			Filename: "foo.yaml",
 		},
@@ -111,22 +112,33 @@
 		},
 	}, {
 		name: "JSONDefault",
+		mode: Input,
 		in: build.File{
 			Filename: "data.json",
 		},
 		out: &FileInfo{
 			File: &build.File{
-				Filename: "data.json",
-				Encoding: "json",
-				Form:     "data",
+				Filename:       "data.json",
+				Encoding:       "json",
+				Interpretation: "auto",
+				Form:           "schema",
 			},
-			Data: true,
+			Definitions:  true,
+			Data:         true,
+			Optional:     true,
+			Constraints:  true,
+			References:   true,
+			Cycles:       true,
+			KeepDefaults: true,
+			Incomplete:   true,
+			Imports:      true,
+			Docs:         true,
 		},
 	}, {
-		name: "JSONSchemaDefault",
+		name: "JSONSchema",
 		in: build.File{
-			Filename: "foo.json",
-			Form:     "schema",
+			Filename:       "foo.json",
+			Interpretation: "jsonschema",
 		},
 		out: &FileInfo{
 			File: &build.File{
@@ -236,7 +248,16 @@
 		mode Mode
 		out  interface{}
 	}{{
-		in: "file.json",
+		in:   "file.json",
+		mode: Input,
+		out: &build.File{
+			Filename:       "file.json",
+			Encoding:       "json",
+			Interpretation: "auto",
+		},
+	}, {
+		in:   "file.json",
+		mode: Def,
 		out: &build.File{
 			Filename: "file.json",
 			Encoding: "json",
@@ -246,7 +267,7 @@
 		out: &build.File{
 			Filename:       "file.json",
 			Encoding:       "json",
-			Interpretation: "jsonschema",
+			Interpretation: "auto",
 			Form:           "schema",
 		},
 	}, {
@@ -298,19 +319,31 @@
 	}{{
 		in: "foo.json baz.yaml",
 		out: []*build.File{
-			{Filename: "foo.json", Encoding: "json"},
-			{Filename: "baz.yaml", Encoding: "yaml"},
+			{
+				Filename:       "foo.json",
+				Encoding:       "json",
+				Interpretation: "auto",
+			},
+			{
+				Filename:       "baz.yaml",
+				Encoding:       "yaml",
+				Interpretation: "auto",
+			},
 		},
 	}, {
-		in: "json: foo.data bar.data json+schema: bar.schema",
+		in: "data: foo.cue",
 		out: []*build.File{
-			{Filename: "foo.data", Encoding: "json"},
+			{Filename: "foo.cue", Encoding: "cue", Form: "data"},
+		},
+	}, {
+		in: "json: foo.json bar.data jsonschema: bar.schema",
+		out: []*build.File{
+			{Filename: "foo.json", Encoding: "json"}, // no auto!
 			{Filename: "bar.data", Encoding: "json"},
 			{
 				Filename:       "bar.schema",
 				Encoding:       "json",
 				Interpretation: "jsonschema",
-				Form:           "schema",
 			},
 		},
 	}, {
diff --git a/internal/filetypes/types.cue b/internal/filetypes/types.cue
index 83d255a..5191f38 100644
--- a/internal/filetypes/types.cue
+++ b/internal/filetypes/types.cue
@@ -79,6 +79,9 @@
 	encodings: cue: {
 		*forms.schema | _
 	}
+	extensions: ".json": interpretation: *"auto" | _
+	extensions: ".yaml": interpretation: *"auto" | _
+	extensions: ".yml": interpretation:  *"auto" | _
 }
 
 modes: export: {
@@ -175,15 +178,7 @@
 
 	cue: encoding: "cue"
 
-	json: encoding: "json"
-	json: *{
-		form: *"" | "data"
-	} | {
-		form: *"schema" | "final"
-
-		interpretation: *"jsonschema" | _
-	}
-
+	json: encoding:  "json"
 	jsonl: encoding: "jsonl"
 	yaml: encoding:  "yaml"
 	proto: encoding: "proto"
@@ -204,6 +199,10 @@
 		tags: lang: string
 	}
 
+	auto: {
+		interpretation: "auto"
+		encoding:       *"json" | _
+	}
 	jsonschema: {
 		interpretation: "jsonschema"
 		encoding:       *"json" | _
@@ -326,6 +325,10 @@
 
 interpretations: "": _
 
+interpretations: auto: {
+	forms.schema
+}
+
 interpretations: jsonschema: {
 	forms.schema
 	encoding: *"json" | _
diff --git a/internal/filetypes/types.go b/internal/filetypes/types.go
index 535781d..9428c2d 100644
--- a/internal/filetypes/types.go
+++ b/internal/filetypes/types.go
@@ -47,5 +47,5 @@
 	return v
 }
 
-// Data size: 1107 bytes.
-var cuegenInstanceData = []byte("\x01\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xd4WQ\x8b\xdc6\x10\xb66W\xa8D\xda\xfc\x82\x82\xab\x87pY\xe8\xbe\x16\x16\u00bd\xa4\x81\xbc\x94\xd2\xd7#\x1c:[\xf6\xb9\xf1J\u0196\xcb\x1d\xdd}h\x9a\xa6\xfd\x0f\xfd\xaf\xd92\x1a\u0276d\xef\x1dIK\xa1~\xb2\xbf\xd1\xcc|\x9a\x19i\xc6_\x1c\xffX\x91\xd5\xf1\u03c4\x1c\xdf&\u0277\xc7_\x1f\x11\xf2\xb8R\x9d\x11*\x93/\x84\x11\x80\x93G\xe4\xecG\xad\rY%\xe4\xec\aan\xc8\xe3\x84|\xf6\xb2\xaaeG\x8e\xef\x93$\xf9\xea\xf8\xfb\x8a\x90//_g\xbd\xdc\x14U\xed4\xdf'\xe4\xf8.I\u038f\xbf=\"\xe4\xf3\x11\u007f\x97\x90\x159\xfb^\xec$\x18:\xb3 K\x92\xe4\u00d3\xbf\x80\t!+B\xa8\xb9kd\xb7\xc9zI><y\u06c8\xec\x8d(ez\xddWu\xce\x18\xb8N\xb7\xdb\xf4\x17F\xc1\xaa\x12;\xb9M\xdd\u04d9\xb6R%\xa3Re:\xafT9\b\xbes\x00\xa3\x952\xb2mZi\x84\xa9\xb4\xba\u0626\xaf\x02\x80\xd1B\xb7\xbb\x8bA1M\u04d7\xba\xdd1jD\xd9]X\xaf\xf4\x12\u077c\xde\x0e\xfe\x0e\xec\xc0\xbc\v\xe0\x86\xcf\xd7\xcf9g\xa1y\x10:%0;\xae\x9d\xb0\aO\u0591\x91\xb7\x06=\x8e\xfb\xe1\x00rF-MT\xe6\xb90\x82\x03\t\no\xa8\x81\xe2\x89(\xeb\xe5\xccV\xd6K\x14v\u064d\u0705\x9a\b\xa1\xf8\xa7N\xab\x992\x80 N\x9f\xa6\xe7\xebQq\xcdy\xba\x1f\x1d\xa7{\xab\x17F\x1dV\x81\xba\xf3\x91\xee\u04eb`G\xf8\xac\xf9(\xe7E\xa5D\r\x06\x9f!\x9dz\x91O\x8d|\xef\xc4n.\a\x10\u0165\x8e\x85\xf8\xf0L\xe7\x10\x90\x19[\x0e\xe0\x90\x16Jka-\x96\x1a\xf0\x83\xb5\u0674\xda\xcc\xccr\x8b:\xa7\xadhn\x82\b[\xc4\u7b4c\xd2V\xba\xac\xe9|\x96\xb6O\"\xeb+\xd5\xd1\x1d\xa3\xbfl\x1d\xf3\xe333s1I\x9e5\xa7\x1b\xa9DS}\x92-\xa7\xcb\xf1\x14\xbd\x90\x85\xe8k\x03\a\xc3\x1e\xf4\xa7\xe19_\xf3o\xc0\x90\xdb\xcd\xc1^\x06\xafT\xa1\u0745\x80\xf5?\x14\x90i{\x99\xee\xd3B\u051dd\xb4\x95\x85l\xa5\xcad\xb7\x9d\v\xb3\xbb\xacF\xc1\x82f.\x8bJU\xc0\x17V\\k]\u00d6\xe1[\u0528\x82X\xa6UgZQ)3\xae{#e\xe36\xd5m\x1dV\xa9L\xef\x9aZ\x1a{s9l\xd7\xe8\xd6x\x06\x88u\xa6\x95b8\x13\x88\xe5:\xeb\xc6-\"&\x8ci\xab\xeb\xde\xe0\x06\x1c\xf7\xb5#\x0f!b\a\xb6\u04f9\u0112\xa8T\u04fb{e\x12m[+c\xea\xd6\xf6rp9\xa3\x9b\xcd\x06K\x87F\x01\xa7\x8eO\x14\xb0\xa9\x86\xb7\xe9\xebq\xb8\x88(]C\xb9w\x1b\xac$\xef\xeb\xe0\x8b\xf4\x80\u0257\xb7\x10\x98\x87\xf9N\x8b\xeca\xc2Q\x90>\x8e0\x94\xd9)\xba\xbao\x8c\x0f\xef\u007f\xed[\xfe,\xea\u007f9\xb1\xff\x88\xab\xbd\xb4O\x91\xcde\xf1\u007f(B\x16\xaa\x0e\x8a\xfel:g\u00f6\xc6^\xe9W`\xec\u04bd\x8d%\xa3\xc3\t\xf64\u01fcN\u030c-,p\x04\xa9pfP\xcd6\x91\xc8}\xa4\x18\xac\x8f\u070c#F\xb4\x9f\x13\xcb\xf5\"\xabS\xcb\xefo\x8at\x1a\xf8\x89\xd6\xd8\xf3\x96\x9dL\x14\x98E\xec\xe2K\x18)\x9f\x0f\x93\x99/\x11o\x94\xf3mz\xe5?\xe6\xe3\xce=3\a\xbc\xf9V\x1du\x82\xb8\xe0\u009ep\x1e\x88\x9f\xd99)@\x18\x8d:Fl/\xec\x1d\xb14\xec\"3i\xd0Obi\xd8Y\xa2C>d\xc2\x06`)L\xc38\x16my\x99\xf8\xc9\xf4\xa1\x97\xd9d4&\x03\xe3\x8e\xf3d9\x9d+\xa3N\xefOV\x90\x9d\xe5\xacx4\x8c\xfc\xfd\xc4\xc3H/G8\x8e]4\xd2\u067f\x81\xa1\x8c\xfc\xb4\x11F&>\xcb\xf14?\xfeV\xe0|\x18N4\xc3\u01a6\x93\xccb\f\x16C\xb0\xb8\xab\xf8t\xbb_\x1dyk\xa4\xea0\xcc\xeel\xb9\xe7\x8aQ\x0e\xffm\x88\xc0\x14\n_\x00\xda\xf6\xbc\xf5 |y\xb4\x06x@k\x80\xeb\xdc-\x0fa\xb5\f\xdb\xc9~0\r_\x16u`\x88\x9a[3A\xe1\xfa\x03\xb4\xd4n\v\x16-5`xMyj\xf6\v.\x9c\xaa\x96\xe3\xed\x12\x8f\xa7\xbc\xd0z\xe3~\x8c&?h~T>\xb0p\xfa\xfd\xf8\x9b\xeb\xf4\xdc\x1e\x0e@'\x8e\u06899\xfd\x01]\x96$\u007f\a\x00\x00\xff\xff\x14\xd0\xf53+\x10\x00\x00")
+// Data size: 1122 bytes.
+var cuegenInstanceData = []byte("\x01\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xccW_k\xe46\x10\xb76)T\xe2Z\xee\v\x14T?\x1c\u05c5\xeeC_\n\vG^\xae\aG\xa1\x94\xbe\x86#(^\xed\xc6=[2\xb6\\\x12\x9a}h{\xbd\xf6S\u07d6\xd1H\x96%{\x13\x92\x96r~I\xf67\x9a?\xfa\u034c4\xfa\xec\xf0\u05c2,\x0e\u007fg\xe4\xf0{\x96}{\xf8\ud110'\xa5\xea\x8cP\x85|)\x8c\x00\x9c\x9c\x90\u04df\xb46d\x91\x91\xd3\x1f\x85\xb9\"O2\xf2\u026b\xb2\x92\x1d9\xbc\u03f2\xec\x8b\u00df\vB>?\u007fS\xf4r\xb5-+\xa7\xf9>#\x87wY\xf6\xfc\xf0\xc7\t!\x9f\x06\xfc]F\x16\xe4\xf4\aQK0tjA\x96e\u0647\xa7\xdfC$\x84,\b\xa1\u6991\u076a\xe8%\xf9\xf0\xf4\x9bF\x14o\xc5N\xf2\u02fe\xac6\x8c\x81k\xbe^\xf3_\x19\x05\xabJ\xd4r\xcd\xdd\u05d9\xb6T;F\xa5*\xf4\xa6T\xbbA\xf0\x9d\x03\x18-\x95\x91m\xd3J#L\xa9\xd5\u065a\xbf\x8e\x00F\xb7\xba\xad\xcf\x06E\xce\xf9+\xdd\u058c\x1a\xb1\xeb\u03acWz\x8en\u07ac\a\u007f{\xb6g\xde\x05\u0106\u07d7/\xf2\x9c\xc5\xe6A\xe8\x94\xc0lX;\x8a\x1e<YGF^\x1b\xf4\x18\xf6\x93\x03\x983j\xc3D\xe5|#\x8c\xc8!\b\n\xff\xa1\x06\x8aG\xa2\xa2\x97\x13[E/Q\xd8\x15W\xb2\x8e5\x11B\xf1\u03ddV\x13e\x00\x83\xb8\x9a\x95W\xb8\xe0F\xd4S9\x80(\xde\xe9T\x88_^\xe8\r\x04\x98\xe4l\xcds\x00\a\x9a(\xad\x84\xb5\xb8\u04c0\xef\xad\u0366\xd5fb6\xb7\xa8s\u068a\xe6*\u06b1E<\x8f\xbb\x84\u019dcQo&4>*X_9.\\\xd1O\xa3\xc5o\x89D\xf3[~1g\x1c\x14C\x16\xc6y|\xa0\xa1\xa0\x8e\xe6t#\x95h\xcaG\xd9r\xba96\xc6K\xb9\x15}e\xa0\xd6m\xef>\x8b[w\x99\u007f\r\x86\x1c!{\xdb\u07ef\xd5V\xbb\x1e\u01d2\xf6\xdf\u04b4\xbd\xe4\xb7|+\xaaN2\xda\u02adl\xa5*d\xb7\x9e\n\x8b\x9b\xa2B\xc1\x8c\xe6FnKUB\xbc\xb0\xe2R\xeb\n\xb6\f\xbfE\x85*\x88\x15Zu\xa6\x15\xa52a\xdd[)\x1b\xb7\xa9n\xed\xb0R\x15\xban*i\xeca\u4c3a\u046d\xf1\x11 \u0599V\x8a\xda\a\x85\xd8F\x17]\xd8\"b\u0098\xb6\xbc\xec\rn\xc0\u017et\xc1\x03El\xcfj\xbd\x91XU\xa5jzwT\x8c\u0636\xe5\x16R\xb7\xb4\xfd\xeerFW\xab\x15V\x1fM\b\xa7.\x9e\x84\xb0\xb1\x86\xb7\xe9Kz8[(]B\xc7t+\xac$\xefk\xef\xf5\xae\x8dT\x1drn\x97\xe7+[G^9-\xa4%\x96wd\x06\xcb\aT\xed\x01\xf2H\xd5\aj\xdaBFuy\rI\xbd\x9f\xebq\x83\xdcOv\x92\xe0\x87\x91\r-r,\\\xdd7\u0197\xc6\xff\xed[\xfe\"\xaa\xff\xb8(\xffU\xac\xdbR\x89\xeaX\xb0\x1b\xb9\xfd\xe8\x1b\bN\xd3XuP\xf4\xe7\x8as6l+\\\xdd~\x05r\xc7o-\x97\x8c\x0e\xa7\x8f\x0f3\xe4ud&\xdc\xe0\x91#H\x853\x83j\xf6\x0eM\xdc'\x8a\xd1\xfa\xc4M\x98x\x92\xfd\x1cY\xaeg\xa3:\xb6\xfc\ue640\x8e\x89\x1fi\x85+\u007f\xde\xc9H\x81Y\xc4.>\x87\t\xf7\xc50(\xfa\x12\xf1F\xf3|\xcd/\xfc\x8f\xe9\xf45\\w~\f\xe3\xb7<\xb7\xe5k\xff\xf3\x93Jr\x8b\xa5\x05\x17\xdfg\xcf#\xf1W\xfcY\x8a0\x9a\xdcv\xa9\xbd\xf8\xdeK\xa5\xf1\r8\x91Fwa*\x8do\u0164\u0247LX\x02\xe6hr\xd4L\xb6<\x1f\xf8\xd1\xf4\xa1\x97\xc9`\x18\x92\x81\xbcC\x06` \u013fv\xbeN\xa6\x14\xdfYQv\xe6\xb3\xe2\u0458\xf9\xbb\x03\x8f\x99\x9eg8\xe5.\x99h\xed\xe3d(#?)\xc5\u0324\xbd\x9c>.\xc2+\a\xc7\xe3x\x1a\x1b66\x9e\xc2f9\x98\xa5`vWiw\xbb\x97W2V@o\xb9\uf085\x11\x83s\x18\xc2\xed/\x16\xa6\a\x87\xc2/6\f\x06)\n\xc7~@\vH.\x9a\xb5\x8b\a\xb3vm\xb5q\xfebX\xcd\xc3\xe6\u068c,\xc3\xf1\a\xe8N\xbb-Xt\xa7\x01\xc3c\u02bb\xb3\xbf\xe0\xc0)+\x19N\x97t\xb4\u03b7Z\xaf\xdc;m\xf4^\xf4c\xfe\x9e\u0173\xcf\xc3O\xae\xf0l9\xd2L\xc7\x1f%\xf1\x84tD\xfd\xc8#\xe4\x1e]\x96e\xff\x04\x00\x00\xff\xff\xbe\xea\xa5\xf0\xdb\x10\x00\x00")