tag:blogger.com,1999:blog-29942926468760773632024-02-25T03:18:53.425-05:00El rincon de exRandom curious things I waste my timeexhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.comBlogger31125tag:blogger.com,1999:blog-2994292646876077363.post-42758114918387099282021-05-04T19:30:00.003-05:002021-05-04T22:52:17.246-05:00Images missing again thanks to Google...Is 2021 and COVID is in the news and my country is in the brink of failure and this blog is mostly dead, but still... I would have prefered these posts of me to remain as a memoir of my past efforts and interests. So is sad to see the images of this blog being lost again. This time it seems it was thanks to <b>Google</b> Sites being deprecated, moved, who knows? (notice the remark here). Something like this happened before with <b>Google++</b> and the Picasa fiasco. If something, this just confirms that Google is a totally unprofessional company for hosting blogs, so sad that they bought Blogger for dying like this. Maybe I'll have to migrate now to Medium or something similar, but I'm still not feeling like writing like before: real life got really busy and serious here. I'll use the opportunity to wish you well and that things get better for us all. Again, sorry for the missing images, you can still look for some of them in the web archive, ie: <a href="https://web.archive.org/web/20200815103310/http://elrinconde-ex.blogspot.com/2012/11/dynamic-programming.html">dynamic programming</a>exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com1tag:blogger.com,1999:blog-2994292646876077363.post-35069459556700004932016-02-04T14:57:00.003-05:002021-03-12T15:12:41.925-05:00The zebra puzzleThe <a href="https://en.wikipedia.org/wiki/Zebra_Puzzle">Zebra Puzzle</a> is a well-known logic puzzle that can be fun to solve if you are bored.<br />
Try below your logic skills in the interactive version I made. Your objective is to get all the conditions true (green). To change the order of elements just click in the ones to swap consecutively.<br />
<br />
<div align="center" class="container">
<canvas id="canvasZebra"></canvas>
<img src="https://raw.githubusercontent.com/ex/js/gh-pages/src/zebra/images/zebra_sprites.png" height="0" id="zebra_sprites" onload="onImageLoad()" width="0" />
</div>
<script>
//------------------------------------------------------------------------------
ZebraPuzzle = function ( canvas, language ) {
var stringsEN = [
'The Englishman lives in the red house',
'The Spaniard owns a dog',
'The Japanese man is a painter',
'The Italian drinks tea',
'The Norwegian lives in the first house on the left',
'The green house is on the right of the white one',
'The photographer breeds snails',
'The diplomat lives in the yellow house',
'Milk is drunk in the middle house',
'The owner of the green house drinks coffee',
'The Norwegian\'s house is next to the blue one',
'The violinist drinks orange juice',
'The fox is in a house next to that of the physician',
'The horse is in a house next to the diplomat'
];
var stringsES = [
'El ingl\xE9s vive en la casa roja',
'El espa\xF1ol tiene un perro',
'El japon\xE9s es un pintor',
'El italiano toma t\xE9',
'El noruego vive en la primera casa a la izquierda',
'La casa verde est\xE1 a la derecha de la blanca',
'El fot\xF3grafo cria caracoles',
'El diplom\xE1tico vive en la casa amarilla',
'Toman leche en la casa del medio',
'El due\xF1o de la casa verde toma caf\xE9',
'La casa del noruego est\xE1 al lado de la casa azul',
'El violinista toma jugo de naranja',
'El zorro est\xE1 en la casa al lado del doctor',
'El caballo est\xE1 en la casa al lado del diplom\xE1tico'
];
// Initial state
var colors = ['Red', 'Yellow', 'Green', 'White', 'Blue'];
var nations = ['Spain', 'Japan', 'Italy', 'England', 'Norway'];
var jobs = ['Violinist', 'Physician', 'Photographer', 'Diplomat', 'Painter'];
var pets = ['Snail', 'Fox', 'Zebra', 'Horse', 'Dog'];
var drinks = ['Coffee', 'Milk', 'Water', 'Juice', 'Tea'];
this.m_imageIndexes = colors.concat( nations, jobs, pets, drinks );
// Current state
this.m_matrix = [colors, nations, jobs, pets, drinks];
this.m_sprites = document.getElementById( "zebra_sprites" );
if ( !canvas || !canvas.getContext || !this.m_sprites ) {
return;
}
this.m_canvas = canvas;
this.m_context = canvas.getContext( '2d' );
this.m_row = -1;
this.m_col = -1;
this.m_loaded = false;
// Register events
var myself = this;
function handlerClick( event ) {
var rect = canvas.getBoundingClientRect();
myself.onClick( event.clientX - rect.left, event.clientY - rect.top );
}
canvas.addEventListener( 'click', handlerClick, false );
// Set layout
this.m_width = canvas.width = 350;
canvas.height = 265 + 280;
this.m_context.fillStyle = 'red';
this.m_context.fillRect( 0, 0, canvas.width, canvas.height );
this.m_context.fillStyle = 'yellow';
this.m_context.font = '12px Verdana';
this.m_context.fillText( 'Loading...', 150, 260 );
var conditions = [];
var strings = ( language === 'es' ) ? stringsES : stringsEN;
// The Englishman lives in the red house
conditions.push( new PuzzleCondition( this.m_context, strings[0], 0, 350, function () {
return myself.m_matrix[1].indexOf( 'England' ) === myself.m_matrix[0].indexOf( 'Red' );
} ) );
// The Spaniard owns a dog
conditions.push( new PuzzleCondition( this.m_context, strings[1], 1, 350, function () {
return myself.m_matrix[1].indexOf( 'Spain' ) === myself.m_matrix[3].indexOf( 'Dog' );
} ) );
// The Japanese man is a painter
conditions.push( new PuzzleCondition( this.m_context, strings[2], 2, 350, function () {
return myself.m_matrix[1].indexOf( 'Japan' ) === myself.m_matrix[2].indexOf( 'Painter' );
} ) );
// The Italian drinks tea
conditions.push( new PuzzleCondition( this.m_context, strings[3], 3, 350, function () {
return myself.m_matrix[1].indexOf( 'Italy' ) === myself.m_matrix[4].indexOf( 'Tea' );
} ) );
// The Norwegian lives in the first house on the left
conditions.push( new PuzzleCondition( this.m_context, strings[4], 4, 350, function () {
return myself.m_matrix[1].indexOf( 'Norway' ) === 0;
} ) );
// The green house is on the right of the white one
conditions.push( new PuzzleCondition( this.m_context, strings[5], 5, 350, function () {
return myself.m_matrix[0].indexOf( 'Green' ) === myself.m_matrix[0].indexOf( 'White' ) + 1;
} ) );
// The photographer breeds snails
conditions.push( new PuzzleCondition( this.m_context, strings[6], 6, 350, function () {
return myself.m_matrix[2].indexOf( 'Photographer' ) === myself.m_matrix[3].indexOf( 'Snail' );
} ) );
// The diplomat lives in the yellow house
conditions.push( new PuzzleCondition( this.m_context, strings[7], 7, 350, function () {
return myself.m_matrix[2].indexOf( 'Diplomat' ) === myself.m_matrix[0].indexOf( 'Yellow' );
} ) );
// Milk is drunk in the middle house
conditions.push( new PuzzleCondition( this.m_context, strings[8], 8, 350, function () {
return myself.m_matrix[4].indexOf( 'Milk' ) === 2;
} ) );
// The owner of the green house drinks coffee
conditions.push( new PuzzleCondition( this.m_context, strings[9], 9, 350, function () {
return myself.m_matrix[0].indexOf( 'Green' ) === myself.m_matrix[4].indexOf( 'Coffee' )
} ) );
// The Norwegian’s house is next to the blue one
conditions.push( new PuzzleCondition( this.m_context, strings[10], 10, 350, function () {
return ( ( myself.m_matrix[1].indexOf( 'Norway' ) === myself.m_matrix[0].indexOf( 'Blue' ) + 1 )
|| ( myself.m_matrix[1].indexOf( 'Norway' ) === myself.m_matrix[0].indexOf( 'Blue' ) - 1 ) );
} ) );
// The violinist drinks orange juice
conditions.push( new PuzzleCondition( this.m_context, strings[11], 11, 350, function () {
return myself.m_matrix[2].indexOf( 'Violinist' ) === myself.m_matrix[4].indexOf( 'Juice' )
} ) );
// The fox is in a house next to that of the physician
conditions.push( new PuzzleCondition( this.m_context, strings[12], 12, 350, function () {
return ( ( myself.m_matrix[3].indexOf( 'Fox' ) === myself.m_matrix[2].indexOf( 'Physician' ) + 1 )
|| ( myself.m_matrix[3].indexOf( 'Fox' ) === myself.m_matrix[2].indexOf( 'Physician' ) - 1 ) );
} ) );
// The horse is in a house next to that of the diplomat
conditions.push( new PuzzleCondition( this.m_context, strings[13], 13, 350, function () {
return ( ( myself.m_matrix[3].indexOf( 'Horse' ) === myself.m_matrix[2].indexOf( 'Diplomat' ) + 1 )
|| ( myself.m_matrix[3].indexOf( 'Horse' ) === myself.m_matrix[2].indexOf( 'Diplomat' ) - 1 ) );
} ) );
this.m_conditions = conditions;
}
ZebraPuzzle.prototype.onLoaded = function () {
this.m_loaded = true;
this.render();
}
ZebraPuzzle.prototype.render = function () {
var x, y, i;
this.m_context.clearRect( 0, 0, this.m_canvas.width, this.m_canvas.height );
// Draw initial state
for ( x = 0; x < 5; x++ ) {
for ( y = 0; y < 5; y++ ) {
i = this.m_imageIndexes.indexOf( this.m_matrix[y][x] );
this.m_context.drawImage( this.m_sprites, 70 * i, 0, 70, 53, 70 * x, 53 * y, 70, 53 );
}
}
var solved = true;
for ( i = 0; i < this.m_conditions.length; i++ ) {
if ( !this.m_conditions[i].render() ) {
solved = false;
}
}
if ( solved ) {
alert( "Congrats!" );
}
};
ZebraPuzzle.prototype.onClick = function ( x, y ) {
if ( !this.m_loaded ) {
return;
}
var col = Math.floor( x / 70 );
var row = Math.floor( y / 53 );
if ( row === this.m_row && col === this.m_col ) {
return;
}
if ( row > 4 ) {
return;
}
if ( row !== this.m_row ) {
this.m_row = row;
this.m_col = col;
this.render();
this.m_context.strokeStyle = "#FF00FF";
this.m_context.lineWidth = 3;
this.m_context.beginPath();
this.m_context.rect( 70 * col, 53 * row, 70, 53 );
this.m_context.stroke();
this.m_context.closePath();
}
else {
var temp = this.m_matrix[row][col];
this.m_matrix[row][col] = this.m_matrix[row][this.m_col];
this.m_matrix[row][this.m_col] = temp;
this.m_row = -1;
this.m_col = -1;
this.render();
}
};
//------------------------------------------------------------------------------
PuzzleCondition = function ( context, label, index, width, evaluator ) {
this.m_context = context;
this.m_label = label;
this.m_y = 265 + 20 * index;
this.m_width = width;
this.m_evaluator = evaluator;
}
PuzzleCondition.prototype.render = function () {
var state = this.m_evaluator();
this.m_context.fillStyle = state ? 'green' : 'red';
this.m_context.fillRect( 0, this.m_y, this.m_width, 20 );
this.m_context.fillStyle = 'yellow';
this.m_context.font = '12px Verdana';
this.m_context.fillText(( state ? '[\u2714] ' : '[\u2718] ' ) + this.m_label, 2, this.m_y + 14 );
return state;
};
var canvasZebra = document.getElementById( "canvasZebra" );
var puzzleZebra = new ZebraPuzzle( canvasZebra );
function onImageLoad() {
puzzleZebra.onLoaded();
}
</script>
If you find yourself struggling or you want an elaborated explanation of how to solve it you can find it <a href="https://www.cs.duke.edu/courses/spring06/cps102/notes/zebra.pdf">here</a>.<br />
<br />
Of course this little problem is ripe to be solved with a little help of code, but the naive brute force implementation would take some time to finish; there are (5!)^5 possibilities after all, that is: 24883200000 possible arrangements that we'll need to check.<br />
<a href="http://ex.github.io/js/src/zebra/worker.html">Here you can find a brute force solution using Web workers</a>, just don't hold your breath. <br />
<br />
Some kind of optimizations would be needed to solve this fast. I was planning to post here my crappy constrained generator code until I found <a href="https://curiosity-driven.org/prolog-interpreter">this amazing post</a> of some guy that created a whole working online <a href="https://en.wikipedia.org/wiki/Prolog">Prolog</a> interpreter to solve this kind of problems. So you better continue there.<br />
<br />
Prolog is "the language" to solve these type of problems, here is my solution in case you are wondering:<br />
<br />
<script src="https://gist.github.com/ex/68d27c3acef502457073.js"></script>
Just ask for: <span style="font-family: "courier new" , "courier" , monospace;">"puzzle(Houses)"</span> in the <a href="https://curiosity-driven.org/prolog-interpreter">online interpreter</a>.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-13605818489866144072015-04-05T15:58:00.000-05:002016-03-25T15:16:04.936-05:00Little chess timer with Haxe and openflI needed a simple chess timer to exercise my rusty chess.<br />
So i did this simple app that you can use to time your games.<br />
<br />
<div style="text-align: center;">
<object data="https://sites.google.com/site/exeqtor/timer.swf" height="480" type="application/x-shockwave-flash" width="320"> <param value="https://sites.google.com/site/exeqtor/timer.swf" name="movie"></object>
</div>
<br />
The HTML5 version it's <a href="http://ex.github.io/js/src/timer/">here</a>.<br />
The APK for Android is on my <a href="https://www.dropbox.com/sh/mbrjle1vohsg6gl/AACQg7bNH7mledhJQy80pRRra?dl=0">dropbox</a>.<br />
For iOS you'll need to build the project, it requires Haxe and openfl.<br />
<br />
The app itself is very simple and only has being configured for 5, 10 and 15 minutes games, but you can change that in the <a href="https://github.com/ex/haxe/tree/master/timer">source code</a>. Happy games!exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-12893665637879689042014-09-21T19:01:00.001-05:002021-03-12T15:28:34.748-05:00Pascal's triangles in HTML5I did this animation <a href="http://elrinconde-ex.blogspot.com/2010/02/pascals-triangle.html">in Flash</a> long time ago, but I wanted to see it on my iPhone so I ported it to JavaScript using canvas and web audio.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
The animation is a <a href="http://en.wikipedia.org/wiki/Pascal%27s_triangle">Pascal triangle</a> reflected in the X and Y axis, where the empty cells are the numbers that are divisible by some random number (not prime) selected for every drawing pass, and the cells in colors are randomly colored accordingly to their residue by the same random number. The source code has all the <a href="http://github.com/ex/js/blob/master/src/Pascal/code.js">nitty-gritty details</a>. <br />
<br />
<div align="center">
<canvas height="501" id="canvas" width="501"> </canvas>
</div>
<script src="http://code.createjs.com/preloadjs-0.4.1.min.js"></script>
<script src="http://code.createjs.com/soundjs-0.5.2.min.js"></script>
<script>
Pascal = function ( canvasName, colors, lineColor, borderColor, backColor, textColor ) {
this.TIME_SHOW = 1000;
this.TIME_FADE = 100;
this.ALPHA_DECREASE = 0.011;
this.ST_DRAW = 0;
this.ST_DELETE_CENTER = 1;
this.ST_SHOW = 2;
this.ST_FADE = 3;
this.PRIMES = [
11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 97, 101, 103, 107, 109, 113,
128, 131, 137, 139, 149, 151, 157, 163, 167, 169, 173, 179, 181, 191, 193, 197, 211, 223, 227, 229,
233, 239, 241, 243, 251, 254, 256, 257, 263, 269, 271, 277, 281, 283, 289, 293, 307, 311
];
var canvas = document.getElementById( canvasName );
if ( !canvas || !canvas.getContext ) {
return;
}
this.m_context = canvas.getContext( '2d' );
this.m_state = this.ST_DRAW;
this.m_colors = colors;
this.m_lineColor = lineColor;
this.m_borderColor = borderColor;
this.m_backColor = backColor;
this.m_textColor = textColor;
this.m_halfWidth = 0;
this.m_halfHeight = 0;
this.m_cellSize = 0;
this.m_nextOrder = 0;
this.m_cells = 0;
this.m_array = null;
this.m_row = 0; // File to draw
this.m_color = 0; // Random index color
this.m_alphaBack = 1;
this.m_halfWidth = Math.floor( canvas.width / 2 )
this.m_halfHeight = Math.floor( canvas.height / 2 );
this.m_cellSize = 3;
this.m_cells = Math.floor( this.m_halfHeight / this.m_cellSize ); // number of cells
this.m_backCanvas = this.createCanvas( this.m_halfWidth, this.m_halfHeight );
this.m_frontCanvas = this.createCanvas( this.m_halfWidth, this.m_halfHeight );
this.m_borderCanvas = this.createCanvas( this.m_halfWidth, this.m_halfHeight );
this.m_frontContext = this.m_frontCanvas.getContext( '2d' );
this.m_boderContext = this.m_borderCanvas.getContext( '2d' );
this.m_backContext = this.m_backCanvas.getContext( '2d' );
this.m_context.fillStyle = this.m_backColor;
this.m_context.fillRect( 0, 0, 2 * this.m_halfWidth, 2 * this.m_halfHeight );
this.m_context.fillStyle = textColor;
this.m_context.font = '12px Verdana';
this.m_context.fillText( 'Loading...', this.m_halfWidth - 30, this.m_halfHeight - 5 );
}
Pascal.prototype.setReady = function setReady() {
this.m_context.fillStyle = this.m_backColor;
this.m_context.fillRect( 0, 0, 2 * this.m_halfWidth, 2 * this.m_halfHeight );
this.m_context.fillStyle = this.m_textColor;
this.m_context.font = '12px Verdana';
this.m_context.fillText( 'Code: Laurens Rodriguez', this.m_halfWidth - 80, this.m_halfHeight - 20 );
this.m_context.fillText( 'Music: Micky Gonzalez', this.m_halfWidth - 80, this.m_halfHeight - 5 );
this.m_context.fillText( "Click to play", this.m_halfWidth - 80, this.m_halfHeight + 10 );
};
Pascal.prototype.start = function start() {
this.m_nextOrder = this.getNextOrder();
this.makeTriangle();
setInterval( this.update.bind( this ), 1000 / 35 );
};
Pascal.prototype.getNextOrder = function getNextOrder() {
var num = 2 + Math.floor( Math.random() * this.m_cells );
while ( ( num > 2 ) && ( ( num == this.m_nextOrder ) || ( this.binaryFind( num, this.PRIMES ) >= 0 ) ) ) {
num--;
}
return num;
};
Pascal.prototype.makeTriangle = function makeTriangle() {
if ( this.m_array != null ) {
this.m_array = null;
}
this.m_array = new Array();
this.m_array[0] = new Array();
this.m_array[0].push( 1 );
for ( var p = 1; p < this.m_cells; p++ ) {
this.m_array[p] = new Array();
this.m_array[p].push( 1 );
for ( var q = 0; q < this.m_array[p - 1].length - 1; q++ ) {
this.m_array[p].push(( this.m_array[p - 1][q] + this.m_array[p - 1][q + 1] ) % this.m_nextOrder );
}
this.m_array[p].push( 1 );
}
for ( p = this.m_cells; p < 2 * this.m_cells - 1; p++ ) {
this.m_array[p] = new Array();
for ( q = 0; q < this.m_array[p - 1].length - 1; q++ ) {
this.m_array[p].push(( this.m_array[p - 1][q] + this.m_array[p - 1][q + 1] ) % this.m_nextOrder );
}
}
};
Pascal.prototype.drawBorderSquare = function drawBorderSquare( x, y, size ) {
this.m_boderContext.fillStyle = this.m_lineColor;
this.m_boderContext.fillRect( x + 1, y + 1, size, size );
};
Pascal.prototype.drawBox = function drawBox( x, y, size, colorIndex, offsetColor ) {
this.m_frontContext.strokeStyle = this.m_borderColor;
this.m_frontContext.strokeRect( x + 0.5, y + 0.5, size, size );
this.m_frontContext.fillStyle = '#' + this.m_colors[( colorIndex + offsetColor ) % this.m_colors.length];
this.m_frontContext.fillRect( x + 1, y + 1, size - 1, size - 1 );
};
Pascal.prototype.onFade = function onFade() {
this.m_state = this.ST_FADE;
setTimeout( this.onRedraw.bind( this ), this.TIME_FADE );
};
Pascal.prototype.onRedraw = function onRedraw() {
this.m_nextOrder = this.getNextOrder();
this.makeTriangle();
this.m_row = 0;
this.m_color = Math.floor( Math.random() * this.m_array.length );
this.m_state = this.ST_DRAW;
};
Pascal.prototype.update = function update() {
switch ( this.m_state ) {
case this.ST_DRAW:
if ( this.m_row >= this.m_array.length ) {
this.m_alphaBack = 1;
this.m_backContext.clearRect( 0, 0, this.m_halfWidth, this.m_halfHeight );
// Swap front and back canvas.
var temp = this.m_frontCanvas;
this.m_frontCanvas = this.m_backCanvas;
this.m_backCanvas = temp;
temp = this.m_frontContext;
this.m_frontContext = this.m_backContext;
this.m_backContext = temp;
this.m_state = this.ST_DELETE_CENTER;
setTimeout( this.onFade.bind( this ), this.TIME_SHOW );
}
else {
this.redrawBmps();
this.m_row++;
if ( ( this.m_row % 2 ) && ( this.m_alphaBack > 0 ) ) {
this.m_alphaBack -= this.ALPHA_DECREASE;
}
}
break;
case this.ST_DELETE_CENTER:
this.m_boderContext.clearRect( 0, 0, this.m_halfWidth, this.m_halfHeight );
this.m_state = this.ST_SHOW;
break;
case this.ST_FADE:
this.m_alphaBack -= this.ALPHA_DECREASE;
break;
case this.ST_SHOW:
return;
}
this.m_context.fillStyle = this.m_backColor;
this.m_context.fillRect( 0, 0, 2 * this.m_halfWidth, 2 * this.m_halfHeight );
this.m_context.save();
for ( var k = 0; k < 4; k++ ) {
this.m_context.setTransform( k < 2 ? -1 : 1, 0, 0, k % 2 ? -1 : 1, this.m_halfWidth, this.m_halfHeight );
this.m_context.globalAlpha = this.m_alphaBack;
this.m_context.drawImage( this.m_backCanvas, 0, 0 );
this.m_context.globalAlpha = 1;
this.m_context.drawImage( this.m_frontCanvas, 0, 0 );
this.m_context.drawImage( this.m_borderCanvas, 0, 0 );
}
this.m_context.restore();
};
Pascal.prototype.redrawBmps = function redrawBmps() {
var p = this.m_row;
this.m_boderContext.clearRect( 0, 0, this.m_halfWidth, this.m_halfHeight );
if ( p < this.m_cells ) {
for ( var q = 0; q < this.m_array[p].length; q++ ) {
if ( this.m_array[p][q] ) {
this.drawBorderSquare( q * this.m_cellSize,
( this.m_array[p].length - q - 1 ) * this.m_cellSize, this.m_cellSize );
this.drawBox( q * this.m_cellSize, ( this.m_array[p].length - q - 1 ) * this.m_cellSize,
this.m_cellSize, this.m_array[p][q], this.m_color );
}
}
}
else {
for ( q = 0; q < this.m_array[p].length; q++ ) {
if ( this.m_array[p][q] ) {
this.drawBorderSquare( ( p + q - this.m_cells + 1 ) * this.m_cellSize,
( this.m_cells - q - 1 ) * this.m_cellSize, this.m_cellSize );
this.drawBox(( p + q - this.m_cells + 1 ) * this.m_cellSize, ( this.m_cells - q - 1 ) * this.m_cellSize,
this.m_cellSize, this.m_array[p][q], this.m_color );
}
}
}
};
Pascal.prototype.createCanvas = function createCanvas( width, height ) {
var canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
return canvas;
};
Pascal.prototype.binaryFind = function binaryFind( value, array ) {
var a = 0;
var b = array.length - 1;
var c;
while ( a <= b ) {
c = a + Math.floor(( b - a ) / 2 );
if ( array[c] == value ) {
return c;
}
else {
if ( array[c] > value ) {
b = c - 1;
} else {
a = c + 1;
}
}
}
return -1;
};
var COLORS = [
'FF00FF', 'FF54CE', 'F574F2', 'CF7FF2', 'FA416B',
'FF5D83', 'F36698', 'D780B1', 'B987CD', 'A8A9D5',
'98B2FC', '00ECFF', 'FFBFF2', 'F4A2FF', 'FFA2E7',
'FFA2C4', 'FFA2A4'
];
var pascal = new Pascal( 'canvas', COLORS, '#FFFFFF', 'rgba(255,255,0,0.35)', '#EB3905', '#FFFF00' );
var canvas = document.getElementById( "canvas" );
function init() {
// Check that we can play audio
if ( !createjs.Sound.initializeDefaultPlugins() ) {
return;
}
var audioPath = '';
var manifest = [
{ id: 'Music', src: 'music.mp3' },
];
var queue = new createjs.LoadQueue( false, 'http://sites.google.com/site/exeqtor/', true );
createjs.LoadQueue.loadTimeout = 12000;
createjs.Sound.alternateExtensions = ["ogg"];
queue.installPlugin( createjs.Sound );
queue.addEventListener( 'fileload', onSoundLoaded );
queue.addEventListener( 'error', onErrorLoading );
queue.loadManifest( manifest );
}
function onErrorLoading( event ) {
pascal.start();
canvas.removeEventListener( 'click', onClick, false );
}
function onSoundLoaded( event ) {
pascal.setReady();
canvas.addEventListener( 'click', onClick, false );
}
function onClick( event ) {
pascal.start();
createjs.Sound.play( 'Music', { loop: -1, volume: 0.75 } );
canvas.removeEventListener( 'click', onClick, false );
}
//init();
pascal.start();
</script>
<br />
The standalone version with music can be found <a href="http://ex.github.io/js/src/Pascal/">here</a>.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-21015328752005901552014-08-24T16:14:00.002-05:002021-04-05T18:08:50.123-05:00Mathematical artOnce upon a time, I considered myself a decent painter. I remember getting second place in some magazine for some Christmas teenagers contest and spending a whole summer painting furiously at my home in the highlands. But, truth be said, I'm awful compared with the professional painters and artists I have found working as a game developer. Most of my art prodigies are forever lost now, maybe for good, but I remember painting something like this when I was entranced on my <a href="http://en.wikipedia.org/wiki/Abstract_expressionism">abstract expressionism</a> period: <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="https://raw.githubusercontent.com/ex/js/gh-pages/src/DrawTweet/paint.png" /></div>
<a name='more'></a><br />
But the image above was not painted by me, it's generated from some short equations. This and a lot of interesting images can be found in this amazing post about <a href="http://codegolf.stackexchange.com/questions/35569/tweetable-mathematical-art">Tweetable Mathematical Art.</a> I translated some of the equations to JavaScript and put a simple page where you can run the demos and try your own:<br />
<br />
<script>
var seed = 1;
function generate() {
return seed = ( seed * 16807 ) % 2147483647;
}
function random() {
return generate() / 2147483647;
}
function Array2D( x, y ) {
var array2D = [];
for ( var i = 0; i < x; i++ ) {
array2D[i] = [];
}
return array2D;
}
function rand( n ) {
return Math.floor( Math.random() * n );
}
// Add entries here:
var nameFunctions = [
'Demo',
'Gradient',
'Horizontals',
'Table cloths',
'Random painter',
'Mandelbrot I',
'Mandelbrot II',
'Sierpinksy'
];
var mathFunctions = [
[
function ( i, j ) {
var q = ( i && j ) ? ( i % j ) & ( j % i ) : 0;
return q > 0 ? 256 - q / 4 : 0;
},
function ( i, j ) {
var q = ( i && j ) ? ( i % j ) + ( j % i ) : 0;
return q > 0 ? 256 - q / 4 : 0;
},
function ( i, j ) {
var q = ( i && j ) ? ( i % j ) | ( j % i ) : 0;
return q > 0 ? 256 - q / 4 : 0;
}
],
[
function ( i, j ) {
var t = Math.cos( Math.atan2( j - 512, i - 512 ) / 2 );
return t * t * 255;
},
function ( i, j ) {
var t = Math.cos( Math.atan2( j - 512, i - 512 ) / 2 - 2 * Math.acos( -1 ) / 3 );
return t * t * 255;
},
function ( i, j ) {
var t = Math.cos( Math.atan2( j - 512, i - 512 ) / 2 + 2 * Math.acos( -1 ) / 3 );
return t * t * 255;
}
],
[
function ( i, j ) {
if ( window['dataRed'] === null ) { window['dataRed'] = 0; }
window['dataRed'] += 2 * Math.random();
var l = Math.floor( window['dataRed'] ); l %= 512;
return l > 255 ? 511 - l : l;
},
function ( i, j ) {
if ( window['dataGreen'] === null ) { window['dataGreen'] = 0; }
window['dataGreen'] += 2 * Math.random();
var l = Math.floor( window['dataGreen'] ); l %= 512;
return l > 255 ? 511 - l : l;
},
function ( i, j ) {
if ( window['dataBlue'] === null ) { window['dataBlue'] = 0; }
window['dataBlue'] += 2 * Math.random();
var l = Math.floor( window['dataBlue'] ); l %= 512;
return l > 255 ? 511 - l : l;
}
],
[
function ( i, j ) {
var s = 3 / ( j + 200 );
var y = ( j + Math.sin(( i * i + ( j - 700 ) * ( j - 700 ) * 5 ) / 100 / 1024 ) * 35 ) * s;
return ( Math.floor(( i + 1024 ) * s + y ) % 2 + Math.floor(( 1024 * 2 - i ) * s + y ) % 2 ) * 127;
},
function ( i, j ) {
var s = 3 / ( j + 200 );
var y = ( j + Math.sin(( i * i + ( j - 700 ) * ( j - 700 ) * 5 ) / 100 / 1024 ) * 35 ) * s;
return ( Math.floor( 5 * ( ( i + 1024 ) * s + y ) ) % 2 + Math.floor( 5 * ( ( 1024 * 2 - i ) * s + y ) ) % 2 ) * 127;
},
function ( i, j ) {
var s = 3 / ( j + 200 );
var y = ( j + Math.sin(( i * i + ( j - 700 ) * ( j - 700 ) * 5 ) / 100 / 1024 ) * 35 ) * s;
return ( Math.floor( 29 * ( ( i + 1024 ) * s + y ) ) % 2 + Math.floor( 29 * ( ( 1024 * 2 - i ) * s + y ) ) % 2 ) * 127;
}
],
[
function ( i, j ) {
if ( window['dataRed'] === null ) { window['dataRed'] = new Array2D( 512, 512 ); }
var c = window['dataRed'], x = Math.floor( i / 2 ), y = Math.floor( j / 2 );
return !c[x][y] ? c[x][y] = !rand( 999 ) ? rand( 256 ) : red(( i + rand( 2 ) ) % 1024, ( j + rand( 2 ) ) % 1024 ) : c[x][y];
},
function ( i, j ) {
if ( window['dataGreen'] === null ) { window['dataGreen'] = new Array2D( 512, 512 ); }
var c = window['dataGreen'], x = Math.floor( i / 2 ), y = Math.floor( j / 2 );
return !c[x][y] ? c[x][y] = !rand( 999 ) ? rand( 256 ) : green(( i + rand( 2 ) ) % 1024, ( j + rand( 2 ) ) % 1024 ) : c[x][y];
},
function ( i, j ) {
if ( window['dataBlue'] === null ) { window['dataBlue'] = new Array2D( 512, 512 ); }
var c = window['dataBlue'], x = Math.floor( i / 2 ), y = Math.floor( j / 2 );
return !c[x][y] ? c[x][y] = !rand( 999 ) ? rand( 256 ) : blue(( i + rand( 2 ) ) % 1024, ( j + rand( 2 ) ) % 1024 ) : c[x][y];
}
],
[
function ( i, j ) {
var a = 0, b = 0, c, d, n = 0;
while ( ( c = a * a ) + ( d = b * b ) < 4 && n++ < 2048 ) {
b = 2 * a * b + j / 5e4 + .06, a = c - d + i / 5e4 + 0.34;
}
window['dataRed'] = Math.floor( n / 4 );
return window['dataRed'];
},
function ( i, j ) {
return 2 * window['dataRed'];
},
function ( i, j ) {
return 4 * window['dataRed'];
}
],
[
function ( i, j ) {
var a = 0, b = 0, c, d, n = 0;
while ( ( c = a * a ) + ( d = b * b ) < 4 && n++ < 880 ) {
b = 2 * a * b + j * 8e-9 - 0.645411; a = c - d + i * 8e-9 + 0.356888;
}
window['dataRed'] = n;
return 255 * Math.pow( ( n - 80 ) / 800, 3.0 );
},
function ( i, j ) {
return 255 * Math.pow( ( window['dataRed'] - 80 ) / 800, 0.7 );
},
function ( i, j ) {
return 255 * Math.pow( ( window['dataRed'] - 80 ) / 800, 0.5 );
}
],
[
function ( i, j ) {
return Math.cos( i & j ) << 7;
},
function ( i, j ) {
return red( 1024 - i, 1024 - j );
},
function ( i, j ) {
return Math.tan( i ^ j ) << 4;
}
],
[
function ( i, j ) {
return 255;
},
function ( i, j ) {
return 255;
},
function ( i, j ) {
return 255;
}
]
];
var e,m=this;function r(){}function t(a){a.B=function(){return a.uc?a.uc:a.uc=new a}}
function aa(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function u(a){return"array"==aa(a)}function ba(a){var b=aa(a);return"array"==b||"object"==b&&"number"==typeof a.length}function v(a){return"string"==typeof a}function w(a){return"number"==typeof a}function ca(a){return"function"==aa(a)}function da(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function ea(a){return a[fa]||(a[fa]=++ga)}var fa="closure_uid_"+(1E9*Math.random()>>>0),ga=0;
function ha(a,b,c){return a.call.apply(a.bind,arguments)}function ia(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function ja(a,b,c){ja=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?ha:ia;return ja.apply(null,arguments)}
function ka(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}}var la=Date.now||function(){return+new Date};function x(a,b){function c(){}c.prototype=b.prototype;a.b=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.fd=function(a,c,f){return b.prototype[c].apply(a,Array.prototype.slice.call(arguments,2))}};var ma;function na(a){return a.replace(/[\t\r\n ]+/g," ").replace(/^[\t\r\n ]+|[\t\r\n ]+$/g,"")}function qa(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}function ra(a){if(!sa.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(ta,"&"));-1!=a.indexOf("<")&&(a=a.replace(ua,"<"));-1!=a.indexOf(">")&&(a=a.replace(va,">"));-1!=a.indexOf('"')&&(a=a.replace(wa,"""));-1!=a.indexOf("'")&&(a=a.replace(xa,"'"));-1!=a.indexOf("\x00")&&(a=a.replace(ya,"�"));return a}
var ta=/&/g,ua=/</g,va=/>/g,wa=/"/g,xa=/'/g,ya=/\x00/g,sa=/[\x00&<>"']/;function za(a,b){return a<b?-1:a>b?1:0}function Aa(a){return String(a).replace(/\-([a-z])/g,function(a,c){return c.toUpperCase()})}function Ba(a){var b=v(void 0)?"undefined".replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08"):"\\s";return a.replace(new RegExp("(^"+(b?"|["+b+"]+":"")+")([a-z])","g"),function(a,b,g){return b+g.toUpperCase()})};var y=Array.prototype,Ca=y.indexOf?function(a,b,c){return y.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(v(a))return v(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},A=y.forEach?function(a,b,c){y.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,g=v(a)?a.split(""):a,f=0;f<d;f++)f in g&&b.call(c,g[f],f,a)},Da=y.filter?function(a,b,c){return y.filter.call(a,b,c)}:function(a,b,c){for(var d=a.length,g=[],f=0,h=v(a)?
a.split(""):a,k=0;k<d;k++)if(k in h){var l=h[k];b.call(c,l,k,a)&&(g[f++]=l)}return g},Ea=y.map?function(a,b,c){return y.map.call(a,b,c)}:function(a,b,c){for(var d=a.length,g=Array(d),f=v(a)?a.split(""):a,h=0;h<d;h++)h in f&&(g[h]=b.call(c,f[h],h,a));return g},Fa=y.every?function(a,b,c){return y.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,g=v(a)?a.split(""):a,f=0;f<d;f++)if(f in g&&!b.call(c,g[f],f,a))return!1;return!0};
function Ga(a,b){var c=0;A(a,function(a,g,f){b.call(void 0,a,g,f)&&++c},void 0);return c}function Ha(a,b){return 0<=Ca(a,b)}function Ia(a,b){var c=Ca(a,b),d;(d=0<=c)&&y.splice.call(a,c,1);return d}function Ja(a){var b=a.length;if(0<b){for(var c=Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]}
function Ka(a,b){for(var c=1;c<arguments.length;c++){var d=arguments[c],g;if(u(d)||(g=ba(d))&&Object.prototype.hasOwnProperty.call(d,"callee"))a.push.apply(a,d);else if(g)for(var f=a.length,h=d.length,k=0;k<h;k++)a[f+k]=d[k];else a.push(d)}}function La(a,b,c,d){y.splice.apply(a,Ma(arguments,1))}function Ma(a,b,c){return 2>=arguments.length?y.slice.call(a,b):y.slice.call(a,b,c)};function Na(a){Na[" "](a);return a}Na[" "]=r;function Oa(){0!=Pa&&(Qa[ea(this)]=this);this.Ea=this.Ea;this.yb=this.yb}var Pa=0,Qa={};Oa.prototype.Ea=!1;Oa.prototype.S=function(){if(!this.Ea&&(this.Ea=!0,this.l(),0!=Pa)){var a=ea(this);delete Qa[a]}};Oa.prototype.l=function(){if(this.yb)for(;this.yb.length;)this.yb.shift()()};function B(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.Aa=!1;this.xc=!0}B.prototype.l=function(){};B.prototype.S=function(){};B.prototype.stopPropagation=function(){this.Aa=!0};B.prototype.preventDefault=function(){this.defaultPrevented=!0;this.xc=!1};var C;a:{var Ra=m.navigator;if(Ra){var Sa=Ra.userAgent;if(Sa){C=Sa;break a}}C=""};function Ta(a,b){for(var c in a)b.call(void 0,a[c],c,a)}function Ua(a,b,c){if(b in a)throw Error('The object already contains the key "'+b+'"');a[b]=c}var Va="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" ");function Wa(a,b){for(var c,d,g=1;g<arguments.length;g++){d=arguments[g];for(c in d)a[c]=d[c];for(var f=0;f<Va.length;f++)c=Va[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};var D;function Xa(){return m.navigator||null}var Ya=-1!=C.indexOf("Opera")||-1!=C.indexOf("OPR"),F=-1!=C.indexOf("Trident")||-1!=C.indexOf("MSIE"),H=-1!=C.indexOf("Gecko")&&-1==C.toLowerCase().indexOf("webkit")&&!(-1!=C.indexOf("Trident")||-1!=C.indexOf("MSIE")),I=-1!=C.toLowerCase().indexOf("webkit"),Za=Xa();D=-1!=(Za&&Za.platform||"").indexOf("Mac");var $a=!!Xa()&&-1!=(Xa().appVersion||"").indexOf("X11");function ab(){var a=m.document;return a?a.documentMode:void 0}
var bb=function(){var a="",b;if(Ya&&m.opera)return a=m.opera.version,ca(a)?a():a;H?b=/rv\:([^\);]+)(\)|;)/:F?b=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:I&&(b=/WebKit\/(\S+)/);b&&(a=(a=b.exec(C))?a[1]:"");return F&&(b=ab(),b>parseFloat(a))?String(b):a}(),cb={};
function J(a){var b;if(!(b=cb[a])){b=0;for(var c=qa(String(bb)).split("."),d=qa(String(a)).split("."),g=Math.max(c.length,d.length),f=0;0==b&&f<g;f++){var h=c[f]||"",k=d[f]||"",l=RegExp("(\\d*)(\\D*)","g"),n=RegExp("(\\d*)(\\D*)","g");do{var p=l.exec(h)||["","",""],s=n.exec(k)||["","",""];if(0==p[0].length&&0==s[0].length)break;b=za(0==p[1].length?0:parseInt(p[1],10),0==s[1].length?0:parseInt(s[1],10))||za(0==p[2].length,0==s[2].length)||za(p[2],s[2])}while(0==b)}b=cb[a]=0<=b}return b}
function db(a){return F&&eb>=a}var fb=m.document,eb=fb&&F?ab()||("CSS1Compat"==fb.compatMode?parseInt(bb,10):5):void 0;var gb=!F||db(9),hb=!F||db(9),ib=F&&!J("9");!I||J("528");H&&J("1.9b")||F&&J("8")||Ya&&J("9.5")||I&&J("528");H&&!J("8")||F&&J("9");function jb(a,b){B.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.state=null;this.bc=!1;this.ha=null;if(a){var c=this.type=a.type;this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(H){var g;a:{try{Na(d.nodeName);g=!0;break a}catch(f){}g=!1}g||(d=null)}}else"mouseover"==
c?d=a.fromElement:"mouseout"==c&&(d=a.toElement);this.relatedTarget=d;this.offsetX=I||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=I||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=
a.metaKey;this.bc=D?a.metaKey:a.ctrlKey;this.state=a.state;this.ha=a;a.defaultPrevented&&this.preventDefault()}}x(jb,B);var kb=[1,4,2];function lb(a){return gb?0==a.ha.button:"click"==a.type?!0:!!(a.ha.button&kb[0])}jb.prototype.stopPropagation=function(){jb.b.stopPropagation.call(this);this.ha.stopPropagation?this.ha.stopPropagation():this.ha.cancelBubble=!0};
jb.prototype.preventDefault=function(){jb.b.preventDefault.call(this);var a=this.ha;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,ib)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};jb.prototype.l=function(){};var mb="closure_listenable_"+(1E6*Math.random()|0),nb=0;function ob(a,b,c,d,g){this.wa=a;this.Ab=null;this.src=b;this.type=c;this.gb=!!d;this.pb=g;this.key=++nb;this.La=this.fb=!1}function pb(a){a.La=!0;a.wa=null;a.Ab=null;a.src=null;a.pb=null};function qb(a){this.src=a;this.D={};this.cb=0}qb.prototype.add=function(a,b,c,d,g){var f=a.toString();a=this.D[f];a||(a=this.D[f]=[],this.cb++);var h=rb(a,b,d,g);-1<h?(b=a[h],c||(b.fb=!1)):(b=new ob(b,this.src,f,!!d,g),b.fb=c,a.push(b));return b};qb.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.D))return!1;var g=this.D[a];b=rb(g,b,c,d);return-1<b?(pb(g[b]),y.splice.call(g,b,1),0==g.length&&(delete this.D[a],this.cb--),!0):!1};
function sb(a,b){var c=b.type;if(!(c in a.D))return!1;var d=Ia(a.D[c],b);d&&(pb(b),0==a.D[c].length&&(delete a.D[c],a.cb--));return d}qb.prototype.Bb=function(a){a=a&&a.toString();var b=0,c;for(c in this.D)if(!a||c==a){for(var d=this.D[c],g=0;g<d.length;g++)++b,pb(d[g]);delete this.D[c];this.cb--}return b};qb.prototype.Ta=function(a,b,c,d){a=this.D[a.toString()];var g=-1;a&&(g=rb(a,b,c,d));return-1<g?a[g]:null};
function rb(a,b,c,d){for(var g=0;g<a.length;++g){var f=a[g];if(!f.La&&f.wa==b&&f.gb==!!c&&f.pb==d)return g}return-1};var tb="closure_lm_"+(1E6*Math.random()|0),ub={},vb=0;function wb(a,b,c,d,g){if(u(b)){for(var f=0;f<b.length;f++)wb(a,b[f],c,d,g);return null}c=xb(c);if(a&&a[mb])a=a.f(b,c,d,g);else{if(!b)throw Error("Invalid event type");var f=!!d,h=yb(a);h||(a[tb]=h=new qb(a));c=h.add(b,c,!1,d,g);c.Ab||(d=zb(),c.Ab=d,d.src=a,d.wa=c,a.addEventListener?a.addEventListener(b.toString(),d,f):a.attachEvent(Ab(b.toString()),d),vb++);a=c}return a}
function zb(){var a=Bb,b=hb?function(c){return a.call(b.src,b.wa,c)}:function(c){c=a.call(b.src,b.wa,c);if(!c)return c};return b}function Cb(a,b,c,d,g){if(u(b))for(var f=0;f<b.length;f++)Cb(a,b[f],c,d,g);else c=xb(c),a&&a[mb]?a.F(b,c,d,g):a&&(a=yb(a))&&(b=a.Ta(b,c,!!d,g))&&Db(b)}
function Db(a){if(w(a)||!a||a.La)return!1;var b=a.src;if(b&&b[mb])return sb(b.ga,a);var c=a.type,d=a.Ab;b.removeEventListener?b.removeEventListener(c,d,a.gb):b.detachEvent&&b.detachEvent(Ab(c),d);vb--;(c=yb(b))?(sb(c,a),0==c.cb&&(c.src=null,b[tb]=null)):pb(a);return!0}function Ab(a){return a in ub?ub[a]:ub[a]="on"+a}function Eb(a,b,c,d){var g=1;if(a=yb(a))if(b=a.D[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.gb==c&&!f.La&&(g&=!1!==Fb(f,d))}return Boolean(g)}
function Fb(a,b){var c=a.wa,d=a.pb||a.src;a.fb&&Db(a);return c.call(d,b)}
function Bb(a,b){if(a.La)return!0;if(!hb){var c;if(!(c=b))a:{c=["window","event"];for(var d=m,g;g=c.shift();)if(null!=d[g])d=d[g];else{c=null;break a}c=d}g=c;c=new jb(g,this);d=!0;if(!(0>g.keyCode||void 0!=g.returnValue)){a:{var f=!1;if(0==g.keyCode)try{g.keyCode=-1;break a}catch(h){f=!0}if(f||void 0==g.returnValue)g.returnValue=!0}g=[];for(f=c.currentTarget;f;f=f.parentNode)g.push(f);for(var f=a.type,k=g.length-1;!c.Aa&&0<=k;k--)c.currentTarget=g[k],d&=Eb(g[k],f,!0,c);for(k=0;!c.Aa&&k<g.length;k++)c.currentTarget=
g[k],d&=Eb(g[k],f,!1,c)}return d}return Fb(a,new jb(b,this))}function yb(a){a=a[tb];return a instanceof qb?a:null}var Gb="__closure_events_fn_"+(1E9*Math.random()>>>0);function xb(a){if(ca(a))return a;a[Gb]||(a[Gb]=function(b){return a.handleEvent(b)});return a[Gb]};function Hb(a){Oa.call(this);this.sc=a;this.ub={}}x(Hb,Oa);var Ib=[];e=Hb.prototype;e.f=function(a,b,c,d){u(b)||(b&&(Ib[0]=b.toString()),b=Ib);for(var g=0;g<b.length;g++){var f=wb(a,b[g],c||this.handleEvent,d||!1,this.sc||this);if(!f)break;this.ub[f.key]=f}return this};
e.F=function(a,b,c,d,g){if(u(b))for(var f=0;f<b.length;f++)this.F(a,b[f],c,d,g);else c=c||this.handleEvent,g=g||this.sc||this,c=xb(c),d=!!d,b=a&&a[mb]?a.Ta(b,c,d,g):a?(a=yb(a))?a.Ta(b,c,d,g):null:null,b&&(Db(b),delete this.ub[b.key]);return this};e.Bb=function(){Ta(this.ub,Db);this.ub={}};e.l=function(){Hb.b.l.call(this);this.Bb()};e.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};function Jb(){}t(Jb);Jb.prototype.Zc=0;function Kb(a){return":"+(a.Zc++).toString(36)};function Lb(){return I?"Webkit":H?"Moz":F?"ms":Ya?"O":null};function Mb(a,b){this.width=a;this.height=b}e=Mb.prototype;e.clone=function(){return new Mb(this.width,this.height)};e.toString=function(){return"("+this.width+" x "+this.height+")"};e.ceil=function(){this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};e.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};e.round=function(){this.width=Math.round(this.width);this.height=Math.round(this.height);return this};
e.scale=function(a,b){var c=w(b)?b:a;this.width*=a;this.height*=c;return this};function K(a,b){this.x=void 0!==a?a:0;this.y=void 0!==b?b:0}e=K.prototype;e.clone=function(){return new K(this.x,this.y)};e.toString=function(){return"("+this.x+", "+this.y+")"};function Ob(a,b){return new K(a.x-b.x,a.y-b.y)}e.ceil=function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this};e.floor=function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this};e.round=function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this};
e.translate=function(a,b){a instanceof K?(this.x+=a.x,this.y+=a.y):(this.x+=a,w(b)&&(this.y+=b));return this};e.scale=function(a,b){var c=w(b)?b:a;this.x*=a;this.y*=c;return this};function Pb(a,b,c,d){this.top=a;this.right=b;this.bottom=c;this.left=d}e=Pb.prototype;e.clone=function(){return new Pb(this.top,this.right,this.bottom,this.left)};e.toString=function(){return"("+this.top+"t, "+this.right+"r, "+this.bottom+"b, "+this.left+"l)"};e.contains=function(a){return this&&a?a instanceof Pb?a.left>=this.left&&a.right<=this.right&&a.top>=this.top&&a.bottom<=this.bottom:a.x>=this.left&&a.x<=this.right&&a.y>=this.top&&a.y<=this.bottom:!1};
e.expand=function(a,b,c,d){da(a)?(this.top-=a.top,this.right+=a.right,this.bottom+=a.bottom,this.left-=a.left):(this.top-=a,this.right+=b,this.bottom+=c,this.left-=d);return this};e.ceil=function(){this.top=Math.ceil(this.top);this.right=Math.ceil(this.right);this.bottom=Math.ceil(this.bottom);this.left=Math.ceil(this.left);return this};e.floor=function(){this.top=Math.floor(this.top);this.right=Math.floor(this.right);this.bottom=Math.floor(this.bottom);this.left=Math.floor(this.left);return this};
e.round=function(){this.top=Math.round(this.top);this.right=Math.round(this.right);this.bottom=Math.round(this.bottom);this.left=Math.round(this.left);return this};e.translate=function(a,b){a instanceof K?(this.left+=a.x,this.right+=a.x,this.top+=a.y,this.bottom+=a.y):(this.left+=a,this.right+=a,w(b)&&(this.top+=b,this.bottom+=b));return this};e.scale=function(a,b){var c=w(b)?b:a;this.left*=a;this.right*=a;this.top*=c;this.bottom*=c;return this};function Qb(a,b,c,d){this.left=a;this.top=b;this.width=c;this.height=d}e=Qb.prototype;e.clone=function(){return new Qb(this.left,this.top,this.width,this.height)};e.toString=function(){return"("+this.left+", "+this.top+" - "+this.width+"w x "+this.height+"h)"};e.contains=function(a){return a instanceof Qb?this.left<=a.left&&this.left+this.width>=a.left+a.width&&this.top<=a.top&&this.top+this.height>=a.top+a.height:a.x>=this.left&&a.x<=this.left+this.width&&a.y>=this.top&&a.y<=this.top+this.height};
e.ceil=function(){this.left=Math.ceil(this.left);this.top=Math.ceil(this.top);this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};e.floor=function(){this.left=Math.floor(this.left);this.top=Math.floor(this.top);this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};e.round=function(){this.left=Math.round(this.left);this.top=Math.round(this.top);this.width=Math.round(this.width);this.height=Math.round(this.height);return this};
e.translate=function(a,b){a instanceof K?(this.left+=a.x,this.top+=a.y):(this.left+=a,w(b)&&(this.top+=b));return this};e.scale=function(a,b){var c=w(b)?b:a;this.left*=a;this.width*=a;this.top*=c;this.height*=c;return this};var Rb=!F||db(9);!H&&!F||F&&db(9)||H&&J("1.9.1");var Sb=F&&!J("9");function Tb(a){return a?new Ub(L(a)):ma||(ma=new Ub)}function Vb(a,b){return v(b)?a.getElementById(b):b}
function Wb(a,b,c,d){a=d||a;b=b&&"*"!=b?b.toUpperCase():"";if(a.querySelectorAll&&a.querySelector&&(b||c))return a.querySelectorAll(b+(c?"."+c:""));if(c&&a.getElementsByClassName){a=a.getElementsByClassName(c);if(b){d={};for(var g=0,f=0,h;h=a[f];f++)b==h.nodeName&&(d[g++]=h);d.length=g;return d}return a}a=a.getElementsByTagName(b||"*");if(c){d={};for(f=g=0;h=a[f];f++)b=h.className,"function"==typeof b.split&&Ha(b.split(/\s+/),c)&&(d[g++]=h);d.length=g;return d}return a}
function Xb(a,b){Ta(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in Yb?a.setAttribute(Yb[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})}var Yb={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",valign:"vAlign",width:"width"};
function Zb(a){return I||"CSS1Compat"!=a.compatMode?a.body||a.documentElement:a.documentElement}function $b(a){return a.parentWindow||a.defaultView}function ac(a,b,c,d){function g(c){c&&b.appendChild(v(c)?a.createTextNode(c):c)}for(;d<c.length;d++){var f=c[d];if(!ba(f)||da(f)&&0<f.nodeType)g(f);else{var h;a:{if(f&&"number"==typeof f.length){if(da(f)){h="function"==typeof f.item||"string"==typeof f.item;break a}if(ca(f)){h="function"==typeof f.item;break a}}h=!1}A(h?Ja(f):f,g)}}}
function bc(a){for(var b;b=a.firstChild;)a.removeChild(b)}function cc(a){return a&&a.parentNode?a.parentNode.removeChild(a):null}function dc(a){return void 0!=a.firstElementChild?a.firstElementChild:ec(a.firstChild)}function ec(a){for(;a&&1!=a.nodeType;)a=a.nextSibling;return a}function fc(a){return da(a)&&1==a.nodeType}
function gc(a,b){if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}function L(a){return 9==a.nodeType?a:a.ownerDocument||a.document}
function hc(a,b){if("textContent"in a)a.textContent=b;else if(3==a.nodeType)a.data=b;else if(a.firstChild&&3==a.firstChild.nodeType){for(;a.lastChild!=a.firstChild;)a.removeChild(a.lastChild);a.firstChild.data=b}else bc(a),a.appendChild(L(a).createTextNode(String(b)))}var ic={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},jc={IMG:" ",BR:"\n"};function kc(a){return lc(a)&&mc(a)}function nc(a,b){b?a.tabIndex=0:(a.tabIndex=-1,a.removeAttribute("tabIndex"))}
function lc(a){a=a.getAttributeNode("tabindex");return null!=a&&a.specified}function mc(a){a=a.tabIndex;return w(a)&&0<=a&&32768>a}function oc(a){var b=[];pc(a,b,!1);return b.join("")}function pc(a,b,c){if(!(a.nodeName in ic))if(3==a.nodeType)c?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in jc)b.push(jc[a.nodeName]);else for(a=a.firstChild;a;)pc(a,b,c),a=a.nextSibling}function Ub(a){this.r=a||m.document||document}e=Ub.prototype;e.p=Tb;
function qc(a){return a.r}e.a=function(a){return Vb(this.r,a)};e.h=function(a,b,c){var d=this.r,g=arguments,f=g[0],h=g[1];if(!Rb&&h&&(h.name||h.type)){f=["<",f];h.name&&f.push(' name="',ra(h.name),'"');if(h.type){f.push(' type="',ra(h.type),'"');var k={};Wa(k,h);delete k.type;h=k}f.push(">");f=f.join("")}f=d.createElement(f);h&&(v(h)?f.className=h:u(h)?f.className=h.join(" "):Xb(f,h));2<g.length&&ac(d,f,g,2);return f};e.createElement=function(a){return this.r.createElement(a)};e.createTextNode=function(a){return this.r.createTextNode(String(a))};
function rc(a){return"CSS1Compat"==a.r.compatMode}function sc(a){var b=a.r;a=Zb(b);b=$b(b);return F&&J("10")&&b.pageYOffset!=a.scrollTop?new K(a.scrollLeft,a.scrollTop):new K(b.pageXOffset||a.scrollLeft,b.pageYOffset||a.scrollTop)}e.appendChild=function(a,b){a.appendChild(b)};e.append=function(a,b){ac(L(a),a,arguments,1)};e.canHaveChildren=function(a){if(1!=a.nodeType)return!1;switch(a.tagName){case "APPLET":case "AREA":case "BASE":case "BR":case "COL":case "COMMAND":case "EMBED":case "FRAME":case "HR":case "IMG":case "INPUT":case "IFRAME":case "ISINDEX":case "KEYGEN":case "LINK":case "NOFRAMES":case "NOSCRIPT":case "META":case "OBJECT":case "PARAM":case "SCRIPT":case "SOURCE":case "STYLE":case "TRACK":case "WBR":return!1}return!0};
e.removeNode=cc;e.pc=dc;e.Uc=fc;e.contains=gc;e.W=function(a){var b;(b="A"==a.tagName||"INPUT"==a.tagName||"TEXTAREA"==a.tagName||"SELECT"==a.tagName||"BUTTON"==a.tagName?!a.disabled&&(!lc(a)||mc(a)):kc(a))&&F?(a=ca(a.getBoundingClientRect)?a.getBoundingClientRect():{height:a.offsetHeight,width:a.offsetWidth},a=null!=a&&0<a.height&&0<a.width):a=b;return a};function tc(a,b){if(v(b)){var c=uc(a,b);c&&(a.style[c]=void 0)}else for(c in b){var d=a,g=b[c],f=uc(d,c);f&&(d.style[f]=g)}}function uc(a,b){var c=Aa(b);if(void 0===a.style[c]){var d=Lb()+Ba(c);if(void 0!==a.style[d])return d}return c}function M(a,b){var c=L(a);return c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(a,null))?c[b]||c.getPropertyValue(b)||"":""}function N(a,b){return M(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style&&a.style[b]}
function vc(a){var b;try{b=a.getBoundingClientRect()}catch(c){return{left:0,top:0,right:0,bottom:0}}F&&a.ownerDocument.body&&(a=a.ownerDocument,b.left-=a.documentElement.clientLeft+a.body.clientLeft,b.top-=a.documentElement.clientTop+a.body.clientTop);return b}
function wc(a){if(F&&!db(8))return a.offsetParent;var b=L(a),c=N(a,"position"),d="fixed"==c||"absolute"==c;for(a=a.parentNode;a&&a!=b;a=a.parentNode)if(c=N(a,"position"),d=d&&"static"==c&&a!=b.documentElement&&a!=b.body,!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||"fixed"==c||"absolute"==c||"relative"==c))return a;return null}
function xc(a){for(var b=new Pb(0,Infinity,Infinity,0),c=Tb(a),d=c.r.body,g=c.r.documentElement,f=Zb(c.r);a=wc(a);)if(!(F&&0==a.clientWidth||I&&0==a.clientHeight&&a==d)&&a!=d&&a!=g&&"visible"!=N(a,"overflow")){var h=yc(a),k;k=a;if(H&&!J("1.9")){var l=parseFloat(M(k,"borderLeftWidth"));if(zc(k))var n=k.offsetWidth-k.clientWidth-l-parseFloat(M(k,"borderRightWidth")),l=l+n;k=new K(l,parseFloat(M(k,"borderTopWidth")))}else k=new K(k.clientLeft,k.clientTop);h.x+=k.x;h.y+=k.y;b.top=Math.max(b.top,h.y);
b.right=Math.min(b.right,h.x+a.clientWidth);b.bottom=Math.min(b.bottom,h.y+a.clientHeight);b.left=Math.max(b.left,h.x)}d=f.scrollLeft;f=f.scrollTop;b.left=Math.max(b.left,d);b.top=Math.max(b.top,f);c=($b(c.r)||window).document;c="CSS1Compat"==c.compatMode?c.documentElement:c.body;c=new Mb(c.clientWidth,c.clientHeight);b.right=Math.min(b.right,d+c.width);b.bottom=Math.min(b.bottom,f+c.height);return 0<=b.top&&0<=b.left&&b.bottom>b.top&&b.right>b.left?b:null}
function yc(a){var b,c=L(a),d=N(a,"position"),g=H&&c.getBoxObjectFor&&!a.getBoundingClientRect&&"absolute"==d&&(b=c.getBoxObjectFor(a))&&(0>b.screenX||0>b.screenY),f=new K(0,0),h;b=c?L(c):document;h=!F||db(9)||rc(Tb(b))?b.documentElement:b.body;if(a==h)return f;if(a.getBoundingClientRect)b=vc(a),a=sc(Tb(c)),f.x=b.left+a.x,f.y=b.top+a.y;else if(c.getBoxObjectFor&&!g)b=c.getBoxObjectFor(a),a=c.getBoxObjectFor(h),f.x=b.screenX-a.screenX,f.y=b.screenY-a.screenY;else{b=a;do{f.x+=b.offsetLeft;f.y+=b.offsetTop;
b!=a&&(f.x+=b.clientLeft||0,f.y+=b.clientTop||0);if(I&&"fixed"==N(b,"position")){f.x+=c.body.scrollLeft;f.y+=c.body.scrollTop;break}b=b.offsetParent}while(b&&b!=a);if(Ya||I&&"absolute"==d)f.y-=c.body.offsetTop;for(b=a;(b=wc(b))&&b!=c.body&&b!=h;)f.x-=b.scrollLeft,Ya&&"TR"==b.tagName||(f.y-=b.scrollTop)}return f}function Ac(a,b){"number"==typeof a&&(a=(b?Math.round(a):a)+"px");return a}
function Bc(a){var b=Cc;if("none"!=N(a,"display"))return b(a);var c=a.style,d=c.display,g=c.visibility,f=c.position;c.visibility="hidden";c.position="absolute";c.display="inline";a=b(a);c.display=d;c.position=f;c.visibility=g;return a}function Cc(a){var b=a.offsetWidth,c=a.offsetHeight,d=I&&!b&&!c;return(void 0===b||d)&&a.getBoundingClientRect?(a=vc(a),new Mb(a.right-a.left,a.bottom-a.top)):new Mb(b,c)}function Dc(a){var b=yc(a);a=Bc(a);return new Qb(b.x,b.y,a.width,a.height)}
function Ec(a,b){a.style.display=b?"":"none"}function zc(a){return"rtl"==N(a,"direction")}var Fc=H?"MozUserSelect":I?"WebkitUserSelect":null;function Gc(a,b,c){c=c?null:a.getElementsByTagName("*");if(Fc){if(b=b?"none":"",a.style[Fc]=b,c){a=0;for(var d;d=c[a];a++)d.style[Fc]=b}}else if(F||Ya)if(b=b?"on":"",a.setAttribute("unselectable",b),c)for(a=0;d=c[a];a++)d.setAttribute("unselectable",b)}
function Hc(a,b){if(/^\d+px?$/.test(b))return parseInt(b,10);var c=a.style.left,d=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;a.style.left=b;var g=a.style.pixelLeft;a.style.left=c;a.runtimeStyle.left=d;return g}function Ic(a,b){var c=a.currentStyle?a.currentStyle[b]:null;return c?Hc(a,c):0}
function Jc(a){if(F){var b=Ic(a,"paddingLeft"),c=Ic(a,"paddingRight"),d=Ic(a,"paddingTop");a=Ic(a,"paddingBottom");return new Pb(d,c,a,b)}b=M(a,"paddingLeft");c=M(a,"paddingRight");d=M(a,"paddingTop");a=M(a,"paddingBottom");return new Pb(parseFloat(d),parseFloat(c),parseFloat(a),parseFloat(b))}var Kc={thin:2,medium:4,thick:6};function Lc(a,b){if("none"==(a.currentStyle?a.currentStyle[b+"Style"]:null))return 0;var c=a.currentStyle?a.currentStyle[b+"Width"]:null;return c in Kc?Kc[c]:Hc(a,c)}
function Mc(a){if(F&&!db(9)){var b=Lc(a,"borderLeft"),c=Lc(a,"borderRight"),d=Lc(a,"borderTop");a=Lc(a,"borderBottom");return new Pb(d,c,a,b)}b=M(a,"borderLeftWidth");c=M(a,"borderRightWidth");d=M(a,"borderTopWidth");a=M(a,"borderBottomWidth");return new Pb(parseFloat(d),parseFloat(c),parseFloat(a),parseFloat(b))}var Nc=/matrix\([0-9\.\-]+, [0-9\.\-]+, [0-9\.\-]+, [0-9\.\-]+, ([0-9\.\-]+)p?x?, ([0-9\.\-]+)p?x?\)/;function O(){Oa.call(this);this.ga=new qb(this);this.Ec=this;this.ac=null}x(O,Oa);O.prototype[mb]=!0;e=O.prototype;e.lb=function(){return this.ac};e.gc=function(a){this.ac=a};e.addEventListener=function(a,b,c,d){wb(this,a,b,c,d)};e.removeEventListener=function(a,b,c,d){Cb(this,a,b,c,d)};
e.dispatchEvent=function(a){var b,c=this.lb();if(c)for(b=[];c;c=c.lb())b.push(c);var c=this.Ec,d=a.type||a;if(v(a))a=new B(a,c);else if(a instanceof B)a.target=a.target||c;else{var g=a;a=new B(d,c);Wa(a,g)}var g=!0,f;if(b)for(var h=b.length-1;!a.Aa&&0<=h;h--)f=a.currentTarget=b[h],g=Pc(f,d,!0,a)&&g;a.Aa||(f=a.currentTarget=c,g=Pc(f,d,!0,a)&&g,a.Aa||(g=Pc(f,d,!1,a)&&g));if(b)for(h=0;!a.Aa&&h<b.length;h++)f=a.currentTarget=b[h],g=Pc(f,d,!1,a)&&g;return g};
e.l=function(){O.b.l.call(this);this.ga&&this.ga.Bb(void 0);this.ac=null};e.f=function(a,b,c,d){return this.ga.add(String(a),b,!1,c,d)};e.F=function(a,b,c,d){return this.ga.remove(String(a),b,c,d)};function Pc(a,b,c,d){b=a.ga.D[String(b)];if(!b)return!0;b=b.concat();for(var g=!0,f=0;f<b.length;++f){var h=b[f];if(h&&!h.La&&h.gb==c){var k=h.wa,l=h.pb||h.src;h.fb&&sb(a.ga,h);g=!1!==k.call(l,d)&&g}}return g&&0!=d.xc}e.Ta=function(a,b,c,d){return this.ga.Ta(String(a),b,c,d)};function Qc(a){O.call(this);this.pa=a||Tb();this.Cb=Rc;this.Ha=null;this.k=!1;this.e=null;this.ja=void 0;this.N=this.G=this.H=this.Zb=null;this.Cc=!1}x(Qc,O);Qc.prototype.Tc=Jb.B();var Rc=null;
function Sc(a,b){switch(a){case 1:return b?"disable":"enable";case 2:return b?"highlight":"unhighlight";case 4:return b?"activate":"deactivate";case 8:return b?"select":"unselect";case 16:return b?"check":"uncheck";case 32:return b?"focus":"blur";case 64:return b?"open":"close"}throw Error("Invalid component state");}function Tc(a){return a.Ha||(a.Ha=Kb(a.Tc))}function Uc(a,b){if(a.H&&a.H.N){var c=a.H.N,d=a.Ha;d in c&&delete c[d];Ua(a.H.N,b,a)}a.Ha=b}e=Qc.prototype;e.a=function(){return this.e};
function P(a){a.ja||(a.ja=new Hb(a));return a.ja}function Vc(a,b){if(a==b)throw Error("Unable to set parent component");if(b&&a.H&&a.Ha&&Wc(a.H,a.Ha)&&a.H!=b)throw Error("Unable to set parent component");a.H=b;Qc.b.gc.call(a,b)}e.getParent=function(){return this.H};e.gc=function(a){if(this.H&&this.H!=a)throw Error("Method not supported");Qc.b.gc.call(this,a)};e.p=function(){return this.pa};e.h=function(){this.e=this.pa.createElement("div")};
function Xc(a,b,c){if(a.k)throw Error("Component already rendered");a.e||a.h();b?b.insertBefore(a.e,c||null):a.pa.r.body.appendChild(a.e);a.H&&!a.H.k||a.u()}e.m=function(a){if(this.k)throw Error("Component already rendered");if(a&&this.L(a)){this.Cc=!0;var b=L(a);this.pa&&this.pa.r==b||(this.pa=Tb(a));this.oa(a);this.u()}else throw Error("Invalid element to decorate");};e.L=function(){return!0};e.oa=function(a){this.e=a};e.u=function(){this.k=!0;Yc(this,function(a){!a.k&&a.a()&&a.u()})};
e.T=function(){Yc(this,function(a){a.k&&a.T()});this.ja&&this.ja.Bb();this.k=!1};e.l=function(){this.k&&this.T();this.ja&&(this.ja.S(),delete this.ja);Yc(this,function(a){a.S()});!this.Cc&&this.e&&cc(this.e);this.H=this.Zb=this.e=this.N=this.G=null;Qc.b.l.call(this)};e.Oa=function(a,b){this.Pa(a,Zc(this),b)};
e.Pa=function(a,b,c){if(a.k&&(c||!this.k))throw Error("Component already rendered");if(0>b||b>Zc(this))throw Error("Child component index out of bounds");this.N&&this.G||(this.N={},this.G=[]);if(a.getParent()==this){var d=Tc(a);this.N[d]=a;Ia(this.G,a)}else Ua(this.N,Tc(a),a);Vc(a,this);La(this.G,b,0,a);a.k&&this.k&&a.getParent()==this?(c=this.o(),c.insertBefore(a.a(),c.childNodes[b]||null)):c?(this.e||this.h(),b=$c(this,b+1),Xc(a,this.o(),b?b.e:null)):this.k&&!a.k&&a.e&&a.e.parentNode&&1==a.e.parentNode.nodeType&&
a.u()};e.o=function(){return this.e};function ad(a){null==a.Cb&&(a.Cb=zc(a.k?a.e:a.pa.r.body));return a.Cb}e.Ba=function(a){if(this.k)throw Error("Component already rendered");this.Cb=a};function Zc(a){return a.G?a.G.length:0}function Wc(a,b){var c;a.N&&b?(c=a.N,c=(b in c?c[b]:void 0)||null):c=null;return c}function $c(a,b){return a.G?a.G[b]||null:null}function Yc(a,b,c){a.G&&A(a.G,b,c)}function bd(a,b){return a.G&&b?Ca(a.G,b):-1}
e.removeChild=function(a,b){if(a){var c=v(a)?a:Tc(a);a=Wc(this,c);if(c&&a){var d=this.N;c in d&&delete d[c];Ia(this.G,a);b&&(a.T(),a.e&&cc(a.e));Vc(a,null)}}if(!a)throw Error("Child is not in parent component");return a};function cd(a){if(a.classList)return a.classList;a=a.className;return v(a)&&a.match(/\S+/g)||[]}function dd(a,b){return a.classList?a.classList.contains(b):Ha(cd(a),b)}function ed(a,b){a.classList?a.classList.add(b):dd(a,b)||(a.className+=0<a.className.length?" "+b:b)}function fd(a,b){if(a.classList)A(b,function(b){ed(a,b)});else{var c={};A(cd(a),function(a){c[a]=!0});A(b,function(a){c[a]=!0});a.className="";for(var d in c)a.className+=0<a.className.length?" "+d:d}}
function gd(a,b){a.classList?a.classList.remove(b):dd(a,b)&&(a.className=Da(cd(a),function(a){return a!=b}).join(" "))}function hd(a,b){a.classList?A(b,function(b){gd(a,b)}):a.className=Da(cd(a),function(a){return!Ha(b,a)}).join(" ")};function id(a,b){if(!a)throw Error("Invalid class name "+a);if(!ca(b))throw Error("Invalid decorator function "+b);jd[a]=b}var kd={},jd={};var ld;function md(a,b){b?a.setAttribute("role",b):a.removeAttribute("role")}function Q(a,b,c){u(c)&&(c=c.join(" "));var d="aria-"+b;""===c||void 0==c?(ld||(ld={atomic:!1,autocomplete:"none",dropeffect:"none",haspopup:!1,live:"off",multiline:!1,multiselectable:!1,orientation:"vertical",readonly:!1,relevant:"additions text",required:!1,sort:"none",busy:!1,disabled:!1,hidden:!1,invalid:"false"}),c=ld,b in c?a.setAttribute(d,c[b]):a.removeAttribute(d)):a.setAttribute(d,c)}
function nd(a,b){var c=a.getAttribute("aria-"+b);return null==c||void 0==c?"":String(c)};function od(){}var pd;t(od);var qd={button:"pressed",checkbox:"checked",menuitem:"selected",menuitemcheckbox:"checked",menuitemradio:"checked",radio:"checked",tab:"selected",treeitem:"selected"};e=od.prototype;e.ia=function(){};e.h=function(a){var b=a.p().h("div",this.ra(a).join(" "),a.J);rd(this,a,b);return b};e.o=function(a){return a};e.Qa=function(a,b,c){if(a=a.a?a.a():a){var d=[b];F&&!J("7")&&(d=sd(cd(a),b),d.push(b));(c?fd:hd)(a,d)}};e.L=function(){return!0};
e.m=function(a,b){b.id&&Uc(a,b.id);var c=this.o(b);c&&c.firstChild?td(a,c.firstChild.nextSibling?Ja(c.childNodes):c.firstChild):a.J=null;var d=0,g=this.g(),f=this.g(),h=!1,k=!1,l=!1,n=Ja(cd(b));A(n,function(a){h||a!=g?k||a!=f?d|=this.mb(a):k=!0:(h=!0,f==g&&(k=!0));1==this.mb(a)&&kc(c)&&nc(c,!1)},this);a.ma=d;h||(n.push(g),f==g&&(k=!0));k||n.push(f);var p=a.O;p&&n.push.apply(n,p);if(F&&!J("7")){var s=sd(n);0<s.length&&(n.push.apply(n,s),l=!0)}if(!h||!k||p||l)b.className=n.join(" ");rd(this,a,b);return b};
e.Ia=function(a){ad(a)&&this.Ba(a.a(),!0);a.isEnabled()&&this.da(a,a.j)};function ud(a,b,c){if(a=c||a.ia())c=b.getAttribute("role")||null,a!=c&&md(b,a)}function rd(a,b,c){b.j||Q(c,"hidden",!b.j);b.isEnabled()||a.R(c,1,!b.isEnabled());R(b,8)&&a.R(c,8,S(b,8));R(b,16)&&a.R(c,16,S(b,16));R(b,64)&&a.R(c,64,S(b,64))}e.Ma=function(a,b){Gc(a,!b,!F&&!Ya)};e.Ba=function(a,b){this.Qa(a,this.g()+"-rtl",b)};e.W=function(a){var b;return R(a,32)&&(b=a.q())?kc(b):!1};
e.da=function(a,b){var c;if(R(a,32)&&(c=a.q())){if(!b&&S(a,32)){try{c.blur()}catch(d){}S(a,32)&&a.ta(null)}kc(c)!=b&&nc(c,b)}};e.Q=function(a,b){Ec(a,b);a&&Q(a,"hidden",!b)};e.A=function(a,b,c){var d=a.a();if(d){var g=this.Ra(b);g&&this.Qa(a,g,c);this.R(d,b,c)}};e.R=function(a,b,c){pd||(pd={1:"disabled",8:"selected",16:"checked",64:"expanded"});b=pd[b];var d=a.getAttribute("role")||null;d&&(d=qd[d]||b,b="checked"==b||"selected"==b?d:b);b&&Q(a,b,c)};
e.I=function(a,b){var c=this.o(a);if(c&&(bc(c),b))if(v(b))hc(c,b);else{var d=function(a){if(a){var b=L(c);c.appendChild(v(a)?b.createTextNode(a):a)}};u(b)?A(b,d):!ba(b)||"nodeType"in b?d(b):A(Ja(b),d)}};e.q=function(a){return a.a()};e.g=function(){return"goog-control"};e.ra=function(a){var b=this.g(),c=[b],d=this.g();d!=b&&c.push(d);b=a.ma;for(d=[];b;){var g=b&-b;d.push(this.Ra(g));b&=~g}c.push.apply(c,d);(a=a.O)&&c.push.apply(c,a);F&&!J("7")&&c.push.apply(c,sd(c));return c};
function sd(a,b){var c=[];b&&(a=a.concat([b]));A([],function(d){!Fa(d,ka(Ha,a))||b&&!Ha(d,b)||c.push(d.join("_"))});return c}e.Ra=function(a){this.hb||vd(this);return this.hb[a]};e.mb=function(a){if(!this.Ac){this.hb||vd(this);var b=this.hb,c={},d;for(d in b)c[b[d]]=d;this.Ac=c}a=parseInt(this.Ac[a],10);return isNaN(a)?0:a};function vd(a){var b=a.g();b.replace(/\xa0|\s/g," ");a.hb={1:b+"-disabled",2:b+"-hover",4:b+"-active",8:b+"-selected",16:b+"-checked",32:b+"-focused",64:b+"-open"}};function wd(a,b,c,d,g){if(!(F||I&&J("525")))return!0;if(D&&g)return xd(a);if(g&&!d)return!1;w(b)&&(b=yd(b));if(!c&&(17==b||18==b||D&&91==b))return!1;if(I&&d&&c)switch(a){case 220:case 219:case 221:case 192:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:return!1}if(F&&d&&b==a)return!1;switch(a){case 13:return!0;case 27:return!I}return xd(a)}
function xd(a){if(48<=a&&57>=a||96<=a&&106>=a||65<=a&&90>=a||I&&0==a)return!0;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0;default:return!1}}function yd(a){if(H)a=zd(a);else if(D&&I)a:switch(a){case 93:a=91;break a}return a}
function zd(a){switch(a){case 61:return 187;case 59:return 186;case 173:return 189;case 224:return 91;case 0:return 224;default:return a}};function Ad(a,b){O.call(this);a&&Bd(this,a,b)}x(Ad,O);e=Ad.prototype;e.e=null;e.sb=null;e.Vb=null;e.tb=null;e.K=-1;e.ka=-1;e.Fb=!1;
var Cd={3:13,12:144,63232:38,63233:40,63234:37,63235:39,63236:112,63237:113,63238:114,63239:115,63240:116,63241:117,63242:118,63243:119,63244:120,63245:121,63246:122,63247:123,63248:44,63272:46,63273:36,63275:35,63276:33,63277:34,63289:144,63302:45},Dd={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},Ed=F||I&&J("525"),Fd=D&&H;e=Ad.prototype;
e.Oc=function(a){I&&(17==this.K&&!a.ctrlKey||18==this.K&&!a.altKey||D&&91==this.K&&!a.metaKey)&&(this.ka=this.K=-1);-1==this.K&&(a.ctrlKey&&17!=a.keyCode?this.K=17:a.altKey&&18!=a.keyCode?this.K=18:a.metaKey&&91!=a.keyCode&&(this.K=91));Ed&&!wd(a.keyCode,this.K,a.shiftKey,a.ctrlKey,a.altKey)?this.handleEvent(a):(this.ka=yd(a.keyCode),Fd&&(this.Fb=a.altKey))};e.Pc=function(a){this.ka=this.K=-1;this.Fb=a.altKey};
e.handleEvent=function(a){var b=a.ha,c,d,g=b.altKey;F&&"keypress"==a.type?(c=this.ka,d=13!=c&&27!=c?b.keyCode:0):I&&"keypress"==a.type?(c=this.ka,d=0<=b.charCode&&63232>b.charCode&&xd(c)?b.charCode:0):Ya?(c=this.ka,d=xd(c)?b.keyCode:0):(c=b.keyCode||this.ka,d=b.charCode||0,Fd&&(g=this.Fb),D&&63==d&&224==c&&(c=191));var f=c=yd(c),h=b.keyIdentifier;c?63232<=c&&c in Cd?f=Cd[c]:25==c&&a.shiftKey&&(f=9):h&&h in Dd&&(f=Dd[h]);a=f==this.K;this.K=f;b=new Gd(f,d,a,b);b.altKey=g;this.dispatchEvent(b)};
e.a=function(){return this.e};function Bd(a,b,c){a.tb&&a.detach();a.e=b;a.sb=wb(a.e,"keypress",a,c);a.Vb=wb(a.e,"keydown",a.Oc,c,a);a.tb=wb(a.e,"keyup",a.Pc,c,a)}e.detach=function(){this.sb&&(Db(this.sb),Db(this.Vb),Db(this.tb),this.tb=this.Vb=this.sb=null);this.e=null;this.ka=this.K=-1};e.l=function(){Ad.b.l.call(this);this.detach()};function Gd(a,b,c,d){jb.call(this,d);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c}x(Gd,jb);function T(a,b,c){Qc.call(this,c);if(!b){b=this.constructor;for(var d;b;){d=ea(b);if(d=kd[d])break;b=b.b?b.b.constructor:null}b=d?ca(d.B)?d.B():new d:null}this.d=b;this.J=void 0!==a?a:null}x(T,Qc);e=T.prototype;e.J=null;e.ma=0;e.bb=39;e.Y=255;e.ab=0;e.j=!0;e.O=null;e.Qb=!0;e.eb=!1;e.cc=null;function Hd(a,b){a.k&&b!=a.Qb&&Id(a,b);a.Qb=b}e.q=function(){return this.d.q(this)};e.kb=function(){return this.M||(this.M=new Ad)};
e.Qa=function(a,b){b?a&&(this.O?Ha(this.O,a)||this.O.push(a):this.O=[a],this.d.Qa(this,a,!0)):a&&this.O&&Ia(this.O,a)&&(0==this.O.length&&(this.O=null),this.d.Qa(this,a,!1))};e.h=function(){var a=this.d.h(this);this.e=a;ud(this.d,a,this.Ua());this.eb||this.d.Ma(a,!1);this.j||this.d.Q(a,!1)};e.Ua=function(){return this.cc};e.o=function(){return this.d.o(this.a())};e.L=function(a){return this.d.L(a)};
e.oa=function(a){this.e=a=this.d.m(this,a);ud(this.d,a,this.Ua());this.eb||this.d.Ma(a,!1);this.j="none"!=a.style.display};e.u=function(){T.b.u.call(this);this.d.Ia(this);if(this.bb&-2&&(this.Qb&&Id(this,!0),R(this,32))){var a=this.q();if(a){var b=this.kb();Bd(b,a);P(this).f(b,"key",this.U).f(a,"focus",this.ob).f(a,"blur",this.ta)}}};
function Id(a,b){var c=P(a),d=a.a();b?(c.f(d,"mouseover",a.Sb).f(d,"mousedown",a.ua).f(d,"mouseup",a.va).f(d,"mouseout",a.Rb),a.Xa!=r&&c.f(d,"contextmenu",a.Xa),F&&c.f(d,"dblclick",a.rc)):(c.F(d,"mouseover",a.Sb).F(d,"mousedown",a.ua).F(d,"mouseup",a.va).F(d,"mouseout",a.Rb),a.Xa!=r&&c.F(d,"contextmenu",a.Xa),F&&c.F(d,"dblclick",a.rc))}e.T=function(){T.b.T.call(this);this.M&&this.M.detach();this.j&&this.isEnabled()&&this.d.da(this,!1)};
e.l=function(){T.b.l.call(this);this.M&&(this.M.S(),delete this.M);delete this.d;this.O=this.J=null};e.I=function(a){this.d.I(this.a(),a);this.J=a};function td(a,b){a.J=b}e.Fa=function(){var a=this.J;if(!a)return"";if(!v(a))if(u(a))a=Ea(a,oc).join("");else{if(Sb&&"innerText"in a)a=a.innerText.replace(/(\r\n|\r|\n)/g,"\n");else{var b=[];pc(a,b,!0);a=b.join("")}a=a.replace(/ \xAD /g," ").replace(/\xAD/g,"");a=a.replace(/\u200B/g,"");Sb||(a=a.replace(/ +/g," "));" "!=a&&(a=a.replace(/^\s*/,""))}return na(a)};
e.Ba=function(a){T.b.Ba.call(this,a);var b=this.a();b&&this.d.Ba(b,a)};e.Ma=function(a){this.eb=a;var b=this.a();b&&this.d.Ma(b,a)};e.Q=function(a,b){if(b||this.j!=a&&this.dispatchEvent(a?"show":"hide")){var c=this.a();c&&this.d.Q(c,a);this.isEnabled()&&this.d.da(this,a);this.j=a;return!0}return!1};e.isEnabled=function(){return!S(this,1)};
e.X=function(a){var b=this.getParent();b&&"function"==typeof b.isEnabled&&!b.isEnabled()||!Jd(this,1,!a)||(a||(this.setActive(!1),this.P(!1)),this.j&&this.d.da(this,a),this.A(1,!a,!0))};e.P=function(a){Jd(this,2,a)&&this.A(2,a)};e.setActive=function(a){Jd(this,4,a)&&this.A(4,a)};e.hc=function(a){Jd(this,8,a)&&this.A(8,a)};e.n=function(a){Jd(this,64,a)&&this.A(64,a)};function S(a,b){return!!(a.ma&b)}
e.A=function(a,b,c){c||1!=a?R(this,a)&&b!=S(this,a)&&(this.d.A(this,a,b),this.ma=b?this.ma|a:this.ma&~a):this.X(!b)};function R(a,b){return!!(a.bb&b)}e.v=function(a,b){if(this.k&&S(this,a)&&!b)throw Error("Component already rendered");!b&&S(this,a)&&this.A(a,!1);this.bb=b?this.bb|a:this.bb&~a};function U(a,b){return!!(a.Y&b)&&R(a,b)}function Jd(a,b,c){return R(a,b)&&S(a,b)!=c&&(!(a.ab&b)||a.dispatchEvent(Sc(b,c)))&&!a.Ea}
e.Sb=function(a){(!a.relatedTarget||!gc(this.a(),a.relatedTarget))&&this.dispatchEvent("enter")&&this.isEnabled()&&U(this,2)&&this.P(!0)};e.Rb=function(a){a.relatedTarget&&gc(this.a(),a.relatedTarget)||!this.dispatchEvent("leave")||(U(this,4)&&this.setActive(!1),U(this,2)&&this.P(!1))};e.Xa=r;e.ua=function(a){this.isEnabled()&&(U(this,2)&&this.P(!0),!lb(a)||I&&D&&a.ctrlKey||(U(this,4)&&this.setActive(!0),this.d.W(this)&&this.q().focus()));this.eb||!lb(a)||I&&D&&a.ctrlKey||a.preventDefault()};
e.va=function(a){this.isEnabled()&&(U(this,2)&&this.P(!0),S(this,4)&&this.za(a)&&U(this,4)&&this.setActive(!1))};e.rc=function(a){this.isEnabled()&&this.za(a)};e.za=function(a){if(U(this,16)){var b=!S(this,16);Jd(this,16,b)&&this.A(16,b)}U(this,8)&&this.hc(!0);U(this,64)&&this.n(!S(this,64));b=new B("action",this);a&&(b.altKey=a.altKey,b.ctrlKey=a.ctrlKey,b.metaKey=a.metaKey,b.shiftKey=a.shiftKey,b.bc=a.bc);return this.dispatchEvent(b)};e.ob=function(){U(this,32)&&Jd(this,32,!0)&&this.A(32,!0)};
e.ta=function(){U(this,4)&&this.setActive(!1);U(this,32)&&Jd(this,32,!1)&&this.A(32,!1)};e.U=function(a){return this.j&&this.isEnabled()&&this.V(a)?(a.preventDefault(),a.stopPropagation(),!0):!1};e.V=function(a){return 13==a.keyCode&&this.za(a)};if(!ca(T))throw Error("Invalid component class "+T);if(!ca(od))throw Error("Invalid renderer class "+od);var Kd=ea(T);kd[Kd]=od;id("goog-control",function(){return new T(null)});function Ld(){}x(Ld,od);t(Ld);e=Ld.prototype;e.ia=function(){};e.m=function(a,b){Hd(a,!1);a.Y&=-256;a.v(32,!1);Ld.b.m.call(this,a,b);a.I(b.value);return b};e.h=function(a){Hd(a,!1);a.Y&=-256;a.v(32,!1);return a.p().h("textarea",{"class":this.ra(a).join(" "),disabled:!a.isEnabled()},a.J||"")};e.L=function(a){return"TEXTAREA"==a.tagName};e.Ba=r;e.W=function(a){return a.isEnabled()};e.da=r;e.A=function(a,b,c){Ld.b.A.call(this,a,b,c);(a=a.a())&&1==b&&(a.disabled=c)};e.R=r;
e.I=function(a,b){a&&(a.value=b)};e.g=function(){return"goog-textarea"};function V(a,b,c){T.call(this,a,b||Ld.B(),c);Hd(this,!1);this.Ma(!0);this.Ga=""!=a;a||(this.J="")}x(V,T);var Md=H||I||F&&db(11);e=V.prototype;e.Ja=!1;e.qb=!1;e.Ga=!1;e.$=0;e.Vc=0;e.Yb=0;e.tc=!1;e.xb=!1;e.fc=!1;e.ec=!1;e.Ka="";function Nd(a){return a.ya.top+a.ya.bottom+a.Gb.top+a.Gb.bottom}function Od(a){var b=a.Yb,c=a.a();b&&c&&a.xb&&(b-=Nd(a));return b}function Pd(a){a.Yb=100;a.resize()}e.ea=function(a){this.I(String(a))};
e.C=function(){return this.a().value!=this.Ka||Qd(this)||this.Ga?this.a().value:""};e.I=function(a){V.b.I.call(this,a);this.Ga=""!=a;this.resize()};e.X=function(a){V.b.X.call(this,a);this.a().disabled=!a};e.resize=function(){this.a()&&this.nb()};function Qd(a){return"placeholder"in a.a()}function Rd(a){a.Ka&&(Qd(a)?a.a().placeholder=a.Ka:!a.a()||a.Ga||a.qb||(ed(a.a(),Sd),a.a().value=a.Ka))}
e.u=function(){V.b.u.call(this);var a=this.a();tc(a,{overflowY:"hidden",overflowX:"auto",boxSizing:"border-box",MsBoxSizing:"border-box",WebkitBoxSizing:"border-box",MozBoxSizing:"border-box"});this.ya=Jc(a);this.Gb=Mc(a);P(this).f(a,"scroll",this.nb).f(a,"focus",this.nb).f(a,"keyup",this.nb).f(a,"mouseup",this.Yc).f(a,"blur",this.Hc);Rd(this);this.resize()};
function Td(a){if(!a.tc){var b=a.a().cloneNode(!1);tc(b,{position:"absolute",height:"auto",top:"-9999px",margin:"0",padding:"1px",border:"1px solid #000",overflow:"hidden"});qc(a.p()).body.appendChild(b);var c=b.scrollHeight;b.style.padding="10px";var d=b.scrollHeight;a.fc=d>c;b.style.borderWidth="10px";a.ec=b.scrollHeight>d;b.style.height="100px";100!=b.offsetHeight&&(a.xb=!0);cc(b);a.tc=!0}b=a.a();isNaN(a.ya.top)&&(a.ya=Jc(b),a.Gb=Mc(b));var c=a.a().scrollHeight,g=a.a(),d=g.offsetHeight-g.clientHeight;
if(!a.fc)var f=a.ya,d=d-(f.top+f.bottom);a.ec||(g=Mc(g),d-=g.top+g.bottom);c+=0<d?d:0;a.xb?c-=Nd(a):(a.fc||(d=a.ya,c+=d.top+d.bottom),a.ec||(a=Mc(b),c+=a.top+a.bottom));return c}function Ud(a,b){a.$!=b&&(a.$=b,a.a().style.height=b+"px")}function Vd(a){var b=a.a();b.style.height="auto";var c=b.value.match(/\n/g)||[];b.rows=c.length+1;a.$=0}var Sd="textarea-placeholder-input";V.prototype.Hc=function(){Qd(this)||(this.qb=!1,""==this.a().value&&(this.Ga=!1,Rd(this)))};
V.prototype.nb=function(a){if(!this.Ja){var b=this.a();!Qd(this)&&a&&"focus"==a.type&&(b.value==this.Ka&&this.Ka&&!this.qb&&(gd(b,Sd),b.value=""),this.qb=!0,this.Ga=""!=b.value);var c=!1;this.Ja=!0;a=this.$;if(b.scrollHeight){var d=!1,g=!1,f=Td(this),h=b.offsetHeight,k=Od(this),l;l=this.Vc;var n=this.a();l&&n&&this.xb&&(l-=Nd(this));k&&f<k?(Ud(this,k),d=!0):l&&f>l?(Ud(this,l),b.style.overflowY="",g=!0):h!=f?Ud(this,f):this.$||(this.$=f);d||g||!Md||(c=!0)}else Vd(this);this.Ja=!1;c&&(b=this.a(),this.Ja||
(this.Ja=!0,(g=b.scrollHeight)?(f=Td(this),c=Od(this),c&&f<=c||(d=this.ya,b.style.paddingBottom=d.bottom+1+"px",Td(this)==f&&(b.style.paddingBottom=d.bottom+g+"px",b.scrollTop=0,g=Td(this)-g,g>=c?Ud(this,g):Ud(this,c)),b.style.paddingBottom=d.bottom+"px")):Vd(this),this.Ja=!1));a!=this.$&&this.dispatchEvent("resize")}};
V.prototype.Yc=function(){var a=this.a(),b=a.offsetHeight;a.filters&&a.filters.length&&(a=a.filters.item("DXImageTransform.Microsoft.DropShadow"))&&(b-=a.offX);b!=this.$&&(this.$=this.Yb=b)};function Wd(){}x(Wd,od);t(Wd);e=Wd.prototype;e.ia=function(){return"button"};e.R=function(a,b,c){switch(b){case 8:case 16:Q(a,"pressed",c);break;default:case 64:case 1:Wd.b.R.call(this,a,b,c)}};e.h=function(a){var b=Wd.b.h.call(this,a);this.$a(b,a.Wa());var c=a.C();c&&this.ea(b,c);R(a,16)&&this.R(b,16,S(a,16));return b};e.m=function(a,b){b=Wd.b.m.call(this,a,b);var c=this.C(b);a.kc=c;a.jc=this.Wa(b);R(a,16)&&this.R(b,16,S(a,16));return b};e.C=r;e.ea=r;e.Wa=function(a){return a.title};
e.$a=function(a,b){a&&(b?a.title=b:a.removeAttribute("title"))};e.g=function(){return"goog-button"};function Xd(){}x(Xd,Wd);t(Xd);e=Xd.prototype;e.h=function(a){var b={"class":"goog-inline-block "+this.ra(a).join(" ")},b=a.p().h("div",b,this.jb(a.J,a.p()));this.$a(b,a.Wa());rd(this,a,b);return b};e.ia=function(){return"button"};e.o=function(a){return a&&a.firstChild&&a.firstChild.firstChild};e.jb=function(a,b){return b.h("div","goog-inline-block "+(this.g()+"-outer-box"),b.h("div","goog-inline-block "+(this.g()+"-inner-box"),a))};e.L=function(a){return"DIV"==a.tagName};
e.m=function(a,b){Yd(b,!0);Yd(b,!1);var c;a:{c=a.p().pc(b);var d=this.g()+"-outer-box";if(c&&dd(c,d)&&(c=a.p().pc(c),d=this.g()+"-inner-box",c&&dd(c,d))){c=!0;break a}c=!1}c||b.appendChild(this.jb(b.childNodes,a.p()));fd(b,["goog-inline-block",this.g()]);return Xd.b.m.call(this,a,b)};e.g=function(){return"goog-custom-button"};
function Yd(a,b){if(a)for(var c=b?a.firstChild:a.lastChild,d;c&&c.parentNode==a;){d=b?c.nextSibling:c.previousSibling;if(3==c.nodeType){var g=c.nodeValue;if(""==qa(g))a.removeChild(c);else{c.nodeValue=b?g.replace(/^[\s\xa0]+/,""):g.replace(/[\s\xa0]+$/,"");break}}else break;c=d}};function Zd(){}x(Zd,Wd);t(Zd);e=Zd.prototype;e.ia=function(){};e.h=function(a){Hd(a,!1);a.Y&=-256;a.v(32,!1);return a.p().h("button",{"class":this.ra(a).join(" "),disabled:!a.isEnabled(),title:a.Wa()||"",value:a.C()||""},a.Fa()||"")};e.L=function(a){return"BUTTON"==a.tagName||"INPUT"==a.tagName&&("button"==a.type||"submit"==a.type||"reset"==a.type)};e.m=function(a,b){Hd(a,!1);a.Y&=-256;a.v(32,!1);if(b.disabled){var c=this.Ra(1);ed(b,c)}return Zd.b.m.call(this,a,b)};
e.Ia=function(a){P(a).f(a.a(),"click",a.za)};e.Ma=r;e.Ba=r;e.W=function(a){return a.isEnabled()};e.da=r;e.A=function(a,b,c){Zd.b.A.call(this,a,b,c);(a=a.a())&&1==b&&(a.disabled=c)};e.C=function(a){return a.value};e.ea=function(a,b){a&&(a.value=b)};e.R=r;function $d(a,b,c){T.call(this,a,b||Zd.B(),c)}x($d,T);e=$d.prototype;e.C=function(){return this.kc};e.ea=function(a){this.kc=a;this.d.ea(this.a(),a)};e.Wa=function(){return this.jc};e.$a=function(a){this.jc=a;this.d.$a(this.a(),a)};e.l=function(){$d.b.l.call(this);delete this.kc;delete this.jc};e.u=function(){$d.b.u.call(this);if(R(this,32)){var a=this.q();a&&P(this).f(a,"keyup",this.V)}};e.V=function(a){return 13==a.keyCode&&"key"==a.type||32==a.keyCode&&"keyup"==a.type?this.za(a):32==a.keyCode};
id("goog-button",function(){return new $d(null)});function ae(a,b,c){$d.call(this,a,b||Xd.B(),c)}x(ae,$d);id("goog-custom-button",function(){return new ae(null)});function be(){}x(be,od);t(be);be.prototype.h=function(a){return a.p().h("div",this.g())};be.prototype.m=function(a,b){b.id&&Uc(a,b.id);if("HR"==b.tagName){var c=b;b=this.h(a);c.parentNode&&c.parentNode.insertBefore(b,c);cc(c)}else ed(b,this.g());return b};be.prototype.I=function(){};be.prototype.g=function(){return"goog-menuseparator"};function ce(a,b){T.call(this,null,a||be.B(),b);this.v(1,!1);this.v(2,!1);this.v(4,!1);this.v(32,!1);this.ma=1}x(ce,T);ce.prototype.u=function(){ce.b.u.call(this);md(this.a(),"separator")};id("goog-menuseparator",function(){return new ce});function de(a){this.lc=a}t(de);e=de.prototype;e.ia=function(){return this.lc};function ee(a,b){a&&(a.tabIndex=b?0:-1)}e.h=function(a){return a.p().h("div",this.ra(a).join(" "))};e.o=function(a){return a};e.L=function(a){return"DIV"==a.tagName};e.m=function(a,b){b.id&&Uc(a,b.id);var c=this.g(),d=!1,g=cd(b);g&&A(g,function(b){b==c?d=!0:b&&(b==c+"-disabled"?a.X(!1):b==c+"-horizontal"?fe(a,ge):b==c+"-vertical"&&fe(a,he))},this);d||ed(b,c);ie(this,a,this.o(b));return b};
function ie(a,b,c){if(c)for(var d=c.firstChild,g;d&&d.parentNode==c;){g=d.nextSibling;if(1==d.nodeType){var f=a.Kb(d);f&&(f.e=d,b.isEnabled()||f.X(!1),b.Oa(f),f.m(d))}else d.nodeValue&&""!=qa(d.nodeValue)||c.removeChild(d);d=g}}e.Kb=function(a){a:{var b;a=cd(a);for(var c=0,d=a.length;c<d;c++)if(b=a[c],b=b in jd?jd[b]():null){a=b;break a}a=null}return a};e.Ia=function(a){a=a.a();Gc(a,!0,H);F&&(a.hideFocus=!0);var b=this.ia();b&&md(a,b)};e.q=function(a){return a.a()};e.g=function(){return"goog-container"};
e.ra=function(a){var b=this.g(),c=[b,a.xa==ge?b+"-horizontal":b+"-vertical"];a.isEnabled()||c.push(b+"-disabled");return c};function je(a){this.lc=a||"menu"}x(je,de);t(je);e=je.prototype;e.L=function(a){return"UL"==a.tagName||je.b.L.call(this,a)};e.Kb=function(a){return"HR"==a.tagName?new ce:je.b.Kb.call(this,a)};e.na=function(a,b){return gc(a.a(),b)};e.g=function(){return"goog-menu"};e.Ia=function(a){je.b.Ia.call(this,a);Q(a.a(),"haspopup","true")};function ke(){this.oc=[]}x(ke,od);t(ke);function le(a,b){var c=a.oc[b];if(!c){switch(b){case 0:c=a.g()+"-highlight";break;case 1:c=a.g()+"-checkbox";break;case 2:c=a.g()+"-content"}a.oc[b]=c}return c}e=ke.prototype;e.ia=function(){return"menuitem"};e.h=function(a){var b=a.p().h("div",this.ra(a).join(" "),me(this,a.J,a.p()));ne(this,a,b,R(a,8)||R(a,16));return b};e.o=function(a){return a&&a.firstChild};
e.m=function(a,b){var c=dc(b),d=le(this,2);c&&dd(c,d)||b.appendChild(me(this,b.childNodes,a.p()));dd(b,"goog-option")&&(a.Db(!0),this.Db(a,b,!0));return ke.b.m.call(this,a,b)};e.I=function(a,b){var c=this.o(a),d=oe(this,a)?c.firstChild:null;ke.b.I.call(this,a,b);d&&!oe(this,a)&&c.insertBefore(d,c.firstChild||null)};function me(a,b,c){a=le(a,2);return c.h("div",a,b)}e.zc=function(a,b,c){a&&b&&ne(this,a,b,c)};e.Db=function(a,b,c){a&&b&&ne(this,a,b,c)};
function oe(a,b){var c=a.o(b);if(c){var c=c.firstChild,d=le(a,1);return!!c&&fc(c)&&dd(c,d)}return!1}function ne(a,b,c,d){ud(a,c,b.Ua());rd(a,b,c);d!=oe(a,c)&&(d?ed(c,"goog-option"):gd(c,"goog-option"),c=a.o(c),d?(a=le(a,1),c.insertBefore(b.p().h("div",a),c.firstChild||null)):c.removeChild(c.firstChild))}e.Ra=function(a){switch(a){case 2:return le(this,0);case 16:case 8:return"goog-option-selected";default:return ke.b.Ra.call(this,a)}};
e.mb=function(a){var b=le(this,0);switch(a){case "goog-option-selected":return 16;case b:return 2;default:return ke.b.mb.call(this,a)}};e.g=function(){return"goog-menuitem"};function W(a,b,c,d){T.call(this,a,d||ke.B(),c);this.ea(b)}x(W,T);e=W.prototype;e.C=function(){var a=this.Zb;return null!=a?a:this.Fa()};e.ea=function(a){this.Zb=a};e.v=function(a,b){W.b.v.call(this,a,b);switch(a){case 8:S(this,16)&&!b&&Jd(this,16,!1)&&this.A(16,!1);var c=this.a();c&&this.d.zc(this,c,b);break;case 16:(c=this.a())&&this.d.Db(this,c,b)}};e.zc=function(a){this.v(8,a)};e.Db=function(a){this.v(16,a)};
e.Fa=function(){var a=this.J;return u(a)?(a=Ea(a,function(a){return fc(a)&&(dd(a,"goog-menuitem-accel")||dd(a,"goog-menuitem-mnemonic-separator"))?"":oc(a)}).join(""),na(a)):W.b.Fa.call(this)};e.va=function(a){var b=this.getParent();if(b){var c=b.wc;b.wc=null;if(b=c&&w(a.clientX))b=new K(a.clientX,a.clientY),b=c==b?!0:c&&b?c.x==b.x&&c.y==b.y:!1;if(b)return}W.b.va.call(this,a)};e.V=function(a){return a.keyCode==this.vc&&this.za(a)?!0:W.b.V.call(this,a)};e.Jc=function(){return this.vc};
id("goog-menuitem",function(){return new W(null)});W.prototype.Ua=function(){return R(this,16)?"menuitemcheckbox":R(this,8)?"menuitemradio":W.b.Ua.call(this)};W.prototype.getParent=function(){return T.prototype.getParent.call(this)};W.prototype.lb=function(){return T.prototype.lb.call(this)};var pe,qe;qe=pe=!1;var re=C;re&&(-1!=re.indexOf("Firefox")||-1!=re.indexOf("Camino")||(-1!=re.indexOf("iPhone")||-1!=re.indexOf("iPod")?pe=!0:-1!=re.indexOf("iPad")&&(qe=!0)));var se=pe,te=qe;function ue(a,b,c,d,g,f,h,k,l){var n,p;if(n=c.offsetParent){var s="HTML"==n.tagName||"BODY"==n.tagName;s&&"static"==N(n,"position")||(p=yc(n),s||(s=(s=zc(n))&&H?-n.scrollLeft:!s||F&&J("8")||"visible"==N(n,"overflowX")?n.scrollLeft:n.scrollWidth-n.clientWidth-n.scrollLeft,p=Ob(p,new K(s,n.scrollTop))))}n=p||new K;p=Dc(a);if(s=xc(a)){var E=new Qb(s.left,s.top,s.right-s.left,s.bottom-s.top),s=Math.max(p.left,E.left),X=Math.min(p.left+p.width,E.left+E.width);if(s<=X){var oa=Math.max(p.top,E.top),E=Math.min(p.top+
p.height,E.top+E.height);oa<=E&&(p.left=s,p.top=oa,p.width=X-s,p.height=E-oa)}}s=Tb(a);oa=Tb(c);if(s.r!=oa.r){var X=s.r.body,oa=$b(oa.r),E=new K(0,0),pa;pa=(pa=L(X))?$b(pa):window;var Oc=X;do{var G;if(pa==oa)G=yc(Oc);else{G=Oc;var Y=void 0;if(G.getBoundingClientRect)Y=vc(G),Y=new K(Y.left,Y.top);else var Y=sc(Tb(G)),z=yc(G),Y=new K(z.x-Y.x,z.y-Y.y);z=void 0;if(H&&!J(12)){z=void 0;z=void 0;b:{var z=G,Nb=Aa("transform");if(void 0===z.style[Nb]&&(Nb=Lb()+Ba(Nb),void 0!==z.style[Nb])){z=(I?"-webkit":
H?"-moz":F?"-ms":Ya?"-o":null)+"-transform";break b}z="transform"}z=(z=N(G,z)||N(G,"transform"))?(G=z.match(Nc))?new K(parseFloat(G[1]),parseFloat(G[2])):new K(0,0):new K(0,0);z=new K(Y.x+z.x,Y.y+z.y)}else z=Y;G=z}E.x+=G.x;E.y+=G.y}while(pa&&pa!=oa&&(Oc=pa.frameElement)&&(pa=pa.parent));X=Ob(E,yc(X));!F||db(9)||rc(s)||(X=Ob(X,sc(s)));p.left+=X.x;p.top+=X.y}a=ve(a,b);b=new K(a&2?p.left+p.width:p.left,a&1?p.top+p.height:p.top);b=Ob(b,n);g&&(b.x+=(a&2?-1:1)*g.x,b.y+=(a&1?-1:1)*g.y);var q;if(h)if(l)q=
l;else if(q=xc(c))q.top-=n.y,q.right-=n.x,q.bottom-=n.y,q.left-=n.x;g=q;l=b.clone();q=ve(c,d);d=Bc(c);a=k?k.clone():d.clone();k=l;l=a;k=k.clone();l=l.clone();a=0;if(f||0!=q)q&2?k.x-=l.width+(f?f.right:0):f&&(k.x+=f.left),q&1?k.y-=l.height+(f?f.bottom:0):f&&(k.y+=f.top);h&&(g?(f=k,q=l,a=0,65==(h&65)&&(f.x<g.left||f.x>=g.right)&&(h&=-2),132==(h&132)&&(f.y<g.top||f.y>=g.bottom)&&(h&=-5),f.x<g.left&&h&1&&(f.x=g.left,a|=1),f.x<g.left&&f.x+q.width>g.right&&h&16&&(q.width=Math.max(q.width-(f.x+q.width-g.right),
0),a|=4),f.x+q.width>g.right&&h&1&&(f.x=Math.max(g.right-q.width,g.left),a|=1),h&2&&(a=a|(f.x<g.left?16:0)|(f.x+q.width>g.right?32:0)),f.y<g.top&&h&4&&(f.y=g.top,a|=2),f.y<=g.top&&f.y+q.height<g.bottom&&h&32&&(q.height=Math.max(q.height-(g.top-f.y),0),f.y=g.top,a|=8),f.y>=g.top&&f.y+q.height>g.bottom&&h&32&&(q.height=Math.max(q.height-(f.y+q.height-g.bottom),0),a|=8),f.y+q.height>g.bottom&&h&4&&(f.y=Math.max(g.bottom-q.height,g.top),a|=2),h&8&&(a=a|(f.y<g.top?64:0)|(f.y+q.height>g.bottom?128:0)),
h=a):h=256,a=h);f=new Qb(0,0,0,0);f.left=k.x;f.top=k.y;f.width=l.width;f.height=l.height;h=a;h&496||(l=new K(f.left,f.top),g=H&&(D||$a)&&J("1.9"),l instanceof K?(k=l.x,l=l.y):(k=l,l=void 0),c.style.left=Ac(k,g),c.style.top=Ac(l,g),a=new Mb(f.width,f.height),d==a||d&&a&&d.width==a.width&&d.height==a.height||(f=a,d=rc(Tb(L(c))),!F||J("10")||d&&J("8")?(c=c.style,H?c.MozBoxSizing="border-box":I?c.WebkitBoxSizing="border-box":c.boxSizing="border-box",c.width=Math.max(f.width,0)+"px",c.height=Math.max(f.height,
0)+"px"):(k=c.style,d?(d=Jc(c),c=Mc(c),k.pixelWidth=f.width-c.left-d.left-d.right-c.right,k.pixelHeight=f.height-c.top-d.top-d.bottom-c.bottom):(k.pixelWidth=f.width,k.pixelHeight=f.height))));return h}function ve(a,b){return(b&4&&zc(a)?b^2:b)&-5};function we(){}we.prototype.dc=function(){};function xe(a,b,c){this.element=a;this.ib=b;this.bd=c}x(xe,we);xe.prototype.dc=function(a,b,c){ue(this.element,this.ib,a,b,void 0,c,this.bd)};function ye(a,b,c,d){xe.call(this,a,b);this.vb=c?5:0;this.$b=d||void 0}x(ye,xe);ye.prototype.Ic=function(){return this.vb};ye.prototype.dc=function(a,b,c,d){var g=ue(this.element,this.ib,a,b,null,c,10,d,this.$b);if(g&496){var f=ze(g,this.ib);b=ze(g,b);g=ue(this.element,f,a,b,null,c,10,d,this.$b);g&496&&(f=ze(g,f),b=ze(g,b),ue(this.element,f,a,b,null,c,this.vb,d,this.$b))}};function ze(a,b){a&48&&(b^=2);a&192&&(b^=1);return b};function Ae(a,b,c,d){ye.call(this,a,b,c||d);if(c||d)this.vb=65|(d?32:132)}x(Ae,ye);function Be(a,b,c){Qc.call(this,c);this.d=b||de.B();this.xa=a||he}x(Be,Qc);var ge="horizontal",he="vertical";e=Be.prototype;e.Wb=null;e.M=null;e.d=null;e.xa=null;e.j=!0;e.qa=!0;e.Jb=!0;e.s=-1;e.t=null;e.ba=!1;e.Fc=!1;e.ad=!0;e.Z=null;e.q=function(){return this.Wb||this.d.q(this)};e.kb=function(){return this.M||(this.M=new Ad(this.q()))};e.h=function(){this.e=this.d.h(this)};e.o=function(){return this.d.o(this.a())};e.L=function(a){return this.d.L(a)};
e.oa=function(a){this.e=this.d.m(this,a);"none"==a.style.display&&(this.j=!1)};e.u=function(){Be.b.u.call(this);Yc(this,function(a){a.k&&Ce(this,a)},this);var a=this.a();this.d.Ia(this);this.Q(this.j,!0);P(this).f(this,"enter",this.Nb).f(this,"highlight",this.Ob).f(this,"unhighlight",this.Tb).f(this,"open",this.Rc).f(this,"close",this.Mb).f(a,"mousedown",this.ua).f(L(a),"mouseup",this.Mc).f(a,["mousedown","mouseup","mouseover","mouseout","contextmenu"],this.Kc);this.W()&&De(this,!0)};
function De(a,b){var c=P(a),d=a.q();b?c.f(d,"focus",a.ob).f(d,"blur",a.ta).f(a.kb(),"key",a.U):c.F(d,"focus",a.ob).F(d,"blur",a.ta).F(a.kb(),"key",a.U)}e.T=function(){this.la(-1);this.t&&this.t.n(!1);this.ba=!1;Be.b.T.call(this)};e.l=function(){Be.b.l.call(this);this.M&&(this.M.S(),this.M=null);this.d=this.t=this.Z=this.Wb=null};e.Nb=function(){return!0};
e.Ob=function(a){var b=bd(this,a.target);if(-1<b&&b!=this.s){var c=Ee(this);c&&c.P(!1);this.s=b;c=Ee(this);this.ba&&c.setActive(!0);this.ad&&this.t&&c!=this.t&&(R(c,64)?c.n(!0):this.t.n(!1))}b=this.a();null!=a.target.a()&&Q(b,"activedescendant",a.target.a().id)};e.Tb=function(a){a.target==Ee(this)&&(this.s=-1);this.a().removeAttribute("aria-activedescendant")};e.Rc=function(a){(a=a.target)&&a!=this.t&&a.getParent()==this&&(this.t&&this.t.n(!1),this.t=a)};
e.Mb=function(a){a.target==this.t&&(this.t=null)};e.ua=function(a){this.qa&&(this.ba=!0);var b=this.q();b&&kc(b)?b.focus():a.preventDefault()};e.Mc=function(){this.ba=!1};e.Kc=function(a){var b;a:{b=a.target;if(this.Z)for(var c=this.a();b&&b!==c;){var d=b.id;if(d in this.Z){b=this.Z[d];break a}b=b.parentNode}b=null}if(b)switch(a.type){case "mousedown":b.ua(a);break;case "mouseup":b.va(a);break;case "mouseover":b.Sb(a);break;case "mouseout":b.Rb(a);break;case "contextmenu":b.Xa(a)}};e.ob=function(){};
e.ta=function(){this.la(-1);this.ba=!1;this.t&&this.t.n(!1)};e.U=function(a){return this.isEnabled()&&this.j&&(0!=Zc(this)||this.Wb)&&this.V(a)?(a.preventDefault(),a.stopPropagation(),!0):!1};
e.V=function(a){var b=Ee(this);if(b&&"function"==typeof b.U&&b.U(a)||this.t&&this.t!=b&&"function"==typeof this.t.U&&this.t.U(a))return!0;if(a.shiftKey||a.ctrlKey||a.metaKey||a.altKey)return!1;switch(a.keyCode){case 27:if(this.W())this.q().blur();else return!1;break;case 36:Fe(this);break;case 35:Ge(this);break;case 38:if(this.xa==he)He(this);else return!1;break;case 37:if(this.xa==ge)ad(this)?Ie(this):He(this);else return!1;break;case 40:if(this.xa==he)Ie(this);else return!1;break;case 39:if(this.xa==
ge)ad(this)?He(this):Ie(this);else return!1;break;default:return!1}return!0};function Ce(a,b){var c=b.a(),c=c.id||(c.id=Tc(b));a.Z||(a.Z={});a.Z[c]=b}e.Oa=function(a,b){Be.b.Oa.call(this,a,b)};e.Pa=function(a,b,c){a.ab|=2;a.ab|=64;!this.W()&&this.Fc||a.v(32,!1);Hd(a,!1);var d=a.getParent()==this?bd(this,a):-1;Be.b.Pa.call(this,a,b,c);a.k&&this.k&&Ce(this,a);a=d;-1==a&&(a=Zc(this));a==this.s?this.s=Math.min(Zc(this)-1,b):a>this.s&&b<=this.s?this.s++:a<this.s&&b>this.s&&this.s--};
e.removeChild=function(a,b){if(a=v(a)?Wc(this,a):a){var c=bd(this,a);-1!=c&&(c==this.s?(a.P(!1),this.s=-1):c<this.s&&this.s--);var d=a.a();d&&d.id&&this.Z&&(c=this.Z,d=d.id,d in c&&delete c[d])}a=Be.b.removeChild.call(this,a,b);Hd(a,!0);return a};function fe(a,b){if(a.a())throw Error("Component already rendered");a.xa=b}
e.Q=function(a,b){if(b||this.j!=a&&this.dispatchEvent(a?"show":"hide")){this.j=a;var c=this.a();c&&(Ec(c,a),this.W()&&ee(this.q(),this.qa&&this.j),b||this.dispatchEvent(this.j?"aftershow":"afterhide"));return!0}return!1};e.isEnabled=function(){return this.qa};e.X=function(a){this.qa!=a&&this.dispatchEvent(a?"enable":"disable")&&(a?(this.qa=!0,Yc(this,function(a){a.Dc?delete a.Dc:a.X(!0)})):(Yc(this,function(a){a.isEnabled()?a.X(!1):a.Dc=!0}),this.ba=this.qa=!1),this.W()&&ee(this.q(),a&&this.j))};
e.W=function(){return this.Jb};e.da=function(a){a!=this.Jb&&this.k&&De(this,a);this.Jb=a;this.qa&&this.j&&ee(this.q(),a)};e.la=function(a){(a=$c(this,a))?a.P(!0):-1<this.s&&Ee(this).P(!1)};e.P=function(a){this.la(bd(this,a))};function Ee(a){return $c(a,a.s)}function Fe(a){Je(a,function(a,c){return(a+1)%c},Zc(a)-1)}function Ge(a){Je(a,function(a,c){a--;return 0>a?c-1:a},0)}function Ie(a){Je(a,function(a,c){return(a+1)%c},a.s)}function He(a){Je(a,function(a,c){a--;return 0>a?c-1:a},a.s)}
function Je(a,b,c){c=0>c?bd(a,a.t):c;var d=Zc(a);c=b.call(a,c,d);for(var g=0;g<=d;){var f=$c(a,c);if(f&&a.nc(f)){a.la(c);break}g++;c=b.call(a,c,d)}}e.nc=function(a){return a.j&&a.isEnabled()&&R(a,2)};id("goog-menuseparator",function(){return new ce});function Ke(){}x(Ke,od);t(Ke);Ke.prototype.g=function(){return"goog-menuheader"};function Le(a,b,c){T.call(this,a,c||Ke.B(),b);this.v(1,!1);this.v(2,!1);this.v(4,!1);this.v(32,!1);this.ma=1}x(Le,T);id("goog-menuheader",function(){return new Le(null)});function Me(a,b){Be.call(this,he,b||je.B(),a);this.da(!1)}x(Me,Be);e=Me.prototype;e.Eb=!0;e.Gc=!1;e.g=function(){return this.d.g()};e.na=function(a){if(this.d.na(this,a))return!0;for(var b=0,c=Zc(this);b<c;b++){var d=$c(this,b);if("function"==typeof d.na&&d.na(a))return!0}return!1};e.fa=function(a){this.Oa(a,!0)};e.Da=function(a,b){this.Pa(a,b,!0)};e.removeItem=function(a){(a=this.removeChild(a,!0))&&a.S()};e.Sa=function(a){return $c(this,a)};e.Lb=function(){return Zc(this)};
e.qc=function(){var a=[];Yc(this,function(b){a.push(b)});return a};e.Q=function(a,b,c){(b=Me.b.Q.call(this,a,b))&&a&&this.k&&this.Eb&&this.q().focus();this.wc=a&&c&&w(c.clientX)?new K(c.clientX,c.clientY):null;return b};e.Nb=function(a){this.Eb&&this.q().focus();return Me.b.Nb.call(this,a)};e.nc=function(a){return(this.Gc||a.isEnabled())&&a.j&&R(a,2)};e.oa=function(a){var b=this.d,c;c=this.p();c=Wb(c.r,"div",b.g()+"-content",a);for(var d=c.length,g=0;g<d;g++)ie(b,this,c[g]);Me.b.oa.call(this,a)};
e.V=function(a){var b=Me.b.V.call(this,a);b||Yc(this,function(c){!b&&c.Jc&&c.vc==a.keyCode&&(this.isEnabled()&&this.P(c),b=c.U(a))},this);return b};e.la=function(a){Me.b.la.call(this,a);var b=$c(this,a);if(b){a=this.a();var b=b.a(),c=yc(b),d=yc(a),g=Mc(a),f=c.x-d.x-g.left,c=c.y-d.y-g.top,d=a.clientHeight-b.offsetHeight,g=a.scrollLeft,h=a.scrollTop,g=g+Math.min(f,Math.max(f-(a.clientWidth-b.offsetWidth),0)),h=h+Math.min(c,Math.max(c-d,0)),b=new K(g,h);a.scrollLeft=b.x;a.scrollTop=b.y}};function Ne(){}x(Ne,Xd);t(Ne);e=Ne.prototype;e.o=function(a){return Ne.b.o.call(this,a&&a.firstChild)};e.m=function(a,b){var c=Wb(document,"*","goog-menu",b)[0];if(c){Ec(c,!1);L(c).body.appendChild(c);var d=new Me;d.m(c);a.Za(d)}return Ne.b.m.call(this,a,b)};e.jb=function(a,b){return Ne.b.jb.call(this,[this.createCaption(a,b),b.h("div","goog-inline-block "+(this.g()+"-dropdown"),"\u00a0")],b)};e.createCaption=function(a,b){return b.h("div","goog-inline-block "+(this.g()+"-caption"),a)};e.g=function(){return"goog-menu-button"};function Oe(a,b){O.call(this);this.Ya=a||1;this.Na=b||m;this.Hb=ja(this.ed,this);this.Xb=la()}x(Oe,O);e=Oe.prototype;e.enabled=!1;e.w=null;e.setInterval=function(a){this.Ya=a;this.w&&this.enabled?(this.stop(),this.start()):this.w&&this.stop()};e.ed=function(){if(this.enabled){var a=la()-this.Xb;0<a&&a<.8*this.Ya?this.w=this.Na.setTimeout(this.Hb,this.Ya-a):(this.w&&(this.Na.clearTimeout(this.w),this.w=null),this.dispatchEvent(Pe),this.enabled&&(this.w=this.Na.setTimeout(this.Hb,this.Ya),this.Xb=la()))}};
e.start=function(){this.enabled=!0;this.w||(this.w=this.Na.setTimeout(this.Hb,this.Ya),this.Xb=la())};e.stop=function(){this.enabled=!1;this.w&&(this.Na.clearTimeout(this.w),this.w=null)};e.l=function(){Oe.b.l.call(this);this.stop();delete this.Na};var Pe="tick";function Z(a,b,c,d,g){$d.call(this,a,c||Ne.B(),d);this.v(64,!0);this.wb=new Ae(null,5);b&&this.Za(b);this.Wc=null;this.w=new Oe(500);!se&&!te||J("533.17.9")||(this.rb=!0);this.Xc=g||je.B()}x(Z,$d);e=Z.prototype;e.rb=!1;e.dd=!1;e.u=function(){Z.b.u.call(this);Qe(this,!0);this.c&&Re(this,this.c,!0);Q(this.e,"haspopup",!!this.c)};e.T=function(){Z.b.T.call(this);Qe(this,!1);if(this.c){this.n(!1);this.c.T();Re(this,this.c,!1);var a=this.c.a();a&&cc(a)}};
e.l=function(){Z.b.l.call(this);this.c&&(this.c.S(),delete this.c);delete this.cd;this.w.S()};e.ua=function(a){Z.b.ua.call(this,a);S(this,4)&&(this.n(!S(this,64),a),this.c&&(this.c.ba=S(this,64)))};e.va=function(a){Z.b.va.call(this,a);this.c&&!S(this,4)&&(this.c.ba=!1)};e.za=function(){this.setActive(!1);return!0};e.Lc=function(a){this.c&&this.c.j&&!this.na(a.target)&&this.n(!1)};e.na=function(a){return a&&gc(this.a(),a)||this.c&&this.c.na(a)||!1};
e.V=function(a){if(32==a.keyCode){if(a.preventDefault(),"keyup"!=a.type)return!0}else if("key"!=a.type)return!1;if(this.c&&this.c.j){var b=this.c.U(a);return 27==a.keyCode?(this.n(!1),!0):b}return 40==a.keyCode||38==a.keyCode||32==a.keyCode||13==a.keyCode?(this.n(!0,a),!0):!1};e.Pb=function(){this.n(!1)};e.Qc=function(){S(this,4)||this.n(!1)};e.ta=function(a){this.rb||this.n(!1);Z.b.ta.call(this,a)};function Se(a){a.c||a.Za(new Me(a.p(),a.Xc));return a.c||null}
e.Za=function(a){var b=this.c;if(a!=b&&(b&&(this.n(!1),this.k&&Re(this,b,!1),delete this.c),this.k&&Q(this.e,"haspopup",!!a),a)){this.c=a;Vc(a,this);a.Q(!1);var c=this.rb;(a.Eb=c)&&a.da(!0);this.k&&Re(this,a,!0)}return b};e.fa=function(a){Se(this).Oa(a,!0)};e.Da=function(a,b){Se(this).Pa(a,b,!0)};e.removeItem=function(a){(a=Se(this).removeChild(a,!0))&&a.S()};e.Sa=function(a){return this.c?$c(this.c,a):null};e.Lb=function(){return this.c?Zc(this.c):0};
e.Q=function(a,b){var c=Z.b.Q.call(this,a,b);c&&!this.j&&this.n(!1);return c};e.X=function(a){Z.b.X.call(this,a);this.isEnabled()||this.n(!1)};
e.n=function(a,b){Z.b.n.call(this,a);if(this.c&&S(this,64)==a){if(a){if(!this.c.k)if(this.dd){var c;c=this.a();(c=void 0!=c.nextElementSibling?c.nextElementSibling:ec(c.nextSibling))?Xc(this.c,c.parentNode,c):Xc(this.c,this.a().parentNode)}else Xc(this.c,void 0);this.Bc=xc(this.a());this.mc=Dc(this.a());Te(this);this.c.la(!b||40!=b.keyCode&&38!=b.keyCode?-1:0)}else{this.setActive(!1);this.c.ba=!1;if(c=this.a())Q(c,"activedescendant",""),Q(c,"owns","");if(null!=this.zb&&(this.zb=void 0,c=this.c.a())){var d=
"",g;d instanceof Mb?(g=d.height,d=d.width):g="";c.style.width=Ac(d,!0);c.style.height=Ac(g,!0)}}this.c.Q(a,!1,b);this.Ea||(c=P(this),d=a?c.f:c.F,d.call(c,qc(this.p()),"mousedown",this.Lc,!0),this.rb&&d.call(c,this.c,"blur",this.Qc),d.call(c,this.w,Pe,this.$c),a?this.w.start():this.w.stop())}};
function Te(a){if(a.c.k){var b=a.wb;a.wb.element=a.cd||a.a();var c=a.c.a();a.c.j||(c.style.visibility="hidden",Ec(c,!0));!a.zb&&a.wb.Ic&&a.wb.vb&32&&(a.zb=Bc(c));b.dc(c,b.ib^1,a.Wc,a.zb);a.c.j||(Ec(c,!1),c.style.visibility="visible")}}
e.$c=function(){var a=Dc(this.a()),b=xc(this.a()),c;c=this.mc;(c=!(c==a||c&&a&&c.left==a.left&&c.width==a.width&&c.top==a.top&&c.height==a.height))||(c=this.Bc,c=!(c==b||c&&b&&c.top==b.top&&c.right==b.right&&c.bottom==b.bottom&&c.left==b.left));c&&(this.mc=a,this.Bc=b,Te(this))};function Re(a,b,c){var d=P(a);c=c?d.f:d.F;c.call(d,b,"action",a.Pb);c.call(d,b,"close",a.Mb);c.call(d,b,"highlight",a.Ob);c.call(d,b,"unhighlight",a.Tb)}
function Qe(a,b){var c=P(a);(b?c.f:c.F).call(c,a.a(),"keydown",a.Nc)}e.Ob=function(a){(a=a.target.a())&&Ue(this,a)};e.Nc=function(a){R(this,32)&&this.q()&&this.c&&this.c.j&&a.stopPropagation()};e.Tb=function(){if(!Ee(this.c)){var a=this.a();Q(a,"activedescendant","");Q(a,"owns","")}};e.Mb=function(a){if(S(this,64)&&a.target instanceof W){a=a.target;var b=a.a();a.j&&S(a,2)&&null!=b&&Ue(this,b)}};
function Ue(a,b){var c=a.a(),d=nd(b,"activedescendant"),d=L(b).getElementById(d)||b;if(!d.id){var g=Jb.B();d.id=Kb(g)}g="";d&&(g=d.id);Q(c,"activedescendant",g);Q(c,"owns",d.id)}id("goog-menu-button",function(){return new Z(null)});function Ve(a){O.call(this);this.aa=[];We(this,a)}x(Ve,O);e=Ve.prototype;e.ca=null;e.yc=null;e.Lb=function(){return this.aa.length};e.Sa=function(a){return this.aa[a]||null};function We(a,b){b&&(A(b,function(a){Xe(this,a,!1)},a),Ka(a.aa,b))}e.fa=function(a){this.Da(a,this.Lb())};e.Da=function(a,b){a&&(Xe(this,a,!1),La(this.aa,b,0,a))};e.removeItem=function(a){a&&Ia(this.aa,a)&&a==this.ca&&(this.ca=null,this.dispatchEvent("select"))};e.sa=function(){return this.ca};e.qc=function(){return Ja(this.aa)};
e.Ca=function(a){a!=this.ca&&(Xe(this,this.ca,!1),this.ca=a,Xe(this,a,!0));this.dispatchEvent("select")};e.Va=function(){var a=this.ca;return a?Ca(this.aa,a):-1};e.ic=function(a){this.Ca(this.Sa(a))};e.clear=function(){var a=this.aa;if(!u(a))for(var b=a.length-1;0<=b;b--)delete a[b];a.length=0;this.ca=null};e.l=function(){Ve.b.l.call(this);delete this.aa;this.ca=null};function Xe(a,b,c){b&&("function"==typeof a.yc?a.yc(b,c):"function"==typeof b.hc&&b.hc(c))};function $(a,b,c,d,g){Z.call(this,a,b,c,d,g||new je("listbox"));this.Ib=this.J;this.Ub=null;this.cc="listbox"}x($,Z);e=$.prototype;e.i=null;e.u=function(){$.b.u.call(this);Ye(this);Ze(this)};e.oa=function(a){$.b.oa.call(this,a);(a=this.Fa())?(this.Ib=a,Ye(this)):this.sa()||this.ic(0)};e.l=function(){$.b.l.call(this);this.i&&(this.i.S(),this.i=null);this.Ib=null};e.Pb=function(a){this.Ca(a.target);$.b.Pb.call(this,a);a.stopPropagation();this.dispatchEvent("action")};
e.Sc=function(){var a=this.sa();$.b.ea.call(this,a&&a.C());Ye(this)};e.Za=function(a){var b=$.b.Za.call(this,a);a!=b&&(this.i&&this.i.clear(),a&&(this.i?Yc(a,function(a){$e(a);this.i.fa(a)},this):af(this,a)));return b};e.fa=function(a){$e(a);$.b.fa.call(this,a);this.i?this.i.fa(a):af(this,Se(this));bf(this)};e.Da=function(a,b){$e(a);$.b.Da.call(this,a,b);this.i?this.i.Da(a,b):af(this,Se(this))};e.removeItem=function(a){$.b.removeItem.call(this,a);this.i&&this.i.removeItem(a)};
e.Ca=function(a){if(this.i){var b=this.sa();this.i.Ca(a);a!=b&&this.dispatchEvent("change")}};e.ic=function(a){this.i&&this.Ca(this.i.Sa(a))};e.ea=function(a){if(null!=a&&this.i)for(var b=0,c;c=this.i.Sa(b);b++)if(c&&"function"==typeof c.C&&c.C()==a){this.Ca(c);return}this.Ca(null)};e.C=function(){var a=this.sa();return a?a.C():null};e.sa=function(){return this.i?this.i.sa():null};e.Va=function(){return this.i?this.i.Va():-1};
function af(a,b){a.i=new Ve;b&&Yc(b,function(a){$e(a);this.i.fa(a)},a);Ze(a)}function Ze(a){a.i&&P(a).f(a.i,"select",a.Sc)}function Ye(a){var b=a.sa();a.I(b?b.Fa():a.Ib);var c=a.d.o(a.a());c&&a.p().Uc(c)&&(null==a.Ub&&(a.Ub=nd(c,"label")),b=(b=b?b.a():null)?nd(b,"label"):a.Ub,Q(c,"label",b),bf(a))}
function bf(a){var b=a.d;if(b&&(b=b.o(a.a()))){var c=a.e;b.id||(b.id=Kb(Jb.B()));md(b,"option");Q(c,"activedescendant",b.id);a.i&&(c=a.i.qc(),c=Ga(c,function(a){return a instanceof W}),Q(b,"setsize",c),Q(b,"posinset",1+a.i.Va()))}}function $e(a){a.cc=a instanceof W?"option":"separator"}e.n=function(a,b){$.b.n.call(this,a,b);S(this,64)?Se(this).la(this.Va()):bf(this)};id("goog-select",function(){return new $(null)});var cf,df,ef,ff,gf,hf;function jf(a){return 16<a?a.toString(16):0<a?"0"+a.toString(16):"00"}
function kf(){window.dataRed=null;window.dataGreen=null;window.dataBlue=null;var a,b,c=cf.getImageData(0,0,df,ef);if(c){for(b=0;b<ef;b++)for(a=0;a<df;a++){var d=4*(a+b*df);c.data[d+0]=window.red(2*a,2*b)&255;c.data[d+1]=window.green(2*a,2*b)&255;c.data[d+2]=window.blue(2*a,2*b)&255;c.data[d+3]=255}cf.putImageData(c,0,0)}else for(b=0;b<ef;b++)for(a=0;a<df;a++)cf.fillStyle="#"+jf(window.red(2*a,2*b))+jf(window.green(2*a,2*b))+jf(window.blue(2*a,2*b)),cf.fillRect(a,b,1,1)}
function lf(a){window.red=window.mathFunctions[a][0];window.green=window.mathFunctions[a][1];window.blue=window.mathFunctions[a][2];ff.I(window.red.toString());gf.I(window.green.toString());hf.I(window.blue.toString());kf()}
function mf(){try{window.red=eval("("+ff.C()+")")}catch(a){alert("Error in Red function:\n"+a);return}try{window.green=eval("("+gf.C()+")")}catch(b){alert("Error in Green function:\n"+b);return}try{window.blue=eval("("+hf.C()+")")}catch(c){alert("Error in Blue function:\n"+c);return}kf()}
function nf(){var a=new ae("Redraw canvas");Xc(a,Vb(document,"btnRun"));a.$a("Press to redraw canvas with current functions.");wb(a,"action",function(){mf()});ff=new V;Pd(ff);ff.m(Vb(document,"textRed"));gf=new V;Pd(gf);gf.m(Vb(document,"textGreen"));hf=new V;Pd(hf);hf.m(Vb(document,"textBlue"));if(a=document.getElementById("canvas"))df=a.width,ef=a.height,a.getContext&&(cf=a.getContext("2d"),lf(0));for(var a=new $,b=0;b<nameFunctions.length;b++)a.fa(new W(nameFunctions[b]));Xc(a,Vb(document,"selectEquations"));
a.ic(0);wb(a,"action",function(a){a=a.target.Va();lf(a)});document.addEventListener("keypress",function(a){a.ctrlKey&&10===a.charCode&&mf()})}var of=["DrawTweet","start"],pf=m;of[0]in pf||!pf.execScript||pf.execScript("var "+of[0]);for(var qf;of.length&&(qf=of.shift());)of.length||void 0===nf?pf=pf[qf]?pf[qf]:pf[qf]={}:pf[qf]=nf;
</script>
<br />
<fieldset>
<label id="selectEquations"> Select a demo: </label>
<br />
<div id="container" style="overflow: hidden;">
<div style="text-align: left;">
<canvas height="512" id="canvas" width="512"></canvas>
</div>
</div>
<div id="btnRun">
</div>
<div style="overflow: hidden;">
Red:<br />
<textarea id="textRed" wrap="off"></textarea>
</div>
<div style="overflow: hidden;">
Green:<br />
<textarea id="textGreen" wrap="off"></textarea>
</div>
<div style="overflow: hidden;">
Blue:<br />
<textarea id="textBlue" wrap="off"></textarea>
</div>
</fieldset>
<script>
DrawTweet.start();
</script>
<br />
I needed to make smaller the canvas to work in the browser, and some equations required some tweaks when translated from C, due to that the equations are not exactly the same from the original post. For the fun, I also ported an animated version of the "Table cloths" demo to <a href="http://glslsandbox.com/e#19386.1">GLSL</a>. You'll need a decent browser to see it, it needs WebGL. The source code is on my <a href="https://github.com/ex/js/tree/master/src/DrawTweet">github account</a>.<br />
<br />
<br />exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-48330782097088207312014-01-05T17:14:00.001-05:002016-03-25T15:23:31.710-05:00IMO 1979 Problem 3I "solved" another International Mathematical Olympiads problem:
<br />
<pre>There are two circles in the plane. Let a point A be one of the points of intersection of these circles.
Two points begin moving simultaneously with constant speeds from the point A, each point along its own circle.
The two points return to the point A at the same time.
Prove that there is a point P in the plane such that at every moment of time
the distance from the point P to the moving points are equal.
</pre>
The demo below draws all the lines that satisfy the condition while the points are moving,
if there is such a point P then our lines must intersect in that point.<br />
<script>
var g,l=this;function aa(a,b){var c=a.split("."),d=l;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b}function ba(){}
function ca(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function m(a){return void 0!==a}function p(a){return"array"==ca(a)}function da(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length}function q(a){return"string"==typeof a}function ea(a){return"number"==typeof a}function fa(a){return"function"==ca(a)}function ga(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function ha(a){return a[ia]||(a[ia]=++ja)}
var ia="closure_uid_"+(1E9*Math.random()>>>0),ja=0;function ka(a,b,c){return a.call.apply(a.bind,arguments)}function la(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}
function ma(a,b,c){ma=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?ka:la;return ma.apply(null,arguments)}function na(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}}var r=Date.now||function(){return+new Date};
function s(a,b){function c(){}c.prototype=b.prototype;a.f=b.prototype;a.prototype=new c;a.Yb=function(a,c,f){var h=Array.prototype.slice.call(arguments,2);return b.prototype[c].apply(a,h)}};var oa;function pa(a){if(!qa.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(ra,"&"));-1!=a.indexOf("<")&&(a=a.replace(ua,"<"));-1!=a.indexOf(">")&&(a=a.replace(va,">"));-1!=a.indexOf('"')&&(a=a.replace(wa,"""));-1!=a.indexOf("'")&&(a=a.replace(xa,"'"));return a}var ra=/&/g,ua=/</g,va=/>/g,wa=/"/g,xa=/'/g,qa=/[&<>"']/;function ya(a,b){return a<b?-1:a>b?1:0};var za,Aa,Ba,Ca,t,Da;function Ea(){return l.navigator?l.navigator.userAgent:null}Ca=Ba=Aa=za=!1;var Fa;if(Fa=Ea()){var Ga=l.navigator;za=0==Fa.lastIndexOf("Opera",0);Aa=!za&&(-1!=Fa.indexOf("MSIE")||-1!=Fa.indexOf("Trident"));Ba=!za&&-1!=Fa.indexOf("WebKit");Ca=!za&&!Ba&&!Aa&&"Gecko"==Ga.product}var Ha=za,u=Aa,v=Ca,w=Ba,Ia,Ja=l.navigator;Ia=Ja&&Ja.platform||"";t=-1!=Ia.indexOf("Mac");Da=-1!=Ia.indexOf("Win");var Ka=-1!=Ia.indexOf("Linux");
function La(){var a=l.document;return a?a.documentMode:void 0}var Ma;a:{var Na="",Oa;if(Ha&&l.opera)var Pa=l.opera.version,Na="function"==typeof Pa?Pa():Pa;else if(v?Oa=/rv\:([^\);]+)(\)|;)/:u?Oa=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:w&&(Oa=/WebKit\/(\S+)/),Oa)var Qa=Oa.exec(Ea()),Na=Qa?Qa[1]:"";if(u){var Ra=La();if(Ra>parseFloat(Na)){Ma=String(Ra);break a}}Ma=Na}var Sa={};
function x(a){var b;if(!(b=Sa[a])){b=0;for(var c=String(Ma).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),d=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var h=c[f]||"",k=d[f]||"",n=RegExp("(\\d*)(\\D*)","g"),y=RegExp("(\\d*)(\\D*)","g");do{var sa=n.exec(h)||["","",""],ta=y.exec(k)||["","",""];if(0==sa[0].length&&0==ta[0].length)break;b=ya(0==sa[1].length?0:parseInt(sa[1],10),0==ta[1].length?0:parseInt(ta[1],10))||ya(0==sa[2].length,0==
ta[2].length)||ya(sa[2],ta[2])}while(0==b)}b=Sa[a]=0<=b}return b}function z(a){return u&&Ta>=a}var Ua=l.document,Ta=Ua&&u?La()||("CSS1Compat"==Ua.compatMode?parseInt(Ma,10):5):void 0;var Va,Wa=!u||z(9);!v&&!u||u&&z(9)||v&&x("1.9.1");u&&x("9");var A=Array.prototype,Xa=A.indexOf?function(a,b,c){return A.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(q(a))return q(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},Ya=A.forEach?function(a,b,c){A.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=q(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},Za=A.filter?function(a,b,c){return A.filter.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=[],f=0,h=q(a)?
a.split(""):a,k=0;k<d;k++)if(k in h){var n=h[k];b.call(c,n,k,a)&&(e[f++]=n)}return e};function $a(a,b){var c=Xa(a,b),d;(d=0<=c)&&A.splice.call(a,c,1);return d}function ab(a){var b=a.length;if(0<b){for(var c=Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]}function bb(a,b,c){return 2>=arguments.length?A.slice.call(a,b):A.slice.call(a,b,c)};function cb(a,b){var c;c=a.className;c=q(c)&&c.match(/\S+/g)||[];for(var d=bb(arguments,1),e=c.length+d.length,f=c,h=0;h<d.length;h++)0<=Xa(f,d[h])||f.push(d[h]);a.className=c.join(" ");return c.length==e};function db(a,b,c){return Math.min(Math.max(a,b),c)};function B(a,b){this.x=m(a)?a:0;this.y=m(b)?b:0}B.prototype.toString=function(){return"("+this.x+", "+this.y+")"};B.prototype.ceil=function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this};B.prototype.round=function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this};function eb(a,b){for(var c in a)b.call(void 0,a[c],c,a)}function fb(){var a=gb,b;for(b in a)return!1;return!0}var hb="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" ");function ib(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<hb.length;f++)c=hb[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};function C(a){return a?new jb(D(a)):Va||(Va=new jb)}function kb(a,b){eb(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in lb?a.setAttribute(lb[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})}var lb={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",valign:"vAlign",width:"width"};
function mb(a,b,c){function d(c){c&&b.appendChild(q(c)?a.createTextNode(c):c)}for(var e=2;e<c.length;e++){var f=c[e];if(!da(f)||ga(f)&&0<f.nodeType)d(f);else{var h;a:{if(f&&"number"==typeof f.length){if(ga(f)){h="function"==typeof f.item||"string"==typeof f.item;break a}if(fa(f)){h="function"==typeof f.item;break a}}h=!1}Ya(h?ab(f):f,d)}}}function nb(a){a&&a.parentNode&&a.parentNode.removeChild(a)}
function ob(a,b){if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}function D(a){return 9==a.nodeType?a:a.ownerDocument||a.document}function jb(a){this.n=a||l.document||document}g=jb.prototype;g.bb=C;g.d=function(a){return q(a)?this.n.getElementById(a):a};
g.pa=function(a,b,c){var d=this.n,e=arguments,f=e[0],h=e[1];if(!Wa&&h&&(h.name||h.type)){f=["<",f];h.name&&f.push(' name="',pa(h.name),'"');if(h.type){f.push(' type="',pa(h.type),'"');var k={};ib(k,h);delete k.type;h=k}f.push(">");f=f.join("")}f=d.createElement(f);h&&(q(h)?f.className=h:p(h)?cb.apply(null,[f].concat(h)):kb(f,h));2<e.length&&mb(d,f,e);return f};g.createElement=function(a){return this.n.createElement(a)};g.createTextNode=function(a){return this.n.createTextNode(String(a))};
function pb(a){var b=a.n;a=w||"CSS1Compat"!=b.compatMode?b.body||b.documentElement:b.documentElement;b=b.parentWindow||b.defaultView;return u&&x("10")&&b.pageYOffset!=a.scrollTop?new B(a.scrollLeft,a.scrollTop):new B(b.pageXOffset||a.scrollLeft,b.pageYOffset||a.scrollTop)}g.appendChild=function(a,b){a.appendChild(b)};g.contains=ob;function qb(a,b,c){da(c)&&(c=c.join(" "));var d="aria-"+b;""===c||void 0==c?(oa||(oa={atomic:!1,autocomplete:"none",dropeffect:"none",haspopup:!1,live:"off",multiline:!1,multiselectable:!1,orientation:"vertical",readonly:!1,relevant:"additions text",required:!1,sort:"none",busy:!1,disabled:!1,hidden:!1,invalid:"false"}),c=oa,b in c?a.setAttribute(d,c[b]):a.removeAttribute(d)):a.setAttribute(d,c)};var rb="closure_listenable_"+(1E6*Math.random()|0);function sb(a){try{return!(!a||!a[rb])}catch(b){return!1}}var tb=0;function ub(a,b,c,d,e){this.Z=a;this.Ta=null;this.src=b;this.type=c;this.capture=!!d;this.Ia=e;this.key=++tb;this.ka=this.Ba=!1}function vb(a){a.ka=!0;a.Z=null;a.Ta=null;a.src=null;a.Ia=null};function E(a){this.src=a;this.m={};this.ya=0}E.prototype.add=function(a,b,c,d,e){var f=this.m[a];f||(f=this.m[a]=[],this.ya++);var h=wb(f,b,d,e);-1<h?(a=f[h],c||(a.Ba=!1)):(a=new ub(b,this.src,a,!!d,e),a.Ba=c,f.push(a));return a};E.prototype.remove=function(a,b,c,d){if(!(a in this.m))return!1;var e=this.m[a];b=wb(e,b,c,d);return-1<b?(vb(e[b]),A.splice.call(e,b,1),0==e.length&&(delete this.m[a],this.ya--),!0):!1};
function xb(a,b){var c=b.type;if(!(c in a.m))return!1;var d=$a(a.m[c],b);d&&(vb(b),0==a.m[c].length&&(delete a.m[c],a.ya--));return d}E.prototype.ja=function(a){var b=0,c;for(c in this.m)if(!a||c==a){for(var d=this.m[c],e=0;e<d.length;e++)++b,vb(d[e]);delete this.m[c];this.ya--}return b};E.prototype.sa=function(a,b,c,d){a=this.m[a];var e=-1;a&&(e=wb(a,b,c,d));return-1<e?a[e]:null};
function wb(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.ka&&f.Z==b&&f.capture==!!c&&f.Ia==d)return e}return-1};function yb(a){yb[" "](a);return a}yb[" "]=ba;function F(){0!=zb&&(Ab[ha(this)]=this)}var zb=0,Ab={};F.prototype.Ya=!1;F.prototype.p=function(){if(!this.Ya&&(this.Ya=!0,this.c(),0!=zb)){var a=ha(this);delete Ab[a]}};F.prototype.c=function(){if(this.wa)for(;this.wa.length;)this.wa.shift()()};function Bb(a){a&&"function"==typeof a.p&&a.p()}function Cb(a){for(var b=0,c=arguments.length;b<c;++b){var d=arguments[b];da(d)?Cb.apply(null,d):Bb(d)}};function G(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.ia=!1;this.zb=!0}G.prototype.c=function(){};G.prototype.p=function(){};G.prototype.preventDefault=function(){this.defaultPrevented=!0;this.zb=!1};function Db(a){a.preventDefault()};var Eb=!u||z(9),Fb=!u||z(9),Gb=u&&!x("9");!w||x("528");v&&x("1.9b")||u&&x("8")||Ha&&x("9.5")||w&&x("528");v&&!x("8")||u&&x("9");function H(a,b){G.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.A=this.state=null;a&&Hb(this,a,b)}s(H,G);var Ib=[1,4,2];
function Hb(a,b,c){var d=a.type=b.type;a.target=b.target||b.srcElement;a.currentTarget=c;if(c=b.relatedTarget){if(v){var e;a:{try{yb(c.nodeName);e=!0;break a}catch(f){}e=!1}e||(c=null)}}else"mouseover"==d?c=b.fromElement:"mouseout"==d&&(c=b.toElement);a.relatedTarget=c;a.offsetX=w||void 0!==b.offsetX?b.offsetX:b.layerX;a.offsetY=w||void 0!==b.offsetY?b.offsetY:b.layerY;a.clientX=void 0!==b.clientX?b.clientX:b.pageX;a.clientY=void 0!==b.clientY?b.clientY:b.pageY;a.screenX=b.screenX||0;a.screenY=b.screenY||
0;a.button=b.button;a.keyCode=b.keyCode||0;a.charCode=b.charCode||("keypress"==d?b.keyCode:0);a.ctrlKey=b.ctrlKey;a.altKey=b.altKey;a.shiftKey=b.shiftKey;a.metaKey=b.metaKey;a.state=b.state;a.A=b;b.defaultPrevented&&a.preventDefault()}H.prototype.preventDefault=function(){H.f.preventDefault.call(this);var a=this.A;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,Gb)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};H.prototype.Kb=function(){return this.A};
H.prototype.c=function(){};var Jb="closure_lm_"+(1E6*Math.random()|0),Kb={},Lb=0;function I(a,b,c,d,e){if(p(b)){for(var f=0;f<b.length;f++)I(a,b[f],c,d,e);return null}c=Mb(c);if(sb(a))a=a.g(b,c,d,e);else{if(!b)throw Error("Invalid event type");var f=!!d,h=Nb(a);h||(a[Jb]=h=new E(a));c=h.add(b,c,!1,d,e);c.Ta||(d=Ob(),c.Ta=d,d.src=a,d.Z=c,a.addEventListener?a.addEventListener(b,d,f):a.attachEvent(b in Kb?Kb[b]:Kb[b]="on"+b,d),Lb++);a=c}return a}
function Ob(){var a=Pb,b=Fb?function(c){return a.call(b.src,b.Z,c)}:function(c){c=a.call(b.src,b.Z,c);if(!c)return c};return b}function Qb(a,b,c,d,e){if(p(b))for(var f=0;f<b.length;f++)Qb(a,b[f],c,d,e);else c=Mb(c),sb(a)?a.Aa(b,c,d,e):a&&(a=Nb(a))&&(b=a.sa(b,c,!!d,e))&&J(b)}
function J(a){if(ea(a)||!a||a.ka)return!1;var b=a.src;if(sb(b))return xb(b.N,a);var c=a.type,d=a.Ta;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent&&b.detachEvent(c in Kb?Kb[c]:Kb[c]="on"+c,d);Lb--;(c=Nb(b))?(xb(c,a),0==c.ya&&(c.src=null,b[Jb]=null)):vb(a);return!0}function Rb(a,b,c,d){var e=1;if(a=Nb(a))if(b=a.m[b])for(b=ab(b),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.ka&&(e&=!1!==Sb(f,d))}return Boolean(e)}
function Sb(a,b){var c=a.Z,d=a.Ia||a.src;a.Ba&&J(a);return c.call(d,b)}
function Pb(a,b){if(a.ka)return!0;if(!Fb){var c;if(!(c=b))a:{c=["window","event"];for(var d=l,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break a}c=d}e=c;c=new H(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(h){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,k=e.length-1;!c.ia&&0<=k;k--)c.currentTarget=e[k],d&=Rb(e[k],f,!0,c);for(k=0;!c.ia&&k<e.length;k++)c.currentTarget=
e[k],d&=Rb(e[k],f,!1,c)}return d}return Sb(a,new H(b,this))}function Nb(a){a=a[Jb];return a instanceof E?a:null}var Tb="__closure_events_fn_"+(1E9*Math.random()>>>0);function Mb(a){return fa(a)?a:a[Tb]||(a[Tb]=function(b){return a.handleEvent(b)})};function K(){F.call(this);this.N=new E(this);this.Fb=this}s(K,F);K.prototype[rb]=!0;g=K.prototype;g.Ra=null;g.jb=function(a){this.Ra=a};g.addEventListener=function(a,b,c,d){I(this,a,b,c,d)};g.removeEventListener=function(a,b,c,d){Qb(this,a,b,c,d)};
g.dispatchEvent=function(a){var b,c=this.Ra;if(c)for(b=[];c;c=c.Ra)b.push(c);var c=this.Fb,d=a.type||a;if(q(a))a=new G(a,c);else if(a instanceof G)a.target=a.target||c;else{var e=a;a=new G(d,c);ib(a,e)}var e=!0,f;if(b)for(var h=b.length-1;!a.ia&&0<=h;h--)f=a.currentTarget=b[h],e=Ub(f,d,!0,a)&&e;a.ia||(f=a.currentTarget=c,e=Ub(f,d,!0,a)&&e,a.ia||(e=Ub(f,d,!1,a)&&e));if(b)for(h=0;!a.ia&&h<b.length;h++)f=a.currentTarget=b[h],e=Ub(f,d,!1,a)&&e;return e};
g.c=function(){K.f.c.call(this);this.N&&this.N.ja(void 0);this.Ra=null};g.g=function(a,b,c,d){return this.N.add(String(a),b,!1,c,d)};g.Aa=function(a,b,c,d){return this.N.remove(String(a),b,c,d)};function Ub(a,b,c,d){b=a.N.m[String(b)];if(!b)return!0;b=ab(b);for(var e=!0,f=0;f<b.length;++f){var h=b[f];if(h&&!h.ka&&h.capture==c){var k=h.Z,n=h.Ia||h.src;h.Ba&&xb(a.N,h);e=!1!==k.call(n,d)&&e}}return e&&!1!=d.zb}g.sa=function(a,b,c,d){return this.N.sa(String(a),b,c,d)};function Vb(a,b){K.call(this);this.da=a||1;this.na=b||l;this.Wa=ma(this.Xb,this);this.fb=r()}s(Vb,K);g=Vb.prototype;g.enabled=!1;g.J=null;g.Xb=function(){if(this.enabled){var a=r()-this.fb;0<a&&a<0.8*this.da?this.J=this.na.setTimeout(this.Wa,this.da-a):(this.J&&(this.na.clearTimeout(this.J),this.J=null),this.dispatchEvent(Wb),this.enabled&&(this.J=this.na.setTimeout(this.Wa,this.da),this.fb=r()))}};g.start=function(){this.enabled=!0;this.J||(this.J=this.na.setTimeout(this.Wa,this.da),this.fb=r())};
g.stop=function(){this.enabled=!1;this.J&&(this.na.clearTimeout(this.J),this.J=null)};g.c=function(){Vb.f.c.call(this);this.stop();delete this.na};var Wb="tick";function Xb(a,b,c){F.call(this);this.gb=a;this.da=b||0;this.ua=c;this.Gb=ma(this.Hb,this)}s(Xb,F);g=Xb.prototype;g.K=0;g.c=function(){Xb.f.c.call(this);this.stop();delete this.gb;delete this.ua};g.start=function(a){this.stop();var b=this.Gb;a=m(a)?a:this.da;if(!fa(b))if(b&&"function"==typeof b.handleEvent)b=ma(b.handleEvent,b);else throw Error("Invalid listener argument");this.K=2147483647<a?-1:l.setTimeout(b,a||0)};g.stop=function(){0!=this.K&&l.clearTimeout(this.K);this.K=0};
g.Hb=function(){this.K=0;this.gb&&this.gb.call(this.ua)};var gb={},Yb=null;function Zb(a){a=ha(a);delete gb[a];fb()&&Yb&&Yb.stop()}function $b(){Yb||(Yb=new Xb(function(){ac()},20));var a=Yb;0!=a.K||a.start()}function ac(){var a=r();eb(gb,function(b){bc(b,a)});fb()||$b()};function L(){K.call(this);this.h=M;this.qa=this.startTime=null}s(L,K);var M=0;L.prototype.Qa=function(){this.o("begin")};L.prototype.ha=function(){this.o("end")};L.prototype.o=function(a){this.dispatchEvent(a)};function cc(a,b,c,d){L.call(this);if(!p(a)||!p(b))throw Error("Start and end parameters must be arrays");if(a.length!=b.length)throw Error("Start and end points must be the same length");this.xa=a;this.Jb=b;this.duration=c;this.lb=d;this.coords=[];this.aa=!1}s(cc,L);g=cc.prototype;g.M=function(a){this.aa=a};g.v=0;
g.play=function(a){if(a||this.h==M)this.v=0,this.coords=this.xa;else if(1==this.h)return!1;Zb(this);this.startTime=a=r();-1==this.h&&(this.startTime-=this.duration*this.v);this.qa=this.startTime+this.duration;this.v||this.Qa();this.o("play");-1==this.h&&this.o("resume");this.h=1;var b=ha(this);b in gb||(gb[b]=this);$b();bc(this,a);return!0};g.stop=function(a){Zb(this);this.h=M;a&&(this.v=1);dc(this,this.v);this.o("stop");this.ha()};g.c=function(){this.h==M||this.stop(!1);this.o("destroy");cc.f.c.call(this)};
function bc(a,b){a.v=(b-a.startTime)/(a.qa-a.startTime);1<=a.v&&(a.v=1);dc(a,a.v);1==a.v?(a.h=M,Zb(a),a.o("finish"),a.ha()):1==a.h&&a.hb()}function dc(a,b){fa(a.lb)&&(b=a.lb(b));a.coords=Array(a.xa.length);for(var c=0;c<a.xa.length;c++)a.coords[c]=(a.Jb[c]-a.xa[c])*b+a.xa[c]}g.hb=function(){this.o("animate")};g.o=function(a){this.dispatchEvent(new ec(a,this))};
function ec(a,b){G.call(this,a);this.coords=b.coords;this.x=b.coords[0];this.y=b.coords[1];this.z=b.coords[2];this.duration=b.duration;this.v=b.v;this.state=b.h}s(ec,G);function N(a,b,c,d){this.top=a;this.right=b;this.bottom=c;this.left=d}N.prototype.toString=function(){return"("+this.top+"t, "+this.right+"r, "+this.bottom+"b, "+this.left+"l)"};N.prototype.contains=function(a){return this&&a?a instanceof N?a.left>=this.left&&a.right<=this.right&&a.top>=this.top&&a.bottom<=this.bottom:a.x>=this.left&&a.x<=this.right&&a.y>=this.top&&a.y<=this.bottom:!1};
N.prototype.ceil=function(){this.top=Math.ceil(this.top);this.right=Math.ceil(this.right);this.bottom=Math.ceil(this.bottom);this.left=Math.ceil(this.left);return this};N.prototype.round=function(){this.top=Math.round(this.top);this.right=Math.round(this.right);this.bottom=Math.round(this.bottom);this.left=Math.round(this.left);return this};function fc(a,b,c,d){this.left=a;this.top=b;this.width=c;this.height=d}fc.prototype.toString=function(){return"("+this.left+", "+this.top+" - "+this.width+"w x "+this.height+"h)"};fc.prototype.contains=function(a){return a instanceof fc?this.left<=a.left&&this.left+this.width>=a.left+a.width&&this.top<=a.top&&this.top+this.height>=a.top+a.height:a.x>=this.left&&a.x<=this.left+this.width&&a.y>=this.top&&a.y<=this.top+this.height};
fc.prototype.ceil=function(){this.left=Math.ceil(this.left);this.top=Math.ceil(this.top);this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};fc.prototype.round=function(){this.left=Math.round(this.left);this.top=Math.round(this.top);this.width=Math.round(this.width);this.height=Math.round(this.height);return this};function gc(a,b){var c=D(a);return c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(a,null))?c[b]||c.getPropertyValue(b)||"":""}function O(a,b){return gc(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style&&a.style[b]}
function hc(a){var b;try{b=a.getBoundingClientRect()}catch(c){return{left:0,top:0,right:0,bottom:0}}u&&a.ownerDocument.body&&(a=a.ownerDocument,b.left-=a.documentElement.clientLeft+a.body.clientLeft,b.top-=a.documentElement.clientTop+a.body.clientTop);return b}
function ic(a){if(u&&!z(8))return a.offsetParent;var b=D(a),c=O(a,"position"),d="fixed"==c||"absolute"==c;for(a=a.parentNode;a&&a!=b;a=a.parentNode)if(c=O(a,"position"),d=d&&"static"==c&&a!=b.documentElement&&a!=b.body,!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||"fixed"==c||"absolute"==c||"relative"==c))return a;return null}
function jc(a){if(1==a.nodeType){var b;if(a.getBoundingClientRect)b=hc(a),b=new B(b.left,b.top);else{b=pb(C(a));var c,d=D(a),e=O(a,"position"),f=v&&d.getBoxObjectFor&&!a.getBoundingClientRect&&"absolute"==e&&(c=d.getBoxObjectFor(a))&&(0>c.screenX||0>c.screenY),h=new B(0,0),k;c=d?D(d):document;(k=!u)||(k=z(9))||(k="CSS1Compat"==C(c).n.compatMode);k=k?c.documentElement:c.body;if(a!=k)if(a.getBoundingClientRect)c=hc(a),d=pb(C(d)),h.x=c.left+d.x,h.y=c.top+d.y;else if(d.getBoxObjectFor&&!f)c=d.getBoxObjectFor(a),
d=d.getBoxObjectFor(k),h.x=c.screenX-d.screenX,h.y=c.screenY-d.screenY;else{f=a;do{h.x+=f.offsetLeft;h.y+=f.offsetTop;f!=a&&(h.x+=f.clientLeft||0,h.y+=f.clientTop||0);if(w&&"fixed"==O(f,"position")){h.x+=d.body.scrollLeft;h.y+=d.body.scrollTop;break}f=f.offsetParent}while(f&&f!=a);if(Ha||w&&"absolute"==e)h.y-=d.body.offsetTop;for(f=a;(f=ic(f))&&f!=d.body&&f!=k;)h.x-=f.scrollLeft,Ha&&"TR"==f.tagName||(h.y-=f.scrollTop)}b=new B(h.x-b.x,h.y-b.y)}if(v&&!x(12)){var n;u?n="-ms-transform":w?n="-webkit-transform":
Ha?n="-o-transform":v&&(n="-moz-transform");var y;n&&(y=O(a,n));y||(y=O(a,"transform"));a=y?(a=y.match(kc))?new B(parseFloat(a[1]),parseFloat(a[2])):new B(0,0):new B(0,0);a=new B(b.x+a.x,b.y+a.y)}else a=b;return a}n=fa(a.Kb);y=a;a.targetTouches?y=a.targetTouches[0]:n&&a.A.targetTouches&&(y=a.A.targetTouches[0]);return new B(y.clientX,y.clientY)}function lc(a){return"rtl"==O(a,"direction")}var mc={thin:2,medium:4,thick:6};
function nc(a,b){if("none"==(a.currentStyle?a.currentStyle[b+"Style"]:null))return 0;var c=a.currentStyle?a.currentStyle[b+"Width"]:null,d;if(c in mc)d=mc[c];else if(/^\d+px?$/.test(c))d=parseInt(c,10);else{d=a.style.left;var e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;a.style.left=c;c=a.style.pixelLeft;a.style.left=d;a.runtimeStyle.left=e;d=c}return d}
function oc(a){if(u&&!z(9)){var b=nc(a,"borderLeft"),c=nc(a,"borderRight"),d=nc(a,"borderTop");a=nc(a,"borderBottom");return new N(d,c,a,b)}b=gc(a,"borderLeftWidth");c=gc(a,"borderRightWidth");d=gc(a,"borderTopWidth");a=gc(a,"borderBottomWidth");return new N(parseFloat(d),parseFloat(c),parseFloat(a),parseFloat(b))}var kc=/matrix\([0-9\.\-]+, [0-9\.\-]+, [0-9\.\-]+, [0-9\.\-]+, ([0-9\.\-]+)p?x?, ([0-9\.\-]+)p?x?\)/;function pc(a){var b=a.offsetLeft,c=a.offsetParent;c||"fixed"!=O(a,"position")||(c=D(a).documentElement);if(!c)return b;if(v)var d=oc(c),b=b+d.left;else z(8)&&(d=oc(c),b-=d.left);return lc(c)?c.clientWidth-(b+a.offsetWidth):b};function P(a,b,c,d,e){cc.call(this,b,c,d,e);this.element=a}s(P,cc);g=P.prototype;g.oa=ba;g.ea=function(){m(this.I)||(this.I=lc(this.element));return this.I};g.hb=function(){this.oa();P.f.hb.call(this)};g.ha=function(){this.oa();P.f.ha.call(this)};g.Qa=function(){this.oa();P.f.Qa.call(this)};function qc(a,b,c,d,e){if(2!=b.length||2!=c.length)throw Error("Start and end points must be 2D");P.apply(this,arguments)}s(qc,P);
qc.prototype.oa=function(){var a=this.aa&&this.ea()?"right":"left";this.element.style[a]=Math.round(this.coords[0])+"px";this.element.style.top=Math.round(this.coords[1])+"px"};function rc(a,b,c,d,e){P.call(this,a,[b],[c],d,e)}s(rc,P);rc.prototype.oa=function(){this.element.style.width=Math.round(this.coords[0])+"px"};function sc(a,b,c,d,e){P.call(this,a,[b],[c],d,e)}s(sc,P);sc.prototype.oa=function(){this.element.style.height=Math.round(this.coords[0])+"px"};function tc(a,b,c,d,e){if(!(u||w&&x("525")))return!0;if(t&&e)return uc(a);if(e&&!d)return!1;ea(b)&&(b=vc(b));if(!c&&(17==b||18==b||t&&91==b))return!1;if(w&&d&&c)switch(a){case 220:case 219:case 221:case 192:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:return!1}if(u&&d&&b==a)return!1;switch(a){case 13:return!(u&&z(9));case 27:return!w}return uc(a)}
function uc(a){if(48<=a&&57>=a||96<=a&&106>=a||65<=a&&90>=a||w&&0==a)return!0;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0;default:return!1}}function vc(a){if(v)a=wc(a);else if(t&&w)a:switch(a){case 93:a=91;break a}return a}
function wc(a){switch(a){case 61:return 187;case 59:return 186;case 173:return 189;case 224:return 91;case 0:return 224;default:return a}};function xc(a,b){K.call(this);a&&(this.La&&this.detach(),this.e=a,this.Ka=I(this.e,"keypress",this,b),this.eb=I(this.e,"keydown",this.cb,b,this),this.La=I(this.e,"keyup",this.Mb,b,this))}s(xc,K);g=xc.prototype;g.e=null;g.Ka=null;g.eb=null;g.La=null;g.r=-1;g.R=-1;g.Va=!1;
var yc={3:13,12:144,63232:38,63233:40,63234:37,63235:39,63236:112,63237:113,63238:114,63239:115,63240:116,63241:117,63242:118,63243:119,63244:120,63245:121,63246:122,63247:123,63248:44,63272:46,63273:36,63275:35,63276:33,63277:34,63289:144,63302:45},zc={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},Ac=u||w&&x("525"),Bc=t&&v;g=xc.prototype;
g.cb=function(a){w&&(17==this.r&&!a.ctrlKey||18==this.r&&!a.altKey||t&&91==this.r&&!a.metaKey)&&(this.R=this.r=-1);-1==this.r&&(a.ctrlKey&&17!=a.keyCode?this.r=17:a.altKey&&18!=a.keyCode?this.r=18:a.metaKey&&91!=a.keyCode&&(this.r=91));Ac&&!tc(a.keyCode,this.r,a.shiftKey,a.ctrlKey,a.altKey)?this.handleEvent(a):(this.R=vc(a.keyCode),Bc&&(this.Va=a.altKey))};g.Mb=function(a){this.R=this.r=-1;this.Va=a.altKey};
g.handleEvent=function(a){var b=a.A,c,d,e=b.altKey;u&&"keypress"==a.type?(c=this.R,d=13!=c&&27!=c?b.keyCode:0):w&&"keypress"==a.type?(c=this.R,d=0<=b.charCode&&63232>b.charCode&&uc(c)?b.charCode:0):Ha?(c=this.R,d=uc(c)?b.keyCode:0):(c=b.keyCode||this.R,d=b.charCode||0,Bc&&(e=this.Va),t&&63==d&&224==c&&(c=191));var f=c=vc(c),h=b.keyIdentifier;c?63232<=c&&c in yc?f=yc[c]:25==c&&a.shiftKey&&(f=9):h&&h in zc&&(f=zc[h]);a=f==this.r;this.r=f;b=new Cc(f,d,a,b);b.altKey=e;this.dispatchEvent(b)};g.d=function(){return this.e};
g.detach=function(){this.Ka&&(J(this.Ka),J(this.eb),J(this.La),this.La=this.eb=this.Ka=null);this.e=null;this.R=this.r=-1};g.c=function(){xc.f.c.call(this);this.detach()};function Cc(a,b,c,d){H.call(this,d);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c}s(Cc,H);function Q(){}Q.Lb=function(){return Q.tb?Q.tb:Q.tb=new Q};Q.prototype.Ub=0;function Dc(a){F.call(this);this.ua=a;this.Ma={}}s(Dc,F);var Ec=[];g=Dc.prototype;g.g=function(a,b,c,d){p(b)||(Ec[0]=b,b=Ec);for(var e=0;e<b.length;e++){var f=I(a,b[e],c||this.handleEvent,d||!1,this.ua||this);if(!f)break;this.Ma[f.key]=f}return this};g.Aa=function(a,b,c,d,e){if(p(b))for(var f=0;f<b.length;f++)this.Aa(a,b[f],c,d,e);else c=c||this.handleEvent,e=e||this.ua||this,c=Mb(c),d=!!d,b=sb(a)?a.sa(b,c,d,e):a?(a=Nb(a))?a.sa(b,c,d,e):null:null,b&&(J(b),delete this.Ma[b.key]);return this};
g.ja=function(){eb(this.Ma,J);this.Ma={}};g.c=function(){Dc.f.c.call(this);this.ja()};g.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};function R(a){K.call(this);this.ca=a||C();this.I=Fc}s(R,K);R.prototype.Qb=Q.Lb();var Fc=null;g=R.prototype;g.K=null;g.Y=!1;g.e=null;g.I=null;g.Sa=null;g.Da=null;g.Ca=null;g.Eb=!1;g.d=function(){return this.e};g.W=function(){this.X||(this.X=new Dc(this));return this.X};g.jb=function(a){if(this.Sa&&this.Sa!=a)throw Error("Method not supported");R.f.jb.call(this,a)};g.bb=function(){return this.ca};g.pa=function(){this.e=this.ca.createElement("div")};
function Gc(a,b){if(a.Y)throw Error("Component already rendered");if(b){a.Eb=!0;var c=D(b);a.ca&&a.ca.n==c||(a.ca=C(b));a.Fa(b);a.Ha()}else throw Error("Invalid element to decorate");}g.Fa=function(a){this.e=a};g.Ha=function(){this.Y=!0;Hc(this,function(a){!a.Y&&a.d()&&a.Ha()})};g.ra=function(){Hc(this,function(a){a.Y&&a.ra()});this.X&&this.X.ja();this.Y=!1};
g.c=function(){this.Y&&this.ra();this.X&&(this.X.p(),delete this.X);Hc(this,function(a){a.p()});!this.Eb&&this.e&&nb(this.e);this.Sa=this.e=this.Ca=this.Da=null;R.f.c.call(this)};g.ea=function(){null==this.I&&(this.I=lc(this.Y?this.e:this.ca.n.body));return this.I};function Hc(a,b){a.Da&&Ya(a.Da,b,void 0)}
g.removeChild=function(a,b){if(a){var c=q(a)?a:a.K||(a.K=":"+(a.Qb.Ub++).toString(36)),d;this.Ca&&c?(d=this.Ca,d=(c in d?d[c]:void 0)||null):d=null;a=d;if(c&&a){d=this.Ca;c in d&&delete d[c];$a(this.Da,a);b&&(a.ra(),a.e&&nb(a.e));c=a;if(null==c)throw Error("Unable to set parent component");c.Sa=null;R.f.jb.call(c,null)}}if(!a)throw Error("Child is not in parent component");return a};function Ic(a,b){K.call(this);var c=this.e=a,c=ga(c)&&1==c.nodeType?this.e:this.e?this.e.body:null;this.Tb=!!c&&lc(c);this.vb=I(this.e,v?"DOMMouseScroll":"mousewheel",this,b)}s(Ic,K);
Ic.prototype.handleEvent=function(a){var b=0,c=0,d=0;a=a.A;if("mousewheel"==a.type){c=1;if(u||w&&(Da||x("532.0")))c=40;d=Jc(-a.wheelDelta,c);m(a.wheelDeltaX)?(b=Jc(-a.wheelDeltaX,c),c=Jc(-a.wheelDeltaY,c)):c=d}else d=a.detail,100<d?d=3:-100>d&&(d=-3),m(a.axis)&&a.axis===a.HORIZONTAL_AXIS?b=d:c=d;ea(this.wb)&&(b=db(b,-this.wb,this.wb));ea(this.xb)&&(c=db(c,-this.xb,this.xb));this.Tb&&(b=-b);b=new Kc(d,a,b,c);this.dispatchEvent(b)};function Jc(a,b){return w&&(t||Ka)&&0!=a%b?a:a/b}
Ic.prototype.c=function(){Ic.f.c.call(this);J(this.vb);this.vb=null};function Kc(a,b,c,d){H.call(this,b);this.type="mousewheel";this.detail=a;this.T=c;this.U=d}s(Kc,H);function Lc(){K.call(this)}s(Lc,K);g=Lc.prototype;g.ba=0;g.B=0;g.u=100;g.q=0;g.$=1;g.H=!1;g.ga=!1;g.S=function(a){a=Mc(this,a);this.ba!=a&&(this.ba=a+this.q>this.u?this.u-this.q:a<this.B?this.B:a,this.H||this.ga||this.dispatchEvent("change"))};g.k=function(){return Mc(this,this.ba)};g.Ua=function(a){a=Mc(this,a);this.q!=a&&(this.q=0>a?0:this.ba+a>this.u?this.u-this.ba:a,this.H||this.ga||this.dispatchEvent("change"))};g.P=function(){var a=this.q;return null==this.$?a:Math.round(a/this.$)*this.$};
g.ma=function(a){if(this.B!=a){var b=this.H;this.H=!0;this.B=a;a+this.q>this.u&&(this.q=this.u-this.B);a>this.ba&&this.S(a);a>this.u&&(this.q=0,this.la(a),this.S(a));(this.H=b)||this.ga||this.dispatchEvent("change")}};g.i=function(){return Mc(this,this.B)};g.la=function(a){a=Mc(this,a);if(this.u!=a){var b=this.H;this.H=!0;this.u=a;a<this.ba+this.q&&this.S(a-this.q);a<this.B&&(this.q=0,this.ma(a),this.S(this.u));a<this.B+this.q&&(this.q=this.u-this.B);(this.H=b)||this.ga||this.dispatchEvent("change")}};
g.j=function(){return Mc(this,this.u)};g.ta=function(){return this.$};function Mc(a,b){return null==a.$?b:a.B+Math.round((b-a.B)/a.$)*a.$};function Nc(a,b,c){K.call(this);this.target=a;this.handle=b||a;this.ub=c||new fc(NaN,NaN,NaN,NaN);this.n=D(a);this.G=new Dc(this);a=na(Bb,this.G);this.wa||(this.wa=[]);this.wa.push(ma(a,void 0));I(this.handle,["touchstart","mousedown"],this.Ab,!1,this)}s(Nc,K);var Oc=u||v&&x("1.9.3");g=Nc.prototype;g.clientX=0;g.clientY=0;g.screenX=0;g.screenY=0;g.Bb=0;g.Cb=0;g.T=0;g.U=0;g.$a=!0;g.V=!1;g.rb=0;g.Rb=!1;g.aa=!1;g.M=function(a){this.aa=a};g.W=function(){return this.G};
g.c=function(){Nc.f.c.call(this);Qb(this.handle,["touchstart","mousedown"],this.Ab,!1,this);this.G.ja();Oc&&this.n.releaseCapture();this.handle=this.target=null};function Pc(a){m(a.I)||(a.I=lc(a.target));return a.I}
g.Ab=function(a){var b="mousedown"==a.type;if(!this.$a||this.V||b&&(!(Eb?0==a.A.button:"click"==a.type||a.A.button&Ib[0])||w&&t&&a.ctrlKey))this.dispatchEvent("earlycancel");else{Qc(a);if(0==this.rb)if(this.dispatchEvent(new Rc("start",this,a.clientX,a.clientY)))this.V=!0,a.preventDefault();else return;else a.preventDefault();var b=this.n,c=b.documentElement,d=!Oc;this.G.g(b,["touchmove","mousemove"],this.Pb,d);this.G.g(b,["touchend","mouseup"],this.Ga,d);Oc?(c.setCapture(!1),this.G.g(c,"losecapture",
this.Ga)):this.G.g(b?b.parentWindow||b.defaultView:window,"blur",this.Ga);u&&this.Rb&&this.G.g(b,"dragstart",Db);this.Wb&&this.G.g(this.Wb,"scroll",this.Vb,d);this.clientX=this.Bb=a.clientX;this.clientY=this.Cb=a.clientY;this.screenX=a.screenX;this.screenY=a.screenY;this.T=this.aa?pc(this.target):this.target.offsetLeft;this.U=this.target.offsetTop;this.ib=pb(C(this.n));r()}};
g.Ga=function(a){this.G.ja();Oc&&this.n.releaseCapture();if(this.V){Qc(a);this.V=!1;var b=Sc(this,this.T),c=Tc(this,this.U);this.dispatchEvent(new Rc("end",this,a.clientX,a.clientY,0,b,c))}else this.dispatchEvent("earlycancel")};function Qc(a){var b=a.type;"touchstart"==b||"touchmove"==b?Hb(a,a.A.targetTouches[0],a.currentTarget):"touchend"!=b&&"touchcancel"!=b||Hb(a,a.A.changedTouches[0],a.currentTarget)}
g.Pb=function(a){if(this.$a){Qc(a);var b=(this.aa&&Pc(this)?-1:1)*(a.clientX-this.clientX),c=a.clientY-this.clientY;this.clientX=a.clientX;this.clientY=a.clientY;this.screenX=a.screenX;this.screenY=a.screenY;if(!this.V){var d=this.Bb-this.clientX,e=this.Cb-this.clientY;if(d*d+e*e>this.rb)if(this.dispatchEvent(new Rc("start",this,a.clientX,a.clientY)))this.V=!0;else{this.Ya||this.Ga(a);return}}c=Uc(this,b,c);b=c.x;c=c.y;this.V&&this.dispatchEvent(new Rc("beforedrag",this,a.clientX,a.clientY,0,b,c))&&
(Vc(this,a,b,c),a.preventDefault())}};function Uc(a,b,c){var d=pb(C(a.n));b+=d.x-a.ib.x;c+=d.y-a.ib.y;a.ib=d;a.T+=b;a.U+=c;b=Sc(a,a.T);a=Tc(a,a.U);return new B(b,a)}g.Vb=function(a){var b=Uc(this,0,0);a.clientX=this.clientX;a.clientY=this.clientY;Vc(this,a,b.x,b.y)};function Vc(a,b,c,d){a.Xa(c,d);a.dispatchEvent(new Rc("drag",a,b.clientX,b.clientY,0,c,d))}
function Sc(a,b){var c=a.ub,d=isNaN(c.left)?null:c.left,c=isNaN(c.width)?0:c.width;return Math.min(null!=d?d+c:Infinity,Math.max(null!=d?d:-Infinity,b))}function Tc(a,b){var c=a.ub,d=isNaN(c.top)?null:c.top,c=isNaN(c.height)?0:c.height;return Math.min(null!=d?d+c:Infinity,Math.max(null!=d?d:-Infinity,b))}g.Xa=function(a,b){this.aa&&Pc(this)?this.target.style.right=a+"px":this.target.style.left=a+"px";this.target.style.top=b+"px"};
function Rc(a,b,c,d,e,f,h){G.call(this,a);this.clientX=c;this.clientY=d;this.left=m(f)?f:b.T;this.top=m(h)?h:b.U;this.Za=b}s(Rc,G);var Wc=!!l.DOMTokenList,Xc=Wc?function(a){return a.classList}:function(a){a=a.className;return q(a)&&a.match(/\S+/g)||[]},Yc=Wc?function(a,b){return a.classList.contains(b)}:function(a,b){var c=Xc(a);return 0<=Xa(c,b)},Zc=Wc?function(a,b){a.classList.add(b)}:function(a,b){Yc(a,b)||(a.className+=0<a.className.length?" "+b:b)},$c=Wc?function(a,b){a.classList.remove(b)}:function(a,b){Yc(a,b)&&(a.className=Za(Xc(a),function(a){return a!=b}).join(" "))};function ad(){L.call(this);this.L=[]}s(ad,L);ad.prototype.add=function(a){0<=Xa(this.L,a)||(this.L.push(a),I(a,"finish",this.yb,!1,this))};ad.prototype.remove=function(a){$a(this.L,a)&&Qb(a,"finish",this.yb,!1,this)};ad.prototype.c=function(){Ya(this.L,function(a){a.p()});this.L.length=0;ad.f.c.call(this)};function bd(){ad.call(this);this.ab=0}s(bd,ad);
bd.prototype.play=function(a){if(0==this.L.length)return!1;if(a||this.h==M)this.ab=0,this.Qa();else if(1==this.h)return!1;this.o("play");-1==this.h&&this.o("resume");var b=-1==this.h&&!a;this.startTime=r();this.qa=null;this.h=1;Ya(this.L,function(c){b&&-1!=c.h||c.play(a)});return!0};bd.prototype.stop=function(a){Ya(this.L,function(b){b.h==M||b.stop(a)});this.h=M;this.qa=r();this.o("stop");this.ha()};
bd.prototype.yb=function(){this.ab++;this.ab==this.L.length&&(this.qa=r(),this.h=M,this.o("finish"),this.ha())};function S(a){R.call(this,a);this.mb=null;this.b=new Lc;I(this.b,"change",this.ob,!1,this)}s(S,R);g=S.prototype;g.C="horizontal";g.Ja=!1;g.Pa=!1;g.F=10;g.Oa=0;g.Sb=!0;g.$a=!0;g.t=!1;g.pa=function(){S.f.pa.call(this);var a=this.bb().pa("div",cd(this.C));this.Fa(a)};
g.Fa=function(a){S.f.Fa.call(this,a);Zc(a,cd(this.C));a=this.d();var b;var c,d,e;b=document;b=a||b;if(b.querySelectorAll&&b.querySelector)b=b.querySelectorAll(".goog-slider-thumb");else if(b.getElementsByClassName){var f=b.getElementsByClassName("goog-slider-thumb");b=f}else{f=b.getElementsByTagName("*");e={};for(c=d=0;b=f[c];c++){var h=b.className,k;if(k="function"==typeof h.split)k=0<=Xa(h.split(/\s+/),"goog-slider-thumb");k&&(e[d++]=b)}e.length=d;b=e}b=b[0];b||(b=this.bb().pa("div","goog-slider-thumb"),
b.setAttribute("role","button"),a.appendChild(b));this.a=this.s=b;this.d().setAttribute("role","slider");dd(this)};
g.Ha=function(){S.f.Ha.call(this);this.D=new Nc(this.a);this.O=new Nc(this.s);this.D.M(this.t);this.O.M(this.t);this.D.Xa=this.O.Xa=ba;this.va=new xc(this.d());this.W().g(this.D,"beforedrag",this.nb).g(this.O,"beforedrag",this.nb).g(this.D,["start","end"],this.pb).g(this.O,["start","end"],this.pb).g(this.va,"key",this.cb).g(this.d(),"mousedown",this.Nb);this.Sb&&(this.fa||(this.fa=new Ic(this.d())),this.W().g(this.fa,"mousewheel",this.Ob));this.d().tabIndex=0;ed(this)};
g.ra=function(){S.f.ra.call(this);Cb(this.D,this.O,this.va,this.fa)};g.nb=function(a){var b=a.Za==this.D?this.a:this.s,c;"vertical"==this.C?(c=this.d().clientHeight-b.offsetHeight,c=(c-a.top)/c*(this.j()-this.i())+this.i()):c=a.left/(this.d().clientWidth-b.offsetWidth)*(this.j()-this.i())+this.i();c=a.Za==this.D?Math.min(Math.max(c,this.i()),this.k()+this.P()):Math.min(Math.max(c,this.k()),this.j());fd(this,b,c)};
g.pb=function(a){var b="start"==a.type,c=this.d();b?Zc(c,"goog-slider-dragging"):$c(c,"goog-slider-dragging");c=a.target.handle;b?Zc(c,"goog-slider-thumb-dragging"):$c(c,"goog-slider-thumb-dragging");a=a.Za==this.D;b?(this.dispatchEvent("e"),this.dispatchEvent(a?"a":"c")):(this.dispatchEvent("f"),this.dispatchEvent(a?"b":"d"))};
g.cb=function(a){var b=!0;switch(a.keyCode){case 36:gd(this,this.i());break;case 35:gd(this,this.j());break;case 33:hd(this,this.F);break;case 34:hd(this,-this.F);break;case 37:var c=this.t&&this.ea()?1:-1;hd(this,a.shiftKey?c*this.F:c*this.za);break;case 40:hd(this,a.shiftKey?-this.F:-this.za);break;case 39:c=this.t&&this.ea()?-1:1;hd(this,a.shiftKey?c*this.F:c*this.za);break;case 38:hd(this,a.shiftKey?this.F:this.za);break;default:b=!1}b&&a.preventDefault()};
g.Nb=function(a){this.d().focus&&this.d().focus();var b=a.target;ob(this.a,b)||ob(this.s,b)||(this.Pa?gd(this,id(this,a)):(this.kb(a),this.w=jd(this,id(this,a)),this.sb="vertical"==this.C?this.Na<this.w.offsetTop:this.Na>kd(this,this.w)+this.w.offsetWidth,a=D(this.d()),this.W().g(a,"mouseup",this.Db,!0).g(this.d(),"mousemove",this.kb),this.Q||(this.Q=new Vb(200),this.W().g(this.Q,Wb,this.qb)),this.qb(),this.Q.start()))};g.Ob=function(a){hd(this,(0<a.detail?-1:1)*this.za);a.preventDefault()};
g.qb=function(){var a;if("vertical"==this.C){var b=this.Na,c=this.w.offsetTop;this.sb?b<c&&(a=T(this,this.w)+this.F):b>c+this.w.offsetHeight&&(a=T(this,this.w)-this.F)}else b=this.Na,c=kd(this,this.w),this.sb?b>c+this.w.offsetWidth&&(a=T(this,this.w)+this.F):b<c&&(a=T(this,this.w)-this.F);m(a)&&fd(this,this.w,a)};g.Db=function(){this.Q&&this.Q.stop();var a=D(this.d());this.W().Aa(a,"mouseup",this.Db,!0).Aa(this.d(),"mousemove",this.kb)};
function ld(a,b){var c,d=a.d();c=jc(b);d=jc(d);c=new B(c.x-d.x,c.y-d.y);return"vertical"==a.C?c.y:a.t&&a.ea()?a.d().clientWidth-c.x:c.x}g.kb=function(a){this.Na=ld(this,a)};function id(a,b){var c=a.i(),d=a.j();if("vertical"==a.C){var e=a.a.offsetHeight,f=a.d().clientHeight-e,e=ld(a,b)-e/2;return(d-c)*(f-e)/f+c}e=a.a.offsetWidth;f=a.d().clientWidth-e;e=ld(a,b)-e/2;return(d-c)*e/f+c}
function T(a,b){if(b==a.a)return a.b.k();if(b==a.s)return a.b.k()+a.b.P();throw Error("Illegal thumb element. Neither minThumb nor maxThumb");}function hd(a,b){Math.abs(b)<a.ta()&&(b=(0==b?0:0>b?-1:1)*a.ta());var c=T(a,a.a)+b,d=T(a,a.s)+b,c=db(c,a.i(),a.j()-a.Oa),d=db(d,a.i()+a.Oa,a.j());md(a,c,d-c)}function fd(a,b,c){var d=Mc(a.b,c);c=b==a.a?d:a.b.k();b=b==a.s?d:a.b.k()+a.b.P();c>=a.i()&&b>=c+a.Oa&&a.j()>=b&&md(a,c,b-c)}
function md(a,b,c){a.i()<=b&&b<=a.j()-c&&a.Oa<=c&&c<=a.j()-b&&(b!=a.k()||c!=a.P())&&(a.b.ga=!0,a.b.Ua(0),a.b.S(b),a.b.Ua(c),a.b.ga=!1,a.ob())}g.i=function(){return this.b.i()};g.ma=function(a){this.b.ma(a)};g.j=function(){return this.b.j()};g.la=function(a){this.b.la(a)};function jd(a,b){return b<=a.b.k()+a.b.P()/2?a.a:a.s}g.ob=function(){ed(this);dd(this);this.dispatchEvent("change")};
function ed(a){if(a.a&&!a.Ja){var b=nd(a,T(a,a.a)),c=nd(a,T(a,a.s));if("vertical"==a.C)a.a.style.top=b.y+"px",a.s.style.top=c.y+"px",a.l&&(b=od(c.y,b.y,a.a.offsetHeight),a.l.style.top=b.offset+"px",a.l.style.height=b.size+"px");else{var d=a.t&&a.ea()?"right":"left";a.a.style[d]=b.x+"px";a.s.style[d]=c.x+"px";a.l&&(b=od(b.x,c.x,a.a.offsetWidth),a.l.style[d]=b.offset+"px",a.l.style.width=b.size+"px")}}}function od(a,b,c){var d=Math.ceil(c/2);return{offset:a+d,size:Math.max(b-a+c-2*d,0)}}
function nd(a,b){var c=new B;if(a.a){var d=a.i(),e=a.j(),e=b==d&&d==e?0:(b-d)/(e-d);"vertical"==a.C?(d=a.d().clientHeight-a.a.offsetHeight,e=Math.round(e*d),c.x=kd(a,a.a),c.y=d-e):(d=Math.round(e*(a.d().clientWidth-a.a.offsetWidth)),c.x=d,c.y=a.a.offsetTop)}return c}
function gd(a,b){b=db(b,a.i(),a.j());a.Ja&&a.Ea.stop(!0);var c=new bd,d,e=jd(a,b),f=a.k(),h=a.P(),k=T(a,e),n=nd(a,k);d=a.ta();Math.abs(b-k)<d&&(b=db(k+(b>k?d:-d),a.i(),a.j()));fd(a,e,b);k=nd(a,T(a,e));d="vertical"==a.C?[kd(a,e),k.y]:[k.x,e.offsetTop];n=new qc(e,[n.x,n.y],d,100);n.M(a.t);c.add(n);a.l&&pd(a,e,f,h,k,c);a.mb&&(e=a.mb.Zb(f,b,100),Ya(e,function(a){c.add(a)}));a.Ea=c;a.W().g(c,"end",a.Ib);a.Ja=!0;c.play(!1)}
function pd(a,b,c,d,e,f){var h=nd(a,c),k=nd(a,c+d);c=h;d=k;b==a.a?c=e:d=e;"vertical"==a.C?(b=od(k.y,h.y,a.a.offsetHeight),h=od(d.y,c.y,a.a.offsetHeight),e=new qc(a.l,[kd(a,a.l),b.offset],[kd(a,a.l),h.offset],100),b=new sc(a.l,b.size,h.size,100)):(b=od(h.x,k.x,a.a.offsetWidth),h=od(c.x,d.x,a.a.offsetWidth),e=new qc(a.l,[b.offset,a.l.offsetTop],[h.offset,a.l.offsetTop],100),b=new rc(a.l,b.size,h.size,100));e.M(a.t);b.M(a.t);f.add(e);f.add(b)}g.Ib=function(){this.Ja=!1};
g.c=function(){S.f.c.call(this);this.Q&&this.Q.p();delete this.Q;this.Ea&&this.Ea.p();delete this.Ea;delete this.a;delete this.s;this.l&&delete this.l;this.b.p();delete this.b;this.va&&(this.va.p(),delete this.va);this.fa&&(this.fa.p(),delete this.fa);this.D&&(this.D.p(),delete this.D);this.O&&(this.O.p(),delete this.O)};g.za=1;g.ta=function(){return this.b.ta()};g.k=function(){return this.b.k()};g.S=function(a){fd(this,this.a,a)};g.P=function(){return this.b.P()};
g.Ua=function(a){fd(this,this.s,this.b.k()+a)};function dd(a){var b=a.d();b&&(qb(b,"valuemin",a.i()),qb(b,"valuemax",a.j()),qb(b,"valuenow",a.k()))}function kd(a,b){return a.t?pc(b):b.offsetLeft};function qd(a){S.call(this,a);this.b.Ua(0)}s(qd,S);function cd(a){return"vertical"==a?"goog-slider-vertical":"goog-slider-horizontal"};var rd,sd=0.75,td=0.75,U=145,ud=36,vd=2*Math.PI/ud,wd=1,V,W,X,Y,Z,$;function xd(a,b,c,d){var e=rd;e.beginPath();e.arc(a,b,c,0,2*Math.PI,!1);e.lineWidth=1;e.strokeStyle=d;e.stroke()}function yd(a,b,c,d,e){var f=rd;f.beginPath();f.lineWidth=1;f.strokeStyle=e;f.moveTo(a,b);f.lineTo(c,d);f.stroke()}
function zd(a,b){var c=W+U*Math.cos(Y+a),d=$+U*Math.sin(Y+a),e=X+V*Math.cos(Z+wd*a),f=$+V*Math.sin(Z+wd*a);b&&(xd(c,d,4,"#FF0000"),xd(e,f,4,"#FF0000"));var h=e-c,k=f-d,n=Math.sqrt(h*h+k*k),h=h/n,c=(c+e)/2,d=(d+f)/2,k=-(k/n);yd(c-400*k,d-400*h,c+400*k,d+400*h,"#FF7777")}
function Ad(a){rd.clearRect(0,0,canvas.width,canvas.height);var b=rd,c=canvas.width,d=canvas.height;b.beginPath();b.rect(0,0,c,d);b.lineWidth=1;b.strokeStyle="#AAAAAA";b.stroke();V=sd*U;b=U*(1+2*sd*td-sd);X=W+b;xd(W,$,U,"#003300");xd(X,$,V,"#003300");Y=Math.acos((U*U+b*b-V*V)/(2*U*b));Z=Math.PI-Math.acos((V*V+b*b-U*U)/(2*V*b));yd(W,$,W+U*Math.cos(Y),$+U*Math.sin(Y),"#7777FF");yd(X,$,X+V*Math.cos(Z),$+V*Math.sin(Z),"#7777FF");0>wd?(yd(W,$,W-V*Math.cos(Z),$-V*Math.sin(Z),"#999999"),yd(X,$,X-U*Math.cos(Y),
$-U*Math.sin(Y),"#999999")):(yd(W,$,W-V*Math.cos(Z),$+V*Math.sin(Z),"#999999"),yd(X,$,X-U*Math.cos(Y),$+U*Math.sin(Y),"#999999"));if("undefined"===typeof a)for(a=1;a<ud;a++)zd(a*vd,!1);else zd(a,!0)}
aa("imo_1979_3.start",function(){var a=document.getElementById("canvas");a.getContext&&(rd=a.getContext("2d"),W=U+(a.width-4*U)/2,$=a.height/2);var a=document.getElementById("s1"),b=new qd;Gc(b,a);b.ma(1);b.la(200);b.addEventListener("change",function(){var a=b.k()/200;sd=document.getElementById("out1").value=a;Ad()});b.Pa=!0;b.S(200*sd);var a=document.getElementById("s2"),c=new qd;Gc(c,a);c.ma(1);c.la(199);c.Pa=!0;c.addEventListener("change",function(){var a=c.k()/200;td=document.getElementById("out2").value=
a;Ad()});c.S(200*td);var a=document.getElementById("s3"),d=new qd;Gc(d,a);d.Pa=!0;d.ma(1);d.la(999);d.addEventListener("change",function(){Ad(Math.PI*d.k()/500)})});aa("imo_1979_3.onChangeDirection",function(a){wd=a?-1:1;Ad()});
</script>
<br />
<div id="container" style="overflow: hidden">
<div id="inner" style="overflow: hidden; width: 900px;">
<div style="float: left; margin-left: 10px; margin-bottom: 5px">
<strong style="display: block; margin-bottom: 3px">r (R1/R2)</strong>
<div id="s1" class="goog-slider" style="width: 200px; height: 20px">
<div style="position:absolute;width:100%;top:9px;border:1px inset white;overflow:hidden;height:0"></div>
<div class="goog-slider-thumb"></div>
</div>
<input type="text" value="0" id="out1" style="margin-left: 0px; margin-top: 5px" readonly>
</div>
<div style="float: left; margin-left: 10px; margin-bottom: 5px">
<strong style="display: block; margin-bottom: 3px">q [R1 - R2, R1 + R2]</strong>
<div id="s2" class="goog-slider" style="width: 200px; height: 20px">
<div style="position:absolute;width:100%;top:9px;border:1px inset white;overflow:hidden;height:0"></div>
<div class="goog-slider-thumb"></div>
</div>
<input type="text" value="0" id="out2" style="margin-left: 0px; margin-top: 5px" readonly>
</div>
<div style="float: left; margin-left: 10px; margin-bottom: 5px">
<strong style="display: block; margin-bottom: 3px">Move</strong>
<div id="s3" class="goog-slider" style="width: 200px; height: 20px">
<div style="position:absolute;width:100%;top:9px;border:1px inset white;overflow:hidden;height:0"></div>
<div class="goog-slider-thumb"></div>
</div>
<input type="checkbox" onclick="imo_1979_3.onChangeDirection(this.checked)">
Inverse direction
</div>
</div>
<div style="text-align: left;">
<canvas height="400" id="canvas" width="600"> </canvas>
</div>
</div>
<script>
imo_1979_3.start();
</script>
The source code can be found here: <a href="http://github.com/ex/js">http://github.com/ex/js</a><br />
And the stand alone demo can be viewed here: <a href="http://ex.github.io/js/src/imo_1979_3/">http://ex.github.io/js/src/imo_1979_3/</a>
exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-20775992800800487792013-07-27T21:19:00.000-05:002015-08-14T00:23:47.835-05:00Killa: the case for mandatory braces<a href="https://github.com/ex/Killa">Killa</a> was created following the Perl/Ruby philosophy of freedom of choice. So you must be wondering why on earth I decided to add the rule of mandatory braces for blocks. Its a valid question, one that I would try to answer in this post. But first a bit of history.<br />
<br />
My first approach to programming was at the university. The career I was following (electrical engineering) was not centered about programming, so programming was for us a mere exercise of getting additional credits. I don't remember much about my time at the university, but what I can not forget is our "code" and by that I mean the spaghetti-like thing that most of us produced for code: horrendous single file programs without proper naming conventions neither formatting with piles and piles of commented sections that for some miraculous reason worked enough to pass the tests at hand.<span style="color: #0000ee;"> </span>I suspect that the pain of working in that kind of environment was the main reason behind me starting to use my own set of code conventions, and even when my programming style has changed a lot through the years, the first rule has always been the same: <i>never ever forget to use braces for code blocks. </i>No excuses.<br />
<br />
So lets see some code. In many C-derived languages (like C++, C#, Java, JavaScript), braces are optional. So this code:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> if ( enemyIsNear() ) {</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> launchTheMissiles();</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> }</span><br />
<br />
can be written like this: <br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> if ( enemyIsNear() )</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> launchTheMissiles();</span><br />
<br />
And this it's perfectly legal and used a lot on industrial-grade software.<br />
<br />
<div style="text-align: center;">
<a href="https://sites.google.com/site/exeqtor/f35.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://sites.google.com/site/exeqtor/f35.jpg" /></a></div>
<br />
You could be wondering (with reason) What is wrong with this? And the answer is nothing... <i>for the time being</i>. The missiles are launched only when enemies are near. But as Joel Spolsky would say, <a href="http://www.joelonsoftware.com/articles/Wrong.html">this code just smells a bit bad</a>. There is nothing preventing another programmer to add a line of code to the conditional and forgetting to add the required braces. Imagine we need to speed up before launching the missiles, we could do this:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> if ( enemyIsNear() )</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: "Courier New",Courier,monospace;"> <span style="color: red;">speedUp();</span></span><span style="color: red;"> </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"> launchTheMissiles();</span><br />
<br />
but now the missiles are launched even if we are not near an enemy! Because the above snippet is equivalent to:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> if ( enemyIsNear() ) {</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: "Courier New",Courier,monospace;"> <span style="color: red;">speedUp();</span></span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="color: red;"> </span></span>
<span style="font-family: "Courier New",Courier,monospace;"> } </span><br />
<span style="font-family: "Courier New",Courier,monospace;"> launchTheMissiles();</span> <br />
<br />
A total waste of missiles just because we were saving a pair of braces! Clearly this is not good, but lets see some arguments that are used against mandatory braces.<br />
<br />
<b>The code compressors</b><br />
<br />
Some professional programmers could favor optional braces under the argument that forcing braces adds one or two
more lines of code in blocks like the one presented at the beginning. After all, under some code conventions the original snippet ends like this:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> if ( enemyIsNear() )</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> {</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;"> launchTheMissiles();</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> }</span><br />
<br />
Four lines instead of two! A total waste of space! Considering all the cases where additional new lines are introduced we obviously get more lengthy programs. <br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="https://sites.google.com/site/exeqtor/code_compressor.jpg" /></div>
<br />
<a href="http://www.codinghorror.com/blog/2007/05/the-best-code-is-no-code-at-all.html">Less code is better</a>, they'll say. OK, sure! But if programming were a simple matter of removing new lines this would be even better:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> if ( enemyIsNear() ) <span style="color: red;">speedUp();</span></span><br />
<br />
You could be wondering: What is wrong now with this line of code?<br />
And I'd say <i>...again nothing</i>! Until you want to test your program and check if the conditional is working in all the cases that it must work. Some kind of <a href="https://en.wikipedia.org/wiki/Code_coverage">code coverage</a>. The program hitting that line doesn't give you any indication about the conditional being passed or not. For this example we could check if the function was called, but that wouldn't be the case if the code were like this:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> if ( enemyIsNear() ) <span style="color: red;">speed = 2 * </span></span><span style="color: red;"><span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: "Courier New",Courier,monospace;">speed</span>;</span></span><br />
<br />
it is not clear how can we validate if the speed is being doubled or not. Adding a breakpoint to this line for debugging is going to stop the program always, so you'll need to break the line anyways for debugging purposes.<br />
<br />
So the argument of using less lines of code is moot, less code is better code but <span style="color: blue;"><i>only if the resulting code does the same, has the same number of errors and maintains the same quality</i></span>. If this were not the case, the code minifiers would be the best coders ever. However, still we need to probe that using mandatory braces produces code of better quality.<br />
<br />
<b>The indentation believers</b><br />
<br />
Some professional programmers would believe that correct indentation is good enough to indicate the beginning and end of code blocks. Indeed, there are some languages that go one step ahead and remove the need for block delimiters, indentation is used for block scoping. In Python the original snippet looks like this:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> if enemyIsNear():</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> launchTheMissiles()</span><br />
<br />
No need to argue about mandatory braces here, there are not even braces now. Cool. Everything is good and well but someday you realize that you need to check the engine heat after launching the missiles, so you do:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> if enemyIsNear():</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> launchTheMissiles()</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: "Courier New",Courier,monospace;"> checkEngineHeat()</span></span><br />
<br />
The engine check is a time wasting process but it's fine because it's done only after we launched the missiles. And then someday another engineer realizes that for this jet model in particular the check is always required, doing:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> if enemyIsNear():</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> launchTheMissiles()</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: "Courier New",Courier,monospace;"> <span style="color: red;">checkEngineHeat()</span></span><span style="color: red;"> </span></span><br />
<br />
Just looking at this change, I tell you, I'll freak out. What if for some reason the guy accidentally deleted my precious tab?. Nope, we don't use tabs here. But What if he thought I was wrong and I did the original change wrong and <i>now he fixed it</i>. I won't rest happy until having a little chat with the cunning fellow. Of course if this where a industrial-grade software, this change alone would come with a lengthy and detailed description in the commit log, or even better with profuse comments on the program itself. But my point stands, <span style="color: blue;"><i>format alone is not enough to clearly state program intention. Indentation is a decoration, it's a luxury, and you must be free to decorate your programs the way you like</i></span>. Just leave my block delimiters alone. The other problem with using indentation as block delimiters is that indentation is done using withespace. And whitespace is by definition <i>not visible. </i>Yes, you could force whitespace to be visible in your editor, but that is forcing and it is at the very least discussable. I mean, if it were good enough we all must be programming in <a href="http://en.wikipedia.org/wiki/Whitespace_%28programming_language%29">Whitespace</a> for the fun. A side benefit of not using indentation for block delimiters is that you can safely ignore any formatting change in code reviews: just ignore whitespace. I won't talk about the issues of copy pasting code in Python, copy pasting code is bad anyways and you deserve to be toasted.<br />
<br />
That said, I don't think I'll convert many believers with my arguments here: they are believers for some reason and I'm just a non believer. For them code like this:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> if ( enemyIsNear() ) {</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> launchTheMissiles();</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: "Courier New",Courier,monospace;"> checkEngineHeat();</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: "Courier New",Courier,monospace;"> } </span></span><br />
<br />
it's just <a href="http://www.secnetix.de/olli/Python/block_indentation.hawk">tiresome and bloated source code</a>.<br />
<br />
<b>The C pitfall</b><br />
<br />
C is a great language, but it could have been even greater. One of it's design fails came when it allowed optional block delimiters. Consider this classical example:<br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;"> if ( enemyIsNear() )<br /> if ( isWeakEnemy() )<br /> launchTheMissiles();<br /> else<br /> watchTV();</span><br />
<br />
You could be thinking that it's safe to watch TV if there are not enemies near. But what this code really is doing is:<br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;"> if ( enemyIsNear() ) {<br /> if ( isWeakEnemy() ) {<br /> launchTheMissiles();<br /><span style="color: red;"> } else {<br /> watchTV();</span></span><br />
<span style="color: red;"><span style="font-family: "Courier New",Courier,monospace;"> }</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"> }</span><br />
<br />
Yeah, you would be watching TV with a mighty enemy on your tail. Scary, isn't? C doesn't use indentation for block scope, but it allows optional block delimiters. Bad things could happen, and now you are falling on your parachute looking the ground under your feet approach.<br />
<br />
About the possible bug with which we started this post, to be honest, I have rarely seen it in production
code, even less in committed code. I can't introduce it because I use mandatory braces, my team can't introduce it, because I established the company guidelines with mandatory braces on it and most of the projects I
have worked on use a code convention that forces mandatory braces or
are careful enough to avoid this class of blunder even if they are not
using mandatory braces. Or that I though. But upgrading Killa with the fixes in the Lua 5.2.2 release (Killa used the Lua 5.2 version) show me some <a href="https://bitbucket.org/ex/love/commits/e9c4cf127e83db8140ff0926ed89755651efb7cb#Lsrc/libraries/lua52/lapi.cF824">remarkable fix in the line 824 of lapi.c</a>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://sites.google.com/site/exeqtor/fix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://sites.google.com/site/exeqtor/fix.png" /></a></div>
<br />
<br />
Looks familiar? Yes. The scary thing is that this bug was able to hide itself for more than one year in a project reviewed by hundreds of savvy programmers (me included) but we totally missed it. When working in Killa I had two options: reformat all the source code to use my code standard (mandatory braces), or leave it like it was. I was in a hurry and decided for the second option, also reformatting the source code would imply a lot more of work back-porting upstream changes. The sad thing is that manual reformatting would have surely made me question the code in mention, the indentation was wrong at the very least.<br />
<br />
For the sake of redemption, The <a href="http://en.wikipedia.org/wiki/Go_language">Go language</a> developed by Ken Thompson (the father of the B language from which C was derived) uses now mandatory braces.<br />
<br />
<b>Conclusion</b><br />
<br />
I argued against two camps that oppose mandatory braces and showed some pitfalls about allowing optional braces in legacy languages, but at the end, I failed on giving you hard evidence about this, I don't have the statistical numbers at hand, just some anecdotal evidence and you are free to think it's not enough.<br />
<br />
The reason I implemented mandatory braces in Killa is that I believe this has the benefit of making clearly evident the structure and intention of the underlying programs. It's not fool safe but a lot better than leaving free choice in this regard.<br />
<br />
Freedom, there is still plenty: you can use tabs if you prefer them, 2 spaces or 8 spaces? it's for you to decide, the Lisp style is your thing? <a href="http://en.wikipedia.org/wiki/Indent_style#Lisp_style">be my guest</a>. Just leave my braces alone.<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"></span>exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-75813465094824061652012-11-25T20:57:00.000-05:002016-03-25T15:35:18.656-05:00Simple Tetris Clone in TypeScriptI wanted to create a JavaScript port of <a href="http://code.google.com/p/simple-tetris-clone/">my tetris clone</a> using the HTML5 canvas element but I never got the drive to start it, so it was lingering for ages in my TODO list. The final push come after <a href="http://www.typescriptlang.org/">TypeScript</a>, a little language from the father of industrial grade languages like Delphi and C#. This was just enough to push me to use my little game to familiarize myself with this new language. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/ts01.png" /></div>
<a name='more'></a><br />
So. How it went?<br />
<br />
My overall experience has been positive. TypeScript
is really a nice addition to the JavaScript ecosystem because it allows
you to catch a lot of errors at compile time. Yes, I'm ashamed to admit
it but <i>I do many of these errors</i>, it was one of the reasons I created <a href="https://github.com/ex/Killa">Killa</a>,
I wanted the compiler to find typos for me. But TypeScript goes many steps ahead and already includes support for optional typing, classes, interfaces and namespaces and at the same time is JavaScript compatible and of practical use right now. I mean: it works.<br />
<br />
The class system is based in the next JavaScript standard and I got used to it after a little time, I would have preferred a more conventional one but it's fine I guess. The optional typing system works like in AS3/Haxe and it's the one I was thinking for Killa so I already liked it. However, there is no "int" type, something that I really missed porting my game, because many errors porting C++ programs to JavaScript happen due to numerical conversion errors.<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">x = 5;</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">// C++</span><br />
<span style="font-family: "Courier New",Courier,monospace;">int n = x / 2; // n = 2</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;">// JavaScript </span><br />
<span style="font-family: "Courier New",Courier,monospace;">var n = x / 2; // n = 2.5</span><br />
<br />
The "this" keyword is also misleading inside a class method because it is still the "this" keyword from JavaScript and it can be the window object in some circumstances (that I found porting my game). Generics are missing, the arrays can be typed but I don't like the way how it's done, I think they can learn a bit from Haxe. At future, I'd love to see TypeScript deviating more and more from JavaScript and fixing the language in the process, however it's a difficult task at this moment because they want/need to support vanilla JavaScript and then you have to support things like "!==", ">>> 0", "[]=={}", etc. If it were me I'll add a JavaScript region tag, something like this:<br />
<span style="font-family: "Courier New",Courier,monospace;"><br /> // paradise zone<br />#define js<br /> // all hell broke loose here...<br />#end<br /> // paradise zone again</span><br />
<br />
This way you could still have JavaScript compatibility, but only in contained zones and you could enforce full TypeScript safety in the rest of your program. You could shrink the dangerous zones progressively as new JavaScript libraries are ported to TypeScript. But enough of my lame opinions, <a href="http://jsfiddle.net/exdev/sxGN3/10/light/">here</a> you can see the auto-generated code, it's not beautiful but it can be followed. There is an option to pass the comments in TypeScript to the generated JavaScript code but as you can see it's not perfect.<br />
<br />
<div style="position: relative;">
<canvas height="320" id="canvasBack" style="left: 50%; margin-left: -240px; position: absolute; top: 0; z-index: 0;" width="480"></canvas>
<canvas height="320" id="canvasStats" style="left: 50%; margin-left: -240px; position: absolute; top: 0; z-index: 1;" width="480"></canvas>
<canvas height="320" id="canvas" style="left: 50%; margin-left: -240px; position: absolute; top: 0; z-index: 2;" width="480"></canvas>
</div>
<script type="application/javascript">
var Stc;
(function (Stc) {
// Clear resources used by platform
// Process events and notify game
// Render the state of the game
// Return the current system time in milliseconds
// Return a random positive integer number
// Data structure that holds information about our tetromino blocks.
var StcTetromino = (function () {
function StcTetromino() {
this.cells = [];
for(var k = 0; k < Game.TETROMINO_SIZE; ++k) {
this.cells[k] = [];
}
}
return StcTetromino;
})();
Stc.StcTetromino = StcTetromino;
// Datas structure for statistical data
var StcStatics = (function () {
function StcStatics() {
this.pieces = [];
}
return StcStatics;
})();
Stc.StcStatics = StcStatics;
var Game = (function () {
function Game() {
this.m_map = [];
for(var k = 0; k < Game.BOARD_TILEMAP_WIDTH; ++k) {
this.m_map[k] = [];
}
this.m_fallingBlock = new StcTetromino();
this.m_nextBlock = new StcTetromino();
}
// Initialize the game. The error code (if any) is saved in [m_errorCode].
Game.BOARD_TILEMAP_WIDTH = 10;
Game.BOARD_TILEMAP_HEIGHT = 22;
Game.INIT_DELAY_FALL = 1000;
Game.SCORE_1_FILLED_ROW = 400;
Game.SCORE_2_FILLED_ROW = 1000;
Game.SCORE_3_FILLED_ROW = 3000;
Game.SCORE_4_FILLED_ROW = 12000;
Game.SCORE_MOVE_DOWN_DIVISOR = 1000;
Game.SCORE_DROP_DIVISOR = 20;
Game.SCORE_DROP_WITH_SHADOW_DIVISOR = 100;
Game.FILLED_ROWS_FOR_LEVEL_UP = 10;
Game.DELAY_FACTOR_FOR_LEVEL_UP = 9;
Game.DELAY_DIVISOR_FOR_LEVEL_UP = 10;
Game.DAS_DELAY_TIMER = 200;
Game.DAS_MOVE_TIMER = 40;
Game.ROTATION_AUTOREPEAT_DELAY = 375;
Game.ROTATION_AUTOREPEAT_TIMER = 200;
Game.ERROR_NONE = 0;
Game.ERROR_PLAYER_QUITS = 1;
Game.ERROR_NO_MEMORY = -1;
Game.ERROR_NO_VIDEO = -2;
Game.ERROR_NO_IMAGES = -3;
Game.ERROR_PLATFORM = -4;
Game.ERROR_ASSERT = -100;
Game.EVENT_NONE = 0;
Game.EVENT_MOVE_DOWN = 1;
Game.EVENT_MOVE_LEFT = 1 << 1;
Game.EVENT_MOVE_RIGHT = 1 << 2;
Game.EVENT_ROTATE_CW = 1 << 3;
Game.EVENT_ROTATE_CCW = 1 << 4;
Game.EVENT_DROP = 1 << 5;
Game.EVENT_PAUSE = 1 << 6;
Game.EVENT_RESTART = 1 << 7;
Game.EVENT_SHOW_NEXT = 1 << 8;
Game.EVENT_SHOW_SHADOW = 1 << 9;
Game.EVENT_QUIT = 1 << 10;
Game.TETROMINO_SIZE = 4;
Game.TETROMINO_TYPES = 7;
Game.TETROMINO_I = 0;
Game.TETROMINO_O = 1;
Game.TETROMINO_T = 2;
Game.TETROMINO_S = 3;
Game.TETROMINO_Z = 4;
Game.TETROMINO_J = 5;
Game.TETROMINO_L = 6;
Game.COLOR_CYAN = 1;
Game.COLOR_RED = 2;
Game.COLOR_BLUE = 3;
Game.COLOR_ORANGE = 4;
Game.COLOR_GREEN = 5;
Game.COLOR_YELLOW = 6;
Game.COLOR_PURPLE = 7;
Game.COLOR_WHITE = 0;
Game.EMPTY_CELL = -1;
Game.prototype.onChangeProcessed = // The platform must call this method after processing a changed state
function () {
this.m_stateChanged = false;
}// Return true if the game state has changed, false otherwise
;
Game.prototype.hasChanged = function () {
return this.m_stateChanged;
}// Return true if the game state has changed, false otherwise
;
Game.prototype.setChanged = function (changed) {
this.m_stateChanged = changed;
}// Return the cell at the specified position
;
Game.prototype.getCell = function (column, row) {
return this.m_map[column][row];
}// Return a reference to the game statistic data
;
Game.prototype.stats = function () {
return this.m_stats;
}// Return current falling tetromino
;
Game.prototype.fallingBlock = function () {
return this.m_fallingBlock;
}// Return next tetromino
;
Game.prototype.nextBlock = function () {
return this.m_nextBlock;
}// Return current error code
;
Game.prototype.errorCode = function () {
return this.m_errorCode;
}// Return true if the game is paused, false otherwise
;
Game.prototype.isPaused = function () {
return this.m_isPaused;
}// Return true if the game has finished, false otherwise
;
Game.prototype.isOver = function () {
return this.m_isOver;
}// Return true if we must show preview tetromino
;
Game.prototype.showPreview = function () {
return this.m_showPreview;
}// Return true if we must show ghost shadow
;
Game.prototype.showShadow = function () {
return this.m_showShadow;
}// Return height gap between shadow and falling tetromino
;
Game.prototype.shadowGap = function () {
return this.m_shadowGap;
};
Game.prototype.init = function (targetPlatform) {
// Store platform reference and start it
this.m_platform = targetPlatform;
// Initialize platform
this.m_errorCode = this.m_platform.init(this);
if(this.m_errorCode == Game.ERROR_NONE) {
// If everything is OK start the game
this.start();
}
};
Game.prototype.end = // Free used resources
function () {
this.m_platform.end();
}// Main function game called every frame
;
Game.prototype.update = function () {
// Read player input
this.m_platform.processEvents();
// Update game state
if(this.m_isOver) {
if((this.m_events & Game.EVENT_RESTART) != 0) {
this.m_isOver = false;
this.start();
}
} else {
// Always handle restart event
if((this.m_events & Game.EVENT_RESTART) != 0) {
this.start();
return;
}
var currentTime = this.m_platform.getSystemTime();
// Process delayed autoshift
var timeDelta = currentTime - this.m_systemTime;
if(this.m_delayDown > 0) {
this.m_delayDown -= timeDelta;
if(this.m_delayDown <= 0) {
this.m_delayDown = Game.DAS_MOVE_TIMER;
this.m_events |= Game.EVENT_MOVE_DOWN;
}
}
if(this.m_delayLeft > 0) {
this.m_delayLeft -= timeDelta;
if(this.m_delayLeft <= 0) {
this.m_delayLeft = Game.DAS_MOVE_TIMER;
this.m_events |= Game.EVENT_MOVE_LEFT;
}
} else {
if(this.m_delayRight > 0) {
this.m_delayRight -= timeDelta;
if(this.m_delayRight <= 0) {
this.m_delayRight = Game.DAS_MOVE_TIMER;
this.m_events |= Game.EVENT_MOVE_RIGHT;
}
}
}
if(this.m_delayRotation > 0) {
this.m_delayRotation -= timeDelta;
if(this.m_delayRotation <= 0) {
this.m_delayRotation = Game.ROTATION_AUTOREPEAT_TIMER;
this.m_events |= Game.EVENT_ROTATE_CW;
}
}
// Always handle pause event
if((this.m_events & Game.EVENT_PAUSE) != 0) {
this.m_isPaused = !this.m_isPaused;
this.m_events = Game.EVENT_NONE;
}
// Check if the game is paused
if(this.m_isPaused) {
// We achieve the effect of pausing the game
// adding the last frame duration to lastFallTime
this.m_lastFallTime += (currentTime - this.m_systemTime);
} else {
if(this.m_events != Game.EVENT_NONE) {
if((this.m_events & Game.EVENT_SHOW_NEXT) != 0) {
this.m_showPreview = !this.m_showPreview;
this.m_stateChanged = true;
}
if((this.m_events & Game.EVENT_SHOW_SHADOW) != 0) {
this.m_showShadow = !this.m_showShadow;
this.m_stateChanged = true;
}
if((this.m_events & Game.EVENT_DROP) != 0) {
this.dropTetromino();
}
if((this.m_events & Game.EVENT_ROTATE_CW) != 0) {
this.rotateTetromino(true);
}
if((this.m_events & Game.EVENT_MOVE_RIGHT) != 0) {
this.moveTetromino(1, 0);
} else {
if((this.m_events & Game.EVENT_MOVE_LEFT) != 0) {
this.moveTetromino(-1, 0);
}
}
if((this.m_events & Game.EVENT_MOVE_DOWN) != 0) {
// Update score if the player accelerates downfall
this.m_stats.score += Math.floor(Game.SCORE_2_FILLED_ROW * (this.m_stats.level + 1) / Game.SCORE_MOVE_DOWN_DIVISOR);
this.moveTetromino(0, 1);
}
this.m_events = Game.EVENT_NONE;
}
// Check if it's time to move downwards the falling tetromino
if(currentTime - this.m_lastFallTime >= this.m_fallingDelay) {
this.moveTetromino(0, 1);
this.m_lastFallTime = currentTime;
}
}
// Save current time for next game update
this.m_systemTime = currentTime;
}
// Draw game state
this.m_platform.renderGame();
}// Process a key down event
;
Game.prototype.onEventStart = function (command) {
switch(command) {
case Game.EVENT_QUIT: {
this.m_errorCode = Game.ERROR_PLAYER_QUITS;
break;
}
case Game.EVENT_MOVE_DOWN: {
this.m_events |= Game.EVENT_MOVE_DOWN;
this.m_delayDown = Game.DAS_DELAY_TIMER;
break;
}
case Game.EVENT_ROTATE_CW: {
this.m_events |= Game.EVENT_ROTATE_CW;
this.m_delayRotation = Game.ROTATION_AUTOREPEAT_DELAY;
break;
}
case Game.EVENT_MOVE_LEFT: {
this.m_events |= Game.EVENT_MOVE_LEFT;
this.m_delayLeft = Game.DAS_DELAY_TIMER;
break;
}
case Game.EVENT_MOVE_RIGHT: {
this.m_events |= Game.EVENT_MOVE_RIGHT;
this.m_delayRight = Game.DAS_DELAY_TIMER;
break;
}
case Game.EVENT_DROP:
case Game.EVENT_RESTART:
case Game.EVENT_PAUSE:
case Game.EVENT_SHOW_NEXT:
case Game.EVENT_SHOW_SHADOW: {
this.m_events |= command;
break;
}
}
}// Process a key up event
;
Game.prototype.onEventEnd = function (command) {
switch(command) {
case Game.EVENT_MOVE_DOWN: {
this.m_delayDown = -1;
break;
}
case Game.EVENT_MOVE_LEFT: {
this.m_delayLeft = -1;
break;
}
case Game.EVENT_MOVE_RIGHT: {
this.m_delayRight = -1;
break;
}
case Game.EVENT_ROTATE_CW: {
this.m_delayRotation = -1;
break;
}
}
}// Set matrix elements to indicated value
;
Game.prototype.setMatrixCells = function (matrix, width, height, value) {
for(var i = 0; i < width; ++i) {
for(var j = 0; j < height; ++j) {
matrix[i][j] = value;
}
}
}// Initialize tetromino cells for every type of tetromino
;
Game.prototype.setTetromino = function (indexTetromino, tetromino) {
// Initialize tetromino cells to empty cells
this.setMatrixCells(tetromino.cells, Game.TETROMINO_SIZE, Game.TETROMINO_SIZE, Game.EMPTY_CELL);
// Almost all the blocks have size 3
tetromino.size = Game.TETROMINO_SIZE - 1;
// Initial configuration from:http://tetris.wikia.com/wiki/SRS
switch(indexTetromino) {
case Game.TETROMINO_I: {
tetromino.cells[0][1] = Game.COLOR_CYAN;
tetromino.cells[1][1] = Game.COLOR_CYAN;
tetromino.cells[2][1] = Game.COLOR_CYAN;
tetromino.cells[3][1] = Game.COLOR_CYAN;
tetromino.size = Game.TETROMINO_SIZE;
break;
}
case Game.TETROMINO_O: {
tetromino.cells[0][0] = Game.COLOR_YELLOW;
tetromino.cells[0][1] = Game.COLOR_YELLOW;
tetromino.cells[1][0] = Game.COLOR_YELLOW;
tetromino.cells[1][1] = Game.COLOR_YELLOW;
tetromino.size = Game.TETROMINO_SIZE - 2;
break;
}
case Game.TETROMINO_T: {
tetromino.cells[0][1] = Game.COLOR_PURPLE;
tetromino.cells[1][0] = Game.COLOR_PURPLE;
tetromino.cells[1][1] = Game.COLOR_PURPLE;
tetromino.cells[2][1] = Game.COLOR_PURPLE;
break;
}
case Game.TETROMINO_S: {
tetromino.cells[0][1] = Game.COLOR_GREEN;
tetromino.cells[1][0] = Game.COLOR_GREEN;
tetromino.cells[1][1] = Game.COLOR_GREEN;
tetromino.cells[2][0] = Game.COLOR_GREEN;
break;
}
case Game.TETROMINO_Z: {
tetromino.cells[0][0] = Game.COLOR_RED;
tetromino.cells[1][0] = Game.COLOR_RED;
tetromino.cells[1][1] = Game.COLOR_RED;
tetromino.cells[2][1] = Game.COLOR_RED;
break;
}
case Game.TETROMINO_J: {
tetromino.cells[0][0] = Game.COLOR_BLUE;
tetromino.cells[0][1] = Game.COLOR_BLUE;
tetromino.cells[1][1] = Game.COLOR_BLUE;
tetromino.cells[2][1] = Game.COLOR_BLUE;
break;
}
case Game.TETROMINO_L: {
tetromino.cells[0][1] = Game.COLOR_ORANGE;
tetromino.cells[1][1] = Game.COLOR_ORANGE;
tetromino.cells[2][0] = Game.COLOR_ORANGE;
tetromino.cells[2][1] = Game.COLOR_ORANGE;
break;
}
}
tetromino.type = indexTetromino;
}// Start a new game
;
Game.prototype.start = function () {
// Initialize game data
this.m_errorCode = Game.ERROR_NONE;
this.m_systemTime = this.m_platform.getSystemTime();
this.m_lastFallTime = this.m_systemTime;
this.m_isOver = false;
this.m_isPaused = false;
this.m_showPreview = true;
this.m_events = Game.EVENT_NONE;
this.m_fallingDelay = Game.INIT_DELAY_FALL;
this.m_showShadow = true;
// Initialize game statistics
this.m_stats = new StcStatics();
this.m_stats.score = 0;
this.m_stats.lines = 0;
this.m_stats.totalPieces = 0;
this.m_stats.level = 0;
for(var i = 0; i < Game.TETROMINO_TYPES; ++i) {
this.m_stats.pieces[i] = 0;
}
// Initialize game tile map
this.setMatrixCells(this.m_map, Game.BOARD_TILEMAP_WIDTH, Game.BOARD_TILEMAP_HEIGHT, Game.EMPTY_CELL);
// Initialize falling tetromino
this.setTetromino(this.m_platform.random() % Game.TETROMINO_TYPES, this.m_fallingBlock);
this.m_fallingBlock.x = Math.floor((Game.BOARD_TILEMAP_WIDTH - this.m_fallingBlock.size) / 2);
this.m_fallingBlock.y = 0;
// Initialize preview tetromino
this.setTetromino(this.m_platform.random() % Game.TETROMINO_TYPES, this.m_nextBlock);
// Initialize events
this.onTetrominoMoved();
// Initialize delayed autoshift
this.m_delayLeft = -1;
this.m_delayRight = -1;
this.m_delayDown = -1;
this.m_delayRotation = -1;
}// Rotate falling tetromino. If there are no collisions when the
// tetromino is rotated this modifies the tetromino's cell buffer.
;
Game.prototype.rotateTetromino = function (clockwise) {
var i, j;
var rotated = [];// temporary array to hold rotated cells
for(var k = 0; k < Game.TETROMINO_SIZE; ++k) {
rotated[k] = [];
}
// If TETROMINO_O is falling return immediately
if(this.m_fallingBlock.type == Game.TETROMINO_O) {
return;// rotation doesn't require any changes
}
// Initialize rotated cells to blank
this.setMatrixCells(rotated, Game.TETROMINO_SIZE, Game.TETROMINO_SIZE, Game.EMPTY_CELL);
// Copy rotated cells to the temporary array
for(i = 0; i < this.m_fallingBlock.size; ++i) {
for(j = 0; j < this.m_fallingBlock.size; ++j) {
if(clockwise) {
rotated[this.m_fallingBlock.size - j - 1][i] = this.m_fallingBlock.cells[i][j];
} else {
rotated[j][this.m_fallingBlock.size - i - 1] = this.m_fallingBlock.cells[i][j];
}
}
}
var wallDisplace = 0;
// Check collision with left wall
if(this.m_fallingBlock.x < 0) {
for(i = 0; (wallDisplace == 0) && (i < -this.m_fallingBlock.x); ++i) {
for(j = 0; j < this.m_fallingBlock.size; ++j) {
if(rotated[i][j] != Game.EMPTY_CELL) {
wallDisplace = i - this.m_fallingBlock.x;
break;
}
}
}
} else {
// Or check collision with right wall
if(this.m_fallingBlock.x > Game.BOARD_TILEMAP_WIDTH - this.m_fallingBlock.size) {
i = this.m_fallingBlock.size - 1;
for(; (wallDisplace == 0) && (i >= Game.BOARD_TILEMAP_WIDTH - this.m_fallingBlock.x); --i) {
for(j = 0; j < this.m_fallingBlock.size; ++j) {
if(rotated[i][j] != Game.EMPTY_CELL) {
wallDisplace = -this.m_fallingBlock.x - i + Game.BOARD_TILEMAP_WIDTH - 1;
break;
}
}
}
}
}
// Check collision with board floor and other cells on board
for(i = 0; i < this.m_fallingBlock.size; ++i) {
for(j = 0; j < this.m_fallingBlock.size; ++j) {
if(rotated[i][j] != Game.EMPTY_CELL) {
// Check collision with bottom border of the map
if(this.m_fallingBlock.y + j >= Game.BOARD_TILEMAP_HEIGHT) {
return;// there was collision therefore return
}
// Check collision with existing cells in the map
if(this.m_map[i + this.m_fallingBlock.x + wallDisplace][j + this.m_fallingBlock.y] != Game.EMPTY_CELL) {
return;// there was collision therefore return
}
}
}
}
// Move the falling piece if there was wall collision and it's a legal move
if(wallDisplace != 0) {
this.m_fallingBlock.x += wallDisplace;
}
// There are no collisions, replace tetromino cells with rotated cells
for(i = 0; i < Game.TETROMINO_SIZE; ++i) {
for(j = 0; j < Game.TETROMINO_SIZE; ++j) {
this.m_fallingBlock.cells[i][j] = rotated[i][j];
}
}
this.onTetrominoMoved();
}// Check if tetromino will collide with something if it is moved in the requested direction.
// If there are collisions returns 1 else returns 0.
;
Game.prototype.checkCollision = function (dx, dy) {
var newx = this.m_fallingBlock.x + dx;
var newy = this.m_fallingBlock.y + dy;
for(var i = 0; i < this.m_fallingBlock.size; ++i) {
for(var j = 0; j < this.m_fallingBlock.size; ++j) {
if(this.m_fallingBlock.cells[i][j] != Game.EMPTY_CELL) {
// Check the tetromino would be inside the left, right and bottom borders
if((newx + i < 0) || (newx + i >= Game.BOARD_TILEMAP_WIDTH) || (newy + j >= Game.BOARD_TILEMAP_HEIGHT)) {
return true;
}
// Check the tetromino won't collide with existing cells in the map
if(this.m_map[newx + i][newy + j] != Game.EMPTY_CELL) {
return true;
}
}
}
}
return false;
}// Game scoring:http://tetris.wikia.com/wiki/Scoring
;
Game.prototype.onFilledRows = function (filledRows) {
// Update total number of filled rows
this.m_stats.lines += filledRows;
// Increase score accordingly to the number of filled rows
switch(filledRows) {
case 1: {
this.m_stats.score += (Game.SCORE_1_FILLED_ROW * (this.m_stats.level + 1));
break;
}
case 2: {
this.m_stats.score += (Game.SCORE_2_FILLED_ROW * (this.m_stats.level + 1));
break;
}
case 3: {
this.m_stats.score += (Game.SCORE_3_FILLED_ROW * (this.m_stats.level + 1));
break;
}
case 4: {
this.m_stats.score += (Game.SCORE_4_FILLED_ROW * (this.m_stats.level + 1));
break;
}
default: {
// This shouldn't happen, but if happens kill the game
this.m_errorCode = Game.ERROR_ASSERT;
}
}
// Check if we need to update the level
if(this.m_stats.lines >= Game.FILLED_ROWS_FOR_LEVEL_UP * (this.m_stats.level + 1)) {
this.m_stats.level++;
// Increase speed for falling tetrominoes
this.m_fallingDelay = Math.floor(Game.DELAY_FACTOR_FOR_LEVEL_UP * this.m_fallingDelay / Game.DELAY_DIVISOR_FOR_LEVEL_UP);
}
}// Move tetromino in the direction specified by (x, y) (in tile units)
// This function detects if there are filled rows or if the move
// lands a falling tetromino, also checks for game over condition.
;
Game.prototype.moveTetromino = function (x, y) {
var i, j;
// Check if the move would create a collision
if(this.checkCollision(x, y)) {
// In case of collision check if move was downwards (y == 1)
if(y == 1) {
// Check if collision occurs when the falling
// tetromino is on the 1st or 2nd row
if(this.m_fallingBlock.y <= 1) {
this.m_isOver = true// if this happens the game is over
;
} else {
// The falling tetromino has reached the bottom,
// so we copy their cells to the board map
for(i = 0; i < this.m_fallingBlock.size; ++i) {
for(j = 0; j < this.m_fallingBlock.size; ++j) {
if(this.m_fallingBlock.cells[i][j] != Game.EMPTY_CELL) {
this.m_map[this.m_fallingBlock.x + i][this.m_fallingBlock.y + j] = this.m_fallingBlock.cells[i][j];
}
}
}
// Check if the landing tetromino has created full rows
var numFilledRows = 0;
for(j = 1; j < Game.BOARD_TILEMAP_HEIGHT; ++j) {
var hasFullRow = true;
for(i = 0; i < Game.BOARD_TILEMAP_WIDTH; ++i) {
if(this.m_map[i][j] == Game.EMPTY_CELL) {
hasFullRow = false;
break;
}
}
// If we found a full row we need to remove that row from the map
// we do that by just moving all the above rows one row below
if(hasFullRow) {
for(x = 0; x < Game.BOARD_TILEMAP_WIDTH; ++x) {
for(y = j; y > 0; --y) {
this.m_map[x][y] = this.m_map[x][y - 1];
}
}
numFilledRows++// increase filled row counter
;
}
}
// Update game statistics
if(numFilledRows > 0) {
this.onFilledRows(numFilledRows);
}
this.m_stats.totalPieces++;
this.m_stats.pieces[this.m_fallingBlock.type]++;
// Use preview tetromino as falling tetromino.
// Copy preview tetromino for falling tetromino
for(i = 0; i < Game.TETROMINO_SIZE; ++i) {
for(j = 0; j < Game.TETROMINO_SIZE; ++j) {
this.m_fallingBlock.cells[i][j] = this.m_nextBlock.cells[i][j];
}
}
this.m_fallingBlock.size = this.m_nextBlock.size;
this.m_fallingBlock.type = this.m_nextBlock.type;
// Reset position
this.m_fallingBlock.y = 0;
this.m_fallingBlock.x = Math.floor((Game.BOARD_TILEMAP_WIDTH - this.m_fallingBlock.size) / 2);
this.onTetrominoMoved();
// Create next preview tetromino
this.setTetromino(this.m_platform.random() % Game.TETROMINO_TYPES, this.m_nextBlock);
}
}
} else {
// There are no collisions, just move the tetromino
this.m_fallingBlock.x += x;
this.m_fallingBlock.y += y;
}
this.onTetrominoMoved();
}// Hard drop
;
Game.prototype.dropTetromino = function () {
// Shadow has already calculated the landing position.
this.m_fallingBlock.y += this.m_shadowGap;
// Force lock.
this.moveTetromino(0, 1);
// Update score
if(this.m_showShadow) {
this.m_stats.score += Math.floor(Game.SCORE_2_FILLED_ROW * (this.m_stats.level + 1) / Game.SCORE_DROP_WITH_SHADOW_DIVISOR);
} else {
this.m_stats.score += Math.floor(Game.SCORE_2_FILLED_ROW * (this.m_stats.level + 1) / Game.SCORE_DROP_DIVISOR);
}
}// This event is called when the falling tetromino is moved
;
Game.prototype.onTetrominoMoved = function () {
var y = 0;
// Calculate number of cells where shadow tetromino would be
while(!this.checkCollision(0, ++y)) {
; ;
}
this.m_shadowGap = y - 1;
this.m_stateChanged = true;
}// Game events are stored in bits in this variable.
// It must be cleared to EVENT_NONE after being used.
;
return Game;
})();
Stc.Game = Game;
var PlatformHTML5 = (function () {
function PlatformHTML5(image) {
this.m_image = image;
var canvas = document.getElementById('canvasBack');
// Draw background
var context = canvas.getContext('2d');
context.drawImage(this.m_image, 0, PlatformHTML5.TEXTURE_SIZE - PlatformHTML5.SCREEN_HEIGHT, PlatformHTML5.SCREEN_WIDTH, PlatformHTML5.SCREEN_HEIGHT, 0, 0, PlatformHTML5.SCREEN_WIDTH, PlatformHTML5.SCREEN_HEIGHT);
canvas = document.getElementById('canvasStats');
this.m_canvasStats = canvas.getContext('2d');
canvas = document.getElementById('canvas');
this.m_canvas = canvas.getContext('2d');
// Register events.
var myself = this;
function handlerKeyDown(event) {
myself.onKeyDown(event);
}
window.addEventListener('keydown', handlerKeyDown, false);
function handlerKeyUp(event) {
myself.onKeyUp(event);
}
window.addEventListener('keyup', handlerKeyUp, false);
function handlerTouchDown(event) {
myself.onTouchStart(event);
}
canvas.onmousedown = handlerTouchDown;
function handlerTouchEnd(event) {
myself.onTouchEnd(event);
}
canvas.onmouseup = handlerTouchEnd;
}
PlatformHTML5.SCREEN_WIDTH = 480;
PlatformHTML5.SCREEN_HEIGHT = 320;
PlatformHTML5.TILE_SIZE = 12;
PlatformHTML5.BOARD_X = 180;
PlatformHTML5.BOARD_Y = 28;
PlatformHTML5.PREVIEW_X = 112;
PlatformHTML5.PREVIEW_Y = 232;
PlatformHTML5.SCORE_X = 72;
PlatformHTML5.SCORE_Y = 86;
PlatformHTML5.SCORE_LENGTH = 10;
PlatformHTML5.LINES_X = 108;
PlatformHTML5.LINES_Y = 68;
PlatformHTML5.LINES_LENGTH = 5;
PlatformHTML5.LEVEL_X = 108;
PlatformHTML5.LEVEL_Y = 50;
PlatformHTML5.LEVEL_LENGTH = 5;
PlatformHTML5.TETROMINO_X = 425;
PlatformHTML5.TETROMINO_L_Y = 79;
PlatformHTML5.TETROMINO_I_Y = 102;
PlatformHTML5.TETROMINO_T_Y = 126;
PlatformHTML5.TETROMINO_S_Y = 150;
PlatformHTML5.TETROMINO_Z_Y = 174;
PlatformHTML5.TETROMINO_O_Y = 198;
PlatformHTML5.TETROMINO_J_Y = 222;
PlatformHTML5.TETROMINO_LENGTH = 5;
PlatformHTML5.PIECES_X = 418;
PlatformHTML5.PIECES_Y = 246;
PlatformHTML5.PIECES_LENGTH = 6;
PlatformHTML5.NUMBER_WIDTH = 7;
PlatformHTML5.NUMBER_HEIGHT = 9;
PlatformHTML5.TEXTURE_SIZE = 512;
PlatformHTML5.FPS = 35;
PlatformHTML5.TY_1 = 50;
PlatformHTML5.TY_2 = 270;
PlatformHTML5.TY_DOWN = 70;
PlatformHTML5.TY_DROP = 250;
PlatformHTML5.TX_1 = 160;
PlatformHTML5.TX_2 = 320;
PlatformHTML5.KEY_A = 65;
PlatformHTML5.KEY_W = 87;
PlatformHTML5.KEY_S = 83;
PlatformHTML5.KEY_D = 68;
PlatformHTML5.KEY_SPACE = 32;
PlatformHTML5.KEY_LEFT = 37;
PlatformHTML5.KEY_RIGHT = 39;
PlatformHTML5.KEY_UP = 38;
PlatformHTML5.KEY_DOWN = 40;
PlatformHTML5.prototype.showOverlay = function (text) {
this.m_canvas.globalAlpha = 0.4;
this.m_canvas.fillStyle = "rgb(0, 0, 0)";
this.m_canvas.fillRect(0, 0, PlatformHTML5.SCREEN_WIDTH, PlatformHTML5.SCREEN_HEIGHT);
this.m_canvas.globalAlpha = 1;
this.m_canvas.fillStyle = "white";
this.m_canvas.font = "20px monospace";
var textWidth = this.m_canvas.measureText(text).width;
this.m_canvas.fillText(text, (PlatformHTML5.SCREEN_WIDTH - textWidth) / 2, PlatformHTML5.SCREEN_HEIGHT / 2);
};
PlatformHTML5.prototype.onTouchStart = function (event) {
var tx = event.layerX;
var ty = event.layerY;
if(tx < PlatformHTML5.TX_1) {
if(ty < PlatformHTML5.TY_1) {
this.m_game.onEventStart(Game.EVENT_RESTART);
} else {
if(ty < PlatformHTML5.TY_2) {
this.m_game.onEventStart(Game.EVENT_MOVE_LEFT);
} else {
this.m_game.onEventStart(Game.EVENT_SHOW_NEXT);
}
}
} else {
if(tx < PlatformHTML5.TX_2) {
if(ty > PlatformHTML5.TY_DROP) {
this.m_game.onEventStart(Game.EVENT_DROP);
} else {
if(ty > PlatformHTML5.TY_DOWN) {
this.m_game.onEventStart(Game.EVENT_MOVE_DOWN);
} else {
this.m_game.onEventStart(Game.EVENT_ROTATE_CW);
}
}
} else {
if(ty < PlatformHTML5.TY_1) {
if(!this.m_game.isOver()) {
if(!this.m_game.isPaused()) {
this.showOverlay("Game is paused");
} else {
// Force redraw.
this.m_game.setChanged(true);
this.renderGame();
}
this.m_game.onEventStart(Game.EVENT_PAUSE);
}
} else {
if(ty < PlatformHTML5.TY_2) {
this.m_game.onEventStart(Game.EVENT_MOVE_RIGHT);
} else {
this.m_game.onEventStart(Game.EVENT_SHOW_SHADOW);
}
}
}
}
console.info("-- touchStart:" + tx + " " + ty);
};
PlatformHTML5.prototype.onTouchEnd = function (event) {
this.m_game.onEventEnd(Game.EVENT_MOVE_LEFT);
this.m_game.onEventEnd(Game.EVENT_MOVE_RIGHT);
this.m_game.onEventEnd(Game.EVENT_MOVE_DOWN);
this.m_game.onEventEnd(Game.EVENT_ROTATE_CW);
};
PlatformHTML5.prototype.onKeyDown = function (event) {
var key = (event.which) ? event.which : event.keyCode;
switch(key) {
case PlatformHTML5.KEY_A:
case PlatformHTML5.KEY_LEFT: {
this.m_game.onEventStart(Game.EVENT_MOVE_LEFT);
break;
}
case PlatformHTML5.KEY_D:
case PlatformHTML5.KEY_RIGHT: {
this.m_game.onEventStart(Game.EVENT_MOVE_RIGHT);
break;
}
case PlatformHTML5.KEY_W:
case PlatformHTML5.KEY_UP: {
this.m_game.onEventStart(Game.EVENT_ROTATE_CW);
break;
}
case PlatformHTML5.KEY_S:
case PlatformHTML5.KEY_DOWN: {
this.m_game.onEventStart(Game.EVENT_MOVE_DOWN);
break;
}
case PlatformHTML5.KEY_SPACE: {
this.m_game.onEventStart(Game.EVENT_DROP);
break;
}
}
};
PlatformHTML5.prototype.onKeyUp = function (event) {
var key = (event.which) ? event.which : event.keyCode;
switch(key) {
case PlatformHTML5.KEY_LEFT: {
this.m_game.onEventEnd(Game.EVENT_MOVE_LEFT);
break;
}
case PlatformHTML5.KEY_RIGHT: {
this.m_game.onEventEnd(Game.EVENT_MOVE_RIGHT);
break;
}
case PlatformHTML5.KEY_UP: {
this.m_game.onEventEnd(Game.EVENT_ROTATE_CW);
break;
}
case PlatformHTML5.KEY_DOWN: {
this.m_game.onEventEnd(Game.EVENT_MOVE_DOWN);
break;
}
}
}// Initializes platform
;
PlatformHTML5.prototype.init = function (game) {
this.m_game = game;
return Game.ERROR_NONE;
}// Clear resources used by platform
;
PlatformHTML5.prototype.end = function () {
this.m_game = null;
}// Process events and notify game
;
PlatformHTML5.prototype.processEvents = function () {
// Events are handled by document handlers, nothing to do here.
}// Render the state of the game
;
PlatformHTML5.prototype.renderGame = function () {
var i, j;
// Check if the game state has changed, if so redraw
if(this.m_game.hasChanged()) {
// Clear canvas.
this.m_canvas.clearRect(0, 0, PlatformHTML5.SCREEN_WIDTH, PlatformHTML5.SCREEN_HEIGHT);
// Draw preview block
if(this.m_game.showPreview()) {
for(i = 0; i < Game.TETROMINO_SIZE; ++i) {
for(j = 0; j < Game.TETROMINO_SIZE; ++j) {
if(this.m_game.nextBlock().cells[i][j] != Game.EMPTY_CELL) {
this.drawTile(PlatformHTML5.PREVIEW_X + (PlatformHTML5.TILE_SIZE * i), PlatformHTML5.PREVIEW_Y + (PlatformHTML5.TILE_SIZE * j), this.m_game.nextBlock().cells[i][j], false);
}
}
}
}
// Draw shadow tetromino
if(this.m_game.showShadow() && this.m_game.shadowGap() > 0) {
for(i = 0; i < Game.TETROMINO_SIZE; ++i) {
for(j = 0; j < Game.TETROMINO_SIZE; ++j) {
if(this.m_game.fallingBlock().cells[i][j] != Game.EMPTY_CELL) {
this.drawTile(PlatformHTML5.BOARD_X + (PlatformHTML5.TILE_SIZE * (this.m_game.fallingBlock().x + i)), PlatformHTML5.BOARD_Y + (PlatformHTML5.TILE_SIZE * (this.m_game.fallingBlock().y + this.m_game.shadowGap() + j)), this.m_game.fallingBlock().cells[i][j], true);
}
}
}
}
// Draw the cells in the board
for(i = 0; i < Game.BOARD_TILEMAP_WIDTH; ++i) {
for(j = 0; j < Game.BOARD_TILEMAP_HEIGHT; ++j) {
if(this.m_game.getCell(i, j) != Game.EMPTY_CELL) {
this.drawTile(PlatformHTML5.BOARD_X + (PlatformHTML5.TILE_SIZE * i), PlatformHTML5.BOARD_Y + (PlatformHTML5.TILE_SIZE * j), this.m_game.getCell(i, j), false);
}
}
}
// Draw falling tetromino
for(i = 0; i < Game.TETROMINO_SIZE; ++i) {
for(j = 0; j < Game.TETROMINO_SIZE; ++j) {
if(this.m_game.fallingBlock().cells[i][j] != Game.EMPTY_CELL) {
this.drawTile(PlatformHTML5.BOARD_X + (PlatformHTML5.TILE_SIZE * (this.m_game.fallingBlock().x + i)), PlatformHTML5.BOARD_Y + (PlatformHTML5.TILE_SIZE * (this.m_game.fallingBlock().y + j)), this.m_game.fallingBlock().cells[i][j], false);
}
}
}
// Draw game statistic data
if(!this.m_game.isPaused()) {
// Clear stats canvas.
this.m_canvasStats.clearRect(0, 0, PlatformHTML5.SCREEN_WIDTH, PlatformHTML5.SCREEN_HEIGHT);
this.drawNumber(PlatformHTML5.LEVEL_X, PlatformHTML5.LEVEL_Y, this.m_game.stats().level, PlatformHTML5.LEVEL_LENGTH, Game.COLOR_WHITE);
this.drawNumber(PlatformHTML5.LINES_X, PlatformHTML5.LINES_Y, this.m_game.stats().lines, PlatformHTML5.LINES_LENGTH, Game.COLOR_WHITE);
this.drawNumber(PlatformHTML5.SCORE_X, PlatformHTML5.SCORE_Y, this.m_game.stats().score, PlatformHTML5.SCORE_LENGTH, Game.COLOR_WHITE);
this.drawNumber(PlatformHTML5.TETROMINO_X, PlatformHTML5.TETROMINO_L_Y, this.m_game.stats().pieces[Game.TETROMINO_L], PlatformHTML5.TETROMINO_LENGTH, Game.COLOR_ORANGE);
this.drawNumber(PlatformHTML5.TETROMINO_X, PlatformHTML5.TETROMINO_I_Y, this.m_game.stats().pieces[Game.TETROMINO_I], PlatformHTML5.TETROMINO_LENGTH, Game.COLOR_CYAN);
this.drawNumber(PlatformHTML5.TETROMINO_X, PlatformHTML5.TETROMINO_T_Y, this.m_game.stats().pieces[Game.TETROMINO_T], PlatformHTML5.TETROMINO_LENGTH, Game.COLOR_PURPLE);
this.drawNumber(PlatformHTML5.TETROMINO_X, PlatformHTML5.TETROMINO_S_Y, this.m_game.stats().pieces[Game.TETROMINO_S], PlatformHTML5.TETROMINO_LENGTH, Game.COLOR_GREEN);
this.drawNumber(PlatformHTML5.TETROMINO_X, PlatformHTML5.TETROMINO_Z_Y, this.m_game.stats().pieces[Game.TETROMINO_Z], PlatformHTML5.TETROMINO_LENGTH, Game.COLOR_RED);
this.drawNumber(PlatformHTML5.TETROMINO_X, PlatformHTML5.TETROMINO_O_Y, this.m_game.stats().pieces[Game.TETROMINO_O], PlatformHTML5.TETROMINO_LENGTH, Game.COLOR_YELLOW);
this.drawNumber(PlatformHTML5.TETROMINO_X, PlatformHTML5.TETROMINO_J_Y, this.m_game.stats().pieces[Game.TETROMINO_J], PlatformHTML5.TETROMINO_LENGTH, Game.COLOR_BLUE);
this.drawNumber(PlatformHTML5.PIECES_X, PlatformHTML5.PIECES_Y, this.m_game.stats().totalPieces, PlatformHTML5.PIECES_LENGTH, Game.COLOR_WHITE);
}
if(this.m_game.isOver()) {
this.showOverlay("Game is over");
}
// Inform the game that we are done with the changed state
this.m_game.onChangeProcessed();
}
}// Return the current system time in milliseconds
;
PlatformHTML5.prototype.getSystemTime = function () {
return Date.now();
}// Return a random positive integer number
;
PlatformHTML5.prototype.random = function () {
// JavaScript maximum integer number is 2^53 = 9007199254740992.
return Math.floor(9007199254740992 * Math.random());
};
PlatformHTML5.prototype.drawTile = function (x, y, tile, shadow) {
this.m_canvas.drawImage(this.m_image, PlatformHTML5.TILE_SIZE * (shadow ? Game.TETROMINO_TYPES + tile + 1 : tile), 0, PlatformHTML5.TILE_SIZE, PlatformHTML5.TILE_SIZE, x, y, PlatformHTML5.TILE_SIZE, PlatformHTML5.TILE_SIZE);
};
PlatformHTML5.prototype.drawNumber = function (x, y, value, length, color) {
var pos = 0;
do {
this.m_canvasStats.drawImage(this.m_image, PlatformHTML5.NUMBER_WIDTH * (value % 10), 1 + PlatformHTML5.TILE_SIZE + PlatformHTML5.NUMBER_HEIGHT * color, PlatformHTML5.NUMBER_WIDTH, PlatformHTML5.NUMBER_HEIGHT, x + PlatformHTML5.NUMBER_WIDTH * (length - pos), y, PlatformHTML5.NUMBER_WIDTH, PlatformHTML5.NUMBER_HEIGHT);
value = Math.floor(value / 10);
}while(++pos < length)
};
return PlatformHTML5;
})();
Stc.PlatformHTML5 = PlatformHTML5;
})(Stc || (Stc = {}));
//------------------------------------------------
var image = new Image();
image.src = "http://sites.google.com/site/exeqtor/stc_sprites.png";
image.onload = function () {
var platform = new Stc.PlatformHTML5(image);
var game = new Stc.Game();
game.init(platform);
function update() {
game.update();
}
setInterval(update, 1000 / Stc.PlatformHTML5.FPS);
};
</script>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
I did not use any fancy JavaScript library for this little game so this probably works only in the latest versions of Firefox and Chrome. Use your mouse for control, clicking in the left/right side of the board would move the falling piece to the left/right side, clicking at the bottom of the board would drop the piece and clicking at the top of the board would make the piece rotate. The stand alone version <a href="http://ex.github.io/js/src/stc/">can be found here</a> (you can use your keyboard there).<br />
<br />
TypeScript really shines when you are using their IDE
support with Visual Studio, because then you can fix your
errors at edition time and you get intellisense and all the other VS
goodies (I can use all my keyboard shortcuts from VC++!!). This is nothing new: <a href="http://www.dartlang.org/">Dart</a>
tried before but it didn't grab my attention. Also in Windows I prefer Visual Studio to any Eclipse based IDE
if possible. By the way the VS team must be proud, the Visual Studio 2012 edition is pretty gorgeous looking and it's very fast. The 2010 version was ugly and slow as hell (I still avoid it as the plague), and following the tendency I was expecting an even worse experience now but it seems they somehow pulled it off:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://sites.google.com/site/exeqtor/ts00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="379" src="http://sites.google.com/site/exeqtor/ts00.png" width="640" /></a></div>
<br />
It's interesting to see somebody with brains
at the top management level behind this Microsoft move, because they didn't screwed this opportunity with things like:<br />
<ul>
<li>we must make this closed source or use
one of our Microsoft licenses: instead they used the Apache 2 license and that for patents issues is
even better than the MIT license. They are even using git for version control.</li>
<li>we must give IDE support only for the
paid Professional Edition of Visual Studio: the internet is full of high quality open-source-cross-platform
IDEs (<a href="http://qt.digia.com/Product/Developer-Tools/">QtCreator </a>any?). I don't see many people paying big bucks for a Windows-only IDE to beta test a new language.</li>
<li>let's bundle TypeScript with some ASP.Net crap and make it work the best in IE only: again no sign of Microsoft only technologies, they support Node.js and their demo was showcased in Chrome (the nerve!).</li>
</ul>
Of course they are not fooling anybody, Microsoft is doing this because it's in their best interest not to lose anything more with the web developer community after all the mess they helped to create. As anecdotal evidence, I didn't
touch any client side JavaScript code for ages due to the terrible
reputation of browsers like IE6, IE7 and IE8. I was under the impression that doing web development shortened your life fixing stupid problems, and I really admire the programmers that created things like Gmail and Facebook with so brittle tools. Things are improving
it seems, but Microsoft still needs to do a lot. I'm not leaving Firefox for
browsing or Chrome for development any time soon. I hope they don't
miss their chance to implement the WebGL specification really soon or their browser is going to lack severely in the 3D games section. Making a whole new engine just to support IE when my game already works in all the other browsers is not going to happen unless there is a gun over my head or a big fat check under my pocket. At this time only grannies and prisoners use IE anyways and they don't play many games.<br />
<br />exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com6tag:blogger.com,1999:blog-2994292646876077363.post-25688649375787846332012-11-04T03:36:00.002-05:002012-11-10T00:06:12.633-05:00Bitwise operators in C, JavaScript, Lua and Killa<div class="tr_bq">
So I thought that porting a <a href="http://en.wikipedia.org/wiki/Game_boy_advance">Game Boy Advance</a> (GBA) console emulator written in JavaScript to Killa would be fun, boy I was wrong... All because I wanted to play with this:</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/bit01.png" /></div>
<br />
<a name='more'></a>I ended ranting about bitwise operators and their issues in the languages I admire.<br />
<br />
<h3>
The case of C</h3>
<br />
Bitwise operations are very useful, especially if you are interfacing with hardware (or emulating it in my case), the C inventors knew this and made <i>the right thing</i> adding these lovely operators to the language from the beginning: ~, &, |, ^, <<, >>. This is not a beginner's tutorial about these operators so if you are not already looking with love these little symbols I'll suggest you <a href="http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/">to read this blog post</a> for some nice tricks with explanations included. Nothing is perfect, however. The original bitwise operators were also used as logical operators in early versions of C (there were no && neither || operators), and thus they had lower priority than the comparison operators ==, !=, <=, <, >=, > to allow <a href="http://en.wikipedia.org/wiki/Short_circuit_evaluation">short circuit evaluations</a>.<br />
<br />
<a href="http://en.wikipedia.org/wiki/Dennis_Ritchie">Dennis Ritchie</a>, the grandfather of <i>all the languages that matter</i>, explains this <a href="http://www.lysator.liu.se/c/dmr-on-or.html">in more depth</a>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dmr.jpg" /></div>
<br />
<blockquote>
<span style="font-family: "Courier New",Courier,monospace;"><br />The priorities of && || vs. == etc. came about in the following way.<br /><br /> Early C had no separate operators for & and && or | and ||. (Got that?) Instead it used the notion (inherited from B and BCPL) of "truth-value context": where a Boolean value was expected, after "if" and "while" and so forth, the & and | operators were interpreted as && and || are now; in ordinary expressions, the bitwise interpretations were used. It worked out pretty well, but was hard to explain. (There was the notion of "top-level operators" in a truth-value context.)<br /><br /> The precedence of & and | were as they are now.<br /><br /> Primarily at the urging of Alan Snyder, the && and || operators were added. This successfully separated the concepts of bitwise operations and short-circuit Boolean evaluation. However, I had cold feet about the precedence problems. For example, there were lots of programs with things like<br /><br /> if (a==b & c==d) ...<br /><br /> In retrospect it would have been better to go ahead and change the precedence of & to higher than ==, but it seemed safer just to split & and && without moving & past an existing operator. (After all, we had several hundred kilobytes of source code, and maybe 3 installations....) </span></blockquote>
<br />
What does this mean for us? Well, it just means that we need to be extra-careful to use additional parenthesis whenever we have expressions mixing bitwise and comparison operators. This isn't a terrible problem <i>per se</i>, because any worthy programmer would use a copious amount of parenthesis anyway, Isn't it?, Isn't it?. But it's bothersome nonetheless because this circumstantial flaw in the design of the C language allows a <a href="http://www.viva64.com/en/d/0159/">sneaky type of bug</a>. It can happens to anybody, it doesn't matter if you work for <a href="http://codereview.chromium.org/9004052/patch/1/3">Google</a> or in the <a href="http://www.viva64.com/en/b/0120/">Doom 3</a> engine, you write something like this and go home happy:<br />
<br />
<script src="https://gist.github.com/4010317.js"> </script>without realizing that behind you your code is really doing this:<br />
<br />
<script src="https://gist.github.com/4010386.js"> </script>when your original intention was to do this:<br />
<br />
<script src="https://gist.github.com/4010400.js"> </script>
The lesson? Use your parenthesis! And never trust in the programmer that says that he knows enough operator precedence rules to avoid using parenthesis. He is lying, nobody can do it, not forever. To make things worse newer languages like <a href="http://en.cppreference.com/w/cpp/language/operator_precedence">C++</a>, <a href="http://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html">Java</a> and <a href="https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Operator_Precedence">JavaScript</a> followed this bad example, all in the name of compatibility.<br />
<br />
How about fixing the operator precedence in C and related languages? Hahaha, don't
dream about it. There are not hundreds of kilobytes of source code and maybe 3
installations now, there are millions and millions! Updating everything
is just impossible. <br />
<br />
<h3>
The case of JavaScript</h3>
<br />
If following the bad example of C was not enough, JavaScript managed to add its own kirks. Because, surely, when you want to use bitwise operators <i>you need them over<b> signed</b> <b>integers</b></i>. And thus the entire code of the JavaScript emulator I was porting was full of (x >>> 0) expressions that make the whole thing smell really ugly. <a href="http://stackoverflow.com/questions/6798111/bitwise-operations-on-32-bit-unsigned-ints">But it was not the programmer fault</a>. In JavaScript you must use >>> 0 to make the result of any bitwise operation interpreted as an <b>unsigned integer</b>. You'll need to translate C bitwise expressions to their JavaScript equivalents in this way:<br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;"> C: (3774191835 >> 2) | 2147483648</span><br />
<span style="font-family: "Courier New",Courier,monospace;">JS: (3774191835 >>> 2 | 2147483648) >>> 0</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> C: 1986735448 << 1</span><br />
<span style="font-family: "Courier New",Courier,monospace;">JS: (1986735448 << 1) >>> 0</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> C: 3774191835 & 4294967295</span><br />
<span style="font-family: "Courier New",Courier,monospace;">JS: (3774191835 & 4294967295) >>> 0</span><br />
<br />
Are you feeling the pain? If JavaScript was going to copy the C bitwise operators (and their flawed precedence) at least it would have maintained the same behavior. Chances of fixing this? The same as making Microsoft, Google, Apple and Mozilla good friends and trashing JavaScript for a better language.<br />
<br />
<h3>
The case of Lua</h3>
<br />
The good news is that Lua doesn't have the above problems. The bad news is that <i>Lua doesn't even have bitwise operators</i>. After some years of user complaining and bitching in <a href="http://lua-users.org/cgi-bin/namazu.cgi?query=bitwise&idxname=lua-l&max=20&result=normal&sort=score">their user list</a>, the benevolent Lua team finally acknowledged that having bitwise operations <i>was a good thing</i>. So they added them to the Lua 5.2 version, but instead of adding them in the the form of sane operators, they added them in the form of library functions. Not only <a href="http://www.lua.org/manual/5.2/manual.html#6.7">they have horrendous names</a> (Lua has <b>not</b>, <b>and</b> and <b>or</b> as keywords, so names like <b>bnot</b>, <b>band</b> and<b> bor</b> where used), the performance is less than stellar, <a href="http://www.sbmintegral.sk/lua/">some guy got bored of wasting CPU cycles</a> and implemented the whole thing with Virtual Machine (VM) opcodes, the performance <a href="http://lua-users.org/lists/lua-l/2011-11/msg00072.html">increased by one order of magnitude</a>. Of course, all this doesn't matter because is not stock Lua and you would still need to write something like this:<br />
<span style="font-family: "Courier New",Courier,monospace;"><br /> C: (3774191835 >> 2) | 2147483648
<br />Lua: bit32.bor(bit32.rshift(3774191835, 2), 2147483648)</span><br />
<br />
But, <i>it's not that ugly</i>... unless you have to port a GBA console emulator.<br />
<br />
<h3>
The case of Killa</h3>
<br />
So I was doing <i>my very own programming language </i>stealing the Lua 5.2 VM and because of that considering to use the same bitwise library <i>for compatibility reasons</i> (or it was my own laziness?). Whichever, the case is that porting the GBA emulator made me realize that <b><i>enough is enough</i></b>, the port was going to take forever and be a real PITA and I was seriously damned if I'd not implemented real bitwise operators<i> </i>for Killa. The good news? I fixed the operators precedence in the process (is now similar to <a href="http://www.techotopia.com/index.php/Ruby_Operator_Precedence">Ruby </a>or <a href="http://www.ibiblio.org/g2swap/byteofpython/read/operator-precedence.html">Python</a>):<br />
<br />
<b><span style="font-family: "Courier New",Courier,monospace;">** Exponentiation<br />! ~ - $ Not, complement, unary minus, length<br />* / % Multiply, divide, and modulo<br />+ - Addition and subtraction<br />>> << Right and left bitwise shift<br />& Bitwise AND<br />^ Bitwise XOR<br />| Bitwise OR<br />.. String Concatenation<br />== != <= < > >= Comparison operators<br />&& Logical AND<br />|| Logical OR</span></b><br />
<br />
The bitwise operators were implemented as VM opcodes for maximum performance. For give you an idea of how fast they are consider <a href="http://elrinconde-ex.blogspot.com/2012/09/killa-my-scripting-language-based-in.html">the problem I solved previously </a>using strings and arrays, that method takes 113.8 seconds to get the answer for (n = 12). Using bitwise operations this time is reduced to 15 seconds:<br />
<br />
<script src="https://gist.github.com/4010867.js"> </script>And for the record the solution in Python takes 23 seconds. So Killa wins.<br />
<br />
Note: this post <i>has some trolling bits</i>, I hope it didn't offend you <i>much</i>.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com6tag:blogger.com,1999:blog-2994292646876077363.post-58601185131098842412012-11-01T14:56:00.001-05:002012-11-02T22:51:10.235-05:00Dynamic programming<div class="separator" style="clear: both; text-align: center;">
</div>
Dynamic Programming (DP) problems are fun, but they can be also difficult to solve. For a better definition than I could give you here I'll redirect you to the <a href="http://en.wikipedia.org/wiki/Dynamic_programming">Wikipedia article</a>, and there are some <a href="http://www.topcoder.com/tc?d1=tutorials&d2=dynProg&module=Static">nice tutorials</a> and <a href="http://apps.topcoder.com/forums/?module=Thread&threadID=697369&start=0">sample problems</a> in TopCoder and <a href="http://www.codechef.com/wiki/tutorial-dynamic-programming">CodeChef</a>. In this post I'm just going to add commentaries to the DP problem <a href="http://elrinconde-ex.blogspot.com/2012/09/killa-my-scripting-language-based-in.html">I solved previously</a> without giving much explanation. This problem comes from the <a href="http://projecteuler.net/problem=393">Project Euler</a> site:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"><b>An n×n grid of squares contains n^2 ants, one ant per square.</b></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><b>All ants decide to move simultaneously to an adjacent square</b></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><b>(usually 4 possibilities, except for ants on the edge of the grid or at the corners). </b></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><b>We define f(n) to be the number of ways this can happen without any ants ending on </b></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><b>the same square and without any two ants crossing the same edge between two squares.</b></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><b>You are given that f(4) = 88. Find f(10).</b>
</span><br />
<br />
<a name='more'></a>
So where to start? Well, if Mr. Intelligence is not around you can always trust in your old pal the Brute Force. <a href="http://elrinconde-ex.blogspot.com/2012/09/combinatorial-explosion-and-project.html">In this post</a> I sketched the code for computing (and drawing) all the different board positions for (n = 4) using a DFS algorithm:<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dp01.png" /></div>
Let's try to guess how much time it would take this algorithm to calculate the number of possible boards when (n = 10). Every ant can move to 2, 3 or 4 cells, however the limitations of 2 ants never crossing the same border and not leaving empty cells make the real options less than 4, lets say that in average every ant has 2 possible options, because there are (n^2) ants this means that there are still (2^(n^2)) = 2^100 different boards. For give you an idea about how much time it would take to compute this, lets say we are using C, and that the code to compute one final board position is almost trivial as the code below:<br />
<br />
<script src="https://gist.github.com/3978133.js?file=gistfile1.c"></script>If this were the case the time my computer would take to compute 10^9 boards would be nearly
0.8 seconds (with an Intel Core 2 Duo at 2.4 GHz for processor). Lets round that time to one second for 10^9 boards, therefore it would take (2^100/10^9) seconds to compute all the other boards. This is more than 40 millions of millions of years! Forever. However, this is just a wild guess, we'll see that the constraints in this particular problem are a lot more restrictive and that the real answer is not that big, (more in the order of 2^57) and I'll need to await "only" 1300 days to get the answer.<br />
<br />
So, How do we compute this faster? This is where DP comes to save the day. The main idea behind DP is to construct the final answer from the smaller parts. What is the smallest part in this problem? A single cell. <b>We need to find a way to construct the final board adding one cell in every step</b>. With every added cell we need to maintain the list of incomplete boards already formed. The total number of these boards when the whole board is assembled is our answer. Easier said than done, let's go step by step. What are the valid moves for the first cell?<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dp02.png" /></div>
Every arrow is an ant passing the border going inside or outside the cell. As we add cells to our zone (the solid color) we need to check that the number of arrows entering the zone is equal to the number of arrows leaving the zone, because the contrary would imply that there are empty cells or cells with more than one ant per cell in the zone. What are examples of invalid moves?<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dp03.png" /></div>
The first case is invalid because we need all the ants to move. We can not add a cell to the occupied zone without ants crossing that cell. The second case is invalid because it would create a cell with more than one ant in the cell. The third case is invalid because it needs that 2 ants are already in the cell, which is not permitted. The fourth case is invalid because the problem states that 2 ants can not pass the same border.<br />
<br />
The tree below shows the whole process for (n = 2) considering only valid moves. Every node is an incomplete board and their children are linked by lines. Every step is a row in the graph. The cell that we'll add to our zone in the next step in marked with a red border. As expected there are only 2 ways to assemble the final board:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dp04.png" /></div>
Lets do some steps for the case of (n = 4):<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dp05.png" /></div>
There are invalid nodes that are discarded like nodes 4.2 and 6.8, and some nodes that are duplicated and we consolidate in one like 6.6. Every parent can have at most 2 children, some parents have 1 or no children so
we could have better estimated the number of options per cell as 1.5
instead of 2 and our wild guessing of the number of final boards would be (1.5^(n^2))
instead of (2^(n^2)). We could start coding the solution sketched up to this point. However there are some observations that would help us to simplify the solution.<br />
<br />
The first observation looking at the above graph is that in every step not all the cells are required to deduce the next board, all the action is happening in the border between the zone and the empty board, so we could ignore all the board cells except
the cells at the zone frontier (the ones marked with red at the right):<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dp06.png" /></div>
The second observation is more tricky and easy to miss. It comes from the realization that for every iteration it really doesn't matter if the arrows go inside or leave our zone from the bottom or the right border. The result is the same: <br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dp07.png" /></div>
So we reduce our possibilities for every cell as EMPTY (no ants crossing the border), IN (an ant going inside the occupied zone), OUT (an ant leaving the zone), IN+OUT (2 ants passing the cell border). This reduces the above graph to this:<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dp08.png" /></div>
The code for generating the next step can be deduced following the graph below: <br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dp09.png" /></div>
Additionally, we'll need to check for valid ranges and avoiding to create invalid cells, like for example: we can't add arrows to the right if we are in the last column, or arrows to the bottom if we are in the last row, or an arrow to the bottom if there is already an arrow with the same type there. The script below shows the steps for (n = 4):<br />
<br />
<div style="text-align: left;">
<canvas color="0x000000" height="280" id="canvas" width="600"> </canvas>
</div>
<script type="application/javascript">
var SIZE = 19;
var IFILL = 1;
var IBORDER = 2;
var IRIN = 4;
var IROUT = 8;
var IBIN = 16;
var IBOUT = 32;
var IIN = 64;
var IOUT = 128;
var IERROR = 256;
var ctx;
function drawState(x, y, w, h, board, noBorder, number) {
ctx.strokeStyle = "rgb(170, 230, 90)";
// Draw board.
for (var k = 0; k < w * h; ++k) {
var px = x + Math.floor(k / h) * SIZE;
var py = y + (k % h) * SIZE;
if ((board[k] & IFILL) != 0) {
ctx.fillStyle = "rgb(170, 210, 60)";
ctx.fillRect(px, py, SIZE, SIZE);
}
else if ((board[k] & IERROR) != 0) {
ctx.fillStyle = "rgb(250, 190, 180)";
ctx.fillRect(px, py, SIZE, SIZE);
}
else {
ctx.strokeRect(px, py, SIZE, SIZE);
}
}
if (!noBorder) {
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + w * SIZE, y);
ctx.lineTo(x + w * SIZE, y + h * SIZE);
ctx.lineTo(x, y + h * SIZE);
ctx.lineTo(x, y);
ctx.strokeStyle = "rgb(180, 90, 190)";
ctx.stroke();
}
// Draw border and arrows over the board.
for (var k = 0; k < w * h; ++k) {
var px = x + Math.floor(k / h) * SIZE;
var py = y + (k % h) * SIZE;
if ((board[k] & IBORDER) != 0) {
ctx.strokeStyle = "rgb(220, 30, 40)";
ctx.strokeRect(px, py, SIZE, SIZE);
}
if ((board[k] & 60) != 0) {
ctx.strokeStyle = "black";
ctx.fillStyle = "black";
if ((board[k] & IROUT) != 0) {
ctx.beginPath();
ctx.moveTo(px + 0.8 * SIZE, py + 0.5 * SIZE);
ctx.lineTo(px + 1.2 * SIZE, py + 0.5 * SIZE);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(px + 1.2 * SIZE, py + 0.3 * SIZE);
ctx.lineTo(px + 1.5 * SIZE, py + 0.5 * SIZE);
ctx.lineTo(px + 1.2 * SIZE, py + 0.7 * SIZE);
ctx.lineTo(px + 1.2 * SIZE, py + 0.3 * SIZE);
ctx.fill();
}
if ((board[k] & IRIN) != 0) {
ctx.beginPath();
ctx.moveTo(px + 1.2 * SIZE, py + 0.5 * SIZE);
ctx.lineTo(px + 0.8 * SIZE, py + 0.5 * SIZE);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(px + 0.8 * SIZE, py + 0.3 * SIZE);
ctx.lineTo(px + 0.5 * SIZE, py + 0.5 * SIZE);
ctx.lineTo(px + 0.8 * SIZE, py + 0.7 * SIZE);
ctx.lineTo(px + 0.8 * SIZE, py + 0.3 * SIZE);
ctx.fill();
}
if ((board[k] & IBOUT) != 0) {
ctx.beginPath();
ctx.moveTo(px + 0.5 * SIZE, py + 0.8 * SIZE);
ctx.lineTo(px + 0.5 * SIZE, py + 1.2 * SIZE);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(px + 0.3 * SIZE, py + 1.2 * SIZE);
ctx.lineTo(px + 0.5 * SIZE, py + 1.5 * SIZE);
ctx.lineTo(px + 0.7 * SIZE, py + 1.2 * SIZE);
ctx.lineTo(px + 0.3 * SIZE, py + 1.2 * SIZE);
ctx.fill();
}
if ((board[k] & IBIN) != 0) {
ctx.beginPath();
ctx.moveTo(px + 0.5 * SIZE, py + 1.3 * SIZE);
ctx.lineTo(px + 0.5 * SIZE, py + 0.8 * SIZE);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(px + 0.3 * SIZE, py + 0.8 * SIZE);
ctx.lineTo(px + 0.5 * SIZE, py + 0.5 * SIZE);
ctx.lineTo(px + 0.7 * SIZE, py + 0.8 * SIZE);
ctx.lineTo(px + 0.3 * SIZE, py + 0.8 * SIZE);
ctx.fill();
}
}
else {
if ((board[k] & IIN) != 0) {
ctx.beginPath();
ctx.moveTo(px + 0.2 * SIZE, py + 0.3 * SIZE);
ctx.lineTo(px + 0.8 * SIZE, py + 0.1 * SIZE);
ctx.lineTo(px + 0.8 * SIZE, py + 0.5 * SIZE);
ctx.lineTo(px + 0.2 * SIZE, py + 0.3 * SIZE);
ctx.fillStyle = "red";
ctx.fill();
}
if ((board[k] & IOUT) != 0) {
ctx.beginPath();
ctx.moveTo(px + 0.2 * SIZE, py + 0.9 * SIZE);
ctx.lineTo(px + 0.8 * SIZE, py + 0.7 * SIZE);
ctx.lineTo(px + 0.2 * SIZE, py + 0.5 * SIZE);
ctx.lineTo(px + 0.2 * SIZE, py + 0.9 * SIZE);
ctx.fillStyle = "blue";
ctx.fill();
}
}
}
if (number > 1) {
ctx.font = "15px verdana";
ctx.fillStyle = "black";
ctx.fillText(number, x + 2, y + 16);
}
}
var IN = 1;
var OUT = 2;
// Insert board to state.
// If the board is already present just increase the board counter.
// Board data is stored as: [boardCount, boardArray]
function insertBoard(state, boardArray, boardCount) {
var boardKey = boardArray.join(",");
if (!state[boardKey]) {
state[boardKey] = [boardCount, boardArray];
}
else {
state[boardKey][0] += boardCount;
}
}
function clone(t) {
return t.slice(0);
}
var states = [];
var stepDraw = 0;
function euler_393(n) {
// Initial board has no ants moving IN or OUT (all zero)
var initBoard = [];
for (var k = 0; k < n; k += 1) {
initBoard[k] = 0;
}
// Initial state contains only the initial board
var state = {};
var init = initBoard.join(",");
states.push(state);
// We are going to use a data type: [counter, board] for storing boards
state[init] = [1, initBoard];
// Columns
for (var col = 0; col < n; ++col) {
// Rows
for (var row = 0; row < n; ++row) {
var newState = {};
var newBoard;
for (var key in state) {
if (state.hasOwnProperty(key)) {
var boardCount = state[key][0];
var board = state[key][1];
if (board[row] == 0) {
if ((row < n - 1) && (col < n - 1)) {
if (board[row + 1] != IN) {
newBoard = clone(board);
newBoard[row] = OUT;
newBoard[row + 1] += IN;
insertBoard(newState, newBoard, boardCount);
}
if (board[row + 1] != OUT) {
newBoard = clone(board);
newBoard[row] = IN;
newBoard[row + 1] += OUT;
insertBoard(newState, newBoard, boardCount);
}
}
}
else if (board[row] == IN) {
if ((row < n - 1) && (board[row + 1] != IN)) {
newBoard = clone(board);
newBoard[row] = 0;
newBoard[row + 1] += IN;
insertBoard(newState, newBoard, boardCount);
}
if (col < n - 1) {
insertBoard(newState, board, boardCount)
}
}
else if (board[row] == OUT) {
if ((row < n - 1) && (board[row + 1] != OUT)) {
newBoard = clone(board);
newBoard[row] = 0;
newBoard[row + 1] += OUT;
insertBoard(newState, newBoard, boardCount);
}
if (col < n - 1) {
insertBoard(newState, board, boardCount);
}
}
else {
// board[row] == IN + OUT
newBoard = clone(board);
newBoard[row] = 0;
insertBoard(newState, newBoard, boardCount);
}
}
}
state = newState;
states.push(state);
}
}
return (state[init] != null) ? state[init][0] : 0;
}
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
ctx = canvas.getContext("2d");
console.log(euler_393(4));
}
function drawStep(step) {
var state = states[step];
var k = 0;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var key in state) {
if (state.hasOwnProperty(key)) {
var boardCount = state[key][0];
var board = [];
var cols = Math.floor(step / 4);
for (var i = 0; i < cols; ++i) {
board = board.concat(1, 1, 1, 1);
}
for (var i = 0; i < step % 4; ++i) {
board = board.concat(1);
}
for (var i = 0; i < 4; ++i) {
var index = 4 * (cols + ((i < step % 4) ? 1 : 0)) + i;
switch (state[key][1][i]) {
case IN:
board[index] = IIN;
break;
case OUT:
board[index] = IOUT;
break;
case (IN + OUT):
board[index] = IIN + IOUT;
break;
}
}
drawState(5 * SIZE * (k % 6), 5 * SIZE * Math.floor(k / 6), 4, 4, board, false, boardCount);
++k;
}
}
}
drawStep(stepDraw);
function onButtonNext() {
if (stepDraw < 16) {
drawStep(++stepDraw);
}
}
function onButtonPrev() {
if (stepDraw > 0) {
drawStep(--stepDraw);
}
}
</script>
<button onclick="onButtonPrev()" type="button">Previous</button>
<button onclick="onButtonNext()" type="button">Next</button>
<br />
<br />
How much time would take this algorithm to find the answer? Every cell has 4 options, there are (n) cells in the border, so in every step would be a maximum of (4^n) nodes, there are (n^2) steps (one step for every cell) so we could guess it would take (n^2)*(4^n) steps to find the answer (if we use a hash data container to look for duplicated nodes in constant time). For (n = 10) this is 100*4^10 (nearly 10^8). We can finish this in C in less than a second! This is an impressive improvement compared to awaiting 1300 days.<br />
<br />
I hope that with this explanation you are ready to code the solution for yourself. However, if that is not the case, there is not helping but sitting with a pencil and paper and trying by hand the above steps. The little fella below approves:<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/dphand.png" /></div>
<br />exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-46991067624510772232012-10-02T23:37:00.001-05:002012-10-28T23:04:03.337-05:00Ruby and the joy of programmingYears ago I used to solve many <a href="http://projecteuler.net/problems">mathematical problems</a> in Ruby. Looking
backwards, I find difficult to explain my decision, because if I remember
correctly Ruby was slow compared with C++ or Perl, and since most of
the time I ended using a brute force approach to solve what my
imagination could not solve efficiently I often ended awaiting longer
times before obtaining my answers. <br />
<br />
However, I can clearly remember that for the first time in my conscious programming life, I
was happy and I was enjoying my time with some programming language. So I didn't worry
too much about some additional time awaiting a stupid brute force
solution. I was solving mathematical puzzles for fun and with Ruby I
could quickly test some crazy ideas, leave the process running and go to do other things. Ruby
was powerful, concise, elegant, and more importantly it didn't force
over me its formatting policies or push me to do things in some way I
didn't like to. I had finally found a language where everything fitted
in place and seemed natural. I thought: this language must have been
created by someone who really likes having fun while programming.<br />
<br />
And
indeed it was. Ruby was created by Yukihiro Matsumoto (aka Matz), who is one of my heroes actually. I found <a href="http://www.artima.com/intv/rubyP.html">an interview he gave about Ruby</a> and there he said something that really touched a fiber inside me, I have always used his
words as an inspiration:<br />
<br />
<script src="https://gist.github.com/3962607.js?file=gistfile1.txt"></script>
Fun to use. Freedom to choose. This man taught me all.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://sites.google.com/site/exeqtor/yukihiro.png?attredirects=0" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://sites.google.com/site/exeqtor/yukihiro.png?attredirects=0" /></a></div>
<a name='more'></a><br />
Is it a sin to look for fun in our craft? I'm not going to say that programming is the the hardest work in the world, neither the most complex, I believe it has its fine share of angst and grieving but not too much. What I can say for sure is that sometimes as a programmer you'll find yourself cornered for some nasty bugs or trying desperately to find a way to make something work between layers and layers of abstraction and piles of ugly code behind a deadline. And at those dark times getting a helping hand from the language or framework that you are using is really priceless. It would save your head of some heavy banging against the wall. If we as programmers can not make things easy and fun to use for our fellow peers, who is going to do it? Even if we don't get it right the first time, we must not admit defeat and try to do things better the next time. This is important for every programmer but for language and API creators is crucially important. I call this the rule of the "joy of programming". If it is not easy and fun to use, don't bother. <br />
<br />
From these years many things have changed, I no longer use Ruby frequently, I found new
languages with different approaches to programming, learned some new things, but the spirit of Ruby is always with me even now, and I think it would follow me forever, in every thing I'll create I'll try to make things the way Matz tried. Nothing less.<br />
<br />
So I was remembering that quote, and I recalled that I used it for years as a programming exercise for possible hires. I gave them this "encrypted" text<br />
<br />
<script src="https://gist.github.com/3962600.js?file=gistfile1.txt"></script>
along with the frequency of characters used by the language of the original message sorted from more to less used:<br />
<br />
<b><span style="font-family: "Courier New",Courier,monospace;">freqLang = "TEOIARNSHMLYGCPUDWFBVKJXQZ" </span></b><br />
<br />
and it was their task to find the table of frequency of the "encrypted" message, compare it with the one given and substitute the characters. It was not trivial as FizzBuzz at all but with the steps to follow already mentioned in the problem many candidates found the problem interesting and fun. The secret message of course was the nice part for me. So here is the solution to my question in <a href="http://github.com/ex/Killa">Killa</a>:<br />
<br />
<script src="https://gist.github.com/3824991.js"> </script>
After all I created Killa because I wanted to have more fun programming in Lua.<br />
<br />exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com3tag:blogger.com,1999:blog-2994292646876077363.post-17670839913341066572012-09-23T01:00:00.000-05:002012-12-31T08:00:57.725-05:00Killa: my scripting language based in Lua and JavaScriptSo I have been working these past months on my own programming language.
It has been something that I've always wanted to do so I'm very happy that I can announce it here.
Without further ado: <a href="https://github.com/ex/Killa">Killa</a>. I finished the 0.1 version months ago but real life just got busy on my end and I have been slacking this post for ages.<br />
<br />
Killa (moon in the <a href="http://en.wikipedia.org/wiki/Quechua_languages">native tongue of my country</a>) brings no new paradigm, neither disrupting ideas to the world of programming languages, to be honest I just stole some ideas from this book <a href="http://www.amazon.com/gp/product/0596517742/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596517742&linkCode=as2&tag=elrindeex-20">JavaScript: The Good Parts</a><img alt="" border="0" class="bguxdzxjqbkdfpukonnp jwivunqvyoenwjuvhruz rwevihkssibprruusejr vyqrxirtaoxiukkanehm xenpbmvrhimbjdlijbyp envdgkbyavpbepqknmhk" height="1" src="http://www.assoc-amazon.com/e/ir?t=elrindeex-20&l=as2&o=1&a=0596517742" style="border: none !important; margin: 0px !important;" width="1" />
added some new ones from the top of my head and made it all work using the Lua 5.2 virtual machine, so... Why to bother? could you ask. Why indeed, with so many new and wonderful and exciting languages like Clojure, D, Haskell, Haxe, and trusted powerhorses like Python, Ruby and your favorite one here, creating a new half baked one could be just me following the new craze of "everybody can make his own programming language" (in 21 days).<br />
<br />
Something of that was of course present, as I said before, I have always wanted to create a programming language, but even with that idea on mind, investing my time in a horrible language just for the sake of creating "my very own programming language" just doesn't click with me. If I'm going to create a programming language it better must be one that I'd really like to use, and I'm picky, programming after all has been my bread and butter for years. So I started saying that Killa just stole some parts of JavaScript and Lua, looking now backwards it's not surprise that I created Killa. I was not happy with any of them.<br />
<br />
JavaScript. It had so many ugly parts that make me feel nervous every time I tried to do some fancy HTML5 stuff, at the end I did nothing (but that is not JavaScript fault, it was just me being lazy). Anyway, I could clearly notice that there were some beautiful gems hidden inside that jungle of errors and blunders. For starters, it was C-like, and C is one of my favorite languages. But, at difference of C, JavaScript had that "plastic" nature that made it powerful for solving problems in a dynamic way. First class functions, closures, that was cool.<br />
<br />
Then it was Lua. I found Lua due to my work as a game programmer. Lua is kind of mainstream in the game industry for scripting, and it's a good little language, at least that was my first impression: it's pretty solid, it has a permissive license, an active community, it's very small and easy to embed inside your game engine, it doesn't take much memory and it's quite fast... but man: Why on earth have the array indexes to start from 1 instead of 0? and Why global by default variables? and Why the Pascal-like syntaxis? and Why not ternary operator? and Why not this and this and that? There were so many little annoyances that irked me daily. Somebody could think that I created Killa just because of external appearances, I don't think so. For me Lua was broken by the myriad of its little annoyances, but it's core was very useful and it was a waste not to use it.<br />
<br />
I also believe that every programming language is by definition broken because none can do every task optimally. Lua was broken for me because I was a programmer working most of the time in a C-like world, having to change languages was a pain. By this definition, Killa is broken too, but it's broken for different people and for different reasons than Lua.<br />
<br />
So I suppose that Killa has a place to fill and stay. I'm happy with the current status, but of course it needs lots of work to be stable. For now let me show you how the language looks, the little program below solves the <a href="http://projecteuler.net/problem=393">Project Euler 393</a> problem under 9 seconds on my computer:<br />
<br />
<script src="https://gist.github.com/3971406.js?file=euler_393.js"></script>
Not as fast as Python for this problem, but Python has the advantage of having immutable tuples that can be used as dictionary keys, I suspect that makes the above algorithm run faster in Python, because in Killa I had to use string keys and additional data handling was required. Also this problem needed support of big numbers that I added to Killa as an extension just now. It's just the port of <a href="http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/#lbc">lbc</a> from Lua to Killa, but because the original library is under the GPL license I can not add it by default to Killa. I plan to maintain Killa under a permissive license for the sake of my fellow game developer peers. GPL-like licenses are very cumbersome for game development.<br />
<br />
So, Who are the potential users of Killa? I think that anybody who likes the embeddable features that Lua has to offer but wants a JavaScript-like experience with many of its ugly parts removed. I could see Killa being used in the videogame industry or the embedded industry. To start things running (and for testing) I have already integrated Killa into two popular game engines: <a href="https://bitbucket.org/ex/love">Love2D </a>and <a href="https://github.com/ex/cocos2d-x">cocos2d-x</a> and I have made some small projects with them.<br />
<br />
That's it. I need to explain with more detail the features of Killa in a future post, until then: happy coding!
<br />
<br />
UPDATE: I used bitwise operators to get the time down to 1.12 seconds, faster than Python (1.8 seconds)!exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-59210186470536254122012-09-16T23:32:00.001-05:002012-09-22T21:33:58.505-05:00Combinatorial explosion and Project Euler 393<div style="text-align: justify;">
Last week I found a nice video about algorithmic combinatorial explosion. It was very interesting but it's a bit sad to see the big sister throwing away her life in order to teach her little students the perils of trying to compute combinatorial problems that grow very fast. The problem they studied didn't seem to be hard, just enumerate all the ways you can go from one corner to the opposite corner in a rectangular grid without passing the same node twice. Deceptively simple, for a 1x1 grid there are only 2 ways, for a 2x2 grid there are 12 ways:
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/euler%202.png" /></div>
<br />
<a name='more'></a><br />
However things get ugly as long as we start increasing the size, for 5x5 grids she needs to use a computer to find the 1'262,816 ways. For grids greater that 6x6, she uses a supercomputer to get the answer without waiting so much and even with that the 8x8 grid takes 4.5 hours to be solved. The 9x9 grid takes 6.5 years to be solved. When she goes to compute the 10x10 grid she must be prepared to await almost 250,000 years to obtain the answer! Enumerating this way the solutions for the 11x11 grid would take the double of the estimated age of the universe. Here is the video:</div>
<br />
<div style="text-align: center;">
<iframe allowfullscreen="allowfullscreen" frameborder="0" height="315" src="http://www.youtube.com/embed/Q4gTV4r0zRs" width="560"></iframe>
</div>
<div style="text-align: justify;">
<br />
Coincidentally, last week <a href="http://projecteuler.net/problem=393">Project Euler problem 393</a> was somehow similar:<br />
<br />
<span style="font-size: small;"><b><span style="font-family: "Courier New",Courier,monospace;">An <var>nx</var><var>n</var> grid of squares contains <var>n</var><sup>2</sup> ants, one ant per square.<br />
All ants decide to move simultaneously to an adjacent square (usually 4
possibilities, except for ants on the edge of the grid or at the
corners). We define <var>f</var>(<var>n</var>) to be the number of ways this can
happen without any ants ending on the same square and without any two
ants crossing the same edge between two squares.</span></b></span><br />
<b><span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: small;">You are given that <var>f</var>(4) = 88. Find <var>f</var>(10).</span> </span></b><br />
<div class="problem_content" role="problem">
<br /></div>
So I decided to give it a shot enumerating the solutions for small numbers. consider for simplicity the case where (n = 2), the initial grid would be:<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/euler_21.png" /></div>
<br />
where we have enumerated the ants from 1 to 4 in their starting cell. The ants can move only one cell to any adjacent cell. If we obviate the condition of two ants never crossing the same edge, the number of different positions we can obtain are the ones shown below:<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/euler_22.png" /></div>
<br />
If we take in account the constraint condition we'll need to discard the positions shown below because to obtain these positions two ants have needed to cross the same edge:<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/euler_23.png" /></div>
<br />
So the problem for the case (n = 2) has only two final positions:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://sites.google.com/site/exeqtor/euler_24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://sites.google.com/site/exeqtor/euler_24.png" /></a></div>
In order to enumerate the solutions for (n = 4) I implemented a <a href="http://en.wikipedia.org/wiki/Depth-first_search">DFS</a> algorithm. Here is the code in <a href="http://github.com/ex/Killa">Killa</a> (my programming language):<br />
<br />
<script src="https://gist.github.com/3734653.js?file=gistfile1.js"></script>
I tried to minimize the memory usage and the condition of two ants not crossing the same edge imposed some additional conditionals that need more explaining but I hope you can follow the code. The answer for (n = 4) is 88, I used the HTML5 canvas element to make a collage with all the different possible final positions. The original board position is:<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/euler_25.png" /></div>
in which every ant is represented with a different cell color and the different ways that they can move are arranged in the 8x11 matrix below:<br />
<div style="text-align: center;">
<canvas height="902" id="canvas" width="660"></canvas></div>
<script type="application/javascript">
var count; // number of final positions
var N; // total cells in the board
var n; // grid partition
var canvas;
var ctx;
function dfs(now, next) {
for (var k = 0; k < N; k += 1) {
// Find an ant to move.
if (now[k] > 0) {
// Try to move it to the right.
if ((k % n < n - 1) && (next[k + 1] == 0)
&& ((next[k] == 0) || (next[k] != now[k] + 1))) {
next[k + 1] = now[k];
now[k] = 0;
dfs(now, next);
now[k] = next[k + 1];
next[k + 1] = 0;
}
// Try to move it to the left.
if ((k % n > 0) && (next[k - 1] == 0)
&& ((next[k] == 0) || (next[k] != now[k] - 1))) {
next[k - 1] = now[k];
now[k] = 0;
dfs(now, next);
now[k] = next[k - 1];
next[k - 1] = 0;
}
// Try to move it up.
if ((k >= n) && (next[k - n] == 0)
&& ((next[k] == 0) || (next[k] != now[k] - n))) {
next[k - n] = now[k];
now[k] = 0;
dfs(now, next);
now[k] = next[k - n];
next[k - n] = 0;
}
// Try to move it down.
if ((k < N - n) && (next[k + n] == 0)
&& ((next[k] == 0) || (next[k] != now[k] + n))) {
next[k + n] = now[k];
now[k] = 0;
dfs(now, next);
now[k] = next[k + n];
next[k + n] = 0;
}
// We don't need to consider the other ants. The above exploration
// has already found all the other possibilities.
break;
}
}
// Check if the position is valid.
for (var i = 0; i < N; i += 1) {
if (next[i] == 0) {
return;
}
}
// We implemented the DFS exploration in such a way that if we got here
// we must have a valid final position. So we increase the counter.
drawBoard(82 * (count % 8), 82 * Math.floor(count / 8), 18, 18, 1, next);
count += 1;
}
function euler_393(size) {
count = 0;
n = size;
N = n * n;
// This array holds the ants that need to be moved.
var now = [];
// This array holds the ants that have already been moved.
var next = [];
// Initialize positions, 0 is empty.
for (var k = 0; k < N; k += 1) {
now[k] = k + 1;
next[k] = 0;
}
// Use Depth First Search to compute the number of possible ways.
dfs(now, next)
}
function drawCell(x, y, w, h, c) {
switch(c) {
case 0: ctx.fillStyle = "rgb(66, 46, 71)"; break;
case 1: ctx.fillStyle = "rgb(77, 87, 117)"; break;
case 2: ctx.fillStyle = "rgb(181, 120, 9)"; break;
case 3: ctx.fillStyle = "rgb(128, 106, 42)"; break;
case 4: ctx.fillStyle = "rgb(0, 255, 221)"; break;
case 5: ctx.fillStyle = "rgb(77, 170, 189)"; break;
case 6: ctx.fillStyle = "rgb(250, 155, 0)"; break;
case 7: ctx.fillStyle = "rgb(255, 204, 51)"; break;
case 8: ctx.fillStyle = "rgb(250, 181, 135)"; break;
case 9: ctx.fillStyle = "rgb(247, 139, 106)"; break;
case 10: ctx.fillStyle = "rgb(171, 217, 4)"; break;
case 11: ctx.fillStyle = "rgb(234, 242, 5)"; break;
case 12: ctx.fillStyle = "rgb(235, 42, 55)"; break;
case 13: ctx.fillStyle = "rgb(242, 90, 73)"; break;
case 14: ctx.fillStyle = "rgb(114, 166, 3)"; break;
default: ctx.fillStyle = "rgb(70, 115, 2)"; break;
}
ctx.fillRect(x, y, w, h);
}
function drawBoard(x, y, w, h, d, board) {
for (var k = 0; k < 16; ++k) {
drawCell(x + (k % 4) * (w + d), y + Math.floor(k / 4) * (h + d),
w, h, board[k] - 1);
}
}
canvas = document.getElementById("canvas");
if (canvas.getContext) {
ctx = canvas.getContext("2d");
euler_393(4);
console.log("TOTAL:" + count);
}
</script>
Observe that the JavaScript code is almost identical to the code in Killa, if we ignore the drawing routines:<br />
<br />
<script src="https://gist.github.com/3768565.js?file=euler393.html"></script>
Be aware that this problem is also a combinatorial problem and it suffers from the same problem in the video, so the DFS algorithm is not going to end for (n = 10). In C++, the above algorithm took some time to
calculate even the modest 6x6 board, I got bored awaiting the answer for the 8x8 board
and killed the process. So how can we solve this for (n = 10)? Take in account that
the problem doesn't require from you to generate all the different
positions (like we did here for showing them graphically), we need only the final
number of positions and that could be done using (spoiler): <span style="color: white;">dynamic programming</span>.<br />
<br />
So have fun coding, and check your <a href="http://en.wikipedia.org/wiki/Bradycardia">heart</a>.<br />
<div>
</div>
</div>
exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com2tag:blogger.com,1999:blog-2994292646876077363.post-37043636372288789442012-05-26T21:06:00.000-05:002012-06-16T18:32:17.618-05:00Mandelbrot fractal viewerOne of the first things I did when I got my computer was to render the <a href="http://en.wikipedia.org/wiki/Mandelbrot_set">Mandelbrot Set</a> fractal. This was centuries ago, and I did it in C using DOS Borland-C++ and its fabulous (for me at that time) graphics library. I played a lot plotting lines and points on the screen those days. I don't know why exactly but this program has been always in my heart, maybe due to the colorful images it gave me:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" height="476" src="https://sites.google.com/site/exeqtor/mandelbrot01.png" width="639" /></div>
<br />
<a name='more'></a>So I was feeling that it was time to revive this code, and make it usable again, this time in Flash for the web. It runs surprisingly fast, and does a bit more than my initial version:<br />
<br />
<div style="text-align: center;">
<object data="https://sites.google.com/site/exeqtor/Mandelbrot.swf" height="480" type="application/x-shockwave-flash" width="640"> <param value="https://sites.google.com/site/exeqtor/Mandelbrot.swf" name="movie">
</object></div>
<br />
Click the above area and use the arrows to move the zoom area (a white rectangle would appear), press ENTER or SPACE to redraw the zoom area. BACKSPACE returns to the previous view. You can press F4 for some of my favorite coloring palettes or F5 for more random ones. The number of iterations steps to compute a fractal point is 256, you can increase this with PAGEUP an decrease it with PAGEDOWN (this could be useful if you see many black spots at higher zoom factors or if your computer is taking a lot of time to draw the fractal, more steps would mean higher precision but also more time to draw the fractal on screen). HOME returns to the default initial area.<br />
<br />
In trouble check the source code <a href="https://github.com/ex/flash/blob/master/mandelbrot/Mandelbrot.as">here</a> so you can look for the additional options this little program has. And for the sake of archeology, <a href="https://gist.github.com/2795822">here</a> is some code of mine of the old days.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-41804735275413687712012-05-26T12:54:00.000-05:002012-06-16T18:24:54.347-05:00Google+ deleted all my blogger images, oh myTo be fair, I deleted them, but Google+ helped. I was thinking in updating this blog with some pseudo technical posts when I noticed all my blogger images were gone. Kaput. Done. <br />
<br />
<a href="https://productforums.google.com/forum/embed/?place=topic/google-plus-discuss/hJHmhE7mcg0/discussion#%21topic/google-plus-discuss/hJHmhE7mcg0/discussion">https://productforums.google.com/forum/embed/?place=topic/google-plus-discuss/hJHmhE7mcg0/discussion#!topic/google-plus-discuss/hJHmhE7mcg0/discussion</a><br />
<br />
<ul>
<li>So basically I signed on this kind of Facebook clone called Google+ just for the kicks and gigs with my Gmail profile.</li>
</ul>
<ul>
<li>I saw a lot of images there that I never uploaded (now that I recall they were from my blogger account! but it was past midnight and I was asleep)</li>
</ul>
<ul>
<li>I deleted these Google+ images without remorse, Who else could have uploaded these images to my Google+ account? hackers are scary... or most likely it was just some glitch in this new Google+ thing.</li>
</ul>
<ul>
<li>Behind the scenes my Blogger images were deleted FOREVER. It seems images were never placed on Blogger itself but in Picasa, I never used Picasa so I didn't know. </li>
</ul>
<ul>
<li>Google+ linked the blog images from Picasa to my Google+ profile, when I deleted these images I was deleting the blog images, the trouble is that they must have anticipated this basic use case, I mean: Where is my gigantic popup blinking in red saying "DELETING THESE IMAGES WILL DELETE FOREVER YOUR BLOG IMAGES?". So there is no recovery I heard.</li>
</ul>
<br />
Kind of troublesome for me because I don't know where are the missing images (many are forever lost, I did them for the post) but catastrophic for many bloggers that had hundreds of images in Blogger. This blog of mine is rarely updated and has very low activity, however I can tell I have invested some time getting everything right, the images were created, edited, re-sized, recreated, etc, etc. Losing that time makes me sad, I can't imagine the amount of time lost for the users that have lost hundreds of images with this.<br />
<br />
It's a very ugly blunder, and it makes me wonder where are all the great usability engineers of Google working these days. In the bright side I saw some improvements and work done in the blogger editor and statistics that were missing the last time I used the site, so I hope that everything is not lost.<br />
<br />
Now, about this blog, well, I'm not feeling like restoring the lost images, I'll put the ones I can find at hand, but if not and you are really curious about some one that is missing drop me a note, sorry about the inconvenience.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com2tag:blogger.com,1999:blog-2994292646876077363.post-61884263929746317462011-10-09T14:18:00.002-05:002012-05-26T17:45:35.215-05:00Tetris clone in UnityI have been working with Unity lately and decided to use it for a a 3D version of my <a href="http://code.google.com/p/simple-tetris-clone/">simple-tetris-clone</a>.<br />
Unity great strengths are the easiness to go from concept to implementation and its support for deployment in multiple platforms. It's not that easy, I can tell, after finishing a project with iOS and Android support but once you get it right, it works.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" height="480" src="http://sites.google.com/site/exeqtor/stc_unity.png" width="720" /></div>
<br />
<br />
You can play it online if you have the Unity plugin installed.<br />
<br />
<a name='more'></a><br />
<br />
<div style="text-align: center;">
<object classid="clsid:444785F1-DE89-4295-863A-D46C3A781394" codebase="http://webplayer.unity3d.com/download_webplayer/UnityWebPlayer.cab#version=3,0,0,0" height="500" id="UnityObject" width="750"> <param name="http://sites.google.com/site/exeqtor/WebPlayer.unity3d" value="WebPlayer" />
<embed id="UnityEmbed" src="http://sites.google.com/site/exeqtor/WebPlayer.unity3d" width="750" height="500" type="application/vnd.unity" pluginspage="http://www.unity3d.com/unity-web-player-3.x" /> </object></div>
<br />
The source is in my <a href="http://code.google.com/p/simple-tetris-clone/source/browse/#svn%2Fbranches%2Funity">repository</a>.<br />
<br />
Right now this port is missing the HUD (and for a demo this size stock Unity GUI support would be somehow OK) but I better don't bother because the GUI system is one of the Unity weakest points and it really sucks big time. Due to <a href="http://forum.unity3d.com/threads/60217-Garbage-collector-spikes-because-of-GUI?highlight=OnGui+problems">performance issues</a> is plainly unusable for mobile devices and it's pretty archaic and limited for everything else. You'll need to buy expensive third party plugins or use (and probably modify) the nice and free <a href="http://forum.unity3d.com/threads/87917-Prime31-UIToolkit-Multi-Resolution-GUI-Solution-Ready-for-Use...and-it-s-free">UIToolkit</a> for sane and usable interfaces in your games, but even then you still will miss support for advanced containers and scrolling. Sight. The good news is that Unity 3.5 will have a brand new GUI system. I really really hope they fix it for then.<br />
So please forgive my laziness but my fingers chucked and rebelled against me when I was trying to type OnGUI(), and using old fashioned GUIText is not my thing :D<br />
<br />
Forgetting the GUI, Unity is an awesome and very fun engine to play with, and it's FREE.<br />
Programming in C# with Visual Studio C# is also a pleasure.<br />
<br />
Have fun.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com10tag:blogger.com,1999:blog-2994292646876077363.post-13230919574940038372011-02-09T22:51:00.007-05:002012-05-31T09:02:57.709-05:00Solving the Tic-Tac-Toe game, finally...The simple game that has been always after me laughing at my programming incompetence.<br />
<br />
The very first time I tried to make a program that could play perfectly the game was in the university, after taking my very first course in Programming Languages (I learned Pascal). It was holidays and I was visiting my cousin and got the nice surprise of him getting a personal computer. It was a powerful 286 with a monochrome amber monitor. I couldn't find anything similar but for you to get the idea, games were played like this in those computers:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" height="315" src="http://sites.google.com/site/exeqtor/herculesaxe.png" width="504" /></div>
<a name='more'></a>My cousin was the first lucky one in getting a computer in our family and he was learning Turbo Pascal by himself. We decided to celebrate our reunion programing the ultimate and invincible "Michi" (that is how the TicTacToe game is called over here). It was the morning and I had to return to my city at the afternoon, so we had only some hours to get it working. He was going to do the main menu and the game board, and I was going to implement the game logic. He started programming while I was writing on paper my part. This is just a bunch of ifs and a 3x3 matrix, How difficult can it be?. I thought. I recall he did a beautiful work, our graphics looked great (all in black and amber of course) but after typing my part the game really bombed, it played awfully, losing idiotically. Without more time to debug my program I had to leave with the bitter taste of defeat on my mouth.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" height="453" src="http://sites.google.com/site/exeqtor/casiofx7700g.jpg" width="506" /></div>
<br />
The second time I tried to solve the game was after getting my programmable calculator Casio FX-7700, a powerful beast of 4K of memory. This calculator only accepted some kind of primitive BASIC but it gave me some moments of solace programming it. In every sense it was my first "computer", because before that I only could test my programs after getting laboratory time on my university laboratory, and that place was always full of people waiting, I had to awake early to sign-in the notebook for reserving a place. I remember using all the 4K of my calculator for my TicTacToe game, it draw the board not only with "X" and "O" but also with different icons that I had diligently drawn pixel by pixel. This time the game logic worked fine. Or that I thought. One of the reasons my first attempt failed so miserably was that I didn't want a "boring" adversary. I know this is ridiculous when the game is boring for starters, but it was very very boring if the computer always played in the center first, or in some corner first. I knew that the computer could play in any place if it was first and indeed had high chances of confusing the casual adversary playing that way, so my game logic must choose randomly from a valid set of possible movements. I have see some TicTacToe game solvers cutting corners starting always in the <a href="http://en.wikipedia.org/wiki/Tictactoe">center</a> or in <a href="http://en.wikipedia.org/wiki/File:Tictactoe-X.svg">some corner</a>. My game seemed to play randomly and even so never to lose. Some day I needed the memory of my calculator for useful stuff, and I had to delete my TicTacToe program so I decided to play the little thing for the last time... and I found a bug I have never seen before! even worse, due to the randomness I couldn't reproduce it later. No time to debug, like the first time, TicTacToe laughing again at me.<br />
<br />
This year I saw again the little monster thanks to a friend, and I decided to settle things once for all with it. This time I didn't want to use a greedy approach to the problem, like before. This time I would generate all the game tree once for all and decide the best move using the <a href="http://www.fierz.ch/strategy1.htm">MiniMax algorithm</a>. You can see the result below (flash):<br />
<br />
<div style="text-align: center;">
<object data="http://sites.google.com/site/exeqtor/TicTacToe.swf" height="500" type="application/x-shockwave-flash" width="500"> <param value="http://sites.google.com/site/exeqtor/TicTacToe.swf" name="movie">
</object></div>
<br />
Left click for doing a move, and clicking while pressing [shift] or [control] undo the last move. The valid moves for the next player are show in black. About the statistics printed on every cell: the first 3 numbers (from top to bottom) are the number of possible games that are won by any part or are drawn (in ascending order). Next is the total of games and finally comes the MiniMax score of that cell. That score is used by the program to show the best moves for the next player. Positive scores are a secure win for the "X" player, negative scores are a secure win for the "O" player, a zero score means a draw.<br />
So TicTacToe in resume looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/tictactoe1.png" /></div>
<br />
The area in blue is proportional to the possible games than "X" can win, the red area is proportional to the number of wins for "O" and the area in yellow are the draws. You can see that there are 255168 games with "X" wining 51% of them, "O" wins only 30% and the rest is a draw. Clearly "X" has an advantage. This is the distribution for the first move:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/tictactoe2.png" /></div>
<br />
You can play in any cell (and like everybody knows) the best you can get is a draw (all the cells have zero for MiniMax score). Playing in the center gives you 60% chances of winning, in any corner a 52% percent of winning and playing in the side 48%. If you play in the corner, you'll get this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/tictactoe4.png" /></div>
<br />
Now the interesting bit is that for the "O" player there is only one valid possible answer for drawing (playing in the center) playing in any other place is a secure win for the "X" player. If "X" plays in the center first:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/tictactoe3.png" /></div>
<br />
the only valid answers for "O" is playing in some corner, and if "X" plays in the side:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/tictactoe5.png" /></div>
<br />
there are only 4 cells that are valid for the "O" player now (I really liked that opening).<br />
<br />
Play with the flash thing and convince yourself that it's unbeatable. I hope I didn't do a mistake again! :D. Source code <a href="http://wonderfl.net/c/2qkP">here</a>.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com5tag:blogger.com,1999:blog-2994292646876077363.post-49432295794912506502011-01-15T17:51:00.002-05:002011-02-09T22:53:53.364-05:00Flasic: a very simple BASIC interpreter for FlashI just found this very little cute BASIC interpreter: <a href="http://journal.stuffwithstuff.com/2010/07/18/jasic-a-complete-interpreter-in-one-java-file/">Jasic</a> and I decided to make a port to Flash in order to add basic graphics commands (just "setcolor" and "putpixel" actually) so I could see the little thing drawing a colored Mandelbrot fractal. It was very easy, thanks to the profusely commented code of the original author (Bob Nystrom). You can select the program demos from the drop-down menu and press the [Run] button to start the interpreter. So without further ado, Flasic:<br />
<br />
<a name='more'></a><br />
<br />
<div style="text-align: center;"><object data="http://sites.google.com/site/exeqtor/flasic.swf" height="600" type="application/x-shockwave-flash" width="800"> <param value="http://sites.google.com/site/exeqtor/flasic.swf" name="movie"></object></div><br />
It's not that slowly, but being a BASIC interpreter inside a Flex interpreter inside a Flash bytecode interpreter surely takes his toll, oh the irony... :D<br />
The code is my <a href="https://bitbucket.org/ex/flasic">repo</a>, many thanks to the original author for releasing his original version, it was fun.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com2tag:blogger.com,1999:blog-2994292646876077363.post-80503708217716803952010-08-08T16:37:00.007-05:002012-11-13T22:11:57.021-05:00Solving the Princess in a Box PuzzleWe can solve the puzzle of my last post using a <a href="http://en.wikipedia.org/wiki/Breadth-first_search">Breadth-first search (BFS)</a> algorithm. The key idea is to realize than the problem is just a Shortest Path problem where the graph, even when it is not completely known at the beginning (because we don't have the graph's adjacency list or adjacency matrix), can be built joining the states the board takes after every movement. The image below can help to understand this. It shows some of the states the board cab take after some movements starting from the beginning.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/pbox00.png" /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<a name='more'></a><br />
<br />
So we are going to work over the graph of all the states the board can take by moving the pieces around. A nice thing is that the BFS algorithm assures us that if we reach our target we would reach it in the minimum number of steps, and if we can't reach our target the problem is indeed impossible to solve. BFS in seudocode looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/pbox01.png" /></div>
<br />
But we'll need to modify some parts of this algorithm in order to solve our problem. See the highlighted lines below:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/pbox02.png" />
</div>
<br />
Lines 1-4 are no longer necessary (or possible) because we don't have the adjacency list or adjacency matrix. We don't even know the number of nodes our graph has!. Line 5 need to change because the above restriction forces us to attach the <i>State </i>property into our node, the same applies to lines 12-16. Line 11 and 18 would be ignored here because we don't need any special process after discovering a graph edge, there are other interesting problems with graphs that can be solved using these steps but not our problem at hand. So our algorithm ends like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/pbox03.png" />
</div>
<br />
In line 4 we have introduced a <i>Discovered</i> data structure, this object stores all the discovered nodes and can be a Set or Hash Table because we need to insert nodes and check their existence fast. This change is required because we don't know all the nodes at the beginning so we can't use a simple Array. As always everything is easy until you have to do it, and here the simple and innocent line 10 can give us a bit of trouble implementing it, but after a bit of code modifications we'll end with this:<br />
<br />
<div style="text-align: center;">
<object data="http://sites.google.com/site/exeqtor/Solver.swf" height="400" type="application/x-shockwave-flash" width="420"> <param value="http://sites.google.com/site/exeqtor/Solver.swf" name="movie"></object></div>
<br />
This can solve the puzzle in less than 3 seconds (in my desktop with Windows7 and a Pentium D at 2.8 GHz) pretty nice if we are talking about Flash here, I have seen some native applications taking similar or longer times.<br />
But some optimizations were required to obtain that speed. Also in order to don't freeze the browser I had to split the process along some frames, but that was easy because in a BFS process there is no recursion involved. It's a shame than Flash still doesn't have threading support despite the fact that HTML5 already got web workers.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com3tag:blogger.com,1999:blog-2994292646876077363.post-19855175877848974052010-07-29T23:38:00.005-05:002012-11-04T23:18:59.425-05:00The Princess in a Box puzzleToday was holidays here (national day) so I finally could finish the <a href="http://en.wikipedia.org/wiki/Professor_Layton_and_the_Curious_Village">Professor Layton and the Curious Village</a> cute little game. If you like puzzles there are no better games than the Professor Layton series. I really recommend them if you own a Nintendo DS. However I don't play games much, so take my advice with a bit of salt: the only games I ever finished were <a href="http://en.wikipedia.org/wiki/Star_Control_II">StarControl II</a>, Starcraft and <a href="http://en.wikipedia.org/wiki/Jeanne_d%27Arc_%28video_game%29">Jeanne d'Arc</a>. That reminds me that the last time I went to Buenos Aires I brought with me a little box with a similar puzzle. I totally forgot about it until I saw again a similar puzzle in the DS game.<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/princessinabox.png" /></div>
<br />
This puzzle has been a long time around us as a member of the family of <a href="http://en.wikipedia.org/wiki/Sliding_puzzle">sliding puzzles</a> and is better known as the <a href="http://en.wikipedia.org/wiki/Klotski">Klotski puzzle</a>, but I'm going to name it here <a href="http://professorlaytonwalkthrough.blogspot.com/2008/02/puzzle097.html">The Princess in a Box</a> puzzle because is the name that it has in the Professor Layton's game. I did this Flash demo if you want to play it:<br />
<a name='more'></a><br />
<div style="text-align: center;">
<object data="http://sites.google.com/site/exeqtor/PrincessSimple.swf" height="350" type="application/x-shockwave-flash" width="400"> <param value="http://sites.google.com/site/exeqtor/PrincessSimple.swf" name="movie"></object></div>
<br />
<div style="text-align: center;">
<object data="http://sites.google.com/site/exeqtor/Princess.swf" height="350" type="application/x-shockwave-flash" width="400"> <param value="http://sites.google.com/site/exeqtor/Princess.swf" name="movie"></object></div>
<br />
The source code is in <a href="http://github.com/ex/flash/tree/master/princess/">my repository</a>, if you like that kind of things.<br />
So that's it for now, have fun. I'll solve the puzzle in my next post.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-78233257931941038112010-04-11T23:43:00.009-05:002012-06-16T18:21:19.986-05:00My very own programming fontI think fonts are the second best friends of programmers.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://sites.google.com/site/exeqtor/code.png" /></div>
<a name='more'></a>When I was a kid I used only Lucida Console, it was easy to find on every Windows installation and had a nice monospace feel. After some time I found the <a href="http://www.proggyfonts.com/index.php?menu=download">Proggy</a> font family and I never looked back. Proggy was my only font for ages. It was very compact and it let me see more lines of code in those small CRT monitors of the time without losing clarity. <br />
<br />
A good programming font makes easy for you to distinguish a zero '0' from a capital letter 'O'; a number '1' from a letter 'l'; and render all the operators and common programming symbols in a way that is very easy to distinguish one from another. (an exclamation '!' from a vertical bar '|', a parenthesis '(' from a bracket '[', etc) But even the good times come to an end.<br />
<br />
And the end came with the advent of bigger monitors and bigger screen resolutions. The problem with Proggy was that it never looked good with sizes bigger than the default (11), and that was very small for me to see in the new resolutions. I did my best effort, but I already use heavy glasses to allow me the strain of small fonts all the day, it was overkill.<br />
<br />
So I sadly had to look for a replacement. I surfed the web looking at all the other programming fonts ever created, nothing caught my attention. I tested many of them but all the time I found myself missing something. After a long time, and because I had to work, I ended with <a href="http://en.wikipedia.org/wiki/Consolas">Consolas</a>.<br />
<br />
But it never was love. I had to said every time I started my editor 'I really like this font', I was making my best effort but deep root I knew it was only because I couldn't find anything better.<br />
<br />
This night I changed again monitors and looking at some code of mine I finally realized that Consolas was definitively not my font, so I decided to create my very own font: <a href="http://sites.google.com/site/exeqtor/luconex.ttf?attredirects=0&d=1">Lucida Console Ex</a>. Yes I'm returning to my origins, maybe I'm already old.<br />
<br />
I'm not really sure if I would end liking this font, maybe creating a bigger Proggy font could be the only acceptable solution. And to clarify, I just modified the default Lucida Console font adding the things I missed the most.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-30883049828357923132010-02-07T14:21:00.008-05:002010-09-12T22:40:04.736-05:00Simple Tetris Clone (FLEX version)I added to my Tetris clone a <a href="http://code.google.com/p/simple-tetris-clone/source/browse/#svn/branches/flex">FLEX branch</a>, it only uses the compiler and not the FLEX class containers (unnecessary for this little game). The point is you can use the free API tools to make a simple game like this, without needing the Flash IDE at all.<br />
<br />
<a name='more'></a><br />
<br />
<div style="text-align: center;"><object data="https://sites.google.com/site/exeqtor/fstc.swf" height="272" type="application/x-shockwave-flash" width="480"> <param value="https://sites.google.com/site/exeqtor/fstc.swf" name="movie"></object></div><br />
It's cool these days to bash anything Adobe related (and the bloated PDF reader or the security holes in the Flash player of course doesn't help). Just for the record <a href="http://flashcrash.dempsky.org/">this page</a> <span style="color: #990000;">can crash your browser</span> (Click on it only if you feel lucky and don't have something to lose). That is a bug that has not been fixed since <a href="http://www.securityfocus.com/archive/1/archive/1/496929/100/0/threaded">2008</a>, and there are many others out there (but with a less spectacular failure outcome). The sad truth is that anybody working (and I mean earning his bread) with Flash technology can tell you scary stories about classes refusing to compile, library assets getting mysteriously corrupted, different movie behavior under different player versions, etc. And if you are angry today I can assure you the bugs were a lot uglier in the AS2 times.<br />
<br />
However, in all honesty, I don't dislike Flash technology, of course hitting a Flash bug makes you feel angry until you find the workaround, but you would learn from your experiences and you would start avoiding the things that don't work, after some time you would have earned an impressive list of best-practices and things to avoid, following that path, working with Flash can be actually a nice experience. I personally like their API design, at the very least they have invested a lot of time and thinking in that.<br />
<br />
Playing with pure Javascript would be the other option (the canvas element seems very promising, I hope the 3D capability goes mainstream) but it's not an option for practical games right now (I mean this year). I have worked in big web game projects and I can't think of another tool that artists can use to recreate Flash animations, making them all sprite-based would make the game bigger in size while losing their vector appeal (easy scaling). Also there is nothing that can run at comparable speed in older browsers today. And don't let me start with the audio... So for making games (not your simple square and circle muted games) I suspect Flash is still going to survive for at least a couple of years. Even if it totally loses the video battle that has already started with the HTML5 video tag.exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com3tag:blogger.com,1999:blog-2994292646876077363.post-75211546680948703742010-02-01T23:59:00.001-05:002014-09-22T12:33:28.699-05:00The Pascal's triangleIt was already time to change the animation for this blog, (the little thing at the right, now with a chess board), so I would do a very short description of the previous one here:<br />
<br />
<div align="center">
<canvas height="501" id="canvas" width="501"> </canvas>
</div>
<script>
Pascal = function ( canvasName, colors, lineColor, borderColor, backColor ) {
this.TIME_SHOW = 1500;
this.TIME_FADE = 100;
this.ALPHA_DECREASE = 0.011;
this.ST_DRAW = 0;
this.ST_DELETE_CENTER = 1;
this.ST_SHOW = 2;
this.ST_FADE = 3;
this.PRIMES = [
11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 97, 101, 103, 107, 109, 113,
128, 131, 137, 139, 149, 151, 157, 163, 167, 169, 173, 179, 181, 191, 193, 197, 211, 223, 227, 229,
233, 239, 241, 243, 251, 254, 256, 257, 263, 269, 271, 277, 281, 283, 289, 293, 307, 311
];
var canvas = document.getElementById( canvasName );
if ( !canvas || !canvas.getContext ) {
return;
}
this.m_context = canvas.getContext( '2d' );
this.m_state = this.ST_DRAW;
this.m_colors = colors;
this.m_lineColor = lineColor;
this.m_borderColor = borderColor;
this.m_backColor = backColor;
this.m_halfWidth = 0;
this.m_halfHeight = 0;
this.m_cellSize = 0;
this.m_nextOrder = 0;
this.m_cells = 0;
this.m_array = null;
this.m_row = 0; // File to draw
this.m_color = 0; // Random index color
this.m_alphaBack = 1;
this.m_halfWidth = Math.floor( canvas.width / 2 )
this.m_halfHeight = Math.floor( canvas.height / 2 );
this.m_cellSize = 3;
this.m_cells = Math.floor( this.m_halfHeight / this.m_cellSize ); // number of cells
this.m_backCanvas = this.createCanvas( this.m_halfWidth, this.m_halfHeight );
this.m_frontCanvas = this.createCanvas( this.m_halfWidth, this.m_halfHeight );
this.m_borderCanvas = this.createCanvas( this.m_halfWidth, this.m_halfHeight );
this.m_frontContext = this.m_frontCanvas.getContext( '2d' );
this.m_boderContext = this.m_borderCanvas.getContext( '2d' );
this.m_backContext = this.m_backCanvas.getContext( '2d' );
// Calculate next
this.m_nextOrder = this.getNextOrder();
this.makeTriangle();
setInterval( this.update.bind( this ), 1000 / 30 );
}
Pascal.prototype.getNextOrder = function getNextOrder() {
var num = 2 + Math.floor( Math.random() * this.m_cells );
while ( ( num > 2 ) && ( ( num == this.m_nextOrder ) || ( this.binaryFind( num, this.PRIMES ) >= 0 ) ) ) {
num--;
}
return num;
};
Pascal.prototype.makeTriangle = function makeTriangle() {
if ( this.m_array != null ) {
this.m_array = null;
}
this.m_array = new Array();
this.m_array[0] = new Array();
this.m_array[0].push( 1 );
for ( var p = 1; p < this.m_cells; p++ ) {
this.m_array[p] = new Array();
this.m_array[p].push( 1 );
for ( var q = 0; q < this.m_array[p - 1].length - 1; q++ ) {
this.m_array[p].push(( this.m_array[p - 1][q] + this.m_array[p - 1][q + 1] ) % this.m_nextOrder );
}
this.m_array[p].push( 1 );
}
for ( p = this.m_cells; p < 2 * this.m_cells - 1; p++ ) {
this.m_array[p] = new Array();
for ( q = 0; q < this.m_array[p - 1].length - 1; q++ ) {
this.m_array[p].push(( this.m_array[p - 1][q] + this.m_array[p - 1][q + 1] ) % this.m_nextOrder );
}
}
};
Pascal.prototype.drawBorderSquare = function drawBorderSquare( x, y, size ) {
this.m_boderContext.fillStyle = this.m_lineColor;
this.m_boderContext.fillRect( x + 1, y + 1, size, size );
};
Pascal.prototype.drawBox = function drawBox( x, y, size, colorIndex, offsetColor ) {
this.m_frontContext.strokeStyle = this.m_borderColor;
this.m_frontContext.strokeRect( x + 0.5, y + 0.5, size, size );
this.m_frontContext.fillStyle = '#' + this.m_colors[( colorIndex + offsetColor ) % this.m_colors.length];
this.m_frontContext.fillRect( x + 1, y + 1, size - 1, size - 1 );
};
Pascal.prototype.onFade = function onFade() {
this.m_state = this.ST_FADE;
setTimeout( this.onRedraw.bind( this ), this.TIME_FADE );
};
Pascal.prototype.onRedraw = function onRedraw() {
this.m_nextOrder = this.getNextOrder();
this.makeTriangle();
this.m_row = 0;
this.m_color = Math.floor( Math.random() * this.m_array.length );
this.m_state = this.ST_DRAW;
};
Pascal.prototype.update = function update() {
switch ( this.m_state ) {
case this.ST_DRAW:
if ( this.m_row >= this.m_array.length ) {
this.m_alphaBack = 1;
this.m_backContext.clearRect( 0, 0, this.m_halfWidth, this.m_halfHeight );
// Swap front and back canvas.
var temp = this.m_frontCanvas;
this.m_frontCanvas = this.m_backCanvas;
this.m_backCanvas = temp;
temp = this.m_frontContext;
this.m_frontContext = this.m_backContext;
this.m_backContext = temp;
this.m_state = this.ST_DELETE_CENTER;
setTimeout( this.onFade.bind( this ), this.TIME_SHOW );
}
else {
this.redrawBmps();
this.m_row++;
if ( ( this.m_row % 2 ) && ( this.m_alphaBack > 0 ) ) {
this.m_alphaBack -= this.ALPHA_DECREASE;
}
}
break;
case this.ST_DELETE_CENTER:
this.m_boderContext.clearRect( 0, 0, this.m_halfWidth, this.m_halfHeight );
this.m_state = this.ST_SHOW;
break;
case this.ST_FADE:
this.m_alphaBack -= this.ALPHA_DECREASE;
break;
case this.ST_SHOW:
return;
}
this.m_context.fillStyle = this.m_backColor;
this.m_context.fillRect( 0, 0, 2 * this.m_halfWidth, 2 * this.m_halfHeight );
this.m_context.save();
for ( var k = 0; k < 4; k++ ) {
this.m_context.setTransform( k < 2 ? -1 : 1, 0, 0, k % 2 ? -1 : 1, this.m_halfWidth, this.m_halfHeight );
this.m_context.globalAlpha = this.m_alphaBack;
this.m_context.drawImage( this.m_backCanvas, 0, 0 );
this.m_context.globalAlpha = 1;
this.m_context.drawImage( this.m_frontCanvas, 0, 0 );
this.m_context.drawImage( this.m_borderCanvas, 0, 0 );
}
this.m_context.restore();
};
Pascal.prototype.redrawBmps = function redrawBmps() {
var p = this.m_row;
this.m_boderContext.clearRect( 0, 0, this.m_halfWidth, this.m_halfHeight );
if ( p < this.m_cells ) {
for ( var q = 0; q < this.m_array[p].length; q++ ) {
if ( this.m_array[p][q] ) {
this.drawBorderSquare( q * this.m_cellSize,
( this.m_array[p].length - q - 1 ) * this.m_cellSize, this.m_cellSize );
this.drawBox( q * this.m_cellSize, ( this.m_array[p].length - q - 1 ) * this.m_cellSize,
this.m_cellSize, this.m_array[p][q], this.m_color );
}
}
}
else {
for ( q = 0; q < this.m_array[p].length; q++ ) {
if ( this.m_array[p][q] ) {
this.drawBorderSquare( ( p + q - this.m_cells + 1 ) * this.m_cellSize,
( this.m_cells - q - 1 ) * this.m_cellSize, this.m_cellSize );
this.drawBox(( p + q - this.m_cells + 1 ) * this.m_cellSize, ( this.m_cells - q - 1 ) * this.m_cellSize,
this.m_cellSize, this.m_array[p][q], this.m_color );
}
}
}
};
Pascal.prototype.createCanvas = function createCanvas( width, height ) {
var canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
return canvas;
};
Pascal.prototype.binaryFind = function binaryFind( value, array ) {
var a = 0;
var b = array.length - 1;
var c;
while ( a <= b ) {
c = a + Math.floor(( b - a ) / 2 );
if ( array[c] == value ) {
return c;
}
else {
if ( array[c] > value ) {
b = c - 1;
} else {
a = c + 1;
}
}
}
return -1;
};
var COLORS_1 = [
'0054CE', '0054CE', '0074F2', '007FF2', '00416B',
'005D83', '006698', '0080B1', '0087CD', '00A9D5',
'00B2FC', '00ECFF', '00BFF2', '00A2FF', '00A2E7',
'00A2C4', '00A2A4'
];
new Pascal( 'canvas', COLORS_1, '#FF9AC5', '#6999FF', '#FFFFFF' );
</script>
This animation is just a <a href="http://en.wikipedia.org/wiki/Pascal_triangle">Pascal's Triangle</a> (reflected in the four quadrants) with different colors for cells accordingly to their modules with some random number not prime that changes with every iteration. The triangle for the next iteration is drawn row by row, while the previous one is faded. Empty spaces are cells that are divisible by the selected random number.<br />
<br />
If you feel more interested, you could check the section 2.3 of the book <a href="http://www.springer.com/math/dyn.+systems/book/978-0-387-20229-7">Chaos and Fractals (Springer)</a> that gave me the inspiration for this. NOTE: I updated this animation to use canvas HTML5 but the old Flash source code is <a href="http://wonderfl.net/code/93deb92acf8af31b40f2912d7ddc4c070e562706">here</a><br />
<br />
Have a nice night!exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0tag:blogger.com,1999:blog-2994292646876077363.post-58595986845503019262010-01-07T15:45:00.003-05:002012-06-02T23:08:02.861-05:00Simulator for a basic computerThis is simple simulator for the basic computer described in the book of Morris Mano "Computer System Architecture". <a href="http://calab.kaist.ac.kr/%7Ehyoon/courses/cs311/classnote_2006.html">Here</a> professor Hyunsoo Yoo maintains a succinct but complete description of this computer, check it if you can't find the book. In special look for the lectures 5 (<a href="http://sites.google.com/site/exeqtor/Ch5.zip">Basic Computer Organization and Design</a>) and 6 (<a href="http://sites.google.com/site/exeqtor/Ch6.zip">Programming the Basic Computer</a>) that this simulator uses as foundation.<br />
<a name='more'></a><div style="text-align: center;">
<br />
<object data="http://sites.google.com/site/exeqtor/sim_basic_comp.swf" height="730" type="application/x-shockwave-flash" width="800"> <param value="http://sites.google.com/site/exeqtor/sim_basic_comp.swf" name="movie">
</object></div>
<br />
This basic computer is built from these components:<br />
<br />
<table border="0" cellpadding="0" cellspacing="0" str="" style="border-collapse: collapse; height: 367px; width: 383px;"><colgroup><col style="width: 74pt;" width="99"></col> <col style="width: 48pt;" width="64"></col> <col style="width: 127pt;" width="169"></col> </colgroup><tbody>
<tr height="27" style="height: 20.25pt;"> <td class="xl25" height="27" style="color: #666666; font-weight: bold; height: 20.25pt; width: 74pt;" width="99">Component</td> <td class="xl26" style="border-left: medium none; color: #666666; font-weight: bold; text-align: center; width: 48pt;" width="64">Bits</td> <td class="xl25" style="border-left: medium none; color: #666666; font-weight: bold; width: 127pt;" width="169">Functionality</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">DR</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">16</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Data Register</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">AR</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">12</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Address Register</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">AC</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">16</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Accumulator</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">IR</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">16</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Instruction Register</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">PC</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">12</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Program Counter</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">TR</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">16</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Temporary Register</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">INPR</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">8</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Input Register</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">OUTR</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">8</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Output Register</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">MEMORY</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">16</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Memory 4096 x 16</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">FGI</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">1</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Input Flag</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">FGO</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">1</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Output Flag</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">IEN</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">1</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Interrupt Enabled Status</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">R</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">1</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Interruption Status</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">I</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">1</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Decode Bit</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">S</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">1</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Halt Status</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">E</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">1</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Carry Bit</td> </tr>
<tr height="19" style="height: 14.25pt;"> <td class="xl28" height="19" style="border-top: medium none; color: #000099; height: 14.25pt;">SC</td> <td class="xl27" num="" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">3</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Sequence Counter</td> </tr>
</tbody></table>
<br />
And the table of instructions it supports is:<br />
<br />
<table border="0" cellpadding="0" cellspacing="0" str="" style="border-collapse: collapse; height: 607px; width: 585px;"><colgroup><col style="width: 23pt;" width="30"></col> <col span="2" style="width: 60pt;" width="80"></col> <col style="width: 60pt;" width="80"></col> <col style="width: 177pt;" width="236"></col> </colgroup><tbody>
<tr height="21" style="height: 15.75pt;"> <td class="xl28" height="21" style="height: 15.75pt; width: 23pt;" width="30"><br /></td> <td class="xl26" rowspan="2" style="color: #666666; font-weight: bold; width: 60pt;" width="80">Symbol</td> <td class="xl26" colspan="2" style="border-left: medium none; color: #666666; font-weight: bold; text-align: center; width: 120pt;" width="160">Hexcode</td> <td class="xl26" rowspan="2" style="font-weight: bold; width: 177pt;" width="236"><span style="color: #666666;"> </span><span style="color: #666666;">Description</span></td> </tr>
<tr height="22" style="height: 16.5pt;"> <td class="xl28" height="22" style="height: 16.5pt;"><br /></td> <td class="xl26" style="border-left: medium none; border-top: medium none; color: #666666; font-weight: bold; text-align: center; width: 60pt;" width="80">I = 0</td> <td class="xl26" style="border-left: medium none; border-top: medium none; color: #666666; font-weight: bold; text-align: center; width: 60pt;" width="80">I = 1</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl29" height="140" rowspan="7" style="color: #336666; height: 105pt; width: 23pt;" width="30">M<br />
E<br />
M<br />
O<br />
R<br />
Y</td> <td class="xl27" style="border-left: medium none; border-top: medium none; color: #336666;">AND</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">0xxx</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">8xxx</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">AND memory word to AC</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #336666; height: 15pt;">ADD</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">1xxx</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">9xxx</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Add memory word to AC</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #336666; height: 15pt;">LDA</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">2xxx</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">Axxx</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Load AC from memory</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #336666; height: 15pt;">STA</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">3xxx</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">Bxxx</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Store content of AC into memory</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #336666; height: 15pt;">BUN</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">4xxx</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">Cxxx</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Branch unconditionally</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #336666; height: 15pt;">BSA</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">5xxx</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">Dxxx</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Branch and save return address</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #336666; height: 15pt;">ISZ</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">6xxx</td> <td class="xl25" style="border-left: medium none; border-top: medium none; color: #990000; text-align: center;">Exxx</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Increment and skip if zero</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl29" height="240" rowspan="12" style="border-top: medium none; color: #000099; height: 180pt; width: 23pt;" width="30">R<br />
E<br />
G<br />
I<br />
S<br />
T<br />
E<br />
R</td> <td class="xl27" style="border-left: medium none; border-top: medium none; color: #000099;">CLA</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7800</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Clear AC</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">CLE</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7400</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Clear E</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">CMA</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7200</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Complement AC</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">CME</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7100</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Complement E</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">CIR</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7080</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Circulate right AC and E</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">CIL</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7040</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Circulate left AC and E</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">INC</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7020</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Increment AC</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">SPA</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7010</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Skip next instruction if AC is positive</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">SNA</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7008</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Skip next instruction if AC is negative</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">SZA</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7004</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Skip next instruction if AC is zero</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">SZE</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7002</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Skip next instruction if E is zero</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #000099; height: 15pt;">HLT</td> <td class="xl25" colspan="2" num="" style="border-left: medium none; color: #990000; text-align: center;">7001</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Halt computer</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl29" height="120" rowspan="6" style="border-top: medium none; color: #993399; height: 90pt; width: 23pt;" width="30">I/O</td> <td class="xl27" style="border-left: medium none; border-top: medium none; color: #993399;">INP</td> <td class="xl25" colspan="2" style="border-left: medium none; color: #990000; text-align: center;">F800</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Input character to AC</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #993399; height: 15pt;">OUT</td> <td class="xl25" colspan="2" style="border-left: medium none; color: #990000; text-align: center;">F400</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Output character from AC</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #993399; height: 15pt;">SKI</td> <td class="xl25" colspan="2" style="border-left: medium none; color: #990000; text-align: center;">F200</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Skip on input flag</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #993399; height: 15pt;">SKO</td> <td class="xl25" colspan="2" style="border-left: medium none; color: #990000; text-align: center;">F100</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Skip on output flag</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #993399; height: 15pt;">ION</td> <td class="xl25" colspan="2" style="border-left: medium none; color: #990000; text-align: center;">F080</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Interrupt on</td> </tr>
<tr height="20" style="height: 15pt;"> <td class="xl27" height="20" style="border-left: medium none; border-top: medium none; color: #993399; height: 15pt;">IOF</td> <td class="xl25" colspan="2" style="border-left: medium none; color: #990000; text-align: center;">F040</td> <td class="xl24" style="border-left: medium none; border-top: medium none;">Interrupt off</td> </tr>
</tbody></table>
<br />
<br />
The assembler is a bit different than the book, it supports labels of any size and uses the character (:) instead of (,) also I added the LBL pseudo instruction that can help in some cases:<br />
<br />
<table border="0" cellpadding="0" cellspacing="0" str="" style="border-collapse: collapse; height: 162px; width: 569px;"><colgroup><col style="width: 60pt;" width="80"></col> <col style="width: 37pt;" width="49"></col> <col style="width: 270pt;" width="360"></col> </colgroup><tbody>
<tr height="17" style="height: 12.75pt;"> <td height="17" style="color: #000099; height: 12.75pt; width: 60pt;" width="80">ORG</td> <td style="color: #660000; width: 37pt;" width="49">N</td> <td str="Hexadecimal number N is the memory loc. " style="width: 270pt;" width="360">Hexadecimal number N is the memory location</td> </tr>
<tr height="17" style="height: 12.75pt;"> <td height="17" style="color: #000099; height: 12.75pt;"><br /></td> <td style="color: #660000;"><br /></td> <td>for the instruction or operand listed in the following line</td> </tr>
<tr height="17" style="height: 12.75pt;"> <td height="17" style="color: #000099; height: 12.75pt;">DEC</td> <td style="color: #660000;">N</td> <td>Signed decimal number N to be converted to the binary</td> </tr>
<tr height="17" style="height: 12.75pt;"> <td height="17" style="color: #000099; height: 12.75pt;">HEX</td> <td style="color: #660000;">N</td> <td>Hexadecimal number N to be converted to the binary</td> </tr>
<tr height="17" style="height: 12.75pt;"> <td height="17" style="color: #000099; height: 12.75pt;">LBL</td> <td style="color: #660000;">L</td> <td>Address of label L to be converted to the binary</td> </tr>
<tr height="17" style="height: 12.75pt;"> <td height="17" style="color: #000099; height: 12.75pt;">END</td> <td><br /></td> <td>End of symbolic program</td> </tr>
</tbody></table>
<br />
There are 3 sample programs to select, or you can enter your own. After your program is ready press the [Assemble] button, if the program is correct you will need to load the binary codes of the program into memory, you can do this pressing the [Load] button. Now you are ready to run the whole program with [Run] or just go cycle by cycle with [Next].<br />
<br />
The source is available under the MIT license in:<br />
<a href="http://code.google.com/p/basic-computer-simulator/">http://code.google.com/p/basic-computer-simulator/</a><br />
<br />
Have fun!exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com6tag:blogger.com,1999:blog-2994292646876077363.post-87229378858311171872009-08-02T11:29:00.002-05:002012-11-05T00:02:32.547-05:00Very very simple interpreter in FlexThis weekend I've been playing with making a very very simple interpreter in Flash, mostly for drawing functions of the form: <span style="font-weight: bold;">z = f(x, y)</span>. This is based in the <a href="http://compilers.iecc.com/crenshaw/tutor4.txt">part 4</a> of the famous <a href="http://compilers.iecc.com/crenshaw/">Lets build a Compiler!</a> tutorial by Jack Crenshaw.<br />
<br /><div style="text-align: center;">
<object data="http://sites.google.com/site/exeqtor/3dplot.swf" height="600" type="application/x-shockwave-flash" width="500"> <param value="http://sites.google.com/site/exeqtor/3dplot.swf" name="movie"></object></div>
<br />
exhttp://www.blogger.com/profile/01910988779640115452noreply@blogger.com0