package main

import (
“fmt”
“math”
“strconv”
“strings”
“unicode”
)

// Define all token types
type TokenType int

const (
EOF TokenType = iota
Number
Plus
Minus
Multiply
Divide
Power
Sqrt
Root
Sin
Cos
Tan
Cot
LeftParen
RightParen
Variable
Comma
)

// Token structure
type Token struct {
Type TokenType
Value string
}

// Function to check if a rune is a space
func isSpace(r rune) bool {
return unicode.IsSpace®
}

// Function to check if a rune is a digit
func isDigit(r rune) bool {
return unicode.IsDigit®
}

// Function to check if a rune is an operator
func isOperator(r rune) bool {
return strings.ContainsRune(“±/^", r)
}

// Add a function to check if a rune is a letter
func isLetter(r rune) bool {
return unicode.IsLetter®
}

// Function to convert an expression string into tokens
func tokenize(expr string) ([]Token, error) {
var tokens []Token
for len(expr) > 0 {
expr = strings.TrimSpace(expr)
if len(expr) == 0 {
break
}
var t Token
switch {
case expr[0] == ‘+’:
t = Token{Plus, “+”}
expr = expr[1:]
case expr[0] == ‘-’:
if isDigit(rune(expr[1])) {
num := strings.TrimLeftFunc(expr[1:], isDigit)
t = Token{Number, “-” + expr[1:len(expr)-len(num)]}
expr = num
} else {
t = Token{Minus, “-”}
expr = expr[1:]
}
case expr[0] == '':
t = Token{Multiply, “*”}
expr = expr[1:]
case expr[0] == ‘/’:
t = Token{Divide, “/”}
expr = expr[1:]
case expr[0] == ‘(’:
t = Token{LeftParen, “(”}
expr = expr[1:]
case expr[0] == ‘)’:
t = Token{RightParen, “)”}
expr = expr[1:]
case expr[0] == ‘,’:
t = Token{Comma, “,”}
expr = expr[1:]
case expr[0] == ‘^’:
t = Token{Power, “^”}
expr = expr[1:]
case strings.HasPrefix(expr, “sqrt”):
t = Token{Sqrt, “sqrt”}
expr = expr[len(“sqrt”):]
case strings.HasPrefix(expr, “root(”):
t = Token{Root, “root”}
expr = expr[len(“root(”):]
case strings.HasPrefix(expr, “sin”):
t = Token{Sin, “sin”}
expr = expr[len(“sin”):]
case strings.HasPrefix(expr, “cos”):
t = Token{Cos, “cos”}
expr = expr[len(“cos”):]
case strings.HasPrefix(expr, “tan”):
t = Token{Tan, “tan”}
expr = expr[len(“tan”):]
case strings.HasPrefix(expr, “cot”):
t = Token{Cot, “cot”}
expr = expr[len(“cot”):]
case isDigit(rune(expr[0])) || (expr[0] == ‘.’ && len(expr) > 1 && isDigit(rune(expr[1]))):
// Найдите длину числовой последовательности
var numLen int
for ; numLen < len(expr) && (isDigit(rune(expr[numLen])) || expr[numLen] == ‘.’); numLen++ {
}
t = Token{Number, expr[:numLen]}
expr = expr[numLen:]
case isLetter(rune(expr[0])):
varName := strings.TrimLeftFunc(expr, isLetter)
t = Token{Variable, expr[:len(expr)-len(varName)]}
expr = varName
default:
return nil, fmt.Errorf(“Неизвестный символ: %c”, expr[0])
}

tokens = append(tokens, t)
}

return tokens, nil
}

// Function to get the precedence of an operator
func precedence(op TokenType) int {
switch op {
case Power:
return 4
case Sqrt, Sin, Cos, Tan, Cot:
return 3
case Multiply, Divide:
return 2
case Plus, Minus:
return 1
case LeftParen, RightParen:
return 0
default:
return 0
}
}

// Function to evaluate an expression given its tokens
func eval(tokens []Token, variableMaps …map[string]float64) (float64, error) {
// Combine all the variable maps
vars := make(map[string]float64)
for _, variableMap := range variableMaps {
for key, value := range variableMap {
vars[key] = value
}
}

applyOperation := func(op Token, args …float64) (float64, error) {
switch op.Type {
case Plus:
return args[0] + args[1], nil
case Minus:
return args[0] - args[1], nil
case Multiply:
return args[0] * args[1], nil
case Divide:
return args[0] / args[1], nil
case Power:
return math.Pow(args[0], args[1]), nil
case Sqrt:
return math.Sqrt(args[0]), nil
case Root:
return math.Pow(args[1], 1.0/args[0]), nil
case Sin:
return math.Sin(args[0]), nil
case Cos:
return math.Cos(args[0]), nil
case Tan:
return math.Tan(args[0]), nil
case Cot:
return 1 / math.Tan(args[0]), nil
default:
return 0, fmt.Errorf(“Неизвестная операция: %v”, op.Value)
}
}

var numStack []float64
var opStack []Token

popNumStack := func() float64 {
val := numStack[len(numStack)-1]
numStack = numStack[:len(numStack)-1]
return val
}

for i := 0; i < len(tokens); i++ {
token := tokens[i]

switch token.Type {
// case Comma:
// if i+1 < len(tokens) && tokens[i+1].Type != LeftParen {
// // Push both numbers onto numStack before processing the root operation
// numStack = append(numStack, popNumStack())
// } else {
// return 0, fmt.Errorf(“Incorrect usage of comma”)
// }
case Root:
// Skip the “root(” token first
i++

// Remove the comma from the tokens list
j := i
open := 1
for ; open != 0 && j < len(tokens); j++ {
if tokens[j].Type == LeftParen {
open++
}
if tokens[j].Type == RightParen {
open–
}
if open == 1 && tokens[j].Type == Comma {
tokens = append(tokens[:j], tokens[j+1:]…)
break
}
}
if j == len(tokens) {
return 0, fmt.Errorf(“Mismatched parentheses”)
}

case Number:
val, err := strconv.ParseFloat(token.Value, 64)
if err != nil {
return 0, err
}
numStack = append(numStack, val)
case Variable:
val, ok := vars[token.Value]
if !ok {
return 0, fmt.Errorf(“Отсутствует значение для переменной: %v”, token.Value)
}
numStack = append(numStack, val)
case Plus, Minus, Multiply, Divide, Power, Sqrt, Sin, Cos, Tan, Cot:
for len(opStack) > 0 && precedence(token.Type) <= precedence(opStack[len(opStack)-1].Type) {
op := opStack[len(opStack)-1]
opStack = opStack[:len(opStack)-1]

var args []float64
if op.Type == Sqrt || op.Type == Sin || op.Type == Cos || op.Type == Tan || op.Type == Cot {
args = []float64{popNumStack()}
} else {
args = []float64{popNumStack(), popNumStack()}
args[0], args[1] = args[1], args[0] // Reverse the order
}

res, err := applyOperation(op, args…)
if err != nil {
return 0, err
}

numStack = append(numStack, res)
}
opStack = append(opStack, token)
case LeftParen:
opStack = append(opStack, token)
case RightParen:
for len(opStack) > 0 && opStack[len(opStack)-1].Type != LeftParen {
op := opStack[len(opStack)-1]
opStack = opStack[:len(opStack)-1]

var args []float64
if op.Type == Sqrt || op.Type == Sin || op.Type == Cos || op.Type == Tan || op.Type == Cot {
args = []float64{popNumStack()}
} else {
args = []float64{popNumStack(), popNumStack()}
args[0], args[1] = args[1], args[0] // Reverse the order
}

res, err := applyOperation(op, args…)
if err != nil {
return 0, err
}

numStack = append(numStack, res)
}
if len(opStack) > 0 && opStack[len(opStack)-1].Type == LeftParen {
opStack = opStack[:len(opStack)-1]
}
}
}

for len(opStack) > 0 {
op := opStack[len(opStack)-1]
opStack = opStack[:len(opStack)-1]

var args []float64
if op.Type == Sqrt || op.Type == Sin || op.Type == Cos || op.Type == Tan || op.Type == Cot {
args = []float64{popNumStack()}
} else {
args = []float64{popNumStack(), popNumStack()}
args[0], args[1] = args[1], args[0] // Reverse the order
}

res, err := applyOperation(op, args…)
if err != nil {
return 0, err
}

numStack = append(numStack, res)
}

if len(numStack) != 1 {
return 0, fmt.Errorf(“Неверное количество аргументов”)
}

return numStack[0], nil
}

func main() {
expr := “root(3, 27)” // Вычисляет кубический корень из 27
tokens, err := tokenize(expr)
if err != nil {
fmt.Println(“Ошибка при токенизации:”, err)
return
}

result, err := eval(tokens)
if err != nil {
fmt.Println(“Ошибка при вычислении:”, err)
return
}
fmt.Printf(”%s = %v\n", expr, result)
}

не корректно вычисляет
результат root(3, 27) = 27