├── Calculator.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Calculator ├── AppDelegate.swift ├── AxesDrawer.swift ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── CalculatorBrain.swift ├── CalculatorViewController.swift ├── GraphView.swift ├── GraphViewController.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── LaunchImage.launchimage │ │ └── Contents.json │ ├── graph-highlighted.imageset │ │ ├── Contents.json │ │ ├── graph-highlighted.png │ │ ├── graph-highlighted@2x.png │ │ └── graph-highlighted@3x.png │ └── graph.imageset │ │ ├── Contents.json │ │ ├── graph.png │ │ ├── graph@2x.png │ │ └── graph@3x.png ├── Info.plist └── StatisticsViewController.swift ├── CalculatorTests ├── CalculatorTests.swift └── Info.plist └── README.md /Calculator.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5B52A8F81A79022500D6D166 /* CalculatorBrain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B52A8F71A79022500D6D166 /* CalculatorBrain.swift */; }; 11 | 5B77EEE11A9BCB7100CA4CC6 /* GraphViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B77EEE01A9BCB7100CA4CC6 /* GraphViewController.swift */; }; 12 | 5B83AB8A1A76819000D713E5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B83AB891A76819000D713E5 /* AppDelegate.swift */; }; 13 | 5B83AB8C1A76819000D713E5 /* CalculatorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B83AB8B1A76819000D713E5 /* CalculatorViewController.swift */; }; 14 | 5B83AB8F1A76819000D713E5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5B83AB8D1A76819000D713E5 /* Main.storyboard */; }; 15 | 5B83AB911A76819000D713E5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5B83AB901A76819000D713E5 /* Images.xcassets */; }; 16 | 5B83AB941A76819000D713E5 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B83AB921A76819000D713E5 /* LaunchScreen.xib */; }; 17 | 5B83ABA01A76819000D713E5 /* CalculatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B83AB9F1A76819000D713E5 /* CalculatorTests.swift */; }; 18 | 5B908F2C1A8FAAB3002BFA3B /* CalculatorBrain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B52A8F71A79022500D6D166 /* CalculatorBrain.swift */; }; 19 | 5B9A1E6B1AA4B9B2003CFE1F /* StatisticsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9A1E6A1AA4B9B2003CFE1F /* StatisticsViewController.swift */; }; 20 | 5BE2C6831A9E6429009760CB /* AxesDrawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE2C6821A9E6429009760CB /* AxesDrawer.swift */; }; 21 | 5BE2C6851A9E683E009760CB /* GraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE2C6841A9E683E009760CB /* GraphView.swift */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | 5B83AB9A1A76819000D713E5 /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = 5B83AB7C1A76819000D713E5 /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = 5B83AB831A76819000D713E5; 30 | remoteInfo = Calculator; 31 | }; 32 | /* End PBXContainerItemProxy section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 5B52A8F71A79022500D6D166 /* CalculatorBrain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalculatorBrain.swift; sourceTree = ""; }; 36 | 5B77EEE01A9BCB7100CA4CC6 /* GraphViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphViewController.swift; sourceTree = ""; }; 37 | 5B83AB841A76819000D713E5 /* Calculator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Calculator.app; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 5B83AB881A76819000D713E5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | 5B83AB891A76819000D713E5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 40 | 5B83AB8B1A76819000D713E5 /* CalculatorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorViewController.swift; sourceTree = ""; }; 41 | 5B83AB8E1A76819000D713E5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 5B83AB901A76819000D713E5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 43 | 5B83AB931A76819000D713E5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 44 | 5B83AB991A76819000D713E5 /* CalculatorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CalculatorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 5B83AB9E1A76819000D713E5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | 5B83AB9F1A76819000D713E5 /* CalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorTests.swift; sourceTree = ""; }; 47 | 5B83ABA91A76945A00D713E5 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 48 | 5B9A1E6A1AA4B9B2003CFE1F /* StatisticsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatisticsViewController.swift; sourceTree = ""; }; 49 | 5BE2C6821A9E6429009760CB /* AxesDrawer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AxesDrawer.swift; sourceTree = ""; }; 50 | 5BE2C6841A9E683E009760CB /* GraphView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphView.swift; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 5B83AB811A76819000D713E5 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | 5B83AB961A76819000D713E5 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 5B83AB7B1A76819000D713E5 = { 72 | isa = PBXGroup; 73 | children = ( 74 | 5B83ABA91A76945A00D713E5 /* README.md */, 75 | 5B83AB861A76819000D713E5 /* Calculator */, 76 | 5B83AB9C1A76819000D713E5 /* CalculatorTests */, 77 | 5B83AB851A76819000D713E5 /* Products */, 78 | ); 79 | sourceTree = ""; 80 | }; 81 | 5B83AB851A76819000D713E5 /* Products */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 5B83AB841A76819000D713E5 /* Calculator.app */, 85 | 5B83AB991A76819000D713E5 /* CalculatorTests.xctest */, 86 | ); 87 | name = Products; 88 | sourceTree = ""; 89 | }; 90 | 5B83AB861A76819000D713E5 /* Calculator */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 5B52A8F71A79022500D6D166 /* CalculatorBrain.swift */, 94 | 5B83AB8B1A76819000D713E5 /* CalculatorViewController.swift */, 95 | 5BE2C6821A9E6429009760CB /* AxesDrawer.swift */, 96 | 5BE2C6841A9E683E009760CB /* GraphView.swift */, 97 | 5B77EEE01A9BCB7100CA4CC6 /* GraphViewController.swift */, 98 | 5B9A1E6A1AA4B9B2003CFE1F /* StatisticsViewController.swift */, 99 | 5B83AB8D1A76819000D713E5 /* Main.storyboard */, 100 | 5B83AB901A76819000D713E5 /* Images.xcassets */, 101 | 5B83AB871A76819000D713E5 /* Supporting Files */, 102 | ); 103 | path = Calculator; 104 | sourceTree = ""; 105 | }; 106 | 5B83AB871A76819000D713E5 /* Supporting Files */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 5B83AB921A76819000D713E5 /* LaunchScreen.xib */, 110 | 5B83AB891A76819000D713E5 /* AppDelegate.swift */, 111 | 5B83AB881A76819000D713E5 /* Info.plist */, 112 | ); 113 | name = "Supporting Files"; 114 | sourceTree = ""; 115 | }; 116 | 5B83AB9C1A76819000D713E5 /* CalculatorTests */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 5B83AB9F1A76819000D713E5 /* CalculatorTests.swift */, 120 | 5B83AB9D1A76819000D713E5 /* Supporting Files */, 121 | ); 122 | path = CalculatorTests; 123 | sourceTree = ""; 124 | }; 125 | 5B83AB9D1A76819000D713E5 /* Supporting Files */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 5B83AB9E1A76819000D713E5 /* Info.plist */, 129 | ); 130 | name = "Supporting Files"; 131 | sourceTree = ""; 132 | }; 133 | /* End PBXGroup section */ 134 | 135 | /* Begin PBXNativeTarget section */ 136 | 5B83AB831A76819000D713E5 /* Calculator */ = { 137 | isa = PBXNativeTarget; 138 | buildConfigurationList = 5B83ABA31A76819000D713E5 /* Build configuration list for PBXNativeTarget "Calculator" */; 139 | buildPhases = ( 140 | 5B83AB801A76819000D713E5 /* Sources */, 141 | 5B83AB811A76819000D713E5 /* Frameworks */, 142 | 5B83AB821A76819000D713E5 /* Resources */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = Calculator; 149 | productName = Calculator; 150 | productReference = 5B83AB841A76819000D713E5 /* Calculator.app */; 151 | productType = "com.apple.product-type.application"; 152 | }; 153 | 5B83AB981A76819000D713E5 /* CalculatorTests */ = { 154 | isa = PBXNativeTarget; 155 | buildConfigurationList = 5B83ABA61A76819000D713E5 /* Build configuration list for PBXNativeTarget "CalculatorTests" */; 156 | buildPhases = ( 157 | 5B83AB951A76819000D713E5 /* Sources */, 158 | 5B83AB961A76819000D713E5 /* Frameworks */, 159 | 5B83AB971A76819000D713E5 /* Resources */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | 5B83AB9B1A76819000D713E5 /* PBXTargetDependency */, 165 | ); 166 | name = CalculatorTests; 167 | productName = CalculatorTests; 168 | productReference = 5B83AB991A76819000D713E5 /* CalculatorTests.xctest */; 169 | productType = "com.apple.product-type.bundle.unit-test"; 170 | }; 171 | /* End PBXNativeTarget section */ 172 | 173 | /* Begin PBXProject section */ 174 | 5B83AB7C1A76819000D713E5 /* Project object */ = { 175 | isa = PBXProject; 176 | attributes = { 177 | LastUpgradeCheck = 0610; 178 | ORGANIZATIONNAME = "m2m server software gmbh"; 179 | TargetAttributes = { 180 | 5B83AB831A76819000D713E5 = { 181 | CreatedOnToolsVersion = 6.1.1; 182 | }; 183 | 5B83AB981A76819000D713E5 = { 184 | CreatedOnToolsVersion = 6.1.1; 185 | TestTargetID = 5B83AB831A76819000D713E5; 186 | }; 187 | }; 188 | }; 189 | buildConfigurationList = 5B83AB7F1A76819000D713E5 /* Build configuration list for PBXProject "Calculator" */; 190 | compatibilityVersion = "Xcode 3.2"; 191 | developmentRegion = English; 192 | hasScannedForEncodings = 0; 193 | knownRegions = ( 194 | en, 195 | Base, 196 | ); 197 | mainGroup = 5B83AB7B1A76819000D713E5; 198 | productRefGroup = 5B83AB851A76819000D713E5 /* Products */; 199 | projectDirPath = ""; 200 | projectRoot = ""; 201 | targets = ( 202 | 5B83AB831A76819000D713E5 /* Calculator */, 203 | 5B83AB981A76819000D713E5 /* CalculatorTests */, 204 | ); 205 | }; 206 | /* End PBXProject section */ 207 | 208 | /* Begin PBXResourcesBuildPhase section */ 209 | 5B83AB821A76819000D713E5 /* Resources */ = { 210 | isa = PBXResourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | 5B83AB8F1A76819000D713E5 /* Main.storyboard in Resources */, 214 | 5B83AB941A76819000D713E5 /* LaunchScreen.xib in Resources */, 215 | 5B83AB911A76819000D713E5 /* Images.xcassets in Resources */, 216 | ); 217 | runOnlyForDeploymentPostprocessing = 0; 218 | }; 219 | 5B83AB971A76819000D713E5 /* Resources */ = { 220 | isa = PBXResourcesBuildPhase; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | /* End PBXResourcesBuildPhase section */ 227 | 228 | /* Begin PBXSourcesBuildPhase section */ 229 | 5B83AB801A76819000D713E5 /* Sources */ = { 230 | isa = PBXSourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | 5B52A8F81A79022500D6D166 /* CalculatorBrain.swift in Sources */, 234 | 5B77EEE11A9BCB7100CA4CC6 /* GraphViewController.swift in Sources */, 235 | 5BE2C6831A9E6429009760CB /* AxesDrawer.swift in Sources */, 236 | 5B83AB8C1A76819000D713E5 /* CalculatorViewController.swift in Sources */, 237 | 5B83AB8A1A76819000D713E5 /* AppDelegate.swift in Sources */, 238 | 5BE2C6851A9E683E009760CB /* GraphView.swift in Sources */, 239 | 5B9A1E6B1AA4B9B2003CFE1F /* StatisticsViewController.swift in Sources */, 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | }; 243 | 5B83AB951A76819000D713E5 /* Sources */ = { 244 | isa = PBXSourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | 5B908F2C1A8FAAB3002BFA3B /* CalculatorBrain.swift in Sources */, 248 | 5B83ABA01A76819000D713E5 /* CalculatorTests.swift in Sources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | /* End PBXSourcesBuildPhase section */ 253 | 254 | /* Begin PBXTargetDependency section */ 255 | 5B83AB9B1A76819000D713E5 /* PBXTargetDependency */ = { 256 | isa = PBXTargetDependency; 257 | target = 5B83AB831A76819000D713E5 /* Calculator */; 258 | targetProxy = 5B83AB9A1A76819000D713E5 /* PBXContainerItemProxy */; 259 | }; 260 | /* End PBXTargetDependency section */ 261 | 262 | /* Begin PBXVariantGroup section */ 263 | 5B83AB8D1A76819000D713E5 /* Main.storyboard */ = { 264 | isa = PBXVariantGroup; 265 | children = ( 266 | 5B83AB8E1A76819000D713E5 /* Base */, 267 | ); 268 | name = Main.storyboard; 269 | sourceTree = ""; 270 | }; 271 | 5B83AB921A76819000D713E5 /* LaunchScreen.xib */ = { 272 | isa = PBXVariantGroup; 273 | children = ( 274 | 5B83AB931A76819000D713E5 /* Base */, 275 | ); 276 | name = LaunchScreen.xib; 277 | sourceTree = ""; 278 | }; 279 | /* End PBXVariantGroup section */ 280 | 281 | /* Begin XCBuildConfiguration section */ 282 | 5B83ABA11A76819000D713E5 /* Debug */ = { 283 | isa = XCBuildConfiguration; 284 | buildSettings = { 285 | ALWAYS_SEARCH_USER_PATHS = NO; 286 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 287 | CLANG_CXX_LIBRARY = "libc++"; 288 | CLANG_ENABLE_MODULES = YES; 289 | CLANG_ENABLE_OBJC_ARC = YES; 290 | CLANG_WARN_BOOL_CONVERSION = YES; 291 | CLANG_WARN_CONSTANT_CONVERSION = YES; 292 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 293 | CLANG_WARN_EMPTY_BODY = YES; 294 | CLANG_WARN_ENUM_CONVERSION = YES; 295 | CLANG_WARN_INT_CONVERSION = YES; 296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 297 | CLANG_WARN_UNREACHABLE_CODE = YES; 298 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 299 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 300 | COPY_PHASE_STRIP = NO; 301 | ENABLE_STRICT_OBJC_MSGSEND = YES; 302 | GCC_C_LANGUAGE_STANDARD = gnu99; 303 | GCC_DYNAMIC_NO_PIC = NO; 304 | GCC_OPTIMIZATION_LEVEL = 0; 305 | GCC_PREPROCESSOR_DEFINITIONS = ( 306 | "DEBUG=1", 307 | "$(inherited)", 308 | ); 309 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 312 | GCC_WARN_UNDECLARED_SELECTOR = YES; 313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 314 | GCC_WARN_UNUSED_FUNCTION = YES; 315 | GCC_WARN_UNUSED_VARIABLE = YES; 316 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 317 | MTL_ENABLE_DEBUG_INFO = YES; 318 | ONLY_ACTIVE_ARCH = YES; 319 | SDKROOT = iphoneos; 320 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 321 | TARGETED_DEVICE_FAMILY = "1,2"; 322 | }; 323 | name = Debug; 324 | }; 325 | 5B83ABA21A76819000D713E5 /* Release */ = { 326 | isa = XCBuildConfiguration; 327 | buildSettings = { 328 | ALWAYS_SEARCH_USER_PATHS = NO; 329 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 330 | CLANG_CXX_LIBRARY = "libc++"; 331 | CLANG_ENABLE_MODULES = YES; 332 | CLANG_ENABLE_OBJC_ARC = YES; 333 | CLANG_WARN_BOOL_CONVERSION = YES; 334 | CLANG_WARN_CONSTANT_CONVERSION = YES; 335 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 336 | CLANG_WARN_EMPTY_BODY = YES; 337 | CLANG_WARN_ENUM_CONVERSION = YES; 338 | CLANG_WARN_INT_CONVERSION = YES; 339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 340 | CLANG_WARN_UNREACHABLE_CODE = YES; 341 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 342 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 343 | COPY_PHASE_STRIP = YES; 344 | ENABLE_NS_ASSERTIONS = NO; 345 | ENABLE_STRICT_OBJC_MSGSEND = YES; 346 | GCC_C_LANGUAGE_STANDARD = gnu99; 347 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 348 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 349 | GCC_WARN_UNDECLARED_SELECTOR = YES; 350 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 351 | GCC_WARN_UNUSED_FUNCTION = YES; 352 | GCC_WARN_UNUSED_VARIABLE = YES; 353 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 354 | MTL_ENABLE_DEBUG_INFO = NO; 355 | SDKROOT = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Release; 360 | }; 361 | 5B83ABA41A76819000D713E5 /* Debug */ = { 362 | isa = XCBuildConfiguration; 363 | buildSettings = { 364 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 365 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 366 | INFOPLIST_FILE = Calculator/Info.plist; 367 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 368 | PRODUCT_NAME = "$(TARGET_NAME)"; 369 | }; 370 | name = Debug; 371 | }; 372 | 5B83ABA51A76819000D713E5 /* Release */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 376 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 377 | INFOPLIST_FILE = Calculator/Info.plist; 378 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 379 | PRODUCT_NAME = "$(TARGET_NAME)"; 380 | }; 381 | name = Release; 382 | }; 383 | 5B83ABA71A76819000D713E5 /* Debug */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | BUNDLE_LOADER = "$(TEST_HOST)"; 387 | FRAMEWORK_SEARCH_PATHS = ( 388 | "$(SDKROOT)/Developer/Library/Frameworks", 389 | "$(inherited)", 390 | ); 391 | GCC_PREPROCESSOR_DEFINITIONS = ( 392 | "DEBUG=1", 393 | "$(inherited)", 394 | ); 395 | INFOPLIST_FILE = CalculatorTests/Info.plist; 396 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 397 | PRODUCT_NAME = "$(TARGET_NAME)"; 398 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Calculator.app/Calculator"; 399 | }; 400 | name = Debug; 401 | }; 402 | 5B83ABA81A76819000D713E5 /* Release */ = { 403 | isa = XCBuildConfiguration; 404 | buildSettings = { 405 | BUNDLE_LOADER = "$(TEST_HOST)"; 406 | FRAMEWORK_SEARCH_PATHS = ( 407 | "$(SDKROOT)/Developer/Library/Frameworks", 408 | "$(inherited)", 409 | ); 410 | INFOPLIST_FILE = CalculatorTests/Info.plist; 411 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 412 | PRODUCT_NAME = "$(TARGET_NAME)"; 413 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Calculator.app/Calculator"; 414 | }; 415 | name = Release; 416 | }; 417 | /* End XCBuildConfiguration section */ 418 | 419 | /* Begin XCConfigurationList section */ 420 | 5B83AB7F1A76819000D713E5 /* Build configuration list for PBXProject "Calculator" */ = { 421 | isa = XCConfigurationList; 422 | buildConfigurations = ( 423 | 5B83ABA11A76819000D713E5 /* Debug */, 424 | 5B83ABA21A76819000D713E5 /* Release */, 425 | ); 426 | defaultConfigurationIsVisible = 0; 427 | defaultConfigurationName = Release; 428 | }; 429 | 5B83ABA31A76819000D713E5 /* Build configuration list for PBXNativeTarget "Calculator" */ = { 430 | isa = XCConfigurationList; 431 | buildConfigurations = ( 432 | 5B83ABA41A76819000D713E5 /* Debug */, 433 | 5B83ABA51A76819000D713E5 /* Release */, 434 | ); 435 | defaultConfigurationIsVisible = 0; 436 | defaultConfigurationName = Release; 437 | }; 438 | 5B83ABA61A76819000D713E5 /* Build configuration list for PBXNativeTarget "CalculatorTests" */ = { 439 | isa = XCConfigurationList; 440 | buildConfigurations = ( 441 | 5B83ABA71A76819000D713E5 /* Debug */, 442 | 5B83ABA81A76819000D713E5 /* Release */, 443 | ); 444 | defaultConfigurationIsVisible = 0; 445 | defaultConfigurationName = Release; 446 | }; 447 | /* End XCConfigurationList section */ 448 | }; 449 | rootObject = 5B83AB7C1A76819000D713E5 /* Project object */; 450 | } 451 | -------------------------------------------------------------------------------- /Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Calculator/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Calculator 4 | // 5 | // Created by Martin Mandl on 26.01.15. 6 | // Copyright (c) 2015 m2m server software gmbh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Calculator/AxesDrawer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxesDrawer.swift 3 | // Calculator 4 | // 5 | // Created by CS193p Instructor. 6 | // Copyright (c) 2015 Stanford University. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AxesDrawer 12 | { 13 | private struct Constants { 14 | static let HashmarkSize: CGFloat = 6 15 | } 16 | 17 | var color = UIColor.blueColor() 18 | var minimumPointsPerHashmark: CGFloat = 40 19 | var contentScaleFactor: CGFloat = 1 // set this from UIView's contentScaleFactor to position axes with maximum accuracy 20 | 21 | convenience init(color: UIColor, contentScaleFactor: CGFloat) { 22 | self.init() 23 | self.color = color 24 | self.contentScaleFactor = contentScaleFactor 25 | } 26 | 27 | convenience init(color: UIColor) { 28 | self.init() 29 | self.color = color 30 | } 31 | 32 | convenience init(contentScaleFactor: CGFloat) { 33 | self.init() 34 | self.contentScaleFactor = contentScaleFactor 35 | } 36 | 37 | // this method is the heart of the AxesDrawer 38 | // it draws in the current graphic context's coordinate system 39 | // therefore origin and bounds must be in the current graphics context's coordinate system 40 | // pointsPerUnit is essentially the "scale" of the axes 41 | // e.g. if you wanted there to be 100 points along an axis between -1 and 1, 42 | // you'd set pointsPerUnit to 50 43 | 44 | func drawAxesInRect(bounds: CGRect, origin: CGPoint, pointsPerUnit: CGFloat) 45 | { 46 | CGContextSaveGState(UIGraphicsGetCurrentContext()) 47 | color.set() 48 | let path = UIBezierPath() 49 | path.moveToPoint(CGPoint(x: bounds.minX, y: align(origin.y))) 50 | path.addLineToPoint(CGPoint(x: bounds.maxX, y: align(origin.y))) 51 | path.moveToPoint(CGPoint(x: align(origin.x), y: bounds.minY)) 52 | path.addLineToPoint(CGPoint(x: align(origin.x), y: bounds.maxY)) 53 | path.stroke() 54 | drawHashmarksInRect(bounds, origin: origin, pointsPerUnit: abs(pointsPerUnit)) 55 | CGContextRestoreGState(UIGraphicsGetCurrentContext()) 56 | } 57 | 58 | // the rest of this class is private 59 | 60 | private func drawHashmarksInRect(bounds: CGRect, origin: CGPoint, pointsPerUnit: CGFloat) 61 | { 62 | if ((origin.x >= bounds.minX) && (origin.x <= bounds.maxX)) || ((origin.y >= bounds.minY) && (origin.y <= bounds.maxY)) 63 | { 64 | // figure out how many units each hashmark must represent 65 | // to respect both pointsPerUnit and minimumPointsPerHashmark 66 | var unitsPerHashmark = minimumPointsPerHashmark / pointsPerUnit 67 | if unitsPerHashmark < 1 { 68 | unitsPerHashmark = pow(10, ceil(log10(unitsPerHashmark))) 69 | } else { 70 | unitsPerHashmark = floor(unitsPerHashmark) 71 | } 72 | 73 | let pointsPerHashmark = pointsPerUnit * unitsPerHashmark 74 | 75 | // figure out which is the closest set of hashmarks (radiating out from the origin) that are in bounds 76 | var startingHashmarkRadius: CGFloat = 1 77 | if !CGRectContainsPoint(bounds, origin) { 78 | let leftx = max(origin.x - bounds.maxX, 0) 79 | let rightx = max(bounds.minX - origin.x, 0) 80 | let downy = max(origin.y - bounds.minY, 0) 81 | let upy = max(bounds.maxY - origin.y, 0) 82 | startingHashmarkRadius = min(min(leftx, rightx), min(downy, upy)) / pointsPerHashmark + 1 83 | } 84 | 85 | // now create a bounding box inside whose edges those four hashmarks lie 86 | let bboxSize = pointsPerHashmark * startingHashmarkRadius * 2 87 | var bbox = CGRect(center: origin, size: CGSize(width: bboxSize, height: bboxSize)) 88 | 89 | // formatter for the hashmark labels 90 | let formatter = NSNumberFormatter() 91 | formatter.maximumFractionDigits = Int(round(-log10(Double(unitsPerHashmark)))) 92 | formatter.minimumIntegerDigits = 1 93 | 94 | // radiate the bbox out until the hashmarks are further out than the bounds 95 | while !CGRectContainsRect(bbox, bounds) 96 | { 97 | let label = formatter.stringFromNumber((origin.x-bbox.minX)/pointsPerUnit)! 98 | if let leftHashmarkPoint = alignedPoint(x: bbox.minX, y: origin.y, insideBounds:bounds) { 99 | drawHashmarkAtLocation(leftHashmarkPoint, .Top("-\(label)")) 100 | } 101 | if let rightHashmarkPoint = alignedPoint(x: bbox.maxX, y: origin.y, insideBounds:bounds) { 102 | drawHashmarkAtLocation(rightHashmarkPoint, .Top(label)) 103 | } 104 | if let topHashmarkPoint = alignedPoint(x: origin.x, y: bbox.minY, insideBounds:bounds) { 105 | drawHashmarkAtLocation(topHashmarkPoint, .Left(label)) 106 | } 107 | if let bottomHashmarkPoint = alignedPoint(x: origin.x, y: bbox.maxY, insideBounds:bounds) { 108 | drawHashmarkAtLocation(bottomHashmarkPoint, .Left("-\(label)")) 109 | } 110 | bbox.inset(dx: -pointsPerHashmark, dy: -pointsPerHashmark) 111 | } 112 | } 113 | } 114 | 115 | private func drawHashmarkAtLocation(location: CGPoint, _ text: AnchoredText) 116 | { 117 | var dx: CGFloat = 0, dy: CGFloat = 0 118 | switch text { 119 | case .Left: dx = Constants.HashmarkSize / 2 120 | case .Right: dx = Constants.HashmarkSize / 2 121 | case .Top: dy = Constants.HashmarkSize / 2 122 | case .Bottom: dy = Constants.HashmarkSize / 2 123 | } 124 | 125 | let path = UIBezierPath() 126 | path.moveToPoint(CGPoint(x: location.x-dx, y: location.y-dy)) 127 | path.addLineToPoint(CGPoint(x: location.x+dx, y: location.y+dy)) 128 | path.stroke() 129 | 130 | text.drawAnchoredToPoint(location, color: color) 131 | } 132 | 133 | private enum AnchoredText 134 | { 135 | case Left(String) 136 | case Right(String) 137 | case Top(String) 138 | case Bottom(String) 139 | 140 | static let VerticalOffset: CGFloat = 3 141 | static let HorizontalOffset: CGFloat = 6 142 | 143 | func drawAnchoredToPoint(location: CGPoint, color: UIColor) { 144 | let attributes = [ 145 | NSFontAttributeName : UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote), 146 | NSForegroundColorAttributeName : color 147 | ] 148 | var textRect = CGRect(center: location, size: text.sizeWithAttributes(attributes)) 149 | switch self { 150 | case Top: textRect.origin.y += textRect.size.height / 2 + AnchoredText.VerticalOffset 151 | case Left: textRect.origin.x += textRect.size.width / 2 + AnchoredText.HorizontalOffset 152 | case Bottom: textRect.origin.y -= textRect.size.height / 2 + AnchoredText.VerticalOffset 153 | case Right: textRect.origin.x -= textRect.size.width / 2 + AnchoredText.HorizontalOffset 154 | } 155 | text.drawInRect(textRect, withAttributes: attributes) 156 | } 157 | 158 | var text: String { 159 | switch self { 160 | case Left(let text): return text 161 | case Right(let text): return text 162 | case Top(let text): return text 163 | case Bottom(let text): return text 164 | } 165 | } 166 | } 167 | 168 | // we want the axes and hashmarks to be exactly on pixel boundaries so they look sharp 169 | // setting contentScaleFactor properly will enable us to put things on the closest pixel boundary 170 | // if contentScaleFactor is left to its default (1), then things will be on the nearest "point" boundary instead 171 | // the lines will still be sharp in that case, but might be a pixel (or more theoretically) off of where they should be 172 | 173 | private func alignedPoint(#x: CGFloat, y: CGFloat, insideBounds: CGRect? = nil) -> CGPoint? 174 | { 175 | let point = CGPoint(x: align(x), y: align(y)) 176 | if let permissibleBounds = insideBounds { 177 | if (!CGRectContainsPoint(permissibleBounds, point)) { 178 | return nil 179 | } 180 | } 181 | return point 182 | } 183 | 184 | private func align(coordinate: CGFloat) -> CGFloat { 185 | return round(coordinate * contentScaleFactor) / contentScaleFactor 186 | } 187 | } 188 | 189 | extension CGRect 190 | { 191 | init(center: CGPoint, size: CGSize) { 192 | self.init(x: center.x-size.width/2, y: center.y-size.height/2, width: size.width, height: size.height) 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /Calculator/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Calculator/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 46 | 56 | 66 | 76 | 86 | 96 | 106 | 116 | 126 | 136 | 146 | 156 | 166 | 176 | 186 | 196 | 206 | 216 | 223 | 233 | 243 | 252 | 258 | 268 | 278 | 288 | 298 | 308 | 318 | 325 | 335 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | -------------------------------------------------------------------------------- /Calculator/CalculatorBrain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalculatorBrain.swift 3 | // Calculator 4 | // 5 | // Created by Martin Mandl on 28.01.15. 6 | // Copyright (c) 2015 m2m server software gmbh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class CalculatorBrain 12 | { 13 | private enum Op: Printable 14 | { 15 | case Operand(Double) 16 | case NullaryOperation(String, () -> Double) 17 | case UnaryOperation(String, Double -> Double, (Double -> String?)?) 18 | case BinaryOperation(String, Int, (Double, Double) -> Double, ((Double, Double) -> String?)?) 19 | case Variable(String) 20 | 21 | var description: String { 22 | get { 23 | switch self { 24 | case .Operand(let operand): 25 | return "\(operand)" 26 | case .NullaryOperation(let symbol, _): 27 | return symbol 28 | case .UnaryOperation(let symbol, _, _): 29 | return symbol 30 | case .BinaryOperation(let symbol, _, _, _): 31 | return symbol 32 | case .Variable(let symbol): 33 | return symbol 34 | } 35 | } 36 | } 37 | 38 | var precedence: Int { 39 | get { 40 | switch self { 41 | case .BinaryOperation(_, let precedence, _, _): 42 | return precedence 43 | default: 44 | return Int.max 45 | } 46 | } 47 | } 48 | } 49 | 50 | private var opStack = [Op]() 51 | 52 | private var knownOps = [String:Op]() 53 | 54 | var variableValues = [String: Double]() 55 | 56 | private var error: String? 57 | 58 | init() { 59 | func learnOp (op: Op) { 60 | knownOps[op.description] = op 61 | } 62 | learnOp(Op.BinaryOperation("×", 2, *, nil)) 63 | learnOp(Op.BinaryOperation("÷", 2, { $1 / $0 }, 64 | { divisor, _ in return divisor == 0.0 ? "Division by Zero" : nil })) 65 | learnOp(Op.BinaryOperation("+", 1, +, nil)) 66 | learnOp(Op.BinaryOperation("−", 1, { $1 - $0 }, nil)) 67 | learnOp(Op.UnaryOperation("√", sqrt, { $0 < 0 ? "SQRT of Neg. Number" : nil })) 68 | learnOp(Op.UnaryOperation("sin", sin, nil)) 69 | learnOp(Op.UnaryOperation("cos", cos, nil)) 70 | learnOp(Op.UnaryOperation("tan", tan, nil)) 71 | learnOp(Op.UnaryOperation("±", { -$0 }, nil)) 72 | learnOp(Op.NullaryOperation("π", { M_PI })) 73 | learnOp(Op.NullaryOperation("e", { M_E })) 74 | } 75 | 76 | typealias PropertyList = AnyObject 77 | var program: PropertyList { // guaranteed to be a property list 78 | get { 79 | // var returnValue = Array() 80 | // for op in opStack { 81 | // returnValue.append(op.description) 82 | // } 83 | // return returnValue 84 | return opStack.map{ $0.description } 85 | } 86 | set { 87 | if let opSymbols = newValue as? Array { 88 | var newOpStack = [Op]() 89 | let numberFormatter = NSNumberFormatter() 90 | //numberFormatter.locale = NSLocale(localeIdentifier: "en_US") 91 | for opSymbol in opSymbols { 92 | if let op = knownOps[opSymbol] { 93 | newOpStack.append(op) 94 | } else if let operand = numberFormatter.numberFromString(opSymbol)?.doubleValue { 95 | newOpStack.append(.Operand(operand)) 96 | } else { 97 | newOpStack.append(.Variable(opSymbol)) 98 | } 99 | } 100 | opStack = newOpStack 101 | } 102 | } 103 | } 104 | 105 | var description: String { 106 | get { 107 | var (result, ops) = ("", opStack) 108 | while ops.count > 0 { 109 | var current: String? 110 | (current, ops, _) = description(ops) 111 | result = result == "" ? current! : "\(current!), \(result)" 112 | } 113 | return result 114 | } 115 | } 116 | 117 | private func description(ops: [Op]) -> (result: String?, remainingOps: [Op], precedence: Int?) { 118 | if !ops.isEmpty { 119 | var remainingOps = ops 120 | let op = remainingOps.removeLast() 121 | switch op { 122 | case .Operand(let operand): 123 | return (String(format: "%g", operand) , remainingOps, op.precedence) 124 | case .NullaryOperation(let symbol, _): 125 | return (symbol, remainingOps, op.precedence); 126 | case .UnaryOperation(let symbol, _, _): 127 | let operandEvaluation = description(remainingOps) 128 | if var operand = operandEvaluation.result { 129 | if op.precedence > operandEvaluation.precedence { 130 | operand = "(\(operand))" 131 | } 132 | return ("\(symbol) \(operand)", operandEvaluation.remainingOps, op.precedence) 133 | } 134 | case .BinaryOperation(let symbol, _, _, _): 135 | let op1Evaluation = description(remainingOps) 136 | if var operand1 = op1Evaluation.result { 137 | if op.precedence > op1Evaluation.precedence { 138 | operand1 = "(\(operand1))" 139 | } 140 | let op2Evaluation = description(op1Evaluation.remainingOps) 141 | if var operand2 = op2Evaluation.result { 142 | if op.precedence > op2Evaluation.precedence { 143 | operand2 = "(\(operand2))" 144 | } 145 | return ("\(operand2) \(symbol) \(operand1)", 146 | op2Evaluation.remainingOps, op.precedence) 147 | } 148 | } 149 | case .Variable(let symbol): 150 | return (symbol, remainingOps, op.precedence) 151 | } 152 | } 153 | return ("?", ops, Int.max) 154 | } 155 | 156 | private func evaluate(ops: [Op]) -> (result: Double?, remainingOps: [Op]) { 157 | if !ops.isEmpty { 158 | var remainingOps = ops 159 | let op = remainingOps.removeLast() 160 | switch op { 161 | case .Operand(let operand): 162 | return (operand, remainingOps) 163 | case .NullaryOperation(_, let operation): 164 | return (operation(), remainingOps) 165 | case .UnaryOperation(_, let operation, let errorTest): 166 | let operandEvaluation = evaluate(remainingOps) 167 | if let operand = operandEvaluation.result { 168 | if let errorMessage = errorTest?(operand) { 169 | error = errorMessage 170 | return (nil, operandEvaluation.remainingOps) 171 | } 172 | return (operation(operand), operandEvaluation.remainingOps) 173 | } 174 | case .BinaryOperation(_, _, let operation, let errorTest): 175 | let op1Evaluation = evaluate(remainingOps) 176 | if let operand1 = op1Evaluation.result { 177 | let op2Evaluation = evaluate(op1Evaluation.remainingOps) 178 | if let operand2 = op2Evaluation.result { 179 | if let errorMessage = errorTest?(operand1, operand2) { 180 | error = errorMessage 181 | return (nil, op2Evaluation.remainingOps) 182 | } 183 | return (operation(operand1, operand2), op2Evaluation.remainingOps) 184 | } 185 | } 186 | case .Variable(let symbol): 187 | if let variable = variableValues[symbol] { 188 | return (variableValues[symbol], remainingOps) 189 | } 190 | error = "Variable Not Set" 191 | return (nil, remainingOps) 192 | } 193 | if error == nil { 194 | error = "Not Enough Operands" 195 | } 196 | } 197 | return (nil, ops) 198 | } 199 | 200 | func showStack() -> String? { 201 | return " ".join(opStack.map{ "\($0)" }) 202 | } 203 | 204 | func evaluate() -> Double? { 205 | error = nil 206 | let (result, remainder) = evaluate(opStack) 207 | //let (result, _) = evaluate(opStack) 208 | //println("\(opStack) = \(result) with \(remainder) left over") 209 | return result 210 | } 211 | 212 | func evaluateAndReportErrors() -> AnyObject? { 213 | let (result, _) = evaluate(opStack) 214 | return result != nil ? result : error 215 | } 216 | 217 | func pushOperand(operand: Double) -> Double? { 218 | opStack.append(Op.Operand(operand)) 219 | return evaluate() 220 | } 221 | 222 | func pushOperand(symbol: String) -> Double? { 223 | opStack.append(Op.Variable(symbol)) 224 | return evaluate() 225 | } 226 | 227 | func popOperand() -> Double? { 228 | if !opStack.isEmpty { 229 | opStack.removeLast() 230 | } 231 | return evaluate() 232 | } 233 | 234 | func performOperation(symbol: String) -> Double? { 235 | if let operation = knownOps[symbol] { 236 | opStack.append(operation); 237 | } 238 | return evaluate() 239 | } 240 | } -------------------------------------------------------------------------------- /Calculator/CalculatorViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalculatorViewController.swift 3 | // Calculator 4 | // 5 | // Created by Martin Mandl on 26.01.15. 6 | // Copyright (c) 2015 m2m server software gmbh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CalculatorViewController: UIViewController 12 | { 13 | @IBOutlet weak var display: UILabel! 14 | @IBOutlet weak var history: UILabel! 15 | @IBOutlet weak var decimalButton: UIButton! 16 | 17 | private var userIsInTheMiddleOfTypingANumber = false 18 | private let decimalSeparator = NSNumberFormatter().decimalSeparator! 19 | 20 | private var brain = CalculatorBrain() 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | decimalButton.setTitle(decimalSeparator, forState: UIControlState.Normal) 25 | display.text = " " 26 | } 27 | 28 | @IBAction func appendDigit(sender: UIButton) { 29 | let digit = sender.currentTitle! 30 | //println("digit = \(digit)"); 31 | 32 | if userIsInTheMiddleOfTypingANumber { 33 | if (digit == decimalSeparator) && (display.text!.rangeOfString(decimalSeparator) != nil) { return } 34 | if (digit == "0") && ((display.text == "0") || (display.text == "-0")) { return } 35 | if (digit != decimalSeparator) && ((display.text == "0") || (display.text == "-0")) { 36 | if (display.text == "0") { 37 | display.text = digit 38 | } else { 39 | display.text = "-" + digit 40 | } 41 | } else { 42 | display.text = display.text! + digit 43 | } 44 | } else { 45 | if digit == decimalSeparator { 46 | display.text = "0" + decimalSeparator 47 | } else { 48 | display.text = digit 49 | } 50 | userIsInTheMiddleOfTypingANumber = true 51 | history.text = brain.description != "?" ? brain.description : "" 52 | } 53 | } 54 | 55 | @IBAction func operate(sender: UIButton) { 56 | if let operation = sender.currentTitle { 57 | if userIsInTheMiddleOfTypingANumber { 58 | if operation == "±" { 59 | let displayText = display.text! 60 | if (displayText.rangeOfString("-") != nil) { 61 | display.text = dropFirst(displayText) 62 | } else { 63 | display.text = "-" + displayText 64 | } 65 | return 66 | } 67 | enter() 68 | } 69 | if let result = brain.performOperation(operation) { 70 | displayValue = result 71 | } else { 72 | // error? 73 | displayValue = nil 74 | } 75 | } 76 | } 77 | 78 | @IBAction func enter() { 79 | userIsInTheMiddleOfTypingANumber = false 80 | if displayValue != nil { 81 | if let result = brain.pushOperand(displayValue!) { 82 | displayValue = result 83 | } else { 84 | // error? 85 | displayValue = nil 86 | } 87 | } 88 | } 89 | 90 | @IBAction func clear() { 91 | brain = CalculatorBrain() 92 | displayValue = nil 93 | history.text = "" 94 | } 95 | 96 | @IBAction func backSpace() { 97 | if userIsInTheMiddleOfTypingANumber { 98 | let displayText = display.text! 99 | if count(displayText) > 1 { 100 | display.text = dropLast(displayText) 101 | if (count(displayText) == 2) && (display.text?.rangeOfString("-") != nil) { 102 | display.text = "-0" 103 | } 104 | } else { 105 | display.text = "0" 106 | } 107 | } else { 108 | if let result = brain.popOperand() { 109 | displayValue = result 110 | } else { 111 | displayValue = nil 112 | } 113 | } 114 | } 115 | 116 | @IBAction func storeVariable(sender: UIButton) { 117 | if let variable = last(sender.currentTitle!) { 118 | if displayValue != nil { 119 | brain.variableValues["\(variable)"] = displayValue 120 | if let result = brain.evaluate() { 121 | displayValue = result 122 | } else { 123 | displayValue = nil 124 | } 125 | } 126 | } 127 | userIsInTheMiddleOfTypingANumber = false 128 | } 129 | 130 | @IBAction func pushVariable(sender: UIButton) { 131 | if userIsInTheMiddleOfTypingANumber { 132 | enter() 133 | } 134 | if let result = brain.pushOperand(sender.currentTitle!) { 135 | displayValue = result 136 | } else { 137 | displayValue = nil 138 | } 139 | } 140 | 141 | private var displayValue: Double? { 142 | get { 143 | return NSNumberFormatter().numberFromString(display.text!)?.doubleValue 144 | } 145 | set { 146 | if (newValue != nil) { 147 | let numberFormatter = NSNumberFormatter() 148 | numberFormatter.numberStyle = .DecimalStyle 149 | numberFormatter.maximumFractionDigits = 10 150 | display.text = numberFormatter.stringFromNumber(newValue!) 151 | } else { 152 | if let result = brain.evaluateAndReportErrors() as? String { 153 | display.text = result 154 | } else { 155 | display.text = " " 156 | } 157 | } 158 | userIsInTheMiddleOfTypingANumber = false 159 | history.text = brain.description != "" ? brain.description + " =" : "" 160 | } 161 | } 162 | 163 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 164 | var destination = segue.destinationViewController as? UIViewController 165 | if let nc = destination as? UINavigationController { 166 | destination = nc.visibleViewController 167 | } 168 | if let gvc = destination as? GraphViewController { 169 | if let identifier = segue.identifier { 170 | switch identifier { 171 | case "plot graph": 172 | gvc.title = brain.description == "" ? "Graph" : brain.description.componentsSeparatedByString(", ").last 173 | gvc.program = brain.program 174 | default: 175 | break 176 | } 177 | } 178 | } 179 | } 180 | 181 | } 182 | 183 | -------------------------------------------------------------------------------- /Calculator/GraphView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GraphView.swift 3 | // Calculator 4 | // 5 | // Created by Martin Mandl on 25.02.15. 6 | // Copyright (c) 2015 m2m server software gmbh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol GraphViewDataSource: class { 12 | func y(x: CGFloat) -> CGFloat? 13 | } 14 | 15 | @IBDesignable 16 | class GraphView: UIView { 17 | 18 | weak var dataSource: GraphViewDataSource? 19 | 20 | @IBInspectable 21 | var scale: CGFloat = 50.0 { didSet { setNeedsDisplay() } } 22 | var origin: CGPoint { 23 | get { 24 | var origin = originRelativeToCenter 25 | if geometryReady { 26 | origin.x += center.x 27 | origin.y += center.y 28 | } 29 | return origin 30 | } 31 | set { 32 | var origin = newValue 33 | if geometryReady { 34 | origin.x -= center.x 35 | origin.y -= center.y 36 | } 37 | originRelativeToCenter = origin 38 | } 39 | } 40 | var lineWidth: CGFloat = 1.0 { didSet { setNeedsDisplay() } } 41 | var color: UIColor = UIColor.blackColor() { didSet { setNeedsDisplay() } } 42 | 43 | private var originRelativeToCenter: CGPoint = CGPoint() { didSet { setNeedsDisplay() } } 44 | private var geometryReady = false 45 | 46 | override func drawRect(rect: CGRect) { 47 | if !geometryReady && originRelativeToCenter != CGPointZero { 48 | var originHelper = origin 49 | geometryReady = true 50 | origin = originHelper 51 | } 52 | 53 | AxesDrawer(contentScaleFactor: contentScaleFactor) 54 | .drawAxesInRect(bounds, origin: origin, pointsPerUnit: scale) 55 | 56 | color.set() 57 | let path = UIBezierPath() 58 | path.lineWidth = lineWidth 59 | var firstValue = true 60 | var point = CGPoint() 61 | for var i = 0; i <= Int(bounds.size.width * contentScaleFactor); i++ { 62 | point.x = CGFloat(i) / contentScaleFactor 63 | if let y = dataSource?.y((point.x - origin.x) / scale) { 64 | if !y.isNormal && !y.isZero { 65 | firstValue = true 66 | continue 67 | } 68 | point.y = origin.y - y * scale 69 | if firstValue { 70 | path.moveToPoint(point) 71 | firstValue = false 72 | } else { 73 | path.addLineToPoint(point) 74 | } 75 | } else { 76 | firstValue = true 77 | } 78 | } 79 | path.stroke() 80 | } 81 | 82 | var snapshot:UIView? 83 | 84 | func zoom(gesture: UIPinchGestureRecognizer) { 85 | switch gesture.state { 86 | case .Began: 87 | snapshot = self.snapshotViewAfterScreenUpdates(false) 88 | snapshot!.alpha = 0.8 89 | self.addSubview(snapshot!) 90 | case .Changed: 91 | let touch = gesture.locationInView(self) 92 | snapshot!.frame.size.height *= gesture.scale 93 | snapshot!.frame.size.width *= gesture.scale 94 | snapshot!.frame.origin.x = snapshot!.frame.origin.x * gesture.scale + (1 - gesture.scale) * touch.x 95 | snapshot!.frame.origin.y = snapshot!.frame.origin.y * gesture.scale + (1 - gesture.scale) * touch.y 96 | gesture.scale = 1.0 97 | case .Ended: 98 | let changedScale = snapshot!.frame.height / self.frame.height 99 | scale *= changedScale 100 | origin.x = origin.x * changedScale + snapshot!.frame.origin.x 101 | origin.y = origin.y * changedScale + snapshot!.frame.origin.y 102 | 103 | snapshot!.removeFromSuperview() 104 | snapshot = nil 105 | default: break 106 | } 107 | } 108 | 109 | func move(gesture: UIPanGestureRecognizer) { 110 | switch gesture.state { 111 | case .Began: 112 | snapshot = self.snapshotViewAfterScreenUpdates(false) 113 | snapshot!.alpha = 0.8 114 | self.addSubview(snapshot!) 115 | case .Changed: 116 | let translation = gesture.translationInView(self) 117 | snapshot!.center.x += translation.x 118 | snapshot!.center.y += translation.y 119 | gesture.setTranslation(CGPointZero, inView: self) 120 | case .Ended: 121 | origin.x += snapshot!.frame.origin.x 122 | origin.y += snapshot!.frame.origin.y 123 | snapshot!.removeFromSuperview() 124 | snapshot = nil 125 | default: break 126 | } 127 | } 128 | 129 | func center(gesture: UITapGestureRecognizer) { 130 | if gesture.state == .Ended { 131 | origin = gesture.locationInView(self) 132 | } 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /Calculator/GraphViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GraphViewController.swift 3 | // Calculator 4 | // 5 | // Created by Martin Mandl on 23.02.15. 6 | // Copyright (c) 2015 m2m server software gmbh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class GraphViewController: UIViewController, GraphViewDataSource, UIPopoverPresentationControllerDelegate 12 | { 13 | @IBOutlet weak var graphView: GraphView! { 14 | didSet { 15 | graphView.dataSource = self 16 | graphView.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: "zoom:")) 17 | graphView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "move:")) 18 | var tap = UITapGestureRecognizer(target: self, action: "center:") 19 | tap.numberOfTapsRequired = 2 20 | graphView.addGestureRecognizer(tap) 21 | 22 | resetStatistics() 23 | 24 | if !resetOrigin { 25 | graphView.origin = origin 26 | } 27 | graphView.scale = scale 28 | } 29 | } 30 | 31 | private struct Keys { 32 | static let Scale = "GraphViewController.Scale" 33 | static let Origin = "GraphViewController.Origin" 34 | 35 | static let SegueIdentifier = "Show Statistics" 36 | } 37 | 38 | private let defaults = NSUserDefaults.standardUserDefaults() 39 | 40 | private var resetOrigin: Bool { 41 | get { 42 | if let originArray = defaults.objectForKey(Keys.Origin) as? [CGFloat] { 43 | return false 44 | } 45 | return true 46 | } 47 | } 48 | 49 | var scale: CGFloat { 50 | get { return defaults.objectForKey(Keys.Scale) as? CGFloat ?? 50.0 } 51 | set { defaults.setObject(newValue, forKey: Keys.Scale) } 52 | } 53 | 54 | private var origin: CGPoint { 55 | get { 56 | var origin = CGPoint() 57 | if let originArray = defaults.objectForKey(Keys.Origin) as? [CGFloat] { 58 | origin.x = originArray.first! 59 | origin.y = originArray.last! 60 | } 61 | return origin 62 | } 63 | set { 64 | defaults.setObject([newValue.x, newValue.y], forKey: Keys.Origin) 65 | } 66 | } 67 | 68 | func zoom(gesture: UIPinchGestureRecognizer) { 69 | graphView.zoom(gesture) 70 | if gesture.state == .Ended { 71 | resetStatistics() 72 | scale = graphView.scale 73 | origin = graphView.origin 74 | } 75 | } 76 | 77 | func move(gesture: UIPanGestureRecognizer) { 78 | graphView.move(gesture) 79 | if gesture.state == .Ended { 80 | resetStatistics() 81 | origin = graphView.origin 82 | } 83 | } 84 | 85 | func center(gesture: UITapGestureRecognizer) { 86 | graphView.center(gesture) 87 | if gesture.state == .Ended { 88 | resetStatistics() 89 | origin = graphView.origin 90 | } 91 | } 92 | 93 | func y(x: CGFloat) -> CGFloat? { 94 | brain.variableValues["M"] = Double(x) 95 | if let y = brain.evaluate() { 96 | if let minValue = statistics["min"] { 97 | statistics["min"] = min(minValue, y) 98 | } else { 99 | statistics["min"] = y 100 | } 101 | if let maxValue = statistics["max"] { 102 | statistics["max"] = max(maxValue, y) 103 | } else { 104 | statistics["max"] = y 105 | } 106 | if let avgValue = statistics["avg"] { 107 | statistics["avg"] = statistics["avg"]! + y 108 | statistics["avgNum"] = statistics["avgNum"]! + 1 109 | } else { 110 | statistics["avg"] = y 111 | statistics["avgNum"] = 1 112 | } 113 | 114 | return CGFloat(y) 115 | } 116 | return nil 117 | } 118 | 119 | private var brain = CalculatorBrain() 120 | typealias PropertyList = AnyObject 121 | var program: PropertyList { 122 | get { 123 | return brain.program 124 | } 125 | set { 126 | brain.program = newValue 127 | } 128 | } 129 | 130 | private var statistics = [String: Double]() 131 | private func resetStatistics() { 132 | statistics["min"] = nil 133 | statistics["max"] = nil 134 | statistics["avg"] = nil 135 | } 136 | 137 | private func finishStatistics() { 138 | if let num = statistics["avgNum"] { 139 | if let avgValue = statistics["avg"] { 140 | statistics["avg"] = avgValue / num 141 | statistics["avgNum"] = nil 142 | } 143 | } 144 | } 145 | 146 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 147 | if let identifer = segue.identifier { 148 | switch identifer { 149 | case Keys.SegueIdentifier: 150 | if let tvc = segue.destinationViewController as? StatisticsViewController { 151 | if let ppc = tvc.popoverPresentationController { 152 | ppc.delegate = self 153 | } 154 | finishStatistics() 155 | var texts = [String]() 156 | for (key, value) in statistics { 157 | texts += ["\(key) = \(value)"] 158 | } 159 | tvc.text = texts.count > 0 ? "\n".join(texts) : "none" 160 | } 161 | default: break 162 | } 163 | } 164 | } 165 | 166 | func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle { 167 | return UIModalPresentationStyle.None 168 | } 169 | 170 | override func viewWillLayoutSubviews() { 171 | super.viewWillLayoutSubviews() 172 | resetStatistics() 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Calculator/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Calculator/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "ipad", 6 | "minimum-system-version" : "7.0", 7 | "extent" : "full-screen", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "landscape", 12 | "idiom" : "ipad", 13 | "minimum-system-version" : "7.0", 14 | "extent" : "full-screen", 15 | "scale" : "1x" 16 | }, 17 | { 18 | "orientation" : "landscape", 19 | "idiom" : "ipad", 20 | "minimum-system-version" : "7.0", 21 | "extent" : "full-screen", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "orientation" : "portrait", 26 | "idiom" : "iphone", 27 | "minimum-system-version" : "7.0", 28 | "scale" : "2x" 29 | }, 30 | { 31 | "orientation" : "portrait", 32 | "idiom" : "iphone", 33 | "minimum-system-version" : "7.0", 34 | "subtype" : "retina4", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "orientation" : "portrait", 39 | "idiom" : "ipad", 40 | "minimum-system-version" : "7.0", 41 | "extent" : "full-screen", 42 | "scale" : "1x" 43 | } 44 | ], 45 | "info" : { 46 | "version" : 1, 47 | "author" : "xcode" 48 | } 49 | } -------------------------------------------------------------------------------- /Calculator/Images.xcassets/graph-highlighted.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "graph-highlighted.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "graph-highlighted@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "graph-highlighted@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Calculator/Images.xcassets/graph-highlighted.imageset/graph-highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m2mtech/calculator-2015/5c51ba53fbaa07b3e3f0f286b5c46b2600d558b6/Calculator/Images.xcassets/graph-highlighted.imageset/graph-highlighted.png -------------------------------------------------------------------------------- /Calculator/Images.xcassets/graph-highlighted.imageset/graph-highlighted@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m2mtech/calculator-2015/5c51ba53fbaa07b3e3f0f286b5c46b2600d558b6/Calculator/Images.xcassets/graph-highlighted.imageset/graph-highlighted@2x.png -------------------------------------------------------------------------------- /Calculator/Images.xcassets/graph-highlighted.imageset/graph-highlighted@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m2mtech/calculator-2015/5c51ba53fbaa07b3e3f0f286b5c46b2600d558b6/Calculator/Images.xcassets/graph-highlighted.imageset/graph-highlighted@3x.png -------------------------------------------------------------------------------- /Calculator/Images.xcassets/graph.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "graph.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "graph@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "graph@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Calculator/Images.xcassets/graph.imageset/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m2mtech/calculator-2015/5c51ba53fbaa07b3e3f0f286b5c46b2600d558b6/Calculator/Images.xcassets/graph.imageset/graph.png -------------------------------------------------------------------------------- /Calculator/Images.xcassets/graph.imageset/graph@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m2mtech/calculator-2015/5c51ba53fbaa07b3e3f0f286b5c46b2600d558b6/Calculator/Images.xcassets/graph.imageset/graph@2x.png -------------------------------------------------------------------------------- /Calculator/Images.xcassets/graph.imageset/graph@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m2mtech/calculator-2015/5c51ba53fbaa07b3e3f0f286b5c46b2600d558b6/Calculator/Images.xcassets/graph.imageset/graph@3x.png -------------------------------------------------------------------------------- /Calculator/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | at.m2m.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Calculator/StatisticsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatisticsViewController.swift 3 | // Calculator 4 | // 5 | // Created by Martin Mandl on 02.03.15. 6 | // Copyright (c) 2015 m2m server software gmbh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class StatisticsViewController: UIViewController { 12 | 13 | @IBOutlet weak var textView: UITextView! { 14 | didSet { 15 | textView.text = text 16 | } 17 | } 18 | 19 | var text: String = "" { 20 | didSet { 21 | textView?.text = text 22 | } 23 | } 24 | 25 | override var preferredContentSize: CGSize { 26 | get { 27 | if textView != nil && presentingViewController != nil { 28 | return textView.sizeThatFits(presentingViewController!.view.bounds.size) 29 | } 30 | return super.preferredContentSize 31 | } 32 | set { super.preferredContentSize = newValue } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /CalculatorTests/CalculatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalculatorTests.swift 3 | // CalculatorTests 4 | // 5 | // Created by Martin Mandl on 26.01.15. 6 | // Copyright (c) 2015 m2m server software gmbh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class CalculatorTests: XCTestCase { 13 | 14 | func testPushOperandVariable() { 15 | var brain = CalculatorBrain() 16 | XCTAssertNil(brain.pushOperand("x")) 17 | XCTAssertEqual(brain.program[0] as! String, "x") 18 | brain.variableValues = ["x": 3.14] 19 | XCTAssertEqual(3.14, brain.pushOperand("x")!) 20 | XCTAssertEqual(6.28, brain.performOperation("+")!) 21 | //println("\(brain.program)") 22 | } 23 | 24 | func testDescription() { 25 | // cos(10) -> cos10 26 | var brain = CalculatorBrain() 27 | XCTAssertEqual(brain.pushOperand(10)!, 10) 28 | XCTAssertTrue(brain.performOperation("cos")! - -0.839 < 0.1) 29 | //XCTAssertEqual(brain.description, "cos(10)") 30 | XCTAssertEqual(brain.description, "cos 10") 31 | 32 | // 3 - 5 33 | brain = CalculatorBrain() 34 | XCTAssertEqual(brain.pushOperand(3)!, 3) 35 | XCTAssertEqual(brain.pushOperand(5)!, 5) 36 | XCTAssertEqual(brain.performOperation("−")!, -2) 37 | XCTAssertEqual(brain.description, "3 − 5") 38 | 39 | // 23.5 40 | brain = CalculatorBrain() 41 | XCTAssertEqual(brain.pushOperand(23.5)!, 23.5) 42 | XCTAssertEqual(brain.description, "23.5") 43 | 44 | // π 45 | brain = CalculatorBrain() 46 | XCTAssertEqual(brain.performOperation("π")!, M_PI) 47 | XCTAssertEqual(brain.description, "π") 48 | 49 | // x 50 | brain = CalculatorBrain() 51 | XCTAssertNil(brain.pushOperand("x")) 52 | XCTAssertEqual(brain.description, "x") 53 | 54 | // √(10) + 3 -> √10 + 3 55 | brain = CalculatorBrain() 56 | XCTAssertEqual(brain.pushOperand(10)!, 10) 57 | XCTAssertTrue(brain.performOperation("√")! - 3.162 < 0.1) 58 | XCTAssertEqual(brain.pushOperand(3)!, 3) 59 | XCTAssertTrue(brain.performOperation("+")! - 6.162 < 0.1) 60 | //XCTAssertEqual(brain.description, "√(10) + 3") 61 | XCTAssertEqual(brain.description, "√ 10 + 3") 62 | 63 | // √(3 + 5) 64 | brain = CalculatorBrain() 65 | XCTAssertEqual(brain.pushOperand(3)!, 3) 66 | XCTAssertEqual(brain.pushOperand(5)!, 5) 67 | XCTAssertEqual(brain.performOperation("+")!, 8) 68 | XCTAssertTrue(brain.performOperation("√")! - 2.828 < 0.1) 69 | XCTAssertEqual(brain.description, "√ (3 + 5)") 70 | 71 | // 3 + (5 + 4) -> 3 + 5 + 4 72 | brain = CalculatorBrain() 73 | XCTAssertEqual(brain.pushOperand(3)!, 3) 74 | XCTAssertEqual(brain.pushOperand(5)!, 5) 75 | XCTAssertEqual(brain.pushOperand(4)!, 4) 76 | XCTAssertEqual(brain.performOperation("+")!, 9) 77 | XCTAssertEqual(brain.performOperation("+")!, 12) 78 | //XCTAssertEqual(brain.description, "3 + (5 + 4)") 79 | XCTAssertEqual(brain.description, "3 + 5 + 4") 80 | 81 | // √(3 + √(5)) ÷ 6 -> √(3 + √5) ÷ 6 82 | brain = CalculatorBrain() 83 | XCTAssertEqual(brain.pushOperand(3)!, 3) 84 | XCTAssertEqual(brain.pushOperand(5)!, 5) 85 | XCTAssertTrue(brain.performOperation("√")! - 2.236 < 0.1) 86 | XCTAssertTrue(brain.performOperation("+")! - 5.236 < 0.1) 87 | XCTAssertTrue(brain.performOperation("√")! - 2.288 < 0.1) 88 | XCTAssertEqual(brain.pushOperand(6)!, 6) 89 | XCTAssertTrue(brain.performOperation("÷")! - 0.381 < 0.1) 90 | //XCTAssertEqual(brain.description, "√(3 + √(5)) ÷ 6") 91 | XCTAssertEqual(brain.description, "√ (3 + √ 5) ÷ 6") 92 | 93 | // ? + 3 94 | brain = CalculatorBrain() 95 | XCTAssertEqual(brain.pushOperand(3)!, 3) 96 | XCTAssertNil(brain.performOperation("+")) 97 | XCTAssertEqual(brain.description, "? + 3") 98 | 99 | // √(3 + 5), cos(π) -> √(3 + 5), cosπ 100 | brain = CalculatorBrain() 101 | XCTAssertEqual(brain.pushOperand(3)!, 3) 102 | XCTAssertEqual(brain.pushOperand(5)!, 5) 103 | XCTAssertEqual(brain.performOperation("+")!, 8) 104 | XCTAssertTrue(brain.performOperation("√")! - 2.828 < 0.1) 105 | XCTAssertEqual(brain.performOperation("π")!, M_PI) 106 | XCTAssertEqual(brain.performOperation("cos")!, -1) 107 | //XCTAssertEqual(brain.description, "√(3 + 5), cos(π)") 108 | XCTAssertEqual(brain.description, "√ (3 + 5), cos π") 109 | 110 | // 3 * (5 + 4) 111 | brain = CalculatorBrain() 112 | XCTAssertEqual(brain.pushOperand(3)!, 3) 113 | XCTAssertEqual(brain.pushOperand(5)!, 5) 114 | XCTAssertEqual(brain.pushOperand(4)!, 4) 115 | XCTAssertEqual(brain.performOperation("+")!, 9) 116 | XCTAssertEqual(brain.performOperation("×")!, 27) 117 | XCTAssertEqual(brain.description, "3 × (5 + 4)") 118 | 119 | // 3 + (6 × 9) 120 | brain = CalculatorBrain() 121 | XCTAssertEqual(brain.pushOperand(3)!, 3) 122 | XCTAssertEqual(brain.pushOperand(6)!, 6) 123 | XCTAssertEqual(brain.pushOperand(9)!, 9) 124 | XCTAssertEqual(brain.performOperation("×")!, 54) 125 | XCTAssertEqual(brain.performOperation("+")!, 57) 126 | XCTAssertEqual(brain.description, "3 + 6 × 9") 127 | 128 | // (3 + 6) × 9 129 | brain = CalculatorBrain() 130 | XCTAssertEqual(brain.pushOperand(3)!, 3) 131 | XCTAssertEqual(brain.pushOperand(6)!, 6) 132 | XCTAssertEqual(brain.performOperation("+")!, 9) 133 | XCTAssertEqual(brain.pushOperand(9)!, 9) 134 | XCTAssertEqual(brain.performOperation("×")!, 81) 135 | XCTAssertEqual(brain.description, "(3 + 6) × 9") 136 | 137 | // println("\(brain.program)") 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /CalculatorTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | at.m2m.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Calculator of the cs193p lecture (Winter 2015) 2 | 3 | written in Xcode 6.3.1 for iOS8.3+ 4 | 5 | 6 | ### various versions are available via branches and tags: 7 | 8 | + [Project #3 Assignment #3 Extra Task #6](http://cs193p.m2m.at/cs193p-project-3-assignment-3-extra-task-6-winter-2015/) -> [project3assignment3extratask6](https://github.com/m2mtech/calculator-2015/tree/project3assignment3extratask6) 9 | + [Project #3 Assignment #3 Extra Task #5](http://cs193p.m2m.at/cs193p-project-3-assignment-3-extra-task-5-winter-2015/) -> [project3assignment3extratask5](https://github.com/m2mtech/calculator-2015/tree/project3assignment3extratask5) 10 | + [Project #3 Assignment #3 Extra Task #4](http://cs193p.m2m.at/cs193p-project-3-assignment-3-extra-task-4-winter-2015/) -> [project3assignment3extratask4](https://github.com/m2mtech/calculator-2015/tree/project3assignment3extratask4) 11 | + [Project #3 Assignment #3 Extra Task #3](http://cs193p.m2m.at/cs193p-project-3-assignment-3-extra-task-3-winter-2015/) -> [project3assignment3extratask3](https://github.com/m2mtech/calculator-2015/tree/project3assignment3extratask3) 12 | + [Project #3 Assignment #3 Extra Task #2](http://cs193p.m2m.at/cs193p-project-3-assignment-3-extra-task-2-winter-2015/) -> [project3assignment3extratask2](https://github.com/m2mtech/calculator-2015/tree/project3assignment3extratask2) 13 | + [Project #3 Assignment #3 Task #11](http://cs193p.m2m.at/cs193p-project-3-assignment-3-task-11-winter-2015/) -> [project3assignment3task11](https://github.com/m2mtech/calculator-2015/tree/project3assignment3task11) 14 | + [Project #3 Assignment #3 Task #10](http://cs193p.m2m.at/cs193p-project-3-assignment-3-task-10-winter-2015/) -> [project3assignment3task10](https://github.com/m2mtech/calculator-2015/tree/project3assignment3task10) 15 | + [Project #3 Assignment #3 Task #9](http://cs193p.m2m.at/cs193p-project-3-assignment-3-task-9-winter-2015/) -> [project3assignment3task9](https://github.com/m2mtech/calculator-2015/tree/project3assignment3task9) 16 | + [Project #3 Assignment #3 Task #8](http://cs193p.m2m.at/cs193p-project-3-assignment-3-task-8-winter-2015/) -> [project3assignment3task8](https://github.com/m2mtech/calculator-2015/tree/project3assignment3task8) 17 | + [Project #3 Assignment #3 Task #7](http://cs193p.m2m.at/cs193p-project-3-assignment-3-task-7-winter-2015/) -> [project3assignment3task7](https://github.com/m2mtech/calculator-2015/tree/project3assignment3task7) 18 | + [Project #3 Assignment #3 Task #6](http://cs193p.m2m.at/cs193p-project-3-assignment-3-task-6-winter-2015/) -> [project3assignment3task6](https://github.com/m2mtech/calculator-2015/tree/project3assignment3task6) 19 | + [Project #3 Assignment #3 Task #5](http://cs193p.m2m.at/cs193p-project-3-assignment-3-task-5-winter-2015/) -> [project3assignment3task5](https://github.com/m2mtech/calculator-2015/tree/project3assignment3task5) 20 | + [Project #3 Assignment #3 Task #4](http://cs193p.m2m.at/cs193p-project-3-assignment-3-task-4-winter-2015/) -> [project3assignment3task4](https://github.com/m2mtech/calculator-2015/tree/project3assignment3task4) 21 | + [Project #3 Assignment #3 Task #3](http://cs193p.m2m.at/cs193p-project-3-assignment-3-task-3-winter-2015/) -> [project3assignment3task3](https://github.com/m2mtech/calculator-2015/tree/project3assignment3task3) 22 | + [Project #3 Assignment #3 Task #2](http://cs193p.m2m.at/cs193p-project-3-assignment-3-task-2-winter-2015/) -> [project3assignment3task2](https://github.com/m2mtech/calculator-2015/tree/project3assignment3task2) 23 | 24 | === 25 | 26 | + [Project #2 Assignment #2 Extra Task #3](http://cs193p.m2m.at/cs193p-project-2-assignment-2-extra-task-3-winter-2015/) -> [project2assignment2extratask3](https://github.com/m2mtech/calculator-2015/tree/project2assignment2extratask3) 27 | + [Project #2 Assignment #2 Extra Task #2](http://cs193p.m2m.at/cs193p-project-2-assignment-2-extra-task-2-winter-2015/) -> [project2assignment2extratask2](https://github.com/m2mtech/calculator-2015/tree/project2assignment2extratask2) 28 | + [Project #2 Assignment #2 Extra Task #1](http://cs193p.m2m.at/cs193p-project-2-assignment-2-extra-task-1-winter-2015/) -> [project2assignment2extratask1](https://github.com/m2mtech/calculator-2015/tree/project2assignment2extratask1) 29 | + [Project #2 Assignment #2 Task #9](http://cs193p.m2m.at/cs193p-project-2-assignment-2-task-9-winter-2015/) -> [project2assignment2task9](https://github.com/m2mtech/calculator-2015/tree/project2assignment2task9) 30 | + [Project #2 Assignment #2 Task #8](http://cs193p.m2m.at/cs193p-project-2-assignment-2-task-8-winter-2015/) -> [project2assignment2task8](https://github.com/m2mtech/calculator-2015/tree/project2assignment2task8) 31 | + [Project #2 Assignment #2 Task #7](http://cs193p.m2m.at/cs193p-project-2-assignment-2-task-7-winter-2015/) -> [project2assignment2task7](https://github.com/m2mtech/calculator-2015/tree/project2assignment2task7) 32 | + [Project #2 Assignment #2 Task #6](http://cs193p.m2m.at/cs193p-project-2-assignment-2-task-6-winter-2015/) -> [project2assignment2task6](https://github.com/m2mtech/calculator-2015/tree/project2assignment2task6) 33 | + [Project #2 Assignment #2 Task #5](http://cs193p.m2m.at/cs193p-project-2-assignment-2-task-5-winter-2015/) -> [project2assignment2task5](https://github.com/m2mtech/calculator-2015/tree/project2assignment2task5) 34 | + [Project #2 Assignment #2 Task #4](http://cs193p.m2m.at/cs193p-project-2-assignment-2-task-4-winter-2015/) -> [project2assignment2task4](https://github.com/m2mtech/calculator-2015/tree/project2assignment2task4) 35 | 36 | [End of Lecture #5](http://cs193p.m2m.at/cs193p-lecture-5-objective-c-compatibility-property-list-views-winter-2015/) -> [EndOfLecture5](https://github.com/m2mtech/calculator-2015/tree/EndOfLecture5) 37 | 38 | + [Project #1 Assignment #1 Internationalization](http://cs193p.m2m.at/cs193p-project-1-assignment-1-internationalization-winter-2015/) -> [project1assignment1internationalization](https://github.com/m2mtech/calculator-2015/tree/project1assignment1internationalization) 39 | + [Project #1 Assignment #1 Extra Task #4](http://cs193p.m2m.at/cs193p-project-1-assignment-1-extra-task-4-winter-2015/) -> [project1assignment1extratask4corrected](https://github.com/m2mtech/calculator-2015/tree/project1assignment1extratask4corrected) 40 | + [Project #1 Assignment #1 Extra Task #3](http://cs193p.m2m.at/cs193p-project-1-assignment-1-extra-task-3-winter-2015/) -> [project1assignment1extratask3](https://github.com/m2mtech/calculator-2015/tree/project1assignment1extratask3) 41 | + [Project #1 Assignment #1 Extra Task #2](http://cs193p.m2m.at/cs193p-project-1-assignment-1-extra-task-2-winter-2015/) -> [project1assignment1extratask2](https://github.com/m2mtech/calculator-2015/tree/project1assignment1extratask2) 42 | + [Project #1 Assignment #1 Extra Task #1](http://cs193p.m2m.at/cs193p-project-1-assignment-1-extra-task-1-winter-2015/) -> [project1assignment1extratask1](https://github.com/m2mtech/calculator-2015/tree/project1assignment1extratask1) 43 | + [Project #1 Assignment #1 Task #5](http://cs193p.m2m.at/cs193p-project-1-assignment-1-task-5-winter-2015/) -> [project1assignment1task5](https://github.com/m2mtech/calculator-2015/tree/project1assignment1task5) 44 | + [Project #1 Assignment #1 Task #4](http://cs193p.m2m.at/cs193p-project-1-assignment-1-task-4-winter-2015/) -> [project1assignment1task4](https://github.com/m2mtech/calculator-2015/tree/project1assignment1task4) 45 | + [Project #1 Assignment #1 Task #3](http://cs193p.m2m.at/cs193p-project-1-assignment-1-task-3-winter-2015/) -> [project1assignment1task3](https://github.com/m2mtech/calculator-2015/tree/project1assignment1task3) 46 | + [Project #1 Assignment #1 Task #2](http://cs193p.m2m.at/cs193p-project-1-assignment-1-task-2-winter-2015/) -> [project1assignment1task2](https://github.com/m2mtech/calculator-2015/tree/project1assignment1task2) 47 | 48 | [End of Lecture #3](http://cs193p.m2m.at/cs193p-lecture-3-applying-mvc-winter-2015/) -> [EndOfLecture3](https://github.com/m2mtech/calculator-2015/tree/EndOfLecture3) 49 | 50 | [End of Lecture #2](http://cs193p.m2m.at/cs193p-lecture-2-more-xcode-and-swift-mvc-winter-2015/) -> [EndOfLecture2](https://github.com/m2mtech/calculator-2015/tree/EndOfLecture2) 51 | 52 | [End of Lecture #1](http://cs193p.m2m.at/cs193p-lecture-1-logistics-ios8-overview-winter-2015/) -> [EndOfLecture1](https://github.com/m2mtech/calculator-2015/tree/EndOfLecture1) --------------------------------------------------------------------------------