Commits

Jay Yu  committed 0441c38

work in progress: Finished descriptionOfProgram, almost done with test
buttons.

TODO: Undo, Extra Credit

  • Participants
  • Parent commits ff068a1

Comments (0)

Files changed (7)

File Calculator.xcodeproj/project.xcworkspace/xcuserdata/jay.xcuserdatad/UserInterfaceState.xcuserstate

Binary file modified.

File Calculator.xcodeproj/xcuserdata/jay.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist

 <Bucket
    type = "1"
    version = "1.0">
+   <ExceptionBreakpoints>
+      <ExceptionBreakpoint
+         shouldBeEnabled = "No"
+         ignoreCount = "0"
+         continueAfterRunningActions = "No"
+         scope = "0"
+         stopOnStyle = "0">
+         <Actions>
+            <BreakpointActionProxy
+               ActionType = "Xcode.BreakpointAction.DebuggerCommand">
+               <ActionContent>
+               </ActionContent>
+            </BreakpointActionProxy>
+         </Actions>
+      </ExceptionBreakpoint>
+   </ExceptionBreakpoints>
 </Bucket>

File Calculator/CalculatorBrain.h

 @interface CalculatorBrain : NSObject
 
 - (void)pushOperand:(double)operand;
+- (void)pushVariable:(NSString *)variable;
 - (double)performOperation:(NSString *)operation;
 - (void)clearStates;
 
 // New in Assignment 2
 @property (readonly) id program;
-+ (double)runProgram:(id)program;
+@property (nonatomic, strong) NSDictionary *variableValues;
++ (double)runProgram:(id)program
+ usingVariableValues:(NSDictionary *)variableValues;
++ (NSSet *)variablesUsedInProgram:(id)program;
 + (NSString *)descriptionOfProgram:(id)program;
 
 @end

File Calculator/CalculatorBrain.m

 
 @implementation CalculatorBrain
 @synthesize programStack = _programStack;
+@synthesize variableValues = _variableValues;
 
+#pragma getters
 - (NSMutableArray *)programStack
 {
     if (!_programStack) {
     return _programStack;
 }
 
+- (id)variableValues
+{
+    if (!_variableValues) {
+        _variableValues = [[NSDictionary alloc] init];
+    }
+    return _variableValues;
+}
+
+- (id)program
+{
+    return [self.programStack copy];
+}
+
+#pragma instance methods
 - (void)pushOperand:(double)operand
 {
     NSNumber *operandObject = [NSNumber numberWithDouble:operand];
     [self.programStack addObject:operandObject];
 }
 
+- (void)pushVariable:(NSString *)variable
+{
+    [self.programStack addObject:variable];
+}
+
 - (double)performOperation:(NSString *)operation
 {
     [self.programStack addObject:operation];
-    return [CalculatorBrain runProgram:self.program];
+    return [CalculatorBrain runProgram:self.program usingVariableValues:self.variableValues];
 }
 
 - (void)clearStates
     [self.programStack removeAllObjects];
 }
 
-- (id)program
-{
-    return [self.programStack copy];
-}
-
 + (double)runProgram:(id)program
+ usingVariableValues:(NSDictionary *)variableValues
 {
     NSMutableArray *stack;
     if ([program isKindOfClass:[NSArray class]]) {
         stack = [program mutableCopy];
     }
-    return [self popOperandOffStack:stack];
+    return [self popOperandOffStack:stack usingVariableValues:variableValues];
 }
 
 + (NSString *)descriptionOfProgram:(id)program
 {
     NSMutableArray *stack;
+    NSMutableArray *descriptions = [[NSMutableArray alloc] init];
     if ([program isKindOfClass:[NSArray class]]) {
         stack = [program mutableCopy];
     }
-    return [self programStringOffStack:stack];
-
+    // Separate descriptions by commas if there are multiple things on the stack
+    while ([stack count] > 0) {
+        [descriptions addObject:[self descriptionOfTopOfStack:stack
+                                                 currentDepth:0  // Always starts with zero!
+                                                lastOperation:nil]];
+    }
+    return [descriptions componentsJoinedByString:@", "];
 }
 
