Merge pull request #1160 from bubski/cg-api

diff --git a/Foundation/NSGeometry.swift b/Foundation/NSGeometry.swift
index 80c8d25..3420df6 100644
--- a/Foundation/NSGeometry.swift
+++ b/Foundation/NSGeometry.swift
@@ -237,11 +237,173 @@
                                         y: -CGFloat.greatestFiniteMagnitude / 2,
                                         width: CGFloat.greatestFiniteMagnitude,
                                         height: CGFloat.greatestFiniteMagnitude)
+
+    public var width: CGFloat { return abs(self.size.width) }
+    public var height: CGFloat { return abs(self.size.height) }
+
+    public var minX: CGFloat { return self.origin.x + min(self.size.width, 0) }
+    public var midX: CGFloat { return (self.minX + self.maxX) * 0.5 }
+    public var maxX: CGFloat { return self.origin.x + max(self.size.width, 0) }
+
+    public var minY: CGFloat { return self.origin.y + min(self.size.height, 0) }
+    public var midY: CGFloat { return (self.minY + self.maxY) * 0.5 }
+    public var maxY: CGFloat { return self.origin.y + max(self.size.height, 0) }
+
+    public var isEmpty: Bool { return self.isNull || self.size.width == 0 || self.size.height == 0 }
+    public var isInfinite: Bool { return self == .infinite }
+    public var isNull: Bool { return self.origin.x == .infinity || self.origin.y == .infinity }
+
+    public func contains(_ point: CGPoint) -> Bool {
+        if self.isNull || self.isEmpty { return false }
+
+        return (self.minX..<self.maxX).contains(point.x) && (self.minY..<self.maxY).contains(point.y)
+    }
+
+    public func contains(_ rect2: CGRect) -> Bool {
+        return self.union(rect2) == self
+    }
+
+    public var standardized: CGRect {
+        if self.isNull { return .null }
+
+        return CGRect(x: self.minX,
+                      y: self.minY,
+                      width: self.width,
+                      height: self.height)
+    }
+
+    public var integral: CGRect {
+        if self.isNull { return self }
+
+        let standardized = self.standardized
+        let x = standardized.origin.x.rounded(.down)
+        let y = standardized.origin.y.rounded(.down)
+        let width = (standardized.origin.x + standardized.size.width).rounded(.up) - x
+        let height = (standardized.origin.y + standardized.size.height).rounded(.up) - y
+        return CGRect(x: x, y: y, width: width, height: height)
+    }
+
+    public func insetBy(dx: CGFloat, dy: CGFloat) -> CGRect {
+        if self.isNull { return self }
+
+        var rect = self.standardized
+
+        rect.origin.x += dx
+        rect.origin.y += dy
+        rect.size.width -= 2 * dx
+        rect.size.height -= 2 * dy
+
+        if rect.size.width < 0 || rect.size.height < 0 {
+            return .null
+        }
+
+        return rect
+    }
+
+    public func union(_ r2: CGRect) -> CGRect {
+        if self.isNull {
+            return r2
+        }
+        else if r2.isNull {
+            return self
+        }
+
+        let rect1 = self.standardized
+        let rect2 = r2.standardized
+
+        let minX = min(rect1.minX, rect2.minX)
+        let minY = min(rect1.minY, rect2.minY)
+        let maxX = max(rect1.maxX, rect2.maxX)
+        let maxY = max(rect1.maxY, rect2.maxY)
+
+        return CGRect(x: minX,
+                      y: minY,
+                      width: maxX - minX,
+                      height: maxY - minY)
+    }
+
+    public func intersection(_ r2: CGRect) -> CGRect {
+        if self.isNull || r2.isNull { return .null }
+
+        let rect1 = self.standardized
+        let rect2 = r2.standardized
+
+        let rect1SpanH = rect1.minX...rect1.maxX
+        let rect1SpanV = rect1.minY...rect1.maxY
+
+        let rect2SpanH = rect2.minX...rect2.maxX
+        let rect2SpanV = rect2.minY...rect2.maxY
+
+        if !rect1SpanH.overlaps(rect2SpanH) || !rect1SpanV.overlaps(rect2SpanV) {
+            return .null
+        }
+
+        let overlapH = rect1SpanH.clamped(to: rect2SpanH)
+        let overlapV = rect1SpanV.clamped(to: rect2SpanV)
+
+        return CGRect(x: overlapH.lowerBound,
+                      y: overlapV.lowerBound,
+                      width: overlapH.upperBound - overlapH.lowerBound,
+                      height: overlapV.upperBound - overlapV.lowerBound)
+    }
+
+    public func intersects(_ r2: CGRect) -> Bool {
+        return !self.intersection(r2).isNull
+    }
+
+    public func offsetBy(dx: CGFloat, dy: CGFloat) -> CGRect {
+        if self.isNull { return self }
+
+        var rect = self.standardized
+        rect.origin.x += dx
+        rect.origin.y += dy
+        return rect
+    }
+
+    public func divided(atDistance: CGFloat, from fromEdge: CGRectEdge) -> (slice: CGRect, remainder: CGRect) {
+        if self.isNull { return (.null, .null) }
+
+        let splitLocation: CGFloat
+        switch fromEdge {
+        case .minXEdge: splitLocation = min(max(atDistance, 0), self.width)
+        case .maxXEdge: splitLocation = min(max(self.width - atDistance, 0), self.width)
+        case .minYEdge: splitLocation = min(max(atDistance, 0), self.height)
+        case .maxYEdge: splitLocation = min(max(self.height - atDistance, 0), self.height)
+        }
+
+        let rect = self.standardized
+        var rect1 = rect
+        var rect2 = rect
+
+        switch fromEdge {
+        case .minXEdge: fallthrough
+        case .maxXEdge:
+            rect1.size.width = splitLocation
+            rect2.origin.x = rect1.maxX
+            rect2.size.width = rect.width - splitLocation
+        case .minYEdge: fallthrough
+        case .maxYEdge:
+            rect1.size.height = splitLocation
+            rect2.origin.y = rect1.maxY
+            rect2.size.height = rect.height - splitLocation
+        }
+
+        switch fromEdge {
+        case .minXEdge: fallthrough
+        case .minYEdge: return (rect1, rect2)
+        case .maxXEdge: fallthrough
+        case .maxYEdge: return (rect2, rect1)
+        }
+    }
 }
 
 extension CGRect: Equatable {
     public static func ==(lhs: CGRect, rhs: CGRect) -> Bool {
-        return lhs.origin == rhs.origin && lhs.size == rhs.size
+        if lhs.isNull && rhs.isNull { return true }
+
+        let r1 = lhs.standardized
+        let r2 = rhs.standardized
+        return r1.origin == r2.origin && r1.size == r2.size
     }
 }
 
