formula.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package utils
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. "unicode"
  7. )
  8. // EvaluateFormula calculates the result of a mathematical formula with variable 'x'
  9. // Supported operators: +, -, *, /
  10. // Supported grouping: ( )
  11. func EvaluateFormula(formula string, x float64) (float64, error) {
  12. if strings.TrimSpace(formula) == "" {
  13. return x, nil
  14. }
  15. postfix, err := infixToPostfix(formula)
  16. if err != nil {
  17. return 0, err
  18. }
  19. return evaluatePostfix(postfix, x)
  20. }
  21. func infixToPostfix(formula string) ([]string, error) {
  22. var output []string
  23. var stack []string // operator stack
  24. i := 0
  25. for i < len(formula) {
  26. char := formula[i]
  27. if unicode.IsSpace(rune(char)) {
  28. i++
  29. continue
  30. }
  31. // Number or Variable 'x'
  32. if unicode.IsDigit(rune(char)) || char == '.' || char == 'x' || char == 'X' {
  33. start := i
  34. if char == 'x' || char == 'X' {
  35. output = append(output, "x")
  36. i++
  37. } else {
  38. for i < len(formula) && (unicode.IsDigit(rune(formula[i])) || formula[i] == '.') {
  39. i++
  40. }
  41. output = append(output, formula[start:i])
  42. }
  43. continue
  44. }
  45. // Operators
  46. if isOperator(char) {
  47. for len(stack) > 0 && isOperator(stack[len(stack)-1][0]) && precedence(stack[len(stack)-1][0]) >= precedence(char) {
  48. output = append(output, stack[len(stack)-1])
  49. stack = stack[:len(stack)-1]
  50. }
  51. stack = append(stack, string(char))
  52. i++
  53. continue
  54. }
  55. // Parentheses
  56. if char == '(' {
  57. stack = append(stack, "(")
  58. i++
  59. continue
  60. }
  61. if char == ')' {
  62. for len(stack) > 0 && stack[len(stack)-1] != "(" {
  63. output = append(output, stack[len(stack)-1])
  64. stack = stack[:len(stack)-1]
  65. }
  66. if len(stack) == 0 {
  67. return nil, fmt.Errorf("mismatched parentheses")
  68. }
  69. stack = stack[:len(stack)-1] // pop '('
  70. i++
  71. continue
  72. }
  73. return nil, fmt.Errorf("invalid character: %c", char)
  74. }
  75. for len(stack) > 0 {
  76. if stack[len(stack)-1] == "(" {
  77. return nil, fmt.Errorf("mismatched parentheses")
  78. }
  79. output = append(output, stack[len(stack)-1])
  80. stack = stack[:len(stack)-1]
  81. }
  82. return output, nil
  83. }
  84. func evaluatePostfix(postfix []string, x float64) (float64, error) {
  85. var stack []float64
  86. for _, token := range postfix {
  87. if token == "x" || token == "X" {
  88. stack = append(stack, x)
  89. } else if val, err := strconv.ParseFloat(token, 64); err == nil {
  90. stack = append(stack, val)
  91. } else {
  92. // Operator
  93. if len(stack) < 2 {
  94. return 0, fmt.Errorf("invalid expression")
  95. }
  96. b := stack[len(stack)-1]
  97. a := stack[len(stack)-2]
  98. stack = stack[:len(stack)-2]
  99. var res float64
  100. switch token {
  101. case "+":
  102. res = a + b
  103. case "-":
  104. res = a - b
  105. case "*":
  106. res = a * b
  107. case "/":
  108. if b == 0 {
  109. return 0, fmt.Errorf("division by zero")
  110. }
  111. res = a / b
  112. default:
  113. return 0, fmt.Errorf("unknown operator: %s", token)
  114. }
  115. stack = append(stack, res)
  116. }
  117. }
  118. if len(stack) != 1 {
  119. return 0, fmt.Errorf("invalid expression result")
  120. }
  121. return stack[0], nil
  122. }
  123. func isOperator(c byte) bool {
  124. return c == '+' || c == '-' || c == '*' || c == '/'
  125. }
  126. func precedence(c byte) int {
  127. switch c {
  128. case '+', '-':
  129. return 1
  130. case '*', '/':
  131. return 2
  132. }
  133. return 0
  134. }