diff --git a/README.md b/README.md index c538cbe..58f4d9c 100644 --- a/README.md +++ b/README.md @@ -20,30 +20,53 @@ Then, you can put the file gomath where do you want (it could be in `/usr/local/ ### Calculate -To parse an expression and calculate it, use `gomath.ParseAndCalculate(string, *gomath.Options) (string, error)`. +To parse an expression, use `gomath.Parse(string) (gomath.Result, error)`. The string is a valid expression, like `1+2` or `2(1/3+4)^5`. -It returns the result of the expression in a string according to the given options. + +The result will give you everything needed to perform operations, like getting the exact representation of the result, +or the ability to convert ```go -res, err := gomath.ParseAndCalculate("1+2", &gomath.Options{}) -err == nil // true -res == "3" // true +res, err := gomath.Parse("1/2+1") +// check the error +res.String == "3/2" // true +res.IsExact // true because 1.5 is the exact representation of 3/2 +res.LaTeX == `\frac{1}{2} + 1` // true +res.Approx(5) == "1.50000" // true ``` -You can modify the result's type with `gomath.Options`. -Set `Decimal` to `true` if you want to have a decimal approximation. -You can specify the number of digits with `Precision`. +You can also call `gomath.ParseAndCalculate(string, *gomath.Options) (string, error)` to directly get the string +representation with the given options or `gomath.ParseAndConvertToLatex(string, *gomath.Options) (string, error)` to get +the $\LaTeX$ code. + +### Creating a function -### Convert to LaTeX +You can create a function with `gomath.NewFunction(string) (gomath.Function, int, error)`. +The string is a valid expression representing a function. +It is composed by the arguments and the expression of the function. +You must separate each argument with a coma (`,`) and separate the arguments and the expression with `->`. +``` +x, y -> x^y +``` +is a valid expression representing a function. +The returned int is the number of arguments. -To parse an expression and convert it into $\LaTeX$, use `gomath.ParseAndConvertToLatex(string, *gomath.Options) (string, error)`. -The string is a valid expression like `1+2` or `2(1/3+4)^5`. -It returns the result of the expression in a string according to the given options. +Then, you can evaluate the function by passing a map containing every argument. +gomath will send you the result (an instance of `gomath.Result`) of the evaluation. ```go -res, err := gomath.ParseAndConvertToLatex("(1+2/3)/2", &gomath.Options{}) -err == nil // true -res == `\frac{1 + \frac{2}{3}}{2}` // true +f, _, err := gomath.NewFunction("x, y -> x^y") +if err != nil { + panic(err) // will not panic because the expression is valid +} +res, err := f(map[string]string{ + "x": "5", + "y": "2" +}) +if err != nil { + panic(err) // will not panic because the expression is valid and every argument is set +} +res.String == "25" // true ``` ### CLI diff --git a/function.go b/function.go new file mode 100644 index 0000000..fe28432 --- /dev/null +++ b/function.go @@ -0,0 +1,50 @@ +package gomath + +import ( + "errors" + "fmt" + "strings" +) + +var ( + ErrInvalidFunction = errors.New("invalid function") + ErrInvalidFunctionCall = errors.New("invalid function call") +) + +type Function func(map[string]string) (Result, error) + +// NewFunction creates a new Function by parsing the given string. It must follow this scheme: +// +// arg1, arg2, arg3... -> expr +// +// Example: +// +// gomath.NewFunction("x, y -> x^y") +// +// It returns the Function and the number of arguments. +func NewFunction(s string) (Function, int, error) { + splits := strings.Split(s, "->") + if len(splits) != 2 { + return nil, 0, errors.Join(ErrInvalidFunction, errors.New("a function is defined by 'args -> expression'")) + } + before := splits[0] + expression := strings.TrimSpace(splits[1]) + var params []string + for _, p := range strings.Split(before, ",") { + params = append(params, strings.TrimSpace(p)) + } + return func(args map[string]string) (Result, error) { + if len(params) != len(args) { + return nil, errors.Join(ErrInvalidFunctionCall, errors.New("not all parameters have been defined")) + } + cp := expression + for _, p := range params { + v, ok := args[p] + if !ok { + return nil, errors.Join(ErrInvalidFunctionCall, fmt.Errorf("missing argument for %s", p)) + } + cp = strings.ReplaceAll(cp, p, v) + } + return Parse(cp) + }, len(params), nil +} diff --git a/function_test.go b/function_test.go new file mode 100644 index 0000000..efb043c --- /dev/null +++ b/function_test.go @@ -0,0 +1,35 @@ +package gomath + +import "testing" + +func TestNewFunction(t *testing.T) { + f, n, err := NewFunction("x -> x^2") + if err != nil { + t.Fatal(err) + } + if n != 1 { + t.Errorf("got %d, want 1", n) + } + result, err := f(map[string]string{"x": "5"}) + if err != nil { + t.Fatal(err) + } + if result.String() != "25" { + t.Errorf("got %s, want 25", result.String()) + } + + f, n, err = NewFunction("x, y -> x^y") + if err != nil { + t.Fatal(err) + } + if n != 2 { + t.Errorf("got %d, want 2", n) + } + result, err = f(map[string]string{"x": "5", "y": "2"}) + if err != nil { + t.Fatal(err) + } + if result.String() != "25" { + t.Errorf("got %s, want 25", result.String()) + } +}