diff --git a/TestFoundation/TestNSGeometry.swift b/TestFoundation/TestNSGeometry.swift
index 953bdd8..3c662f9 100644
--- a/TestFoundation/TestNSGeometry.swift
+++ b/TestFoundation/TestNSGeometry.swift
@@ -16,6 +16,28 @@
     import SwiftXCTest
 #endif
 
+private func assertEqual(_ rect: CGRect,
+                         x: CGFloat,
+                         y: CGFloat,
+                         width: CGFloat,
+                         height: CGFloat,
+                         accuracy: CGFloat? = nil,
+                         _ message: @autoclosure () -> String = "",
+                         file: StaticString = #file,
+                         line: UInt = #line) {
+
+    if let accuracy = accuracy {
+        XCTAssertEqual(rect.origin.x, x, accuracy: accuracy, message, file: file, line: line)
+        XCTAssertEqual(rect.origin.y, y, accuracy: accuracy, message, file: file, line: line)
+        XCTAssertEqual(rect.size.width, width, accuracy: accuracy, message, file: file, line: line)
+        XCTAssertEqual(rect.size.height, height, accuracy: accuracy, message, file: file, line: line)
+    } else {
+        XCTAssertEqual(rect.origin.x, x, message, file: file, line: line)
+        XCTAssertEqual(rect.origin.y, y, message, file: file, line: line)
+        XCTAssertEqual(rect.size.width, width, message, file: file, line: line)
+        XCTAssertEqual(rect.size.height, height, message, file: file, line: line)
+    }
+}
 
 class TestNSGeometry : XCTestCase {
 
@@ -32,6 +54,21 @@
             ("test_CGRect_BasicConstruction", test_CGRect_BasicConstruction),
             ("test_CGRect_ExtendedConstruction", test_CGRect_ExtendedConstruction),
             ("test_CGRect_SpecialValues", test_CGRect_SpecialValues),
+            ("test_CGRect_IsNull", test_CGRect_IsNull),
+            ("test_CGRect_IsInfinite", test_CGRect_IsInfinite),
+            ("test_CGRect_IsEmpty", test_CGRect_IsEmpty),
+            ("test_CGRect_Equatable", test_CGRect_Equatable),
+            ("test_CGRect_CalculatedGeometricProperties", test_CGRect_CalculatedGeometricProperties),
+            ("test_CGRect_Standardized", test_CGRect_Standardized),
+            ("test_CGRect_Integral", test_CGRect_Integral),
+            ("test_CGRect_ContainsPoint", test_CGRect_ContainsPoint),
+            ("test_CGRect_ContainsRect", test_CGRect_ContainsRect),
+            ("test_CGRect_Union", test_CGRect_Union),
+            ("test_CGRect_Intersection", test_CGRect_Intersection),
+            ("test_CGRect_Intersects", test_CGRect_Intersects),
+            ("test_CGRect_OffsetBy", test_CGRect_OffsetBy),
+            ("test_CGRect_Divide", test_CGRect_Divide),
+            ("test_CGRect_InsetBy", test_CGRect_InsetBy),
             ("test_NSEdgeInsets_BasicConstruction", test_NSEdgeInsets_BasicConstruction),
             ("test_NSEdgeInsetsEqual", test_NSEdgeInsetsEqual),
             ("test_NSMakePoint", test_NSMakePoint),
@@ -195,6 +232,674 @@
         XCTAssertEqual(r4.size.width, CGFloat(3))
         XCTAssertEqual(r4.size.height, CGFloat(4))
     }
