From 45140ad6bfeed8f64e4cc2a1ac907a5439ffb326 Mon Sep 17 00:00:00 2001 From: Maciej Matyjas Date: Thu, 24 Mar 2016 10:04:11 +0000 Subject: [PATCH 1/3] V commit --- roman-numbers.playground/Contents.swift | 6 ++++-- run-it.sh | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100755 run-it.sh diff --git a/roman-numbers.playground/Contents.swift b/roman-numbers.playground/Contents.swift index 313d2b8..445d011 100644 --- a/roman-numbers.playground/Contents.swift +++ b/roman-numbers.playground/Contents.swift @@ -1,6 +1,8 @@ //: Convert to and from Roman numerals +//: - [GitHub](https://github.com/HcomCoolCode/romans) +//: - [Stash](http://stash.hcom/users/mmatyjas/repos/romans/browse) -import UIKit +import Foundation func romanToNum(romanNumber: String) -> Int { return romanNumber.characters.count @@ -21,4 +23,4 @@ assert(romanToNum(numToRoman(three)) == three, "3 -> III -> 3") assert(numToRoman(romanToNum(III)) == III, "III -> 3 -> III") let IV = "IV" -assert(romanToNum(IV) == 4, "IV is four") \ No newline at end of file +assert(romanToNum(IV) == 4, "IV is four") diff --git a/run-it.sh b/run-it.sh new file mode 100755 index 0000000..3ef84e6 --- /dev/null +++ b/run-it.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +swift roman-numbers.playground/Contents.swift From 4dfe7750edf23843cd67485f2c73001b1feafd93 Mon Sep 17 00:00:00 2001 From: Michael Voong Date: Fri, 25 Mar 2016 15:49:11 +0000 Subject: [PATCH 2/3] Simple to understand implementation --- Roman/Roman.xcodeproj/project.pbxproj | 356 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + Roman/Roman/KeyboardUtils.swift | 17 + Roman/Roman/RomanNumeralsConverter.swift | 95 +++++ Roman/Roman/main.swift | 20 + Roman/RomanTests/Info.plist | 24 ++ .../RomanNumeralsConverterTests.swift | 111 ++++++ 7 files changed, 630 insertions(+) create mode 100644 Roman/Roman.xcodeproj/project.pbxproj create mode 100644 Roman/Roman.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Roman/Roman/KeyboardUtils.swift create mode 100644 Roman/Roman/RomanNumeralsConverter.swift create mode 100644 Roman/Roman/main.swift create mode 100644 Roman/RomanTests/Info.plist create mode 100644 Roman/RomanTests/RomanNumeralsConverterTests.swift diff --git a/Roman/Roman.xcodeproj/project.pbxproj b/Roman/Roman.xcodeproj/project.pbxproj new file mode 100644 index 0000000..4a758aa --- /dev/null +++ b/Roman/Roman.xcodeproj/project.pbxproj @@ -0,0 +1,356 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 271BE6611CA5D360003965B7 /* RomanNumeralsConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271BE6601CA5D360003965B7 /* RomanNumeralsConverter.swift */; }; + 271BE6631CA5D3DE003965B7 /* RomanNumeralsConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271BE6621CA5D3DE003965B7 /* RomanNumeralsConverterTests.swift */; }; + 271BE6641CA5D476003965B7 /* RomanNumeralsConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271BE6601CA5D360003965B7 /* RomanNumeralsConverter.swift */; }; + 271BE66A1CA600B2003965B7 /* KeyboardUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271BE6691CA600B2003965B7 /* KeyboardUtils.swift */; }; + 4DCD25D91CA402C000000D07 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DCD25D81CA402C000000D07 /* main.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 4DCD25D31CA402C000000D07 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 271BE6601CA5D360003965B7 /* RomanNumeralsConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RomanNumeralsConverter.swift; sourceTree = ""; }; + 271BE6621CA5D3DE003965B7 /* RomanNumeralsConverterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RomanNumeralsConverterTests.swift; sourceTree = ""; }; + 271BE6691CA600B2003965B7 /* KeyboardUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardUtils.swift; sourceTree = ""; }; + 4DCD25D51CA402C000000D07 /* Roman */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Roman; sourceTree = BUILT_PRODUCTS_DIR; }; + 4DCD25D81CA402C000000D07 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + 4DCD25E31CA402D700000D07 /* RomanTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RomanTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 4DCD25E71CA402D700000D07 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4DCD25EB1CA4031400000D07 /* roman-numbers.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = "roman-numbers.playground"; path = "../roman-numbers.playground"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4DCD25D21CA402C000000D07 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4DCD25E01CA402D700000D07 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4DCD25CC1CA402C000000D07 = { + isa = PBXGroup; + children = ( + 4DCD25EB1CA4031400000D07 /* roman-numbers.playground */, + 4DCD25D71CA402C000000D07 /* Roman */, + 4DCD25E41CA402D700000D07 /* RomanTests */, + 4DCD25D61CA402C000000D07 /* Products */, + ); + sourceTree = ""; + }; + 4DCD25D61CA402C000000D07 /* Products */ = { + isa = PBXGroup; + children = ( + 4DCD25D51CA402C000000D07 /* Roman */, + 4DCD25E31CA402D700000D07 /* RomanTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 4DCD25D71CA402C000000D07 /* Roman */ = { + isa = PBXGroup; + children = ( + 4DCD25D81CA402C000000D07 /* main.swift */, + 271BE6601CA5D360003965B7 /* RomanNumeralsConverter.swift */, + 271BE6691CA600B2003965B7 /* KeyboardUtils.swift */, + ); + path = Roman; + sourceTree = ""; + }; + 4DCD25E41CA402D700000D07 /* RomanTests */ = { + isa = PBXGroup; + children = ( + 4DCD25E71CA402D700000D07 /* Info.plist */, + 271BE6621CA5D3DE003965B7 /* RomanNumeralsConverterTests.swift */, + ); + path = RomanTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4DCD25D41CA402C000000D07 /* Roman */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4DCD25DC1CA402C000000D07 /* Build configuration list for PBXNativeTarget "Roman" */; + buildPhases = ( + 4DCD25D11CA402C000000D07 /* Sources */, + 4DCD25D21CA402C000000D07 /* Frameworks */, + 4DCD25D31CA402C000000D07 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Roman; + productName = Roman; + productReference = 4DCD25D51CA402C000000D07 /* Roman */; + productType = "com.apple.product-type.tool"; + }; + 4DCD25E21CA402D700000D07 /* RomanTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4DCD25E81CA402D700000D07 /* Build configuration list for PBXNativeTarget "RomanTests" */; + buildPhases = ( + 4DCD25DF1CA402D700000D07 /* Sources */, + 4DCD25E01CA402D700000D07 /* Frameworks */, + 4DCD25E11CA402D700000D07 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RomanTests; + productName = RomanTests; + productReference = 4DCD25E31CA402D700000D07 /* RomanTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4DCD25CD1CA402C000000D07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0730; + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = Hotels.com; + TargetAttributes = { + 4DCD25D41CA402C000000D07 = { + CreatedOnToolsVersion = 7.3; + }; + 4DCD25E21CA402D700000D07 = { + CreatedOnToolsVersion = 7.3; + }; + }; + }; + buildConfigurationList = 4DCD25D01CA402C000000D07 /* Build configuration list for PBXProject "Roman" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 4DCD25CC1CA402C000000D07; + productRefGroup = 4DCD25D61CA402C000000D07 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4DCD25D41CA402C000000D07 /* Roman */, + 4DCD25E21CA402D700000D07 /* RomanTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4DCD25E11CA402D700000D07 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4DCD25D11CA402C000000D07 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 271BE6611CA5D360003965B7 /* RomanNumeralsConverter.swift in Sources */, + 271BE66A1CA600B2003965B7 /* KeyboardUtils.swift in Sources */, + 4DCD25D91CA402C000000D07 /* main.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4DCD25DF1CA402D700000D07 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 271BE6631CA5D3DE003965B7 /* RomanNumeralsConverterTests.swift in Sources */, + 271BE6641CA5D476003965B7 /* RomanNumeralsConverter.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 4DCD25DA1CA402C000000D07 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 4DCD25DB1CA402C000000D07 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 4DCD25DD1CA402C000000D07 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 4DCD25DE1CA402C000000D07 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 4DCD25E91CA402D700000D07 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = RomanTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.hotels.RomanTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 4DCD25EA1CA402D700000D07 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = RomanTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.hotels.RomanTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4DCD25D01CA402C000000D07 /* Build configuration list for PBXProject "Roman" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4DCD25DA1CA402C000000D07 /* Debug */, + 4DCD25DB1CA402C000000D07 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4DCD25DC1CA402C000000D07 /* Build configuration list for PBXNativeTarget "Roman" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4DCD25DD1CA402C000000D07 /* Debug */, + 4DCD25DE1CA402C000000D07 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4DCD25E81CA402D700000D07 /* Build configuration list for PBXNativeTarget "RomanTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4DCD25E91CA402D700000D07 /* Debug */, + 4DCD25EA1CA402D700000D07 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4DCD25CD1CA402C000000D07 /* Project object */; +} diff --git a/Roman/Roman.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Roman/Roman.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..3842fbc --- /dev/null +++ b/Roman/Roman.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Roman/Roman/KeyboardUtils.swift b/Roman/Roman/KeyboardUtils.swift new file mode 100644 index 0000000..47bbb80 --- /dev/null +++ b/Roman/Roman/KeyboardUtils.swift @@ -0,0 +1,17 @@ +// +// KeyboardUtils.swift +// Roman +// +// Created by Michael Voong on 25/03/2016. +// Copyright © 2016 Hotels.com. All rights reserved. +// + +import Foundation + +func getKeyboardInput() -> String { + let keyboard = NSFileHandle.fileHandleWithStandardInput() + let inputData = keyboard.availableData + let string = NSString(data: inputData, encoding:NSUTF8StringEncoding) as! String + + return string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) +} \ No newline at end of file diff --git a/Roman/Roman/RomanNumeralsConverter.swift b/Roman/Roman/RomanNumeralsConverter.swift new file mode 100644 index 0000000..40b9c3f --- /dev/null +++ b/Roman/Roman/RomanNumeralsConverter.swift @@ -0,0 +1,95 @@ +// +// ArabicToRomanNumeralsConverter.swift +// Roman +// +// Created by Michael Voong on 25/03/2016. +// Copyright © 2016 Hotels.com. All rights reserved. +// + +import Foundation + +typealias NumeralPair = (value: UInt, numeral: String) + +let latinToRomainPairs: [NumeralPair] = + [(1000, "M"), + (1000, "M"), + (1000, "M"), + (1000, "M"), + (900, "CM"), + (500, "D"), + (500, "D"), + (500, "D"), + (400, "CD"), + (100, "C"), + (100, "C"), + (100, "C"), + (90, "XC"), + (50, "L"), + (50, "L"), + (50, "L"), + (40, "XL"), + (10, "X"), + (10, "X"), + (10, "X"), + (9, "IX"), + (5, "V"), + (5, "V"), + (5, "V"), + (4, "IV"), + (1, "I"), + (1, "I"), + (1, "I")] + +enum Error: ErrorType { + case InvalidInput +} + +func arabicToRomanNumerals(number: UInt) throws -> String { + guard number > 0 else { + throw Error.InvalidInput + } + + var remainingNumber = number + var numerals = "" + + for pair in latinToRomainPairs { + if pair.value <= remainingNumber { + remainingNumber -= pair.value + numerals += pair.numeral + if remainingNumber == 0 { + break + } + } + } + + if remainingNumber > 0 { + throw Error.InvalidInput + } + + return numerals +} + +func romanNumeralsToArabic(numerals: String) throws -> UInt { + guard numerals.characters.count > 0 else { + throw Error.InvalidInput + } + + var value = UInt(0) + let scanner = NSScanner(string: numerals) + scanner.charactersToBeSkipped = nil + + for pair in latinToRomainPairs { + if scanner.scanString(pair.numeral, intoString: nil) { + value += pair.value + if scanner.atEnd { + break + } + } + } + + if !scanner.atEnd { + throw Error.InvalidInput + } + + return value +} diff --git a/Roman/Roman/main.swift b/Roman/Roman/main.swift new file mode 100644 index 0000000..b17c3dc --- /dev/null +++ b/Roman/Roman/main.swift @@ -0,0 +1,20 @@ +// +// main.swift +// Roman +// +// Created by Michael Voong on 24/03/2016. +// Copyright © 2016 Hotels.com. All rights reserved. +// + +import Foundation + +while (true) { + do { + let input = getKeyboardInput() + print(try romanNumeralsToArabic(input)) +// print(try arabicToRomanNumerals(UInt(input) ?? 0)) + } catch { + print("Invalid input") + } +} + diff --git a/Roman/RomanTests/Info.plist b/Roman/RomanTests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/Roman/RomanTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Roman/RomanTests/RomanNumeralsConverterTests.swift b/Roman/RomanTests/RomanNumeralsConverterTests.swift new file mode 100644 index 0000000..930a25c --- /dev/null +++ b/Roman/RomanTests/RomanNumeralsConverterTests.swift @@ -0,0 +1,111 @@ +// +// RomanNumeralsConverterTests.swift +// Roman +// +// Created by Michael Voong on 25/03/2016. +// Copyright © 2016 Hotels.com. All rights reserved. +// + +import XCTest + +class RomanNumeralsConverterTests: XCTestCase { + // Arabic to numerals + func testArabicToRomanNumerals_simpleCases() { + XCTAssertEqual(try arabicToRomanNumerals(30), "XXX") + XCTAssertEqual(try arabicToRomanNumerals(4000), "MMMM") + XCTAssertEqual(try arabicToRomanNumerals(4010), "MMMMX") + } + + func testArabicToRomanNumerals_subtraction() { + XCTAssertEqual(try arabicToRomanNumerals(4), "IV") + XCTAssertEqual(try arabicToRomanNumerals(9), "IX") + XCTAssertEqual(try arabicToRomanNumerals(556), "DLVI") + XCTAssertEqual(try arabicToRomanNumerals(698), "DCXCVIII") + XCTAssertEqual(try arabicToRomanNumerals(4999), "MMMMCMXCIX") + } + + func testArabicToRomanNumerals_error() { + XCTAssertThrowsError(try arabicToRomanNumerals(41004)) + XCTAssertThrowsError(try arabicToRomanNumerals(0)) + } + + func testArabicToRomanNumerals_performance() { + measureBlock { + do { + for _ in 0..<100 { + XCTAssertEqual(try! arabicToRomanNumerals(30), "XXX") + XCTAssertEqual(try! arabicToRomanNumerals(4000), "MMMM") + XCTAssertEqual(try! arabicToRomanNumerals(4010), "MMMMX") + XCTAssertEqual(try! arabicToRomanNumerals(4), "IV") + XCTAssertEqual(try! arabicToRomanNumerals(9), "IX") + XCTAssertEqual(try! arabicToRomanNumerals(556), "DLVI") + XCTAssertEqual(try! arabicToRomanNumerals(698), "DCXCVIII") + XCTAssertEqual(try! arabicToRomanNumerals(4999), "MMMMCMXCIX") + } + } + } + } + + // Numerals to Arabic + func testRomanNumeralsToArabic_simpleCases() { + XCTAssertTrue(try romanNumeralsToArabic("I") == 1) + XCTAssertTrue(try romanNumeralsToArabic("II") == 2) + XCTAssertTrue(try romanNumeralsToArabic("III") == 3) + XCTAssertTrue(try romanNumeralsToArabic("IV") == 4) + XCTAssertTrue(try romanNumeralsToArabic("V") == 5) + XCTAssertTrue(try romanNumeralsToArabic("VI") == 6) + XCTAssertTrue(try romanNumeralsToArabic("VII") == 7) + XCTAssertTrue(try romanNumeralsToArabic("VIII") == 8) + XCTAssertTrue(try romanNumeralsToArabic("IX") == 9) + XCTAssertTrue(try romanNumeralsToArabic("X") == 10) + XCTAssertTrue(try romanNumeralsToArabic("XI") == 11) + XCTAssertTrue(try romanNumeralsToArabic("XII") == 12) + XCTAssertTrue(try romanNumeralsToArabic("XLV") == 45) // 10,50,5 + } + + func testRomanNumeralsToArabic_moreComplicatedCases() { + XCTAssertTrue(try romanNumeralsToArabic("DCCC") == 800) + XCTAssertTrue(try romanNumeralsToArabic("CCXXVI") == 226) + XCTAssertTrue(try romanNumeralsToArabic("MDCCXII") == 1712) + XCTAssertTrue(try romanNumeralsToArabic("XCIX") == 99) // 10,100,1,10 + XCTAssertTrue(try romanNumeralsToArabic("CDIV") == 404) + XCTAssertTrue(try romanNumeralsToArabic("CMLXXXVIII") == 988) // 100,1000,50,30,8 + XCTAssertTrue(try romanNumeralsToArabic("MCMXIV") == 1914) + XCTAssertTrue(try romanNumeralsToArabic("MMXVI") == 2016) + XCTAssertTrue(try romanNumeralsToArabic("MCMXCVI") == 1996) + XCTAssertTrue(try romanNumeralsToArabic("MMDLXIX") == 2569) + XCTAssertTrue(try romanNumeralsToArabic("MMMM") == 4000) // 4 M's is fine + XCTAssertTrue(try romanNumeralsToArabic("MMMMC") == 4100) + XCTAssertTrue(try romanNumeralsToArabic("MMMMCMXCIX") == 4999) // Largest representable + } + + func testRomanNumberalsToArabic_error() { + XCTAssertThrowsError(try romanNumeralsToArabic("HELLO")) + XCTAssertThrowsError(try romanNumeralsToArabic(" ASD")) + XCTAssertThrowsError(try romanNumeralsToArabic(" ")) + XCTAssertThrowsError(try romanNumeralsToArabic(",")) + XCTAssertThrowsError(try romanNumeralsToArabic("")) + } + + func testRomanNumberalsToArabic_performance() { + measureBlock { + do { + for _ in 0..<100 { + XCTAssertTrue(try! romanNumeralsToArabic("DCCC") == 800) + XCTAssertTrue(try! romanNumeralsToArabic("CCXXVI") == 226) + XCTAssertTrue(try! romanNumeralsToArabic("MDCCXII") == 1712) + XCTAssertTrue(try! romanNumeralsToArabic("XCIX") == 99) // 10,100,1,10 + XCTAssertTrue(try! romanNumeralsToArabic("CDIV") == 404) + XCTAssertTrue(try! romanNumeralsToArabic("CMLXXXVIII") == 988) // 100,1000,50,30,8 + XCTAssertTrue(try! romanNumeralsToArabic("MCMXIV") == 1914) + XCTAssertTrue(try! romanNumeralsToArabic("MMXVI") == 2016) + XCTAssertTrue(try! romanNumeralsToArabic("MCMXCVI") == 1996) + XCTAssertTrue(try! romanNumeralsToArabic("MMDLXIX") == 2569) + XCTAssertTrue(try! romanNumeralsToArabic("MMMM") == 4000) // 4 M's is fine + XCTAssertTrue(try! romanNumeralsToArabic("MMMMC") == 4100) + XCTAssertTrue(try! romanNumeralsToArabic("MMMMCMXCIX") == 4999) // Largest representable + } + } + } + } +} From be0f6fa74e6580186f20ab4e894e604724f39855 Mon Sep 17 00:00:00 2001 From: Michael Voong Date: Thu, 31 Mar 2016 21:11:20 +0100 Subject: [PATCH 3/3] Transitive case and tests --- Roman/Roman/RomanNumeralsConverter.swift | 15 ++++++++++----- Roman/Roman/main.swift | 5 +++-- .../RomanTests/RomanNumeralsConverterTests.swift | 8 ++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Roman/Roman/RomanNumeralsConverter.swift b/Roman/Roman/RomanNumeralsConverter.swift index 40b9c3f..4f0b3e0 100644 --- a/Roman/Roman/RomanNumeralsConverter.swift +++ b/Roman/Roman/RomanNumeralsConverter.swift @@ -41,12 +41,12 @@ let latinToRomainPairs: [NumeralPair] = (1, "I")] enum Error: ErrorType { - case InvalidInput + case InvalidInput(reason: String) } func arabicToRomanNumerals(number: UInt) throws -> String { guard number > 0 else { - throw Error.InvalidInput + throw Error.InvalidInput(reason: "Number cannot be 0") } var remainingNumber = number @@ -63,7 +63,7 @@ func arabicToRomanNumerals(number: UInt) throws -> String { } if remainingNumber > 0 { - throw Error.InvalidInput + throw Error.InvalidInput(reason: "Invalid number") } return numerals @@ -71,7 +71,7 @@ func arabicToRomanNumerals(number: UInt) throws -> String { func romanNumeralsToArabic(numerals: String) throws -> UInt { guard numerals.characters.count > 0 else { - throw Error.InvalidInput + throw Error.InvalidInput(reason: "Must not pass empty string") } var value = UInt(0) @@ -88,7 +88,12 @@ func romanNumeralsToArabic(numerals: String) throws -> UInt { } if !scanner.atEnd { - throw Error.InvalidInput + throw Error.InvalidInput(reason: "Invalid numerals") + } + + let correctNumerals = try arabicToRomanNumerals(value) + if correctNumerals.caseInsensitiveCompare(numerals) != .OrderedSame { + throw Error.InvalidInput(reason: "Numerals for \(value) should be \(correctNumerals)") } return value diff --git a/Roman/Roman/main.swift b/Roman/Roman/main.swift index b17c3dc..496d0ff 100644 --- a/Roman/Roman/main.swift +++ b/Roman/Roman/main.swift @@ -11,10 +11,11 @@ import Foundation while (true) { do { let input = getKeyboardInput() + print(try romanNumeralsToArabic(input)) // print(try arabicToRomanNumerals(UInt(input) ?? 0)) - } catch { - print("Invalid input") + } catch Error.InvalidInput(let message) { + print("Invalid input: \(message)") } } diff --git a/Roman/RomanTests/RomanNumeralsConverterTests.swift b/Roman/RomanTests/RomanNumeralsConverterTests.swift index 930a25c..801038c 100644 --- a/Roman/RomanTests/RomanNumeralsConverterTests.swift +++ b/Roman/RomanTests/RomanNumeralsConverterTests.swift @@ -108,4 +108,12 @@ class RomanNumeralsConverterTests: XCTestCase { } } } + + // Mixed + + func testTransitive() { + for i: UInt in 1...4999 { + XCTAssertEqual(try romanNumeralsToArabic(arabicToRomanNumerals(i)), i) + } + } }