diff --git a/backend/README.md b/backend/README.md index 7312b35..6ba6a1f 100644 --- a/backend/README.md +++ b/backend/README.md @@ -49,17 +49,18 @@ src ## Math Calculation ### Evalutaion Algorithm -1. Parse the expression string by splitting by `+` and `-`, resulting a list of sub-expressions (`ExpressionMD`). -2. For each sub-expression, turn it into a `Fraction`. +1. Parse the expression string by splitting by `+` and `-`, resulting a list of sub-expressions (`Summand`). +2. For each `Summand`, turn it into a `Fraction`. 3. Sum all fractions by `Fraction.add(Fraction)`. - It uses **lowest common multiple** to add fractions. - [Decimal.js](https://mikemcl.github.io/decimal.js/) is used for the basic arithmetic operations. 4. Evaluate the result by `Fraction.evaluate()`. + - result is converted to string to avoid overflow and keep precision. ### Example 1. Input string: `5 / 3 / 4 * 9 - 2 * 3 / 8` 2. Parse into sub-expressions: `5 / 3 / 4 * 9` and `-2 * 3 / 8` -3. Turn these into fractions: $\frac{5 * 9}{3 * 4}$ and $-\frac{2 * 3}{8}$ +3. Turn them into fractions: $\frac{5 * 9}{3 * 4}$ and $-\frac{2 * 3}{8}$ 4. find equivalent fractions with same denominator: $\frac{90}{24}$ and $-\frac{18}{24}$ 5. Sum these fractions: $\frac{90}{24} - \frac{18}{24} = \frac{72}{24} = 3$ diff --git a/backend/src/math/evaluate.ts b/backend/src/math/evaluate.ts index 53f566b..278c3de 100644 --- a/backend/src/math/evaluate.ts +++ b/backend/src/math/evaluate.ts @@ -1,4 +1,4 @@ -import { ExpressionMD } from './types'; +import { Summand } from './types'; const operators = new Set(['+', '-', '*', '/']); // isValidCommand checks if the command is a allowed mathematical expression @@ -32,22 +32,23 @@ function evaluate(s: string): string { } s = s.replace(/ /g,''); // remove all spaces - const exps = parseExpressionMDs(s).map(expMD => expMD.toFraction()); - const first = exps.shift(); + const summands = parseExpression(s); + const fractions = summands.map(expMD => expMD.toFraction()); + const first = fractions.shift(); if (!first) { return "0" } // early return - if (exps.length === 0) { + if (fractions.length === 0) { return first.evaluate() } - const result = exps.reduce((acc, expMD) => acc.add(expMD), first); + const result = fractions.reduce((acc, f) => acc.add(f), first); return result.evaluate().toString(); } -// parseExpressionMDs parse the expression into list of ExpressionMD -const parseExpressionMDs = (s: string): ExpressionMD[] => { - const expressions: ExpressionMD[] = []; +// parseExpression parse the expression into list of Summand +const parseExpression = (s: string): Summand[] => { + const summands: Summand[] = []; let currExp = ""; let prevOp = true; if (s[0] === '-') { @@ -56,19 +57,19 @@ const parseExpressionMDs = (s: string): ExpressionMD[] => { } for (const token of s.split('')) { if (token === '+') { - expressions.push(new ExpressionMD(prevOp, currExp)); + summands.push(new Summand(prevOp, currExp)); prevOp = true currExp = ""; } else if (token === '-') { - expressions.push(new ExpressionMD(prevOp, currExp)); + summands.push(new Summand(prevOp, currExp)); prevOp = false; currExp = ""; } else { currExp += token; } } - expressions.push(new ExpressionMD(prevOp, currExp)); // last one - return expressions; + summands.push(new Summand(prevOp, currExp)); // last token + return summands; } export { isValidCommand, evaluate }; \ No newline at end of file diff --git a/backend/src/math/types.ts b/backend/src/math/types.ts index 1da9ce1..cdd7580 100644 --- a/backend/src/math/types.ts +++ b/backend/src/math/types.ts @@ -1,7 +1,7 @@ import { Decimal } from "decimal.js"; -// ExpressionMD is a mathematical expression only contains numbers, multiplication, division -export class ExpressionMD { +// Summand is a mathematical expression only contains numbers, multiplication, division +export class Summand { public sign: boolean; public exp: string; constructor(sign: boolean, exp: string) { @@ -21,6 +21,8 @@ export class ExpressionMD { if (indexOfFirstOperator === this.exp.length) { return new Fraction(numerator, new Decimal(1)); } + + // parse the expression of multiplication and division let prevOp = this.exp[indexOfFirstOperator]; let currStr = ""; for (let i = indexOfFirstOperator + 1; i < this.exp.length; i++) {