diff --git a/src/lesson2/engine.ts b/src/lesson2/engine.ts index 78aee1d7..edb67304 100644 --- a/src/lesson2/engine.ts +++ b/src/lesson2/engine.ts @@ -4,9 +4,27 @@ import { mathOperators, mathPriorities, mathOperatorsPriorities, + UnionOperationType, + unionOperators } from "./mathOperators"; -const [FIRST, SECOND] = mathPriorities; +const [ZERO, FIRST, SECOND] = mathPriorities; + +export const zeroPrioritiesCalc = (stack: ParsedLineType): ParsedLineType => + stack.reduce((result, item) => { + const prevItem = result[result.length - 1]; + + if (!isNumber(String(item)) && mathOperatorsPriorities[item] === ZERO) { + if (!unionOperators[item]) { + throw new TypeError("Unexpected stack!"); + } + const action = unionOperators[item] as UnionOperationType; + result = [...result.slice(0, -1), action(Number(prevItem))]; + } else { + result.push(item); + } + return result; + }, []); export const firstPrioritiesCalc = (stack: ParsedLineType): ParsedLineType => stack.reduce((result, nextItem) => { @@ -27,6 +45,8 @@ export const firstPrioritiesCalc = (stack: ParsedLineType): ParsedLineType => return result; }, []); + + export const secondPrioritiesCalc = (stack: ParsedLineType): number => stack.reduce((result, nextItem, key) => { const item = stack[key - 1]; @@ -35,8 +55,11 @@ export const secondPrioritiesCalc = (stack: ParsedLineType): number => throw new TypeError("Unexpected stack!"); } - if (!isNumber(String(item)) && mathOperatorsPriorities[item] === SECOND) { + if (!isNumber(String(item)) && + mathOperatorsPriorities[item] === SECOND && + item !== undefined) { result = mathOperators[item](Number(result), Number(nextItem)); } return result; }, Number(stack[0])); + diff --git a/src/lesson2/helpers.ts b/src/lesson2/helpers.ts index b5a4b6ae..7e52e94a 100644 --- a/src/lesson2/helpers.ts +++ b/src/lesson2/helpers.ts @@ -1 +1,14 @@ +import { mathOperators, +unionOperators } from "./mathOperators"; + +import { zeroPrioritiesCalc, + firstPrioritiesCalc, + secondPrioritiesCalc } from "./engine"; + +import { parser } from "./parser"; + export const isNumber = (item: string): boolean => !isNaN(Number(item)); + +export const isOperator = (item: string): boolean => item in mathOperators; + +export const isUnionOperator = (item: string): boolean => item in unionOperators; \ No newline at end of file diff --git a/src/lesson2/mathOperators.test.ts b/src/lesson2/mathOperators.test.ts index aad07e8d..4638de73 100644 --- a/src/lesson2/mathOperators.test.ts +++ b/src/lesson2/mathOperators.test.ts @@ -1,4 +1,4 @@ -import { mul, div, add, minus } from "./mathOperators"; +import { mul, div, add, minus, factorial, pow, sqr } from "./mathOperators"; describe("mathOperators test cases", () => { it("mul 1 * 2 to equal 2", () => { @@ -21,7 +21,39 @@ describe("mathOperators test cases", () => { expect(add(4, 2)).toBe(6); }); + it("add 10 + 12 to equal 22", () => { + expect(add(10, 12)).toBe(22); + }); + it("minus 4 - 2 to equal 2", () => { expect(minus(4, 2)).toBe(2); }); + + it("minus 34 - 20 to equal 14", () => { + expect(minus(34, 20)).toBe(14); + }); + + it("factorial 5 to equal 120", () => { + expect(factorial(5)).toBe(120); + }); + + it("factorial 3 to equal 6", () => { + expect(factorial(3)).toBe(6); + }); + + it("power 3 * 4 to equal 81", () => { + expect(pow(3, 4)).toBe(81); + }); + + it("power 5 * 6 to equal 15625", () => { + expect(pow(5, 6)).toBe(15625); + }); + + it("sqr 6 to equal 36", () => { + expect(sqr(6)).toBe(36); + }); + + it("sqr 10 to equal 100", () => { + expect(sqr(10)).toBe(100); + }); }); diff --git a/src/lesson2/mathOperators.ts b/src/lesson2/mathOperators.ts index af8eb770..923e01c7 100644 --- a/src/lesson2/mathOperators.ts +++ b/src/lesson2/mathOperators.ts @@ -1,4 +1,7 @@ export type ScalarOperationType = (first: number, second: number) => number; +export type UnionOperationType = (first: number) => number; + +export type MathOperationType = UnionOperationType | ScalarOperationType; export const mul: ScalarOperationType = ( first: number, @@ -20,19 +23,39 @@ export const minus: ScalarOperationType = ( second: number ): number => first - second; -export const mathOperators: { [key: string]: ScalarOperationType } = { +export const pow: ScalarOperationType = ( + first: number, + second: number +): number => Math.pow(first, second); + +export const factorial: UnionOperationType = (first: number): number => { + return first ? first * factorial(first - 1) : 1; +}; + +export const sqr: UnionOperationType = (first: number): number => pow(first, 2); + +export const unionOperators: { [key: string]: MathOperationType } = { + "!": factorial, + "**": sqr, +}; + +export const mathOperators: { [key: string]: MathOperationType } = { "*": mul, "/": div, "+": add, "-": minus, + "^": pow, }; -export const mathPriorities: number[] = [1, 2]; +export const mathPriorities: number[] = [0, 1, 2]; -const [FIRST, SECOND] = mathPriorities; +const [ZERO, FIRST, SECOND] = mathPriorities; export const mathOperatorsPriorities: { [key: string]: number } = { + "!": ZERO, + "**": ZERO, "*": FIRST, + "^": FIRST, "/": FIRST, "+": SECOND, "-": SECOND, diff --git a/src/lesson2/parseBrackets.test.ts b/src/lesson2/parseBrackets.test.ts new file mode 100644 index 00000000..1c0226ed --- /dev/null +++ b/src/lesson2/parseBrackets.test.ts @@ -0,0 +1,27 @@ +import { parseBrackets } from "./parseBrackets"; + +describe("parseBrackets correct cases", () => { + it("(1 + 32) * 2", () => { + expect(parseBrackets("(1 + 32) * 2")).toEqual("33 * 2"); + }); + + it("66 / (6 + 2 * 20)", () => { + expect(parseBrackets("66 / (6 + 2 * 20)")).toEqual("66 / 46"); + }); + + it("1 + 32 - 2 + 2", () => { + expect(parseBrackets("(1 + 32) - (2 + 2)")).toEqual("33 - 4"); + }); + + it("1 + (4 * (2 + 2))", () => { + expect(parseBrackets("1 + (4 * (2 + 2))")).toEqual("1 + 16"); + }); +}); + +describe("parseBrackets invalid cases", () => { + it("2 + ((1 + 33 - 2)", () => { + expect(() => parseBrackets("2 + ((1 + 33 - 2)")).toThrow( + RangeError("Invalid string length") + ); + }); +}); \ No newline at end of file diff --git a/src/lesson2/parseBrackets.ts b/src/lesson2/parseBrackets.ts new file mode 100644 index 00000000..1a7ec87e --- /dev/null +++ b/src/lesson2/parseBrackets.ts @@ -0,0 +1,35 @@ +import { parser } from "./parser"; +import { zeroPrioritiesCalc, firstPrioritiesCalc, secondPrioritiesCalc } from "./engine"; + +export const parseBrackets = (line: string): string => { + +var k = 1; + +do { +var openBracket = line.lastIndexOf("("); + if (openBracket < 0) { + k = 0; + } + else { + var closeBracket = line.indexOf(")",openBracket); + var inside = line.slice(openBracket+1,closeBracket); + var parsed = parser(inside) + + if(parsed === null) { + throw new TypeError("Unexpected string"); + } + + var zeroPriorities = zeroPrioritiesCalc(parsed) + var firstPriorities = firstPrioritiesCalc(zeroPriorities) + var secondPriorities = secondPrioritiesCalc(firstPriorities) + + var changed = secondPriorities + + line = line.substr(0, openBracket) + changed.toString() + line.substr(closeBracket + 1); + + } +} while (k==1) + +return line; + +} \ No newline at end of file diff --git a/src/lesson2/parser.test.ts b/src/lesson2/parser.test.ts index 5593e312..d898f0a6 100644 --- a/src/lesson2/parser.test.ts +++ b/src/lesson2/parser.test.ts @@ -12,6 +12,14 @@ describe("Parser correct cases", () => { it("1 + 32 - 2 + 2", () => { expect(parser("1 + 32 - 2 + 2")).toEqual([1, "+", 32, "-", 2, "+", 2]); }); + + it("1 + 32 ! - 2 + 2", () => { + expect(parser("1 + 32 ! - 2 + 2")).toEqual([1, "+", 32, "!", "-", 2, "+", 2]); + }); + + it("1 + 32 - 2 + 2 **", () => { + expect(parser("1 + 32 - 2 + 2 **")).toEqual([1, "+", 32, "-", 2, "+", 2, "**"]); + }); }); describe("Parser invalid cases", () => { @@ -24,4 +32,12 @@ describe("Parser invalid cases", () => { it("1 ! 33 - 2", () => { expect(() => parser("1 ! 33 - 2")).toThrow(TypeError("Unexpected string")); }); + + it("1 ! ++ 33 - 2", () => { + expect(() => parser("1 ! ++ 33 - 2")).toThrow(TypeError("Unexpected string")); + }); + + it("1 ! 33 - 2 /", () => { + expect(() => parser("1 ! 33 - 2 /")).toThrow(TypeError("Unexpected string")); + }); }); diff --git a/src/lesson2/parser.ts b/src/lesson2/parser.ts index 118ff662..aff18bc7 100644 --- a/src/lesson2/parser.ts +++ b/src/lesson2/parser.ts @@ -1,27 +1,41 @@ -import { isNumber } from "./helpers"; -import { mathOperators } from "./mathOperators"; +import { isNumber, isOperator, isUnionOperator } from "./helpers"; +import { mathOperators, +unionOperators } from "./mathOperators"; + +import { parseBrackets } from "./parseBrackets"; export type ParsedLineType = (number | string)[]; export const parser = (line: string): ParsedLineType | null => { - const stack = line.split(" "); + const parsedBrackets = parseBrackets(line) + const stack = parsedBrackets.split(" "); return stack.reduce((result, item, key) => { const prevItem = stack[key - 1]; - const isValidNumberPush = !isNumber(prevItem) && isNumber(item); + const isValidNumberPush = !isNumber(prevItem) && isNumber(item) && !unionOperators.hasOwnProperty(prevItem); const isValidOperatorPush = - isNumber(prevItem) && + (isNumber(prevItem) || isUnionOperator(prevItem)) && + !isNumber(item) && + mathOperators.hasOwnProperty(item) && + isOperator(item); + + const isValidUnionOperatorPush = + (isNumber(prevItem) || isUnionOperator(prevItem)) && !isNumber(item) && - mathOperators.hasOwnProperty(item); + unionOperators.hasOwnProperty(item) && + isUnionOperator(item); if (isValidNumberPush) { result.push(Number(item)); } else if (isValidOperatorPush) { result.push(item); + } else if(isValidUnionOperatorPush) { + result.push(item); } else { throw new TypeError("Unexpected string"); } + return result; }, []); -}; +}; \ No newline at end of file diff --git a/src/lesson2/runner.test.ts b/src/lesson2/runner.test.ts index 1318dce3..e78d41f6 100644 --- a/src/lesson2/runner.test.ts +++ b/src/lesson2/runner.test.ts @@ -12,6 +12,54 @@ describe("Runner simple cases", () => { it("2 + 32", () => { expect(runner("2 + 32")).toEqual(34); }); + + it("5 + 32", () => { + expect(runner("5 + 32")).toEqual(37); + }); + + it("2 - 32", () => { + expect(runner("2 - 32")).toEqual(-30); + }); + + it("25 - 5", () => { + expect(runner("25 - 5")).toEqual(20); + }); + + it("2 / 2", () => { + expect(runner("2 / 2")).toEqual(1); + }); + + it("12 / 3", () => { + expect(runner("12 / 3")).toEqual(4); + }); + + it("2 ^ 2", () => { + expect(runner("2 ^ 2")).toEqual(4); + }); + + it("5 ^ 2", () => { + expect(runner("5 ^ 2")).toEqual(25); + }); + + it("5 ! + 4", () => { + expect(runner("5 ! + 4")).toEqual(124); + }); + + it("3 ! + 4", () => { + expect(runner("3 ! + 4")).toEqual(10); + }); + + it("5 ! * 2", () => { + expect(runner("5 ! * 2")).toEqual(240); + }); + + it("5 ! / 2", () => { + expect(runner("5 ! / 2")).toEqual(60); + }); + + it("5 ! / 4", () => { + expect(runner("5 ! / 4")).toEqual(30); + }); }); describe("Runner tripled/mixed cases", () => { @@ -26,6 +74,22 @@ describe("Runner tripled/mixed cases", () => { it("2 + 2 * 3", () => { expect(runner("2 + 2 * 3")).toEqual(8); }); + + it("2 + 2 / 2", () => { + expect(runner("2 + 2 / 2")).toEqual(3); + }); + + it("2 + 2 + 5 !", () => { + expect(runner("2 + 2 + 5 !")).toEqual(124); + }); + + it("2 ^ 2 / 4", () => { + expect(runner("2 ^ 2 / 4")).toEqual(1); + }); + + it("2 ** + 2", () => { + expect(runner("2 ** + 2")).toEqual(6); + }); }); describe("Runner long cases", () => { @@ -36,4 +100,12 @@ describe("Runner long cases", () => { it("20 - 10 * 10 / 5 - 3", () => { expect(runner("20 - 10 * 10 / 5 - 3")).toEqual(-3); }); + + it("10 - 5 ** + 10 / 5 - 3", () => { + expect(runner("10 - 5 ** + 10 / 5 - 3")).toEqual(-16); + }); + + it("5 ! - 10 * 10 / 5 - 3", () => { + expect(runner("5 ! - 10 * 10 / 5 - 3")).toEqual(97); + }); }); diff --git a/src/lesson2/runner.ts b/src/lesson2/runner.ts index 920249fd..7eb2f4ba 100644 --- a/src/lesson2/runner.ts +++ b/src/lesson2/runner.ts @@ -1,6 +1,6 @@ import { parser } from "./parser"; -import { firstPrioritiesCalc, secondPrioritiesCalc } from "./engine"; +import { zeroPrioritiesCalc, firstPrioritiesCalc, secondPrioritiesCalc } from "./engine"; export const runner = (line: string): number => { const stack = parser(line); @@ -9,11 +9,18 @@ export const runner = (line: string): number => { throw new TypeError("Unexpected string"); } - const firstPrioritiesRes = firstPrioritiesCalc(stack); + const zeroPrioritiesRes = zeroPrioritiesCalc(stack); + + if (zeroPrioritiesRes.length === 1) { + return Number(zeroPrioritiesRes[0]); + } + + const firstPrioritiesRes = firstPrioritiesCalc(zeroPrioritiesRes); if (firstPrioritiesRes.length === 1) { return Number(firstPrioritiesRes[0]); } + return secondPrioritiesCalc(firstPrioritiesRes); };