├── src ├── react-app-env.d.ts ├── reportWebVitals.ts ├── utils │ ├── transform │ │ ├── envs │ │ │ └── list.ts │ │ ├── commands │ │ │ ├── olymp.ts │ │ │ └── textsize.ts │ │ ├── kinds │ │ │ └── mathAlign.ts │ │ ├── textCommand.ts │ │ ├── mathKind.ts │ │ ├── environment.ts │ │ ├── mathCommand.ts │ │ └── command.ts │ ├── helpers.ts │ ├── dimens.ts │ ├── stringify.ts │ └── parse.ts ├── App.tsx ├── hooks │ └── useApiGet.ts ├── index.tsx ├── components │ ├── DescriptionRenderer.tsx │ ├── example.ts │ └── Converter.tsx └── boj-unify.scss ├── public ├── robots.txt ├── favicon.ico └── index.html ├── .gitignore ├── .github └── workflows │ └── deploy.yml ├── tsconfig.json ├── LICENSE ├── package.json └── README.md /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solved-ac/boj-description-converter/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.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 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy to GitHub Pages 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | build-and-deploy: 8 | if: ${{ github.ref == 'refs/heads/main' }} 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Check out repository code 12 | uses: actions/checkout@v2 13 | - name: Install dependencies 14 | run: npm install 15 | - name: Predeploy 16 | run: npm run predeploy 17 | - name: Deploy 18 | uses: peaceiris/actions-gh-pages@v3 19 | with: 20 | github_token: ${{ secrets.GITHUB_TOKEN }} 21 | publish_dir: ./build 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/transform/envs/list.ts: -------------------------------------------------------------------------------- 1 | import { latexParser as lp } from "latex-utensils"; 2 | import { Node } from "latex-utensils/out/types/src/latex/latex_parser_types"; 3 | import { TransformerArgs } from "../../helpers"; 4 | import { transformNodeArray } from "../../parse"; 5 | 6 | export const transformListChildren = ( 7 | s: Node[], 8 | args: TransformerArgs 9 | ): string => { 10 | let ret = ""; 11 | 12 | const len = s.length; 13 | let stack: Node[] = []; 14 | 15 | for (let i = 0; i < len; i++) { 16 | const cur = s[i]; 17 | if (lp.isCommand(cur) && cur.name === "item") { 18 | if (stack) { 19 | const item = transformNodeArray(stack, args); 20 | if (item) ret += `
  • ${item}
  • `; 21 | stack = []; 22 | } 23 | } else { 24 | stack.push(cur); 25 | } 26 | } 27 | if (stack) { 28 | const item = transformNodeArray(stack, args); 29 | if (item) ret += `
  • ${item}
  • `; 30 | stack = []; 31 | } 32 | return ret; 33 | }; 34 | -------------------------------------------------------------------------------- /src/utils/helpers.ts: -------------------------------------------------------------------------------- 1 | import { latexParser as lp } from "latex-utensils"; 2 | import { 3 | Command, 4 | Environment, 5 | Node 6 | } from "latex-utensils/out/types/src/latex/latex_parser_types"; 7 | import { transformNode } from "./parse"; 8 | 9 | export type CommandTransformer = (s: Command, args: TransformerArgs) => string; 10 | export type EnvTransformer = (s: Environment, args: TransformerArgs) => string; 11 | export type NodeTransformer = (s: Node, args: TransformerArgs) => string; 12 | 13 | export interface TransformerArgs { 14 | renderMath?: boolean; 15 | italicMath?: boolean; 16 | verbatim?: boolean; 17 | allowParbreaks?: boolean; 18 | } 19 | 20 | export const transformSimpleArg = ( 21 | arg: lp.Group | lp.OptionalArg, 22 | args: TransformerArgs 23 | ): string => { 24 | return transformNode(arg.content, args); 25 | }; 26 | 27 | export const h = 28 | (str: TemplateStringsArray): CommandTransformer => 29 | (s, args) => 30 | `<${str}>${transformSimpleArg(s.args[0], args)}`; 31 | 32 | export const s = 33 | (str: TemplateStringsArray): CommandTransformer => 34 | () => 35 | str[0]; 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Suhyun Park 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 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/anchor-is-valid */ 2 | import styled from "@emotion/styled"; 3 | import { Typo } from "@solved-ac/ui-react"; 4 | import React from "react"; 5 | import Converter from "./components/Converter"; 6 | 7 | const AppContainer = styled.div` 8 | display: flex; 9 | flex-direction: column; 10 | height: 100%; 11 | padding: 32px; 12 | `; 13 | 14 | const js = 15 | "var o=CKEDITOR.filter.instances;Object.keys(o).forEach((k)=>o[k].disable())"; 16 | 17 | const App: React.FC = () => { 18 | return ( 19 | 20 |
    21 |
    30 | 31 | BOJ Stack 디스크립션 툴 32 | 33 | by solved.ac 34 |
    35 |
    36 | 49 | 50 |
    51 | ); 52 | }; 53 | 54 | export default App; 55 | -------------------------------------------------------------------------------- /src/hooks/useApiGet.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosRequestConfig } from "axios"; 2 | import { useEffect, useState } from "react"; 3 | import isEqual from "react-fast-compare"; 4 | 5 | export type GetState = 6 | | { loaded: false } 7 | | { loaded: true; error: true; errorMessage: string } 8 | | { loaded: true; error: false; data: T }; 9 | 10 | const useApiGet = ( 11 | url: string, 12 | params?: AxiosRequestConfig | undefined 13 | ) => { 14 | const [stateUrl, setStateUrl] = useState(url); 15 | const [stateParams, setStateParams] = useState< 16 | AxiosRequestConfig | undefined 17 | >(params); 18 | const [state, setState] = useState>({ loaded: false }); 19 | 20 | useEffect(() => { 21 | if (url !== stateUrl) setStateUrl(url); 22 | }, [url, stateUrl]); 23 | 24 | useEffect(() => { 25 | if (!isEqual(params, stateParams)) setStateParams(params); 26 | }, [params, stateParams]); 27 | 28 | useEffect(() => { 29 | const source = axios.CancelToken.source(); 30 | const token = source.token; 31 | setState({ loaded: false }); 32 | axios 33 | .get(stateUrl, { ...stateParams, cancelToken: token }) 34 | .then((res) => setState({ loaded: true, error: false, data: res.data })) 35 | .catch((err) => 36 | setState({ 37 | loaded: true, 38 | error: true, 39 | errorMessage: axios.isAxiosError(err) ? err.message : err.toString(), 40 | }) 41 | ); 42 | return () => { 43 | source.cancel("Operation canceled by url change."); 44 | }; 45 | }, [stateUrl, stateParams]); 46 | 47 | return state; 48 | }; 49 | 50 | export default useApiGet; 51 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import { ThemeProvider } from "@emotion/react"; 2 | import { SolvedGlobalStyles, solvedThemes } from "@solved-ac/ui-react"; 3 | import { MathJaxContext } from "better-react-mathjax"; 4 | import React from "react"; 5 | import ReactDOM from "react-dom/client"; 6 | import App from "./App"; 7 | import reportWebVitals from "./reportWebVitals"; 8 | 9 | const config = { 10 | tex: { 11 | inlineMath: [ 12 | ["$", "$"], 13 | ["\\(", "\\)"], 14 | ], 15 | displayMath: [ 16 | ["$$", "$$"], 17 | ["\\[", "\\]"], 18 | ], 19 | processEscapes: true, 20 | tags: "ams", 21 | autoload: { 22 | color: [], 23 | colorv2: ["color"], 24 | }, 25 | packages: { "[+]": ["noerrors"] }, 26 | }, 27 | options: { 28 | ignoreHtmlClass: "no-mathjax|redactor-editor", 29 | processHtmlClass: "mathjax", 30 | enableMenu: false, 31 | }, 32 | chtml: { 33 | scale: 0.9, 34 | }, 35 | loader: { 36 | load: ["input/tex", "output/chtml", "[tex]/noerrors"], 37 | }, 38 | }; 39 | 40 | const root = ReactDOM.createRoot( 41 | document.getElementById("root") as HTMLElement 42 | ); 43 | root.render( 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ); 53 | 54 | // If you want to start measuring performance in your app, pass a function 55 | // to log results (for example: reportWebVitals(console.log)) 56 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 57 | reportWebVitals(); 58 | -------------------------------------------------------------------------------- /src/utils/transform/commands/olymp.ts: -------------------------------------------------------------------------------- 1 | import { CommandTransformer, transformSimpleArg } from "../../helpers"; 2 | import { transformNode } from "../../parse"; 3 | 4 | const DELIMITER = "\n\n\n\n"; 5 | 6 | const o = 7 | (str: TemplateStringsArray): CommandTransformer => 8 | (s, args) => 9 | `${DELIMITER}\n

    ${str}

    \n`; 10 | 11 | export const unsupported = (s: string) => 12 | `${s}`; 13 | 14 | const input: CommandTransformer = (s, args) => 15 | `${transformSimpleArg( 16 | s.args[0], 17 | args 18 | )}`; 19 | 20 | const regularExmp = (s: string) => { 21 | return s 22 | .split("
    ") 23 | .map((x) => x.trim()) 24 | .filter((x) => x) 25 | .join("\n"); 26 | }; 27 | 28 | const exmp: CommandTransformer = (s, args) => { 29 | const res = s.args.map((a) => transformNode(a.content, args)); 30 | return `${res 31 | .map((r) => `
    ${regularExmp(r)}
    `) 32 | .join("")}`; 33 | }; 34 | 35 | const olympTransformers = { 36 | Specification: o`사양`, 37 | Interaction: o`인터랙션`, 38 | InputFile: o`입력`, 39 | OutputFile: o`출력`, 40 | Example: o`입출력 예시`, 41 | Examples: o`입출력 예시`, 42 | Explanation: o`설명`, 43 | Explanations: o`설명`, 44 | Illustration: o`그림`, 45 | Scoring: o`점수 계산 방법`, 46 | Note: o`노트`, 47 | Notes: o`노트`, 48 | Constraints: o`제한`, 49 | SubtaskOne: o`서브태스크 1`, 50 | SubtaskTwo: o`서브태스크 2`, 51 | SubtaskThree: o`서브태스크 3`, 52 | SubtaskFour: o`서브태스크 4`, 53 | SubtaskFive: o`서브태스크 5`, 54 | SubtaskSix: o`서브태스크 6`, 55 | Subtask: o`서브태스크`, 56 | t: input, 57 | exmp, 58 | }; 59 | 60 | export default olympTransformers; 61 | -------------------------------------------------------------------------------- /src/utils/transform/commands/textsize.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "latex-utensils/out/types/src/latex/latex_parser_types"; 2 | import { CommandTransformer, TransformerArgs, transformSimpleArg } from "../../helpers"; 3 | 4 | export enum TextSizeDefined { 5 | tiny = '.7em', 6 | scriptsize = '.75em', 7 | small = '.85em', 8 | normalsize = '1em', 9 | large = '1.15em', 10 | Large = '1.3em', 11 | LARGE = '1.45em', 12 | huge = '1.75em', 13 | Huge = '2em', 14 | }; 15 | 16 | const _textSizeCommand = (ts: TextSizeDefined, s: Command, args: TransformerArgs) => 17 | `${transformSimpleArg(s.args[0], args)}`; 18 | 19 | const tiny: CommandTransformer = (s, args) => _textSizeCommand(TextSizeDefined.tiny, s, args); 20 | const scriptsize: CommandTransformer = (s, args) => _textSizeCommand(TextSizeDefined.scriptsize, s, args); 21 | const small: CommandTransformer = (s, args) => _textSizeCommand(TextSizeDefined.small, s, args); 22 | const normalsize: CommandTransformer = (s, args) => _textSizeCommand(TextSizeDefined.normalsize, s, args); 23 | const large: CommandTransformer = (s, args) => _textSizeCommand(TextSizeDefined.large, s, args); 24 | const Large: CommandTransformer = (s, args) => _textSizeCommand(TextSizeDefined.Large, s, args); 25 | const LARGE: CommandTransformer = (s, args) => _textSizeCommand(TextSizeDefined.LARGE, s, args); 26 | const huge: CommandTransformer = (s, args) => _textSizeCommand(TextSizeDefined.huge, s, args); 27 | const Huge: CommandTransformer = (s, args) => _textSizeCommand(TextSizeDefined.Huge, s, args); 28 | 29 | const textsizeCommandTransformers = { 30 | tiny, 31 | scriptsize, 32 | small, 33 | normalsize, 34 | large, 35 | Large, 36 | LARGE, 37 | huge, 38 | Huge, 39 | }; 40 | 41 | export default textsizeCommandTransformers; 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@solved-ac/boj-description-converter", 3 | "homepage": "https://solved-ac.github.io/boj-description-converter", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "@emotion/react": "^11.9.3", 8 | "@emotion/styled": "^11.9.3", 9 | "@solved-ac/ui-react": "^0.2.7", 10 | "@testing-library/jest-dom": "^5.16.4", 11 | "@testing-library/react": "^13.2.0", 12 | "@testing-library/user-event": "^13.5.0", 13 | "@types/axios": "^0.14.0", 14 | "@types/jest": "^27.5.1", 15 | "@types/jsonp": "^0.2.1", 16 | "@types/node": "^16.11.35", 17 | "@types/react": "^18.0.9", 18 | "@types/react-dom": "^18.0.4", 19 | "axios": "^0.27.2", 20 | "better-react-mathjax": "^2.0.1", 21 | "gh-pages": "^4.0.0", 22 | "jsonp": "^0.2.1", 23 | "latex-utensils": "^4.3.0", 24 | "node-sass": "^7.0.1", 25 | "polished": "^4.2.2", 26 | "react": "^18.1.0", 27 | "react-dom": "^18.1.0", 28 | "react-fast-compare": "^3.2.0", 29 | "react-scripts": "5.0.1", 30 | "sass": "^1.52.3", 31 | "typescript": "^4.6.4", 32 | "web-vitals": "^2.1.4" 33 | }, 34 | "scripts": { 35 | "start": "react-scripts start", 36 | "build": "react-scripts build", 37 | "test": "react-scripts test", 38 | "eject": "react-scripts eject", 39 | "predeploy": "npm run build", 40 | "deploy": "gh-pages -d build" 41 | }, 42 | "eslintConfig": { 43 | "extends": [ 44 | "react-app", 45 | "react-app/jest" 46 | ] 47 | }, 48 | "browserslist": { 49 | "production": [ 50 | ">0.2%", 51 | "not dead", 52 | "not op_mini all" 53 | ], 54 | "development": [ 55 | "last 1 chrome version", 56 | "last 1 firefox version", 57 | "last 1 safari version" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/utils/transform/kinds/mathAlign.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "latex-utensils/out/types/src/latex/latex_parser_types"; 2 | import { NodeTransformer } from "../../helpers"; 3 | import { transformMathNodeArray } from "../../parse"; 4 | 5 | const transformMathAlign: NodeTransformer = (s, args) => { 6 | if (s.kind !== "env.math.align") return ""; 7 | let ret = ""; 8 | 9 | const len = s.content.length; 10 | const open = new Map(); 11 | 12 | ret += ''; 13 | open.set("line", true); 14 | 15 | ret += ''; 16 | open.set("equation", true); 17 | 18 | let tempNodes: Node[] = []; 19 | 20 | for (let i = 0; i < len; i++) { 21 | const cur = s.content[i]; 22 | if (cur.kind === "alignmentTab") { 23 | ret += transformMathNodeArray(tempNodes, args); 24 | tempNodes = []; 25 | if (open.get("equation")) { 26 | ret += ' '; 27 | } else { 28 | ret += ''; 29 | } 30 | continue; 31 | } 32 | if (cur.kind === "linebreak") { 33 | ret += transformMathNodeArray(tempNodes, args); 34 | tempNodes = []; 35 | if (open.get("equation")) { 36 | ret += ""; 37 | open.set("equation", false); 38 | } 39 | if (open.get("line")) { 40 | ret += ""; 41 | open.set("line", false); 42 | } 43 | if (i + 1 < len) { 44 | ret += ''; 45 | open.set("line", true); 46 | 47 | ret += ''; 48 | open.set("equation", true); 49 | } 50 | continue; 51 | } 52 | tempNodes.push(cur); 53 | } 54 | ret += transformMathNodeArray(tempNodes, args); 55 | tempNodes = []; 56 | 57 | return ret; 58 | }; 59 | 60 | export default transformMathAlign; 61 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 21 | 28 | 37 | BOJ 디스크립션 변환기 38 | 39 | 40 | 41 |
    42 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/utils/transform/textCommand.ts: -------------------------------------------------------------------------------- 1 | import { groupToCssDimens } from "../dimens"; 2 | import { CommandTransformer, h, s } from "../helpers"; 3 | import { transformNodeArray } from "../parse"; 4 | import olympTransformers from "./commands/olymp"; 5 | import textsizeCommandTransformers from "./commands/textsize"; 6 | 7 | export const unsupported = (s: string) => 8 | `${s}`; 9 | 10 | const includegraphics: CommandTransformer = (s, args) => { 11 | const urlArgs = s.args.filter((a) => a.kind === "arg.group"); 12 | return unsupported( 13 | `[여기에 그림 입력: "${transformNodeArray(urlArgs, args)}"]` 14 | ); 15 | }; 16 | 17 | const textmd: CommandTransformer = (s, args) => { 18 | return `${transformNodeArray( 19 | s.args, 20 | args 21 | )}`; 22 | }; 23 | 24 | const caption: CommandTransformer = (s, args) => { 25 | return `
    그림 ${unsupported( 26 | "A.0" 27 | )}: ${transformNodeArray(s.args, args)}
    `; 28 | }; 29 | 30 | const vspace: CommandTransformer = (s, args) => { 31 | if (s.args.length !== 1) return ""; 32 | if (s.args[0].kind !== "arg.group") return ""; 33 | 34 | return ``; 37 | }; 38 | 39 | const hspace: CommandTransformer = (s, args) => { 40 | if (s.args.length !== 1) return ""; 41 | if (s.args[0].kind !== "arg.group") return ""; 42 | 43 | return ``; 46 | }; 47 | 48 | const textCommandTransformers = { 49 | // The order has referred to this page: https://polygon.codeforces.com/docs/statements-tex-manual 50 | ...olympTransformers, 51 | bf: h`strong`, 52 | textbf: h`strong`, 53 | it: h`em`, 54 | textit: h`em`, 55 | /* t: defined at `commands/olymp.ts` */ 56 | tt: h`code`, 57 | texttt: h`code`, 58 | emph: h`u`, 59 | underline: h`u`, 60 | sout: h`del`, 61 | textsc: h`del`, 62 | ...textsizeCommandTransformers, 63 | includegraphics, 64 | epigraph: h`blockquote`, 65 | textmd, 66 | textsf: h`span`, 67 | textrm: h`span`, 68 | textsuperscript: h`sup`, 69 | textsubscript: h`sub`, 70 | centering: s``, 71 | caption, 72 | vspace, 73 | hspace, 74 | }; 75 | 76 | export default textCommandTransformers; 77 | -------------------------------------------------------------------------------- /src/utils/transform/mathKind.ts: -------------------------------------------------------------------------------- 1 | import { NodeTransformer } from "../helpers"; 2 | import { 3 | isMathOperator, 4 | parse, 5 | transform, 6 | transformMathNode, 7 | transformNode 8 | } from "../parse"; 9 | 10 | export const mathOperators = "+-*/|<>=&:"; 11 | 12 | const superscript: NodeTransformer = (s, args) => 13 | (s.kind === "superscript" && 14 | s.arg && 15 | `${transformMathNode(s.arg, args)}`) || 16 | ""; 17 | 18 | const subscript: NodeTransformer = (s, args) => 19 | (s.kind === "subscript" && 20 | s.arg && 21 | `${transformMathNode(s.arg, args)}`) || 22 | ""; 23 | 24 | const character: NodeTransformer = (s, args) => { 25 | const { italicMath } = args; 26 | if (s.kind !== "math.character") return ""; 27 | if (/[0-9,(){}[\]]/.exec(s.content) || isMathOperator(s)) 28 | return s.content.replace(/-/gi, "−"); 29 | return italicMath ? `${s.content}` : s.content; 30 | }; 31 | 32 | const delimiter: NodeTransformer = (s, args) => { 33 | if (s.kind !== "math.math_delimiters") return ""; 34 | const lp = parse(`$${s.left}$`); 35 | const l = typeof lp !== "string" && transform(lp, args); 36 | const rp = parse(`$${s.right}$`); 37 | const r = typeof rp !== "string" && transform(rp, args); 38 | return `${l}${transformMathNode(s.content, args)}${r}`; 39 | }; 40 | 41 | const matchingDelimiter: NodeTransformer = (s, args) => { 42 | if (s.kind !== "math.matching_delimiters") return ""; 43 | const lp = parse(`$${s.left}$`); 44 | const l = typeof lp !== "string" && transform(lp, args); 45 | const rp = parse(`$${s.right}$`); 46 | const r = typeof rp !== "string" && transform(rp, args); 47 | return `${l}${transformMathNode(s.content, args)}${r}`; 48 | }; 49 | 50 | const linebreak: NodeTransformer = (s, args) => { 51 | return "
    "; 52 | }; 53 | 54 | const text: NodeTransformer = (s, args) => { 55 | if (s.kind !== "command.text") return ""; 56 | return transformNode(s.arg, args); 57 | }; 58 | 59 | const alignmentTab: NodeTransformer = (s, args) => { 60 | if (s.kind !== "alignmentTab") return ""; 61 | return ""; 62 | }; 63 | 64 | const mathKindTransformers = { 65 | superscript, 66 | subscript, 67 | "math.character": character, 68 | "math.math_delimiters": delimiter, 69 | "math.matching_delimiters": matchingDelimiter, 70 | "command.text": text, 71 | alignmentTab, 72 | linebreak, 73 | }; 74 | 75 | export default mathKindTransformers; 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # boj-description-converter 2 | 3 | [solved-ac.github.io/boj-description-converter](https://solved-ac.github.io/boj-description-converter/) 4 | 5 | Convert [UCPC](https://github.com/ucpcc/ucpc2020-description-layout)-flavored [olymp.sty](https://github.com/GassaFM/olymp.sty) based TeX problem statements to HTML, complying with [BOJ Stack](https://stack.acmicpc.net/guide/problem) and [UCPC](https://github.com/ucpcc/problemsetting-guidelines) formatting guidelines. 6 | 7 | ## Why? 8 | 9 | For contests on BOJ with printed problemset, it is often challenging to: 10 | * Keep TeX and HTML descriptions synced - especially when multiple problemsetters are working on multiple problems 11 | * Convert one format to another - although BOJ supports MathJax, it is such a tedious and easily mistakable job to translate TeX markups to HTML. Few examples include: 12 | * `\alpha` → `α` (greek letter 'alpha') 13 | * ``` `` ... '' ``` → `“ ... ”` (double quotes) 14 | * `\textit{...}` → `...` (italic text) 15 | * `\textbf{...}` → `...` (bold text) 16 | * `\begin{center}...\end{center}` → `

    ...` (centered text) 17 | * `\t{...}` → `...` (colored verbatim) 18 | * `\begin{itemize} \item ... \end{itemize}` → `

    • ...
    ` (unordered list) 19 | 20 | This project aims to eliminate manual converting work by problemsetters, and make problemsetters to just focus on setting great problems. 21 | 22 | ## Usage 23 | 24 | 1. Copy-paste [olymp.sty](https://github.com/GassaFM/olymp.sty) based TeX problem statement into the left input area. 25 | 1. Preview the render output and fix the statement if needed. 26 | 1. Copy-paste generated HTML to BOJ Stack. 27 | 28 | ### 'I hate MathJax' Mode 29 | 30 | This tool offers the ability to try rendering the equations in pure HTML while avoiding MathJax as much as possible. Currently supports the following: 31 | 32 | * Superscripts and subscripts 33 | * Most of the TeX math symbol rendering commands, i. e. `\le`, `\delta`, `\rightarrow` 34 | * Italic texts on variables 35 | * Spacing around operators and functions like `ln` or `sin` 36 | * Fractions 37 | * `\sum` 38 | 39 | ## Contributing 40 | 41 | Contributions are welcome! 42 | 43 | ## TODO 44 | 45 | * Cleanup code 46 | * `tabular` environment 47 | * Converting `\color` names to hex string 48 | * 49 | -------------------------------------------------------------------------------- /src/utils/transform/environment.ts: -------------------------------------------------------------------------------- 1 | import { groupToCssDimens } from "../dimens"; 2 | import { EnvTransformer } from "../helpers"; 3 | import { transformNodeArray } from "../parse"; 4 | import { transformListChildren } from "./envs/list"; 5 | 6 | const children: EnvTransformer = (s, args) => 7 | transformNodeArray(s.content, args); 8 | 9 | const problem: EnvTransformer = (s, args) => 10 | transformNodeArray(s.content, { ...args, allowParbreaks: true }); 11 | 12 | const center: EnvTransformer = (s, args) => 13 | `

    ${children(s, args)}

    \n\n`; 14 | 15 | const quote: EnvTransformer = (s, args) => 16 | `
    ${children(s, { ...args, allowParbreaks: true })}
    `; 17 | 18 | const itemize: EnvTransformer = (s, args) => 19 | `
      ${transformListChildren(s.content, args)}
    \n\n`; 20 | 21 | const enumerate: EnvTransformer = (s, args) => { 22 | const parseEnumerateListStyleType = (arg: string) => { 23 | if (arg === "i.") return "lower-roman"; 24 | if (arg === "I.") return "upper-roman"; 25 | if (arg === "a.") return "lower-alpha"; 26 | if (arg === "A.") return "upper-alpha"; 27 | return null; 28 | }; 29 | 30 | const listStyleType = 31 | s.args.length === 1 && 32 | s.args[0].content.length === 1 && 33 | s.args[0].content[0].kind === "text.string" 34 | ? parseEnumerateListStyleType(s.args[0].content[0].content) 35 | : null; 36 | 37 | if (listStyleType) { 38 | return `
      ${transformListChildren( 39 | s.content, 40 | args 41 | )}
    \n\n`; 42 | } 43 | return `
      ${transformListChildren(s.content, args)}
    \n\n`; 44 | }; 45 | 46 | const example: EnvTransformer = (s, args) => 47 | `${transformNodeArray( 48 | s.content.filter((s) => s.kind === "command" && s.name === "exmp"), 49 | args 50 | )}
    표준 입력(stdin)표준 출력(stdout)
    `; 51 | 52 | const figure: EnvTransformer = (s, args) => 53 | `
    ${children(s, args)}
    `; 54 | 55 | const minipage: EnvTransformer = (s, args) => { 56 | const widthArg = s.args.find((a) => a.kind === "arg.group"); 57 | if (!widthArg || widthArg.kind !== "arg.group") 58 | return `${children(s, args)}`; 59 | 60 | const width = groupToCssDimens(widthArg); 61 | 62 | return `${children( 63 | s, 64 | args 65 | )}`; 66 | }; 67 | 68 | const textEnvTransformers = { 69 | problem, 70 | center, 71 | quote, 72 | itemize, 73 | enumerate, 74 | example, 75 | figure, 76 | minipage, 77 | }; 78 | 79 | export default textEnvTransformers; 80 | -------------------------------------------------------------------------------- /src/utils/dimens.ts: -------------------------------------------------------------------------------- 1 | import { Group } from "latex-utensils/out/types/src/latex/latex_parser"; 2 | 3 | export type LatexUnit = 4 | | "pt" 5 | | "mm" 6 | | "cm" 7 | | "in" 8 | | "ex" 9 | | "em" 10 | | "bp" 11 | | "pc" 12 | | "dd" 13 | | "cc" 14 | | "nd" 15 | | "nc" 16 | | "sp" 17 | | `\\${string}`; 18 | 19 | export type LatexDimens = [number, LatexUnit]; 20 | 21 | const latexUnitRegex = 22 | /^([+-]?([0-9]+([.][0-9]*)?|[.][0-9]+))?(pt|mm|cm|in|ex|em|bp|pc|dd|cc|nd|nc|sp|\\.+)$/gi; 23 | 24 | export const parseLatexDimens = (s: string): LatexDimens => { 25 | latexUnitRegex.lastIndex = 0; 26 | const match = latexUnitRegex.exec(s); 27 | if (!match) return [0, "mm"]; 28 | return [match[1] ? +match[1] : 0, match[4] as LatexUnit]; 29 | }; 30 | 31 | // https://en.wikibooks.org/wiki/LaTeX/Lengths 32 | export const toCssDimens = ( 33 | [v, unit]: LatexDimens, 34 | args: { 35 | // use TeX definitions instead of matching CSS ones 36 | strict?: boolean; 37 | // use px and relative units only 38 | screen?: boolean; 39 | // if screen, round to integer pixels 40 | round?: boolean; 41 | } = { strict: true, screen: true, round: true } 42 | ): string => { 43 | const { strict, screen, round } = args; 44 | 45 | // TeX-specific: bp, dd, cc, nd, nc, sp 46 | if (unit === "bp") return toCssDimens([v / 72, "in"], args); 47 | if (unit === "dd") return toCssDimens([(v * 1238) / 1157, "pt"], args); 48 | if (unit === "cc") return toCssDimens([(v * 1238 * 12) / 1157, "pt"], args); 49 | if (unit === "nd") return toCssDimens([(v * 685) / 642, "pt"], args); 50 | if (unit === "nc") return toCssDimens([(v * 685 * 12) / 642, "pt"], args); 51 | if (unit === "sp") return toCssDimens([v / 65536, "pt"], args); 52 | 53 | // Non-inch units: mm, cm 54 | if (unit === "mm") return toCssDimens([v / 10 / 2.54, "in"], args); 55 | if (unit === "cm") return toCssDimens([v / 2.54, "in"], args); 56 | 57 | // Relative units: ex, em 58 | if (unit === "ex" || unit === "em") return `${v}${unit}`; 59 | 60 | // Both defined in TeX and CSS: pt, pc 61 | if (unit === "pt") { 62 | if (strict) return toCssDimens([v / 72.27, "in"], args); 63 | return toCssDimens([v / 72, "in"], args); 64 | } 65 | if (unit === "pc") return toCssDimens([v * 12, "pt"], args); 66 | 67 | // Base unit: in 68 | if (unit === "in") { 69 | if (screen) return `${round ? Math.round(v * 96) : v * 96}px`; 70 | return `${v}${unit}`; 71 | } 72 | if (!v) return "0"; 73 | 74 | // Default lengths 75 | if (unit === "\\paperwidth") return `${v * 100}vw`; 76 | if (unit === "\\paperheight") return `${v * 100}vh`; 77 | if (unit === "\\linewidth") return `${v * 100}%`; 78 | if (unit === "\\textwidth") return `${v * 100}%`; 79 | if (unit === "\\parskip") return `${v}em`; 80 | 81 | return "0"; 82 | }; 83 | 84 | export const groupToCssDimens = (widthArg: Group) => { 85 | const dimen = widthArg.content 86 | .map((c) => { 87 | if (c.kind === "command") return `\\${c.name}`; 88 | if (c.kind === "text.string") return c.content; 89 | return ""; 90 | }) 91 | .join(""); 92 | 93 | return toCssDimens(parseLatexDimens(dimen)); 94 | }; 95 | -------------------------------------------------------------------------------- /src/components/DescriptionRenderer.tsx: -------------------------------------------------------------------------------- 1 | import { ThemeProvider } from "@emotion/react"; 2 | import styled from "@emotion/styled"; 3 | import { Card, solvedThemes } from "@solved-ac/ui-react"; 4 | import { MathJax } from "better-react-mathjax"; 5 | import { transparentize } from "polished"; 6 | import { useEffect, useState } from "react"; 7 | 8 | const CopyToClipboardOverlay = styled.div` 9 | position: absolute; 10 | left: 0; 11 | right: 0; 12 | top: 0; 13 | bottom: 0; 14 | opacity: 0; 15 | transition: opacity 0.1s ease; 16 | pointer-events: none; 17 | border-radius: 8px; 18 | border: 2px solid ${({ theme }) => theme.color.solvedAc}; 19 | background: ${({ theme }) => transparentize(0.9, theme.color.solvedAc)}; 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: 2em; 24 | font-weight: bold; 25 | backdrop-filter: blur(3px); 26 | color: ${({ theme }) => theme.color.text.primary.main}; 27 | `; 28 | 29 | const CopyToClipboardWrapper = styled.div` 30 | position: relative; 31 | width: 100%; 32 | flex: 1 0 0; 33 | cursor: pointer; 34 | 35 | &:hover > div.overlay { 36 | opacity: 1; 37 | } 38 | `; 39 | 40 | const RenderedDescription = styled.div` 41 | p { 42 | display: block; 43 | margin-block-start: 1em; 44 | margin-block-end: 1em; 45 | margin-inline-start: 0px; 46 | margin-inline-end: 0px; 47 | } 48 | h2 { 49 | display: block; 50 | font-size: 1.5em; 51 | margin-block-start: 0.83em; 52 | margin-block-end: 0.83em; 53 | margin-inline-start: 0px; 54 | margin-inline-end: 0px; 55 | font-weight: bold; 56 | } 57 | ul { 58 | display: block; 59 | list-style-type: disc; 60 | margin-block-start: 1em; 61 | margin-block-end: 1em; 62 | margin-inline-start: 0px; 63 | margin-inline-end: 0px; 64 | padding-inline-start: 40px; 65 | } 66 | ol { 67 | display: block; 68 | list-style-type: decimal; 69 | margin-block-start: 1em; 70 | margin-block-end: 1em; 71 | margin-inline-start: 0px; 72 | margin-inline-end: 0px; 73 | padding-inline-start: 40px; 74 | } 75 | `; 76 | 77 | interface Props { 78 | html: boolean; 79 | content: string; 80 | } 81 | 82 | const DescriptionRenderer: React.FC = (props) => { 83 | const { html, content } = props; 84 | const [copied, setCopied] = useState(false); 85 | 86 | const handleCopyToClipboard = (html?: boolean) => { 87 | if (html) { 88 | navigator.clipboard.writeText(content).then(() => { 89 | setCopied(true); 90 | }); 91 | return; 92 | } 93 | 94 | // Requires to run below line in Stack: 95 | // var o=CKEDITOR.filter.instances;Object.keys(o).forEach((k)=>o[k].disable()) 96 | navigator.clipboard 97 | .write([ 98 | new ClipboardItem({ 99 | "text/html": new Blob([content], { type: "text/html" }), 100 | "text/plain": new Blob([content], { type: "text/plain" }), 101 | }), 102 | ]) 103 | .then(() => { 104 | setCopied(true); 105 | }) 106 | .catch((err) => console.log(err)); 107 | }; 108 | 109 | useEffect(() => { 110 | const delay = setTimeout(() => { 111 | setCopied(false); 112 | }, 500); 113 | return () => clearTimeout(delay); 114 | }, [copied]); 115 | 116 | if (html) { 117 | return ( 118 | 119 | handleCopyToClipboard(true)}> 120 | 128 | {content} 129 | 130 | 131 | {copied ? "✓" : "HTML로 복사하기"} 132 | 133 | 134 | 135 | ); 136 | } 137 | 138 | return ( 139 | handleCopyToClipboard(false)}> 140 | 141 | 145 | 146 | 147 | {copied ? "✓" : "DOM 엘리먼트로 복사하기"} 148 | 149 | 150 | ); 151 | }; 152 | 153 | export default DescriptionRenderer; 154 | -------------------------------------------------------------------------------- /src/boj-unify.scss: -------------------------------------------------------------------------------- 1 | .preview { 2 | article, 3 | aside, 4 | details, 5 | figcaption, 6 | figure, 7 | footer, 8 | header, 9 | hgroup, 10 | main, 11 | nav, 12 | section, 13 | summary { 14 | display: block; 15 | } 16 | 17 | audio, 18 | canvas, 19 | progress, 20 | video { 21 | display: inline-block; 22 | vertical-align: baseline; 23 | } 24 | 25 | audio:not([controls]) { 26 | display: none; 27 | height: 0; 28 | } 29 | 30 | [hidden], 31 | template { 32 | display: none; 33 | } 34 | 35 | a { 36 | background: 0 0; 37 | } 38 | 39 | a:active, 40 | a:hover { 41 | outline: 0; 42 | } 43 | 44 | abbr[title] { 45 | border-bottom: 1px dotted; 46 | } 47 | 48 | b, 49 | strong { 50 | font-weight: 700; 51 | } 52 | 53 | dfn { 54 | font-style: italic; 55 | } 56 | 57 | h1 { 58 | margin: 0.67em 0; 59 | font-size: 2em; 60 | } 61 | 62 | mark { 63 | color: #000; 64 | background: #ff0; 65 | } 66 | 67 | small { 68 | font-size: 80%; 69 | } 70 | 71 | sub, 72 | sup { 73 | position: relative; 74 | font-size: 75%; 75 | line-height: 0; 76 | vertical-align: baseline; 77 | } 78 | 79 | sup { 80 | top: -0.5em; 81 | } 82 | 83 | sub { 84 | bottom: -0.25em; 85 | } 86 | 87 | img { 88 | border: 0; 89 | } 90 | 91 | svg:not(:root) { 92 | overflow: hidden; 93 | } 94 | 95 | figure { 96 | margin: 1em 40px; 97 | } 98 | 99 | hr { 100 | height: 0; 101 | -webkit-box-sizing: content-box; 102 | -moz-box-sizing: content-box; 103 | box-sizing: content-box; 104 | } 105 | 106 | pre { 107 | overflow: auto; 108 | } 109 | 110 | code, 111 | kbd, 112 | pre, 113 | samp { 114 | font-family: monospace, monospace; 115 | font-size: 1em; 116 | } 117 | 118 | button, 119 | input, 120 | optgroup, 121 | select, 122 | textarea { 123 | margin: 0; 124 | font: inherit; 125 | color: inherit; 126 | } 127 | 128 | button { 129 | overflow: visible; 130 | } 131 | 132 | button, 133 | select { 134 | text-transform: none; 135 | } 136 | 137 | button, 138 | html input[type="button"], 139 | input[type="reset"], 140 | input[type="submit"] { 141 | -webkit-appearance: button; 142 | cursor: pointer; 143 | } 144 | 145 | button[disabled], 146 | html input[disabled] { 147 | cursor: default; 148 | } 149 | 150 | button::-moz-focus-inner, 151 | input::-moz-focus-inner { 152 | padding: 0; 153 | border: 0; 154 | } 155 | 156 | input { 157 | line-height: normal; 158 | } 159 | 160 | input[type="checkbox"], 161 | input[type="radio"] { 162 | -webkit-box-sizing: border-box; 163 | -moz-box-sizing: border-box; 164 | box-sizing: border-box; 165 | padding: 0; 166 | } 167 | 168 | input[type="number"]::-webkit-inner-spin-button, 169 | input[type="number"]::-webkit-outer-spin-button { 170 | height: auto; 171 | } 172 | 173 | input[type="search"] { 174 | -webkit-box-sizing: content-box; 175 | -moz-box-sizing: content-box; 176 | box-sizing: content-box; 177 | -webkit-appearance: textfield; 178 | } 179 | 180 | input[type="search"]::-webkit-search-cancel-button, 181 | input[type="search"]::-webkit-search-decoration { 182 | -webkit-appearance: none; 183 | } 184 | 185 | fieldset { 186 | padding: 0.35em 0.625em 0.75em; 187 | margin: 0 2px; 188 | border: 1px solid silver; 189 | } 190 | 191 | legend { 192 | padding: 0; 193 | border: 0; 194 | } 195 | 196 | textarea { 197 | overflow: auto; 198 | } 199 | 200 | optgroup { 201 | font-weight: 700; 202 | } 203 | 204 | table { 205 | border-spacing: 0; 206 | border-collapse: collapse; 207 | width: 100%; 208 | } 209 | 210 | td, 211 | th { 212 | padding: 0; 213 | } 214 | 215 | pre { 216 | display: block; 217 | padding: 9.5px; 218 | margin: 0 0 10px; 219 | font-size: 13px; 220 | line-height: 1.42857143; 221 | color: #333; 222 | word-break: break-all; 223 | word-wrap: break-word; 224 | background-color: #f5f5f5; 225 | border: 1px solid #ccc; 226 | border-radius: 4px; 227 | } 228 | 229 | pre.sampledata { 230 | width: 100%; 231 | height: 100%; 232 | } 233 | 234 | code { 235 | padding: 2px 4px; 236 | font-size: 90%; 237 | border-radius: 4px; 238 | } 239 | 240 | .problem-text blockquote, 241 | .problem-text blockquote p { 242 | font-size: medium; 243 | } 244 | blockquote { 245 | padding: 5px 15px; 246 | border-left-width: 2px; 247 | } 248 | blockquote { 249 | padding: 10px 20px; 250 | margin: 0 0 20px; 251 | font-size: 17.5px; 252 | border-left: 5px solid #eee; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/utils/stringify.ts: -------------------------------------------------------------------------------- 1 | import { latexParser as lp } from "latex-utensils"; 2 | 3 | function stringifyArray( 4 | arry: lp.Node[], 5 | options: { lineBreak: string } 6 | ): string { 7 | const len = arry.length; 8 | let ret = ""; 9 | for (let i = 0; i < len; i++) { 10 | const cur = arry[i]; 11 | ret += stringify(cur, options); 12 | if ( 13 | lp.isSpace(cur) || 14 | lp.isSoftbreak(cur) || 15 | lp.isCommandParameter(cur) || 16 | lp.isSubscript(cur) || 17 | lp.isSuperscript(cur) 18 | ) { 19 | continue; 20 | } 21 | if (i + 1 < len && lp.isTextString(arry[i + 1])) { 22 | ret += " "; 23 | continue; 24 | } 25 | if ( 26 | i + 1 < len && 27 | lp.isMathCharacter(arry[i + 1]) && 28 | !lp.isMathCharacter(cur) 29 | ) { 30 | ret += " "; 31 | continue; 32 | } 33 | if ( 34 | i + 1 < len && 35 | lp.isCommand(cur) && 36 | cur.args.length === 0 && 37 | lp.isCommandParameter(arry[i + 1]) 38 | ) { 39 | ret += " "; 40 | continue; 41 | } 42 | } 43 | return ret; 44 | } 45 | 46 | export function stringify( 47 | node: lp.Node | lp.Node[], 48 | options = { lineBreak: "" } 49 | ): string { 50 | const lineBreak = options.lineBreak; 51 | if (Array.isArray(node)) { 52 | return stringifyArray(node, options); 53 | } 54 | if (lp.isTextString(node)) { 55 | return node.content; 56 | } 57 | if (lp.isCommand(node)) { 58 | return "\\" + node.name + stringifyArray(node.args, options); 59 | } 60 | if (lp.isAmsMathTextCommand(node)) { 61 | return "\\text{" + stringifyArray(node.arg.content, options) + "}"; 62 | } 63 | if (lp.isDefCommand(node)) { 64 | return "\\def" + node.token + stringifyArray(node.args, options); 65 | } 66 | if (lp.isUrlCommand(node)) { 67 | return `\\url{${node.url}}`; 68 | } 69 | if (lp.isHrefCommand(node)) { 70 | const content = stringifyArray(node.content, options); 71 | if (node.arg) { 72 | return ( 73 | "\\href" + stringify(node.arg, options) + `{${node.url}}{${content}}` 74 | ); 75 | } else { 76 | return `\\href{${node.url}}{${content}}`; 77 | } 78 | } 79 | if (lp.isLabelCommand(node)) { 80 | return `\\${node.name}{${node.label}}`; 81 | } 82 | if ( 83 | lp.isEnvironment(node) || 84 | lp.isMathEnv(node) || 85 | lp.isMathEnvAligned(node) 86 | ) { 87 | const begin = "\\begin{" + node.name + "}"; 88 | const args = stringifyArray(node.args, options); 89 | const content = stringifyArray(node.content, options); 90 | const end = "\\end{" + node.name + "}"; 91 | return ( 92 | begin + 93 | args.trim() + 94 | lineBreak + 95 | content.trim() + 96 | lineBreak + 97 | end + 98 | lineBreak 99 | ); 100 | } 101 | if (lp.isGroup(node)) { 102 | return "{" + stringifyArray(node.content, options) + "}"; 103 | } 104 | if (lp.isOptionalArg(node)) { 105 | return "[" + stringifyArray(node.content, options) + "]"; 106 | } 107 | if (lp.isParbreak(node)) { 108 | return "\\par" + lineBreak; 109 | } 110 | if (lp.isSpace(node)) { 111 | return " "; 112 | } 113 | if (lp.isSoftbreak(node)) { 114 | return "\n"; 115 | } 116 | if (lp.isLinebreak(node)) { 117 | return "\\\\"; 118 | } 119 | if (lp.isSuperscript(node)) { 120 | if (node.arg) { 121 | return "^" + stringify(node.arg, options); 122 | } else { 123 | return "^"; 124 | } 125 | } 126 | if (lp.isSubscript(node)) { 127 | if (node.arg) { 128 | return "_" + stringify(node.arg, options); 129 | } else { 130 | return "_"; 131 | } 132 | } 133 | if (lp.isAlignmentTab(node)) { 134 | return "&"; 135 | } 136 | if (lp.isCommandParameter(node)) { 137 | return "#" + node.nargs; 138 | } 139 | if (lp.isActiveCharacter(node)) { 140 | return "~"; 141 | } 142 | if (lp.isIgnore(node)) { 143 | return ""; 144 | } 145 | if (lp.isVerb(node)) { 146 | return "\\verb" + node.escape + node.content + node.escape; 147 | } 148 | if (lp.isVerbatim(node)) { 149 | return "\\begin{verbatim}" + node.content + "\\end{verbatim}" + lineBreak; 150 | } 151 | if (lp.isMinted(node)) { 152 | const args = stringify(node.args); 153 | return ( 154 | "\\begin{minted}" + args + node.content + "\\end{minted}" + lineBreak 155 | ); 156 | } 157 | if (lp.isLstlisting(node)) { 158 | const arg = node.arg ? stringify(node.arg) : ""; 159 | return "\\begin{lstlisting}" + arg + node.content + "\\end{lstlisting}"; 160 | } 161 | if (lp.isInlienMath(node)) { 162 | return "$" + stringifyArray(node.content, options) + "$"; 163 | } 164 | if (lp.isDisplayMath(node)) { 165 | return ( 166 | "\\[" + 167 | lineBreak + 168 | stringifyArray(node.content, options).trim() + 169 | lineBreak + 170 | "\\]" + 171 | lineBreak 172 | ); 173 | } 174 | if (lp.isMathCharacter(node)) { 175 | return node.content; 176 | } 177 | if (lp.isMatchingDelimiters(node)) { 178 | return ( 179 | "\\left" + 180 | node.left + 181 | " " + 182 | stringifyArray(node.content, options) + 183 | " " + 184 | "\\right" + 185 | node.right 186 | ); 187 | } 188 | if (lp.isMathDelimiters(node)) { 189 | return ( 190 | node.lcommand + 191 | node.left + 192 | stringifyArray(node.content, options) + 193 | node.rcommand + 194 | node.right 195 | ); 196 | } 197 | 198 | // node must be the never type here. 199 | const dummy: never = node; 200 | return dummy; 201 | } 202 | -------------------------------------------------------------------------------- /src/components/example.ts: -------------------------------------------------------------------------------- 1 | export const sungshiftdang = ` 2 | \\begin{problem}{성싶당}{표준 입력(stdin)}{표준 출력(stdout)}{3\\,초}{1024\\,MB} 3 | 4 | 최근 수현이는 빵가게 <성싶당>을 차렸다. 겉은 과자보다 바삭하고 속은 포근할 정도로 촉촉한 빵을 매일 구워 파는 성싶당은 얼마 지나지 않아 인스타 빵지순례 명소가 될 정도로 인기 베이커리가 되었지만, 안타깝게도 강화된 거리두기로 손님이 매우 줄었다. 5 | 6 | 성싶당은 시대의 흐름에 따라 배달 주문을 받기 시작했다. 빵 하나에는 $N$가지의 재료가 들어가는데, 각각의 재료는 두 가지 바리에이션이 있어서 두 가지 중 하나를 고를 수 있다. 예를 들자면, 반죽은 밀가루나 통밀가루 중 하나를 고를 수 있고, 시럽은 메이플 시럽과 딸기 시럽 중 하나를 고를 수 있는 식이다. 따라서 $2^N$종류의 빵이 만들어질 수 있다. 아니나 다를까 맙소사, 배달 첫날부터 $2^N$개의 주문이 들어왔는데 주문으로 들어온 빵의 종류가 모두 달랐다. 7 | 8 | 아무리 빵 굽기에 숙련된 수현이라도 $2^N$개의 주문을 처리하는 건 무리였다. 다행히도 성싶당의 직원들은 각자 재료 한 종류씩을 빠르게 준비할 수 있어서, 여러 직원이 한 재료씩 각자 동시에 준비하면 시간을 절약할 수 있다. 따라서 수현이는 다음 식이 최소가 되도록 주문들의 순서를 바꾸려고 한다. 9 | 10 | \\[\\sum_{i=1}^{2^N-1} (\\text{$i$번째 주문과 $i + 1$번째 주문에서 겹치는 재료의 종류의 수})\\] 11 | 12 | 단, 첫 번째로 주문한 손님의 빵은 첫 번째로 만들려고 한다. $N$과 첫 번째 손님의 주문이 주어질 때, 위의 식이 최소가 되도록 하려면 주문들을 어떤 순서로 처리해야 하는지 알려주자. 13 | 14 | \\InputFile 15 | 다음과 같이 입력이 주어진다. 16 | 17 | \\begin{examplebox} 18 | $N$ 19 | 20 | $p_1$ 21 | \\end{examplebox} 22 | 23 | \\begin{itemize} 24 | \\item $N$은 성싶당 빵에 들어가는 재료의 수다. ($1 \\leq N \\leq 20$) 25 | \\item $p_1$은 첫 번째 주문을 의미하는 문자열이다. $i$번째 문자는 $i$번째 재료 종류를 의미하며, 배달 주문 시스템의 대역폭을 아끼기 위해 한 바리에이션을 \\t{0}으로, 다른 바리에이션을 \\t{1}로 표현한다. ($\\left|c_0\\right|=N$) 26 | \\end{itemize} 27 | 28 | \\OutputFile 29 | 주문 $2^N$개를 $p_1$으로 시작해 한 줄에 하나씩 출력한다. 각 주문은 입력 형식과 같은 방식으로, $i$번째 문자가 $i$번째 재료의 종류가 되도록 길이 $N$의 문자열을 구성해 출력한다. 30 | 31 | 조건을 만족하는 주문 순서가 여러 개 있을 경우 그중 하나만 출력한다. 32 | 33 | \\Examples 34 | 35 | \\begin{example} 36 | \\exmp{ 37 | 1 38 | 0 39 | }{% 40 | 0 41 | 1 42 | }% 43 | \\exmp{ 44 | 2 45 | 00 46 | }{% 47 | 00 48 | 11 49 | 10 50 | 01 51 | }% 52 | \\end{example} 53 | 54 | \\end{problem} 55 | 56 | `; 57 | 58 | export const physed = ` 59 | \\begin{problem}{코딩은 체육과목 입니다}{표준 입력(stdin)}{표준 출력(stdout)}{0.5\\,초}{1024\\,MB} 60 | 61 | \\begin{figure}[h!] 62 | \\centering 63 | \\includegraphics[width=.4\\linewidth]{../pictures/long-long-long-img1.png} 64 | \\end{figure} 65 | 66 | 오늘은 혜아의 면접 날이다. 면접 준비를 열심히 해서 앞선 질문들을 잘 대답한 혜아는 이제 마지막으로 칠판에 직접 코딩하는 문제를 받았다. 혜아가 받은 문제는 두 수를 더하는 문제였다. C++ 책을 열심히 읽었던 혜아는 간단히 두 수를 더하는 코드를 칠판에 적었다. 코드를 본 면접관은 다음 질문을 했다. \`\`만약, 입출력이 $N$바이트 크기의 정수라면 프로그램을 어떻게 구현해야 할까요?" 67 | 68 | 혜아는 책에 있는 정수 자료형과 관련된 내용을 기억해 냈다. 책에는 \\t{long int}는 $4$바이트 정수까지 저장할 수 있는 정수 자료형이고 \\t{long long int}는 $8$바이트 정수까지 저장할 수 있는 정수 자료형이라고 적혀 있었다. 혜아는 이런 생각이 들었다. \`\`\\t{int} 앞에 \\t{long}을 하나씩 더 붙일 때마다 $4$바이트씩 저장할 수 있는 공간이 늘어나는 걸까? 분명 \\t{long long long int}는 $12$바이트, \\t{long long long long int}는 $16$바이트까지 저장할 수 있는 정수 자료형일 거야!" 그렇게 혜아는 당황하는 면접관의 얼굴을 뒤로한 채 칠판에 정수 자료형을 써 내려가기 시작했다. 69 | 70 | 혜아가 $N$바이트 정수까지 저장할 수 있다고 생각해서 칠판에 쓴 정수 자료형의 이름은 무엇일까? 71 | 72 | \\InputFile 73 | 첫 번째 줄에는 문제의 정수 $N$이 주어진다. $(4 \\le N \\le 1\\,000$; $N$은 $4$의 배수$)$ 74 | 75 | \\OutputFile 76 | 혜아가 $N$바이트 정수까지 저장할 수 있다고 생각하는 정수 자료형의 이름을 출력하여라. 77 | 78 | \\Examples 79 | \\begin{example} 80 | \\exmp{ 81 | 4 82 | }{% 83 | long int 84 | }% 85 | \\exmp{ 86 | 20 87 | }{% 88 | long long long long long int 89 | }% 90 | \\end{example} 91 | 92 | \\Note 93 | 94 | 출력에서 \\t{long}과 \\t{long}, \\t{long}과 \\t{int} 사이에는 공백이 하나씩 들어간다. 95 | 96 | 실제로 C++에서 각 정수 자료형이 저장할 수 있는 수의 크기는 환경에 따라 달라질 수 있다. 덧붙여, 실제로 문제 내용과 같이 \\t{long long long int}와 같은 자료형을 사용한 코드를 GCC의 C++ 컴파일러를 사용해 컴파일하려고 할 경우 \\texttt{'long long long' is too long for GCC}라는 에러 메시지와 함께 컴파일되지 않는다. 97 | 98 | \\end{problem} 99 | `; 100 | 101 | export const raspberries = ` 102 | \\begin{problem}{라즈베리 파이}{표준 입력(stdin)}{표준 출력(stdout)}{2\\,초}{1024\\,MB} 103 | 104 | \\begin{figure}[!htbp] 105 | \\centering 106 | \\begin{minipage}[t]{.5\\textwidth}\\centering% 107 | \\includegraphics[width=\\textwidth]{../hanbyeol.png} 108 | \\end{minipage} 109 | \\end{figure} 110 | 111 | 한별이는 $3$년 만에 오프라인으로 개최되는 UCPC 본선을 맞아 특별한 이벤트를 계획했다. 바로 참가자들과 라즈베리 파이를 나눠 먹는 것이다! 한별이는 원기둥 모양의 파이를 모든 조각이 밑면이 부채꼴인 기둥 모양이 되도록 $M$등분하고, 각 조각 위에 라즈베리를 하나씩 놓았다. 그리고 각 조각에 시계 방향 순서대로 $1$번에서 $M$번까지 번호를 붙였다. 112 | 113 | 한별이는 대회에 총 $N$($N \\leq M$)명의 참가자가 참가한다는 말을 듣고, 각 참가자에게 몇 번째 조각을 나눠줄지 미리 정해 두었다. 마침내 모든 참가자가 대회장에 도착하고 한별이가 계획대로 파이 조각을 나눠주려는 순간, 한 참가자가 파이 위에 올려진 한 라즈베리를 가리키며 \`\`나에게 저 라즈베리를 주지 않는다면 문제를 $10$분 만에 다 풀어버리겠다!''라고 선언했다. 그러자 다른 참가자들도 하나둘씩 본인이 원하는 라즈베리를 말하기 시작했고, 결국 모든 참가자가 본인이 먹고 싶은 라즈베리를 하나씩 말하고 돌아갔다. 114 | 115 | 한별이는 참가자들의 요구를 들어주기 위해 파이에 장식된 라즈베리들의 위치를 조정하려 한다. 그러나 이 라즈베리는 환경 변화에 민감해서 다음과 같은 방법으로 옮기지 않으면 금방 상해 버리고 만다. 116 | 117 | \\begin{itemize} 118 | \\item 조각 하나를 선택하여, 그 조각에 있는 모든 라즈베리를 바로 다음 조각으로 옮긴다. 119 | \\end{itemize} 120 | 121 | 여기서 $1$번 조각의 바로 다음 조각은 $2$번 조각, $2$번 조각의 바로 다음 조각은 $3$번 조각, $\\cdots$, $M-1$번 조각의 바로 다음 조각은 $M$번 조각, $M$번 조각의 바로 다음 조각은 $1$번 조각이다. 122 | 123 | 라즈베리는 상하면 다른 라즈베리에도 나쁜 영향을 주므로, 한별이는 어떤 라즈베리도 상하지 않도록 하면서 라즈베리를 최소한으로 옮겨서 모든 참가자의 요구를 들어주려고 한다. 대회가 $10$분 만에 끝나 버리는 참사를 막기 위해 한별이를 도와주자. 124 | 125 | 단, 참가자들은 자신이 원하는 라즈베리와 다른 라즈베리를 같이 먹게 되는 것은 신경 쓰지 않으며, 라즈베리를 옮길 때 라즈베리 여러 개가 있는 조각을 선택하더라도 이동 횟수는 한 번으로 친다. 126 | 127 | 128 | \\InputFile 129 | 130 | 첫 번째 줄에는 파이의 조각 수 $M$과 대회 참가자의 수 $N$이 공백으로 구분되어 주어진다. $(1 \\leq N \\leq M \\leq 300\\,000)$ 131 | 132 | 두 번째 줄에는 $N$개의 정수 $a_1, \\cdots, a_N$이 공백으로 구분되어 주어진다. $(1 \\leq a_i \\leq M)$ $a_i$는 $i$번째 참가자에게 배정한 파이 조각의 번호를 의미하며, 모든 $a_i$는 서로 다르다. 133 | 134 | 세 번째 줄에는 $N$개의 정수 $b_1, \\cdots, b_N$이 공백으로 구분되어 주어진다. $(1 \\leq b_i \\leq M)$ $b_i$는 $i$번째 참가자가 원하는 라즈베리가 위치한 파이 조각의 번호를 의미한다. 135 | 136 | 137 | \\OutputFile 138 | 139 | 라즈베리를 상하지 않게 하면서 모든 참가자의 요구를 들어줄 수 있으면 라즈베리의 이동 횟수의 최솟값을 출력한다. 그렇지 않은 경우 $-1$을 출력한다. 140 | 141 | \\Examples 142 | \\begin{example} 143 | \\exmp{ 144 | 5 2 145 | 3 5 146 | 1 4 147 | }{% 148 | 3 149 | }% 150 | \\exmp{ 151 | 3 2 152 | 3 2 153 | 1 2 154 | }{% 155 | 5 156 | }% 157 | \\exmp{ 158 | 4 3 159 | 1 3 4 160 | 1 1 3 161 | }{% 162 | -1 163 | }% 164 | \\end{example} 165 | 166 | \\Note 167 | 첫 번째 예제에서, 다음과 같은 방법으로 라즈베리를 총 $3$번 옮겼을 때 모든 참가자의 요구를 들어줄 수 있다. 이보다 적게 옮기는 방법은 없다. 168 | \\begin{enumerate}[i.] 169 | \\item $1$번 조각에 있는 모든 라즈베리를 $2$번 조각으로 옮긴다. 170 | \\item $2$번 조각에 있는 모든 라즈베리를 $3$번 조각으로 옮긴다. 171 | \\item $4$번 조각에 있는 모든 라즈베리를 $5$번 조각으로 옮긴다. 172 | \\end{enumerate} 173 | 174 | \\end{problem} 175 | `; 176 | -------------------------------------------------------------------------------- /src/components/Converter.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import { Button, TextField, Typo } from "@solved-ac/ui-react"; 3 | import React, { useState } from "react"; 4 | import "../boj-unify.scss"; 5 | import { 6 | findFirstProblemEnv, 7 | parse, 8 | transformNode, 9 | transformProblemEnv 10 | } from "../utils/parse"; 11 | import DescriptionRenderer from "./DescriptionRenderer"; 12 | import { physed, raspberries, sungshiftdang } from "./example"; 13 | 14 | const ConverterContainer = styled.div` 15 | flex: 1 0 0; 16 | height: 100%; 17 | display: flex; 18 | gap: 16px; 19 | `; 20 | 21 | const ConverterSection = styled.div` 22 | flex: 1 0 0; 23 | display: flex; 24 | flex-direction: column; 25 | `; 26 | 27 | const titleBojStackName = (s: string) => { 28 | if (s === "Statement") return "문제"; 29 | if (s === "InputFile") return "입력"; 30 | if (s === "OutputFile") return "출력"; 31 | if (s === "Interaction") return "인터랙션"; 32 | if (s === "Note") return "노트"; 33 | if (s === "Notes") return "노트"; 34 | if (s === "Constraints") return "제한"; 35 | if (s === "Examples") return "예제"; 36 | return s; 37 | }; 38 | 39 | const Converter: React.FC = () => { 40 | const [latex, setLatex] = useState(sungshiftdang); 41 | const [html, setHtml] = useState(false); 42 | const [jax, setJax] = useState(true); 43 | 44 | const toParse = latex || sungshiftdang; 45 | const parsed = parse(toParse); 46 | 47 | const problemEnv = 48 | (typeof parsed !== "string" && findFirstProblemEnv(parsed.content)) || null; 49 | const transformed = (problemEnv && 50 | transformProblemEnv(problemEnv, { 51 | renderMath: !jax, 52 | })) || 53 | (typeof parsed !== "string" && 54 | transformNode(parsed.content, { 55 | renderMath: !jax, 56 | })) || { meta: {}, content: [] }; 57 | 58 | console.log(parsed); 59 | return ( 60 | 61 | 62 |
    63 | 64 | LaTeX 65 | 66 | 예시: 67 | 76 | 82 | 91 |
    92 | 93 | multiline 94 | value={latex} 95 | onChange={(e: React.ChangeEvent) => 96 | setLatex(e.target.value) 97 | } 98 | style={{ width: "100%", flex: "1 0 0" }} 99 | /> 100 |
    101 | 102 |
    103 | 104 | Result 105 | 106 | 109 | 112 |
    113 |
    123 | {typeof transformed === "string" ? ( 124 | 125 | ) : ( 126 | <> 127 | {transformed.meta.title && ( 128 | 132 | )} 133 | 137 | {transformed.meta.input && ( 138 | 139 | 입력{" "} 140 | 145 | 146 | )} 147 | {transformed.meta.output && ( 148 | 149 | 출력{" "} 150 | 155 | 156 | )} 157 | {transformed.meta.timeLimit && ( 158 | 159 | 시간 제한{" "} 160 | 165 | 166 | )} 167 | {transformed.meta.memoryLimit && ( 168 | 169 | 메모리 제한{" "} 170 | 175 | 176 | )} 177 | 178 | {transformed.content.map((t) => ( 179 | 180 | {titleBojStackName(t.title)} 181 | 182 | 183 | ))} 184 | 185 | )} 186 |
    187 |
    188 |
    189 | ); 190 | }; 191 | 192 | export default Converter; 193 | 194 | // CKEDITOR.config.copyFormatting_allowRules = '*' 195 | -------------------------------------------------------------------------------- /src/utils/transform/mathCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandTransformer, s } from "../helpers"; 2 | import { transformMathNode } from "../parse"; 3 | 4 | export const mathCommandOperators = [ 5 | "cdot", 6 | "gtrdot", 7 | "pmod", 8 | "cdotp", 9 | "intercal", 10 | "pod", 11 | "centerdot", 12 | "land", 13 | "rhd", 14 | "circ", 15 | "leftthreetimes", 16 | "rightthreetimes", 17 | "amalg", 18 | "circledast", 19 | "ldotp", 20 | "rtimes", 21 | "And", 22 | "circledcirc", 23 | "lor", 24 | "setminus", 25 | "ast", 26 | "circleddash", 27 | "lessdot", 28 | "smallsetminus", 29 | "barwedge", 30 | "Cup", 31 | "lhd", 32 | "sqcap", 33 | "bigcirc", 34 | "cup", 35 | "ltimes", 36 | "sqcup", 37 | "bmodmod", 38 | "bmod", 39 | "curlyvee", 40 | "mod", 41 | "times", 42 | "boxdot", 43 | "curlywedge", 44 | "mp", 45 | "unlhd", 46 | "boxminus", 47 | "div", 48 | "odot", 49 | "unrhd", 50 | "boxplus", 51 | "divideontimes", 52 | "ominus", 53 | "uplus", 54 | "boxtimes", 55 | "dotplus", 56 | "oplus", 57 | "vee", 58 | "bullet", 59 | "doublebarwedge", 60 | "otimes", 61 | "veebar", 62 | "Cap", 63 | "doublecap", 64 | "oslash", 65 | "wedge", 66 | "cap", 67 | "doublecup", 68 | "pm", 69 | "plusmn", 70 | "wr", 71 | "doteqdot", 72 | "lessapprox", 73 | "smile", 74 | "eqcirc", 75 | "lesseqgtr", 76 | "sqsubset", 77 | "eqcolon", 78 | "minuscolon", 79 | "lesseqqgtr", 80 | "sqsubseteq", 81 | "Eqcolon", 82 | "minuscoloncolon", 83 | "lessgtr", 84 | "sqsupset", 85 | "approx", 86 | "eqqcolon", 87 | "equalscolon", 88 | "lesssim", 89 | "sqsupseteq", 90 | "approxcolon", 91 | "Eqqcolon", 92 | "equalscoloncolon", 93 | "ll", 94 | "Subset", 95 | "approxcoloncolon", 96 | "eqsim", 97 | "lll", 98 | "subset", 99 | "sub", 100 | "approxeq", 101 | "eqslantgtr", 102 | "llless", 103 | "subseteq", 104 | "sube", 105 | "asymp", 106 | "eqslantless", 107 | "lt", 108 | "subseteqq", 109 | "backepsilon", 110 | "equiv", 111 | "mid", 112 | "succ", 113 | "backsim", 114 | "fallingdotseq", 115 | "models", 116 | "succapprox", 117 | "backsimeq", 118 | "frown", 119 | "multimap", 120 | "succcurlyeq", 121 | "between", 122 | "ge", 123 | "origof", 124 | "succeq", 125 | "bowtie", 126 | "geq", 127 | "owns", 128 | "succsim", 129 | "bumpeq", 130 | "geqq", 131 | "parallel", 132 | "Supset", 133 | "Bumpeq", 134 | "geqslant", 135 | "perp", 136 | "supset", 137 | "circeq", 138 | "gg", 139 | "pitchfork", 140 | "supseteq", 141 | "supe", 142 | "colonapprox", 143 | "ggg", 144 | "prec", 145 | "supseteqq", 146 | "Colonapprox", 147 | "coloncolonapprox", 148 | "gggtr", 149 | "precapprox", 150 | "thickapprox", 151 | "coloneq", 152 | "colonminus", 153 | "gt", 154 | "preccurlyeq", 155 | "thicksim", 156 | "Coloneq", 157 | "coloncolonminus", 158 | "gtrapprox", 159 | "preceq", 160 | "trianglelefteq", 161 | "coloneqq", 162 | "colonequals", 163 | "gtreqless", 164 | "precsim", 165 | "triangleq", 166 | "Coloneqq", 167 | "coloncolonequals", 168 | "gtreqqless", 169 | "propto", 170 | "trianglerighteq", 171 | "colonsim", 172 | "gtrless", 173 | "risingdotseq", 174 | "varpropto", 175 | "Colonsim", 176 | "coloncolonsim", 177 | "gtrsim", 178 | "shortmid", 179 | "vartriangle", 180 | "cong", 181 | "imageof", 182 | "shortparallel", 183 | "vartriangleleft", 184 | "curlyeqprec", 185 | "in", 186 | "isin", 187 | "sim", 188 | "vartriangleright", 189 | "curlyeqsucc", 190 | "Join", 191 | "simcolon", 192 | "vcentcolon", 193 | "ratio", 194 | "dashv", 195 | "le", 196 | "simcoloncolon", 197 | "vdash", 198 | "dblcolon", 199 | "coloncolon", 200 | "leq", 201 | "simeq", 202 | "vDash", 203 | "doteq", 204 | "leqq", 205 | "smallfrown", 206 | "Vdash", 207 | "Doteq", 208 | "leqslant", 209 | "smallsmile", 210 | "Vvdash", 211 | "gnapprox", 212 | "ngeqslant", 213 | "nsubseteq", 214 | "precneqq", 215 | "gneq", 216 | "ngtr", 217 | "nsubseteqq", 218 | "precnsim", 219 | "gneqq", 220 | "nleq", 221 | "nsucc", 222 | "subsetneq", 223 | "gnsim", 224 | "nleqq", 225 | "nsucceq", 226 | "subsetneqq", 227 | "gvertneqq", 228 | "nleqslant", 229 | "nsupseteq", 230 | "succnapprox", 231 | "lnapprox", 232 | "nless", 233 | "nsupseteqq", 234 | "succneqq", 235 | "lneq", 236 | "nmid", 237 | "ntriangleleft", 238 | "succnsim", 239 | "lneqq", 240 | "notin", 241 | "ntrianglelefteq", 242 | "supsetneq", 243 | "lnsim", 244 | "notni", 245 | "ntriangleright", 246 | "supsetneqq", 247 | "lvertneqq", 248 | "nparallel", 249 | "ntrianglerighteq", 250 | "varsubsetneq", 251 | "ncong", 252 | "nprec", 253 | "nvdash", 254 | "varsubsetneqq", 255 | "ne", 256 | "npreceq", 257 | "nvDash", 258 | "varsupsetneq", 259 | "neq", 260 | "nshortmid", 261 | "nVDash", 262 | "varsupsetneqq", 263 | "ngeq", 264 | "nshortparallel", 265 | "nVdash", 266 | "ngeqq", 267 | "nsim", 268 | "precnapprox", 269 | "circlearrowleft", 270 | "leftharpoonup", 271 | "rArr", 272 | "circlearrowright", 273 | "leftleftarrows", 274 | "rarr", 275 | "curvearrowleft", 276 | "leftrightarrow", 277 | "restriction", 278 | "curvearrowright", 279 | "Leftrightarrow", 280 | "rightarrow", 281 | "Darr", 282 | "leftrightarrows", 283 | "Rightarrow", 284 | "dArr", 285 | "leftrightharpoons", 286 | "rightarrowtail", 287 | "darr", 288 | "leftrightsquigarrow", 289 | "rightharpoondown", 290 | "dashleftarrow", 291 | "Lleftarrow", 292 | "rightharpoonup", 293 | "dashrightarrow", 294 | "longleftarrow", 295 | "rightleftarrows", 296 | "downarrow", 297 | "Longleftarrow", 298 | "rightleftharpoons", 299 | "Downarrow", 300 | "longleftrightarrow", 301 | "rightrightarrows", 302 | "downdownarrows", 303 | "Longleftrightarrow", 304 | "rightsquigarrow", 305 | "downharpoonleft", 306 | "longmapsto", 307 | "Rrightarrow", 308 | "downharpoonright", 309 | "longrightarrow", 310 | "Rsh", 311 | "gets", 312 | "Longrightarrow", 313 | "searrow", 314 | "Harr", 315 | "looparrowleft", 316 | "swarrow", 317 | "hArr", 318 | "looparrowright", 319 | "to", 320 | "harr", 321 | "Lrarr", 322 | "twoheadleftarrow", 323 | "hookleftarrow", 324 | "lrArr", 325 | "twoheadrightarrow", 326 | "hookrightarrow", 327 | "lrarr", 328 | "Uarr", 329 | "iff", 330 | "Lsh", 331 | "uArr", 332 | "impliedby", 333 | "mapsto", 334 | "uarr", 335 | "implies", 336 | "nearrow", 337 | "uparrow", 338 | "Larr", 339 | "nleftarrow", 340 | "Uparrow", 341 | "lArr", 342 | "nLeftarrow", 343 | "updownarrow", 344 | "larr", 345 | "nleftrightarrow", 346 | "Updownarrow", 347 | "leadsto", 348 | "nLeftrightarrow", 349 | "upharpoonleft", 350 | "leftarrow", 351 | "nrightarrow", 352 | "upharpoonright", 353 | "Leftarrow", 354 | "nRightarrow", 355 | "upuparrows", 356 | "leftarrowtail", 357 | "nwarrow", 358 | "leftharpoondown", 359 | "Rarr", 360 | "in", 361 | "ni", 362 | "isin", 363 | "subset", 364 | "supset", 365 | "mid", 366 | "land", 367 | "lor", 368 | "notni", 369 | "sum", 370 | "int", 371 | "iint", 372 | "iiint", 373 | "oint", 374 | "oiint", 375 | "oiiint", 376 | "prod", 377 | "coprod", 378 | "bigotimes", 379 | "bigoplus", 380 | "bigodot", 381 | "biguplus", 382 | "bigvee", 383 | "bigwedge", 384 | "bigcap", 385 | "bigsqcap", 386 | "bigcup", 387 | "arcsin", 388 | "cosec", 389 | "deg", 390 | "sec", 391 | "arccos", 392 | "cosh", 393 | "dim", 394 | "sin", 395 | "arctan", 396 | "cot", 397 | "exp", 398 | "sinh", 399 | "arctg", 400 | "cotg", 401 | "hom", 402 | "sh", 403 | "arcctg", 404 | "coth", 405 | "ker", 406 | "tan", 407 | "arg", 408 | "csc", 409 | "lg", 410 | "tanh", 411 | "ch", 412 | "ctg", 413 | "ln", 414 | "tg", 415 | "cos", 416 | "cth", 417 | "log", 418 | "th", 419 | "argmax", 420 | "injlim", 421 | "min", 422 | "varinjlim", 423 | "argmin", 424 | "lim", 425 | "plim", 426 | "varliminf", 427 | "det", 428 | "liminf", 429 | "Pr", 430 | "varlimsup", 431 | "gcd", 432 | "limsup", 433 | "projlim", 434 | "varprojlim", 435 | "inf", 436 | "max", 437 | "sup", 438 | "operatorname", 439 | ]; 440 | 441 | const frac: CommandTransformer = (s, args) => { 442 | const [p, q] = s.args; 443 | return `${transformMathNode( 444 | p, 445 | args 446 | )}${transformMathNode( 447 | q, 448 | args 449 | )}`; 450 | }; 451 | 452 | const underline: CommandTransformer = (s, args) => { 453 | return `${transformMathNode(s.args[0], args)}`; 454 | }; 455 | 456 | const mathsf: CommandTransformer = (s, args) => { 457 | return transformMathNode(s.args[0], args); 458 | }; 459 | 460 | const mathbf: CommandTransformer = (s, args) => { 461 | return `${transformMathNode(s.args[0], args)}`; 462 | }; 463 | 464 | const mathrm: CommandTransformer = (s, args) => { 465 | return transformMathNode(s.args[0], { 466 | ...args, 467 | italicMath: false, 468 | }); 469 | }; 470 | 471 | const sqrt: CommandTransformer = (s, args) => { 472 | if (!s.args.length) return "√"; 473 | return `√ ${transformMathNode( 474 | s.args[0], 475 | args 476 | )}`; 477 | }; 478 | 479 | const mathCommandTransformers = { 480 | displaystyle: s``, 481 | mathsf, 482 | mathbf, 483 | mathrm, 484 | operatorname: mathrm, 485 | frac, 486 | underline, 487 | arcsin: s`arcsin`, 488 | cosec: s`cosec`, 489 | deg: s`deg`, 490 | sec: s`sec`, 491 | arccos: s`arccos`, 492 | cosh: s`cosh`, 493 | dim: s`dim`, 494 | sin: s`sin`, 495 | arctan: s`arctan`, 496 | cot: s`cot`, 497 | exp: s`exp`, 498 | sinh: s`sinh`, 499 | arctg: s`arctg`, 500 | cotg: s`cotg`, 501 | hom: s`hom`, 502 | sh: s`sh`, 503 | arcctg: s`arcctg`, 504 | coth: s`coth`, 505 | ker: s`ker`, 506 | tan: s`tan`, 507 | arg: s`arg`, 508 | csc: s`csc`, 509 | lg: s`lg`, 510 | tanh: s`tanh`, 511 | ch: s`ch`, 512 | ctg: s`ctg`, 513 | ln: s`ln`, 514 | tg: s`tg`, 515 | cos: s`cos`, 516 | cth: s`cth`, 517 | log: s`log`, 518 | th: s`th`, 519 | argmax: s`arg max`, 520 | injlim: s`inj lim`, 521 | min: s`min`, 522 | argmin: s`arg min`, 523 | lim: s`lim`, 524 | plim: s`plim`, 525 | det: s`det`, 526 | liminf: s`liminf`, 527 | Pr: s`Pr`, 528 | gcd: s`gcd`, 529 | limsup: s`lim sup`, 530 | projlim: s`proj lim`, 531 | inf: s`inf`, 532 | max: s`max`, 533 | sup: s`sup`, 534 | sqrt, 535 | }; 536 | 537 | export default mathCommandTransformers; 538 | -------------------------------------------------------------------------------- /src/utils/parse.ts: -------------------------------------------------------------------------------- 1 | import { latexParser as lp } from "latex-utensils"; 2 | import { 3 | AstRoot, 4 | Environment, 5 | Node 6 | } from "latex-utensils/out/types/src/latex/latex_parser_types"; 7 | import { TransformerArgs } from "./helpers"; 8 | import { stringify } from "./stringify"; 9 | import commonCommandTransformers from "./transform/command"; 10 | import textEnvTransformers from "./transform/environment"; 11 | import transformMathAlign from "./transform/kinds/mathAlign"; 12 | import mathCommandTransformers, { 13 | mathCommandOperators 14 | } from "./transform/mathCommand"; 15 | import mathKindTransformers, { mathOperators } from "./transform/mathKind"; 16 | import commandTransformers, { unsupported } from "./transform/textCommand"; 17 | 18 | export const regularizeText = (s: string) => 19 | s 20 | .replace(/``/g, "“") 21 | .replace(/''/g, "”") 22 | .replace(/'/g, "’") 23 | .replace(/"/g, "”") 24 | .replace(/"/g, "”") 25 | .replace(/`/g, "‘") 26 | .replace(/'/g, "’"); 27 | 28 | export const escapeHtml = (s: string) => 29 | s 30 | .replace(/&/g, "&") 31 | .replace(//g, ">") 33 | .replace(/"/g, """) 34 | .replace(/'/g, "'"); 35 | 36 | export const parse = (s: string): AstRoot | string => { 37 | try { 38 | return lp.parse(s, { startRule: "Root" }) as AstRoot; 39 | } catch (e) { 40 | return s; 41 | } 42 | }; 43 | 44 | export const isMathOperator = (s: Node) => { 45 | if (s.kind === "math.character") { 46 | return mathOperators.includes(s.content); 47 | } 48 | if (s.kind === "command") { 49 | return mathCommandOperators.includes(s.name); 50 | } 51 | return false; 52 | }; 53 | 54 | const bigOperators = [ 55 | "sum", 56 | "prod", 57 | "lim", 58 | "liminf", 59 | "limsup", 60 | "projlim", 61 | "injlim", 62 | "plim", 63 | "inf", 64 | "sup", 65 | "max", 66 | "argmax", 67 | "min", 68 | "argmin", 69 | "gcd", 70 | "det", 71 | "bigcap", 72 | "bigcup", 73 | "bigodot", 74 | "bigoplus", 75 | "bigotimes", 76 | "bigsqcup", 77 | "biguplus", 78 | "bigvee", 79 | "bigwedge", 80 | "int", 81 | "coprod", 82 | "oint", 83 | "iint", 84 | "iiint", 85 | "iiiint", 86 | "idotsint", 87 | "oiint", 88 | "varoint", 89 | "varint", 90 | ]; 91 | 92 | export const isMathBigOperator = (s: Node) => { 93 | if (s.kind === "command") { 94 | return bigOperators.includes(s.name); 95 | } 96 | return false; 97 | }; 98 | 99 | export const transformMathNodeArray = ( 100 | s: Node[], 101 | args: TransformerArgs 102 | ): string => { 103 | let ret = ""; 104 | 105 | const len = s.length; 106 | for (let i = 0; i < len; i++) { 107 | const cur = s[i]; 108 | if (i + 1 < len && isMathBigOperator(cur)) { 109 | const nxt1 = s[i + 1]; 110 | if (nxt1.kind === "superscript") { 111 | if (i + 2 < len) { 112 | const nxt2 = s[i + 2]; 113 | if (nxt2.kind === "subscript") { 114 | ret += `${ 115 | nxt1.arg ? transformMathNode(nxt1.arg, args) : " " 116 | }${transformMathNode( 117 | cur, 118 | args 119 | )}${ 120 | nxt2.arg ? transformMathNode(nxt2.arg, args) : " " 121 | } `; 122 | i += 2; 123 | continue; 124 | } 125 | } 126 | ret += `${ 127 | nxt1.arg ? transformMathNode(nxt1.arg, args) : " " 128 | }${transformMathNode( 129 | cur, 130 | args 131 | )}  `; 132 | i += 1; 133 | continue; 134 | } 135 | if (nxt1.kind === "subscript") { 136 | if (i + 2 < len) { 137 | const nxt2 = s[i + 2]; 138 | if (nxt2.kind === "superscript") { 139 | ret += `${ 140 | nxt2.arg ? transformMathNode(nxt2.arg, args) : " " 141 | }${transformMathNode( 142 | cur, 143 | args 144 | )}${ 145 | nxt1.arg ? transformMathNode(nxt1.arg, args) : " " 146 | } `; 147 | i += 2; 148 | continue; 149 | } 150 | } 151 | ret += ` ${transformMathNode( 152 | cur, 153 | args 154 | )}${ 155 | nxt1.arg ? transformMathNode(nxt1.arg, args) : " " 156 | } `; 157 | i += 1; 158 | continue; 159 | } 160 | } 161 | ret += transformMathNode(cur, args); 162 | if (i + 1 < len && (isMathOperator(cur) || isMathOperator(s[i + 1]))) { 163 | if ( 164 | cur.kind === "math.character" && 165 | (cur.content === "-" || cur.content === "+") 166 | ) { 167 | if (!i) continue; 168 | if (isMathOperator(s[i - 1])) continue; 169 | } 170 | ret += " "; 171 | continue; 172 | } 173 | if (i + 1 < len && cur.kind === "math.character") { 174 | if (cur.content === ",") ret += " "; 175 | } 176 | } 177 | return ret; 178 | }; 179 | 180 | export const transformMathNode = ( 181 | s: Node | Node[], 182 | args: TransformerArgs 183 | ): string => { 184 | if (Array.isArray(s)) return transformMathNodeArray(s, args); 185 | if (s.kind === "arg.group") return transformMathNode(s.content, args); 186 | 187 | if (Object.keys(mathKindTransformers).includes(s.kind)) 188 | return mathKindTransformers[s.kind as keyof typeof mathKindTransformers]( 189 | s, 190 | args 191 | ); 192 | 193 | if (s.kind === "command") { 194 | if (Object.keys(commonCommandTransformers).includes(s.name)) 195 | return commonCommandTransformers[ 196 | s.name as keyof typeof commonCommandTransformers 197 | ](s, args); 198 | if (Object.keys(mathCommandTransformers).includes(s.name)) 199 | return mathCommandTransformers[ 200 | s.name as keyof typeof mathCommandTransformers 201 | ](s, args); 202 | } 203 | 204 | return unsupported(JSON.stringify(s)); 205 | }; 206 | 207 | const paragraphEnvs = ["center", "figure", "itemize", "enumerate", "quote"]; 208 | const paragraphKinds = ["env.math.align"]; 209 | 210 | export const transformNodeArray = ( 211 | s: Node[], 212 | args: TransformerArgs 213 | ): string => { 214 | const { renderMath, allowParbreaks } = args; 215 | const subArgs = { ...args, allowParbreaks: false }; 216 | let ret = ""; 217 | let paragraph = ""; 218 | 219 | const len = s.length; 220 | const open = new Map(); 221 | 222 | for (let i = 0; i < len; i++) { 223 | const cur = s[i]; 224 | if (lp.isEnvironment(cur)) { 225 | if (paragraphEnvs.includes(cur.name)) { 226 | if (paragraph) { 227 | ret += allowParbreaks ? `

    ${paragraph}

    \n\n` : paragraph; 228 | paragraph = ""; 229 | } 230 | ret += transformNode(cur, subArgs); 231 | continue; 232 | } 233 | } 234 | if (cur.kind === "env.math.align") { 235 | if (paragraph) { 236 | ret += allowParbreaks ? `

    ${paragraph}

    \n\n` : paragraph; 237 | paragraph = ""; 238 | } 239 | if (!renderMath) { 240 | ret += transformNode( 241 | { kind: "displayMath", content: [cur], location: cur.location }, 242 | { ...subArgs, italicMath: true } 243 | ); 244 | } else { 245 | ret += '

    '; 246 | ret += transformMathAlign(cur, { ...subArgs, italicMath: true }); 247 | ret += "

    \n\n"; 248 | } 249 | continue; 250 | } 251 | if (lp.isParbreak(cur)) { 252 | if (i + 1 < len) { 253 | const nxt = s[i + 1]; 254 | if ( 255 | (lp.isEnvironment(nxt) && paragraphEnvs.includes(nxt.name)) || 256 | paragraphKinds.includes(cur.kind) 257 | ) { 258 | if (paragraph) { 259 | ret += allowParbreaks ? `

    ${paragraph}

    \n\n` : paragraph; 260 | paragraph = ""; 261 | } 262 | continue; 263 | } 264 | } 265 | if (paragraph) { 266 | ret += allowParbreaks ? `

    ${paragraph}

    \n\n` : paragraph; 267 | paragraph = ""; 268 | } 269 | continue; 270 | } 271 | if (lp.isCommand(cur)) { 272 | if (cur.name === "color") { 273 | if (open.get("color")) ret += "
    "; 274 | ret += ``; 278 | open.set("color", true); 279 | continue; 280 | } 281 | } 282 | paragraph += transformNode(cur, subArgs); 283 | } 284 | if (paragraph) { 285 | ret += allowParbreaks ? `

    ${paragraph}

    \n\n` : paragraph; 286 | paragraph = ""; 287 | } 288 | if (open.get("color")) ret += "
    "; 289 | return ret; 290 | }; 291 | 292 | export const transformNode = ( 293 | s: Node | Node[], 294 | args: TransformerArgs 295 | ): string => { 296 | const { renderMath } = args; 297 | if (Array.isArray(s)) return transformNodeArray(s, args); 298 | 299 | if (s.kind === "arg.group") { 300 | return transformNodeArray(s.content, args); 301 | } 302 | if (s.kind === "env") { 303 | if (Object.keys(textEnvTransformers).includes(s.name)) 304 | return textEnvTransformers[s.name as keyof typeof textEnvTransformers]( 305 | s, 306 | args 307 | ); 308 | return transformNodeArray(s.content, args); 309 | } 310 | if (s.kind === "command") { 311 | if (Object.keys(commonCommandTransformers).includes(s.name)) 312 | return commonCommandTransformers[ 313 | s.name as keyof typeof commonCommandTransformers 314 | ](s, args); 315 | if (Object.keys(commandTransformers).includes(s.name)) 316 | return commandTransformers[s.name as keyof typeof commandTransformers]( 317 | s, 318 | args 319 | ); 320 | } 321 | if (s.kind === "command.href" || s.kind === "command.url") { 322 | if (s.kind === "command.href") 323 | return `${transformNodeArray(s.content, args)}`; 324 | if (s.kind === "command.url") 325 | return `${s.url}`; 326 | } 327 | if (s.kind === "env.lstlisting") { 328 | return `
    ${escapeHtml(s.content)}
    `; 329 | } 330 | if (s.kind === "subscript" || s.kind === "superscript") { 331 | /** 332 | * for Text Escape 333 | * 334 | * Subscript and superscript in the formula are processed in `transformMathNode()`. 335 | * This processing corresponds to situations where things should be treated as plain text, 336 | * but are processed as subscripts and superscripts in formulas. 337 | */ 338 | if (s.kind === "subscript") return "_"; 339 | if (s.kind === "superscript") return "^"; 340 | } 341 | if (s.kind === "inlineMath") { 342 | if (!renderMath) return escapeHtml(stringify(s).trim()); 343 | return transformMathNode(s.content, { ...args, italicMath: true }); 344 | } 345 | if (s.kind === "displayMath") { 346 | if (!renderMath) return escapeHtml(stringify(s).trim()); 347 | return `
    ${transformMathNode(s.content, { 348 | ...args, 349 | italicMath: true, 350 | })}
    `; 351 | } 352 | if (s.kind === "space" || s.kind === "softbreak") return " "; 353 | if (s.kind === "linebreak") return "
    "; 354 | if (s.kind === "text.string") return regularizeText(escapeHtml(s.content)); 355 | return unsupported(JSON.stringify(s)); 356 | }; 357 | 358 | export const transform = (s: AstRoot, args: TransformerArgs) => { 359 | try { 360 | return transformNodeArray(s.content, args); 361 | } catch (e) { 362 | return (e as any).toString(); 363 | } 364 | }; 365 | 366 | const sectionBreakCommands = [ 367 | "InputFile", 368 | "OutputFile", 369 | "Interaction", 370 | "Note", 371 | "Notes", 372 | "Constraints", 373 | "Examples", 374 | ]; 375 | 376 | export const transformProblem = ( 377 | s: Node[], 378 | args: TransformerArgs 379 | ): { title: string; body: string }[] => { 380 | const ret: { title: string; body: string }[] = []; 381 | 382 | const len = s.length; 383 | let section = "Statement"; 384 | let stack: Node[] = []; 385 | 386 | for (let i = 0; i < len; i++) { 387 | const cur = s[i]; 388 | if (lp.isCommand(cur) && sectionBreakCommands.includes(cur.name)) { 389 | ret.push({ title: section, body: transformNodeArray(stack, args) }); 390 | stack = []; 391 | section = cur.name; 392 | } else { 393 | stack.push(cur); 394 | } 395 | } 396 | if (stack) { 397 | const item = transformNodeArray(stack, args); 398 | if (item) 399 | ret.push({ title: section, body: transformNodeArray(stack, args) }); 400 | stack = []; 401 | } 402 | 403 | return ret; 404 | }; 405 | 406 | export const findFirstProblemEnv = ( 407 | node: Node | Node[] 408 | ): Environment | null => { 409 | if (Array.isArray(node)) { 410 | return (node.find((x) => findFirstProblemEnv(x)) as Environment) || null; 411 | } 412 | if (lp.isEnvironment(node) && node.name === "problem") return node; 413 | return null; 414 | }; 415 | 416 | export interface ProblemMeta { 417 | title?: string; 418 | input?: string; 419 | output?: string; 420 | timeLimit?: string; 421 | memoryLimit?: string; 422 | } 423 | 424 | export const transformProblemEnv = ( 425 | s: Environment, 426 | args: TransformerArgs 427 | ): 428 | | string 429 | | { 430 | meta: ProblemMeta; 431 | content: { title: string; body: string }[]; 432 | } => { 433 | if (!s) return "No problem env found."; 434 | try { 435 | const meta: ProblemMeta = {}; 436 | const problemArgs = s.args; 437 | 438 | if (problemArgs.length >= 1) 439 | meta.title = transformNode(problemArgs[0].content, args); 440 | if (problemArgs.length >= 2) 441 | meta.input = transformNode(problemArgs[1].content, args); 442 | if (problemArgs.length >= 3) 443 | meta.output = transformNode(problemArgs[2].content, args); 444 | if (problemArgs.length >= 4) 445 | meta.timeLimit = transformNode(problemArgs[3].content, args); 446 | if (problemArgs.length >= 5) 447 | meta.memoryLimit = transformNode(problemArgs[4].content, args); 448 | 449 | return { 450 | meta, 451 | content: transformProblem(s.content, { ...args, allowParbreaks: true }), 452 | }; 453 | } catch (e) { 454 | return (e as any).toString(); 455 | } 456 | }; 457 | -------------------------------------------------------------------------------- /src/utils/transform/command.ts: -------------------------------------------------------------------------------- 1 | import { s } from "../helpers"; 2 | 3 | const commonCommandTransformers = { 4 | "#": s`#`, 5 | " ": s` `, 6 | "{": s`{`, 7 | "}": s`}`, 8 | "&": s`&`, 9 | "@": s`@`, 10 | "%": s`%`, 11 | ",": s` `, 12 | ";": s`  `, 13 | quad: s` `, 14 | qquad: s`  `, 15 | newpage: s``, 16 | pagebreak: s``, 17 | _: s`_`, 18 | mathexclam: s`!`, 19 | mathoctothorpe: s`#`, 20 | mathdollar: s`$`, 21 | mathpercent: s`%`, 22 | mathampersand: s`&`, 23 | lparen: s`(`, 24 | rparen: s`)`, 25 | mathplus: s`+`, 26 | mathcomma: s`,`, 27 | mathperiod: s`.`, 28 | mathslash: s`/`, 29 | mathcolon: s`:`, 30 | mathsemicolon: s`;`, 31 | less: s`<`, 32 | equal: s`=`, 33 | greater: s`>`, 34 | mathquestion: s`?`, 35 | mathatsign: s`@`, 36 | lbrack: s`[`, 37 | backslash: s`\\`, 38 | rbrack: s`]`, 39 | lbrace: s`{`, 40 | vert: s`|`, 41 | lvert: s`|`, 42 | rvert: s`|`, 43 | rbrace: s`}`, 44 | mathsterling: s`£`, 45 | mathyen: s`¥`, 46 | mathsection: s`§`, 47 | neg: s`¬`, 48 | pm: s`±`, 49 | mathparagraph: s`¶`, 50 | cdotp: s`·`, 51 | times: s`×`, 52 | texttimes: s`×`, 53 | matheth: s`ð`, 54 | div: s`÷`, 55 | Zbar: s`Ƶ`, 56 | Alpha: s`Α`, 57 | Beta: s`Β`, 58 | Gamma: s`Γ`, 59 | Delta: s`Δ`, 60 | Epsilon: s`Ε`, 61 | Zeta: s`Ζ`, 62 | Eta: s`Η`, 63 | Theta: s`Θ`, 64 | Iota: s`Ι`, 65 | Kappa: s`Κ`, 66 | Lambda: s`Λ`, 67 | Mu: s`Μ`, 68 | Nu: s`Ν`, 69 | Xi: s`Ξ`, 70 | Omicron: s`Ο`, 71 | Pi: s`Π`, 72 | Rho: s`Ρ`, 73 | Sigma: s`Σ`, 74 | Tau: s`Τ`, 75 | Upsilon: s`Υ`, 76 | Phi: s`Φ`, 77 | Chi: s`Χ`, 78 | Psi: s`Ψ`, 79 | Omega: s`Ω`, 80 | alpha: s`α`, 81 | beta: s`β`, 82 | gamma: s`γ`, 83 | delta: s`δ`, 84 | varepsilon: s`ε`, 85 | zeta: s`ζ`, 86 | eta: s`η`, 87 | theta: s`θ`, 88 | iota: s`ι`, 89 | kappa: s`κ`, 90 | lambda: s`λ`, 91 | mu: s`μ`, 92 | nu: s`ν`, 93 | xi: s`ξ`, 94 | omicron: s`ο`, 95 | pi: s`π`, 96 | rho: s`ρ`, 97 | varsigma: s`ς`, 98 | sigma: s`σ`, 99 | tau: s`τ`, 100 | upsilon: s`υ`, 101 | varphi: s`φ`, 102 | chi: s`χ`, 103 | psi: s`ψ`, 104 | omega: s`ω`, 105 | vartheta: s`ϑ`, 106 | phi: s`ϕ`, 107 | varpi: s`ϖ`, 108 | upDigamma: s`Ϝ`, 109 | updigamma: s`ϝ`, 110 | varkappa: s`ϰ`, 111 | varrho: s`ϱ`, 112 | varTheta: s`ϴ`, 113 | epsilon: s`ϵ`, 114 | upbackepsilon: s`϶`, 115 | horizbar: s`―`, 116 | Vert: s`‖`, 117 | twolowline: s`‗`, 118 | dagger: s`†`, 119 | ddagger: s`‡`, 120 | smblkcircle: s`•`, 121 | enleadertwodots: s`‥`, 122 | ldots: s`…`, 123 | prime: s`′`, 124 | dprime: s`″`, 125 | trprime: s`‴`, 126 | backprime: s`‵`, 127 | backdprime: s`‶`, 128 | backtrprime: s`‷`, 129 | caretinsert: s`‸`, 130 | Exclam: s`‼`, 131 | tieconcat: s`⁀`, 132 | hyphenbullet: s`⁃`, 133 | fracslash: s`⁄`, 134 | Question: s`⁇`, 135 | closure: s`⁐`, 136 | qprime: s`⁗`, 137 | euro: s`€`, 138 | leftharpoonaccent: s`⃐`, 139 | overleftharpoon: s`⃐`, 140 | rightharpoonaccent: s`⃑`, 141 | overrightharpoon: s`⃑`, 142 | vertoverlay: s`⃒`, 143 | overleftarrow: s`⃖`, 144 | overrightarrow: s`⃗`, 145 | vec: s`⃗`, 146 | dddot: s`⃛`, 147 | ddddot: s`⃜`, 148 | enclosecircle: s`⃝`, 149 | enclosesquare: s`⃞`, 150 | enclosediamond: s`⃟`, 151 | overleftrightarrow: s`⃡`, 152 | enclosetriangle: s`⃤`, 153 | annuity: s`⃧`, 154 | threeunderdot: s`⃨`, 155 | widebridgeabove: s`⃩`, 156 | underrightharpoondown: s`⃬`, 157 | underleftharpoondown: s`⃭`, 158 | underleftarrow: s`⃮`, 159 | underrightarrow: s`⃯`, 160 | asteraccent: s`⃰`, 161 | Eulerconst: s`ℇ`, 162 | Planckconst: s`ℎ`, 163 | hslash: s`ℏ`, 164 | Im: s`ℑ`, 165 | ell: s`ℓ`, 166 | wp: s`℘`, 167 | Re: s`ℜ`, 168 | mho: s`℧`, 169 | turnediota: s`℩`, 170 | Angstrom: s`Å`, 171 | Finv: s`Ⅎ`, 172 | aleph: s`ℵ`, 173 | beth: s`ℶ`, 174 | gimel: s`ℷ`, 175 | daleth: s`ℸ`, 176 | Game: s`⅁`, 177 | sansLturned: s`⅂`, 178 | sansLmirrored: s`⅃`, 179 | Yup: s`⅄`, 180 | PropertyLine: s`⅊`, 181 | upand: s`⅋`, 182 | leftarrow: s`←`, 183 | uparrow: s`↑`, 184 | rightarrow: s`→`, 185 | to: s`→`, 186 | downarrow: s`↓`, 187 | leftrightarrow: s`↔`, 188 | updownarrow: s`↕`, 189 | nwarrow: s`↖`, 190 | nearrow: s`↗`, 191 | searrow: s`↘`, 192 | swarrow: s`↙`, 193 | nleftarrow: s`↚`, 194 | nrightarrow: s`↛`, 195 | leftwavearrow: s`↜`, 196 | rightwavearrow: s`↝`, 197 | twoheadleftarrow: s`↞`, 198 | twoheaduparrow: s`↟`, 199 | twoheadrightarrow: s`↠`, 200 | twoheaddownarrow: s`↡`, 201 | leftarrowtail: s`↢`, 202 | rightarrowtail: s`↣`, 203 | mapsfrom: s`↤`, 204 | mapsup: s`↥`, 205 | mapsto: s`↦`, 206 | mapsdown: s`↧`, 207 | updownarrowbar: s`↨`, 208 | hookleftarrow: s`↩`, 209 | hookrightarrow: s`↪`, 210 | looparrowleft: s`↫`, 211 | looparrowright: s`↬`, 212 | leftrightsquigarrow: s`↭`, 213 | nleftrightarrow: s`↮`, 214 | downzigzagarrow: s`↯`, 215 | Lsh: s`↰`, 216 | Rsh: s`↱`, 217 | Ldsh: s`↲`, 218 | Rdsh: s`↳`, 219 | linefeed: s`↴`, 220 | carriagereturn: s`↵`, 221 | curvearrowleft: s`↶`, 222 | curvearrowright: s`↷`, 223 | barovernorthwestarrow: s`↸`, 224 | barleftarrowrightarrowbar: s`↹`, 225 | acwopencirclearrow: s`↺`, 226 | cwopencirclearrow: s`↻`, 227 | leftharpoonup: s`↼`, 228 | leftharpoondown: s`↽`, 229 | upharpoonright: s`↾`, 230 | upharpoonleft: s`↿`, 231 | rightharpoonup: s`⇀`, 232 | rightharpoondown: s`⇁`, 233 | downharpoonright: s`⇂`, 234 | downharpoonleft: s`⇃`, 235 | rightleftarrows: s`⇄`, 236 | updownarrows: s`⇅`, 237 | leftrightarrows: s`⇆`, 238 | leftleftarrows: s`⇇`, 239 | upuparrows: s`⇈`, 240 | rightrightarrows: s`⇉`, 241 | downdownarrows: s`⇊`, 242 | leftrightharpoons: s`⇋`, 243 | rightleftharpoons: s`⇌`, 244 | nLeftarrow: s`⇍`, 245 | nLeftrightarrow: s`⇎`, 246 | nRightarrow: s`⇏`, 247 | Leftarrow: s`⇐`, 248 | Uparrow: s`⇑`, 249 | Rightarrow: s`⇒`, 250 | Downarrow: s`⇓`, 251 | Leftrightarrow: s`⇔`, 252 | Updownarrow: s`⇕`, 253 | Nwarrow: s`⇖`, 254 | Nearrow: s`⇗`, 255 | Searrow: s`⇘`, 256 | Swarrow: s`⇙`, 257 | Lleftarrow: s`⇚`, 258 | Rrightarrow: s`⇛`, 259 | leftsquigarrow: s`⇜`, 260 | rightsquigarrow: s`⇝`, 261 | nHuparrow: s`⇞`, 262 | nHdownarrow: s`⇟`, 263 | leftdasharrow: s`⇠`, 264 | updasharrow: s`⇡`, 265 | rightdasharrow: s`⇢`, 266 | downdasharrow: s`⇣`, 267 | barleftarrow: s`⇤`, 268 | rightarrowbar: s`⇥`, 269 | leftwhitearrow: s`⇦`, 270 | upwhitearrow: s`⇧`, 271 | rightwhitearrow: s`⇨`, 272 | downwhitearrow: s`⇩`, 273 | whitearrowupfrombar: s`⇪`, 274 | circleonrightarrow: s`⇴`, 275 | downuparrows: s`⇵`, 276 | rightthreearrows: s`⇶`, 277 | nvleftarrow: s`⇷`, 278 | nvrightarrow: s`⇸`, 279 | nvleftrightarrow: s`⇹`, 280 | nVleftarrow: s`⇺`, 281 | nVrightarrow: s`⇻`, 282 | nVleftrightarrow: s`⇼`, 283 | leftarrowtriangle: s`⇽`, 284 | rightarrowtriangle: s`⇾`, 285 | leftrightarrowtriangle: s`⇿`, 286 | forall: s`∀`, 287 | complement: s`∁`, 288 | partial: s`∂`, 289 | exists: s`∃`, 290 | nexists: s`∄`, 291 | varnothing: s`∅`, 292 | increment: s`∆`, 293 | nabla: s`∇`, 294 | in: s`∈`, 295 | notin: s`∉`, 296 | smallin: s`∊`, 297 | ni: s`∋`, 298 | nni: s`∌`, 299 | smallni: s`∍`, 300 | QED: s`∎`, 301 | prod: s`∏`, 302 | coprod: s`∐`, 303 | sum: s`∑`, 304 | minus: s`−`, 305 | mp: s`∓`, 306 | dotplus: s`∔`, 307 | divslash: s`∕`, 308 | smallsetminus: s`∖`, 309 | ast: s`∗`, 310 | vysmwhtcircle: s`∘`, 311 | vysmblkcircle: s`∙`, 312 | // sqrt: s`√`, 313 | surd: s`√`, 314 | cuberoot: s`∛`, 315 | fourthroot: s`∜`, 316 | propto: s`∝`, 317 | infty: s`∞`, 318 | rightangle: s`∟`, 319 | angle: s`∠`, 320 | measuredangle: s`∡`, 321 | sphericalangle: s`∢`, 322 | mid: s`∣`, 323 | nmid: s`∤`, 324 | parallel: s`∥`, 325 | nparallel: s`∦`, 326 | wedge: s`∧`, 327 | vee: s`∨`, 328 | cap: s`∩`, 329 | cup: s`∪`, 330 | int: s`∫`, 331 | iint: s`∬`, 332 | iiint: s`∭`, 333 | oint: s`∮`, 334 | oiint: s`∯`, 335 | oiiint: s`∰`, 336 | intclockwise: s`∱`, 337 | varointclockwise: s`∲`, 338 | ointctrclockwise: s`∳`, 339 | therefore: s`∴`, 340 | because: s`∵`, 341 | mathratio: s`∶`, 342 | Colon: s`∷`, 343 | dotminus: s`∸`, 344 | dashcolon: s`∹`, 345 | dotsminusdots: s`∺`, 346 | kernelcontraction: s`∻`, 347 | sim: s`∼`, 348 | backsim: s`∽`, 349 | invlazys: s`∾`, 350 | sinewave: s`∿`, 351 | wr: s`≀`, 352 | nsim: s`≁`, 353 | eqsim: s`≂`, 354 | simeq: s`≃`, 355 | nsime: s`≄`, 356 | cong: s`≅`, 357 | simneqq: s`≆`, 358 | ncong: s`≇`, 359 | approx: s`≈`, 360 | napprox: s`≉`, 361 | approxeq: s`≊`, 362 | approxident: s`≋`, 363 | backcong: s`≌`, 364 | asymp: s`≍`, 365 | Bumpeq: s`≎`, 366 | bumpeq: s`≏`, 367 | doteq: s`≐`, 368 | Doteq: s`≑`, 369 | fallingdotseq: s`≒`, 370 | risingdotseq: s`≓`, 371 | coloneqq: s`≔`, 372 | eqqcolon: s`≕`, 373 | eqcirc: s`≖`, 374 | circeq: s`≗`, 375 | arceq: s`≘`, 376 | wedgeq: s`≙`, 377 | veeeq: s`≚`, 378 | stareq: s`≛`, 379 | triangleq: s`≜`, 380 | eqdef: s`≝`, 381 | measeq: s`≞`, 382 | questeq: s`≟`, 383 | ne: s`≠`, 384 | neq: s`≠`, 385 | equiv: s`≡`, 386 | nequiv: s`≢`, 387 | Equiv: s`≣`, 388 | leq: s`≤`, 389 | le: s`≤`, 390 | geq: s`≥`, 391 | ge: s`≥`, 392 | leqq: s`≦`, 393 | geqq: s`≧`, 394 | lneqq: s`≨`, 395 | gneqq: s`≩`, 396 | ll: s`≪`, 397 | gg: s`≫`, 398 | between: s`≬`, 399 | nasymp: s`≭`, 400 | nless: s`≮`, 401 | ngtr: s`≯`, 402 | nleq: s`≰`, 403 | ngeq: s`≱`, 404 | lesssim: s`≲`, 405 | gtrsim: s`≳`, 406 | nlesssim: s`≴`, 407 | ngtrsim: s`≵`, 408 | lessgtr: s`≶`, 409 | gtrless: s`≷`, 410 | nlessgtr: s`≸`, 411 | ngtrless: s`≹`, 412 | prec: s`≺`, 413 | succ: s`≻`, 414 | preccurlyeq: s`≼`, 415 | succcurlyeq: s`≽`, 416 | precsim: s`≾`, 417 | succsim: s`≿`, 418 | nprec: s`⊀`, 419 | nsucc: s`⊁`, 420 | subset: s`⊂`, 421 | supset: s`⊃`, 422 | nsubset: s`⊄`, 423 | nsupset: s`⊅`, 424 | subseteq: s`⊆`, 425 | supseteq: s`⊇`, 426 | nsubseteq: s`⊈`, 427 | nsupseteq: s`⊉`, 428 | subsetneq: s`⊊`, 429 | supsetneq: s`⊋`, 430 | cupleftarrow: s`⊌`, 431 | cupdot: s`⊍`, 432 | uplus: s`⊎`, 433 | sqsubset: s`⊏`, 434 | sqsupset: s`⊐`, 435 | sqsubseteq: s`⊑`, 436 | sqsupseteq: s`⊒`, 437 | sqcap: s`⊓`, 438 | sqcup: s`⊔`, 439 | oplus: s`⊕`, 440 | ominus: s`⊖`, 441 | otimes: s`⊗`, 442 | oslash: s`⊘`, 443 | odot: s`⊙`, 444 | circledcirc: s`⊚`, 445 | circledast: s`⊛`, 446 | circledequal: s`⊜`, 447 | circleddash: s`⊝`, 448 | boxplus: s`⊞`, 449 | boxminus: s`⊟`, 450 | boxtimes: s`⊠`, 451 | boxdot: s`⊡`, 452 | vdash: s`⊢`, 453 | dashv: s`⊣`, 454 | top: s`⊤`, 455 | bot: s`⊥`, 456 | assert: s`⊦`, 457 | models: s`⊧`, 458 | vDash: s`⊨`, 459 | Vdash: s`⊩`, 460 | Vvdash: s`⊪`, 461 | VDash: s`⊫`, 462 | nvdash: s`⊬`, 463 | nvDash: s`⊭`, 464 | nVdash: s`⊮`, 465 | nVDash: s`⊯`, 466 | prurel: s`⊰`, 467 | scurel: s`⊱`, 468 | vartriangleleft: s`⊲`, 469 | vartriangleright: s`⊳`, 470 | trianglelefteq: s`⊴`, 471 | trianglerighteq: s`⊵`, 472 | origof: s`⊶`, 473 | imageof: s`⊷`, 474 | multimap: s`⊸`, 475 | hermitmatrix: s`⊹`, 476 | intercal: s`⊺`, 477 | veebar: s`⊻`, 478 | barwedge: s`⊼`, 479 | barvee: s`⊽`, 480 | measuredrightangle: s`⊾`, 481 | varlrtriangle: s`⊿`, 482 | bigwedge: s`⋀`, 483 | bigvee: s`⋁`, 484 | bigcap: s`⋂`, 485 | bigcup: s`⋃`, 486 | smwhtdiamond: s`⋄`, 487 | cdot: s`⋅`, 488 | star: s`⋆`, 489 | divideontimes: s`⋇`, 490 | bowtie: s`⋈`, 491 | ltimes: s`⋉`, 492 | rtimes: s`⋊`, 493 | leftthreetimes: s`⋋`, 494 | rightthreetimes: s`⋌`, 495 | backsimeq: s`⋍`, 496 | curlyvee: s`⋎`, 497 | curlywedge: s`⋏`, 498 | Subset: s`⋐`, 499 | Supset: s`⋑`, 500 | Cap: s`⋒`, 501 | Cup: s`⋓`, 502 | pitchfork: s`⋔`, 503 | equalparallel: s`⋕`, 504 | lessdot: s`⋖`, 505 | gtrdot: s`⋗`, 506 | lll: s`⋘`, 507 | ggg: s`⋙`, 508 | lesseqgtr: s`⋚`, 509 | gtreqless: s`⋛`, 510 | eqless: s`⋜`, 511 | eqgtr: s`⋝`, 512 | curlyeqprec: s`⋞`, 513 | curlyeqsucc: s`⋟`, 514 | npreccurlyeq: s`⋠`, 515 | nsucccurlyeq: s`⋡`, 516 | nsqsubseteq: s`⋢`, 517 | nsqsupseteq: s`⋣`, 518 | sqsubsetneq: s`⋤`, 519 | sqsupsetneq: s`⋥`, 520 | lnsim: s`⋦`, 521 | gnsim: s`⋧`, 522 | precnsim: s`⋨`, 523 | succnsim: s`⋩`, 524 | nvartriangleleft: s`⋪`, 525 | nvartriangleright: s`⋫`, 526 | ntrianglelefteq: s`⋬`, 527 | ntrianglerighteq: s`⋭`, 528 | vdots: s`⋮`, 529 | cdots: s`⋯`, 530 | dots: s`…`, 531 | iddots: s`⋰`, 532 | ddots: s`⋱`, 533 | disin: s`⋲`, 534 | varisins: s`⋳`, 535 | isins: s`⋴`, 536 | isindot: s`⋵`, 537 | varisinobar: s`⋶`, 538 | isinobar: s`⋷`, 539 | isinvb: s`⋸`, 540 | isinE: s`⋹`, 541 | nisd: s`⋺`, 542 | varnis: s`⋻`, 543 | nis: s`⋼`, 544 | varniobar: s`⋽`, 545 | niobar: s`⋾`, 546 | bagmember: s`⋿`, 547 | diameter: s`⌀`, 548 | house: s`⌂`, 549 | varbarwedge: s`⌅`, 550 | vardoublebarwedge: s`⌆`, 551 | lceil: s`⌈`, 552 | rceil: s`⌉`, 553 | lfloor: s`⌊`, 554 | rfloor: s`⌋`, 555 | invnot: s`⌐`, 556 | sqlozenge: s`⌑`, 557 | profline: s`⌒`, 558 | profsurf: s`⌓`, 559 | viewdata: s`⌗`, 560 | turnednot: s`⌙`, 561 | ulcorner: s`⌜`, 562 | urcorner: s`⌝`, 563 | llcorner: s`⌞`, 564 | lrcorner: s`⌟`, 565 | inttop: s`⌠`, 566 | intbottom: s`⌡`, 567 | frown: s`⌢`, 568 | smile: s`⌣`, 569 | varhexagonlrbonds: s`⌬`, 570 | conictaper: s`⌲`, 571 | topbot: s`⌶`, 572 | obar: s`⌽`, 573 | APLnotslash: s`⌿`, 574 | APLnotbackslash: s`⍀`, 575 | APLboxupcaret: s`⍓`, 576 | APLboxquestion: s`⍰`, 577 | rangledownzigzagarrow: s`⍼`, 578 | hexagon: s`⎔`, 579 | lparenuend: s`⎛`, 580 | lparenextender: s`⎜`, 581 | lparenlend: s`⎝`, 582 | rparenuend: s`⎞`, 583 | rparenextender: s`⎟`, 584 | rparenlend: s`⎠`, 585 | lbrackuend: s`⎡`, 586 | lbrackextender: s`⎢`, 587 | lbracklend: s`⎣`, 588 | rbrackuend: s`⎤`, 589 | rbrackextender: s`⎥`, 590 | rbracklend: s`⎦`, 591 | lbraceuend: s`⎧`, 592 | lbracemid: s`⎨`, 593 | lbracelend: s`⎩`, 594 | vbraceextender: s`⎪`, 595 | rbraceuend: s`⎫`, 596 | rbracemid: s`⎬`, 597 | rbracelend: s`⎭`, 598 | intextender: s`⎮`, 599 | harrowextender: s`⎯`, 600 | lmoustache: s`⎰`, 601 | rmoustache: s`⎱`, 602 | sumtop: s`⎲`, 603 | sumbottom: s`⎳`, 604 | overbracket: s`⎴`, 605 | underbracket: s`⎵`, 606 | bbrktbrk: s`⎶`, 607 | sqrtbottom: s`⎷`, 608 | lvboxline: s`⎸`, 609 | rvboxline: s`⎹`, 610 | varcarriagereturn: s`⏎`, 611 | overparen: s`⏜`, 612 | underparen: s`⏝`, 613 | overbrace: s`⏞`, 614 | underbrace: s`⏟`, 615 | obrbrak: s`⏠`, 616 | ubrbrak: s`⏡`, 617 | trapezium: s`⏢`, 618 | benzenr: s`⏣`, 619 | strns: s`⏤`, 620 | fltns: s`⏥`, 621 | accurrent: s`⏦`, 622 | elinters: s`⏧`, 623 | blanksymbol: s`␢`, 624 | mathvisiblespace: s`␣`, 625 | bdtriplevdash: s`┆`, 626 | blockuphalf: s`▀`, 627 | blocklowhalf: s`▄`, 628 | blockfull: s`█`, 629 | blocklefthalf: s`▌`, 630 | blockrighthalf: s`▐`, 631 | blockqtrshaded: s`░`, 632 | blockhalfshaded: s`▒`, 633 | blockthreeqtrshaded: s`▓`, 634 | mdlgblksquare: s`■`, 635 | mdlgwhtsquare: s`□`, 636 | squoval: s`▢`, 637 | blackinwhitesquare: s`▣`, 638 | squarehfill: s`▤`, 639 | squarevfill: s`▥`, 640 | squarehvfill: s`▦`, 641 | squarenwsefill: s`▧`, 642 | squareneswfill: s`▨`, 643 | squarecrossfill: s`▩`, 644 | smblksquare: s`▪`, 645 | smwhtsquare: s`▫`, 646 | hrectangleblack: s`▬`, 647 | hrectangle: s`▭`, 648 | vrectangleblack: s`▮`, 649 | vrectangle: s`▯`, 650 | parallelogramblack: s`▰`, 651 | parallelogram: s`▱`, 652 | bigblacktriangleup: s`▲`, 653 | bigtriangleup: s`△`, 654 | blacktriangle: s`▴`, 655 | vartriangle: s`▵`, 656 | blacktriangleright: s`▶`, 657 | triangleright: s`▷`, 658 | smallblacktriangleright: s`▸`, 659 | smalltriangleright: s`▹`, 660 | blackpointerright: s`►`, 661 | whitepointerright: s`▻`, 662 | bigblacktriangledown: s`▼`, 663 | bigtriangledown: s`▽`, 664 | blacktriangledown: s`▾`, 665 | triangledown: s`▿`, 666 | blacktriangleleft: s`◀`, 667 | triangleleft: s`◁`, 668 | smallblacktriangleleft: s`◂`, 669 | smalltriangleleft: s`◃`, 670 | blackpointerleft: s`◄`, 671 | whitepointerleft: s`◅`, 672 | mdlgblkdiamond: s`◆`, 673 | mdlgwhtdiamond: s`◇`, 674 | blackinwhitediamond: s`◈`, 675 | fisheye: s`◉`, 676 | mdlgwhtlozenge: s`◊`, 677 | mdlgwhtcircle: s`○`, 678 | dottedcircle: s`◌`, 679 | circlevertfill: s`◍`, 680 | bullseye: s`◎`, 681 | mdlgblkcircle: s`●`, 682 | circlelefthalfblack: s`◐`, 683 | circlerighthalfblack: s`◑`, 684 | circlebottomhalfblack: s`◒`, 685 | circletophalfblack: s`◓`, 686 | circleurquadblack: s`◔`, 687 | blackcircleulquadwhite: s`◕`, 688 | blacklefthalfcircle: s`◖`, 689 | blackrighthalfcircle: s`◗`, 690 | inversebullet: s`◘`, 691 | inversewhitecircle: s`◙`, 692 | invwhiteupperhalfcircle: s`◚`, 693 | invwhitelowerhalfcircle: s`◛`, 694 | ularc: s`◜`, 695 | urarc: s`◝`, 696 | lrarc: s`◞`, 697 | llarc: s`◟`, 698 | topsemicircle: s`◠`, 699 | botsemicircle: s`◡`, 700 | lrblacktriangle: s`◢`, 701 | llblacktriangle: s`◣`, 702 | ulblacktriangle: s`◤`, 703 | urblacktriangle: s`◥`, 704 | smwhtcircle: s`◦`, 705 | squareleftblack: s`◧`, 706 | squarerightblack: s`◨`, 707 | squareulblack: s`◩`, 708 | squarelrblack: s`◪`, 709 | boxbar: s`◫`, 710 | trianglecdot: s`◬`, 711 | triangleleftblack: s`◭`, 712 | trianglerightblack: s`◮`, 713 | lgwhtcircle: s`◯`, 714 | squareulquad: s`◰`, 715 | squarellquad: s`◱`, 716 | squarelrquad: s`◲`, 717 | squareurquad: s`◳`, 718 | circleulquad: s`◴`, 719 | circlellquad: s`◵`, 720 | circlelrquad: s`◶`, 721 | circleurquad: s`◷`, 722 | ultriangle: s`◸`, 723 | urtriangle: s`◹`, 724 | lltriangle: s`◺`, 725 | mdwhtsquare: s`◻`, 726 | mdblksquare: s`◼`, 727 | mdsmwhtsquare: s`◽`, 728 | mdsmblksquare: s`◾`, 729 | lrtriangle: s`◿`, 730 | bigstar: s`★`, 731 | bigwhitestar: s`☆`, 732 | astrosun: s`☉`, 733 | danger: s`☡`, 734 | blacksmiley: s`☻`, 735 | sun: s`☼`, 736 | rightmoon: s`☽`, 737 | leftmoon: s`☾`, 738 | female: s`♀`, 739 | male: s`♂`, 740 | spadesuit: s`♠`, 741 | heartsuit: s`♡`, 742 | diamondsuit: s`♢`, 743 | clubsuit: s`♣`, 744 | varspadesuit: s`♤`, 745 | varheartsuit: s`♥`, 746 | vardiamondsuit: s`♦`, 747 | varclubsuit: s`♧`, 748 | quarternote: s`♩`, 749 | eighthnote: s`♪`, 750 | twonotes: s`♫`, 751 | flat: s`♭`, 752 | natural: s`♮`, 753 | sharp: s`♯`, 754 | acidfree: s`♾`, 755 | dicei: s`⚀`, 756 | diceii: s`⚁`, 757 | diceiii: s`⚂`, 758 | diceiv: s`⚃`, 759 | dicev: s`⚄`, 760 | dicevi: s`⚅`, 761 | circledrightdot: s`⚆`, 762 | circledtwodots: s`⚇`, 763 | blackcircledrightdot: s`⚈`, 764 | blackcircledtwodots: s`⚉`, 765 | Hermaphrodite: s`⚥`, 766 | mdwhtcircle: s`⚪`, 767 | mdblkcircle: s`⚫`, 768 | mdsmwhtcircle: s`⚬`, 769 | neuter: s`⚲`, 770 | checkmark: s`✓`, 771 | maltese: s`✠`, 772 | circledstar: s`✪`, 773 | varstar: s`✶`, 774 | dingasterisk: s`✽`, 775 | lbrbrak: s`❲`, 776 | rbrbrak: s`❳`, 777 | draftingarrow: s`➛`, 778 | threedangle: s`⟀`, 779 | whiteinwhitetriangle: s`⟁`, 780 | perp: s`⟂`, 781 | subsetcirc: s`⟃`, 782 | supsetcirc: s`⟄`, 783 | lbag: s`⟅`, 784 | rbag: s`⟆`, 785 | veedot: s`⟇`, 786 | bsolhsub: s`⟈`, 787 | suphsol: s`⟉`, 788 | longdivision: s`⟌`, 789 | diamondcdot: s`⟐`, 790 | wedgedot: s`⟑`, 791 | upin: s`⟒`, 792 | pullback: s`⟓`, 793 | pushout: s`⟔`, 794 | leftouterjoin: s`⟕`, 795 | rightouterjoin: s`⟖`, 796 | fullouterjoin: s`⟗`, 797 | bigbot: s`⟘`, 798 | bigtop: s`⟙`, 799 | DashVDash: s`⟚`, 800 | dashVdash: s`⟛`, 801 | multimapinv: s`⟜`, 802 | vlongdash: s`⟝`, 803 | longdashv: s`⟞`, 804 | cirbot: s`⟟`, 805 | lozengeminus: s`⟠`, 806 | concavediamond: s`⟡`, 807 | concavediamondtickleft: s`⟢`, 808 | concavediamondtickright: s`⟣`, 809 | whitesquaretickleft: s`⟤`, 810 | whitesquaretickright: s`⟥`, 811 | lBrack: s`⟦`, 812 | rBrack: s`⟧`, 813 | langle: s`⟨`, 814 | rangle: s`⟩`, 815 | lAngle: s`⟪`, 816 | rAngle: s`⟫`, 817 | Lbrbrak: s`⟬`, 818 | Rbrbrak: s`⟭`, 819 | lgroup: s`⟮`, 820 | rgroup: s`⟯`, 821 | UUparrow: s`⟰`, 822 | DDownarrow: s`⟱`, 823 | acwgapcirclearrow: s`⟲`, 824 | cwgapcirclearrow: s`⟳`, 825 | rightarrowonoplus: s`⟴`, 826 | longleftarrow: s`⟵`, 827 | longrightarrow: s`⟶`, 828 | longleftrightarrow: s`⟷`, 829 | Longleftarrow: s`⟸`, 830 | Longrightarrow: s`⟹`, 831 | Longleftrightarrow: s`⟺`, 832 | longmapsfrom: s`⟻`, 833 | longmapsto: s`⟼`, 834 | Longmapsfrom: s`⟽`, 835 | Longmapsto: s`⟾`, 836 | longrightsquigarrow: s`⟿`, 837 | nvtwoheadrightarrow: s`⤀`, 838 | nVtwoheadrightarrow: s`⤁`, 839 | nvLeftarrow: s`⤂`, 840 | nvRightarrow: s`⤃`, 841 | nvLeftrightarrow: s`⤄`, 842 | twoheadmapsto: s`⤅`, 843 | Mapsfrom: s`⤆`, 844 | Mapsto: s`⤇`, 845 | downarrowbarred: s`⤈`, 846 | uparrowbarred: s`⤉`, 847 | Uuparrow: s`⤊`, 848 | Ddownarrow: s`⤋`, 849 | leftbkarrow: s`⤌`, 850 | rightbkarrow: s`⤍`, 851 | leftdbkarrow: s`⤎`, 852 | dbkarrow: s`⤏`, 853 | drbkarrow: s`⤐`, 854 | rightdotarrow: s`⤑`, 855 | baruparrow: s`⤒`, 856 | downarrowbar: s`⤓`, 857 | nvrightarrowtail: s`⤔`, 858 | nVrightarrowtail: s`⤕`, 859 | twoheadrightarrowtail: s`⤖`, 860 | nvtwoheadrightarrowtail: s`⤗`, 861 | nVtwoheadrightarrowtail: s`⤘`, 862 | lefttail: s`⤙`, 863 | righttail: s`⤚`, 864 | leftdbltail: s`⤛`, 865 | rightdbltail: s`⤜`, 866 | diamondleftarrow: s`⤝`, 867 | rightarrowdiamond: s`⤞`, 868 | diamondleftarrowbar: s`⤟`, 869 | barrightarrowdiamond: s`⤠`, 870 | nwsearrow: s`⤡`, 871 | neswarrow: s`⤢`, 872 | hknwarrow: s`⤣`, 873 | hknearrow: s`⤤`, 874 | hksearrow: s`⤥`, 875 | hkswarrow: s`⤦`, 876 | tona: s`⤧`, 877 | toea: s`⤨`, 878 | tosa: s`⤩`, 879 | towa: s`⤪`, 880 | rdiagovfdiag: s`⤫`, 881 | fdiagovrdiag: s`⤬`, 882 | seovnearrow: s`⤭`, 883 | neovsearrow: s`⤮`, 884 | fdiagovnearrow: s`⤯`, 885 | rdiagovsearrow: s`⤰`, 886 | neovnwarrow: s`⤱`, 887 | nwovnearrow: s`⤲`, 888 | rightcurvedarrow: s`⤳`, 889 | uprightcurvearrow: s`⤴`, 890 | downrightcurvedarrow: s`⤵`, 891 | leftdowncurvedarrow: s`⤶`, 892 | rightdowncurvedarrow: s`⤷`, 893 | cwrightarcarrow: s`⤸`, 894 | acwleftarcarrow: s`⤹`, 895 | acwoverarcarrow: s`⤺`, 896 | acwunderarcarrow: s`⤻`, 897 | curvearrowrightminus: s`⤼`, 898 | curvearrowleftplus: s`⤽`, 899 | cwundercurvearrow: s`⤾`, 900 | ccwundercurvearrow: s`⤿`, 901 | acwcirclearrow: s`⥀`, 902 | cwcirclearrow: s`⥁`, 903 | rightarrowshortleftarrow: s`⥂`, 904 | leftarrowshortrightarrow: s`⥃`, 905 | shortrightarrowleftarrow: s`⥄`, 906 | rightarrowplus: s`⥅`, 907 | leftarrowplus: s`⥆`, 908 | rightarrowx: s`⥇`, 909 | leftrightarrowcircle: s`⥈`, 910 | twoheaduparrowcircle: s`⥉`, 911 | leftrightharpoonupdown: s`⥊`, 912 | leftrightharpoondownup: s`⥋`, 913 | updownharpoonrightleft: s`⥌`, 914 | updownharpoonleftright: s`⥍`, 915 | leftrightharpoonupup: s`⥎`, 916 | updownharpoonrightright: s`⥏`, 917 | leftrightharpoondowndown: s`⥐`, 918 | updownharpoonleftleft: s`⥑`, 919 | barleftharpoonup: s`⥒`, 920 | rightharpoonupbar: s`⥓`, 921 | barupharpoonright: s`⥔`, 922 | downharpoonrightbar: s`⥕`, 923 | barleftharpoondown: s`⥖`, 924 | rightharpoondownbar: s`⥗`, 925 | barupharpoonleft: s`⥘`, 926 | downharpoonleftbar: s`⥙`, 927 | leftharpoonupbar: s`⥚`, 928 | barrightharpoonup: s`⥛`, 929 | upharpoonrightbar: s`⥜`, 930 | bardownharpoonright: s`⥝`, 931 | leftharpoondownbar: s`⥞`, 932 | barrightharpoondown: s`⥟`, 933 | upharpoonleftbar: s`⥠`, 934 | bardownharpoonleft: s`⥡`, 935 | leftharpoonsupdown: s`⥢`, 936 | upharpoonsleftright: s`⥣`, 937 | rightharpoonsupdown: s`⥤`, 938 | downharpoonsleftright: s`⥥`, 939 | leftrightharpoonsup: s`⥦`, 940 | leftrightharpoonsdown: s`⥧`, 941 | rightleftharpoonsup: s`⥨`, 942 | rightleftharpoonsdown: s`⥩`, 943 | leftharpoonupdash: s`⥪`, 944 | dashleftharpoondown: s`⥫`, 945 | rightharpoonupdash: s`⥬`, 946 | dashrightharpoondown: s`⥭`, 947 | updownharpoonsleftright: s`⥮`, 948 | downupharpoonsleftright: s`⥯`, 949 | rightimply: s`⥰`, 950 | equalrightarrow: s`⥱`, 951 | similarrightarrow: s`⥲`, 952 | leftarrowsimilar: s`⥳`, 953 | rightarrowsimilar: s`⥴`, 954 | rightarrowapprox: s`⥵`, 955 | ltlarr: s`⥶`, 956 | leftarrowless: s`⥷`, 957 | gtrarr: s`⥸`, 958 | subrarr: s`⥹`, 959 | leftarrowsubset: s`⥺`, 960 | suplarr: s`⥻`, 961 | leftfishtail: s`⥼`, 962 | rightfishtail: s`⥽`, 963 | upfishtail: s`⥾`, 964 | downfishtail: s`⥿`, 965 | Vvert: s`⦀`, 966 | mdsmblkcircle: s`⦁`, 967 | typecolon: s`⦂`, 968 | lBrace: s`⦃`, 969 | rBrace: s`⦄`, 970 | lParen: s`⦅`, 971 | rParen: s`⦆`, 972 | llparenthesis: s`⦇`, 973 | rrparenthesis: s`⦈`, 974 | llangle: s`⦉`, 975 | rrangle: s`⦊`, 976 | lbrackubar: s`⦋`, 977 | rbrackubar: s`⦌`, 978 | lbrackultick: s`⦍`, 979 | rbracklrtick: s`⦎`, 980 | lbracklltick: s`⦏`, 981 | rbrackurtick: s`⦐`, 982 | langledot: s`⦑`, 983 | rangledot: s`⦒`, 984 | lparenless: s`⦓`, 985 | rparengtr: s`⦔`, 986 | Lparengtr: s`⦕`, 987 | Rparenless: s`⦖`, 988 | lblkbrbrak: s`⦗`, 989 | rblkbrbrak: s`⦘`, 990 | fourvdots: s`⦙`, 991 | vzigzag: s`⦚`, 992 | measuredangleleft: s`⦛`, 993 | rightanglesqr: s`⦜`, 994 | rightanglemdot: s`⦝`, 995 | angles: s`⦞`, 996 | angdnr: s`⦟`, 997 | gtlpar: s`⦠`, 998 | sphericalangleup: s`⦡`, 999 | turnangle: s`⦢`, 1000 | revangle: s`⦣`, 1001 | angleubar: s`⦤`, 1002 | revangleubar: s`⦥`, 1003 | wideangledown: s`⦦`, 1004 | wideangleup: s`⦧`, 1005 | measanglerutone: s`⦨`, 1006 | measanglelutonw: s`⦩`, 1007 | measanglerdtose: s`⦪`, 1008 | measangleldtosw: s`⦫`, 1009 | measangleurtone: s`⦬`, 1010 | measangleultonw: s`⦭`, 1011 | measangledrtose: s`⦮`, 1012 | measangledltosw: s`⦯`, 1013 | revemptyset: s`⦰`, 1014 | emptysetobar: s`⦱`, 1015 | emptysetocirc: s`⦲`, 1016 | emptysetoarr: s`⦳`, 1017 | emptysetoarrl: s`⦴`, 1018 | circlehbar: s`⦵`, 1019 | circledvert: s`⦶`, 1020 | circledparallel: s`⦷`, 1021 | obslash: s`⦸`, 1022 | operp: s`⦹`, 1023 | obot: s`⦺`, 1024 | olcross: s`⦻`, 1025 | odotslashdot: s`⦼`, 1026 | uparrowoncircle: s`⦽`, 1027 | circledwhitebullet: s`⦾`, 1028 | circledbullet: s`⦿`, 1029 | olessthan: s`⧀`, 1030 | ogreaterthan: s`⧁`, 1031 | cirscir: s`⧂`, 1032 | cirE: s`⧃`, 1033 | boxdiag: s`⧄`, 1034 | boxbslash: s`⧅`, 1035 | boxast: s`⧆`, 1036 | boxcircle: s`⧇`, 1037 | boxbox: s`⧈`, 1038 | boxonbox: s`⧉`, 1039 | triangleodot: s`⧊`, 1040 | triangleubar: s`⧋`, 1041 | triangles: s`⧌`, 1042 | triangleserifs: s`⧍`, 1043 | rtriltri: s`⧎`, 1044 | ltrivb: s`⧏`, 1045 | vbrtri: s`⧐`, 1046 | lfbowtie: s`⧑`, 1047 | rfbowtie: s`⧒`, 1048 | fbowtie: s`⧓`, 1049 | lftimes: s`⧔`, 1050 | rftimes: s`⧕`, 1051 | hourglass: s`⧖`, 1052 | blackhourglass: s`⧗`, 1053 | lvzigzag: s`⧘`, 1054 | rvzigzag: s`⧙`, 1055 | Lvzigzag: s`⧚`, 1056 | Rvzigzag: s`⧛`, 1057 | iinfin: s`⧜`, 1058 | tieinfty: s`⧝`, 1059 | nvinfty: s`⧞`, 1060 | dualmap: s`⧟`, 1061 | laplac: s`⧠`, 1062 | lrtriangleeq: s`⧡`, 1063 | shuffle: s`⧢`, 1064 | eparsl: s`⧣`, 1065 | smeparsl: s`⧤`, 1066 | eqvparsl: s`⧥`, 1067 | gleichstark: s`⧦`, 1068 | thermod: s`⧧`, 1069 | downtriangleleftblack: s`⧨`, 1070 | downtrianglerightblack: s`⧩`, 1071 | blackdiamonddownarrow: s`⧪`, 1072 | mdlgblklozenge: s`⧫`, 1073 | circledownarrow: s`⧬`, 1074 | blackcircledownarrow: s`⧭`, 1075 | errbarsquare: s`⧮`, 1076 | errbarblacksquare: s`⧯`, 1077 | errbardiamond: s`⧰`, 1078 | errbarblackdiamond: s`⧱`, 1079 | errbarcircle: s`⧲`, 1080 | errbarblackcircle: s`⧳`, 1081 | ruledelayed: s`⧴`, 1082 | setminus: s`⧵`, 1083 | dsol: s`⧶`, 1084 | rsolbar: s`⧷`, 1085 | xsol: s`⧸`, 1086 | xbsol: s`⧹`, 1087 | doubleplus: s`⧺`, 1088 | tripleplus: s`⧻`, 1089 | lcurvyangle: s`⧼`, 1090 | rcurvyangle: s`⧽`, 1091 | tplus: s`⧾`, 1092 | tminus: s`⧿`, 1093 | bigodot: s`⨀`, 1094 | bigoplus: s`⨁`, 1095 | bigotimes: s`⨂`, 1096 | bigcupdot: s`⨃`, 1097 | biguplus: s`⨄`, 1098 | bigsqcap: s`⨅`, 1099 | bigsqcup: s`⨆`, 1100 | conjquant: s`⨇`, 1101 | disjquant: s`⨈`, 1102 | bigtimes: s`⨉`, 1103 | modtwosum: s`⨊`, 1104 | sumint: s`⨋`, 1105 | iiiint: s`⨌`, 1106 | intbar: s`⨍`, 1107 | intBar: s`⨎`, 1108 | fint: s`⨏`, 1109 | cirfnint: s`⨐`, 1110 | awint: s`⨑`, 1111 | rppolint: s`⨒`, 1112 | scpolint: s`⨓`, 1113 | npolint: s`⨔`, 1114 | pointint: s`⨕`, 1115 | sqint: s`⨖`, 1116 | intlarhk: s`⨗`, 1117 | intx: s`⨘`, 1118 | intcap: s`⨙`, 1119 | intcup: s`⨚`, 1120 | upint: s`⨛`, 1121 | lowint: s`⨜`, 1122 | Join: s`⨝`, 1123 | bigtriangleleft: s`⨞`, 1124 | zcmp: s`⨟`, 1125 | zpipe: s`⨠`, 1126 | zproject: s`⨡`, 1127 | ringplus: s`⨢`, 1128 | plushat: s`⨣`, 1129 | simplus: s`⨤`, 1130 | plusdot: s`⨥`, 1131 | plussim: s`⨦`, 1132 | plussubtwo: s`⨧`, 1133 | plustrif: s`⨨`, 1134 | commaminus: s`⨩`, 1135 | minusdot: s`⨪`, 1136 | minusfdots: s`⨫`, 1137 | minusrdots: s`⨬`, 1138 | opluslhrim: s`⨭`, 1139 | oplusrhrim: s`⨮`, 1140 | vectimes: s`⨯`, 1141 | dottimes: s`⨰`, 1142 | timesbar: s`⨱`, 1143 | btimes: s`⨲`, 1144 | smashtimes: s`⨳`, 1145 | otimeslhrim: s`⨴`, 1146 | otimesrhrim: s`⨵`, 1147 | otimeshat: s`⨶`, 1148 | Otimes: s`⨷`, 1149 | odiv: s`⨸`, 1150 | triangleplus: s`⨹`, 1151 | triangleminus: s`⨺`, 1152 | triangletimes: s`⨻`, 1153 | intprod: s`⨼`, 1154 | intprodr: s`⨽`, 1155 | fcmp: s`⨾`, 1156 | amalg: s`⨿`, 1157 | capdot: s`⩀`, 1158 | uminus: s`⩁`, 1159 | barcup: s`⩂`, 1160 | barcap: s`⩃`, 1161 | capwedge: s`⩄`, 1162 | cupvee: s`⩅`, 1163 | cupovercap: s`⩆`, 1164 | capovercup: s`⩇`, 1165 | cupbarcap: s`⩈`, 1166 | capbarcup: s`⩉`, 1167 | twocups: s`⩊`, 1168 | twocaps: s`⩋`, 1169 | closedvarcup: s`⩌`, 1170 | closedvarcap: s`⩍`, 1171 | Sqcap: s`⩎`, 1172 | Sqcup: s`⩏`, 1173 | closedvarcupsmashprod: s`⩐`, 1174 | wedgeodot: s`⩑`, 1175 | veeodot: s`⩒`, 1176 | Wedge: s`⩓`, 1177 | Vee: s`⩔`, 1178 | wedgeonwedge: s`⩕`, 1179 | veeonvee: s`⩖`, 1180 | bigslopedvee: s`⩗`, 1181 | bigslopedwedge: s`⩘`, 1182 | veeonwedge: s`⩙`, 1183 | wedgemidvert: s`⩚`, 1184 | veemidvert: s`⩛`, 1185 | midbarwedge: s`⩜`, 1186 | midbarvee: s`⩝`, 1187 | doublebarwedge: s`⩞`, 1188 | wedgebar: s`⩟`, 1189 | wedgedoublebar: s`⩠`, 1190 | varveebar: s`⩡`, 1191 | doublebarvee: s`⩢`, 1192 | veedoublebar: s`⩣`, 1193 | dsub: s`⩤`, 1194 | rsub: s`⩥`, 1195 | eqdot: s`⩦`, 1196 | dotequiv: s`⩧`, 1197 | equivVert: s`⩨`, 1198 | equivVvert: s`⩩`, 1199 | dotsim: s`⩪`, 1200 | simrdots: s`⩫`, 1201 | simminussim: s`⩬`, 1202 | congdot: s`⩭`, 1203 | asteq: s`⩮`, 1204 | hatapprox: s`⩯`, 1205 | approxeqq: s`⩰`, 1206 | eqqplus: s`⩱`, 1207 | pluseqq: s`⩲`, 1208 | eqqsim: s`⩳`, 1209 | Coloneq: s`⩴`, 1210 | eqeq: s`⩵`, 1211 | eqeqeq: s`⩶`, 1212 | ddotseq: s`⩷`, 1213 | equivDD: s`⩸`, 1214 | ltcir: s`⩹`, 1215 | gtcir: s`⩺`, 1216 | ltquest: s`⩻`, 1217 | gtquest: s`⩼`, 1218 | leqslant: s`⩽`, 1219 | geqslant: s`⩾`, 1220 | lesdot: s`⩿`, 1221 | gesdot: s`⪀`, 1222 | lesdoto: s`⪁`, 1223 | gesdoto: s`⪂`, 1224 | lesdotor: s`⪃`, 1225 | gesdotol: s`⪄`, 1226 | lessapprox: s`⪅`, 1227 | gtrapprox: s`⪆`, 1228 | lneq: s`⪇`, 1229 | gneq: s`⪈`, 1230 | lnapprox: s`⪉`, 1231 | gnapprox: s`⪊`, 1232 | lesseqqgtr: s`⪋`, 1233 | gtreqqless: s`⪌`, 1234 | lsime: s`⪍`, 1235 | gsime: s`⪎`, 1236 | lsimg: s`⪏`, 1237 | gsiml: s`⪐`, 1238 | lgE: s`⪑`, 1239 | glE: s`⪒`, 1240 | lesges: s`⪓`, 1241 | gesles: s`⪔`, 1242 | eqslantless: s`⪕`, 1243 | eqslantgtr: s`⪖`, 1244 | elsdot: s`⪗`, 1245 | egsdot: s`⪘`, 1246 | eqqless: s`⪙`, 1247 | eqqgtr: s`⪚`, 1248 | eqqslantless: s`⪛`, 1249 | eqqslantgtr: s`⪜`, 1250 | simless: s`⪝`, 1251 | simgtr: s`⪞`, 1252 | simlE: s`⪟`, 1253 | simgE: s`⪠`, 1254 | Lt: s`⪡`, 1255 | Gt: s`⪢`, 1256 | partialmeetcontraction: s`⪣`, 1257 | glj: s`⪤`, 1258 | gla: s`⪥`, 1259 | ltcc: s`⪦`, 1260 | gtcc: s`⪧`, 1261 | lescc: s`⪨`, 1262 | gescc: s`⪩`, 1263 | smt: s`⪪`, 1264 | lat: s`⪫`, 1265 | smte: s`⪬`, 1266 | late: s`⪭`, 1267 | bumpeqq: s`⪮`, 1268 | preceq: s`⪯`, 1269 | succeq: s`⪰`, 1270 | precneq: s`⪱`, 1271 | succneq: s`⪲`, 1272 | preceqq: s`⪳`, 1273 | succeqq: s`⪴`, 1274 | precneqq: s`⪵`, 1275 | succneqq: s`⪶`, 1276 | precapprox: s`⪷`, 1277 | succapprox: s`⪸`, 1278 | precnapprox: s`⪹`, 1279 | succnapprox: s`⪺`, 1280 | Prec: s`⪻`, 1281 | Succ: s`⪼`, 1282 | subsetdot: s`⪽`, 1283 | supsetdot: s`⪾`, 1284 | subsetplus: s`⪿`, 1285 | supsetplus: s`⫀`, 1286 | submult: s`⫁`, 1287 | supmult: s`⫂`, 1288 | subedot: s`⫃`, 1289 | supedot: s`⫄`, 1290 | subseteqq: s`⫅`, 1291 | supseteqq: s`⫆`, 1292 | subsim: s`⫇`, 1293 | supsim: s`⫈`, 1294 | subsetapprox: s`⫉`, 1295 | supsetapprox: s`⫊`, 1296 | subsetneqq: s`⫋`, 1297 | supsetneqq: s`⫌`, 1298 | lsqhook: s`⫍`, 1299 | rsqhook: s`⫎`, 1300 | csub: s`⫏`, 1301 | csup: s`⫐`, 1302 | csube: s`⫑`, 1303 | csupe: s`⫒`, 1304 | subsup: s`⫓`, 1305 | supsub: s`⫔`, 1306 | subsub: s`⫕`, 1307 | supsup: s`⫖`, 1308 | suphsub: s`⫗`, 1309 | supdsub: s`⫘`, 1310 | forkv: s`⫙`, 1311 | topfork: s`⫚`, 1312 | mlcp: s`⫛`, 1313 | forks: s`⫝̸`, 1314 | forksnot: s`⫝`, 1315 | shortlefttack: s`⫞`, 1316 | shortdowntack: s`⫟`, 1317 | shortuptack: s`⫠`, 1318 | perps: s`⫡`, 1319 | vDdash: s`⫢`, 1320 | dashV: s`⫣`, 1321 | Dashv: s`⫤`, 1322 | DashV: s`⫥`, 1323 | varVdash: s`⫦`, 1324 | Barv: s`⫧`, 1325 | vBar: s`⫨`, 1326 | vBarv: s`⫩`, 1327 | barV: s`⫪`, 1328 | Vbar: s`⫫`, 1329 | Not: s`⫬`, 1330 | bNot: s`⫭`, 1331 | revnmid: s`⫮`, 1332 | cirmid: s`⫯`, 1333 | midcir: s`⫰`, 1334 | topcir: s`⫱`, 1335 | nhpar: s`⫲`, 1336 | parsim: s`⫳`, 1337 | interleave: s`⫴`, 1338 | nhVvert: s`⫵`, 1339 | threedotcolon: s`⫶`, 1340 | lllnest: s`⫷`, 1341 | gggnest: s`⫸`, 1342 | leqqslant: s`⫹`, 1343 | geqqslant: s`⫺`, 1344 | trslash: s`⫻`, 1345 | biginterleave: s`⫼`, 1346 | sslash: s`⫽`, 1347 | talloblong: s`⫾`, 1348 | bigtalloblong: s`⫿`, 1349 | squaretopblack: s`⬒`, 1350 | squarebotblack: s`⬓`, 1351 | squareurblack: s`⬔`, 1352 | squarellblack: s`⬕`, 1353 | diamondleftblack: s`⬖`, 1354 | diamondrightblack: s`⬗`, 1355 | diamondtopblack: s`⬘`, 1356 | diamondbotblack: s`⬙`, 1357 | dottedsquare: s`⬚`, 1358 | lgblksquare: s`⬛`, 1359 | lgwhtsquare: s`⬜`, 1360 | vysmblksquare: s`⬝`, 1361 | vysmwhtsquare: s`⬞`, 1362 | pentagonblack: s`⬟`, 1363 | pentagon: s`⬠`, 1364 | varhexagon: s`⬡`, 1365 | varhexagonblack: s`⬢`, 1366 | hexagonblack: s`⬣`, 1367 | lgblkcircle: s`⬤`, 1368 | mdblkdiamond: s`⬥`, 1369 | mdwhtdiamond: s`⬦`, 1370 | mdblklozenge: s`⬧`, 1371 | mdwhtlozenge: s`⬨`, 1372 | smblkdiamond: s`⬩`, 1373 | smblklozenge: s`⬪`, 1374 | smwhtlozenge: s`⬫`, 1375 | blkhorzoval: s`⬬`, 1376 | whthorzoval: s`⬭`, 1377 | blkvertoval: s`⬮`, 1378 | whtvertoval: s`⬯`, 1379 | circleonleftarrow: s`⬰`, 1380 | leftthreearrows: s`⬱`, 1381 | leftarrowonoplus: s`⬲`, 1382 | longleftsquigarrow: s`⬳`, 1383 | nvtwoheadleftarrow: s`⬴`, 1384 | nVtwoheadleftarrow: s`⬵`, 1385 | twoheadmapsfrom: s`⬶`, 1386 | twoheadleftdbkarrow: s`⬷`, 1387 | leftdotarrow: s`⬸`, 1388 | nvleftarrowtail: s`⬹`, 1389 | nVleftarrowtail: s`⬺`, 1390 | twoheadleftarrowtail: s`⬻`, 1391 | nvtwoheadleftarrowtail: s`⬼`, 1392 | nVtwoheadleftarrowtail: s`⬽`, 1393 | leftarrowx: s`⬾`, 1394 | leftcurvedarrow: s`⬿`, 1395 | equalleftarrow: s`⭀`, 1396 | bsimilarleftarrow: s`⭁`, 1397 | leftarrowbackapprox: s`⭂`, 1398 | rightarrowgtr: s`⭃`, 1399 | rightarrowsupset: s`⭄`, 1400 | LLeftarrow: s`⭅`, 1401 | RRightarrow: s`⭆`, 1402 | bsimilarrightarrow: s`⭇`, 1403 | rightarrowbackapprox: s`⭈`, 1404 | similarleftarrow: s`⭉`, 1405 | leftarrowapprox: s`⭊`, 1406 | leftarrowbsimilar: s`⭋`, 1407 | rightarrowbsimilar: s`⭌`, 1408 | medwhitestar: s`⭐`, 1409 | medblackstar: s`⭑`, 1410 | smwhitestar: s`⭒`, 1411 | rightpentagonblack: s`⭓`, 1412 | rightpentagon: s`⭔`, 1413 | postalmark: s`〒`, 1414 | hzigzag: s`〰`, 1415 | }; 1416 | 1417 | export default commonCommandTransformers; 1418 | --------------------------------------------------------------------------------