Repository: appc/spec
Pull request #684 (2017-01-26 00:11:15)

remove k8s.io/kubernetes dependency
This commit removes k8s.io/kubernetes as a dependency. The one part of
the repo that we were using, k8s.io/kubernetes/pkg/api/resource, has
been copied to schema/types/resource, and very minimally modified to
work in its new location. A README.md has been added to this new
location describing where the files came from.

This was necessary to avoid a cyclic dependency with the kubernetes
repo.

Fixes https://github.com/appc/spec/issues/683.

dgonyeo: One thing about this I was uncertain about was that I left all of the copyright attributions at the top of the copied in files as `Copyright 2014 The Kubernetes Authors`, should this be changed?
s-urbaniak: woah, thanks!

I guess we can cut a bit more, namely the whole protobuf dependency.

Looking at what this type implements now it seems a bit too much:
```
 struct type Quantity
    implements encoding/json.Marshaler
 pointer type *Quantity
    implements encoding/json.Unmarshaler
    implements fmt.Stringer
    implements github.com/appc/docker2aci/vendor/github.com/gogo/protobuf/proto.Marshaler
    implements github.com/appc/docker2aci/vendor/github.com/gogo/protobuf/proto.Message
    implements github.com/appc/docker2aci/vendor/github.com/gogo/protobuf/proto.Sizer
    implements github.com/appc/docker2aci/vendor/github.com/gogo/protobuf/proto.Unmarshaler
    implements github.com/appc/spec/vendor/github.com/gogo/protobuf/proto.Marshaler
    implements github.com/appc/spec/vendor/github.com/gogo/protobuf/proto.Message
    implements github.com/appc/spec/vendor/github.com/gogo/protobuf/proto.Sizer
    implements github.com/appc/spec/vendor/github.com/gogo/protobuf/proto.Unmarshaler
    implements github.com/golang/protobuf/proto.Marshaler
    implements github.com/golang/protobuf/proto.Message
    implements github.com/golang/protobuf/proto.Unmarshaler
    implements k8s.io/kubernetes/vendor/github.com/gogo/protobuf/proto.Marshaler
    implements k8s.io/kubernetes/vendor/github.com/gogo/protobuf/proto.Message
    implements k8s.io/kubernetes/vendor/github.com/gogo/protobuf/proto.Sizer
    implements k8s.io/kubernetes/vendor/github.com/gogo/protobuf/proto.Unmarshaler
    implements runtime.stringer
```

Some of the above are redundant, reducing to:
```
    implements k8s.io/kubernetes/vendor/github.com/gogo/protobuf/proto.Marshaler
    implements k8s.io/kubernetes/vendor/github.com/gogo/protobuf/proto.Message
    implements k8s.io/kubernetes/vendor/github.com/gogo/protobuf/proto.Sizer
    implements k8s.io/kubernetes/vendor/github.com/gogo/protobuf/proto.Unmarshaler
```

which all reside in `quantity_proto.go`. I suggest we delete this file, and thus also get rid of the protobuf dependency. Also I suggest we delete the generated protobuf. Summarizing I would also get rid of those files:

- `schema/types/resource/generated.pb.go`
- `schema/types/resource/generated.proto`
- `schema/types/resource/quantity_proto.go`

This Quantity type is quite involved, using fixed point arithmetic, so I would leave all those tests.

Patch:
diff --git a/glide.lock b/glide.lock
index c17a44f..9d96571 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,5 +1,5 @@
-hash: c8c998224f4cc27a742806398b8e81dec16a89ce83a8ec68a2e6e8e4692e8fe7
-updated: 2016-10-26T02:06:24.373221607-07:00
+hash: 8b25ad4ed6df28ab1f97f21f0744ff4fd42cba97cecad3443b4f1d039ca4f6cf
+updated: 2017-01-25T15:30:44.303100067-08:00
 imports:
 - name: github.com/coreos/go-semver
   version: 294930c1e79c64e7dbe360054274fdad492c8cf5
@@ -22,8 +22,4 @@ imports:
   - html/atom
 - name: gopkg.in/inf.v0
   version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
-- name: k8s.io/kubernetes
-  version: 1bed5bb1a72be482ea507da3057b689d5a3034f1
-  subpackages:
-  - pkg/api/resource
 testImports: []
diff --git a/glide.yaml b/glide.yaml
index 84a6507..04c3859 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -1,4 +1,4 @@
-package: "github.com/appc/spec"
+package: github.com/appc/spec
 import:
 - package: github.com/coreos/go-semver
   version: ~0.1.0
@@ -21,7 +21,3 @@ import:
   - html/atom
 - package: gopkg.in/inf.v0
   version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
