This commit is contained in:
Daniel Lehenbauer 2020-12-12 22:30:53 +00:00
Родитель ee7d719274
Коммит 4b2e098640
20 изменённых файлов: 459 добавлений и 0 удалений

Просмотреть файл

@ -3,6 +3,7 @@ dependencies:
'@rush-temp/es': 'file:projects/es.tgz'
'@rush-temp/eslint-config': 'file:projects/eslint-config.tgz_eslint@7.14.0+typescript@4.1.2'
'@rush-temp/example': 'file:projects/example.tgz'
'@rush-temp/frugallist': 'file:projects/frugallist.tgz'
'@rush-temp/graph': 'file:projects/graph.tgz'
'@rush-temp/handletable': 'file:projects/handletable.tgz'
'@rush-temp/heap': 'file:projects/heap.tgz'
@ -2875,6 +2876,23 @@ packages:
integrity: sha512-hKDbQi6qxpzLLPswZI9hv3c6UaImw/3ypjPw8TxBdBgch4t8DrpjtFjKZOr5B7mgPdAk7ZA67NF2vY5xTM+a6g==
tarball: 'file:projects/example.tgz'
version: 0.0.0
'file:projects/frugallist.tgz':
dependencies:
'@types/mocha': 8.0.4
'@types/node': 14.14.10
best-random: 1.0.3
eslint: 7.14.0
hotloop: 1.2.0
mocha: 8.2.1
rimraf: 3.0.2
ts-node: 9.0.0_typescript@4.1.2
typescript: 4.1.2
dev: false
name: '@rush-temp/frugallist'
resolution:
integrity: sha512-VbPT6NIgsrDGg3UU/3y5ORe4J+Mqy24aXfgzvM48RtdM2CSMxnM2lVWZliJstSlz8q+0BGi2+fyPc5+bqXCpDA==
tarball: 'file:projects/frugallist.tgz'
version: 0.0.0
'file:projects/graph.tgz':
dependencies:
'@types/mocha': 8.0.4
@ -3059,6 +3077,7 @@ specifiers:
'@rush-temp/es': 'file:./projects/es.tgz'
'@rush-temp/eslint-config': 'file:./projects/eslint-config.tgz'
'@rush-temp/example': 'file:./projects/example.tgz'
'@rush-temp/frugallist': 'file:./projects/frugallist.tgz'
'@rush-temp/graph': 'file:./projects/graph.tgz'
'@rush-temp/handletable': 'file:./projects/handletable.tgz'
'@rush-temp/heap': 'file:./projects/heap.tgz'

Просмотреть файл

@ -0,0 +1,6 @@
module.exports = {
"extends": [ "@tiny-calc/eslint-config" ],
parserOptions: {
project: './tsconfig.eslint.json',
},
}

