git subrepo clone (merge) https://github.com/kubernetes-incubator/metrics-server.git metrics-server

subrepo:
  subdir:   "metrics-server"
  merged:   "92d8412"
upstream:
  origin:   "https://github.com/kubernetes-incubator/metrics-server.git"
  branch:   "master"
  commit:   "92d8412"
git-subrepo:
  version:  "0.4.0"
  origin:   "???"
  commit:   "???"
diff --git a/metrics-server/vendor/k8s.io/gengo/namer/doc.go b/metrics-server/vendor/k8s.io/gengo/namer/doc.go
new file mode 100644
index 0000000..8a44ea9
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/gengo/namer/doc.go
@@ -0,0 +1,31 @@
+/*
+Copyright 2015 The Kubernetes 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 namer has support for making different type naming systems.
+//
+// This is because sometimes you want to refer to the literal type, sometimes
+// you want to make a name for the thing you're generating, and you want to
+// make the name based on the type. For example, if you have `type foo string`,
+// you want to be able to generate something like `func FooPrinter(f *foo) {
+// Print(string(*f)) }`; that is, you want to refer to a public name, a literal
+// name, and the underlying literal name.
+//
+// This package supports the idea of a "Namer" and a set of "NameSystems" to
+// support these use cases.
+//
+// Additionally, a "RawNamer" can optionally keep track of what needs to be
+// imported.
+package namer // import "k8s.io/gengo/namer"
diff --git a/metrics-server/vendor/k8s.io/gengo/namer/import_tracker.go b/metrics-server/vendor/k8s.io/gengo/namer/import_tracker.go
new file mode 100644
index 0000000..37094b2
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/gengo/namer/import_tracker.go
@@ -0,0 +1,112 @@
+/*
+Copyright 2015 The Kubernetes 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 namer
+
+import (
+	"sort"
+
+	"k8s.io/gengo/types"
+)
+
+// ImportTracker may be passed to a namer.RawNamer, to track the imports needed
+// for the types it names.
+//
+// TODO: pay attention to the package name (instead of renaming every package).
+type DefaultImportTracker struct {
+	pathToName map[string]string
+	// forbidden names are in here. (e.g. "go" is a directory in which
+	// there is code, but "go" is not a legal name for a package, so we put
+	// it here to prevent us from naming any package "go")
+	nameToPath map[string]string
+	local      types.Name
+
+	// Returns true if a given types is an invalid type and should be ignored.
+	IsInvalidType func(*types.Type) bool
+	// Returns the final local name for the given name
+	LocalName func(types.Name) string
+	// Returns the "import" line for a given (path, name).
+	PrintImport func(string, string) string
+}
+
+func NewDefaultImportTracker(local types.Name) DefaultImportTracker {
+	return DefaultImportTracker{
+		pathToName: map[string]string{},
+		nameToPath: map[string]string{},
+		local:      local,
+	}
+}
+
+func (tracker *DefaultImportTracker) AddTypes(types ...*types.Type) {
+	for _, t := range types {
+		tracker.AddType(t)
+	}
+}
+func (tracker *DefaultImportTracker) AddType(t *types.Type) {
+	if tracker.local.Package == t.Name.Package {
+		return
+	}
+
+	if tracker.IsInvalidType(t) {
+		if t.Kind == types.Builtin {
+			return
+		}
+		if _, ok := tracker.nameToPath[t.Name.Package]; !ok {
+			tracker.nameToPath[t.Name.Package] = ""
+		}
+		return
+	}
+
+	if len(t.Name.Package) == 0 {
+		return
+	}
+	path := t.Name.Path
+	if len(path) == 0 {
+		path = t.Name.Package
+	}
+	if _, ok := tracker.pathToName[path]; ok {
+		return
+	}
+	name := tracker.LocalName(t.Name)
+	tracker.nameToPath[name] = path
+	tracker.pathToName[path] = name
+}
+
+func (tracker *DefaultImportTracker) ImportLines() []string {
+	importPaths := []string{}
+	for path := range tracker.pathToName {
+		importPaths = append(importPaths, path)
+	}
+	sort.Sort(sort.StringSlice(importPaths))
+	out := []string{}
+	for _, path := range importPaths {
+		out = append(out, tracker.PrintImport(path, tracker.pathToName[path]))
+	}
+	return out
+}
+
+// LocalNameOf returns the name you would use to refer to the package at the
+// specified path within the body of a file.
+func (tracker *DefaultImportTracker) LocalNameOf(path string) string {
+	return tracker.pathToName[path]
+}
+
+// PathOf returns the path that a given localName is referring to within the
+// body of a file.
+func (tracker *DefaultImportTracker) PathOf(localName string) (string, bool) {
+	name, ok := tracker.nameToPath[localName]
+	return name, ok
+}
diff --git a/metrics-server/vendor/k8s.io/gengo/namer/namer.go b/metrics-server/vendor/k8s.io/gengo/namer/namer.go
new file mode 100644
index 0000000..d700a00
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/gengo/namer/namer.go
@@ -0,0 +1,383 @@
+/*
+Copyright 2015 The Kubernetes 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 namer
+
+import (
+	"path/filepath"
+	"strings"
+
+	"k8s.io/gengo/types"
+)
+
+const (
+	// GoSeperator is used to split go import paths.
+	// Forward slash is used instead of filepath.Seperator because it is the
+	// only universally-accepted path delimiter and the only delimiter not
+	// potentially forbidden by Go compilers. (In particular gc does not allow
+	// the use of backslashes in import paths.)
+	// See https://golang.org/ref/spec#Import_declarations.
+	// See also https://github.com/kubernetes/gengo/issues/83#issuecomment-367040772.
+	GoSeperator = "/"
+)
+
+// Returns whether a name is a private Go name.
+func IsPrivateGoName(name string) bool {
+	return len(name) == 0 || strings.ToLower(name[:1]) == name[:1]
+}
+
+// NewPublicNamer is a helper function that returns a namer that makes
+// CamelCase names. See the NameStrategy struct for an explanation of the
+// arguments to this constructor.
+func NewPublicNamer(prependPackageNames int, ignoreWords ...string) *NameStrategy {
+	n := &NameStrategy{
+		Join:                Joiner(IC, IC),
+		IgnoreWords:         map[string]bool{},
+		PrependPackageNames: prependPackageNames,
+	}
+	for _, w := range ignoreWords {
+		n.IgnoreWords[w] = true
+	}
+	return n
+}
+
+// NewPrivateNamer is a helper function that returns a namer that makes
+// camelCase names. See the NameStrategy struct for an explanation of the
+// arguments to this constructor.
+func NewPrivateNamer(prependPackageNames int, ignoreWords ...string) *NameStrategy {
+	n := &NameStrategy{
+		Join:                Joiner(IL, IC),
+		IgnoreWords:         map[string]bool{},
+		PrependPackageNames: prependPackageNames,
+	}
+	for _, w := range ignoreWords {
+		n.IgnoreWords[w] = true
+	}
+	return n
+}
+
+// NewRawNamer will return a Namer that makes a name by which you would
+// directly refer to a type, optionally keeping track of the import paths
+// necessary to reference the names it provides. Tracker may be nil.
+// The 'pkg' is the full package name, in which the Namer is used - all
+// types from that package will be referenced by just type name without
+// referencing the package.
+//
+// For example, if the type is map[string]int, a raw namer will literally
+// return "map[string]int".
+//
+// Or if the type, in package foo, is "type Bar struct { ... }", then the raw
+// namer will return "foo.Bar" as the name of the type, and if 'tracker' was
+// not nil, will record that package foo needs to be imported.
+func NewRawNamer(pkg string, tracker ImportTracker) *rawNamer {
+	return &rawNamer{pkg: pkg, tracker: tracker}
+}
+
+// Names is a map from Type to name, as defined by some Namer.
+type Names map[*types.Type]string
+
+// Namer takes a type, and assigns a name.
+//
+// The purpose of this complexity is so that you can assign coherent
+// side-by-side systems of names for the types. For example, you might want a
+// public interface, a private implementation struct, and also to reference
+// literally the type name.
+//
+// Note that it is safe to call your own Name() function recursively to find
+// the names of keys, elements, etc. This is because anonymous types can't have
+// cycles in their names, and named types don't require the sort of recursion
+// that would be problematic.
+type Namer interface {
+	Name(*types.Type) string
+}
+
+// NameSystems is a map of a system name to a namer for that system.
+type NameSystems map[string]Namer
+
+// NameStrategy is a general Namer. The easiest way to use it is to copy the
+// Public/PrivateNamer variables, and modify the members you wish to change.
+//
+// The Name method produces a name for the given type, of the forms:
+// Anonymous types: <Prefix><Type description><Suffix>
+// Named types: <Prefix><Optional Prepended Package name(s)><Original name><Suffix>
+//
+// In all cases, every part of the name is run through the capitalization
+// functions.
+//
+// The IgnoreWords map can be set if you have directory names that are
+// semantically meaningless for naming purposes, e.g. "proto".
+//
+// Prefix and Suffix can be used to disambiguate parallel systems of type
+// names. For example, if you want to generate an interface and an
+// implementation, you might want to suffix one with "Interface" and the other
+// with "Implementation". Another common use-- if you want to generate private
+// types, and one of your source types could be "string", you can't use the
+// default lowercase private namer. You'll have to add a suffix or prefix.
+type NameStrategy struct {
+	Prefix, Suffix string
+	Join           func(pre string, parts []string, post string) string
+
+	// Add non-meaningful package directory names here (e.g. "proto") and
+	// they will be ignored.
+	IgnoreWords map[string]bool
+
+	// If > 0, prepend exactly that many package directory names (or as
+	// many as there are).  Package names listed in "IgnoreWords" will be
+	// ignored.
+	//
+	// For example, if Ignore words lists "proto" and type Foo is in
+	// pkg/server/frobbing/proto, then a value of 1 will give a type name
+	// of FrobbingFoo, 2 gives ServerFrobbingFoo, etc.
+	PrependPackageNames int
+
+	// A cache of names thus far assigned by this namer.
+	Names
+}
+
+// IC ensures the first character is uppercase.
+func IC(in string) string {
+	if in == "" {
+		return in
+	}
+	return strings.ToUpper(in[:1]) + in[1:]
+}
+
+// IL ensures the first character is lowercase.
+func IL(in string) string {
+	if in == "" {
+		return in
+	}
+	return strings.ToLower(in[:1]) + in[1:]
+}
+
+// Joiner lets you specify functions that preprocess the various components of
+// a name before joining them. You can construct e.g. camelCase or CamelCase or
+// any other way of joining words. (See the IC and IL convenience functions.)
+func Joiner(first, others func(string) string) func(pre string, in []string, post string) string {
+	return func(pre string, in []string, post string) string {
+		tmp := []string{others(pre)}
+		for i := range in {
+			tmp = append(tmp, others(in[i]))
+		}
+		tmp = append(tmp, others(post))
+		return first(strings.Join(tmp, ""))
+	}
+}
+
+func (ns *NameStrategy) removePrefixAndSuffix(s string) string {
+	// The join function may have changed capitalization.
+	lowerIn := strings.ToLower(s)
+	lowerP := strings.ToLower(ns.Prefix)
+	lowerS := strings.ToLower(ns.Suffix)
+	b, e := 0, len(s)
+	if strings.HasPrefix(lowerIn, lowerP) {
+		b = len(ns.Prefix)
+	}
+	if strings.HasSuffix(lowerIn, lowerS) {
+		e -= len(ns.Suffix)
+	}
+	return s[b:e]
+}
+
+var (
+	importPathNameSanitizer = strings.NewReplacer("-", "_", ".", "")
+)
+
+// filters out unwanted directory names and sanitizes remaining names.
+func (ns *NameStrategy) filterDirs(path string) []string {
+	allDirs := strings.Split(path, GoSeperator)
+	dirs := make([]string, 0, len(allDirs))
+	for _, p := range allDirs {
+		if ns.IgnoreWords == nil || !ns.IgnoreWords[p] {
+			dirs = append(dirs, importPathNameSanitizer.Replace(p))
+		}
+	}
+	return dirs
+}
+
+// See the comment on NameStrategy.
+func (ns *NameStrategy) Name(t *types.Type) string {
+	if ns.Names == nil {
+		ns.Names = Names{}
+	}
+	if s, ok := ns.Names[t]; ok {
+		return s
+	}
+
+	if t.Name.Package != "" {
+		dirs := append(ns.filterDirs(t.Name.Package), t.Name.Name)
+		i := ns.PrependPackageNames + 1
+		dn := len(dirs)
+		if i > dn {
+			i = dn
+		}
+		name := ns.Join(ns.Prefix, dirs[dn-i:], ns.Suffix)
+		ns.Names[t] = name
+		return name
+	}
+
+	// Only anonymous types remain.
+	var name string
+	switch t.Kind {
+	case types.Builtin:
+		name = ns.Join(ns.Prefix, []string{t.Name.Name}, ns.Suffix)
+	case types.Map:
+		name = ns.Join(ns.Prefix, []string{
+			"Map",
+			ns.removePrefixAndSuffix(ns.Name(t.Key)),
+			"To",
+			ns.removePrefixAndSuffix(ns.Name(t.Elem)),
+		}, ns.Suffix)
+	case types.Slice:
+		name = ns.Join(ns.Prefix, []string{
+			"Slice",
+			ns.removePrefixAndSuffix(ns.Name(t.Elem)),
+		}, ns.Suffix)
+	case types.Pointer:
+		name = ns.Join(ns.Prefix, []string{
+			"Pointer",
+			ns.removePrefixAndSuffix(ns.Name(t.Elem)),
+		}, ns.Suffix)
+	case types.Struct:
+		names := []string{"Struct"}
+		for _, m := range t.Members {
+			names = append(names, ns.removePrefixAndSuffix(ns.Name(m.Type)))
+		}
+		name = ns.Join(ns.Prefix, names, ns.Suffix)
+	case types.Chan:
+		name = ns.Join(ns.Prefix, []string{
+			"Chan",
+			ns.removePrefixAndSuffix(ns.Name(t.Elem)),
+		}, ns.Suffix)
+	case types.Interface:
+		// TODO: add to name test
+		names := []string{"Interface"}
+		for _, m := range t.Methods {
+			// TODO: include function signature
+			names = append(names, m.Name.Name)
+		}
+		name = ns.Join(ns.Prefix, names, ns.Suffix)
+	case types.Func:
+		// TODO: add to name test
+		parts := []string{"Func"}
+		for _, pt := range t.Signature.Parameters {
+			parts = append(parts, ns.removePrefixAndSuffix(ns.Name(pt)))
+		}
+		parts = append(parts, "Returns")
+		for _, rt := range t.Signature.Results {
+			parts = append(parts, ns.removePrefixAndSuffix(ns.Name(rt)))
+		}
+		name = ns.Join(ns.Prefix, parts, ns.Suffix)
+	default:
+		name = "unnameable_" + string(t.Kind)
+	}
+	ns.Names[t] = name
+	return name
+}
+
+// ImportTracker allows a raw namer to keep track of the packages needed for
+// import. You can implement yourself or use the one in the generation package.
+type ImportTracker interface {
+	AddType(*types.Type)
+	LocalNameOf(packagePath string) string
+	PathOf(localName string) (string, bool)
+	ImportLines() []string
+}
+
+type rawNamer struct {
+	pkg     string
+	tracker ImportTracker
+	Names
+}
+
+// Name makes a name the way you'd write it to literally refer to type t,
+// making ordinary assumptions about how you've imported t's package (or using
+// r.tracker to specifically track the package imports).
+func (r *rawNamer) Name(t *types.Type) string {
+	if r.Names == nil {
+		r.Names = Names{}
+	}
+	if name, ok := r.Names[t]; ok {
+		return name
+	}
+	if t.Name.Package != "" {
+		var name string
+		if r.tracker != nil {
+			r.tracker.AddType(t)
+			if t.Name.Package == r.pkg {
+				name = t.Name.Name
+			} else {
+				name = r.tracker.LocalNameOf(t.Name.Package) + "." + t.Name.Name
+			}
+		} else {
+			if t.Name.Package == r.pkg {
+				name = t.Name.Name
+			} else {
+				name = filepath.Base(t.Name.Package) + "." + t.Name.Name
+			}
+		}
+		r.Names[t] = name
+		return name
+	}
+	var name string
+	switch t.Kind {
+	case types.Builtin:
+		name = t.Name.Name
+	case types.Map:
+		name = "map[" + r.Name(t.Key) + "]" + r.Name(t.Elem)
+	case types.Slice:
+		name = "[]" + r.Name(t.Elem)
+	case types.Pointer:
+		name = "*" + r.Name(t.Elem)
+	case types.Struct:
+		elems := []string{}
+		for _, m := range t.Members {
+			elems = append(elems, m.Name+" "+r.Name(m.Type))
+		}
+		name = "struct{" + strings.Join(elems, "; ") + "}"
+	case types.Chan:
+		// TODO: include directionality
+		name = "chan " + r.Name(t.Elem)
+	case types.Interface:
+		// TODO: add to name test
+		elems := []string{}
+		for _, m := range t.Methods {
+			// TODO: include function signature
+			elems = append(elems, m.Name.Name)
+		}
+		name = "interface{" + strings.Join(elems, "; ") + "}"
+	case types.Func:
+		// TODO: add to name test
+		params := []string{}
+		for _, pt := range t.Signature.Parameters {
+			params = append(params, r.Name(pt))
+		}
+		results := []string{}
+		for _, rt := range t.Signature.Results {
+			results = append(results, r.Name(rt))
+		}
+		name = "func(" + strings.Join(params, ",") + ")"
+		if len(results) == 1 {
+			name += " " + results[0]
+		} else if len(results) > 1 {
+			name += " (" + strings.Join(results, ",") + ")"
+		}
+	default:
+		name = "unnameable_" + string(t.Kind)
+	}
+	r.Names[t] = name
+	return name
+}
diff --git a/metrics-server/vendor/k8s.io/gengo/namer/order.go b/metrics-server/vendor/k8s.io/gengo/namer/order.go
new file mode 100644
index 0000000..f86282b
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/gengo/namer/order.go
@@ -0,0 +1,69 @@
+/*
+Copyright 2015 The Kubernetes 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 namer
+
+import (
+	"sort"
+
+	"k8s.io/gengo/types"
+)
+
+// Orderer produces an ordering of types given a Namer.
+type Orderer struct {
+	Namer
+}
+
+// OrderUniverse assigns a name to every type in the Universe, including Types,
+// Functions and Variables, and returns a list sorted by those names.
+func (o *Orderer) OrderUniverse(u types.Universe) []*types.Type {
+	list := tList{
+		namer: o.Namer,
+	}
+	for _, p := range u {
+		for _, t := range p.Types {
+			list.types = append(list.types, t)
+		}
+		for _, f := range p.Functions {
+			list.types = append(list.types, f)
+		}
+		for _, v := range p.Variables {
+			list.types = append(list.types, v)
+		}
+	}
+	sort.Sort(list)
+	return list.types
+}
+
+// OrderTypes assigns a name to every type, and returns a list sorted by those
+// names.
+func (o *Orderer) OrderTypes(typeList []*types.Type) []*types.Type {
+	list := tList{
+		namer: o.Namer,
+		types: typeList,
+	}
+	sort.Sort(list)
+	return list.types
+}
+
+type tList struct {
+	namer Namer
+	types []*types.Type
+}
+
+func (t tList) Len() int           { return len(t.types) }
+func (t tList) Less(i, j int) bool { return t.namer.Name(t.types[i]) < t.namer.Name(t.types[j]) }
+func (t tList) Swap(i, j int)      { t.types[i], t.types[j] = t.types[j], t.types[i] }
diff --git a/metrics-server/vendor/k8s.io/gengo/namer/plural_namer.go b/metrics-server/vendor/k8s.io/gengo/namer/plural_namer.go
new file mode 100644
index 0000000..40bdcc6
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/gengo/namer/plural_namer.go
@@ -0,0 +1,120 @@
+/*
+Copyright 2015 The Kubernetes 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 namer
+
+import (
+	"strings"
+
+	"k8s.io/gengo/types"
+)
+
+var consonants = "bcdfghjklmnpqrsttvwxyz"
+
+type pluralNamer struct {
+	// key is the case-sensitive type name, value is the case-insensitive
+	// intended output.
+	exceptions map[string]string
+	finalize   func(string) string
+}
+
+// NewPublicPluralNamer returns a namer that returns the plural form of the input
+// type's name, starting with a uppercase letter.
+func NewPublicPluralNamer(exceptions map[string]string) *pluralNamer {
+	return &pluralNamer{exceptions, IC}
+}
+
+// NewPrivatePluralNamer returns a namer that returns the plural form of the input
+// type's name, starting with a lowercase letter.
+func NewPrivatePluralNamer(exceptions map[string]string) *pluralNamer {
+	return &pluralNamer{exceptions, IL}
+}
+
+// NewAllLowercasePluralNamer returns a namer that returns the plural form of the input
+// type's name, with all letters in lowercase.
+func NewAllLowercasePluralNamer(exceptions map[string]string) *pluralNamer {
+	return &pluralNamer{exceptions, strings.ToLower}
+}
+
+// Name returns the plural form of the type's name. If the type's name is found
+// in the exceptions map, the map value is returned.
+func (r *pluralNamer) Name(t *types.Type) string {
+	singular := t.Name.Name
+	var plural string
+	var ok bool
+	if plural, ok = r.exceptions[singular]; ok {
+		return r.finalize(plural)
+	}
+	if len(singular) < 2 {
+		return r.finalize(plural)
+	}
+
+	switch rune(singular[len(singular)-1]) {
+	case 's', 'x', 'z':
+		plural = esPlural(singular)
+	case 'y':
+		sl := rune(singular[len(singular)-2])
+		if isConsonant(sl) {
+			plural = iesPlural(singular)
+		} else {
+			plural = sPlural(singular)
+		}
+	case 'h':
+		sl := rune(singular[len(singular)-2])
+		if sl == 'c' || sl == 's' {
+			plural = esPlural(singular)
+		} else {
+			plural = sPlural(singular)
+		}
+	case 'e':
+		sl := rune(singular[len(singular)-2])
+		if sl == 'f' {
+			plural = vesPlural(singular[:len(singular)-1])
+		} else {
+			plural = sPlural(singular)
+		}
+	case 'f':
+			plural = vesPlural(singular)
+	default:
+		plural = sPlural(singular)
+	}
+	return r.finalize(plural)
+}
+
+func iesPlural(singular string) string {
+	return singular[:len(singular)-1] + "ies"
+}
+
+func vesPlural(singular string) string {
+	return singular[:len(singular)-1] + "ves"
+}
+
+func esPlural(singular string) string {
+	return singular + "es"
+}
+
+func sPlural(singular string) string {
+	return singular + "s"
+}
+
+func isConsonant(char rune) bool {
+	for _, c := range consonants {
+		if char == c {
+			return true
+		}
+	}
+	return false
+}