├── 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 |
4 |

{{data.info}}

5 |
6 |
7 | 8 | 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 |

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 A1A2...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 < i1 < · · · < 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 | 32 | 33 |
28 | 30 | 31 |
34 | 35 | 36 | 42 | 43 |
38 | 40 | 41 |
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 | 74 | 75 |
70 | 72 | 73 |
76 | 77 | 78 | 84 | 85 |
80 | 82 | 83 |
86 |
87 |
89 | empty Array 90 |
91 |
92 |
93 |
94 | 95 |

96 | Output: 97 | 98 | 100 | 101 |

102 |
103 |
104 |
105 | 106 |

107 | {{useAuxiliaryTable ? 'Primary Table' : 'Table'}} 108 | 109 | {{mainTableGets}} {{mainTableGets === 1 ? 'read' : 'reads'}} 111 |   112 | {{mainTableSets}} {{mainTableSets === 1 ? 'write' : 'writes'}} 113 |   114 | 115 |

116 | 117 | 118 | 119 | 129 | 130 |
125 | 127 | 128 |
131 | 132 | 133 | 143 | 144 |
139 | 141 | 142 |
145 |
146 |
147 |
148 |
149 | 150 |

151 | Secondary Table 152 | 153 | {{auxiliaryTableGets}} {{auxiliaryTableGets === 1 ? 'read' : 'reads'}} 155 |   156 | {{auxiliaryTableSets}} {{auxiliaryTableSets === 1 ? 'write' : 'writes'}} 157 |   158 | 159 |

160 | 161 | 162 | 163 | 173 | 174 |
169 | 171 | 172 |
175 | 176 | 177 | 187 | 188 |
183 | 185 | 186 |
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 | 220 | 227 | 234 | 241 | 248 | 255 | 261 | 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 | --------------------------------------------------------------------------------