-- package: k8s.io/kubernetes
-  version: ~1.4.0
-  subpackages:
-  - pkg/api/resource
diff --git a/schema/types/isolator_resources.go b/schema/types/isolator_resources.go
index 771c73f..2ec250d 100644
--- a/schema/types/isolator_resources.go
+++ b/schema/types/isolator_resources.go
@@ -19,7 +19,7 @@ import (
 	"errors"
 	"fmt"
 
-	"k8s.io/kubernetes/pkg/api/resource"
+	"github.com/appc/spec/schema/types/resource"
 )
 
 var (
diff --git a/schema/types/resource/README.md b/schema/types/resource/README.md
new file mode 100644
index 0000000..b3c16d2
--- /dev/null
+++ b/schema/types/resource/README.md
@@ -0,0 +1,4 @@
+This package was copied in from the Kubernetes repo to avoid a cyclic
+dependency. These files were taken from master from
+github.com/kubernetes/kubernetes at commit hash
+b0deb2eb8f4037421077f77cb163dbb4c0a2a9f5.
diff --git a/vendor/k8s.io/kubernetes/pkg/api/resource/amount.go b/schema/types/resource/amount.go
similarity index 99%
rename from vendor/k8s.io/kubernetes/pkg/api/resource/amount.go
rename to schema/types/resource/amount.go
index 2d3012c..6ae823a 100644
--- a/vendor/k8s.io/kubernetes/pkg/api/resource/amount.go
+++ b/schema/types/resource/amount.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2014 The Kubernetes Authors.
+Copyright 2014 The Kubernetes Authors All rights reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/vendor/k8s.io/kubernetes/pkg/api/resource/math.go b/schema/types/resource/math.go
similarity index 99%
rename from vendor/k8s.io/kubernetes/pkg/api/resource/math.go
rename to schema/types/resource/math.go
index 887ac74..163aafa 100644
--- a/vendor/k8s.io/kubernetes/pkg/api/resource/math.go
+++ b/schema/types/resource/math.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2014 The Kubernetes Authors.
+Copyright 2014 The Kubernetes Authors All rights reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/vendor/k8s.io/kubernetes/pkg/api/resource/quantity.go b/schema/types/resource/quantity.go
similarity index 98%
rename from vendor/k8s.io/kubernetes/pkg/api/resource/quantity.go
rename to schema/types/resource/quantity.go
index 823dd5e..96877b4 100644
--- a/vendor/k8s.io/kubernetes/pkg/api/resource/quantity.go
+++ b/schema/types/resource/quantity.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2014 The Kubernetes Authors.
+Copyright 2014 The Kubernetes Authors All rights reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -87,6 +87,7 @@ import (
 // writing some sort of special handling code in the hopes that that will
 // cause implementors to also use a fixed point implementation.
 //
+// +gencopy=false
 // +protobuf=true
 // +protobuf.embed=string
 // +protobuf.options.marshal=false
@@ -385,16 +386,6 @@ func ParseQuantity(str string) (Quantity, error) {
 	return Quantity{d: infDecAmount{amount}, Format: format}, nil
 }
 
-// DeepCopy returns a deep-copy of the Quantity value.  Note that the method
-// receiver is a value, so we can mutate it in-place and return it.
-func (q Quantity) DeepCopy() Quantity {
-	if q.d.Dec != nil {
-		tmp := &inf.Dec{}
-		q.d.Dec = tmp.Set(q.d.Dec)
-	}
-	return q
-}
-
 // CanonicalizeBytes returns the canonical form of q and its suffix (see comment on Quantity).
 //
 // Note about BinarySI:
diff --git a/vendor/k8s.io/kubernetes/pkg/api/resource/scale_int.go b/schema/types/resource/scale_int.go
similarity index 97%
rename from vendor/k8s.io/kubernetes/pkg/api/resource/scale_int.go
rename to schema/types/resource/scale_int.go
index 55e177b..173de1a 100644
--- a/vendor/k8s.io/kubernetes/pkg/api/resource/scale_int.go
+++ b/schema/types/resource/scale_int.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2015 The Kubernetes Authors.
+Copyright 2015 The Kubernetes Authors All rights reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/vendor/k8s.io/kubernetes/pkg/api/resource/suffix.go b/schema/types/resource/suffix.go
similarity index 98%
rename from vendor/k8s.io/kubernetes/pkg/api/resource/suffix.go
rename to schema/types/resource/suffix.go
index 5ed7abe..0aa2ce2 100644
--- a/vendor/k8s.io/kubernetes/pkg/api/resource/suffix.go
+++ b/schema/types/resource/suffix.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2014 The Kubernetes Authors.
+Copyright 2014 The Kubernetes Authors All rights reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/vendor/k8s.io/kubernetes/LICENSE b/vendor/k8s.io/kubernetes/LICENSE
deleted file mode 100644
index 00b2401..0000000
--- a/vendor/k8s.io/kubernetes/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright 2014 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.
diff --git a/vendor/k8s.io/kubernetes/pkg/api/resource/generated.pb.go b/vendor/k8s.io/kubernetes/pkg/api/resource/generated.pb.go
deleted file mode 100644
index 3bde152..0000000
--- a/vendor/k8s.io/kubernetes/pkg/api/resource/generated.pb.go
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-Copyright 2016 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.
-*/
-
-// Code generated by protoc-gen-gogo.
-// source: k8s.io/kubernetes/pkg/api/resource/generated.proto
-// DO NOT EDIT!
-
-/*
-	Package resource is a generated protocol buffer package.
-
-	It is generated from these files:
-		k8s.io/kubernetes/pkg/api/resource/generated.proto
-
-	It has these top-level messages:
-		Quantity
-*/
-package resource
-
-import proto "github.com/gogo/protobuf/proto"
-import fmt "fmt"
-import math "math"
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-const _ = proto.GoGoProtoPackageIsVersion1
-
-func (m *Quantity) Reset()                    { *m = Quantity{} }
-func (*Quantity) ProtoMessage()               {}
-func (*Quantity) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} }
-
-func init() {
-	proto.RegisterType((*Quantity)(nil), "k8s.io.kubernetes.pkg.api.resource.Quantity")
-}
-
-var fileDescriptorGenerated = []byte{
-	// 222 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x32, 0xca, 0xb6, 0x28, 0xd6,
-	0xcb, 0xcc, 0xd7, 0xcf, 0x2e, 0x4d, 0x4a, 0x2d, 0xca, 0x4b, 0x2d, 0x49, 0x2d, 0xd6, 0x2f, 0xc8,
-	0x4e, 0xd7, 0x4f, 0x2c, 0xc8, 0xd4, 0x2f, 0x4a, 0x2d, 0xce, 0x2f, 0x2d, 0x4a, 0x4e, 0xd5, 0x4f,
-	0x4f, 0xcd, 0x4b, 0x2d, 0x4a, 0x2c, 0x49, 0x4d, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x52,
-	0x82, 0xe8, 0xd1, 0x43, 0xe8, 0xd1, 0x03, 0xea, 0xd1, 0x03, 0xea, 0xd1, 0x83, 0xe9, 0x91, 0xd2,
-	0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0x4f, 0xcf, 0xd7,
-	0x07, 0x6b, 0x4d, 0x2a, 0x4d, 0x03, 0xf3, 0xc0, 0x1c, 0x30, 0x0b, 0x62, 0xa4, 0x94, 0x21, 0x76,
-	0x67, 0x94, 0x96, 0x64, 0xe6, 0xe8, 0x67, 0xe6, 0x95, 0x14, 0x97, 0x14, 0xa1, 0xbb, 0x42, 0xc9,
-	0x82, 0x8b, 0x23, 0xb0, 0x34, 0x31, 0xaf, 0x24, 0xb3, 0xa4, 0x52, 0x48, 0x8c, 0x8b, 0x0d, 0xa8,
-	0x24, 0x33, 0x2f, 0x5d, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xca, 0xb3, 0x12, 0x99, 0xb1,
-	0x40, 0x9e, 0xa1, 0x63, 0xa1, 0x3c, 0xc3, 0x04, 0x20, 0x5e, 0x00, 0xc4, 0x0d, 0x77, 0x14, 0x18,
-	0x9c, 0xb4, 0x4e, 0x3c, 0x94, 0x63, 0xb8, 0x00, 0xc4, 0x37, 0x80, 0xb8, 0xe1, 0x91, 0x1c, 0xe3,
-	0x09, 0x20, 0xbe, 0x00, 0xc4, 0x0f, 0x80, 0x78, 0xc2, 0x63, 0x39, 0x86, 0x28, 0x0e, 0x98, 0x3f,
-	0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0x90, 0x1c, 0x7f, 0xff, 0x20, 0x01, 0x00, 0x00,
-}
diff --git a/vendor/k8s.io/kubernetes/pkg/api/resource/quantity_proto.go b/vendor/k8s.io/kubernetes/pkg/api/resource/quantity_proto.go
deleted file mode 100644
index 74dfb4e..0000000
--- a/vendor/k8s.io/kubernetes/pkg/api/resource/quantity_proto.go
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
-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 resource
-
-import (
-	"fmt"
-	"io"
-
-	"github.com/gogo/protobuf/proto"
-)
-
-var _ proto.Sizer = &Quantity{}
-
-func (m *Quantity) Marshal() (data []byte, err error) {
-	size := m.Size()
-	data = make([]byte, size)
-	n, err := m.MarshalTo(data)
-	if err != nil {
-		return nil, err
-	}
-	return data[:n], nil
-}
-
-// MarshalTo is a customized version of the generated Protobuf unmarshaler for a struct
-// with a single string field.
-func (m *Quantity) MarshalTo(data []byte) (int, error) {
-	var i int
-	_ = i
-	var l int
-	_ = l
-
-	data[i] = 0xa
-	i++
-	// BEGIN CUSTOM MARSHAL
-	out := m.String()
-	i = encodeVarintGenerated(data, i, uint64(len(out)))
-	i += copy(data[i:], out)
-	// END CUSTOM MARSHAL
-
-	return i, nil
-}
-
-func encodeVarintGenerated(data []byte, offset int, v uint64) int {
-	for v >= 1<<7 {
-		data[offset] = uint8(v&0x7f | 0x80)
-		v >>= 7
-		offset++
-	}
-	data[offset] = uint8(v)
-	return offset + 1
-}
-
-func (m *Quantity) Size() (n int) {
-	var l int
-	_ = l
-
-	// BEGIN CUSTOM SIZE
-	l = len(m.String())
-	// END CUSTOM SIZE
-
-	n += 1 + l + sovGenerated(uint64(l))
-	return n
-}
-
-func sovGenerated(x uint64) (n int) {
-	for {
-		n++
-		x >>= 7
-		if x == 0 {
-			break
-		}
-	}
-	return n
-}
-
-// Unmarshal is a customized version of the generated Protobuf unmarshaler for a struct
-// with a single string field.
-func (m *Quantity) Unmarshal(data []byte) error {
-	l := len(data)
-	iNdEx := 0
-	for iNdEx < l {
-		preIndex := iNdEx
-		var wire uint64
-		for shift := uint(0); ; shift += 7 {
-			if shift >= 64 {
-				return ErrIntOverflowGenerated
-			}
-			if iNdEx >= l {
-				return io.ErrUnexpectedEOF
-			}
-			b := data[iNdEx]
-			iNdEx++
-			wire |= (uint64(b) & 0x7F) << shift
-			if b < 0x80 {
-				break
-			}
-		}
-		fieldNum := int32(wire >> 3)
-		wireType := int(wire & 0x7)
-		if wireType == 4 {
-			return fmt.Errorf("proto: Quantity: wiretype end group for non-group")
-		}
-		if fieldNum <= 0 {
-			return fmt.Errorf("proto: Quantity: illegal tag %d (wire type %d)", fieldNum, wire)
-		}
-		switch fieldNum {
-		case 1:
-			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field String_", wireType)
-			}
-			var stringLen uint64
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return ErrIntOverflowGenerated
-				}
-				if iNdEx >= l {
-					return io.ErrUnexpectedEOF
-				}
-				b := data[iNdEx]
-				iNdEx++
-				stringLen |= (uint64(b) & 0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			intStringLen := int(stringLen)
-			if intStringLen < 0 {
-				return ErrInvalidLengthGenerated
-			}
-			postIndex := iNdEx + intStringLen
-			if postIndex > l {
-				return io.ErrUnexpectedEOF
-			}
-			s := string(data[iNdEx:postIndex])
-
-			// BEGIN CUSTOM DECODE
-			p, err := ParseQuantity(s)
-			if err != nil {
-				return err
-			}
-			*m = p
-			// END CUSTOM DECODE
-
-			iNdEx = postIndex
-		default:
-			iNdEx = preIndex
-			skippy, err := skipGenerated(data[iNdEx:])
-			if err != nil {
-				return err
-			}
-			if skippy < 0 {
-				return ErrInvalidLengthGenerated
-			}
-			if (iNdEx + skippy) > l {
-				return io.ErrUnexpectedEOF
-			}
-			iNdEx += skippy
-		}
-	}
-
-	if iNdEx > l {
-		return io.ErrUnexpectedEOF
-	}
-	return nil
-}
-
-func skipGenerated(data []byte) (n int, err error) {
-	l := len(data)
-	iNdEx := 0
-	for iNdEx < l {
-		var wire uint64
-		for shift := uint(0); ; shift += 7 {
-			if shift >= 64 {
-				return 0, ErrIntOverflowGenerated
-			}
-			if iNdEx >= l {
-				return 0, io.ErrUnexpectedEOF
-			}
-			b := data[iNdEx]
-			iNdEx++
-			wire |= (uint64(b) & 0x7F) << shift
-			if b < 0x80 {
-				break
-			}
-		}
-		wireType := int(wire & 0x7)
-		switch wireType {
-		case 0:
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return 0, ErrIntOverflowGenerated
-				}
-				if iNdEx >= l {
-					return 0, io.ErrUnexpectedEOF
-				}
-				iNdEx++
-				if data[iNdEx-1] < 0x80 {
-					break
-				}
-			}
-			return iNdEx, nil
-		case 1:
-			iNdEx += 8
-			return iNdEx, nil
-		case 2:
-			var length int
-			for shift := uint(0); ; shift += 7 {
-				if shift >= 64 {
-					return 0, ErrIntOverflowGenerated
-				}
-				if iNdEx >= l {
-					return 0, io.ErrUnexpectedEOF
-				}
-				b := data[iNdEx]
-				iNdEx++
-				length |= (int(b) & 0x7F) << shift
-				if b < 0x80 {
-					break
-				}
-			}
-			iNdEx += length
-			if length < 0 {
-				return 0, ErrInvalidLengthGenerated
-			}
-			return iNdEx, nil
-		case 3:
-			for {
-				var innerWire uint64
-				var start int = iNdEx
-				for shift := uint(0); ; shift += 7 {
-					if shift >= 64 {
-						return 0, ErrIntOverflowGenerated
-					}
-					if iNdEx >= l {
-						return 0, io.ErrUnexpectedEOF
-					}
-					b := data[iNdEx]
-					iNdEx++
-					innerWire |= (uint64(b) & 0x7F) << shift
-					if b < 0x80 {
-						break
-					}
-				}
-				innerWireType := int(innerWire & 0x7)
-				if innerWireType == 4 {
-					break
-				}
-				next, err := skipGenerated(data[start:])
-				if err != nil {
-					return 0, err
-				}
-				iNdEx = start + next
-			}
-			return iNdEx, nil
-		case 4:
-			return iNdEx, nil
-		case 5:
-			iNdEx += 4
-			return iNdEx, nil
-		default:
-			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
-		}
-	}
-	panic("unreachable")
-}
-
-var (
-	ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
-	ErrIntOverflowGenerated   = fmt.Errorf("proto: integer overflow")
-)