-+ (BOOL)isHigherPriority:(NSString *)operator1:(NSString *)operator2
++ (NSString *)descriptionOfTopOfStack:(NSMutableArray *)stack
+                         currentDepth:(int)depth  // Depth of the recursion
+                        lastOperation:(NSString *)lastOperation
 {
-    NSLog(@"Comparing: %@, %@", operator1, operator2);
-    if (!operator1 || !operator2) {
-        return NO;
+    NSString *result = nil;
+    id topOfStack = [stack lastObject];
+    if (topOfStack) {
+        [stack removeLastObject];
+    }
+    depth = depth + 1;
+
+    if ([topOfStack isKindOfClass:[NSNumber class]]) {
+        result = [NSString stringWithFormat:@"%g", [topOfStack doubleValue]];
+
+    } else if ([topOfStack isKindOfClass:[NSString class]]) {
+        NSString *operation = topOfStack;
+        if ([self isBinaryOperation:operation]) {
+            NSString *operandRight = [self descriptionOfTopOfStack:stack currentDepth:depth lastOperation:operation];
+            NSString *operandLeft = [self descriptionOfTopOfStack:stack currentDepth:depth lastOperation:operation];
+
+            // Icing on the cake: Automatically convert x * x to x²
+            // Only works if x is not an (expression)
+            NSRange range = [operandLeft rangeOfString:@" "];
+            if (range.location == NSNotFound
+                && [@"*" isEqualToString:operation]
+                && [operandLeft isEqualToString:operandRight]) {
+
+                result = [NSString stringWithFormat:@"%@²", operandLeft];
+            } else {
+                NSString *template;
+                if ([self useBracket:depth withCurrentOperation:operation withLastOperation:lastOperation]) {
+                    template = @"(%@ %@ %@)";
+                } else {
+                    template = @"%@ %@ %@";
+                }
+                
+                result = [NSString stringWithFormat:template, operandLeft, operation, operandRight];
+            }
+
+        } else if ([self isUnaryOperation:operation]) {
+            NSString *template;
+            if ([self useBracket:depth withCurrentOperation:operation withLastOperation:lastOperation]) {
+                template = @"%@(%@)";
+            } else {
+                template = @"%@%@";
+            }
+            result = [NSString stringWithFormat:template, operation, [self descriptionOfTopOfStack:stack currentDepth:depth lastOperation:operation]];
+
+        } else if ([@"π" isEqualToString:operation]) {
+            result = @"π";
+
+        } else {
+            // Variable, make a copy
+            result = [NSString stringWithFormat:@"%@", operation];
+        }
     }
 
-    NSDictionary *lookUpTable = [[NSDictionary alloc] initWithObjectsAndKeys:
-                         [NSNumber numberWithInt:100], @"sqrt",
-                         [NSNumber numberWithInt:100], @"tan",
-                         [NSNumber numberWithInt:100], @"cos",
-                         [NSNumber numberWithInt:100], @"sin",
-                         [NSNumber numberWithInt:90], @"*",
-                         [NSNumber numberWithInt:90], @"/",
-                         [NSNumber numberWithInt:10], @"+",
-                         [NSNumber numberWithInt:10], @"-",
-                         nil];
+    return result;
+}
 
-    if ([[lookUpTable valueForKey:operator1] intValue] > [[lookUpTable valueForKey:operator2] intValue]) {
++ (BOOL)isBinaryOperation:(NSString *)operation
+{
+    if ([@"+" isEqualToString:operation]
+        || [@"-" isEqualToString:operation]
+        || [@"*" isEqualToString:operation]
+        || [@"/" isEqualToString:operation]) {
         return YES;
     }
     return NO;
 }
 
-+ (NSString *)lookAheadOperationInStack:(NSMutableArray *)stack
++ (BOOL)isUnaryOperation:(NSString *)operation
 {
-    id topOfStack = [stack lastObject];
-    while (topOfStack) {
-        [stack removeLastObject];
-        if ([topOfStack isKindOfClass:[NSString class]]) {
-            return topOfStack;
-        }
-        topOfStack = [stack lastObject];
+    if ([@"sin" isEqualToString:operation]
+        || [@"cos" isEqualToString:operation]
+        || [@"tan" isEqualToString:operation]
+        || [@"sqrt" isEqualToString:operation]
+        || [@"√" isEqualToString:operation]
+        || [@"±" isEqualToString:operation]
+        || [@"+/-" isEqualToString:operation]
+        ) {
+        return YES;
     }
-    return nil;
+    return NO;
 }
 
-+ (NSString *)programStringOffStack:(NSMutableArray *)stack
++ (BOOL)useBracket:(int)depth withCurrentOperation:(NSString *)currentOperation
+                                 withLastOperation:(NSString *)lastOperation
 {
-    NSString *result = nil;
-    NSString *nextOperation = nil;
-    id topOfStack = [stack lastObject];
-    if (topOfStack) {
-        [stack removeLastObject];
+    if (depth == 1 && [self isBinaryOperation:currentOperation]) {
+        return NO;
     }
 
-    if ([topOfStack isKindOfClass:[NSNumber class]]) {
-        result = [NSString stringWithFormat:@"%g", [topOfStack doubleValue]];
-
-
-    } else if ([topOfStack isKindOfClass:[NSString class]]) {
-        NSString *operation = topOfStack;
-        nextOperation = [self lookAheadOperationInStack:[stack mutableCopy]];
-        NSLog(@"programStringOffStack: next operation: %@", nextOperation);
-        BOOL useBracket = NO;
-        if (  [self isHigherPriority:operation:nextOperation]) {
-            useBracket = YES;
-        }
-
-        if ([operation isEqualToString:@"+"] || [@"-" isEqualToString:operation] || [@"*" isEqualToString:operation]) {
-            NSString *operandRight = [self programStringOffStack:stack];
-            NSString *format = @"(%@ %@ %@)";
-            if (false && useBracket) {
-                format = @"%@ %@ %@";
-            }
-            result = [NSString stringWithFormat:format, [self programStringOffStack:stack], operation, operandRight];
-
-        } else if ([@"/" isEqualToString:operation]) {
-            NSString *divisor = [self programStringOffStack:stack];
-            if ([divisor isEqualToString:@"0"] || [divisor isEqualToString:@"-0"]) {
-                result = [NSString stringWithFormat:@"(%@ / %@)", [self programStringOffStack:stack], divisor];
-            } else {
-                result = @"NaN";  // NaN means Not A Number
-            }
-        } else if ([@"sin" isEqualToString:operation]) {
-            result = [NSString stringWithFormat:@"sin(%@)", [self programStringOffStack:stack]];
-
-        } else if ([@"cos" isEqualToString:operation]) {
-            result = [NSString stringWithFormat:@"cos√(%@)", [self programStringOffStack:stack]];
-
-        } else if ([@"sqrt" isEqualToString:operation]) {
-            result = [NSString stringWithFormat:@"√(%@)", [self programStringOffStack:stack]];
-
-        } else if ([@"π" isEqualToString:operation]) {
-            result = @"π";
-
-        } else if ([@"+/-" isEqualToString:operation]) {
-            result = [NSString stringWithFormat:@"±(%@)", [self programStringOffStack:stack]];
-
-        }
+    if (([@"*" isEqualToString:currentOperation] || [@"/" isEqualToString:currentOperation])
+        && [self isBinaryOperation:lastOperation]) {
+        return NO;
     }
 
-    NSLog(@"programStringOffStack: %@, next operation: %@", result, nextOperation);
-    return result;
+    if (([@"+" isEqualToString:currentOperation] || [@"-" isEqualToString:currentOperation])
+        && ([@"+" isEqualToString:lastOperation] || [@"-" isEqualToString:lastOperation])) {
+        return NO;
+    }
 
+    if ([self isUnaryOperation:lastOperation] && [self isBinaryOperation:currentOperation]) {
+        return NO;
+    }
+
+    return YES;
 }
 
 + (double)popOperandOffStack:(NSMutableArray *)stack
+         usingVariableValues:(NSDictionary *)variableValues
 {
     double result = 0;
     id topOfStack = [stack lastObject];
 
     if ([topOfStack isKindOfClass:[NSNumber class]]) {
         result = [topOfStack doubleValue];
+
     } else if ([topOfStack isKindOfClass:[NSString class]]) {
         NSString *operation = topOfStack;
         if ([operation isEqualToString:@"+"]) {
-            result = [self popOperandOffStack:stack] + [self popOperandOffStack:stack];
+            result = [self popOperandOffStack:stack usingVariableValues:variableValues] + [self popOperandOffStack:stack usingVariableValues:variableValues];
         } else if ([@"*" isEqualToString:operation]) {
-            result = [self popOperandOffStack:stack] * [self popOperandOffStack:stack];
+            result = [self popOperandOffStack:stack usingVariableValues:variableValues] * [self popOperandOffStack:stack usingVariableValues:variableValues];
         } else if ([@"-" isEqualToString:operation]) {
-            double subtrahend = [self popOperandOffStack:stack];
-            result = [self popOperandOffStack:stack] - subtrahend;
+            double subtrahend = [self popOperandOffStack:stack usingVariableValues:variableValues];
+            result = [self popOperandOffStack:stack usingVariableValues:variableValues] - subtrahend;
         } else if ([@"/" isEqualToString:operation]) {
-            double divisor = [self popOperandOffStack:stack];
+            double divisor = [self popOperandOffStack:stack usingVariableValues:variableValues];
             if (divisor) {
-                result = [self popOperandOffStack:stack] / divisor;
+                result = [self popOperandOffStack:stack usingVariableValues:variableValues] / divisor;
             }
         } else if ([@"sin" isEqualToString:operation]) {
-            result = sin([self popOperandOffStack:stack]);
+            result = sin([self popOperandOffStack:stack usingVariableValues:variableValues]);
         } else if ([@"cos" isEqualToString:operation]) {
-            result = cos([self popOperandOffStack:stack]);
+            result = cos([self popOperandOffStack:stack usingVariableValues:variableValues]);
         } else if ([@"sqrt" isEqualToString:operation]) {
-            double operand = [self popOperandOffStack:stack];
+            double operand = [self popOperandOffStack:stack usingVariableValues:variableValues];
             if (operand < 0) {
                 result = 0;
             } else {
         } else if ([@"π" isEqualToString:operation]) {
             result = M_PI;
         } else if ([@"+/-" isEqualToString:operation]) {
-            result = [self popOperandOffStack:stack];
+            result = [self popOperandOffStack:stack usingVariableValues:variableValues];
             if (result != 0) {
                 result = result * -1;
             }
+        } else {
+            // Must be a variable!
+            NSNumber *variableValue = [variableValues valueForKey:operation];
+            if (variableValue != nil) {
+                result = [variableValue doubleValue];
+            }
         }
     }
     return result;
 }
 
++ (NSSet *)variablesUsedInProgram:(id)program
+{
+    NSMutableSet *variables = [[NSMutableSet alloc] init];
+    [variables addObjectsFromArray:program];
+    [variables filterUsingPredicate:[NSPredicate predicateWithFormat:@"SELF = 'x' OR SELF = 'y' OR SELF = 'z'"]];
+    return [variables copy];
+}
+
 @end

File Calculator/CalculatorViewController.h

 
 @property (weak, nonatomic) IBOutlet UILabel *display;
 @property (weak, nonatomic) IBOutlet UILabel *history;
+@property (weak, nonatomic) IBOutlet UILabel *variablesDisplay;
 
 @end

File Calculator/CalculatorViewController.m

 @interface CalculatorViewController ()
 @property (nonatomic, strong) CalculatorBrain *brain;
 @property (nonatomic) BOOL userIsInTheMiddleOfEnteringANumber;
+@property (nonatomic, strong) NSDictionary *testVariableValues;
 @end
 
 @implementation CalculatorViewController
 @synthesize brain = _brain;
 @synthesize display = _display;
 @synthesize history = _history;
+@synthesize variablesDisplay = _variablesDisplay;
 @synthesize userIsInTheMiddleOfEnteringANumber = _userIsInTheMiddleOfEnteringANumber;
+@synthesize testVariableValues = _testVariableValues;
 
 - (CalculatorBrain *)brain
 {
     return _brain;
 }
 
+- (NSDictionary *)testVariableValues
+{
+    if (!_testVariableValues) {
+        _testVariableValues = [[NSDictionary alloc] init];
+    }
+    return _testVariableValues;
+}
+
 - (void)appendHistory:(NSString *)aString;
 {
     // Implements Required Task #4, Assignment 1
 //    [self appendHistory:@"="];
 }
 
+- (IBAction)variablePressed:(UIButton *)sender
+{
+    NSString *variable = sender.currentTitle;
+    [self.brain pushVariable:variable];
+    self.history.text = [CalculatorBrain descriptionOfProgram:self.brain.program];
+}
+
 - (IBAction)allClear
 {
     [self.brain clearStates];
     }
 }
 
+- (IBAction)testPressed:(UIButton *)sender
+{
+    NSString *testNumber = sender.currentTitle;
+
+    if ([@"Test 1" isEqualToString:testNumber]) {
+        self.testVariableValues = [NSDictionary dictionaryWithObjectsAndKeys:
+                                   [NSNumber numberWithDouble:9.23], @"x", 
+                                   [NSNumber numberWithDouble:-20], @"y", 
+                                   [NSNumber numberWithDouble:0], @"z", nil]; 
+    }
+
+    if ([@"Test 2" isEqualToString:testNumber]) {
+        self.testVariableValues = [NSDictionary dictionaryWithObjectsAndKeys:
+                                   [NSNumber numberWithDouble:-2.11], @"x", 
+                                   [NSNumber numberWithDouble:38.000], @"y", 
+                                   [NSNumber numberWithDouble:0.18], @"z", nil]; 
+    }
+
+    if ([@"Test 3" isEqualToString:testNumber]) {
+        self.testVariableValues = nil; 
+        self.variablesDisplay.text = @"Variable not set";
+        return;
+    }
+    
+    NSMutableArray *varList = [[NSMutableArray alloc] init];
+    for (id variable in [CalculatorBrain variablesUsedInProgram:self.brain.program]) {
+        [varList addObject:[NSString stringWithFormat:@"%@ = %@", variable, [self.testVariableValues objectForKey:variable]]];        
+    }
+    self.variablesDisplay.text = [varList componentsJoinedByString:@", "];
+}
+
 - (void)viewDidUnload {
-    [self setHistory:nil];
+    self.history = nil;
+    self.variablesDisplay = nil;
+    self.testVariableValues = nil; 
+    [self setVariablesDisplay:nil];
     [super viewDidUnload];
 }
 @end

File Calculator/en.lproj/MainStoryboard.storyboard

                                 </connections>
                             </button>
                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="LRj-Y9-noo">
-                                <rect key="frame" x="20" y="281" width="64" height="40"/>
+                                <rect key="frame" x="20" y="329" width="64" height="40"/>
                                 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                 <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
                                 <state key="normal" title="sin">
                                 </connections>
                             </button>
                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="KKZ-L7-EkC">
-                                <rect key="frame" x="92" y="281" width="64" height="40"/>
+                                <rect key="frame" x="92" y="329" width="64" height="40"/>
                                 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                 <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
                                 <state key="normal" title="cos">
                                 </connections>
                             </button>
                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="xLr-Pv-Ba9">
-                                <rect key="frame" x="164" y="281" width="64" height="40"/>
+                                <rect key="frame" x="164" y="329" width="64" height="40"/>
                                 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                 <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
                                 <state key="normal" title="sqrt">
                                 </connections>
                             </button>
                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="kAe-yq-NPe">
-                                <rect key="frame" x="236" y="329" width="64" height="88"/>
+                                <rect key="frame" x="236" y="329" width="64" height="40"/>
                                 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                 <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
                                 <state key="normal" title="Enter">
                                 </connections>
                             </button>
                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="c7h-b2-JmJ">
-                                <rect key="frame" x="20" y="329" width="64" height="40"/>
+                                <rect key="frame" x="20" y="378" width="64" height="40"/>
                                 <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                 <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
                                 <state key="normal" title="+/-">
                                     <action selector="invertSignPressed" destination="2" eventType="touchUpInside" id="ZK3-iW-jHb"/>
                                 </connections>
                             </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="YZI-en-ST1">
+                                <rect key="frame" x="20" y="281" width="64" height="40"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                                <state key="normal" title="x">
+                                    <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <state key="highlighted">
+                                    <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="variablePressed:" destination="2" eventType="touchUpInside" id="n2I-qS-Q9M"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="EWk-Fz-elZ">
+                                <rect key="frame" x="92" y="281" width="64" height="40"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                                <state key="normal" title="y">
+                                    <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <state key="highlighted">
+                                    <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="variablePressed:" destination="2" eventType="touchUpInside" id="xzA-W5-mjs"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="TwT-RN-7Pd">
+                                <rect key="frame" x="164" y="281" width="64" height="40"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                                <state key="normal" title="z">
+                                    <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <state key="highlighted">
+                                    <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="variablePressed:" destination="2" eventType="touchUpInside" id="9BW-ix-N33"/>
+                                </connections>
+                            </button>
+                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="vars:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="Ftu-AO-3GO">
+                                <rect key="frame" x="20" y="425" width="280" height="15"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                                <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="DGX-6e-d6I">
+                                <rect key="frame" x="92" y="378" width="64" height="40"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                                <state key="normal" title="Test 1">
+                                    <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <state key="highlighted">
+                                    <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="testPressed:" destination="2" eventType="touchUpInside" id="dDa-ud-Hsq"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="lje-dH-XYs">
+                                <rect key="frame" x="162" y="378" width="68" height="40"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                                <state key="normal" title="Test 2">
+                                    <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <state key="highlighted">
+                                    <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="testPressed:" destination="2" eventType="touchUpInside" id="QPY-is-51U"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="Zwc-1V-KUR">
+                                <rect key="frame" x="234" y="378" width="68" height="40"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                                <state key="normal" title="Test 3">
+                                    <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <state key="highlighted">
+                                    <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="testPressed:" destination="2" eventType="touchUpInside" id="bEv-2L-4hg"/>
+                                </connections>
+                            </button>
                         </subviews>
                         <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                     </view>
                     <connections>
                         <outlet property="display" destination="DpB-XB-3Sa" id="EX8-CC-xcJ"/>
                         <outlet property="history" destination="g8O-fd-gpD" id="lEa-h8-59a"/>
+                        <outlet property="variablesDisplay" destination="Ftu-AO-3GO" id="aqK-Q3-Rd4"/>
                     </connections>
                 </viewController>
             </objects>
             <point key="canvasLocation" x="175" y="72"/>
         </scene>
     </scenes>
+    <classes>
+        <class className="CalculatorViewController" superclassName="UIViewController">
+            <source key="sourceIdentifier" type="project" relativePath="./Classes/CalculatorViewController.h"/>
+            <relationships>
+                <relationship kind="outlet" name="display" candidateClass="UILabel"/>
+                <relationship kind="outlet" name="history" candidateClass="UILabel"/>
+                <relationship kind="outlet" name="variablesDisplay" candidateClass="UILabel"/>
+            </relationships>
+        </class>
+    </classes>
     <simulatedMetricsContainer key="defaultSimulatedMetrics">
         <simulatedStatusBarMetrics key="statusBar"/>
         <simulatedOrientationMetrics key="orientation"/>