diff --git a/DiceKit.xcodeproj/project.pbxproj b/DiceKit.xcodeproj/project.pbxproj index bd46119..54b40d0 100644 --- a/DiceKit.xcodeproj/project.pbxproj +++ b/DiceKit.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 531084C31B5B0761008DD696 /* Die.Roll.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531084C11B5B0102008DD696 /* Die.Roll.swift */; }; 531E4F5F1B73F5E20073F01A /* EquatableTestUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531E4F5E1B73F5E20073F01A /* EquatableTestUtilities.swift */; }; 531E4F611B7400270073F01A /* Arbitrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531E4F601B7400270073F01A /* Arbitrary.swift */; }; + 533B55F41BDDC0D100C08EDB /* OutcomeWithSuccessfulness_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 533B55F31BDDC0D100C08EDB /* OutcomeWithSuccessfulness_Tests.swift */; }; 533D0C771B6419F8003A7D32 /* FrequencyDistribution_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 533D0C761B6419F8003A7D32 /* FrequencyDistribution_Tests.swift */; }; 533D0C7B1B6423FA003A7D32 /* ProbabilityMass_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 533D0C7A1B6423FA003A7D32 /* ProbabilityMass_Tests.swift */; }; 533D0C7D1B643F6C003A7D32 /* Constant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 533D0C7C1B643F6C003A7D32 /* Constant.swift */; }; @@ -18,6 +19,9 @@ 533F88091B52B988003838C8 /* DiceKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 533F87FE1B52B988003838C8 /* DiceKit.framework */; }; 533F880E1B52B988003838C8 /* Die_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 533F880D1B52B988003838C8 /* Die_Tests.swift */; }; 534900341B78172900FA2804 /* ArithmeticType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534900331B78172900FA2804 /* ArithmeticType.swift */; }; + 537675F81BDDB81A00F6CE9C /* OutcomeWithSuccessfulness.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537675F71BDDB81A00F6CE9C /* OutcomeWithSuccessfulness.swift */; }; + 537675FA1BDDB88700F6CE9C /* Successfulness.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537675F91BDDB88700F6CE9C /* Successfulness.swift */; }; + 537675FC1BDDBAD400F6CE9C /* Successfulness_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537675FB1BDDBAD400F6CE9C /* Successfulness_Tests.swift */; }; 5379780B1B531B21005818EC /* Die.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5379780A1B531B21005818EC /* Die.swift */; }; 538743871B5316BA00EB15C8 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 530FEDD11B530CAB00960BFD /* Nimble.framework */; }; 538AD9651B75A66F001B5CB5 /* MockExpression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538AD9641B75A66F001B5CB5 /* MockExpression.swift */; }; @@ -94,6 +98,7 @@ 531084C11B5B0102008DD696 /* Die.Roll.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Die.Roll.swift; sourceTree = ""; }; 531E4F5E1B73F5E20073F01A /* EquatableTestUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EquatableTestUtilities.swift; sourceTree = ""; }; 531E4F601B7400270073F01A /* Arbitrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Arbitrary.swift; sourceTree = ""; }; + 533B55F31BDDC0D100C08EDB /* OutcomeWithSuccessfulness_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutcomeWithSuccessfulness_Tests.swift; sourceTree = ""; }; 533D0C761B6419F8003A7D32 /* FrequencyDistribution_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FrequencyDistribution_Tests.swift; sourceTree = ""; }; 533D0C7A1B6423FA003A7D32 /* ProbabilityMass_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProbabilityMass_Tests.swift; sourceTree = ""; }; 533D0C7C1B643F6C003A7D32 /* Constant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constant.swift; sourceTree = ""; }; @@ -104,6 +109,9 @@ 533F880D1B52B988003838C8 /* Die_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Die_Tests.swift; sourceTree = ""; }; 533F880F1B52B988003838C8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 534900331B78172900FA2804 /* ArithmeticType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArithmeticType.swift; sourceTree = ""; }; + 537675F71BDDB81A00F6CE9C /* OutcomeWithSuccessfulness.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutcomeWithSuccessfulness.swift; sourceTree = ""; }; + 537675F91BDDB88700F6CE9C /* Successfulness.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Successfulness.swift; sourceTree = ""; }; + 537675FB1BDDBAD400F6CE9C /* Successfulness_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Successfulness_Tests.swift; sourceTree = ""; }; 5379780A1B531B21005818EC /* Die.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Die.swift; sourceTree = ""; }; 538AD9641B75A66F001B5CB5 /* MockExpression.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockExpression.swift; sourceTree = ""; }; 538AD9681B7678B7001B5CB5 /* Die.Roll_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Die.Roll_Tests.swift; sourceTree = ""; }; @@ -232,10 +240,12 @@ 531084BF1B5AF993008DD696 /* MultiplicationExpressionResult.swift */, 53ECD0E11B5B498A002D859F /* NegationExpression.swift */, 53ECD0E31B5B5185002D859F /* NegationExpressionResult.swift */, + 537675F71BDDB81A00F6CE9C /* OutcomeWithSuccessfulness.swift */, 53FA0EAB1B6579F300C8D3B5 /* ProbabilisticExpressionType.swift */, 539D568D1B5C7BDA00AC6A37 /* ProbabilityMass.swift */, 8B4C14011B7F8B47008AC355 /* SubtractionExpression.swift */, 8B4C14031B7FA8A2008AC355 /* SubtractionExpressionResult.swift */, + 537675F91BDDB88700F6CE9C /* Successfulness.swift */, ); path = DiceKit; sourceTree = ""; @@ -264,10 +274,12 @@ 533EB1ED1B5B293000B3F8A1 /* MultiplicationExpressionResult_Tests.swift */, 539D56831B5B6D1A00AC6A37 /* NegationExpression_Tests.swift */, 539D56851B5B6D2B00AC6A37 /* NegationExpressionResult_Tests.swift */, + 533B55F31BDDC0D100C08EDB /* OutcomeWithSuccessfulness_Tests.swift */, 53FA0EAD1B657A3600C8D3B5 /* ProbabilisticExpressionType_Tests.swift */, 533D0C7A1B6423FA003A7D32 /* ProbabilityMass_Tests.swift */, 8BFAA9F81B7ED8E800EF65AE /* SubtractionExpression_Tests.swift */, 8BFAA9FA1B7F708000EF65AE /* SubtractionExpressionResult_Tests.swift */, + 537675FB1BDDBAD400F6CE9C /* Successfulness_Tests.swift */, ); path = DiceKitTests; sourceTree = ""; @@ -430,6 +442,7 @@ 53D1EC7C1B6312E100433D2C /* FrequencyDistribution.swift in Sources */, 53FA0EAC1B6579F300C8D3B5 /* ProbabilisticExpressionType.swift in Sources */, 531084C01B5AF993008DD696 /* MultiplicationExpressionResult.swift in Sources */, + 537675F81BDDB81A00F6CE9C /* OutcomeWithSuccessfulness.swift in Sources */, 539D568C1B5C7B7700AC6A37 /* Dictionary+Functions.swift in Sources */, 6E9F18981B7FDF0D00AB8893 /* MinimizationExpressionResult.swift in Sources */, 53D1EC7E1B63133E00433D2C /* ApproximatelyEquatable.swift in Sources */, @@ -448,6 +461,7 @@ 53CFC30F1B5AD674009C6C8F /* MultiplicationExpression.swift in Sources */, 53ECD0E41B5B5185002D859F /* NegationExpressionResult.swift in Sources */, 533D0C7D1B643F6C003A7D32 /* Constant.swift in Sources */, + 537675FA1BDDB88700F6CE9C /* Successfulness.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -464,7 +478,9 @@ 53D05EEC1B546C73007CE7FC /* Int+Random_Tests.swift in Sources */, 6E9F18941B7FD16100AB8893 /* MinimizationExpression_Tests.swift in Sources */, 539D568A1B5B6D6A00AC6A37 /* AdditionExpressionResult_Tests.swift in Sources */, + 533B55F41BDDC0D100C08EDB /* OutcomeWithSuccessfulness_Tests.swift in Sources */, 533D0C771B6419F8003A7D32 /* FrequencyDistribution_Tests.swift in Sources */, + 537675FC1BDDBAD400F6CE9C /* Successfulness_Tests.swift in Sources */, 6E0F58C91B8D63690095087F /* MinimizationExpressionResult_Tests.swift in Sources */, 538AD96B1B768931001B5CB5 /* Constant_Tests.swift in Sources */, 533F880E1B52B988003838C8 /* Die_Tests.swift in Sources */, diff --git a/DiceKit/FrequencyDistribution.swift b/DiceKit/FrequencyDistribution.swift index ccfef66..e5ac5b8 100644 --- a/DiceKit/FrequencyDistribution.swift +++ b/DiceKit/FrequencyDistribution.swift @@ -13,6 +13,9 @@ public protocol FrequencyDistributionOutcomeType: InvertibleMultiplicativeType, /// The number that will be used when determining how many times to perform another /// expression when multiplied by `self`. var multiplierEquivalent: Int { get } + + // Force Int for IntegerLiteralType + init(integerLiteral: Int) } @@ -49,7 +52,7 @@ public struct FrequencyDistribution: Equatable { + + public let outcome: Outcome + public let successfulness: Successfulness + + public init(_ outcome: Outcome, successfulness: Successfulness) { + self.outcome = outcome + self.successfulness = successfulness + } +} + +// MARK: - CustomStringConvertible + +extension OutcomeWithSuccessfulness: CustomStringConvertible { + public var description: String { + if successfulness == .additiveIdentity { + return "\(outcome)" + } else { + return "(\(outcome) with \(successfulness.rawDescription(surroundWithParentheses: false)))" + } + } +} + +// MARK: - CustomDebugStringConvertible + +extension OutcomeWithSuccessfulness: CustomDebugStringConvertible { + public var debugDescription: String { + return "OutcomeWithSuccessfulness(\(String(reflecting: outcome)), successfulness: \(String(reflecting: successfulness)))" + } +} + +// MARK: - Equatable + +public func == (lhs: OutcomeWithSuccessfulness, rhs: OutcomeWithSuccessfulness) -> Bool { + return lhs.outcome == rhs.outcome && lhs.successfulness == rhs.successfulness +} + +// MARK: - Comparable + +extension OutcomeWithSuccessfulness: Comparable { +} + +public func < (lhs: OutcomeWithSuccessfulness, rhs: OutcomeWithSuccessfulness) -> Bool { + if lhs.outcome == rhs.outcome { + return lhs.successfulness < rhs.successfulness + } else { + return lhs.outcome < rhs.outcome + } +} + +// MARK: - Hashable + +extension OutcomeWithSuccessfulness: Hashable { + + public var hashValue: Int { + return outcome.hashValue ^ successfulness.hashValue + } +} + +// MARK: - ArithmeticType + +extension OutcomeWithSuccessfulness: ArithmeticType { + + // TODO: Change to stored properties when allowed by Swift + public static var additiveIdentity: OutcomeWithSuccessfulness { + return OutcomeWithSuccessfulness(.additiveIdentity, successfulness: .additiveIdentity) + } + public static var multiplicativeIdentity: OutcomeWithSuccessfulness { + return OutcomeWithSuccessfulness(.multiplicativeIdentity, successfulness: .multiplicativeIdentity) + } + + // MARK: IntegerLiteralType + + public init(integerLiteral value: Int) { + self.init(Outcome(integerLiteral: value), successfulness: 0) + } +} + +public func + (lhs: OutcomeWithSuccessfulness, rhs: OutcomeWithSuccessfulness) -> OutcomeWithSuccessfulness { + let outcome = lhs.outcome + rhs.outcome + let successfulness = lhs.successfulness + rhs.successfulness + return OutcomeWithSuccessfulness(outcome, successfulness: successfulness) +} + +public func - (lhs: OutcomeWithSuccessfulness, rhs: OutcomeWithSuccessfulness) -> OutcomeWithSuccessfulness { + let outcome = lhs.outcome - rhs.outcome + let successfulness = lhs.successfulness - rhs.successfulness + return OutcomeWithSuccessfulness(outcome, successfulness: successfulness) +} + +public func * (lhs: OutcomeWithSuccessfulness, rhs: OutcomeWithSuccessfulness) -> OutcomeWithSuccessfulness { + let outcome = lhs.outcome * rhs.outcome + let successfulness = lhs.successfulness * rhs.successfulness + return OutcomeWithSuccessfulness(outcome, successfulness: successfulness) +} + +public func / (lhs: OutcomeWithSuccessfulness, rhs: OutcomeWithSuccessfulness) -> OutcomeWithSuccessfulness { + let outcome = lhs.outcome / rhs.outcome + let successfulness = lhs.successfulness / rhs.successfulness + return OutcomeWithSuccessfulness(outcome, successfulness: successfulness) +} + +public func % (lhs: OutcomeWithSuccessfulness, rhs: OutcomeWithSuccessfulness) -> OutcomeWithSuccessfulness { + let outcome = lhs.outcome % rhs.outcome + let successfulness = lhs.successfulness % rhs.successfulness + return OutcomeWithSuccessfulness(outcome, successfulness: successfulness) +} + +// MARK: - FrequencyDistributionOutcomeType + +extension OutcomeWithSuccessfulness: FrequencyDistributionOutcomeType { + + public var multiplierEquivalent: Int { + return outcome.multiplierEquivalent + } +} diff --git a/DiceKit/Successfulness.swift b/DiceKit/Successfulness.swift new file mode 100644 index 0000000..8ccba17 --- /dev/null +++ b/DiceKit/Successfulness.swift @@ -0,0 +1,183 @@ +// +// Successfulness.swift +// DiceKit +// +// Created by Brentley Jones on 10/25/15. +// Copyright © 2015 Brentley Jones. All rights reserved. +// + +/// The number of successes and failures of an `ExpressionType`. +/// +/// `Successfulness` can represent the actual number of successes and failures that occured while evaluating an +/// `ExpressionType`, or it can represent the possibility of successes and failures in the context of +/// a `FrequencyDistribution`. +public struct Successfulness: Equatable { + /// The number of successes. + /// + /// This value cannot be negative. See `rawSuccesses`. + public let successes: Int + /// The number of failures. + /// + /// This value cannot be negative. See `rawFailures`. + public let failures: Int + + /// The raw number of successes. + /// + /// This value can be negative. It's used as part of arithmetic. + public let rawSuccesses: Int + /// The raw number of failures. + /// + /// This value can be negative. It's used as part of arithmetic. + public let rawFailures: Int + + public init(successes: Int, failures: Int) { + self.rawSuccesses = successes + self.rawFailures = failures + + self.successes = max(0,successes) + self.failures = max(0,failures) + } +} + +// MARK: - CustomStringConvertible + +extension Successfulness: CustomStringConvertible { + public var description: String { + return rawDescription(surroundWithParentheses: true) + } + + func rawDescription(surroundWithParentheses surround: Bool) -> String { + let innerDescription: String + switch (successes, failures) { + case (0, 0): + innerDescription = "0 Successes" + case let (s, 0): + innerDescription = "\(s) Successes" + case let (0, f): + innerDescription = "\(f) Failures" + case let (s, f): + innerDescription = "\(s) Successes and \(f) Failures" + } + + return surround ? "(\(innerDescription))" : innerDescription + } +} + +// MARK: - CustomDebugStringConvertible + +extension Successfulness: CustomDebugStringConvertible { + public var debugDescription: String { + return "Successfulness(rawSuccesses: \(rawSuccesses), rawFailures: \(rawFailures))" + } +} + +// MARK: - CustomPlaygroundQuickLookable + +extension Successfulness: CustomPlaygroundQuickLookable { + public func customPlaygroundQuickLook() -> PlaygroundQuickLook { + return PlaygroundQuickLook.Point(Float64(successes), Float64(failures)) + } +} + +// MARK: - Equatable + +public func == (lhs: Successfulness, rhs: Successfulness) -> Bool { + return lhs.rawSuccesses == rhs.rawSuccesses && lhs.rawFailures == rhs.rawFailures +} + +// MARK: - Comparable + +extension Successfulness: Comparable { +} + +public func < (lhs: Successfulness, rhs: Successfulness) -> Bool { + let lhsMult = lhs.multiplierEquivalent + let rhsMult = rhs.multiplierEquivalent + if lhsMult == rhsMult { + if lhs.rawSuccesses == rhs.rawSuccesses { + return lhs.rawFailures < rhs.rawFailures + } else { + return lhs.rawSuccesses < rhs.rawSuccesses + } + } else { + return lhsMult < rhsMult + } +} + +// MARK: - Hashable + +extension Successfulness: Hashable { + public var hashValue: Int { + return rawSuccesses.hashValue ^ rawFailures.hashValue + } +} + +// MARK: - ArithmeticType + +extension Successfulness: ArithmeticType { + public static let additiveIdentity = Successfulness(successes: 0, failures: 0) + public static let multiplicativeIdentity = Successfulness(successes: 1, failures: 1) + + // MARK: IntegerLiteralType + + public typealias IntegerLiteralType = Int + + public init(integerLiteral value: IntegerLiteralType) { + let successes: Int + let failures: Int + if value > 0 { + // Interpret it as a successes count + successes = value + failures = 0 + } else if value < 0 { + // Interpret it as a failures count + successes = 0 + failures = -value + } else { + successes = 0 + failures = 0 + } + + self.init(successes: successes, failures: failures) + } +} + +public func + (lhs: Successfulness, rhs: Successfulness) -> Successfulness { + let successes = lhs.rawSuccesses + rhs.rawSuccesses + let failures = lhs.rawFailures + rhs.rawFailures + return Successfulness(successes: successes, failures: failures) +} + +public func - (lhs: Successfulness, rhs: Successfulness) -> Successfulness { + let successes = lhs.rawSuccesses - rhs.rawSuccesses + let failures = lhs.rawFailures - rhs.rawFailures + return Successfulness(successes: successes, failures: failures) +} + +public func * (lhs: Successfulness, rhs: Successfulness) -> Successfulness { + let successes = lhs.rawSuccesses * rhs.rawSuccesses + let failures = lhs.rawFailures * rhs.rawFailures + return Successfulness(successes: successes, failures: failures) +} + +public func / (lhs: Successfulness, rhs: Successfulness) -> Successfulness { + let successes = lhs.rawSuccesses / rhs.rawSuccesses + let failures = lhs.rawFailures / rhs.rawFailures + return Successfulness(successes: successes, failures: failures) +} + +public func % (lhs: Successfulness, rhs: Successfulness) -> Successfulness { + let successes = lhs.rawSuccesses % rhs.rawSuccesses + let failures = lhs.rawFailures % rhs.rawFailures + return Successfulness(successes: successes, failures: failures) +} + +// MARK: - FrequencyDistributionOutcomeType + +extension Successfulness: FrequencyDistributionOutcomeType { + + public var multiplierEquivalent: Int { + return successes - failures + } + +} diff --git a/DiceKitTests/Arbitrary.swift b/DiceKitTests/Arbitrary.swift index 96e1225..1dfc0c7 100644 --- a/DiceKitTests/Arbitrary.swift +++ b/DiceKitTests/Arbitrary.swift @@ -94,3 +94,35 @@ public struct FrequencyDistributionOf.shrink(bl.getFrequencyDistribution).map(FrequencyDistributionOf.init) } } + +extension Successfulness: Arbitrary { + public static func create(x : Int)(y : Int) -> Successfulness { + return Successfulness(successes: x, failures: y) + } + + public static var arbitrary : Gen { + return Successfulness.create <^> Int.arbitrary <*> Int.arbitrary + } +} + +// TODO: Replace this with the commented block once Swift generics are fixed (probably Swift 2) +extension OutcomeWithSuccessfulness: Arbitrary { + static func create(x : Int)(y : Successfulness) -> OutcomeWithSuccessfulness { + let outcome = Outcome(integerLiteral: x) + return OutcomeWithSuccessfulness(outcome, successfulness: y) + } + + public static var arbitrary : Gen { + return OutcomeWithSuccessfulness.create <^> Int.arbitrary <*> Successfulness.arbitrary + } +} + +//extension OutcomeWithSuccessfulness: Arbitrary where Outcome: Arbitrary { +// static func create(x : Outcome)(y : Successfulness) -> OutcomeWithSuccessfulness { +// return OutcomeWithSuccessfulness(outcome: x, successfulness: y) +// } +// +// public static var arbitrary : Gen { +// return OutcomeWithSuccessfulness.create <^> Outcome.arbitrary <*> Successfulness.arbitrary +// } +//} diff --git a/DiceKitTests/OutcomeWithSuccessfulness_Tests.swift b/DiceKitTests/OutcomeWithSuccessfulness_Tests.swift new file mode 100644 index 0000000..4bc0e6f --- /dev/null +++ b/DiceKitTests/OutcomeWithSuccessfulness_Tests.swift @@ -0,0 +1,267 @@ +// +// OutcomeWithSuccessfulness_Tests.swift +// DiceKit +// +// Created by Brentley Jones on 10/25/15. +// Copyright © 2015 Brentley Jones. All rights reserved. +// + +import XCTest +import Nimble +import SwiftCheck + +@testable import DiceKit + +class OutcomeWithSuccessfulness_Tests: XCTestCase { +} + +// MARK: - Initialization +extension OutcomeWithSuccessfulness_Tests { + + func test_init() { + property("init") <- forAll { + (outcome: Int, successfulness: Successfulness) in + + let x = OutcomeWithSuccessfulness(outcome, successfulness: successfulness) + + let outcomeTest = x.outcome == outcome + let successfulnessTest = x.successfulness == successfulness + + return outcomeTest && successfulnessTest + } + } +} + +// MARK: - CustomDebugStringConvertible +extension OutcomeWithSuccessfulness_Tests { + func test_CustomDebugStringConvertible() { + let outcomeWithSuccessfulness = OutcomeWithSuccessfulness(14, + successfulness: Successfulness(successes: 6, failures: 4)) + let expected = "OutcomeWithSuccessfulness(\(14), successfulness: \(String(reflecting: Successfulness(successes: 6, failures: 4))))" + + let result = String(reflecting: outcomeWithSuccessfulness) + + expect(result) == expected + } +} + +// MARK: - CustomStringConvertible +extension OutcomeWithSuccessfulness_Tests { + func test_CustomStringConvertible_onlySuccesses() { + let outcomeWithSuccessfulness = OutcomeWithSuccessfulness(14, + successfulness: Successfulness(successes: 6, failures: 0)) + let expected = "(\(outcomeWithSuccessfulness.outcome) with \(outcomeWithSuccessfulness.successfulness.rawDescription(surroundWithParentheses: false)))" + + let result = String(outcomeWithSuccessfulness) + + expect(result) == expected + } + + func test_CustomStringConvertible_onlyFailures() { + let outcomeWithSuccessfulness = OutcomeWithSuccessfulness(14, + successfulness: Successfulness(successes: 0, failures: 4)) + let expected = "(\(outcomeWithSuccessfulness.outcome) with \(outcomeWithSuccessfulness.successfulness.rawDescription(surroundWithParentheses: false)))" + + let result = String(outcomeWithSuccessfulness) + + expect(result) == expected + } + + func test_CustomStringConvertible_bothSuccessesAndFailures() { + let outcomeWithSuccessfulness = OutcomeWithSuccessfulness(14, + successfulness: Successfulness(successes: 6, failures: 4)) + let expected = "(\(outcomeWithSuccessfulness.outcome) with \(outcomeWithSuccessfulness.successfulness.rawDescription(surroundWithParentheses: false)))" + + let result = String(outcomeWithSuccessfulness) + + expect(result) == expected + } + + func test_CustomStringConvertible_noSuccessesOoFailures() { + let outcomeWithSuccessfulness = OutcomeWithSuccessfulness(14, + successfulness: Successfulness(successes: 0, failures: 0)) + let expected = "\(outcomeWithSuccessfulness.outcome)" + + let result = String(outcomeWithSuccessfulness) + + expect(result) == expected + } +} + +// MARK: - Equatable +extension OutcomeWithSuccessfulness_Tests { + + func test_shouldBeReflexive() { + property("reflexive") <- forAll { + (x: Int, y: Successfulness) in + + return EquatableTestUtilities.checkReflexive { OutcomeWithSuccessfulness(x, successfulness: y) } + } + } + + func test_shouldBeSymmetric() { + property("symmetric") <- forAll { + (x: Int, y: Successfulness) in + + return EquatableTestUtilities.checkSymmetric { OutcomeWithSuccessfulness(x, successfulness: y) } + } + } + + func test_shouldBeTransitive() { + property("transitive") <- forAll { + (x: Int, y: Successfulness) in + + return EquatableTestUtilities.checkTransitive { OutcomeWithSuccessfulness(x, successfulness: y) } + } + } + + func test_shouldBeAbleToNotEquate() { + property("non-equal") <- forAll { + (a: Int, b: Successfulness, c: Int, d: Successfulness) in + + return (a != c || b != d) ==> { + EquatableTestUtilities.checkNotEquate( + { OutcomeWithSuccessfulness(a, successfulness: b) }, + { OutcomeWithSuccessfulness(c, successfulness: d) } + ) + } + } + } +} + +// MARK: - Comparable +extension OutcomeWithSuccessfulness_Tests { + + func test_operator_lessThan() { + // Higher outcomes wins + expect(OutcomeWithSuccessfulness(9, successfulness: 14)) < OutcomeWithSuccessfulness(10, successfulness: 13) + + // Otherwise higher successfulness wins + expect(OutcomeWithSuccessfulness(10, successfulness: 12)) < OutcomeWithSuccessfulness(10, successfulness: 13) + } +} + +// MARK: - Hashable +extension OutcomeWithSuccessfulness_Tests { + + func test_hashValue() { + property("x == y then x.hashValue == y.hashValue") <- forAll { + (x: Int, y: Successfulness) in + + let a = OutcomeWithSuccessfulness(x, successfulness: y) + let b = OutcomeWithSuccessfulness(x, successfulness: y) + + return a.hashValue == b.hashValue + } + } +} + +// MARK: - AdditiveType, InvertibleAdditiveType, MultiplicativeType, InvertibleMultiplicativeType +extension OutcomeWithSuccessfulness_Tests { + + func test_additiveIdentity() { + property("x + 0 == x == 0 + x") <- forAll { + (x: Int, y: Successfulness) in + + let a = OutcomeWithSuccessfulness(x, successfulness: y) + + let a1 = a + .additiveIdentity + let a2 = .additiveIdentity + a + + return a == a1 && a == a2 + } + } + + func test_multiplicativeIdentity() { + property("x * 1 == x == 1 * x") <- forAll { + (x: Int, y: Successfulness) in + + let a = OutcomeWithSuccessfulness(x, successfulness: y) + + let a1 = a * .multiplicativeIdentity + let a2 = .multiplicativeIdentity * a + + return a == a1 && a == a2 + } + } + + func test_operator_add() { + let x = OutcomeWithSuccessfulness(5, + successfulness: Successfulness(successes: 0, failures: -1)) + let y = OutcomeWithSuccessfulness(-2, + successfulness: Successfulness(successes: 8, failures: 2)) + let expected = OutcomeWithSuccessfulness(x.outcome + y.outcome, + successfulness: x.successfulness + y.successfulness) + + let z = x + y + + expect(z) == expected + } + + func test_operator_subtract() { + let x = OutcomeWithSuccessfulness(5, + successfulness: Successfulness(successes: 0, failures: -1)) + let y = OutcomeWithSuccessfulness(-2, + successfulness: Successfulness(successes: 8, failures: 2)) + let expected = OutcomeWithSuccessfulness(x.outcome - y.outcome, + successfulness: x.successfulness - y.successfulness) + + let z = x - y + + expect(z) == expected + } + + func test_operator_multiply() { + let x = OutcomeWithSuccessfulness(5, + successfulness: Successfulness(successes: 0, failures: -1)) + let y = OutcomeWithSuccessfulness(-2, + successfulness: Successfulness(successes: 8, failures: 2)) + let expected = OutcomeWithSuccessfulness(x.outcome * y.outcome, + successfulness: x.successfulness * y.successfulness) + + let z = x * y + + expect(z) == expected + } + + func test_operator_divide() { + let x = OutcomeWithSuccessfulness(5, + successfulness: Successfulness(successes: 0, failures: -1)) + let y = OutcomeWithSuccessfulness(-2, + successfulness: Successfulness(successes: 8, failures: 2)) + let expected = OutcomeWithSuccessfulness(x.outcome / y.outcome, + successfulness: x.successfulness / y.successfulness) + + let z = x / y + + expect(z) == expected + } + + func test_operator_remainder() { + let x = OutcomeWithSuccessfulness(5, + successfulness: Successfulness(successes: 0, failures: -1)) + let y = OutcomeWithSuccessfulness(-2, + successfulness: Successfulness(successes: 8, failures: 2)) + let expected = OutcomeWithSuccessfulness(x.outcome % y.outcome, + successfulness: x.successfulness % y.successfulness) + + let z = x % y + + expect(z) == expected + } +} + +// MARK: - FrequencyDistributionOutcomeType +extension OutcomeWithSuccessfulness_Tests { + + func test_multiplierEquivalent() { + property("multiplierEquivalent") <- forAll { + (x: Int, y: Successfulness) in + + let outcomeWithSuccessfulness = OutcomeWithSuccessfulness(x, successfulness: y) + let multiplierEquivalent = x + + return outcomeWithSuccessfulness.multiplierEquivalent == multiplierEquivalent + } + } +} diff --git a/DiceKitTests/Successfulness_Tests.swift b/DiceKitTests/Successfulness_Tests.swift new file mode 100644 index 0000000..6530a7f --- /dev/null +++ b/DiceKitTests/Successfulness_Tests.swift @@ -0,0 +1,257 @@ +// +// Successfulness_Tests.swift +// DiceKit +// +// Created by Brentley Jones on 10/25/15. +// Copyright © 2015 Brentley Jones. All rights reserved. +// + +import XCTest +import Nimble +import SwiftCheck + +import DiceKit + +class Successfulness_Tests: XCTestCase { +} + +// MARK: - Initialization +extension Successfulness_Tests { + func test_init() { + property("init") <- forAll { + (successes: Int, failures: Int) in + + let successfulness = Successfulness(successes: successes, failures: failures) + + let successesTest = successfulness.rawSuccesses == successes + let failuresTest = successfulness.rawFailures == failures + + return successesTest && failuresTest + } + } + + func test_init_integerLiteral() { + property("init integerLiteral") <- forAll { + (x: Int) in + + let expectedSuccessed = x > 0 ? x : 0 + let expectedFailures = x < 0 ? -x : 0 + + let successfulness = Successfulness(integerLiteral: x) + + let successesTest = successfulness.successes == expectedSuccessed + let failuresTest = successfulness.failures == expectedFailures + + return successesTest && failuresTest + } + } +} + +// MARK: - CustomDebugStringConvertible +extension Successfulness_Tests { + func test_CustomDebugStringConvertible() { + let successfulness = Successfulness(successes: 6, failures: 4) + let expected = "Successfulness(rawSuccesses: 6, rawFailures: 4)" + + let result = String(reflecting: successfulness) + + expect(result) == expected + } +} + +// MARK: - CustomStringConvertible +extension Successfulness_Tests { + func test_CustomStringConvertible_onlySuccesses() { + let successfulness = Successfulness(successes: 6, failures: 0) + let expected = "(6 Successes)" + + let result = String(successfulness) + + expect(result) == expected + } + + func test_CustomStringConvertible_onlyFailures() { + let successfulness = Successfulness(successes: 0, failures: 4) + let expected = "(4 Failures)" + + let result = String(successfulness) + + expect(result) == expected + } + + func test_CustomStringConvertible_bothSuccessesAndFailures() { + let successfulness = Successfulness(successes: 6, failures: 4) + let expected = "(6 Successes and 4 Failures)" + + let result = String(successfulness) + + expect(result) == expected + } + + func test_CustomStringConvertible_noSuccessesOoFailures() { + let successfulness = Successfulness(successes: 0, failures: 0) + let expected = "(0 Successes)" + + let result = String(successfulness) + + expect(result) == expected + } +} + +// MARK: - Equatable +extension Successfulness_Tests { + func test_shouldBeReflexive() { + property("reflexive") <- forAll { + (x: Int, y: Int) in + + return EquatableTestUtilities.checkReflexive { Successfulness(successes: x, failures: y) } + } + } + + func test_shouldBeSymmetric() { + property("symmetric") <- forAll { + (x: Int, y: Int) in + + return EquatableTestUtilities.checkSymmetric { Successfulness(successes: x, failures: y) } + } + } + + func test_shouldBeTransitive() { + property("transitive") <- forAll { + (x: Int, y: Int) in + + return EquatableTestUtilities.checkTransitive { Successfulness(successes: x, failures: y) } + } + } + + func test_shouldBeAbleToNotEquate() { + property("non-equal") <- forAll { + (a: Int, b: Int, c: Int, d: Int) in + + return (a != c || b != d) ==> { + EquatableTestUtilities.checkNotEquate( + { Successfulness(successes: a, failures: b) }, + { Successfulness(successes: c, failures: d) } + ) + } + } + } +} + +// MARK: - Comparable +extension Successfulness_Tests { + func test_operator_lessThan() { + // Higher combinded (successes - failures) wins + expect(Successfulness(successes: 10, failures: 9)) < Successfulness(successes: 9, failures: 7) + + // Otherwise higher successes/lower failures wins + expect(Successfulness(successes: 10, failures: 9)) < Successfulness(successes: 11, failures: 10) + } +} + +// MARK: - Hashable +extension Successfulness_Tests { + func test_hashValue() { + property("x == y then x.hashValue == y.hashValue") <- forAll { + (x: Int, y: Int) in + + let a = Successfulness(successes: x, failures: y) + let b = Successfulness(successes: x, failures: y) + + return a.hashValue == b.hashValue + } + } +} + +// MARK: - AdditiveType, InvertibleAdditiveType, MultiplicativeType, InvertibleMultiplicativeType +extension Successfulness_Tests { + func test_additiveIdentity() { + property("x + 0 == x == 0 + x") <- forAll { + (x: Int, y: Int) in + + let a = Successfulness(successes: x, failures: y) + + let a1 = a + .additiveIdentity + let a2 = .additiveIdentity + a + + return a == a1 && a == a2 + } + } + + func test_multiplicativeIdentity() { + property("x * 1 == x == 1 * x") <- forAll { + (x: Int, y: Int) in + + let a = Successfulness(successes: x, failures: y) + + let a1 = a * .multiplicativeIdentity + let a2 = .multiplicativeIdentity * a + + return a == a1 && a == a2 + } + } + + func test_operator_add() { + let x = Successfulness(successes: 3, failures: 1) + let y = Successfulness(successes: -1, failures: 4) + let expected = Successfulness(successes: 2, failures: 5) + + let z = x + y + + expect(z) == expected + } + + func test_operator_subtract() { + let x = Successfulness(successes: 3, failures: 1) + let y = Successfulness(successes: -1, failures: 4) + let expected = Successfulness(successes: 4, failures: -3) + + let z = x - y + + expect(z) == expected + } + + func test_operator_multiply() { + let x = Successfulness(successes: 3, failures: 1) + let y = Successfulness(successes: -1, failures: 4) + let expected = Successfulness(successes: -3, failures: 4) + + let z = x * y + + expect(z) == expected + } + + func test_operator_divide() { + let x = Successfulness(successes: 3, failures: 1) + let y = Successfulness(successes: -1, failures: 4) + let expected = Successfulness(successes: -3, failures: 0) + + let z = x / y + + expect(z) == expected + } + + func test_operator_remainder() { + let x = Successfulness(successes: 3, failures: 1) + let y = Successfulness(successes: -1, failures: 4) + let expected = Successfulness(successes: 0, failures: 1) + + let z = x % y + + expect(z) == expected + } +} + +// MARK: - FrequencyDistributionOutcomeType +extension Successfulness_Tests { + func test_multiplierEquivalent() { + property("multiplierEquivalent") <- forAll { + (x: Int, y: Int) in + + let successfulness = Successfulness(successes: x, failures: y) + let multiplierEquivalent = max(0,x) - max(0,y) + + return successfulness.multiplierEquivalent == multiplierEquivalent + } + } +}