aboutsummaryrefslogtreecommitdiff
path: root/keyboards/rubi/lib/calc.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/rubi/lib/calc.c')
-rw-r--r--keyboards/rubi/lib/calc.c261
1 files changed, 261 insertions, 0 deletions
diff --git a/keyboards/rubi/lib/calc.c b/keyboards/rubi/lib/calc.c
new file mode 100644
index 000000000..7796a9be4
--- /dev/null
+++ b/keyboards/rubi/lib/calc.c
@@ -0,0 +1,261 @@
1/*
2This is the modified version of [calculator by MWWorks](https://github.com/MWWorks/mw_calc_numpad/blob/master/calc.c). Below is the quote from [MWWorks](https://github.com/MWWorks).
3
4 Calculator for QMK-based keyboard by MWWorks, https://mwworks.uk
5 This is free, usual disclaimers, don't use it to calculate megaton yields, surgery plans, etc
6
7 I did not plan to reinvent the wheel for this - I figured surely somebody somewhere has working calculator code?
8 Found lots but none that actually work like you expect a calculator to, hence DIYing it
9
10 As such, this is probably a bit janky, especially as I am a bit of a hack at C
11 Seems to be working well, with occasional glitchs, solved by clearing it
12 And some occasional floating-point issues - eg get a long decimal rather than the whole number you were expecting
13 Feel free to fix it! I think it needs to detect the precision of the two operands and then figure out what the precision of the result should be
14
15*/
16#include "rubi.h"
17
18static uint8_t calc_current_operand = 0;
19static char calc_operand_0[CALC_DIGITS+1] = "";
20static char calc_operand_1[CALC_DIGITS+1] = "";
21char calc_result[CALC_DIGITS+1] = "";
22static char calc_status[CALC_DIGITS+1] = "";
23static char calc_operator = ' ';
24static bool calc_reset = false;
25
26
27void calcBegin(void){
28}
29
30//update display
31void calcUpdate(void){
32 if (calc_display_lines == 2) {
33 if((calc_current_operand == 1) || (calc_reset)){
34 strcpy(calc_status, calc_operand_0);
35 if((strlen(calc_operand_0)>0) || (strlen(calc_operand_1)>0)){
36 uint8_t len = strlen(calc_status);
37 if (!(calc_operator == 's' || calc_operator == 'r' || calc_operator == 'n')) {
38 calc_status[len] = calc_operator;
39 }
40 calc_status[len+1] = 0;
41 if(calc_reset
42 && !(calc_operator == 's' || calc_operator == 'r' || calc_operator == 'n')){
43 strncat(calc_status, calc_operand_1, CALC_DIGITS-strlen(calc_status));
44 calc_operator = ' ';
45 }
46 }
47 strcpy(calc_status_display, calc_status);
48 }
49 } else if (calc_display_lines == 1) {
50 if(calc_reset
51 && !(calc_operator == 's' || calc_operator == 'r' || calc_operator == 'n')){
52 calc_operator = ' ';
53 }
54 }
55 calc_operator_display = calc_operator;
56 strcpy(calc_result_display, calc_result);
57}
58
59//perform calculation on the 2 operands
60void calcOperands(void){
61 float result = 0;
62 switch (calc_operator){
63
64 //standard operators
65 case '+':
66 result = strtod(calc_operand_0, NULL) + strtod(calc_operand_1, NULL);
67 break;
68
69 case '-':
70 result = strtod(calc_operand_0, NULL) - strtod(calc_operand_1, NULL);
71 break;
72
73 case '/':
74 result = strtod(calc_operand_0, NULL) / strtod(calc_operand_1, NULL);
75 break;
76
77 case '*':
78 result = strtod(calc_operand_0, NULL) * strtod(calc_operand_1, NULL);
79 break;
80
81 //single operand operators - these are all in 2
82 case 's':
83 result = sqrt(strtod(calc_operand_0, NULL));
84 break;
85
86 case 'r':
87 result = 1/(strtod(calc_operand_0, NULL));
88 break;
89
90 }
91
92 //now convert the float result into a string
93 //we know the total string size but we need to find the size of the integer component to know how much we have for decimals
94 uint8_t magnitude = ceil(log10(result));
95 uint8_t max_decimals = CALC_DIGITS-magnitude-1;
96 //but max it at 7 because that seems the useful limit of our floats
97 if(max_decimals>7){
98 max_decimals = 7;
99 }
100 dtostrf(result, CALC_DIGITS, max_decimals, calc_result);
101
102 //now to clean up the result - we need it clean as it may be the input of next calculation
103 //this seems a lot of code to format this string :| note that this c doesn't support float in sprintf
104 uint8_t i;
105
106 //first find if theres a dot
107 uint8_t dotpos = CALC_DIGITS+1;
108 for(i=0; i<strlen(calc_result); i++){
109 if(calc_result[i] == '.'){
110 dotpos = i;
111 break;
112 }
113 }
114
115 //if there is, work back to it and remove trailing 0 or .
116 if(dotpos>=0){
117 for(i=strlen(calc_result)-1; i>=dotpos; i--){
118 if((calc_result[i] == '0') || (calc_result[i] == '.')){
119 calc_result[i] = 0;
120 }else{
121 break;
122 }
123 }
124 }
125
126 //now find how many leading spaces
127 uint8_t spaces = 0;
128 for(i=0; i<strlen(calc_result); i++){
129 if(calc_result[i] == ' '){
130 spaces++;
131 }else{
132 break;
133 }
134 }
135
136 //and shift the string
137 for(i=0; i<strlen(calc_result)-spaces; i++){
138 calc_result[i] = calc_result[i+spaces];
139 }
140 calc_result[strlen(calc_result)-spaces] = 0;
141
142 calcUpdate();
143 //the result is available as the first operand for another calculation
144 strcpy(calc_operand_0, calc_result);
145 calc_operand_1[0] = 0;
146
147}
148
149void calcInput(char input){
150 char *operand = calc_operand_0;
151 if(calc_current_operand == 1){
152 operand = calc_operand_1;
153 }
154 uint8_t len = strlen(operand);
155
156 if(
157 ((input >= 48) && (input <= 57)) ||
158 (input == '.')
159 ){
160 //if this is following an equals, then we start from scratch as if new calculation
161 if(calc_reset == true){
162 calc_reset = false;
163 calc_current_operand = 0;
164 calc_operand_0[0] = 0;
165 calc_operand_1[0] = 0;
166 operand = calc_operand_0;
167 len = 0;
168 }
169
170 if(len<CALC_DIGITS){
171 operand[len] = input;
172 operand[len+1] = 0;
173 strcpy(calc_result, operand);
174 calcUpdate();
175 }
176
177 //special input to backspace
178 }else if(input == 'x'){
179 operand[len-1] = 0;
180 strcpy(calc_result, operand);
181 calcUpdate();
182
183 //clear
184 }else if(input == 'c'){
185 operand[0] = 0;
186 calc_operand_0[0] = 0;
187 calc_operand_1[0] = 0;
188 calc_operator = ' ';
189 calc_reset = true;
190 strcpy(calc_result, operand);
191 calcUpdate();
192
193 //special input switch neg/pos
194 }else if((input == 'n') && (len>0)){
195 uint8_t i;
196
197 if(operand[0] == '-'){
198 for(i=1; i<=len; i++){
199 operand[i-1] = operand[i];
200 }
201 }else if(len<CALC_DIGITS){
202 for(i=0; i<=len; i++){
203 operand[len-i+1] = operand[len-i];
204 }
205 operand[0] = '-';
206 }
207 calc_operator = input;
208 strcpy(calc_result, operand);
209 calcUpdate();
210
211
212 //standard 2 operand operators
213 }else if((input == '+') || (input == '-') || (input == '*') || (input == '/')){
214
215 //get ready for second operand
216 if(calc_current_operand == 0){
217 calc_operator = input;
218 calc_current_operand = 1;
219 calcUpdate();
220
221 //we pressed = we now expect a new second operand
222 }else if(calc_reset){
223 calc_operator = input;
224 calc_reset = false;
225 calc_operand_1[0] = 0;
226 calcUpdate();
227
228 }else {
229 //if we use this on the second operand, calculate first, then ready for a second operand again
230 if (strlen(calc_operand_1)>0){
231 calcOperands();
232 }
233 calc_operand_1[0] = 0;
234 calc_operator = input;
235 calcUpdate();
236 }
237
238
239 }else if(input == '='){
240 //only accept = if we are on the second operand
241 if(calc_current_operand == 1){
242 //keep the second operand for a subsequent press of =; but flag to reset if start entry of new operand
243 calc_reset = true;
244 calcOperands();
245 }
246
247 //single operands - square root and reciprocal - needs to operate on 0 so it works after a previous = result
248 }else if((input == 's') || (input == 'r')){
249 //but maybe we started entering 1
250 if(calc_current_operand == 1 && !calc_reset){
251 strcpy(calc_operand_0, calc_operand_1);
252 }
253 calc_current_operand = 1;
254 calc_operand_1[0] = 0;
255 calc_operator = input;
256 calc_reset = true; //simulate another =
257 calcOperands();
258
259 }
260
261}