Sort numerically-keyed maps by numeric value.

This matches the C++ output and the specification
(https://developers.google.com/protocol-buffers/docs/proto#maps).

Technically we don't have to do this for the wire format,
but it's faster and less code to make them the same.
diff --git a/proto/lib.go b/proto/lib.go
index 0b28b08..95f7975 100644
--- a/proto/lib.go
+++ b/proto/lib.go
@@ -211,6 +211,7 @@
 	"fmt"
 	"log"
 	"reflect"
+	"sort"
 	"strconv"
 	"sync"
 )
@@ -787,12 +788,39 @@
 // If this turns out to be inefficient we can always consider other options,
 // such as doing a Schwartzian transform.
 
-type mapKeys []reflect.Value
+func mapKeys(vs []reflect.Value) sort.Interface {
+	s := mapKeySorter{
+		vs: vs,
+		// default Less function: textual comparison
+		less: func(a, b reflect.Value) bool {
+			return fmt.Sprint(a.Interface()) < fmt.Sprint(b.Interface())
+		},
+	}
 
-func (s mapKeys) Len() int      { return len(s) }
-func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-func (s mapKeys) Less(i, j int) bool {
-	return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface())
+	// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps;
+	// numeric keys are sorted numerically.
+	if len(vs) == 0 {
+		return s
+	}
+	switch vs[0].Kind() {
+	case reflect.Int32, reflect.Int64:
+		s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
+	case reflect.Uint32, reflect.Uint64:
+		s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
+	}
+
+	return s
+}
+
+type mapKeySorter struct {
+	vs   []reflect.Value
+	less func(a, b reflect.Value) bool
+}
+
+func (s mapKeySorter) Len() int      { return len(s.vs) }
+func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
+func (s mapKeySorter) Less(i, j int) bool {
+	return s.less(s.vs[i], s.vs[j])
 }
 
 // isProto3Zero reports whether v is a zero proto3 value.
diff --git a/proto/text.go b/proto/text.go
index f7dc58a..f3db2cf 100644
--- a/proto/text.go
+++ b/proto/text.go
@@ -245,7 +245,7 @@
 		}
 		if fv.Kind() == reflect.Map {
 			// Map fields are rendered as a repeated struct with key/value fields.
-			keys := fv.MapKeys() // TODO: should we sort these for deterministic output?
+			keys := fv.MapKeys()
 			sort.Sort(mapKeys(keys))
 			for _, key := range keys {
 				val := fv.MapIndex(key)
diff --git a/proto/text_test.go b/proto/text_test.go
index 39861d1..64579e9 100644
--- a/proto/text_test.go
+++ b/proto/text_test.go
@@ -421,10 +421,19 @@
 		{&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
 		// empty map
 		{&pb.MessageWithMap{}, ``},
-		// non-empty map; current map format is the same as a repeated struct
+		// non-empty map; map format is the same as a repeated struct,
+		// and they are sorted by key (numerically for numeric keys).
 		{
-			&pb.MessageWithMap{NameMapping: map[int32]string{1234: "Feist"}},
-			`name_mapping:<key:1234 value:"Feist" >`,
+			&pb.MessageWithMap{NameMapping: map[int32]string{
+				-1:      "Negatory",
+				7:       "Lucky",
+				1234:    "Feist",
+				6345789: "Otis",
+			}},
+			`name_mapping:<key:-1 value:"Negatory" > ` +
+				`name_mapping:<key:7 value:"Lucky" > ` +
+				`name_mapping:<key:1234 value:"Feist" > ` +
+				`name_mapping:<key:6345789 value:"Otis" >`,
 		},
 		// map with nil value; not well-defined, but we shouldn't crash
 		{