├── docs
├── .nojekyll
├── favicon.ico
├── _next
│ └── static
│ │ ├── EH0gDrrN2QaEVGdrrTGQZ
│ │ ├── _ssgManifest.js
│ │ └── _buildManifest.js
│ │ ├── chunks
│ │ ├── pages
│ │ │ ├── _error-7397496ca01950b1.js
│ │ │ └── _app-3b3ea252cf13885f.js
│ │ └── webpack-8d2153bfd274df32.js
│ │ └── css
│ │ └── 9af00fe6c25f953e.css
├── 404.html
└── index.html
├── styles
├── common
│ ├── layout.scss
│ ├── anims.scss
│ └── common.scss
├── key.module.scss
├── sys-keys.module.scss
├── index.module.scss
├── basic-keys.module.scss
├── func-keys.module.scss
├── panel.module.scss
└── screen.module.scss
├── public
└── favicon.ico
├── README.md
├── test
├── math-deprecated
│ ├── tests-deprecated
│ ├── fact-test.ts0
│ ├── ncr-npr-test.ts0
│ ├── algorithm-test.ts0
│ ├── dec-test.ts0
│ ├── degree-test.ts0
│ └── frac-test.ts0
├── preprocess-test.ts
├── test-core
│ └── test-core.ts
├── parse-test.ts
└── calculate-test.ts
├── .eslintrc.json
├── .prettierrc.json
├── test.bat
├── pages
├── _app.tsx
├── index.tsx
└── _document.tsx
├── modules
├── math
│ ├── constants.ts
│ ├── calculations
│ │ ├── fact.ts
│ │ └── ncr-npr.ts
│ ├── algorithm.ts
│ ├── degree-conversions.ts
│ └── value-type-basics
│ │ ├── dec-basics.ts
│ │ ├── frac-basics.ts
│ │ └── degree-basics.ts
└── calc-core
│ ├── objs
│ ├── lexem.ts
│ ├── types.ts
│ ├── internal-number.ts
│ ├── check-fns.ts
│ └── operators.ts
│ ├── calculate.ts
│ └── preprocessing.ts
├── next.config.js
├── clean.bat
├── .gitignore
├── components
├── panel.tsx
├── key.tsx
├── sys-keys.tsx
├── screen.tsx
├── basic-keys.tsx
└── func-keys.tsx
├── tsconfig.json
├── package.json
├── LICENSE
├── logics
├── screen.ts
├── sys-keys.ts
├── index.ts
├── basic-keys.ts
└── func-keys.ts
└── observables
├── calculator-memory.ts
├── strings-res.ts
└── calculator-state.ts
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/styles/common/layout.scss:
--------------------------------------------------------------------------------
1 | $z-index-main: 600;
2 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ErnestThePoet/ec-82-ms/HEAD/docs/favicon.ico
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ErnestThePoet/ec-82-ms/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 在线科学计算器 Ernest's Online Scientific Calculator EC-82MS
2 | 核心实现介绍:https://zhuanlan.zhihu.com/p/596644979
--------------------------------------------------------------------------------
/test/math-deprecated/tests-deprecated:
--------------------------------------------------------------------------------
1 | tests are performed prior to the Decimal.js upgrade and is therefore deprecated.
--------------------------------------------------------------------------------
/docs/_next/static/EH0gDrrN2QaEVGdrrTGQZ/_ssgManifest.js:
--------------------------------------------------------------------------------
1 | self.__SSG_MANIFEST=new Set,self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals",
3 | "rules": {
4 | "@next/next/no-img-element": "off"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "arrowParens": "avoid",
4 | "bracketSameLine": true,
5 | "singleQuote": false,
6 | "semi": true,
7 | "trailingComma": "none"
8 | }
9 |
--------------------------------------------------------------------------------
/test.bat:
--------------------------------------------------------------------------------
1 | :: This script automatically compile and run a single .ts test.
2 | :: Usage: ./test xxx
3 | call tsc test/%1%-test.ts
4 | node --trace-uncaught test/%1%-test.js
5 | call clean>nul 2>nul
--------------------------------------------------------------------------------
/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import type { AppProps } from "next/app";
2 |
3 | function MyApp({ Component, pageProps }: AppProps) {
4 | return ;
5 | }
6 |
7 | export default MyApp;
8 |
--------------------------------------------------------------------------------
/modules/math/constants.ts:
--------------------------------------------------------------------------------
1 | export const E =
2 | "2.71828_18284_59045_23536_02874_71352_66249_77572_47093_69996";
3 | export const PI =
4 | "3.14159_26535_89793_23846_26433_83279_50288_41971_69399_37511";
5 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | swcMinify: true,
5 | basePath: "/ec-82-ms",
6 | assetPrefix: "/ec-82-ms"
7 | }
8 |
9 | module.exports = nextConfig
10 |
--------------------------------------------------------------------------------
/styles/key.module.scss:
--------------------------------------------------------------------------------
1 | .divKeyWrapper {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 |
6 | > div:nth-child(1) {
7 | position: absolute;
8 | transform: translateY(-100%);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/clean.bat:
--------------------------------------------------------------------------------
1 | :: This script cleans up all js files in modules folder which are generated during tests.
2 | echo ========== CLEAN UP ==========
3 | cd modules
4 | del /a /f /s /q "*.js"
5 | cd ../observables
6 | del /a /f /s /q "*.js"
7 | cd ../test
8 | del /a /f /s /q "*.js"
--------------------------------------------------------------------------------
/modules/math/calculations/fact.ts:
--------------------------------------------------------------------------------
1 | import Decimal from "decimal.js";
2 |
3 | export function fact(x: Decimal): Decimal {
4 | let res = new Decimal(1);
5 | for (let i = new Decimal(1); i.lte(x); i = i.add(1)) {
6 | res = res.mul(i);
7 | }
8 | return res;
9 | }
10 |
--------------------------------------------------------------------------------
/docs/_next/static/chunks/pages/_error-7397496ca01950b1.js:
--------------------------------------------------------------------------------
1 | (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[820],{1981:function(a,b,c){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_error",function(){return c(9185)}])}},function(a){a.O(0,[774,888,179],function(){var b;return a(a.s=1981)}),_N_E=a.O()}])
--------------------------------------------------------------------------------
/modules/calc-core/objs/lexem.ts:
--------------------------------------------------------------------------------
1 | import type { Operator } from "./operators";
2 | import { InternalNumber } from "./internal-number";
3 |
4 | type LexemType = "OP" | "NBR";
5 | type LexemObj = Operator | InternalNumber;
6 |
7 | export interface Lexem {
8 | type: LexemType;
9 | obj: LexemObj;
10 | }
11 |
--------------------------------------------------------------------------------
/styles/sys-keys.module.scss:
--------------------------------------------------------------------------------
1 | .divSysKeys {
2 | width: 100%;
3 | height: 6%;
4 |
5 | display: flex;
6 | justify-content: space-between;
7 |
8 | > div:not(:last-child) {
9 | width: 35%;
10 | height: 100%;
11 | display: flex;
12 | justify-content: space-evenly;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/styles/index.module.scss:
--------------------------------------------------------------------------------
1 | @import "./common/layout";
2 |
3 | .mainMain {
4 | position: fixed;
5 | top: 0;
6 | left: 0;
7 | width: 100%;
8 | height: 100%;
9 | z-index: $z-index-main;
10 |
11 | display: flex;
12 | justify-content: center;
13 |
14 | font-family: "Ubuntu", sans-serif;
15 |
16 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
17 | }
18 |
--------------------------------------------------------------------------------
/test/math-deprecated/fact-test.ts0:
--------------------------------------------------------------------------------
1 | import { assertEquals, runTests } from "../test-core/test-core";
2 | import { fact } from "../../modules/math/calculations/fact";
3 |
4 | function test() {
5 | assertEquals(1, fact(0));
6 | assertEquals(1, fact(1));
7 | assertEquals(2, fact(2));
8 | assertEquals(720, fact(6));
9 | assertEquals(6227020800, fact(13));
10 | }
11 |
12 | runTests(test);
--------------------------------------------------------------------------------
/docs/_next/static/EH0gDrrN2QaEVGdrrTGQZ/_buildManifest.js:
--------------------------------------------------------------------------------
1 | self.__BUILD_MANIFEST={__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":["static/chunks/e82996df-c7170adf15c45c28.js","static/chunks/414-a53dbdc98f11ce0b.js","static/css/9af00fe6c25f953e.css","static/chunks/pages/index-7846f9ab06000802.js"],"/_error":["static/chunks/pages/_error-7397496ca01950b1.js"],sortedPages:["/","/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB()
--------------------------------------------------------------------------------
/modules/math/calculations/ncr-npr.ts:
--------------------------------------------------------------------------------
1 | import Decimal from "decimal.js";
2 | import { fact } from "./fact";
3 |
4 | export function nPr(n: Decimal, r: Decimal): Decimal {
5 | let res = new Decimal(1);
6 | for (let i = n; i.gt(Decimal.sub(n, r)); i = i.sub(1)) {
7 | res = res.mul(i);
8 | }
9 | return res;
10 | }
11 |
12 | export function nCr(n: Decimal, r: Decimal): Decimal {
13 | return nPr(n, r).div(fact(r));
14 | }
15 |
--------------------------------------------------------------------------------
/styles/basic-keys.module.scss:
--------------------------------------------------------------------------------
1 | .divBasicKeys {
2 | box-sizing: border-box;
3 | width: 100%;
4 | height: 33%;
5 |
6 | padding: 12px 15px 15px 15px;
7 |
8 | display: flex;
9 | flex-direction: column;
10 | justify-content: space-between;
11 |
12 | > div {
13 | display: flex;
14 | justify-content: space-between;
15 | align-items: flex-end;
16 |
17 | height: 18%;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/styles/common/anims.scss:
--------------------------------------------------------------------------------
1 | @keyframes insert-course {
2 | from {
3 | border-left: 1px solid black;
4 | }
5 |
6 | 50% {
7 | border-left: 1px solid transparent;
8 | }
9 |
10 | to {
11 | border-left: 1px solid black;
12 | }
13 | }
14 |
15 | @keyframes overwrite-course {
16 | from {
17 | border-bottom: 1px solid black;
18 | }
19 |
20 | 50% {
21 | border-bottom: 1px solid transparent;
22 | }
23 |
24 | to {
25 | border-bottom: 1px solid black;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 | /.VSCodeCounter
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 | .pnpm-debug.log*
28 |
29 | # local env files
30 | .env*.local
31 |
32 | # vercel
33 | .vercel
34 |
35 | # typescript
36 | *.tsbuildinfo
37 | next-env.d.ts
38 |
--------------------------------------------------------------------------------
/components/panel.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styles from "../styles/panel.module.scss";
3 |
4 | import Screen from "./screen";
5 | import SysKeys from "./sys-keys";
6 | import FuncKeys from "./func-keys";
7 | import BasicKeys from "./basic-keys";
8 |
9 | export default class Panel extends React.Component {
10 | constructor(props: {}) {
11 | super(props);
12 | }
13 |
14 | render = () => (
15 |
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": false,
6 | "noImplicitAny": true,
7 | "skipLibCheck": true,
8 | "strict": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "noEmit": true,
11 | "esModuleInterop": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "jsx": "preserve",
17 | "incremental": true
18 | },
19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
20 | "exclude": ["node_modules"]
21 | }
22 |
--------------------------------------------------------------------------------
/modules/math/algorithm.ts:
--------------------------------------------------------------------------------
1 | import Decimal from "decimal.js";
2 | // both m and n needs to be integers.
3 | // always return positive or 0.
4 | export function gcd(m: Decimal, n: Decimal): Decimal {
5 | m = m.abs();
6 | n = n.abs();
7 |
8 | while (n.gt(0)) {
9 | const t = m.mod(n);
10 | m = n;
11 | n = t;
12 | }
13 | return m;
14 | }
15 |
16 | // always return positive or 0.
17 | export function lcm(m: Decimal, n: Decimal): Decimal {
18 | if (m.isZero() && n.isZero()) {
19 | return new Decimal(0);
20 | }
21 |
22 | m = m.abs();
23 | n = n.abs();
24 |
25 | return m.div(gcd(m, n)).mul(n);
26 | }
27 |
--------------------------------------------------------------------------------
/styles/common/common.scss:
--------------------------------------------------------------------------------
1 | @mixin slim-scroll-bar {
2 | &::-webkit-scrollbar {
3 | width: 5px;
4 | height: 5px;
5 | }
6 |
7 | &::-webkit-scrollbar-thumb {
8 | border-radius: 3px;
9 | background-color: rgb(180, 180, 180);
10 | //box-shadow: 0 2px 3px 0px #9B9B9B;
11 | }
12 |
13 | &::-webkit-scrollbar-thumb:hover {
14 | background-color: rgb(190, 190, 190);
15 | }
16 |
17 | &::-webkit-scrollbar-thumb:active {
18 | background-color: rgb(200, 200, 200);
19 | }
20 |
21 | &::-webkit-scrollbar-track {
22 | background-color: rgb(230, 230, 230);
23 | border-radius: 3px;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/modules/calc-core/objs/types.ts:
--------------------------------------------------------------------------------
1 | import Decimal from "decimal.js";
2 | import { InternalNumber } from "./internal-number";
3 | // common types
4 |
5 | // RI:
6 | // u and d are integers.
7 | // d is always positive.
8 | export interface FracValue {
9 | u: Decimal;
10 | d: Decimal;
11 | }
12 |
13 | // RI:
14 | // d and m are [0,59] integers.
15 | // s is [0,60).
16 | export interface DegreeValue {
17 | d: Decimal;
18 | m: Decimal;
19 | s: Decimal;
20 | neg: boolean;
21 | }
22 |
23 | export interface OperationFn {
24 | (...operands: InternalNumber[]): InternalNumber;
25 | }
26 |
27 | export interface CheckFn {
28 | (...operands: InternalNumber[]): { ok: boolean; msg: string };
29 | }
30 |
--------------------------------------------------------------------------------
/styles/func-keys.module.scss:
--------------------------------------------------------------------------------
1 | .divFuncKeys {
2 | box-sizing: border-box;
3 | width: 100%;
4 | height: 27%;
5 | padding: 10px 15px;
6 |
7 | display: flex;
8 | flex-direction: column;
9 | justify-content: space-between;
10 |
11 | > div:first-child {
12 | display: flex;
13 | justify-content: space-between;
14 | height: 16%;
15 |
16 | > div {
17 | width: 31%;
18 | display: flex;
19 | justify-content: space-between;
20 | }
21 | }
22 |
23 | > div:not(:first-child) {
24 | display: flex;
25 | justify-content: space-between;
26 | align-items: flex-end;
27 |
28 | height: 16%;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/components/key.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styles from "../styles/key.module.scss";
3 |
4 | interface KeyProps {
5 | role: "ksys" | "kfunc" | "kfuncr1" | "kbasic" | "kbasicy";
6 | content?: any;
7 | upperContent?: any;
8 | onClick: () => void;
9 | }
10 |
11 | export default class Key extends React.Component {
12 | constructor(props: KeyProps) {
13 | super(props);
14 | }
15 |
16 | render = () => (
17 | this.props.onClick()}>
21 |
{this.props.upperContent}
22 |
23 |
{this.props.content}
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Head from "next/head";
3 | import styles from "../styles/index.module.scss";
4 | import * as L from "../logics/index";
5 | import stringsRes from "../observables/strings-res";
6 |
7 | import Panel from "../components/panel";
8 |
9 | class Home extends React.Component {
10 | constructor(props: any) {
11 | super(props);
12 | }
13 |
14 | componentDidMount = () => {
15 | L.initialize();
16 | };
17 |
18 | render = () => (
19 |
20 |
21 |
{stringsRes.strings.APP_TITLE}
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | }
30 |
31 | export default Home;
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ec-82ms",
3 | "version": "1.9.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "export": "next build && next export -o docs && touch docs/.nojekyll",
9 | "start": "next start",
10 | "lint": "next lint"
11 | },
12 | "dependencies": {
13 | "decimal.js": "^10.4.0",
14 | "gsap": "^3.10.4",
15 | "mobx": "^6.6.1",
16 | "mobx-react-lite": "^3.4.0",
17 | "next": "12.2.4",
18 | "react": "18.2.0",
19 | "react-dom": "18.2.0",
20 | "sass": "^1.54.3"
21 | },
22 | "devDependencies": {
23 | "@types/node": "18.6.4",
24 | "@types/react": "18.0.16",
25 | "@types/react-dom": "18.0.6",
26 | "eslint": "8.21.0",
27 | "eslint-config-next": "12.2.4",
28 | "typescript": "4.7.4"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/test/preprocess-test.ts:
--------------------------------------------------------------------------------
1 | import { KeyEntry, KEY_ENTRIES } from "../modules/calc-core/objs/key-entry";
2 | import { preprocess } from "../modules/calc-core/preprocessing";
3 |
4 | function test() {
5 | // 3*(5+3sin(-5.5--6
6 | const ke1: KeyEntry[] = [
7 | KEY_ENTRIES.n3,
8 | KEY_ENTRIES.mul,
9 | KEY_ENTRIES.lBracket,
10 | KEY_ENTRIES.n5,
11 | KEY_ENTRIES.add,
12 | KEY_ENTRIES.n3,
13 | KEY_ENTRIES.sin,
14 | KEY_ENTRIES.sub,
15 | KEY_ENTRIES.n5,
16 | KEY_ENTRIES.nDot,
17 | KEY_ENTRIES.n5,
18 | KEY_ENTRIES.sub,
19 | KEY_ENTRIES.sub,
20 | KEY_ENTRIES.n6
21 | ];
22 |
23 | console.log(
24 | preprocess(ke1)
25 | .map(x => x.id)
26 | .join(" ")
27 | );
28 | }
29 |
30 | test();
31 |
--------------------------------------------------------------------------------
/test/math-deprecated/ncr-npr-test.ts0:
--------------------------------------------------------------------------------
1 | import { assertEquals, runTests } from "../test-core/test-core";
2 | import { nCr, nPr } from "../../modules/math/calculations/ncr-npr";
3 |
4 | function testnPr() {
5 | assertEquals(1, nPr(0, 0));
6 | assertEquals(1, nPr(1, 0));
7 | assertEquals(1, nPr(5, 0));
8 | assertEquals(1, nPr(1, 1));
9 | assertEquals(5, nPr(5, 1));
10 | assertEquals(20, nPr(5, 2));
11 | assertEquals(60, nPr(5, 3));
12 | assertEquals(120, nPr(5, 4));
13 | assertEquals(120, nPr(5, 5));
14 | }
15 |
16 | function testnCr() {
17 | assertEquals(1, nCr(0, 0));
18 | assertEquals(1, nCr(1, 0));
19 | assertEquals(1, nCr(5, 0));
20 | assertEquals(1, nCr(1, 1));
21 | assertEquals(5, nCr(5, 1));
22 | assertEquals(10, nCr(5, 2));
23 | assertEquals(10, nCr(5, 3));
24 | assertEquals(5, nCr(5, 4));
25 | assertEquals(1, nCr(5, 5));
26 | }
27 |
28 | runTests(testnPr,testnCr);
--------------------------------------------------------------------------------
/docs/_next/static/chunks/pages/_app-3b3ea252cf13885f.js:
--------------------------------------------------------------------------------
1 | (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[888],{6840:function(a,b,c){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_app",function(){return c(5656)}])},5656:function(a,b,c){"use strict";c.r(b);var d=c(1799),e=c(5893);b.default=function(a){var b=a.Component,c=a.pageProps;return(0,e.jsx)(b,(0,d.Z)({},c))}},1799:function(a,b,c){"use strict";function d(a,b,c){return b in a?Object.defineProperty(a,b,{value:c,enumerable:!0,configurable:!0,writable:!0}):a[b]=c,a}function e(a){for(var b=1;b
18 | {/*This Head element affects all pages.*/}
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
29 |
30 |