<file_sep>//
//  ViewController.swift
//  Calculator
//
//  Created by ezios on 15/11/28.
//  Copyright (c) 2015年 ezios. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var display: UILabel!
    @IBOutlet weak var displayOperation: UILabel!
    
    var isUserIsInTypeing = false
    var brain = CalculatorBrain()

    //数字键
    @IBAction func appendDigit(sender: UIButton) {
        var digit = sender.currentTitle!
     
        switch(digit) {
        case ".":
            //已输入一个小数点将退出
            if (display.text!.rangeOfString(digit) != nil) {
                return
            }
            
            //输入第一个小数点自动在前面加零
            if isUserIsInTypeing == false {
                digit = "0."
            }
        default:
            break
        }

        if isUserIsInTypeing {
            display.text = display.text! + digit
        }else {
            display.text = digit
            isUserIsInTypeing = true
        }
    }
    
    //回车键
    @IBAction func enter() {
        if isUserIsInTypeing {
            var displayText = display.text!
            //检测是否为负数
            if displayText.hasPrefix("-") {
                display.text = dropFirst(displayText)
                
                brain.pushOperand(displayValue)
                showResult(brain.performOperation("±"))
            } else {
                showResult(brain.pushOperand(displayValue))
            }
        }
    }
    
    //变量压入键
    @IBAction func pushVariable(sender: UIButton) {
        showResult(brain.pushOperand(sender.currentTitle!))
    }
    
    //变量赋值键
    @IBAction func assignmentVariableValue() {
        if isUserIsInTypeing {
            showResult(brain.pushVariableValue(displayValue))
        }
    }
    
    //运算符键
    @IBAction func operation(sender: UIButton) {
        let operatorType = sender.currentTitle!
        
        if isUserIsInTypeing {
            enter()
        }
        
        showResult(brain.performOperation(operatorType))
    }
    
    //正负操作键
    @IBAction func negation(sender: UIButton) {
        if isUserIsInTypeing {
            if display.text!.hasPrefix("-") {
                display.text = dropFirst(display.text!)
            } else {
                display.text = "-" + display.text!
            }
        } else {
            operation(sender)
        }
    }

    //退格键
    @IBAction func backspace() {
        if isUserIsInTypeing {
            display.text = dropLast(display.text!)

            if count(display.text!) == 0 {
                isUserIsInTypeing = false
                displayValue = nil
            }
        } else {
            showResult(brain.undo())
        }
    }
    
    //运算符键
    @IBAction func showOperator(sender: UIButton) {
        displayOperation.text = sender.currentTitle!
    }
    //非运算符键
    @IBAction func hideOperator() {
        displayOperation.text = ""
    }
    
    
    //清除键 回归初始状态
    @IBAction func clear() {
        displayValue = nil
        
        brain.reset()
    }
    
    //显示结果
    func showResult(result: Double?) {
        displayValue = result
        display.text = brain.description + " = " + display.text!
    }
    
    //display的计算属性
    var displayValue: Double? {
        //get 转换标签显示的文本为double
        get {
            if let number = NSNumberFormatter().numberFromString(display.text!)?.doubleValue {
                return number
            }
            return nil
        }
        set {
            isUserIsInTypeing = false

            if newValue != nil {
                display.text = "\(newValue!)"
            } else {
                display.text = " "
            }
        }
    }
}

<file_sep>//
//  CalculatorBrain.swift
//  Calculator
//
//  Created by ezios on 15/12/5.
//  Copyright (c) 2015年 ezios. All rights reserved.
//
import Foundation

class CalculatorBrain {
    //存储操作数和操作符的枚举
    private enum Op: Printable{
        case Operand(Double)
        case Variable(String)
        case UnitaryOperation(String, Double -> Double)
        case BinaryOperation(String, (Double, Double) -> Double)
        
        var description: String {
            get {
                switch self {
                case .Operand(let value):
                    return "\(value)"
                case .Variable(let symbol):
                    return symbol
                case .UnitaryOperation(let symbol, _):
                    return symbol
                case .BinaryOperation(let symbol, _):
                    return symbol
                }
            }
        }
    }
    
    //计算栈
    private var opStack = [Op]()
    //计算函数字典
    private var knownsOps = [String: Op]()
    //变量字典
    private var variableValues = [String: Double]()
    //描述算式
    var description: String {
        get {
            if !opStack.isEmpty {
                var (describe, remainder) = evalcuteDescribe(opStack, operation: "first")
                
                println("\(opStack) =  \(describe) with \(remainder) left over")
                
                return describe!
            }
            return ""
        }
    }

    
    //构造器
    init() {
        func learnOp(op: Op) {
            knownsOps[op.description] = op
        }

        learnOp(Op.BinaryOperation("×", *))
        learnOp(Op.BinaryOperation("÷") { $1 / $0 })
        learnOp(Op.BinaryOperation("+", +))
        learnOp(Op.BinaryOperation("−") { $1 - $0 })
        learnOp(Op.UnitaryOperation("√", sqrt))
        learnOp(Op.UnitaryOperation("sin", sin))
        learnOp(Op.UnitaryOperation("cos", cos))
        learnOp(Op.UnitaryOperation("±", -))
        
        variableValues["π"] = M_PI
    }
    
