calculator: extend the calculator logic with scientific operators
[nit.git] / examples / calculator / src / calculator_logic.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Business logic of a calculator
16 module calculator_logic
17
18 import serialization
19
20 # Hold the state of the calculator and its services
21 class CalculatorContext
22 auto_serializable
23
24 # Result of the last operation
25 var result: nullable Numeric = null
26
27 # Last operation pushed with `push_op`
28 var last_op: nullable Text = null
29
30 # Is `last_op` an unary operation or a '='?
31 var last_op_was_unary = false
32
33 # Value currently being entered
34 var current: nullable String = null
35
36 # Text to display on screen
37 fun display_text: String
38 do
39 var buf = new FlatBuffer
40
41 var last_op = last_op
42 var result = result
43 if result != null then
44 if last_op_was_unary then buf.append "{last_op or else "?"} "
45
46 buf.append result.to_s
47 buf.add ' '
48 end
49
50 if last_op != null and not last_op_was_unary then
51 buf.append last_op
52 buf.add ' '
53 end
54
55 var current = current
56 if current != null then
57 buf.append current.to_s
58 buf.add ' '
59 end
60
61 return buf.to_s
62 end
63
64 # Push operation `op`, will usually execute the last operation
65 fun push_op(op: Text)
66 do
67 # Constants
68 # TODO Protect constants to preserve full precision and to forbid appending extra digits
69 if op == "π" then
70 if last_op_was_unary then clear
71 current = pi.to_s
72 return
73 else if op == "e" then
74 if last_op_was_unary then clear
75 current = 2.718282.to_s
76 return
77
78 # Clear screen
79 else if op == "C" then
80 clear
81 return
82 end
83
84 # For all operators, apply pending operators
85 apply_last_op_if_any
86
87 var result = self.result or else 0
88
89 last_op = op
90 last_op_was_unary = true
91
92 # Unary operators
93 if op == "√" then
94 self.result = result.to_f.sqrt
95 else if op == "x²" then
96 self.result = result.to_f.pow(2.0)
97 else if op == "x!" then
98 self.result = result.to_i.factorial
99 else if op == "sin" then
100 self.result = result.to_f.sin
101 else if op == "cos" then
102 self.result = result.to_f.cos
103 else if op == "tan" then
104 self.result = result.to_f.tan
105
106 # =
107 else if op == "=" then
108 current = null
109
110 # Binary operators
111 else
112 self.result = result # Set as same or 0
113 last_op_was_unary = false
114 current = null
115 end
116 end
117
118 # Clear all state
119 private fun clear
120 do
121 result = null
122 last_op = null
123 current = null
124 end
125
126 # Push a digit
127 fun push_digit(digit: Int)
128 do
129 if last_op_was_unary then clear
130
131 var current = current
132 if current == null then current = ""
133 current += digit.to_s
134 self.current = current
135 end
136
137 # Switch entry mode from integer to decimal
138 fun switch_to_decimals
139 do
140 var current = current
141 if current == null then current = "0"
142 if not current.chars.has('.') then current += "."
143 self.current = current
144 end
145
146 # Execute the last operation it not null
147 protected fun apply_last_op_if_any
148 do
149 var op = last_op
150 var result = result
151 var current = current
152 self.current = null
153
154 if current == null then return
155
156 if op == null then
157 result = current.to_n
158 else if result != null then
159 if op == "+" then
160 result = result.add(current.to_n)
161 else if op == "-" then
162 result = result.sub(current.to_n)
163 else if op == "/" or op == "÷" then
164 result = result.div(current.to_n)
165 else if op == "*" or op == "×" then
166 result = result.mul(current.to_n)
167 else if op == "%" then
168 result = result.to_i % current.to_i
169 else if op == "xⁿ" then
170 result = result.to_f.pow(current.to_f)
171 else if op == "log" then
172 result = result.to_f.log_base(current.to_f)
173 end
174 end
175
176 self.result = result
177 end
178 end
179
180 redef universal Float
181 redef fun to_s do return to_precision(6)
182 end