├── 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 | 31 | ); 32 | } 33 | } 34 | 35 | export default MyDocument; 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ernest Cui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /modules/math/degree-conversions.ts: -------------------------------------------------------------------------------- 1 | import Decimal from "decimal.js"; 2 | import calculatorState from "../../observables/calculator-state"; 3 | import * as C from "../math/constants"; 4 | 5 | export function degreeToRad(degree: Decimal): Decimal { 6 | return new Decimal(C.PI).mul(degree).div(180); 7 | } 8 | 9 | export function gradeToRad(grade: Decimal): Decimal { 10 | return new Decimal(C.PI).mul(grade).div(200); 11 | } 12 | 13 | export function radToDegree(rad: Decimal): Decimal { 14 | return new Decimal(180).mul(rad).div(C.PI); 15 | } 16 | 17 | export function radToGrade(rad: Decimal): Decimal { 18 | return new Decimal(200).mul(rad).div(C.PI); 19 | } 20 | 21 | export function fromCurrentDegreeToRad(x: Decimal) { 22 | switch (calculatorState.drgMode) { 23 | case "D": 24 | x = degreeToRad(x); 25 | break; 26 | case "G": 27 | x = gradeToRad(x); 28 | break; 29 | } 30 | return x; 31 | } 32 | 33 | export function toCurrentDegreeFromRad(x: Decimal) { 34 | switch (calculatorState.drgMode) { 35 | case "D": 36 | x = radToDegree(x); 37 | break; 38 | case "G": 39 | x = radToGrade(x); 40 | break; 41 | } 42 | return x; 43 | } 44 | -------------------------------------------------------------------------------- /logics/screen.ts: -------------------------------------------------------------------------------- 1 | import cs, { CalculatorDRGMode } from "../observables/calculator-state"; 2 | import cm from "../observables/calculator-memory"; 3 | import { InternalNumber } from "../modules/calc-core/objs/internal-number"; 4 | import Decimal from "decimal.js"; 5 | import sr, { LangType } from "../observables/strings-res"; 6 | 7 | export const onKeyEntryImgClick = (index: number) => { 8 | cs.setDisplayMode("NORMAL_EDIT"); 9 | cs.setCursorIndex(index); 10 | }; 11 | 12 | export const onDrgClick = (drg: CalculatorDRGMode) => { 13 | cs.setDRGMode(drg); 14 | cs.setDisplayMode("NORMAL_EDIT"); 15 | }; 16 | 17 | export const onClearClick = (mode: 0 | 1 | 2) => { 18 | switch (mode) { 19 | case 2: 20 | cs.setDRGMode("D"); 21 | cs.isInsert = true; 22 | case 0: 23 | cm.clear(); 24 | cs.entries = []; 25 | cs.historyEntries = []; 26 | cs.calcResult = new InternalNumber("DEC", new Decimal(0)); 27 | cs.dispResult = new InternalNumber("DEC", new Decimal(0)); 28 | cs.setEntryIndex(0); 29 | cs.setCursorIndex(0); 30 | break; 31 | case 1: 32 | cs.setDRGMode("D"); 33 | cs.isInsert = true; 34 | break; 35 | } 36 | 37 | cs.setDisplayMode("NORMAL_EDIT"); 38 | }; 39 | 40 | export const onLangClick = (lang: LangType) => { 41 | sr.switchLanguage(lang); 42 | cs.setDisplayMode("NORMAL_EDIT"); 43 | }; 44 | -------------------------------------------------------------------------------- /docs/_next/static/chunks/webpack-8d2153bfd274df32.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";var a,b,c,d,e={},f={};function g(a){var b=f[a];if(void 0!==b)return b.exports;var c=f[a]={exports:{}},d=!0;try{e[a](c,c.exports,g),d=!1}finally{d&&delete f[a]}return c.exports}g.m=e,a=[],g.O=function(b,c,d,e){if(c){e=e||0;for(var f=a.length;f>0&&a[f-1][2]>e;f--)a[f]=a[f-1];a[f]=[c,d,e];return}for(var h=1/0,f=0;f=e&&Object.keys(g.O).every(function(a){return g.O[a](c[j])})?c.splice(j--,1):(i=!1,ea)[i]; 24 | const bValue = (b)[i]; 25 | 26 | if (typeof aValue !== typeof bValue) { 27 | return false; 28 | } 29 | 30 | // now a,b has the common member of same type. 31 | 32 | if (typeof aValue === "object") { 33 | if (!isObjectEqual(aValue, bValue)) { 34 | return false; 35 | } 36 | } else { 37 | if (aValue !== bValue) { 38 | return false; 39 | } 40 | } 41 | } 42 | 43 | return true; 44 | } 45 | 46 | export function assertObjectEquals(expected: object, actual: object) { 47 | if (!isObjectEqual(expected, actual)) { 48 | throw ( 49 | `Test Failed:\n*** Expected:` + 50 | `${JSON.stringify(expected)}\n*** Actual: ` + 51 | `${JSON.stringify(actual)}` 52 | ); 53 | } 54 | } 55 | 56 | export function runTests(...testFns: Array<() => void>) { 57 | console.log(`Test Start - total ${testFns.length} tests`); 58 | console.log("======================"); 59 | 60 | for (let i = 0; i < testFns.length; i++) { 61 | testFns[i](); 62 | console.log(`TestFn ${i} passed.`); 63 | } 64 | 65 | console.log("======================"); 66 | console.log("Test Complete"); 67 | } 68 | -------------------------------------------------------------------------------- /test/math-deprecated/dec-test.ts0: -------------------------------------------------------------------------------- 1 | import { assertEquals, assertObjectEquals, runTests } from "../test-core/test-core"; 2 | import * as DB from "../../modules/math/value-type-basics/dec-basics"; 3 | 4 | function testTryToFracValue() { 5 | assertObjectEquals({ ok: true, frac: { u: 0, d: 1 } }, 6 | DB.tryToFracValue(0)); 7 | 8 | assertObjectEquals({ ok: true, frac: { u: 1, d: 1 } }, 9 | DB.tryToFracValue(1)); 10 | 11 | assertObjectEquals({ ok: true, frac: { u: -1, d: 1 } }, 12 | DB.tryToFracValue(-1)); 13 | 14 | assertObjectEquals({ ok: true, frac: { u: 2, d: 1 } }, 15 | DB.tryToFracValue(2)); 16 | 17 | assertObjectEquals({ ok: true, frac: { u: -1, d: 2 } }, 18 | DB.tryToFracValue(-0.5)); 19 | 20 | assertObjectEquals({ ok: true, frac: { u: 1, d: 256 } }, 21 | DB.tryToFracValue(0.00390625)); 22 | 23 | assertObjectEquals({ ok: true, frac: { u: 333_333, d: 1000000 } }, 24 | DB.tryToFracValue(0.333_333)); 25 | 26 | assertObjectEquals({ ok: true, frac: { u: 333_333_333_333, d: 1_000_000_000_000 } }, 27 | DB.tryToFracValue(0.333_333_333_333)); 28 | 29 | assertObjectEquals({ ok: true, frac: { u: -333_333_333_333, d: 1_000_000_000_000 } }, 30 | DB.tryToFracValue(-0.333_333_333_333)); 31 | 32 | assertObjectEquals({ ok: false }, 33 | DB.tryToFracValue(0.3_333_333_333_333)); 34 | 35 | assertObjectEquals({ ok: true, frac: { u: 1_333_333_333_333, d: 1_000_000 } }, 36 | DB.tryToFracValue(1_333_333.333_333)); 37 | 38 | assertObjectEquals({ ok: true, frac: { u: -1_333_333_333_333, d: 1_000_000 } }, 39 | DB.tryToFracValue(-1_333_333.333_333)); 40 | 41 | assertObjectEquals({ ok: false }, 42 | DB.tryToFracValue(1_333_333.3_333_333)); 43 | 44 | assertObjectEquals({ ok: true, frac: { u: 1_333_333_333_333, d: 10 } }, 45 | DB.tryToFracValue(133_333_333_333.3)); 46 | 47 | assertObjectEquals({ ok: false }, 48 | DB.tryToFracValue(1_333_333_333_333.3)); 49 | 50 | assertObjectEquals({ ok: true, frac: { u: 1_333_333_333_333, d: 1 } }, 51 | DB.tryToFracValue(1_333_333_333_333)); 52 | } 53 | 54 | runTests(testTryToFracValue); -------------------------------------------------------------------------------- /components/sys-keys.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "../styles/sys-keys.module.scss"; 3 | import Key from "./key"; 4 | import * as L from "../logics/sys-keys"; 5 | 6 | export default class SysKeys extends React.Component { 7 | constructor(props: {}) { 8 | super(props); 9 | } 10 | 11 | render = () => ( 12 |
13 |
14 | SHIFT} 17 | onClick={() => { 18 | L.onShiftClick(); 19 | }}> 20 | ALPHA} 23 | onClick={() => { 24 | L.onAlphaClick(); 25 | }}> 26 |
27 | 28 |
29 | MODE CLR} 32 | onClick={() => { 33 | L.onModeClrClick(); 34 | }}> 35 | LANG} 38 | onClick={() => { 39 | L.onLangClick(); 40 | }}> 41 |
42 | 43 |
44 | { 46 | L.onDirClick("U"); 47 | }}> 48 | ▲ 49 | 50 | { 52 | L.onDirClick("L"); 53 | }}> 54 | ▲ 55 | 56 | { 58 | L.onDirClick("D"); 59 | }}> 60 | ▲ 61 | 62 | { 64 | L.onDirClick("R"); 65 | }}> 66 | ▲ 67 | 68 |
69 |
70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 404: This page could not be found

404

This page could not be found.

