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.
staros/math.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
}