diff options
Diffstat (limited to 'keyboards/rubi/lib/calc.c')
| -rw-r--r-- | keyboards/rubi/lib/calc.c | 261 |
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 | /* | ||
| 2 | This 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 | |||
| 18 | static uint8_t calc_current_operand = 0; | ||
| 19 | static char calc_operand_0[CALC_DIGITS+1] = ""; | ||
| 20 | static char calc_operand_1[CALC_DIGITS+1] = ""; | ||
| 21 | char calc_result[CALC_DIGITS+1] = ""; | ||
| 22 | static char calc_status[CALC_DIGITS+1] = ""; | ||
| 23 | static char calc_operator = ' '; | ||
| 24 | static bool calc_reset = false; | ||
| 25 | |||
| 26 | |||
| 27 | void calcBegin(void){ | ||
| 28 | } | ||
| 29 | |||
| 30 | //update display | ||
| 31 | void 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 | ||
| 60 | void 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 | |||
| 149 | void 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 | } | ||
