JavaScript preprocessors can help make authoring JavaScript easier and more convenient. For instance, CoffeeScript can help prevent easy-to-make mistakes and offer a cleaner syntax and Babel can bring ECMAScript 6 features to browsers that only support ECMAScript 5.
Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
.container-fluid
h1.h3 Value preimage under an increasing function
markdown:
*f: ℕ x ℕ → ℕ* is strictly increasing in both arguments.
Given *n ∈ ℕ*, find its preimage under *f*, i.e. all *i*, *j* such that *f(i, j) = n*.
#visualization
svg.viz
overflow: visible
width: 100%
.form-horizontal .checkbox
margin-bottom: 20px
inspectNum = (label, value) ->
"#{value} #{if value == 1 then label else label + 's'}"
class Algorithm
constructor: (@n, @f) ->
@visited = []
@found = []
fOrig = @f
@f = (i, j) =>
@recordVisited i, j
fOrig i, j
t0 = new Date()
try
@find()
catch e
@error = e
@f = fOrig
t1 = new Date()
@t = t1 - t0
@key = "#{@constructor.name}-#{@n}"
recordFound: (i, j) ->
@found.push [i, j]
recordVisited: (i, j) ->
@visited.push [i, j]
name: -> @constructor.name.replace(/([a-z])([A-Z])/g, '$1 $2')
stats: ->
"#{@visited.length} steps, #{@found.length} results. #{@t.toLocaleString()}ms"
class DescendFromTop extends Algorithm
find: (i = 1, j = 1) ->
n = @n
return if i + j - 1 > n
v = @f(i, j)
if v == @n
@recordFound i, j
else if v < n
@find i + 1, j if i + 1 >= j
@find i, j + 1 if j + 1 > i
return
class DescendWithBinarySearch extends Algorithm
find: (i = 1, maxI = @n + 1, j = 1, maxJ = @n + 1) ->
{n, f} = @
return if i + j - 1 > n || i >= maxI || j >= maxJ || i < 1 || j < 1
left = [(maxI + i - 1) >> 1, j ]
right = [i , (maxJ + j - 1) >> 1]
for [midI, midJ] in [left, right]
midV = f(midI, midJ)
midP = [midI, midJ]
if midV == n
@recordFound midI, midJ
else
if midV < n
@find midP[0] + 1, maxI, j, maxJ
@find i, maxI, midP[1] + 1, maxJ
else # if midV > n
branches = _.compact [
([[midI - 1, midJ ], [i, midP[0], j, maxJ ]] if midI > 1)
([[midI , midJ - 1], [i, maxI , j, midP[1]]] if midJ > 1)
]
for [prev, nextArgs] in branches
pv = f prev[0], prev[1]
if pv == n
@recordFound prev[0], prev[1]
else if pv > n
@find nextArgs...
return
# Visualisation
R = React
D = R.DOM
RAD2DEG = 57.2957795
SQRT2 = Math.sqrt 2
segScale = (v, fromA, fromB, toA, toB) ->
rate = (toB - toA) / (fromB - fromA)
v1 = v * rate
v1 = toA + 1 / v1 if (toB < toA) ^ (fromB < fromA)
if v1 > (toMax = Math.max(toA, toB))
v1 = toMax
else if v1 < (toMin = Math.min(toA, toB))
v1 = toMin
v1
class GraphLayout
defaults =
height: 600
hideTextLevel: 16
constructor: (props) ->
props = _.extend {}, props, defaults
{finder, height} = props
@key = @constructor.name
@height = height
@width = width = Math.round SQRT2 * height
{visited} = finder
@levels = levels = if visited.length
[maxVi, maxVj] = _.max visited, ([i, j]) -> i + j
maxVi + maxVj - 1
else
0
@nodes = levels * (levels + 1) >> 1
@kx = width / levels
@ky = height / levels
@scale = 1 / levels
{@hideTextLevel} = props
getX: (i, j) ->
@kx * (@levels + i - j) / 2
getY: (i, j) ->
@ky * (i + j - 1.5)
isHideText: ->
@levels >= @hideTextLevel
stats: ->
[
inspectNum 'node' , @nodes
inspectNum 'level', @levels
].join(', ')
Graph = R.createClass
getDefaultProps: ->
graphAttr:
className : 'viz'
fill : '#fafafa'
stroke : '#616161'
textAnchor : 'middle'
dominantBaseline : 'middle'
nodeTextAttr:
fill : '#424242'
strokeWidth : 0
visitedProps:
fill: '#d0d9ff'
foundProps:
fill: '#a3e9a4'
renderNodes: ->
{layout, finder, visitedProps, foundProps} = @props
{width, height, levels} = layout
circleRadius = Math.round height / (levels * 2)
{f, visited, found} = finder
nodes =
for i in [1..levels]
for j in [1..levels - i + 1]
x = layout.getX(i, j)
y = layout.getY(i, j)
key = "node-#{i}-#{j}"
_.compact [
D.circle
cx : x
cy : y
r : circleRadius
key : "#{key}-c"
unless layout.isHideText()
textProps = _.extend {}, @props.nodeTextAttr,
x : x
y : y
dy : '0.3em'
key : "#{key}-t"
D.text textProps, f(i, j)
]
nodeAt = ([i, j]) -> nodes[i - 1][j - 1][0]
visited.forEach (pos) =>
_.extend nodeAt(pos).props, visitedProps
found.forEach (pos) =>
_.extend nodeAt(pos).props, foundProps
_.flatten nodes
render: ->
if @props.layout.nodes > 0
{layout} = @props
{width, height} = layout
svgAttr = _.extend {}, @props.graphAttr,
version : '1.1'
strokeWidth : (if layout.isHideText() then 0 else 1)
viewBox : "0 0 #{width} #{height}"
preserveAspectRatio: "xMidYMid meet"
style:
fontSize: "#{segScale(layout.scale, 0.15, 1, 1.14, 15)}em"
D.svg svgAttr, @renderNodes()
else
D.p className: 'text text-warning', 'No nodes visited'
GraphFinderPanel = R.createClass
render: ->
{finder, doVis, layoutProps} = @props
layout = new GraphLayout(_.extend {}, {finder}, layoutProps)
D.div className: 'panel panel-default text-center',
D.div className: 'panel-heading',
D.h4 className: 'panel-title', finder.name()
D.div className: 'panel-body',
if finder.error
D.p className: 'text text-danger', finder.error.toString()
else if doVis
Graph {finder, layout}
else
D.p className: 'text text-info', 'drawing disabled'
D.div className: 'panel-footer',
joinWith [
D.text key: 'l', layout.stats()
D.text key: 'f', finder.stats()
], -> D.br()
joinWith = (lines, sep) ->
[fst, rest...] = lines
_.reduce rest, ((list, text) -> list.push(sep(), text); list), [fst]
Visualisation = R.createClass
getInitialState: ->
n: 20
fStr: 'i + j + i * j + Math.round(Math.sin(i))'
doVis: true
getDefaultProps: ->
finders: [
DescendFromTop
DescendWithBinarySearch
]
render: ->
try
fn = eval("(function(i,j){return #{@state.fStr}})")
catch e
fn = null
{n, doVis} = @state
D.div null,
D.div className: 'form-horizontal',
D.div
className: 'form-group'
D.label
className: 'control-label col-sm-1 text-right'
htmlFor: 'fStr-input'
"f(i, j)"
D.div className: 'col-sm-11', D.input
id : 'fStr-input'
name : 'fStr'
type : 'text'
value : @state.fStr
onChange : @fStrChanged
className: 'form-control'
D.div
className: 'form-group'
D.label
className: 'control-label col-sm-1 text-right'
htmlFor : 'n-input'
"n"
D.div className: 'col-sm-11', D.input
id : 'n-input'
name : 'n'
type : 'number'
min : 1
value : n
onChange : @nChanged
className: 'form-control'
style : {width: '80px'}
D.div
className: 'checkbox col-sm-offset-1'
D.label
htmlFor: 'do-vis-input'
D.input
id : 'do-vis-input'
name : 'do-vis'
type : 'checkbox'
checked : doVis
onChange: @doVisChanged
' Draw'
if fn
D.div className: 'row',
@props.finders.map (finderClass) =>
finder = new finderClass n, fn
D.div className: 'col-sm-6', key: finder.key,
GraphFinderPanel {finder, doVis}
else
D.p className: 'alert alert-warning', 'Invalid f'
nChanged: (e) ->
update = {n: (+e.target.value) }
update.doVis = false if update.n > 200
@setState update
fStrChanged: (e) ->
@setState fStr: e.target.value
doVisChanged: (e) ->
@setState doVis: e.target.checked
R.renderComponent(
Visualisation()
document.getElementById('visualization')
)
Also see: Tab Triggers