├── src
├── app
│ ├── app.component.css
│ ├── app.component.html
│ ├── dialogs
│ │ ├── confirmation-dialog
│ │ │ ├── confirmation-dialog.component.css
│ │ │ ├── confirmation-dialog.component.html
│ │ │ └── confirmation-dialog.component.ts
│ │ └── animation-dialog
│ │ │ ├── animation-dialog.component.css
│ │ │ ├── animation-dialog.component.ts
│ │ │ └── animation-dialog.component.html
│ ├── app.component.ts
│ ├── providers
│ │ ├── custom-problem.service.ts
│ │ ├── animation-data.service.ts
│ │ └── progress.service.ts
│ ├── app.module.ts
│ ├── select-problem
│ │ ├── select-problem.component.css
│ │ ├── select-problem.component.html
│ │ └── select-problem.component.ts
│ └── solve-problem
│ │ └── solve-problem.component.css
├── favicon.ico
├── assets
│ ├── images
│ │ ├── Logo.png
│ │ └── Logo.psd
│ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ ├── codemirror
│ │ ├── languages
│ │ │ └── javascript
│ │ │ │ ├── typescript.html
│ │ │ │ ├── json-ld.html
│ │ │ │ ├── index.html
│ │ │ │ └── test.js
│ │ └── codemirror.css
│ └── problems
│ │ ├── problem-directory.json
│ │ ├── chain-matrix-multiplication.dp.json
│ │ ├── levenshtein-distance.dp.json
│ │ ├── knapsack-with-repetition.dp.json
│ │ ├── shortest-path-in-dag.dp.json
│ │ ├── longest-path-in-dag.dp.json
│ │ ├── knapsack-without-repetition.dp.json
│ │ ├── longest-common-subsequence.dp.json
│ │ └── longest-increasing-subsequence.dp.json
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── styles.css
├── tsconfig.app.json
├── tslint.json
├── browserslist
├── main.ts
├── tsconfig.spec.json
├── test.ts
├── karma.conf.js
├── index.html
├── theme.scss
└── polyfills.ts
├── dist-utility-files
├── .gitignore
├── package.json
└── server.js
├── e2e
├── src
│ ├── app.po.ts
│ └── app.e2e-spec.ts
└── tsconfig.e2e.json
├── .editorconfig
├── tsconfig.json
├── .gitignore
├── LICENSE.md
├── package.json
├── tslint.json
├── README.md
└── angular.json
/src/app/app.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dist-utility-files/.gitignore:
--------------------------------------------------------------------------------
1 | # .gitignore files for dist folder
2 |
3 | # Node Modules
4 | /node_modules
5 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debkbanerji/dynamic-programming-visualization/HEAD/src/favicon.ico
--------------------------------------------------------------------------------
/src/assets/images/Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debkbanerji/dynamic-programming-visualization/HEAD/src/assets/images/Logo.png
--------------------------------------------------------------------------------
/src/assets/images/Logo.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debkbanerji/dynamic-programming-visualization/HEAD/src/assets/images/Logo.psd
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | VERSION: require('../../package.json').version
4 | };
5 |
--------------------------------------------------------------------------------
/src/assets/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debkbanerji/dynamic-programming-visualization/HEAD/src/assets/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/src/assets/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debkbanerji/dynamic-programming-visualization/HEAD/src/assets/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debkbanerji/dynamic-programming-visualization/HEAD/src/assets/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/src/assets/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/debkbanerji/dynamic-programming-visualization/HEAD/src/assets/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/src/app/dialogs/confirmation-dialog/confirmation-dialog.component.css:
--------------------------------------------------------------------------------
1 | .mat-raised-button {
2 | margin: 4px !important;
3 | margin-left: 0 !important;
4 | margin-right: 8px !important;
5 | }
6 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.css']
7 | })
8 | export class AppComponent {
9 | }
10 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #EEEEEE;
3 | overflow: visible;
4 | }
5 |
6 | mat-card {
7 | margin: 5px;
8 | }
9 |
10 | mat-expansion-panel {
11 | margin: 5px !important;
12 | }
13 |
14 | .CodeMirror {
15 | height: auto;
16 | }
17 |
--------------------------------------------------------------------------------
/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 4
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "es2015",
6 | "types": [
7 | "node"
8 | ]
9 | },
10 | "exclude": [
11 | "src/test.ts",
12 | "**/*.spec.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('workspace-project App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to app!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/browserslist:
--------------------------------------------------------------------------------
1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed
5 | > 0.5%
6 | last 2 versions
7 | Firefox ESR
8 | not dead
9 | # IE 9-11
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.log(err));
13 |
--------------------------------------------------------------------------------
/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "module": "commonjs",
6 | "types": [
7 | "jasmine",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "test.ts",
13 | "polyfills.ts"
14 | ],
15 | "include": [
16 | "**/*.spec.ts",
17 | "**/*.d.ts"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/dist-utility-files/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dynamic-programming-visualization",
3 | "version": "0.0.0",
4 | "main": "server.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "start": "node server.js"
8 | },
9 | "author": "Deb Banerji",
10 | "license": "MIT",
11 | "dependencies": {
12 | "body-parser": "^1.17.2",
13 | "express": "^4.14.0",
14 | "path": "^0.12.7"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "moduleResolution": "node",
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "node_modules/@types"
14 | ],
15 | "lib": [
16 | "es2017",
17 | "dom"
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/dialogs/confirmation-dialog/confirmation-dialog.component.html:
--------------------------------------------------------------------------------
1 |
2 |
{{data.title}}
3 |
6 |
7 | {{data.cancelText}}
8 | {{data.acceptText}}
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/app/providers/custom-problem.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 |
3 | @Injectable({
4 | providedIn: 'root'
5 | })
6 | export class CustomProblemService {
7 |
8 | private customProblem: any = null;
9 |
10 | constructor() {
11 | }
12 |
13 | setCustomProblem(problem: any) {
14 | this.customProblem = problem;
15 | }
16 |
17 | customProblemExists(): boolean {
18 | return this.customProblem !== null
19 | }
20 |
21 | popCustomProblem() {
22 | const result = this.customProblem;
23 | this.customProblem = null;
24 | return result;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/dialogs/confirmation-dialog/confirmation-dialog.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Inject} from '@angular/core';
2 | import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material';
3 |
4 | @Component({
5 | selector: 'app-confirmation-solution-dialog',
6 | templateUrl: './confirmation-dialog.component.html',
7 | styleUrls: ['./confirmation-dialog.component.css']
8 | })
9 | export class ConfirmationDialogComponent {
10 |
11 | constructor(
12 | public dialogRef: MatDialogRef,
13 | @Inject(MAT_DIALOG_DATA) public data: any) {
14 | }
15 |
16 | onNoClick(): void {
17 | this.dialogRef.close();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false,
7 | VERSION: require('../../package.json').version
8 | };
9 |
10 | /*
11 | * In development mode, to ignore zone related error stack frames such as
12 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
13 | * import the following file, but please comment it out in production mode
14 | * because it will have performance impact when throw error
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /tmp
4 | /out-tsc
5 |
6 | # dependencies
7 | /node_modules
8 |
9 | # IDEs and editors
10 | /.idea
11 | .project
12 | .classpath
13 | .c9/
14 | *.launch
15 | .settings/
16 | *.sublime-workspace
17 |
18 | # IDE - VSCode
19 | .vscode/*
20 | !.vscode/settings.json
21 | !.vscode/tasks.json
22 | !.vscode/launch.json
23 | !.vscode/extensions.json
24 |
25 | # misc
26 | /.sass-cache
27 | /connect.lock
28 | /coverage
29 | /libpeerconnection.log
30 | npm-debug.log
31 | testem.log
32 | /typings
33 |
34 | # e2e
35 | /e2e/*.js
36 | /e2e/*.map
37 |
38 | # System Files
39 | .DS_Store
40 | Thumbs.db
41 |
42 | # package-lock
43 | package-lock.json
44 |
45 | # root build
46 | /3rdpartylicenses.txt
47 | /favicon.ico
48 | /index.html
49 | /*.bundle.js
50 | /*.bundle.css
51 | /assets
52 |
--------------------------------------------------------------------------------
/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: ['html', 'lcovonly'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false
30 | });
31 | };
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | License
2 |
3 | Copyright (c) [2017-present] [Deb Banerji]
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. Additionally, all copies or
14 | substantial portions of the software shall include credit to the original
15 | author.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
24 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Dynamic Programming
6 |
7 |
8 |
9 |
10 |
11 |
12 |
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/theme.scss:
--------------------------------------------------------------------------------
1 | @import '~@angular/material/theming';
2 |
3 | @include mat-core();
4 |
5 | $app-primary: mat-palette($mat-red, A400);
6 | $app-accent: mat-palette($mat-pink);
7 | $app-warn: mat-palette($mat-red);
8 |
9 | $app-theme: mat-light-theme($app-primary, $app-accent, $app-warn);
10 |
11 | @include angular-material-theme($app-theme);
12 |
13 | .dark-theme {
14 | color: $light-primary-text;
15 | $dark-primary: mat-palette($mat-pink);
16 | $dark-accent: mat-palette($mat-pink, A200, A100, A400);
17 | $dark-warn: mat-palette($mat-red);
18 |
19 | $dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn);
20 |
21 | @include angular-material-theme($dark-theme);
22 |
23 | background-color: #303030 !important;
24 | }
25 |
26 | .main-content-container {
27 | min-width: 100vw;
28 | min-height: 100vh;
29 | }
30 |
31 | input {
32 | background-color: rgba(0, 0, 0, 0.1);
33 | border: rgba(0, 0, 0, 0.5);
34 | }
35 |
36 | mat-dialog-container {
37 | padding: 0 !important;
38 | }
39 |
40 | app-animation-dialog {
41 | overflow: hidden;
42 | width: 95vw;
43 | }
44 |
45 |
46 | .mat-progress-bar .mat-progress-bar-fill {
47 | transition-duration: 50ms !important;
48 | }
49 |
50 | .text-grey {
51 | color: #757575;
52 | }
53 |
54 | .text-red {
55 | color: #DC3545;
56 | }
57 |
58 | .text-orange {
59 | color: #EF6C00;
60 | }
61 |
62 | .text-yellow {
63 | color: #F9A825;
64 | }
65 |
66 | .text-green {
67 | color: #28A745;
68 | }
69 |
--------------------------------------------------------------------------------
/src/app/providers/animation-data.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 |
3 | @Injectable({
4 | providedIn: 'root'
5 | })
6 | export class AnimationDataService {
7 |
8 | public title: string;
9 | public subtitle: string;
10 | public result;
11 | public solution;
12 | public input: any;
13 | public log: any;
14 | public useAuxiliaryTable: boolean;
15 | public mainTableDimension1: any;
16 | public mainTableDimension2: any;
17 | public transposeTable: boolean;
18 |
19 | constructor() {
20 | }
21 |
22 | initialize(
23 | title,
24 | subtitle,
25 | result,
26 | solution,
27 | input,
28 | log,
29 | useAuxiliaryTable: boolean,
30 | mainTableDimension1,
31 | mainTableDimension2,
32 | transposeTable: boolean
33 | ) {
34 | this.title = title;
35 | this.subtitle = subtitle;
36 | this.result = result;
37 | this.solution = solution;
38 | this.input = input;
39 | this.log = JSON.parse(JSON.stringify(log)); // copy log to avoid mutation issues
40 | this.useAuxiliaryTable = useAuxiliaryTable;
41 | this.mainTableDimension1 = mainTableDimension1;
42 | this.mainTableDimension2 = mainTableDimension2;
43 | this.transposeTable = transposeTable;
44 | }
45 |
46 | clear() {
47 | this.title = null;
48 | this.subtitle = null;
49 | this.result = null;
50 | this.solution = null;
51 | this.input = null;
52 | this.log = null;
53 | this.useAuxiliaryTable = null;
54 | this.mainTableDimension1 = null;
55 | this.mainTableDimension2 = null;
56 | this.transposeTable = null;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/assets/codemirror/languages/javascript/typescript.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeMirror: TypeScript mode
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
24 |
25 |
26 | TypeScript mode
27 |
28 |
29 |
51 |
52 |
59 |
60 | This is a specialization of the JavaScript mode .
61 |
62 |
--------------------------------------------------------------------------------
/dist-utility-files/server.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const os = require('os');
3 | const path = require('path');
4 | const bodyParser = require('body-parser');
5 |
6 | const app = express();
7 |
8 | const port = process.env.PORT || 3000;
9 | /*
10 | To set a port other than 3000:
11 | in Unix:
12 |
13 | $ PORT=1234 node server.js
14 |
15 | in Windows:
16 |
17 | set PORT=1234
18 | node server.js
19 | */
20 |
21 | const ifaces = os.networkInterfaces();
22 |
23 | Object.keys(ifaces).forEach(function (ifname) {
24 | let alias = 0;
25 |
26 | ifaces[ifname].forEach(function (iface) {
27 | if ('IPv4' !== iface.family || iface.internal !== false) {
28 | // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
29 | return;
30 | }
31 |
32 | if (alias >= 1) {
33 | // this single interface has multiple ipv4 addresses
34 | console.log(ifname + ':' + alias, iface.address);
35 | } else {
36 | // this interface has only one ipv4 adress
37 | console.log(ifname, iface.address);
38 | }
39 | ++alias;
40 | });
41 | });
42 |
43 | // parse application/x-www-form-urlencoded
44 | app.use(bodyParser.urlencoded({extended: false}));
45 |
46 | // parse application/json
47 | app.use(bodyParser.json());
48 |
49 | const frontendFolder = path.join(__dirname, 'dynamic-programming-visualization');
50 | // Point static path to frontend folder
51 | app.use(express.static(frontendFolder));
52 | console.log("Serving static from " + frontendFolder);
53 |
54 | // Catch all other routes and return the index file
55 | app.get('*', (req, res) => {
56 | res.sendFile(path.join(frontendFolder, 'index.html'));
57 | });
58 |
59 | app.listen(port, function () {
60 | console.log('listening on port ' + port);
61 | console.log('press Ctrl + C to shut down server');
62 | });
63 |
--------------------------------------------------------------------------------
/src/assets/problems/problem-directory.json:
--------------------------------------------------------------------------------
1 | {
2 | "version":"2019.8.8-master",
3 | "sections": [
4 | {
5 | "name": "Strings and Subsequences",
6 | "problems": [
7 | {
8 | "name": "Longest Increasing Subsequence",
9 | "id": "longest-increasing-subsequence"
10 | },
11 | {
12 | "name": "Longest Common Subsequence",
13 | "id": "longest-common-subsequence"
14 | },
15 | {
16 | "name": "Levenshtein Distance",
17 | "id": "levenshtein-distance"
18 | }
19 | ]
20 | },
21 | {
22 | "name": "Knapsack Variations",
23 | "problems": [
24 | {
25 | "name": "Knapsack With Repetition",
26 | "id": "knapsack-with-repetition"
27 | },
28 | {
29 | "name": "Knapsack Without Repetition",
30 | "id": "knapsack-without-repetition"
31 | }
32 | ]
33 | },
34 | {
35 | "name": "Intervals",
36 | "problems": [
37 | {
38 | "name": "Chain Matrix Multiplication",
39 | "id": "chain-matrix-multiplication"
40 | }
41 | ]
42 | },
43 | {
44 | "name": "Directed Acyclic Graphs",
45 | "problems": [
46 | {
47 | "name": "Shortest Path in DAG",
48 | "id": "shortest-path-in-dag"
49 | },
50 | {
51 | "name": "Longest Path in DAG",
52 | "id": "longest-path-in-dag"
53 | }
54 | ]
55 | }
56 | ]
57 | }
58 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dynamic-programming-visualization",
3 | "version": "2019.8.8",
4 | "scripts": {
5 | "ng": "ng",
6 | "develop": "ng serve",
7 | "start": "cd dist && npm start",
8 | "postinstall": "npm run build && cd dist && npm install",
9 | "build": "ng build --prod && cp -r dist-utility-files/. dist",
10 | "messy-build": "ng build && cp -r dist-utility-files/. dist",
11 | "test": "ng test",
12 | "lint": "ng lint",
13 | "e2e": "ng e2e"
14 | },
15 | "private": true,
16 | "dependencies": {
17 | "@angular-devkit/build-angular": "~0.6.0",
18 | "@angular/animations": "^6.0.0",
19 | "@angular/cdk": "^6.0.0",
20 | "@angular/cli": "~6.0.0",
21 | "@angular/common": "^6.0.0",
22 | "@angular/compiler": "^6.0.0",
23 | "@angular/compiler-cli": "^6.0.0",
24 | "@angular/core": "^6.0.0",
25 | "@angular/forms": "^6.0.0",
26 | "@angular/http": "^6.0.0",
27 | "@angular/language-service": "^6.0.0",
28 | "@angular/material": "^6.0.0",
29 | "@angular/platform-browser": "^6.0.0",
30 | "@angular/platform-browser-dynamic": "^6.0.0",
31 | "@angular/router": "^6.0.0",
32 | "core-js": "^2.5.4",
33 | "hammerjs": "^2.0.8",
34 | "rxjs": "^6.0.0",
35 | "typescript": "~2.7.2",
36 | "zone.js": "^0.8.26"
37 | },
38 | "devDependencies": {
39 | "@types/jasmine": "~2.8.6",
40 | "@types/jasminewd2": "~2.0.3",
41 | "@types/node": "~8.9.4",
42 | "codelyzer": "~4.2.1",
43 | "jasmine-core": "~2.99.1",
44 | "jasmine-spec-reporter": "~4.2.1",
45 | "karma": "~1.7.1",
46 | "karma-chrome-launcher": "~2.2.0",
47 | "karma-coverage-istanbul-reporter": "~1.4.2",
48 | "karma-jasmine": "~1.1.1",
49 | "karma-jasmine-html-reporter": "^0.2.2",
50 | "protractor": "~5.3.0",
51 | "ts-node": "~5.0.1",
52 | "tslint": "~5.9.1"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/assets/codemirror/languages/javascript/json-ld.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeMirror: JSON-LD mode
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
29 | JSON-LD mode
30 |
31 |
32 |
61 |
62 |
70 |
71 | This is a specialization of the JavaScript mode .
72 |
73 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import {BrowserModule} from '@angular/platform-browser';
2 | import {NgModule} from '@angular/core';
3 |
4 | import 'hammerjs';
5 |
6 | import {AppComponent} from './app.component';
7 | import {SolveProblemComponent} from './solve-problem/solve-problem.component';
8 | import {RouterModule, Routes} from '@angular/router';
9 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
10 | import {FormsModule} from '@angular/forms';
11 | import {
12 | MatButtonModule,
13 | MatCardModule,
14 | MatCheckboxModule,
15 | MatExpansionModule,
16 | MatFormFieldModule,
17 | MatInputModule,
18 | MatSelectModule,
19 | MatDialogModule,
20 | MatTabsModule,
21 | MatProgressBarModule,
22 | MatSlideToggleModule,
23 | MatProgressSpinnerModule,
24 | MatTooltipModule
25 | } from '@angular/material';
26 | import {HttpClientModule} from '@angular/common/http';
27 | import {ConfirmationDialogComponent} from './dialogs/confirmation-dialog/confirmation-dialog.component';
28 | import {SelectProblemComponent} from './select-problem/select-problem.component';
29 | import {AnimationDialogComponent} from './dialogs/animation-dialog/animation-dialog.component';
30 |
31 | const routes: Routes = [
32 | {path: 'problem/:problem-name', component: SolveProblemComponent},
33 | {path: 'select-problem', component: SelectProblemComponent},
34 | {path: '**', redirectTo: 'select-problem', pathMatch: 'full'} // Default route
35 | ];
36 |
37 |
38 | @NgModule({
39 | declarations: [
40 | AppComponent,
41 | SolveProblemComponent,
42 | ConfirmationDialogComponent,
43 | AnimationDialogComponent,
44 | SelectProblemComponent
45 | ],
46 | imports: [
47 | BrowserModule,
48 | BrowserAnimationsModule,
49 | HttpClientModule,
50 | FormsModule,
51 | MatCardModule,
52 | MatFormFieldModule,
53 | MatInputModule,
54 | MatSelectModule,
55 | MatExpansionModule,
56 | MatTabsModule,
57 | MatButtonModule,
58 | MatCheckboxModule,
59 | MatSlideToggleModule,
60 | MatDialogModule,
61 | MatProgressBarModule,
62 | MatProgressSpinnerModule,
63 | MatTooltipModule,
64 | RouterModule.forRoot(routes)
65 | ],
66 | entryComponents: [
67 | ConfirmationDialogComponent,
68 | AnimationDialogComponent
69 | ],
70 | providers: [],
71 | bootstrap: [AppComponent]
72 | })
73 | export class AppModule {
74 | }
75 |
--------------------------------------------------------------------------------
/src/app/select-problem/select-problem.component.css:
--------------------------------------------------------------------------------
1 | .main-content-container {
2 | padding: 5px;
3 | }
4 |
5 | #main-title-div {
6 | text-align: center;
7 | margin-top: 5px;
8 | width: 100%;
9 | }
10 |
11 | .main-title-container {
12 | display: inline-block;
13 | }
14 |
15 | .main-title {
16 | font-weight: bold;
17 | margin: 0;
18 | display: inline-block;
19 | }
20 |
21 | .main-title-logo-span {
22 | word-space: nowrap;
23 | display: -moz-inline-stack;
24 | display: inline-block;
25 | vertical-align: top;
26 | }
27 |
28 | .logo {
29 | display: inline-block;
30 | float: left;
31 | width: 0.9em;
32 | height: 0.9em;
33 | position: relative;
34 | top: 2px;
35 | margin-right: 8px;
36 | }
37 |
38 | .subtitle-div-container {
39 | text-align: center;
40 | }
41 |
42 | .subtitle-div {
43 | display: inline-block;
44 | }
45 |
46 | .subtitle-div-section {
47 | margin-top: 8px;
48 | margin-bottom: 8px;
49 | display: inline-block;
50 | }
51 |
52 | .external-link {
53 | cursor: pointer;
54 | color: unset;
55 | }
56 |
57 | .subtitle-item {
58 | padding-left: 8px;
59 | padding-right: 8px;
60 | }
61 |
62 | .source-code-link {
63 | vertical-align: text-bottom;
64 | }
65 |
66 | .version-number {
67 | vertical-align: bottom;
68 | }
69 |
70 | .dark-mode-toggle {
71 | padding-top: 8px;
72 | }
73 |
74 | input {
75 | background-color: transparent;
76 | }
77 |
78 | #custom-problem-error-text {
79 | margin-top: 4px;
80 | }
81 |
82 | .mat-expansion-panel {
83 | margin: 0 !important;
84 | }
85 |
86 | .expanded-panel {
87 | margin-top: 10px !important;
88 | margin-bottom: 10px !important;
89 | }
90 |
91 | .problem-count-summary {
92 | margin-left: 8px;
93 | }
94 |
95 | .problem-file-version {
96 | margin-left: 2px;
97 | opacity: 0.5;
98 | }
99 |
100 | .section-title {
101 | font-weight: bold;
102 | margin-left: 5px;
103 | margin-top: 10px;
104 | margin-bottom: 5px;
105 | }
106 |
107 | .problem-link {
108 | cursor: pointer;
109 | font-weight: 600;
110 | padding: 10px;
111 | }
112 |
113 | .problem-link:hover {
114 | background-color: rgba(0.7, 0.7, 0.7, 0.1);
115 | }
116 |
117 | .section-separator {
118 | border-top-color: gray;
119 | }
120 |
121 | .custom-problem-card {
122 | margin: 10px;
123 | }
124 |
125 | .custom-problem-container {
126 | margin-left: 5px;
127 | }
128 |
129 | .progress-container {
130 | font-weight: normal;
131 | }
132 |
133 | .page-footer {
134 | display: flex;
135 | justify-content: center;
136 | }
137 |
--------------------------------------------------------------------------------
/src/app/dialogs/animation-dialog/animation-dialog.component.css:
--------------------------------------------------------------------------------
1 | .no-padding {
2 | padding: 0;
3 | }
4 |
5 | mat-card {
6 | padding: 5px;
7 | }
8 |
9 | .main-header {
10 | font-weight: bold;
11 | font-size: large;
12 | }
13 |
14 | .result-header {
15 | float: right;
16 | padding-top: 3px;
17 | padding-right: 3px;
18 | }
19 |
20 | .main-container {
21 | width: 75vw;
22 | max-height: 95vh;
23 | padding: 5px;
24 | overflow-y: auto;
25 | overflow-x: hidden;
26 | }
27 |
28 | .main-header-container {
29 | padding-bottom: 2px;
30 | }
31 |
32 | mat-expansion-panel-header {
33 | padding-left: 8px !important;
34 | height: 36px !important;
35 | }
36 |
37 | .section-card-header {
38 | padding-left: 3px;
39 | display: inline-block;
40 | width: 100%;
41 | }
42 |
43 | .slightly-bold-label {
44 | font-weight: 600;
45 | }
46 |
47 | .result-card {
48 | height: 36px;
49 | }
50 |
51 | .table-card {
52 | height: 100%;
53 | flex-grow: 1;
54 | }
55 |
56 | /*DP Table*/
57 |
58 | .dp-table {
59 | width: 100%;
60 | height: 100%;
61 | }
62 |
63 | .dp-tr {
64 | height: 5vw;
65 | }
66 |
67 | .dp-td {
68 | width: 5vw;
69 | height: 5vw;
70 | padding: 0;
71 | border: 1px solid #AAAAAA;
72 | text-align: center;
73 | }
74 |
75 | .dp-td:after {
76 | }
77 |
78 | .dp-table-data {
79 | position: absolute;
80 | /*top: 0;*/
81 | /*bottom: 0;*/
82 | /*left: 0;*/
83 | /*right: 0;*/
84 | height: 100%;
85 | }
86 |
87 | .glyphicon-stop {
88 | padding-bottom: 2px;
89 | padding-right: 2px;
90 | }
91 |
92 | .get-entry {
93 | color: #00c5ff;
94 | }
95 |
96 | .set-entry {
97 | color: #ff1744;
98 | }
99 |
100 | .get-animation {
101 | background-color: rgba(0, 197, 255, 0.25);
102 | }
103 |
104 | .set-animation {
105 | background-color: rgba(255, 23, 68, 0.25);
106 | }
107 |
108 | .frames-progress-bar {
109 | margin: 4px;
110 | }
111 |
112 | .controls-container {
113 | width: 100%;
114 | display: inline-block;
115 | }
116 |
117 | .controls-left-container {
118 | display: inline-block;
119 | vertical-align: top;
120 | }
121 |
122 | .controls-center-container {
123 | display: inline-block;
124 | text-align: center;
125 | vertical-align: top;
126 | }
127 |
128 | .controls-right-container {
129 | display: inline-block;
130 | text-align: right;
131 | vertical-align: top;
132 | }
133 |
134 | .operation-count-container {
135 | padding-left: 4px;
136 | }
137 |
138 | .operation-count {
139 | font-size: small;
140 | }
141 |
142 | .table-operation-count-container {
143 | display: inline-block;
144 | float: right;
145 | }
146 |
147 | .control-button {
148 | margin: 4px;
149 | }
150 |
151 | .control-glyphicon {
152 | padding-bottom: 4px;
153 | }
154 |
155 | .animation-speed-selector {
156 | padding-top: 8px;
157 | padding-right: 4px;
158 | }
159 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** IE10 and IE11 requires the following for the Reflect API. */
41 | // import 'core-js/es6/reflect';
42 |
43 |
44 | /** Evergreen browsers require these. **/
45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
46 | import 'core-js/es7/reflect';
47 |
48 |
49 | /**
50 | * Web Animations `@angular/platform-browser/animations`
51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
53 | **/
54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
55 |
56 | /**
57 | * By default, zone.js will patch all possible macroTask and DomEvents
58 | * user can disable parts of macroTask/DomEvents patch by setting following flags
59 | */
60 |
61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
64 |
65 | /*
66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
68 | */
69 | // (window as any).__Zone_enable_cross_context_check = true;
70 |
71 | /***************************************************************************************************
72 | * Zone JS is required by default for Angular itself.
73 | */
74 | import 'zone.js/dist/zone'; // Included with Angular CLI.
75 |
76 |
77 |
78 | /***************************************************************************************************
79 | * APPLICATION IMPORTS
80 | */
81 |
--------------------------------------------------------------------------------
/src/app/providers/progress.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 |
3 | const PROBLEM_PREFIX = '__PROBLEM_PROGRESS_DATA__';
4 | const DARK_MODE_MARKER = '__DARK_MODE__';
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class ProgressService {
10 |
11 | private hasLocalStorage;
12 |
13 | constructor() {
14 | const component = this;
15 | component.hasLocalStorage = typeof (Storage) !== 'undefined';
16 | }
17 |
18 | getProblemProgressObjectSetIfNotExists(problemId: string, defaultValue) {
19 | const component = this;
20 | return component.getItemSetIfNotExists(PROBLEM_PREFIX + problemId, defaultValue);
21 | }
22 |
23 | getProblemProgressObjectNullIfNotExists(problemId: string) {
24 | const component = this;
25 | const key = PROBLEM_PREFIX + problemId;
26 | return component.hasItem(key) ? component.getItem(key) : null;
27 | }
28 |
29 | setProblemProgressObjectAsCompletedSetIfNotExists(problemId: string, solutionType: string, defaultValue) {
30 | const component = this;
31 | const key = PROBLEM_PREFIX + problemId;
32 | const currentProgress = component.getItemSetIfNotExists(key, defaultValue);
33 | currentProgress.hasSolvedSolutionTypes[solutionType] = true;
34 | component.setItem(key, currentProgress);
35 | }
36 |
37 | markProblemAsSolutionRevealedSetIfNotExists(problemId: string, defaultValue) {
38 | const component = this;
39 | const key = PROBLEM_PREFIX + problemId;
40 | const currentProgress = component.getItemSetIfNotExists(problemId, defaultValue);
41 | currentProgress.hasRevealedSolution = true;
42 | component.setItem(key, currentProgress);
43 | }
44 |
45 | getDarkModeStatus() {
46 | const component = this;
47 | return component.getItemSetIfNotExists(DARK_MODE_MARKER, false);
48 | }
49 |
50 | setDarkModeStatus(isDarkTheme: boolean) {
51 | const component = this;
52 | return component.setItem(DARK_MODE_MARKER, isDarkTheme);
53 | }
54 |
55 | public getHasLocalStorage(): boolean {
56 | return this.hasLocalStorage;
57 | }
58 |
59 | // Local storage wrapper functions
60 | private deleteItem(key: string): void {
61 | this.assertHasLocalStorage();
62 | localStorage.removeItem(key);
63 | }
64 |
65 | private setItem(key: string, value: any): void {
66 | this.assertHasLocalStorage();
67 | localStorage.setItem(key, JSON.stringify(value));
68 | }
69 |
70 | private hasItem(key: string): boolean {
71 | this.assertHasLocalStorage();
72 | return typeof localStorage[key] !== 'undefined';
73 | }
74 |
75 | private getItemSetIfNotExists(key: string, defaultValue: any): any {
76 | const component = this;
77 | component.assertHasLocalStorage();
78 | const exists = component.hasItem(key);
79 | if (exists) {
80 | return component.getItem(key);
81 | } else {
82 | component.setItem(key, defaultValue);
83 | return defaultValue;
84 | }
85 | }
86 |
87 | private getItem(key: string): any {
88 | this.assertHasLocalStorage();
89 | return JSON.parse(localStorage.getItem(key));
90 | }
91 |
92 | clearAll(): void {
93 | this.assertHasLocalStorage();
94 | localStorage.clear();
95 | }
96 |
97 | private assertHasLocalStorage(): void {
98 | if (!this.getHasLocalStorage()) {
99 | throw 'Local storage not available on browser';
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs/Rx"
22 | ],
23 | "import-spacing": true,
24 | "indent": [
25 | true,
26 | "spaces"
27 | ],
28 | "interface-over-type-literal": true,
29 | "label-position": true,
30 | "max-line-length": [
31 | true,
32 | 140
33 | ],
34 | "member-access": false,
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-arg": true,
47 | "no-bitwise": true,
48 | "no-console": [
49 | true,
50 | "debug",
51 | "info",
52 | "time",
53 | "timeEnd",
54 | "trace"
55 | ],
56 | "no-construct": true,
57 | "no-debugger": true,
58 | "no-duplicate-super": true,
59 | "no-empty": false,
60 | "no-empty-interface": true,
61 | "no-eval": true,
62 | "no-inferrable-types": [
63 | true,
64 | "ignore-params"
65 | ],
66 | "no-misused-new": true,
67 | "no-non-null-assertion": true,
68 | "no-shadowed-variable": true,
69 | "no-string-literal": false,
70 | "no-string-throw": true,
71 | "no-switch-case-fall-through": true,
72 | "no-trailing-whitespace": true,
73 | "no-unnecessary-initializer": true,
74 | "no-unused-expression": true,
75 | "no-use-before-declare": true,
76 | "no-var-keyword": true,
77 | "object-literal-sort-keys": false,
78 | "one-line": [
79 | true,
80 | "check-open-brace",
81 | "check-catch",
82 | "check-else",
83 | "check-whitespace"
84 | ],
85 | "prefer-const": true,
86 | "quotemark": [
87 | true,
88 | "single"
89 | ],
90 | "radix": true,
91 | "semicolon": [
92 | true,
93 | "always"
94 | ],
95 | "triple-equals": [
96 | true,
97 | "allow-null-check"
98 | ],
99 | "typedef-whitespace": [
100 | true,
101 | {
102 | "call-signature": "nospace",
103 | "index-signature": "nospace",
104 | "parameter": "nospace",
105 | "property-declaration": "nospace",
106 | "variable-declaration": "nospace"
107 | }
108 | ],
109 | "unified-signatures": true,
110 | "variable-name": false,
111 | "whitespace": [
112 | true,
113 | "check-branch",
114 | "check-decl",
115 | "check-operator",
116 | "check-separator",
117 | "check-type"
118 | ],
119 | "no-output-on-prefix": true,
120 | "use-input-property-decorator": true,
121 | "use-output-property-decorator": true,
122 | "use-host-property-decorator": true,
123 | "no-input-rename": true,
124 | "no-output-rename": true,
125 | "use-life-cycle-interface": true,
126 | "use-pipe-transform-interface": true,
127 | "component-class-suffix": true,
128 | "directive-class-suffix": true
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/app/solve-problem/solve-problem.component.css:
--------------------------------------------------------------------------------
1 | .row.no-gutter {
2 | margin-left: 0;
3 | margin-right: 0;
4 | }
5 |
6 | .loading-spinner-container {
7 | height: 100vh;
8 | width: 100vw;
9 | text-align: center;
10 | }
11 |
12 | .page-container {
13 | min-height: 100vh;
14 | /*width: 100vw;*/
15 | }
16 |
17 | .page-loading-spinner {
18 | text-align: center;
19 | margin: 25vh auto auto;
20 | }
21 |
22 | .main-header-card {
23 | padding: 5px;
24 | }
25 |
26 | .home-button {
27 | vertical-align: top;
28 | margin: 0;
29 | padding-top: 4px;
30 | padding-left: 4px;
31 | cursor: pointer;
32 | }
33 |
34 | .main-header {
35 | font-weight: bold;
36 | font-size: x-large;
37 | margin: 0;
38 | }
39 |
40 | .dark-mode-toggle {
41 | display: inline-block;
42 | float: right;
43 | padding-right: 6px;
44 | }
45 |
46 | .section-header {
47 | font-weight: bold;
48 | font-size: large;
49 | }
50 |
51 | .row.no-gutter [class*='col-']:not(:first-child),
52 | .row.no-gutter [class*='col-']:not(:last-child) {
53 | padding-right: 0;
54 | padding-left: 0;
55 | }
56 |
57 | #code-card {
58 | padding: 5px;
59 | }
60 |
61 | .code {
62 | font-family: Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, sans-serif;
63 | }
64 |
65 | .default-entry-container {
66 | padding-bottom: 10px;
67 | }
68 |
69 | .initialization-textarea-container {
70 | margin-bottom: 10px;
71 | }
72 |
73 | /*.input-field-very-small {*/
74 | /*width: 20px;*/
75 | /*}*/
76 |
77 | /*.input-field-small {*/
78 | /*width: 100px;*/
79 | /*}*/
80 |
81 | .set-next-value-container {
82 | width: calc(100% - 45px);
83 | max-width: calc(100% - 45px);
84 | min-width: calc(100% - 45px);
85 | float: right;
86 | }
87 |
88 | #return-value-textarea-outer-container {
89 | width: 100%;
90 | display: inline-block;
91 | }
92 |
93 | .initialization-textarea-container, #return-value-textarea-container, .code-editor {
94 | width: calc(100% - 8px);
95 | max-width: calc(100% - 8px);
96 | min-width: calc(100% - 8px);
97 | float: right;
98 | display: inline-block;
99 | }
100 |
101 | #set-next-entry-textarea-container {
102 | width: 90%;
103 | max-width: 90%;
104 | min-width: 90%;
105 | }
106 |
107 | .full-width {
108 | width: 100%;
109 | max-width: 100%;
110 | min-width: 100%;
111 | }
112 |
113 | .problem-definition-panel {
114 | padding-bottom: 5px;
115 | }
116 |
117 | .half-page-card {
118 | margin-top: 0;
119 | }
120 |
121 | .custom-input-helper-text {
122 | margin-top: 8px;
123 | margin-bottom: 8px;
124 | }
125 |
126 | .custom-input-field {
127 | width: 100%;
128 | }
129 |
130 | pre {
131 | background-color: unset;
132 | color: unset;
133 | }
134 |
135 | .input-display {
136 | padding-top: 8px;
137 | }
138 |
139 | /*DP Table*/
140 |
141 | .dp-table {
142 | width: 90%;
143 | }
144 |
145 | .dp-tr {
146 | height: 5vw;
147 | }
148 |
149 | .dp-td {
150 | width: 5vw;
151 | height: 5vw;
152 | padding: 0;
153 | border: 1px solid #AAAAAA;
154 | text-align: center;
155 | }
156 |
157 | .dp-td:after {
158 | }
159 |
160 | .dp-table-data {
161 | position: absolute;
162 | /*top: 0;*/
163 | /*bottom: 0;*/
164 | /*left: 0;*/
165 | /*right: 0;*/
166 | height: 100%;
167 | }
168 |
169 | .slightly-bold-label {
170 | font-weight: 600;
171 | }
172 |
173 | .view-animation-button, .view-input-json-toggle {
174 | float: right;
175 | display: inline-block;
176 | vertical-align: top;
177 | }
178 |
179 | .play-animation-glyphicon {
180 | font-size: smaller;
181 | padding-right: 2px;
182 | vertical-align: top;
183 | position: relative;
184 | padding-bottom: 4px;
185 | }
186 |
187 | .transpose-button {
188 | margin: 5px;
189 | }
190 |
191 | .transpose-text {
192 | cursor: pointer;
193 | text-underline: none;
194 | }
195 |
196 | .run-tests-button {
197 | font-size: large;
198 | padding-bottom: 4px;
199 | padding-right: 6px;
200 | }
201 |
202 | #reveal-solution-button {
203 | margin-left: 10px;
204 | float: right;
205 | }
206 |
207 | .correct-result-but-different-table-text {
208 |
209 | }
210 |
211 | .test-result-label {
212 | display: inline;
213 | vertical-align: middle;
214 | }
215 |
216 | .glyphicon-time {
217 | padding-right: 4px;
218 | }
219 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dynamic Programming Visualization
2 |
3 | [**dp.debkbanerji.com**](https://dp.debkbanerji.com)
4 |
5 | In browser visualization for dynamic programming algorithms
6 |
7 | ## Deployed Version
8 |
9 | A deployed version of the master branch can be found at [dp.debkbanerji.com](https://dp.debkbanerji.com). Note that this is the master version and is therefore subject to change whenever there are changes to the source code or problems and so it is good to be aware of its volatile nature. Also, it may not have 100% uptime. If you need a more stable, reliable version of the application, please see the distribution section of this document.
10 |
11 | ## Creating a Problem
12 |
13 | If you'd like to create your own problem, it is recommended that you modify one of the `.dp.json` problem files in `src/assets/problems`. The field names should be pretty self explanatory. Note that every problem must have bottom up code defined, but a top down solution is optional - if top down code is not included, this option will not be shown to the user.
14 |
15 | Problems may also optionally have a more detailed solution in addition to the default result (for example, the subsequence itself in addition to the length of the subsequence for the longest increasing subsequence problem). In this case, the `solution` field in `output` must also be defined alongside the necessary code. Note that if you're also allowing for top down code, you must also create a detailed version of the top down code for finding the full solution. (For a total of 4 different versions of the problem)
16 |
17 | It's nice to have a good variety of test cases, in order to cover edge cases. A large number of test cases also helps with debugging as well as making sure a bad solution doesn't pass all test cases. Also, make sure none of your tet cases are very large, since those aren't always useful, they don't display well and the application actually has to process each test case, often multiple times.
18 |
19 | Note that only the input of each test case is defined - the output is generated automatically based on your provided code, so make sure it's correct. (and your inputs are valid) One of the reasons for this is that it is extremely tedious to manually step through large inputs and lay out the expected outputs. Another is that the application needs to actually run the provided code in order to generate the corresponding animation.
20 |
21 | You can test the problem you've created by using the custom problem section of the application's home page. If there is an issue with your problem, it is recommended you first compare it with one of the existing problems. If this does not fix your issue, try running the app locally and stepping through the source code. (It is much more difficult to pinpoint the lines that are causing issues in the built version of the application)
22 |
23 | ## Contributing a Problem
24 |
25 | If you want to contribute a problem you've created to the application, add the problem's `.dp.json` file to `src/assets/problems` and modify the `sections` field within `src\assets/problems/problem-directory.json`. Then, make a pull request on the GitHub Page. Make sure to test the problem before submitting it.
26 |
27 | ## Starting a Development Server
28 |
29 | You'll first need to install `node.js` and `npm`. Then, run `npm install` in the source code directory followed `npm run develop` and navigate to `localhost:4200` to begin development
30 |
31 | ## Building the app
32 |
33 | Run `npm run build` to build the app. The built app can be found within `dist`. The main app itself consists entirely of static frontend files. If you only want those (without the basic server stuff) you can just grab the files within `dist/dynamic-programming-visualization`
34 |
35 | ## Distribution
36 |
37 | You may want to build and distribute a version of this software for educational purposes, likely with a modified problem set (you can do this while leaving the rest of the code untouched by modifying the problems and problem directory within `assets/problems`). If you choose to do this, it is highly recommended that you build the app using the instructions outlined above and deploy the static files to your server, with a different version defined in `assets/problems/problem-directory.json`, removing `-master` from the version string, so users are aware that this is not the master version of the problem file. If you choose to do this, please do not modify the section of the page crediting the original author(s). Also, note that any problems distributed with the master version of this application are publicly available alongside the source code on its GitHub page. Finally, be aware that this software is designed purely as a learning tool and so should not be used to evaluate students.
38 |
--------------------------------------------------------------------------------
/src/assets/codemirror/languages/javascript/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeMirror: JavaScript mode
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
29 | JavaScript mode
30 |
31 |
32 |
81 |
82 |
90 |
91 |
92 | JavaScript mode supports several configuration options:
93 |
94 | json which will set the mode to expect JSON
95 | data rather than a JavaScript program.
96 | jsonld which will set the mode to expect
97 | JSON-LD linked data rather
98 | than a JavaScript program (demo ).
99 | typescript which will activate additional
100 | syntax highlighting and some other things for TypeScript code
101 | (demo ).
102 | statementIndent which (given a number) will
103 | determine the amount of indentation to use for statements
104 | continued on a new line.
105 | wordCharacters, a regexp that indicates which
106 | characters should be considered part of an identifier.
107 | Defaults to /[\w$]/, which does not handle
108 | non-ASCII identifiers. Can be set to something more elaborate
109 | to improve Unicode support.
110 |
111 |
112 |
113 | MIME types defined: text/javascript, application/json, application/ld+json, text/typescript, application/typescript.
114 |
115 |
--------------------------------------------------------------------------------
/src/assets/problems/chain-matrix-multiplication.dp.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Chain Matrix Multiplication",
3 | "problem-statement": "Suppose you are given a sequence (chain) A1 , A2 , ... ,An of n matrices to be multiplied, and you wish to compute the product A1 A2 ...An . You are given as input an array dims , containing the dimensions of the matrices, such that the dimensions of the matrices are dims0 × dims1 , dims1 × dims2 , ... ,dimsn-1 × dimsn . Assume that multiplying two matrices of dimensions p × q and q × r requires pqr scalar multiplications. What is the smallest number of scalar multiplications required to multiply the chain of matrices together in order?",
4 | "input": {
5 | "n": "Number of matrices",
6 | "dims": "Array of matrix dimensions, with length n + 1"
7 | },
8 | "output": {
9 | "result": "The minimum number of scalar multiplications required to multiply the chain of matrices together"
10 | },
11 | "provided-solution": {
12 | "tableShape": "2d",
13 | "tableDimension1": "n",
14 | "tableDimension2": "n",
15 | "initializationCode": "// No other initialization necessary",
16 | "for1Variable": "L",
17 | "for1Init": "0",
18 | "for1Condition": "L <= n",
19 | "for1Update": "L = L + 1",
20 | "for2Variable": "i",
21 | "for2Init": "0",
22 | "for2Condition": "i < n - L",
23 | "for2Update": "i = i + 1",
24 | "setNextEntryCode": "j = i + L;\nif (L == 0) {\n entry = 0;\n} else {\n let optimalCost = Number.MAX_VALUE; // optimal cost of the subproblem defined by i, j\n for (k = i; k < j; k++) {\n // kOpt is the optimal cost if we had split at k\n kOpt = T(i, k) // optimal cost of left subproblem\n + T(k + 1, j) // optimal cost of right subproblem\n + dims[i] * dims[k + 1] * dims[j + 1]; // cost to combine the two subproblems\n if (kOpt < optimalCost) {\n optimalCost = kOpt;\n }\n }\n entry = optimalCost; // write the optimal cost into entry i,j\n}",
25 | "defaultTableEntry": "",
26 | "useDefaultTableEntry": false,
27 | "returnValueCode": "result = T(0, n - 1);",
28 | "nextEntryIndex1": "i",
29 | "nextEntryIndex2": "j",
30 | "useAuxiliaryTableWithDetailedSolution": true,
31 | "tableEntryDefinition": "T[ i ][ j ] Is the minimum number of scalar multiplications required to multiply all the matrices from matrix i to matrix j together"
32 | },
33 | "test-cases": [
34 | {
35 | "name": "Test Case 1",
36 | "input": {
37 | "n": 4,
38 | "dims": [
39 | 50,
40 | 20,
41 | 1,
42 | 10,
43 | 100
44 | ]
45 | }
46 | },
47 | {
48 | "name": "Test Case 2",
49 | "input": {
50 | "n": 6,
51 | "dims": [
52 | 30,
53 | 35,
54 | 15,
55 | 5,
56 | 10,
57 | 20,
58 | 25
59 | ]
60 | }
61 | },
62 | {
63 | "name": "Test Case 3",
64 | "input": {
65 | "n": 7,
66 | "dims": [
67 | 5,
68 | 2,
69 | 4,
70 | 10,
71 | 8,
72 | 5,
73 | 5,
74 | 2
75 | ]
76 | }
77 | },
78 | {
79 | "name": "Test Case 4",
80 | "input": {
81 | "n": 7,
82 | "dims": [
83 | 5,
84 | 3,
85 | 4,
86 | 7,
87 | 8,
88 | 3,
89 | 5,
90 | 9
91 | ]
92 | }
93 | },
94 | {
95 | "name": "Test Case 5",
96 | "input": {
97 | "n": 4,
98 | "dims": [
99 | 40,
100 | 30,
101 | 10,
102 | 15,
103 | 30
104 | ]
105 | }
106 | },
107 | {
108 | "name": "Edge Case 1",
109 | "input": {
110 | "n": 1,
111 | "dims": [
112 | 10,
113 | 20
114 | ]
115 | }
116 | },
117 | {
118 | "name": "Edge Case 2",
119 | "input": {
120 | "n": 1,
121 | "dims": [
122 | 10,
123 | 20,
124 | 30
125 | ]
126 | }
127 | }
128 | ]
129 | }
130 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "dynamic-programming-visualization": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {},
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:browser",
15 | "options": {
16 | "outputPath": "dist/dynamic-programming-visualization",
17 | "index": "src/index.html",
18 | "main": "src/main.ts",
19 | "polyfills": "src/polyfills.ts",
20 | "tsConfig": "src/tsconfig.app.json",
21 | "assets": [
22 | "src/favicon.ico",
23 | "src/assets"
24 | ],
25 | "styles": [
26 | "src/styles.css",
27 | "src/theme.scss"
28 | ],
29 | "scripts": []
30 | },
31 | "configurations": {
32 | "production": {
33 | "fileReplacements": [
34 | {
35 | "replace": "src/environments/environment.ts",
36 | "with": "src/environments/environment.prod.ts"
37 | }
38 | ],
39 | "optimization": true,
40 | "outputHashing": "all",
41 | "sourceMap": false,
42 | "extractCss": true,
43 | "namedChunks": false,
44 | "aot": true,
45 | "extractLicenses": true,
46 | "vendorChunk": false,
47 | "buildOptimizer": true
48 | }
49 | }
50 | },
51 | "serve": {
52 | "builder": "@angular-devkit/build-angular:dev-server",
53 | "options": {
54 | "browserTarget": "dynamic-programming-visualization:build"
55 | },
56 | "configurations": {
57 | "production": {
58 | "browserTarget": "dynamic-programming-visualization:build:production"
59 | }
60 | }
61 | },
62 | "extract-i18n": {
63 | "builder": "@angular-devkit/build-angular:extract-i18n",
64 | "options": {
65 | "browserTarget": "dynamic-programming-visualization:build"
66 | }
67 | },
68 | "test": {
69 | "builder": "@angular-devkit/build-angular:karma",
70 | "options": {
71 | "main": "src/test.ts",
72 | "polyfills": "src/polyfills.ts",
73 | "tsConfig": "src/tsconfig.spec.json",
74 | "karmaConfig": "src/karma.conf.js",
75 | "styles": [
76 | "styles.css"
77 | ],
78 | "scripts": [],
79 | "assets": [
80 | "src/favicon.ico",
81 | "src/assets"
82 | ]
83 | }
84 | },
85 | "lint": {
86 | "builder": "@angular-devkit/build-angular:tslint",
87 | "options": {
88 | "tsConfig": [
89 | "src/tsconfig.app.json",
90 | "src/tsconfig.spec.json"
91 | ],
92 | "exclude": [
93 | "**/node_modules/**"
94 | ]
95 | }
96 | }
97 | }
98 | },
99 | "dynamic-programming-visualization-e2e": {
100 | "root": "e2e/",
101 | "projectType": "application",
102 | "architect": {
103 | "e2e": {
104 | "builder": "@angular-devkit/build-angular:protractor",
105 | "options": {
106 | "protractorConfig": "e2e/protractor.conf.js",
107 | "devServerTarget": "dynamic-programming-visualization:serve"
108 | }
109 | },
110 | "lint": {
111 | "builder": "@angular-devkit/build-angular:tslint",
112 | "options": {
113 | "tsConfig": "e2e/tsconfig.e2e.json",
114 | "exclude": [
115 | "**/node_modules/**"
116 | ]
117 | }
118 | }
119 | }
120 | }
121 | },
122 | "defaultProject": "dynamic-programming-visualization"
123 | }
124 |
--------------------------------------------------------------------------------
/src/app/select-problem/select-problem.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Dynamic
9 |
10 |
11 | Programming Visualization
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | v{{version}}
21 |
25 | Dark Mode
26 |
27 |
28 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
68 |
72 | {{item.type}}
73 |
74 |
75 | {{item.type}}
76 | Solution Revealed
77 |
78 |
79 |
80 |
81 |
82 | Not yet attempted
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
Custom Problem
92 |
93 |
Here you can solve a problem from a file on your computer
94 |
Be careful when choosing a file, as depending on where it came from, it may contain
95 | mistakes or try to do sketchy things
96 |
98 |
{{customProblemErrorText}}
99 |
100 |
101 |
102 |
109 |
110 |
--------------------------------------------------------------------------------
/src/app/select-problem/select-problem.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Inject, OnInit} from '@angular/core';
2 | import {ActivatedRoute, Router} from '@angular/router';
3 | import {CustomProblemService} from '../providers/custom-problem.service';
4 | import {DOCUMENT} from '@angular/common';
5 | import {Title} from '@angular/platform-browser';
6 | import {environment} from '../../environments/environment';
7 | import {HttpClient} from '@angular/common/http';
8 | import {ProgressService} from '../providers/progress.service';
9 |
10 | @Component({
11 | selector: 'app-select-problem',
12 | templateUrl: './select-problem.component.html',
13 | styleUrls: ['./select-problem.component.css']
14 | })
15 | export class SelectProblemComponent implements OnInit {
16 |
17 | isDarkTheme = false;
18 |
19 | LOGO_URL = '/assets/images/Logo.png';
20 |
21 | customPanelOpenState = false;
22 | customProblemFile: any;
23 | customProblemErrorText: string;
24 |
25 | totalProblems: number;
26 |
27 | version = environment.VERSION;
28 |
29 | sections = [];
30 | problemFileVersion = null;
31 |
32 | progressData = {};
33 |
34 | constructor(
35 | private router: Router,
36 | private customProblemService: CustomProblemService,
37 | @Inject(DOCUMENT) document,
38 | private route: ActivatedRoute,
39 | private titleService: Title,
40 | private http: HttpClient,
41 | private progressService: ProgressService
42 | ) {
43 | }
44 |
45 | ngOnInit() {
46 | const component = this;
47 | if (component.progressService.getHasLocalStorage()) {
48 | component.isDarkTheme = component.progressService.getDarkModeStatus();
49 | }
50 | component.titleService.setTitle('Dynamic Programming');
51 | component.http.get('../assets/problems/problem-directory.json').subscribe((data: any) => {
52 | component.sections = data.sections;
53 | component.problemFileVersion = data.version;
54 | component.totalProblems = 0;
55 | component.sections.forEach(function (section) {
56 | component.totalProblems += section.problems.length;
57 | });
58 | component.setUpProgressData();
59 | });
60 | }
61 |
62 | setUpProgressData(): void {
63 | const component = this;
64 | component.progressData = {};
65 | if (component.progressService.getHasLocalStorage()) {
66 | const objectDefaultProgressTypePromises = [];
67 | component.sections.forEach(function (section) {
68 | component.totalProblems += section.problems.length;
69 | section.problems.forEach(function (problem) {
70 | objectDefaultProgressTypePromises.push(new Promise((resolve, reject) => {
71 | component.http.get('../assets/problems/' + problem['id'] + '.dp.json').subscribe(data => {
72 | // resolve(data);
73 | const solutionTypes = ['bottomUp']; // Bottom up solution should always exist
74 | if (data['provided-solution'].returnValueTopDownCode) {
75 | solutionTypes.push('topDown');
76 | }
77 | if (data['output'].solution) {
78 | solutionTypes.push('detailedBottomUp');
79 | if (data['provided-solution'].returnValueTopDownCode) {
80 | solutionTypes.push('detailedTopDown');
81 | }
82 | }
83 | resolve({
84 | id: problem.id,
85 | solutionTypes
86 | });
87 | }, err => {
88 | reject(err);
89 | });
90 | }));
91 | });
92 | });
93 |
94 | Promise.all(objectDefaultProgressTypePromises).then(problems => {
95 | problems.forEach((problem => {
96 | const progressData = component.progressService.getProblemProgressObjectNullIfNotExists(problem['id']);
97 | if (progressData) {
98 | const progressMap = progressData.hasSolvedSolutionTypes;
99 | const progressArray = [];
100 | let basicSolved = progressMap['bottomUp'] || progressMap['topDown'];
101 | if (progressMap.hasOwnProperty('detailedBottomUp') || progressMap.hasOwnProperty('detailedTopDown')) {
102 | const detailedSolved = progressMap['detailedBottomUp'] || progressMap['detailedTopDown'] && !progressData.hasRevealedSolution;
103 | const detailedProgressObject = {
104 | 'type': detailedSolved ? 'Solution with Reconstruction Found' : 'Solution with Reconstruction Not Yet Found',
105 | 'completed': detailedSolved
106 | };
107 | basicSolved = basicSolved || detailedSolved;
108 | progressArray.push(detailedProgressObject);
109 | }
110 | basicSolved = basicSolved && !progressData.hasRevealedSolution;
111 | const basicProgressObject = {
112 | 'type': basicSolved ? 'Solution Found' : 'Solution Not Yet Found',
113 | 'completed': basicSolved && !progressData.hasRevealedSolution
114 | };
115 | progressArray.unshift(basicProgressObject);
116 | component.progressData[problem['id']] =
117 | {
118 | hasRevealedSolution: progressData.hasRevealedSolution,
119 | progressArray
120 | };
121 | }
122 | })
123 | );
124 | });
125 | }
126 | }
127 |
128 | openProblem(id: string): void {
129 | this.router.navigate(['problem/' + id]);
130 | }
131 |
132 | solveCustomProblem(): void {
133 | const component: SelectProblemComponent = this;
134 | const problemFileInput: any = document.getElementById('custom-problem-file-select');
135 | const problemFile = problemFileInput.files[0];
136 | const reader = new FileReader();
137 | reader.onload = function (e) {
138 | try {
139 | const target: any = e.target;
140 | const fileText = target.result;
141 | const problem = JSON.parse(fileText);
142 | component.customProblemService.setCustomProblem(problem);
143 | component.router.navigate(['problem/custom']);
144 | } catch (err) {
145 | component.customProblemErrorText = err.message;
146 | }
147 | };
148 | reader.readAsText(problemFile);
149 | }
150 |
151 | onDarkModeChange() {
152 | const component = this;
153 | if (component.progressService.getHasLocalStorage()) {
154 | component.progressService.setDarkModeStatus(component.isDarkTheme);
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/assets/problems/levenshtein-distance.dp.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Levenshtein Distance",
3 | "problem-statement": "You are given as input two arrays of characters, arr1[0], . . . , arr1[n1 - 1] and arr2[0], . . . , arr2[n2 - 1] , which represent strings. The goal of this problem is to find the Levenshtein distance between the two input strings, which is defined as the number of edits it takes to transform one string into another. Here, an edit is defined as either an insertion of a character, deletion of a character, or substitution of a character with another character.",
4 | "input": {
5 | "arr1": "First Input Array",
6 | "arr2": "Second Input Array",
7 | "n1": "Length of First Input Array (Equal to arr1.length )",
8 | "n2": "Length of Second Input Array (Equal to arr2.length )"
9 | },
10 | "output": {
11 | "result": "The Levenshtein distance between the 2 input arrays"
12 | },
13 | "provided-solution": {
14 | "tableShape": "2d",
15 | "tableDimension1": "n1 + 1",
16 | "tableDimension2": "n2 + 1",
17 | "initializationCode": "// No other initialization necessary",
18 | "for1Variable": "i",
19 | "for1Init": "0",
20 | "for1Condition": "i <= n1",
21 | "for1Update": "i = i + 1",
22 | "for2Variable": "j",
23 | "for2Init": "0",
24 | "for2Condition": "j <= n2",
25 | "for2Update": "j = j + 1",
26 | "setNextEntryCode": "if (i === 0) {\n entry = j;\n} else if (j === 0) {\n entry = i;\n} else if(arr1[i - 1] === arr2[j - 1]) {\n entry = T(i - 1, j - 1);\n} else {\n entry = 1 + Math.min(\n T(i, j - 1),\n T(i - 1, j),\n T(i - 1, j - 1)\n );\n}",
27 | "defaultTableEntry": "",
28 | "useDefaultTableEntry": false,
29 | "returnValueCode": "result = T(n1, n2);",
30 | "nextEntryIndex1": "i",
31 | "nextEntryIndex2": "j",
32 | "setNextEntryTopDownCode": "if (i === 0) {\n entry = j;\n} else if (j === 0) {\n entry = i;\n} else if(arr1[i - 1] === arr2[j - 1]) {\n entry = getTableEntry(i - 1, j - 1);\n} else {\n entry = 1 + Math.min(\n getTableEntry(i, j - 1),\n getTableEntry(i - 1, j),\n getTableEntry(i - 1, j - 1)\n );\n}",
33 | "returnValueTopDownCode": "result = getTableEntry(n1, n2);",
34 | "tableEntryDefinition": "T[ i ][ j ] Is the Levenshtein Distance between arr1[0], . . . , arr1[i - 1] and arr2[0], . . . , arr2[j - 1] .",
35 | "solutionNotes": "This solution runs in O(n1 * n2) time. When populating T[ i ][ j ] , we consider the three possible edit operations that could be used to reduce the problem to that involving shorter prefixes."
36 | },
37 | "test-cases": [
38 | {
39 | "name": "Test Case 1",
40 | "input": {
41 | "n1": 7,
42 | "n2": 7,
43 | "arr1": [
44 | "t",
45 | "a",
46 | "n",
47 | "g",
48 | "l",
49 | "e",
50 | "d"
51 | ],
52 | "arr2": [
53 | "a",
54 | "l",
55 | "a",
56 | "d",
57 | "d",
58 | "i",
59 | "n"
60 | ]
61 | }
62 | },
63 | {
64 | "name": "Test Case 2",
65 | "input": {
66 | "n1": 11,
67 | "n2": 10,
68 | "arr1": [
69 | "e",
70 | "x",
71 | "p",
72 | "o",
73 | "n",
74 | "e",
75 | "n",
76 | "t",
77 | "i",
78 | "a",
79 | "l"
80 | ],
81 | "arr2": [
82 | "p",
83 | "o",
84 | "l",
85 | "y",
86 | "n",
87 | "o",
88 | "m",
89 | "i",
90 | "a",
91 | "l"
92 | ]
93 | }
94 | },
95 | {
96 | "name": "Test Case 3",
97 | "input": {
98 | "n1": 6,
99 | "n2": 7,
100 | "arr1": [
101 | "A",
102 | "B",
103 | "A",
104 | "Z",
105 | "D",
106 | "C"
107 | ],
108 | "arr2": [
109 | "B",
110 | "A",
111 | "C",
112 | "B",
113 | "A",
114 | "D",
115 | "Z"
116 | ]
117 | }
118 | },
119 | {
120 | "name": "Test Case 4",
121 | "input": {
122 | "n1": 9,
123 | "n2": 9,
124 | "arr1": [
125 | "t",
126 | "o",
127 | "m",
128 | "r",
129 | "i",
130 | "d",
131 | "d",
132 | "l",
133 | "e"
134 | ],
135 | "arr2": [
136 | "v",
137 | "o",
138 | "l",
139 | "d",
140 | "e",
141 | "m",
142 | "o",
143 | "r",
144 | "t"
145 | ]
146 | }
147 | },
148 | {
149 | "name": "Edge Case 1",
150 | "input": {
151 | "n1": 0,
152 | "n2": 0,
153 | "arr1": [
154 | ],
155 | "arr2": [
156 | ]
157 | }
158 | },
159 | {
160 | "name": "Edge Case 2",
161 | "input": {
162 | "n1": 1,
163 | "n2": 1,
164 | "arr1": [
165 | "A"
166 | ],
167 | "arr2": [
168 | "A"
169 | ]
170 | }
171 | },
172 | {
173 | "name": "Edge Case 3",
174 | "input": {
175 | "n1": 1,
176 | "n2": 1,
177 | "arr1": [
178 | "A"
179 | ],
180 | "arr2": [
181 | "B"
182 | ]
183 | }
184 | },
185 | {
186 | "name": "Edge Case 4",
187 | "input": {
188 | "n1": 4,
189 | "n2": 4,
190 | "arr1": [
191 | "A",
192 | "B",
193 | "C",
194 | "D"
195 | ],
196 | "arr2": [
197 | "A",
198 | "B",
199 | "C",
200 | "D"
201 | ]
202 | }
203 | },
204 | {
205 | "name": "Edge Case 5",
206 | "input": {
207 | "n1": 4,
208 | "n2": 4,
209 | "arr1": [
210 | "A",
211 | "B",
212 | "C",
213 | "D"
214 | ],
215 | "arr2": [
216 | "D",
217 | "C",
218 | "B",
219 | "A"
220 | ]
221 | }
222 | }
223 | ]
224 | }
225 |
--------------------------------------------------------------------------------
/src/assets/problems/knapsack-with-repetition.dp.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Knapsack With Repetition",
3 | "problem-statement": "You are a roguish thief who has broken into a comically well decorated house. You have with you a knapsack of weight capacity W , where W is an integer. There are n types of items in the house, each of integer weight and some value. It is your goal to maximize the combined value of the items that you can fit in your knapsack. For this version of the problem, there exist unlimited copies of each type of item (somehow) so you can take the same item type more than once.",
4 | "input": {
5 | "W": "Maximum Capacity",
6 | "n": "Number of Item Types",
7 | "weights": "Array of item weights, where entry i represents the weight of item i",
8 | "values": "Array of item values, where entry i represents the value of item i"
9 | },
10 | "output": {
11 | "result": "The maximum value that can be attained with a knapsack of capacity W",
12 | "solution": "An array of the item indices that lead to this maximum value"
13 | },
14 | "provided-solution": {
15 | "tableShape": "1d",
16 | "tableDimension1": "W + 1",
17 | "initializationCode": "// No other initialization necessary",
18 | "for1Variable": "i",
19 | "for1Init": "0",
20 | "for1Condition": "i < W + 1",
21 | "for1Update": "i = i + 1",
22 | "setNextEntryCode": "for (let j = 0 ; j < n; j = j + 1) { // for each item\n const itemWeight = weights[j];\n const itemValue = values[j];\n if (itemWeight <= i) {\n const valueWithItem = (T(i - itemWeight)) + itemValue;\n if (valueWithItem > entry) {\n entry = valueWithItem;\n }\n }\n}",
23 | "defaultTableEntry": "0",
24 | "useDefaultTableEntry": true,
25 | "returnValueCode": "result = T(W);",
26 | "nextEntryIndex1": "i",
27 | "setNextEntryTopDownCode": "for (let j = 0 ; j < n; j = j + 1) { // for each item\n const itemWeight = weights[j];\n const itemValue = values[j];\n if (itemWeight <= i) {\n const valueWithItem = (getTableEntry(i - itemWeight)) + itemValue;\n if (valueWithItem > entry) {\n entry = valueWithItem;\n }\n }\n}",
28 | "returnValueTopDownCode": "result = getTableEntry(W);",
29 | "useAuxiliaryTableWithDetailedSolution": true,
30 | "detailedSetNextEntryCode": "let secondaryEntry = null;\n\nfor (let j = 0 ; j < n; j = j + 1) { // for each item\n const itemWeight = weights[j];\n const itemValue = values[j];\n if (itemWeight <= i) {\n const valueWithItem = (T(i - itemWeight)) + itemValue;\n if (valueWithItem > entry) {\n entry = valueWithItem;\n secondaryEntry = j;\n }\n }\n}",
31 | "detailedReturnValueCode": "result = T(W);\n\nsolution = [];\nlet weightIndex = W;\nlet item = T2(weightIndex);\nwhile (item !== null) {\n solution.push(item);\n weightIndex = weightIndex - weights[item];\n item = T2(weightIndex);\n}",
32 | "detailedSetNextEntryTopDownCode": "let secondaryEntry = null;\n\nfor (let j = 0 ; j < n; j = j + 1) { // for each item\n const itemWeight = weights[j];\n const itemValue = values[j];\n if (itemWeight <= i) {\n const valueWithItem = (getTableEntry(i - itemWeight)) + itemValue;\n if (valueWithItem > entry) {\n entry = valueWithItem;\n secondaryEntry = j;\n }\n }\n}",
33 | "detailedReturnValueTopDownCode": "result = getTableEntry(W);\n\nsolution = [];\nlet weightIndex = W;\nlet item = T2(weightIndex);\nwhile (item !== null) {\n solution.push(item);\n weightIndex = weightIndex - weights[item];\n item = T2(weightIndex);\n}",
34 | "tableEntryDefinition": "T[ i ] Is the maximum value obtainable with a knapsack of capacity i ",
35 | "auxiliaryTableEntryDefinition": "T2[ i ] is the index of the last item added to the hypothetical knapsack of capacity i ",
36 | "solutionNotes": "This solution runs in O(nW) time. When populating T[ i ] , we check every single item to see the largest value that can be obtained if we have to take the item, so we pretend to remove the item and add its value back in, checking the maximum value obtainable with the weight of the item subtracted.Therefore, T[ W ] contains our final answer"
37 | },
38 | "test-cases": [
39 | {
40 | "name": "Test Case 1",
41 | "input": {
42 | "W": 10,
43 | "n": 4,
44 | "weights": [
45 | 6,
46 | 3,
47 | 4,
48 | 2
49 | ],
50 | "values": [
51 | 30,
52 | 14,
53 | 16,
54 | 9
55 | ]
56 | }
57 | },
58 | {
59 | "name": "Test Case 2",
60 | "input": {
61 | "W": 6,
62 | "n": 4,
63 | "weights": [
64 | 1,
65 | 2,
66 | 3,
67 | 4
68 | ],
69 | "values": [
70 | 12,
71 | 17,
72 | 18,
73 | 25
74 | ]
75 | }
76 | },
77 | {
78 | "name": "Test Case 3",
79 | "input": {
80 | "W": 7,
81 | "n": 5,
82 | "weights": [
83 | 1,
84 | 2,
85 | 3,
86 | 4,
87 | 6
88 | ],
89 | "values": [
90 | 12,
91 | 17,
92 | 18,
93 | 25,
94 | 30
95 | ]
96 | }
97 | },
98 | {
99 | "name": "Test Case 4",
100 | "input": {
101 | "W": 16,
102 | "n": 8,
103 | "weights": [
104 | 1,
105 | 2,
106 | 3,
107 | 4,
108 | 6,
109 | 5,
110 | 9,
111 | 5
112 | ],
113 | "values": [
114 | 12,
115 | 17,
116 | 18,
117 | 25,
118 | 30,
119 | 23,
120 | 54,
121 | 75
122 | ]
123 | }
124 | },
125 | {
126 | "name": "Edge Case 1",
127 | "input": {
128 | "W": 0,
129 | "n": 5,
130 | "weights": [
131 | 1,
132 | 2,
133 | 3,
134 | 4,
135 | 6
136 | ],
137 | "values": [
138 | 12,
139 | 17,
140 | 18,
141 | 25,
142 | 30
143 | ]
144 | }
145 | },
146 | {
147 | "name": "Edge Case 2",
148 | "input": {
149 | "W": 4,
150 | "n": 0,
151 | "weights": [
152 | ],
153 | "values": [
154 | ]
155 | }
156 | },
157 | {
158 | "name": "Edge Case 3",
159 | "input": {
160 | "W": 4,
161 | "n": 3,
162 | "weights": [
163 | 2,
164 | 2,
165 | 3
166 | ],
167 | "values": [
168 | 99,
169 | 100,
170 | 151
171 | ]
172 | }
173 | }
174 | ]
175 | }
176 |
--------------------------------------------------------------------------------
/src/assets/problems/shortest-path-in-dag.dp.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Shortest Path in DAG",
3 | "problem-statement": "You are given as input an n × n adjacency matrix, adj that represents a DAG (directed acyclic graph) with vertices labelled 0 ... n-1 . The vertices are labelled in topological order, and adj[ i ][ j ] contains the weight of the edge from vertex i to vertex j , or null if no such edge exists. The goal of this problem is to find the shortest path from vertex 0 to vertex n - 1 .",
4 | "input": {
5 | "n": "Number of vertices",
6 | "adj": "The adjacency matrix"
7 | },
8 | "output": {
9 | "result": "The length of the shortest path from vertex 0 to vertex n - 1 , or infinity if no path exists",
10 | "solution": "An array of the vertices along this path, including the start and end vertices, or an empty array if no path exists"
11 | },
12 | "provided-solution": {
13 | "tableShape": "1d",
14 | "tableDimension1": "n",
15 | "initializationCode": "// No other initialization necessary",
16 | "for1Variable": "i",
17 | "for1Init": "0",
18 | "for1Condition": "i < n",
19 | "for1Update": "i = i + 1",
20 | "setNextEntryCode": "if (i === 0) {\n entry = 0;\n} else {\n // For every vertex, j before i\n for (let j = 0; j < i; j = j + 1) {\n // If there is an edge from j to i\n if (adj[j][i] !== null) {\n entry = Math.min(entry, T(j) + adj[j][i]);\n }\n }\n}",
21 | "defaultTableEntry": "Infinity",
22 | "useDefaultTableEntry": true,
23 | "returnValueCode": "result = T(n - 1);",
24 | "nextEntryIndex1": "i",
25 | "setNextEntryTopDownCode": "if (i === 0) {\n entry = 0;\n} else {\n // For every vertex, j before i\n for (let j = 0; j < i; j = j + 1) {\n // If there is an edge from j to i\n if (adj[j][i] !== null) {\n entry = Math.min(entry, getTableEntry(j) + adj[j][i]);\n }\n }\n}",
26 | "returnValueTopDownCode": "result = getTableEntry(n - 1);",
27 | "useAuxiliaryTableWithDetailedSolution": true,
28 | "detailedSetNextEntryCode": "let secondaryEntry = null;\nif (i === 0) {\n entry = 0;\n} else {\n // For every vertex, j before i\n for (let j = 0; j < i; j = j + 1) {\n // If there is an edge from j to i\n if (adj[j][i] !== null) {\n if (T(j) + adj[j][i] < entry) {\n entry = T(j) + adj[j][i];\n secondaryEntry = j;\n }\n }\n }\n}",
29 | "detailedReturnValueCode": "let solution = [];\nif (T(n - 1) !== Infinity) {\n let i = n - 1;\n while (i !== null) {\n // Append to the front of solution\n solution.unshift(i);\n i = T2(i);\n }\n}\nlet result = T(n - 1);",
30 | "detailedSetNextEntryTopDownCode": "let secondaryEntry = null;\nif (i === 0) {\n entry = 0;\n} else {\n // For every vertex, j before i\n for (let j = 0; j < i; j = j + 1) {\n // If there is an edge from j to i\n if (adj[j][i] !== null) {\n if (getTableEntry(j) + adj[j][i] < entry) {\n entry = getTableEntry(j) + adj[j][i];\n secondaryEntry = j;\n }\n }\n }\n}",
31 | "detailedReturnValueTopDownCode": "let solution = [];\nif (getTableEntry(n - 1) !== Infinity) {\n let i = n - 1;\n while (i !== null) {\n // Append to the front of solution\n solution.unshift(i);\n i = T2(i);\n }\n}\nlet result = T(n - 1);",
32 | "tableEntryDefinition": "T[ i ] is the length of the shortest path from vertex 0 to vertex i , or infinity if no such path exists",
33 | "auxiliaryTableEntryDefinition": "T2[ i ] is the index of the second last vertex along the shortest path from vertex 0 to vertex i , or null if vertex i has no incoming edges",
34 | "solutionNotes": "This solution runs in O(n2 ) time, since the graph is represented using an adjacency matrix. This is because we need to check all vertices before vertex i when populating T[ i ] . If the DAG is instead represented as an adjacency list, this can be improved to O(m + n) , where m is the number of edges in the DAG, with preprocessing that doesn't affect the asymptotic time complexity. Note that the time taken to topologically sort a DAG is linear in the size of its representation (assuming it's an adjacency list or an adjacency matrix)."
35 | },
36 | "test-cases": [
37 | {
38 | "name": "Test Case 1",
39 | "input": {
40 | "n": 6,
41 | "adj":
42 | [
43 | [null,2,1,null,null,null],
44 | [null,null,4,null,3,null],
45 | [null,null,null,6,null,null],
46 | [null,null,null,null,1,2],
47 | [null,null,null,null,null,1],
48 | [null,null,null,null,null,null]
49 | ]
50 | }
51 | },
52 | {
53 | "name": "Test Case 2",
54 | "input": {
55 | "n": 6,
56 | "adj":
57 | [
58 | [null,2,1,null,null,null],
59 | [null,null,4,null,3,null],
60 | [null,null,null,-6,null,null],
61 | [null,null,null,null,1,2],
62 | [null,null,null,null,null,1],
63 | [null,null,null,null,null,null]
64 | ]
65 | }
66 | },
67 | {
68 | "name": "Test Case 3",
69 | "input": {
70 | "n": 6,
71 | "adj":
72 | [
73 | [null,5,1,2,null,null],
74 | [null,null,4,8,null,null],
75 | [null,null,null,6,null,null],
76 | [null,null,null,null,4,2],
77 | [null,null,null,null,null,3],
78 | [null,null,null,null,null,null]
79 | ]
80 | }
81 | },
82 | {
83 | "name": "Edge Case 1",
84 | "input": {
85 | "n": 1,
86 | "adj":
87 | [
88 | [null]
89 | ]
90 | }
91 | },
92 | {
93 | "name": "Edge Case 2",
94 | "input": {
95 | "n": 2,
96 | "adj":
97 | [
98 | [null, 1],
99 | [null,null]
100 | ]
101 | }
102 | },
103 | {
104 | "name": "Edge Case 3",
105 | "input": {
106 | "n": 6,
107 | "adj":
108 | [
109 | [null,null,null,null,null,null],
110 | [null,null,null,null,null,null],
111 | [null,null,null,null,null,null],
112 | [null,null,null,null,null,null],
113 | [null,null,null,null,null,null],
114 | [null,null,null,null,null,null]
115 | ]
116 | }
117 | },
118 | {
119 | "name": "Edge Case 4",
120 | "input": {
121 | "n": 6,
122 | "adj":
123 | [
124 | [null,1,1,1,1,1],
125 | [null,null,1,1,1,1],
126 | [null,null,null,1,1,1],
127 | [null,null,null,null,1,1],
128 | [null,null,null,null,null,1],
129 | [null,null,null,null,null,null]
130 | ]
131 | }
132 | },
133 | {
134 | "name": "Edge Case 5",
135 | "input": {
136 | "n": 6,
137 | "adj":
138 | [
139 | [null,-1,-1,-1,-1,-1],
140 | [null,null,-1,-1,-1,-1],
141 | [null,null,null,-1,-1,-1],
142 | [null,null,null,null,-1,-1],
143 | [null,null,null,null,null,-1],
144 | [null,null,null,null,null,null]
145 | ]
146 | }
147 | }
148 | ]
149 | }
150 |
--------------------------------------------------------------------------------
/src/assets/problems/longest-path-in-dag.dp.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Longest Path in DAG",
3 | "problem-statement": "You are given as input an n × n adjacency matrix, adj that represents a DAG (directed acyclic graph) with vertices labelled 0 ... n-1 . The vertices are labelled in topological order, and adj[ i ][ j ] contains the weight of the edge from vertex i to vertex j , or null if no such edge exists. The goal of this problem is to find the longest path from vertex 0 to vertex n - 1 .",
4 | "input": {
5 | "n": "Number of vertices",
6 | "adj": "The adjacency matrix"
7 | },
8 | "output": {
9 | "result": "The length of the longest path from vertex 0 to vertex n - 1 , or negative infinity if no path exists",
10 | "solution": "An array of the vertices along this path, including the start and end vertices, or an empty array if no path exists"
11 | },
12 | "provided-solution": {
13 | "tableShape": "1d",
14 | "tableDimension1": "n",
15 | "initializationCode": "// No other initialization necessary",
16 | "for1Variable": "i",
17 | "for1Init": "0",
18 | "for1Condition": "i < n",
19 | "for1Update": "i = i + 1",
20 | "setNextEntryCode": "if (i === 0) {\n entry = 0;\n} else {\n // For every vertex, j before i\n for (let j = 0; j < i; j = j + 1) {\n // If there is an edge from j to i\n if (adj[j][i] !== null) {\n entry = Math.max(entry, T(j) + adj[j][i]);\n }\n }\n}",
21 | "defaultTableEntry": "-Infinity",
22 | "useDefaultTableEntry": true,
23 | "returnValueCode": "result = T(n - 1);",
24 | "nextEntryIndex1": "i",
25 | "setNextEntryTopDownCode": "if (i === 0) {\n entry = 0;\n} else {\n // For every vertex, j before i\n for (let j = 0; j < i; j = j + 1) {\n // If there is an edge from j to i\n if (adj[j][i] !== null) {\n entry = Math.max(entry, getTableEntry(j) + adj[j][i]);\n }\n }\n}",
26 | "returnValueTopDownCode": "result = getTableEntry(n - 1);",
27 | "useAuxiliaryTableWithDetailedSolution": true,
28 | "detailedSetNextEntryCode": "let secondaryEntry = null;\nif (i === 0) {\n entry = 0;\n} else {\n // For every vertex, j before i\n for (let j = 0; j < i; j = j + 1) {\n // If there is an edge from j to i\n if (adj[j][i] !== null) {\n if (T(j) + adj[j][i] > entry) {\n entry = T(j) + adj[j][i];\n secondaryEntry = j;\n }\n }\n }\n}",
29 | "detailedReturnValueCode": "let solution = [];\nif (T(n - 1) !== -Infinity) {\n let i = n - 1;\n while (i !== null) {\n // Append to the front of solution\n solution.unshift(i);\n i = T2(i);\n }\n}\nlet result = T(n - 1);",
30 | "detailedSetNextEntryTopDownCode": "let secondaryEntry = null;\nif (i === 0) {\n entry = 0;\n} else {\n // For every vertex, j before i\n for (let j = 0; j < i; j = j + 1) {\n // If there is an edge from j to i\n if (adj[j][i] !== null) {\n if (getTableEntry(j) + adj[j][i] > entry) {\n entry = getTableEntry(j) + adj[j][i];\n secondaryEntry = j;\n }\n }\n }\n}",
31 | "detailedReturnValueTopDownCode": "let solution = [];\nif (getTableEntry(n - 1) !== -Infinity) {\n let i = n - 1;\n while (i !== null) {\n // Append to the front of solution\n solution.unshift(i);\n i = T2(i);\n }\n}\nlet result = T(n - 1);",
32 | "tableEntryDefinition": "T[ i ] is the length of the longest path from vertex 0 to vertex i , or negative infinity if no such path exists",
33 | "auxiliaryTableEntryDefinition": "T2[ i ] is the index of the second last vertex along the longest path from vertex 0 to vertex i , or null if vertex i has no incoming edges",
34 | "solutionNotes": "This solution runs in O(n2 ) time, since the graph is represented using an adjacency matrix. This is because we need to check all vertices before vertex i when populating T[ i ] . If the DAG is instead represented as an adjacency list, this can be improved to O(m + n) , where m is the number of edges in the DAG, with preprocessing that doesn't affect the asymptotic time complexity. Note that the time taken to topologically sort a DAG is linear in the size of its representation (assuming it's an adjacency list or an adjacency matrix)."
35 | },
36 | "test-cases": [
37 | {
38 | "name": "Test Case 1",
39 | "input": {
40 | "n": 6,
41 | "adj":
42 | [
43 | [null,2,1,null,null,null],
44 | [null,null,4,null,3,null],
45 | [null,null,null,6,null,null],
46 | [null,null,null,null,1,2],
47 | [null,null,null,null,null,1],
48 | [null,null,null,null,null,null]
49 | ]
50 | }
51 | },
52 | {
53 | "name": "Test Case 2",
54 | "input": {
55 | "n": 6,
56 | "adj":
57 | [
58 | [null,2,1,null,null,null],
59 | [null,null,4,null,3,null],
60 | [null,null,null,-6,null,null],
61 | [null,null,null,null,1,2],
62 | [null,null,null,null,null,1],
63 | [null,null,null,null,null,null]
64 | ]
65 | }
66 | },
67 | {
68 | "name": "Test Case 3",
69 | "input": {
70 | "n": 6,
71 | "adj":
72 | [
73 | [null,5,1,2,null,null],
74 | [null,null,4,8,null,null],
75 | [null,null,null,6,null,null],
76 | [null,null,null,null,4,2],
77 | [null,null,null,null,null,3],
78 | [null,null,null,null,null,null]
79 | ]
80 | }
81 | },
82 | {
83 | "name": "Edge Case 1",
84 | "input": {
85 | "n": 1,
86 | "adj":
87 | [
88 | [null]
89 | ]
90 | }
91 | },
92 | {
93 | "name": "Edge Case 2",
94 | "input": {
95 | "n": 2,
96 | "adj":
97 | [
98 | [null, 1],
99 | [null,null]
100 | ]
101 | }
102 | },
103 | {
104 | "name": "Edge Case 3",
105 | "input": {
106 | "n": 6,
107 | "adj":
108 | [
109 | [null,null,null,null,null,null],
110 | [null,null,null,null,null,null],
111 | [null,null,null,null,null,null],
112 | [null,null,null,null,null,null],
113 | [null,null,null,null,null,null],
114 | [null,null,null,null,null,null]
115 | ]
116 | }
117 | },
118 | {
119 | "name": "Edge Case 4",
120 | "input": {
121 | "n": 6,
122 | "adj":
123 | [
124 | [null,1,1,1,1,1],
125 | [null,null,1,1,1,1],
126 | [null,null,null,1,1,1],
127 | [null,null,null,null,1,1],
128 | [null,null,null,null,null,1],
129 | [null,null,null,null,null,null]
130 | ]
131 | }
132 | },
133 | {
134 | "name": "Edge Case 5",
135 | "input": {
136 | "n": 6,
137 | "adj":
138 | [
139 | [null,-1,-1,-1,-1,-1],
140 | [null,null,-1,-1,-1,-1],
141 | [null,null,null,-1,-1,-1],
142 | [null,null,null,null,-1,-1],
143 | [null,null,null,null,null,-1],
144 | [null,null,null,null,null,null]
145 | ]
146 | }
147 | }
148 | ]
149 | }
150 |
--------------------------------------------------------------------------------
/src/assets/problems/knapsack-without-repetition.dp.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Knapsack Without Repetition",
3 | "problem-statement": "You are a roguish thief who has broken into a comically well decorated house. You have with you a knapsack of weight capacity W , where W is an integer. There are n items in the house, each of integer weight and some value. It is your goal to maximize the combined value of the items that you can fit in your knapsack. For this version of the problem, there exists exactly one of each item, so you cannot take the same item twice.",
4 | "input": {
5 | "W": "Maximum Capacity",
6 | "n": "Number of Items",
7 | "weights": "Array of item weights, where entry i represents the weight of item i",
8 | "values": "Array of item values, where entry i represents the value of item i"
9 | },
10 | "output": {
11 | "result": "The maximum value that can be attained with a knapsack of capacity W",
12 | "solution": "An array of the item indices that lead to this maximum value"
13 | },
14 | "provided-solution": {
15 | "tableShape": "2d",
16 | "tableDimension1": "n + 1",
17 | "tableDimension2": "W + 1",
18 | "initializationCode": "// No other initialization necessary",
19 | "for1Variable": "i",
20 | "for1Init": "0",
21 | "for1Condition": "i < n + 1",
22 | "for1Update": "i = i + 1",
23 | "for2Variable": "j",
24 | "for2Init": "0",
25 | "for2Condition": "j < W + 1",
26 | "for2Update": "j = j + 1",
27 | "setNextEntryCode": "if (i == 0 || j == 0) {\n\tentry = 0;\n} else {\n\tlet itemWeight = weights[i - 1];\n\tlet valueWithoutLastItem = T(i - 1, j);\n\tif (itemWeight > j) {\n\t\tentry = valueWithoutLastItem;\n\t} else {\n\t\tlet valueWithLastItem = T(i - 1, j - itemWeight) + values[i - 1];\n\t\tif (valueWithLastItem > valueWithoutLastItem) {\n\t\t\tentry = valueWithLastItem;\n\t\t} else {\n\t\t\tentry = valueWithoutLastItem;\n\t\t}\n\t}\n}",
28 | "defaultTableEntry": "",
29 | "useDefaultTableEntry": false,
30 | "returnValueCode": "result = T(n, W);",
31 | "nextEntryIndex1": "i",
32 | "nextEntryIndex2": "j",
33 | "setNextEntryTopDownCode": "if (i == 0 || j == 0) {\n\tentry = 0;\n} else {\n\tlet itemWeight = weights[i - 1];\n\tlet valueWithoutLastItem = getTableEntry(i - 1, j);\n\tif (itemWeight > j) {\n\t\tentry = valueWithoutLastItem;\n\t} else {\n\t\tlet valueWithLastItem = getTableEntry(i - 1, j - itemWeight) + values[i - 1];\n\t\tif (valueWithLastItem > valueWithoutLastItem) {\n\t\t\tentry = valueWithLastItem;\n\t\t} else {\n\t\t\tentry = valueWithoutLastItem;\n\t\t}\n\t}\n}",
34 | "returnValueTopDownCode": "result = getTableEntry(n, W);",
35 | "useAuxiliaryTableWithDetailedSolution": false,
36 | "detailedSetNextEntryCode": "if (i == 0 || j == 0) {\n\tentry = 0;\n} else {\n\tlet itemWeight = weights[i - 1];\n\tlet valueWithoutLastItem = T(i - 1, j);\n\tif (itemWeight > j) {\n\t\tentry = valueWithoutLastItem;\n\t} else {\n\t\tlet valueWithLastItem = T(i - 1, j - itemWeight) + values[i - 1];\n\t\tif (valueWithLastItem > valueWithoutLastItem) {\n\t\t\tentry = valueWithLastItem;\n\t\t} else {\n\t\t\tentry = valueWithoutLastItem;\n\t\t}\n\t}\n}",
37 | "detailedReturnValueCode": "const result = T(n, W);\n\nconst solution = [];\nlet i = n;\nlet j = W;\n\nwhile (i > 0 && j > 0) {\n if (T(i - 1, j) !== T(i, j)) {\n solution.unshift(i - 1); // appending correct item index to front of solution\n j = j - weights[i - 1]; \n }\n i = i - 1;\n}",
38 | "detailedSetNextEntryTopDownCode": "if (i == 0 || j == 0) {\n\tentry = 0;\n} else {\n\tlet itemWeight = weights[i - 1];\n\tlet valueWithoutLastItem = getTableEntry(i - 1, j);\n\tif (itemWeight > j) {\n\t\tentry = valueWithoutLastItem;\n\t} else {\n\t\tlet valueWithLastItem = getTableEntry(i - 1, j - itemWeight) + values[i - 1];\n\t\tif (valueWithLastItem > valueWithoutLastItem) {\n\t\t\tentry = valueWithLastItem;\n\t\t} else {\n\t\t\tentry = valueWithoutLastItem;\n\t\t}\n\t}\n}",
39 | "detailedReturnValueTopDownCode": "const result = getTableEntry(n, W);\n\nconst solution = [];\nlet i = n;\nlet j = W;\n\nwhile (i > 0 && j > 0) {\n if (T(i - 1, j) !== T(i, j)) {\n solution.unshift(i - 1); // appending correct item index to front of solution\n j = j - weights[i - 1]; \n }\n i = i - 1;\n}",
40 | "tableEntryDefinition": "T[ i ][ j ] Is the maximum value obtainable with a knapsack of capacity j using the first i items.",
41 | "solutionNotes": "This solution runs in O(nW) time. When populating T[ i ][ j ] , we consider the case where we would take item i , as well as the case where we would not take item i ."
42 | },
43 | "test-cases": [
44 | {
45 | "name": "Test Case 1",
46 | "input": {
47 | "W": 10,
48 | "n": 4,
49 | "weights": [
50 | 6,
51 | 3,
52 | 4,
53 | 2
54 | ],
55 | "values": [
56 | 30,
57 | 14,
58 | 16,
59 | 9
60 | ]
61 | }
62 | },
63 | {
64 | "name": "Test Case 2",
65 | "input": {
66 | "W": 6,
67 | "n": 4,
68 | "weights": [
69 | 1,
70 | 2,
71 | 3,
72 | 4
73 | ],
74 | "values": [
75 | 12,
76 | 17,
77 | 18,
78 | 25
79 | ]
80 | }
81 | },
82 | {
83 | "name": "Test Case 3",
84 | "input": {
85 | "W": 7,
86 | "n": 5,
87 | "weights": [
88 | 1,
89 | 2,
90 | 3,
91 | 4,
92 | 6
93 | ],
94 | "values": [
95 | 12,
96 | 17,
97 | 18,
98 | 25,
99 | 30
100 | ]
101 | }
102 | },
103 | {
104 | "name": "Test Case 4",
105 | "input": {
106 | "W": 16,
107 | "n": 8,
108 | "weights": [
109 | 1,
110 | 2,
111 | 3,
112 | 4,
113 | 6,
114 | 5,
115 | 9,
116 | 5
117 | ],
118 | "values": [
119 | 12,
120 | 17,
121 | 18,
122 | 25,
123 | 30,
124 | 23,
125 | 54,
126 | 75
127 | ]
128 | }
129 | },
130 | {
131 | "name": "Edge Case 1",
132 | "input": {
133 | "W": 0,
134 | "n": 5,
135 | "weights": [
136 | 1,
137 | 2,
138 | 3,
139 | 4,
140 | 6
141 | ],
142 | "values": [
143 | 12,
144 | 17,
145 | 18,
146 | 25,
147 | 30
148 | ]
149 | }
150 | },
151 | {
152 | "name": "Edge Case 2",
153 | "input": {
154 | "W": 4,
155 | "n": 0,
156 | "weights": [
157 | ],
158 | "values": [
159 | ]
160 | }
161 | },
162 | {
163 | "name": "Edge Case 3",
164 | "input": {
165 | "W": 4,
166 | "n": 3,
167 | "weights": [
168 | 2,
169 | 2,
170 | 3
171 | ],
172 | "values": [
173 | 99,
174 | 100,
175 | 151
176 | ]
177 | }
178 | }
179 | ]
180 | }
181 |
--------------------------------------------------------------------------------
/src/assets/problems/longest-common-subsequence.dp.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Longest Common Subsequence",
3 | "problem-statement": "For an array of the form arr[0], . . . , arr[n - 1] , a subsequence is any subset of arr[0], . . . , arr[n - 1] taken in order, of the form arr[i0 ], arr[i1 ], ... , arr[ik ] where 0 <= i0 < i 1 < · · · < ik < n . The goal of the problem is to find the longest common subsequence between two input arrays, arr1 and arr2 .",
4 | "input": {
5 | "arr1": "First Input Array",
6 | "arr2": "Second Input Array",
7 | "n1": "Length of First Input Array (Equal to arr1.length )",
8 | "n2": "Length of Second Input Array (Equal to arr2.length )"
9 | },
10 | "output": {
11 | "result": "The length of the longest common subsequence",
12 | "solution": "The elements of the longest common subsequence taken in order"
13 | },
14 | "provided-solution": {
15 | "tableShape": "2d",
16 | "tableDimension1": "n1 + 1",
17 | "tableDimension2": "n2 + 1",
18 | "initializationCode": "// No other initialization necessary",
19 | "for1Variable": "i",
20 | "for1Init": "0",
21 | "for1Condition": "i <= n1",
22 | "for1Update": "i = i + 1",
23 | "for2Variable": "j",
24 | "for2Init": "0",
25 | "for2Condition": "j <= n2",
26 | "for2Update": "j = j + 1",
27 | "setNextEntryCode": "if (i === 0 || j === 0) {\n entry = 0;\n} else if (arr1[i - 1] === arr2[j - 1]) {\n entry = T(i - 1, j - 1) + 1;\n} else {\n entry = Math.max(T(i - 1, j), T(i, j - 1));\n}",
28 | "defaultTableEntry": "",
29 | "useDefaultTableEntry": false,
30 | "returnValueCode": "result = T(n1, n2);",
31 | "nextEntryIndex1": "i",
32 | "nextEntryIndex2": "j",
33 | "setNextEntryTopDownCode": "if (i === 0 || j === 0) {\n entry = 0;\n} else if (arr1[i - 1] === arr2[j - 1]) {\n entry = getTableEntry(i - 1, j - 1) + 1;\n} else {\n entry = Math.max(getTableEntry(i - 1, j), getTableEntry(i, j - 1));\n}",
34 | "returnValueTopDownCode": "result = getTableEntry(n1, n2);",
35 | "useAuxiliaryTableWithDetailedSolution": false,
36 | "detailedSetNextEntryCode": "if (i === 0 || j === 0) {\n entry = 0;\n} else if (arr1[i - 1] === arr2[j - 1]) {\n entry = T(i - 1, j - 1) + 1;\n} else {\n entry = Math.max(T(i - 1, j), T(i, j - 1));\n}",
37 | "detailedReturnValueCode": "const result = T(n1, n2);\nconst solution = []\nlet i = n1;\nlet j = n2;\nwhile (i > 0 && j > 0) {\n if (arr1[i - 1] == arr2[j - 1]) {\n solution.unshift(arr1[i - 1]);\n i--;\n j--; // reduce values of i and j\n } else if (T(i - 1, j) > T(i, j - 1)) {\n i--;\n } else {\n j--;\n }\n}",
38 | "detailedSetNextEntryTopDownCode": "if (i === 0 || j === 0) {\n entry = 0;\n} else if (arr1[i - 1] === arr2[j - 1]) {\n entry = getTableEntry(i - 1, j - 1) + 1;\n} else {\n entry = Math.max(getTableEntry(i - 1, j), getTableEntry(i, j - 1));\n}",
39 | "detailedReturnValueTopDownCode": "const result = getTableEntry(n1, n2);\nconst solution = []\nlet i = n1;\nlet j = n2;\nwhile (i > 0 && j > 0) {\n if (arr1[i - 1] == arr2[j - 1]) {\n solution.unshift(arr1[i - 1]);\n i--;\n j--; // reduce values of i and j\n } else if (getTableEntry(i - 1, j) > getTableEntry(i, j - 1)) {\n i--;\n } else {\n j--;\n }\n}",
40 | "tableEntryDefinition": "T[ i ][ j ] Is the longest common subsequence between arr1[0], . . . , arr1[i - 1] and arr2[0], . . . , arr2[j - 1] .",
41 | "solutionNotes": "This solution runs in O(n1 * n2) time. When populating T[ i ][ j ] , we consider the case where arr1[i - 1] and arr2[j - 1] match, and the case where they do not."
42 | },
43 | "test-cases": [
44 | {
45 | "name": "Test Case 1",
46 | "input": {
47 | "n1": 7,
48 | "n2": 7,
49 | "arr1": [
50 | "t",
51 | "a",
52 | "n",
53 | "g",
54 | "l",
55 | "e",
56 | "d"
57 | ],
58 | "arr2": [
59 | "a",
60 | "l",
61 | "a",
62 | "d",
63 | "d",
64 | "i",
65 | "n"
66 | ]
67 | }
68 | },
69 | {
70 | "name": "Test Case 2",
71 | "input": {
72 | "n1": 11,
73 | "n2": 10,
74 | "arr1": [
75 | "e",
76 | "x",
77 | "p",
78 | "o",
79 | "n",
80 | "e",
81 | "n",
82 | "t",
83 | "i",
84 | "a",
85 | "l"
86 | ],
87 | "arr2": [
88 | "p",
89 | "o",
90 | "l",
91 | "y",
92 | "n",
93 | "o",
94 | "m",
95 | "i",
96 | "a",
97 | "l"
98 | ]
99 | }
100 | },
101 | {
102 | "name": "Test Case 3",
103 | "input": {
104 | "n1": 6,
105 | "n2": 7,
106 | "arr1": [
107 | "A",
108 | "B",
109 | "A",
110 | "Z",
111 | "D",
112 | "C"
113 | ],
114 | "arr2": [
115 | "B",
116 | "A",
117 | "C",
118 | "B",
119 | "A",
120 | "D",
121 | "Z"
122 | ]
123 | }
124 | },
125 | {
126 | "name": "Test Case 4",
127 | "input": {
128 | "n1": 9,
129 | "n2": 9,
130 | "arr1": [
131 | "t",
132 | "o",
133 | "m",
134 | "r",
135 | "i",
136 | "d",
137 | "d",
138 | "l",
139 | "e"
140 | ],
141 | "arr2": [
142 | "v",
143 | "o",
144 | "l",
145 | "d",
146 | "e",
147 | "m",
148 | "o",
149 | "r",
150 | "t"
151 | ]
152 | }
153 | },
154 | {
155 | "name": "Edge Case 1",
156 | "input": {
157 | "n1": 0,
158 | "n2": 0,
159 | "arr1": [
160 | ],
161 | "arr2": [
162 | ]
163 | }
164 | },
165 | {
166 | "name": "Edge Case 2",
167 | "input": {
168 | "n1": 1,
169 | "n2": 1,
170 | "arr1": [
171 | "A"
172 | ],
173 | "arr2": [
174 | "A"
175 | ]
176 | }
177 | },
178 | {
179 | "name": "Edge Case 3",
180 | "input": {
181 | "n1": 1,
182 | "n2": 1,
183 | "arr1": [
184 | "A"
185 | ],
186 | "arr2": [
187 | "B"
188 | ]
189 | }
190 | },
191 | {
192 | "name": "Edge Case 4",
193 | "input": {
194 | "n1": 4,
195 | "n2": 4,
196 | "arr1": [
197 | "A",
198 | "B",
199 | "C",
200 | "D"
201 | ],
202 | "arr2": [
203 | "A",
204 | "B",
205 | "C",
206 | "D"
207 | ]
208 | }
209 | },
210 | {
211 | "name": "Edge Case 5",
212 | "input": {
213 | "n1": 4,
214 | "n2": 4,
215 | "arr1": [
216 | "A",
217 | "B",
218 | "C",
219 | "D"
220 | ],
221 | "arr2": [
222 | "D",
223 | "C",
224 | "B",
225 | "A"
226 | ]
227 | }
228 | }
229 | ]
230 | }
231 |
--------------------------------------------------------------------------------
/src/assets/problems/longest-increasing-subsequence.dp.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Longest Increasing Subsequence",
3 | "problem-statement": "You are given an array of n numbers arr[0], . . . , arr[n - 1] . A subsequence is any subset of these numbers taken in order, of the form arr[i0], arr[i1], ... , arr[ik] where 0 <= i0 < i1 < · · · < ik < n , and an increasing subsequence is one in which the numbers are getting strictly larger. The goal of the problem is to find the increasing subsequence of greatest length.",
4 | "input": {
5 | "arr": "Input Array",
6 | "n": "Length of Input Array (Equal to arr.length )"
7 | },
8 | "output": {
9 | "result": "The length of the longest increasing subsequence",
10 | "solution": "The indices of the elements in the longest increasing subsequence (note that the indices rather than the elements themselves should be returned, since the indices are unique)"
11 | },
12 | "provided-solution": {
13 | "tableShape": "1d",
14 | "tableDimension1": "n",
15 | "initializationCode": "// No other initialization necessary",
16 | "for1Variable": "i",
17 | "for1Init": "0",
18 | "for1Condition": "i < n",
19 | "for1Update": "i = i + 1",
20 | "setNextEntryCode": "// In the worst case, the longest increasing subsequence ending\n// at i is the element at i itself, with length 1\n// We start with this assumption, then see if we can improve it\n\nfor (j = 0; j < i; j++) {\n // for every previous index j, check if we can get a better\n // result by extending the sequence ending at that index\n const previousSequenceLength = T(j);\n if (entry < previousSequenceLength + 1 && arr[i] > arr[j]) {\n entry = previousSequenceLength + 1;\n }\n}",
21 | "defaultTableEntry": "1",
22 | "useDefaultTableEntry": true,
23 | "returnValueCode": "let result = 0;\n\n// We need to return the max of the table entries\nfor (let i = 0; i < n; i++) {\n const possibleMax = T(i);\n if (possibleMax > result) {\n result = possibleMax;\n }\n}",
24 | "nextEntryIndex1": "i",
25 | "setNextEntryTopDownCode": "// In the worst case, the longest increasing subsequence ending\n// at i is the element at i itself, with length 1\n// We start with this assumption, then see if we can improve it\n\nfor (j = 0; j < i; j++) {\n // for every previous index j, check if we can get a better\n // result by extending the sequence ending at that index\n const previousSequenceLength = getTableEntry(j);\n if (entry < previousSequenceLength + 1 && arr[i] > arr[j]) {\n entry = previousSequenceLength + 1;\n }\n}",
26 | "returnValueTopDownCode": "let result = 0;\n\n// We need to return the max of the table entries\nfor (let i = 0; i < n; i++) {\n const possibleMax = getTableEntry(i);\n if (possibleMax > result) {\n result = possibleMax;\n }\n}",
27 | "useAuxiliaryTableWithDetailedSolution": true,
28 | "detailedSetNextEntryCode": "// In the worst case, the longest increasing subsequence ending\n// at i is the element at i itself, with length 1\n// We start with this assumption, then see if we can improve it\n\n// We also store the index of the last element of the\n// subsequence that was extended (initially null)\nlet secondaryEntry = null;\n\nfor (j = 0; j < i; j++) {\n // for every previous index j, check if we can get a better\n // result by extending the sequence ending at that index\n const previousSequenceLength = T(j);\n if (entry < previousSequenceLength + 1 && arr[i] > arr[j]) {\n entry = previousSequenceLength + 1;\n secondaryEntry = j;\n }\n}",
29 | "detailedReturnValueCode": "let result = 0;\nlet lastItemIndex = null;\n\n// We need to return the max of the table entries\nfor (let i = 0; i < n; i++) {\n const possibleMax = T(i);\n if (possibleMax > result) {\n result = possibleMax;\n lastItemIndex = i;\n }\n}\n\nlet solution = [];\n// backtrack from the end\nwhile (lastItemIndex !== null) {\n solution.unshift(lastItemIndex); // append to front\n lastItemIndex = T2(lastItemIndex);\n}",
30 | "detailedSetNextEntryTopDownCode": "// In the worst case, the longest increasing subsequence ending\n// at i is the element at i itself, with length 1\n// We start with this assumption, then see if we can improve it\n\n// We also store the index of the last element of the\n// subsequence that was extended (initially null)\nlet secondaryEntry = null;\n\nfor (j = 0; j < i; j++) {\n // for every previous index j, check if we can get a better\n // result by extending the sequence ending at that index\n const previousSequenceLength = getTableEntry(j);\n if (entry < previousSequenceLength + 1 && arr[i] > arr[j]) {\n entry = previousSequenceLength + 1;\n secondaryEntry = j;\n }\n}",
31 | "detailedReturnValueTopDownCode": "let result = 0;\nlet lastItemIndex = null;\n\n// We need to return the max of the table entries\nfor (let i = 0; i < n; i++) {\n const possibleMax = getTableEntry(i);\n if (possibleMax > result) {\n result = possibleMax;\n lastItemIndex = i;\n }\n}\n\nlet solution = [];\n// backtrack from the end\nwhile (lastItemIndex !== null) {\n solution.unshift(lastItemIndex); // append to front\n lastItemIndex = T2(lastItemIndex);\n}",
32 | "tableEntryDefinition": "T[ i ] is the length of the longest increasing subsequence whose last element is at index i ",
33 | "auxiliaryTableEntryDefinition": "T2[ i ] is the index of the second last element of the longest increasing subsequence whose last element is at index i ",
34 | "solutionNotes": "This solution runs in O(n2 ) time. Note that it is not enough to store the length of the longest increasing subsequence within the first i elements - we must use a stricter condition so that we know which sequences can be extended when checking previous table entries. This also means that we need to return the largest value of the table entries as our final result, since the longest subsequence may end at any index of the table."
35 | },
36 | "test-cases": [
37 | {
38 | "name": "Test Case 1",
39 | "input": {
40 | "n": 8,
41 | "arr": [
42 | 5,
43 | 7,
44 | 4,
45 | -3,
46 | 9,
47 | 1,
48 | 4,
49 | 8
50 | ]
51 | }
52 | },
53 | {
54 | "name": "Test Case 2",
55 | "input": {
56 | "n": 13,
57 | "arr": [
58 | 7,
59 | 11,
60 | -5,
61 | -2,
62 | 5,
63 | 1,
64 | 16,
65 | 6,
66 | 7,
67 | 11,
68 | 8,
69 | 9,
70 | 0
71 | ]
72 | }
73 | },
74 | {
75 | "name": "Test Case 3",
76 | "input": {
77 | "n": 20,
78 | "arr": [
79 | 0,
80 | 1,
81 | 1,
82 | 8,
83 | 9,
84 | 9,
85 | 9,
86 | 8,
87 | 8,
88 | 1,
89 | 9,
90 | 9,
91 | 9,
92 | 1,
93 | 1,
94 | 9,
95 | 7,
96 | 2,
97 | 5,
98 | 3
99 | ]
100 | }
101 | },
102 | {
103 | "name": "Edge Case 1",
104 | "input": {
105 | "n": 1,
106 | "arr": [
107 | 0
108 | ]
109 | }
110 | },
111 | {
112 | "name": "Edge Case 2",
113 | "input": {
114 | "n": 1,
115 | "arr": [
116 | 1
117 | ]
118 | }
119 | },
120 | {
121 | "name": "Edge Case 3",
122 | "input": {
123 | "n": 0,
124 | "arr": [
125 | ]
126 | }
127 | },
128 | {
129 | "name": "Edge Case 4",
130 | "input": {
131 | "n": 10,
132 | "arr": [
133 | 0,
134 | 1,
135 | 2,
136 | 3,
137 | 4,
138 | 5,
139 | 6,
140 | 7,
141 | 8,
142 | 9
143 | ]
144 | }
145 | },
146 | {
147 | "name": "Edge Case 5",
148 | "input": {
149 | "n": 10,
150 | "arr": [
151 | 9,
152 | 8,
153 | 7,
154 | 6,
155 | 5,
156 | 4,
157 | 3,
158 | 2,
159 | 1,
160 | 0
161 | ]
162 | }
163 | }
164 | ]
165 | }
166 |
--------------------------------------------------------------------------------
/src/assets/codemirror/codemirror.css:
--------------------------------------------------------------------------------
1 | /* BASICS */
2 |
3 | .CodeMirror {
4 | /* Set height, width, borders, and global font properties here */
5 | font-family: monospace;
6 | height: 300px;
7 | /*color: black;*/
8 | direction: ltr;
9 | }
10 |
11 | /* PADDING */
12 |
13 | .CodeMirror-lines {
14 | padding: 4px 0; /* Vertical padding around content */
15 | }
16 |
17 | .CodeMirror pre {
18 | padding: 0 4px; /* Horizontal padding of content */
19 | }
20 |
21 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
22 | background-color: white; /* The little square between H and V scrollbars */
23 | }
24 |
25 | /* GUTTER */
26 |
27 | .CodeMirror-gutters {
28 | border-right: 1px solid #ddd;
29 | background-color: rgba(134, 134, 129, 0.94);
30 | white-space: nowrap;
31 | }
32 |
33 | .CodeMirror-linenumbers {
34 | }
35 |
36 | .CodeMirror-linenumber {
37 | padding: 0 3px 0 5px;
38 | min-width: 20px;
39 | text-align: right;
40 | color: white;
41 | white-space: nowrap;
42 | }
43 |
44 | .CodeMirror-guttermarker {
45 | color: black;
46 | }
47 |
48 | .CodeMirror-guttermarker-subtle {
49 | color: #999;
50 | }
51 |
52 | /* CURSOR */
53 |
54 | .CodeMirror-cursor {
55 | border-left: 1px solid #868686;
56 | border-right: none;
57 | width: 0;
58 | }
59 |
60 | /* Shown when moving in bi-directional text */
61 | .CodeMirror div.CodeMirror-secondarycursor {
62 | border-left: 1px solid silver;
63 | }
64 |
65 | .cm-fat-cursor .CodeMirror-cursor {
66 | width: auto;
67 | border: 0 !important;
68 | background: #7e7;
69 | }
70 |
71 | .cm-fat-cursor div.CodeMirror-cursors {
72 | z-index: 1;
73 | }
74 |
75 | .cm-fat-cursor-mark {
76 | background-color: rgba(20, 255, 20, 0.5);
77 | -webkit-animation: blink 1.06s steps(1) infinite;
78 | -moz-animation: blink 1.06s steps(1) infinite;
79 | animation: blink 1.06s steps(1) infinite;
80 | }
81 |
82 | .cm-animate-fat-cursor {
83 | width: auto;
84 | border: 0;
85 | -webkit-animation: blink 1.06s steps(1) infinite;
86 | -moz-animation: blink 1.06s steps(1) infinite;
87 | animation: blink 1.06s steps(1) infinite;
88 | background-color: #7e7;
89 | }
90 |
91 | @-moz-keyframes blink {
92 | 0% {
93 | }
94 | 50% {
95 | background-color: transparent;
96 | }
97 | 100% {
98 | }
99 | }
100 |
101 | @-webkit-keyframes blink {
102 | 0% {
103 | }
104 | 50% {
105 | background-color: transparent;
106 | }
107 | 100% {
108 | }
109 | }
110 |
111 | @keyframes blink {
112 | 0% {
113 | }
114 | 50% {
115 | background-color: transparent;
116 | }
117 | 100% {
118 | }
119 | }
120 |
121 | /* Can style cursor different in overwrite (non-insert) mode */
122 | .CodeMirror-overwrite .CodeMirror-cursor {
123 | }
124 |
125 | .cm-tab {
126 | display: inline-block;
127 | text-decoration: inherit;
128 | }
129 |
130 | .CodeMirror-rulers {
131 | position: absolute;
132 | left: 0;
133 | right: 0;
134 | top: -50px;
135 | bottom: -20px;
136 | overflow: hidden;
137 | }
138 |
139 | .CodeMirror-ruler {
140 | border-left: 1px solid #ccc;
141 | top: 0;
142 | bottom: 0;
143 | position: absolute;
144 | }
145 |
146 | /* DEFAULT THEME */
147 |
148 | .cm-s-default .cm-header {
149 | color: blue;
150 | }
151 |
152 | .cm-s-default .cm-quote {
153 | color: #090;
154 | }
155 |
156 | .cm-negative {
157 | color: #d44;
158 | }
159 |
160 | .cm-positive {
161 | color: #292;
162 | }
163 |
164 | .cm-header, .cm-strong {
165 | font-weight: bold;
166 | }
167 |
168 | .cm-em {
169 | font-style: italic;
170 | }
171 |
172 | .cm-link {
173 | text-decoration: underline;
174 | }
175 |
176 | .cm-strikethrough {
177 | text-decoration: line-through;
178 | }
179 |
180 | .cm-s-default .cm-keyword {
181 | color: #e700bf;
182 | }
183 |
184 | .cm-s-default .cm-atom {
185 | color: #0098d3;
186 | }
187 |
188 | .cm-s-default .cm-number {
189 | color: #20c583;
190 | }
191 |
192 | .cm-s-default .cm-def {
193 | color: #00c5ff;
194 | }
195 |
196 | .cm-s-default .cm-variable,
197 | .cm-s-default .cm-punctuation,
198 | .cm-s-default .cm-property,
199 | .cm-s-default .cm-operator {
200 | }
201 |
202 | .cm-s-default .cm-variable-2 {
203 | color: #0086c1;
204 | }
205 |
206 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {
207 | color: #085;
208 | }
209 |
210 | .cm-s-default .cm-comment {
211 | color: #c96900;
212 | }
213 |
214 | .cm-s-default .cm-string {
215 | color: #a11;
216 | }
217 |
218 | .cm-s-default .cm-string-2 {
219 | color: #f50;
220 | }
221 |
222 | .cm-s-default .cm-meta {
223 | color: #555;
224 | }
225 |
226 | .cm-s-default .cm-qualifier {
227 | color: #999999;
228 | }
229 |
230 | .cm-s-default .cm-builtin {
231 | color: #30a;
232 | }
233 |
234 | .cm-s-default .cm-bracket {
235 | color: #997;
236 | }
237 |
238 | .cm-s-default .cm-tag {
239 | color: #19b400;
240 | }
241 |
242 | .cm-s-default .cm-attribute {
243 | color: #00c;
244 | }
245 |
246 | .cm-s-default .cm-hr {
247 | color: #a1a1a1;
248 | }
249 |
250 | .cm-s-default .cm-link {
251 | color: #0000eb;
252 | }
253 |
254 | .cm-s-default .cm-error {
255 | color: #f00;
256 | }
257 |
258 | .cm-invalidchar {
259 | color: #f00;
260 | }
261 |
262 | .CodeMirror-composing {
263 | border-bottom: 2px solid;
264 | }
265 |
266 | /* Default styles for common addons */
267 |
268 | div.CodeMirror span.CodeMirror-matchingbracket {
269 | color: #0b0;
270 | }
271 |
272 | div.CodeMirror span.CodeMirror-nonmatchingbracket {
273 | color: #a22;
274 | }
275 |
276 | .CodeMirror-matchingtag {
277 | background: rgba(255, 150, 0, .3);
278 | }
279 |
280 | .CodeMirror-activeline-background {
281 | background: #e8f2ff;
282 | }
283 |
284 | /* STOP */
285 |
286 | /* The rest of this file contains styles related to the mechanics of
287 | the editor. You probably shouldn't touch them. */
288 |
289 | .CodeMirror {
290 | position: relative;
291 | overflow: hidden;
292 | background: rgba(0, 0, 0, 0.05);
293 | }
294 |
295 | .CodeMirror-scroll {
296 | overflow: scroll !important; /* Things will break if this is overridden */
297 | /* 30px is the magic margin used to hide the element's real scrollbars */
298 | /* See overflow: hidden in .CodeMirror */
299 | margin-bottom: -30px;
300 | margin-right: -30px;
301 | padding-bottom: 30px;
302 | height: 100%;
303 | outline: none; /* Prevent dragging from highlighting the element */
304 | position: relative;
305 | }
306 |
307 | .CodeMirror-sizer {
308 | position: relative;
309 | border-right: 30px solid transparent;
310 | }
311 |
312 | /* The fake, visible scrollbars. Used to force redraw during scrolling
313 | before actual scrolling happens, thus preventing shaking and
314 | flickering artifacts. */
315 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
316 | position: absolute;
317 | z-index: 6;
318 | display: none;
319 | }
320 |
321 | .CodeMirror-vscrollbar {
322 | right: 0;
323 | top: 0;
324 | overflow-x: hidden;
325 | overflow-y: scroll;
326 | }
327 |
328 | .CodeMirror-hscrollbar {
329 | bottom: 0;
330 | left: 0;
331 | overflow-y: hidden;
332 | overflow-x: scroll;
333 | }
334 |
335 | .CodeMirror-scrollbar-filler {
336 | right: 0;
337 | bottom: 0;
338 | }
339 |
340 | .CodeMirror-gutter-filler {
341 | left: 0;
342 | bottom: 0;
343 | }
344 |
345 | .CodeMirror-gutters {
346 | position: absolute;
347 | left: 0;
348 | top: 0;
349 | min-height: 100%;
350 | z-index: 3;
351 | }
352 |
353 | .CodeMirror-gutter {
354 | white-space: normal;
355 | height: 100%;
356 | display: inline-block;
357 | vertical-align: top;
358 | margin-bottom: -30px;
359 | }
360 |
361 | .CodeMirror-gutter-wrapper {
362 | position: absolute;
363 | z-index: 4;
364 | background: none !important;
365 | border: none !important;
366 | }
367 |
368 | .CodeMirror-gutter-background {
369 | position: absolute;
370 | top: 0;
371 | bottom: 0;
372 | z-index: 4;
373 | }
374 |
375 | .CodeMirror-gutter-elt {
376 | position: absolute;
377 | cursor: default;
378 | z-index: 4;
379 | }
380 |
381 | .CodeMirror-gutter-wrapper ::selection {
382 | background-color: transparent
383 | }
384 |
385 | .CodeMirror-gutter-wrapper ::-moz-selection {
386 | background-color: transparent
387 | }
388 |
389 | .CodeMirror-lines {
390 | cursor: text;
391 | min-height: 1px; /* prevents collapsing before first draw */
392 | }
393 |
394 | .CodeMirror pre {
395 | /* Reset some styles that the rest of the page might have set */
396 | -moz-border-radius: 0;
397 | -webkit-border-radius: 0;
398 | border-radius: 0;
399 | border-width: 0;
400 | background: transparent;
401 | font-family: inherit;
402 | font-size: inherit;
403 | margin: 0;
404 | white-space: pre;
405 | word-wrap: normal;
406 | line-height: inherit;
407 | color: inherit;
408 | z-index: 2;
409 | position: relative;
410 | overflow: visible;
411 | -webkit-tap-highlight-color: transparent;
412 | -webkit-font-variant-ligatures: contextual;
413 | font-variant-ligatures: contextual;
414 | }
415 |
416 | .CodeMirror-wrap pre {
417 | word-wrap: break-word;
418 | white-space: pre-wrap;
419 | word-break: normal;
420 | }
421 |
422 | .CodeMirror-linebackground {
423 | position: absolute;
424 | left: 0;
425 | right: 0;
426 | top: 0;
427 | bottom: 0;
428 | z-index: 0;
429 | }
430 |
431 | .CodeMirror-linewidget {
432 | position: relative;
433 | z-index: 2;
434 | padding: 0.1px; /* Force widget margins to stay inside of the container */
435 | }
436 |
437 | .CodeMirror-widget {
438 | }
439 |
440 | .CodeMirror-rtl pre {
441 | direction: rtl;
442 | }
443 |
444 | .CodeMirror-code {
445 | outline: none;
446 | }
447 |
448 | /* Force content-box sizing for the elements where we expect it */
449 | .CodeMirror-scroll,
450 | .CodeMirror-sizer,
451 | .CodeMirror-gutter,
452 | .CodeMirror-gutters,
453 | .CodeMirror-linenumber {
454 | -moz-box-sizing: content-box;
455 | box-sizing: content-box;
456 | }
457 |
458 | .CodeMirror-measure {
459 | position: absolute;
460 | width: 100%;
461 | height: 0;
462 | overflow: hidden;
463 | visibility: hidden;
464 | }
465 |
466 | .CodeMirror-cursor {
467 | position: absolute;
468 | pointer-events: none;
469 | }
470 |
471 | .CodeMirror-measure pre {
472 | position: static;
473 | }
474 |
475 | div.CodeMirror-cursors {
476 | visibility: hidden;
477 | position: relative;
478 | z-index: 3;
479 | }
480 |
481 | div.CodeMirror-dragcursors {
482 | visibility: visible;
483 | }
484 |
485 | .CodeMirror-focused div.CodeMirror-cursors {
486 | visibility: visible;
487 | }
488 |
489 | .CodeMirror-selected {
490 | background: #d9d9d9;
491 | }
492 |
493 | .CodeMirror-focused .CodeMirror-selected {
494 | background: #d7d4f0;
495 | }
496 |
497 | .CodeMirror-crosshair {
498 | cursor: crosshair;
499 | }
500 |
501 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection {
502 | /*background: rgba(0.1, 0.1, 0.1, 0.1);*/
503 | }
504 |
505 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
506 | /*background: rgba(0.1, 0.1, 0.1, 0.1);*/
507 | }
508 |
509 | .cm-searching {
510 | background-color: #ffa;
511 | background-color: rgba(255, 255, 0, .4);
512 | }
513 |
514 | /* Used to force a border model for a node */
515 | .cm-force-border {
516 | padding-right: .1px;
517 | }
518 |
519 | @media print {
520 | /* Hide the cursor when printing */
521 | .CodeMirror div.CodeMirror-cursors {
522 | visibility: hidden;
523 | }
524 | }
525 |
526 | /* See issue #2901 */
527 | .cm-tab-wrap-hack:after {
528 | content: '';
529 | }
530 |
531 | /* Help users use markselection to safely style text background */
532 | span.CodeMirror-selectedtext {
533 | background: none;
534 | }
535 |
--------------------------------------------------------------------------------
/src/app/dialogs/animation-dialog/animation-dialog.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Inject, OnInit} from '@angular/core';
2 | import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material';
3 | import {AnimationDataService} from '../../providers/animation-data.service';
4 |
5 | @Component({
6 | selector: 'app-animation-dialog',
7 | templateUrl: './animation-dialog.component.html',
8 | styleUrls: ['./animation-dialog.component.css']
9 | })
10 | export class AnimationDialogComponent implements OnInit {
11 |
12 | objectKeys = Object.keys;
13 |
14 | isDarkTheme: boolean;
15 | title: string;
16 | subtitle: string;
17 | result;
18 | solution;
19 | showSolution: boolean;
20 | input;
21 | log;
22 | useAuxiliaryTable: boolean;
23 | tableDimension1: number;
24 | tableDimension2: number;
25 |
26 | currentMainTable: any;
27 | currentAuxiliaryTable: any;
28 | isTable2d: boolean;
29 | transposeTable: boolean;
30 |
31 | totalGets: number;
32 | totalSets: number;
33 | mainTableGets: number;
34 | mainTableSets: number;
35 | auxiliaryTableGets: number;
36 | auxiliaryTableSets: number;
37 |
38 | currentFrame: number;
39 | totalFrames: number;
40 | isCurrentlyPlayingAnimation: boolean = false;
41 | currentlyPlayingIntervalVariable: any = null;
42 | currentlySwitchingPlayState: boolean = false;
43 |
44 | playIntervalTimeMSOptions = [
45 | {text: 'Slower', value: 750},
46 | {text: 'Slow', value: 300},
47 | {text: 'Medium', value: 180},
48 | {text: 'Fast', value: 80},
49 | {text: 'Faster', value: 25},
50 | {text: 'Even Faster', value: 2}
51 | ];
52 | playIntervalTimeMS: number = this.playIntervalTimeMSOptions[2].value;
53 |
54 | numSkipSections: number = 8;
55 | skipFrames = [];
56 |
57 | lastAffectedIndex1: number = -1;
58 | lastAffectedIndex2: number = -1;
59 | lastOperation: string = null;
60 | lastAffectedTable: string = null;
61 |
62 | constructor(
63 | public dialogRef: MatDialogRef,
64 | public animationDataService: AnimationDataService,
65 | @Inject(MAT_DIALOG_DATA) public data: any) {
66 | }
67 |
68 | ngOnInit() {
69 | this.isDarkTheme = this.data.isDarkTheme;
70 | this.title = this.animationDataService.title;
71 | this.subtitle = this.animationDataService.subtitle;
72 | this.result = this.animationDataService.result;
73 | this.solution = this.animationDataService.solution;
74 | this.showSolution = !!this.solution || this.solution === 0;
75 | this.input = this.animationDataService.input;
76 | this.log = this.animationDataService.log;
77 | this.useAuxiliaryTable = this.animationDataService.useAuxiliaryTable;
78 | this.tableDimension1 = this.animationDataService.mainTableDimension1;
79 | this.tableDimension2 = this.animationDataService.mainTableDimension2;
80 | this.isTable2d = this.tableDimension2 >= 0;
81 | this.transposeTable = this.animationDataService.transposeTable;
82 | this.resetTables();
83 | this.currentFrame = 0;
84 | this.totalFrames = this.log.length + 1;
85 | this.totalGets = 0;
86 | this.totalSets = 0;
87 | this.mainTableGets = 0;
88 | this.mainTableSets = 0;
89 | this.auxiliaryTableGets = 0;
90 | this.auxiliaryTableSets = 0;
91 | for (let i = 0; i < this.log.length; i++) {
92 | const entry = this.log[i];
93 | if (entry.action === 'set') {
94 | this.totalSets++;
95 | if (this.getRelevantTable(entry) === this.currentMainTable) {
96 | this.mainTableSets++;
97 | } else {
98 | this.auxiliaryTableSets++;
99 | }
100 | } else if (entry.action === 'get') {
101 | this.totalGets++;
102 | if (this.getRelevantTable(entry) === this.currentMainTable) {
103 | this.mainTableGets++;
104 | } else {
105 | this.auxiliaryTableGets++;
106 | }
107 | }
108 | if (this.isTable2d && this.transposeTable) {
109 | const index1 = entry.index2;
110 | const index2 = entry.index1;
111 | this.log[i].index1 = index1;
112 | this.log[i].index2 = index2;
113 | }
114 | }
115 |
116 | const skipSectionLength = this.totalFrames / this.numSkipSections;
117 | for (let i = 0; i < this.numSkipSections; i++) {
118 | this.skipFrames.push(Math.round(i * skipSectionLength));
119 | }
120 | this.skipFrames.push(this.totalFrames);
121 | }
122 |
123 | playAnimation(): void {
124 | const component = this;
125 | component.isCurrentlyPlayingAnimation = true;
126 | component.currentlySwitchingPlayState = false;
127 | component.currentlyPlayingIntervalVariable = setInterval(function () {
128 | if (component.isCurrentlyPlayingAnimation) {
129 | if (component.canStepForward()) {
130 | component.stepForward();
131 | } else {
132 | component.stopPlayingAnimation();
133 | }
134 | }
135 | }, component.playIntervalTimeMS);
136 | }
137 |
138 | resetAnimation(): void {
139 | this.currentFrame = 0;
140 | this.resetTables();
141 | }
142 |
143 | stopPlayingAnimation(): void {
144 | const component = this;
145 | component.isCurrentlyPlayingAnimation = false;
146 | component.currentlySwitchingPlayState = true;
147 | if (component.currentlyPlayingIntervalVariable) {
148 | clearInterval(component.currentlyPlayingIntervalVariable);
149 | component.currentlyPlayingIntervalVariable = null;
150 | }
151 | setTimeout(function () {
152 | component.currentlySwitchingPlayState = false;
153 | }, component.playIntervalTimeMS + 10);
154 | }
155 |
156 | stepBackward(): void {
157 | if (this.currentFrame <= this.log.length) {
158 | const entry = this.log[this.currentFrame - 1];
159 | const table = this.getRelevantTable(entry);
160 | const cell = this.getTableCell(table, entry.index1, entry.index2);
161 | if (entry.action == 'set') {
162 | cell.pop();
163 | }
164 | }
165 | const lastOperationFrame = this.currentFrame - 2;
166 | if (lastOperationFrame >= 0 && lastOperationFrame < this.log.length) {
167 | const lastOperationEntry = this.log[lastOperationFrame];
168 | this.lastAffectedIndex1 = lastOperationEntry.index1;
169 | this.lastAffectedIndex2 = lastOperationEntry.index2;
170 | this.lastOperation = lastOperationEntry.action;
171 | this.lastAffectedTable = this.getRelevantTable(lastOperationEntry);
172 | } else {
173 | this.lastAffectedIndex1 = -1;
174 | this.lastAffectedIndex2 = -1;
175 | this.lastOperation = null;
176 | this.lastAffectedTable = null;
177 | }
178 | this.currentFrame--;
179 | }
180 |
181 | stepForward(): void {
182 | if (this.currentFrame < this.log.length) {
183 | const entry = this.log[this.currentFrame];
184 | const table = this.getRelevantTable(entry);
185 | const cell = this.getTableCell(table, entry.index1, entry.index2);
186 | if (entry.action == 'set') {
187 | cell.push(entry.value);
188 | }
189 | this.lastOperation = entry.action;
190 | this.lastAffectedIndex1 = entry.index1;
191 | this.lastAffectedIndex2 = entry.index2;
192 | this.lastAffectedTable = table;
193 | } else {
194 | this.lastAffectedIndex1 = -1;
195 | this.lastAffectedIndex2 = -1;
196 | this.lastOperation = null;
197 | this.lastAffectedTable = null;
198 | }
199 | this.currentFrame++;
200 | }
201 |
202 | canStepBackward(): boolean {
203 | return this.currentFrame > 0;
204 | }
205 |
206 | canStepForward(): boolean {
207 | return this.currentFrame < this.totalFrames;
208 | }
209 |
210 | skipForward() {
211 | this.stepForward();
212 | while (this.canStepForward() && this.skipFrames.indexOf(this.currentFrame) < 0) {
213 | this.stepForward();
214 | }
215 | }
216 |
217 | skipBackward() {
218 | this.stepBackward();
219 | while (this.canStepBackward() && this.skipFrames.indexOf(this.currentFrame) < 0) {
220 | this.stepBackward();
221 | }
222 | }
223 |
224 | getRelevantTable(logEntry) {
225 | if (logEntry.table === 'T') {
226 | return this.currentMainTable;
227 | } else if (logEntry.table === 'T2') {
228 | return this.currentAuxiliaryTable;
229 | }
230 | }
231 |
232 | getTableCell(table, i, j) {
233 | if (!this.isTable2d) {
234 | return table[i];
235 | } else {
236 | return table[i][j];
237 | }
238 | }
239 |
240 | getTableDisplayedValue(table, i, j) {
241 | let targetCellArray;
242 | if (j < 0) {
243 | targetCellArray = table[i];
244 | } else {
245 | targetCellArray = table[i][j];
246 | }
247 | if (targetCellArray.length === 0) {
248 | return '';
249 | } else {
250 | return this.getDisplayedValue(targetCellArray[targetCellArray.length - 1]);
251 | }
252 | }
253 |
254 | getDisplayedValue(value: any): string {
255 | if (value === null || value === undefined) {
256 | return 'null';
257 | }
258 | if ((typeof value) === (typeof 1) || value === 'infinity' || value === '-infinity') {
259 | if (value === Number.MAX_SAFE_INTEGER || value === Number.MAX_VALUE || value === 'infinity') {
260 | return '∞';
261 | } else if (value === Number.MIN_SAFE_INTEGER || value === Number.MIN_VALUE || value === '-infinity') {
262 | return '-∞';
263 | } else {
264 | return value !== parseInt(value) ? value.toFixed(2) : Math.floor(value);
265 | }
266 | } else if ((typeof value) === (typeof true)) {
267 | return value ? '✔ ' : '✘ ';
268 | } else if ((typeof value) === (typeof 'string')) {
269 | return value;
270 | } else {
271 | return JSON.stringify(value);
272 | }
273 | }
274 |
275 | isArray(item: any): boolean {
276 | return item && item.constructor === Array;
277 | }
278 |
279 | shouldDisplayArray(item: any): boolean {
280 | if (this.isRectangular2dArray(item)) {
281 | return item.length > 0 && item[0].length > 0;
282 | } else {
283 | return item.length > 0;
284 | }
285 | }
286 |
287 | isRectangular2dArray(item: any): boolean {
288 | if (this.isArray(item) && item.constructor === Array && item.length > 0 && item[0]) {
289 | if (item[0].constructor !== Array) {
290 | return false;
291 | }
292 | for (let i = 1; i < item.length; i++) {
293 | if (item[i].constructor !== Array || item[i].length !== item[i - 1].length) {
294 | return false;
295 | }
296 | }
297 | return true;
298 | } else {
299 | return false;
300 | }
301 | }
302 |
303 | resetTables() {
304 | this.currentMainTable = [];
305 | this.currentAuxiliaryTable = [];
306 | [this.currentMainTable, this.currentAuxiliaryTable].forEach(table => {
307 | if (this.isTable2d) {
308 | for (let i = 0; i < this.tableDimension1; i++) {
309 | let nextArray = [];
310 | for (let i = 0; i < this.tableDimension2; i++) {
311 | nextArray.push([]);
312 | }
313 | table.push(nextArray);
314 | }
315 | } else {
316 | for (let i = 0; i < this.tableDimension1; i++) {
317 | table.push([]);
318 | }
319 | }
320 | });
321 | }
322 |
323 | onNoClick(): void {
324 | this.dialogRef.close();
325 | }
326 |
327 | range(_p: number, _t?: number, _s?: number): Array {
328 |
329 | let start: number = (_t) ? _p : 0;
330 | let stop: number = (_t) ? _t : _p;
331 | let step: number = (_s) ? _s : 1;
332 |
333 | let t: Array = [];
334 | for (let i = start; i < stop; i = i + step) {
335 | t.push(i);
336 | }
337 |
338 | return t;
339 | }
340 |
341 | }
342 |
--------------------------------------------------------------------------------
/src/app/dialogs/animation-dialog/animation-dialog.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{title}}
7 |
8 | {{subtitle}}
9 |
10 |
11 |
12 |
13 |
14 |
15 | Input
16 |
17 |
18 |
{{inputField}}:
19 |
21 |
22 |
24 |
25 |
26 |
28 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
40 |
41 |
42 |
43 |
44 |
45 |
47 | empty Array
48 |
49 |
50 |
51 |
52 |
53 | Output
54 |
55 | Result:
56 |
58 |
59 |
60 | Solution:
61 |
63 |
64 |
66 |
67 |
68 |
70 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
80 |
82 |
83 |
84 |
85 |
86 |
87 |
89 | empty Array
90 |
91 |
92 |
93 |
94 |
95 |
102 |
103 |
104 |
105 |
106 |
116 |
117 |
118 |
119 |
125 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
139 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
160 |
161 |
162 |
163 |
169 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
183 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
199 |
200 |
201 |
202 |
203 |
204 | {{log.length}} table {{log.length === 1 ? 'operation' : 'operations'}}
205 |
206 | {{totalGets}} {{totalGets === 1 ? 'read' : 'reads'}}
208 |
209 | {{totalSets}} {{totalSets === 1 ? 'write' : 'writes'}}
210 |
211 |
212 |
213 |
218 |
219 |
220 |
225 |
226 |
227 |
232 |
233 |
234 |
239 |
240 |
241 |
246 |
247 |
248 |
253 |
254 |
255 |
259 |
260 |
261 |
265 |
266 |
267 |
268 |
269 |
270 |
273 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
--------------------------------------------------------------------------------
/src/assets/codemirror/languages/javascript/test.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE
3 |
4 | (function() {
5 | var mode = CodeMirror.getMode({indentUnit: 2}, "javascript");
6 | function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
7 |
8 | MT("locals",
9 | "[keyword function] [def foo]([def a], [def b]) { [keyword var] [def c] [operator =] [number 10]; [keyword return] [variable-2 a] [operator +] [variable-2 c] [operator +] [variable d]; }");
10 |
11 | MT("comma-and-binop",
12 | "[keyword function](){ [keyword var] [def x] [operator =] [number 1] [operator +] [number 2], [def y]; }");
13 |
14 | MT("destructuring",
15 | "([keyword function]([def a], [[[def b], [def c] ]]) {",
16 | " [keyword let] {[def d], [property foo]: [def c][operator =][number 10], [def x]} [operator =] [variable foo]([variable-2 a]);",
17 | " [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];",
18 | "})();");
19 |
20 | MT("destructure_trailing_comma",
21 | "[keyword let] {[def a], [def b],} [operator =] [variable foo];",
22 | "[keyword let] [def c];"); // Parser still in good state?
23 |
24 | MT("class_body",
25 | "[keyword class] [def Foo] {",
26 | " [property constructor]() {}",
27 | " [property sayName]() {",
28 | " [keyword return] [string-2 `foo${][variable foo][string-2 }oo`];",
29 | " }",
30 | "}");
31 |
32 | MT("class",
33 | "[keyword class] [def Point] [keyword extends] [variable SuperThing] {",
34 | " [keyword get] [property prop]() { [keyword return] [number 24]; }",
35 | " [property constructor]([def x], [def y]) {",
36 | " [keyword super]([string 'something']);",
37 | " [keyword this].[property x] [operator =] [variable-2 x];",
38 | " }",
39 | "}");
40 |
41 | MT("anonymous_class_expression",
42 | "[keyword const] [def Adder] [operator =] [keyword class] [keyword extends] [variable Arithmetic] {",
43 | " [property add]([def a], [def b]) {}",
44 | "};");
45 |
46 | MT("named_class_expression",
47 | "[keyword const] [def Subber] [operator =] [keyword class] [def Subtract] {",
48 | " [property sub]([def a], [def b]) {}",
49 | "};");
50 |
51 | MT("class_async_method",
52 | "[keyword class] [def Foo] {",
53 | " [property sayName1]() {}",
54 | " [keyword async] [property sayName2]() {}",
55 | "}");
56 |
57 | MT("import",
58 | "[keyword function] [def foo]() {",
59 | " [keyword import] [def $] [keyword from] [string 'jquery'];",
60 | " [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];",
61 | "}");
62 |
63 | MT("import_trailing_comma",
64 | "[keyword import] {[def foo], [def bar],} [keyword from] [string 'baz']")
65 |
66 | MT("import_dynamic",
67 | "[keyword import]([string 'baz']).[property then]")
68 |
69 | MT("import_dynamic",
70 | "[keyword const] [def t] [operator =] [keyword import]([string 'baz']).[property then]")
71 |
72 | MT("const",
73 | "[keyword function] [def f]() {",
74 | " [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];",
75 | "}");
76 |
77 | MT("for/of",
78 | "[keyword for]([keyword let] [def of] [keyword of] [variable something]) {}");
79 |
80 | MT("for await",
81 | "[keyword for] [keyword await]([keyword let] [def of] [keyword of] [variable something]) {}");
82 |
83 | MT("generator",
84 | "[keyword function*] [def repeat]([def n]) {",
85 | " [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])",
86 | " [keyword yield] [variable-2 i];",
87 | "}");
88 |
89 | MT("let_scoping",
90 | "[keyword function] [def scoped]([def n]) {",
91 | " { [keyword var] [def i]; } [variable-2 i];",
92 | " { [keyword let] [def j]; [variable-2 j]; } [variable j];",
93 | " [keyword if] ([atom true]) { [keyword const] [def k]; [variable-2 k]; } [variable k];",
94 | "}");
95 |
96 | MT("switch_scoping",
97 | "[keyword switch] ([variable x]) {",
98 | " [keyword default]:",
99 | " [keyword let] [def j];",
100 | " [keyword return] [variable-2 j]",
101 | "}",
102 | "[variable j];")
103 |
104 | MT("leaving_scope",
105 | "[keyword function] [def a]() {",
106 | " {",
107 | " [keyword const] [def x] [operator =] [number 1]",
108 | " [keyword if] ([atom true]) {",
109 | " [keyword let] [def y] [operator =] [number 2]",
110 | " [keyword var] [def z] [operator =] [number 3]",
111 | " [variable console].[property log]([variable-2 x], [variable-2 y], [variable-2 z])",
112 | " }",
113 | " [variable console].[property log]([variable-2 x], [variable y], [variable-2 z])",
114 | " }",
115 | " [variable console].[property log]([variable x], [variable y], [variable-2 z])",
116 | "}")
117 |
118 | MT("quotedStringAddition",
119 | "[keyword let] [def f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];");
120 |
121 | MT("quotedFatArrow",
122 | "[keyword let] [def f] [operator =] [variable a] [operator +] [string '=>'] [operator +] [variable c];");
123 |
124 | MT("fatArrow",
125 | "[variable array].[property filter]([def a] [operator =>] [variable-2 a] [operator +] [number 1]);",
126 | "[variable a];", // No longer in scope
127 | "[keyword let] [def f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];",
128 | "[variable c];");
129 |
130 | MT("spread",
131 | "[keyword function] [def f]([def a], [meta ...][def b]) {",
132 | " [variable something]([variable-2 a], [meta ...][variable-2 b]);",
133 | "}");
134 |
135 | MT("quasi",
136 | "[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
137 |
138 | MT("quasi_no_function",
139 | "[variable x] [operator =] [string-2 `fofdlakj${][variable x] [operator +] [string-2 `foo`] [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
140 |
141 | MT("indent_statement",
142 | "[keyword var] [def x] [operator =] [number 10]",
143 | "[variable x] [operator +=] [variable y] [operator +]",
144 | " [atom Infinity]",
145 | "[keyword debugger];");
146 |
147 | MT("indent_if",
148 | "[keyword if] ([number 1])",
149 | " [keyword break];",
150 | "[keyword else] [keyword if] ([number 2])",
151 | " [keyword continue];",
152 | "[keyword else]",
153 | " [number 10];",
154 | "[keyword if] ([number 1]) {",
155 | " [keyword break];",
156 | "} [keyword else] [keyword if] ([number 2]) {",
157 | " [keyword continue];",
158 | "} [keyword else] {",
159 | " [number 10];",
160 | "}");
161 |
162 | MT("indent_for",
163 | "[keyword for] ([keyword var] [def i] [operator =] [number 0];",
164 | " [variable i] [operator <] [number 100];",
165 | " [variable i][operator ++])",
166 | " [variable doSomething]([variable i]);",
167 | "[keyword debugger];");
168 |
169 | MT("indent_c_style",
170 | "[keyword function] [def foo]()",
171 | "{",
172 | " [keyword debugger];",
173 | "}");
174 |
175 | MT("indent_else",
176 | "[keyword for] (;;)",
177 | " [keyword if] ([variable foo])",
178 | " [keyword if] ([variable bar])",
179 | " [number 1];",
180 | " [keyword else]",
181 | " [number 2];",
182 | " [keyword else]",
183 | " [number 3];");
184 |
185 | MT("indent_funarg",
186 | "[variable foo]([number 10000],",
187 | " [keyword function]([def a]) {",
188 | " [keyword debugger];",
189 | "};");
190 |
191 | MT("indent_below_if",
192 | "[keyword for] (;;)",
193 | " [keyword if] ([variable foo])",
194 | " [number 1];",
195 | "[number 2];");
196 |
197 | MT("indent_semicolonless_if",
198 | "[keyword function] [def foo]() {",
199 | " [keyword if] ([variable x])",
200 | " [variable foo]()",
201 | "}")
202 |
203 | MT("indent_semicolonless_if_with_statement",
204 | "[keyword function] [def foo]() {",
205 | " [keyword if] ([variable x])",
206 | " [variable foo]()",
207 | " [variable bar]()",
208 | "}")
209 |
210 | MT("multilinestring",
211 | "[keyword var] [def x] [operator =] [string 'foo\\]",
212 | "[string bar'];");
213 |
214 | MT("scary_regexp",
215 | "[string-2 /foo[[/]]bar/];");
216 |
217 | MT("indent_strange_array",
218 | "[keyword var] [def x] [operator =] [[",
219 | " [number 1],,",
220 | " [number 2],",
221 | "]];",
222 | "[number 10];");
223 |
224 | MT("param_default",
225 | "[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {",
226 | " [keyword return] [variable-2 x];",
227 | "}");
228 |
229 | MT("new_target",
230 | "[keyword function] [def F]([def target]) {",
231 | " [keyword if] ([variable-2 target] [operator &&] [keyword new].[keyword target].[property name]) {",
232 | " [keyword return] [keyword new]",
233 | " .[keyword target];",
234 | " }",
235 | "}");
236 |
237 | MT("async",
238 | "[keyword async] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }");
239 |
240 | MT("async_assignment",
241 | "[keyword const] [def foo] [operator =] [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; };");
242 |
243 | MT("async_object",
244 | "[keyword let] [def obj] [operator =] { [property async]: [atom false] };");
245 |
246 | // async be highlighet as keyword and foo as def, but it requires potentially expensive look-ahead. See #4173
247 | MT("async_object_function",
248 | "[keyword let] [def obj] [operator =] { [property async] [property foo]([def args]) { [keyword return] [atom true]; } };");
249 |
250 | MT("async_object_properties",
251 | "[keyword let] [def obj] [operator =] {",
252 | " [property prop1]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },",
253 | " [property prop2]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },",
254 | " [property prop3]: [keyword async] [keyword function] [def prop3]([def args]) { [keyword return] [atom true]; },",
255 | "};");
256 |
257 | MT("async_arrow",
258 | "[keyword const] [def foo] [operator =] [keyword async] ([def args]) [operator =>] { [keyword return] [atom true]; };");
259 |
260 | MT("async_jquery",
261 | "[variable $].[property ajax]({",
262 | " [property url]: [variable url],",
263 | " [property async]: [atom true],",
264 | " [property method]: [string 'GET']",
265 | "});");
266 |
267 | MT("async_variable",
268 | "[keyword const] [def async] [operator =] {[property a]: [number 1]};",
269 | "[keyword const] [def foo] [operator =] [string-2 `bar ${][variable async].[property a][string-2 }`];")
270 |
271 | MT("bigint", "[number 1n] [operator +] [number 0x1afn] [operator +] [number 0o064n] [operator +] [number 0b100n];")
272 |
273 | MT("async_comment",
274 | "[keyword async] [comment /**/] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }");
275 |
276 | MT("indent_switch",
277 | "[keyword switch] ([variable x]) {",
278 | " [keyword default]:",
279 | " [keyword return] [number 2]",
280 | "}")
281 |
282 | MT("regexp_corner_case",
283 | "[operator +]{} [operator /] [atom undefined];",
284 | "[[[meta ...][string-2 /\\//] ]];",
285 | "[keyword void] [string-2 /\\//];",
286 | "[keyword do] [string-2 /\\//]; [keyword while] ([number 0]);",
287 | "[keyword if] ([number 0]) {} [keyword else] [string-2 /\\//];",
288 | "[string-2 `${][variable async][operator ++][string-2 }//`];",
289 | "[string-2 `${]{} [operator /] [string-2 /\\//}`];")
290 |
291 | MT("return_eol",
292 | "[keyword return]",
293 | "{} [string-2 /5/]")
294 |
295 | var ts_mode = CodeMirror.getMode({indentUnit: 2}, "application/typescript")
296 | function TS(name) {
297 | test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1))
298 | }
299 |
300 | TS("typescript_extend_type",
301 | "[keyword class] [def Foo] [keyword extends] [type Some][operator <][type Type][operator >] {}")
302 |
303 | TS("typescript_arrow_type",
304 | "[keyword let] [def x]: ([variable arg]: [type Type]) [operator =>] [type ReturnType]")
305 |
306 | TS("typescript_class",
307 | "[keyword class] [def Foo] {",
308 | " [keyword public] [keyword static] [property main]() {}",
309 | " [keyword private] [property _foo]: [type string];",
310 | "}")
311 |
312 | TS("typescript_literal_types",
313 | "[keyword import] [keyword *] [keyword as] [def Sequelize] [keyword from] [string 'sequelize'];",
314 | "[keyword interface] [def MyAttributes] {",
315 | " [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];",
316 | " [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];",
317 | "}",
318 | "[keyword interface] [def MyInstance] [keyword extends] [type Sequelize].[type Instance] [operator <] [type MyAttributes] [operator >] {",
319 | " [property rawAttributes]: [type MyAttributes];",
320 | " [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];",
321 | " [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];",
322 | "}")
323 |
324 | TS("typescript_extend_operators",
325 | "[keyword export] [keyword interface] [def UserModel] [keyword extends]",
326 | " [type Sequelize].[type Model] [operator <] [type UserInstance], [type UserAttributes] [operator >] {",
327 | " [property findById]: (",
328 | " [variable userId]: [type number]",
329 | " ) [operator =>] [type Promise] [operator <] [type Array] [operator <] { [property id], [property name] } [operator >>];",
330 | " [property updateById]: (",
331 | " [variable userId]: [type number],",
332 | " [variable isActive]: [type boolean]",
333 | " ) [operator =>] [type Promise] [operator <] [type AccountHolderNotificationPreferenceInstance] [operator >];",
334 | " }")
335 |
336 | TS("typescript_interface_with_const",
337 | "[keyword const] [def hello]: {",
338 | " [property prop1][operator ?]: [type string];",
339 | " [property prop2][operator ?]: [type string];",
340 | "} [operator =] {};")
341 |
342 | TS("typescript_double_extend",
343 | "[keyword export] [keyword interface] [def UserAttributes] {",
344 | " [property id][operator ?]: [type number];",
345 | " [property createdAt][operator ?]: [type Date];",
346 | "}",
347 | "[keyword export] [keyword interface] [def UserInstance] [keyword extends] [type Sequelize].[type Instance][operator <][type UserAttributes][operator >], [type UserAttributes] {",
348 | " [property id]: [type number];",
349 | " [property createdAt]: [type Date];",
350 | "}");
351 |
352 | TS("typescript_index_signature",
353 | "[keyword interface] [def A] {",
354 | " [[ [variable prop]: [type string] ]]: [type any];",
355 | " [property prop1]: [type any];",
356 | "}");
357 |
358 | TS("typescript_generic_class",
359 | "[keyword class] [def Foo][operator <][type T][operator >] {",
360 | " [property bar]() {}",
361 | " [property foo](): [type Foo] {}",
362 | "}")
363 |
364 | TS("typescript_type_when_keyword",
365 | "[keyword export] [keyword type] [type AB] [operator =] [type A] [operator |] [type B];",
366 | "[keyword type] [type Flags] [operator =] {",
367 | " [property p1]: [type string];",
368 | " [property p2]: [type boolean];",
369 | "};")
370 |
371 | TS("typescript_type_when_not_keyword",
372 | "[keyword class] [def HasType] {",
373 | " [property type]: [type string];",
374 | " [property constructor]([def type]: [type string]) {",
375 | " [keyword this].[property type] [operator =] [variable-2 type];",
376 | " }",
377 | " [property setType]({ [def type] }: { [property type]: [type string]; }) {",
378 | " [keyword this].[property type] [operator =] [variable-2 type];",
379 | " }",
380 | "}")
381 |
382 | TS("typescript_function_generics",
383 | "[keyword function] [def a]() {}",
384 | "[keyword function] [def b][operator <][type IA] [keyword extends] [type object], [type IB] [keyword extends] [type object][operator >]() {}",
385 | "[keyword function] [def c]() {}")
386 |
387 | TS("typescript_complex_return_type",
388 | "[keyword function] [def A]() {",
389 | " [keyword return] [keyword this].[property property];",
390 | "}",
391 | "[keyword function] [def B](): [type Promise][operator <]{ [[ [variable key]: [type string] ]]: [type any] } [operator |] [atom null][operator >] {",
392 | " [keyword return] [keyword this].[property property];",
393 | "}")
394 |
395 | TS("typescript_complex_type_casting",
396 | "[keyword const] [def giftpay] [operator =] [variable config].[property get]([string 'giftpay']) [keyword as] { [[ [variable platformUuid]: [type string] ]]: { [property version]: [type number]; [property apiCode]: [type string]; } };")
397 |
398 | TS("typescript_keyof",
399 | "[keyword function] [def x][operator <][type T] [keyword extends] [keyword keyof] [type X][operator >]([def a]: [type T]) {",
400 | " [keyword return]")
401 |
402 | TS("typescript_new_typeargs",
403 | "[keyword let] [def x] [operator =] [keyword new] [variable Map][operator <][type string], [type Date][operator >]([string-2 `foo${][variable bar][string-2 }`])")
404 |
405 | TS("modifiers",
406 | "[keyword class] [def Foo] {",
407 | " [keyword public] [keyword abstract] [property bar]() {}",
408 | " [property constructor]([keyword readonly] [keyword private] [def x]) {}",
409 | "}")
410 |
411 | TS("arrow prop",
412 | "({[property a]: [def p] [operator =>] [variable-2 p]})")
413 |
414 | TS("generic in function call",
415 | "[keyword this].[property a][operator <][type Type][operator >]([variable foo]);",
416 | "[keyword this].[property a][operator <][variable Type][operator >][variable foo];")
417 |
418 | TS("type guard",
419 | "[keyword class] [def Appler] {",
420 | " [keyword static] [property assertApple]([def fruit]: [type Fruit]): [variable-2 fruit] [keyword is] [type Apple] {",
421 | " [keyword if] ([operator !]([variable-2 fruit] [keyword instanceof] [variable Apple]))",
422 | " [keyword throw] [keyword new] [variable Error]();",
423 | " }",
424 | "}")
425 |
426 | TS("type as variable",
427 | "[variable type] [operator =] [variable x] [keyword as] [type Bar];");
428 |
429 | TS("enum body",
430 | "[keyword export] [keyword const] [keyword enum] [def CodeInspectionResultType] {",
431 | " [def ERROR] [operator =] [string 'problem_type_error'],",
432 | " [def WARNING] [operator =] [string 'problem_type_warning'],",
433 | " [def META],",
434 | "}")
435 |
436 | TS("parenthesized type",
437 | "[keyword class] [def Foo] {",
438 | " [property x] [operator =] [keyword new] [variable A][operator <][type B], [type string][operator |](() [operator =>] [type void])[operator >]();",
439 | " [keyword private] [property bar]();",
440 | "}")
441 |
442 | TS("abstract class",
443 | "[keyword export] [keyword abstract] [keyword class] [def Foo] {}")
444 |
445 | var jsonld_mode = CodeMirror.getMode(
446 | {indentUnit: 2},
447 | {name: "javascript", jsonld: true}
448 | );
449 | function LD(name) {
450 | test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1));
451 | }
452 |
453 | LD("json_ld_keywords",
454 | '{',
455 | ' [meta "@context"]: {',
456 | ' [meta "@base"]: [string "http://example.com"],',
457 | ' [meta "@vocab"]: [string "http://xmlns.com/foaf/0.1/"],',
458 | ' [property "likesFlavor"]: {',
459 | ' [meta "@container"]: [meta "@list"]',
460 | ' [meta "@reverse"]: [string "@beFavoriteOf"]',
461 | ' },',
462 | ' [property "nick"]: { [meta "@container"]: [meta "@set"] },',
463 | ' [property "nick"]: { [meta "@container"]: [meta "@index"] }',
464 | ' },',
465 | ' [meta "@graph"]: [[ {',
466 | ' [meta "@id"]: [string "http://dbpedia.org/resource/John_Lennon"],',
467 | ' [property "name"]: [string "John Lennon"],',
468 | ' [property "modified"]: {',
469 | ' [meta "@value"]: [string "2010-05-29T14:17:39+02:00"],',
470 | ' [meta "@type"]: [string "http://www.w3.org/2001/XMLSchema#dateTime"]',
471 | ' }',
472 | ' } ]]',
473 | '}');
474 |
475 | LD("json_ld_fake",
476 | '{',
477 | ' [property "@fake"]: [string "@fake"],',
478 | ' [property "@contextual"]: [string "@identifier"],',
479 | ' [property "user@domain.com"]: [string "@graphical"],',
480 | ' [property "@ID"]: [string "@@ID"]',
481 | '}');
482 | })();
483 |
--------------------------------------------------------------------------------