You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
6.4 KiB
Go
306 lines
6.4 KiB
Go
package staros
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func Calc(math string) (float64, error) {
|
|
math = strings.Replace(math, " ", "", -1)
|
|
math = strings.ToLower(math)
|
|
if err := check(math); err != nil {
|
|
return 0, err
|
|
}
|
|
result,err:=calc(math)
|
|
if err!=nil {
|
|
return 0,err
|
|
}
|
|
return floatRound(result,15),nil
|
|
}
|
|
|
|
func floatRound(f float64, n int) float64 {
|
|
format := "%." + strconv.Itoa(n) + "f"
|
|
res, _ := strconv.ParseFloat(fmt.Sprintf(format, f), 64)
|
|
return res
|
|
}
|
|
|
|
func check(math string) error {
|
|
math = strings.Replace(math, " ", "", -1)
|
|
math = strings.ToLower(math)
|
|
var bracketSum int
|
|
var signReady bool
|
|
for k, v := range math {
|
|
if string([]rune{v}) == "(" {
|
|
bracketSum++
|
|
}
|
|
if string([]rune{v}) == ")" {
|
|
bracketSum--
|
|
}
|
|
if bracketSum < 0 {
|
|
return fmt.Errorf("err at position %d.Reason is right bracket position not correct,except (", k)
|
|
}
|
|
if containSign(string([]rune{v})) {
|
|
if signReady {
|
|
if string([]rune{v}) != "+" && string([]rune{v}) != "-" {
|
|
return fmt.Errorf("err at position %d.Reason is sign %s not correct", k, string([]rune{v}))
|
|
}
|
|
} else {
|
|
signReady = true
|
|
continue
|
|
}
|
|
}
|
|
signReady = false
|
|
}
|
|
if bracketSum != 0 {
|
|
return fmt.Errorf("Error:right bracket is not equal as left bracket")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func calc(math string) (float64, error) {
|
|
var bracketLeft int
|
|
var bracketRight int
|
|
var DupStart int = -1
|
|
for pos, str := range math {
|
|
if string(str) == "(" {
|
|
bracketLeft = pos
|
|
}
|
|
if string(str) == ")" {
|
|
bracketRight = pos
|
|
break
|
|
}
|
|
}
|
|
if bracketRight == 0 && bracketLeft != 0 || (bracketLeft > bracketRight) {
|
|
return 0, fmt.Errorf("Error:bracket not correct at %d ,except )", bracketLeft)
|
|
}
|
|
if bracketRight == 0 && bracketLeft == 0 {
|
|
return calcLong(math)
|
|
}
|
|
line := math[bracketLeft+1 : bracketRight]
|
|
num, err := calcLong(line)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
for i := bracketLeft - 1; i >= 0; i-- {
|
|
if !containSign(math[i : i+1]) {
|
|
DupStart = i
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
if DupStart != -1 {
|
|
sign := math[DupStart:bracketLeft]
|
|
num, err := calcDuaFloat(sign, num)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
math = math[:DupStart] + fmt.Sprintf("%.15f", num) + math[bracketRight+1:]
|
|
DupStart = -1
|
|
} else {
|
|
math = math[:bracketLeft] + fmt.Sprintf("%.15f", num) + math[bracketRight+1:]
|
|
}
|
|
return calc(math)
|
|
}
|
|
|
|
func calcLong(str string) (float64, error) {
|
|
var sigReady bool = false
|
|
var sigApply bool = false
|
|
var numPool []float64
|
|
var operPool []string
|
|
var numStr string
|
|
var oper string
|
|
if str[0:1] == "+" || str[0:1] == "-" {
|
|
sigReady = true
|
|
}
|
|
for _, stp := range str {
|
|
if sigReady && containSign(string(stp)) {
|
|
sigReady = false
|
|
sigApply = true
|
|
oper = string(stp)
|
|
continue
|
|
}
|
|
if !containSign(string(stp)) {
|
|
sigReady = false
|
|
numStr = string(append([]rune(numStr), stp))
|
|
continue
|
|
}
|
|
if !sigReady {
|
|
sigReady = true
|
|
}
|
|
if sigApply {
|
|
num, err := calcDua(oper, numStr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
sigApply = false
|
|
numPool = append(numPool, num)
|
|
} else {
|
|
num, err := parseNumbic(numStr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
numPool = append(numPool, num)
|
|
}
|
|
numStr = ""
|
|
operPool = append(operPool, string(stp))
|
|
}
|
|
if sigApply {
|
|
num, err := calcDua(oper, numStr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
numPool = append(numPool, num)
|
|
} else {
|
|
num, err := parseNumbic(numStr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
numPool = append(numPool, num)
|
|
}
|
|
return calcPool(numPool, operPool)
|
|
}
|
|
|
|
func calcPool(numPool []float64, operPool []string) (float64, error) {
|
|
if len(numPool) == 1 && len(operPool) == 0 {
|
|
return numPool[0], nil
|
|
}
|
|
if len(numPool) < len(operPool) {
|
|
return 0, errors.New(("Operate Signal Is too much"))
|
|
}
|
|
calcFunc := func(k int, v string) (float64, error) {
|
|
num, err := calcSigFloat(numPool[k], v, numPool[k+1])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
tmp := append(numPool[:k], num)
|
|
numPool = append(tmp, numPool[k+2:]...)
|
|
operPool = append(operPool[:k], operPool[k+1:]...)
|
|
return calcPool(numPool, operPool)
|
|
}
|
|
for k, v := range operPool {
|
|
if v == "^" {
|
|
return calcFunc(k, v)
|
|
}
|
|
}
|
|
for k, v := range operPool {
|
|
if v == "*" || v == "/" {
|
|
return calcFunc(k, v)
|
|
}
|
|
}
|
|
for k, v := range operPool {
|
|
return calcFunc(k, v)
|
|
}
|
|
return 0, nil
|
|
}
|
|
|
|
func calcSigFloat(floatA float64, b string, floatC float64) (float64, error) {
|
|
switch b {
|
|
case "+":
|
|
return floatRound(floatA + floatC,15), nil
|
|
case "-":
|
|
return floatRound(floatA - floatC,15), nil
|
|
case "*":
|
|
return floatRound(floatA * floatC,15), nil
|
|
case "/":
|
|
if floatC == 0 {
|
|
return 0, errors.New("Divisor cannot be 0")
|
|
}
|
|
return floatRound(floatA / floatC,15), nil
|
|
case "^":
|
|
return math.Pow(floatA, floatC), nil
|
|
}
|
|
return 0, fmt.Errorf("unexpect method:%s", b)
|
|
}
|
|
|
|
func calcSig(a, b, c string) (float64, error) {
|
|
floatA, err := parseNumbic(a)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
floatC, err := parseNumbic(c)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return calcSigFloat(floatA, b, floatC)
|
|
}
|
|
|
|
func calcDuaFloat(a string, floatB float64) (float64, error) {
|
|
switch a {
|
|
case "sin":
|
|
return math.Sin(floatB), nil
|
|
case "cos":
|
|
return math.Cos(floatB), nil
|
|
case "tan":
|
|
return math.Tan(floatB), nil
|
|
case "abs":
|
|
return math.Abs(floatB), nil
|
|
case "arcsin":
|
|
return math.Asin(floatB), nil
|
|
case "arccos":
|
|
return math.Acos(floatB), nil
|
|
case "arctan":
|
|
return math.Atan(floatB), nil
|
|
case "sqrt":
|
|
return math.Sqrt(floatB), nil
|
|
case "loge":
|
|
return math.Log(floatB), nil
|
|
case "log10":
|
|
return math.Log10(floatB), nil
|
|
case "log2":
|
|
return math.Log2(floatB), nil
|
|
case "floor":
|
|
return math.Floor(floatB), nil
|
|
case "ceil":
|
|
return math.Ceil(floatB), nil
|
|
case "round":
|
|
return math.Round(floatB), nil
|
|
case "trunc":
|
|
return math.Trunc(floatB), nil
|
|
case "+":
|
|
return 0 + floatB, nil
|
|
case "-":
|
|
return 0 - floatB, nil
|
|
}
|
|
return 0, fmt.Errorf("unexpect method:%s", a)
|
|
}
|
|
func calcDua(a, b string) (float64, error) {
|
|
floatB, err := parseNumbic(b)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return calcDuaFloat(a, floatB)
|
|
}
|
|
|
|
func parseNumbic(str string) (float64, error) {
|
|
switch str {
|
|
case "pi":
|
|
return float64(math.Pi), nil
|
|
case "e":
|
|
return float64(math.E), nil
|
|
default:
|
|
return strconv.ParseFloat(str, 64)
|
|
}
|
|
}
|
|
|
|
func containSign(str string) bool {
|
|
var sign []string = []string{"+", "-", "*", "/", "^"}
|
|
for _, v := range sign {
|
|
if str == v {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func contain(pool []string, str string) bool {
|
|
for _, v := range pool {
|
|
if v == str {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|