Skip to content

Commit

Permalink
Version 1.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Sominemo committed Dec 6, 2021
1 parent 4f9adc2 commit 04d30e8
Show file tree
Hide file tree
Showing 7 changed files with 468 additions and 30 deletions.
38 changes: 35 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,47 @@
# 1.3.0

## Math Tree
- Important change: `MathNode` is now a class of `MathExpression` interface.
Compared to MathNode, MathExpression may return null in `calc()` method.
- New: `getUsedVariables()` method for `MathExpression` and `MathNode`.
This method goes down the math tree to find any uses of `MathVariable`
and returns names of all variables.
- New: `MathExpression` object family - `MathComparison`:
- `MathComparisonEquation` (=)
- `MathComparisonGreater` (>)
- `MathComparisonLess` (<)

## Parsing
- New: `MathNodeExpression.fromStringExtended()` method allows you to
interpret equations and comparisons. Compared to `fromString`,
it returns `MathExpression` instead of `MathNode`, since comparisons
can't guarantee result.
- New: `MathNodeExpression.getPotentialVariableNames()` analyzes given
math expression string for possible use of variables. Refer to
documentation for rough edges before using it.
- New: `MathNodeExpression.builtInVariables` gives a list of built-in
predefined variable names.
- New: `MathNodeExpression.isVariableNameValid()` lets you check if
the parser can work with a given name.

## Misc.
- Changed input parameters type for `CantProcessExpressionException`.
- Small documentation fixes.

# 1.2.0

- Fix README
- Moved integrating features to a separate package library `math_parser_integrate`
- Fix README.
- Moved integrating features to a separate package library
`math_parser_integrate`.


# 1.1.0

- Custom variables support.
- `MathFunctionX`deprecated.
- `MathVariable` introduced.
- You need to pass an instance of `MathVariableValues` instead of a num to the `calc()` function now
- You need to pass an instance of `MathVariableValues` instead of a num
to the `calc()` function now.


# 1.0.0
Expand Down
41 changes: 37 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,27 @@
Process math expressions, convert them to machine-readable
form, and calculate them.

This package is aimed to help you to work with formulas,
parts of equations and other forms of simple math
This package is aimed to help you to work with formulas,
parts of equations and other forms of simple math
expressions in your projects. This package supports custom
variables too.

## Math Tree

The library provides a family of `MathNode` classes, most of
them have subnodes that are being calculated recursively.
The library provides a family of `MathExpression` and
`MathNode` classes, most of them have subnodes that are being
calculated recursively.

There are such types of MathNode:

- `MathFunction` (and `MathFunctionWithTwoArguments` subclass)
- `MathValue`
- `MathOperator`

Types of `MathExpression`:

- `MathComparison`

All the child classes names begin with the family they belong to.

