├── .devcontainer ├── .bashrc ├── Dockerfile └── devcontainer.json ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ └── codeql.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── SECURITY.md ├── common ├── config │ └── rush │ │ ├── .npmrc │ │ ├── command-line.json │ │ ├── common-versions.json │ │ ├── pnpm-lock.yaml │ │ ├── pnpmfile.js │ │ └── version-policies.json └── scripts │ ├── install-run-rush.js │ ├── install-run-rushx.js │ └── install-run.js ├── docs ├── building.md └── spec.md ├── experimental ├── graph │ ├── .eslintrc.js │ ├── .gitignore │ ├── README.md │ ├── bench │ │ ├── index.ts │ │ ├── json │ │ │ └── parse.ts │ │ └── utils.ts │ ├── docs │ │ └── design.md │ ├── package.json │ ├── src │ │ ├── graph.ts │ │ ├── graphshape.ts │ │ ├── index.ts │ │ ├── objectgraph.ts │ │ └── types.ts │ ├── test │ │ ├── object.spec.ts │ │ ├── shape.spec.ts │ │ └── utils.ts │ ├── tsconfig.eslint.json │ ├── tsconfig.json │ └── tsconfig.prof.json ├── int63 │ ├── .gitignore │ ├── bench │ │ ├── bench-63.js │ │ ├── bench-js.js │ │ ├── bench-wasm.js │ │ └── index.js │ ├── index.js │ ├── int62-js.js │ ├── int62-wasm.js │ ├── int63.js │ ├── package-lock.json │ └── package.json └── tree │ ├── .eslintrc.js │ ├── .gitignore │ ├── bench │ ├── index.ts │ └── matrix │ │ ├── index.ts │ │ ├── rowmajor-256x256-read-baseline.ts │ │ └── rowmajor-256x256-read.ts │ ├── package.json │ ├── src │ ├── index.ts │ ├── tree.ts │ ├── treeshape.ts │ └── types.ts │ ├── test │ ├── bottomuptree.ts │ ├── eval.spec.ts │ ├── evaltree.ts │ ├── examples │ │ └── uuid │ │ │ ├── crypto-node.ts │ │ │ ├── crypto-web.ts │ │ │ ├── index.spec.ts │ │ │ └── uuidtree.ts │ ├── inputtree.ts │ ├── parse.spec.ts │ ├── tree.spec.ts │ └── utils.ts │ ├── tsconfig.eslint.json │ ├── tsconfig.json │ └── tsconfig.prof.json ├── package-lock.json ├── package.json ├── packages ├── common │ ├── binary │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src │ │ │ ├── endianness │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── is │ │ │ │ ├── index.ts │ │ │ │ ├── int16.ts │ │ │ │ ├── int32.ts │ │ │ │ ├── int8.ts │ │ │ │ ├── uint16.ts │ │ │ │ ├── uint32.ts │ │ │ │ ├── uint8.ts │ │ │ │ ├── xint16.ts │ │ │ │ ├── xint32.ts │ │ │ │ └── xint8.ts │ │ │ ├── reinterpret │ │ │ │ ├── float64x1.ts │ │ │ │ ├── index.ts │ │ │ │ ├── int16x4.ts │ │ │ │ ├── int32x2.ts │ │ │ │ ├── int8x8.ts │ │ │ │ ├── uint16x4.ts │ │ │ │ ├── uint32x2.ts │ │ │ │ └── uint8x8.ts │ │ │ ├── to │ │ │ │ ├── boolean.ts │ │ │ │ ├── index.ts │ │ │ │ ├── int16.ts │ │ │ │ ├── int32.ts │ │ │ │ ├── int8.ts │ │ │ │ ├── uint16.ts │ │ │ │ ├── uint32.ts │ │ │ │ └── uint8.ts │ │ │ └── types.ts │ │ ├── test │ │ │ ├── endianness.spec.ts │ │ │ ├── pretty.ts │ │ │ ├── types.spec.ts │ │ │ ├── types.ts │ │ │ └── values.ts │ │ ├── tsconfig.eslint.json │ │ └── tsconfig.json │ ├── config │ │ ├── eslint-config │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ └── ts-config │ │ │ ├── package.json │ │ │ └── tsconfig.json │ ├── datastructures │ │ ├── frugallist │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── bench │ │ │ │ ├── foreach-array.ts │ │ │ │ ├── foreach-frugallist.ts │ │ │ │ ├── foreach-twofield.ts │ │ │ │ ├── foreach.ts │ │ │ │ ├── impl │ │ │ │ │ └── twofield.ts │ │ │ │ └── index.ts │ │ │ ├── package.json │ │ │ ├── src │ │ │ │ ├── forEach.ts │ │ │ │ ├── get.ts │ │ │ │ ├── index.ts │ │ │ │ ├── length.ts │ │ │ │ ├── push.ts │ │ │ │ ├── removeFirst.ts │ │ │ │ └── types.ts │ │ │ ├── test │ │ │ │ ├── frugallist.spec.ts │ │ │ │ └── testfixture.ts │ │ │ ├── tsconfig.eslint.json │ │ │ └── tsconfig.json │ │ ├── handletable │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── bench │ │ │ │ ├── handletable-get-set-x256.ts │ │ │ │ ├── handletable-get-x256.ts │ │ │ │ ├── index.ts │ │ │ │ ├── map-get-x256-uuid.ts │ │ │ │ ├── map-get-x256.ts │ │ │ │ └── uuid.ts │ │ │ ├── package.json │ │ │ ├── src │ │ │ │ └── index.ts │ │ │ ├── test │ │ │ │ ├── handletable.spec.ts │ │ │ │ ├── handletable.stress.spec.ts │ │ │ │ └── testfixture.ts │ │ │ ├── tsconfig.eslint.json │ │ │ └── tsconfig.json │ │ └── heap │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── bench │ │ │ ├── heap-push-ascending.ts │ │ │ ├── heap-push-descending.ts │ │ │ ├── heap-push-pop-ascending.ts │ │ │ ├── heap-push-pop-descending.ts │ │ │ └── index.ts │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── heap.ts │ │ │ └── index.ts │ │ │ ├── test │ │ │ ├── heap.spec.ts │ │ │ ├── heap.stress.spec.ts │ │ │ └── testfixture.ts │ │ │ ├── tsconfig.eslint.json │ │ │ └── tsconfig.json │ ├── scheduler │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── bench │ │ │ ├── coalesce │ │ │ │ ├── async.ts │ │ │ │ ├── promise-number.ts │ │ │ │ ├── promise-void.ts │ │ │ │ └── prompt.ts │ │ │ └── index.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── coalesce.ts │ │ │ └── index.ts │ │ ├── test │ │ │ └── coalesce.spec.ts │ │ ├── tsconfig.eslint.json │ │ └── tsconfig.json │ └── test │ │ └── monkey │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src │ │ └── index.ts │ │ ├── test │ │ └── monkey.spec.ts │ │ ├── tsconfig.eslint.json │ │ └── tsconfig.json ├── core │ ├── micro │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── bench │ │ │ ├── index.ts │ │ │ ├── matrix │ │ │ │ ├── index.ts │ │ │ │ ├── rowmajor-256x256-read-baseline.ts │ │ │ │ └── rowmajor-256x256-read.ts │ │ │ ├── profile.ts │ │ │ ├── sheetlet │ │ │ │ ├── cached-10x10.ts │ │ │ │ ├── cached-2x2.ts │ │ │ │ ├── index.ts │ │ │ │ ├── recalc-10x10.ts │ │ │ │ └── recalc-2x2.ts │ │ │ └── util.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── adjust-tree │ │ │ │ ├── arrayUtil.ts │ │ │ │ ├── common.ts │ │ │ │ ├── delete.ts │ │ │ │ ├── index.ts │ │ │ │ ├── node.ts │ │ │ │ ├── tree.ts │ │ │ │ ├── types.ts │ │ │ │ └── validity.ts │ │ │ ├── binder.ts │ │ │ ├── cell.ts │ │ │ ├── consumerset.ts │ │ │ ├── core.ts │ │ │ ├── formula.ts │ │ │ ├── functions.ts │ │ │ ├── index.ts │ │ │ ├── key.ts │ │ │ ├── map │ │ │ │ └── producer.ts │ │ │ ├── matrix.ts │ │ │ ├── matrix │ │ │ │ ├── dense.ts │ │ │ │ ├── producer.ts │ │ │ │ └── rowmajor.ts │ │ │ ├── permutation │ │ │ │ └── index.ts │ │ │ ├── range.ts │ │ │ ├── sheetlet.ts │ │ │ ├── types.ts │ │ │ └── vector │ │ │ │ ├── dense.ts │ │ │ │ └── producer.ts │ │ ├── test │ │ │ ├── basic-adjust-tree.spec.ts │ │ │ ├── consumerset.spec.ts │ │ │ ├── formula.spec.ts │ │ │ ├── key.spec.ts │ │ │ ├── matrix.spec.ts │ │ │ ├── random-adjust-tree.ts │ │ │ ├── sample-adjust-tree.spec.ts │ │ │ ├── sheetlet.spec.ts │ │ │ ├── sheets.ts │ │ │ ├── util.ts │ │ │ └── vector.spec.ts │ │ ├── tsconfig.eslint.json │ │ ├── tsconfig.json │ │ └── tsconfig.prof.json │ └── nano │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── bench │ │ ├── index.ts │ │ └── util.ts │ │ ├── package.json │ │ ├── src │ │ ├── ast.ts │ │ ├── compiler.ts │ │ ├── core.ts │ │ ├── debug.ts │ │ ├── index.ts │ │ ├── interpreter.ts │ │ ├── parser.ts │ │ └── types.ts │ │ ├── test │ │ ├── nano.spec.ts │ │ ├── tsconfig.json │ │ ├── util │ │ │ ├── index.ts │ │ │ ├── loggingConsumer.ts │ │ │ └── objectTypes.ts │ │ └── writerMonad.spec.ts │ │ ├── tsconfig.eslint.json │ │ └── tsconfig.json ├── test │ └── example │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src │ │ ├── async.ts │ │ ├── index.ts │ │ └── types.ts │ │ ├── tsconfig.eslint.json │ │ └── tsconfig.json └── types │ ├── es │ ├── .eslintrc.js │ ├── .gitignore │ ├── bench │ │ └── json.ts │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── produce.ts │ ├── test │ │ ├── produce.spec.ts │ │ └── tsconfig.json │ ├── tsconfig.eslint.json │ ├── tsconfig.json │ └── tsconfig.prof.json │ ├── type-utils │ ├── .eslintrc.js │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── nullConsumer.ts │ ├── test │ │ ├── nullConsumer.spec.ts │ │ └── tsconfig.json │ ├── tsconfig.eslint.json │ ├── tsconfig.json │ └── tsconfig.prof.json │ └── types │ ├── .eslintrc.js │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ ├── compareFunction.ts │ ├── immutable.ts │ ├── index.ts │ ├── jsonable.ts │ ├── matrix.ts │ ├── record.ts │ ├── tuple.ts │ └── vector.ts │ ├── test-d │ └── immutable.test-d.ts │ ├── test │ ├── compareFunction.spec.ts │ ├── record.spec.ts │ ├── tsconfig.json │ └── tuple.spec.ts │ ├── tsconfig.eslint.json │ ├── tsconfig.json │ └── tsconfig.prof.json └── rush.json /.devcontainer/.bashrc: -------------------------------------------------------------------------------- 1 | # ~/.bashrc: executed by bash(1) for non-login shells. 2 | 3 | # Note: PS1 and umask are already set in /etc/profile. You should not 4 | # need this unless you want different defaults for root. 5 | # umask 022 6 | export PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' 7 | 8 | # You may uncomment the following lines if you want `ls' to be colorized: 9 | export LS_OPTIONS='--color=auto' 10 | eval "`dircolors`" 11 | alias ls='ls $LS_OPTIONS' 12 | alias ll='ls $LS_OPTIONS -l' 13 | alias l='ls $LS_OPTIONS -lA' 14 | 15 | # Some more alias to avoid making mistakes: 16 | alias rm='rm -i' 17 | alias cp='cp -i' 18 | alias mv='mv -i' 19 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------------------------------------- 2 | # Copyright (c) Microsoft Corporation. All rights reserved. 3 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. 4 | #------------------------------------------------------------------------------------------------------------- 5 | 6 | # Derived from the javascript-node container template here: 7 | # https://github.com/microsoft/vscode-dev-containers/tree/master/containers/javascript-node 8 | 9 | # [Choice] Node.js version: 14, 12, 10 10 | ARG VARIANT="14-buster" 11 | FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT} 12 | 13 | # Install Docker CLI / Docker-Compose and create '/usr/local/share/docker-init.sh' to proxy the 14 | # docker socket. (We retrieve the script from the 'docker-from-docker' dev container template) 15 | RUN mkdir /tmp/library-scripts \ 16 | && curl -o /tmp/library-scripts/docker-debian.sh -sS https://raw.githubusercontent.com/microsoft/vscode-dev-containers/c3214ad7fcf5086e898e3941a36e0171959a48ca/containers/azure-ansible/.devcontainer/library-scripts/docker-debian.sh \ 17 | && /bin/bash /tmp/library-scripts/docker-debian.sh "${ENABLE_NONROOT_DOCKER}" "${SOURCE_SOCKET}" "${TARGET_SOCKET}" "node" \ 18 | && rm -rf /tmp/library-scripts/ 19 | 20 | # Install additional desired packages here. Base image includes the following: 21 | # 22 | # eslint (global), node/npm, nvm, yarn, zsh (+ oh-my-zsh) 23 | # curl, g++, git, make, procps, python, wget 24 | # 25 | # (See https://github.com/microsoft/vscode-dev-containers/tree/master/containers/javascript-node/.devcontainer) 26 | 27 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 28 | # && apt-get -y install --no-install-recommends \ 29 | # vim 30 | 31 | # Clean up after apt-get 32 | RUN apt-get autoremove -y \ 33 | && apt-get clean -y \ 34 | && rm -rf /var/lib/apt/lists/* 35 | 36 | USER node 37 | 38 | # For convenience, install the Rush CLI globally. Note that the '/bin/rush' script 39 | # automatically installs and caches the versions of rush/pnpm specified in 'rush.json' 40 | # in the project's '/common/temp' folder. (i.e., the version of the globally installed 41 | # '@microsoft/rush' package doesn't matter.) 42 | RUN bash -ci "npm i -g @microsoft/rush" 43 | 44 | # Install our custom .bashrc for colorized prompt and 'ls' 45 | COPY ./.bashrc /home/node/ 46 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | // For format details, see https://aka.ms/vscode-remote/devcontainer.json 7 | { 8 | "name": "Tiny-Calc", 9 | "build": { "dockerfile": "Dockerfile" }, 10 | 11 | // Expose 'docker.sock' to the dev container and proxy access for the 'node' user. 12 | "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind" ], 13 | "postStartCommand": "/usr/local/share/docker-init.sh", 14 | 15 | // Set *default* container specific settings.json values on container create. 16 | "settings": { "terminal.integrated.shell.linux": "/bin/bash" }, 17 | 18 | // Add the IDs of extensions you want installed when the container is created. 19 | // 20 | // Note: 'ms-vscode-remote.remote-containers' is install locally, not inside the 21 | // dev container, and therefore is excluded from this list. 22 | "extensions": [ 23 | "dbaeumer.vscode-eslint", 24 | "drewbourne.vscode-remark-lint", 25 | "eamodio.gitlens", 26 | "editorconfig.editorconfig", 27 | "esbenp.prettier-vscode", 28 | "ms-azuretools.vscode-docker", 29 | "streetsidesoftware.code-spell-checker" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Don't go up further looking for more .editorconfig files 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 4 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | # C-style doc comments 13 | block_comment_start = /* 14 | block_comment = * 15 | block_comment_end = */ 16 | 17 | [*.{js,jsx,ts,tsx}] 18 | indent_size = 4 19 | 20 | [{*.yml,package.json,rush.json}] 21 | indent_size = 2 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Don't allow people to merge changes to these generated files, because the result 2 | # may be invalid. You need to run "rush update" again. 3 | shrinkwrap.yaml merge=binary 4 | npm-shrinkwrap.json merge=binary 5 | yarn.lock merge=binary 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [12.x, 14.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm ci 28 | - run: npm run build 29 | - run: npm test 30 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "Code scanning - action" 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 19 * * 0' 8 | 9 | jobs: 10 | CodeQL-Build: 11 | 12 | # CodeQL runs on ubuntu-latest and windows-latest 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v2 18 | with: 19 | # We must fetch at least the immediate parents so that if this is 20 | # a pull request then we can checkout the head. 21 | fetch-depth: 2 22 | 23 | # If this run was triggered by a pull request event, then checkout 24 | # the head of the pull request instead of the merge commit. 25 | - run: git checkout HEAD^2 26 | if: ${{ github.event_name == 'pull_request' }} 27 | 28 | # Initializes the CodeQL tools for scanning. 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v1 31 | # Override language selection by uncommenting this and choosing your languages 32 | # with: 33 | # languages: go, javascript, csharp, python, cpp, java 34 | 35 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 36 | # If this step fails, then you should remove it and run the build manually (see below) 37 | - name: Autobuild 38 | uses: github/codeql-action/autobuild@v1 39 | 40 | # ℹ️ Command-line programs to run using the OS shell. 41 | # 📚 https://git.io/JvXDl 42 | 43 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 44 | # and modify them (or add more) to build your code if your project 45 | # uses a compiled language 46 | 47 | #- run: | 48 | # make bootstrap 49 | # make release 50 | 51 | - name: Perform CodeQL Analysis 52 | uses: github/codeql-action/analyze@v1 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Note: Patterns in this '.gitignore' should be scoped to artifacts of the '@plasma/root' module. 2 | # Modules contained within the monorepo should function independently of the monorepo 3 | # infrastructure and therefore declare their own '.gitignore' files. 4 | 5 | # NPM dependencies (root only) 6 | /node_modules 7 | 8 | # Common Rush/pnpm cache 9 | /common/temp 10 | 11 | # Rush artifacts emitted into modules contained within the monorepo. Note that this is not a 12 | # violation of our principle that contained modules function independently as these artifacts 13 | # are created by the monorepo tools. 14 | **/.rush 15 | **/*.build.log 16 | **/*.build.error.log 17 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "eamodio.gitlens", 4 | "editorconfig.editorconfig", 5 | "dbaeumer.vscode-eslint", 6 | "esbenp.prettier-vscode", 7 | "streetsidesoftware.code-spell-checker", 8 | "drewbourne.vscode-remark-lint" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | ".git": true, // Ignore Git repo internals 4 | "**/.DS_Store": true, // Ignore macOS desktop services store 5 | 6 | // Rush caches a large number of files in '/common/temp'. Exclude these from the 7 | // workspace to reduce load on the file system watcher. 8 | "common/temp": true, 9 | }, 10 | 11 | // Allow comments in the following JSON files. Unfortunately, this does not support 12 | // relative paths or variable expansion (e.g., ${workspaceFolder}), so we can only key 13 | // on the file names. 14 | "files.associations": { 15 | "rush.json": "jsonc", 16 | "command-line.json": "jsonc", 17 | "command-versions.json": "jsonc", 18 | "version-policies.json": "jsonc" 19 | }, 20 | 21 | // ESLint: Mono repo support 22 | "eslint.nodePath": "packages/common/config/eslint-config/node_modules/", // Work around https://github.com/microsoft/vscode-eslint/issues/1012 23 | "eslint.workingDirectories": [{ "mode": "auto" }], // Find '.eslintrc.js' in local package folder. 24 | 25 | // Spell check dictionary 26 | "cSpell.words": [ 27 | "Jsonable", 28 | "affordances", 29 | "deserialization", 30 | "deserializes", 31 | "endregion", 32 | "interoperate", 33 | "interoperation", 34 | "multibyte", 35 | "prefetching", 36 | "serializable", 37 | "signedness", 38 | "unopinionated" 39 | ], 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 5 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 6 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 7 | 8 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 9 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 10 | provided by the bot. You will only need to do this once across all repos using our CLA. 11 | 12 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 13 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 14 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 15 | -------------------------------------------------------------------------------- /common/config/rush/.npmrc: -------------------------------------------------------------------------------- 1 | # Rush uses this file to configure the package registry, regardless of whether the 2 | # package manager is PNPM, NPM, or Yarn. Prior to invoking the package manager, 3 | # Rush will always copy this file to the folder where installation is performed. 4 | # When NPM is the package manager, Rush works around NPM's processing of 5 | # undefined environment variables by deleting any lines that reference undefined 6 | # environment variables. 7 | # 8 | # DO NOT SPECIFY AUTHENTICATION CREDENTIALS IN THIS FILE. It should only be used 9 | # to configure registry sources. 10 | 11 | registry=https://registry.npmjs.org/ 12 | always-auth=false 13 | -------------------------------------------------------------------------------- /common/config/rush/common-versions.json: -------------------------------------------------------------------------------- 1 | /** 2 | * This configuration file specifies NPM dependency version selections that affect all projects 3 | * in a Rush repo. For full documentation, please see https://rushjs.io 4 | */ 5 | { 6 | "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json", 7 | 8 | /** 9 | * A table that specifies a "preferred version" for a dependency package. The "preferred version" 10 | * is typically used to hold an indirect dependency back to a specific version, however generally 11 | * it can be any SemVer range specifier (e.g. "~1.2.3"), and it will narrow any (compatible) 12 | * SemVer range specifier. See the Rush documentation for details about this feature. 13 | */ 14 | "preferredVersions": { 15 | 16 | /** 17 | * When someone asks for "^1.0.0" make sure they get "1.2.3" when working in this repo, 18 | * instead of the latest version. 19 | */ 20 | // "some-library": "1.2.3" 21 | }, 22 | 23 | /** 24 | * The "rush check" command can be used to enforce that every project in the repo must specify 25 | * the same SemVer range for a given dependency. However, sometimes exceptions are needed. 26 | * The allowedAlternativeVersions table allows you to list other SemVer ranges that will be 27 | * accepted by "rush check" for a given dependency. 28 | * 29 | * IMPORTANT: THIS TABLE IS FOR *ADDITIONAL* VERSION RANGES THAT ARE ALTERNATIVES TO THE 30 | * USUAL VERSION (WHICH IS INFERRED BY LOOKING AT ALL PROJECTS IN THE REPO). 31 | * This design avoids unnecessary churn in this file. 32 | */ 33 | "allowedAlternativeVersions": { 34 | 35 | /** 36 | * For example, allow some projects to use an older TypeScript compiler 37 | * (in addition to whatever "usual" version is being used by other projects in the repo): 38 | */ 39 | // "typescript": [ 40 | // "~2.4.0" 41 | // ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /common/config/rush/pnpmfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * When using the PNPM package manager, you can use pnpmfile.js to workaround 5 | * dependencies that have mistakes in their package.json file. (This feature is 6 | * functionally similar to Yarn's "resolutions".) 7 | * 8 | * For details, see the PNPM documentation: 9 | * https://pnpm.js.org/docs/en/hooks.html 10 | * 11 | * IMPORTANT: SINCE THIS FILE CONTAINS EXECUTABLE CODE, MODIFYING IT IS LIKELY 12 | * TO INVALIDATE ANY CACHED DEPENDENCY ANALYSIS. We recommend to run "rush update --full" 13 | * after any modification to pnpmfile.js. 14 | * 15 | */ 16 | module.exports = { 17 | hooks: { 18 | readPackage 19 | } 20 | }; 21 | 22 | /** 23 | * This hook is invoked during installation before a package's dependencies 24 | * are selected. 25 | * The `packageJson` parameter is the deserialized package.json 26 | * contents for the package that is about to be installed. 27 | * The `context` parameter provides a log() function. 28 | * The return value is the updated object. 29 | */ 30 | function readPackage(packageJson, context) { 31 | 32 | // // The karma types have a missing dependency on typings from the log4js package. 33 | // if (packageJson.name === '@types/karma') { 34 | // context.log('Fixed up dependencies for @types/karma'); 35 | // packageJson.dependencies['log4js'] = '0.6.38'; 36 | // } 37 | 38 | return packageJson; 39 | } 40 | -------------------------------------------------------------------------------- /common/config/rush/version-policies.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "policyName": "public", 4 | "definitionName": "lockStepVersion", 5 | "version": "0.0.0-alpha.5", 6 | "nextBump": "prerelease" 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /common/scripts/install-run-rushx.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | // See the @microsoft/rush package's LICENSE file for license information. 4 | Object.defineProperty(exports, "__esModule", { value: true }); 5 | // THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. 6 | // 7 | // This script is intended for usage in an automated build environment where the Rush command may not have 8 | // been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush 9 | // specified in the rush.json configuration file (if not already installed), and then pass a command-line to the 10 | // rushx command. 11 | // 12 | // An example usage would be: 13 | // 14 | // node common/scripts/install-run-rushx.js custom-command 15 | // 16 | // For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ 17 | require("./install-run-rush"); 18 | //# sourceMappingURL=install-run-rushx.js.map -------------------------------------------------------------------------------- /experimental/graph/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /experimental/graph/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /experimental/graph/README.md: -------------------------------------------------------------------------------- 1 | # Graph 2 | Experimental graph data structure for performing incremental computation. 3 | 4 | TODO 5 | * Design 6 | * Reconcile tree encoding with the Forest (immutable) style design patterns 7 | * Global IDs 8 | * Add skeleton abstraction around identity that would allow you to tinker with various schemes in the 9 | actual code. 10 | * Tree 11 | * Constrain graph to tree (or make graph a special encoding of tree?) 12 | * Schema 13 | * Add type & cardinality constraints to encoding. 14 | * Synthetic nodes 15 | * Try implementing synthetic nodes from string primitives? 16 | * Lifetime: currently 'deleteNode()' leaves dangling references in the graph to delete nodes. 17 | * ACR: Auto-delete nodes when they no longer referenced. 18 | * Bidirectional graph: track parents and remove references on delete. 19 | * Efficient diff 20 | * Perf 21 | * Intern / reuse type info. 22 | * Improve children representation: 23 | * Store as (GraphNode | GraphNode[]) to avoid array alloc for 0/1 children. 24 | * Binary 25 | * Bugs 26 | * Graph can not store 'undefined' scalar (array encoding should use a sentinel to avoid conflict) 27 | -------------------------------------------------------------------------------- /experimental/graph/bench/index.ts: -------------------------------------------------------------------------------- 1 | import { run } from "hotloop"; 2 | 3 | (async () => { 4 | await run([ 5 | { "path": "./sheetlet/cached-2x2.ts" }, 6 | { "path": "./sheetlet/recalc-2x2.ts" }, 7 | ]); 8 | 9 | await run([ 10 | { "path": "./sheetlet/cached-10x10.ts" }, 11 | { "path": "./sheetlet/recalc-10x10.ts" }, 12 | ]); 13 | 14 | await run([ 15 | { "path": "./matrix/rowmajor-256x256-read-baseline.ts" }, 16 | { "path": "./matrix/rowmajor-256x256-read.ts" }, 17 | ]); 18 | })(); 19 | -------------------------------------------------------------------------------- /experimental/graph/bench/json/parse.ts: -------------------------------------------------------------------------------- 1 | import { benchmark } from "hotloop"; 2 | import { IMatrixReader, IMatrixWriter } from "@tiny-calc/nano/src/types"; 3 | 4 | export function sumBenchmark(name: string, reader: IMatrixReader) { 5 | const { rowCount, colCount } = reader; 6 | 7 | benchmark(`Matrix (${name} ${rowCount}x${colCount}): sum row-wise`, () => { 8 | let sum = 0; 9 | 10 | for (let row = 0; row < rowCount; row++) { 11 | for (let col = 0; col < colCount; col++) { 12 | sum += reader.getCell(row, col); 13 | } 14 | } 15 | 16 | consume(sum); 17 | }); 18 | } 19 | 20 | export function fill( 21 | reader: IMatrixReader, 22 | writer: IMatrixWriter, 23 | rowStart = 0, 24 | colStart = 0, 25 | rowCount = reader.rowCount - rowStart, 26 | colCount = reader.colCount - colStart, 27 | value = (row: number, col: number) => row * rowCount + col 28 | ) { 29 | const rowEnd = rowStart + rowCount; 30 | const colEnd = colStart + colCount; 31 | 32 | for (let row = rowStart; row < rowEnd; row++) { 33 | for (let col = colStart; col < colEnd; col++) { 34 | writer.setCell(row, col, value(row, col) as any); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /experimental/graph/bench/utils.ts: -------------------------------------------------------------------------------- 1 | import { Monkey } from "@tiny-calc-test/monkey"; 2 | import { JsonableObject } from "@tiny-calc/types"; 3 | 4 | interface IGraphSpec { 5 | seed: number; 6 | depth: number; 7 | } 8 | 9 | export function generateGraph(spec: IGraphSpec) { 10 | const m = new Monkey(spec.seed); 11 | let current: JsonableObject; 12 | let d = spec.depth; 13 | 14 | const add = (parent, child) => { 15 | if (Array.isArray(parent)) { 16 | parent.push(child); 17 | } else { 18 | parent[m.chooseString(7)] = child; 19 | } 20 | } 21 | 22 | if (d > 0) { 23 | current = m.choose([ 24 | { 25 | scale: 0.5, 26 | action: () => { 27 | 28 | } 29 | }, { 30 | scale: 0.5, 31 | action: () => { 32 | 33 | } 34 | } 35 | ]); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /experimental/graph/docs/design.md: -------------------------------------------------------------------------------- 1 | # Layers 2 | 3 | ## Local Data Structure 4 | An observable graph data structure that efficiently supports non-collaborative reads/writes, serialization/deserialization, and 5 | change notifications. 6 | 7 | 8 | 9 | (One possibility is that this a proxied JSON-like object graph.) 10 | 11 | ## Adjustment 12 | A secondary data structure that tracks the information necessary to adjust ops from a past refSeq# to the current local head. 13 | 14 | Once clients have coalesced on the minSeq#, this data structure may be discarded until new edits arrive. There may be a specialized 15 | variants of the adjustment data structure for offline and/or undo/redo. 16 | 17 | ## Summaries / History 18 | A component that serializes and deserializes the primary and secondary data structures. (The extent of history retained should 19 | be options.) 20 | 21 | # Ops 22 | We may find that applying stronger typing improves op efficiency. (For example, allowing us to meld adjacent string nodes.) 23 | 24 | One model is SharedMap's value type, which provides a way to define the legal ops on a value (e.g., incr.) 25 | https://redis.io/commands 26 | 27 | -------------------------------------------------------------------------------- /experimental/graph/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc-experimental/graph", 3 | "version": "0.0.0-alpha.5", 4 | "main": "dist/index.js", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/microsoft/tiny-calc.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "bench": "cd bench && ts-node index.ts", 14 | "build": "tsc", 15 | "clean": "rimraf ./dist *.build.log", 16 | "dev": "npm run build -- --watch", 17 | "lint": "eslint --ext=ts --format visualstudio src", 18 | "prof": "tsc --project tsconfig.prof.json && node --prof temp/bench/profile.js && node --prof-process isolate-*-v8.log > profile.log && rm isolate-*-v8.log", 19 | "test": "mocha -r ts-node/register test/**/*.spec.ts", 20 | "adjust-random": "ts-node test/random-adjust-tree.ts" 21 | }, 22 | "dependencies": { 23 | "@tiny-calc/frugallist": "0.0.0-alpha.5", 24 | "@tiny-calc/handletable": "0.0.0-alpha.5", 25 | "@tiny-calc/types": "0.0.0-alpha.5" 26 | }, 27 | "devDependencies": { 28 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 29 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 30 | "@tiny-calc-test/monkey": "0.0.0-alpha.5", 31 | "@types/mocha": "^8.0.4", 32 | "@types/node": "^14.14.10", 33 | "best-random": "^1.0.3", 34 | "eslint": "^7.13.0", 35 | "hotloop": "^1.2.0", 36 | "mocha": "^8.2.1", 37 | "rimraf": "^3.0.2", 38 | "ts-node": "^9.0.0", 39 | "typescript": "^4.0.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /experimental/graph/src/graph.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { GraphNode, IGraphConsumer, IGraphProducer, IGraphReader, IGraphWriter } from "./types"; 7 | import { GraphShape } from "./graphshape"; 8 | 9 | export class Graph extends GraphShape implements IGraphProducer, IGraphReader, IGraphWriter { 10 | private readonly nodeToValue: T[] = []; 11 | 12 | // #region IGraphProducer 13 | 14 | public openGraph(consumer: IGraphConsumer): IGraphReader { 15 | super.openGraph(consumer); 16 | return this; 17 | } 18 | 19 | public closeGraph(consumer: IGraphConsumer): void { 20 | super.closeGraph(consumer); 21 | } 22 | 23 | // #endregion IGraphProducer 24 | 25 | // #region IGraphReader 26 | 27 | public getNode(node: GraphNode): T { 28 | return this.nodeToValue[node]; 29 | } 30 | 31 | // #endregion IGraphReader 32 | 33 | // #region IGraphWriter 34 | 35 | public setNode(node: GraphNode, value: T): void { 36 | this.nodeToValue[node] = value; 37 | this.invalidateNode(node); 38 | } 39 | 40 | // #endregion IGraphWriter 41 | 42 | private invalidateNode(node: GraphNode) { 43 | this.forEachConsumer((consumer) => (consumer as IGraphConsumer).nodeChanged(node, this)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /experimental/graph/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { GraphShape } from "./graphshape"; 7 | export { 8 | GraphNode, 9 | IGraphShapeConsumer, 10 | IGraphShapeProducer, 11 | IGraphShapeReader, 12 | IGraphShapeWriter, 13 | } from "./types"; 14 | export { ObjectGraph } from "./objectgraph"; 15 | -------------------------------------------------------------------------------- /experimental/graph/test/utils.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { strict as assert } from "assert"; 7 | import { IGraphShapeProducer, GraphNode, IGraphShapeReader } from "../src"; 8 | 9 | export const nullGraphConsumer = { childrenChanged(): void {} }; 10 | 11 | export type GraphShapeDescription = [node: GraphNode, children?: GraphShapeDescription[]]; 12 | 13 | function descriptionToReader(desc: GraphShapeDescription): IGraphShapeReader { 14 | const shape: GraphNode[][] = []; 15 | 16 | const visit = (root: GraphShapeDescription) => { 17 | const node = root[0]; 18 | if (shape[node] === undefined) { 19 | const children = root[1] ?? []; 20 | shape[node] = children.map(([child]) => child); 21 | for (const child of children) { 22 | visit(child); 23 | } 24 | } 25 | } 26 | 27 | visit(desc); 28 | 29 | return { 30 | getChildCount(parent: GraphNode) { return shape[parent].length }, 31 | getChild(parent: GraphNode, index: number) { return shape[parent][index] } 32 | } 33 | } 34 | 35 | export function shapeToString(reader: IGraphShapeReader): string { 36 | const visited = new Set(); 37 | const visit = (root: GraphNode) => { 38 | if (visited.has(root)) { 39 | return `${root}*`; 40 | } 41 | 42 | visited.add(root); 43 | 44 | let s = `${root}`; 45 | const children = []; 46 | for (let i = 0; i < reader.getChildCount(root); i++) { 47 | children.push(visit(reader.getChild(root, i))); 48 | } 49 | 50 | if (children.length > 0) { 51 | s = `${s} (${children.join(" ")})`; 52 | } 53 | 54 | return s; 55 | } 56 | 57 | return visit(GraphNode.root); 58 | } 59 | 60 | export function descriptionToString(expected: GraphShapeDescription): string { 61 | return shapeToString(descriptionToReader(expected)); 62 | } 63 | 64 | export function expectShape(actual: IGraphShapeProducer, expected: GraphShapeDescription): void { 65 | const actualReader = actual.openGraph(nullGraphConsumer); 66 | try { 67 | const actualStr = shapeToString(actualReader); 68 | const expectedStr = descriptionToString(expected); 69 | 70 | assert.equal(actualStr, expectedStr); 71 | } finally { 72 | actual.closeGraph(nullGraphConsumer); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /experimental/graph/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /experimental/graph/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /experimental/graph/tsconfig.prof.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/tool-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./temp", 5 | "rootDir": ".", 6 | "outDir": "./temp" 7 | }, 8 | "include": ["bench/profile.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /experimental/int63/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /experimental/int63/bench/bench-63.js: -------------------------------------------------------------------------------- 1 | const { Int63 } = require("../int63") 2 | const { benchmark } = require("hotloop") 3 | 4 | let hi30 = (Math.random() * 0x100000000) >>> 2; 5 | let lo32 = 0; 6 | let sum = 0; 7 | 8 | benchmark("Int63 (js)", () => { 9 | sum += Int63(hi30, lo32++); 10 | }); 11 | 12 | console.log(sum); // Side-effect using computed result to prevent dead code elimination 13 | -------------------------------------------------------------------------------- /experimental/int63/bench/bench-js.js: -------------------------------------------------------------------------------- 1 | const { Int62 } = require("../int62-js") 2 | const { benchmark } = require("hotloop") 3 | 4 | let hi30 = (Math.random() * 0x100000000) >>> 2; 5 | let lo32 = 0; 6 | let sum = 0; 7 | 8 | benchmark("Int62 as reinterpret cast (js)", () => { 9 | sum += Int62(hi30, lo32++); 10 | }); 11 | 12 | console.log(sum); // Side-effect using computed result to prevent dead code elimination 13 | -------------------------------------------------------------------------------- /experimental/int63/bench/bench-wasm.js: -------------------------------------------------------------------------------- 1 | const { Int62 } = require("../int62-wasm") 2 | const { benchmark } = require("hotloop") 3 | 4 | let hi30 = (Math.random() * 0x100000000) >>> 2; 5 | let lo32 = 0; 6 | let sum = 0; 7 | 8 | benchmark("Int62 as reinterpret cast (wasm)", () => { 9 | sum += Int62(hi30, lo32++); 10 | }); 11 | 12 | console.log(sum); // Side-effect using computed result to prevent dead code elimination 13 | -------------------------------------------------------------------------------- /experimental/int63/bench/index.js: -------------------------------------------------------------------------------- 1 | const { run } = require("hotloop"); 2 | 3 | run([ 4 | { "path": "./bench/bench-js.js" }, 5 | { "path": "./bench/bench-wasm.js" }, 6 | { "path": "./bench/bench-63.js" }, 7 | ]); 8 | -------------------------------------------------------------------------------- /experimental/int63/index.js: -------------------------------------------------------------------------------- 1 | const js = require("./int62-js").Int62; 2 | const wasm = require("./int62-wasm").Int62; 3 | const { Int63 } = require("./int63"); 4 | const assert = require("assert").strict; 5 | 6 | const hex = (n) => `0x${n.toString(16).padStart(8, '0')}` 7 | 8 | const patterns = [ 9 | [0, 0], 10 | [0, 1], 11 | [1, 0], 12 | [0x3fffffff, 0xffffffff], // Largest Int62 13 | [0x40000000, 0x00000000], // negative zero 14 | [0x7ff00000, 0x00000000], // +infinity 15 | [0xfff00000, 0x00000000], // -infinity 16 | [0x7ff00000, 0x00000001], [0x7ff7ffff, 0xffffffff], // qnan range 1 17 | [0xfff00000, 0x00000001], [0xfff7ffff, 0xffffffff], // qnan range 2 18 | [0x7ff80000, 0x00000000], [0x7fffffff, 0xffffffff], // snan range 1 19 | [0xfff80000, 0x00000000], [0xffffffff, 0xffffffff] // snan range 2 20 | ]; 21 | 22 | console.log("\nInt63:") 23 | for (const [hi, lo] of patterns) { 24 | console.log(`${hex(hi)} ${hex(lo)} -> ${`${Int63(hi, lo)}`.padEnd(20)} (reinterpret: ${js(hi, lo)})`); 25 | 26 | // Sanity check that wasm and js yield the same result. 27 | assert.equal(js(hi, lo), wasm(hi, lo)); 28 | } 29 | 30 | // Unfortunately the Int63 scheme can produce -0, which collapses to 0 when stringified to JSON 31 | { 32 | const zero = Int63(0, 0); 33 | const negativeZero = Int63(0x40000000, 0); 34 | assert.notEqual(zero, negativeZero); // 0 !== -0 35 | assert.equal(zero, Math.abs(negativeZero)); // 0 === | -0 | 36 | } -------------------------------------------------------------------------------- /experimental/int63/int62-js.js: -------------------------------------------------------------------------------- 1 | const i32x2 = new Int32Array(2); 2 | const f64x1 = new Float64Array(i32x2.buffer); 3 | 4 | module.exports = { 5 | Int62: (hi30, lo32) => { 6 | i32x2[0] = lo32; 7 | i32x2[1] = hi30; 8 | return f64x1[0]; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /experimental/int63/int63.js: -------------------------------------------------------------------------------- 1 | const i32x2 = new Int32Array(2); 2 | const f64x1 = new Float64Array(i32x2.buffer); 3 | 4 | module.exports = { 5 | Int63: (hi31, lo32) => { 6 | i32x2[0] = lo32; 7 | 8 | // All non-finite numbers set the 62nd bit. To avoid these, we move 9 | // the 62nd bit to the 63rd and clear the 62nd. 10 | i32x2[1] = (hi31 >>> 30) << 31 // Isolate bit 30 and move to position 31 11 | | ((hi31 << 2) >>> 2); // Combine with the lower bits 12 | 13 | return f64x1[0]; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /experimental/int63/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "int62", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/benchmark": { 8 | "version": "1.0.33", 9 | "resolved": "https://registry.npmjs.org/@types/benchmark/-/benchmark-1.0.33.tgz", 10 | "integrity": "sha512-rG7Ieasa9UfZJnL72qiFvY9ivhEIYjCGgfcLLb5tJ/EL9+Mcxernj6W3HVCv/cOfJYuwNUwvVVhnrKl8iT8aqA==" 11 | }, 12 | "@types/minimist": { 13 | "version": "1.2.0", 14 | "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", 15 | "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=" 16 | }, 17 | "benchmark": { 18 | "version": "2.1.4", 19 | "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", 20 | "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", 21 | "requires": { 22 | "lodash": "^4.17.4", 23 | "platform": "^1.3.3" 24 | } 25 | }, 26 | "hotloop": { 27 | "version": "1.1.0", 28 | "resolved": "https://registry.npmjs.org/hotloop/-/hotloop-1.1.0.tgz", 29 | "integrity": "sha512-vJ8qU6KJyE6hDCodoQerFIMfSDYGDbVw2NN82gf6cGvr53SN6kAsdYSFZm4q/5wd0lRy9pBN24Cbep00AY5v0A==", 30 | "requires": { 31 | "@types/benchmark": "^1.0.31", 32 | "@types/minimist": "^1.2.0", 33 | "benchmark": "^2.1.4", 34 | "minimist": "^1.2.5" 35 | } 36 | }, 37 | "lodash": { 38 | "version": "4.17.20", 39 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 40 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" 41 | }, 42 | "minimist": { 43 | "version": "1.2.5", 44 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 45 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 46 | }, 47 | "platform": { 48 | "version": "1.3.6", 49 | "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", 50 | "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /experimental/int63/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "index.js", 3 | "scripts": { 4 | "bench": "node bench/index.js", 5 | "test": "node index.js" 6 | }, 7 | "dependencies": { 8 | "hotloop": "^1.1.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /experimental/tree/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /experimental/tree/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /experimental/tree/bench/index.ts: -------------------------------------------------------------------------------- 1 | import { run } from "hotloop"; 2 | 3 | (async () => { 4 | await run([ 5 | { "path": "./sheetlet/cached-2x2.ts" }, 6 | { "path": "./sheetlet/recalc-2x2.ts" }, 7 | ]); 8 | 9 | await run([ 10 | { "path": "./sheetlet/cached-10x10.ts" }, 11 | { "path": "./sheetlet/recalc-10x10.ts" }, 12 | ]); 13 | 14 | await run([ 15 | { "path": "./matrix/rowmajor-256x256-read-baseline.ts" }, 16 | { "path": "./matrix/rowmajor-256x256-read.ts" }, 17 | ]); 18 | })(); 19 | -------------------------------------------------------------------------------- /experimental/tree/bench/matrix/index.ts: -------------------------------------------------------------------------------- 1 | import { benchmark } from "hotloop"; 2 | import { IMatrixReader, IMatrixWriter } from "@tiny-calc/nano/src/types"; 3 | 4 | export function sumBenchmark(name: string, reader: IMatrixReader) { 5 | const { rowCount, colCount } = reader; 6 | 7 | benchmark(`Matrix (${name} ${rowCount}x${colCount}): sum row-wise`, () => { 8 | let sum = 0; 9 | 10 | for (let row = 0; row < rowCount; row++) { 11 | for (let col = 0; col < colCount; col++) { 12 | sum += reader.getCell(row, col); 13 | } 14 | } 15 | 16 | consume(sum); 17 | }); 18 | } 19 | 20 | export function fill( 21 | reader: IMatrixReader, 22 | writer: IMatrixWriter, 23 | rowStart = 0, 24 | colStart = 0, 25 | rowCount = reader.rowCount - rowStart, 26 | colCount = reader.colCount - colStart, 27 | value = (row: number, col: number) => row * rowCount + col 28 | ) { 29 | const rowEnd = rowStart + rowCount; 30 | const colEnd = colStart + colCount; 31 | 32 | for (let row = rowStart; row < rowEnd; row++) { 33 | for (let col = colStart; col < colEnd; col++) { 34 | writer.setCell(row, col, value(row, col) as any); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /experimental/tree/bench/matrix/rowmajor-256x256-read-baseline.ts: -------------------------------------------------------------------------------- 1 | import { IMatrixWriter, IMatrixReader } from "@tiny-calc/nano/src/types"; 2 | import { fill, sumBenchmark } from "."; 3 | 4 | export class Array256x256 implements IMatrixWriter, IMatrixReader { 5 | private readonly cells: number[] = new Array(this.rowCount * this.colCount).fill(0); 6 | 7 | //#region IMatrixReader 8 | 9 | public readonly matrixProducer = undefined as any; 10 | 11 | public get rowCount() { return 256; } 12 | public get colCount() { return 256; } 13 | 14 | public getCell(row: number, col: number) { 15 | return this.cells[(row << 8) + col]; 16 | } 17 | 18 | //#endregion IMatrixReader 19 | 20 | //#region IMatrixWriter 21 | 22 | public setCell(row: number, col: number, value: number) { 23 | this.cells[(row << 8) + col] = value; 24 | } 25 | 26 | //#endregion IMatrixWriter 27 | } 28 | 29 | const matrix = new Array256x256(); 30 | 31 | fill(matrix, matrix); 32 | sumBenchmark("Baseline", matrix); 33 | -------------------------------------------------------------------------------- /experimental/tree/bench/matrix/rowmajor-256x256-read.ts: -------------------------------------------------------------------------------- 1 | import { RowMajorMatrix, DenseVector } from "../../src"; 2 | import { nullConsumer } from "../util"; 3 | import { sumBenchmark, fill } from "."; 4 | 5 | const rows = new DenseVector(); 6 | const cols = new DenseVector(); 7 | const matrix = new RowMajorMatrix(rows, cols); 8 | rows.splice(/* start: */ 0, /* deleteCount: */ 0, /* insertCount: */ 256); 9 | cols.splice(/* start: */ 0, /* deleteCount: */ 0, /* insertCount: */ 256); 10 | 11 | fill(matrix, matrix); 12 | sumBenchmark("RowMajor", matrix.openMatrix(nullConsumer)); 13 | -------------------------------------------------------------------------------- /experimental/tree/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc-experimental/tree", 3 | "version": "0.0.0-alpha.5", 4 | "main": "dist/index.js", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/microsoft/tiny-calc.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "bench": "cd bench && ts-node index.ts", 14 | "build": "tsc", 15 | "clean": "rimraf ./dist *.build.log", 16 | "dev": "npm run build -- --watch", 17 | "lint": "eslint --ext=ts --format visualstudio src", 18 | "prof": "tsc --project tsconfig.prof.json && node --prof temp/bench/profile.js && node --prof-process isolate-*-v8.log > profile.log && rm isolate-*-v8.log", 19 | "test": "mocha -r ts-node/register test/**/*.spec.ts", 20 | "adjust-random": "ts-node test/random-adjust-tree.ts" 21 | }, 22 | "dependencies": { 23 | "@tiny-calc/frugallist": "0.0.0-alpha.5", 24 | "@tiny-calc/handletable": "0.0.0-alpha.5", 25 | "@tiny-calc/types": "0.0.0-alpha.5" 26 | }, 27 | "devDependencies": { 28 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 29 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 30 | "@tiny-calc/nano": "0.0.0-alpha.5", 31 | "@types/mocha": "^8.0.4", 32 | "@types/node": "^14.14.10", 33 | "best-random": "^1.0.3", 34 | "eslint": "^7.13.0", 35 | "hotloop": "^1.2.0", 36 | "mocha": "^8.2.1", 37 | "rimraf": "^3.0.2", 38 | "ts-node": "^9.0.0", 39 | "typescript": "^4.0.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /experimental/tree/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { 7 | ITreeShapeConsumer, 8 | ITreeShapeProducer, 9 | ITreeShapeReader, 10 | ITreeShapeWriter, 11 | ITreeConsumer, 12 | ITreeProducer, 13 | ITreeReader, 14 | ITreeWriter, 15 | TreeNode, 16 | TreeNodeLocation, 17 | } from './types'; 18 | 19 | export { Tree } from './tree'; 20 | export { TreeShape } from './treeshape'; 21 | -------------------------------------------------------------------------------- /experimental/tree/test/bottomuptree.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { 7 | Tree, 8 | TreeNode, 9 | TreeNodeLocation, 10 | ITreeReader, 11 | ITreeProducer, 12 | ITreeShapeReader 13 | } from "../src"; 14 | 15 | export abstract class BottomUpTree extends Tree { 16 | private readonly dirty: boolean[] = [true]; 17 | private readonly input: ITreeReader; 18 | private readonly results: TOut[] = []; 19 | 20 | public constructor (input: ITreeProducer) { 21 | super(); 22 | 23 | this.input = input.openTree(/* consumer: */ this); 24 | } 25 | 26 | protected get shape(): ITreeShapeReader { return this.input; } 27 | 28 | protected abstract evalNode(node:TreeNode, input: ITreeReader, descendants: ITreeReader): TOut; 29 | 30 | // #region ITreeReader 31 | 32 | public getNode(node: TreeNode): TOut { 33 | if (this.isDirty(node)) { 34 | const result = this.results[node] = this.evalNode(node, /* input: */ this.input, /* descendants */ this); 35 | this.clearDirty(node); 36 | return result; 37 | } else { 38 | return this.results[node]; 39 | } 40 | } 41 | 42 | // #endregion ITreeWriter 43 | 44 | protected isDirty(node: TreeNode): boolean { 45 | return this.dirty[node] !== false; 46 | } 47 | 48 | protected clearDirty(node: TreeNode): void { 49 | this.dirty[node] = false; 50 | } 51 | 52 | protected invalidate(node: TreeNode): void { 53 | while (!this.dirty[node]) { 54 | this.dirty[node] = true; 55 | node = this.getParent(node); 56 | } 57 | } 58 | 59 | // #region ITreeConsumer 60 | 61 | public nodeChanged(node: TreeNode): void { 62 | this.invalidate(node); 63 | 64 | super.nodeChanged(node); 65 | } 66 | 67 | public nodeMoved(node: TreeNode, oldLocation: TreeNodeLocation): void { 68 | this.invalidate(node); 69 | this.invalidate(this.parentOfLocation(oldLocation)); 70 | 71 | super.nodeMoved(node, oldLocation); 72 | } 73 | 74 | // #endregion ITreeConsumer 75 | } 76 | -------------------------------------------------------------------------------- /experimental/tree/test/evaltree.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { TreeNode, ITreeConsumer, ITreeReader, ITreeProducer } from "../src"; 7 | import { BottomUpTree } from "./bottomuptree"; 8 | import { none } from "./utils"; 9 | 10 | export type BinOp = (left: number, right: number) => number; 11 | export type Expr = BinOp | number; 12 | 13 | export const add = (left: number, right: number): number => left + right; 14 | 15 | function isBinOp(expr: Expr): expr is BinOp { 16 | return typeof(expr) !== "number"; 17 | } 18 | 19 | export class EvalTree extends BottomUpTree implements ITreeConsumer { 20 | private evalCounter = 0; 21 | 22 | public constructor (exprTree: ITreeProducer) { 23 | super(exprTree); 24 | } 25 | 26 | public evalNode(node: TreeNode, input: ITreeReader, descendants: ITreeReader): number { 27 | this.evalCounter++; 28 | 29 | const expr = input.getNode(node); 30 | 31 | return isBinOp(expr) 32 | ? this.applyOp(node, expr, descendants) 33 | : expr; 34 | } 35 | 36 | private applyOp(node: TreeNode, op: BinOp, descendants: ITreeReader) { 37 | node = descendants.getFirstChild(node); 38 | let accumulator = descendants.getNode(node); 39 | 40 | // eslint-disable-next-line no-constant-condition 41 | while (true) { 42 | node = descendants.getNextSibling(node); 43 | if (node === none) { 44 | break; 45 | } 46 | 47 | accumulator = op(accumulator, descendants.getNode(node)); 48 | } 49 | 50 | return accumulator; 51 | } 52 | 53 | public get evalCount(): number { return this.evalCounter; } 54 | public resetEvalCount(): void { this.evalCounter = 0; } 55 | } 56 | -------------------------------------------------------------------------------- /experimental/tree/test/examples/uuid/crypto-node.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | const { randomFillSync } = require('crypto'); 7 | module.exports = randomFillSync; 8 | -------------------------------------------------------------------------------- /experimental/tree/test/examples/uuid/crypto-web.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | declare var crypto: any; 7 | 8 | module.exports = crypto.getRandomValues.bind(crypto); 9 | -------------------------------------------------------------------------------- /experimental/tree/test/inputtree.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { Tree, TreeNode, ITreeWriter, ITreeShapeProducer, ITreeShapeReader } from "../src"; 7 | 8 | export class InputTree extends Tree implements ITreeWriter { 9 | private readonly values: T[] = []; 10 | protected readonly shape: ITreeShapeReader; 11 | 12 | public constructor (shape: ITreeShapeProducer) { 13 | super(); 14 | 15 | this.shape = shape.openTree(/* consumer: */ this); 16 | } 17 | 18 | // #region ITreeReader 19 | 20 | public getNode(node: TreeNode): T { 21 | return this.values[node]; 22 | } 23 | 24 | // #endregion ITreeReader 25 | 26 | // #region ITreeWriter 27 | 28 | public setNode(node: TreeNode, value: T): void { 29 | this.values[node] = value; 30 | this.invalidateNode(node); 31 | } 32 | 33 | // #endregion ITreeWriter 34 | } 35 | -------------------------------------------------------------------------------- /experimental/tree/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /experimental/tree/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /experimental/tree/tsconfig.prof.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/tool-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./temp", 5 | "rootDir": ".", 6 | "outDir": "./temp" 7 | }, 8 | "include": ["bench/profile.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm_ci_requires_name_field", 3 | "lockfileVersion": 1 4 | } 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm_ci_requires_name_field", 3 | "repository": { 4 | "type": "git", 5 | "url": "https://github.com/microsoft/tiny-calc.git" 6 | }, 7 | "author": "Microsoft", 8 | "license": "MIT", 9 | "scripts": { 10 | "rush": "node common/scripts/install-run-rush.js", 11 | "rush:repair": "git clean -xfd && (rm common/config/rush/pnpm-lock.yaml || true) && npm run rush -- update --purge --recheck", 12 | "postinstall": "npm run rush -- install", 13 | "build": "npm run rush -- build", 14 | "test": "npm run rush -- test", 15 | "prepublishOnly": "npm run build && npm run test", 16 | "setversion": "npm run rush -- version --ensure-version-policy --version-policy public --override-version" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/common/binary/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/common/binary/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/common/binary/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/binary", 3 | "version": "0.0.0-alpha.5", 4 | "main": "dist/index.js", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/microsoft/tiny-calc.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "bench": "cd bench && ts-node index.ts", 14 | "build": "tsc", 15 | "clean": "rimraf ./dist *.build.log", 16 | "dev": "npm run build -- --watch", 17 | "lint": "eslint --ext=ts --format visualstudio src", 18 | "test": "mocha -r ts-node/register test/**/*.spec.ts" 19 | }, 20 | "dependencies": { 21 | "@tiny-calc/types": "0.0.0-alpha.5" 22 | }, 23 | "devDependencies": { 24 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 25 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 26 | "@tiny-calc-test/type-utils": "0.0.0-alpha.5", 27 | "@types/mocha": "^8.0.4", 28 | "@types/node": "^14.14.10", 29 | "best-random": "^1.0.3", 30 | "eslint": "^7.13.0", 31 | "hotloop": "^1.2.0", 32 | "mocha": "^8.2.1", 33 | "rimraf": "^3.0.2", 34 | "ts-node": "^9.0.0", 35 | "typescript": "^4.0.5" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/common/binary/src/endianness/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { uint8x8, uint16x4 } from "../reinterpret"; 7 | 8 | /** 9 | * Equal to 'bigEndian' (false) or 'littleEndian' (true) depending on the native endianness 10 | * of the host machine. 11 | * 12 | * Compatible with the DataView APIs that take an optional 'littleEndian' argument. 13 | */ 14 | export const nativeEndian: boolean = (() => { 15 | uint16x4[0] = 0xff00; 16 | return uint8x8[0] === 0; 17 | })(); 18 | -------------------------------------------------------------------------------- /packages/common/binary/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export * from "./to"; 7 | export * from "./is"; 8 | export * from "./types"; 9 | export * from "./reinterpret"; 10 | export * from "./endianness"; 11 | -------------------------------------------------------------------------------- /packages/common/binary/src/is/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { isInt8 } from "./int8"; 7 | export { isInt16 } from "./int16"; 8 | export { isInt32 } from "./int32"; 9 | export { isUint8 } from "./uint8"; 10 | export { isUint16 } from "./uint16"; 11 | export { isUint32 } from "./uint32"; 12 | export { isXint8 } from "./xint8"; 13 | export { isXint16 } from "./xint16"; 14 | export { isXint32 } from "./xint32"; 15 | -------------------------------------------------------------------------------- /packages/common/binary/src/is/int16.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { toInt16 } from "../to/int16"; 7 | 8 | export function isInt16(value: number): boolean { 9 | return toInt16(value) === value; 10 | } 11 | -------------------------------------------------------------------------------- /packages/common/binary/src/is/int32.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { toInt32 } from "../to/int32"; 7 | 8 | export function isInt32(value: number): boolean { 9 | return toInt32(value) === value; 10 | } 11 | -------------------------------------------------------------------------------- /packages/common/binary/src/is/int8.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { toInt8 } from "../to/int8"; 7 | 8 | export function isInt8(value: number): boolean { 9 | return toInt8(value) === value; 10 | } 11 | -------------------------------------------------------------------------------- /packages/common/binary/src/is/uint16.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { toUint16 } from "../to/uint16"; 7 | 8 | export function isUint16(value: number): boolean { 9 | return toUint16(value) === value; 10 | } 11 | -------------------------------------------------------------------------------- /packages/common/binary/src/is/uint32.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { toUint32 } from "../to/uint32"; 7 | 8 | export function isUint32(value: number): boolean { 9 | return toUint32(value) === value; 10 | } 11 | -------------------------------------------------------------------------------- /packages/common/binary/src/is/uint8.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { toUint8 } from "../to/uint8"; 7 | 8 | export function isUint8(value: number): boolean { 9 | return toUint8(value) === value; 10 | } 11 | -------------------------------------------------------------------------------- /packages/common/binary/src/is/xint16.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { isInt16 } from "./int16"; 7 | import { isUint16 } from "./uint16"; 8 | 9 | export function isXint16(value: number): boolean { 10 | return isInt16(value) || isUint16(value); 11 | } 12 | -------------------------------------------------------------------------------- /packages/common/binary/src/is/xint32.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { isInt32 } from "./int32"; 7 | import { isUint32 } from "./uint32"; 8 | 9 | export function isXint32(value: number): boolean { 10 | return isInt32(value) || isUint32(value); 11 | } 12 | -------------------------------------------------------------------------------- /packages/common/binary/src/is/xint8.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { isInt8 } from "./int8"; 7 | import { isUint8 } from "./uint8"; 8 | 9 | export function isXint8(value: number): boolean { 10 | return isInt8(value) || isUint8(value); 11 | } 12 | -------------------------------------------------------------------------------- /packages/common/binary/src/reinterpret/float64x1.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | /** 7 | * One of several preallocated TypedArrays that share their underlying buffer. Used for 8 | * reinterpreting bits. 9 | * 10 | * Note that TypedArrays expose the endianness of the host machine. Use with care. 11 | * 12 | * (See: 'float64x1', 'int8x8', 'int16x4', 'int32x2', 'uint8x8', 'uint16x4', 'uint32x2') 13 | */ 14 | export const float64x1: Float64Array = new Float64Array(1); 15 | -------------------------------------------------------------------------------- /packages/common/binary/src/reinterpret/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { float64x1 } from "./float64x1"; 7 | export { int32x2 } from "./int32x2"; 8 | export { int16x4 } from "./int16x4"; 9 | export { int8x8 } from "./int8x8"; 10 | export { uint32x2 } from "./uint32x2"; 11 | export { uint16x4 } from "./uint16x4"; 12 | export { uint8x8 } from "./uint8x8"; 13 | -------------------------------------------------------------------------------- /packages/common/binary/src/reinterpret/int16x4.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { float64x1 } from "./float64x1"; 7 | 8 | /** 9 | * One of several preallocated TypedArrays that share their underlying buffer. Used for 10 | * reinterpreting bits. 11 | * 12 | * Note that TypedArrays expose the endianness of the host machine. Use with care. 13 | * 14 | * (See: 'float64x1', 'int8x8', 'int16x4', 'int32x2', 'uint8x8', 'uint16x4', 'uint32x2') 15 | */ 16 | export const int16x4: Int16Array = new Int16Array(float64x1.buffer); 17 | -------------------------------------------------------------------------------- /packages/common/binary/src/reinterpret/int32x2.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { float64x1 } from "./float64x1"; 7 | 8 | /** 9 | * One of several preallocated TypedArrays that share their underlying buffer. Used for 10 | * reinterpreting bits. 11 | * 12 | * Note that TypedArrays expose the endianness of the host machine. Use with care. 13 | * 14 | * (See: 'float64x1', 'int8x8', 'int16x4', 'int32x2', 'uint8x8', 'uint16x4', 'uint32x2') 15 | */ 16 | export const int32x2: Int32Array = new Int32Array(float64x1.buffer); 17 | -------------------------------------------------------------------------------- /packages/common/binary/src/reinterpret/int8x8.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { float64x1 } from "./float64x1"; 7 | 8 | /** 9 | * One of several preallocated TypedArrays that share their underlying buffer. Used for 10 | * reinterpreting bits. 11 | * 12 | * Note that TypedArrays expose the endianness of the host machine. Use with care. 13 | * 14 | * (See: 'float64x1', 'int8x8', 'int16x4', 'int32x2', 'uint8x8', 'uint16x4', 'uint32x2') 15 | */ 16 | export const int8x8: Int8Array = new Int8Array(float64x1.buffer); 17 | -------------------------------------------------------------------------------- /packages/common/binary/src/reinterpret/uint16x4.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { float64x1 } from "./float64x1"; 7 | 8 | /** 9 | * One of several preallocated TypedArrays that share their underlying buffer. Used for 10 | * reinterpreting bits. 11 | * 12 | * Note that TypedArrays expose the endianness of the host machine. Use with care. 13 | * 14 | * (See: 'float64x1', 'int8x8', 'int16x4', 'int32x2', 'uint8x8', 'uint16x4', 'uint32x2') 15 | */ 16 | export const uint16x4: Uint16Array = new Uint16Array(float64x1.buffer); 17 | -------------------------------------------------------------------------------- /packages/common/binary/src/reinterpret/uint32x2.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { float64x1 } from "./float64x1"; 7 | 8 | /** 9 | * One of several preallocated TypedArrays that share their underlying buffer. Used for 10 | * reinterpreting bits. 11 | * 12 | * Note that TypedArrays expose the endianness of the host machine. Use with care. 13 | * 14 | * (See: 'float64x1', 'int8x8', 'int16x4', 'int32x2', 'uint8x8', 'uint16x4', 'uint32x2') 15 | */ 16 | export const uint32x2: Uint32Array = new Uint32Array(float64x1.buffer); 17 | -------------------------------------------------------------------------------- /packages/common/binary/src/reinterpret/uint8x8.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { float64x1 } from "./float64x1"; 7 | 8 | /** 9 | * One of several preallocated TypedArrays that share their underlying buffer. Used for 10 | * reinterpreting bits. 11 | * 12 | * Note that TypedArrays expose the endianness of the host machine. Use with care. 13 | * 14 | * (See: 'float64x1', 'int8x8', 'int16x4', 'int32x2', 'uint8x8', 'uint16x4', 'uint32x2') 15 | */ 16 | export const uint8x8: Uint8Array = new Uint8Array(float64x1.buffer); 17 | -------------------------------------------------------------------------------- /packages/common/binary/src/to/boolean.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export function toBoolean(value: number): boolean { 7 | return value !== 0; 8 | } 9 | -------------------------------------------------------------------------------- /packages/common/binary/src/to/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { toInt8 } from "./int8"; 7 | export { toInt16 } from "./int16"; 8 | export { toInt32 } from "./int32"; 9 | export { toUint8 } from "./uint8"; 10 | export { toUint16 } from "./uint16"; 11 | export { toUint32 } from "./uint32"; 12 | -------------------------------------------------------------------------------- /packages/common/binary/src/to/int16.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export function toInt16(value: number | boolean): number { 7 | return (value as number) << 16 >> 16; 8 | } 9 | -------------------------------------------------------------------------------- /packages/common/binary/src/to/int32.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export function toInt32(value: number | boolean): number { 7 | // eslint-disable-next-line no-bitwise 8 | return (value as number) | 0; 9 | } 10 | -------------------------------------------------------------------------------- /packages/common/binary/src/to/int8.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export function toInt8(value: number | boolean): number { 7 | return (value as number) << 24 >> 24; 8 | } 9 | -------------------------------------------------------------------------------- /packages/common/binary/src/to/uint16.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export function toUint16(value: number | boolean): number { 7 | return (value as number) << 16 >>> 16; 8 | } 9 | -------------------------------------------------------------------------------- /packages/common/binary/src/to/uint32.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export function toUint32(value: number | boolean): number { 7 | return (value as number) >>> 0; 8 | } 9 | -------------------------------------------------------------------------------- /packages/common/binary/src/to/uint8.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export function toUint8(value: number | boolean): number { 7 | return (value as number) << 24 >>> 24; 8 | } 9 | -------------------------------------------------------------------------------- /packages/common/binary/test/endianness.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { strict as assert } from "assert"; 7 | import { bigEndian, littleEndian, nativeEndian } from "../src"; 8 | 9 | describe("Endianness", () => { 10 | it("'nativeEndian' must match host machine endianness", () => { 11 | const buffer = new ArrayBuffer(2); 12 | new DataView(buffer).setInt16(0, 0xff, /* littleEndian: */ true); 13 | const expected = new Int16Array(buffer)[0] === 0xff; 14 | 15 | assert.equal(nativeEndian, expected); 16 | }); 17 | 18 | describe("DataView compatibility", () => { 19 | it("'littleEndian' must be assigned the value true", () => { 20 | assert.equal(littleEndian, true); 21 | }); 22 | 23 | it("'bigEndian' must be assigned the value false", () => { 24 | assert.equal(bigEndian, false); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/common/binary/test/pretty.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | const zero = "0"; 7 | 8 | export function pretty(value: any) { 9 | switch (typeof value) { 10 | case "number": 11 | if (Math.trunc(value) !== value || Math.abs(value) === Infinity) { 12 | return value.toString(); 13 | } 14 | 15 | const digits = value.toString(16).substr(value < 0 ? 1 : 0); 16 | return `${value < 0 ? "-" : ""}0x${zero.substr((digits.length + 1) % 2)}${digits}`; 17 | 18 | case "undefined": 19 | return "undefined"; 20 | 21 | default: 22 | return JSON.stringify(value); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/common/binary/test/values.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { INumericType } from "./types"; 7 | 8 | const special = [undefined, null, true, false, NaN, -Infinity, -0, +0, 0.5, "1", +Infinity]; 9 | 10 | export function getTestValues(type: INumericType): any[] { 11 | return special.concat([ type.min - 1, type.min, type.max, type.max + 1 ]); 12 | } 13 | -------------------------------------------------------------------------------- /packages/common/binary/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/common/binary/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/common/config/eslint-config/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | -------------------------------------------------------------------------------- /packages/common/config/eslint-config/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | // This is a workaround for https://github.com/eslint/eslint/issues/3458 7 | require('@rushstack/eslint-config/patch/modern-module-resolution'); 8 | 9 | module.exports = { 10 | extends: ['@rushstack/eslint-config/profile/node'], 11 | plugins: ['header'], 12 | rules: { 13 | 'header/header': [ 14 | 'error', 15 | 'block', 16 | '!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n ', 17 | /* newLines: */ 2 // '\n\n' = 1 blank line after block comment 18 | ] 19 | }, 20 | overrides: [{ 21 | // Declare an override that applies to TypeScript files only 22 | files: ['*.ts', '*.tsx'], 23 | rules: { 24 | // RATIONALE: Not concerned about readability for non-TypeScript developers. (Harmless) 25 | '@typescript-eslint/no-parameter-properties': 'off', 26 | 27 | // RATIONALE: Underscore prefixes are not common practice for TS/JS devs, but we allow them when 28 | // needed to prevent collisions between public/private accessors. 29 | // Docs: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/naming-convention.md 30 | '@typescript-eslint/naming-convention': [ 31 | 'error', 32 | { selector: 'accessor', modifiers: ['private'], format: ['camelCase'], 'leadingUnderscore': 'allow' }, 33 | ], 34 | 35 | // RATIONALE: Allow the convenience of type inference for internal declarations, but require explicit 36 | // typing for module exports to prevent unintentionally leaking implementation details. 37 | '@typescript-eslint/explicit-module-boundary-types': 'error', 38 | '@typescript-eslint/explicit-function-return-type': 'off', 39 | '@typescript-eslint/typedef': 'off', 40 | } 41 | }] 42 | }; 43 | -------------------------------------------------------------------------------- /packages/common/config/eslint-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/eslint-config", 3 | "version": "0.0.0-alpha.5", 4 | "main": "index.js", 5 | "description": "Common ESLint config", 6 | "sideEffects": "false", 7 | "repository": { 8 | "type": "git", 9 | "url" : "https://github.com/microsoft/plasma.git" 10 | }, 11 | "author": "Microsoft", 12 | "license": "MIT", 13 | "scripts": { 14 | "build": "echo No build steps necessary.", 15 | "lint": "echo No lint steps necessary.", 16 | "test": "echo No test steps necessary." 17 | }, 18 | "devDependencies": { 19 | "@rushstack/eslint-config": "^2.3.1", 20 | "eslint-plugin-header": "^3.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/common/config/ts-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/ts-config", 3 | "version": "0.0.0-alpha.5", 4 | "description": "Common TypeScript config", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url" : "https://github.com/microsoft/plasma.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "echo No build steps necessary.", 14 | "lint": "echo No lint steps necessary.", 15 | "test": "echo No test steps necessary." 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/common/config/ts-config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "lib": ["es2019"], 8 | "forceConsistentCasingInFileNames": true, 9 | "sourceMap": true, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "incremental": true, 13 | "preserveConstEnums": false, 14 | "strict": true, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "noUnusedLocals": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/bench/foreach-array.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { getTestArgs } from "hotloop"; 7 | import { forEachBench } from "./foreach"; 8 | 9 | const { count } = getTestArgs(); 10 | const list: number[] = new Array(count).fill(0).map((value, index) => index); 11 | 12 | forEachBench( 13 | "Array", 14 | count, 15 | list, 16 | (array, callback) => { 17 | for (let i = 0; i < array.length; i++) { 18 | callback(array[i]); 19 | } 20 | } 21 | ) 22 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/bench/foreach-frugallist.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { getTestArgs } from "hotloop"; 7 | import { FrugalList, FrugalList_push, FrugalList_forEach } from "../src"; 8 | import { forEachBench } from "./foreach"; 9 | 10 | const { count } = getTestArgs(); 11 | 12 | let list: FrugalList = undefined; 13 | for (let i = 0; i < count; i++) { 14 | list = FrugalList_push(list, i); 15 | } 16 | 17 | forEachBench( 18 | "FrugalList", 19 | count, 20 | list, 21 | FrugalList_forEach 22 | ) 23 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/bench/foreach-twofield.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { getTestArgs } from "hotloop"; 7 | import { TwoField, TwoField_push, TwoField_forEach } from "./impl/twofield"; 8 | import { forEachBench } from "./foreach"; 9 | 10 | const { count } = getTestArgs(); 11 | 12 | const list: TwoField = {}; 13 | for (let i = 0; i < count; i++) { 14 | TwoField_push(list, i); 15 | } 16 | 17 | forEachBench( 18 | "TwoField", 19 | count, 20 | list, 21 | TwoField_forEach 22 | ) 23 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/bench/foreach.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { strict as assert } from "assert"; 7 | import { benchmark } from "hotloop"; 8 | 9 | let consumedCount = 0; 10 | let consumedCache: any; 11 | 12 | export function forEachBench( 13 | name: string, 14 | count: number, 15 | self: TSelf, 16 | forEach: (self: TSelf, callback: (consumer: TConsumer) => void) => void 17 | ): void { 18 | // Sanity check that list was initialized correctly. 19 | let sum = 0; 20 | forEach(self, () => { 21 | sum++; 22 | }); 23 | assert.equal(sum, count); 24 | 25 | benchmark(`${name}: ForEach(length=${count})`, () => { 26 | forEach(self, (item) => { 27 | // Paranoid defense against dead code elimination. 28 | consumedCount++; 29 | consumedCount |= 0; 30 | 31 | if (consumedCount === 0) { 32 | consumedCache = item; 33 | } 34 | }); 35 | }); 36 | } 37 | 38 | // Prevent v8's optimizer from identifying 'cached' as an unused value. 39 | process.on('exit', () => { 40 | if (consumedCount === -1) { 41 | console.log(`Ignore this: ${consumedCache}`); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/bench/impl/twofield.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export type TwoFieldItem = Exclude 7 | 8 | export type TwoField = { 9 | item0?: T; 10 | items?: T[]; 11 | } 12 | 13 | export function TwoField_push(self: TwoField, consumer: TwoFieldItem) { 14 | if (self.item0 === undefined) { 15 | self.item0 = consumer; 16 | } else if (self.items === undefined) { 17 | self.items = [consumer]; 18 | } else { 19 | self.items.push(consumer); 20 | } 21 | } 22 | 23 | export function TwoField_delete(self: TwoField, consumer: TwoFieldItem) { 24 | if (self.item0 === undefined) { 25 | self.item0 = consumer; 26 | } else if (self.items === undefined) { 27 | self.items = [consumer]; 28 | } else { 29 | self.items.push(consumer); 30 | } 31 | } 32 | 33 | export function TwoField_forEach(self: TwoField, callback: (consumer: T) => void): void { 34 | if (self.item0 === undefined) { 35 | return; 36 | } 37 | 38 | callback(self.item0); 39 | 40 | if (self.items !== undefined) { 41 | for (let i = 0; i < self.items.length; i++) { 42 | callback(self.items[i]); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/bench/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { run } from "hotloop"; 7 | 8 | // eslint-disable-next-line no-void 9 | void (async () => { 10 | let count = 0; 11 | console.group(`ConsumerSet (length=${count})`) 12 | await run([ 13 | { "path": "./foreach-array.ts", args: { count }}, 14 | { "path": "./foreach-frugallist.ts", args: { count }}, 15 | { "path": "./foreach-twofield.ts", args: { count }}, 16 | ]); 17 | console.groupEnd(); 18 | 19 | count = 1; 20 | console.group(`ConsumerSet (length=${count})`) 21 | await run([ 22 | { "path": "./foreach-array.ts", args: { count }}, 23 | { "path": "./foreach-frugallist.ts", args: { count }}, 24 | { "path": "./foreach-twofield.ts", args: { count }}, 25 | ]); 26 | console.groupEnd(); 27 | 28 | count = 2; 29 | console.group(`ConsumerSet (length=${count})`) 30 | await run([ 31 | { "path": "./foreach-array.ts", args: { count }}, 32 | { "path": "./foreach-frugallist.ts", args: { count }}, 33 | { "path": "./foreach-twofield.ts", args: { count }}, 34 | ]); 35 | console.groupEnd(); 36 | })(); 37 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/frugallist", 3 | "version": "0.0.0-alpha.5", 4 | "main": "dist/index.js", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/microsoft/tiny-calc.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "bench": "cd bench && ts-node index.ts", 14 | "build": "tsc", 15 | "clean": "rimraf ./dist *.build.log", 16 | "dev": "npm run build -- --watch", 17 | "lint": "eslint --ext=ts --format visualstudio src", 18 | "test": "mocha -r ts-node/register test/**/*.spec.ts" 19 | }, 20 | "devDependencies": { 21 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 22 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 23 | "@types/mocha": "^8.0.4", 24 | "@types/node": "^14.14.10", 25 | "best-random": "^1.0.3", 26 | "eslint": "^7.13.0", 27 | "hotloop": "^1.2.0", 28 | "mocha": "^8.2.1", 29 | "rimraf": "^3.0.2", 30 | "ts-node": "^9.0.0", 31 | "typescript": "^4.0.5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/src/forEach.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { FrugalList, FrugalListItem } from "./types"; 7 | 8 | export function FrugalList_forEach(self: FrugalList, callback: (value: FrugalListItem) => void): void { 9 | if (self !== undefined) { 10 | if (Array.isArray(self)) { 11 | for (let index = 0; index < self.length; index++) { 12 | callback(self[index]); 13 | } 14 | } else { 15 | callback(/* value: */ self); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/src/get.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { FrugalList, FrugalListItem } from "./types"; 7 | 8 | export function FrugalList_get(self: FrugalList, index: number): FrugalListItem | undefined { 9 | return self === undefined 10 | ? undefined 11 | : Array.isArray(self) 12 | ? self[index] 13 | : index === 0 14 | ? self 15 | : undefined; 16 | } 17 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { FrugalList, FrugalListItem } from "./types"; 7 | export { FrugalList_forEach } from "./forEach"; 8 | export { FrugalList_get } from "./get"; 9 | export { FrugalList_length } from "./length"; 10 | export { FrugalList_push } from "./push"; 11 | export { FrugalList_removeFirst } from "./removeFirst"; 12 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/src/length.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { FrugalList } from "./types"; 7 | 8 | export function FrugalList_length(self: FrugalList): number { 9 | return self === undefined 10 | ? 0 11 | : Array.isArray(self) 12 | ? self.length 13 | : 1; 14 | } 15 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/src/push.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { FrugalList, FrugalListItem } from "./types"; 7 | 8 | export function FrugalList_push(self: FrugalList, value: FrugalListItem): FrugalList { 9 | if (self === undefined) { 10 | return value; 11 | } 12 | 13 | if (Array.isArray(self)) { 14 | self.push(value); 15 | return self; 16 | } else { 17 | return [self, value]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/src/removeFirst.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { FrugalList, FrugalListItem } from "./types"; 7 | 8 | export function FrugalList_removeFirst(self: FrugalList, value: FrugalListItem): FrugalList { 9 | if (self === value) { 10 | return undefined; 11 | } 12 | 13 | if (Array.isArray(self)) { 14 | const index = self.indexOf(value); 15 | if (index >= 0) { 16 | self.splice(/* start: */ index, /* deleteCount: */ 1); 17 | } 18 | } 19 | 20 | return self; 21 | } 22 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/src/types.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | /** 7 | * Must not store the value 'undefined' or any Array type inside a FrugalList. 8 | */ 9 | export type FrugalListItem = Exclude>; // eslint-disable-line @typescript-eslint/no-explicit-any 10 | 11 | export type FrugalList = undefined | FrugalListItem | Array>; 12 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/test/frugallist.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | import { TestFixture } from "./testfixture"; 8 | 9 | describe("FrugalList", () => { 10 | const values = [0, 1, 2]; 11 | let list: TestFixture; 12 | 13 | beforeEach(() => { 14 | list = new TestFixture(); 15 | }); 16 | 17 | it("push", () => { 18 | for (const value of values) { 19 | list.push(value); 20 | } 21 | }); 22 | 23 | it("remove in order", () => { 24 | for (const value of values) { 25 | list.push(value); 26 | } 27 | 28 | for (const value of values) { 29 | list.removeFirst(value); 30 | } 31 | }); 32 | 33 | it("remove in reverse order", () => { 34 | for (const value of values) { 35 | list.push(value); 36 | } 37 | 38 | const reversed = values.slice(0).reverse(); 39 | for (const value of reversed) { 40 | list.removeFirst(value); 41 | } 42 | }); 43 | 44 | it("remove with duplicates", () => { 45 | for (const value of values) { 46 | list.push(value); 47 | } 48 | 49 | for (const value of values) { 50 | list.push(value); 51 | } 52 | 53 | for (const value of values) { 54 | list.removeFirst(value); 55 | } 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/test/testfixture.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | import { strict as assert } from "assert"; 8 | import { 9 | FrugalList, 10 | FrugalList_push, 11 | FrugalListItem, 12 | FrugalList_removeFirst, 13 | FrugalList_forEach, 14 | FrugalList_length, 15 | FrugalList_get 16 | } from "../src"; 17 | 18 | export class TestFixture { 19 | private actual: FrugalList; 20 | private expected: FrugalListItem[] = []; 21 | 22 | public push(item: FrugalListItem): void { 23 | this.actual = FrugalList_push(this.actual, item); 24 | this.expected.push(item); 25 | 26 | this.vet(); 27 | } 28 | 29 | public removeFirst(item: FrugalListItem): void { 30 | this.actual = FrugalList_removeFirst(this.actual, item); 31 | const index = this.expected.indexOf(item); 32 | if (index >= 0) { 33 | this.expected.splice(/* start: */ index, /* deleteCount: */ 1); 34 | } 35 | 36 | this.vet(); 37 | } 38 | 39 | private vet() { 40 | const actualLen = FrugalList_length(this.actual); 41 | assert.equal(actualLen, this.expected.length, 42 | "FrugalList_length() must return number of contained items."); 43 | 44 | for (let i = 0; i < actualLen; i++) { 45 | assert.deepEqual(FrugalList_get(this.actual, i), this.expected[i], 46 | "FrugalList_get() must return item at specified 'index'."); 47 | } 48 | 49 | const actual: FrugalListItem[] = []; 50 | 51 | FrugalList_forEach(this.actual, (item) => { 52 | actual.push(item); 53 | }); 54 | 55 | assert.deepEqual(actual, this.expected, 56 | "FrugalList_forEach() must enumerate contained items in order."); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/common/datastructures/frugallist/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/bench/handletable-get-set-x256.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { HandleTable, Handle } from "../src"; 7 | import { benchmark } from "hotloop"; 8 | 9 | const table = new HandleTable(); 10 | const handles: Handle[] = []; 11 | 12 | for (let i = 0; i < 256; i++) { 13 | handles.push(table.add(i)); 14 | } 15 | 16 | benchmark(`HandleTable.get()/.set() x256`, () => { 17 | let sum = 0; 18 | 19 | for (const handle of handles) { 20 | const next = table.get(handle) + sum; 21 | table.set(handle, next); 22 | sum += next; 23 | } 24 | 25 | return sum; 26 | }); 27 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/bench/handletable-get-x256.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { HandleTable, Handle } from "../src"; 7 | import { benchmark } from "hotloop"; 8 | 9 | const table = new HandleTable(); 10 | const handles: Handle[] = []; 11 | 12 | for (let i = 0; i < 256; i++) { 13 | handles.push(table.add(i)); 14 | } 15 | 16 | benchmark(`HandleTable.get() x256`, () => { 17 | let sum = 0; 18 | 19 | for (const handle of handles) { 20 | sum += table.get(handle); 21 | } 22 | 23 | return sum; 24 | }); 25 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/bench/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { run } from "hotloop"; 7 | 8 | run([ 9 | { "path": "./handletable-get-x256.ts" }, 10 | { "path": "./handletable-get-set-x256.ts" }, 11 | { "path": "./map-get-x256.ts" }, 12 | { "path": "./map-get-x256-uuid.ts" }, 13 | ]).catch(console.error); 14 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/bench/map-get-x256-uuid.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { benchmark } from "hotloop"; 7 | import { uuid } from "./uuid"; 8 | 9 | const map = new Map(); 10 | const keys: string[] = []; 11 | 12 | for (let i = 0; i < 256; i++) { 13 | const key = uuid(); 14 | keys.push(key); 15 | map.set(key, i); 16 | } 17 | 18 | benchmark(`Map.get() x256`, () => { 19 | let sum = 0; 20 | 21 | for (const key of keys) { 22 | sum += map.get(key) as number; 23 | } 24 | 25 | return sum; 26 | }); 27 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/bench/map-get-x256.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { benchmark } from "hotloop"; 7 | 8 | const map = new Map(); 9 | const keys: number[] = []; 10 | 11 | for (let i = 0; i < 256; i++) { 12 | keys.push(i); 13 | map.set(i, i); 14 | } 15 | 16 | benchmark(`Map.get() x256`, () => { 17 | let sum = 0; 18 | 19 | for (const key of keys) { 20 | sum += map.get(key) as number; 21 | } 22 | 23 | return sum; 24 | }); 25 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/bench/uuid.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { randomFillSync } from "crypto"; 7 | 8 | // Precompute u8 -> 2 digit hex string lookup table. Per spec, must be lower-case. 9 | const h: string[] = new Array(256) 10 | .fill(0) 11 | .map((_, i) => i.toString(16).padStart(2, "0")); 12 | 13 | // Reusable 16b buffer 14 | const b = new Uint8Array(16); 15 | 16 | export function uuid(): string { 17 | randomFillSync(b); 18 | 19 | // https://tools.ietf.org/html/rfc4122 20 | /* eslint-disable no-bitwise */ 21 | b[6] = (b[6] & 0x0f) | 0x40; // Version 22 | b[8] = (b[8] & 0x3f) | 0x80; // Variant 23 | /* eslint-enable no-bitwise */ 24 | 25 | // 8-4-4-4-12 26 | return `${h[b[0]]}${h[b[1]]}${h[b[2]]}${h[b[3]]}-${h[b[4]]}${h[b[5]]}-${h[b[6]]}${h[b[7]]}-${h[b[8]]}${h[b[9]]}-${h[b[10]]}${h[b[11]]}${h[b[12]]}${h[b[13]]}${h[b[14]]}${h[b[15]]}`; 27 | } 28 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/handletable", 3 | "version": "0.0.0-alpha.5", 4 | "main": "dist/index.js", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/microsoft/tiny-calc.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "bench": "cd bench && ts-node index.ts", 14 | "build": "tsc", 15 | "clean": "rimraf ./dist *.build.log", 16 | "dev": "npm run build -- --watch", 17 | "lint": "eslint --ext=ts --format visualstudio src", 18 | "test": "mocha -r ts-node/register test/**/*.spec.ts" 19 | }, 20 | "devDependencies": { 21 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 22 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 23 | "@types/mocha": "^8.0.4", 24 | "@types/node": "^14.14.10", 25 | "best-random": "^1.0.3", 26 | "eslint": "^7.13.0", 27 | "hotloop": "^1.2.0", 28 | "mocha": "^8.2.1", 29 | "rimraf": "^3.0.2", 30 | "ts-node": "^9.0.0", 31 | "typescript": "^4.0.5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export const enum Handle { 7 | none = 0, 8 | } 9 | 10 | /** 11 | * A handle table provides a fast mapping from an integer `handle` to a value `T`. 12 | */ 13 | export class HandleTable { 14 | // Note: the first slot of the 'handles' array is reserved to store the pointer to the first 15 | // free handle. We initialize this slot with a pointer to slot '1', which will cause 16 | // us to delay allocate the following slot in the array on the first allocation. 17 | public constructor(private readonly handles: (T | number)[] = [1]) { } 18 | 19 | public clear(): void { 20 | // Restore the HandleTable's initial state by deleting all items in the handles array 21 | // and then re-inserting the value '1' in the 0th slot. (See comment at `handles` decl 22 | // for explanation.) 23 | this.handles.splice(0, this.handles.length, 1); 24 | } 25 | 26 | /** 27 | * Allocate a slot in the handle table, set the slot to the given 'value', and 28 | * return the associated handle. 29 | * 30 | * Call 'delete()' when you are finished with the slot to recycle the handle. 31 | */ 32 | public add(value: T): THandle { 33 | const free = this.next; 34 | this.next = (this.handles[free] as THandle) ?? (free + 1); 35 | this.handles[free] = value; 36 | return free; 37 | } 38 | 39 | /** 40 | * Returns the given handle to the free list. 41 | */ 42 | public delete(handle: THandle): void { 43 | this.handles[handle] = this.next; 44 | this.next = handle; 45 | } 46 | 47 | /** 48 | * Get the value `T` associated with the given handle, if any. 49 | */ 50 | public get(handle: THandle): T { 51 | return this.handles[handle] as T; 52 | } 53 | 54 | /** 55 | * Set the value `T` associated with the given handle. 56 | */ 57 | public set(handle: THandle, value: T): void { 58 | this.handles[handle] = value; 59 | } 60 | 61 | // Private helpers to get/set the head of the free list, which is stored in the 0th slot 62 | // of the handle array. 63 | private get next() { return this.handles[0] as THandle; } 64 | private set next(handle: THandle) { this.handles[0] = handle; } 65 | 66 | public toJSON(): (T | number)[] { return this.handles; } 67 | } 68 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/test/handletable.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | import { strict as assert } from "assert"; 8 | import { TestFixture } from "./testfixture"; 9 | import { Handle } from "../src"; 10 | 11 | describe("HandleTable", () => { 12 | const testValues = ["1", "2", "3"]; 13 | let table: TestFixture; 14 | 15 | beforeEach(() => { 16 | table = new TestFixture(); 17 | }) 18 | 19 | it("add x 1 / get x 1", () => { 20 | const h1 = table.add("1"); 21 | 22 | assert.equal(table.get(h1), "1"); 23 | }); 24 | 25 | for (let count = 1; count <= testValues.length; count++) { 26 | const values = testValues.slice(count); 27 | 28 | it(`add / get / delete x ${count}`, () => { 29 | for (const value of values) { 30 | const handle = table.add(value); 31 | assert.equal(table.get(handle), value); 32 | table.delete(handle); 33 | } 34 | }); 35 | } 36 | 37 | for (let count = 2; count <= testValues.length; count++) { 38 | const values = testValues.slice(count); 39 | 40 | it(`add x ${count} / get x ${count} / delete x ${count}`, () => { 41 | const handles: Handle[] = []; 42 | 43 | for (const value of values) { 44 | const handle = table.add(value); 45 | handles.push(handle); 46 | assert.equal(table.get(handle), value); 47 | } 48 | 49 | for (let i = 0; i < handles.length; i++) { 50 | assert.equal(table.get(handles[i]), values[i]); 51 | } 52 | 53 | for (let i = handles.length - 1; i >= 0; i--) { 54 | const handle = handles[i]; 55 | assert.equal(table.get(handle), values[i]); 56 | table.delete(handle); 57 | } 58 | }); 59 | } 60 | }); 61 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/test/handletable.stress.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | import { TestFixture } from "./testfixture"; 8 | import { Monkey } from "../../../test/monkey/src"; 9 | 10 | function hex(n: number) { 11 | return n.toString(16).padStart(8, '0'); 12 | } 13 | 14 | describe("HandleTable (stress)", () => { 15 | function stress(seed: number, iterations: number, targetSize: number) { 16 | it(`(seed=0x${hex(seed)}, iterations=${iterations}, targetSize=${targetSize})`, () => { 17 | const table = new TestFixture(); 18 | const monkey = new Monkey(seed); 19 | 20 | for (let i = 0; i < iterations; i++) { 21 | monkey.choose([{ 22 | scale: targetSize, 23 | action: () => { 24 | table.add(monkey.chooseString(4)); 25 | } 26 | }, { 27 | scale: Math.max(table.usedCount - (targetSize - 1), 0), 28 | action: () => { 29 | table.delete(monkey.chooseItem(table.usedHandles)); 30 | } 31 | }]) 32 | } 33 | }); 34 | } 35 | 36 | stress(/* seed: */ 0xd3ff477c, /* iterations: */ 1000, /* targetSize: */ 1); 37 | stress(/* seed: */ 0x07607759, /* iterations: */ 1000, /* targetSize: */ 10); 38 | stress(/* seed: */ 0x8d7dc8d7, /* iterations: */ 1000, /* targetSize: */ 100); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/common/datastructures/handletable/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/bench/heap-push-ascending.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { Heap } from "../src"; 7 | import { benchmark, getTestArgs } from "hotloop"; 8 | 9 | const heap = new Heap((left, right) => left - right); 10 | const { count } = getTestArgs(); 11 | 12 | benchmark(`Heap.push() x ${count} (Ascending)`, () => { 13 | for (let i = 0; i < count; i++) { 14 | heap.push(i); 15 | } 16 | 17 | heap.clear(); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/bench/heap-push-descending.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { Heap } from "../src"; 7 | import { benchmark, getTestArgs } from "hotloop"; 8 | 9 | const heap = new Heap((left, right) => left - right); 10 | const { count } = getTestArgs(); 11 | 12 | benchmark(`Heap.push() x ${count} (Descending)`, () => { 13 | for (let i = count; i > 0; i--) { 14 | heap.push(i); 15 | } 16 | 17 | heap.clear(); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/bench/heap-push-pop-ascending.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { Heap } from "../src"; 7 | import { benchmark, getTestArgs } from "hotloop"; 8 | 9 | const heap = new Heap((left, right) => left - right); 10 | const { count } = getTestArgs(); 11 | let sum = 0; 12 | 13 | benchmark(`Heap.push() x ${count} / Heap.pop() x ${count} (Ascending)`, () => { 14 | for (let i = 0; i < count; i++) { 15 | heap.push(i); 16 | } 17 | 18 | let top: number | undefined; 19 | 20 | // eslint-disable-next-line no-cond-assign 21 | while ((top = heap.pop()) !== undefined) { 22 | sum += top; 23 | } 24 | 25 | return sum; 26 | }); 27 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/bench/heap-push-pop-descending.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { Heap } from "../src"; 7 | import { benchmark, getTestArgs } from "hotloop"; 8 | 9 | const heap = new Heap((left, right) => left - right); 10 | const { count } = getTestArgs(); 11 | let sum = 0; 12 | 13 | benchmark(`Heap.push() x ${count} / Heap.pop() x ${count} (Descending)`, () => { 14 | for (let i = count; i > 0; i--) { 15 | heap.push(i); 16 | } 17 | 18 | let top: number | undefined; 19 | 20 | // eslint-disable-next-line no-cond-assign 21 | while ((top = heap.pop()) !== undefined) { 22 | sum += top; 23 | } 24 | 25 | return sum; 26 | }); 27 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/bench/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { run } from "hotloop"; 7 | 8 | const count = 256 * 256; 9 | 10 | run([ 11 | { "path": "./heap-push-ascending.ts", args: { count }}, 12 | { "path": "./heap-push-descending.ts", args: { count }}, 13 | { "path": "./heap-push-pop-ascending.ts", args: { count }}, 14 | { "path": "./heap-push-pop-descending.ts", args: { count }}, 15 | ]).catch(console.error); 16 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/heap", 3 | "version": "0.0.0-alpha.5", 4 | "main": "dist/index.js", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/microsoft/tiny-calc.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "bench": "cd bench && ts-node index.ts", 14 | "build": "tsc", 15 | "clean": "rimraf ./dist *.build.log", 16 | "dev": "npm run build -- --watch", 17 | "lint": "eslint --ext=ts --format visualstudio src", 18 | "test": "mocha -r ts-node/register test/**/*.spec.ts" 19 | }, 20 | "dependencies": { 21 | "@tiny-calc/types": "0.0.0-alpha.5" 22 | }, 23 | "devDependencies": { 24 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 25 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 26 | "@types/mocha": "^8.0.4", 27 | "@types/node": "^14.14.10", 28 | "best-random": "^1.0.3", 29 | "eslint": "^7.13.0", 30 | "hotloop": "^1.2.0", 31 | "mocha": "^8.2.1", 32 | "rimraf": "^3.0.2", 33 | "ts-node": "^9.0.0", 34 | "typescript": "^4.0.5" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { Heap } from "./heap"; 7 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/test/heap.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | import { strict as assert } from "assert"; 8 | import { TestFixture } from "./testfixture"; 9 | 10 | describe("Heap", () => { 11 | const v = new Array(8) 12 | .fill(undefined) 13 | .map((value, index) => { 14 | return { value: index } 15 | }); 16 | 17 | const compareFn = (left: { value: number }, right: { value: number }) => left.value - right.value; 18 | 19 | let heap: TestFixture<{ value: number}>; 20 | 21 | beforeEach(() => { 22 | heap = new TestFixture<{ value: number }>(compareFn); 23 | }) 24 | 25 | it("peek empty", () => { 26 | assert.equal(heap.peek(), undefined); 27 | }); 28 | 29 | describe("push/pop", () => { 30 | function test(order: number[]) { 31 | const input = order.map((value) => v[value].value); 32 | const output = order.sort((left, right) => left - right); 33 | 34 | it(`${JSON.stringify(input)} -> ${JSON.stringify(output)}`, () => { 35 | for (const index of order) { 36 | heap.push(v[index]); 37 | } 38 | 39 | for (const expected of output) { 40 | assert.equal(heap.peek()!.value, expected); 41 | assert.equal(heap.pop()!.value, expected); 42 | } 43 | 44 | assert.equal(heap.peek(), undefined); 45 | assert.equal(heap.pop(), undefined); 46 | }); 47 | } 48 | 49 | // test([0]); 50 | // test([0, 1]); 51 | // test([1, 0]); 52 | test([0, 1, 2, 3, 4, 5, 6]); 53 | test([6, 5, 4, 3, 2, 1, 0]); 54 | }); 55 | 56 | it("orders initial items", () => { 57 | heap = new TestFixture<{ value: number }>(compareFn, [ 58 | v[6], v[5], v[6], v[5], v[4], v[3], v[0], v[4], v[3], v[2], v[2], v[1], v[1], v[0] 59 | ]); 60 | for (let i = 0; i < 6; i++) { 61 | assert.equal(heap.pop(), v[i]); 62 | assert.equal(heap.pop(), v[i]); 63 | } 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/test/heap.stress.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | import { TestFixture } from "./testfixture"; 8 | import { Monkey } from "../../../test/monkey/src"; 9 | 10 | function hex(n: number) { 11 | return n.toString(16).padStart(8, '0'); 12 | } 13 | 14 | describe("Heap (stress)", () => { 15 | function stress(seed: number, iterations: number, targetSize: number) { 16 | it(`(seed=0x${hex(seed)}, iterations=${iterations}, targetSize=${targetSize})`, () => { 17 | const monkey = new Monkey(seed); 18 | 19 | const heap = new TestFixture( 20 | (left, right) => left - right, 21 | new Array(targetSize) 22 | .fill(0) 23 | .map(() => monkey.chooseInt(0, targetSize)) 24 | ); 25 | 26 | while (heap.pop()); 27 | 28 | while (iterations--) { 29 | monkey.choose([{ 30 | scale: targetSize, 31 | action: () => { 32 | heap.push(monkey.chooseInt(0, targetSize * 2)); 33 | } 34 | }, { 35 | scale: Math.max(heap.length - (targetSize - 1), 0), 36 | action: () => { 37 | heap.pop() 38 | } 39 | }]); 40 | } 41 | 42 | while (heap.pop()); 43 | }); 44 | } 45 | 46 | stress(/* seed: */ 0xd3ff477c, /* iterations: */ 1000, /* targetSize: */ 10); 47 | stress(/* seed: */ 0x07607759, /* iterations: */ 1000, /* targetSize: */ 100); 48 | }); 49 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/test/testfixture.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | import { strict as assert } from "assert"; 8 | import { Heap } from "../src"; 9 | import { CompareFunction } from "@tiny-calc/types"; 10 | 11 | export class TestFixture { 12 | private readonly actual: Heap; 13 | private readonly expected: T[] = []; 14 | 15 | public constructor( 16 | private readonly compareFn: CompareFunction, 17 | items: T[] = [] 18 | ) { 19 | this.actual = new Heap(compareFn, items); 20 | this.expected = items.slice().sort(this.compareFn); 21 | 22 | this.vet(); 23 | } 24 | 25 | public push(value: T): void { 26 | this.actual.push(value); 27 | this.expected.push(value); 28 | this.expected.sort(this.compareFn); 29 | 30 | this.vet(); 31 | } 32 | 33 | public peek(): T | undefined { 34 | const actual = this.actual.peek(); 35 | const expected = this.expected[0]; 36 | 37 | assert.equal(actual, expected); 38 | this.vet(); 39 | 40 | return actual; 41 | } 42 | 43 | public pop(): T | undefined { 44 | const actual = this.actual.pop(); 45 | const expected = this.expected.shift(); 46 | 47 | assert.equal(actual, expected); 48 | this.vet(); 49 | 50 | return actual; 51 | } 52 | 53 | public get length(): number { 54 | const actual = this.actual.length; 55 | const expected = this.expected.length; 56 | 57 | assert.equal(actual, expected); 58 | this.vet(); 59 | 60 | return actual; 61 | } 62 | 63 | private vet() { 64 | assert.equal(this.actual.length, this.expected.length); 65 | assert.equal(this.actual.peek(), this.expected[0]); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/common/datastructures/heap/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/common/datastructures/heap/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/common/scheduler/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/common/scheduler/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/common/scheduler/bench/coalesce/async.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { coalesce, done } from "../../src"; 7 | import { benchmarkAsync } from "hotloop"; 8 | 9 | let complete: { resolve: () => void }; 10 | 11 | const fn = coalesce( 12 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 13 | (callback) => { done.then(callback); }, 14 | () => { complete.resolve(); }, 15 | ); 16 | 17 | benchmarkAsync(`coalesce (async void)`, (deferred) => { 18 | complete = deferred; 19 | fn(); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/common/scheduler/bench/coalesce/promise-number.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { coalesce, done } from "../../src"; 7 | import { benchmarkPromise } from "hotloop"; 8 | 9 | let counter = 0; 10 | 11 | const fn: () => Promise = coalesce( 12 | (callback) => done.then(callback), 13 | () => counter++, 14 | ); 15 | 16 | benchmarkPromise(`coalesce (Promise)`, async () => { 17 | await fn(); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/common/scheduler/bench/coalesce/promise-void.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { coalesce, done } from "../../src"; 7 | import { benchmarkPromise } from "hotloop"; 8 | 9 | const fn = coalesce( 10 | (callback) => done.then(callback), 11 | () => { }, 12 | ); 13 | 14 | benchmarkPromise(`coalesce (Promise)`, fn); 15 | -------------------------------------------------------------------------------- /packages/common/scheduler/bench/coalesce/prompt.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { coalesce } from "../../src"; 7 | import { benchmark } from "hotloop"; 8 | 9 | const fn = coalesce( 10 | (callback) => callback(), 11 | () => {}, 12 | ); 13 | 14 | benchmark(`coalesce (prompt void)`, () => { 15 | fn(); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/common/scheduler/bench/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { run } from "hotloop"; 7 | 8 | run([ 9 | { "path": "./coalesce/prompt.ts" }, 10 | { "path": "./coalesce/async.ts" }, 11 | { "path": "./coalesce/promise-void.ts" }, 12 | { "path": "./coalesce/promise-number.ts" }, 13 | ]).catch(console.error); 14 | -------------------------------------------------------------------------------- /packages/common/scheduler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/scheduler", 3 | "version": "0.0.0-alpha.5", 4 | "main": "dist/index.js", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/microsoft/tiny-calc.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "bench": "cd bench && ts-node index.ts", 14 | "build": "tsc", 15 | "clean": "rimraf ./dist *.build.log", 16 | "dev": "npm run build -- --watch", 17 | "lint": "eslint --ext=ts --format visualstudio src", 18 | "test": "mocha -r ts-node/register test/**/*.spec.ts" 19 | }, 20 | "devDependencies": { 21 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 22 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 23 | "@types/mocha": "^8.0.4", 24 | "@types/node": "^14.14.10", 25 | "best-random": "^1.0.3", 26 | "eslint": "^7.13.0", 27 | "hotloop": "^1.2.0", 28 | "mocha": "^8.2.1", 29 | "rimraf": "^3.0.2", 30 | "ts-node": "^9.0.0", 31 | "typescript": "^4.0.5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/common/scheduler/src/coalesce.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export const done: Promise = Promise.resolve(); 7 | 8 | export function coalesce( 9 | queue: (callback: () => U) => Exclude, // eslint-disable-line @rushstack/no-new-null 10 | callback: () => U 11 | ): () => T { 12 | /* eslint-disable @rushstack/no-null */ 13 | let pending: T | null = null; 14 | 15 | return () => { 16 | if (pending === null) { 17 | pending = queue(() => { 18 | // Reset `pending` before invoking `callback()` in case `callback()` throws, 19 | // in which case the returned function would otherwise be permanently stuck 20 | // in the pending state. 21 | pending = null; 22 | 23 | return callback(); 24 | }); 25 | } 26 | 27 | return pending; 28 | } 29 | /* eslint-enable @rushstack/no-null */ 30 | } 31 | -------------------------------------------------------------------------------- /packages/common/scheduler/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { done, coalesce } from "./coalesce"; 7 | -------------------------------------------------------------------------------- /packages/common/scheduler/test/coalesce.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | import { strict as assert } from "assert"; 8 | import { done, coalesce } from "../src"; 9 | 10 | describe("coalesce()", () => { 11 | it("must coalesce repeated calls until dispatched", async () => { 12 | let count = 0; 13 | 14 | const fn: () => Promise = coalesce( 15 | (callback) => done.then(callback), 16 | () => { ++count; } 17 | ); 18 | 19 | assert.equal(count, 0); 20 | 21 | await Promise.all([fn(), fn()]); 22 | assert.equal(count, 1); 23 | 24 | await done; 25 | assert.equal(count, 1); 26 | }); 27 | 28 | it("must return values from callback and dispatcher", async () => { 29 | let count = 0; 30 | 31 | const fn = coalesce( 32 | (callback) => done.then(callback), 33 | () => ++count 34 | ); 35 | 36 | assert.deepEqual(await Promise.all([fn(), fn()]), [1, 1]); 37 | }); 38 | 39 | it("must resume after exception", async () => { 40 | let count = 0; 41 | 42 | const fn = coalesce( 43 | (callback) => done.then(callback), 44 | () => { 45 | if (++count === 1) { 46 | throw new Error(); 47 | } 48 | 49 | return count; 50 | } 51 | ); 52 | 53 | try { 54 | await fn(); 55 | 56 | assert.fail("First invocation of fn() must return a rejected promise."); 57 | } catch { 58 | // do nothing 59 | } 60 | 61 | assert.equal(await fn(), 2); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /packages/common/scheduler/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/common/scheduler/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/common/test/monkey/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/common/test/monkey/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/common/test/monkey/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc-test/monkey", 3 | "version": "0.0.0-alpha.5", 4 | "main": "dist/index.js", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/microsoft/tiny-calc.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "tsc", 14 | "clean": "rimraf ./dist *.build.log", 15 | "dev": "npm run build -- --watch", 16 | "lint": "eslint --ext=ts --format visualstudio src", 17 | "test": "mocha -r ts-node/register test/**/*.spec.ts" 18 | }, 19 | "devDependencies": { 20 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 21 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 22 | "@types/mocha": "^8.0.4", 23 | "@types/node": "^14.14.10", 24 | "best-random": "^1.0.3", 25 | "eslint": "^7.13.0", 26 | "hotloop": "^1.2.0", 27 | "mocha": "^8.2.1", 28 | "rimraf": "^3.0.2", 29 | "ts-node": "^9.0.0", 30 | "typescript": "^4.0.5" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/common/test/monkey/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { Random } from "best-random"; 7 | 8 | export interface IMonkeyOption { 9 | scale: number, 10 | action: () => T, 11 | } 12 | 13 | export class Monkey { 14 | private readonly rng: Random; 15 | 16 | public constructor (seed = (Math.random() * 0x100000000) >>> 0) { 17 | this.rng = new Random(seed); 18 | } 19 | 20 | public choose(options: IMonkeyOption[]): T { 21 | let choice = this.chooseInt( 22 | /* start: */ 0, 23 | /* end: */ options.reduce((accumulator, option) => accumulator + option.scale, 0)); 24 | 25 | for (const option of options) { 26 | choice -= option.scale; 27 | 28 | if (choice < 0) { 29 | return option.action(); 30 | } 31 | } 32 | 33 | throw new Error(`'scale' of all options must sum to exactly 1.0, but got '${ 34 | options.reduce((accum, current) => accum + current.scale, 0) 35 | }'`); 36 | } 37 | 38 | public chooseInt(start: number, end: number): number { 39 | // eslint-disable-next-line no-bitwise 40 | return ((this.rng.float64() * (end - start)) | 0) + start; 41 | } 42 | 43 | public chooseItem(items: T[]): T { 44 | return items[this.chooseInt(/* start: */ 0, /* end: */ items.length)]; 45 | } 46 | 47 | public chooseString(length: number, alphabet = " !\"$%&'()*+,/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[|]^_`abcdefghijklmnopqrstuvwxyz{}~"): string { 48 | let s = ""; 49 | 50 | do { 51 | s += alphabet[this.chooseInt(0, alphabet.length)]; 52 | } while (s.length < length); 53 | 54 | return s.slice(0, length); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/common/test/monkey/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/common/test/monkey/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/micro/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/micro/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/core/micro/bench/index.ts: -------------------------------------------------------------------------------- 1 | import { run } from "hotloop"; 2 | 3 | (async () => { 4 | await run([ 5 | { "path": "./sheetlet/cached-2x2.ts" }, 6 | { "path": "./sheetlet/recalc-2x2.ts" }, 7 | ]); 8 | 9 | await run([ 10 | { "path": "./sheetlet/cached-10x10.ts" }, 11 | { "path": "./sheetlet/recalc-10x10.ts" }, 12 | ]); 13 | 14 | await run([ 15 | { "path": "./matrix/rowmajor-256x256-read-baseline.ts" }, 16 | { "path": "./matrix/rowmajor-256x256-read.ts" }, 17 | ]); 18 | })(); 19 | -------------------------------------------------------------------------------- /packages/core/micro/bench/matrix/index.ts: -------------------------------------------------------------------------------- 1 | import { benchmark } from "hotloop"; 2 | import { IMatrixReader, IMatrixWriter } from "@tiny-calc/nano/src/types"; 3 | import { consume } from "../util"; 4 | 5 | export function sumBenchmark(name: string, reader: IMatrixReader) { 6 | const { rowCount, colCount } = reader; 7 | 8 | benchmark(`Matrix (${name} ${rowCount}x${colCount}): sum row-wise`, () => { 9 | let sum = 0; 10 | 11 | for (let row = 0; row < rowCount; row++) { 12 | for (let col = 0; col < colCount; col++) { 13 | sum += reader.getCell(row, col); 14 | } 15 | } 16 | 17 | consume(sum); 18 | }); 19 | } 20 | 21 | export function fill( 22 | reader: IMatrixReader, 23 | writer: IMatrixWriter, 24 | rowStart = 0, 25 | colStart = 0, 26 | rowCount = reader.rowCount - rowStart, 27 | colCount = reader.colCount - colStart, 28 | value = (row: number, col: number) => row * rowCount + col 29 | ) { 30 | const rowEnd = rowStart + rowCount; 31 | const colEnd = colStart + colCount; 32 | 33 | for (let row = rowStart; row < rowEnd; row++) { 34 | for (let col = colStart; col < colEnd; col++) { 35 | writer.setCell(row, col, value(row, col) as any); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/micro/bench/matrix/rowmajor-256x256-read-baseline.ts: -------------------------------------------------------------------------------- 1 | import { IMatrixWriter, IMatrixReader } from "@tiny-calc/nano/src/types"; 2 | import { fill, sumBenchmark } from "."; 3 | 4 | export class Array256x256 implements IMatrixWriter, IMatrixReader { 5 | private readonly cells: number[] = new Array(this.rowCount * this.colCount).fill(0); 6 | 7 | //#region IMatrixReader 8 | 9 | public readonly matrixProducer = undefined as any; 10 | 11 | public get rowCount() { return 256; } 12 | public get colCount() { return 256; } 13 | 14 | public getCell(row: number, col: number) { 15 | return this.cells[(row << 8) + col]; 16 | } 17 | 18 | //#endregion IMatrixReader 19 | 20 | //#region IMatrixWriter 21 | 22 | public setCell(row: number, col: number, value: number) { 23 | this.cells[(row << 8) + col] = value; 24 | } 25 | 26 | //#endregion IMatrixWriter 27 | } 28 | 29 | const matrix = new Array256x256(); 30 | 31 | fill(matrix, matrix); 32 | sumBenchmark("Baseline", matrix); 33 | -------------------------------------------------------------------------------- /packages/core/micro/bench/matrix/rowmajor-256x256-read.ts: -------------------------------------------------------------------------------- 1 | import { RowMajorMatrix, DenseVector } from "../../src"; 2 | import { nullConsumer } from "../util"; 3 | import { sumBenchmark, fill } from "."; 4 | 5 | const rows = new DenseVector(); 6 | const cols = new DenseVector(); 7 | const matrix = new RowMajorMatrix(rows, cols); 8 | rows.splice(/* start: */ 0, /* deleteCount: */ 0, /* insertCount: */ 256); 9 | cols.splice(/* start: */ 0, /* deleteCount: */ 0, /* insertCount: */ 256); 10 | 11 | fill(matrix, matrix); 12 | sumBenchmark("RowMajor", matrix.openMatrix(nullConsumer)); 13 | -------------------------------------------------------------------------------- /packages/core/micro/bench/profile.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | // Runs RecalcTest in a tight loop outside of the Benchmark suite to make profiling 7 | // analysis a bit easier. 8 | 9 | import { makeBenchmark, evalSheet } from "../test/sheets"; 10 | 11 | function recalcTest(size: number) { 12 | const { sheet, setAt } = makeBenchmark(size); 13 | evalSheet(sheet, size); 14 | console.time("profile"); 15 | for (let i = 0; i < 1000; i++) { 16 | setAt(0, 0, i); 17 | evalSheet(sheet, size); 18 | } 19 | console.timeEnd("profile"); 20 | } 21 | 22 | recalcTest(10); 23 | -------------------------------------------------------------------------------- /packages/core/micro/bench/sheetlet/cached-10x10.ts: -------------------------------------------------------------------------------- 1 | import { cachedTest } from "."; 2 | 3 | cachedTest(10); -------------------------------------------------------------------------------- /packages/core/micro/bench/sheetlet/cached-2x2.ts: -------------------------------------------------------------------------------- 1 | import { cachedTest } from "."; 2 | 3 | cachedTest(2); -------------------------------------------------------------------------------- /packages/core/micro/bench/sheetlet/index.ts: -------------------------------------------------------------------------------- 1 | import { benchmark } from "hotloop"; 2 | import { evalSheet, makeBenchmark } from "../../test/sheets"; 3 | 4 | // These benchmarks involve a square matrix where all cells except [0,0] are a sum 5 | // of all cells above and to the left. Changing [0,0] causes all other cells to recalculate. 6 | 7 | export function cachedTest(size: number) { 8 | const { sheet } = makeBenchmark(size); 9 | const reader = sheet.openMatrix(undefined as any); 10 | evalSheet(reader, size); 11 | benchmark(`Cached: ${size}x${size}`, () => { return evalSheet(reader, size); }); 12 | } 13 | 14 | export function recalcTest(size: number) { 15 | const { sheet, setAt } = makeBenchmark(size); 16 | evalSheet(sheet.openMatrix(undefined as any), size); 17 | 18 | let i = 1; 19 | benchmark(`Recalc: ${size}x${size}`, () => { 20 | setAt(0, 0, i = ~i); // Toggle [0,0] between 1 and -2 21 | return evalSheet(sheet.openMatrix(undefined as any), size); 22 | }); 23 | } -------------------------------------------------------------------------------- /packages/core/micro/bench/sheetlet/recalc-10x10.ts: -------------------------------------------------------------------------------- 1 | import { recalcTest } from "."; 2 | 3 | recalcTest(10); -------------------------------------------------------------------------------- /packages/core/micro/bench/sheetlet/recalc-2x2.ts: -------------------------------------------------------------------------------- 1 | import { recalcTest } from "."; 2 | 3 | recalcTest(2); -------------------------------------------------------------------------------- /packages/core/micro/bench/util.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { IMatrixConsumer } from "@tiny-calc/types"; 7 | import process from "process"; 8 | 9 | export const nullConsumer: IMatrixConsumer = { 10 | rowsChanged() { }, 11 | colsChanged() { }, 12 | cellsChanged() { }, 13 | } 14 | 15 | let count = 0; 16 | let cached: any; 17 | 18 | /** 19 | * Paranoid defense against dead code elimination. 20 | */ 21 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any 22 | export function consume(value: any): void { 23 | count++; 24 | if (count === 0) { 25 | cached = value; 26 | } 27 | } 28 | 29 | // Prevent v8's optimizer from identifying 'cached' as an unused value. 30 | process.on('exit', () => { 31 | if ((count >>> 0) === 0) { 32 | console.log(`Ignore this: ${cached}`); 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /packages/core/micro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/micro", 3 | "version": "0.0.0-alpha.5", 4 | "main": "dist/index.js", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/microsoft/tiny-calc.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "bench": "cd bench && ts-node index.ts", 14 | "build": "tsc", 15 | "clean": "rimraf ./dist *.build.log", 16 | "dev": "npm run build -- --watch", 17 | "lint": "eslint --ext=ts --format visualstudio src", 18 | "prof": "tsc --project tsconfig.prof.json && node --prof temp/bench/profile.js && node --prof-process isolate-*-v8.log > profile.log && rm isolate-*-v8.log", 19 | "test": "mocha -r ts-node/register test/**/*.spec.ts", 20 | "adjust-random": "ts-node test/random-adjust-tree.ts" 21 | }, 22 | "dependencies": { 23 | "@tiny-calc/nano": "0.0.0-alpha.5", 24 | "@tiny-calc/types": "0.0.0-alpha.5" 25 | }, 26 | "devDependencies": { 27 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 28 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 29 | "@types/mocha": "^8.0.4", 30 | "@types/node": "^14.14.10", 31 | "best-random": "^1.0.3", 32 | "eslint": "^7.13.0", 33 | "hotloop": "^1.2.0", 34 | "mocha": "^8.2.1", 35 | "rimraf": "^3.0.2", 36 | "ts-node": "^9.0.0", 37 | "typescript": "^4.0.5" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/core/micro/src/adjust-tree/arrayUtil.ts: -------------------------------------------------------------------------------- 1 | export function initArray(size: number, element: T): T[] { 2 | const array = []; 3 | for (let i = 0; i < size; i++) { 4 | array.push(element); 5 | } 6 | return array; 7 | } 8 | 9 | /** 10 | * Move the contents of arr from range `start` to `end` to the right by 11 | * `delta places`. Assumes that the caller knows the correct bounds. 12 | * 13 | * @param arr 14 | * @param start 15 | * @param end 16 | * @param delta 17 | */ 18 | export function shiftR(arr: unknown[], start: number, end: number, delta: number): void { 19 | for (let i = end + delta; i >= start + delta; i--) { 20 | arr[i] = arr[i - delta]; 21 | } 22 | } 23 | 24 | export function deleteAndShift(arr: T[], start: number, count: number, arrLength: number, empty: T): T[] { 25 | const out: T[] = []; 26 | for (let i = start; i < arrLength; i++) { 27 | const readingIdx = i + count; 28 | out.push(arr[i]); 29 | arr[i] = readingIdx < arrLength ? arr[readingIdx] : empty; 30 | } 31 | return out; 32 | } 33 | 34 | export function deleteAndShiftLossy(arr: T[], start: number, count: number, arrLength: number, empty: T): void { 35 | for (let i = start; i < arrLength; i++) { 36 | const readingIdx = i + count; 37 | arr[i] = readingIdx < arrLength ? arr[readingIdx] : empty; 38 | } 39 | } 40 | 41 | export function clearR(arr: T[], start: number, arrLength: number, empty: T): void { 42 | for (let i = start; i < arrLength; i++) { 43 | arr[i] = empty; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/core/micro/src/adjust-tree/common.ts: -------------------------------------------------------------------------------- 1 | export function assert(message: string, cond: boolean): asserts cond { 2 | if (!cond) { 3 | console.log(new Error().stack); 4 | throw message; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/micro/src/adjust-tree/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | createTree, 3 | createTreeDebug, 4 | forEachInSegmentRange, 5 | loadTree, 6 | } from "./tree"; 7 | 8 | export { 9 | AdjustTree, 10 | AdjustTreeDebug, 11 | SegmentRange, 12 | TreeConfiguration, 13 | } from "./types"; 14 | -------------------------------------------------------------------------------- /packages/core/micro/src/adjust-tree/validity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AdjustNode, 3 | NodeKind, 4 | TreeContext, 5 | } from "./types"; 6 | 7 | import { 8 | EMPTY, 9 | } from "./node"; 10 | 11 | export function validate(existing: boolean, test: boolean, message: string): boolean { 12 | if (!test) { 13 | console.log(new Error().stack); 14 | } 15 | return existing && test; 16 | } 17 | 18 | export function validateNode(context: TreeContext, node: AdjustNode, minSize: number, height: number): [boolean, number, number] { 19 | let valid = true; 20 | valid = validate(valid, node.size >= minSize, "min size"); 21 | let keySize = node.lengths.indexOf(EMPTY); 22 | keySize = keySize === -1 ? node.lengths.length : keySize; 23 | valid = validate(valid, keySize === node.size, "length size in sync with cache"); 24 | if (node.kind === NodeKind.Interior) { 25 | let childSize = node.segments.indexOf(undefined!); 26 | childSize = childSize === -1 ? node.segments.length : childSize; 27 | valid = validate(valid, node.lengths.length === context.interiorLengthSize, "length array has correct size"); 28 | valid = validate(valid, node.segments.length === context.interiorChidrenSize, "children array has correct size"); 29 | let totalLen = 0; 30 | let childHeight: number | undefined; 31 | for (let i = 0; i < keySize; i++) { 32 | const child = node.segments[i]!; 33 | const [ok, len, h] = validateNode(context, child, minSize, height + 1); 34 | if (childHeight === undefined) { 35 | childHeight = h; 36 | } 37 | else { 38 | valid = validate(valid, h === childHeight, "subnode height"); 39 | } 40 | totalLen += len; 41 | valid = validate(valid, ok, "subnode validation"); 42 | valid = validate(valid, len === node.lengths[i], "sublengths in parent are correct"); 43 | } 44 | return [valid, totalLen, childHeight!]; 45 | } 46 | valid = validate(valid, node.lengths.length === context.leafLengthSize, "leaf length array is fundamentally correct size"); 47 | valid = validate(valid, node.segments.length === context.leafSegmentSize, "leaf segment array is fundamentally correct size"); 48 | valid = validate(valid, node.segments[node.size - 1] !== context.emptySegment, "leaf segment array does not empty with empty segment") 49 | let totalLen = 0; 50 | for (let i = 0; i < keySize; i++) { 51 | const len = node.lengths[i]!; 52 | totalLen += len; 53 | } 54 | return [valid, totalLen, height]; 55 | } 56 | -------------------------------------------------------------------------------- /packages/core/micro/src/binder.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { pointToKey } from "./key"; 7 | import { createGrid } from "./matrix"; 8 | import { Binder } from "./types"; 9 | 10 | const newSet = () => new Set(); 11 | 12 | export function initBinder(): Binder { 13 | let grid = createGrid>(); 14 | const volatile = new Set(); 15 | return { 16 | getVolatile: () => volatile, 17 | bindCell(fromRow: number, fromCol: number, toRow: number, toCol: number) { 18 | const s = grid.readOrWrite(fromRow, fromCol, newSet) 19 | if (s) { 20 | s.add(pointToKey(toRow, toCol)); 21 | } 22 | }, 23 | getDependents(row: number, col: number) { 24 | return grid.getCell(row, col); 25 | }, 26 | clearDependents(row: number, col: number) { 27 | const s = grid.getCell(row, col); 28 | if (s) { 29 | s.clear(); 30 | } 31 | }, 32 | clear: () => { 33 | grid = createGrid(); 34 | }, 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /packages/core/micro/src/cell.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { 7 | Primitive, 8 | } from "@tiny-calc/nano"; 9 | 10 | import { 11 | CalcFlags, 12 | CalcState, 13 | Cell, 14 | CellValue, 15 | FormulaCell, 16 | Value, 17 | ValueCell 18 | } from "./types"; 19 | 20 | import { strict as assert } from "assert"; 21 | 22 | function valueCell(content: Primitive): ValueCell { 23 | return { state: CalcState.Clean, content }; 24 | } 25 | 26 | export function isFormulaCell(cell: Cell): cell is FormulaCell { 27 | return "formula" in cell; 28 | } 29 | 30 | function makeValueCell(value: Primitive) { 31 | let content: Primitive; 32 | switch (typeof value) { 33 | case "number": 34 | case "boolean": 35 | content = value; 36 | break; 37 | case "string": 38 | content = parseValue(value); 39 | break; 40 | default: 41 | return assert.fail(`Unknown primitive ${value}`) 42 | } 43 | return valueCell(content); 44 | } 45 | 46 | function makeFormulaCell(row: number, col: number, text: string): FormulaCell { 47 | return { 48 | state: CalcState.Dirty, 49 | flags: CalcFlags.None, 50 | row, 51 | col, 52 | formula: text, 53 | value: undefined, 54 | node: undefined, 55 | }; 56 | } 57 | 58 | export function makeCell(row: number, col: number, value: Value) { 59 | if (value === undefined || value === "") { 60 | return undefined; 61 | } 62 | if (typeof value === "string" && value[0] === "=") { 63 | return makeFormulaCell(row, col, value.substring(1)); 64 | } 65 | return makeValueCell(value); 66 | } 67 | 68 | function parseValue(value: string): Primitive { 69 | const upper = value.toUpperCase(); 70 | if (upper === "TRUE") { 71 | return true; 72 | } 73 | if (upper === "FALSE") { 74 | return false; 75 | } 76 | const asNumber = Number(value); 77 | return isNaN(asNumber) ? value : asNumber; 78 | } 79 | -------------------------------------------------------------------------------- /packages/core/micro/src/consumerset.ts: -------------------------------------------------------------------------------- 1 | export type ConsumerSet = undefined | T | Array<{ value: T, count: number }> 2 | 3 | export function addConsumer(self: ConsumerSet, value: T): ConsumerSet { 4 | if (self === undefined) { 5 | return value; 6 | } 7 | 8 | if (self === value) { 9 | return [{ value, count: 2 }]; 10 | } 11 | 12 | if (Array.isArray(self)) { 13 | for (const i of self) { 14 | if (i.value === value) { 15 | i.count++; 16 | return self; 17 | } 18 | } 19 | 20 | self.push({ value: value, count: 1 }); 21 | return self; 22 | } else { 23 | return [{ value: self, count: 1 }, { value, count: 1 }]; 24 | } 25 | } 26 | 27 | export function removeConsumer(self: ConsumerSet, value: T): ConsumerSet { 28 | if (self === value) { 29 | return undefined; 30 | } 31 | 32 | if (Array.isArray(self)) { 33 | for (let i = 0; i < self.length; i++) { 34 | const item = self[i]; 35 | if (item.value === value) { 36 | item.count--; 37 | 38 | if (item.count === 0) { 39 | self.splice(/* start: */ i, /* deleteCount: */ 1); 40 | i--; 41 | } 42 | 43 | return self.length === 0 44 | ? undefined 45 | : self; 46 | } 47 | } 48 | } 49 | 50 | return self; 51 | } 52 | 53 | export function forEachConsumer(self: ConsumerSet, callback: (consumer: T, count: number) => void) { 54 | if (self === undefined) { 55 | return; 56 | } 57 | 58 | if (Array.isArray(self)) { 59 | for (const { value, count } of self) { 60 | callback(value, count); 61 | } 62 | } else { 63 | callback(/* value: */ self, /* count: */ 1); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/core/micro/src/core.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { 7 | errors as errorsC, 8 | makeError, 9 | Pending, 10 | } from "@tiny-calc/nano"; 11 | 12 | import { 13 | CalcFlags, 14 | Fiber, 15 | FormulaCell, 16 | FunctionFiber, 17 | FunctionRunner, 18 | FunctionSkolem, 19 | FunctionTask, 20 | PendingTask, 21 | Range, 22 | RangeContext, 23 | } from "./types"; 24 | 25 | export function makePendingFunction( 26 | state: FunctionTask, range: Range, context: RangeContext, row: number, column: number, runner: FunctionRunner 27 | ): FunctionFiber { 28 | return { state, range, context, flags: CalcFlags.None, row, column, runner }; 29 | } 30 | 31 | export function isPending(content: any): content is Pending { 32 | return typeof content === "object" && "kind" in content && content.kind === "Pending"; 33 | } 34 | 35 | export function isPendingTask(content: any): content is PendingTask { 36 | return isPending(content) && "fiber" in content; 37 | } 38 | 39 | export function isFormulaFiber(fiber: Fiber): fiber is FormulaCell { 40 | return typeof fiber.state === "number"; 41 | } 42 | 43 | /** 44 | * Basic errors. 45 | */ 46 | export const errors = { 47 | ...errorsC, 48 | unknownField: makeError("#UNKNOWN!"), 49 | calc: makeError("#CALC!"), 50 | cycle: makeError("#CYCLE!"), 51 | ref: makeError("#REF!"), 52 | fallbackCoercion: makeError("#VALUE!"), 53 | parseFailure: makeError("#PARSE!"), 54 | evalFailure: makeError("#EVAL!"), 55 | } as const; 56 | -------------------------------------------------------------------------------- /packages/core/micro/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { IGrid } from "./types"; 7 | export { createSheetletProducer, ISheetlet } from "./sheetlet"; 8 | export { DenseVector } from "./vector/dense"; 9 | export { DenseMatrix } from "./matrix/dense"; 10 | export { RowMajorMatrix } from "./matrix/rowmajor"; 11 | export { 12 | createTree, 13 | createTreeDebug, 14 | forEachInSegmentRange, 15 | } from "./adjust-tree/tree"; 16 | 17 | export { 18 | AdjustTree, 19 | AdjustTreeDebug, 20 | SegmentRange, 21 | TreeConfiguration, 22 | } from "./adjust-tree/types"; 23 | 24 | export { 25 | forEachPermutation, 26 | PermutationSequence, 27 | PermutationSequenceSnapshot, 28 | } from "./permutation"; 29 | -------------------------------------------------------------------------------- /packages/core/micro/src/key.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | // Note that the exponents sum to 2^53, which fully utilizes the exact integer range of a Float64. 7 | export const maxRows = 0x100000000 as const; // 2^32 = x4096 Excel maximum of 1,048,576 rows 8 | export const maxCols = 0x200000 as const; // 2^21 = x128 Excel maximum of 16,384 columns 9 | const colMask = 0x1FFFFF as const; 10 | 11 | /** Encode the given RC0 `row`/`col` as a 53b integer key. */ 12 | export function pointToKey(row: number, col: number) { 13 | // Note: Can not replace multiply with shift as product exceeds 32b. 14 | return row * maxCols + col; 15 | } 16 | 17 | /** Decode the given `key` to it's RC0 row/col. */ 18 | export function keyToPoint(position: number) { 19 | // Can not replace division with shift as numerator exceeds 32b, but the quotient can 20 | // be safely converted to a Uint32 in lieu of 'Math.floor()' for a measurable speedup. 21 | const row = (position / maxCols) >>> 0; 22 | 23 | // The column portion is less than 32b and resides in the low bits of the Uint53. We 24 | // can safely extract it with mask. 25 | const col = position & colMask; 26 | return [ row, col ]; 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/micro/src/map/producer.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { IProducer, IReader, IConsumer } from "@tiny-calc/types"; 7 | import { ConsumerSet, addConsumer, removeConsumer, forEachConsumer } from "../consumerset"; 8 | 9 | export abstract class Producer implements IProducer, IReader { 10 | private consumers?: ConsumerSet>; 11 | 12 | //#region IProducer 13 | 14 | public open(consumer: IConsumer): IReader { 15 | this.consumers = addConsumer(this.consumers, consumer); 16 | return this; 17 | } 18 | 19 | public close(consumer: IConsumer): void { 20 | this.consumers = removeConsumer(this.consumers, consumer); 21 | } 22 | 23 | //#endregion IProducer 24 | 25 | //#region IReader 26 | 27 | public abstract get(key: K): TMap[K]; 28 | 29 | public get producer():IProducer { return this; } 30 | 31 | //#endregion IReader 32 | 33 | protected invalidateValue(key: K): void { 34 | forEachConsumer(this.consumers, (consumer) => { 35 | consumer.keyChanged(key, this); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/micro/src/matrix/dense.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { MatrixProducer } from "./producer"; 7 | 8 | /** 9 | * A row/col-major agnostic base class 10 | */ 11 | export abstract class DenseMatrix extends MatrixProducer { 12 | protected cells: T[] = []; 13 | protected abstract get majorCount(): number; 14 | protected abstract get stride(): number; 15 | 16 | protected getCellCore(major: number, minor: number): T { 17 | return this.cells[this.getMajorIndex(major) + minor]; 18 | } 19 | 20 | protected setCellCore(major: number, minor: number, value: T) { 21 | this.cells[this.getMajorIndex(major) + minor] = value; 22 | } 23 | 24 | protected spliceMajor(start: number, removedCount: number, insertedCount: number) { 25 | // TODO: Using the spread operator with `.splice()` can exhaust the stack (node v12 x64) 26 | this.cells.splice( 27 | this.getMajorIndex(start), 28 | removedCount * this.stride, 29 | ...new Array(insertedCount * this.stride)); 30 | } 31 | 32 | protected spliceMinor(start: number, removedCount: number, insertedCount: number) { 33 | const emptyCells = new Array(insertedCount); 34 | const stride = this.stride; 35 | for (let r = this.majorCount, c = start; r > 0; r--, c += stride) { 36 | // TODO: Using the spread operator with `.splice()` can exhaust the stack (node v12 x64) 37 | this.cells.splice(c, removedCount, ...emptyCells); 38 | } 39 | } 40 | 41 | private getMajorIndex(major: number) { 42 | return major * this.stride; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/core/micro/src/matrix/producer.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { IMatrixProducer, IMatrixReader, IMatrixConsumer } from "@tiny-calc/types"; 7 | import { ConsumerSet, addConsumer, removeConsumer, forEachConsumer } from "../consumerset"; 8 | 9 | export abstract class MatrixProducer implements IMatrixProducer, IMatrixReader { 10 | private matrixConsumers?: ConsumerSet>; 11 | 12 | //#region IMatrixProducer 13 | 14 | public openMatrix(consumer: IMatrixConsumer): IMatrixReader { 15 | this.matrixConsumers = addConsumer(this.matrixConsumers, consumer); 16 | return this; 17 | } 18 | 19 | public closeMatrix(consumer: IMatrixConsumer): void { 20 | this.matrixConsumers = removeConsumer(this.matrixConsumers, consumer); 21 | } 22 | 23 | //#endregion IMatrixProducer 24 | 25 | //#region IMatrixReader 26 | 27 | public abstract get rowCount(): number; 28 | public abstract get colCount(): number; 29 | public abstract getCell(row: number, col: number): T; 30 | public get matrixProducer(): IMatrixProducer { return this; } 31 | 32 | //#endregion IMatrixReader 33 | 34 | protected invalidateRows(rowStart: number, removedCount: number, insertedCount: number): void { 35 | forEachConsumer(this.matrixConsumers, (consumer) => { 36 | consumer.rowsChanged(rowStart, removedCount, insertedCount, /* producer: */ this); 37 | }); 38 | } 39 | 40 | protected invalidateCols(colStart: number, removedCount: number, insertedCount: number): void { 41 | forEachConsumer(this.matrixConsumers, (consumer) => { 42 | consumer.colsChanged(colStart, removedCount, insertedCount, /* producer: */ this); 43 | }); 44 | } 45 | 46 | protected invalidateCells(rowStart: number, colStart: number, rowCount: number, colCount: number): void { 47 | forEachConsumer(this.matrixConsumers, (consumer) => { 48 | consumer.cellsChanged(rowStart, colStart, rowCount, colCount, /* producer: */ this); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/core/micro/src/matrix/rowmajor.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { DenseMatrix } from "./dense"; 7 | import { IMatrixWriter, IVectorConsumer, IVectorProducer, IVectorReader } from "@tiny-calc/types"; 8 | 9 | export class RowMajorMatrix extends DenseMatrix implements IMatrixWriter, IVectorConsumer { 10 | private readonly rowReader: IVectorReader; 11 | private readonly colReader: IVectorReader; 12 | 13 | public constructor(rows: IVectorProducer, cols: IVectorProducer) { 14 | super(); 15 | 16 | this.rowReader = rows.openVector(this); 17 | this.colReader = cols.openVector(this); 18 | 19 | this.cells = new Array(this.rowCount * this.colCount).fill(undefined); 20 | } 21 | 22 | //#region IMatrixReader 23 | 24 | public get rowCount(): number { return this.rowReader.length; } 25 | public get colCount(): number { return this.colReader.length; } 26 | 27 | public getCell(row: number, col: number): T { 28 | return this.getCellCore(/* major: */ row, /* minor: */ col); 29 | } 30 | 31 | //#endregion IMatrixReader 32 | 33 | //#region IMatrixWriter 34 | 35 | public setCell(row: number, col: number, value: T): void { 36 | this.setCellCore(/* major: */ row, /* minor: */ col, value); 37 | this.invalidateCells(row, col, /* rowCount: */ 1, /* colCount: */ 1); 38 | } 39 | 40 | //#endregion IMatrixReader 41 | 42 | //#region IVectorConsumer 43 | 44 | public itemsChanged(start: number, removedCount: number, insertedCount: number, producer: IVectorProducer): void { 45 | if (producer === this.rowReader.vectorProducer) { 46 | this.spliceMajor(start, removedCount, insertedCount); 47 | this.invalidateRows(start, removedCount, insertedCount); 48 | } else { 49 | this.spliceMinor(start, removedCount, insertedCount); 50 | this.invalidateCols(start, removedCount, insertedCount); 51 | } 52 | } 53 | 54 | //#endregion IVectorConsumer 55 | 56 | //#region DenseMatrix 57 | 58 | protected get majorCount(): number { return this.rowCount; } 59 | protected get stride(): number { return this.colCount; } 60 | 61 | //#endregion DenseMatrix 62 | } 63 | -------------------------------------------------------------------------------- /packages/core/micro/src/vector/dense.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { IVectorWriter } from "@tiny-calc/types"; 7 | import { VectorProducer } from "./producer"; 8 | 9 | function toArray(iterable?: Iterable): T[] { 10 | return iterable === undefined 11 | ? [] 12 | : Array.isArray(iterable) 13 | ? iterable 14 | : Array.from(iterable); 15 | } 16 | 17 | export class DenseVector extends VectorProducer implements IVectorWriter { 18 | public constructor (private items: T[] = []) { super(); } 19 | 20 | public get length(): number { 21 | return this.items.length; 22 | } 23 | 24 | public getItem(index: number): T { 25 | return this.items[index]; 26 | } 27 | 28 | public setItem(index: number, value: T): void { 29 | this.items[index] = value; 30 | this.invalidateItems(index, /* removedCount: */ 1, /* insertedCount: */ 1); 31 | } 32 | 33 | public resize(start: number, deleteCount: number, insertCount: number, values?: Iterable): void { 34 | const inserted = toArray(values); 35 | inserted.length = insertCount; 36 | 37 | // TODO: Using the spread operator with `.splice()` can exhaust the stack (node v12 x64) 38 | this.items.splice(start, deleteCount, ...inserted); 39 | 40 | this.invalidateItems(start, deleteCount, inserted.length); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/core/micro/src/vector/producer.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { IVectorProducer, IVectorReader, IVectorConsumer } from "@tiny-calc/types"; 7 | import { ConsumerSet, addConsumer, removeConsumer, forEachConsumer } from "../consumerset"; 8 | 9 | export abstract class VectorProducer implements IVectorProducer, IVectorReader { 10 | private vectorConsumers?: ConsumerSet>; 11 | 12 | //#region IVectorProducer 13 | 14 | public openVector(consumer: IVectorConsumer): IVectorReader { 15 | this.vectorConsumers = addConsumer(this.vectorConsumers, consumer); 16 | return this; 17 | } 18 | 19 | public closeVector(consumer: IVectorConsumer): void { 20 | this.vectorConsumers = removeConsumer(this.vectorConsumers, consumer); 21 | } 22 | 23 | //#endregion IVectorProducer 24 | 25 | //#region IVectorReader 26 | 27 | public abstract get length(): number; 28 | public abstract getItem(index: number): T; 29 | public get vectorProducer(): IVectorProducer { return this; } 30 | 31 | //#endregion IVectorReader 32 | 33 | protected invalidateItems(start: number, removedCount: number, insertedCount: number): void { 34 | forEachConsumer(this.vectorConsumers, (consumer) => { 35 | consumer.itemsChanged(start, removedCount, insertedCount, /* producer: */ this); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/micro/test/consumerset.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { strict as assert } from "assert"; 7 | import "mocha"; 8 | import { ConsumerSet, addConsumer, removeConsumer, forEachConsumer } from "../src/consumerset"; 9 | 10 | class TestFixture { 11 | private readonly expected = new Map(); 12 | private actual: ConsumerSet = undefined; 13 | 14 | public add(value: T) { 15 | const count = this.expected.get(value); 16 | this.expected.set(value, 17 | count === undefined 18 | ? 1 19 | : count + 1); 20 | 21 | this.actual = addConsumer(this.actual, value); 22 | 23 | this.check(); 24 | } 25 | 26 | public remove(value: T) { 27 | const count = this.expected.get(value); 28 | 29 | if (count === 1) { 30 | this.expected.delete(value); 31 | } else if (count !== undefined) { 32 | this.expected.set(value, count - 1); 33 | } 34 | 35 | this.actual = removeConsumer(this.actual, value); 36 | 37 | this.check(); 38 | } 39 | 40 | private check() { 41 | const actual: [T, number][] = []; 42 | 43 | forEachConsumer(this.actual, (consumer, count) => { 44 | actual.push([consumer, count]); 45 | return true; 46 | }); 47 | 48 | assert.deepEqual(new Map(actual), this.expected); 49 | assert.equal(this.actual === undefined, this.expected.size === 0); 50 | } 51 | } 52 | 53 | describe("ConsumerSet", () => { 54 | let set: TestFixture 55 | 56 | beforeEach(() => { 57 | set = new TestFixture(); 58 | }); 59 | 60 | it("insert 1 into empty", () => { 61 | set.add("one"); 62 | }); 63 | 64 | it("insert 2 into empty", () => { 65 | set.add("one"); 66 | set.add("two"); 67 | }); 68 | 69 | it("insert duplicate into 1", () => { 70 | set.add("one"); 71 | set.add("one"); 72 | }); 73 | 74 | it("insert duplicates into 2", () => { 75 | set.add("one"); 76 | set.add("two"); 77 | set.add("one"); 78 | set.add("two"); 79 | }); 80 | 81 | it("remove 1 from empty", () => { 82 | set.remove("one"); 83 | }); 84 | 85 | it("remove 1 from 1", () => { 86 | set.add("one"); 87 | set.remove("one"); 88 | }); 89 | 90 | it("remove 2 from 2", () => { 91 | set.add("one"); 92 | set.add("two"); 93 | set.remove("one"); 94 | set.remove("two"); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /packages/core/micro/test/formula.spec.ts: -------------------------------------------------------------------------------- 1 | import "mocha"; 2 | import { strict as assert } from "assert"; 3 | 4 | import { 5 | ident, 6 | } from "@tiny-calc/nano"; 7 | 8 | import { createFormulaParser } from "../src/formula"; 9 | 10 | 11 | const parser = createFormulaParser(); 12 | 13 | describe("Formula Parser", () => { 14 | 15 | function parseTest(input: string, expected: object) { 16 | const [errors, res] = parser(input); 17 | assert.deepStrictEqual(errors, false); 18 | assert.deepStrictEqual(res, expected); 19 | } 20 | 21 | it("should parse refs ok", () => { 22 | parseTest("A1", ident({ row1: 0, col1: 0 })); 23 | parseTest("a1", ident({ row1: 0, col1: 0 })); 24 | parseTest("Aa1", ident({ row1: 0, col1: 26 })); 25 | parseTest("A1:B1", ident({ row1: 0, col1: 0, row2: 0, col2: 1 })); 26 | parseTest("$A$1:$B$1", ident({ row1: 0, col1: 0, row2: 0, col2: 1 })); 27 | parseTest("A1:$B$1", ident({ row1: 0, col1: 0, row2: 0, col2: 1 })); 28 | parseTest("A$1:$B$1", ident({ row1: 0, col1: 0, row2: 0, col2: 1 })); 29 | }); 30 | it("should handle size limits", () => { 31 | parseTest("A1048576:B1048576", ident({ row1: 1048576 - 1, col1: 0, row2: 1048576 - 1, col2: 1 })); 32 | parseTest("A1048577", ident("A1048577")); 33 | parseTest("A1048576:B1048577", ident("A1048576:B1048577")); 34 | parseTest("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1", ident("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1")); 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /packages/core/micro/test/key.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { strict as assert } from "assert"; 7 | import "mocha"; 8 | import { maxRows, maxCols, pointToKey, keyToPoint } from "../src/key"; 9 | 10 | describe("Matrix Key", () => { 11 | const valid = [ 12 | [0, 0], // Trivial 13 | [0, 1], 14 | [1, 0], 15 | [maxRows - 1, 0], // Limits 16 | [0, maxCols - 1], 17 | [maxRows - 1, maxCols - 1], 18 | ]; 19 | 20 | for (const expected of valid) { 21 | it(`[${expected[0]},${expected[1]}]`, () => { 22 | const key = pointToKey(expected[0], expected[1]); 23 | const actual = keyToPoint(key); 24 | assert.deepEqual(actual, expected); 25 | }); 26 | } 27 | 28 | it("Key mapping should fully utilize, but not exceed, the safe integer range", () => { 29 | assert(pointToKey(maxRows - 1, maxCols - 1) === Number.MAX_SAFE_INTEGER); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/core/micro/test/random-adjust-tree.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AdjustTreeDebug, 3 | createTreeDebug, 4 | TreeConfiguration, 5 | } from "../src/index"; 6 | 7 | import { simpleDeletionTest } from "./util"; 8 | 9 | const config = (order: number): TreeConfiguration => ({ 10 | emptySegment: -1, 11 | order, 12 | extractSegmentRange: element => { return { retained: element, removed: element }; } 13 | }) 14 | 15 | const makeTree = (order: number) => { 16 | const t = createTreeDebug(config(order)); 17 | for (let i = 0; i < 1000; i++) { 18 | t.insertRange(i * 10, 10, i); 19 | } 20 | return t; 21 | } 22 | 23 | const granularity = 0.03; 24 | const order = 2; 25 | 26 | function deleteRandom(t: AdjustTreeDebug, { pos, length }: { pos?: number, length?: number }) { 27 | const startR = Math.random(); 28 | const len = t.getLength(); 29 | if (len === 0) { 30 | return 'done' as const; 31 | } 32 | let available; 33 | if (pos === undefined) { 34 | let posR = Math.floor(startR * (len - 1)); 35 | available = (len - posR); 36 | while (available < 1) { 37 | posR = Math.floor(startR * (len - 1)); 38 | available = (len - posR); 39 | } 40 | pos = posR; 41 | } 42 | else { 43 | available = len - pos; 44 | } 45 | if (length === undefined) { 46 | let countR = Math.random(); 47 | let attempt = Math.floor(countR * available * granularity); 48 | while (attempt > available || attempt < 1) { 49 | countR = Math.random(); 50 | attempt = Math.ceil(countR * available * granularity); 51 | } 52 | length = attempt; 53 | } 54 | try { 55 | simpleDeletionTest(t, { pos, length }) 56 | return 'ok' as const; 57 | } 58 | catch (e) { 59 | return 'fail' as const; 60 | } 61 | } 62 | 63 | function runRandomTest(iterations: number) { 64 | for (let i = 0; i < iterations; i++) { 65 | const tree = makeTree(order); 66 | console.log(`round ${i}`); 67 | let valid: "ok" | "done" | "fail" = "ok"; 68 | while (valid === "ok") { 69 | valid = deleteRandom(tree, {}); 70 | } 71 | if (valid === "fail") { 72 | throw "fail"; 73 | } 74 | } 75 | } 76 | 77 | runRandomTest(100); 78 | -------------------------------------------------------------------------------- /packages/core/micro/test/sheets.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { createSheetletProducer, Sheetlet } from "../src/sheetlet"; 7 | import { createGrid, matrixProducer } from "../src/matrix"; 8 | import { consume } from "../bench/util"; 9 | import { Primitive } from "@tiny-calc/nano"; 10 | import { IMatrixReader } from "@tiny-calc/types"; 11 | import { Value } from "../src/types"; 12 | 13 | /** Convert a 0-based column index into an Excel-like column name (e.g., 0 -> 'A') */ 14 | function c0ToName(colIndex: number) { 15 | let name = ""; 16 | 17 | do { 18 | const mod = colIndex % 26; 19 | name = `${String.fromCharCode(65 + mod)}${name}`; 20 | // tslint:disable-next-line:no-parameter-reassignment 21 | colIndex = Math.trunc(colIndex / 26) - 1; 22 | } while (colIndex >= 0); 23 | 24 | return name; 25 | } 26 | 27 | export function makeBenchmark(size: number): { sheet: Sheetlet, setAt: (row: number, col: number, value: Primitive) => void } { 28 | const cells = new Array(size).fill(undefined).map(() => { return new Array(size) }); 29 | 30 | cells[0][0] = 0; 31 | 32 | // Cells in column 0 sum all cells above. 33 | for (let r = 1; r < size; r++) { 34 | cells[r][0] = `=SUM(A1:A${r})`; 35 | } 36 | 37 | // Cells in row 0 sum all cells to the left. 38 | for (let c = 1; c < size; c++) { 39 | cells[0][c] = `=SUM(A1:${c0ToName(c - 1)}1)`; 40 | } 41 | 42 | // Interior cells sum all cells above and/or to the left. 43 | for (let r = 1; r < size; r++) { 44 | for (let c = 1; c < size; c++) { 45 | const col = c0ToName(c); 46 | cells[r][c] = `=SUM(${col}1:${col}${r}) + SUM(A1:${c0ToName(c - 1)}${r + 1})`; 47 | } 48 | } 49 | 50 | const matrix = matrixProducer(cells); 51 | const sheet = createSheetletProducer(matrix, createGrid()); 52 | 53 | return { 54 | sheet, 55 | setAt: (row: number, col: number, value: Primitive) => { 56 | matrix.write(row, col, value); 57 | sheet.invalidate(row, col); 58 | } 59 | }; 60 | } 61 | 62 | export function evalSheet(sheet: IMatrixReader, size: number) { 63 | for (let r = 0; r < size; r++) { 64 | for (let c = 0; c < size; c++) { 65 | consume(sheet.getCell(r, c)); 66 | } 67 | } 68 | return sheet; 69 | } 70 | -------------------------------------------------------------------------------- /packages/core/micro/test/util.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from "assert"; 2 | 3 | import { 4 | AdjustTreeDebug, 5 | forEachInSegmentRange, 6 | SegmentRange, 7 | } from "../src/index"; 8 | 9 | function segmentRangeLength(segmentRange: SegmentRange): number { 10 | let len = 0; 11 | forEachInSegmentRange(segmentRange, l => (len += l, true)) 12 | return len; 13 | } 14 | 15 | export function simpleDeletionTest(tree: AdjustTreeDebug, spec: { pos: number, length: number }) { 16 | const startLen = tree.getLength(); 17 | const rng = tree.deleteRange(spec.pos, spec.length); 18 | assert(tree.getLength() === startLen - spec.length); 19 | assert(segmentRangeLength(rng) === spec.length); 20 | assert(tree.validate()); 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/micro/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/core/micro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/micro/tsconfig.prof.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./temp", 5 | "rootDir": ".", 6 | "outDir": "./temp" 7 | }, 8 | "include": ["bench/profile.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/nano/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/nano/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/core/nano/bench/index.ts: -------------------------------------------------------------------------------- 1 | import { Suite } from "benchmark"; 2 | import { consume, runSuite, runSuites } from "./util"; 3 | import { compile } from "../src/compiler"; 4 | import { suites as jsonSuites } from "./json"; 5 | 6 | runSuites(jsonSuites); 7 | 8 | const formulas = [ 9 | "0", 10 | "1 + 2 + 3 + 4 = 10 - 10 + 10", 11 | "IF(1*2*3*4<>24, 'hello' + 'world', 10 / 2)" 12 | ]; 13 | 14 | { 15 | const suite = new Suite("Compilation Only"); 16 | for (const formula of formulas) { 17 | suite.add(`'${formula}'`, () => { consume(compile(formula)); }); 18 | } 19 | runSuite(suite); 20 | } 21 | 22 | { 23 | const suite = new Suite("Evaluation Only"); 24 | for (const formula of formulas) { 25 | const compiled = compile(formula); 26 | suite.add(`'${formula}'`, () => { consume(compiled!(undefined, undefined as any)); }); 27 | } 28 | runSuite(suite); 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/nano/bench/util.ts: -------------------------------------------------------------------------------- 1 | import { Suite } from "benchmark"; 2 | const process = require("process"); 3 | 4 | let count = 0; 5 | let cached: any; 6 | 7 | /** 8 | * Paranoid defense against dead code elimination. 9 | */ 10 | export function consume(value: any) { 11 | count++; 12 | if ((count >>> 0) === 0) { 13 | cached = value; 14 | } 15 | } 16 | 17 | // Prevent v8's optimizer from identifying 'cached' as an unused value. 18 | process.on('exit', () => { 19 | if ((count >>> 0) === 0) { 20 | console.log(`Ignore this: ${cached}`); 21 | } 22 | }); 23 | 24 | export function runSuite(suite: Suite) { 25 | count = 0; 26 | 27 | console.log(); 28 | console.group((suite as any)["name"]); 29 | return suite 30 | .on("cycle", (event: any) => { 31 | console.log(String(event.target)); 32 | }) 33 | .on("error", (event: any) => { 34 | console.error(String(event.target.error)); 35 | }) 36 | .on("complete", (event: any) => { 37 | console.groupEnd(); 38 | console.log(`Fastest is ${event.currentTarget.filter("fastest").map("name")}`); 39 | }) 40 | .run(); 41 | } 42 | 43 | export function runSuites(suites: Iterable) { 44 | for (const suite of suites) { 45 | runSuite(suite); 46 | } 47 | } -------------------------------------------------------------------------------- /packages/core/nano/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/nano", 3 | "version": "0.0.0-alpha.5", 4 | "main": "dist/index.js", 5 | "sideEffects": "false", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/microsoft/tiny-calc.git" 9 | }, 10 | "author": "Microsoft", 11 | "license": "MIT", 12 | "scripts": { 13 | "bench": "ts-node bench/index.ts", 14 | "build": "tsc", 15 | "clean": "rimraf ./dist *.build.log", 16 | "dev": "npm run build -- --watch", 17 | "lint": "eslint --ext=ts --format visualstudio src", 18 | "test": "mocha -r ts-node/register test/**/*.spec.ts" 19 | }, 20 | "dependencies": { 21 | "@tiny-calc/types": "0.0.0-alpha.5" 22 | }, 23 | "devDependencies": { 24 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 25 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 26 | "@types/benchmark": "^1.0.31", 27 | "@types/mocha": "^8.0.4", 28 | "@types/node": "^14.14.10", 29 | "benchmark": "^2.1.4", 30 | "eslint": "^7.13.0", 31 | "mocha": "^8.2.1", 32 | "rimraf": "^3.0.2", 33 | "ts-node": "^9.0.0", 34 | "typescript": "^4.0.5" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/core/nano/src/debug.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export function error(message: string): never { 7 | throw new Error(message); 8 | } 9 | 10 | export function assertNever(proof: never, message: string = "assertNever violation"): never { 11 | return error(message); 12 | } 13 | 14 | // TODO: assertion signature 15 | export function assert(expression: boolean, message = "False expression"): void { 16 | if (!expression) { 17 | return error(message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/nano/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { 7 | createAlgebra, 8 | ExpressionNode, 9 | ident, 10 | NodeKind, 11 | parseExpression, 12 | } from "./ast" 13 | 14 | export { 15 | CoreRuntime, 16 | Delay, 17 | Delayed, 18 | isDelayed, 19 | errors, 20 | makeError, 21 | makeTracer, 22 | } from "./core"; 23 | 24 | export { 25 | compile, 26 | Formula, 27 | } from "./compiler"; 28 | 29 | export { 30 | evaluate, 31 | evalContext, 32 | EvalContext, 33 | interpret, 34 | Interpreter 35 | } from "./interpreter"; 36 | 37 | export { 38 | CalcFun, 39 | CalcObj, 40 | CalcValue, 41 | ComparableType, 42 | DispatchPattern, 43 | NumericType, 44 | Pending, 45 | Primitive, 46 | ReadableType, 47 | ReferenceType, 48 | Resolver, 49 | Runtime, 50 | TypeMap, 51 | TypeName, 52 | } from "./types"; 53 | 54 | export { 55 | AlgebraContext, 56 | createBooleanErrorHandler, 57 | createParser, 58 | ExpAlgebra, 59 | ParserErrorHandler, 60 | Parser, 61 | } from "./parser"; 62 | -------------------------------------------------------------------------------- /packages/core/nano/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { "rootDir": "..", }, 4 | "include": [ 5 | "../src/**/*.ts", 6 | "../test/**/*.ts" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/nano/test/util/index.ts: -------------------------------------------------------------------------------- 1 | export { LoggingConsumer } from "./loggingConsumer"; 2 | export { nullConsumer } from "./nullConsumer"; 3 | -------------------------------------------------------------------------------- /packages/core/nano/test/util/objectTypes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CalcValue, 3 | CalcObj, 4 | Pending, 5 | TypeMap, 6 | TypeName 7 | } from "../../src/index"; 8 | 9 | export function createRefMap(value: CalcValue): TypeMap, O> { 10 | return { [TypeName.Reference]: { dereference: () => value } } 11 | } 12 | 13 | export function createReadMap(read: (prop: string, context: O) => CalcValue | Pending>): TypeMap, O> { 14 | return { [TypeName.Readable]: { read: (_v, p, c) => read(p, c) } } 15 | } 16 | 17 | export function createObjFromMap(name: string, map: TypeMap, O>): CalcObj { 18 | return { typeMap: () => map, serialise: () => name } 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/nano/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/core/nano/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/test/example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/test/example/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/test/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc-test/example", 3 | "main": "dist/index.js", 4 | "sideEffects": "false", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/microsoft/tiny-calc.git" 8 | }, 9 | "author": "Microsoft", 10 | "license": "MIT", 11 | "scripts": { 12 | "build": "tsc", 13 | "clean": "rimraf ./dist *.build.log", 14 | "dev": "npm run build -- --watch", 15 | "lint": "eslint --ext=ts --format visualstudio src", 16 | "test": "ts-node src/index.ts" 17 | }, 18 | "dependencies": { 19 | "@tiny-calc/nano": "0.0.0-alpha.5", 20 | "@tiny-calc/types": "0.0.0-alpha.5", 21 | "node-fetch": "^2.6.0" 22 | }, 23 | "devDependencies": { 24 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 25 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 26 | "@types/node": "^14.14.10", 27 | "rimraf": "^3.0.2", 28 | "ts-node": "^9.0.0", 29 | "typescript": "^4.0.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/test/example/src/index.ts: -------------------------------------------------------------------------------- 1 | import { ListFormula, context } from "./types"; 2 | 3 | /** 4 | * Without a memo-producer this formula may return false. 5 | */ 6 | const listFormula = new ListFormula(context, "IF(Time.Now = Time.Now, Math.Max(Time.Now,Time.Now) = Math.Min(Time.Now,Time.Now), 'not ' + 'ok')", v => { 7 | const invariant = v.every(x => x === true); 8 | console.log(`value = ${JSON.stringify(v[0])}; uniform = ${invariant}`); 9 | }); 10 | 11 | for (let i = 0; i < 10; i++) { 12 | listFormula.keyChanged(); 13 | } 14 | -------------------------------------------------------------------------------- /packages/test/example/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | ] 8 | } -------------------------------------------------------------------------------- /packages/test/example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/types/es/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/types/es/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/types/es/bench/json.ts: -------------------------------------------------------------------------------- 1 | import { Suite } from "benchmark"; 2 | import { consume } from "./util"; 3 | import { produce } from "../src/produce"; 4 | import { nullConsumer } from "../test/util"; 5 | 6 | export const suites: Suite[] = []; 7 | 8 | { 9 | const array = new Array(10000).fill(0).map(() => Math.random()); 10 | const producer = produce(array.slice()); 11 | const reader = producer.openVector(nullConsumer); 12 | 13 | const suite = new Suite("Native Array vs. IReader"); 14 | 15 | suite.add(`array`, 16 | () => { 17 | let sum = 0; 18 | for (let i = array.length - 1; i >= 0; i--) { 19 | sum += array[i]; 20 | } 21 | return consume(sum); 22 | }); 23 | 24 | suite.add(`openVector().read()`, 25 | () => { 26 | let sum = 0; 27 | for (let i = reader.length - 1; i >= 0; i--) { 28 | sum += producer.openVector(nullConsumer).read(i); 29 | }; 30 | return consume(sum); 31 | }); 32 | 33 | suite.add(`cached reader`, () => { 34 | let sum = 0; 35 | for (let i = reader.length - 1; i >= 0; i--) { 36 | sum += reader.read(i); 37 | }; 38 | return consume(sum); 39 | }); 40 | 41 | suites.push(suite); 42 | } -------------------------------------------------------------------------------- /packages/types/es/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/es", 3 | "description": "Interoperability between Tiny-Calc and native ES types", 4 | "version": "0.0.0-alpha.5", 5 | "main": "dist/index.js", 6 | "sideEffects": "false", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/microsoft/tiny-calc.git" 10 | }, 11 | "author": "Microsoft", 12 | "license": "MIT", 13 | "scripts": { 14 | "bench": "cd bench && ts-node index.ts", 15 | "build": "tsc", 16 | "clean": "rimraf ./dist *.build.log", 17 | "dev": "npm run build -- --watch", 18 | "lint": "eslint --ext=ts --format visualstudio src", 19 | "prof": "tsc --project tsconfig.prof.json && node --prof temp/bench/profile.js && node --prof-process isolate-*-v8.log > profile.log && rm isolate-*-v8.log", 20 | "test": "mocha -r ts-node/register test/**/*.spec.ts", 21 | "adjust-random": "ts-node test/random-adjust-tree.ts" 22 | }, 23 | "dependencies": { 24 | "@tiny-calc/types": "0.0.0-alpha.5" 25 | }, 26 | "devDependencies": { 27 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 28 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 29 | "@tiny-calc-test/type-utils": "0.0.0-alpha.5", 30 | "@types/mocha": "^8.0.4", 31 | "@types/node": "^14.14.10", 32 | "best-random": "^1.0.3", 33 | "eslint": "^7.13.0", 34 | "hotloop": "^1.2.0", 35 | "mocha": "^8.2.1", 36 | "rimraf": "^3.0.2", 37 | "ts-node": "^9.0.0", 38 | "typescript": "^4.0.5" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/types/es/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { produce } from "./produce"; 7 | -------------------------------------------------------------------------------- /packages/types/es/src/produce.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | /* eslint-disable @typescript-eslint/no-explicit-any */ 7 | 8 | import { IMapProducer, IProducer, IShapeProducer, IVectorProducer } from "@tiny-calc/types"; 9 | 10 | // Assigned to IProducer.close(), serves as a sentinel to detect and break cycles. 11 | const unitFn = () => {}; 12 | 13 | const props = { 14 | // #region IProducer 15 | open: { value: function() { return this; }}, 16 | close: { value: unitFn }, 17 | // #endregion IProducer 18 | 19 | // #region IReader 20 | get: { value: function(key: PropertyKey) { return (this as any)[key]; }}, 21 | // #endregion IReader 22 | 23 | // #region IShapeReader 24 | keys: { value: function() { return Object.keys(this); }}, 25 | has: { value: function (key: any) { return Object.keys(this).includes(key); }}, 26 | size: { get: function() { return Object.keys(this).length; }}, 27 | // #endregion IShapeReader 28 | } 29 | 30 | const vectorProps = { 31 | // #region IProducer, IReader, IShapeReader 32 | ...props, 33 | 34 | has: { value: function (key: number) { 35 | return (key >>> 0) < (this as unknown as ArrayLike).length; 36 | }}, 37 | 38 | size: { get: function() { 39 | return (this as unknown as ArrayLike).length; 40 | }}, 41 | // #endregion IProducer, IReader, IShapeReader 42 | 43 | // #region IVectorProducer 44 | openVector: props.open, 45 | closeVector: props.close, 46 | // #endregion IVectorProducer 47 | 48 | // #region IVectorReader 49 | getItem: props.get, 50 | // #endregion IVectorReader 51 | 52 | // Note that IVectorShapeReader.length is already present on ArrayLike 53 | } 54 | 55 | export function produce(subject: ArrayLike): IProducer> & IShapeProducer & IVectorProducer; 56 | export function produce>(subject: T): IMapProducer; 57 | export function produce>(subject: T): unknown { 58 | // Detect cycles and early exit. 59 | if ((subject as IProducer).close === unitFn) { 60 | return subject as IProducer & IShapeProducer>; 61 | } 62 | 63 | Object.defineProperties(subject, 64 | Array.isArray(subject) 65 | ? vectorProps 66 | : props); 67 | 68 | // Recurse into objects 69 | for (const value of Object.values(subject)) { 70 | if (typeof value === "object" && value) { 71 | produce(value); 72 | } 73 | } 74 | 75 | return subject; 76 | } 77 | -------------------------------------------------------------------------------- /packages/types/es/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { "rootDir": "..", }, 4 | "include": [ 5 | "../src/**/*.ts", 6 | "../test/**/*.ts" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/types/es/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/types/es/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/types/es/tsconfig.prof.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/tool-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./temp", 5 | "rootDir": ".", 6 | "outDir": "./temp" 7 | }, 8 | "include": ["bench/profile.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/types/type-utils/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/types/type-utils/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/types/type-utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc-test/type-utils", 3 | "description": "Utilities for vetting that @tiny-calc/types contracts", 4 | "version": "0.0.0-alpha.5", 5 | "main": "dist/index.js", 6 | "sideEffects": "false", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/microsoft/tiny-calc.git" 10 | }, 11 | "author": "Microsoft", 12 | "license": "MIT", 13 | "scripts": { 14 | "bench": "cd bench && ts-node index.ts", 15 | "build": "tsc", 16 | "clean": "rimraf ./dist *.build.log", 17 | "dev": "npm run build -- --watch", 18 | "lint": "eslint --ext=ts --format visualstudio src", 19 | "prof": "tsc --project tsconfig.prof.json && node --prof temp/bench/profile.js && node --prof-process isolate-*-v8.log > profile.log && rm isolate-*-v8.log", 20 | "test": "mocha -r ts-node/register test/**/*.spec.ts", 21 | "adjust-random": "ts-node test/random-adjust-tree.ts" 22 | }, 23 | "dependencies": { 24 | "@tiny-calc/types": "0.0.0-alpha.5" 25 | }, 26 | "devDependencies": { 27 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 28 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 29 | "@types/mocha": "^8.0.4", 30 | "@types/node": "^14.14.10", 31 | "best-random": "^1.0.3", 32 | "eslint": "^7.13.0", 33 | "hotloop": "^1.2.0", 34 | "mocha": "^8.2.1", 35 | "rimraf": "^3.0.2", 36 | "ts-node": "^9.0.0", 37 | "typescript": "^4.0.5" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/types/type-utils/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { nullConsumer } from "./nullConsumer"; 7 | -------------------------------------------------------------------------------- /packages/types/type-utils/src/nullConsumer.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { 7 | IConsumer, 8 | IMatrixConsumer, 9 | IMatrixShapeConsumer, 10 | IShapeConsumer, 11 | IVectorConsumer, 12 | IVectorShapeConsumer, 13 | } from "@tiny-calc/types"; 14 | 15 | class NullConsumer implements 16 | IConsumer, 17 | IMatrixConsumer, 18 | IMatrixShapeConsumer, 19 | IShapeConsumer, 20 | IVectorConsumer, 21 | IVectorShapeConsumer 22 | { 23 | public rowsChanged(): void { } 24 | public colsChanged(): void { } 25 | public cellsChanged(): void { } 26 | public keyChanged(): void { } 27 | public itemsChanged(): void { } 28 | } 29 | 30 | /** A generic test consumer that ignores all change notifications. */ 31 | export const nullConsumer: 32 | IConsumer 33 | & IShapeConsumer 34 | & IMatrixConsumer 35 | & IMatrixShapeConsumer 36 | & IVectorConsumer 37 | & IVectorShapeConsumer 38 | = new NullConsumer(); 39 | -------------------------------------------------------------------------------- /packages/types/type-utils/test/nullConsumer.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | 8 | describe("NullConsumer", () => { 9 | // TODO: Currently a placeholder to allow "npm test" to succeed. 10 | }); 11 | -------------------------------------------------------------------------------- /packages/types/type-utils/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { "rootDir": "..", }, 4 | "include": [ 5 | "../src/**/*.ts", 6 | "../test/**/*.ts" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/types/type-utils/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/types/type-utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/types/type-utils/tsconfig.prof.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/tool-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./temp", 5 | "rootDir": ".", 6 | "outDir": "./temp" 7 | }, 8 | "include": ["bench/profile.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/types/types/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ "@tiny-calc/eslint-config" ], 3 | parserOptions: { 4 | project: './tsconfig.eslint.json', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/types/types/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM dependencies 2 | /node_modules 3 | 4 | # Build artifacts 5 | /dist 6 | 7 | # Typescript incremental build cache 8 | /*.tsbuildinfo 9 | -------------------------------------------------------------------------------- /packages/types/types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tiny-calc/types", 3 | "description": "TypeScript definitions for Tiny-Calc data types", 4 | "version": "0.0.0-alpha.5", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "sideEffects": "false", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/microsoft/tiny-calc.git" 11 | }, 12 | "author": "Microsoft", 13 | "license": "MIT", 14 | "scripts": { 15 | "bench": "cd bench && ts-node index.ts", 16 | "build": "tsc", 17 | "clean": "rimraf ./dist *.build.log", 18 | "dev": "npm run build -- --watch", 19 | "lint": "eslint --ext=ts --format visualstudio src", 20 | "prof": "tsc --project tsconfig.prof.json && node --prof temp/bench/profile.js && node --prof-process isolate-*-v8.log > profile.log && rm isolate-*-v8.log", 21 | "test": "npm run test:tsd && npm run test:mocha", 22 | "test:mocha": "mocha -r ts-node/register test/**/*.spec.ts", 23 | "test:tsd": "tsd" 24 | }, 25 | "devDependencies": { 26 | "@tiny-calc/eslint-config": "0.0.0-alpha.5", 27 | "@tiny-calc/ts-config": "0.0.0-alpha.5", 28 | "@types/mocha": "^8.0.4", 29 | "@types/node": "^14.14.10", 30 | "best-random": "^1.0.3", 31 | "eslint": "^7.13.0", 32 | "hotloop": "^1.2.0", 33 | "mocha": "^8.2.1", 34 | "rimraf": "^3.0.2", 35 | "ts-node": "^9.0.0", 36 | "typescript": "^4.0.5", 37 | "tsd": "^0.13.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/types/types/src/compareFunction.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | /** 7 | * Function used to determine the relative order of the given values. Returns a negative 8 | * value if the left argument is less than right argument, zero if they're equal, and a 9 | * positive value if the left argument is greater than the right. 10 | * 11 | * If the left and right values can not be compared, a CompareFunction should return NaN 12 | * and a stable sort algorithm should preserve the original relative ordering of the 13 | * left/right arguments. 14 | * 15 | * When comparing numeric values, `left - right` orders the numbers in ascending order 16 | * while `right - left' orders numbers in descending order. 17 | */ 18 | export type CompareFunction = (left: T, right: T) => number; 19 | -------------------------------------------------------------------------------- /packages/types/types/src/immutable.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | /** 7 | * Recursively applies 'readonly' to all properties of the given object graph. 8 | */ 9 | export type Immutable = { 10 | readonly [K in keyof T]: Immutable; 11 | } 12 | -------------------------------------------------------------------------------- /packages/types/types/src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export { CompareFunction } from './compareFunction'; 7 | export { Immutable } from "./immutable"; 8 | export { Tuple } from "./tuple"; 9 | export { 10 | Jsonable, 11 | JsonableObject, 12 | JsonableArray, 13 | JsonablePrimitive 14 | } from "./jsonable"; 15 | 16 | export { 17 | IConsumer, 18 | IMapProducer, 19 | IProducer, 20 | IReader, 21 | IShapeConsumer, 22 | IShapeProducer, 23 | IShapeReader, 24 | IShapeWriter, 25 | IWriter 26 | } from './record'; 27 | 28 | export { 29 | IVectorConsumer, 30 | IVectorProducer, 31 | IVectorReader, 32 | IVectorShapeConsumer, 33 | IVectorShapeProducer, 34 | IVectorShapeReader, 35 | IVectorShapeWriter, 36 | IVectorWriter 37 | } from './vector'; 38 | 39 | export { 40 | IMatrixConsumer, 41 | IMatrixProducer, 42 | IMatrixReader, 43 | IMatrixShapeConsumer, 44 | IMatrixShapeProducer, 45 | IMatrixShapeReader, 46 | IMatrixShapeWriter, 47 | IMatrixWriter 48 | } from './matrix'; 49 | -------------------------------------------------------------------------------- /packages/types/types/src/jsonable.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | export type JsonablePrimitive = undefined | null | boolean | number | string; 7 | 8 | // eslint-disable-next-line @typescript-eslint/consistent-type-definitions 9 | export type JsonableObject = { 10 | [key: string]: Jsonable 11 | [key: number]: Jsonable 12 | }; 13 | 14 | export type JsonableArray = Jsonable[]; 15 | 16 | /** 17 | * Used to constrain a value to types that are serializable as JSON. The `T` type parameter may be used to 18 | * customize the type of the leaves to support situations where a `replacer` is used to handle special values. 19 | * (e.g., `Jsonable` allows using a replacer to serialize 'Map' instances.) 20 | * 21 | * Note that the Json type does not protect against the following pitfalls when serializing `undefined` and 22 | * non-finite numbers: 23 | * 24 | * - `undefined` properties on objects are omitted (i.e., properties become undefined instead of equal to undefined). 25 | * - When `undefined` appears as the root object or as an array element it is coerced to `null`. 26 | * - Non-finite numbers (`NaN`, `+/-Infinity`) are also coerced to `null`. 27 | * - (`null` always serializes as `null`.) 28 | */ 29 | export type Jsonable = T | JsonablePrimitive | JsonableArray | JsonableObject; 30 | -------------------------------------------------------------------------------- /packages/types/types/src/tuple.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | /** 7 | * A non-empty fixed-length readonly Array. Uses the type system to ensure that 8 | * Tuples are initialized with an array of the specified `Length`. 9 | */ 10 | export type Tuple = readonly [T, ...T[]] & { 11 | readonly length: Length; 12 | 13 | // Note: Must not expose indexed setter as ES arrays support setting indices greater 14 | // than the length by dynamically resizing the array. 15 | }; 16 | -------------------------------------------------------------------------------- /packages/types/types/test-d/immutable.test-d.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import { expectError } from 'tsd'; 7 | import { Immutable } from '../dist/immutable'; 8 | 9 | const m = { 10 | a: [ 11 | { n: 1, o: { n: 2 }}, 12 | ] 13 | } 14 | 15 | const i: Immutable = m; 16 | 17 | expectError(i.a = []); 18 | expectError(i.a.push({ n: 2, o: { n: 3 }})); 19 | expectError(i.a[0].n = 2); 20 | expectError(i.a[0].o = { n: 2 }); 21 | expectError(i.a[0].o.n++); 22 | 23 | m.a = []; 24 | m.a.push({ n: 2, o: { n: 3 }}); 25 | m.a[0].n = 2; 26 | m.a[0].o = { n: 2 }; 27 | m.a[0].o.n++; 28 | -------------------------------------------------------------------------------- /packages/types/types/test/compareFunction.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | import { strict as assert } from "assert"; 8 | import { CompareFunction } from "../src"; 9 | 10 | describe("CompareFunction", () => { 11 | it("must be compatible with Array.sort()", () => { 12 | const fn: CompareFunction = (left, right) => left - right; 13 | 14 | assert.deepEqual( 15 | [0, 3, 1, 2].sort(fn), 16 | [0, 1, 2, 3] 17 | ) 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/types/types/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { "rootDir": "..", }, 4 | "include": [ 5 | "../src/**/*.ts", 6 | "../test/**/*.ts" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/types/types/test/tuple.spec.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. 4 | */ 5 | 6 | import "mocha"; 7 | import { strict as assert } from "assert"; 8 | import { Tuple } from "../src"; 9 | 10 | describe("Tuple", () => { 11 | describe("construction", () => { 12 | it("can construct Tuple", () => { 13 | const t1: Tuple = [0]; 14 | assert.equal(Array.isArray(t1), true); 15 | }); 16 | 17 | it("can construct Tuple", () => { 18 | const t2: Tuple = [0, 1]; 19 | assert.equal(Array.isArray(t2), true); 20 | }); 21 | }); 22 | 23 | it("is compatible with ReadonlyArray", () => { 24 | const tuple: Tuple = [1, 2, 3]; 25 | const array: ReadonlyArray = tuple; 26 | 27 | assert.equal(array.length, 3); 28 | assert.equal(array[2], 3); 29 | assert.equal( 30 | array.reduce( 31 | (accumulator, value) => value + accumulator, 32 | 0), 33 | 6); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/types/types/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // extend your base config so you don't have to redefine your compilerOptions 3 | "extends": "./tsconfig.json", 4 | "include": [ 5 | "src/**/*.ts", 6 | "test/**/*.ts", 7 | "bench/**/*.ts", 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/types/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/ts-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./dist", 5 | "rootDir": "./src", 6 | "outDir": "./dist" 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/types/types/tsconfig.prof.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tiny-calc/tool-config/tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "./temp", 5 | "rootDir": ".", 6 | "outDir": "./temp" 7 | }, 8 | "include": ["bench/profile.ts"] 9 | } 10 | --------------------------------------------------------------------------------