Test patch:
diff --git a/schema/types/isolator_resources_test.go b/schema/types/isolator_resources_test.go
index 747b775..e53c15b 100644
--- a/schema/types/isolator_resources_test.go
+++ b/schema/types/isolator_resources_test.go
@@ -18,7 +18,7 @@ import (
 	"reflect"
 	"testing"
 
-	"k8s.io/kubernetes/pkg/api/resource"
+	"github.com/appc/spec/schema/types/resource"
 )
 
 func mustQuantity(s string) *resource.Quantity {
diff --git a/schema/types/resource/amount_test.go b/schema/types/resource/amount_test.go
new file mode 100644
index 0000000..acaad63
--- /dev/null
+++ b/schema/types/resource/amount_test.go
@@ -0,0 +1,111 @@
+/*
+Copyright 2014 The Kubernetes Authors All rights reserved.
+
+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 resource
+
+import (
+	"testing"
+)
+
+func TestInt64AmountAsInt64(t *testing.T) {
+	for _, test := range []struct {
+		value  int64
+		scale  Scale
+		result int64
+		ok     bool
+	}{
+		{100, 0, 100, true},
+		{100, 1, 1000, true},
+		{100, -5, 0, false},
+		{100, 100, 0, false},
+	} {
+		r, ok := int64Amount{value: test.value, scale: test.scale}.AsInt64()
+		if r != test.result {
+			t.Errorf("%v: unexpected result: %d", test, r)
+		}
+		if ok != test.ok {
+			t.Errorf("%v: unexpected ok: %t", test, ok)
+		}
+	}
+}
+
+func TestInt64AmountAdd(t *testing.T) {
+	for _, test := range []struct {
+		a, b, c int64Amount
+		ok      bool
+	}{
+		{int64Amount{value: 100, scale: 1}, int64Amount{value: 10, scale: 2}, int64Amount{value: 200, scale: 1}, true},
+		{int64Amount{value: 100, scale: 1}, int64Amount{value: 1, scale: 2}, int64Amount{value: 110, scale: 1}, true},
+		{int64Amount{value: 100, scale: 1}, int64Amount{value: 1, scale: 100}, int64Amount{value: 1, scale: 100}, false},
+		{int64Amount{value: -5, scale: 2}, int64Amount{value: 50, scale: 1}, int64Amount{value: 0, scale: 1}, true},
+		{int64Amount{value: -5, scale: 2}, int64Amount{value: 5, scale: 2}, int64Amount{value: 0, scale: 2}, true},
+
+		{int64Amount{value: mostPositive, scale: -1}, int64Amount{value: 1, scale: -1}, int64Amount{value: 0, scale: -1}, false},
+		{int64Amount{value: mostPositive, scale: -1}, int64Amount{value: 0, scale: -1}, int64Amount{value: mostPositive, scale: -1}, true},
+		{int64Amount{value: mostPositive / 10, scale: 1}, int64Amount{value: 10, scale: 0}, int64Amount{value: mostPositive, scale: -1}, false},
+	} {
+		c := test.a
+		ok := c.Add(test.b)
+		if ok != test.ok {
+			t.Errorf("%v: unexpected ok: %t", test, ok)
+		}
+		if ok {
+			if c != test.c {
+				t.Errorf("%v: unexpected result: %d", test, c)
+			}
+		} else {
+			if c != test.a {
+				t.Errorf("%v: overflow addition mutated source: %d", test, c)
+			}
+		}
+
+		// addition is commutative
+		c = test.b
+		if ok := c.Add(test.a); ok != test.ok {
+			t.Errorf("%v: unexpected ok: %t", test, ok)
+		}
+		if ok {
+			if c != test.c {
+				t.Errorf("%v: unexpected result: %d", test, c)
+			}
+		} else {
+			if c != test.b {
+				t.Errorf("%v: overflow addition mutated source: %d", test, c)
+			}
+		}
+	}
+}
+func TestInt64AsCanonicalString(t *testing.T) {
+	for _, test := range []struct {
+		value    int64
+		scale    Scale
+		result   string
+		exponent int32
+	}{
+		{100, 0, "100", 0},
+		{100, 1, "1", 3},
+		{100, -1, "10", 0},
+		{10800, -10, "1080", -9},
+	} {
+		r, exp := int64Amount{value: test.value, scale: test.scale}.AsCanonicalBytes(nil)
+		if string(r) != test.result {
+			t.Errorf("%v: unexpected result: %s", test, r)
+		}
+		if exp != test.exponent {
+			t.Errorf("%v: unexpected exponent: %d", test, exp)
+		}
+	}
+}
diff --git a/schema/types/resource/math_test.go b/schema/types/resource/math_test.go
new file mode 100644
index 0000000..0fdda12
--- /dev/null
+++ b/schema/types/resource/math_test.go
@@ -0,0 +1,211 @@
+/*
+Copyright 2014 The Kubernetes Authors All rights reserved.
+
+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 resource
+
+import (
+	"testing"
+)
+
+func TestDetectOverflowAdd(t *testing.T) {
+	for _, test := range []struct {
+		a, b int64
+		c    int64
+		ok   bool
+	}{
+		{0, 0, 0, true},
+		{-1, 1, 0, true},
+		{0, 1, 1, true},
+		{2, 2, 4, true},
+		{2, -2, 0, true},
+		{-2, -2, -4, true},
+
+		{mostNegative, -1, 0, false},
+		{mostNegative, 1, mostNegative + 1, true},
+		{mostPositive, -1, mostPositive - 1, true},
+		{mostPositive, 1, 0, false},
+
+		{mostNegative, mostPositive, -1, true},
+		{mostPositive, mostNegative, -1, true},
+		{mostPositive, mostPositive, 0, false},
+		{mostNegative, mostNegative, 0, false},
+
+		{-mostPositive, mostNegative, 0, false},
+		{mostNegative, -mostPositive, 0, false},
+		{-mostPositive, -mostPositive, 0, false},
+	} {
+		c, ok := int64Add(test.a, test.b)
+		if c != test.c {
+			t.Errorf("%v: unexpected result: %d", test, c)
+		}
+		if ok != test.ok {
+			t.Errorf("%v: unexpected overflow: %t", test, ok)
+		}
+		// addition is commutative
+		d, ok2 := int64Add(test.b, test.a)
+		if c != d || ok != ok2 {
+			t.Errorf("%v: not commutative: %d %t", test, d, ok2)
+		}
+	}
+}
+
+func TestDetectOverflowMultiply(t *testing.T) {
+	for _, test := range []struct {
+		a, b int64
+		c    int64
+		ok   bool
+	}{
+		{0, 0, 0, true},
+		{-1, 1, -1, true},
+		{-1, -1, 1, true},
+		{1, 1, 1, true},
+		{0, 1, 0, true},
+		{1, 0, 0, true},
+		{2, 2, 4, true},
+		{2, -2, -4, true},
+		{-2, -2, 4, true},
+
+		{mostNegative, -1, 0, false},
+		{mostNegative, 1, mostNegative, true},
+		{mostPositive, -1, -mostPositive, true},
+		{mostPositive, 1, mostPositive, true},
+
+		{mostNegative, mostPositive, 0, false},
+		{mostPositive, mostNegative, 0, false},
+		{mostPositive, mostPositive, 1, false},
+		{mostNegative, mostNegative, 0, false},
+
+		{-mostPositive, mostNegative, 0, false},
+		{mostNegative, -mostPositive, 0, false},
+		{-mostPositive, -mostPositive, 1, false},
+	} {
+		c, ok := int64Multiply(test.a, test.b)
+		if c != test.c {
+			t.Errorf("%v: unexpected result: %d", test, c)
+		}
+		if ok != test.ok {
+			t.Errorf("%v: unexpected overflow: %t", test, ok)
+		}
+		// multiplication is commutative
+		d, ok2 := int64Multiply(test.b, test.a)
+		if c != d || ok != ok2 {
+			t.Errorf("%v: not commutative: %d %t", test, d, ok2)
+		}
+	}
+}
+
+func TestDetectOverflowScale(t *testing.T) {
+	for _, a := range []int64{0, -1, 1, 10, -10, mostPositive, mostNegative, -mostPositive} {
+		for _, b := range []int64{1, 2, 10, 100, 1000, mostPositive} {
+			expect, expectOk := int64Multiply(a, b)
+
+			c, ok := int64MultiplyScale(a, b)
+			if c != expect {
+				t.Errorf("%d*%d: unexpected result: %d", a, b, c)
+			}
+			if ok != expectOk {
+				t.Errorf("%d*%d: unexpected overflow: %t", a, b, ok)
+			}
+		}
+		for _, test := range []struct {
+			base int64
+			fn   func(a int64) (int64, bool)
+		}{
+			{10, int64MultiplyScale10},
+			{100, int64MultiplyScale100},
+			{1000, int64MultiplyScale1000},
+		} {
+			expect, expectOk := int64Multiply(a, test.base)
+			c, ok := test.fn(a)
+			if c != expect {
+				t.Errorf("%d*%d: unexpected result: %d", a, test.base, c)
+			}
+			if ok != expectOk {
+				t.Errorf("%d*%d: unexpected overflow: %t", a, test.base, ok)
+			}
+		}
+	}
+}
+
+func TestRemoveInt64Factors(t *testing.T) {
+	for _, test := range []struct {
+		value  int64
+		max    int64
+		result int64
+		scale  int32
+	}{
+		{100, 10, 1, 2},
+		{100, 10, 1, 2},
+		{100, 100, 1, 1},
+		{1, 10, 1, 0},
+	} {
+		r, s := removeInt64Factors(test.value, test.max)
+		if r != test.result {
+			t.Errorf("%v: unexpected result: %d", test, r)
+		}
+		if s != test.scale {
+			t.Errorf("%v: unexpected scale: %d", test, s)
+		}
+	}
+}
+
+func TestNegativeScaleInt64(t *testing.T) {
+	for _, test := range []struct {
+		base   int64
+		scale  Scale
+		result int64
+		exact  bool
+	}{
+		{1234567, 0, 1234567, true},
+		{1234567, 1, 123457, false},
+		{1234567, 2, 12346, false},
+		{1234567, 3, 1235, false},
+		{1234567, 4, 124, false},
+
+		{-1234567, 0, -1234567, true},
+		{-1234567, 1, -123457, false},
+		{-1234567, 2, -12346, false},
+		{-1234567, 3, -1235, false},
+		{-1234567, 4, -124, false},
+
+		{1000, 0, 1000, true},
+		{1000, 1, 100, true},
+		{1000, 2, 10, true},
+		{1000, 3, 1, true},
+		{1000, 4, 1, false},
+
+		{-1000, 0, -1000, true},
+		{-1000, 1, -100, true},
+		{-1000, 2, -10, true},
+		{-1000, 3, -1, true},
+		{-1000, 4, -1, false},
+
+		{0, 0, 0, true},
+		{0, 1, 0, true},
+		{0, 2, 0, true},
+
+		// negative scale is undefined behavior
+		{1000, -1, 1000, true},
+	} {
+		result, exact := negativeScaleInt64(test.base, test.scale)
+		if result != test.result {
+			t.Errorf("%v: unexpected result: %d", test, result)
+		}
+		if exact != test.exact {
+			t.Errorf("%v: unexpected exact: %t", test, exact)
+		}
+	}
+}
diff --git a/schema/types/resource/quantity_example_test.go b/schema/types/resource/quantity_example_test.go
new file mode 100644
index 0000000..104f8ea
--- /dev/null
+++ b/schema/types/resource/quantity_example_test.go
@@ -0,0 +1,59 @@
+/*
+Copyright 2014 The Kubernetes Authors All rights reserved.
+
+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 resource_test
+
+import (
+	"fmt"
+
+	"github.com/appc/spec/schema/types/resource"
+)
+
+func ExampleFormat() {
+	memorySize := resource.NewQuantity(5*1024*1024*1024, resource.BinarySI)
+	fmt.Printf("memorySize = %v\n", memorySize)
+
+	diskSize := resource.NewQuantity(5*1000*1000*1000, resource.DecimalSI)
+	fmt.Printf("diskSize = %v\n", diskSize)
+
+	cores := resource.NewMilliQuantity(5300, resource.DecimalSI)
+	fmt.Printf("cores = %v\n", cores)
+
+	// Output:
+	// memorySize = 5Gi
+	// diskSize = 5G
+	// cores = 5300m
+}
+
+func ExampleMustParse() {
+	memorySize := resource.MustParse("5Gi")
+	fmt.Printf("memorySize = %v (%v)\n", memorySize.Value(), memorySize.Format)
+
+	diskSize := resource.MustParse("5G")
+	fmt.Printf("diskSize = %v (%v)\n", diskSize.Value(), diskSize.Format)
+
+	cores := resource.MustParse("5300m")
+	fmt.Printf("milliCores = %v (%v)\n", cores.MilliValue(), cores.Format)
+
+	cores2 := resource.MustParse("5.4")
+	fmt.Printf("milliCores = %v (%v)\n", cores2.MilliValue(), cores2.Format)
+
+	// Output:
+	// memorySize = 5368709120 (BinarySI)
+	// diskSize = 5000000000 (DecimalSI)
+	// milliCores = 5300 (DecimalSI)
+	// milliCores = 5400 (DecimalSI)
+}
diff --git a/schema/types/resource/quantity_test.go b/schema/types/resource/quantity_test.go
new file mode 100644
index 0000000..5f2e7be
--- /dev/null
+++ b/schema/types/resource/quantity_test.go
@@ -0,0 +1,1334 @@
+/*
+Copyright 2014 The Kubernetes Authors All rights reserved.
+
+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 resource
+
+import (
+	"encoding/json"
+	"math/rand"
+	"strings"
+	"testing"
+	"unicode"
+
+	fuzz "github.com/google/gofuzz"
+	"github.com/spf13/pflag"
+
+	inf "gopkg.in/inf.v0"
+)
+
+var (
+	testQuantityFlag = QuantityFlag("quantityFlag", "1M", "dummy flag for testing the quantity flag mechanism")
+)
+
+var useInfDec bool
+
+func amount(i int64, exponent int) infDecAmount {
+	// See the below test-- scale is the negative of an exponent.
+	return infDecAmount{inf.NewDec(i, inf.Scale(-exponent))}
+}
+
+func dec(i int64, exponent int) infDecAmount {
+	// See the below test-- scale is the negative of an exponent.
+	return infDecAmount{inf.NewDec(i, inf.Scale(-exponent))}
+}
+
+func decQuantity(i int64, exponent int, format Format) Quantity {
+	return Quantity{d: dec(i, exponent), Format: format}
+}
+
+func intQuantity(i int64, exponent Scale, format Format) Quantity {
+	return Quantity{i: int64Amount{value: i, scale: exponent}, Format: format}
+}
+
+func TestDec(t *testing.T) {
+	table := []struct {
+		got    infDecAmount
+		expect string
+	}{
+		{dec(1, 0), "1"},
+		{dec(1, 1), "10"},
+		{dec(5, 2), "500"},
+		{dec(8, 3), "8000"},
+		{dec(2, 0), "2"},
+		{dec(1, -1), "0.1"},
+		{dec(3, -2), "0.03"},
+		{dec(4, -3), "0.004"},
+	}
+
+	for _, item := range table {
+		if e, a := item.expect, item.got.Dec.String(); e != a {
+			t.Errorf("expected %v, got %v", e, a)
+		}
+	}
+}
+
+// TestQuantityParseZero ensures that when a 0 quantity is passed, its string value is 0
+func TestQuantityParseZero(t *testing.T) {
+	zero := MustParse("0")
+	if expected, actual := "0", zero.String(); expected != actual {
+		t.Errorf("Expected %v, actual %v", expected, actual)
+	}
+}
+
+// TestQuantityAddZeroPreservesSuffix verifies that a suffix is preserved
+// independent of the order of operations when adding a zero and non-zero val
+func TestQuantityAddZeroPreservesSuffix(t *testing.T) {
+	testValues := []string{"100m", "1Gi"}
+	zero := MustParse("0")
+	for _, testValue := range testValues {
+		value := MustParse(testValue)
+		v1 := *value.Copy()
+		// ensure non-zero + zero = non-zero (suffix preserved)
+		v1.Add(zero)
+		// ensure zero + non-zero = non-zero (suffix preserved)
+		v2 := *zero.Copy()
+		v2.Add(value)
+
+		if v1.String() != testValue {
+			t.Errorf("Expected %v, actual %v", testValue, v1.String())
+			continue
+		}
+		if v2.String() != testValue {
+			t.Errorf("Expected %v, actual %v", testValue, v2.String())
+		}
+	}
+}
+
+// TestQuantitySubZeroPreservesSuffix verifies that a suffix is preserved
+// independent of the order of operations when subtracting a zero and non-zero val
+func TestQuantitySubZeroPreservesSuffix(t *testing.T) {
+	testValues := []string{"100m", "1Gi"}
+	zero := MustParse("0")
+	for _, testValue := range testValues {
+		value := MustParse(testValue)
+		v1 := *value.Copy()
+		// ensure non-zero - zero = non-zero (suffix preserved)
+		v1.Sub(zero)
+		// ensure we preserved the input value
+		if v1.String() != testValue {
+			t.Errorf("Expected %v, actual %v", testValue, v1.String())
+		}
+
+		// ensure zero - non-zero = -non-zero (suffix preserved)
+		v2 := *zero.Copy()
+		v2.Sub(value)
+		negVal := *value.Copy()
+		negVal.Neg()
+		if v2.String() != negVal.String() {
+			t.Errorf("Expected %v, actual %v", negVal.String(), v2.String())
+		}
+	}
+}
+
+// Verifies that you get 0 as canonical value if internal value is 0, and not 0<suffix>
+func TestQuantityCanocicalizeZero(t *testing.T) {
+	val := MustParse("1000m")
+	val.i.Sub(int64Amount{value: 1})
+	zero := Quantity{i: val.i, Format: DecimalSI}
+	if expected, actual := "0", zero.String(); expected != actual {
+		t.Errorf("Expected %v, actual %v", expected, actual)
+	}
+}
+
+func TestQuantityCmp(t *testing.T) {
+	table := []struct {
+		x      string
+		y      string
+		expect int
+	}{
+		{"0", "0", 0},
+		{"100m", "50m", 1},
+		{"50m", "100m", -1},
+		{"10000T", "100Gi", 1},
+	}
+	for _, testCase := range table {
+		q1 := MustParse(testCase.x)
+		q2 := MustParse(testCase.y)
+		if result := q1.Cmp(q2); result != testCase.expect {
+			t.Errorf("X: %v, Y: %v, Expected: %v, Actual: %v", testCase.x, testCase.y, testCase.expect, result)
+		}
+	}
+
+	nils := []struct {
+		x      *inf.Dec
+		y      *inf.Dec
+		expect int
+	}{
+		{dec(0, 0).Dec, dec(0, 0).Dec, 0},
+		{nil, dec(0, 0).Dec, 0},
+		{dec(0, 0).Dec, nil, 0},
+		{nil, nil, 0},
+		{nil, dec(10, 0).Dec, -1},
+		{nil, dec(-10, 0).Dec, 1},
+		{dec(10, 0).Dec, nil, 1},
+		{dec(-10, 0).Dec, nil, -1},
+	}
+	for _, nilCase := range nils {
+		q1 := Quantity{d: infDecAmount{nilCase.x}, Format: DecimalSI}
+		q2 := Quantity{d: infDecAmount{nilCase.y}, Format: DecimalSI}
+		if result := q1.Cmp(q2); result != nilCase.expect {
+			t.Errorf("X: %v, Y: %v, Expected: %v, Actual: %v", nilCase.x, nilCase.y, nilCase.expect, result)
+		}
+	}
+}
+
+func TestParseQuantityString(t *testing.T) {
+	table := []struct {
+		input              string
+		positive           bool
+		value              string
+		num, denom, suffix string
+	}{
+		{"0.025Ti", true, "0.025", "0", "025", "Ti"},
+		{"1.025Ti", true, "1.025", "1", "025", "Ti"},
+		{"-1.025Ti", false, "-1.025", "1", "025", "Ti"},
+		{".", true, ".", "0", "", ""},
+		{"-.", false, "-.", "0", "", ""},
+		{"1E-3", true, "1", "1", "", "E-3"},
+	}
+	for _, test := range table {
+		positive, value, num, denom, suffix, err := parseQuantityString(test.input)
+		if err != nil {
+			t.Errorf("%s: error: %v", test.input, err)
+			continue
+		}
+		if positive != test.positive || value != test.value || num != test.num || denom != test.denom || suffix != test.suffix {
+			t.Errorf("%s: unmatched: %t %q %q %q %q", test.input, positive, value, num, denom, suffix)
+		}
+	}
+}
+
+func TestQuantityParse(t *testing.T) {
+	if _, err := ParseQuantity(""); err == nil {
+		t.Errorf("expected empty string to return error")
+	}
+
+	table := []struct {
+		input  string
+		expect Quantity
+	}{
+		{"0", decQuantity(0, 0, DecimalSI)},
+		{"0n", decQuantity(0, 0, DecimalSI)},
+		{"0u", decQuantity(0, 0, DecimalSI)},
+		{"0m", decQuantity(0, 0, DecimalSI)},
+		{"0Ki", decQuantity(0, 0, BinarySI)},
+		{"0k", decQuantity(0, 0, DecimalSI)},
+		{"0Mi", decQuantity(0, 0, BinarySI)},
+		{"0M", decQuantity(0, 0, DecimalSI)},
+		{"0Gi", decQuantity(0, 0, BinarySI)},
+		{"0G", decQuantity(0, 0, DecimalSI)},
+		{"0Ti", decQuantity(0, 0, BinarySI)},
+		{"0T", decQuantity(0, 0, DecimalSI)},
+
+		// Quantity less numbers are allowed
+		{"1", decQuantity(1, 0, DecimalSI)},
+
+		// Binary suffixes
+		{"1Ki", decQuantity(1024, 0, BinarySI)},
+		{"8Ki", decQuantity(8*1024, 0, BinarySI)},
+		{"7Mi", decQuantity(7*1024*1024, 0, BinarySI)},
+		{"6Gi", decQuantity(6*1024*1024*1024, 0, BinarySI)},
+		{"5Ti", decQuantity(5*1024*1024*1024*1024, 0, BinarySI)},
+		{"4Pi", decQuantity(4*1024*1024*1024*1024*1024, 0, BinarySI)},
+		{"3Ei", decQuantity(3*1024*1024*1024*1024*1024*1024, 0, BinarySI)},
+
+		{"10Ti", decQuantity(10*1024*1024*1024*1024, 0, BinarySI)},
+		{"100Ti", decQuantity(100*1024*1024*1024*1024, 0, BinarySI)},
+
+		// Decimal suffixes
+		{"5n", decQuantity(5, -9, DecimalSI)},
+		{"4u", decQuantity(4, -6, DecimalSI)},
+		{"3m", decQuantity(3, -3, DecimalSI)},
+		{"9", decQuantity(9, 0, DecimalSI)},
+		{"8k", decQuantity(8, 3, DecimalSI)},
+		{"50k", decQuantity(5, 4, DecimalSI)},
+		{"7M", decQuantity(7, 6, DecimalSI)},
+		{"6G", decQuantity(6, 9, DecimalSI)},
+		{"5T", decQuantity(5, 12, DecimalSI)},
+		{"40T", decQuantity(4, 13, DecimalSI)},
+		{"300T", decQuantity(3, 14, DecimalSI)},
+		{"2P", decQuantity(2, 15, DecimalSI)},
+		{"1E", decQuantity(1, 18, DecimalSI)},
+
+		// Decimal exponents
+		{"1E-3", decQuantity(1, -3, DecimalExponent)},
+		{"1e3", decQuantity(1, 3, DecimalExponent)},
+		{"1E6", decQuantity(1, 6, DecimalExponent)},
+		{"1e9", decQuantity(1, 9, DecimalExponent)},
+		{"1E12", decQuantity(1, 12, DecimalExponent)},
+		{"1e15", decQuantity(1, 15, DecimalExponent)},
+		{"1E18", decQuantity(1, 18, DecimalExponent)},
+
+		// Nonstandard but still parsable
+		{"1e14", decQuantity(1, 14, DecimalExponent)},
+		{"1e13", decQuantity(1, 13, DecimalExponent)},
+		{"1e3", decQuantity(1, 3, DecimalExponent)},
+		{"100.035k", decQuantity(100035, 0, DecimalSI)},
+
+		// Things that look like floating point
+		{"0.001", decQuantity(1, -3, DecimalSI)},
+		{"0.0005k", decQuantity(5, -1, DecimalSI)},
+		{"0.005", decQuantity(5, -3, DecimalSI)},
+		{"0.05", decQuantity(5, -2, DecimalSI)},
+		{"0.5", decQuantity(5, -1, DecimalSI)},
+		{"0.00050k", decQuantity(5, -1, DecimalSI)},
+		{"0.00500", decQuantity(5, -3, DecimalSI)},
+		{"0.05000", decQuantity(5, -2, DecimalSI)},
+		{"0.50000", decQuantity(5, -1, DecimalSI)},
+		{"0.5e0", decQuantity(5, -1, DecimalExponent)},
+		{"0.5e-1", decQuantity(5, -2, DecimalExponent)},
+		{"0.5e-2", decQuantity(5, -3, DecimalExponent)},
+		{"0.5e0", decQuantity(5, -1, DecimalExponent)},
+		{"10.035M", decQuantity(10035, 3, DecimalSI)},
+
+		{"1.2e3", decQuantity(12, 2, DecimalExponent)},
+		{"1.3E+6", decQuantity(13, 5, DecimalExponent)},
+		{"1.40e9", decQuantity(14, 8, DecimalExponent)},
+		{"1.53E12", decQuantity(153, 10, DecimalExponent)},
+		{"1.6e15", decQuantity(16, 14, DecimalExponent)},
+		{"1.7E18", decQuantity(17, 17, DecimalExponent)},
+
+		{"9.01", decQuantity(901, -2, DecimalSI)},
+		{"8.1k", decQuantity(81, 2, DecimalSI)},
+		{"7.123456M", decQuantity(7123456, 0, DecimalSI)},
+		{"6.987654321G", decQuantity(6987654321, 0, DecimalSI)},
+		{"5.444T", decQuantity(5444, 9, DecimalSI)},
+		{"40.1T", decQuantity(401, 11, DecimalSI)},
+		{"300.2T", decQuantity(3002, 11, DecimalSI)},
+		{"2.5P", decQuantity(25, 14, DecimalSI)},
+		{"1.01E", decQuantity(101, 16, DecimalSI)},
+
+		// Things that saturate/round
+		{"3.001n", decQuantity(4, -9, DecimalSI)},
+		{"1.1E-9", decQuantity(2, -9, DecimalExponent)},
+		{"0.0000000001", decQuantity(1, -9, DecimalSI)},
+		{"0.0000000005", decQuantity(1, -9, DecimalSI)},
+		{"0.00000000050", decQuantity(1, -9, DecimalSI)},
+		{"0.5e-9", decQuantity(1, -9, DecimalExponent)},
+		{"0.9n", decQuantity(1, -9, DecimalSI)},
+		{"0.00000012345", decQuantity(124, -9, DecimalSI)},
+		{"0.00000012354", decQuantity(124, -9, DecimalSI)},
+		{"9Ei", Quantity{d: maxAllowed, Format: BinarySI}},
+		{"9223372036854775807Ki", Quantity{d: maxAllowed, Format: BinarySI}},
+		{"12E", decQuantity(12, 18, DecimalSI)},
+
+		// We'll accept fractional binary stuff, too.
+		{"100.035Ki", decQuantity(10243584, -2, BinarySI)},
+		{"0.5Mi", decQuantity(.5*1024*1024, 0, BinarySI)},
+		{"0.05Gi", decQuantity(536870912, -1, BinarySI)},
+		{"0.025Ti", decQuantity(274877906944, -1, BinarySI)},
+
+		// Things written by trolls
+		{"0.000000000001Ki", decQuantity(2, -9, DecimalSI)}, // rounds up, changes format
+		{".001", decQuantity(1, -3, DecimalSI)},
+		{".0001k", decQuantity(100, -3, DecimalSI)},
+		{"1.", decQuantity(1, 0, DecimalSI)},
+		{"1.G", decQuantity(1, 9, DecimalSI)},
+	}
+
+	for _, asDec := range []bool{false, true} {
+		for _, item := range table {
+			got, err := ParseQuantity(item.input)
+			if err != nil {
+				t.Errorf("%v: unexpected error: %v", item.input, err)
+				continue
+			}
+			if asDec {
+				got.AsDec()
+			}
+
+			if e, a := item.expect, got; e.Cmp(a) != 0 {
+				t.Errorf("%v: expected %v, got %v", item.input, e.String(), a.String())
+			}
+			if e, a := item.expect.Format, got.Format; e != a {
+				t.Errorf("%v: expected %#v, got %#v", item.input, e, a)
+			}
+
+			if asDec {
+				if i, ok := got.AsInt64(); i != 0 || ok {
+					t.Errorf("%v: expected inf.Dec to return false for AsInt64: %d", item.input, i)
+				}
+				continue
+			}
+			i, ok := item.expect.AsInt64()
+			if !ok {
+				continue
+			}
+			j, ok := got.AsInt64()
+			if !ok {
+				if got.d.Dec == nil && got.i.scale >= 0 {
+					t.Errorf("%v: is an int64Amount, but can't return AsInt64: %v", item.input, got)
+				}
+				continue
+			}
+			if i != j {
+				t.Errorf("%v: expected equivalent representation as int64: %d %d", item.input, i, j)
+			}
+		}
+
+		for _, item := range table {
+			got, err := ParseQuantity(item.input)
+			if err != nil {
+				t.Errorf("%v: unexpected error: %v", item.input, err)
+				continue
+			}
+
+			if asDec {
+				got.AsDec()
+			}
+
+			// verify that we can decompose the input and get the same result by building up from the base.
+			positive, _, num, denom, suffix, err := parseQuantityString(item.input)
+			if err != nil {
+				t.Errorf("%v: unexpected error: %v", item.input, err)
+				continue
+			}
+			if got.Sign() >= 0 && !positive || got.Sign() < 0 && positive {
+				t.Errorf("%v: positive was incorrect: %t", item.input, positive)
+				continue
+			}
+			var value string
+			if !positive {
+				value = "-"
+			}
+			value += num
+			if len(denom) > 0 {
+				value += "." + denom
+			}
+			value += suffix
+			if len(value) == 0 {
+				t.Errorf("%v: did not parse correctly, %q %q %q", item.input, num, denom, suffix)
+			}
+			expected, err := ParseQuantity(value)
+			if err != nil {
+				t.Errorf("%v: unexpected error for %s: %v", item.input, value, err)
+				continue
+			}
+			if expected.Cmp(got) != 0 {
+				t.Errorf("%v: not the same as %s", item.input, value)
+				continue
+			}
+		}
+
+		// Try the negative version of everything
+		desired := &inf.Dec{}
+		expect := Quantity{d: infDecAmount{Dec: desired}}
+		for _, item := range table {
+			got, err := ParseQuantity("-" + strings.TrimLeftFunc(item.input, unicode.IsSpace))
+			if err != nil {
+				t.Errorf("-%v: unexpected error: %v", item.input, err)
+				continue
+			}
+			if asDec {
+				got.AsDec()
+			}
+
+			expected := item.expect
+			desired.Neg(expected.AsDec())
+
+			if e, a := expect, got; e.Cmp(a) != 0 {
+				t.Errorf("%v: expected %s, got %s", item.input, e.String(), a.String())
+			}
+			if e, a := expected.Format, got.Format; e != a {
+				t.Errorf("%v: expected %#v, got %#v", item.input, e, a)
+			}
+		}
+
+		// Try everything with an explicit +
+		for _, item := range table {
+			got, err := ParseQuantity("+" + strings.TrimLeftFunc(item.input, unicode.IsSpace))
+			if err != nil {
+				t.Errorf("-%v: unexpected error: %v", item.input, err)
+				continue
+			}
+			if asDec {
+				got.AsDec()
+			}
+
+			if e, a := item.expect, got; e.Cmp(a) != 0 {
+				t.Errorf("%v(%t): expected %s, got %s", item.input, asDec, e.String(), a.String())
+			}
+			if e, a := item.expect.Format, got.Format; e != a {
+				t.Errorf("%v: expected %#v, got %#v", item.input, e, a)
+			}
+		}
+	}
+
+	invalid := []string{
+		"1.1.M",
+		"1+1.0M",
+		"0.1mi",
+		"0.1am",
+		"aoeu",
+		".5i",
+		"1i",
+		"-3.01i",
+		"-3.01e-",
+
+		// trailing whitespace is forbidden
+		" 1",
+		"1 ",
+	}
+	for _, item := range invalid {
+		_, err := ParseQuantity(item)
+		if err == nil {
+			t.Errorf("%v parsed unexpectedly", item)
+		}
+	}
+}
+
+func TestQuantityRoundUp(t *testing.T) {
+	table := []struct {
+		in     string
+		scale  Scale
+		expect Quantity
+		ok     bool
+	}{
+		{"9.01", -3, decQuantity(901, -2, DecimalSI), true},
+		{"9.01", -2, decQuantity(901, -2, DecimalSI), true},
+		{"9.01", -1, decQuantity(91, -1, DecimalSI), false},
+		{"9.01", 0, decQuantity(10, 0, DecimalSI), false},
+		{"9.01", 1, decQuantity(10, 0, DecimalSI), false},
+		{"9.01", 2, decQuantity(100, 0, DecimalSI), false},
+
+		{"-9.01", -3, decQuantity(-901, -2, DecimalSI), true},
+		{"-9.01", -2, decQuantity(-901, -2, DecimalSI), true},
+		{"-9.01", -1, decQuantity(-91, -1, DecimalSI), false},
+		{"-9.01", 0, decQuantity(-10, 0, DecimalSI), false},
+		{"-9.01", 1, decQuantity(-10, 0, DecimalSI), false},
+		{"-9.01", 2, decQuantity(-100, 0, DecimalSI), false},
+	}
+
+	for _, asDec := range []bool{false, true} {
+		for _, item := range table {
+			got, err := ParseQuantity(item.in)
+			if err != nil {
+				t.Fatalf("unexpected error: %v", err)
+			}
+			expect := *item.expect.Copy()
+			if asDec {
+				got.AsDec()
+			}
+			if ok := got.RoundUp(item.scale); ok != item.ok {
+				t.Errorf("%s(%d,%t): unexpected ok: %t", item.in, item.scale, asDec, ok)
+			}
+			if got.Cmp(expect) != 0 {
+				t.Errorf("%s(%d,%t): unexpected round: %s vs %s", item.in, item.scale, asDec, got.String(), expect.String())
+			}
+		}
+	}
+}
+
+func TestQuantityCmpInt64AndDec(t *testing.T) {
+	table := []struct {
+		a, b Quantity
+		cmp  int
+	}{
+		{intQuantity(901, -2, DecimalSI), intQuantity(901, -2, DecimalSI), 0},
+		{intQuantity(90, -1, DecimalSI), intQuantity(901, -2, DecimalSI), -1},
+		{intQuantity(901, -2, DecimalSI), intQuantity(900, -2, DecimalSI), 1},
+		{intQuantity(0, 0, DecimalSI), intQuantity(0, 0, DecimalSI), 0},
+		{intQuantity(0, 1, DecimalSI), intQuantity(0, -1, DecimalSI), 0},
+		{intQuantity(0, -1, DecimalSI), intQuantity(0, 1, DecimalSI), 0},
+		{intQuantity(800, -3, DecimalSI), intQuantity(1, 0, DecimalSI), -1},
+		{intQuantity(800, -3, DecimalSI), intQuantity(79, -2, DecimalSI), 1},
+
+		{intQuantity(mostPositive, 0, DecimalSI), intQuantity(1, -1, DecimalSI), 1},
+		{intQuantity(mostPositive, 1, DecimalSI), intQuantity(1, 0, DecimalSI), 1},
+		{intQuantity(mostPositive, 1, DecimalSI), intQuantity(1, 1, DecimalSI), 1},
+		{intQuantity(mostPositive, 1, DecimalSI), intQuantity(0, 1, DecimalSI), 1},
+		{intQuantity(mostPositive, -16, DecimalSI), intQuantity(1, 3, DecimalSI), -1},
+
+		{intQuantity(mostNegative, 0, DecimalSI), intQuantity(0, 0, DecimalSI), -1},
+		{intQuantity(mostNegative, -18, DecimalSI), intQuantity(-1, 0, DecimalSI), -1},
+		{intQuantity(mostNegative, -19, DecimalSI), intQuantity(-1, 0, DecimalSI), 1},
+
+		{intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(1, 1, DecimalSI), 0},
+		{intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(-10, 0, DecimalSI), 1},
+		{intQuantity(-1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(-10, 0, DecimalSI), 0},
+		{intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(1, 0, DecimalSI), 1},
+
+		{intQuantity(1*1000000*1000000*1000000+1, -17, DecimalSI), intQuantity(1, 1, DecimalSI), 1},
+		{intQuantity(1*1000000*1000000*1000000-1, -17, DecimalSI), intQuantity(1, 1, DecimalSI), -1},
+	}
+
+	for _, item := range table {
+		if cmp := item.a.Cmp(item.b); cmp != item.cmp {
+			t.Errorf("%#v: unexpected Cmp: %d", item, cmp)
+		}
+		if cmp := item.b.Cmp(item.a); cmp != -item.cmp {
+			t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp)
+		}
+	}
+
+	for _, item := range table {
+		a, b := *item.a.Copy(), *item.b.Copy()
+		a.AsDec()
+		if cmp := a.Cmp(b); cmp != item.cmp {
+			t.Errorf("%#v: unexpected Cmp: %d", item, cmp)
+		}
+		if cmp := b.Cmp(a); cmp != -item.cmp {
+			t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp)
+		}
+	}
+
+	for _, item := range table {
+		a, b := *item.a.Copy(), *item.b.Copy()
+		b.AsDec()
+		if cmp := a.Cmp(b); cmp != item.cmp {
+			t.Errorf("%#v: unexpected Cmp: %d", item, cmp)
+		}
+		if cmp := b.Cmp(a); cmp != -item.cmp {
+			t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp)
+		}
+	}
+
+	for _, item := range table {
+		a, b := *item.a.Copy(), *item.b.Copy()
+		a.AsDec()
+		b.AsDec()
+		if cmp := a.Cmp(b); cmp != item.cmp {
+			t.Errorf("%#v: unexpected Cmp: %d", item, cmp)
+		}
+		if cmp := b.Cmp(a); cmp != -item.cmp {
+			t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp)
+		}
+	}
+}
+
+func TestQuantityNeg(t *testing.T) {
+	table := []struct {
+		a   Quantity
+		out string
+	}{
+		{intQuantity(901, -2, DecimalSI), "-9010m"},
+		{decQuantity(901, -2, DecimalSI), "-9010m"},
+	}
+
+	for i, item := range table {
+		out := *item.a.Copy()
+		out.Neg()
+		if out.Cmp(item.a) == 0 {
+			t.Errorf("%d: negating an item should not mutate the source: %s", i, out.String())
+		}
+		if out.String() != item.out {
+			t.Errorf("%d: negating did not equal exact value: %s", i, out.String())
+		}
+	}
+}
+
+func TestQuantityString(t *testing.T) {
+	table := []struct {
+		in        Quantity
+		expect    string
+		alternate string
+	}{
+		{decQuantity(1024*1024*1024, 0, BinarySI), "1Gi", "1024Mi"},
+		{decQuantity(300*1024*1024, 0, BinarySI), "300Mi", "307200Ki"},
+		{decQuantity(6*1024, 0, BinarySI), "6Ki", ""},
+		{decQuantity(1001*1024*1024*1024, 0, BinarySI), "1001Gi", "1025024Mi"},
+		{decQuantity(1024*1024*1024*1024, 0, BinarySI), "1Ti", "1024Gi"},
+		{decQuantity(5, 0, BinarySI), "5", "5000m"},
+		{decQuantity(500, -3, BinarySI), "500m", "0.5"},
+		{decQuantity(1, 9, DecimalSI), "1G", "1000M"},
+		{decQuantity(1000, 6, DecimalSI), "1G", "0.001T"},
+		{decQuantity(1000000, 3, DecimalSI), "1G", ""},
+		{decQuantity(1000000000, 0, DecimalSI), "1G", ""},
+		{decQuantity(1, -3, DecimalSI), "1m", "1000u"},
+		{decQuantity(80, -3, DecimalSI), "80m", ""},
+		{decQuantity(1080, -3, DecimalSI), "1080m", "1.08"},
+		{decQuantity(108, -2, DecimalSI), "1080m", "1080000000n"},
+		{decQuantity(10800, -4, DecimalSI), "1080m", ""},
+		{decQuantity(300, 6, DecimalSI), "300M", ""},
+		{decQuantity(1, 12, DecimalSI), "1T", ""},
+		{decQuantity(1234567, 6, DecimalSI), "1234567M", ""},
+		{decQuantity(1234567, -3, BinarySI), "1234567m", ""},
+		{decQuantity(3, 3, DecimalSI), "3k", ""},
+		{decQuantity(1025, 0, BinarySI), "1025", ""},
+		{decQuantity(0, 0, DecimalSI), "0", ""},
+		{decQuantity(0, 0, BinarySI), "0", ""},
+		{decQuantity(1, 9, DecimalExponent), "1e9", ".001e12"},
+		{decQuantity(1, -3, DecimalExponent), "1e-3", "0.001e0"},
+		{decQuantity(1, -9, DecimalExponent), "1e-9", "1000e-12"},
+		{decQuantity(80, -3, DecimalExponent), "80e-3", ""},
+		{decQuantity(300, 6, DecimalExponent), "300e6", ""},
+		{decQuantity(1, 12, DecimalExponent), "1e12", ""},
+		{decQuantity(1, 3, DecimalExponent), "1e3", ""},
+		{decQuantity(3, 3, DecimalExponent), "3e3", ""},
+		{decQuantity(3, 3, DecimalSI), "3k", ""},
+		{decQuantity(0, 0, DecimalExponent), "0", "00"},
+		{decQuantity(1, -9, DecimalSI), "1n", ""},
+		{decQuantity(80, -9, DecimalSI), "80n", ""},
+		{decQuantity(1080, -9, DecimalSI), "1080n", ""},
+		{decQuantity(108, -8, DecimalSI), "1080n", ""},
+		{decQuantity(10800, -10, DecimalSI), "1080n", ""},
+		{decQuantity(1, -6, DecimalSI), "1u", ""},
+		{decQuantity(80, -6, DecimalSI), "80u", ""},
+		{decQuantity(1080, -6, DecimalSI), "1080u", ""},
+	}
+	for _, item := range table {
+		got := item.in.String()
+		if e, a := item.expect, got; e != a {
+			t.Errorf("%#v: expected %v, got %v", item.in, e, a)
+		}
+		q, err := ParseQuantity(item.expect)
+		if err != nil {
+			t.Errorf("%#v: unexpected error: %v", item.expect, err)
+		}
+		if len(q.s) == 0 || q.s != item.expect {
+			t.Errorf("%#v: did not copy canonical string on parse: %s", item.expect, q.s)
+		}
+		if len(item.alternate) == 0 {
+			continue
+		}
+		q, err = ParseQuantity(item.alternate)
+		if err != nil {
+			t.Errorf("%#v: unexpected error: %v", item.expect, err)
+			continue
+		}
+		if len(q.s) != 0 {
+			t.Errorf("%#v: unexpected nested string: %v", item.expect, q.s)
+		}
+		if q.String() != item.expect {
+			t.Errorf("%#v: unexpected alternate canonical: %v", item.expect, q.String())
+		}
+		if len(q.s) == 0 || q.s != item.expect {
+			t.Errorf("%#v: did not set canonical string on ToString: %s", item.expect, q.s)
+		}
+	}
+	desired := &inf.Dec{} // Avoid modifying the values in the table.
+	for _, item := range table {
+		if item.in.Cmp(Quantity{}) == 0 {
+			// Don't expect it to print "-0" ever
+			continue
+		}
+		q := item.in
+		q.d = infDecAmount{desired.Neg(q.AsDec())}
+		if e, a := "-"+item.expect, q.String(); e != a {
+			t.Errorf("%#v: expected %v, got %v", item.in, e, a)
+		}
+	}
+}
+
+func TestQuantityParseEmit(t *testing.T) {
+	table := []struct {
+		in     string
+		expect string
+	}{
+		{"1Ki", "1Ki"},
+		{"1Mi", "1Mi"},
+		{"1Gi", "1Gi"},
+		{"1024Mi", "1Gi"},
+		{"1000M", "1G"},
+		{".001Ki", "1024m"},
+		{".000001Ki", "1024u"},
+		{".000000001Ki", "1024n"},
+		{".000000000001Ki", "2n"},
+	}
+
+	for _, item := range table {
+		q, err := ParseQuantity(item.in)
+		if err != nil {
+			t.Errorf("Couldn't parse %v", item.in)
+			continue
+		}
+		if e, a := item.expect, q.String(); e != a {
+			t.Errorf("%#v: expected %v, got %v", item.in, e, a)
+		}
+	}
+	for _, item := range table {
+		q, err := ParseQuantity("-" + item.in)
+		if err != nil {
+			t.Errorf("Couldn't parse %v", item.in)
+			continue
+		}
+		if q.Cmp(Quantity{}) == 0 {
+			continue
+		}
+		if e, a := "-"+item.expect, q.String(); e != a {
+			t.Errorf("%#v: expected %v, got %v (%#v)", item.in, e, a, q.i)
+		}
+	}
+}
+
+var fuzzer = fuzz.New().Funcs(
+	func(q *Quantity, c fuzz.Continue) {
+		q.i = Zero
+		if c.RandBool() {
+			q.Format = BinarySI
+			if c.RandBool() {
+				dec := &inf.Dec{}
+				q.d = infDecAmount{Dec: dec}
+				dec.SetScale(0)
+				dec.SetUnscaled(c.Int63())
+				return
+			}
+			// Be sure to test cases like 1Mi
+			dec := &inf.Dec{}
+			q.d = infDecAmount{Dec: dec}
+			dec.SetScale(0)
+			dec.SetUnscaled(c.Int63n(1024) << uint(10*c.Intn(5)))
+			return
+		}
+		if c.RandBool() {
+			q.Format = DecimalSI
+		} else {
+			q.Format = DecimalExponent
+		}
+		if c.RandBool() {
+			dec := &inf.Dec{}
+			q.d = infDecAmount{Dec: dec}
+			dec.SetScale(inf.Scale(c.Intn(4)))
+			dec.SetUnscaled(c.Int63())
+			return
+		}
+		// Be sure to test cases like 1M
+		dec := &inf.Dec{}
+		q.d = infDecAmount{Dec: dec}
+		dec.SetScale(inf.Scale(3 - c.Intn(15)))
+		dec.SetUnscaled(c.Int63n(1000))
+	},
+)
+
+func TestJSON(t *testing.T) {
+	for i := 0; i < 500; i++ {
+		q := &Quantity{}
+		fuzzer.Fuzz(q)
+		b, err := json.Marshal(q)
+		if err != nil {
+			t.Errorf("error encoding %v: %v", q, err)
+			continue
+		}
+		q2 := &Quantity{}
+		err = json.Unmarshal(b, q2)
+		if err != nil {
+			t.Logf("%d: %s", i, string(b))
+			t.Errorf("%v: error decoding %v: %v", q, string(b), err)
+		}
+		if q2.Cmp(*q) != 0 {
+			t.Errorf("Expected equal: %v, %v (json was '%v')", q, q2, string(b))
+		}
+	}
+}
+
+func TestJSONWhitespace(t *testing.T) {
+	q := Quantity{}
+	testCases := []struct {
+		in     string
+		expect string
+	}{
+		{`" 1"`, "1"},
+		{`"1 "`, "1"},
+		{`1`, "1"},
+		{` 1`, "1"},
+		{`1 `, "1"},
+		{`10`, "10"},
+		{`-1`, "-1"},
+		{` -1`, "-1"},
+	}
+	for _, test := range testCases {
+		if err := json.Unmarshal([]byte(test.in), &q); err != nil {
+			t.Errorf("%q: %v", test.in, err)
+		}
+		if q.String() != test.expect {
+			t.Errorf("unexpected string: %q", q.String())
+		}
+	}
+}
+
+func TestMilliNewSet(t *testing.T) {
+	table := []struct {
+		value  int64
+		format Format
+		expect string
+		exact  bool
+	}{
+		{1, DecimalSI, "1m", true},
+		{1000, DecimalSI, "1", true},
+		{1234000, DecimalSI, "1234", true},
+		{1024, BinarySI, "1024m", false}, // Format changes
+		{1000000, "invalidFormatDefaultsToExponent", "1e3", true},
+		{1024 * 1024, BinarySI, "1048576m", false}, // Format changes
+	}
+
+	for _, item := range table {
+		q := NewMilliQuantity(item.value, item.format)
+		if e, a := item.expect, q.String(); e != a {
+			t.Errorf("Expected %v, got %v; %#v", e, a, q)
+		}
+		if !item.exact {
+			continue
+		}
+		q2, err := ParseQuantity(q.String())
+		if err != nil {
+			t.Errorf("Round trip failed on %v", q)
+		}
+		if e, a := item.value, q2.MilliValue(); e != a {
+			t.Errorf("Expected %v, got %v", e, a)
+		}
+	}
+
+	for _, item := range table {
+		q := NewQuantity(0, item.format)
+		q.SetMilli(item.value)
+		if e, a := item.expect, q.String(); e != a {
+			t.Errorf("Set: Expected %v, got %v; %#v", e, a, q)
+		}
+	}
+}
+
+func TestNewSet(t *testing.T) {
+	table := []struct {
+		value  int64
+		format Format
+		expect string
+	}{
+		{1, DecimalSI, "1"},
+		{1000, DecimalSI, "1k"},
+		{1234000, DecimalSI, "1234k"},
+		{1024, BinarySI, "1Ki"},
+		{1000000, "invalidFormatDefaultsToExponent", "1e6"},
+		{1024 * 1024, BinarySI, "1Mi"},
+	}
+
+	for _, asDec := range []bool{false, true} {
+		for _, item := range table {
+			q := NewQuantity(item.value, item.format)
+			if asDec {
+				q.ToDec()
+			}
+			if e, a := item.expect, q.String(); e != a {
+				t.Errorf("Expected %v, got %v; %#v", e, a, q)
+			}
+			q2, err := ParseQuantity(q.String())
+			if err != nil {
+				t.Errorf("Round trip failed on %v", q)
+			}
+			if e, a := item.value, q2.Value(); e != a {
+				t.Errorf("Expected %v, got %v", e, a)
+			}
+		}
+
+		for _, item := range table {
+			q := NewQuantity(0, item.format)
+			q.Set(item.value)
+			if asDec {
+				q.ToDec()
+			}
+			if e, a := item.expect, q.String(); e != a {
+				t.Errorf("Set: Expected %v, got %v; %#v", e, a, q)
+			}
+		}
+	}
+}
+
+func TestNewScaledSet(t *testing.T) {
+	table := []struct {
+		value  int64
+		scale  Scale
+		expect string
+	}{
+		{1, Nano, "1n"},
+		{1000, Nano, "1u"},
+		{1, Micro, "1u"},
+		{1000, Micro, "1m"},
+		{1, Milli, "1m"},
+		{1000, Milli, "1"},
+		{1, 0, "1"},
+		{0, Nano, "0"},
+		{0, Micro, "0"},
+		{0, Milli, "0"},
+		{0, 0, "0"},
+	}
+
+	for _, item := range table {
+		q := NewScaledQuantity(item.value, item.scale)
+		if e, a := item.expect, q.String(); e != a {
+			t.Errorf("Expected %v, got %v; %#v", e, a, q)
+		}
+		q2, err := ParseQuantity(q.String())
+		if err != nil {
+			t.Errorf("Round trip failed on %v", q)
+		}
+		if e, a := item.value, q2.ScaledValue(item.scale); e != a {
+			t.Errorf("Expected %v, got %v", e, a)
+		}
+		q3 := NewQuantity(0, DecimalSI)
+		q3.SetScaled(item.value, item.scale)
+		if q.Cmp(*q3) != 0 {
+			t.Errorf("Expected %v and %v to be equal", q, q3)
+		}
+	}
+}
+
+func TestScaledValue(t *testing.T) {
+	table := []struct {
+		fromScale Scale
+		toScale   Scale
+		expected  int64
+	}{
+		{Nano, Nano, 1},
+		{Nano, Micro, 1},
+		{Nano, Milli, 1},
+		{Nano, 0, 1},
+		{Micro, Nano, 1000},
+		{Micro, Micro, 1},
+		{Micro, Milli, 1},
+		{Micro, 0, 1},
+		{Milli, Nano, 1000 * 1000},
+		{Milli, Micro, 1000},
+		{Milli, Milli, 1},
+		{Milli, 0, 1},
+		{0, Nano, 1000 * 1000 * 1000},
+		{0, Micro, 1000 * 1000},
+		{0, Milli, 1000},
+		{0, 0, 1},
+	}
+
+	for _, item := range table {
+		q := NewScaledQuantity(1, item.fromScale)
+		if e, a := item.expected, q.ScaledValue(item.toScale); e != a {
+			t.Errorf("%v to %v: Expected %v, got %v", item.fromScale, item.toScale, e, a)
+		}
+	}
+}
+
+func TestUninitializedNoCrash(t *testing.T) {
+	var q Quantity
+
+	q.Value()
+	q.MilliValue()
+	q.Copy()
+	_ = q.String()
+	q.MarshalJSON()
+}
+
+func TestCopy(t *testing.T) {
+	q := NewQuantity(5, DecimalSI)
+	c := q.Copy()
+	c.Set(6)
+	if q.Value() == 6 {
+		t.Errorf("Copy didn't")
+	}
+}
+
+func TestQFlagSet(t *testing.T) {
+	qf := qFlag{&Quantity{}}
+	qf.Set("1Ki")
+	if e, a := "1Ki", qf.String(); e != a {
+		t.Errorf("Unexpected result %v != %v", e, a)
+	}
+}
+
+func TestQFlagIsPFlag(t *testing.T) {
+	var pfv pflag.Value = qFlag{}
+	if e, a := "quantity", pfv.Type(); e != a {
+		t.Errorf("Unexpected result %v != %v", e, a)
+	}
+}
+
+func TestSub(t *testing.T) {
+	tests := []struct {
+		a        Quantity
+		b        Quantity
+		expected Quantity
+	}{
+		{decQuantity(10, 0, DecimalSI), decQuantity(1, 1, DecimalSI), decQuantity(0, 0, DecimalSI)},
+		{decQuantity(10, 0, DecimalSI), decQuantity(1, 0, BinarySI), decQuantity(9, 0, DecimalSI)},
+		{decQuantity(10, 0, BinarySI), decQuantity(1, 0, DecimalSI), decQuantity(9, 0, BinarySI)},
+		{Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI), decQuantity(-50, 0, DecimalSI)},
+		{decQuantity(50, 0, DecimalSI), Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI)},
+		{Quantity{Format: DecimalSI}, Quantity{Format: DecimalSI}, decQuantity(0, 0, DecimalSI)},
+	}
+
+	for i, test := range tests {
+		test.a.Sub(test.b)
+		if test.a.Cmp(test.expected) != 0 {
+			t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String())
+		}
+	}
+}
+
+func TestNeg(t *testing.T) {
+	tests := []struct {
+		a        Quantity
+		b        Quantity
+		expected Quantity
+	}{
+		{a: intQuantity(0, 0, DecimalSI), expected: intQuantity(0, 0, DecimalSI)},
+		{a: Quantity{}, expected: Quantity{}},
+		{a: intQuantity(10, 0, BinarySI), expected: intQuantity(-10, 0, BinarySI)},
+		{a: intQuantity(-10, 0, BinarySI), expected: intQuantity(10, 0, BinarySI)},
+		{a: decQuantity(0, 0, DecimalSI), expected: intQuantity(0, 0, DecimalSI)},
+		{a: decQuantity(10, 0, BinarySI), expected: intQuantity(-10, 0, BinarySI)},
+		{a: decQuantity(-10, 0, BinarySI), expected: intQuantity(10, 0, BinarySI)},
+	}
+
+	for i, test := range tests {
+		a := test.a.Copy()
+		a.Neg()
+		// ensure value is same
+		if a.Cmp(test.expected) != 0 {
+			t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), a.String())
+		}
+	}
+}
+
+func TestAdd(t *testing.T) {
+	tests := []struct {
+		a        Quantity
+		b        Quantity
+		expected Quantity
+	}{
+		{decQuantity(10, 0, DecimalSI), decQuantity(1, 1, DecimalSI), decQuantity(20, 0, DecimalSI)},
+		{decQuantity(10, 0, DecimalSI), decQuantity(1, 0, BinarySI), decQuantity(11, 0, DecimalSI)},
+		{decQuantity(10, 0, BinarySI), decQuantity(1, 0, DecimalSI), decQuantity(11, 0, BinarySI)},
+		{Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI), decQuantity(50, 0, DecimalSI)},
+		{decQuantity(50, 0, DecimalSI), Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI)},
+		{Quantity{Format: DecimalSI}, Quantity{Format: DecimalSI}, decQuantity(0, 0, DecimalSI)},
+	}
+
+	for i, test := range tests {
+		test.a.Add(test.b)
+		if test.a.Cmp(test.expected) != 0 {
+			t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String())
+		}
+	}
+}
+
+func TestAddSubRoundTrip(t *testing.T) {
+	for k := -10; k <= 10; k++ {
+		q := Quantity{Format: DecimalSI}
+		var order []int64
+		for i := 0; i < 100; i++ {
+			j := rand.Int63()
+			order = append(order, j)
+			q.Add(*NewScaledQuantity(j, Scale(k)))
+		}
+		for _, j := range order {
+			q.Sub(*NewScaledQuantity(j, Scale(k)))
+		}
+		if !q.IsZero() {
+			t.Errorf("addition and subtraction did not cancel: %s", &q)
+		}
+	}
+}
+
+func TestAddSubRoundTripAcrossScales(t *testing.T) {
+	q := Quantity{Format: DecimalSI}
+	var order []int64
+	for i := 0; i < 100; i++ {
+		j := rand.Int63()
+		order = append(order, j)
+		q.Add(*NewScaledQuantity(j, Scale(j%20-10)))
+	}
+	for _, j := range order {
+		q.Sub(*NewScaledQuantity(j, Scale(j%20-10)))
+	}
+	if !q.IsZero() {
+		t.Errorf("addition and subtraction did not cancel: %s", &q)
+	}
+}
+
+func TestNegateRoundTrip(t *testing.T) {
+	for _, asDec := range []bool{false, true} {
+		for k := -10; k <= 10; k++ {
+			for i := 0; i < 100; i++ {
+				j := rand.Int63()
+				q := *NewScaledQuantity(j, Scale(k))
+				if asDec {
+					q.AsDec()
+				}
+
+				b := q.Copy()
+				b.Neg()
+				b.Neg()
+				if b.Cmp(q) != 0 {
+					t.Errorf("double negation did not cancel: %s", &q)
+				}
+			}
+		}
+	}
+}
+func benchmarkQuantities() []Quantity {
+	return []Quantity{
+		intQuantity(1024*1024*1024, 0, BinarySI),
+		intQuantity(1024*1024*1024*1024, 0, BinarySI),
+		intQuantity(1000000, 3, DecimalSI),
+		intQuantity(1000000000, 0, DecimalSI),
+		intQuantity(1, -3, DecimalSI),
+		intQuantity(80, -3, DecimalSI),
+		intQuantity(1080, -3, DecimalSI),
+		intQuantity(0, 0, BinarySI),
+		intQuantity(1, 9, DecimalExponent),
+		intQuantity(1, -9, DecimalSI),
+		intQuantity(1000000, 10, DecimalSI),
+	}
+}
+
+func BenchmarkQuantityString(b *testing.B) {
+	values := benchmarkQuantities()
+	b.ResetTimer()
+	var s string
+	for i := 0; i < b.N; i++ {
+		q := values[i%len(values)]
+		q.s = ""
+		s = q.String()
+	}
+	b.StopTimer()
+	if len(s) == 0 {
+		b.Fatal(s)
+	}
+}
+
+func BenchmarkQuantityStringPrecalc(b *testing.B) {
+	values := benchmarkQuantities()
+	for i := range values {
+		_ = values[i].String()
+	}
+	b.ResetTimer()
+	var s string
+	for i := 0; i < b.N; i++ {
+		q := values[i%len(values)]
+		s = q.String()
+	}
+	b.StopTimer()
+	if len(s) == 0 {
+		b.Fatal(s)
+	}
+}
+
+func BenchmarkQuantityStringBinarySI(b *testing.B) {
+	values := benchmarkQuantities()
+	for i := range values {
+		values[i].Format = BinarySI
+	}
+	b.ResetTimer()
+	var s string
+	for i := 0; i < b.N; i++ {
+		q := values[i%len(values)]
+		q.s = ""
+		s = q.String()
+	}
+	b.StopTimer()
+	if len(s) == 0 {
+		b.Fatal(s)
+	}
+}
+
+func BenchmarkQuantityMarshalJSON(b *testing.B) {
+	values := benchmarkQuantities()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		q := values[i%len(values)]
+		q.s = ""
+		if _, err := q.MarshalJSON(); err != nil {
+			b.Fatal(err)
+		}
+	}
+	b.StopTimer()
+}
+
+func BenchmarkQuantityUnmarshalJSON(b *testing.B) {
+	values := benchmarkQuantities()
+	var json [][]byte
+	for _, v := range values {
+		data, _ := v.MarshalJSON()
+		json = append(json, data)
+	}
+
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		var q Quantity
+		if err := q.UnmarshalJSON(json[i%len(values)]); err != nil {
+			b.Fatal(err)
+		}
+	}
+	b.StopTimer()
+}
+
+func BenchmarkParseQuantity(b *testing.B) {
+	values := benchmarkQuantities()
+	var strings []string
+	for _, v := range values {
+		strings = append(strings, v.String())
+	}
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		if _, err := ParseQuantity(strings[i%len(values)]); err != nil {
+			b.Fatal(err)
+		}
+	}
+	b.StopTimer()
+}
+
+func BenchmarkCanonicalize(b *testing.B) {
+	values := benchmarkQuantities()
+	b.ResetTimer()
+	buffer := make([]byte, 0, 100)
+	for i := 0; i < b.N; i++ {
+		s, _ := values[i%len(values)].CanonicalizeBytes(buffer)
+		if len(s) == 0 {
+			b.Fatal(s)
+		}
+	}
+	b.StopTimer()
+}
+
+func BenchmarkQuantityRoundUp(b *testing.B) {
+	values := benchmarkQuantities()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		q := values[i%len(values)]
+		copied := q
+		copied.RoundUp(-3)
+	}
+	b.StopTimer()
+}
+
+func BenchmarkQuantityCopy(b *testing.B) {
+	values := benchmarkQuantities()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		values[i%len(values)].Copy()
+	}
+	b.StopTimer()
+}
+
+func BenchmarkQuantityAdd(b *testing.B) {
+	values := benchmarkQuantities()
+	base := &Quantity{}
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		q := values[i%len(values)]
+		base.d.Dec = nil
+		base.i = int64Amount{value: 100}
+		base.Add(q)
+	}
+	b.StopTimer()
+}
+
+func BenchmarkQuantityCmp(b *testing.B) {
+	values := benchmarkQuantities()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		q := values[i%len(values)]
+		if q.Cmp(q) != 0 {
+			b.Fatal(q)
+		}
+	}
+	b.StopTimer()
+}
diff --git a/schema/types/resource/scale_int_test.go b/schema/types/resource/scale_int_test.go
new file mode 100644
index 0000000..1b4390e
--- /dev/null
+++ b/schema/types/resource/scale_int_test.go
@@ -0,0 +1,85 @@
+/*
+Copyright 2015 The Kubernetes Authors All rights reserved.
+
+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 resource
+
+import (
+	"math"
+	"math/big"
+	"testing"
+)
+
+func TestScaledValueInternal(t *testing.T) {
+	tests := []struct {
+		unscaled *big.Int
+		scale    int
+		newScale int
+
+		want int64
+	}{
+		// remain scale
+		{big.NewInt(1000), 0, 0, 1000},
+
+		// scale down
+		{big.NewInt(1000), 0, -3, 1},
+		{big.NewInt(1000), 3, 0, 1},
+		{big.NewInt(0), 3, 0, 0},
+
+		// always round up
+		{big.NewInt(999), 3, 0, 1},
+		{big.NewInt(500), 3, 0, 1},
+		{big.NewInt(499), 3, 0, 1},
+		{big.NewInt(1), 3, 0, 1},
+		// large scaled value does not lose precision
+		{big.NewInt(0).Sub(maxInt64, bigOne), 1, 0, (math.MaxInt64-1)/10 + 1},
+		// large intermidiate result.
+		{big.NewInt(1).Exp(big.NewInt(10), big.NewInt(100), nil), 100, 0, 1},
+
+		// scale up
+		{big.NewInt(0), 0, 3, 0},
+		{big.NewInt(1), 0, 3, 1000},
+		{big.NewInt(1), -3, 0, 1000},
+		{big.NewInt(1000), -3, 2, 100000000},
+		{big.NewInt(0).Div(big.NewInt(math.MaxInt64), bigThousand), 0, 3,
+			(math.MaxInt64 / 1000) * 1000},
+	}
+
+	for i, tt := range tests {
+		old := (&big.Int{}).Set(tt.unscaled)
+		got := scaledValue(tt.unscaled, tt.scale, tt.newScale)
+		if got != tt.want {
+			t.Errorf("#%d: got = %v, want %v", i, got, tt.want)
+		}
+		if tt.unscaled.Cmp(old) != 0 {
+			t.Errorf("#%d: unscaled = %v, want %v", i, tt.unscaled, old)
+		}
+	}
+}
+
+func BenchmarkScaledValueSmall(b *testing.B) {
+	s := big.NewInt(1000)
+	for i := 0; i < b.N; i++ {
+		scaledValue(s, 3, 0)
+	}
+}
+
+func BenchmarkScaledValueLarge(b *testing.B) {
+	s := big.NewInt(math.MaxInt64)
+	s.Mul(s, big.NewInt(1000))
+	for i := 0; i < b.N; i++ {
+		scaledValue(s, 10, 0)
+	}
+}