8
packages/common/datastructures/frugallist/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
# NPM dependencies
/node_modules
# Build artifacts
/dist
# Typescript incremental build cache
/*.tsbuildinfo

Просмотреть файл

@ -0,0 +1,21 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { getTestArgs } from "hotloop";
import { forEachBench } from "./foreach";
const { count } = getTestArgs();
const list: number[] = new Array(count).fill(0).map((value, index) => index);
forEachBench(
"Array",
count,
list,
(array, callback) => {
for (let i = 0; i < array.length; i++) {
callback(array[i]);
}
}
)

Просмотреть файл

@ -0,0 +1,22 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { getTestArgs } from "hotloop";
import { FrugalList, FrugalList_push, FrugalList_forEach } from "../src";
import { forEachBench } from "./foreach";
const { count } = getTestArgs();
let list: FrugalList<number> = undefined;
for (let i = 0; i < count; i++) {
list = FrugalList_push(list, i);
}
forEachBench(
"FrugalList",
count,
list,
FrugalList_forEach
)

Просмотреть файл

@ -0,0 +1,22 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { getTestArgs } from "hotloop";
import { TwoField, TwoField_push, TwoField_forEach } from "./impl/twofield";
import { forEachBench } from "./foreach";
const { count } = getTestArgs();
const list: TwoField<number> = {};
for (let i = 0; i < count; i++) {
TwoField_push(list, i);
}
forEachBench(
"TwoField",
count,
list,
TwoField_forEach
)

Просмотреть файл

@ -0,0 +1,43 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { strict as assert } from "assert";
import { benchmark } from "hotloop";
let consumedCount = 0;
let consumedCache: any;
export function forEachBench<TSelf, TConsumer>(
name: string,
count: number,
self: TSelf,
forEach: (self: TSelf, callback: (consumer: TConsumer) => void) => void
): void {
// Sanity check that list was initialized correctly.
let sum = 0;
forEach(self, () => {
sum++;
});
assert.equal(sum, count);
benchmark(`${name}: ForEach(length=${count})`, () => {
forEach(self, (item) => {
// Paranoid defense against dead code elimination.
consumedCount++;
consumedCount |= 0;
if (consumedCount === 0) {
consumedCache = item;
}
});
});
}
// Prevent v8's optimizer from identifying 'cached' as an unused value.
process.on('exit', () => {
if (consumedCount === -1) {
console.log(`Ignore this: ${consumedCache}`);
}
});

Просмотреть файл

@ -0,0 +1,45 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
export type TwoFieldItem<T> = Exclude<T, undefined>
export type TwoField<T> = {
item0?: T;
items?: T[];
}
export function TwoField_push<T>(self: TwoField<T>, consumer: TwoFieldItem<T>) {
if (self.item0 === undefined) {
self.item0 = consumer;
} else if (self.items === undefined) {
self.items = [consumer];
} else {
self.items.push(consumer);
}
}
export function TwoField_delete<T>(self: TwoField<T>, consumer: TwoFieldItem<T>) {
if (self.item0 === undefined) {
self.item0 = consumer;
} else if (self.items === undefined) {
self.items = [consumer];
} else {
self.items.push(consumer);
}
}
export function TwoField_forEach<T>(self: TwoField<T>, callback: (consumer: T) => void): void {
if (self.item0 === undefined) {
return;
}
callback(self.item0);
if (self.items !== undefined) {
for (let i = 0; i < self.items.length; i++) {
callback(self.items[i]);
}
}
}

Просмотреть файл

@ -0,0 +1,36 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { run } from "hotloop";
// eslint-disable-next-line no-void
void (async () => {
let count = 0;
console.group(`ConsumerSet (length=${count})`)
await run([
{ "path": "./foreach-array.ts", args: { count }},
{ "path": "./foreach-frugallist.ts", args: { count }},
{ "path": "./foreach-twofield.ts", args: { count }},
]);
console.groupEnd();
count = 1;
console.group(`ConsumerSet (length=${count})`)
await run([
{ "path": "./foreach-array.ts", args: { count }},
{ "path": "./foreach-frugallist.ts", args: { count }},
{ "path": "./foreach-twofield.ts", args: { count }},
]);
console.groupEnd();
count = 2;
console.group(`ConsumerSet (length=${count})`)
await run([
{ "path": "./foreach-array.ts", args: { count }},
{ "path": "./foreach-frugallist.ts", args: { count }},
{ "path": "./foreach-twofield.ts", args: { count }},
]);
console.groupEnd();
})();

Просмотреть файл

@ -0,0 +1,33 @@
{
"name": "@tiny-calc/frugallist",
"version": "0.0.0-alpha.5",
"main": "dist/index.js",
"sideEffects": "false",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/tiny-calc.git"
},
"author": "Microsoft",
"license": "MIT",
"scripts": {
"bench": "cd bench && ts-node index.ts",
"build": "tsc",
"clean": "rimraf ./dist *.build.log",
"dev": "npm run build -- --watch",
"lint": "eslint --ext=ts --format visualstudio src",
"test": "mocha -r ts-node/register test/**/*.spec.ts"
},
"devDependencies": {
"@tiny-calc/eslint-config": "0.0.0-alpha.5",
"@tiny-calc/ts-config": "0.0.0-alpha.5",
"@types/mocha": "^8.0.4",
"@types/node": "^14.14.10",
"best-random": "^1.0.3",
"eslint": "^7.13.0",
"hotloop": "^1.2.0",
"mocha": "^8.2.1",
"rimraf": "^3.0.2",
"ts-node": "^9.0.0",
"typescript": "^4.0.5"
}
}

Просмотреть файл

