460 行
14 KiB
JavaScript
460 行
14 KiB
JavaScript
// Copyright 2011 David Galles, University of San Francisco. All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other materials
|
|
// provided with the distribution.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
|
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// The views and conclusions contained in the software and documentation are those of the
|
|
// authors and should not be interpreted as representing official policies, either expressed
|
|
// or implied, of the University of San Francisco
|
|
|
|
|
|
function Hash(am, w, h)
|
|
{
|
|
if (am == undefined)
|
|
{
|
|
return;
|
|
}
|
|
this.init(am, w, h);
|
|
}
|
|
|
|
Hash.prototype = new Algorithm();
|
|
Hash.prototype.constructor = Hash;
|
|
Hash.superclass = Algorithm.prototype;
|
|
|
|
var MAX_HASH_LENGTH = 10;
|
|
|
|
|
|
var HASH_NUMBER_START_X = 200;
|
|
var HASH_X_DIFF = 7;
|
|
var HASH_NUMBER_START_Y = 10;
|
|
var HASH_ADD_START_Y = 30;
|
|
var HASH_INPUT_START_X = 60;
|
|
var HASH_INPUT_X_DIFF = 7;
|
|
var HASH_INPUT_START_Y = 45;
|
|
var HASH_ADD_LINE_Y = 42;
|
|
var HASH_RESULT_Y = 50;
|
|
var ELF_HASH_SHIFT = 10;
|
|
|
|
var HASH_LABEL_X = 300;
|
|
var HASH_LABEL_Y = 30;
|
|
var HASH_LABEL_DELTA_X = 50;
|
|
|
|
var HIGHLIGHT_COLOR = "#0000FF";
|
|
|
|
|
|
|
|
Hash.prototype.init = function(am, w, h)
|
|
{
|
|
var sc = Hash.superclass;
|
|
var fn = sc.init;
|
|
fn.call(this,am, w, h);
|
|
this.addControls();
|
|
this.nextIndex = 0;
|
|
this.hashingIntegers = true;
|
|
|
|
}
|
|
|
|
Hash.prototype.addControls = function()
|
|
{
|
|
this.insertField = addControlToAlgorithmBar("Text", "");
|
|
this.insertField.size = MAX_HASH_LENGTH;
|
|
this.insertField.onkeydown = this.returnSubmit(this.insertField, this.insertCallback.bind(this), MAX_HASH_LENGTH, true);
|
|
this.insertButton = addControlToAlgorithmBar("Button", "添加");
|
|
this.insertButton.onclick = this.insertCallback.bind(this);
|
|
|
|
this.deleteField = addControlToAlgorithmBar("Text", "");
|
|
this.deleteField.size = MAX_HASH_LENGTH;
|
|
this.deleteField.onkeydown = this.returnSubmit(this.insertField, this.deleteCallback.bind(this), MAX_HASH_LENGTH, true);
|
|
this.deleteButton = addControlToAlgorithmBar("Button", "删除");
|
|
this.deleteButton.onclick = this.deleteCallback.bind(this);
|
|
|
|
|
|
this.findField = addControlToAlgorithmBar("Text", "");
|
|
this.findField.size = MAX_HASH_LENGTH;
|
|
this.findField.onkeydown = this.returnSubmit(this.insertField, this.findCallback.bind(this), MAX_HASH_LENGTH, true);
|
|
this.findButton = addControlToAlgorithmBar("Button", "搜索");
|
|
this.findButton.onclick = this.findCallback.bind(this);
|
|
|
|
|
|
var radioButtonList = addRadioButtonGroupToAlgorithmBar(["Hash Integer", "Hash Strings"], "HashType");
|
|
this.hashIntegerButton = radioButtonList[0];
|
|
this.hashIntegerButton.onclick = this.changeHashTypeCallback.bind(this, true);
|
|
// this.hashIntegerButton.onclick = this.hashIntegerCallback.bind(this);
|
|
this.hashStringButton = radioButtonList[1];
|
|
this.hashStringButton.onclick = this.changeHashTypeCallback.bind(this, false);
|
|
|
|
// this.hashStringButton.onclick = this.hashStringCallback.bind(this);
|
|
this.hashIntegerButton.checked = true;
|
|
}
|
|
|
|
|
|
// Do this extra level of wrapping to get undo to work properly.
|
|
// (also, so that we only implement the action if we are changing the
|
|
// radio button)
|
|
Hash.prototype.changeHashTypeCallback = function(newHashingIntegers, event)
|
|
{
|
|
if (this.hashingIntegers != newHashingIntegers)
|
|
{
|
|
this.implementAction(this.changeHashType.bind(this), newHashingIntegers);
|
|
}
|
|
|
|
}
|
|
|
|
Hash.prototype.changeHashType = function(newHashingIntegerValue)
|
|
{
|
|
this.hashingIntegers = newHashingIntegerValue;
|
|
if (this.hashingIntegers)
|
|
{
|
|
this.hashIntegerButton.checked = true;
|
|
this.insertField.onkeydown = this.returnSubmit(this.insertField, this.insertCallback.bind(this), MAX_HASH_LENGTH, true);
|
|
this.deleteField.onkeydown = this.returnSubmit(this.insertField, this.deleteCallback.bind(this), MAX_HASH_LENGTH, true);
|
|
this.findField.onkeydown = this.returnSubmit(this.insertField, this.findCallback.bind(this), MAX_HASH_LENGTH, true);
|
|
}
|
|
else
|
|
{
|
|
this.hashStringButton.checked = true;
|
|
this.insertField.onkeydown = this.returnSubmit(this.insertField, this.insertCallback.bind(this), MAX_HASH_LENGTH, false);
|
|
this.deleteField.onkeydown = this.returnSubmit(this.insertField, this.deleteCallback.bind(this), MAX_HASH_LENGTH, false);
|
|
this.findField.onkeydown = this.returnSubmit(this.insertField, this.findCallback.bind(this), MAX_HASH_LENGTH, false);
|
|
}
|
|
return this.resetAll();
|
|
}
|
|
|
|
|
|
Hash.prototype.doHash = function(input)
|
|
{
|
|
if (this.hashingIntegers)
|
|
{
|
|
var labelID1 = this.nextIndex++;
|
|
var labelID2 = this.nextIndex++;
|
|
var highlightID = this.nextIndex++;
|
|
var index = parseInt(input) % this.table_size;
|
|
this.currHash = parseInt(input);
|
|
|
|
this.cmd("CreateLabel", labelID1, input + " % " + String(this.table_size) + " = " , HASH_LABEL_X, HASH_LABEL_Y);
|
|
this.cmd("CreateLabel", labelID2,index, HASH_LABEL_X + HASH_LABEL_DELTA_X, HASH_LABEL_Y);
|
|
this.cmd("Step");
|
|
this.cmd("CreateHighlightCircle", highlightID, HIGHLIGHT_COLOR, HASH_LABEL_X + HASH_LABEL_DELTA_X, HASH_LABEL_Y);
|
|
this.cmd("Move", highlightID, this.indexXPos[index], this.indexYPos[index]);
|
|
this.cmd("Step");
|
|
this.cmd("Delete", labelID1);
|
|
this.cmd("Delete", labelID2);
|
|
this.cmd("Delete", highlightID);
|
|
this.nextIndex -= 3;
|
|
|
|
return index;
|
|
|
|
}
|
|
else
|
|
{
|
|
var oldnextIndex = this.nextIndex;
|
|
var label1= this.nextIndex++;
|
|
this.cmd("CreateLabel", label1, "Hashing:" , 10, 45, 0);
|
|
var wordToHashID = new Array(input.length);
|
|
var wordToHash = new Array(input.length);
|
|
for (var i = 0; i < input.length; i++)
|
|
{
|
|
wordToHashID[i] = this.nextIndex++;
|
|
wordToHash[i] = input.charAt(i);
|
|
this.cmd("CreateLabel", wordToHashID[i], wordToHash[i], HASH_INPUT_START_X + i * HASH_INPUT_X_DIFF, HASH_INPUT_START_Y, 0);
|
|
}
|
|
var digits = new Array(32);
|
|
var hashValue = new Array(32);
|
|
var nextByte = new Array(8);
|
|
var nextByteID = new Array(8);
|
|
var resultDigits = new Array(32);
|
|
var floatingDigits = new Array(4);
|
|
var floatingVals = new Array(4);
|
|
|
|
var operatorID = this.nextIndex++;
|
|
var barID = this.nextIndex++;
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
hashValue[i] = 0;
|
|
digits[i] = this.nextIndex++;
|
|
resultDigits[i] = this.nextIndex++;
|
|
}
|
|
for (i=0; i<8; i++)
|
|
{
|
|
nextByteID[i] = this.nextIndex++;
|
|
}
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
floatingDigits[i] = this.nextIndex++;
|
|
}
|
|
this.cmd("Step");
|
|
for (i = wordToHash.length-1; i >= 0; i--)
|
|
{
|
|
for (j = 0; j < 32; j++)
|
|
{
|
|
this.cmd("CreateLabel", digits[j],hashValue[j], HASH_NUMBER_START_X + j * HASH_X_DIFF, HASH_NUMBER_START_Y, 0);
|
|
}
|
|
this.cmd("Delete", wordToHashID[i]);
|
|
var nextChar = wordToHash[i].charCodeAt(0);
|
|
for (var j = 7; j >= 0; j--)
|
|
{
|
|
nextByte[j] = nextChar % 2;
|
|
nextChar = Math.floor((nextChar / 2));
|
|
this.cmd("CreateLabel", nextByteID[j], nextByte[j], HASH_INPUT_START_X + i*HASH_INPUT_X_DIFF, HASH_INPUT_START_Y, 0);
|
|
this.cmd("Move", nextByteID[j], HASH_NUMBER_START_X + (j + 24) * HASH_X_DIFF, HASH_ADD_START_Y);
|
|
}
|
|
this.cmd("Step");
|
|
this.cmd("CreateRectangle", barID, "", 32 * HASH_X_DIFF, 0, HASH_NUMBER_START_X, HASH_ADD_LINE_Y,"left","bottom");
|
|
this.cmd("CreateLabel", operatorID, "+", HASH_NUMBER_START_X, HASH_ADD_START_Y, 0);
|
|
this.cmd("Step");
|
|
|
|
var carry = 0;
|
|
for (j = 7; j>=0; j--)
|
|
{
|
|
hashValue[j+24] = hashValue[j+24] + nextByte[j] + carry;
|
|
if (hashValue[j+24] > 1)
|
|
{
|
|
hashValue[j+24] = hashValue[j+24] - 2;
|
|
carry = 1;
|
|
}
|
|
else
|
|
{
|
|
carry = 0;
|
|
}
|
|
}
|
|
for (j = 23; j>=0; j--)
|
|
{
|
|
hashValue[j] = hashValue[j] + carry;
|
|
if (hashValue[j] > 1)
|
|
{
|
|
hashValue[j] = hashValue[j] - 2;
|
|
carry = 1;
|
|
}
|
|
else
|
|
{
|
|
carry = 0;
|
|
}
|
|
}
|
|
for (j = 0; j < 32; j++)
|
|
{
|
|
this.cmd("CreateLabel", resultDigits[j], hashValue[j], HASH_NUMBER_START_X + j * HASH_X_DIFF, HASH_RESULT_Y, 0);
|
|
}
|
|
|
|
this.cmd("Step");
|
|
for (j=0; j<8; j++)
|
|
{
|
|
this.cmd("Delete", nextByteID[j]);
|
|
}
|
|
this.cmd("Delete", barID);
|
|
this.cmd("Delete", operatorID);
|
|
for (j = 0; j<32; j++)
|
|
{
|
|
this.cmd("Delete", digits[j]);
|
|
this.cmd("Move", resultDigits[j], HASH_NUMBER_START_X + j * HASH_X_DIFF, HASH_NUMBER_START_Y)
|
|
}
|
|
this.cmd("Step");
|
|
if (i > 0)
|
|
{
|
|
for (j = 0; j < 32; j++)
|
|
{
|
|
this.cmd("Move", resultDigits[j], HASH_NUMBER_START_X + (j - 4) * HASH_X_DIFF, HASH_NUMBER_START_Y)
|
|
}
|
|
this.cmd("Step");
|
|
for (j = 0; j < 28; j++)
|
|
{
|
|
floatingVals[j] = hashValue[j];
|
|
hashValue[j] = hashValue[j+4];
|
|
}
|
|
|
|
for (j = 0; j < 4; j++)
|
|
{
|
|
this.cmd("Move", resultDigits[j], HASH_NUMBER_START_X + (j + ELF_HASH_SHIFT) * HASH_X_DIFF, HASH_ADD_START_Y);
|
|
hashValue[j+28] = 0;
|
|
this.cmd("CreateLabel", floatingDigits[j],0, HASH_NUMBER_START_X + (j + 28) * HASH_X_DIFF, HASH_NUMBER_START_Y,0);
|
|
if (floatingVals[j])
|
|
{
|
|
hashValue[j + ELF_HASH_SHIFT] = 1 - hashValue[j + ELF_HASH_SHIFT];
|
|
}
|
|
}
|
|
this.cmd("CreateRectangle", barID, "", 32 * HASH_X_DIFF, 0, HASH_NUMBER_START_X, HASH_ADD_LINE_Y,"left","bottom");
|
|
this.cmd("CreateLabel", operatorID, "XOR", HASH_NUMBER_START_X, HASH_ADD_START_Y, 0);
|
|
this.cmd("Step");
|
|
for (j = 0; j < 32; j++)
|
|
{
|
|
this.cmd("CreateLabel", digits[j], hashValue[j], HASH_NUMBER_START_X + j * HASH_X_DIFF, HASH_RESULT_Y, 0);
|
|
}
|
|
this.cmd("Step");
|
|
|
|
this.cmd("Delete", operatorID);
|
|
this.cmd("Delete", barID);
|
|
for (j = 0; j<32; j++)
|
|
{
|
|
this.cmd("Delete", resultDigits[j]);
|
|
this.cmd("Move", digits[j], HASH_NUMBER_START_X + j * HASH_X_DIFF, HASH_NUMBER_START_Y)
|
|
}
|
|
for (j = 0; j < 4; j++)
|
|
{
|
|
this.cmd("Delete", floatingDigits[j]);
|
|
}
|
|
this.cmd("Step");
|
|
for (j = 0; j<32; j++)
|
|
{
|
|
this.cmd("Delete", digits[j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j<32; j++)
|
|
{
|
|
this.cmd("Delete", resultDigits[j]);
|
|
}
|
|
}
|
|
|
|
}
|
|
this.cmd("Delete", label1);
|
|
for (j = 0; j < 32; j++)
|
|
{
|
|
this.cmd("CreateLabel", digits[j],hashValue[j], HASH_NUMBER_START_X + j * HASH_X_DIFF, HASH_NUMBER_START_Y, 0);
|
|
}
|
|
this.currHash = 0;
|
|
for (j=0; j < 32; j++)
|
|
{
|
|
this.currHash = this.currHash * 2 + hashValue[j];
|
|
}
|
|
this.cmd("CreateLabel", label1, " = " + String(this.currHash), HASH_NUMBER_START_X + 32*HASH_X_DIFF, HASH_NUMBER_START_Y, 0);
|
|
this.cmd("Step");
|
|
for (j = 0; j < 32; j++)
|
|
{
|
|
this.cmd("Delete", digits[j]);
|
|
}
|
|
|
|
var label2 = this.nextIndex++;
|
|
this.cmd("SetText", label1, String(this.currHash) + " % " + String(this.table_size) + " = ");
|
|
index = this.currHash % this.table_size;
|
|
this.cmd("CreateLabel", label2, index, HASH_NUMBER_START_X + 32*HASH_X_DIFF + 105, HASH_NUMBER_START_Y, 0);
|
|
this.cmd("Step");
|
|
highlightID = this.nextIndex++;
|
|
this.cmd("CreateHighlightCircle", highlightID, HIGHLIGHT_COLOR, HASH_NUMBER_START_X + 30*HASH_X_DIFF + 120, HASH_NUMBER_START_Y+ 15);
|
|
this.cmd("Move", highlightID, this.indexXPos[index], this.indexYPos[index]);
|
|
this.cmd("Step");
|
|
this.cmd("Delete", highlightID);
|
|
this.cmd("Delete", label1);
|
|
this.cmd("Delete", label2);
|
|
//this.nextIndex = oldnextIndex;
|
|
|
|
return index;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
Hash.prototype.resetAll =function()
|
|
{
|
|
this.insertField.value = "";
|
|
this.deleteField.value = "";
|
|
this.findField.value = "";
|
|
return [];
|
|
|
|
}
|
|
Hash.prototype.insertCallback = function(event)
|
|
{
|
|
var insertedValue = this.insertField.value;
|
|
if (insertedValue != "")
|
|
{
|
|
this.insertField.value = "";
|
|
this.implementAction(this.insertElement.bind(this),insertedValue);
|
|
}
|
|
}
|
|
|
|
Hash.prototype.deleteCallback = function(event)
|
|
{
|
|
var deletedValue = this.deleteField.value
|
|
if (deletedValue != "")
|
|
{
|
|
this.deleteField.value = "";
|
|
this.implementAction(this.deleteElement.bind(this),deletedValue);
|
|
}
|
|
}
|
|
|
|
Hash.prototype.findCallback = function(event)
|
|
{
|
|
var findValue = this.findField.value;
|
|
if (findValue != "")
|
|
{
|
|
this.findField.value = "";
|
|
this.implementAction(this.findElement.bind(this),findValue);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Hash.prototype.insertElement = function(elem)
|
|
{
|
|
|
|
}
|
|
|
|
Hash.prototype.deleteElement = function(elem)
|
|
{
|
|
|
|
|
|
}
|
|
Hash.prototype.findElement = function(elem)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// NEED TO OVERRIDE IN PARENT
|
|
Hash.prototype.reset = function()
|
|
{
|
|
this.hashIntegerButton.checked = true;
|
|
}
|
|
|
|
|
|
Hash.prototype.disableUI = function(event)
|
|
{
|
|
this.insertField.disabled = true;
|
|
this.insertButton.disabled = true;
|
|
this.deleteField.disabled = true;
|
|
this.deleteButton.disabled = true;
|
|
this.findField.disabled = true;
|
|
this.findButton.disabled = true;
|
|
}
|
|
|
|
Hash.prototype.enableUI = function(event)
|
|
{
|
|
|
|
this.insertField.disabled = false;
|
|
this.insertButton.disabled = false;
|
|
this.deleteField.disabled = false;
|
|
this.deleteButton.disabled = false;
|
|
this.findField.disabled = false;
|
|
this.findButton.disabled = false;
|
|
}
|
|
|
|
|
|
/* no init, this is only a base class!
|
|
var currentAlg;
|
|
function init()
|
|
{
|
|
var animManag = initCanvas();
|
|
currentAlg = new Hash(animManag, canvas.width, canvas.height);
|
|
}
|
|
*/ |