Move general reflect logic to internal/value (#15)

This internal package has the API:
	Format(reflect.Value, useStringer bool) string
	SortKeys(vs []reflect.Value) []reflect.Value

While moving Format and SortKeys, we also tweak the formatting logic
to print the type on named primitive types.
diff --git a/cmp/compare.go b/cmp/compare.go
index 2294579..4fff3fd 100644
--- a/cmp/compare.go
+++ b/cmp/compare.go
@@ -31,6 +31,7 @@
 	"reflect"
 
 	"github.com/google/go-cmp/cmp/internal/diff"
+	"github.com/google/go-cmp/cmp/internal/value"
 )
 
 // BUG: Maps with keys containing NaN values cannot be properly compared due to
@@ -442,7 +443,7 @@
 	step := &mapIndex{pathStep: pathStep{t.Elem()}}
 	s.curPath.push(step)
 	defer s.curPath.pop()
-	for _, k := range sortKeys(append(vx.MapKeys(), vy.MapKeys()...)) {
+	for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) {
 		step.key = k
 		vvx := vx.MapIndex(k)
 		vvy := vy.MapIndex(k)
diff --git a/cmp/compare_test.go b/cmp/compare_test.go
index 11c30c2..d7d80d5 100644
--- a/cmp/compare_test.go
+++ b/cmp/compare_test.go
@@ -1438,8 +1438,8 @@
 	-: "southbay2"
 	+: "southbay"
 *{teststructs.Eagle}.Dreamers[1].Animal[0].(teststructs.Goat).Immutable.State:
-	-: 6
-	+: 5
+	-: testprotos.Goat_States(6)
+	+: testprotos.Goat_States(5)
 {teststructs.Eagle}.Slaps[0].Immutable.MildSlap:
 	-: false
 	+: true
@@ -1652,8 +1652,8 @@
 	-: &teststructs.MockTable{state: []string{"a", "c"}}
 	+: &teststructs.MockTable{state: []string{"a", "b", "c"}}
 {teststructs.Dirt}.Discord:
-	-: 554
-	+: 500
+	-: teststructs.DiscordState(554)
+	+: teststructs.DiscordState(500)
 λ({teststructs.Dirt}.Proto):
 	-: "blah"
 	+: "proto"
@@ -1751,10 +1751,10 @@
 	-: 0x04
 	+: 0x03
 {teststructs.Cartel}.poisons[0].poisonType:
-	-: 1
-	+: 5
+	-: testprotos.PoisonType(1)
+	+: testprotos.PoisonType(5)
 {teststructs.Cartel}.poisons[1->?]:
-	-: &teststructs.Poison{poisonType: 2, manufactuer: "acme2"}
+	-: &teststructs.Poison{poisonType: testprotos.PoisonType(2), manufactuer: "acme2"}
 	+: <non-existent>`,
 	}}
 }
diff --git a/cmp/internal/value/format.go b/cmp/internal/value/format.go
new file mode 100644
index 0000000..e26395d
--- /dev/null
+++ b/cmp/internal/value/format.go
@@ -0,0 +1,258 @@
+// 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.md file.
+
+package value
+
+import (
+	"fmt"
+	"reflect"
+	"strings"
+	"unicode"
+	"unicode/utf8"
+)
+
+// formatFakePointers controls whether to substitute pointer addresses with nil.
+// This is used for deterministic testing.
+var formatFakePointers = false
+
+var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
+
+// Format formats the value v as a string.
+//
+// This is similar to fmt.Sprintf("%+v", v) except this:
+//	* Prints the type unless it can be elided
+//	* Avoids printing struct fields that are zero
+//	* Prints a nil-slice as being nil, not empty
+//	* Prints map entries in deterministic order
+func Format(v reflect.Value, useStringer bool) string {
+	return formatAny(v, formatConfig{useStringer, true, true, !formatFakePointers}, nil)
+}
+
+type formatConfig struct {
+	useStringer    bool // Should the String method be used if available?
+	printType      bool // Should we print the type before the value?
+	followPointers bool // Should we recursively follow pointers?
+	realPointers   bool // Should we print the real address of pointers?
+}
+
+func formatAny(v reflect.Value, conf formatConfig, visited map[uintptr]bool) string {
+	// TODO: Should this be a multi-line printout in certain situations?
+
+	if !v.IsValid() {
+		return "<non-existent>"
+	}
+	if conf.useStringer && v.Type().Implements(stringerIface) {
+		if v.Kind() == reflect.Ptr && v.IsNil() {
+			return "<nil>"
+		}
+		return fmt.Sprintf("%q", v.Interface().(fmt.Stringer).String())
+	}
+
+	switch v.Kind() {
+	case reflect.Bool:
+		return formatPrimitive(v.Type(), v.Bool(), conf)
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return formatPrimitive(v.Type(), v.Int(), conf)
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		if v.Type().PkgPath() == "" || v.Kind() == reflect.Uintptr {
+			// Unnamed uints are usually bytes or words, so use hexadecimal.
+			return formatPrimitive(v.Type(), formatHex(v.Uint()), conf)
+		}
+		return formatPrimitive(v.Type(), v.Uint(), conf)
+	case reflect.Float32, reflect.Float64:
+		return formatPrimitive(v.Type(), v.Float(), conf)
+	case reflect.Complex64, reflect.Complex128:
+		return formatPrimitive(v.Type(), v.Complex(), conf)
+	case reflect.String:
+		return formatPrimitive(v.Type(), fmt.Sprintf("%q", v), conf)
+	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
+		return formatPointer(v, conf)
+	case reflect.Ptr:
+		if v.IsNil() {
+			if conf.printType {
+				return fmt.Sprintf("(%v)(nil)", v.Type())
+			}
+			return "<nil>"
+		}
+		if visited[v.Pointer()] || !conf.followPointers {
+			return formatPointer(v, conf)
+		}
+		visited = insertPointer(visited, v.Pointer())
+		return "&" + formatAny(v.Elem(), conf, visited)
+	case reflect.Interface:
+		if v.IsNil() {
+			if conf.printType {
+				return fmt.Sprintf("%v(nil)", v.Type())
+			}
+			return "<nil>"
+		}
+		return formatAny(v.Elem(), conf, visited)
+	case reflect.Slice:
+		if v.IsNil() {
+			if conf.printType {
+				return fmt.Sprintf("%v(nil)", v.Type())
+			}
+			return "<nil>"
+		}
+		if visited[v.Pointer()] {
+			return formatPointer(v, conf)
+		}
+		visited = insertPointer(visited, v.Pointer())
+		fallthrough
+	case reflect.Array:
+		var ss []string
+		subConf := conf
+		subConf.printType = v.Type().Elem().Kind() == reflect.Interface
+		for i := 0; i < v.Len(); i++ {
+			s := formatAny(v.Index(i), subConf, visited)
+			ss = append(ss, s)
+		}
+		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
+		if conf.printType {
+			return v.Type().String() + s
+		}
+		return s
+	case reflect.Map:
+		if v.IsNil() {
+			if conf.printType {
+				return fmt.Sprintf("%v(nil)", v.Type())
+			}
+			return "<nil>"
+		}
+		if visited[v.Pointer()] {
+			return formatPointer(v, conf)
+		}
+		visited = insertPointer(visited, v.Pointer())
+
+		var ss []string
+		subConf := conf
+		subConf.printType = v.Type().Elem().Kind() == reflect.Interface
+		for _, k := range SortKeys(v.MapKeys()) {
+			sk := formatAny(k, formatConfig{realPointers: conf.realPointers}, visited)
+			sv := formatAny(v.MapIndex(k), subConf, visited)
+			ss = append(ss, fmt.Sprintf("%s: %s", sk, sv))
+		}
+		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
+		if conf.printType {
+			return v.Type().String() + s
+		}
+		return s
+	case reflect.Struct:
+		var ss []string
+		subConf := conf
+		subConf.printType = true
+		for i := 0; i < v.NumField(); i++ {
+			vv := v.Field(i)
+			if isZero(vv) {
+				continue // Elide zero value fields
+			}
+			name := v.Type().Field(i).Name
+			subConf.useStringer = conf.useStringer && isExported(name)
+			s := formatAny(vv, subConf, visited)
+			ss = append(ss, fmt.Sprintf("%s: %s", name, s))
+		}
+		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
+		if conf.printType {
+			return v.Type().String() + s
+		}
+		return s
+	default:
+		panic(fmt.Sprintf("%v kind not handled", v.Kind()))
+	}
+}
+
+func formatPrimitive(t reflect.Type, v interface{}, conf formatConfig) string {
+	if conf.printType && t.PkgPath() != "" {
+		return fmt.Sprintf("%v(%v)", t, v)
+	}
+	return fmt.Sprintf("%v", v)
+}
+
+func formatPointer(v reflect.Value, conf formatConfig) string {
+	p := v.Pointer()
+	if !conf.realPointers {
+		p = 0 // For deterministic printing purposes
+	}
+	s := formatHex(uint64(p))
+	if conf.printType {
+		return fmt.Sprintf("(%v)(%s)", v.Type(), s)
+	}
+	return s
+}
+
+func formatHex(u uint64) string {
+	var f string
+	switch {
+	case u <= 0xff:
+		f = "0x%02x"
+	case u <= 0xffff:
+		f = "0x%04x"
+	case u <= 0xffffff:
+		f = "0x%06x"
+	case u <= 0xffffffff:
+		f = "0x%08x"
+	case u <= 0xffffffffff:
+		f = "0x%010x"
+	case u <= 0xffffffffffff:
+		f = "0x%012x"
+	case u <= 0xffffffffffffff:
+		f = "0x%014x"
+	case u <= 0xffffffffffffffff:
+		f = "0x%016x"
+	}
+	return fmt.Sprintf(f, u)
+}
+
+// insertPointer insert p into m, allocating m if necessary.
+func insertPointer(m map[uintptr]bool, p uintptr) map[uintptr]bool {
+	if m == nil {
+		m = make(map[uintptr]bool)
+	}
+	m[p] = true
+	return m
+}
+
+// isZero reports whether v is the zero value.
+// This does not rely on Interface and so can be used on unexported fields.
+func isZero(v reflect.Value) bool {
+	switch v.Kind() {
+	case reflect.Bool:
+		return v.Bool() == false
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return v.Int() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return v.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return v.Float() == 0
+	case reflect.Complex64, reflect.Complex128:
+		return v.Complex() == 0
+	case reflect.String:
+		return v.String() == ""
+	case reflect.UnsafePointer:
+		return v.Pointer() == 0
+	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
+		return v.IsNil()
+	case reflect.Array:
+		for i := 0; i < v.Len(); i++ {
+			if !isZero(v.Index(i)) {
+				return false
+			}
+		}
+		return true
+	case reflect.Struct:
+		for i := 0; i < v.NumField(); i++ {
+			if !isZero(v.Field(i)) {
+				return false
+			}
+		}
+		return true
+	}
+	return false
+}
+
+// isExported reports whether the identifier is exported.
+func isExported(id string) bool {
+	r, _ := utf8.DecodeRuneInString(id)
+	return unicode.IsUpper(r)
+}
diff --git a/cmp/internal/value/format_test.go b/cmp/internal/value/format_test.go
new file mode 100644
index 0000000..c44884f
--- /dev/null
+++ b/cmp/internal/value/format_test.go
@@ -0,0 +1,91 @@
+// 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.md file.
+
+package value
+
+import (
+	"bytes"
+	"io"
+	"reflect"
+	"testing"
+)
+
+func TestFormat(t *testing.T) {
+	type key struct {
+		a int
+		b string
+		c chan bool
+	}
+
+	tests := []struct {
+		in   interface{}
+		want string
+	}{{
+		in:   []int{},
+		want: "[]int{}",
+	}, {
+		in:   []int(nil),
+		want: "[]int(nil)",
+	}, {
+		in:   []int{1, 2, 3, 4, 5},
+		want: "[]int{1, 2, 3, 4, 5}",
+	}, {
+		in:   []interface{}{1, true, "hello", struct{ A, B int }{1, 2}},
+		want: "[]interface {}{1, true, \"hello\", struct { A int; B int }{A: 1, B: 2}}",
+	}, {
+		in:   []struct{ A, B int }{{1, 2}, {0, 4}, {}},
+		want: "[]struct { A int; B int }{{A: 1, B: 2}, {B: 4}, {}}",
+	}, {
+		in:   map[*int]string{new(int): "hello"},
+		want: "map[*int]string{0x00: \"hello\"}",
+	}, {
+		in:   map[key]string{{}: "hello"},
+		want: "map[value.key]string{{}: \"hello\"}",
+	}, {
+		in:   map[key]string{{a: 5, b: "key", c: make(chan bool)}: "hello"},
+		want: "map[value.key]string{{a: 5, b: \"key\", c: (chan bool)(0x00)}: \"hello\"}",
+	}, {
+		in:   map[io.Reader]string{new(bytes.Reader): "hello"},
+		want: "map[io.Reader]string{0x00: \"hello\"}",
+	}, {
+		in: func() interface{} {
+			var a = []interface{}{nil}
+			a[0] = a
+			return a
+		}(),
+		want: "[]interface {}{([]interface {})(0x00)}",
+	}, {
+		in: func() interface{} {
+			type A *A
+			var a A
+			a = &a
+			return a
+		}(),
+		want: "&(value.A)(0x00)",
+	}, {
+		in: func() interface{} {
+			type A map[*A]A
+			a := make(A)
+			a[&a] = a
+			return a
+		}(),
+		want: "value.A{0x00: 0x00}",
+	}, {
+		in: func() interface{} {
+			var a [2]interface{}
+			a[0] = &a
+			return a
+		}(),
+		want: "[2]interface {}{&[2]interface {}{(*[2]interface {})(0x00), interface {}(nil)}, interface {}(nil)}",
+	}}
+
+	formatFakePointers = true
+	defer func() { formatFakePointers = false }()
+	for i, tt := range tests {
+		got := Format(reflect.ValueOf(tt.in), true)
+		if got != tt.want {
+			t.Errorf("test %d, pretty print:\ngot  %q\nwant %q", i, got, tt.want)
+		}
+	}
+}
diff --git a/cmp/internal/value/sort.go b/cmp/internal/value/sort.go
new file mode 100644
index 0000000..da00896
--- /dev/null
+++ b/cmp/internal/value/sort.go
@@ -0,0 +1,111 @@
+// 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.md file.
+
+package value
+
+import (
+	"fmt"
+	"math"
+	"reflect"
+	"sort"
+)
+
+// SortKey sorts a list of map keys, deduplicating keys if necessary.
+// The type of each value must be comparable.
+func SortKeys(vs []reflect.Value) []reflect.Value {
+	if len(vs) == 0 {
+		return vs
+	}
+
+	// Sort the map keys.
+	sort.Sort(valueSorter(vs))
+
+	// Deduplicate keys (fails for NaNs).
+	vs2 := vs[:1]
+	for _, v := range vs[1:] {
+		if v.Interface() != vs2[len(vs2)-1].Interface() {
+			vs2 = append(vs2, v)
+		}
+	}
+	return vs2
+}
+
+// TODO: Use sort.Slice once Google AppEngine is on Go1.8 or above.
+type valueSorter []reflect.Value
+
+func (vs valueSorter) Len() int           { return len(vs) }
+func (vs valueSorter) Less(i, j int) bool { return isLess(vs[i], vs[j]) }
+func (vs valueSorter) Swap(i, j int)      { vs[i], vs[j] = vs[j], vs[i] }
+
+// isLess is a generic function for sorting arbitrary map keys.
+// The inputs must be of the same type and must be comparable.
+func isLess(x, y reflect.Value) bool {
+	switch x.Type().Kind() {
+	case reflect.Bool:
+		return !x.Bool() && y.Bool()
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return x.Int() < y.Int()
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return x.Uint() < y.Uint()
+	case reflect.Float32, reflect.Float64:
+		fx, fy := x.Float(), y.Float()
+		return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy)
+	case reflect.Complex64, reflect.Complex128:
+		cx, cy := x.Complex(), y.Complex()
+		rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy)
+		if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) {
+			return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy)
+		}
+		return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry)
+	case reflect.Ptr, reflect.UnsafePointer, reflect.Chan:
+		return x.Pointer() < y.Pointer()
+	case reflect.String:
+		return x.String() < y.String()
+	case reflect.Array:
+		for i := 0; i < x.Len(); i++ {
+			if isLess(x.Index(i), y.Index(i)) {
+				return true
+			}
+			if isLess(y.Index(i), x.Index(i)) {
+				return false
+			}
+		}
+		return false
+	case reflect.Struct:
+		for i := 0; i < x.NumField(); i++ {
+			if isLess(x.Field(i), y.Field(i)) {
+				return true
+			}
+			if isLess(y.Field(i), x.Field(i)) {
+				return false
+			}
+		}
+		return false
+	case reflect.Interface:
+		vx, vy := x.Elem(), y.Elem()
+		if !vx.IsValid() || !vy.IsValid() {
+			return !vx.IsValid() && vy.IsValid()
+		}
+		tx, ty := vx.Type(), vy.Type()
+		if tx == ty {
+			return isLess(x.Elem(), y.Elem())
+		}
+		if tx.Kind() != ty.Kind() {
+			return vx.Kind() < vy.Kind()
+		}
+		if tx.String() != ty.String() {
+			return tx.String() < ty.String()
+		}
+		if tx.PkgPath() != ty.PkgPath() {
+			return tx.PkgPath() < ty.PkgPath()
+		}
+		// This can happen in rare situations, so we fallback to just comparing
+		// the unique pointer for a reflect.Type. This guarantees deterministic
+		// ordering within a program, but it is obviously not stable.
+		return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer()
+	default:
+		// Must be Func, Map, or Slice; which are not comparable.
+		panic(fmt.Sprintf("%T is not comparable", x.Type()))
+	}
+}
diff --git a/cmp/reporter_test.go b/cmp/internal/value/sort_test.go
similarity index 60%
rename from cmp/reporter_test.go
rename to cmp/internal/value/sort_test.go
index 7310077..d3f254b 100644
--- a/cmp/reporter_test.go
+++ b/cmp/internal/value/sort_test.go
@@ -2,93 +2,17 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE.md file.
 
-package cmp
+package value_test
 
 import (
-	"bytes"
-	"io"
 	"math"
 	"reflect"
 	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/internal/value"
 )
 
-func TestFormatAny(t *testing.T) {
-	type key struct {
-		a int
-		b string
-		c chan bool
-	}
-
-	tests := []struct {
-		in   interface{}
-		want string
-	}{{
-		in:   []int{},
-		want: "[]int{}",
-	}, {
-		in:   []int(nil),
-		want: "[]int(nil)",
-	}, {
-		in:   []int{1, 2, 3, 4, 5},
-		want: "[]int{1, 2, 3, 4, 5}",
-	}, {
-		in:   []interface{}{1, true, "hello", struct{ A, B int }{1, 2}},
-		want: "[]interface {}{1, true, \"hello\", struct { A int; B int }{A: 1, B: 2}}",
-	}, {
-		in:   []struct{ A, B int }{{1, 2}, {0, 4}, {}},
-		want: "[]struct { A int; B int }{{A: 1, B: 2}, {B: 4}, {}}",
-	}, {
-		in:   map[*int]string{new(int): "hello"},
-		want: "map[*int]string{0x00: \"hello\"}",
-	}, {
-		in:   map[key]string{{}: "hello"},
-		want: "map[cmp.key]string{{}: \"hello\"}",
-	}, {
-		in:   map[key]string{{a: 5, b: "key", c: make(chan bool)}: "hello"},
-		want: "map[cmp.key]string{{a: 5, b: \"key\", c: (chan bool)(0x00)}: \"hello\"}",
-	}, {
-		in:   map[io.Reader]string{new(bytes.Reader): "hello"},
-		want: "map[io.Reader]string{0x00: \"hello\"}",
-	}, {
-		in: func() interface{} {
-			var a = []interface{}{nil}
-			a[0] = a
-			return a
-		}(),
-		want: "[]interface {}{([]interface {})(0x00)}",
-	}, {
-		in: func() interface{} {
-			type A *A
-			var a A
-			a = &a
-			return a
-		}(),
-		want: "&(cmp.A)(0x00)",
-	}, {
-		in: func() interface{} {
-			type A map[*A]A
-			a := make(A)
-			a[&a] = a
-			return a
-		}(),
-		want: "cmp.A{0x00: 0x00}",
-	}, {
-		in: func() interface{} {
-			var a [2]interface{}
-			a[0] = &a
-			return a
-		}(),
-		want: "[2]interface {}{&[2]interface {}{(*[2]interface {})(0x00), interface {}(nil)}, interface {}(nil)}",
-	}}
-
-	for i, tt := range tests {
-		got := formatAny(reflect.ValueOf(tt.in), formatConfig{true, true, true, false}, nil)
-		if got != tt.want {
-			t.Errorf("test %d, pretty print:\ngot  %q\nwant %q", i, got, tt.want)
-		}
-	}
-}
-
 func TestSortKeys(t *testing.T) {
 	type (
 		MyString string
@@ -101,14 +25,14 @@
 		EmptyStruct struct{}
 	)
 
-	opts := []Option{
-		Comparer(func(x, y float64) bool {
+	opts := []cmp.Option{
+		cmp.Comparer(func(x, y float64) bool {
 			if math.IsNaN(x) && math.IsNaN(y) {
 				return true
 			}
 			return x == y
 		}),
-		Comparer(func(x, y complex128) bool {
+		cmp.Comparer(func(x, y complex128) bool {
 			rx, ix, ry, iy := real(x), imag(x), real(y), imag(y)
 			if math.IsNaN(rx) && math.IsNaN(ry) {
 				rx, ry = 0, 0
@@ -118,11 +42,11 @@
 			}
 			return rx == ry && ix == iy
 		}),
-		Comparer(func(x, y chan bool) bool { return true }),
-		Comparer(func(x, y chan int) bool { return true }),
-		Comparer(func(x, y chan float64) bool { return true }),
-		Comparer(func(x, y chan interface{}) bool { return true }),
-		Comparer(func(x, y *int) bool { return true }),
+		cmp.Comparer(func(x, y chan bool) bool { return true }),
+		cmp.Comparer(func(x, y chan int) bool { return true }),
+		cmp.Comparer(func(x, y chan float64) bool { return true }),
+		cmp.Comparer(func(x, y chan interface{}) bool { return true }),
+		cmp.Comparer(func(x, y *int) bool { return true }),
 	}
 
 	tests := []struct {
@@ -188,7 +112,8 @@
 			[2]int{2, 3}, [2]int{2, 4}, [2]int{4, 0}, MyArray([2]int{2, 4}),
 			make(chan bool), make(chan bool), make(chan int), make(chan interface{}),
 			new(int), new(int),
-			MyString("abc"), MyString("abcd"), MyString("abcde"), "abc", "abcd", "abcde", "bar", "foo",
+			"abc", "abcd", "abcde", "bar", "foo",
+			MyString("abc"), MyString("abcd"), MyString("abcde"),
 			EmptyStruct{},
 			MyStruct{"alpha", [2]int{3, 3}, make(chan float64)},
 			MyStruct{"bravo", [2]int{2, 3}, make(chan float64)},
@@ -217,11 +142,11 @@
 	for i, tt := range tests {
 		keys := append(reflect.ValueOf(tt.in).MapKeys(), reflect.ValueOf(tt.in).MapKeys()...)
 		var got []interface{}
-		for _, k := range sortKeys(keys) {
+		for _, k := range value.SortKeys(keys) {
 			got = append(got, k.Interface())
 		}
-		if !Equal(got, tt.want, opts...) {
-			t.Errorf("test %d, output mismatch:\ngot  %#v\nwant %#v", i, got, tt.want)
+		if d := cmp.Diff(tt.want, got, opts...); d != "" {
+			t.Errorf("test %d, output mismatch (-want +got):\n%s", i, d)
 		}
 	}
 }
diff --git a/cmp/reporter.go b/cmp/reporter.go
index 3f3454b..faebc05 100644
--- a/cmp/reporter.go
+++ b/cmp/reporter.go
@@ -6,14 +6,11 @@
 
 import (
 	"fmt"
-	"math"
 	"reflect"
-	"sort"
 	"strings"
-)
 
-// TODO: Can we leave the interface for a reporter here in the cmp package
-// and somehow extract the implementation of defaultReporter into cmp/report?
+	"github.com/google/go-cmp/cmp/internal/value"
+)
 
 type defaultReporter struct {
 	Option
@@ -34,12 +31,12 @@
 	const maxLines = 256
 	r.ndiffs++
 	if r.nbytes < maxBytes && r.nlines < maxLines {
-		sx := prettyPrint(x, true)
-		sy := prettyPrint(y, true)
+		sx := value.Format(x, true)
+		sy := value.Format(y, true)
 		if sx == sy {
 			// Stringer is not helpful, so rely on more exact formatting.
-			sx = prettyPrint(x, false)
-			sy = prettyPrint(y, false)
+			sx = value.Format(x, false)
+			sy = value.Format(y, false)
 		}
 		s := fmt.Sprintf("%#v:\n\t-: %s\n\t+: %s\n", p, sx, sy)
 		r.diffs = append(r.diffs, s)
@@ -55,327 +52,3 @@
 	}
 	return fmt.Sprintf("%s... %d more differences ...", s, len(r.diffs)-r.ndiffs)
 }
-
-var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
-
-func prettyPrint(v reflect.Value, useStringer bool) string {
-	return formatAny(v, formatConfig{useStringer, true, true, true}, nil)
-}
-
-type formatConfig struct {
-	useStringer    bool // Should the String method be used if available?
-	printType      bool // Should we print the type before the value?
-	followPointers bool // Should we recursively follow pointers?
-	realPointers   bool // Should we print the real address of pointers?
-}
-
-// formatAny prints the value v in a pretty formatted manner.
-// This is similar to fmt.Sprintf("%+v", v) except this:
-//	* Prints the type unless it can be elided.
-//	* Avoids printing struct fields that are zero.
-//	* Prints a nil-slice as being nil, not empty.
-//	* Prints map entries in deterministic order.
-func formatAny(v reflect.Value, conf formatConfig, visited map[uintptr]bool) string {
-	// TODO: Should this be a multi-line printout in certain situations?
-
-	if !v.IsValid() {
-		return "<non-existent>"
-	}
-	if conf.useStringer && v.Type().Implements(stringerIface) {
-		if v.Kind() == reflect.Ptr && v.IsNil() {
-			return "<nil>"
-		}
-		return fmt.Sprintf("%q", v.Interface().(fmt.Stringer).String())
-	}
-
-	switch v.Kind() {
-	case reflect.Bool:
-		return fmt.Sprint(v.Bool())
-	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		return fmt.Sprint(v.Int())
-	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
-		if v.Type().PkgPath() == "" || v.Kind() == reflect.Uintptr {
-			return formatHex(v.Uint()) // Unnamed uints are usually bytes or words
-		}
-		return fmt.Sprint(v.Uint()) // Named uints are usually enumerations
-	case reflect.Float32, reflect.Float64:
-		return fmt.Sprint(v.Float())
-	case reflect.Complex64, reflect.Complex128:
-		return fmt.Sprint(v.Complex())
-	case reflect.String:
-		return fmt.Sprintf("%q", v)
-	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
-		return formatPointer(v, conf)
-	case reflect.Ptr:
-		if v.IsNil() {
-			if conf.printType {
-				return fmt.Sprintf("(%v)(nil)", v.Type())
-			}
-			return "<nil>"
-		}
-		if visited[v.Pointer()] || !conf.followPointers {
-			return formatPointer(v, conf)
-		}
-		visited = insertPointer(visited, v.Pointer())
-		return "&" + formatAny(v.Elem(), conf, visited)
-	case reflect.Interface:
-		if v.IsNil() {
-			if conf.printType {
-				return fmt.Sprintf("%v(nil)", v.Type())
-			}
-			return "<nil>"
-		}
-		return formatAny(v.Elem(), conf, visited)
-	case reflect.Slice:
-		if v.IsNil() {
-			if conf.printType {
-				return fmt.Sprintf("%v(nil)", v.Type())
-			}
-			return "<nil>"
-		}
-		if visited[v.Pointer()] {
-			return formatPointer(v, conf)
-		}
-		visited = insertPointer(visited, v.Pointer())
-		fallthrough
-	case reflect.Array:
-		var ss []string
-		subConf := conf
-		subConf.printType = v.Type().Elem().Kind() == reflect.Interface
-		for i := 0; i < v.Len(); i++ {
-			s := formatAny(v.Index(i), subConf, visited)
-			ss = append(ss, s)
-		}
-		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
-		if conf.printType {
-			return v.Type().String() + s
-		}
-		return s
-	case reflect.Map:
-		if v.IsNil() {
-			if conf.printType {
-				return fmt.Sprintf("%v(nil)", v.Type())
-			}
-			return "<nil>"
-		}
-		if visited[v.Pointer()] {
-			return formatPointer(v, conf)
-		}
-		visited = insertPointer(visited, v.Pointer())
-
-		var ss []string
-		subConf := conf
-		subConf.printType = v.Type().Elem().Kind() == reflect.Interface
-		for _, k := range sortKeys(v.MapKeys()) {
-			sk := formatAny(k, formatConfig{realPointers: conf.realPointers}, visited)
-			sv := formatAny(v.MapIndex(k), subConf, visited)
-			ss = append(ss, fmt.Sprintf("%s: %s", sk, sv))
-		}
-		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
-		if conf.printType {
-			return v.Type().String() + s
-		}
-		return s
-	case reflect.Struct:
-		var ss []string
-		subConf := conf
-		subConf.printType = true
-		for i := 0; i < v.NumField(); i++ {
-			vv := v.Field(i)
-			if isZero(vv) {
-				continue // Elide zero value fields
-			}
-			name := v.Type().Field(i).Name
-			subConf.useStringer = conf.useStringer && isExported(name)
-			s := formatAny(vv, subConf, visited)
-			ss = append(ss, fmt.Sprintf("%s: %s", name, s))
-		}
-		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
-		if conf.printType {
-			return v.Type().String() + s
-		}
-		return s
-	default:
-		panic(fmt.Sprintf("%v kind not handled", v.Kind()))
-	}
-}
-
-func formatPointer(v reflect.Value, conf formatConfig) string {
-	p := v.Pointer()
-	if !conf.realPointers {
-		p = 0 // For deterministic printing purposes
-	}
-	s := formatHex(uint64(p))
-	if conf.printType {
-		return fmt.Sprintf("(%v)(%s)", v.Type(), s)
-	}
-	return s
-}
-
-func formatHex(u uint64) string {
-	var f string
-	switch {
-	case u <= 0xff:
-		f = "0x%02x"
-	case u <= 0xffff:
-		f = "0x%04x"
-	case u <= 0xffffff:
-		f = "0x%06x"
-	case u <= 0xffffffff:
-		f = "0x%08x"
-	case u <= 0xffffffffff:
-		f = "0x%010x"
-	case u <= 0xffffffffffff:
-		f = "0x%012x"
-	case u <= 0xffffffffffffff:
-		f = "0x%014x"
-	case u <= 0xffffffffffffffff:
-		f = "0x%016x"
-	}
-	return fmt.Sprintf(f, u)
-}
-
-// insertPointer insert p into m, allocating m if necessary.
-func insertPointer(m map[uintptr]bool, p uintptr) map[uintptr]bool {
-	if m == nil {
-		m = make(map[uintptr]bool)
-	}
-	m[p] = true
-	return m
-}
-
-// isZero reports whether v is the zero value.
-// This does not rely on Interface and so can be used on unexported fields.
-func isZero(v reflect.Value) bool {
-	switch v.Kind() {
-	case reflect.Bool:
-		return v.Bool() == false
-	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		return v.Int() == 0
-	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
-		return v.Uint() == 0
-	case reflect.Float32, reflect.Float64:
-		return v.Float() == 0
-	case reflect.Complex64, reflect.Complex128:
-		return v.Complex() == 0
-	case reflect.String:
-		return v.String() == ""
-	case reflect.UnsafePointer:
-		return v.Pointer() == 0
-	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
-		return v.IsNil()
-	case reflect.Array:
-		for i := 0; i < v.Len(); i++ {
-			if !isZero(v.Index(i)) {
-				return false
-			}
-		}
-		return true
-	case reflect.Struct:
-		for i := 0; i < v.NumField(); i++ {
-			if !isZero(v.Field(i)) {
-				return false
-			}
-		}
-		return true
-	}
-	return false
-}
-
-// isLess is a generic function for sorting arbitrary map keys.
-// The inputs must be of the same type and must be comparable.
-func isLess(x, y reflect.Value) bool {
-	switch x.Type().Kind() {
-	case reflect.Bool:
-		return !x.Bool() && y.Bool()
-	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		return x.Int() < y.Int()
-	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
-		return x.Uint() < y.Uint()
-	case reflect.Float32, reflect.Float64:
-		fx, fy := x.Float(), y.Float()
-		return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy)
-	case reflect.Complex64, reflect.Complex128:
-		cx, cy := x.Complex(), y.Complex()
-		rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy)
-		if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) {
-			return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy)
-		}
-		return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry)
-	case reflect.Ptr, reflect.UnsafePointer, reflect.Chan:
-		return x.Pointer() < y.Pointer()
-	case reflect.String:
-		return x.String() < y.String()
-	case reflect.Array:
-		for i := 0; i < x.Len(); i++ {
-			if isLess(x.Index(i), y.Index(i)) {
-				return true
-			}
-			if isLess(y.Index(i), x.Index(i)) {
-				return false
-			}
-		}
-		return false
-	case reflect.Struct:
-		for i := 0; i < x.NumField(); i++ {
-			if isLess(x.Field(i), y.Field(i)) {
-				return true
-			}
-			if isLess(y.Field(i), x.Field(i)) {
-				return false
-			}
-		}
-		return false
-	case reflect.Interface:
-		vx, vy := x.Elem(), y.Elem()
-		if !vx.IsValid() || !vy.IsValid() {
-			return !vx.IsValid() && vy.IsValid()
-		}
-		tx, ty := vx.Type(), vy.Type()
-		if tx == ty {
-			return isLess(x.Elem(), y.Elem())
-		}
-		if tx.Kind() != ty.Kind() {
-			return vx.Kind() < vy.Kind()
-		}
-		if tx.String() != ty.String() {
-			return tx.String() < ty.String()
-		}
-		if tx.PkgPath() != ty.PkgPath() {
-			return tx.PkgPath() < ty.PkgPath()
-		}
-		// This can happen in rare situations, so we fallback to just comparing
-		// the unique pointer for a reflect.Type. This guarantees deterministic
-		// ordering within a program, but it is obviously not stable.
-		return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer()
-	default:
-		// Must be Func, Map, or Slice; which are not comparable.
-		panic(fmt.Sprintf("%T is not comparable", x.Type()))
-	}
-}
-
-// sortKey sorts a list of map keys, deduplicating keys if necessary.
-func sortKeys(vs []reflect.Value) []reflect.Value {
-	if len(vs) == 0 {
-		return vs
-	}
-
-	// Sort the map keys.
-	sort.Sort(valueSorter(vs))
-
-	// Deduplicate keys (fails for NaNs).
-	vs2 := vs[:1]
-	for _, v := range vs[1:] {
-		if v.Interface() != vs2[len(vs2)-1].Interface() {
-			vs2 = append(vs2, v)
-		}
-	}
-	return vs2
-}
-
-// TODO: Use sort.Slice once Google AppEngine is on Go1.8 or above.
-type valueSorter []reflect.Value
-
-func (vs valueSorter) Len() int           { return len(vs) }
-func (vs valueSorter) Less(i, j int) bool { return isLess(vs[i], vs[j]) }
-func (vs valueSorter) Swap(i, j int)      { vs[i], vs[j] = vs[j], vs[i] }