@ -0,0 +1,18 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { FrugalList, FrugalListItem } from "./types";
export function FrugalList_forEach<T>(self: FrugalList<T>, callback: (value: FrugalListItem<T>) => void): void {
if (self !== undefined) {
if (Array.isArray(self)) {
for (let index = 0; index < self.length; index++) {
callback(self[index]);
}
} else {
callback(/* value: */ self);
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
export { FrugalList, FrugalListItem } from "./types";
export { FrugalList_forEach } from "./forEach";
export { FrugalList_push } from "./push";
export { FrugalList_removeFirst } from "./removeFirst";

Просмотреть файл

@ -0,0 +1,19 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { FrugalList, FrugalListItem } from "./types";
export function FrugalList_push<T>(self: FrugalList<T>, value: FrugalListItem<T>): FrugalList<T> {
if (self === undefined) {
return value;
}
if (Array.isArray(self)) {
self.push(value);
return self;
} else {
return [self, value];
}
}

Просмотреть файл

@ -0,0 +1,21 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { FrugalList, FrugalListItem } from "./types";
export function FrugalList_removeFirst<T>(self: FrugalList<T>, value: FrugalListItem<T>): FrugalList<T> {
if (self === value) {
return undefined;
}
if (Array.isArray(self)) {
const index = self.indexOf(value);
if (index >= 0) {
self.splice(/* start: */ index, /* deleteCount: */ 1);
}
}
return self;
}

Просмотреть файл

@ -0,0 +1,11 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* Must not store the value 'undefined' or any Array type inside a FrugalList.
*/
export type FrugalListItem<T> = Exclude<T, undefined | Array<any>>; // eslint-disable-line @typescript-eslint/no-explicit-any
export type FrugalList<T> = undefined | FrugalListItem<T> | Array<FrugalListItem<T>>;

Просмотреть файл

@ -0,0 +1,57 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import "mocha";
import { TestFixture } from "./testfixture";
describe("FrugalList", () => {
const values = [0, 1, 2];
let list: TestFixture<number>;
beforeEach(() => {
list = new TestFixture<number>();
});
it("push", () => {
for (const value of values) {
list.push(value);
}
});
it("remove in order", () => {
for (const value of values) {
list.push(value);
}
for (const value of values) {
list.removeFirst(value);
}
});
it("remove in reverse order", () => {
for (const value of values) {
list.push(value);
}
const reversed = values.slice(0).reverse();
for (const value of reversed) {
list.removeFirst(value);
}
});
it("remove with duplicates", () => {
for (const value of values) {
list.push(value);
}
for (const value of values) {
list.push(value);
}
for (const value of values) {
list.removeFirst(value);
}
});
});

Просмотреть файл

@ -0,0 +1,45 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import "mocha";
import { strict as assert } from "assert";
import {
FrugalList,
FrugalList_push,
FrugalListItem,
FrugalList_removeFirst,
FrugalList_forEach
} from "../src";
export class TestFixture<T> {
private actual: FrugalList<T>;
private expected: FrugalListItem<T>[] = [];
public push(item: FrugalListItem<T>): void {
this.actual = FrugalList_push(this.actual, item);
this.expected.push(item);
this.vet();
}
public removeFirst(item: FrugalListItem<T>): void {
this.actual = FrugalList_removeFirst(this.actual, item);
const index = this.expected.indexOf(item);
if (index >= 0) {
this.expected.splice(/* start: */ index, /* deleteCount: */ 1);
}
this.vet();
}
private vet() {
const actual: FrugalListItem<T>[] = [];
FrugalList_forEach(this.actual, (item) => {
actual.push(item);
})
assert.deepEqual(actual, this.expected);
}
}

Просмотреть файл

@ -0,0 +1,9 @@
{
// extend your base config so you don't have to redefine your compilerOptions
"extends": "./tsconfig.json",
"include": [
"src/**/*.ts",
"test/**/*.ts",
"bench/**/*.ts",
]
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"extends": "@tiny-calc/ts-config/tsconfig.json",
"compilerOptions": {
"declarationDir": "./dist",
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src/**/*.ts"]
}

Просмотреть файл

@ -46,6 +46,12 @@
"reviewCategory": "production",
"versionPolicyName": "public"
},
{
"packageName": "@tiny-calc/frugallist",
"projectFolder": "packages/common/datastructures/frugallist",
"reviewCategory": "production",
"versionPolicyName": "public"
},
{
"packageName": "@tiny-calc/handletable",
"projectFolder": "packages/common/datastructures/handletable",