    //递归计算栈中元素
    private func evalcuate(ops: [Op]) -> (result: Double?, remainingOps: [Op]) {
        if !ops.isEmpty {
            var remainingOps = ops
            let op = remainingOps.removeLast()
            
            switch op {
            case .Operand(let result):
                return (result, remainingOps)
            case .Variable(let symbol):
                return (variableValues[symbol], remainingOps)
            case .UnitaryOperation(_, let operationFunc):
                let operand = evalcuate(remainingOps)
                
                if let result = operand.result {
                    return (operationFunc(result), operand.remainingOps)
                }
            case .BinaryOperation(_, let operationFunc):
                let operand1 = evalcuate(remainingOps)
                
                if let result1 = operand1.result {
                    let operand2 = evalcuate(operand1.remainingOps)
                    
                    if let result2 = operand2.result {
                        return (operationFunc(result1, result2), operand2.remainingOps)
                    }
                }
            }
        }
        return (nil, ops)
    }
    
    //计算栈
    func evalcuate() -> Double? {
        if !opStack.isEmpty {
            let (result, remainder) = evalcuate(opStack)
            println("\(opStack) =  \(result) with \(remainder) left over")
            return result
        }
        return nil
    }
    
    //计算表达式
    private func evalcuteDescribe(ops: [Op], operation: String?) -> (describe: String?, remainingOps: [Op]) {
        if !ops.isEmpty {
            var remainingOps = ops
            let op = remainingOps.removeLast()
            //最后剩余的栈
            var lastOps = remainingOps
            //最后输出的描述
            var describe: String
            
            switch op {
            case .Operand(let value): //操作数直接返回值
                describe = "\(value)"
            case .Variable(let symbol): //变量直接返回名字
                describe = "\(symbol)"
            case .UnitaryOperation(let symbol, _): //一元运算符递归出一个操作数后用（）包裹并返回
                let operand = evalcuteDescribe(remainingOps, operation: "unitary")
                
                if let result = operand.describe {
                    describe = "\(symbol)(\(result))"
                    lastOps = operand.remainingOps
                } else { //如果没有获取到值使用？来代替
                    describe = "\(symbol)(?)"
                    lastOps = operand.remainingOps
                }
            case .BinaryOperation(let symbol, _): //二元运算符递归出两个操作数 并返回（1 + 2）的格式
                let operand1 = evalcuteDescribe(remainingOps, operation: symbol)
                //可否优化括号
                let isOptimizable = {
                    () -> Bool in
                    //第一次计算和一元运算符以及满足结合律的等式无需括号
                    if operation == "+" || operation == "-" || operation == "unitary" || operation == "first" {
                        return true
                    } else if (operation == "×" || operation == "÷") && (symbol == "×" || symbol == "÷") {
                        return true
                    } else {
                        return false
                    }
                }()
                
                if let result1 = operand1.describe {
                    let operand2 = evalcuteDescribe(operand1.remainingOps, operation: symbol)
                    
                    if let result2 = operand2.describe {
                        if isOptimizable {
                            describe = "\(result2) \(symbol) \(result1)"
                        } else {
                            describe = "(\(result2) \(symbol) \(result1))"
                        }
                        lastOps = operand2.remainingOps
                    } else { //如果没有获取到值使用？来代替
                        if isOptimizable {
                            describe = "? \(symbol) \(result1)"
                        } else {
                            describe = "(? \(symbol) \(result1))"
                        }
   
                        lastOps = operand2.remainingOps
                    }
                } else { //如果没有获取到值使用？来代替
                    if isOptimizable {
                        describe = "? \(symbol) ?"
                    } else {
                        describe = "(? \(symbol) ?)"
                    }

                    lastOps = operand1.remainingOps
                }
            }
            
            if lastOps.isEmpty { //如果栈已取尽返回结果
                return (describe, lastOps)
            } else if operation != nil && operation != "first" { //操作符不为空且不是第一次计算时直接返回结果
                return (describe, lastOps)
            } else { //否则继续递归，并用 ， 隔开之前的结果
                let (remainingDescrible, remainLastOps) = evalcuteDescribe(lastOps, operation: "first")
                
                return (remainingDescrible! + ", " + describe, remainLastOps)
            }
        }
        
        return (nil, ops)
    }
    
    //操作数入栈
    func pushOperand(operand: Double?) -> Double? {
        if operand != nil {
            opStack.append(Op.Operand(operand!))
            return evalcuate()
        }
        
        return nil
    }
    
    //变量入栈
    func pushOperand(symbol: String) -> Double? {
        opStack.append(Op.Variable(symbol))
        return evalcuate()
    }
    
    //变量赋值
    func pushVariableValue(operand: Double?) -> Double? {
        if operand != nil {
            variableValues["M"] = operand!
        }
        return evalcuate()
    }
    
    //运算符入栈
    func performOperation(op: String) -> Double? {
        if let opertion = knownsOps[op] {
            opStack.append(opertion)
        }

        return evalcuate()
    }
    
    //撤销
    func undo() -> Double? {
        if !opStack.isEmpty {
            opStack.removeLast()
        }

        return evalcuate()
    }
    
    //重置计算器 返回初始状态
    func reset() {
        if !opStack.isEmpty {
            opStack.removeAll()
        }
        if variableValues["M"] != nil {
            variableValues["M"] = nil
        }
    }
}