+
+    func test_CGRect_IsNull() {
+        XCTAssertTrue(CGRect.null.isNull)
+        XCTAssertTrue(CGRect(x: CGFloat.infinity, y: CGFloat.infinity, width: 0, height: 0).isNull)
+        XCTAssertTrue(CGRect(x: CGFloat.infinity, y: CGFloat.infinity, width: 10, height: 10).isNull)
+        XCTAssertTrue(CGRect(x: CGFloat.infinity, y: CGFloat.infinity, width: -10, height: -10).isNull)
+        XCTAssertTrue(CGRect(x: CGFloat.infinity, y: 0, width: 0, height: 0).isNull)
+        XCTAssertTrue(CGRect(x: 0, y: CGFloat.infinity, width: 0, height: 0).isNull)
+        XCTAssertFalse(CGRect(x: 0, y: 0, width: 0, height: 0).isNull)
+    }
+
+    func test_CGRect_IsInfinite() {
+        XCTAssertTrue(CGRect.infinite.isInfinite)
+
+        XCTAssertFalse(CGRect(x: 0,
+                              y: CGRect.infinite.origin.y,
+                              width: CGRect.infinite.size.width,
+                              height: CGRect.infinite.size.height).isInfinite)
+
+        XCTAssertFalse(CGRect(x: CGRect.infinite.origin.x,
+                              y: 0,
+                              width: CGRect.infinite.size.width,
+                              height: CGRect.infinite.size.height).isInfinite)
+
+        XCTAssertFalse(CGRect(x: CGRect.infinite.origin.x,
+                              y: CGRect.infinite.origin.y,
+                              width: 0,
+                              height: CGRect.infinite.size.height).isInfinite)
+
+        XCTAssertFalse(CGRect(x: CGRect.infinite.origin.x,
+                              y: CGRect.infinite.origin.y,
+                              width: CGRect.infinite.size.width,
+                              height: 0).isInfinite)
+
+        XCTAssertFalse(CGRect(x: CGFloat.infinity,
+                              y: CGFloat.infinity,
+                              width: CGFloat.infinity,
+                              height: CGFloat.infinity).isInfinite)
+
+        XCTAssertFalse(CGRect.null.isInfinite)
+    }
+
+    func test_CGRect_IsEmpty() {
+        XCTAssertTrue(CGRect.zero.isEmpty)
+        XCTAssertTrue(CGRect.null.isEmpty)
+        XCTAssertTrue(CGRect(x: 10, y: 20, width: 30, height: 0).isEmpty)
+        XCTAssertTrue(CGRect(x: 10, y: 20, width: 0, height: 30).isEmpty)
+        XCTAssertTrue(CGRect(x: 10, y: 20, width: -30, height: 0).isEmpty)
+        XCTAssertTrue(CGRect(x: 10, y: 20, width: 0, height: -30).isEmpty)
+
+        var r1 = CGRect.null
+        r1.origin.x = 0
+        XCTAssertTrue(r1.isEmpty)
+
+        var r2 = CGRect.null
+        r2.origin.y = 0
+        XCTAssertTrue(r2.isEmpty)
+
+        var r3 = CGRect.null
+        r3.size.width = 20
+        XCTAssertTrue(r3.isEmpty)
+
+        var r4 = CGRect.null
+        r4.size.height = 20
+        XCTAssertTrue(r4.isEmpty)
+
+        var r5 = CGRect.null
+        r5.size.width = 20
+        r5.size.height = 20
+        XCTAssertTrue(r5.isEmpty)
+
+        XCTAssertFalse(CGRect.infinite.isEmpty)
+        XCTAssertFalse(CGRect.infinite.isEmpty)
+    }
+
+    func test_CGRect_Equatable() {
+        XCTAssertEqual(CGRect(x: 10, y: 20, width: 30, height: 40), CGRect(x: 10, y: 20, width: 30, height: 40))
+        XCTAssertEqual(CGRect(x: -10, y: -20, width: -30, height: -40), CGRect(x: -10, y: -20, width: -30, height: -40))
+        XCTAssertEqual(CGRect(x: -10, y: -20, width: 30, height: 40), CGRect(x: 20, y: 20, width: -30, height: -40))
+
+        XCTAssertNotEqual(CGRect(x: 10, y: 20, width: 30, height: 40), CGRect(x: 10, y: 20, width: 30, height: -40))
+        XCTAssertNotEqual(CGRect(x: 10, y: 20, width: 30, height: 40), CGRect(x: 10, y: 20, width: -30, height: 40))
+        XCTAssertNotEqual(CGRect(x: 10, y: 20, width: 30, height: 40), CGRect(x: 10, y: -20, width: 30, height: 40))
+        XCTAssertNotEqual(CGRect(x: 10, y: 20, width: 30, height: 40), CGRect(x: -10, y: 20, width: 30, height: 40))
+
+        XCTAssertEqual(CGRect.infinite, CGRect.infinite)
+        XCTAssertEqual(CGRect.null, CGRect.null)
+        XCTAssertNotEqual(CGRect.infinite, CGRect.null)
+
+        var r1 = CGRect.null
+        r1.size = CGSize(width: 20, height: 20)
+        XCTAssertEqual(r1, CGRect.null)
+
+        var r2 = CGRect.null
+        r2.origin.x = 20
+        XCTAssertEqual(r2, CGRect.null)
+
+        var r3 = CGRect.null
+        r3.origin.y = 20
+        XCTAssertEqual(r3, CGRect.null)
+
+        var r4 = CGRect.null
+        r4.origin = CGPoint(x: 10, y: 20)
+        XCTAssertNotEqual(r4, CGRect.null)
+    }
+
+    func test_CGRect_CalculatedGeometricProperties() {
+        let ε = CGFloat(0.00001)
+
+        let r1 = CGRect(x: 1.2, y: 3.4, width: 5.6, height: 7.8)
+        XCTAssertEqual(r1.width, 5.6, accuracy: ε)
+        XCTAssertEqual(r1.height, 7.8, accuracy: ε)
+
+        XCTAssertEqual(r1.minX, 1.2, accuracy: ε)
+        XCTAssertEqual(r1.midX, 4, accuracy: ε)
+        XCTAssertEqual(r1.maxX, 6.8, accuracy: ε)
+
+        XCTAssertEqual(r1.minY, 3.4, accuracy: ε)
+        XCTAssertEqual(r1.midY, 7.3, accuracy: ε)
+        XCTAssertEqual(r1.maxY, 11.2, accuracy: ε)
+
+        let r2 = CGRect(x: -1.2, y: -3.4, width: 5.6, height: 7.8)
+        XCTAssertEqual(r2.width, 5.6, accuracy: ε)
+        XCTAssertEqual(r2.height, 7.8, accuracy: ε)
+
+        XCTAssertEqual(r2.minX, -1.2, accuracy: ε)
+        XCTAssertEqual(r2.midX, 1.6, accuracy: ε)
+        XCTAssertEqual(r2.maxX, 4.4, accuracy: ε)
+
+        XCTAssertEqual(r2.minY, -3.4, accuracy: ε)
+        XCTAssertEqual(r2.midY, 0.5, accuracy: ε)
+        XCTAssertEqual(r2.maxY, 4.4, accuracy: ε)
+
+        let r3 = CGRect(x: 1.2, y: 3.4, width: -5.6, height: -7.8)
+        XCTAssertEqual(r3.width, 5.6, accuracy: ε)
+        XCTAssertEqual(r3.height, 7.8, accuracy: ε)
+
+        XCTAssertEqual(r3.minX, -4.4, accuracy: ε)
+        XCTAssertEqual(r3.midX, -1.6, accuracy: ε)
+        XCTAssertEqual(r3.maxX, 1.2, accuracy: ε)
+
+        XCTAssertEqual(r3.minY, -4.4, accuracy: ε)
+        XCTAssertEqual(r3.midY, -0.5, accuracy: ε)
+        XCTAssertEqual(r3.maxY, 3.4, accuracy: ε)
+
+        let r4 = CGRect(x: -1.2, y: -3.4, width: -5.6, height: -7.8)
+        XCTAssertEqual(r4.width, 5.6, accuracy: ε)
+        XCTAssertEqual(r4.height, 7.8, accuracy: ε)
+
+        XCTAssertEqual(r4.minX, -6.8, accuracy: ε)
+        XCTAssertEqual(r4.midX, -4.0, accuracy: ε)
+        XCTAssertEqual(r4.maxX, -1.2, accuracy: ε)
+
+        XCTAssertEqual(r4.minY, -11.2, accuracy: ε)
+        XCTAssertEqual(r4.midY, -7.3, accuracy: ε)
+        XCTAssertEqual(r4.maxY, -3.4, accuracy: ε)
+    }
+
+    func test_CGRect_Standardized() {
+        let ε = CGFloat(0.00001)
+        let nullX = CGRect.null.origin.x
+        let nullY = CGRect.null.origin.y
+        let nullWidth = CGRect.null.size.width
+        let nullHeight = CGRect.null.size.height
+
+        let r1 = CGRect(x: 1.9, y: 1.9, width: 10.1, height: 10.2).standardized
+        assertEqual(r1, x: 1.9, y: 1.9, width: 10.1, height: 10.2, accuracy: ε)
+
+        let r2 = CGRect(x: 1.9, y: 1.9, width: -10.1, height: -10.2).standardized
+        assertEqual(r2, x: -8.2, y: -8.3, width: 10.1, height: 10.2, accuracy: ε)
+
+        let r3 = CGRect(x: -1.9, y: -1.9, width: 10.1, height: 10.2).standardized
+        assertEqual(r3, x: -1.9, y: -1.9, width: 10.1, height: 10.2, accuracy: ε)
+
+        let r4 = CGRect(x: -1.9, y: -1.9, width: -10.1, height: -10.2).standardized
+        assertEqual(r4, x: -12, y: -12.1, width: 10.1, height: 10.2, accuracy: ε)
+
+        let r5 = CGRect.null.standardized
+        assertEqual(r5, x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+
+        var r6 = CGRect.null
+        r6.size = CGSize(width: 10, height: 20)
+        r6 = r6.standardized
+        assertEqual(r6, x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+
+        var r7 = CGRect.null
+        r7.size = CGSize(width: -10, height: -20)
+        r7 = r7.standardized
+        assertEqual(r7, x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+
+        var r8 = CGRect.null
+        r8.origin.x = 20
+        r8 = r8.standardized
+        assertEqual(r8, x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+
+        var r9 = CGRect.null
+        r9.origin.y = 20
+        r9 = r9.standardized
+        assertEqual(r9, x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+
+        var r10 = CGRect.null
+        r10.origin = CGPoint(x: 10, y: 20)
+        r10 = r10.standardized
+        assertEqual(r10, x: 10, y: 20, width: 0, height: 0)
+    }
+
+    func test_CGRect_Integral() {
+        let ε = CGFloat(0.00001)
+
+        let r1 = CGRect(x: 1.9, y: 1.9, width: 10.1, height: 10.2).integral
+        XCTAssertEqual(r1.origin.x, 1, accuracy: ε)
+        XCTAssertEqual(r1.origin.y, 1, accuracy: ε)
+        XCTAssertEqual(r1.size.width, 11, accuracy: ε)
+        XCTAssertEqual(r1.size.height, 12, accuracy: ε)
+
+        let r2 = CGRect(x: 1.9, y: 1.9, width: -10.1, height: -10.2).integral
+        XCTAssertEqual(r2.origin.x, -9, accuracy: ε)
+        XCTAssertEqual(r2.origin.y, -9, accuracy: ε)
+        XCTAssertEqual(r2.size.width, 11, accuracy: ε)
+        XCTAssertEqual(r2.size.height, 11, accuracy: ε)
+
+        let r3 = CGRect(x: -1.9, y: -1.9, width: 10.1, height: 10.2).integral
+        XCTAssertEqual(r3.origin.x, -2, accuracy: ε)
+        XCTAssertEqual(r3.origin.y, -2, accuracy: ε)
+        XCTAssertEqual(r3.size.width, 11, accuracy: ε)
+        XCTAssertEqual(r3.size.height, 11, accuracy: ε)
+
+        let r4 = CGRect(x: -1.9, y: -1.9, width: -10.1, height: -10.2).integral
+        XCTAssertEqual(r4.origin.x, -12, accuracy: ε)
+        XCTAssertEqual(r4.origin.y, -13, accuracy: ε)
+        XCTAssertEqual(r4.size.width, 11, accuracy: ε)
+        XCTAssertEqual(r4.size.height, 12, accuracy: ε)
+
+        let r5 = CGRect.null.integral
+        XCTAssertEqual(r5.origin.x, CGRect.null.origin.x)
+        XCTAssertEqual(r5.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(r5.size.width, CGRect.null.size.width)
+        XCTAssertEqual(r5.size.height, CGRect.null.size.height)
+
+        var r6 = CGRect.null
+        r6.origin.x = 10
+        r6.size = CGSize(width: -20, height: -30)
+        r6 = r6.integral
+        XCTAssertEqual(r6.origin.x, 10)
+        XCTAssertEqual(r6.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(r6.size.width, -20)
+        XCTAssertEqual(r6.size.height, -30)
+
+        var r7 = CGRect.null
+        r7.origin.y = 10
+        r7.size = CGSize(width: -20, height: -30)
+        r7 = r7.integral
+        XCTAssertEqual(r7.origin.x, CGRect.null.origin.y)
+        XCTAssertEqual(r7.origin.y, 10)
+        XCTAssertEqual(r7.size.width, -20)
+        XCTAssertEqual(r7.size.height, -30)
+
+        var r8 = CGRect.null
+        r8.origin = CGPoint(x: 10, y: 20)
+        r8.size = CGSize(width: -30, height: -40)
+        r8 = r8.integral
+        XCTAssertEqual(r8.origin.x, -20)
+        XCTAssertEqual(r8.origin.y, -20)
+        XCTAssertEqual(r8.size.width, 30)
+        XCTAssertEqual(r8.size.height, 40)
+    }
+
+    func test_CGRect_ContainsPoint() {
+        XCTAssertFalse(CGRect.null.contains(CGPoint()))
+        XCTAssertFalse(CGRect.zero.contains(CGPoint()))
+
+        let r1 = CGRect(x: 5, y: 5, width: 10, height: 10)
+        XCTAssertFalse(r1.contains(CGPoint(x: 1, y: 2)))
+        XCTAssertFalse(r1.contains(CGPoint(x: 7, y: 2)))
+        XCTAssertFalse(r1.contains(CGPoint(x: 2, y: 7)))
+        XCTAssertFalse(r1.contains(CGPoint(x: -7, y: -7)))
+        XCTAssertFalse(r1.contains(CGPoint(x: 15, y: 15)))
+        XCTAssertTrue(r1.contains(CGPoint(x: 7, y: 7)))
+        XCTAssertTrue(r1.contains(CGPoint(x: 10, y: 10)))
+        XCTAssertTrue(r1.contains(CGPoint(x: 5, y: 5)))
+
+        let r2 = CGRect(x: -5, y: -5, width: -10, height: -10)
+        XCTAssertFalse(r2.contains(CGPoint(x: -1, y: -2)))
+        XCTAssertFalse(r2.contains(CGPoint(x: -7, y: -2)))
+        XCTAssertFalse(r2.contains(CGPoint(x: -2, y: -7)))
+        XCTAssertFalse(r2.contains(CGPoint(x: 7, y: 7)))
+        XCTAssertFalse(r2.contains(CGPoint(x: -5, y: -5)))
+        XCTAssertTrue(r2.contains(CGPoint(x: -7, y: -7)))
+        XCTAssertTrue(r2.contains(CGPoint(x: -10, y: -10)))
+        XCTAssertTrue(r2.contains(CGPoint(x: -15, y: -15)))
+
+        XCTAssertTrue(CGRect.infinite.contains(CGPoint()))
+    }
+
+    func test_CGRect_ContainsRect() {
+        XCTAssertFalse(CGRect.zero.contains(.infinite))
+        XCTAssertTrue(CGRect.zero.contains(.null))
+        XCTAssertTrue(CGRect.zero.contains(CGRect.zero))
+        XCTAssertFalse(CGRect.zero.contains(CGRect(x: -1.2, y: -3.4, width: 5.6, height: 7.8)))
+
+        XCTAssertFalse(CGRect.null.contains(.infinite))
+        XCTAssertTrue(CGRect.null.contains(.null))
+        XCTAssertFalse(CGRect.null.contains(CGRect.zero))
+        XCTAssertFalse(CGRect.null.contains(CGRect(x: -1.2, y: -3.4, width: 5.6, height: 7.8)))
+
+        XCTAssertTrue(CGRect.infinite.contains(.infinite))
+        XCTAssertTrue(CGRect.infinite.contains(.null))
+        XCTAssertTrue(CGRect.infinite.contains(CGRect.zero))
+        XCTAssertTrue(CGRect.infinite.contains(CGRect(x: -1.2, y: -3.4, width: 5.6, height: 7.8)))
+
+        let r1 = CGRect(x: 10, y: 20, width: 30, height: 40)
+        XCTAssertTrue(r1.contains(r1))
+
+        let r2 = CGRect(x: -10, y: -20, width: -30, height: -40)
+        XCTAssertTrue(r2.contains(r2))
+
+        let r3 = CGRect(x: -10, y: -20, width: 30, height: 40)
+        let r4 = CGRect(x: 20, y: 20, width: -30, height: -40)
+        XCTAssertTrue(r3.contains(r4))
+
+        let r5 = CGRect(x: -10, y: -10, width: 20, height: 20)
+        let r6 = CGRect(x: -5, y: -5, width: 10, height: 10)
+        XCTAssertTrue(r5.contains(r6))
+        XCTAssertFalse(r6.contains(r5))
+    }
+
+    func test_CGRect_Union() {
+        let r1 = CGRect.null
+        let r2 = CGRect.null
+        let u1 = r1.union(r2)
+        XCTAssertEqual(u1.origin.x, CGRect.null.origin.x)
+        XCTAssertEqual(u1.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(u1.size.width, CGRect.null.size.width)
+        XCTAssertEqual(u1.size.height, CGRect.null.size.height)
+
+        let r3 = CGRect.null
+        var r4 = CGRect.null
+        r4.size = CGSize(width: 10, height: 20)
+        let u2 = r3.union(r4)
+        XCTAssertEqual(u2.origin.x, r4.origin.x)
+        XCTAssertEqual(u2.origin.y, r4.origin.y)
+        XCTAssertEqual(u2.size.width, r4.size.width)
+        XCTAssertEqual(u2.size.height, r4.size.height)
+
+        let u3 = r4.union(r3)
+        XCTAssertEqual(u3.origin.x, r3.origin.x)
+        XCTAssertEqual(u3.origin.y, r3.origin.y)
+        XCTAssertEqual(u3.size.width, r3.size.width)
+        XCTAssertEqual(u3.size.height, r3.size.height)
+
+        let r5 = CGRect(x: -1.2, y: -3.4, width: -5.6, height: -7.8)
+        let r6 = CGRect(x: 1.2, y: 3.4, width: 5.6, height: 7.8)
+        let u4 = r5.union(r6)
+        XCTAssertEqual(u4.origin.x, -6.8)
+        XCTAssertEqual(u4.origin.y, -11.2)
+        XCTAssertEqual(u4.size.width, 13.6)
+        XCTAssertEqual(u4.size.height, 22.4)
+
+        let r7 = CGRect(x: 1, y: 2, width: 3, height: 4)
+        let r8 = CGRect.infinite
+        let u5 = r7.union(r8)
+        XCTAssertEqual(u5.origin.x, r8.origin.x)
+        XCTAssertEqual(u5.origin.y, r8.origin.y)
+        XCTAssertEqual(u5.size.width, r8.size.width)
+        XCTAssertEqual(u5.size.height, r8.size.height)
+    }
+
+    func test_CGRect_Intersection() {
+        let r1 = CGRect(x: 10, y: 10, width: 50, height: 60)
+        let r2 = CGRect(x: 25, y: 25, width: 60, height: 70)
+        let i1 = r1.intersection(r2)
+        XCTAssertEqual(i1.origin.x, 25)
+        XCTAssertEqual(i1.origin.y, 25)
+        XCTAssertEqual(i1.size.width, 35)
+        XCTAssertEqual(i1.size.height, 45)
+
+        let r3 = CGRect(x: 85, y: 95, width: -60, height: -70)
+        let i2 = r1.intersection(r3)
+        XCTAssertEqual(i2.origin.x, 25)
+        XCTAssertEqual(i2.origin.y, 25)
+        XCTAssertEqual(i2.size.width, 35)
+        XCTAssertEqual(i2.size.height, 45)
+
+        let r4 = CGRect(x: -10, y: -10, width: -30, height: -30)
+        let i3 = r1.intersection(r4)
+        XCTAssertEqual(i3.origin.x, CGRect.null.origin.x)
+        XCTAssertEqual(i3.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(i3.size.width, CGRect.null.size.width)
+        XCTAssertEqual(i3.size.height, CGRect.null.size.height)
+
+        let r5 = CGRect.null
+        let i4 = r1.intersection(r5)
+        XCTAssertEqual(i4.origin.x, CGRect.null.origin.x)
+        XCTAssertEqual(i4.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(i4.size.width, CGRect.null.size.width)
+        XCTAssertEqual(i4.size.height, CGRect.null.size.height)
+
+        let i5 = r5.intersection(r1)
+        XCTAssertEqual(i5.origin.x, CGRect.null.origin.x)
+        XCTAssertEqual(i5.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(i5.size.width, CGRect.null.size.width)
+        XCTAssertEqual(i5.size.height, CGRect.null.size.height)
+
+        var r6 = CGRect.null
+        r6.size = CGSize(width: 10, height: 20)
+        r6.origin.x = 30
+        let i6 = r5.intersection(r6)
+        XCTAssertEqual(i6.origin.x, CGRect.null.origin.x)
+        XCTAssertEqual(i6.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(i6.size.width, CGRect.null.size.width)
+        XCTAssertEqual(i6.size.height, CGRect.null.size.height)
+
+        let i7 = r1.intersection(.infinite)
+        XCTAssertEqual(i7.origin.x, r1.origin.x)
+        XCTAssertEqual(i7.origin.y, r1.origin.y)
+        XCTAssertEqual(i7.size.width, r1.size.width)
+        XCTAssertEqual(i7.size.height, r1.size.height)
+
+        let i8 = CGRect.infinite.intersection(.infinite)
+        XCTAssertEqual(i8.origin.x, CGRect.infinite.origin.x)
+        XCTAssertEqual(i8.origin.y, CGRect.infinite.origin.y)
+        XCTAssertEqual(i8.size.width, CGRect.infinite.size.width)
+        XCTAssertEqual(i8.size.height, CGRect.infinite.size.height)
+
+        let r7 = CGRect(x: -10, y: -10, width: 20, height: 20)
+        let i9 = r7.intersection(.zero)
+        XCTAssertEqual(i9.origin.x, 0)
+        XCTAssertEqual(i9.origin.y, 0)
+        XCTAssertEqual(i9.size.width, 0)
+        XCTAssertEqual(i9.size.height, 0)
+    }
+
+    func test_CGRect_Intersects() {
+        let r1 = CGRect(x: 10, y: 10, width: 50, height: 60)
+        let r2 = CGRect(x: 25, y: 25, width: 60, height: 70)
+        XCTAssertTrue(r1.intersects(r2))
+
+        let r3 = CGRect(x: 85, y: 95, width: -60, height: -70)
+        XCTAssertTrue(r1.intersects(r3))
+
+        let r4 = CGRect(x: -10, y: -10, width: -30, height: -30)
+        XCTAssertFalse(r1.intersects(r4))
+
+        let r5 = CGRect.null
+        XCTAssertFalse(r1.intersects(r5))
+        XCTAssertFalse(r5.intersects(r1))
+
+        var r6 = CGRect.null
+        r6.size = CGSize(width: 10, height: 20)
+        r6.origin.x = 30
+        XCTAssertFalse(r5.intersects(r6))
+
+        XCTAssertTrue(r1.intersects(.infinite))
+
+        XCTAssertTrue(CGRect.infinite.intersects(.infinite))
+
+        let r7 = CGRect(x: -10, y: -10, width: 20, height: 20)
+        XCTAssertTrue(r7.intersects(.zero))
+    }
+
+    func test_CGRect_OffsetBy() {
+        var r1 = CGRect.null
+        r1.size = CGSize(width: 10, height: 20)
+        r1.origin.x = 30
+        let o1 = r1.offsetBy(dx: 40, dy: 50)
+        XCTAssertEqual(o1.origin.x, r1.origin.x)
+        XCTAssertEqual(o1.origin.y, r1.origin.y)
+        XCTAssertEqual(o1.size.width, r1.size.width)
+        XCTAssertEqual(o1.size.height, r1.size.height)
+
+        var r2 = CGRect.null
+        r2.size = CGSize(width: 10, height: 20)
+        r2.origin.y = 30
+        let o2 = r2.offsetBy(dx: 40, dy: 50)
+        XCTAssertEqual(o2.origin.x, r2.origin.x)
+        XCTAssertEqual(o2.origin.y, r2.origin.y)
+        XCTAssertEqual(o2.size.width, r2.size.width)
+        XCTAssertEqual(o2.size.height, r2.size.height)
+
+        let o3 = CGRect(x: 1.2, y: 3.4, width: 5.6, height: 7.8).offsetBy(dx: 10.5, dy: 20.5)
+        XCTAssertEqual(o3.origin.x, 11.7)
+        XCTAssertEqual(o3.origin.y, 23.9)
+        XCTAssertEqual(o3.size.width, 5.6)
+        XCTAssertEqual(o3.size.height, 7.8)
+
+        let o4 = CGRect(x: -1.2, y: -3.4, width: -5.6, height: -7.8).offsetBy(dx: -10.5, dy: -20.5)
+        XCTAssertEqual(o4.origin.x, -17.3)
+        XCTAssertEqual(o4.origin.y, -31.7)
+        XCTAssertEqual(o4.size.width, 5.6)
+        XCTAssertEqual(o4.size.height, 7.8)
+    }
+
+    func test_CGRect_Divide() {
+        let r1 = CGRect(x: 10, y: 20, width: 30, height: 40)
+        let d1 = r1.divided(atDistance: 10, from: .minXEdge)
+        XCTAssertEqual(d1.slice.origin.x, 10)
+        XCTAssertEqual(d1.slice.origin.y, 20)
+        XCTAssertEqual(d1.slice.size.width, 10)
+        XCTAssertEqual(d1.slice.size.height, 40)
+        XCTAssertEqual(d1.remainder.origin.x, 20)
+        XCTAssertEqual(d1.remainder.origin.y, 20)
+        XCTAssertEqual(d1.remainder.size.width, 20)
+        XCTAssertEqual(d1.remainder.size.height, 40)
+
+        let d2 = r1.divided(atDistance: 10, from: .maxXEdge)
+        XCTAssertEqual(d2.slice.origin.x, 30)
+        XCTAssertEqual(d2.slice.origin.y, 20)
+        XCTAssertEqual(d2.slice.size.width, 10)
+        XCTAssertEqual(d2.slice.size.height, 40)
+        XCTAssertEqual(d2.remainder.origin.x, 10)
+        XCTAssertEqual(d2.remainder.origin.y, 20)
+        XCTAssertEqual(d2.remainder.size.width, 20)
+        XCTAssertEqual(d2.remainder.size.height, 40)
+
+        let d3 = r1.divided(atDistance: 10, from: .minYEdge)
+        XCTAssertEqual(d3.slice.origin.x, 10)
+        XCTAssertEqual(d3.slice.origin.y, 20)
+        XCTAssertEqual(d3.slice.size.width, 30)
+        XCTAssertEqual(d3.slice.size.height, 10)
+        XCTAssertEqual(d3.remainder.origin.x, 10)
+        XCTAssertEqual(d3.remainder.origin.y, 30)
+        XCTAssertEqual(d3.remainder.size.width, 30)
+        XCTAssertEqual(d3.remainder.size.height, 30)
+
+        let d4 = r1.divided(atDistance: 10, from: .maxYEdge)
+        XCTAssertEqual(d4.slice.origin.x, 10)
+        XCTAssertEqual(d4.slice.origin.y, 50)
+        XCTAssertEqual(d4.slice.size.width, 30)
+        XCTAssertEqual(d4.slice.size.height, 10)
+        XCTAssertEqual(d4.remainder.origin.x, 10)
+        XCTAssertEqual(d4.remainder.origin.y, 20)
+        XCTAssertEqual(d4.remainder.size.width, 30)
+        XCTAssertEqual(d4.remainder.size.height, 30)
+
+        let d5 = r1.divided(atDistance: 31, from: .minXEdge)
+        XCTAssertEqual(d5.slice.origin.x, 10)
+        XCTAssertEqual(d5.slice.origin.y, 20)
+        XCTAssertEqual(d5.slice.size.width, 30)
+        XCTAssertEqual(d5.slice.size.height, 40)
+        XCTAssertEqual(d5.remainder.origin.x, 40)
+        XCTAssertEqual(d5.remainder.origin.y, 20)
+        XCTAssertEqual(d5.remainder.size.width, 0)
+        XCTAssertEqual(d5.remainder.size.height, 40)
+
+        let d6 = r1.divided(atDistance: 31, from: .maxXEdge)
+        XCTAssertEqual(d6.slice.origin.x, 10)
+        XCTAssertEqual(d6.slice.origin.y, 20)
+        XCTAssertEqual(d6.slice.size.width, 30)
+        XCTAssertEqual(d6.slice.size.height, 40)
+        XCTAssertEqual(d6.remainder.origin.x, 10)
+        XCTAssertEqual(d6.remainder.origin.y, 20)
+        XCTAssertEqual(d6.remainder.size.width, 0)
+        XCTAssertEqual(d6.remainder.size.height, 40)
+
+        let d7 = r1.divided(atDistance: 41, from: .minYEdge)
+        XCTAssertEqual(d7.slice.origin.x, 10)
+        XCTAssertEqual(d7.slice.origin.y, 20)
+        XCTAssertEqual(d7.slice.size.width, 30)
+        XCTAssertEqual(d7.slice.size.height, 40)
+        XCTAssertEqual(d7.remainder.origin.x, 10)
+        XCTAssertEqual(d7.remainder.origin.y, 60)
+        XCTAssertEqual(d7.remainder.size.width, 30)
+        XCTAssertEqual(d7.remainder.size.height, 0)
+
+        let d8 = r1.divided(atDistance: 41, from: .maxYEdge)
+        XCTAssertEqual(d8.slice.origin.x, 10)
+        XCTAssertEqual(d8.slice.origin.y, 20)
+        XCTAssertEqual(d8.slice.size.width, 30)
+        XCTAssertEqual(d8.slice.size.height, 40)
+        XCTAssertEqual(d8.remainder.origin.x, 10)
+        XCTAssertEqual(d8.remainder.origin.y, 20)
+        XCTAssertEqual(d8.remainder.size.width, 30)
+        XCTAssertEqual(d8.remainder.size.height, 0)
+
+        let d9 = CGRect(x: -10, y: -20, width: -30, height: -40).divided(atDistance: 10, from: .minXEdge)
+        XCTAssertEqual(d9.slice.origin.x, -40)
+        XCTAssertEqual(d9.slice.origin.y, -60)
+        XCTAssertEqual(d9.slice.size.width, 10)
+        XCTAssertEqual(d9.slice.size.height, 40)
+        XCTAssertEqual(d9.remainder.origin.x, -30)
+        XCTAssertEqual(d9.remainder.origin.y, -60)
+        XCTAssertEqual(d9.remainder.size.width, 20)
+        XCTAssertEqual(d9.remainder.size.height, 40)
+
+        var r2 = CGRect.null
+        r2.size = CGSize(width: 10, height: 20)
+        r2.origin.x = 30
+        let d10 = r2.divided(atDistance: 10, from: .minXEdge)
+        XCTAssertEqual(d10.slice.origin.x, CGRect.null.origin.x)
+        XCTAssertEqual(d10.slice.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(d10.slice.size.width, CGRect.null.size.width)
+        XCTAssertEqual(d10.slice.size.height, CGRect.null.size.height)
+        XCTAssertEqual(d10.remainder.origin.x, CGRect.null.origin.x)
+        XCTAssertEqual(d10.remainder.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(d10.remainder.size.width, CGRect.null.size.width)
+        XCTAssertEqual(d10.remainder.size.height, CGRect.null.size.height)
+
+        var r3 = CGRect.null
+        r3.size = CGSize(width: 10, height: 20)
+        r3.origin.y = 30
+        let d11 = r3.divided(atDistance: 10, from: .minXEdge)
+        XCTAssertEqual(d11.slice.origin.x, CGRect.null.origin.x)
+        XCTAssertEqual(d11.slice.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(d11.slice.size.width, CGRect.null.size.width)
+        XCTAssertEqual(d11.slice.size.height, CGRect.null.size.height)
+        XCTAssertEqual(d11.remainder.origin.x, CGRect.null.origin.x)
+        XCTAssertEqual(d11.remainder.origin.y, CGRect.null.origin.y)
+        XCTAssertEqual(d11.remainder.size.width, CGRect.null.size.width)
+        XCTAssertEqual(d11.remainder.size.height, CGRect.null.size.height)
+    }
+
+    func test_CGRect_InsetBy() {
+        let ε = CGFloat(0.00001)
+        let nullX = CGRect.null.origin.x
+        let nullY = CGRect.null.origin.y
+        let nullWidth = CGRect.null.size.width
+        let nullHeight = CGRect.null.size.height
+
+        let r1 = CGRect(x: 1.2, y: 3.4, width: 5.6, height: 7.8)
+        assertEqual(r1.insetBy(dx: 2.8, dy: 0), x: 4, y: 3.4, width: 0, height: 7.8, accuracy: ε)
+        assertEqual(r1.insetBy(dx: 0, dy: 3.9), x: 1.2, y: 7.3, width: 5.6, height: 0, accuracy: ε)
+        assertEqual(r1.insetBy(dx: 10, dy: 10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r1.insetBy(dx: 10, dy: -10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r1.insetBy(dx: -10, dy: 10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r1.insetBy(dx: -10, dy: -10), x: -8.8, y: -6.6, width: 25.6, height: 27.8, accuracy: ε)
+
+        let r2 = CGRect(x: 1.2, y: 3.4, width: -5.6, height: -7.8)
+        assertEqual(r2.insetBy(dx: 2.8, dy: 0), x: -1.6, y: -4.4, width: 0, height: 7.8, accuracy: ε)
+        assertEqual(r2.insetBy(dx: 0, dy: 3.9), x: -4.4, y: -0.5, width: 5.6, height: 0, accuracy: ε)
+        assertEqual(r2.insetBy(dx: 10, dy: 10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r2.insetBy(dx: 10, dy: -10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r2.insetBy(dx: -10, dy: 10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r2.insetBy(dx: -10, dy: -10), x: -14.4, y: -14.4, width: 25.6, height: 27.8, accuracy: ε)
+
+        let r3 = CGRect(x: -1.2, y: -3.4, width: 5.6, height: 7.8)
+        assertEqual(r3.insetBy(dx: 2.8, dy: 0), x: 1.6, y: -3.4, width: 0, height: 7.8, accuracy: ε)
+        assertEqual(r3.insetBy(dx: 0, dy: 3.9), x: -1.2, y: 0.5, width: 5.6, height: 0, accuracy: ε)
+        assertEqual(r3.insetBy(dx: 10, dy: 10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r3.insetBy(dx: 10, dy: -10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r3.insetBy(dx: -10, dy: 10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r3.insetBy(dx: -10, dy: -10), x: -11.2, y: -13.4, width: 25.6, height: 27.8, accuracy: ε)
+
+        let r4 = CGRect(x: -1.2, y: -3.4, width: -5.6, height: -7.8)
+        assertEqual(r4.insetBy(dx: 2.8, dy: 0), x: -4, y: -11.2, width: 0, height: 7.8, accuracy: ε)
+        assertEqual(r4.insetBy(dx: 0, dy: 3.9), x: -6.8, y: -7.3, width: 5.6, height: 0, accuracy: ε)
+        assertEqual(r4.insetBy(dx: 10, dy: 10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r4.insetBy(dx: 10, dy: -10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r4.insetBy(dx: -10, dy: 10), x: nullX, y: nullY, width: nullWidth, height: nullHeight)
+        assertEqual(r4.insetBy(dx: -10, dy: -10), x: -16.8, y: -21.2, width: 25.6, height: 27.8, accuracy: ε)
+
+        var r5 = CGRect.null
+        r5.size = CGSize(width: 10, height: 20)
+        r5.origin.x = 30
+        let i1 = r5.insetBy(dx: 50, dy: 60)
+        XCTAssertEqual(i1.origin.x, 30)
+        XCTAssertEqual(i1.origin.y, r5.origin.y)
+        XCTAssertEqual(i1.size.width, 10)
+        XCTAssertEqual(i1.size.height, 20)
+
+        var r6 = CGRect.null
+        r6.size = CGSize(width: 10, height: 20)
+        r6.origin.y = 30
+        let i2 = r6.insetBy(dx: 50, dy: 60)
+        XCTAssertEqual(i2.origin.x, r6.origin.x)
+        XCTAssertEqual(i2.origin.y, 30)
+        XCTAssertEqual(i2.size.width, 10)
+        XCTAssertEqual(i2.size.height, 20)
+    }
     
     func test_CGRect_SpecialValues() {
         let r1 = CGRect.null