## Evaluation
Expand All @@ -36,6 +41,9 @@ MathOperatorAddition(
).calc(MathVariableValues.x(5));
```

You can also evaluate `MathExpression.calc`, but this method
doesn't guarantee numeric result, so it may return false.

## Parsing String to MathNode

The library can parse general mathematical expressions strings
Expand Down Expand Up @@ -97,6 +105,31 @@ final expression = MathNodeExpression.fromString(

More complicated work with variables is shown off in example.

You can also parse equations with `MathNodeExpression.fromStringExtended`,
refer to example for this.

### Detect used variable names

You can detect possible variable names used in a string math expression
using `MathNodeExpression.getPotentialVariableNames`.

Detecting variable names works properly only when implicit multiplication
is disabled.

```dart
final expr = '2*a+b';
final vars = MathNodeExpression.getPotentialVariableNames(
expr,
hideBuiltIns: true,
);
MathNodeExpression.fromString(
expr,
variableNames: vars,
isImplicitMultiplication: false,
);
```

## Other Features

### Numerical methods for Definite Integrals
Expand Down
40 changes: 37 additions & 3 deletions example/math_parser_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,44 @@ void main() {
// Display the parsed expression in human-readable form
print(expression);

// Evaluate the expression with `x = 20`, `y = 2`, `theta = 1/2`
// List all used variables
print(expression.getUsedVariables());

// Evaluate the expression with `x = 20`, `y = 2`, `theta = 1/2`, 'x_1 = 3'
// and display result
print(expression.calc(
MathVariableValues({'x': 20, 'y': 2, 'Θ': 0.5, 'x_1': 3}),
print(
expression.calc(
MathVariableValues({'x': 20, 'y': 2, 'Θ': 0.5, 'x_1': 3}),
),
);

// Compare expressions
print(
MathNodeExpression.fromStringExtended('2x-x=8x/2x-x=2').calc(
MathVariableValues.x(2),
),
);

// Detect possible variable names
final stringExpression =
'((2*x)^(e^3 + 4) + cos(3)*x) / log[x_1*2 + 3^2*e](2 + (3*y)^2)^5 * (2 + y)*(x^2 + 3) + arcctg(Θ)';

// Remove built-in variables if you are going to ask a user to enter the
// values
final vars = MathNodeExpression.getPotentialVariableNames(
stringExpression,
hideBuiltIns: true,
);

// Show detected variables
print(vars);

// Use the vars to parse the math expression
// Variable detection works properly only with implicit multiplication
print(MathNodeExpression.fromString(
stringExpression,
variableNames: vars,
isImplicitMultiplication: false,
));
}

Expand Down
148 changes: 147 additions & 1 deletion lib/src/math_node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,37 @@ class MathVariableValues {
factory MathVariableValues.x(num x) => MathVariableValues({'x': x});
}

/// Any Math-related object
///
/// Main implementers are decedents of [MathNode] and [MathComparison] classes.
abstract class MathExpression {
/// Math expression constructor
const MathExpression();

/// Generalized value for all sorts of math expressions, but result is not
/// guaranteed.
///
/// Tries to return the most appropriate result for given object type.
/// For example, when working with [MathNode], it always returns its
/// [MathNode.calc] result. For [MathComparisonEquation], a value of subnodes is
/// being returned but only if they equal, else null will be returned.
/// For [MathComparisonGreater] and [MathComparisonLess] returns a greater or a
/// smaller value accordingly even if the expression isn't true.
num? calc(
MathVariableValues values,
);

/// Get mentioned variables that are required to evaluate the expression
///
/// Searches the math tree for any [MathVariable] instances and returns a set
/// of their names.
Set<String> getUsedVariables();
}

/// Basic math expression unit
///
/// Defines the [calc] method for every child to implement.
abstract class MathNode {
abstract class MathNode extends MathExpression {
/// Evaluate the expression
///
/// Will calculate the value of the given expression using the given [x] value
Expand All @@ -51,6 +78,7 @@ abstract class MathNode {
/// You can check an expression or its parts (subnodes) for being
/// constant with the isConst() method of the ExtensionConstant* extension
/// family.
@override
num calc(
MathVariableValues values,
);
Expand All @@ -72,6 +100,11 @@ abstract class MathFunction implements MathNode {
///
/// The first and only parameters is supposed to be function's argument
const MathFunction(this.x1);

@override
Set<String> getUsedVariables() {
return x1.getUsedVariables();
}
}

/// Mathematical function with two arguments
Expand All @@ -94,6 +127,11 @@ abstract class MathFunctionWithTwoArguments implements MathFunction {
///
/// The parameters are supposed to be function's arguments
const MathFunctionWithTwoArguments(this.x1, this.x2);

@override
Set<String> getUsedVariables() {
return {...x1.getUsedVariables(), ...x2.getUsedVariables()};
}
}

/// Mathematical operator
Expand All @@ -109,6 +147,11 @@ abstract class MathOperator implements MathNode {

/// Creates a new mathematical operator
const MathOperator(this.left, this.right);

@override
Set<String> getUsedVariables() {
return {...left.getUsedVariables(), ...right.getUsedVariables()};
}
}

/// Constant value
Expand All @@ -126,6 +169,11 @@ class MathValue extends MathNode {

@override
String toString() => 'VALUE($value)';

@override
Set<String> getUsedVariables() {
return {};
}
}

/// A math variable
Expand All @@ -146,6 +194,11 @@ class MathVariable extends MathNode {

@override
String toString() => 'VAR[$variableName]';

@override
Set<String> getUsedVariables() {
return {variableName};
}
}

/// Addition operator (+)
Expand Down Expand Up @@ -437,3 +490,96 @@ class MathFunctionLn extends MathFunctionLog {
@override
String toString() => '[LOG($x1, E]';
}

/// A parent class for comparisons
abstract class MathComparison extends MathExpression {
/// Left comparable
final MathExpression left;

/// Right comparable
final MathExpression right;

/// Creates a new comparable
const MathComparison(this.left, this.right);

@override
Set<String> getUsedVariables() {
return {...left.getUsedVariables(), ...right.getUsedVariables()};
}
}

/// Equation
///
/// The equation class which can contain multiple MathExpressions to be compared
class MathComparisonEquation extends MathComparison {
@override
num? calc(MathVariableValues values) {
final leftResult = left.calc(values);
final rightResult = right.calc(values);

if (leftResult == rightResult) return leftResult;

return null;
}

@override
String toString() {
return '[$left = $right]';
}

/// Creates an equation
const MathComparisonEquation(MathExpression left, MathExpression right)
: super(left, right);
}

/// If Greater Comparison
///
/// Looks for a bigger MathExpression
class MathComparisonGreater extends MathComparison {
@override
num? calc(MathVariableValues values) {
final leftResult = left.calc(values);
if (leftResult == null) return null;

final rightResult = right.calc(values);
if (rightResult == null) return null;

if (leftResult > rightResult) return leftResult;
return rightResult;
}

@override
String toString() {
return '[$left > $right]';
}

/// Creates a greater comparison
const MathComparisonGreater(MathExpression left, MathExpression right)
: super(left, right);
}

/// If Greater Comparison
///
/// Looks for a bigger MathExpression
class MathComparisonLess extends MathComparison {
@override
num? calc(MathVariableValues values) {
final leftResult = left.calc(values);
if (leftResult == null) return null;

final rightResult = right.calc(values);
if (rightResult == null) return null;

if (leftResult < rightResult) return leftResult;
return rightResult;
}

@override
String toString() {
return '[$left < $right]';
}

/// Creates a less comparison
const MathComparisonLess(MathExpression left, MathExpression right)
: super(left, right);
}
Loading

0 comments on commit 04d30e8

Please sign in to comment.