'AVCS Voice Calculator & Conversions (v1.1) -- Calculate expressions with up to 2 operations and 3 values "Any way you say it" 'by SemlerPDX Mar2020/Mar2021 'VETERANS-GAMING.COM 'demonstration video on youtube: https://youtu.be/bCjuYh92M-I 'When I say: *[divided by;divide by;divided that;divide that;divided;divide;multiply;times;multiplied by;multiply that;multiplied that;times that;add;add that;added that;plus;subtract;subtracted;minus;% of;to the power of;percent of;swear;square;square root of;square of;squared;cubed;cube;cuban;° c;° f;kelvin;celcius;celsius;centigrade;fahrenheit;kelvin;calvin;cm;centimeters;centimetres;inches;foot;feet;meters;metres;yards;yours;miles;km;kilometers;kilometres;feet per second;mph;miles per hour;kph;kilometers per hour;kilometres per hour;knots;mach;mock;mark]*;*[-1;-2;-3;-4;-5;-6;-7;-8;-9]* 'Set text [~avcs_cmd] to '{TXTWORDTONUM:"{CMD}"}' (Trim) (Lower Case) ' (and the next action is to run this inline, after that, just says the result with TTS) Imports Microsoft.VisualBasic Imports System Imports System.Linq Imports System.Math Imports System.Globalization Public Class VAInline dim decimalSeparator as string = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator dim decimalPlaces as Int32 = 3 'Default = 3 | rounds results -- change via voice command 'Set Decimal Places [to;] [0..16]' 'Operational Keyword Arrays dim OperatorsArray() as string = { "divided", "divide", "times", "multiplied", "multiply", "add", "plus", "subtract", "subtracted", "minus", "-", "%", "percent", "power", "square", "squared", "squareroot", "cubed", "cubes", "cube", "cuban", "cuba" } dim ConvertorsArray() as string = { "c", "f", "k", "cm", "celsius", "celcius", "centigrade", "fahrenheit", "kelvin", "calvin", "inches", "centimeters", "centimetres", "foot", "feet", "m", "meters", "metres", "yards", "yours", "miles", "km", "kilometers", "kilometres", "fps", "feetpersecond", "mps", "meterspersecond", "metrespersecond", "mph", "milesperhour", "kph", "kilometersperhour", "kilometresperhour", "knots", "mach", "mark", "mock" } dim OpsArray() as string = { "sum", "sub", "prd", "dif", "exp", "per", "sqr" } dim DoubleOpsArray() as string = { "sum", "sub", "prd", "dif" } dim SymbolsArray() as string = { "°", "%", "^", "-", ":", "$", "£", "€", "¢", "¥", "ƒ" } dim PiArray() as string = { "pi", "pie", "tie", "high", "bye", "eye", "espy", "pine", "pied", "ply", "tie" } 'Speech Parsing Vars dim cmd as string = "" dim charArray() as char dim cmdSegments() as string 'Values Decimal Vars dim operand1 as decimal? dim operand2 as decimal? dim operand3 as decimal? dim operandLast as decimal? dim result as decimal? dim resultLast as decimal? 'Operations String Vars dim operator1 as string dim operator2 as string dim operatorText1 as string = "" dim operatorText2 as string = "" dim equationText as string = "" 'Calc Systems Vars dim operationsOrder as integer = 0 dim countHistory as integer = 1 dim countFaults as integer = 0 dim randomText as string dim subReverse as boolean dim calcActive as boolean 'Function to remove spaces from keywords, handle homophones & edge case words Private Function ReplaceEdgeCaseWords(ByRef cmd as string) if (cmd.StartsWith("ad ")) cmd = cmd.Replace("ad ","add ") end if cmd = cmd.Replace( " too "," to " ).Replace( "swear ","square " ).Replace( "square root","squareroot" ).Replace( "to the power","power" ).Replace( "to value"," value" ).Replace( "2 value"," value" ).Replace( "value ","value" ).Replace( "to that"," that" ).Replace( "2 that"," that" ).Replace( " tell them"," kelvin" ).Replace( " per ","per" ).Replace( " ad "," add " ).Replace( "added to","add " ).Replace( "added 2","add " ).Replace( "added ","add " ).Replace( "add that 2","add that " ).Replace( "add that to","add that " ).Replace( "÷"," divided by " ).Replace( "¼","0.25" ).Replace( "½","0.5" ).Replace( "¾","0.75" ) end function 'Function to fix large number separators by culture Private Function FixLargeNumberSeparator(ByRef cmd as string) 'ex. (7,000.32 into 7000.32) [or] (7.000,32 into 7000,32) if ((cmd.Contains(".")) or (cmd.Contains(","))) if (decimalSeparator <> ".") if (cmd.Contains(".")) cmd = cmd.Replace(".","") end if elseif (decimalSeparator <> ",") if (cmd.Contains(",")) cmd = cmd.Replace(",","") end if end if end if end function 'Function to transform large number words and concat with the number said just before Private Function FixLargeWordNum(ByRef largeType as string, ByRef lastNum as decimal?) select case largeType case "million" lastNum *= 1000000 case "billion" lastNum *= 1000000000 case "trillion" lastNum *= 1000000000000 case "quadrillion" lastNum *= 1000000000000000 case else largeType = "AVCS Calculation Error - unable to resolve '" + lastNum.ToString() + " " + largeType + "' as an opperand" end select end function 'Function to test for "by" as Pi in specific cases Private Function HasPi(ByVal operatorName as string) if (not((operatorName.Contains("root")) or (operatorName.StartsWith("square")) or (operatorName.StartsWith("cub")))) cmd = cmd.Replace(" "," ").Replace(" "," ").Replace("divided by","").Replace("divide by","").Replace("multiplied by","").Replace("multiply by","").Replace("that by","") if ((cmd.StartsWith("by ")) or (cmd.Contains(" by ")) or (cmd.EndsWith(" by"))) Return true end if end if end function 'Function to set keyword and readable text/symbol for Operators Private Function SetOperator(ByRef operatorName as string, ByRef operatorText as string) if ((operatorName.Contains("%")) or (operatorName.Contains("percent"))) operatorText = "% of" operatorName = "per" elseif (operatorName.StartsWith("divide")) operatorText = "/" operatorName = "dif" elseif ((operatorName.Contains("times")) or (operatorName.StartsWith("multipl"))) operatorText = "x" operatorName = "prd" elseif ((operatorName.Contains("plus")) or (operatorName.StartsWith("add"))) operatorText = "+" operatorName = "sum" elseif ((operatorName.StartsWith("subtract")) or (operatorName.Contains("minus")) or (operatorName.Contains("-"))) operatorText = "-" operatorName = "sub" elseif (operatorName.Contains("power")) operatorText = "ⁿ" 'to the power of operatorName = "exp" elseif (operatorName.Contains("squareroot")) operatorText = "√" 'the square root of operatorName = "sqr" elseif (operatorName.StartsWith("square")) operatorText = "²" 'squared operatorName = "exp" elseif (operatorName.StartsWith("cub")) operatorText = "³" 'cubed operatorName = "exp" end if end function 'Function to set keyword and readable text/symbol for Conversions Private Function SetConvertor(ByRef operatorName as string, ByRef operatorText as string) 'VELOCITY CONVERSIONS============== if ((operatorName.Contains("fps")) or (operatorName.Contains("feetpersecond"))) operatorText = "feet per second" operatorName = "fps2" elseif ((operatorName.Contains("mps")) or (operatorName.Contains("meterspersecond")) or (operatorName.Contains("metrespersecond"))) operatorText = "meters per second" operatorName = "mps2" elseif ((operatorName.Contains("mph")) or (operatorName.Contains("milesperhour"))) operatorText = "miles per hour" operatorName = "mph2" elseif ((operatorName.Contains("kph")) or (operatorName.Contains("kilometersperhour")) or (operatorName.Contains("kilometresperhour"))) operatorText = "kilometers per hour" operatorName = "kph2" elseif (operatorName.Contains("knots")) operatorText = "knots" operatorName = "kts2" elseif ((operatorName.Contains("mach")) or (operatorName.Contains("mock")) or (operatorName.Contains("mark"))) operatorText = "mach" operatorName = "mch2" 'DISTANCE CONVERSIONS============== elseif (operatorName.Contains("inches")) operatorText = "inches" operatorName = "in2" elseif ((operatorName.Contains("cm")) or (operatorName.Contains("centimeters")) or (operatorName.Contains("centimetres"))) operatorText = "centimeters" operatorName = "cm2" elseif ((operatorName.Contains("feet")) or (operatorName.Contains("foot"))) operatorText = "feet" operatorName = "ft2" elseif ((operatorName.Contains("yards")) or (operatorName.Contains("yours"))) operatorText = "yards" operatorName = "yd2" elseif ((operatorName.Contains("km")) or (operatorName.Contains("kilometers")) or (operatorName.Contains("kilometres"))) operatorText = "kilometers" operatorName = "km2" elseif ((operatorName.Contains("mi")) or (operatorName.Contains("miles"))) operatorText = "miles" operatorName = "mi2" elseif ((operatorName.Contains("meters")) or (operatorName.Contains("metres"))) operatorText = "meters" operatorName = "mt2" 'TEMPERATURE CONVERSIONS============== elseif (operatorName.Contains("fahrenheit")) operatorText = "degrees fahrenheit" operatorName = "f2" elseif ((operatorName.Contains("celcius")) or (operatorName.Contains("celsius"))) operatorText = "degrees celcius" operatorName = "c2" elseif (operatorName.Contains("centigrade")) operatorText = "degrees centigrade" operatorName = "c2" elseif ((operatorName.Contains("kelvin")) or (operatorName.Contains("calvin"))) operatorText = "kelvin" operatorName = "k2" end if end function 'Main Maths Function Private Function GetResult(ByVal ops as string, ByVal val1 as decimal?, Optional ByVal val2 as decimal? = 0) select case ops case "sum" val1 += val2 case "sub" val1 -= val2 case "prd" val1 *= val2 case "dif" val1 /= val2 case "per" val1 *= (val2 / 100) case "exp" val1 = Pow(val1, val2) case "sqr" val1 = Sqrt(val1) case else val1 = nothing end select Return val1 end function 'Main Conversions Function Private Function GetConversion(ByVal ops as string, ByVal val as decimal?) select case ops '==DISTANCE=========== 'inches case "in2cm" val *= 0.393701 case "in2ft" val *= 0.0833333 case "in2mt" val *= 0.0254 case "in2yd" val *= 0.0277778 case "in2km" val /= 39370 case "in2mi" val /= 63360 'cm case "cm2in" val *= 0.393701 case "cm2ft" val *= 0.0328084 case "cm2mt" val /= 100 case "cm2yd" val *= 0.0109361 case "cm2km" val /= 100000 case "cm2mi" val /= 160934 'ft case "ft2in" val *= 12 case "ft2cm" val *= 30.48 case "ft2mt" val *= 0.3048 case "ft2yd" val *= 0.333333 case "ft2km" val /= 3280.84 case "ft2mi" val /= 5280 'mt case "mt2in" val *= 39.3701 case "mt2cm" val *= 100 case "mt2ft" val *= 3.28084 case "mt2yd" val *= 1.09361 case "mt2km" val /= 1000 case "mt2mi" val /= 1609 'yd case "yd2in" val *= 36 case "yd2cm" val *= 91.44 case "yd2ft" val *= 3 case "yd2mt" val *= 0.9144 case "yd2km" val /= 1093.61 case "yd2mi" val /= 1760 'km case "km2in" val *= 39370.1 case "km2cm" val *= 100000 case "km2ft" val *= 3280.84 case "km2mt" val *= 1000 case "km2yd" val *= 1093.61 case "km2mi" val *= 0.621371 'miles case "mi2in" val *= 63360 case "mi2cm" val *= 160934.4 case "mi2ft" val *= 5280 case "mi2mt" val *= 1609.34 case "mi2yd" val *= 1760 case "mi2km" val *= 1.60934 '==VELOCITY=========== 'fps case "fps2mps" val *= 0.3048 case "fps2kph" val *= 1.09728 case "fps2mph" val *= 0.681818 case "fps2kts" val *= 0.592484 case "fps2mch" val *= 0.00088863 'mps case "mps2fps" val *= 3.28084 case "mps2kph" val *= 3.6 case "mps2mph" val *= 2.23694 case "mps2kts" val *= 1.94384 case "mps2mch" val *= 0.00291545 'kph case "kph2fps" val *= 0.911344 case "kph2mps" val *= 0.277778 case "kph2mph" val *= 0.6214 case "kph2kts" val *= 1.852 case "kph2mch" val *= 1234.8 'mph case "mph2fps" val *= 1.46667 case "mph2mps" val *= 0.44704 case "mph2kph" val *= 1.60934 case "mph2kts" val *= 0.868976 case "mph2mch" val *= 0.00130332 'knots case "kts2fps" val *= 1.68781 case "kts2mps" val *= 0.514444 case "kts2mph" val *= 1.15078 case "kts2kph" val *= 1.852 case "kts2mch" val *= 0.0015 'mach case "mch2fps" val *= 1125.33 case "mch2mps" val *= 343 case "mch2mph" val *= 767.269 case "mch2kph" val *= 1234.8 case "mch2kts" val *= 666.73866 '==TEMPERATURE=========== 'f case "f2c" val = ((val - 32) * 5) / 9 case "f2k" val = ((val + 459.67) * 5) / 9 'c case "c2f" val = ((val * 9) / 5) + 32 case "c2k" val = ((val * 9) / 5) + 32 val = ((val + 459.67) * 5) / 9 'kelvin case "k2f" val = ((val * 9) / 5) - 459.67 case "k2c" val = ((val * 9) / 5) - 459.67 val = ((val - 32) * 5) / 9 case else val = nothing end select Return val end function Public Sub Main() VA.SetText("avcs_calc_return", nothing) 'Debugging and Testing Check (bypassed if called via spoken command) dim cmdTest as string = vaProxy.Utility.ParseTokens("{CMD}") if (cmdTest = "") VA.SetText("~avcs_cmd", "by the way what is 3 plus 3 times by") VA.SetInt("AVCS_CALC_ResultsDecimalPlaces", 2) else 'Check Calc Mode state, exit if off if ((VA.GetBoolean("AVCS_CALC_MODE_ON")) isNot nothing) calcActive = VA.GetBoolean("AVCS_CALC_MODE_ON") end if 'Provide Message for wildcard recognition when Calculator Mode OFF (infrequent prompt in case user does not know) if (not(calcActive)) if ((VA.GetInt("AVCS_CALC_FAULT")) isNot nothing) countFaults = VA.GetInt("AVCS_CALC_FAULT") + 1 if (countFaults >= 3) if (countFaults >= 15) VA.WriteToLog("Calculation recognized, but won't be computed (this is common when calculator is OFF)", "purple") VA.SetInt("AVCS_CALC_FAULT", nothing) countFaults = 0 end if else randomText = "Calculator is OFF. To enable, say, '" + vaProxy.Utility.ParseTokens("{TXTRANDOM:Enable;Begin;Turn On;Start}") + " Voice Calculator'" VA.WriteToLog(randomText, "purple") end if VA.SetInt("AVCS_CALC_FAULT", countFaults) else VA.SetInt("AVCS_CALC_FAULT", 1) end if 'Exit Calc Function when Mode OFF Exit Sub end if end if 'Get Custom Decimal Place Settings (if exists) if ((VA.GetInt("AVCS_CALC_ResultsDecimalPlaces")) isNot nothing) decimalPlaces = Convert.ToInt32(VA.GetInt("AVCS_CALC_ResultsDecimalPlaces")) end if 'Parse Spoken Command for Equation(s) if ((VA.GetText("~avcs_cmd")) isNot nothing) cmd = VA.GetText("~avcs_cmd") 'Process spoken command formatting and keywords ReplaceEdgeCaseWords(cmd) FixLargeNumberSeparator(cmd) if (cmd.StartsWith("fore to")) cmd = cmd.Substring(7, cmd.Length - 7) elseif (cmd.StartsWith("for to")) cmd = cmd.Substring(6, cmd.Length - 6) elseif (cmd.StartsWith("or to")) cmd = cmd.Substring(5, cmd.Length - 5) elseif (cmd.StartsWith("1 is")) cmd = cmd.Substring(4, cmd.Length - 4) end if 'Evaluate spoken command split at each character and space out symbols charArray = cmd.ToCharArray cmd = "" for each character as string in charArray if (SymbolsArray.Contains(character)) cmd += " " + character.ToString() + " " else cmd += character.ToString() end if next 'Evaluate all segments of speech split at {SPACE} and parse for operands and operators cmdSegments = cmd.Split(New String() {" "},StringSplitOptions.None) for each segment as string in cmdSegments if (segment <> "") 'Handle numbers recognized as homophone words select case segment case "too" segment = "2" case "to" segment = "2" case "trees" segment = "3" case "tree" segment = "3" case "fore" segment = "4" case "for" segment = "4" case "ate" segment = "8" case "ape" segment = "8" case "valuetoo" segment = "value2" case "valueto" segment = "value2" case "valuetrees" segment = "value3" case "valuetree" segment = "value3" case "valuefore" segment = "value4" case "valuefor" segment = "value4" case "valueate" segment = "value8" end select 'Handle short form of convertors select case segment case "c" segment = "celcius" case "f" segment = "fahrenheit" case "k" segment = "kelvin" case "m" segment = "meters" end select 'Handle "subtract A from B" order of operations error if (segment.Contains("from")) if ((operand2 is nothing) and(operand1 isNot nothing) and ((operator1 isNot nothing) and (operator1.Contains("subtract")))) subReverse = true end if end if 'Handle "that" reference to last calculation result if (segment.Contains("that")) if ((VA.GetDecimal("AVCS_CALC_LastResult")) isNot nothing) segment = VA.GetDecimal("AVCS_CALC_LastResult").ToString() end if end if 'Handle "value" reference to previous calculation result if ((segment.StartsWith("value")) and (IsNumeric(segment.Substring(segment.Length - 1, 1)))) if ((VA.GetDecimal(segment)) isNot nothing) segment = VA.GetDecimal(segment).ToString() else VA.WriteToLog(segment + " does not exist in memory", "red") Exit Sub end if end if 'Handle negative temperatures if ((operator2 isNot nothing) and (operand1 isNot nothing)) if ((operationsOrder = 2) and (ConvertorsArray.Contains(operator2))) operator1 = operator2 operator2 = nothing operand1 -= (operand1 * 2) operationsOrder = 5 end if end if 'Handle "pi" and homophone variants if (PiArray.Contains(segment)) if (operand1 is nothing) operand1 = Pi elseif (operand2 is nothing) operand2 = Pi elseif (operand3 is nothing) operand3 = Pi end if 'Set Operands ------- elseif ((IsNumeric(segment)) and (operand1 is nothing)) try operand1 = Convert.ToDecimal(segment) operandLast = operand1 if (operationsOrder = 0) operationsOrder = 1 'for edge case: (b = Pi) end if catch VA.WriteToLog("AVCS Calculation Error at Operand 1", "red") end try elseif ((IsNumeric(segment)) and (operand2 is nothing)) try operand2 = Convert.ToDecimal(segment) operandLast = operand2 catch VA.WriteToLog("AVCS Calculation Error at Operand 2", "red") end try elseif ((IsNumeric(segment)) and (operand3 is nothing)) try operand3 = Convert.ToDecimal(segment) operandLast = operand3 catch VA.WriteToLog("AVCS Calculation Error at Operand 3", "red") end try 'Set Operators ------- elseif ((OperatorsArray.Contains(segment)) and (operator1 is nothing)) operator1 = segment if (operationsOrder = 0) operationsOrder = 2 'for edge case: (a = Pi) end if elseif ((OperatorsArray.Contains(segment)) and (operator2 is nothing)) operator2 = segment if ((operationsOrder = 1) and (operand2 isNot nothing)) operationsOrder = 3 'for edge case: (c = b and b = Pi) elseif ((operationsOrder = 1) and (operand2 is nothing)) operationsOrder = 4 'for edge case: (c = Pi) end if 'Set Convertors ------- elseif ((ConvertorsArray.Contains(segment)) and (operator1 is nothing)) operator1 = segment operationsOrder = 5 'for edge case null: (no way Pi) elseif ((ConvertorsArray.Contains(segment)) and (operator2 is nothing)) operator2 = segment 'Handle "..illions" ------- elseif (segment.EndsWith("illion")) if ((operand1 isNot nothing) and (operandLast = operand1)) FixLargeWordNum(segment,operand1) elseif ((operand2 isNot nothing) and (operandLast = operand2)) FixLargeWordNum(segment,operand2) elseif ((operand3 isNot nothing) and (operandLast = operand3)) FixLargeWordNum(segment,operand3) end if if (segment.StartsWith("AVCS Calculation Error")) VA.WriteToLog(segment, "red") Exit Sub end if end if end if next 'Evaluate for Pi recognized as "by" (edge cases) if (operationsOrder < 5) '-- 1 operation catch if ((operator2 is nothing) and (operand2 is nothing)) if (HasPi(operator1)) if (operationsOrder = 1) operand2 = Pi elseif (operationsOrder = 2) operand2 = operand1 operand1 = Pi end if end if '-- 2 operations catch elseif ((operator2 isNot nothing) and (operand3 is nothing)) if ((HasPi(operator1)) or (HasPi(operator2))) if (operationsOrder = 4) operand3 = operand2 operand2 = Pi elseif (operationsOrder = 3) operand3 = Pi elseif (operationsOrder = 2) operand3 = operand2 operand2 = operand1 operand1 = Pi end if end if end if end if 'Evaluate Operator 1 ====== if (operator1 isNot nothing) 'Handle null operands on Squared/Cubed operators if ((operator1.StartsWith("square")) and (not(operator1.Contains("root")))) operand2 = 2 elseif (operator1.StartsWith("cub")) operand2 = 3 'Handle Reverse order of operations on Subtract elseif ((operator1.Contains("subtract")) and (subReverse)) dim operand4 as decimal? = operand2 operand2 = operand1 operand1 = operand4 end if 'Set Operator 1 keyword and get text form of Operator/Convertor if (not(OperatorsArray.Contains(operator1))) SetConvertor(operator1,operatorText1) else SetOperator(operator1,operatorText1) end if else VA.WriteToLog("AVCS Calculation Error on Null Operator 1", "red") Exit Sub end if 'Evaluate Operator 2 ====== if (operator2 isNot nothing) 'Handle null operands on Squared/Cubed operators if ((operator2.StartsWith("square")) and (not(operator2.Contains("root")))) operand3 = 2 elseif (operator2.Contains("cub")) operand3 = 3 end if 'Set Operator 2 keyword and get text form of Operator/Convertor if (not(OpsArray.Contains(operator1))) SetConvertor(operator2,operatorText2) operator1 = operator1 + operator2.Substring(0, operator2.Length - 1) operator2 = nothing else SetOperator(operator2,operatorText2) 'Calculate Square Root on Multi-Part Equation to avoid errors on null operand3 'ex. 200 subtract square root of 128 = 188.69 = a-(Sqrt(b)) if ((operator2 = "sqr") and (operand3 is nothing)) equationText = operand1.ToString() + " " + operatorText1 + " " + operatorText2 + " " + operand2.ToString() + " " operand2 = Sqrt(operand2) operator2 = nothing 'Calculate Percentage before to avoid order of operations and text display errors 'ex. 200 subtract 20 % = 160 = a-(b*(a/100)) elseif ((operator2 = "per") and (operand3 is nothing)) equationText = operand1.ToString() + " " + operatorText1 + " " + operand2.ToString() + " " + operatorText2 + " " + operand1.ToString() + " " operand3 = operand1 operand2 = operand2*(operand3 / 100) operand3 = nothing operator2 = nothing 'ex. 200 subtract 20 % of 100 = 180 = a-(b*(c/100) elseif ((operator2 = "per") and (operand3 isNot nothing)) equationText = operand1.ToString() + " " + operatorText1 + " " + operand2.ToString() + " " + operatorText2 + " " + operand3.ToString() + " " operand2 = operand2*(operand3 / 100) operand3 = nothing operator2 = nothing 'Calculate Exponents before to avoid order of operations and text display errors 'ex. 4 plus 7 to the power of 3 = 347 = a+(b^c) elseif (((operator2 = "exp") and (operatorText2.StartsWith("to"))) and (operand3 isNot nothing)) equationText = operand1.ToString() + " " + operatorText1 + " " + operand2.ToString() + " " + operatorText2 + " " + operand3.ToString() + " " operand2 = Pow(operand2, operand3) operand3 = nothing operator2 = nothing 'ex. 4 plus 7 cubed = 347 = a+b³ [or] 4 plus 7 squared = 53 = a+b² elseif (((operator2 = "exp") and (not(operatorText2.StartsWith("to")))) and (operand3 isNot nothing)) equationText = operand1.ToString() + " " + operatorText1 + " " + operand2.ToString() + " " + operatorText2 + " " operand2 = Pow(operand2, operand3) operand3 = nothing operator2 = nothing end if end if end if 'Final Evaluation for "8" recognized as "a" if ((DoubleOpsArray.Contains(operator1)) and (operator2 is nothing) and (operand1 isNot nothing) and (operand2 is nothing)) if ((cmd.Contains(" a ")) or (cmd.EndsWith(" a"))) if (operationsOrder = 1) operand2 = 8 else operand2 = operand1 operand1 = 8 end if end if end if '==CALCULATE RESULT== if (OpsArray.Contains(operator1)) 'Build Equation Text for display in VA Event log if (equationText = "") if (operand1 isNot nothing) equationText += operand1.ToString() + " " end if if (operator1 isNot nothing) equationText += operatorText1 + " " end if if (operand2 isNot nothing) equationText += operand2.ToString() + " " end if if (operator2 isNot nothing) equationText += operatorText2 + " " end if if (operand3 isNot nothing) equationText += operand3.ToString() + " " end if 'Calculate Square Root on Multi-Part Equation to avoid incorrect operations math and display text 'ex. square root of 144 times 7 = 84 = b*(Sqrt(a)) if ((operator1 = "sqr") and ((operator2 isNot nothing) and (operand2 isNot nothing))) equationText = operatorText1 + " " + operand1.ToString() + " " + operatorText2 + " " + operand2.ToString() + " " end if 'Remove any whitespace between % and number(s) if (equationText.Contains(" %")) equationText = equationText.Replace(" %","%") end if end if 'MATHS BEGIN-------------------------------- 'If there are 2 operations if (operator2 isNot nothing) 'If there are 2 operations with 3 values if (operand3 isNot nothing) if ((operand1 isNot nothing) and (operand2 isNot nothing)) result = GetResult(operator1,operand1,operand2) end if if (result isNot nothing) result = GetResult(operator2,result,operand3) end if 'Else if there are 2 operations with 2 values elseif (operand2 isNot nothing) 'Handle Square Root on Second Operand if ((operator1 = "sqr") and (operand1 isNot nothing)) result = Sqrt(operand1) else result = nothing end if if ((operator1 = "sqr") and (result isNot nothing)) result = GetResult(operator2,result,operand2) else if ((result isNot nothing) and (operand1 isNot nothing)) result = GetResult(operator1,operand1,result) elseif (operand1 isNot nothing) result = GetResult(operator2,operand1,operand2) end if end if end if 'Else if there is 1 operation with up to 2 values elseif (operator1 isNot nothing) if ((operand1 isNot nothing) and (operand2 isNot nothing)) result = GetResult(operator1,operand1,operand2) if ((operatorText1 = "cubed") or (operatorText1 = "squared")) equationText = equationText.Replace(" " + operand2.ToString() + " "," ") end if elseif ((operand1 isNot nothing) and (not(DoubleOpsArray.Contains(operator1)))) result = GetResult(operator1,operand1) equationText = equationText.Replace(operand1.ToString() + " ","") + operand1.ToString() + " " end if end if else 'Else this is a Unit Conversion with 1 value if (operator1 isNot nothing) if (operand1 isNot nothing) result = GetConversion(operator1,operand1) equationText = operand1.ToString() + " " + operatorText1 + " = " end if else VA.WriteToLog("AVCS Calculation Error on Null Operator 1", "red") end if end if 'Evaluate Result ====== if (result isNot nothing) try 'Try to compare decimal with integer version of itself to discover decimal places, then round dim checkResult as Int32 = Convert.ToInt32(result) if (result = checkResult) 'For TTS brevity use the one that couldn't end with .000 result = checkResult elseif (checkResult <> result) dim roundResult as Double = result result = Round(roundResult, decimalPlaces) end if catch 'Result is a whole number already, does not need rounding for TTS brevity end try 'Correct Pi in equation text string to 2 decimal places for TTS brevity if (equationText.Contains(Pi.ToString())) equationText = equationText.Replace(Pi.ToString(), "3.14") end if 'Get/Increment/Set Current Operations History Count if ((VA.GetInt("AVCS_CALC_CountOperations")) isNot nothing) countHistory = VA.GetInt("AVCS_CALC_CountOperations") countHistory += 1 if (countHistory > 99) countHistory = 1 end if end if VA.SetInt("AVCS_CALC_CountOperations", countHistory) 'Save to Calc History & Display Last complete equation with result in Event Log if (OpsArray.Contains(operator1)) equationText = equationText.Replace(" ²","²").Replace(" ³","³").Replace(" ⁿ ","ⁿ").Replace("√ ","√") VA.SetText("AVCS_CALC_LastOperation", equationText + "= " + result.ToString()) VA.SetText("AVCS_CALC_LastOperation_" + countHistory.ToString(), equationText + "= " + result.ToString()) VA.WriteToLog("Value " + countHistory.ToString() + " = " + equationText + "= " + result.ToString(), "green") else VA.SetText("AVCS_CALC_LastOperation", equationText + result.ToString() + " " + operatorText2) VA.SetText("AVCS_CALC_LastOperation_" + countHistory.ToString(), equationText + result.ToString() + " " + operatorText2) VA.WriteToLog("Value " + countHistory.ToString() + " = " + equationText + result.ToString() + " " + operatorText2, "green") end if 'Save to Memory, Set Last for "that" reference, and Return Result for TTS VA.SetDecimal("value" + countHistory.ToString(), result) VA.SetDecimal("AVCS_CALC_LastResult", result) VA.SetText("avcs_calc_return", result.ToString()) end if end if End Sub End Class