Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@
/doc/*
*.trace
*~
_build/
rebar.lock
__pycache__/
src/calc_lexer.erl
src/calc_parser.erl
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM erlang:26
WORKDIR /usr/src/mathex
COPY . .
RUN rebar3 compile
EXPOSE 4040
CMD ["erl","-noshell","-pa","_build/default/lib/mathex/ebin","-eval","mathex_mcp_server:start(), timer:sleep(infinity)"]
121 changes: 80 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,93 @@
mathex
======
[![Build Status](https://api.travis-ci.org/nisbus/mathex.png?branch=master)](https://api.travis-ci.org/nisbus/mathex.png?branch=master)
[![Build Status](https://api.travis-ci.org/nisbus/mathex.png?branch=master)](https://api.travis-ci.org/nisbus/mathex.png?branch=master)

Extra math functions for Erlang
Extra math and statistics functions for Erlang.
The library provides calculations such as moving averages,
standard deviation, skewness, kurtosis and correlation.

The API can be used standalone from any Erlang application.
All functions operate on lists of numbers and return either a single
value or a list of values.

This library contains the following functions:

## Return a single value

* average
* sum
* stdev_sample
* stdev_population
* skew
* kurtosis
* variance
* covariance

## Return a list of values

* moving_average
* correlation
* correlation_matrix

Most of these are well documented elsewhere except maybe the
correlation_matrix.
The correlation matrix will take in a list of numeric lists
and correlate each list in the collection with all the other lists
of the collection.
The result will look like this:

## Return a single value

* average
* sum
* stdev_sample
* stdev_population
* skew
* kurtosis
* variance
* covariance

## Return a list of values

* moving_average
* correlation
* correlation_matrix

In addition to statistics, mathex can parse and evaluate simple algebraic
expressions. See `mathex_calc:eval/1` for evaluating a string expression and
`mathex_calc:calculate/1` for applying an expression to a list of dated values.

Most of these are well documented elsewhere except maybe the
correlation_matrix.
The correlation matrix will take in a list of numeric lists
and correlate each list in the collection with all the other lists
of the collection.
The result will look like this:

```erlang
[{integer(),integer(),float()},
{integer(),integer(),float()},
{integer(),integer(),float()},
{integer(),integer(),float()}]
```

Where the first int is the index of a list in the collection and the
second integer the index of the list it's being correlated to.
The float is the correlation of the two.
It will give you all possible combinations and their correlation.


# To Do
Add a sort function to return the correlation_matrix sorted desc/asc.
Add more functions.
Add unit tests.
Add more documentation.


## Documentation

Where the first int is the index of a list in the collection and the
second integer the index of the list it's being correlated to.
The float is the correlation of the two.
It will give you all possible combinations and their correlation.

# To Do
Add a sort function to return the correlation_matrix sorted desc/asc.
Add more functions.
Add unit tests.
Add more documentation.

## MCP server and client

This project exposes the statistics and calculator functions over a minimal
line-based TCP protocol. The easiest way to run the server is via Docker:

```bash
docker build -t mathex .
docker run -p 4040:4040 mathex
```

Once running, you can query the server without any Erlang knowledge by using
the provided Python script:

```bash
python3 tools/mcp_client.py sum 1 2 3
python3 tools/mcp_client.py calc "5+5"
```

The client connects to `localhost:4040` by default and prints the result of the
requested function or expression.

## Running tests

EUnit tests are included and can be executed with:

```bash
rebar3 eunit
```

The tests cover the public API and a few edge cases.

## Documentation
Check out the [gh-pages](http://nisbus.github.com/mathex)
32 changes: 32 additions & 0 deletions src/calc_lexer.xrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Definitions.

Digit = [0-9]
WS = ([\000-\s]|%.*)
Plus = \+
Minus = \-
Multiply = \*
Divide = \/
Power = \^
Function = [A-Za-z]
Open = \(
Close = \)
Op = [\+|\^|\*|\/]

Rules.

\-?{Digit}+ : {token, {float, TokenLine, convert_to_float(TokenChars)}}.
\-?{Digit}+\.{Digit}+((E|e)(\+|\-)?{D}+)? : {token, {float, TokenLine, list_to_float(TokenChars)}}.
{Plus} : {token, {add, TokenLine, list_to_atom(TokenChars)}}.
{Minus} : {token, {minus, TokenLine, list_to_atom(TokenChars)}}.
{Multiply} : {token, {multiply, TokenLine, list_to_atom(TokenChars)}}.
{Divide} : {token, {divide, TokenLine, list_to_atom(TokenChars)}}.
{Power} : {token, {power, TokenLine, list_to_atom(TokenChars)}}.
{Open} : {token, {open, TokenLine, list_to_atom(TokenChars)}}.
{Close} : {token, {close, TokenLine, list_to_atom(TokenChars)}}.
{Function}+ : {token, {fn, TokenLine, list_to_atom(TokenChars)}}.
{WS}+ : skip_token.

Erlang code.

convert_to_float(List) ->
list_to_float(List++".0000000").
95 changes: 95 additions & 0 deletions src/calc_parser.yrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
Nonterminals
eval expr term factor.

Terminals
open close add minus multiply divide float power fn.

Rootsymbol eval.

Left 100 float.
Left 200 open.
Left 300 close.
Left 400 fn.
Left 500 add.
Left 600 minus.
Left 700 multiply.
Left 800 divide.
Left 900 power.

eval -> expr : '$1'.

expr -> expr add term : add('$1','$3').
expr -> expr minus term : subtract('$1','$3').
expr -> term : '$1'.

term -> term multiply factor : multiply('$1','$3').
term -> term divide factor : divide('$1','$3').
term -> term power factor : power('$1','$3').
term -> term fn factor : unwrap('$3').
term -> factor : '$1'.

factor -> float : unwrap('$1').
factor -> open expr close : '$2'.
factor -> fn factor : math(unwrap('$1'),'$2').
factor -> fn : math(unwrap('$1')).

Erlang code.



unwrap({_,_,V}) -> V;
unwrap({_,V}) -> V;
unwrap(V) -> V.

add(A,B) ->
A+B.
subtract(A,B) ->
A-B.
divide(A,B) ->
A/B.
multiply(A,B) ->
A*B.
power(A,B) ->
math:pow(A,B).
math(pi) ->
math:pi();
math(Other) ->
io:format("Unknown operator ~p~n",[Other]),
0.0.
math(sqrt,A) ->
math:sqrt(A);
math(sin,A) ->
math:sin(A);
math(cos,A) ->
math:cos(A);
math(tan,A) ->
math:tan(A);
math(asin,A) ->
math:asin(A);
math(acos,A) ->
math:acos(A);
math(atan,A) ->
math:atan(A);
math(sinh,A) ->
math:sinh(A);
math(cosh,A) ->
math:cosh(A);
math(tanh,A) ->
math:tanh(A);
math(asinh,A) ->
math:asinh(A);
math(acosh,A) ->
math:acosh(A);
math(atanh,A) ->
math:atanh(A);
math(exp,A) ->
math:exp(A);
math(log,A) ->
math:log(A);
math(erf,A) ->
math:erf(A);
math(erfc,A) ->
math:erfc(A);
math(Other,_A) ->
io:format("Unknown operator ~p~n",[Other]),
0.0.
Loading