-------------------------------------------------------------------------------- /modules/calc-core/calculate.ts: -------------------------------------------------------------------------------- 1 | import { Lexem } from "./objs/lexem"; 2 | import { InternalNumber } from "./objs/internal-number"; 3 | import { Operator } from "./objs/operators"; 4 | import stringRes from "../../observables/strings-res"; 5 | 6 | interface CalculateResult { 7 | success: boolean; 8 | msg: string; 9 | result?: InternalNumber; 10 | } 11 | 12 | export function calculate(lexems: Lexem[]): CalculateResult { 13 | const operands: InternalNumber[] = []; 14 | 15 | for (let i = 0; i < lexems.length; i++) { 16 | if (lexems[i].type === "NBR") { 17 | operands.push(lexems[i].obj as InternalNumber); 18 | } else { 19 | const op: Operator = lexems[i].obj as Operator; 20 | // check remaining arg count 21 | if (operands.length < op.argN) { 22 | return { 23 | success: false, 24 | msg: stringRes.strings.CALC_CK_ERROR_MSGS 25 | .INSUFFICENT_OPERANDS 26 | }; 27 | } 28 | 29 | // check validaty then calculate 30 | switch (op.argN) { 31 | case 1: 32 | const opr11: InternalNumber = operands.pop()!; 33 | const ckResult1 = op.ck(opr11); 34 | if (!ckResult1.ok) { 35 | return { 36 | success: false, 37 | msg: ckResult1.msg 38 | }; 39 | } 40 | operands.push(op.op(opr11)); 41 | break; 42 | case 2: 43 | const opr22: InternalNumber = operands.pop()!; 44 | const opr21: InternalNumber = operands.pop()!; 45 | const ckResult2 = op.ck(opr21, opr22); 46 | if (!ckResult2.ok) { 47 | return { 48 | success: false, 49 | msg: ckResult2.msg 50 | }; 51 | } 52 | operands.push(op.op(opr21, opr22)); 53 | break; 54 | case 3: 55 | const opr33: InternalNumber = operands.pop()!; 56 | const opr32: InternalNumber = operands.pop()!; 57 | const opr31: InternalNumber = operands.pop()!; 58 | const ckResult3 = op.ck(opr31, opr32, opr33); 59 | if (!ckResult3.ok) { 60 | return { 61 | success: false, 62 | msg: ckResult3.msg 63 | }; 64 | } 65 | operands.push(op.op(opr31, opr32, opr33)); 66 | break; 67 | } 68 | } 69 | } 70 | 71 | if (operands.length !== 1) { 72 | return { 73 | success: false, 74 | msg: stringRes.strings.CALC_CK_ERROR_MSGS.OPERAND_STACK 75 | }; 76 | } 77 | 78 | return { 79 | success: true, 80 | msg: "", 81 | result: operands.pop()! 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /modules/math/value-type-basics/frac-basics.ts: -------------------------------------------------------------------------------- 1 | import Decimal from "decimal.js"; 2 | import type { FracValue, DegreeValue } from "../../calc-core/objs/types"; 3 | import { gcd, lcm } from "../algorithm"; 4 | import * as DB from "./dec-basics"; 5 | import * as DGB from "./degree-basics"; 6 | 7 | export function toDecValue(x: FracValue): Decimal { 8 | return x.u.div(x.d); 9 | } 10 | 11 | export function toDegreeValue(x: FracValue): DegreeValue { 12 | return DB.toDegreeValue(toDecValue(x)); 13 | } 14 | 15 | // u and d follow same RI as FracValue except u and d may not be int. 16 | export function fromTerminatingDiv(u: Decimal, d: Decimal): FracValue { 17 | const isNegative = (u.lt(0) && d.gt(0)) || (u.gt(0) && d.lt(0)); 18 | 19 | u = u.abs(); 20 | d = d.abs(); 21 | 22 | const uStrSplitted = u.toString().split("."); 23 | const dStrSplitted = d.toString().split("."); 24 | 25 | const tensToMul = Math.max( 26 | (uStrSplitted[1] ?? "").length, 27 | (dStrSplitted[1] ?? "").length 28 | ); 29 | 30 | u = u.mul(Decimal.pow(10, tensToMul)); 31 | d = d.mul(Decimal.pow(10, tensToMul)); 32 | 33 | return reduce({ u: isNegative ? u.neg() : u, d }); 34 | } 35 | 36 | export function reduce(x: FracValue): FracValue { 37 | const fracGcd = gcd(x.u, x.d); 38 | return { 39 | u: x.u.div(fracGcd), 40 | d: x.d.div(fracGcd) 41 | }; 42 | } 43 | 44 | // x must not evaluate to 0. 45 | export function invert(x: FracValue): FracValue { 46 | return { 47 | u: x.u.lt(0) ? x.d.abs().neg() : x.d.abs(), 48 | d: x.u.abs() 49 | }; 50 | } 51 | 52 | ///////////////////// Operations with frac ///////////////////// 53 | export function addFrac(x: FracValue, y: FracValue): FracValue { 54 | const fracLcm = lcm(x.d, y.d); 55 | const sum: FracValue = { 56 | u: x.u.mul(fracLcm.div(x.d)).add(y.u.mul(fracLcm.div(y.d))), 57 | d: fracLcm 58 | }; 59 | 60 | return reduce(sum); 61 | } 62 | 63 | export function subFrac(x: FracValue, y: FracValue): FracValue { 64 | const fracLcm = lcm(x.d, y.d); 65 | const sum: FracValue = { 66 | u: x.u.mul(fracLcm.div(x.d)).sub(y.u.mul(fracLcm.div(y.d))), 67 | d: fracLcm 68 | }; 69 | 70 | return reduce(sum); 71 | } 72 | 73 | export function mulFrac(x: FracValue, y: FracValue): FracValue { 74 | const crossGcd1 = gcd(x.u, y.d); 75 | const crossGcd2 = gcd(x.d, y.u); 76 | 77 | const product: FracValue = { 78 | u: x.u.div(crossGcd1).mul(y.u.div(crossGcd2)), 79 | d: x.d.div(crossGcd2).mul(y.d.div(crossGcd1)) 80 | }; 81 | 82 | return reduce(product); 83 | } 84 | 85 | // y must not evaluate to 0. 86 | export function divFrac(x: FracValue, y: FracValue): FracValue { 87 | return mulFrac(x, invert(y)); 88 | } 89 | 90 | export function intPower(x: FracValue, y: Decimal): FracValue { 91 | if (y.gte(0)) { 92 | return reduce({ u: x.u.pow(y), d: x.d.pow(y) }); 93 | } else { 94 | return reduce({ u: x.d.pow(y.neg()), d: x.u.pow(y.neg()) }); 95 | } 96 | } 97 | 98 | ///////////////////// Operations with decimal ///////////////////// 99 | export function addDec(x: FracValue, y: Decimal): FracValue { 100 | const decFrac = DB.toFracValue(y); 101 | 102 | return addFrac(x, decFrac); 103 | } 104 | 105 | export function subDec(x: FracValue, y: Decimal): FracValue { 106 | return addDec(x, y.neg()); 107 | } 108 | 109 | export function mulDec(x: FracValue, y: Decimal): FracValue { 110 | const decFrac = DB.toFracValue(y); 111 | 112 | return mulFrac(x, decFrac); 113 | } 114 | 115 | // y must not evaluate to 0. 116 | export function divDec(x: FracValue, y: Decimal): FracValue { 117 | const decFrac = DB.toFracValue(y); 118 | 119 | return divFrac(x, decFrac); 120 | } 121 | 122 | ///////////////////// Operations with degree ///////////////////// 123 | export function addDegree(x: FracValue, y: DegreeValue): FracValue { 124 | const yFrac = DGB.toFracValue(y); 125 | 126 | return addFrac(x, yFrac); 127 | } 128 | 129 | export function subDegree(x: FracValue, y: DegreeValue): FracValue { 130 | return addDegree(x, { ...y, neg: !y.neg }); 131 | } 132 | 133 | export function mulDegree(x: FracValue, y: DegreeValue): FracValue { 134 | return mulFrac(x, DGB.toFracValue(y)); 135 | } 136 | 137 | // y must not evaluate to 0. 138 | export function divDegree(x: FracValue, y: DegreeValue): FracValue { 139 | return divFrac(x, DGB.toFracValue(y)); 140 | } 141 | -------------------------------------------------------------------------------- /logics/sys-keys.ts: -------------------------------------------------------------------------------- 1 | import cs from "../observables/calculator-state"; 2 | 3 | export const onShiftClick = () => { 4 | if (cs.funcMode === "SHIFT") { 5 | cs.setFuncMode("NONE"); 6 | } else { 7 | cs.setFuncMode("SHIFT"); 8 | } 9 | }; 10 | 11 | export const onAlphaClick = () => { 12 | if (cs.funcMode === "ALPHA") { 13 | cs.setFuncMode("NONE"); 14 | } else { 15 | cs.setFuncMode("ALPHA"); 16 | } 17 | }; 18 | 19 | export const onDirClick = (dir: "U" | "D" | "L" | "R") => { 20 | cs.clearFuncMode(); 21 | if (cs.displayMode === "NORMAL_EDIT") { 22 | switch (dir) { 23 | case "U": 24 | // prevent losing input data 25 | if (cs.entries.length !== 0) { 26 | return; 27 | } 28 | if (cs.entryIndex - 1 >= 0) { 29 | cs.entryIndex--; 30 | cs.entries = Object.assign( 31 | [], 32 | cs.historyEntries[cs.entryIndex] 33 | ); 34 | cs.setCursorIndex(cs.entries.length); 35 | cs.setDisplayMode("NORMAL_SHOW"); 36 | } 37 | break; 38 | case "D": 39 | if (cs.entries.length !== 0) { 40 | return; 41 | } 42 | if (cs.entryIndex + 1 < cs.historyEntries.length) { 43 | cs.entryIndex++; 44 | cs.entries = Object.assign( 45 | [], 46 | cs.historyEntries[cs.entryIndex] 47 | ); 48 | cs.setCursorIndex(cs.entries.length); 49 | cs.setDisplayMode("NORMAL_SHOW"); 50 | } 51 | break; 52 | case "L": 53 | if (cs.cursorIndex - 1 >= 0) { 54 | cs.setCursorIndex(cs.cursorIndex - 1); 55 | } 56 | break; 57 | case "R": 58 | if (cs.cursorIndex + 1 <= cs.entries.length) { 59 | cs.setCursorIndex(cs.cursorIndex + 1); 60 | } 61 | break; 62 | } 63 | } else if (cs.displayMode === "NORMAL_SHOW") { 64 | switch (dir) { 65 | case "U": 66 | if (cs.entryIndex - 1 >= 0) { 67 | cs.entryIndex--; 68 | cs.entries = Object.assign( 69 | [], 70 | cs.historyEntries[cs.entryIndex] 71 | ); 72 | cs.setCursorIndex(cs.entries.length); 73 | } 74 | break; 75 | case "D": 76 | if (cs.entryIndex + 1 < cs.historyEntries.length) { 77 | cs.entryIndex++; 78 | cs.entries = Object.assign( 79 | [], 80 | cs.historyEntries[cs.entryIndex] 81 | ); 82 | cs.setCursorIndex(cs.entries.length); 83 | } 84 | break; 85 | case "L": 86 | case "R": 87 | cs.setDisplayMode("NORMAL_EDIT"); 88 | cs.setCursorIndex(cs.entries.length); 89 | break; 90 | } 91 | } else if (cs.displayMode === "ERROR") { 92 | switch (dir) { 93 | case "L": 94 | case "R": 95 | cs.setDisplayMode("NORMAL_EDIT"); 96 | cs.setCursorIndex(cs.entries.length); 97 | break; 98 | } 99 | } 100 | }; 101 | 102 | export const onModeClrClick = () => { 103 | if (cs.displayMode === "DRG") { 104 | cs.setDisplayMode("NORMAL_EDIT"); 105 | } else if ( 106 | cs.displayMode === "NORMAL_EDIT" || 107 | cs.displayMode === "NORMAL_SHOW" 108 | ) { 109 | if (cs.funcMode === "SHIFT") { 110 | cs.setDisplayMode("CLEAR"); 111 | } else { 112 | cs.setDisplayMode("DRG"); 113 | } 114 | } 115 | 116 | cs.clearFuncMode(); 117 | }; 118 | 119 | export const onLangClick = () => { 120 | if (cs.displayMode === "LANG") { 121 | cs.setDisplayMode("NORMAL_EDIT"); 122 | } else if ( 123 | cs.displayMode === "NORMAL_EDIT" || 124 | cs.displayMode === "NORMAL_SHOW" 125 | ) { 126 | if (cs.funcMode === "NONE") { 127 | cs.setDisplayMode("LANG"); 128 | } 129 | } 130 | 131 | cs.clearFuncMode(); 132 | }; 133 | -------------------------------------------------------------------------------- /modules/math/value-type-basics/degree-basics.ts: -------------------------------------------------------------------------------- 1 | import Decimal from "decimal.js"; 2 | import type { FracValue, DegreeValue } from "../../calc-core/objs/types"; 3 | import * as DB from "./dec-basics"; 4 | import * as FB from "./frac-basics"; 5 | 6 | export function isAbsGreaterThan(x: DegreeValue, y: DegreeValue): boolean { 7 | if (x.d.gt(y.d)) { 8 | return true; 9 | } else if (x.d.lt(y.d)) { 10 | return false; 11 | } 12 | 13 | if (x.m.gt(y.m)) { 14 | return true; 15 | } else if (x.m.lt(y.m)) { 16 | return false; 17 | } 18 | 19 | if (x.s.gt(y.s)) { 20 | return true; 21 | } else { 22 | return false; 23 | } 24 | } 25 | 26 | export function toDecValue(x: DegreeValue): Decimal { 27 | const decAbs = x.d.add(x.m.div(60)).add(x.s.div(3600)); 28 | return x.neg ? decAbs.neg() : decAbs; 29 | } 30 | 31 | export function toFracValue(x: DegreeValue): FracValue { 32 | const sFrac = FB.fromTerminatingDiv(x.s, new Decimal(3600)); 33 | 34 | const res = FB.addFrac( 35 | sFrac, 36 | FB.addDec({ u: x.m, d: new Decimal(60) }, x.d) 37 | ); 38 | 39 | if (x.neg) { 40 | res.u = res.u.neg(); 41 | } 42 | 43 | return res; 44 | } 45 | 46 | // d,m,s only need to be non-negative. 47 | export function fromDmsNeg( 48 | d: Decimal, 49 | m: Decimal, 50 | s: Decimal, 51 | neg: boolean 52 | ): DegreeValue { 53 | // step 1: make d and m integers 54 | m = m.add(d.mod(1).mul(60)); 55 | d = d.floor(); 56 | 57 | s = s.add(m.mod(1).mul(60)); 58 | m = m.floor(); 59 | 60 | // step 2: reduce m and s 61 | m = m.add(s.div(60).floor()); 62 | s = s.mod(60); 63 | d = d.add(m.div(60).floor()); 64 | m = m.mod(60); 65 | 66 | return { 67 | d, 68 | m, 69 | s, 70 | neg 71 | }; 72 | } 73 | 74 | ///////////////////// Operations with degree ///////////////////// 75 | export function addDegree(x: DegreeValue, y: DegreeValue): DegreeValue { 76 | if (x.neg && y.neg) { 77 | return fromDmsNeg(x.d.add(y.d), x.m.add(y.m), x.s.add(y.s), true); 78 | } else if (!x.neg && !y.neg) { 79 | return fromDmsNeg(x.d.add(y.d), x.m.add(y.m), x.s.add(y.s), false); 80 | } else { 81 | // make sure x.neg && !y.neg 82 | if (y.neg && !x.neg) { 83 | const t = y; 84 | y = x; 85 | x = t; 86 | } 87 | 88 | if (isAbsGreaterThan(x, y)) { 89 | let s = x.s.sub(y.s); 90 | let m = x.m.sub(y.m); 91 | let d = x.d.sub(y.d); 92 | 93 | if (s.lt(0)) { 94 | m = m.sub(1); 95 | s = s.add(60); 96 | } 97 | 98 | if (m.lt(0)) { 99 | d = d.sub(1); 100 | m = m.add(60); 101 | } 102 | 103 | d = d.abs(); 104 | 105 | return { d, m, s, neg: true }; 106 | } else { 107 | let s = y.s.sub(x.s); 108 | let m = y.m.sub(x.m); 109 | let d = y.d.sub(x.d); 110 | 111 | if (s.lt(0)) { 112 | m = m.sub(1); 113 | s = s.add(60); 114 | } 115 | 116 | if (m.lt(0)) { 117 | d = d.sub(1); 118 | m = m.add(60); 119 | } 120 | 121 | d = d.abs(); 122 | 123 | return { d, m, s, neg: false }; 124 | } 125 | } 126 | } 127 | 128 | export function subDegree(x: DegreeValue, y: DegreeValue): DegreeValue { 129 | return addDegree(x, { ...y, neg: !y.neg }); 130 | } 131 | 132 | export function mulDegree(x: DegreeValue, y: DegreeValue): Decimal { 133 | return toDecValue(x).mul(toDecValue(y)); 134 | } 135 | 136 | export function divDegree(x: DegreeValue, y: DegreeValue): FracValue { 137 | return FB.divFrac(toFracValue(x), toFracValue(y)); 138 | } 139 | 140 | ///////////////////// Operations with dec ///////////////////// 141 | export function subDec(x: DegreeValue, y: Decimal): Decimal { 142 | return DB.addDegree(y.neg(), x); 143 | } 144 | 145 | // y must not evaluate to 0. 146 | export function divDec(x: DegreeValue, y: Decimal): FracValue { 147 | return FB.divDec(toFracValue(x), y); 148 | } 149 | 150 | ///////////////////// Operations with frac ///////////////////// 151 | export function subFrac(x: DegreeValue, y: FracValue): FracValue { 152 | return FB.addDegree({ u: y.u.neg(), d: y.d }, x); 153 | } 154 | 155 | // y must not evaluate to 0. 156 | export function divFrac(x: DegreeValue, y: FracValue): FracValue { 157 | return FB.divFrac(toFracValue(x), y); 158 | } 159 | -------------------------------------------------------------------------------- /styles/panel.module.scss: -------------------------------------------------------------------------------- 1 | @mixin key-label { 2 | text-shadow: 1px 1px 1px rgb(0 0 0 / 10%); 3 | 4 | font-size: 12px; 5 | font-weight: bold; 6 | 7 | cursor: pointer; 8 | user-select: none; 9 | } 10 | 11 | @mixin key { 12 | cursor: pointer; 13 | user-select: none; 14 | 15 | display: flex; 16 | justify-content: center; 17 | align-items: center; 18 | 19 | box-shadow: inset 1px 1px 1px 0px rgb(255 255 255 / 80%), 20 | inset -1px -1px 1px 0px rgb(40 49 85 / 30%), 21 | 1px 1px 3px 0px rgb(40 49 85 / 10%); 22 | 23 | transition-duration: 200ms; 24 | } 25 | 26 | @mixin key-trans-dark { 27 | &:hover { 28 | filter: opacity(0.9); 29 | } 30 | 31 | &:active { 32 | filter: opacity(0.83); 33 | } 34 | } 35 | 36 | @mixin key-trans-light { 37 | &:hover { 38 | filter: brightness(95%); 39 | } 40 | 41 | &:active { 42 | filter: brightness(90%); 43 | } 44 | } 45 | 46 | .divPanelWrapper { 47 | position: relative; 48 | width: m#{i}n(100%, 50vh); 49 | height: 100%; 50 | background-color: #eaecf3; 51 | border-radius: 10px; 52 | 53 | display: flex; 54 | flex-direction: column; 55 | align-items: center; 56 | 57 | [role="klw"] { 58 | @include key-label; 59 | color: white; 60 | font-size: 18px; 61 | } 62 | [role="klgr"] { 63 | @include key-label; 64 | color: rgb(96, 96, 96); 65 | font-size: 18px; 66 | } 67 | 68 | [role="klb"] { 69 | @include key-label; 70 | color: black; 71 | font-size: 13px; 72 | } 73 | 74 | [role="klg"] { 75 | @include key-label; 76 | color: #16c60c; 77 | font-size: 12px; 78 | } 79 | 80 | [role="klo"] { 81 | @include key-label; 82 | color: orangered; 83 | font-size: 12px; 84 | } 85 | 86 | [role="ksys"] { 87 | width: 35%; 88 | height: 55%; 89 | } 90 | [role="ksys"] > div:nth-child(2) { 91 | @include key; 92 | @include key-trans-dark; 93 | 94 | width: 100%; 95 | height: 100%; 96 | 97 | background-image: linear-gradient(135deg, #525252, #000000); 98 | border-radius: 10px; 99 | } 100 | 101 | [role="kdir"] { 102 | @include key; 103 | // only if panel is position:relative can this width 104 | // is relative to panel's. 105 | position: absolute; 106 | 107 | left: 50%; 108 | transform: translate(-50%, -10px); 109 | 110 | background-image: linear-gradient(135deg, #525252, #000000); 111 | width: 32%; 112 | height: 12%; 113 | 114 | border-top-left-radius: 50%; 115 | border-top-right-radius: 50%; 116 | border-bottom-left-radius: 50%; 117 | border-bottom-right-radius: 50%; 118 | 119 | cursor: pointer; 120 | 121 | > span { 122 | width: 30%; 123 | height: 50%; 124 | text-align: center; 125 | //background-color: rebeccapurple; 126 | font-size: 15px; 127 | position: absolute; 128 | color: white; 129 | } 130 | 131 | > span:nth-child(1) { 132 | left: 50%; 133 | transform: translate(-50%, -40%); 134 | } 135 | 136 | > span:nth-child(2) { 137 | left: 0; 138 | transform: translate(25%, 0) rotate(270deg); 139 | } 140 | 141 | > span:nth-child(3) { 142 | left: 50%; 143 | transform: translate(-50%, 50%) rotate(180deg); 144 | } 145 | 146 | > span:nth-child(4) { 147 | left: 100%; 148 | transform: translate(-120%, 0) rotate(90deg); 149 | } 150 | } 151 | 152 | [role="kfunc"] { 153 | width: 14%; 154 | height: 100%; 155 | } 156 | [role="kfuncr1"] { 157 | width: 45%; 158 | height: 100%; 159 | } 160 | [role="kfunc"] > div:nth-child(2), 161 | [role="kfuncr1"] > div:nth-child(2) { 162 | @include key; 163 | @include key-trans-light; 164 | 165 | width: 100%; 166 | height: 100%; 167 | 168 | background-image: linear-gradient(135deg, #f4f5f6, #fff); 169 | border-radius: 6px; 170 | } 171 | 172 | [role="kbasic"], 173 | [role="kbasicy"] { 174 | width: 17%; 175 | height: 100%; 176 | } 177 | [role="kbasic"] > div:nth-child(2), 178 | [role="kbasicy"] > div:nth-child(2) { 179 | @include key; 180 | @include key-trans-dark; 181 | 182 | width: 100%; 183 | height: 100%; 184 | 185 | background-image: linear-gradient(135deg, #4c4c4c, #000000); 186 | border-radius: 6px; 187 | } 188 | 189 | [role="kbasicy"] > div:nth-child(2) { 190 | @include key; 191 | @include key-trans-light; 192 | 193 | background-image: linear-gradient(135deg, #fffb00, #e5e100); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /test/parse-test.ts: -------------------------------------------------------------------------------- 1 | import { KeyEntry, KEY_ENTRIES } from "../modules/calc-core/objs/key-entry"; 2 | import { Operator } from "../modules/calc-core/objs/operators"; 3 | import { preprocess } from "../modules/calc-core/preprocessing"; 4 | import { parse } from "../modules/calc-core/parse"; 5 | import { InternalNumber } from "../modules/calc-core/objs/internal-number"; 6 | 7 | function printResult(ke: KeyEntry[]) { 8 | ke = preprocess(ke); 9 | 10 | console.log(ke.map(x => x.id).join(" ")); 11 | 12 | const pr = parse(ke); 13 | 14 | if (pr.success) { 15 | console.log( 16 | pr.lexems 17 | .map(x => 18 | x.type === "NBR" 19 | ? (x.obj).toString() 20 | : (x.obj).id 21 | ) 22 | .join(" ") 23 | ); 24 | } else { 25 | console.log(pr.msg); 26 | } 27 | } 28 | 29 | function test1() { 30 | // 3*(5+3sin(-(5.5--6 31 | // =>3*(5+3*sin(neg((-5.5+6)))) 32 | const ke: KeyEntry[] = [ 33 | KEY_ENTRIES.n3, 34 | KEY_ENTRIES.mul, 35 | KEY_ENTRIES.lBracket, 36 | KEY_ENTRIES.n5, 37 | KEY_ENTRIES.add, 38 | KEY_ENTRIES.n3, 39 | KEY_ENTRIES.sin, 40 | KEY_ENTRIES.sub, 41 | KEY_ENTRIES.lBracket, 42 | KEY_ENTRIES.n5, 43 | KEY_ENTRIES.nDot, 44 | KEY_ENTRIES.n5, 45 | KEY_ENTRIES.sub, 46 | KEY_ENTRIES.sub, 47 | KEY_ENTRIES.n6 48 | ]; 49 | 50 | printResult(ke); 51 | } 52 | 53 | function test2() { 54 | // 6E 55 | const ke: KeyEntry[] = [KEY_ENTRIES.n6, KEY_ENTRIES.E]; 56 | 57 | printResult(ke); 58 | } 59 | 60 | function test3() { 61 | // 5-Pol(5,6 62 | const ke: KeyEntry[] = [ 63 | KEY_ENTRIES.n5, 64 | KEY_ENTRIES.sub, 65 | KEY_ENTRIES.pol, 66 | KEY_ENTRIES.n5, 67 | KEY_ENTRIES.comma, 68 | KEY_ENTRIES.n6 69 | ]; 70 | 71 | printResult(ke); 72 | } 73 | 74 | function test4() { 75 | // 5-Pol(+5,6Ccos(A+B)) 76 | const ke: KeyEntry[] = [ 77 | KEY_ENTRIES.n5, 78 | KEY_ENTRIES.sub, 79 | KEY_ENTRIES.pol, 80 | KEY_ENTRIES.add, 81 | KEY_ENTRIES.n5, 82 | KEY_ENTRIES.comma, 83 | KEY_ENTRIES.n6, 84 | KEY_ENTRIES.C, 85 | KEY_ENTRIES.cos, 86 | KEY_ENTRIES.A, 87 | KEY_ENTRIES.add, 88 | KEY_ENTRIES.B, 89 | KEY_ENTRIES.rBracket, 90 | KEY_ENTRIES.rBracket 91 | ]; 92 | 93 | printResult(ke); 94 | } 95 | 96 | function test5() { 97 | // 5-Pol(5,6)% 98 | const ke: KeyEntry[] = [ 99 | KEY_ENTRIES.n5, 100 | KEY_ENTRIES.sub, 101 | KEY_ENTRIES.pol, 102 | KEY_ENTRIES.n5, 103 | KEY_ENTRIES.comma, 104 | KEY_ENTRIES.n6, 105 | KEY_ENTRIES.rBracket, 106 | KEY_ENTRIES.percent 107 | ]; 108 | 109 | printResult(ke); 110 | } 111 | 112 | function test6() { 113 | // 5-Pol(,7³ 114 | const ke: KeyEntry[] = [ 115 | KEY_ENTRIES.n5, 116 | KEY_ENTRIES.sub, 117 | KEY_ENTRIES.pol, 118 | KEY_ENTRIES.comma, 119 | KEY_ENTRIES.n7, 120 | KEY_ENTRIES.cube 121 | ]; 122 | 123 | printResult(ke); 124 | } 125 | 126 | function test7() { 127 | // 5'66' 128 | const ke: KeyEntry[] = [ 129 | KEY_ENTRIES.n5, 130 | KEY_ENTRIES.degree, 131 | KEY_ENTRIES.n6, 132 | KEY_ENTRIES.n6, 133 | KEY_ENTRIES.degree 134 | ]; 135 | 136 | printResult(ke); 137 | } 138 | 139 | function test8() { 140 | // 3+5/(7+8(1+2))² 141 | const ke: KeyEntry[] = [ 142 | KEY_ENTRIES.n3, 143 | KEY_ENTRIES.add, 144 | KEY_ENTRIES.n5, 145 | KEY_ENTRIES.div, 146 | KEY_ENTRIES.lBracket, 147 | KEY_ENTRIES.n7, 148 | KEY_ENTRIES.add, 149 | KEY_ENTRIES.n8, 150 | KEY_ENTRIES.lBracket, 151 | KEY_ENTRIES.n1, 152 | KEY_ENTRIES.add, 153 | KEY_ENTRIES.n2, 154 | KEY_ENTRIES.rBracket, 155 | KEY_ENTRIES.rBracket, 156 | KEY_ENTRIES.sqrt 157 | ]; 158 | 159 | printResult(ke); 160 | } 161 | 162 | function test9() { 163 | // 5-Pol(5,Pol(7,9)) 164 | const ke: KeyEntry[] = [ 165 | KEY_ENTRIES.n5, 166 | KEY_ENTRIES.sub, 167 | KEY_ENTRIES.pol, 168 | KEY_ENTRIES.n5, 169 | KEY_ENTRIES.comma, 170 | KEY_ENTRIES.pol, 171 | KEY_ENTRIES.n7, 172 | KEY_ENTRIES.comma, 173 | KEY_ENTRIES.n9, 174 | KEY_ENTRIES.rBracket 175 | ]; 176 | 177 | printResult(ke); 178 | } 179 | 180 | function test10() { 181 | // Pol(Pol(1,2),Pol(3,4 182 | const ke: KeyEntry[] = [ 183 | KEY_ENTRIES.pol, 184 | KEY_ENTRIES.pol, 185 | KEY_ENTRIES.n1, 186 | KEY_ENTRIES.comma, 187 | KEY_ENTRIES.n2, 188 | KEY_ENTRIES.rBracket, 189 | KEY_ENTRIES.comma, 190 | KEY_ENTRIES.pol, 191 | KEY_ENTRIES.n3, 192 | KEY_ENTRIES.comma, 193 | KEY_ENTRIES.n4 194 | ]; 195 | 196 | printResult(ke); 197 | } 198 | 199 | test10(); 200 | -------------------------------------------------------------------------------- /test/calculate-test.ts: -------------------------------------------------------------------------------- 1 | import { KeyEntry, KEY_ENTRIES } from "../modules/calc-core/objs/key-entry"; 2 | import { Operator } from "../modules/calc-core/objs/operators"; 3 | import { preprocess } from "../modules/calc-core/preprocessing"; 4 | import { parse } from "../modules/calc-core/parse"; 5 | import { calculate } from "../modules/calc-core/calculate"; 6 | import { InternalNumber } from "../modules/calc-core/objs/internal-number"; 7 | 8 | function printResult(ke: KeyEntry[]) { 9 | ke = preprocess(ke); 10 | 11 | console.log(ke.map(x => x.id).join(" ")); 12 | 13 | const pr = parse(ke); 14 | 15 | if (pr.success) { 16 | console.log( 17 | pr.lexems 18 | .map(x => 19 | x.type === "NBR" 20 | ? (x.obj).toString() 21 | : (x.obj).id 22 | ) 23 | .join(" ") 24 | ); 25 | } else { 26 | console.log(pr.msg); 27 | return; 28 | } 29 | 30 | const cr = calculate(pr.lexems); 31 | 32 | if (cr.success) { 33 | console.log(cr.result!.toString()); 34 | } else { 35 | console.log(cr.msg); 36 | } 37 | } 38 | 39 | function test1() { 40 | // 3*(5+3sin(-(5.5--6 41 | // =>3*(5+3*sin(neg((5.5+6)))) 42 | const ke: KeyEntry[] = [ 43 | KEY_ENTRIES.n3, 44 | KEY_ENTRIES.mul, 45 | KEY_ENTRIES.lBracket, 46 | KEY_ENTRIES.n5, 47 | KEY_ENTRIES.add, 48 | KEY_ENTRIES.n3, 49 | KEY_ENTRIES.sin, 50 | KEY_ENTRIES.sub, 51 | KEY_ENTRIES.lBracket, 52 | KEY_ENTRIES.n5, 53 | KEY_ENTRIES.nDot, 54 | KEY_ENTRIES.n5, 55 | KEY_ENTRIES.sub, 56 | KEY_ENTRIES.sub, 57 | KEY_ENTRIES.n6 58 | ]; 59 | 60 | printResult(ke); 61 | } 62 | 63 | function test2() { 64 | // 1.2-1 65 | const ke: KeyEntry[] = [ 66 | KEY_ENTRIES.n1, 67 | KEY_ENTRIES.nDot, 68 | KEY_ENTRIES.n2, 69 | KEY_ENTRIES.sub, 70 | KEY_ENTRIES.n1 71 | ]; 72 | 73 | printResult(ke); 74 | } 75 | 76 | function test3() { 77 | // 5-Pol(5,6 78 | const ke: KeyEntry[] = [ 79 | KEY_ENTRIES.n5, 80 | KEY_ENTRIES.sub, 81 | KEY_ENTRIES.pol, 82 | KEY_ENTRIES.n5, 83 | KEY_ENTRIES.comma, 84 | KEY_ENTRIES.n6 85 | ]; 86 | 87 | printResult(ke); 88 | } 89 | 90 | function test4() { 91 | // 5-Pol(+5,6Ccos(A+B)) 92 | const ke: KeyEntry[] = [ 93 | KEY_ENTRIES.n5, 94 | KEY_ENTRIES.sub, 95 | KEY_ENTRIES.pol, 96 | KEY_ENTRIES.add, 97 | KEY_ENTRIES.n5, 98 | KEY_ENTRIES.comma, 99 | KEY_ENTRIES.n6, 100 | KEY_ENTRIES.C, 101 | KEY_ENTRIES.cos, 102 | KEY_ENTRIES.A, 103 | KEY_ENTRIES.add, 104 | KEY_ENTRIES.B, 105 | KEY_ENTRIES.rBracket, 106 | KEY_ENTRIES.rBracket 107 | ]; 108 | 109 | printResult(ke); 110 | } 111 | 112 | function test5() { 113 | // 5-Pol(5,6)% 114 | const ke: KeyEntry[] = [ 115 | KEY_ENTRIES.n5, 116 | KEY_ENTRIES.sub, 117 | KEY_ENTRIES.pol, 118 | KEY_ENTRIES.n5, 119 | KEY_ENTRIES.comma, 120 | KEY_ENTRIES.n6, 121 | KEY_ENTRIES.rBracket, 122 | KEY_ENTRIES.percent 123 | ]; 124 | 125 | printResult(ke); 126 | } 127 | 128 | function test6() { 129 | // 5-Pol(,7³ 130 | const ke: KeyEntry[] = [ 131 | KEY_ENTRIES.n5, 132 | KEY_ENTRIES.sub, 133 | KEY_ENTRIES.pol, 134 | KEY_ENTRIES.comma, 135 | KEY_ENTRIES.n7, 136 | KEY_ENTRIES.cube 137 | ]; 138 | 139 | printResult(ke); 140 | } 141 | 142 | function test7() { 143 | // 5'66' 144 | const ke: KeyEntry[] = [ 145 | KEY_ENTRIES.n5, 146 | KEY_ENTRIES.degree, 147 | KEY_ENTRIES.n6, 148 | KEY_ENTRIES.n6, 149 | KEY_ENTRIES.degree 150 | ]; 151 | 152 | printResult(ke); 153 | } 154 | 155 | function test8() { 156 | // 5.6/. 157 | const ke: KeyEntry[] = [ 158 | KEY_ENTRIES.n5, 159 | KEY_ENTRIES.nDot, 160 | KEY_ENTRIES.n6, 161 | KEY_ENTRIES.frac, 162 | KEY_ENTRIES.nDot 163 | ]; 164 | 165 | printResult(ke); 166 | } 167 | 168 | function test9() { 169 | // 5.6/. 170 | const ke: KeyEntry[] = [ 171 | KEY_ENTRIES.n1, 172 | KEY_ENTRIES.nDot, 173 | KEY_ENTRIES.n2, 174 | KEY_ENTRIES.frac, 175 | KEY_ENTRIES.sub, 176 | KEY_ENTRIES.n2 177 | ]; 178 | 179 | printResult(ke); 180 | } 181 | 182 | function test10() { 183 | // 250/(22*60+25)*3.6 184 | const ke: KeyEntry[] = [ 185 | KEY_ENTRIES.n2, 186 | KEY_ENTRIES.n5, 187 | KEY_ENTRIES.n0, 188 | KEY_ENTRIES.div, 189 | KEY_ENTRIES.lBracket, 190 | KEY_ENTRIES.n2, 191 | KEY_ENTRIES.n2, 192 | KEY_ENTRIES.mul, 193 | KEY_ENTRIES.n6, 194 | KEY_ENTRIES.n0, 195 | KEY_ENTRIES.add, 196 | KEY_ENTRIES.n2, 197 | KEY_ENTRIES.n5, 198 | KEY_ENTRIES.rBracket, 199 | KEY_ENTRIES.mul, 200 | KEY_ENTRIES.n3, 201 | KEY_ENTRIES.nDot, 202 | KEY_ENTRIES.n6 203 | ]; 204 | 205 | printResult(ke); 206 | } 207 | 208 | test10(); 209 | -------------------------------------------------------------------------------- /logics/index.ts: -------------------------------------------------------------------------------- 1 | import Decimal from "decimal.js"; 2 | import cs from "../observables/calculator-state"; 3 | import cm from "../observables/calculator-memory"; 4 | import stringsRes from "../observables/strings-res"; 5 | import * as LS from "./sys-keys"; 6 | import * as LF from "./func-keys"; 7 | import * as LB from "./basic-keys"; 8 | 9 | export const initialize = () => { 10 | Decimal.set({ 11 | precision: 50 12 | }); 13 | 14 | cm.loadFromLocalStorage(); 15 | stringsRes.switchLangFromStorage(); 16 | 17 | window.onkeydown = onWindowKeydown; 18 | }; 19 | 20 | const onWindowKeydown = (e: KeyboardEvent) => { 21 | switch (e.key) { 22 | case "0": 23 | LB.onR4C1Click(); 24 | break; 25 | case ".": 26 | LB.onR4C2Click(); 27 | break; 28 | case "Enter": 29 | case "=": 30 | LB.onR4C5Click(); 31 | break; 32 | case "1": 33 | LB.onR3C1Click(); 34 | break; 35 | case "2": 36 | LB.onR3C2Click(); 37 | break; 38 | case "3": 39 | LB.onR3C3Click(); 40 | break; 41 | case "+": 42 | LB.onR3C4Click(); 43 | break; 44 | case "-": 45 | LB.onR3C5Click(); 46 | break; 47 | case "4": 48 | LB.onR2C1Click(); 49 | break; 50 | case "5": 51 | LB.onR2C2Click(); 52 | break; 53 | case "6": 54 | LB.onR2C3Click(); 55 | break; 56 | case "*": 57 | LB.onR2C4Click(); 58 | break; 59 | case "/": 60 | if (e.code === "NumpadDivide") { 61 | LB.onR2C5Click(); 62 | } else { 63 | LF.onR2C1Click(); 64 | } 65 | 66 | break; 67 | case "7": 68 | LB.onR1C1Click(); 69 | break; 70 | case "8": 71 | LB.onR1C2Click(); 72 | break; 73 | case "9": 74 | LB.onR1C3Click(); 75 | break; 76 | case "Backspace": 77 | LB.onR1C4Click(); 78 | break; 79 | case "Delete": 80 | LB.onR1C5Click(); 81 | break; 82 | case "Insert": 83 | inputShiftEntryFromKeyboard(e.key); 84 | break; 85 | // case "Shift": 86 | // LS.onShiftClick(); 87 | // break; 88 | case "ArrowUp": 89 | LS.onDirClick("U"); 90 | break; 91 | case "ArrowDown": 92 | LS.onDirClick("D"); 93 | break; 94 | case "ArrowLeft": 95 | LS.onDirClick("L"); 96 | break; 97 | case "ArrowRight": 98 | LS.onDirClick("R"); 99 | break; 100 | case "Home": 101 | cs.setDisplayMode("NORMAL_EDIT"); 102 | cs.setCursorIndex(0); 103 | break; 104 | case "End": 105 | cs.setDisplayMode("NORMAL_EDIT"); 106 | cs.setCursorIndex(cs.entries.length); 107 | break; 108 | case "'": 109 | LF.onR3C2Click(); 110 | break; 111 | case ",": 112 | LF.onR4C5Click(); 113 | break; 114 | case "(": 115 | case ")": 116 | case "s": 117 | case "S": 118 | inputNormalEntryFromKeyboard(e.key.toUpperCase()); 119 | break; 120 | case "a": 121 | case "A": 122 | case "b": 123 | case "B": 124 | case "c": 125 | case "C": 126 | case "d": 127 | case "D": 128 | case "e": 129 | case "E": 130 | case "f": 131 | case "F": 132 | case "x": 133 | case "X": 134 | case "y": 135 | case "Y": 136 | case "m": 137 | case "M": 138 | inputAlphaEntryFromKeyboard(e.key.toUpperCase()); 139 | break; 140 | } 141 | }; 142 | 143 | const inputNormalEntryFromKeyboard = (key: string) => { 144 | cs.clearFuncMode(); 145 | 146 | switch (key) { 147 | case "(": 148 | LF.onR4C3Click(); 149 | break; 150 | case ")": 151 | LF.onR4C4Click(); 152 | break; 153 | case "S": 154 | LB.onR4C4Click(); 155 | break; 156 | } 157 | }; 158 | 159 | const inputShiftEntryFromKeyboard = (key: string) => { 160 | cs.setFuncMode("SHIFT"); 161 | 162 | switch (key) { 163 | case "Insert": 164 | LB.onR1C4Click(); 165 | break; 166 | } 167 | }; 168 | 169 | const inputAlphaEntryFromKeyboard = (key: string) => { 170 | cs.setFuncMode("ALPHA"); 171 | 172 | switch (key) { 173 | case "A": 174 | LF.onR3C1Click(); 175 | break; 176 | case "B": 177 | LF.onR3C2Click(); 178 | break; 179 | case "C": 180 | LF.onR3C3Click(); 181 | break; 182 | case "D": 183 | LF.onR3C4Click(); 184 | break; 185 | case "E": 186 | LF.onR3C5Click(); 187 | break; 188 | case "F": 189 | LF.onR3C6Click(); 190 | break; 191 | case "X": 192 | LF.onR4C4Click(); 193 | break; 194 | case "Y": 195 | LF.onR4C5Click(); 196 | break; 197 | case "M": 198 | LF.onR4C6Click(); 199 | break; 200 | } 201 | }; 202 | -------------------------------------------------------------------------------- /styles/screen.module.scss: -------------------------------------------------------------------------------- 1 | @import "./common/anims"; 2 | @import "./common/common"; 3 | 4 | .divScreenWrapper { 5 | box-sizing: border-box; 6 | background-color: #aaa; 7 | 8 | flex-grow: 1; 9 | 10 | width: calc(100% - 2em); 11 | height: 150px; 12 | 13 | margin: 1em 0.5em 2em 0.5em; 14 | padding: 12px 16px 11px 16px; 15 | box-shadow: inset 1px 1px 1px 0 rgb(40 49 85 / 40%); 16 | border-radius: 18px; 17 | background: #fff; 18 | } 19 | 20 | .divNormalWrapper { 21 | width: 100%; 22 | height: 100%; 23 | 24 | display: flex; 25 | flex-direction: column; 26 | 27 | > div[role="entries"] { 28 | display: flex; 29 | justify-content: flex-start; 30 | flex-wrap: wrap; 31 | 32 | min-height: 1.5em; 33 | max-height: 48%; 34 | overflow: auto; 35 | //background-color: rebeccapurple; 36 | 37 | @include slim-scroll-bar; 38 | 39 | > img { 40 | cursor: text; 41 | } 42 | } 43 | 44 | > div[role="result"] { 45 | font-size: 42px; 46 | word-wrap: break-word; 47 | text-align: end; 48 | flex-grow: 1; 49 | overflow: auto; 50 | 51 | @include slim-scroll-bar; 52 | } 53 | 54 | > div[role="error"] { 55 | color: red; 56 | font-size: 18px; 57 | word-wrap: break-word; 58 | text-align: start; 59 | 60 | flex-grow: 1; 61 | overflow: auto; 62 | 63 | @include slim-scroll-bar; 64 | } 65 | 66 | > div[role="mode"] { 67 | font-size: 12px; 68 | font-weight: bold; 69 | height: 8%; 70 | margin-top: auto; 71 | 72 | transform: translateY(8px); 73 | 74 | user-select: none; 75 | 76 | background-color: #f7f7f7; 77 | border-radius: 8px; 78 | 79 | > span:nth-child(1) { 80 | color: brown; 81 | position: absolute; 82 | left: 10px; 83 | } 84 | 85 | > span:nth-child(2) { 86 | color: goldenrod; 87 | position: absolute; 88 | left: 60px; 89 | } 90 | 91 | > span:nth-child(3) { 92 | color: mediumblue; 93 | position: absolute; 94 | right: 10px; 95 | } 96 | } 97 | } 98 | 99 | .imgNormal { 100 | margin-bottom: 5px; 101 | } 102 | 103 | .imgCursor { 104 | animation-duration: 0.75s; 105 | animation-iteration-count: infinite; 106 | } 107 | 108 | .imgInsert { 109 | @extend .imgNormal; 110 | @extend .imgCursor; 111 | animation-name: insert-course; 112 | } 113 | 114 | .imgOverwrite { 115 | @extend .imgNormal; 116 | @extend .imgCursor; 117 | animation-name: overwrite-course; 118 | } 119 | 120 | .divDrgWrapper { 121 | width: 100%; 122 | height: 100%; 123 | 124 | display: flex; 125 | 126 | justify-content: space-evenly; 127 | align-items: center; 128 | 129 | > div { 130 | width: 80px; 131 | height: 80px; 132 | border-radius: 50%; 133 | 134 | font-size: 18px; 135 | font-weight: bold; 136 | 137 | text-align: center; 138 | line-height: 80px; 139 | 140 | box-shadow: 3px 3px 5px 0px rgba(0, 0, 0, 0.2); 141 | 142 | user-select: none; 143 | cursor: pointer; 144 | 145 | transition-duration: 200ms; 146 | 147 | &:hover { 148 | filter: brightness(90%); 149 | } 150 | } 151 | 152 | > div:nth-child(1) { 153 | background-color: #f5f1c8; 154 | } 155 | 156 | > div:nth-child(2) { 157 | background-color: #ffdee7; 158 | } 159 | 160 | > div:nth-child(3) { 161 | background-color: #d4e7e2; 162 | } 163 | } 164 | 165 | .divClearWrapper { 166 | width: 100%; 167 | height: 100%; 168 | 169 | display: flex; 170 | 171 | justify-content: space-evenly; 172 | align-items: center; 173 | 174 | > div { 175 | width: 80px; 176 | height: 80px; 177 | border-radius: 50%; 178 | 179 | font-size: 15px; 180 | font-weight: bold; 181 | 182 | text-align: center; 183 | line-height: 80px; 184 | 185 | box-shadow: 3px 3px 5px 0px rgba(0, 0, 0, 0.2); 186 | 187 | user-select: none; 188 | cursor: pointer; 189 | 190 | transition-duration: 200ms; 191 | 192 | &:hover { 193 | background-color: #cce8f4; 194 | } 195 | } 196 | } 197 | 198 | .divLangWrapper { 199 | width: 100%; 200 | height: 100%; 201 | 202 | display: flex; 203 | 204 | justify-content: space-evenly; 205 | align-items: center; 206 | 207 | > div { 208 | width: 100px; 209 | height: 100px; 210 | border-radius: 50%; 211 | 212 | font-size: 20px; 213 | font-weight: bold; 214 | 215 | text-align: center; 216 | line-height: 100px; 217 | 218 | box-shadow: 3px 3px 5px 0px rgba(0, 0, 0, 0.2); 219 | 220 | user-select: none; 221 | cursor: pointer; 222 | 223 | transition-duration: 200ms; 224 | 225 | &:hover { 226 | background-color: #fce2de; 227 | } 228 | } 229 | } 230 | 231 | .divAboutWrapper { 232 | width: 100%; 233 | height: 100%; 234 | 235 | display: flex; 236 | flex-direction: column; 237 | justify-content: center; 238 | align-items: center; 239 | 240 | > title { 241 | display: block; 242 | font-size: 15px; 243 | text-align: center; 244 | margin: 8px 0; 245 | } 246 | 247 | > title:nth-child(1) { 248 | font-weight: bold; 249 | } 250 | 251 | > div { 252 | margin: 8px 0; 253 | 254 | > a { 255 | color: #0d6efd; 256 | text-decoration: underline; 257 | 258 | &:hover { 259 | color: #0a58ca; 260 | } 261 | } 262 | 263 | > a:nth-child(1) { 264 | margin: 0 5px; 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /modules/calc-core/objs/internal-number.ts: -------------------------------------------------------------------------------- 1 | import Decimal from "decimal.js"; 2 | import type { FracValue, DegreeValue } from "./types"; 3 | import { toDecValue } from "../../math/value-type-basics/frac-basics"; 4 | 5 | type InternalNumberType = "DEC" | "FRAC" | "DEGREE"; 6 | 7 | interface StorageFracValue { 8 | u: string; 9 | d: string; 10 | } 11 | interface StorageDegreeValue { 12 | neg: boolean; 13 | d: string; 14 | m: string; 15 | s: string; 16 | } 17 | 18 | interface StorageObject { 19 | type: InternalNumberType; 20 | value: string | StorageFracValue | StorageDegreeValue; 21 | } 22 | 23 | const DISP_SIGNIFICANT_DIGITS: number = 20; 24 | const DISP_ZERO_BOUND: string = "1e-20"; 25 | const DISP_EXP_BOUND: string = "1e20"; 26 | const DISP_FRAC_BOUND: string = "1e20"; 27 | 28 | // Immutable 29 | export class InternalNumber { 30 | // an InternalNumber object only maintains the value of its current type. 31 | private numberType: InternalNumberType = "DEC"; 32 | 33 | private decValue: Decimal = new Decimal(0); 34 | 35 | // if evaluates to integer, 36 | // or if u or d exceeds Number.MAX_SAFE_INTEGER, 37 | // frac value is still preserved. 38 | private fracValue: FracValue = { 39 | u: new Decimal(0), 40 | d: new Decimal(1) 41 | }; 42 | 43 | private degreeValue: DegreeValue = { 44 | d: new Decimal(0), 45 | m: new Decimal(0), 46 | s: new Decimal(0), 47 | neg: false 48 | }; 49 | 50 | constructor( 51 | type: InternalNumberType, 52 | value: Decimal | FracValue | DegreeValue 53 | ) { 54 | this.numberType = type; 55 | switch (type) { 56 | case "DEC": 57 | this.decValue = value; 58 | break; 59 | case "FRAC": 60 | Object.assign(this.fracValue, value); 61 | break; 62 | case "DEGREE": 63 | Object.assign(this.degreeValue, value); 64 | break; 65 | } 66 | } 67 | 68 | get type(): InternalNumberType { 69 | return this.numberType; 70 | } 71 | 72 | get dec(): Decimal { 73 | return this.decValue; 74 | } 75 | 76 | get frac(): FracValue { 77 | return this.fracValue; 78 | } 79 | 80 | get degree(): DegreeValue { 81 | return this.degreeValue; 82 | } 83 | 84 | toString(): string { 85 | switch (this.numberType) { 86 | case "DEC": 87 | if (this.decValue.abs().lt(DISP_EXP_BOUND)) { 88 | if (this.decValue.abs().lt(DISP_ZERO_BOUND)) { 89 | return "0"; 90 | } 91 | return this.decValue 92 | .toSignificantDigits(DISP_SIGNIFICANT_DIGITS) 93 | .toString(); 94 | } else { 95 | return this.decValue.toExponential(DISP_SIGNIFICANT_DIGITS); 96 | } 97 | case "FRAC": 98 | if ( 99 | this.fracValue.u.abs().gte(DISP_FRAC_BOUND) || 100 | this.fracValue.d.abs().gte(DISP_FRAC_BOUND) 101 | ) { 102 | return new InternalNumber( 103 | "DEC", 104 | toDecValue(this.fracValue) 105 | ).toString(); 106 | } 107 | return ( 108 | this.fracValue.u.toString() + 109 | "/" + 110 | this.fracValue.d.toString() 111 | ); 112 | case "DEGREE": 113 | return ( 114 | `${ 115 | this.degreeValue.neg ? "-" : "" 116 | }${this.degreeValue.d.toString()}°` + 117 | `${this.degreeValue.m.toString()}'` + 118 | `${this.degreeValue.s.toString()}"` 119 | ); 120 | } 121 | } 122 | 123 | toStorageObjectString(): string { 124 | const storageObject: StorageObject = { 125 | type: this.numberType, 126 | value: "" 127 | }; 128 | 129 | switch (this.numberType) { 130 | case "DEC": 131 | storageObject.value = this.decValue.toString(); 132 | break; 133 | case "FRAC": 134 | storageObject.value = { 135 | u: this.fracValue.u.toString(), 136 | d: this.fracValue.d.toString() 137 | }; 138 | break; 139 | case "DEGREE": 140 | storageObject.value = { 141 | neg: this.degreeValue.neg, 142 | d: this.degreeValue.d.toString(), 143 | m: this.degreeValue.m.toString(), 144 | s: this.degreeValue.s.toString() 145 | }; 146 | break; 147 | } 148 | 149 | return JSON.stringify(storageObject); 150 | } 151 | } 152 | 153 | export function fromStorageObjectString(str: string): InternalNumber { 154 | const storageObject: StorageObject = JSON.parse(str); 155 | 156 | switch (storageObject.type) { 157 | case "DEC": 158 | return new InternalNumber( 159 | "DEC", 160 | new Decimal(storageObject.value as string) 161 | ); 162 | case "FRAC": 163 | return new InternalNumber("FRAC", { 164 | u: new Decimal((storageObject.value).u), 165 | d: new Decimal((storageObject.value).d) 166 | }); 167 | case "DEGREE": 168 | return new InternalNumber("DEGREE", { 169 | neg: (storageObject.value).neg, 170 | d: new Decimal((storageObject.value).d), 171 | m: new Decimal((storageObject.value).m), 172 | s: new Decimal((storageObject.value).s) 173 | }); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /modules/calc-core/objs/check-fns.ts: -------------------------------------------------------------------------------- 1 | import Decimal from "decimal.js"; 2 | import calculatorState from "../../../observables/calculator-state"; 3 | import stringsRes from "../../../observables/strings-res"; 4 | import { InternalNumber } from "./internal-number"; 5 | import { PI } from "../../math/constants"; 6 | import { 7 | getDecValue, 8 | isNegative, 9 | isZero, 10 | isPositive, 11 | isZeroPositive, 12 | isNonNegativeInteger, 13 | isOdd, 14 | div 15 | } from "../../math/internal-number-math"; 16 | import type { CheckFn } from "./types"; 17 | 18 | const gzCheck: (funcName: string) => CheckFn = 19 | (funcName: string) => 20 | (...operands: InternalNumber[]) => ({ 21 | ok: isPositive(operands[0]), 22 | msg: funcName + stringsRes.strings.CALC_CK_ERROR_MSGS.GOT_NOT_POSITIVE 23 | }); 24 | 25 | interface CheckFns { 26 | alwaysTrue: CheckFn; 27 | ///// taking 1 arg 28 | sqrtCheck: CheckFn; // >=0 29 | logCheck: CheckFn; // >0 30 | lnCheck: CheckFn; // >0 31 | tanCheck: CheckFn; // !=odd multiples of pi/2 32 | asinAcosCheck: CheckFn; // -1<=x<=1 33 | acoshCheck: CheckFn; // >=1 34 | atanhCheck: CheckFn; // -1=0, integer 36 | invCheck: CheckFn; // !=0 37 | ///// taking 2 args 38 | nCrnPrCheck: CheckFn; // both integer, both >=0 and x>=y 39 | powCheck: CheckFn; // when x<0, y can only be integer 40 | rootCheck: CheckFn; // x!=0; when y<0, x can only be odd integer 41 | divCheck: CheckFn; // y!=0 42 | recCheck: CheckFn; // x>=0 43 | 44 | createDegreeCheck: CheckFn; // m and s non-negative 45 | createFracCheck: CheckFn; // d!=0 46 | } 47 | 48 | export const CHECK_FNS: CheckFns = { 49 | alwaysTrue: () => ({ ok: true, msg: "" }), 50 | sqrtCheck: (...operands: InternalNumber[]) => ({ 51 | ok: isZeroPositive(operands[0]), 52 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.SQRT 53 | }), 54 | logCheck: gzCheck("log() "), 55 | lnCheck: gzCheck("ln() "), 56 | tanCheck: (...operands: InternalNumber[]) => { 57 | let halfPi = new Decimal(90); 58 | 59 | switch (calculatorState.drgMode) { 60 | case "R": 61 | halfPi = new Decimal(PI).div(2); 62 | break; 63 | case "G": 64 | halfPi = new Decimal(50); 65 | break; 66 | } 67 | 68 | return { 69 | ok: !isOdd(div(operands[0], new InternalNumber("DEC", halfPi))), 70 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.TAN 71 | }; 72 | }, 73 | asinAcosCheck: (...operands: InternalNumber[]) => { 74 | const decValue = getDecValue(operands[0]); 75 | return { 76 | ok: decValue.gte(-1) && decValue.lte(1), 77 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.ASINACOS 78 | }; 79 | }, 80 | acoshCheck: (...operands: InternalNumber[]) => { 81 | const decValue = getDecValue(operands[0]); 82 | return { 83 | ok: decValue.gte(1), 84 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.ACOSH 85 | }; 86 | }, 87 | atanhCheck: (...operands: InternalNumber[]) => { 88 | const decValue = getDecValue(operands[0]); 89 | return { 90 | ok: decValue.gt(-1) && decValue.lt(1), 91 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.ATANH 92 | }; 93 | }, 94 | factCheck: (...operands: InternalNumber[]) => ({ 95 | ok: isNonNegativeInteger(operands[0]), 96 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.FACT 97 | }), 98 | invCheck: (...operands: InternalNumber[]) => ({ 99 | ok: !isZero(operands[0]), 100 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.INV 101 | }), 102 | nCrnPrCheck: (...operands: InternalNumber[]) => { 103 | if ( 104 | !isNonNegativeInteger(operands[0]) || 105 | !isNonNegativeInteger(operands[1]) 106 | ) { 107 | return { 108 | ok: false, 109 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.COMBINE_NOT_NNINT 110 | }; 111 | } 112 | 113 | if (getDecValue(operands[0]).lt(getDecValue(operands[1]))) { 114 | return { 115 | ok: false, 116 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.COMBINE_X_LT_Y 117 | }; 118 | } 119 | 120 | return { 121 | ok: true, 122 | msg: "" 123 | }; 124 | }, 125 | powCheck: (...operands: InternalNumber[]) => ({ 126 | ok: isZeroPositive(operands[0]) || getDecValue(operands[1]).isInteger(), 127 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.POW 128 | }), 129 | rootCheck: (...operands: InternalNumber[]) => { 130 | if (isZero(operands[0])) { 131 | return { 132 | ok: false, 133 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.ROOT_X_ZERO 134 | }; 135 | } 136 | 137 | if (isNegative(operands[1]) && !isOdd(operands[0])) { 138 | return { 139 | ok: false, 140 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS 141 | .ROOT_Y_NEG_X_NOT_ODD_INT 142 | }; 143 | } 144 | 145 | return { 146 | ok: true, 147 | msg: "" 148 | }; 149 | }, 150 | divCheck: (...operands: InternalNumber[]) => ({ 151 | ok: !isZero(operands[1]), 152 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.DIV 153 | }), 154 | recCheck: (...operands: InternalNumber[]) => ({ 155 | ok: isZeroPositive(operands[0]), 156 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.REC 157 | }), 158 | 159 | createDegreeCheck: (...operands: InternalNumber[]) => ({ 160 | ok: isZeroPositive(operands[1]) && isZeroPositive(operands[2]), 161 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.CREATE_DEGREE 162 | }), 163 | createFracCheck: (...operands: InternalNumber[]) => ({ 164 | ok: !isZero(operands[1]), 165 | msg: stringsRes.strings.CALC_CK_ERROR_MSGS.CREATE_FRAC 166 | }) 167 | }; 168 | -------------------------------------------------------------------------------- /modules/calc-core/preprocessing.ts: -------------------------------------------------------------------------------- 1 | import { 2 | KeyEntry, 3 | KEY_ENTRIES, 4 | isOpUnaryL, 5 | isOpUnaryR, 6 | isLBracketEqv, 7 | isRBracket, 8 | isVar, 9 | isNum, 10 | isOpBinary 11 | } from "./objs/key-entry"; 12 | 13 | function appendAns(entries: KeyEntry[]): void { 14 | if (entries.length === 1 && isOpUnaryL(entries[0])) { 15 | entries.push(KEY_ENTRIES.ANS); 16 | } 17 | } 18 | 19 | function reduceAddSub(entries: KeyEntry[]): void { 20 | // ++ -> + 21 | // +- -> - 22 | // -+ -> - 23 | // -- -> + 24 | 25 | let i = 0; 26 | 27 | while (i < entries.length - 1) { 28 | switch (entries[i].id) { 29 | case "ADD": 30 | switch (entries[i + 1].id) { 31 | case "ADD": 32 | case "SUB": 33 | entries.splice(i, 1); 34 | break; 35 | default: 36 | i++; 37 | break; 38 | } 39 | break; 40 | case "SUB": 41 | switch (entries[i + 1].id) { 42 | case "ADD": 43 | entries.splice(i + 1, 1); 44 | break; 45 | case "SUB": 46 | entries.splice(i, 1); 47 | entries[i] = KEY_ENTRIES.add; 48 | break; 49 | default: 50 | i++; 51 | break; 52 | } 53 | break; 54 | default: 55 | i++; 56 | break; 57 | } 58 | } 59 | } 60 | 61 | function reducePosNeg(entries: KeyEntry[]): void { 62 | // previous preprocessing eusures there is no continuous +,-. 63 | // X -> UnaryL | BinaryFn | BracketL | BinaryOp | Comma | 64 | // Y -> UnaryL | BinaryFn | BracketL | Var | Num 65 | // reduce rules: 66 | // X+Y => XY 67 | // X-Y => XnegY) 68 | // note that neg is a special UnaryL because no LBracket is shown. 69 | // that's why we manually add the RBracket. 70 | 71 | const isX = (x: KeyEntry) => 72 | isLBracketEqv(x) || isOpBinary(x) || x.id === "COMMA"; 73 | const isY = (x: KeyEntry) => isLBracketEqv(x) || isVar(x) || isNum(x); 74 | 75 | for (let i = -1; i <= entries.length - 3; i++) { 76 | if ( 77 | (i === -1 || (i >= 0 && isX(entries[i]))) && 78 | entries[i + 1].id === "ADD" && 79 | isY(entries[i + 2]) 80 | ) { 81 | entries.splice(i + 1, 1); 82 | // next search should start from X 83 | i--; 84 | } else if ( 85 | (i === -1 || (i >= 0 && isX(entries[i]))) && 86 | entries[i + 1].id === "SUB" && 87 | isY(entries[i + 2]) 88 | ) { 89 | entries[i + 1] = KEY_ENTRIES.neg; 90 | // for LBracketEqv, append a rBracket after its matching rBracket. 91 | // if it has no matching rBracket, do nothing and leave the two 92 | // padding jobs for next preprocessing step. 93 | if (isLBracketEqv(entries[i + 2])) { 94 | let bracketDiff = 0; 95 | let probeIndex = i + 3; 96 | while (probeIndex < entries.length) { 97 | if (isLBracketEqv(entries[probeIndex])) { 98 | bracketDiff++; 99 | } else if (isRBracket(entries[probeIndex])) { 100 | if (bracketDiff === 0) { 101 | break; 102 | } else { 103 | bracketDiff--; 104 | } 105 | } 106 | probeIndex++; 107 | } 108 | 109 | if (probeIndex >= entries.length) { 110 | continue; 111 | } else { 112 | entries.splice(probeIndex + 1, 0, KEY_ENTRIES.rBracket); 113 | } 114 | } else if (isNum(entries[i + 2])) { 115 | let probeIndex = i + 3; 116 | while ( 117 | probeIndex < entries.length && 118 | isNum(entries[probeIndex]) 119 | ) { 120 | probeIndex++; 121 | } 122 | 123 | entries.splice(probeIndex, 0, KEY_ENTRIES.rBracket); 124 | } else { 125 | entries.splice(i + 3, 0, KEY_ENTRIES.rBracket); 126 | } 127 | 128 | i--; 129 | } 130 | } 131 | } 132 | 133 | function padRBrackets(entries: KeyEntry[]): void { 134 | let bracketDiffCount = 0; 135 | 136 | for (const i of entries) { 137 | if (isLBracketEqv(i)) { 138 | bracketDiffCount++; 139 | } else if (i.id === ")") { 140 | bracketDiffCount--; 141 | } 142 | } 143 | 144 | if (bracketDiffCount > 0) { 145 | for (let i = 0; i < bracketDiffCount; i++) { 146 | entries.push(KEY_ENTRIES.rBracket); 147 | } 148 | } 149 | } 150 | 151 | function fillMul(entries: KeyEntry[]): void { 152 | // places to fill MUL: 153 | // ()() 154 | // ()Var 155 | // Var() 156 | // VarVar 157 | // Num() 158 | // NumVar 159 | // UnaryR() 160 | // UnaryRVar 161 | // Degree() 162 | // DegreeVar 163 | 164 | for (let i = 0; i < entries.length - 1; i++) { 165 | if ( 166 | isRBracket(entries[i]) || 167 | isVar(entries[i]) || 168 | isNum(entries[i]) || 169 | isOpUnaryR(entries[i]) || 170 | entries[i].id === "DEGREE" 171 | ) { 172 | if (isLBracketEqv(entries[i + 1]) || isVar(entries[i + 1])) { 173 | entries.splice(i + 1, 0, KEY_ENTRIES.mul); 174 | } 175 | } 176 | } 177 | } 178 | 179 | export function preprocess(entries_: KeyEntry[]): KeyEntry[] { 180 | // this will be reflected in screen 181 | appendAns(entries_); 182 | 183 | const entries = Object.assign([], entries_); 184 | reduceAddSub(entries); 185 | reducePosNeg(entries); 186 | padRBrackets(entries); 187 | fillMul(entries); 188 | 189 | return entries; 190 | } 191 | -------------------------------------------------------------------------------- /components/screen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { observer } from "mobx-react-lite"; 3 | import cs from "../observables/calculator-state"; 4 | import styles from "../styles/screen.module.scss"; 5 | import stringsRes from "../observables/strings-res"; 6 | import * as L from "../logics/screen"; 7 | 8 | export default class Screen extends React.Component { 9 | constructor(props: {}) { 10 | super(props); 11 | } 12 | 13 | ThisComponent = observer(() => ( 14 |
15 | {(cs.displayMode === "NORMAL_EDIT" || 16 | cs.displayMode === "NORMAL_SHOW" || 17 | cs.displayMode === "ERROR") && ( 18 |
19 |
20 | {cs.entries.map((x, i) => ( 21 | {x.id} L.onKeyEntryImgClick(i)} 33 | src={`data:image/svg+xml;utf8,${encodeURIComponent( 34 | x.svg 35 | )}`} 36 | /> 37 | ))} 38 | CURSOR 54 | L.onKeyEntryImgClick(cs.entries.length) 55 | } 56 | src={`data:image/svg+xml;utf8,${encodeURIComponent( 57 | '' 58 | )}`} 59 | /> 60 |
61 | 62 | {cs.displayMode === "ERROR" && ( 63 |
{cs.errorMessage}
64 | )} 65 | 66 | {cs.displayMode !== "ERROR" && ( 67 |
{cs.dispResult.toString()}
68 | )} 69 | 70 |
71 | {cs.funcMode === "NONE" ? "" : cs.funcMode} 72 | 73 | {cs.hypMode ? "HYP" : ""} 74 | 75 | {cs.drgMode} 76 |
77 |
78 | )} 79 | 80 | {cs.displayMode === "DRG" && ( 81 |
82 |
L.onDrgClick("D")}> 83 | {stringsRes.strings.DEG} 84 |
85 |
L.onDrgClick("R")}> 86 | {stringsRes.strings.RAD} 87 |
88 |
L.onDrgClick("G")}> 89 | {stringsRes.strings.GRA} 90 |
91 |
92 | )} 93 | 94 | {cs.displayMode === "CLEAR" && ( 95 |
96 |
L.onClearClick(0)}> 97 | {stringsRes.strings.CLEAR[0]} 98 |
99 |
L.onClearClick(1)}> 100 | {stringsRes.strings.CLEAR[1]} 101 |
102 |
L.onClearClick(2)}> 103 | {stringsRes.strings.CLEAR[2]} 104 |
105 |
106 | )} 107 | 108 | {cs.displayMode === "LANG" && ( 109 |
110 |
L.onLangClick("ZH_CN")}>中文
111 |
L.onLangClick("EN")}>English
112 |
113 | )} 114 | 115 | {cs.displayMode === "ABOUT" && ( 116 |
117 | {"Ernest's Web Calculator EC-82MS"} 118 | {"Made with LOVE❤️ by Ernest Cui"} 119 | {"August, 2022"} 120 |
121 | Repositories: 122 | 123 | Github 124 | 125 | Gitee 126 |
127 |
128 | )} 129 |
130 | )); 131 | 132 | render = () => ; 133 | } 134 | -------------------------------------------------------------------------------- /components/basic-keys.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "../styles/basic-keys.module.scss"; 3 | import Key from "./key"; 4 | import * as L from "../logics/basic-keys"; 5 | 6 | export default class BasicKeys extends React.Component { 7 | constructor(props: {}) { 8 | super(props); 9 | } 10 | 11 | render = () => ( 12 |
13 |
14 | 7} 17 | onClick={() => { 18 | L.onR1C1Click(); 19 | }} 20 | /> 21 | 8} 24 | onClick={() => { 25 | L.onR1C2Click(); 26 | }} 27 | /> 28 | 9} 31 | onClick={() => { 32 | L.onR1C3Click(); 33 | }} 34 | /> 35 | INS} 38 | content={DEL} 39 | onClick={() => { 40 | L.onR1C4Click(); 41 | }} 42 | /> 43 | ABOUT} 46 | content={AC} 47 | onClick={() => { 48 | L.onR1C5Click(); 49 | }} 50 | /> 51 |
52 | 53 |
54 | 4} 57 | onClick={() => { 58 | L.onR2C1Click(); 59 | }} 60 | /> 61 | 5} 64 | onClick={() => { 65 | L.onR2C2Click(); 66 | }} 67 | /> 68 | 6} 71 | onClick={() => { 72 | L.onR2C3Click(); 73 | }} 74 | /> 75 | ×} 78 | onClick={() => { 79 | L.onR2C4Click(); 80 | }} 81 | /> 82 | ÷} 85 | onClick={() => { 86 | L.onR2C5Click(); 87 | }} 88 | /> 89 |
90 | 91 |
92 | 1} 95 | onClick={() => { 96 | L.onR3C1Click(); 97 | }} 98 | /> 99 | 2} 102 | onClick={() => { 103 | L.onR3C2Click(); 104 | }} 105 | /> 106 | D} 109 | content={3} 110 | onClick={() => { 111 | L.onR3C3Click(); 112 | }} 113 | /> 114 | R} 117 | content={+} 118 | onClick={() => { 119 | L.onR3C4Click(); 120 | }} 121 | /> 122 | G} 125 | content={-} 126 | onClick={() => { 127 | L.onR3C5Click(); 128 | }} 129 | /> 130 |
131 | 132 |
133 | 0} 136 | onClick={() => { 137 | L.onR4C1Click(); 138 | }} 139 | /> 140 | Ran#} 143 | content={.} 144 | onClick={() => { 145 | L.onR4C2Click(); 146 | }} 147 | /> 148 | π} 151 | content={EXP} 152 | onClick={() => { 153 | L.onR4C3Click(); 154 | }} 155 | /> 156 | Ans} 159 | onClick={() => { 160 | L.onR4C4Click(); 161 | }} 162 | /> 163 | %} 166 | content={ = } 167 | onClick={() => { 168 | L.onR4C5Click(); 169 | }} 170 | /> 171 |
172 |
173 | ); 174 | } 175 | -------------------------------------------------------------------------------- /modules/calc-core/objs/operators.ts: -------------------------------------------------------------------------------- 1 | import { CHECK_FNS } from "./check-fns"; 2 | import type { CheckFn, OperationFn } from "./types"; 3 | import * as INM from "../../math/internal-number-math"; 4 | 5 | export interface Operator { 6 | id: string; 7 | argN: number; 8 | op: OperationFn; 9 | ck: CheckFn; 10 | } 11 | 12 | export const OPERATORS_UNARY_L: readonly Operator[] = [ 13 | { 14 | id: "CBRT", 15 | argN: 1, 16 | op: INM.cbrt, 17 | ck: CHECK_FNS.alwaysTrue 18 | }, 19 | { 20 | id: "SQRT", 21 | argN: 1, 22 | op: INM.sqrt, 23 | ck: CHECK_FNS.sqrtCheck 24 | }, 25 | { 26 | id: "LOG", 27 | argN: 1, 28 | op: INM.log10, 29 | ck: CHECK_FNS.logCheck 30 | }, 31 | { 32 | id: "LN", 33 | argN: 1, 34 | op: INM.ln, 35 | ck: CHECK_FNS.lnCheck 36 | }, 37 | { 38 | id: "EXP10", 39 | argN: 1, 40 | op: INM.exp10, 41 | ck: CHECK_FNS.alwaysTrue 42 | }, 43 | { 44 | id: "EXP", 45 | argN: 1, 46 | op: INM.exp, 47 | ck: CHECK_FNS.alwaysTrue 48 | }, 49 | { 50 | id: "SIN", 51 | argN: 1, 52 | op: INM.sin, 53 | ck: CHECK_FNS.alwaysTrue 54 | }, 55 | { 56 | id: "COS", 57 | argN: 1, 58 | op: INM.cos, 59 | ck: CHECK_FNS.alwaysTrue 60 | }, 61 | { 62 | id: "TAN", 63 | argN: 1, 64 | op: INM.tan, 65 | ck: CHECK_FNS.tanCheck 66 | }, 67 | { 68 | id: "SINH", 69 | argN: 1, 70 | op: INM.sinh, 71 | ck: CHECK_FNS.alwaysTrue 72 | }, 73 | { 74 | id: "COSH", 75 | argN: 1, 76 | op: INM.cosh, 77 | ck: CHECK_FNS.alwaysTrue 78 | }, 79 | { 80 | id: "TANH", 81 | argN: 1, 82 | op: INM.tanh, 83 | ck: CHECK_FNS.alwaysTrue 84 | }, 85 | { 86 | id: "ASIN", 87 | argN: 1, 88 | op: INM.asin, 89 | ck: CHECK_FNS.asinAcosCheck 90 | }, 91 | { 92 | id: "ACOS", 93 | argN: 1, 94 | op: INM.acos, 95 | ck: CHECK_FNS.asinAcosCheck 96 | }, 97 | { 98 | id: "ATAN", 99 | argN: 1, 100 | op: INM.atan, 101 | ck: CHECK_FNS.alwaysTrue 102 | }, 103 | { 104 | id: "ASINH", 105 | argN: 1, 106 | op: INM.asinh, 107 | ck: CHECK_FNS.alwaysTrue 108 | }, 109 | { 110 | id: "ACOSH", 111 | argN: 1, 112 | op: INM.acosh, 113 | ck: CHECK_FNS.acoshCheck 114 | }, 115 | { 116 | id: "ATANH", 117 | argN: 1, 118 | op: INM.atanh, 119 | ck: CHECK_FNS.atanhCheck 120 | }, 121 | { 122 | id: "NEG", 123 | argN: 1, 124 | op: INM.negative, 125 | ck: CHECK_FNS.alwaysTrue 126 | } 127 | ] as const; 128 | 129 | export const OPERATORS_UNARY_R: readonly Operator[] = [ 130 | { 131 | id: "FACT", 132 | argN: 1, 133 | op: INM.fact, 134 | ck: CHECK_FNS.factCheck 135 | }, 136 | { 137 | id: "INV", 138 | argN: 1, 139 | op: INM.inv, 140 | ck: CHECK_FNS.invCheck 141 | }, 142 | { 143 | id: "CUBE", 144 | argN: 1, 145 | op: INM.cube, 146 | ck: CHECK_FNS.alwaysTrue 147 | }, 148 | { 149 | id: "SQR", 150 | argN: 1, 151 | op: INM.sqr, 152 | ck: CHECK_FNS.alwaysTrue 153 | }, 154 | { 155 | id: "PERCENT", 156 | argN: 1, 157 | op: INM.percent, 158 | ck: CHECK_FNS.alwaysTrue 159 | }, 160 | { 161 | id: "FROM_D", 162 | argN: 1, 163 | op: INM.fromD, 164 | ck: CHECK_FNS.alwaysTrue 165 | }, 166 | { 167 | id: "FROM_R", 168 | argN: 1, 169 | op: INM.fromR, 170 | ck: CHECK_FNS.alwaysTrue 171 | }, 172 | { 173 | id: "FROM_G", 174 | argN: 1, 175 | op: INM.fromG, 176 | ck: CHECK_FNS.alwaysTrue 177 | } 178 | ] as const; 179 | 180 | export const OPERATORS_BINARY: readonly Operator[] = [ 181 | { 182 | id: "NPR", 183 | argN: 2, 184 | op: INM.nPr, 185 | ck: CHECK_FNS.nCrnPrCheck 186 | }, 187 | { 188 | id: "NCR", 189 | argN: 2, 190 | op: INM.nCr, 191 | ck: CHECK_FNS.nCrnPrCheck 192 | }, 193 | { 194 | id: "POW", 195 | argN: 2, 196 | op: INM.pow, 197 | ck: CHECK_FNS.powCheck 198 | }, 199 | { 200 | id: "ROOT", 201 | argN: 2, 202 | op: INM.root, 203 | ck: CHECK_FNS.rootCheck 204 | }, 205 | { 206 | id: "ADD", 207 | argN: 2, 208 | op: INM.add, 209 | ck: CHECK_FNS.alwaysTrue 210 | }, 211 | { 212 | id: "SUB", 213 | argN: 2, 214 | op: INM.sub, 215 | ck: CHECK_FNS.alwaysTrue 216 | }, 217 | { 218 | id: "MUL", 219 | argN: 2, 220 | op: INM.mul, 221 | ck: CHECK_FNS.alwaysTrue 222 | }, 223 | { 224 | id: "DIV", 225 | argN: 2, 226 | op: INM.div, 227 | ck: CHECK_FNS.divCheck 228 | }, 229 | { 230 | id: "FRAC", 231 | argN: 2, 232 | op: INM.div, 233 | ck: CHECK_FNS.createFracCheck 234 | } 235 | ] as const; 236 | 237 | export const OPERATORS_BINARY_FN: readonly Operator[] = [ 238 | { 239 | id: "POL", 240 | argN: 2, 241 | op: INM.pol, 242 | ck: CHECK_FNS.alwaysTrue 243 | }, 244 | { 245 | id: "REC", 246 | argN: 2, 247 | op: INM.rec, 248 | ck: CHECK_FNS.recCheck 249 | } 250 | ] as const; 251 | 252 | export const OPERATORS_TERNARY_FN: readonly Operator[] = [ 253 | { 254 | id: "CREATE_DEGREE", 255 | argN: 3, 256 | op: INM.createDegree, 257 | ck: CHECK_FNS.createDegreeCheck 258 | } 259 | // { 260 | // id: "CREATE_FRAC", 261 | // argN: 3, 262 | // op: INM.createFrac, 263 | // ck: CHECK_FNS.createFracCheck 264 | // } 265 | ] as const; 266 | 267 | const FULL_OPERATORS: readonly Operator[] = OPERATORS_UNARY_L.concat( 268 | OPERATORS_UNARY_R 269 | ) 270 | .concat(OPERATORS_BINARY) 271 | .concat(OPERATORS_BINARY_FN) 272 | .concat(OPERATORS_TERNARY_FN); 273 | 274 | export function getOperatorById(id: string): Operator { 275 | return FULL_OPERATORS.find(x => x.id === id)!; 276 | } 277 | -------------------------------------------------------------------------------- /observables/calculator-memory.ts: -------------------------------------------------------------------------------- 1 | import Decimal from "decimal.js"; 2 | import { 3 | InternalNumber, 4 | fromStorageObjectString 5 | } from "../modules/calc-core/objs/internal-number"; 6 | 7 | class CalculatorMemory { 8 | ans_: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 9 | A_: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 10 | B_: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 11 | C_: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 12 | D_: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 13 | E_: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 14 | F_: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 15 | X_: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 16 | Y_: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 17 | M_: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 18 | 19 | get ans(): InternalNumber { 20 | return this.ans_; 21 | } 22 | get A(): InternalNumber { 23 | return this.A_; 24 | } 25 | get B(): InternalNumber { 26 | return this.B_; 27 | } 28 | get C(): InternalNumber { 29 | return this.C_; 30 | } 31 | get D(): InternalNumber { 32 | return this.D_; 33 | } 34 | get E(): InternalNumber { 35 | return this.E_; 36 | } 37 | get F(): InternalNumber { 38 | return this.F_; 39 | } 40 | get X(): InternalNumber { 41 | return this.X_; 42 | } 43 | get Y(): InternalNumber { 44 | return this.Y_; 45 | } 46 | get M(): InternalNumber { 47 | return this.M_; 48 | } 49 | 50 | set ans(value: InternalNumber) { 51 | this.ans_ = value; 52 | localStorage.setItem("memAns", this.ans_.toStorageObjectString()); 53 | } 54 | set A(value: InternalNumber) { 55 | this.A_ = value; 56 | localStorage.setItem("memA", this.A_.toStorageObjectString()); 57 | } 58 | set B(value: InternalNumber) { 59 | this.B_ = value; 60 | localStorage.setItem("memB", this.B_.toStorageObjectString()); 61 | } 62 | set C(value: InternalNumber) { 63 | this.C_ = value; 64 | localStorage.setItem("memC", this.C_.toStorageObjectString()); 65 | } 66 | set D(value: InternalNumber) { 67 | this.D_ = value; 68 | localStorage.setItem("memD", this.D_.toStorageObjectString()); 69 | } 70 | set E(value: InternalNumber) { 71 | this.E_ = value; 72 | localStorage.setItem("memE", this.E_.toStorageObjectString()); 73 | } 74 | set F(value: InternalNumber) { 75 | this.F_ = value; 76 | localStorage.setItem("memF", this.F_.toStorageObjectString()); 77 | } 78 | set X(value: InternalNumber) { 79 | this.X_ = value; 80 | localStorage.setItem("memX", this.X_.toStorageObjectString()); 81 | } 82 | set Y(value: InternalNumber) { 83 | this.Y_ = value; 84 | localStorage.setItem("memY", this.Y_.toStorageObjectString()); 85 | } 86 | set M(value: InternalNumber) { 87 | this.M_ = value; 88 | localStorage.setItem("memM", this.M_.toStorageObjectString()); 89 | } 90 | 91 | clear() { 92 | this.ans = new InternalNumber("DEC", new Decimal(0)); 93 | this.A = new InternalNumber("DEC", new Decimal(0)); 94 | this.B = new InternalNumber("DEC", new Decimal(0)); 95 | this.C = new InternalNumber("DEC", new Decimal(0)); 96 | this.D = new InternalNumber("DEC", new Decimal(0)); 97 | this.E = new InternalNumber("DEC", new Decimal(0)); 98 | this.F = new InternalNumber("DEC", new Decimal(0)); 99 | this.X = new InternalNumber("DEC", new Decimal(0)); 100 | this.Y = new InternalNumber("DEC", new Decimal(0)); 101 | this.M = new InternalNumber("DEC", new Decimal(0)); 102 | } 103 | 104 | loadFromLocalStorage() { 105 | let key = "memAns"; 106 | if (localStorage.getItem(key) !== null) { 107 | this.ans_ = fromStorageObjectString(localStorage.getItem(key)!); 108 | } else { 109 | this.ans = new InternalNumber("DEC", new Decimal(0)); 110 | } 111 | 112 | key = "memA"; 113 | if (localStorage.getItem(key) !== null) { 114 | this.A_ = fromStorageObjectString(localStorage.getItem(key)!); 115 | } else { 116 | this.A = new InternalNumber("DEC", new Decimal(0)); 117 | } 118 | 119 | key = "memB"; 120 | if (localStorage.getItem(key) !== null) { 121 | this.B_ = fromStorageObjectString(localStorage.getItem(key)!); 122 | } else { 123 | this.B = new InternalNumber("DEC", new Decimal(0)); 124 | } 125 | 126 | key = "memC"; 127 | if (localStorage.getItem(key) !== null) { 128 | this.C_ = fromStorageObjectString(localStorage.getItem(key)!); 129 | } else { 130 | this.C = new InternalNumber("DEC", new Decimal(0)); 131 | } 132 | 133 | key = "memD"; 134 | if (localStorage.getItem(key) !== null) { 135 | this.D_ = fromStorageObjectString(localStorage.getItem(key)!); 136 | } else { 137 | this.D = new InternalNumber("DEC", new Decimal(0)); 138 | } 139 | 140 | key = "memE"; 141 | if (localStorage.getItem(key) !== null) { 142 | this.E_ = fromStorageObjectString(localStorage.getItem(key)!); 143 | } else { 144 | this.E = new InternalNumber("DEC", new Decimal(0)); 145 | } 146 | 147 | key = "memF"; 148 | if (localStorage.getItem(key) !== null) { 149 | this.F_ = fromStorageObjectString(localStorage.getItem(key)!); 150 | } else { 151 | this.F = new InternalNumber("DEC", new Decimal(0)); 152 | } 153 | 154 | key = "memX"; 155 | if (localStorage.getItem(key) !== null) { 156 | this.X_ = fromStorageObjectString(localStorage.getItem(key)!); 157 | } else { 158 | this.X = new InternalNumber("DEC", new Decimal(0)); 159 | } 160 | 161 | key = "memY"; 162 | if (localStorage.getItem(key) !== null) { 163 | this.Y_ = fromStorageObjectString(localStorage.getItem(key)!); 164 | } else { 165 | this.Y = new InternalNumber("DEC", new Decimal(0)); 166 | } 167 | 168 | key = "memM"; 169 | if (localStorage.getItem(key) !== null) { 170 | this.M_ = fromStorageObjectString(localStorage.getItem(key)!); 171 | } else { 172 | this.M = new InternalNumber("DEC", new Decimal(0)); 173 | } 174 | } 175 | } 176 | 177 | export default new CalculatorMemory(); 178 | -------------------------------------------------------------------------------- /observables/strings-res.ts: -------------------------------------------------------------------------------- 1 | import { makeAutoObservable } from "mobx"; 2 | 3 | const STRINGS_ZH_CN_ERROR_PREFIXES = { 4 | PARSE: "语法解析错误: ", 5 | CALC: "计算错误: " 6 | }; 7 | 8 | const STRINGS_ZH_CN = { 9 | APP_TITLE: "EC-82MS Online Scientific Calculator", 10 | DEG: "角度", 11 | RAD: "弧度", 12 | GRA: "斜度", 13 | CLEAR: ["清除存储", "清除模式", "清除全部"], 14 | PARSE_ERROR_MSGS: { 15 | MISSING_L_BRACKET: STRINGS_ZH_CN_ERROR_PREFIXES.PARSE + "缺少左括号 (", 16 | MISSING_R_BRACKET: STRINGS_ZH_CN_ERROR_PREFIXES.PARSE + "缺少右括号 )", 17 | UNEXPECTED_ENTRY: 18 | STRINGS_ZH_CN_ERROR_PREFIXES.PARSE + "不合法的按键输入", 19 | INSUFFICENT_OPERANDS: 20 | STRINGS_ZH_CN_ERROR_PREFIXES.PARSE + "操作数数量不足", 21 | MISSING_COMMA: STRINGS_ZH_CN_ERROR_PREFIXES.PARSE + "缺少逗号 ,", 22 | TOO_MANY_DEGREE_SYMBOL: 23 | STRINGS_ZH_CN_ERROR_PREFIXES.PARSE + "度数 °符号过多", 24 | TOO_MANY_DECIMAL_POINT: 25 | STRINGS_ZH_CN_ERROR_PREFIXES.PARSE + "小数点 .过多", 26 | EMPTY_BRACKETS: STRINGS_ZH_CN_ERROR_PREFIXES.PARSE + "不允许空括号" 27 | }, 28 | CALC_CK_ERROR_MSGS: { 29 | GOT_NOT_POSITIVE: STRINGS_ZH_CN_ERROR_PREFIXES.CALC + "接收到了非正值", 30 | SQRT: STRINGS_ZH_CN_ERROR_PREFIXES.CALC + "sqrt()接收到了负值", 31 | TAN: STRINGS_ZH_CN_ERROR_PREFIXES.CALC + "tan()接收到了π/2的奇数倍", 32 | ASINACOS: 33 | STRINGS_ZH_CN_ERROR_PREFIXES.CALC + 34 | "arcsin()或arccos()接收到了[-1,1]以外的值", 35 | ACOSH: 36 | STRINGS_ZH_CN_ERROR_PREFIXES.CALC + "acosh()接收到了[1,+∞)以外的值", 37 | ATANH: 38 | STRINGS_ZH_CN_ERROR_PREFIXES.CALC + "atanh()接收到了(-1,1)以外的值", 39 | FACT: STRINGS_ZH_CN_ERROR_PREFIXES.CALC + "阶乘操作数不是非负整数", 40 | INV: STRINGS_ZH_CN_ERROR_PREFIXES.CALC + "不能求0的倒数", 41 | COMBINE_NOT_NNINT: 42 | STRINGS_ZH_CN_ERROR_PREFIXES.CALC + "排列组合操作数不是非负整数", 43 | COMBINE_X_LT_Y: STRINGS_ZH_CN_ERROR_PREFIXES.CALC + "排列组合操作数n { 7 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 8 | return; 9 | } 10 | 11 | if (cs.funcMode === "NONE") { 12 | cs.inputEntry(KEY_ENTRIES.n7); 13 | } 14 | 15 | cs.clearFuncMode(); 16 | }; 17 | 18 | export const onR1C2Click = () => { 19 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 20 | return; 21 | } 22 | 23 | if (cs.funcMode === "NONE") { 24 | cs.inputEntry(KEY_ENTRIES.n8); 25 | } 26 | 27 | cs.clearFuncMode(); 28 | }; 29 | 30 | export const onR1C3Click = () => { 31 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 32 | return; 33 | } 34 | 35 | if (cs.funcMode === "NONE") { 36 | cs.inputEntry(KEY_ENTRIES.n9); 37 | } 38 | 39 | cs.clearFuncMode(); 40 | }; 41 | 42 | export const onR1C4Click = () => { 43 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 44 | return; 45 | } 46 | 47 | if (cs.funcMode === "NONE") { 48 | if (cs.displayMode === "NORMAL_EDIT") { 49 | cs.deleteEntry(); 50 | } 51 | } else if (cs.funcMode === "SHIFT") { 52 | cs.toggleIsInsert(); 53 | } 54 | 55 | cs.clearFuncMode(); 56 | }; 57 | 58 | export const onR1C5Click = () => { 59 | if (cs.funcMode === "SHIFT") { 60 | cs.setDisplayMode("ABOUT"); 61 | } else { 62 | cs.setDisplayMode("NORMAL_EDIT"); 63 | cs.entries = []; 64 | cs.setCursorIndex(0); 65 | cs.setEntryIndex(cs.historyEntries.length); 66 | cs.dispResult = new InternalNumber("DEC", new Decimal(0)); 67 | } 68 | 69 | cs.clearFuncMode(); 70 | }; 71 | 72 | export const onR2C1Click = () => { 73 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 74 | return; 75 | } 76 | 77 | if (cs.funcMode === "NONE") { 78 | cs.inputEntry(KEY_ENTRIES.n4); 79 | } 80 | 81 | cs.clearFuncMode(); 82 | }; 83 | 84 | export const onR2C2Click = () => { 85 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 86 | return; 87 | } 88 | 89 | if (cs.funcMode === "NONE") { 90 | cs.inputEntry(KEY_ENTRIES.n5); 91 | } 92 | 93 | cs.clearFuncMode(); 94 | }; 95 | 96 | export const onR2C3Click = () => { 97 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 98 | return; 99 | } 100 | 101 | if (cs.funcMode === "NONE") { 102 | cs.inputEntry(KEY_ENTRIES.n6); 103 | } 104 | 105 | cs.clearFuncMode(); 106 | }; 107 | 108 | export const onR2C4Click = () => { 109 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 110 | return; 111 | } 112 | 113 | if (cs.funcMode === "NONE") { 114 | cs.inputEntry(KEY_ENTRIES.mul); 115 | } 116 | 117 | cs.clearFuncMode(); 118 | }; 119 | 120 | export const onR2C5Click = () => { 121 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 122 | return; 123 | } 124 | 125 | if (cs.funcMode === "NONE") { 126 | cs.inputEntry(KEY_ENTRIES.div); 127 | } 128 | 129 | cs.clearFuncMode(); 130 | }; 131 | 132 | export const onR3C1Click = () => { 133 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 134 | return; 135 | } 136 | 137 | if (cs.funcMode === "NONE") { 138 | cs.inputEntry(KEY_ENTRIES.n1); 139 | } 140 | 141 | cs.clearFuncMode(); 142 | }; 143 | 144 | export const onR3C2Click = () => { 145 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 146 | return; 147 | } 148 | 149 | if (cs.funcMode === "NONE") { 150 | cs.inputEntry(KEY_ENTRIES.n2); 151 | } 152 | 153 | cs.clearFuncMode(); 154 | }; 155 | 156 | export const onR3C3Click = () => { 157 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 158 | return; 159 | } 160 | 161 | if (cs.funcMode === "NONE") { 162 | cs.inputEntry(KEY_ENTRIES.n3); 163 | } else if (cs.funcMode === "SHIFT") { 164 | cs.inputEntry(KEY_ENTRIES.fromD); 165 | } 166 | 167 | cs.clearFuncMode(); 168 | }; 169 | 170 | export const onR3C4Click = () => { 171 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 172 | return; 173 | } 174 | 175 | if (cs.funcMode === "NONE") { 176 | cs.inputEntry(KEY_ENTRIES.add); 177 | } else if (cs.funcMode === "SHIFT") { 178 | cs.inputEntry(KEY_ENTRIES.fromR); 179 | } 180 | 181 | cs.clearFuncMode(); 182 | }; 183 | 184 | export const onR3C5Click = () => { 185 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 186 | return; 187 | } 188 | 189 | if (cs.funcMode === "NONE") { 190 | cs.inputEntry(KEY_ENTRIES.sub); 191 | } else if (cs.funcMode === "SHIFT") { 192 | cs.inputEntry(KEY_ENTRIES.fromG); 193 | } 194 | 195 | cs.clearFuncMode(); 196 | }; 197 | 198 | export const onR4C1Click = () => { 199 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 200 | return; 201 | } 202 | 203 | if (cs.funcMode === "NONE") { 204 | cs.inputEntry(KEY_ENTRIES.n0); 205 | } 206 | 207 | cs.clearFuncMode(); 208 | }; 209 | 210 | export const onR4C2Click = () => { 211 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 212 | return; 213 | } 214 | 215 | if (cs.funcMode === "NONE") { 216 | cs.inputEntry(KEY_ENTRIES.nDot); 217 | } else if (cs.funcMode === "SHIFT") { 218 | cs.inputEntry(KEY_ENTRIES.RAN); 219 | } 220 | 221 | cs.clearFuncMode(); 222 | }; 223 | 224 | export const onR4C3Click = () => { 225 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 226 | return; 227 | } 228 | 229 | if (cs.funcMode === "NONE") { 230 | cs.inputEntry(KEY_ENTRIES.exp10); 231 | } else if (cs.funcMode === "SHIFT") { 232 | cs.inputEntry(KEY_ENTRIES.PI); 233 | } 234 | 235 | cs.clearFuncMode(); 236 | }; 237 | 238 | export const onR4C4Click = () => { 239 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 240 | return; 241 | } 242 | 243 | if (cs.funcMode === "NONE") { 244 | cs.inputEntry(KEY_ENTRIES.ANS); 245 | } 246 | 247 | cs.clearFuncMode(); 248 | }; 249 | 250 | export const onR4C5Click = () => { 251 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 252 | return; 253 | } 254 | 255 | if (cs.funcMode === "NONE") { 256 | cs.calculate(); 257 | } else if (cs.funcMode === "SHIFT") { 258 | cs.inputEntry(KEY_ENTRIES.percent); 259 | } 260 | 261 | cs.clearFuncMode(); 262 | }; 263 | -------------------------------------------------------------------------------- /test/math-deprecated/degree-test.ts0: -------------------------------------------------------------------------------- 1 | import { assertEquals, assertObjectEquals, runTests } from "../test-core/test-core"; 2 | import * as DGB from "../../modules/math/value-type-basics/degree-basics"; 3 | 4 | function testTryToFracValue() { 5 | assertObjectEquals({ ok: true, frac: { u: 0, d: 1 } }, 6 | DGB.tryToFracValue({ d: 0, m: 0, s: 0, neg: false })); 7 | 8 | assertObjectEquals({ ok: true, frac: { u: 1, d: 1 } }, 9 | DGB.tryToFracValue({ d: 1, m: 0, s: 0, neg: false })); 10 | 11 | assertObjectEquals({ ok: true, frac: { u: -1, d: 1 } }, 12 | DGB.tryToFracValue({ d: 1, m: 0, s: 0, neg: true })); 13 | 14 | assertObjectEquals({ ok: true, frac: { u: 5, d: 4 } }, 15 | DGB.tryToFracValue({ d: 1, m: 15, s: 0, neg: false })); 16 | 17 | assertObjectEquals({ ok: true, frac: { u: 1, d: 3 } }, 18 | DGB.tryToFracValue({ d: 0, m: 20, s: 0, neg: false })); 19 | 20 | assertObjectEquals({ ok: true, frac: { u: -1, d: 3 } }, 21 | DGB.tryToFracValue({ d: 0, m: 20, s: 0, neg: true })); 22 | 23 | assertObjectEquals({ ok: true, frac: { u: 1, d: 120 } }, 24 | DGB.tryToFracValue({ d: 0, m: 0, s: 30, neg: false })); 25 | 26 | assertObjectEquals({ ok: true, frac: { u: 12359, d: 3600 } }, 27 | DGB.tryToFracValue({ d: 3, m: 25, s: 59, neg: false })); 28 | 29 | assertObjectEquals({ ok: true, frac: { u: -3001, d: 3000 } }, 30 | DGB.tryToFracValue({ d: 1, m: 0, s: 1.2, neg: true })); 31 | } 32 | 33 | function testFromDmsNeg() { 34 | assertObjectEquals({ d: 0, m: 0, s: 0, neg: false }, 35 | DGB.fromDmsNeg(0, 0, 0, false)); 36 | 37 | assertObjectEquals({ d: 1, m: 0, s: 0, neg: false }, 38 | DGB.fromDmsNeg(1, 0, 0, false)); 39 | 40 | assertObjectEquals({ d: 1, m: 12, s: 0, neg: true }, 41 | DGB.fromDmsNeg(1.2, 0, 0, true)); 42 | 43 | assertObjectEquals({ d: 1, m: 13, s: 19.4, neg: false }, 44 | DGB.fromDmsNeg(1.2, 1.3, 1.4, false)); 45 | 46 | assertObjectEquals({ d: 2, m: 0, s: 0, neg: false }, 47 | DGB.fromDmsNeg(1, 60, 0, false)); 48 | 49 | assertObjectEquals({ d: 2, m: 5, s: 0, neg: false }, 50 | DGB.fromDmsNeg(1, 65, 0, false)); 51 | 52 | assertObjectEquals({ d: 2, m: 1, s: 2.5, neg: false }, 53 | DGB.fromDmsNeg(1, 60, 62.5, false)); 54 | 55 | assertObjectEquals({ d: 5, m: 46, s: 28.5, neg: true }, 56 | DGB.fromDmsNeg(1.5, 250.8, 340.5, true)); 57 | } 58 | 59 | function testIsAbsGreaterThan() { 60 | assertEquals(false, DGB.isAbsGreaterThan( 61 | { d: 0, m: 0, s: 0, neg: false }, 62 | { d: 0, m: 0, s: 0, neg: false } 63 | )); 64 | 65 | assertEquals(false, DGB.isAbsGreaterThan( 66 | { d: 0, m: 0, s: 0, neg: false }, 67 | { d: 1, m: 0, s: 0, neg: false } 68 | )); 69 | 70 | assertEquals(false, DGB.isAbsGreaterThan( 71 | { d: 0, m: 0, s: 0, neg: false }, 72 | { d: 0, m: 1, s: 0, neg: false } 73 | )); 74 | 75 | assertEquals(false, DGB.isAbsGreaterThan( 76 | { d: 0, m: 0, s: 0, neg: false }, 77 | { d: 0, m: 0, s: 1, neg: false } 78 | )); 79 | 80 | assertEquals(true, DGB.isAbsGreaterThan( 81 | { d: 1, m: 0, s: 0, neg: false }, 82 | { d: 0, m: 0, s: 0, neg: false } 83 | )); 84 | 85 | assertEquals(true, DGB.isAbsGreaterThan( 86 | { d: 0, m: 1, s: 0, neg: false }, 87 | { d: 0, m: 0, s: 0, neg: false } 88 | )); 89 | 90 | assertEquals(true, DGB.isAbsGreaterThan( 91 | { d: 0, m: 0, s: 1, neg: false }, 92 | { d: 0, m: 0, s: 0, neg: false } 93 | )); 94 | 95 | assertEquals(true, DGB.isAbsGreaterThan( 96 | { d: 2, m: 0, s: 0, neg: false }, 97 | { d: 1, m: 59, s: 59.9, neg: false } 98 | )); 99 | 100 | assertEquals(true, DGB.isAbsGreaterThan( 101 | { d: 0, m: 59, s: 58, neg: false }, 102 | { d: 0, m: 58, s: 59, neg: false } 103 | )); 104 | 105 | assertEquals(true, DGB.isAbsGreaterThan( 106 | { d: 0, m: 59, s: 58, neg: false }, 107 | { d: 0, m: 59, s: 57.5, neg: false } 108 | )); 109 | } 110 | 111 | function testAddDegree() { 112 | // simple cases 113 | assertObjectEquals({ d: 0, m: 0, s: 0, neg: false }, 114 | DGB.addDegree({ d: 0, m: 0, s: 0, neg: false }, { d: 0, m: 0, s: 0, neg: false })); 115 | 116 | assertObjectEquals({ d: 0, m: 0, s: 0, neg: false }, 117 | DGB.addDegree({ d: 0, m: 0, s: 0, neg: false }, { d: 0, m: 0, s: 0, neg: true })); 118 | 119 | assertObjectEquals({ d: 1, m: 2, s: 3, neg: false }, 120 | DGB.addDegree({ d: 0, m: 0, s: 0, neg: false }, { d: 1, m: 2, s: 3, neg: false })); 121 | 122 | // both !neg 123 | assertObjectEquals({ d: 18, m: 29, s: 40, neg: false }, 124 | DGB.addDegree({ d: 1, m: 2, s: 3, neg: false }, { d: 17, m: 27, s: 37, neg: false })); 125 | 126 | assertObjectEquals({ d: 45, m: 7, s: 28, neg: false }, 127 | DGB.addDegree({ d: 11, m: 22, s: 33, neg: false }, { d: 33, m: 44, s: 55, neg: false })); 128 | 129 | // both neg 130 | assertObjectEquals({ d: 18, m: 29, s: 40, neg: true }, 131 | DGB.addDegree({ d: 1, m: 2, s: 3, neg: true }, { d: 17, m: 27, s: 37, neg: true })); 132 | 133 | assertObjectEquals({ d: 45, m: 7, s: 28, neg: true }, 134 | DGB.addDegree({ d: 11, m: 22, s: 33, neg: true }, { d: 33, m: 44, s: 55, neg: true })); 135 | 136 | // one neg, one !neg 137 | // abs(neg) greater 138 | assertObjectEquals({ d: 10, m: 20, s: 30, neg: true }, 139 | DGB.addDegree({ d: 0, m: 0, s: 0, neg: false }, { d: 10, m: 20, s: 30, neg: true })); 140 | 141 | assertObjectEquals({ d: 10, m: 20, s: 30, neg: true }, 142 | DGB.addDegree({ d: 10, m: 20, s: 30, neg: true }, { d: 0, m: 0, s: 0, neg: false })); 143 | 144 | assertObjectEquals({ d: 3, m: 57, s: 56, neg: true }, 145 | DGB.addDegree({ d: 1, m: 7, s: 9, neg: false }, { d: 5, m: 5, s: 5, neg: true })); 146 | 147 | assertObjectEquals({ d: 3, m: 57, s: 56, neg: true }, 148 | DGB.addDegree({ d: 5, m: 5, s: 5, neg: true }, { d: 1, m: 7, s: 9, neg: false })); 149 | 150 | assertObjectEquals({ d: 0, m: 0, s: 11, neg: true }, 151 | DGB.addDegree({ d: 3, m: 1, s: 50, neg: false }, { d: 3, m: 2, s: 1, neg: true })); 152 | 153 | assertObjectEquals({ d: 0, m: 0, s: 11, neg: true }, 154 | DGB.addDegree({ d: 3, m: 2, s: 1, neg: true }, { d: 3, m: 1, s: 50, neg: false })); 155 | 156 | // abs(!neg) greater 157 | assertObjectEquals({ d: 2, m: 38, s: 48, neg: false }, 158 | DGB.addDegree({ d: 3, m: 52, s: 4, neg: false }, { d: 1, m: 13, s: 16, neg: true })); 159 | 160 | assertObjectEquals({ d: 2, m: 38, s: 48, neg: false }, 161 | DGB.addDegree({ d: 1, m: 13, s: 16, neg: true },{ d: 3, m: 52, s: 4, neg: false })); 162 | 163 | assertObjectEquals({ d: 1, m: 52, s: 58, neg: false }, 164 | DGB.addDegree({ d: 3, m: 52, s: 4, neg: false }, { d: 1, m: 59, s: 6, neg: true })); 165 | 166 | assertObjectEquals({ d: 1, m: 52, s: 58, neg: false }, 167 | DGB.addDegree({ d: 1, m: 59, s: 6, neg: true }, { d: 3, m: 52, s: 4, neg: false })); 168 | 169 | // abs equal 170 | assertObjectEquals({ d: 0, m: 0, s: 0, neg: false }, 171 | DGB.addDegree({ d: 3, m: 1, s: 50, neg: false }, { d: 3, m: 1, s: 50, neg: true })); 172 | } 173 | 174 | runTests( 175 | testTryToFracValue, 176 | testFromDmsNeg, 177 | 178 | testIsAbsGreaterThan, 179 | testAddDegree 180 | ); -------------------------------------------------------------------------------- /observables/calculator-state.ts: -------------------------------------------------------------------------------- 1 | import { makeAutoObservable } from "mobx"; 2 | import { 3 | KeyEntry, 4 | isOpBinary, 5 | isOpUnaryR, 6 | KEY_ENTRIES 7 | } from "../modules/calc-core/objs/key-entry"; 8 | import Decimal from "decimal.js"; 9 | import { InternalNumber } from "../modules/calc-core/objs/internal-number"; 10 | import { 11 | getDegreeValue, 12 | getDecValue, 13 | getFracValue 14 | } from "../modules/math/internal-number-math"; 15 | import { preprocess } from "../modules/calc-core/preprocessing"; 16 | import { parse } from "../modules/calc-core/parse"; 17 | import { calculate } from "../modules/calc-core/calculate"; 18 | import calculatorMemory from "./calculator-memory"; 19 | 20 | type CalculatorDisplayMode = 21 | | "NORMAL_EDIT" 22 | | "NORMAL_SHOW" 23 | | "ERROR" 24 | | "CLEAR" 25 | | "DRG" 26 | | "LANG" 27 | | "ABOUT"; 28 | export type CalculatorDRGMode = "D" | "R" | "G"; 29 | type CalculatorFuncMode = "NONE" | "SHIFT" | "ALPHA" | "STO" | "RCL"; 30 | 31 | class CalculatorState { 32 | constructor() { 33 | makeAutoObservable(this); 34 | } 35 | 36 | historyEntries: Array = []; 37 | entries: KeyEntry[] = []; 38 | 39 | entryIndex: number = 0; 40 | cursorIndex: number = 0; 41 | 42 | calcResult: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 43 | dispResult: InternalNumber = new InternalNumber("DEC", new Decimal(0)); 44 | 45 | errorMessage: string = ""; 46 | 47 | /////////////// Modes 48 | isInsert: boolean = true; 49 | 50 | displayMode: CalculatorDisplayMode = "NORMAL_EDIT"; 51 | 52 | // degree, radian or grade 53 | drgMode: CalculatorDRGMode = "D"; 54 | 55 | // special function mode: shift, alpha, ... 56 | funcMode: CalculatorFuncMode = "NONE"; 57 | 58 | // hyp mode, a standalone function mode 59 | hypMode: boolean = false; 60 | 61 | inputEntry(ke: KeyEntry) { 62 | switch (this.displayMode) { 63 | case "NORMAL_EDIT": 64 | if (this.isInsert) { 65 | this.entries.splice(this.cursorIndex, 0, ke); 66 | this.setCursorIndex(this.cursorIndex + 1); 67 | } else { 68 | if (this.cursorIndex >= this.entries.length) { 69 | this.entries.push(ke); 70 | this.setCursorIndex(this.entries.length); 71 | } else { 72 | this.entries[this.cursorIndex] = ke; 73 | } 74 | } 75 | break; 76 | case "NORMAL_SHOW": 77 | if (isOpBinary(ke) || isOpUnaryR(ke)) { 78 | this.entries = [KEY_ENTRIES.ANS, ke]; 79 | } else { 80 | this.entries = [ke]; 81 | } 82 | 83 | this.setCursorIndex(this.entries.length); 84 | // start new entry 85 | this.setEntryIndex(this.historyEntries.length); 86 | this.displayMode = "NORMAL_EDIT"; 87 | break; 88 | } 89 | } 90 | 91 | deleteEntry() { 92 | if (this.displayMode === "NORMAL_EDIT") { 93 | // when cursor is not at beginning, delete the entry before cursor 94 | if (this.cursorIndex - 1 >= 0) { 95 | this.entries.splice(this.cursorIndex - 1, 1); 96 | this.setCursorIndex(this.cursorIndex - 1); 97 | } 98 | // if cursor is at beginning, delete the first entry 99 | else if (this.cursorIndex === 0) { 100 | if (this.entries.length > 0) { 101 | this.entries.shift(); 102 | } 103 | } 104 | } 105 | } 106 | 107 | setEntryIndex(index: number) { 108 | this.entryIndex = index; 109 | } 110 | 111 | // cursor index can be [0,this.entries.length] 112 | setCursorIndex(index: number) { 113 | if (index < 0) { 114 | index = 0; 115 | } 116 | 117 | if (index > this.entries.length) { 118 | index = this.entries.length; 119 | } 120 | 121 | this.cursorIndex = index; 122 | } 123 | 124 | setCalcResult(result: InternalNumber) { 125 | this.calcResult = result; 126 | } 127 | 128 | setDispResult(result: InternalNumber) { 129 | this.dispResult = result; 130 | } 131 | 132 | toggleIsInsert() { 133 | this.isInsert = !this.isInsert; 134 | } 135 | 136 | setDisplayMode(newMode: CalculatorDisplayMode) { 137 | this.displayMode = newMode; 138 | } 139 | 140 | setDRGMode(newMode: CalculatorDRGMode) { 141 | this.drgMode = newMode; 142 | } 143 | 144 | setFuncMode(newMode: CalculatorFuncMode) { 145 | this.funcMode = newMode; 146 | } 147 | 148 | setHypMode(hypMode: boolean) { 149 | this.hypMode = hypMode; 150 | } 151 | 152 | clearFuncMode() { 153 | this.funcMode = "NONE"; 154 | this.hypMode = false; 155 | } 156 | 157 | toggleDecFrac() { 158 | switch (this.dispResult.type) { 159 | case "DEC": 160 | case "DEGREE": 161 | this.dispResult = new InternalNumber( 162 | "FRAC", 163 | getFracValue(this.calcResult) 164 | ); 165 | break; 166 | case "FRAC": 167 | this.dispResult = new InternalNumber( 168 | "DEC", 169 | getDecValue(this.calcResult) 170 | ); 171 | break; 172 | } 173 | } 174 | 175 | toggleDecDegree() { 176 | switch (this.dispResult.type) { 177 | case "DEC": 178 | case "FRAC": 179 | this.dispResult = new InternalNumber( 180 | "DEGREE", 181 | getDegreeValue(this.calcResult) 182 | ); 183 | break; 184 | case "DEGREE": 185 | this.dispResult = new InternalNumber( 186 | "DEC", 187 | getDecValue(this.calcResult) 188 | ); 189 | break; 190 | } 191 | } 192 | 193 | calculate() { 194 | if (this.entries.length === 0) { 195 | return; 196 | } 197 | const entriesCopy = preprocess(this.entries); 198 | 199 | const parseResult = parse(entriesCopy); 200 | if (!parseResult.success) { 201 | this.errorMessage = parseResult.msg; 202 | this.displayMode = "ERROR"; 203 | return; 204 | } 205 | 206 | const calculateResult = calculate(parseResult.lexems); 207 | if (!calculateResult.success) { 208 | this.errorMessage = calculateResult.msg; 209 | this.displayMode = "ERROR"; 210 | return; 211 | } 212 | 213 | this.historyEntries.push(Object.assign([], this.entries)); 214 | this.entryIndex = this.historyEntries.length - 1; 215 | 216 | this.calcResult = calculateResult.result!; 217 | 218 | // for integer frac values, display in integer 219 | const decCalcResult = getDecValue(this.calcResult); 220 | if (this.calcResult.type === "FRAC" && decCalcResult.isInteger()) { 221 | this.dispResult = new InternalNumber("DEC", decCalcResult); 222 | } else { 223 | this.dispResult = calculateResult.result!; 224 | } 225 | 226 | calculatorMemory.ans = calculateResult.result!; 227 | 228 | this.displayMode = "NORMAL_SHOW"; 229 | } 230 | } 231 | 232 | export default new CalculatorState(); 233 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | EC-82MS Online Scientific Calculator
CURSOR
0
D
SHIFT
ALPHA
MODE CLR
LANG
x!
x-1
nPr
nCr
Rec(
Pol(
3
x3
d/c
x2
x
^
10x
log
ex      e
ln
          A
( - )
←     B
° ′ ″
          C
hyp
sin-1 D
sin
cos-1 E
cos
tan-1 F
tan
STO
RCL
ENG
(
          X
)
          Y
,
M-     M
M+
7
8
9
INS
DEL
ABOUT
AC
4
5
6
×
÷
1
2
D
3
R
+
G
-
0
Ran#
.
π
EXP
Ans
%
=
-------------------------------------------------------------------------------- /test/math-deprecated/frac-test.ts0: -------------------------------------------------------------------------------- 1 | import { assertEquals,assertObjectEquals, runTests } from "../test-core/test-core"; 2 | import * as FB from "../../modules/math/value-type-basics/frac-basics"; 3 | 4 | function testTryFromTerminatingDiv() { 5 | assertObjectEquals({ ok: true, frac: { u: 0, d: 1 } }, FB.tryFromTerminatingDiv(0, 1)); 6 | assertObjectEquals({ ok: true, frac: { u: 0, d: 1 } }, FB.tryFromTerminatingDiv(0, 5)); 7 | assertObjectEquals({ ok: true, frac: { u: 2, d: 5 } }, FB.tryFromTerminatingDiv(2, 5)); 8 | assertObjectEquals({ ok: true, frac: { u: 1, d: 6 } }, FB.tryFromTerminatingDiv(10, 60)); 9 | assertObjectEquals({ ok: true, frac: { u: 0, d: 1 } }, FB.tryFromTerminatingDiv(0, 2.4)); 10 | assertObjectEquals({ ok: true, frac: { u: 1, d: 2 } }, FB.tryFromTerminatingDiv(1.2, 2.4)); 11 | assertObjectEquals({ ok: true, frac: { u: 1, d: 256 } }, FB.tryFromTerminatingDiv(0.00390625, 1)); 12 | assertObjectEquals({ ok: true, frac: { u: -1, d: 256 } }, FB.tryFromTerminatingDiv(-0.00390625, 1)); 13 | 14 | assertObjectEquals({ ok: true, frac: { u: 1, d: 3160320 } }, 15 | FB.tryFromTerminatingDiv(0.00390625, 12345)); 16 | 17 | assertObjectEquals({ ok: true, frac: { u: 1, d: 31604736 } }, 18 | FB.tryFromTerminatingDiv(0.00390625, 123456)); 19 | } 20 | 21 | function testReduce() { 22 | assertObjectEquals({ u: 0, d: 1 }, FB.reduce({ u: 0, d: 1 })); 23 | assertObjectEquals({ u: 0, d: 1 }, FB.reduce({ u: 0, d: 3 })); 24 | assertObjectEquals({ u: 1, d: 1 }, FB.reduce({ u: 3, d: 3 })); 25 | assertObjectEquals({ u: 1, d: 3 }, FB.reduce({ u: 3, d: 9 })); 26 | assertObjectEquals({ u: 3, d: 1 }, FB.reduce({ u: 9, d: 3 })); 27 | } 28 | 29 | ///////////////////// Operations with frac ///////////////////// 30 | function testAddFrac() { 31 | assertObjectEquals({ u: 0, d: 1 }, 32 | FB.addFrac({ u: 0, d: 2 }, { u: 0, d: 3 })); 33 | 34 | assertObjectEquals({ u: 0, d: 1 }, 35 | FB.addFrac({ u: 1, d: 2 }, { u: -1, d: 2 })); 36 | 37 | assertObjectEquals({ u: 1, d: 1 }, 38 | FB.addFrac({ u: 1, d: 2 }, { u: 1, d: 2 })); 39 | 40 | assertObjectEquals({ u: 5, d: 6 }, 41 | FB.addFrac({ u: 1, d: 2 }, { u: 1, d: 3 })); 42 | 43 | assertObjectEquals({ u: 1, d: 6 }, 44 | FB.addFrac({ u: 1, d: 2 }, { u: -1, d: 3 })); 45 | 46 | assertObjectEquals({ u: 4595, d: 6 }, 47 | FB.addFrac({ u: 197, d: 2 }, { u: 2002, d: 3 })); 48 | } 49 | 50 | function testSubFrac() { 51 | assertObjectEquals({ u: 0, d: 1 }, 52 | FB.subFrac({ u: 0, d: 2 }, { u: 0, d: 3 })); 53 | 54 | assertObjectEquals({ u: 1, d: 1 }, 55 | FB.subFrac({ u: 1, d: 2 }, { u: -1, d: 2 })); 56 | 57 | assertObjectEquals({ u: 0, d: 1 }, 58 | FB.subFrac({ u: 1, d: 2 }, { u: 1, d: 2 })); 59 | 60 | assertObjectEquals({ u: 1, d: 6 }, 61 | FB.subFrac({ u: 1, d: 2 }, { u: 1, d: 3 })); 62 | 63 | assertObjectEquals({ u: 5, d: 6 }, 64 | FB.subFrac({ u: 1, d: 2 }, { u: -1, d: 3 })); 65 | 66 | assertObjectEquals({ u: -3413, d: 6 }, 67 | FB.subFrac({ u: 197, d: 2 }, { u: 2002, d: 3 })); 68 | } 69 | 70 | function testMulFrac() { 71 | assertObjectEquals({ u: 0, d: 1 }, 72 | FB.mulFrac({ u: 0, d: 2 }, { u: 1, d: 3 })); 73 | 74 | assertObjectEquals({ u: -1, d: 4 }, 75 | FB.mulFrac({ u: 1, d: 2 }, { u: -1, d: 2 })); 76 | 77 | assertObjectEquals({ u: 1, d: 1 }, 78 | FB.mulFrac({ u: 5, d: 2 }, { u: 2, d: 5 })); 79 | 80 | assertObjectEquals({ u: 197197, d: 3 }, 81 | FB.mulFrac({ u: 197, d: 2 }, { u: 2002, d: 3 })); 82 | } 83 | 84 | function testDivFrac() { 85 | assertObjectEquals({ u: 0, d: 1 }, 86 | FB.divFrac({ u: 0, d: 2 }, { u: 1, d: 3 })); 87 | 88 | assertObjectEquals({ u: -1, d: 1 }, 89 | FB.divFrac({ u: 1, d: 2 }, { u: -1, d: 2 })); 90 | 91 | assertObjectEquals({ u: 1, d: 1 }, 92 | FB.divFrac({ u: 1, d: 2 }, { u: 1, d: 2 })); 93 | 94 | assertObjectEquals({ u: 3, d: 2 }, 95 | FB.divFrac({ u: 1, d: 2 }, { u: 1, d: 3 })); 96 | 97 | assertObjectEquals({ u: -3, d: 2 }, 98 | FB.divFrac({ u: 1, d: 2 }, { u: -1, d: 3 })); 99 | 100 | assertObjectEquals({ u: 591, d: 4004 }, 101 | FB.divFrac({ u: 197, d: 2 }, { u: 2002, d: 3 })); 102 | } 103 | 104 | function testIntPower() { 105 | assertObjectEquals({ u: 1, d: 1 }, FB.intPower({ u: 1, d: 2 }, 0)); 106 | assertObjectEquals({ u: 1, d: 2 }, FB.intPower({ u: 1, d: 2 }, 1)); 107 | assertObjectEquals({ u: 1, d: 4 }, FB.intPower({ u: 1, d: 2 }, 2)); 108 | assertObjectEquals({ u: -1, d: 2 }, FB.intPower({ u: -1, d: 2 }, 1)); 109 | assertObjectEquals({ u: 1, d: 4 }, FB.intPower({ u: -1, d: 2 }, 2)); 110 | assertObjectEquals({ u: 2, d: 1 }, FB.intPower({ u: 1, d: 2 }, -1)); 111 | assertObjectEquals({ u: 4, d: 1 }, FB.intPower({ u: 1, d: 2 }, -2)); 112 | assertObjectEquals({ u: 27, d: 125 }, FB.intPower({ u: 5, d: 3 }, -3)); 113 | } 114 | 115 | ///////////////////// Operations with dec ///////////////////// 116 | function testAddDec() { 117 | assertObjectEquals({ isFrac: true, value: { u: 1, d: 1 } }, 118 | FB.addDec({ u: 0, d: 1 }, 1)); 119 | 120 | assertObjectEquals({ isFrac: true, value: { u: -1, d: 1 } }, 121 | FB.addDec({ u: 0, d: 1 }, -1)); 122 | 123 | assertObjectEquals({ isFrac: true, value: { u: 17, d: 10 } }, 124 | FB.addDec({ u: 1, d: 2 }, 1.2)); 125 | 126 | assertObjectEquals({ isFrac: true, value: { u: -7, d: 10 } }, 127 | FB.addDec({ u: 1, d: 2 }, -1.2)); 128 | 129 | assertObjectEquals({ isFrac: true, value: { u: 1_333_333_333_333, d: 1_000_000_000_000 } }, 130 | FB.addDec({ u: 0, d: 1 }, 1.333_333_333_333)); 131 | 132 | assertObjectEquals({ isFrac: true, value: { u: -1_333_333_333_333, d: 1_000_000_000_000 } }, 133 | FB.addDec({ u: 0, d: 1 }, -1.333_333_333_333)); 134 | 135 | assertObjectEquals({ isFrac: true, value: { u: 4_999_999_999_999, d: 3_000_000_000_000 } }, 136 | FB.addDec({ u: 1, d: 3 }, 1.333_333_333_333)); 137 | 138 | assertObjectEquals({ isFrac: true, value: { u: -2_999_999_999_999, d: 3_000_000_000_000 } }, 139 | FB.addDec({ u: 1, d: 3 }, -1.333_333_333_333)); 140 | } 141 | 142 | function testSubDec() { 143 | assertObjectEquals({ isFrac: true, value: { u: -1, d: 1 } }, 144 | FB.subDec({ u: 0, d: 1 }, 1)); 145 | 146 | assertObjectEquals({ isFrac: true, value: { u: 1, d: 1 } }, 147 | FB.subDec({ u: 0, d: 1 }, -1)); 148 | 149 | assertObjectEquals({ isFrac: true, value: { u: -7, d: 10 } }, 150 | FB.subDec({ u: 1, d: 2 }, 1.2)); 151 | 152 | assertObjectEquals({ isFrac: true, value: { u: 17, d: 10 } }, 153 | FB.subDec({ u: 1, d: 2 }, -1.2)); 154 | 155 | assertObjectEquals({ isFrac: true, value: { u: -2_999_999_999_999, d: 3_000_000_000_000 } }, 156 | FB.subDec({ u: 1, d: 3 }, 1.333_333_333_333)); 157 | 158 | assertObjectEquals({ isFrac: true, value: { u: 4_999_999_999_999, d: 3_000_000_000_000 } }, 159 | FB.subDec({ u: 1, d: 3 }, -1.333_333_333_333)); 160 | } 161 | 162 | function testMulDec() { 163 | assertObjectEquals({ isFrac: true, value: { u: 0, d: 1 } }, 164 | FB.mulDec({ u: 1, d: 3 }, 0)); 165 | 166 | assertObjectEquals({ isFrac: true, value: { u: 0, d: 1 } }, 167 | FB.mulDec({ u: 0, d: 1 }, 1)); 168 | 169 | assertObjectEquals({ isFrac: true, value: { u: -1, d: 1 } }, 170 | FB.mulDec({ u: 1, d: 1 }, -1)); 171 | 172 | assertObjectEquals({ isFrac: true, value: { u: 3, d: 5 } }, 173 | FB.mulDec({ u: 1, d: 2 }, 1.2)); 174 | 175 | assertObjectEquals({ isFrac: true, value: { u: -3, d: 5 } }, 176 | FB.mulDec({ u: 1, d: 2 }, -1.2)); 177 | } 178 | 179 | function testDivDec() { 180 | assertObjectEquals({ isFrac: true, value: { u: 0, d: 1 } }, 181 | FB.divDec({ u: 0, d: 1 }, 1)); 182 | 183 | assertObjectEquals({ isFrac: true, value: { u: -1, d: 1 } }, 184 | FB.divDec({ u: 1, d: 1 }, -1)); 185 | 186 | assertObjectEquals({ isFrac: true, value: { u: 5, d: 12 } }, 187 | FB.divDec({ u: 1, d: 2 }, 1.2)); 188 | 189 | assertObjectEquals({ isFrac: true, value: { u: -5, d: 12 } }, 190 | FB.divDec({ u: 1, d: 2 }, -1.2)); 191 | } 192 | 193 | ///////////////////// Operations with degree ///////////////////// 194 | function testAddDegree() { 195 | assertObjectEquals({ isFrac: true, value: { u: 0, d: 1 } }, 196 | FB.addDegree({ u: 0, d: 2 }, { d: 0, m: 0, s: 0, neg: false })); 197 | 198 | assertObjectEquals({ isFrac: true, value: { u: 1, d: 1 } }, 199 | FB.addDegree({ u: 0, d: 2 }, { d: 1, m: 0, s: 0, neg: false })); 200 | 201 | assertObjectEquals({ isFrac: true, value: { u: -1, d: 1 } }, 202 | FB.addDegree({ u: 0, d: 2 }, { d: 1, m: 0, s: 0, neg: true })); 203 | 204 | assertObjectEquals({ isFrac: true, value: { u: 5461, d: 3600 } }, 205 | FB.addDegree({ u: 1, d: 2 }, { d: 1, m: 1, s: 1, neg: false })); 206 | 207 | assertObjectEquals({ isFrac: true, value: { u: -293, d: 240 } }, 208 | FB.addDegree({ u: 1, d: 3 }, { d: 1.5, m: 2.25, s: 60, neg: true })); 209 | } 210 | 211 | function testSubDegree() { 212 | assertObjectEquals({ isFrac: true, value: { u: 0, d: 1 } }, 213 | FB.subDegree({ u: 0, d: 2 }, { d: 0, m: 0, s: 0, neg: false })); 214 | 215 | assertObjectEquals({ isFrac: true, value: { u: -1, d: 1 } }, 216 | FB.subDegree({ u: 0, d: 2 }, { d: 1, m: 0, s: 0, neg: false })); 217 | 218 | assertObjectEquals({ isFrac: true, value: { u: 1, d: 1 } }, 219 | FB.subDegree({ u: 0, d: 2 }, { d: 1, m: 0, s: 0, neg: true })); 220 | 221 | assertObjectEquals({ isFrac: true, value: { u: -1861, d: 3600 } }, 222 | FB.subDegree({ u: 1, d: 2 }, { d: 1, m: 1, s: 1, neg: false })); 223 | 224 | assertObjectEquals({ isFrac: true, value: { u: 151, d: 80 } }, 225 | FB.subDegree({ u: 1, d: 3 }, { d: 1.5, m: 2.25, s: 60, neg: true })); 226 | } 227 | 228 | runTests( 229 | testTryFromTerminatingDiv, 230 | testReduce, 231 | 232 | testAddFrac, 233 | testSubFrac, 234 | testMulFrac, 235 | testDivFrac, 236 | testIntPower, 237 | 238 | testAddDec, 239 | testSubDec, 240 | testMulDec, 241 | testDivDec, 242 | 243 | testAddDegree, 244 | testSubDegree 245 | ); -------------------------------------------------------------------------------- /components/func-keys.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "../styles/func-keys.module.scss"; 3 | import Key from "./key"; 4 | import * as L from "../logics/func-keys"; 5 | 6 | export default class FuncKeys extends React.Component { 7 | constructor(props: {}) { 8 | super(props); 9 | } 10 | 11 | render = () => ( 12 |
13 |
14 |
15 | x!} 18 | content={ 19 | 20 | x-1 21 | 22 | } 23 | onClick={() => { 24 | L.onR1C1Click(); 25 | }} 26 | /> 27 | 28 | nPr} 31 | content={nCr} 32 | onClick={() => { 33 | L.onR1C2Click(); 34 | }} 35 | /> 36 |
37 |
38 | {"Rec("}} 41 | content={{"Pol("}} 42 | onClick={() => { 43 | L.onR1C3Click(); 44 | }} 45 | /> 46 | 47 | 51 | 3√ 52 | 53 | } 54 | content={ 55 | 56 | x3 57 | 58 | } 59 | onClick={() => { 60 | L.onR1C4Click(); 61 | }} 62 | /> 63 |
64 |
65 | 66 |
67 | d/c} 70 | onClick={() => { 71 | L.onR2C1Click(); 72 | }} 73 | /> 74 | √} 77 | onClick={() => { 78 | L.onR2C2Click(); 79 | }} 80 | /> 81 | 85 | x2 86 | 87 | } 88 | onClick={() => { 89 | L.onR2C3Click(); 90 | }} 91 | /> 92 | 96 | x√ 97 | 98 | } 99 | content={^} 100 | onClick={() => { 101 | L.onR2C4Click(); 102 | }} 103 | /> 104 | 108 | 10x 109 | 110 | } 111 | content={log} 112 | onClick={() => { 113 | L.onR2C5Click(); 114 | }} 115 | /> 116 | 120 | 121 | ex 122 |        123 | 124 | e 125 | 126 | } 127 | content={ln} 128 | onClick={() => { 129 | L.onR2C6Click(); 130 | }} 131 | /> 132 |
133 | 134 |
135 | 139 |           A 140 | 141 | } 142 | content={{"( - )"}} 143 | onClick={() => { 144 | L.onR3C1Click(); 145 | }} 146 | /> 147 | 151 | 152 | ←      153 | 154 | B 155 | 156 | } 157 | content={{"° ′ ″"}} 158 | onClick={() => { 159 | L.onR3C2Click(); 160 | }} 161 | /> 162 | 166 |           C 167 | 168 | } 169 | content={hyp} 170 | onClick={() => { 171 | L.onR3C3Click(); 172 | }} 173 | /> 174 | 178 | 179 | sin-1{" "} 180 | 181 | D 182 | 183 | } 184 | content={sin} 185 | onClick={() => { 186 | L.onR3C4Click(); 187 | }} 188 | /> 189 | 193 | 194 | cos-1{" "} 195 | 196 | E 197 | 198 | } 199 | content={cos} 200 | onClick={() => { 201 | L.onR3C5Click(); 202 | }} 203 | /> 204 | 208 | 209 | tan-1{" "} 210 | 211 | F 212 | 213 | } 214 | content={tan} 215 | onClick={() => { 216 | L.onR3C6Click(); 217 | }} 218 | /> 219 |
220 | 221 |
222 | STO} 225 | content={RCL} 226 | onClick={() => { 227 | L.onR4C1Click(); 228 | }} 229 | /> 230 | ←} 233 | content={ENG} 234 | onClick={() => {}} 235 | /> 236 | {"("}} 239 | onClick={() => { 240 | L.onR4C3Click(); 241 | }} 242 | /> 243 | 247 |           X 248 | 249 | } 250 | content={{")"}} 251 | onClick={() => { 252 | L.onR4C4Click(); 253 | }} 254 | /> 255 | 259 |           Y 260 | 261 | } 262 | content={,} 263 | onClick={() => { 264 | L.onR4C5Click(); 265 | }} 266 | /> 267 | 271 | 272 | M-      273 | 274 | M 275 | 276 | } 277 | content={M+} 278 | onClick={() => { 279 | L.onR4C6Click(); 280 | }} 281 | /> 282 |
283 |
284 | ); 285 | } 286 | -------------------------------------------------------------------------------- /logics/func-keys.ts: -------------------------------------------------------------------------------- 1 | import { KEY_ENTRIES } from "../modules/calc-core/objs/key-entry"; 2 | import cs from "../observables/calculator-state"; 3 | import cm from "../observables/calculator-memory"; 4 | import { add, sub } from "../modules/math/internal-number-math"; 5 | 6 | export const onR1C1Click = () => { 7 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 8 | return; 9 | } 10 | 11 | if (cs.funcMode === "SHIFT") { 12 | cs.inputEntry(KEY_ENTRIES.fact); 13 | } else if (cs.funcMode === "NONE") { 14 | cs.inputEntry(KEY_ENTRIES.inv); 15 | } 16 | 17 | cs.clearFuncMode(); 18 | }; 19 | 20 | export const onR1C2Click = () => { 21 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 22 | return; 23 | } 24 | 25 | if (cs.funcMode === "SHIFT") { 26 | cs.inputEntry(KEY_ENTRIES.npr); 27 | } else if (cs.funcMode === "NONE") { 28 | cs.inputEntry(KEY_ENTRIES.ncr); 29 | } 30 | 31 | cs.clearFuncMode(); 32 | }; 33 | 34 | export const onR1C3Click = () => { 35 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 36 | return; 37 | } 38 | 39 | if (cs.funcMode === "SHIFT") { 40 | cs.inputEntry(KEY_ENTRIES.rec); 41 | } else if (cs.funcMode === "NONE") { 42 | cs.inputEntry(KEY_ENTRIES.pol); 43 | } 44 | 45 | cs.clearFuncMode(); 46 | }; 47 | 48 | export const onR1C4Click = () => { 49 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 50 | return; 51 | } 52 | 53 | if (cs.funcMode === "SHIFT") { 54 | cs.inputEntry(KEY_ENTRIES.cbrt); 55 | } else if (cs.funcMode === "NONE") { 56 | cs.inputEntry(KEY_ENTRIES.cube); 57 | } 58 | 59 | cs.clearFuncMode(); 60 | }; 61 | 62 | export const onR2C1Click = () => { 63 | if (cs.funcMode === "NONE") { 64 | if (cs.displayMode === "NORMAL_EDIT") { 65 | cs.inputEntry(KEY_ENTRIES.frac); 66 | } else if (cs.displayMode === "NORMAL_SHOW") { 67 | cs.toggleDecFrac(); 68 | } 69 | } 70 | 71 | cs.clearFuncMode(); 72 | }; 73 | 74 | export const onR2C2Click = () => { 75 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 76 | return; 77 | } 78 | 79 | if (cs.funcMode === "NONE") { 80 | cs.inputEntry(KEY_ENTRIES.sqrt); 81 | } 82 | 83 | cs.clearFuncMode(); 84 | }; 85 | 86 | export const onR2C3Click = () => { 87 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 88 | return; 89 | } 90 | 91 | if (cs.funcMode === "NONE") { 92 | cs.inputEntry(KEY_ENTRIES.sqr); 93 | } 94 | 95 | cs.clearFuncMode(); 96 | }; 97 | 98 | export const onR2C4Click = () => { 99 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 100 | return; 101 | } 102 | 103 | if (cs.funcMode === "SHIFT") { 104 | cs.inputEntry(KEY_ENTRIES.root); 105 | } else if (cs.funcMode === "NONE") { 106 | cs.inputEntry(KEY_ENTRIES.pow); 107 | } 108 | 109 | cs.clearFuncMode(); 110 | }; 111 | 112 | export const onR2C5Click = () => { 113 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 114 | return; 115 | } 116 | 117 | if (cs.funcMode === "SHIFT") { 118 | cs.inputEntry(KEY_ENTRIES.exp10); 119 | } else if (cs.funcMode === "NONE") { 120 | cs.inputEntry(KEY_ENTRIES.log); 121 | } 122 | 123 | cs.clearFuncMode(); 124 | }; 125 | 126 | export const onR2C6Click = () => { 127 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 128 | return; 129 | } 130 | 131 | if (cs.funcMode === "SHIFT") { 132 | cs.inputEntry(KEY_ENTRIES.exp); 133 | } else if (cs.funcMode === "ALPHA") { 134 | cs.inputEntry(KEY_ENTRIES.e); 135 | } else if (cs.funcMode === "NONE") { 136 | cs.inputEntry(KEY_ENTRIES.ln); 137 | } 138 | 139 | cs.clearFuncMode(); 140 | }; 141 | 142 | export const onR3C1Click = () => { 143 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 144 | return; 145 | } 146 | 147 | if (cs.funcMode === "ALPHA" || cs.funcMode === "RCL") { 148 | cs.inputEntry(KEY_ENTRIES.A); 149 | } else if (cs.funcMode === "NONE") { 150 | cs.inputEntry(KEY_ENTRIES.neg); 151 | } else if (cs.funcMode === "STO" && cs.displayMode === "NORMAL_SHOW") { 152 | cm.A = cs.calcResult; 153 | cs.dispResult = cs.calcResult; 154 | cs.inputEntry(KEY_ENTRIES.A); 155 | } 156 | 157 | cs.clearFuncMode(); 158 | }; 159 | 160 | export const onR3C2Click = () => { 161 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 162 | return; 163 | } 164 | 165 | if (cs.funcMode === "ALPHA" || cs.funcMode === "RCL") { 166 | cs.inputEntry(KEY_ENTRIES.B); 167 | } else if (cs.funcMode === "SHIFT") { 168 | if (cs.displayMode === "NORMAL_SHOW") { 169 | cs.toggleDecDegree(); 170 | } 171 | } else if (cs.funcMode === "NONE") { 172 | if (cs.displayMode === "NORMAL_SHOW") { 173 | cs.toggleDecDegree(); 174 | } else if (cs.displayMode === "NORMAL_EDIT") { 175 | cs.inputEntry(KEY_ENTRIES.degree); 176 | } 177 | } else if (cs.funcMode === "STO" && cs.displayMode === "NORMAL_SHOW") { 178 | cm.B = cs.calcResult; 179 | cs.dispResult = cs.calcResult; 180 | cs.inputEntry(KEY_ENTRIES.B); 181 | } 182 | 183 | cs.clearFuncMode(); 184 | }; 185 | 186 | export const onR3C3Click = () => { 187 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 188 | return; 189 | } 190 | 191 | if (cs.funcMode === "ALPHA" || cs.funcMode === "RCL") { 192 | cs.inputEntry(KEY_ENTRIES.C); 193 | } else if (cs.funcMode === "STO" && cs.displayMode === "NORMAL_SHOW") { 194 | cm.C = cs.calcResult; 195 | cs.dispResult = cs.calcResult; 196 | cs.inputEntry(KEY_ENTRIES.C); 197 | } else if (!cs.hypMode) { 198 | cs.setHypMode(true); 199 | return; 200 | } 201 | 202 | cs.clearFuncMode(); 203 | }; 204 | 205 | export const onR3C4Click = () => { 206 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 207 | return; 208 | } 209 | 210 | if (cs.funcMode === "ALPHA" || cs.funcMode === "RCL") { 211 | cs.inputEntry(KEY_ENTRIES.D); 212 | } else if (cs.funcMode === "SHIFT") { 213 | if (cs.hypMode) { 214 | cs.inputEntry(KEY_ENTRIES.asinh) 215 | } 216 | else { 217 | cs.inputEntry(KEY_ENTRIES.asin); 218 | } 219 | } else if (cs.hypMode) { 220 | cs.inputEntry(KEY_ENTRIES.sinh); 221 | } else if (cs.funcMode === "NONE") { 222 | cs.inputEntry(KEY_ENTRIES.sin); 223 | } else if (cs.funcMode === "STO" && cs.displayMode === "NORMAL_SHOW") { 224 | cm.D = cs.calcResult; 225 | cs.dispResult = cs.calcResult; 226 | cs.inputEntry(KEY_ENTRIES.D); 227 | } 228 | 229 | cs.clearFuncMode(); 230 | }; 231 | 232 | export const onR3C5Click = () => { 233 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 234 | return; 235 | } 236 | 237 | if (cs.funcMode === "ALPHA" || cs.funcMode === "RCL") { 238 | cs.inputEntry(KEY_ENTRIES.E); 239 | } else if (cs.funcMode === "SHIFT") { 240 | if (cs.hypMode) { 241 | cs.inputEntry(KEY_ENTRIES.acosh); 242 | } else { 243 | cs.inputEntry(KEY_ENTRIES.acos); 244 | } 245 | } else if (cs.hypMode) { 246 | cs.inputEntry(KEY_ENTRIES.cosh); 247 | } else if (cs.funcMode === "NONE") { 248 | cs.inputEntry(KEY_ENTRIES.cos); 249 | } else if (cs.funcMode === "STO" && cs.displayMode === "NORMAL_SHOW") { 250 | cm.E = cs.calcResult; 251 | cs.dispResult = cs.calcResult; 252 | cs.inputEntry(KEY_ENTRIES.E); 253 | } 254 | 255 | cs.clearFuncMode(); 256 | }; 257 | 258 | export const onR3C6Click = () => { 259 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 260 | return; 261 | } 262 | 263 | if (cs.funcMode === "ALPHA" || cs.funcMode === "RCL") { 264 | cs.inputEntry(KEY_ENTRIES.F); 265 | } else if (cs.funcMode === "SHIFT") { 266 | if (cs.hypMode) { 267 | cs.inputEntry(KEY_ENTRIES.atanh); 268 | } else { 269 | cs.inputEntry(KEY_ENTRIES.atan); 270 | } 271 | } else if (cs.hypMode) { 272 | cs.inputEntry(KEY_ENTRIES.tanh); 273 | } else if (cs.funcMode === "NONE") { 274 | cs.inputEntry(KEY_ENTRIES.tan); 275 | } else if (cs.funcMode === "STO" && cs.displayMode === "NORMAL_SHOW") { 276 | cm.F = cs.calcResult; 277 | cs.dispResult = cs.calcResult; 278 | cs.inputEntry(KEY_ENTRIES.F); 279 | } 280 | 281 | cs.clearFuncMode(); 282 | }; 283 | 284 | export const onR4C1Click = () => { 285 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 286 | return; 287 | } 288 | 289 | if (cs.funcMode === "SHIFT") { 290 | cs.setFuncMode("STO"); 291 | return; 292 | } else if (cs.funcMode === "NONE") { 293 | cs.setFuncMode("RCL"); 294 | return; 295 | } 296 | 297 | cs.clearFuncMode(); 298 | }; 299 | 300 | export const onR4C3Click = () => { 301 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 302 | return; 303 | } 304 | 305 | if (cs.funcMode === "NONE") { 306 | cs.inputEntry(KEY_ENTRIES.lBracket); 307 | } 308 | 309 | cs.clearFuncMode(); 310 | }; 311 | 312 | export const onR4C4Click = () => { 313 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 314 | return; 315 | } 316 | 317 | if (cs.funcMode === "NONE") { 318 | cs.inputEntry(KEY_ENTRIES.rBracket); 319 | } else if (cs.funcMode === "ALPHA" || cs.funcMode === "RCL") { 320 | cs.inputEntry(KEY_ENTRIES.X); 321 | } else if (cs.funcMode === "STO" && cs.displayMode === "NORMAL_SHOW") { 322 | cm.X = cs.calcResult; 323 | cs.dispResult = cs.calcResult; 324 | cs.inputEntry(KEY_ENTRIES.X); 325 | } 326 | 327 | cs.clearFuncMode(); 328 | }; 329 | 330 | export const onR4C5Click = () => { 331 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 332 | return; 333 | } 334 | 335 | if (cs.funcMode === "NONE") { 336 | cs.inputEntry(KEY_ENTRIES.comma); 337 | } else if (cs.funcMode === "ALPHA" || cs.funcMode === "RCL") { 338 | cs.inputEntry(KEY_ENTRIES.Y); 339 | } else if (cs.funcMode === "STO" && cs.displayMode === "NORMAL_SHOW") { 340 | cm.Y = cs.calcResult; 341 | cs.dispResult = cs.calcResult; 342 | cs.inputEntry(KEY_ENTRIES.Y); 343 | } 344 | 345 | cs.clearFuncMode(); 346 | }; 347 | 348 | export const onR4C6Click = () => { 349 | if (cs.displayMode !== "NORMAL_EDIT" && cs.displayMode !== "NORMAL_SHOW") { 350 | return; 351 | } 352 | 353 | if (cs.funcMode === "NONE") { 354 | if (cs.displayMode === "NORMAL_SHOW") { 355 | cm.M = add(cm.M, cs.calcResult); 356 | cs.inputEntry(KEY_ENTRIES.M); 357 | cs.calcResult = cm.M; 358 | cs.dispResult = cm.M; 359 | } 360 | } else if (cs.funcMode === "SHIFT") { 361 | if (cs.displayMode === "NORMAL_SHOW") { 362 | cm.M = sub(cm.M, cs.calcResult); 363 | cs.inputEntry(KEY_ENTRIES.M); 364 | cs.calcResult = cm.M; 365 | cs.dispResult = cm.M; 366 | } 367 | } else if (cs.funcMode === "ALPHA" || cs.funcMode === "RCL") { 368 | cs.inputEntry(KEY_ENTRIES.M); 369 | } else if (cs.funcMode === "STO" && cs.displayMode === "NORMAL_SHOW") { 370 | cm.M = cs.calcResult; 371 | cs.dispResult = cs.calcResult; 372 | cs.inputEntry(KEY_ENTRIES.M); 373 | } 374 | 375 | cs.clearFuncMode(); 376 | }; 377 | -------------------------------------------------------------------------------- /docs/_next/static/css/9af00fe6c25f953e.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes screen_insert-course__21_jI{0%{border-left:1px solid #000}50%{border-left:1px solid transparent}to{border-left:1px solid #000}}@keyframes screen_insert-course__21_jI{0%{border-left:1px solid #000}50%{border-left:1px solid transparent}to{border-left:1px solid #000}}@-webkit-keyframes screen_overwrite-course__ifkEV{0%{border-bottom:1px solid #000}50%{border-bottom:1px solid transparent}to{border-bottom:1px solid #000}}@keyframes screen_overwrite-course__ifkEV{0%{border-bottom:1px solid #000}50%{border-bottom:1px solid transparent}to{border-bottom:1px solid #000}}.screen_divScreenWrapper__DOLEi{box-sizing:border-box;background-color:#aaa;flex-grow:1;width:calc(100% - 2em);height:150px;margin:1em .5em 2em;padding:12px 16px 11px;box-shadow:inset 1px 1px 1px 0 rgba(40,49,85,.4);border-radius:18px;background:#fff}.screen_divNormalWrapper__33OaI{width:100%;height:100%;display:flex;flex-direction:column}.screen_divNormalWrapper__33OaI>div[role=entries]{display:flex;justify-content:flex-start;flex-wrap:wrap;min-height:1.5em;max-height:48%;overflow:auto}.screen_divNormalWrapper__33OaI>div[role=entries]::-webkit-scrollbar{width:5px;height:5px}.screen_divNormalWrapper__33OaI>div[role=entries]::-webkit-scrollbar-thumb{border-radius:3px;background-color:#b4b4b4}.screen_divNormalWrapper__33OaI>div[role=entries]::-webkit-scrollbar-thumb:hover{background-color:#bebebe}.screen_divNormalWrapper__33OaI>div[role=entries]::-webkit-scrollbar-thumb:active{background-color:#c8c8c8}.screen_divNormalWrapper__33OaI>div[role=entries]::-webkit-scrollbar-track{background-color:#e6e6e6;border-radius:3px}.screen_divNormalWrapper__33OaI>div[role=entries]>img{cursor:text}.screen_divNormalWrapper__33OaI>div[role=result]{font-size:42px;word-wrap:break-word;text-align:end;flex-grow:1;overflow:auto}.screen_divNormalWrapper__33OaI>div[role=result]::-webkit-scrollbar{width:5px;height:5px}.screen_divNormalWrapper__33OaI>div[role=result]::-webkit-scrollbar-thumb{border-radius:3px;background-color:#b4b4b4}.screen_divNormalWrapper__33OaI>div[role=result]::-webkit-scrollbar-thumb:hover{background-color:#bebebe}.screen_divNormalWrapper__33OaI>div[role=result]::-webkit-scrollbar-thumb:active{background-color:#c8c8c8}.screen_divNormalWrapper__33OaI>div[role=result]::-webkit-scrollbar-track{background-color:#e6e6e6;border-radius:3px}.screen_divNormalWrapper__33OaI>div[role=error]{color:red;font-size:18px;word-wrap:break-word;text-align:start;flex-grow:1;overflow:auto}.screen_divNormalWrapper__33OaI>div[role=error]::-webkit-scrollbar{width:5px;height:5px}.screen_divNormalWrapper__33OaI>div[role=error]::-webkit-scrollbar-thumb{border-radius:3px;background-color:#b4b4b4}.screen_divNormalWrapper__33OaI>div[role=error]::-webkit-scrollbar-thumb:hover{background-color:#bebebe}.screen_divNormalWrapper__33OaI>div[role=error]::-webkit-scrollbar-thumb:active{background-color:#c8c8c8}.screen_divNormalWrapper__33OaI>div[role=error]::-webkit-scrollbar-track{background-color:#e6e6e6;border-radius:3px}.screen_divNormalWrapper__33OaI>div[role=mode]{font-size:12px;font-weight:700;height:8%;margin-top:auto;transform:translateY(8px);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#f7f7f7;border-radius:8px}.screen_divNormalWrapper__33OaI>div[role=mode]>span:first-child{color:brown;position:absolute;left:10px}.screen_divNormalWrapper__33OaI>div[role=mode]>span:nth-child(2){color:#daa520;position:absolute;left:60px}.screen_divNormalWrapper__33OaI>div[role=mode]>span:nth-child(3){color:#0000cd;position:absolute;right:10px}.screen_imgInsert____WmM,.screen_imgNormal__PpbYi,.screen_imgOverwrite__HBA__{margin-bottom:5px}.screen_imgCursor__wtszt,.screen_imgInsert____WmM,.screen_imgOverwrite__HBA__{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.screen_imgInsert____WmM{-webkit-animation-name:screen_insert-course__21_jI;animation-name:screen_insert-course__21_jI}.screen_imgOverwrite__HBA__{-webkit-animation-name:screen_overwrite-course__ifkEV;animation-name:screen_overwrite-course__ifkEV}.screen_divDrgWrapper__Bq7rI{width:100%;height:100%;display:flex;justify-content:space-evenly;align-items:center}.screen_divDrgWrapper__Bq7rI>div{width:80px;height:80px;border-radius:50%;font-size:18px;font-weight:700;text-align:center;line-height:80px;box-shadow:3px 3px 5px 0 rgba(0,0,0,.2);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;transition-duration:.2s}.screen_divDrgWrapper__Bq7rI>div:hover{filter:brightness(90%)}.screen_divDrgWrapper__Bq7rI>div:first-child{background-color:#f5f1c8}.screen_divDrgWrapper__Bq7rI>div:nth-child(2){background-color:#ffdee7}.screen_divDrgWrapper__Bq7rI>div:nth-child(3){background-color:#d4e7e2}.screen_divClearWrapper__gw6GL{width:100%;height:100%;display:flex;justify-content:space-evenly;align-items:center}.screen_divClearWrapper__gw6GL>div{width:80px;height:80px;border-radius:50%;font-size:15px;font-weight:700;text-align:center;line-height:80px;box-shadow:3px 3px 5px 0 rgba(0,0,0,.2);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;transition-duration:.2s}.screen_divClearWrapper__gw6GL>div:hover{background-color:#cce8f4}.screen_divLangWrapper__FFm9m{width:100%;height:100%;display:flex;justify-content:space-evenly;align-items:center}.screen_divLangWrapper__FFm9m>div{width:100px;height:100px;border-radius:50%;font-size:20px;font-weight:700;text-align:center;line-height:100px;box-shadow:3px 3px 5px 0 rgba(0,0,0,.2);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;transition-duration:.2s}.screen_divLangWrapper__FFm9m>div:hover{background-color:#fce2de}.screen_divAboutWrapper__j_Fmp{width:100%;height:100%;display:flex;flex-direction:column;justify-content:center;align-items:center}.screen_divAboutWrapper__j_Fmp>title{display:block;font-size:15px;text-align:center;margin:8px 0}.screen_divAboutWrapper__j_Fmp>title:first-child{font-weight:700}.screen_divAboutWrapper__j_Fmp>div{margin:8px 0}.screen_divAboutWrapper__j_Fmp>div>a{color:#0d6efd;text-decoration:underline}.screen_divAboutWrapper__j_Fmp>div>a:hover{color:#0a58ca}.screen_divAboutWrapper__j_Fmp>div>a:first-child{margin:0 5px}.key_divKeyWrapper__wqED0{display:flex;flex-direction:column;align-items:center}.key_divKeyWrapper__wqED0>div:first-child{position:absolute;transform:translateY(-100%)}.sys-keys_divSysKeys__S_xXZ{width:100%;height:6%;display:flex;justify-content:space-between}.sys-keys_divSysKeys__S_xXZ>div:not(:last-child){width:35%;height:100%;display:flex;justify-content:space-evenly}.func-keys_divFuncKeys__VViov{box-sizing:border-box;width:100%;height:27%;padding:10px 15px;display:flex;flex-direction:column;justify-content:space-between}.func-keys_divFuncKeys__VViov>div:first-child{display:flex;justify-content:space-between;height:16%}.func-keys_divFuncKeys__VViov>div:first-child>div{width:31%;display:flex;justify-content:space-between}.func-keys_divFuncKeys__VViov>div:not(:first-child){display:flex;justify-content:space-between;align-items:flex-end;height:16%}.basic-keys_divBasicKeys__6yzrQ{box-sizing:border-box;width:100%;height:33%;padding:12px 15px 15px;display:flex;flex-direction:column;justify-content:space-between}.basic-keys_divBasicKeys__6yzrQ>div{display:flex;justify-content:space-between;align-items:flex-end;height:18%}.panel_divPanelWrapper__aqrE_{position:relative;width:min(100%,50vh);height:100%;background-color:#eaecf3;border-radius:10px;display:flex;flex-direction:column;align-items:center}.panel_divPanelWrapper__aqrE_ [role=klw]{color:#fff}.panel_divPanelWrapper__aqrE_ [role=klgr],.panel_divPanelWrapper__aqrE_ [role=klw]{text-shadow:1px 1px 1px rgba(0,0,0,.1);font-size:12px;font-weight:700;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:18px}.panel_divPanelWrapper__aqrE_ [role=klgr]{color:#606060}.panel_divPanelWrapper__aqrE_ [role=klb]{font-size:12px;color:#000;font-size:13px}.panel_divPanelWrapper__aqrE_ [role=klb],.panel_divPanelWrapper__aqrE_ [role=klg]{text-shadow:1px 1px 1px rgba(0,0,0,.1);font-weight:700;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.panel_divPanelWrapper__aqrE_ [role=klg]{color:#16c60c;font-size:12px}.panel_divPanelWrapper__aqrE_ [role=klo]{text-shadow:1px 1px 1px rgba(0,0,0,.1);font-weight:700;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;color:#ff4500;font-size:12px}.panel_divPanelWrapper__aqrE_ [role=ksys]{width:35%;height:55%}.panel_divPanelWrapper__aqrE_ [role=ksys]>div:nth-child(2){cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;justify-content:center;align-items:center;box-shadow:inset 1px 1px 1px 0 hsla(0,0%,100%,.8),inset -1px -1px 1px 0 rgba(40,49,85,.3),1px 1px 3px 0 rgba(40,49,85,.1);transition-duration:.2s;width:100%;height:100%;background-image:linear-gradient(135deg,#525252,#000);border-radius:10px}.panel_divPanelWrapper__aqrE_ [role=ksys]>div:nth-child(2):hover{filter:opacity(.9)}.panel_divPanelWrapper__aqrE_ [role=ksys]>div:nth-child(2):active{filter:opacity(.83)}.panel_divPanelWrapper__aqrE_ [role=kdir]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;justify-content:center;align-items:center;box-shadow:inset 1px 1px 1px 0 hsla(0,0%,100%,.8),inset -1px -1px 1px 0 rgba(40,49,85,.3),1px 1px 3px 0 rgba(40,49,85,.1);transition-duration:.2s;position:absolute;left:50%;transform:translate(-50%,-10px);background-image:linear-gradient(135deg,#525252,#000);width:32%;height:12%;border-top-left-radius:50%;border-top-right-radius:50%;border-bottom-left-radius:50%;border-bottom-right-radius:50%;cursor:pointer}.panel_divPanelWrapper__aqrE_ [role=kdir]>span{width:30%;height:50%;text-align:center;font-size:15px;position:absolute;color:#fff}.panel_divPanelWrapper__aqrE_ [role=kdir]>span:first-child{left:50%;transform:translate(-50%,-40%)}.panel_divPanelWrapper__aqrE_ [role=kdir]>span:nth-child(2){left:0;transform:translate(25%) rotate(270deg)}.panel_divPanelWrapper__aqrE_ [role=kdir]>span:nth-child(3){left:50%;transform:translate(-50%,50%) rotate(180deg)}.panel_divPanelWrapper__aqrE_ [role=kdir]>span:nth-child(4){left:100%;transform:translate(-120%) rotate(90deg)}.panel_divPanelWrapper__aqrE_ [role=kfunc]{width:14%;height:100%}.panel_divPanelWrapper__aqrE_ [role=kfuncr1]{width:45%;height:100%}.panel_divPanelWrapper__aqrE_ [role=kfunc]>div:nth-child(2),.panel_divPanelWrapper__aqrE_ [role=kfuncr1]>div:nth-child(2){cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;justify-content:center;align-items:center;box-shadow:inset 1px 1px 1px 0 hsla(0,0%,100%,.8),inset -1px -1px 1px 0 rgba(40,49,85,.3),1px 1px 3px 0 rgba(40,49,85,.1);transition-duration:.2s;width:100%;height:100%;background-image:linear-gradient(135deg,#f4f5f6,#fff);border-radius:6px}.panel_divPanelWrapper__aqrE_ [role=kfunc]>div:nth-child(2):hover,.panel_divPanelWrapper__aqrE_ [role=kfuncr1]>div:nth-child(2):hover{filter:brightness(95%)}.panel_divPanelWrapper__aqrE_ [role=kfunc]>div:nth-child(2):active,.panel_divPanelWrapper__aqrE_ [role=kfuncr1]>div:nth-child(2):active{filter:brightness(90%)}.panel_divPanelWrapper__aqrE_ [role=kbasic],.panel_divPanelWrapper__aqrE_ [role=kbasicy]{width:17%;height:100%}.panel_divPanelWrapper__aqrE_ [role=kbasic]>div:nth-child(2),.panel_divPanelWrapper__aqrE_ [role=kbasicy]>div:nth-child(2){cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;justify-content:center;align-items:center;box-shadow:inset 1px 1px 1px 0 hsla(0,0%,100%,.8),inset -1px -1px 1px 0 rgba(40,49,85,.3),1px 1px 3px 0 rgba(40,49,85,.1);transition-duration:.2s;width:100%;height:100%;background-image:linear-gradient(135deg,#4c4c4c,#000);border-radius:6px}.panel_divPanelWrapper__aqrE_ [role=kbasic]>div:nth-child(2):hover,.panel_divPanelWrapper__aqrE_ [role=kbasicy]>div:nth-child(2):hover{filter:opacity(.9)}.panel_divPanelWrapper__aqrE_ [role=kbasic]>div:nth-child(2):active,.panel_divPanelWrapper__aqrE_ [role=kbasicy]>div:nth-child(2):active{filter:opacity(.83)}.panel_divPanelWrapper__aqrE_ [role=kbasicy]>div:nth-child(2){cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;justify-content:center;align-items:center;box-shadow:inset 1px 1px 1px 0 hsla(0,0%,100%,.8),inset -1px -1px 1px 0 rgba(40,49,85,.3),1px 1px 3px 0 rgba(40,49,85,.1);transition-duration:.2s;background-image:linear-gradient(135deg,#fffb00,#e5e100)}.panel_divPanelWrapper__aqrE_ [role=kbasicy]>div:nth-child(2):hover{filter:brightness(95%)}.panel_divPanelWrapper__aqrE_ [role=kbasicy]>div:nth-child(2):active{filter:brightness(90%)}.styles_mainMain__SRvb6{position:fixed;top:0;left:0;width:100%;height:100%;z-index:600;display:flex;justify-content:center;font-family:Ubuntu,sans-serif;-webkit-tap-highlight-color:rgba(0,0,0,0)} --------------------------------------------------------------------------------