From fe3024ca0f41d5921ff6467089eee276b153edf7 Mon Sep 17 00:00:00 2001 From: Alexander Putilin Date: Thu, 6 May 2021 02:41:17 +0100 Subject: [PATCH 1/2] New demos --- package.json | 2 +- src/autogenerated/new_demos.html | 18 ++++++++++++++++ src/chapter1_simplified_hash.js | 30 +++++++++++++------------- src/index.js | 2 ++ src/mustache/new_demos.json | 3 +++ src/new_demos.js | 36 ++++++++++++++++++++++++++++++++ webpack.dev.js | 4 ++++ webpack.prod.js | 4 ++++ 8 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 src/autogenerated/new_demos.html create mode 100644 src/mustache/new_demos.json create mode 100644 src/new_demos.js diff --git a/package.json b/package.json index c13cfa4..6fb3679 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "test:pyunit": "npm run extractcode && ./unittest_python.sh", "test": "npm run jest && npm run test:pyunit && npm run test:pystress", "build:ssr": "./ssr-all.sh", - "update:html": "mkdir -p build && (for i in {chapter1,chapter2,chapter3,chapter4}; do mustache src/mustache/$i.json src/page.html.template > src/autogenerated/$i.html; done)", + "update:html": "mkdir -p build && (for i in {chapter1,chapter2,chapter3,chapter4,new_demos}; do mustache src/mustache/$i.json src/page.html.template > src/autogenerated/$i.html; done)", "start": "webpack-dev-server --config webpack.dev.js --host 0.0.0.0", "serve": "http-server -p 9090 dist/", "build": "npm run build:ssr && webpack --config webpack.prod.js", diff --git a/src/autogenerated/new_demos.html b/src/autogenerated/new_demos.html new file mode 100644 index 0000000..1241b14 --- /dev/null +++ b/src/autogenerated/new_demos.html @@ -0,0 +1,18 @@ + + + + Inside python dict — an explorable explanation + + + + + + + + +
+ + + diff --git a/src/chapter1_simplified_hash.js b/src/chapter1_simplified_hash.js index f282b60..064d0cb 100644 --- a/src/chapter1_simplified_hash.js +++ b/src/chapter1_simplified_hash.js @@ -172,7 +172,7 @@ class SimplifiedInsertAll extends BreakpointFunction { this._overwritten = []; } - run(_originalList, isBroken = false) { + run(_originalList, isBroken = false, overrideSize = null) { this.fmtIsBroken = isBroken; this.originalList = new ImmutableList(_originalList); this.newList = new ImmutableList(); @@ -180,7 +180,13 @@ class SimplifiedInsertAll extends BreakpointFunction { this.fmtMissingNumbers = new ImmutableList(); this.newListWithReplacements = new ImmutableList(); } - const startSize = (isBroken ? 1 : 2) * this.originalList.size; + let startSize; + + if (!overrideSize) { + startSize = (isBroken ? 1 : 2) * this.originalList.size; + } else { + overrideSize = startSize; + } for (let i = 0; i < startSize; ++i) { this.newList = this.newList.push(null); if (isBroken) { @@ -245,21 +251,15 @@ let formatSimplifiedInsertAllDescription = function(bp, prevBp) { bp.number }`; case 'compute-idx': - return `Compute the slot index: ${bp.newListIdx} == ${bp.number} % ${ - bp.newList.size - }`; + return `Compute the slot index: ${bp.newListIdx} == ${bp.number} % ${bp.newList.size}`; case 'check-collision': return chapter1_2_FormatCheckCollision(bp.newList, bp.newListIdx, bp.fmtCollisionCount); case 'next-idx': - return `Keep probing, the next slot will be ${bp.newListIdx} == (${ - prevBp.newListIdx - } + 1) % ${bp.newList.size}`; + return `Keep probing, the next slot will be ${bp.newListIdx} == (${prevBp.newListIdx} + 1) % ${bp.newList.size}`; case 'assign-elem': { const prevNumber = prevBp.newList.get(bp.newListIdx); if (prevNumber != null) { - return `Collision of ${bp.number} with ${prevNumber} in slot ${ - bp.newListIdx - } - the number is overwritten`; + return `Collision of ${bp.number} with ${prevNumber} in slot ${bp.newListIdx} - the number is overwritten`; } else { return `Put ${bp.number} in slot ${bp.newListIdx}`; } @@ -351,9 +351,7 @@ class SimplifiedSearch extends BreakpointFunction { let formatSimplifiedSearchDescription = function(bp) { switch (bp.point) { case 'compute-idx': - return `Compute the slot index: ${bp.newListIdx} == ${bp.number} % ${ - bp.newList.size - }`; + return `Compute the slot index: ${bp.newListIdx} == ${bp.number} % ${bp.newList.size}`; case 'check-not-found': return commonFormatCheckNotFound(bp.newList, bp.newListIdx, bp.fmtCollisionCount); case 'check-found': @@ -618,8 +616,8 @@ export class Chapter1_SimplifiedHash extends ChapterComponent {

Accessing a single element by index is very fast. Accessing only a few elements would be fast - too. We don't want to be doing a linear scan over the whole list every time we look up a number, so - we need to organize our data in a clever way. + too. We don't want to be doing a linear scan over the whole list every time we look up a number, + so we need to organize our data in a clever way.

Here's how.

diff --git a/src/index.js b/src/index.js index e45687e..28a902a 100644 --- a/src/index.js +++ b/src/index.js @@ -3,12 +3,14 @@ import {Chapter1_SimplifiedHash} from './chapter1_simplified_hash.js'; import {Chapter2_HashTableFunctions} from './chapter2_hash_table_functions.js'; import {Chapter3_HashClass} from './chapter3_hash_class.js'; import {Chapter4_RealPythonDict} from './chapter4_real_python_dict.js'; +import {NewDemos} from './new_demos.js'; export const CHAPTER_ID_TO_COMPONENT = { chapter1: Chapter1_SimplifiedHash, chapter2: Chapter2_HashTableFunctions, chapter3: Chapter3_HashClass, chapter4: Chapter4_RealPythonDict, + new_demos: NewDemos, }; if (typeof window !== 'undefined') { diff --git a/src/mustache/new_demos.json b/src/mustache/new_demos.json new file mode 100644 index 0000000..aee6c01 --- /dev/null +++ b/src/mustache/new_demos.json @@ -0,0 +1,3 @@ +{ + "chapters": "['new_demos']" +} diff --git a/src/new_demos.js b/src/new_demos.js new file mode 100644 index 0000000..830b0a6 --- /dev/null +++ b/src/new_demos.js @@ -0,0 +1,36 @@ +import * as React from 'react'; + +import {ChapterComponent, Subcontainerize, singularOrPlural, DynamicP, DebounceWhenOutOfView} from './util'; +import {Chapter1_SimplifiedHash} from './chapter1_simplified_hash.js'; + +export class NewDemos extends Chapter1_SimplifiedHash { + constructor() { + super(); + /*this.state = { + numbers: [ + BigNumber(1), + BigNumber(56), + BigNumber(50), + BigNumber(2), + BigNumber(44), + BigNumber(25), + BigNumber(17), + BigNumber(4), + ], + }*/ + } + + render() { + const siaRes10 = this.runSimplifiedInsertAll(this.state.numbers, false, this.state.number + 1); + + return ( +

+

Demos

+ +

Hello, world!

+

Hello, world!

+
+
+ ); + } +} diff --git a/webpack.dev.js b/webpack.dev.js index ca295d9..777008d 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -22,6 +22,10 @@ module.exports = merge(common, { template: 'src/autogenerated/chapter4.html', filename: 'chapter4.html', }), + new HtmlWebpackPlugin({ + template: 'src/autogenerated/new_demos.html', + filename: 'new_demos.html', + }), ], devServer: { contentBase: './dist', diff --git a/webpack.prod.js b/webpack.prod.js index b240c77..870d87a 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -28,6 +28,10 @@ module.exports = merge(common, { template: 'build/chapter4.html', filename: 'chapter4.html', }), + new HtmlWebpackPlugin({ + template: 'build/new_demos.html', + filename: 'new_demos.html', + }), new BundleAnalyzerPlugin(), ], }); From 962da301fe5b59ee21f22601106b66c4cc99882e Mon Sep 17 00:00:00 2001 From: Alexander Putilin Date: Thu, 6 May 2021 18:20:21 +0100 Subject: [PATCH 2/2] Show the last breakpoint --- src/code_blocks.js | 122 +++++++++++++++++++++++++++------------------ src/styles.css | 5 ++ 2 files changed, 79 insertions(+), 48 deletions(-) diff --git a/src/code_blocks.js b/src/code_blocks.js index 5e7e539..af3e851 100644 --- a/src/code_blocks.js +++ b/src/code_blocks.js @@ -1310,6 +1310,55 @@ export class Tetris extends React.PureComponent { } } +export function formatBp(bp, prevBp, formatBpDesc) { + let desc = null; + if (typeof formatBpDesc === 'function') { + desc = formatBpDesc(bp, prevBp); + } else { + for (const formatFunc of formatBpDesc) { + desc = formatFunc(bp, prevBp); + if (desc != null) break; + } + } + return desc; +} + +export function getVisibleBreakpoints(code, breakpoints, time) { + let visibleBreakpoints = {}; + let pointToLevel = {}; + for (let [line, bpPoint, level] of code) { + if (line === '' || bpPoint === '') { + continue; + } + if (level === undefined) { + pointToLevel = null; + break; + } + pointToLevel[bpPoint] = level; + } + + let prevBp = null; + for (let [t, bp] of breakpoints.entries()) { + if (t > time) { + break; + } + + if (bp.point in visibleBreakpoints) { + let level = pointToLevel[bp.point]; + for (let visibleBpPoint in visibleBreakpoints) { + if (pointToLevel[visibleBpPoint] >= level) { + delete visibleBreakpoints[visibleBpPoint]; + } + } + } + + visibleBreakpoints[bp.point] = {bp, prevBp}; + prevBp = bp; + } + + return visibleBreakpoints; +} + class CodeBlockWithActiveLineAndAnnotations extends React.Component { constructor() { super(); @@ -1330,7 +1379,6 @@ class CodeBlockWithActiveLineAndAnnotations extends React.Component { }); getCodeWithExplanationHtmlLines(visibleBreakpoints, activeBp) { - const t1 = performance.now(); const code = this.props.code; const hlLines = this._highlightLines(code); let lines = []; @@ -1345,15 +1393,7 @@ class CodeBlockWithActiveLineAndAnnotations extends React.Component { if (bpPoint in visibleBreakpoints) { const {bp, prevBp} = visibleBreakpoints[bpPoint]; - let desc = null; - if (typeof this.props.formatBpDesc === 'function') { - desc = this.props.formatBpDesc(bp, prevBp); - } else { - for (const formatBpDesc of this.props.formatBpDesc) { - desc = formatBpDesc(bp, prevBp); - if (desc != null) break; - } - } + const desc = formatBp(bp, prevBp, this.props.formatBpDesc); if (desc == null) { throw new Error('Unknown bp type: ' + bpPoint); @@ -1399,47 +1439,10 @@ class CodeBlockWithActiveLineAndAnnotations extends React.Component { return lines; } - getVisibleBreakpoints(activeBp) { - const t1 = performance.now(); - let visibleBreakpoints = {}; - let pointToLevel = {}; - for (let [line, bpPoint, level] of this.props.code) { - if (line === '' || bpPoint === '') { - continue; - } - if (level === undefined) { - pointToLevel = null; - break; - } - pointToLevel[bpPoint] = level; - } - - let prevBp = null; - for (let [time, bp] of this.props.breakpoints.entries()) { - if (time > this.props.time) { - break; - } - - if (bp.point in visibleBreakpoints) { - let level = pointToLevel[bp.point]; - for (let visibleBpPoint in visibleBreakpoints) { - if (pointToLevel[visibleBpPoint] >= level) { - delete visibleBreakpoints[visibleBpPoint]; - } - } - } - - visibleBreakpoints[bp.point] = {bp, prevBp}; - prevBp = bp; - } - - return visibleBreakpoints; - } - render() { let activeBp = this.props.breakpoints[this.props.time]; - const visibleBreakpoints = this.getVisibleBreakpoints(activeBp); + const visibleBreakpoints = getVisibleBreakpoints(this.props.code, this.props.breakpoints, this.props.time); const lines = this.getCodeWithExplanationHtmlLines(visibleBreakpoints, activeBp); return ( @@ -1792,6 +1795,18 @@ export class VisualizedCode extends React.Component { } } + getLastDesc() { + const currentBp = this.props.breakpoints[this.state.time]; + // Pretty hacky as an experiment + const visibleBreakpoints = getVisibleBreakpoints(this.props.code, this.props.breakpoints, this.state.time); + if (visibleBreakpoints.length === 0) { + return null; + } + console.log('VB', visibleBreakpoints, currentBp.point); + const {bp, prevBp} = visibleBreakpoints[currentBp.point]; + return formatBp(bp, prevBp, this.props.formatBpDesc); + } + render() { const windowWidth = this.props.windowWidth; const windowHeight = this.props.windowHeight; @@ -1801,6 +1816,9 @@ export class VisualizedCode extends React.Component { const smallerFont = tallScreen || serverSide; let bp = this.props.breakpoints[this.state.time]; + + const lastDesc = this.getLastDesc(); + const StateVisualization = this.props.stateVisualization; let codeHeight; @@ -1835,6 +1853,14 @@ export class VisualizedCode extends React.Component { maxTime={this.props.breakpoints.length - 1} autoplayByDefault={this.props.autoplayByDefault} /> +
+ +