├── 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)}${str}>`;
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 |
37 |
38 | 중요: DOM 엘리먼트로 복사하려면 Stack에서 다음을 실행하세요.
39 | 링크를 드래그해 북마크바에 끌어다 놓으면 북마크를 클릭하는 것만으로도
40 | 가능합니다.
41 |
42 |
43 |
44 | {js}
45 |
46 |
47 |
48 |
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 | You need to enable JavaScript to run this app.
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 | `표준 입력(stdin) 표준 출력(stdout) ${transformNodeArray(
48 | s.content.filter((s) => s.kind === "command" && s.name === "exmp"),
49 | args
50 | )}
`;
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 | setLatex(sungshiftdang)}
69 | style={{ lineHeight: 1.2 }}
70 | >
71 | 성싶당
72 |
73 | #20941
74 |
75 |
76 | setLatex(physed)} style={{ lineHeight: 1.2 }}>
77 | 체육과목
78 |
79 | #25314
80 |
81 |
82 | setLatex(raspberries)}
84 | style={{ lineHeight: 1.2 }}
85 | >
86 | 라즈베리
87 |
88 | #25386
89 |
90 |
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 | setHtml((p) => !p)}>
107 | {html ? "소스 코드" : "DOM"}
108 |
109 | setJax((p) => !p)}>
110 | {jax ? "MathJax 수식" : "HTML 수식"}
111 |
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 |
--------------------------------------------------------------------------------