// @Author Tudor Marian, tudorm@cs.cornell.edu
// Computer Science Dept.
// Cornell University

// depends on BigInteger java script implementations jsbn.js, jsbn2.js

var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode */

/**
 * The file implements a PRNG with polynomial bit expansion using a one way
 * function f, and it's hard core predicate h. The output, given the seed x
 * is PRNG(x) = h(x) || h(f(x)) || h(f(f(x))) || ... || h(f^n(x)), where n 
 * is the number of random bits that form the output. 
 */
function prng(seed, length)
{
	if (length <= 0)
		return null;
				
	// the product of two large prime numbers
	var n1 = new BigInteger("5333489141114439061971195" +
		"20990930293621669107471261503941175175154254040757615452233025855568" +
		"80124787420407982934711598608882458503707320798427121718297057508240" +
		"8225871047494375876032256264628862047444318410423661394163885361370794" +
		"6846706873618846049770876974351547618412534751572012188278299856201787" +
		"53590070094832590984996538944864098768441393001588445368757", 10);
	
	var n2 = new BigInteger("11478563645839076141586476232"+
		"018506728187554863122739459630619451988740822848167793859569195729"+
		"3001429422079139987156012579927980353776881168470112868071148035944"+
		"63318586080148965125011295495975064433333281076644656140333029171539"+
		"68889638438723406403392130234612987840009048600897532332024794857844"+
		"783383166486610460002365527922395158750169976456781636081253565388262"+
		"863834935421172792740661699881967400104339438647182458106665110257688"+
		"755486917165269203104547939740299655064938342897200382760066667843926"+
		"2636536306955821062345454611138690532961867037484923349", 10)
	
	var n = n1;
			
	// this comes from a byte array
	var seedAsInt = new BigInteger(seed, null);
		
	// must fit inside the field
	if (seedAsInt.compareTo(n) > 0)
		seedAsInt = seedAsInt.mod(n);
		
	// padd the length to be word alined (every 4 bytes)
	var tail = length % 4;
	// integer division by 4 is shifting right 2 bits
	var len = length >> 2;
	if (tail != 0)
		len += 1;
	var b = new Array(len);
	var mask;
	for (var i = 0; i < len; i++)
	{
		b[i] = 0;
		mask = 0x80000000;
		for (var j = 0; j < 32; j++)
		{
			b[i] |= (hardCoreBit(seedAsInt) != 0) ? mask : 0x00000000;
			mask >>>= 1;
			seedAsInt = oneWayPermutation(seedAsInt, n);
		}
	}
	return b;
}

// LSB is a hard core predicate for the rabin function
function hardCoreBit(input)
{
	// the BigInteger number is stored in little endian format
	return input[0] & 0x00000001;
}

// Rabin function
function oneWayPermutation(input, n)
{
	var r = nbi();
	input.squareTo(r);
	return r.mod(n);
}

/*
 * Convert an 8-bit or 16-bit string to an array of big-endian words
 * In 8-bit function, characters >255 have their hi-byte silently ignored.
 */
function str2binb(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
  return bin;
}

/*
 * Convert a string to an array of little-endian words
 * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
 */
function str2binl(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
  return bin;
}
