/*
	This file was derived from the p5.js source code at
	https://github.com/processing/p5.js

	Copyright (c) the p5.js contributors and Andre Seidelt <superilu@yahoo.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
* @module p5compat
*/

/**********************************************************************************************************************
 * array functions
 */

/**
 * Adds a value to the end of an array. Extends the length of
 * the array by one. Maps to Array.push().
 *
 * @method append
 * @param {Array} array Array to append
 * @param {any} value to be added to the Array
 * @return {Array} the array that was appended to
 * @example
 * function setup() {
 *   var myArray = ['Mango', 'Apple', 'Papaya'];
 *   print(myArray); // ['Mango', 'Apple', 'Papaya']
 *
 *   append(myArray, 'Peach');
 *   print(myArray); // ['Mango', 'Apple', 'Papaya', 'Peach']
 * }
 */
exports.append = function (array, value) {
	array.push(value);
	return array;
};

/**
 * Copies an array (or part of an array) to another array. The src array is
 * copied to the dst array, beginning at the position specified by
 * srcPosition and into the position specified by dstPosition. The number of
 * elements to copy is determined by length. Note that copying values
 * overwrites existing values in the destination array. To append values
 * instead of overwriting them, use concat().
 * <br><br>
 * The simplified version with only two arguments, arrayCopy(src, dst),
 * copies an entire array to another of the same size. It is equivalent to
 * arrayCopy(src, 0, dst, 0, src.length).
 * <br><br>
 * Using this function is far more efficient for copying array data than
 * iterating through a for() loop and copying each element individually.
 *
 * @method arrayCopy
 * @deprecated
 * @param {Array}  src           the source Array
 * @param {Integer} srcPosition  starting position in the source Array
 * @param {Array}  dst           the destination Array
 * @param {Integer} dstPosition   starting position in the destination Array
 * @param {Integer} length        number of Array elements to be copied
 *
 * @example
 * var src = ['A', 'B', 'C'];
 * var dst = [1, 2, 3];
 * var srcPosition = 1;
 * var dstPosition = 0;
 * var length = 2;
 *
 * print(src); // ['A', 'B', 'C']
 * print(dst); // [ 1 ,  2 ,  3 ]
 *
 * arrayCopy(src, srcPosition, dst, dstPosition, length);
 * print(dst); // ['B', 'C', 3]
 */
exports.arrayCopy = function (src, srcPosition, dst, dstPosition, length) {
	// the index to begin splicing from dst array
	var start;
	var end;

	if (typeof length !== 'undefined') {
		end = Math.min(length, src.length);
		start = dstPosition;
		src = src.slice(srcPosition, end + srcPosition);
	} else {
		if (typeof dst !== 'undefined') {
			// src, dst, length
			// rename  so we don't get confused
			end = dst;
			end = Math.min(end, src.length);
		} else {
			// src, dst
			end = src.length;
		}

		start = 0;
		// rename  so we don't get confused
		dst = srcPosition;
		src = src.slice(0, end);
	}

	// Since we are not returning the array and JavaScript is pass by reference
	// we must modify the actual values of the array
	// instead of reassigning arrays
	Array.prototype.splice.apply(dst, [start, end].concat(src));
};

/**
 * Concatenates two arrays, maps to Array.concat(). Does not modify the
 * input arrays.
 *
 * @method concat
 * @param {Array} a first Array to concatenate
 * @param {Array} b second Array to concatenate
 * @return {Array} concatenated array
 *
 * @example
 * function setup() {
 *   var arr1 = ['A', 'B', 'C'];
 *   var arr2 = [1, 2, 3];
 *
 *   print(arr1); // ['A','B','C']
 *   print(arr2); // [1,2,3]
 *
 *   var arr3 = concat(arr1, arr2);
 *
 *   print(arr1); // ['A','B','C']
 *   print(arr2); // [1, 2, 3]
 *   print(arr3); // ['A','B','C', 1, 2, 3]
 * }
 */
exports.concat = function (list0, list1) {
	return list0.concat(list1);
};

/**
 * Reverses the order of an array, maps to Array.reverse()
 *
 * @method reverse
 * @param {Array} list Array to reverse
 * @return {Array} the reversed list
 * @example
 * function setup() {
 *   var myArray = ['A', 'B', 'C'];
 *   print(myArray); // ['A','B','C']
 *
 *   reverse(myArray);
 *   print(myArray); // ['C','B','A']
 * }
 */
exports.reverse = function (list) {
	return list.reverse();
};

/**
 * Decreases an array by one element and returns the shortened array,
 * maps to Array.pop().
 *
 * @method shorten
 * @param  {Array} list Array to shorten
 * @return {Array} shortened Array
 * @example
 * function setup() {
 *   var myArray = ['A', 'B', 'C'];
 *   print(myArray); // ['A', 'B', 'C']
 *   var newArray = shorten(myArray);
 *   print(myArray); // ['A','B','C']
 *   print(newArray); // ['A','B']
 * }
 */
exports.shorten = function (list) {
	list.pop();
	return list;
};

/**
 * Randomizes the order of the elements of an array. Implements
 * <a href='http://Bost.Ocks.org/mike/shuffle/' target=_blank>
 * Fisher-Yates Shuffle Algorithm</a>.
 *
 * @method shuffle
 * @param  {Array}   array  Array to shuffle
 * @param  {Boolean} [bool] modify passed array
 * @return {Array}   shuffled Array
 * @example
 * function setup() {
 *   var regularArr = ['ABC', 'def', createVector(), TAU, Math.E];
 *   print(regularArr);
 *   shuffle(regularArr, true); // force modifications to passed array
 *   print(regularArr);
 *
 *   // By default shuffle() returns a shuffled cloned array:
 *   var newArr = shuffle(regularArr);
 *   print(regularArr);
 *   print(newArr);
 * }
 */
exports.shuffle = function (arr, bool) {
	var isView = ArrayBuffer && ArrayBuffer.isView && ArrayBuffer.isView(arr);
	arr = bool || isView ? arr : arr.slice();

	var rnd,
		tmp,
		idx = arr.length;
	while (idx > 1) {
		rnd = (Math.random() * idx) | 0;

		tmp = arr[--idx];
		arr[idx] = arr[rnd];
		arr[rnd] = tmp;
	}

	return arr;
};

/**
 * Sorts an array of numbers from smallest to largest, or puts an array of
 * words in alphabetical order. The original array is not modified; a
 * re-ordered array is returned. The count parameter states the number of
 * elements to sort. For example, if there are 12 elements in an array and
 * count is set to 5, only the first 5 elements in the array will be sorted.
 *
 * @method sort
 * @param {Array} list Array to sort
 * @param {Integer} [count] number of elements to sort, starting from 0
 * @return {Array} the sorted list
 *
 * @example
 * function setup() {
 *   var words = ['banana', 'apple', 'pear', 'lime'];
 *   print(words); // ['banana', 'apple', 'pear', 'lime']
 *   var count = 4; // length of array
 *
 *   words = sort(words, count);
 *   print(words); // ['apple', 'banana', 'lime', 'pear']
 * }
 * 
 * function setup() {
 *   var numbers = [2, 6, 1, 5, 14, 9, 8, 12];
 *   print(numbers); // [2, 6, 1, 5, 14, 9, 8, 12]
 *   var count = 5; // Less than the length of the array
 *
 *   numbers = sort(numbers, count);
 *   print(numbers); // [1,2,5,6,14,9,8,12]
 * }
 */
exports.sort = function (list, count) {
	var arr = count ? list.slice(0, Math.min(count, list.length)) : list;
	var rest = count ? list.slice(Math.min(count, list.length)) : [];
	if (typeof arr[0] === 'string') {
		arr = arr.sort();
	} else {
		arr = arr.sort(function (a, b) {
			return a - b;
		});
	}
	return arr.concat(rest);
};

/**
 * Inserts a value or an array of values into an existing array. The first
 * parameter specifies the initial array to be modified, and the second
 * parameter defines the data to be inserted. The third parameter is an index
 * value which specifies the array position from which to insert data.
 * (Remember that array index numbering starts at zero, so the first position
 * is 0, the second position is 1, and so on.)
 *
 * @method splice
 * @param {Array}  list Array to splice into
 * @param {any}    value value to be spliced in
 * @param {Integer} position in the array from which to insert data
 * @return {Array} the list
 *
 * @example
 * function setup() {
 *   var myArray = [0, 1, 2, 3, 4];
 *   var insArray = ['A', 'B', 'C'];
 *   print(myArray); // [0, 1, 2, 3, 4]
 *   print(insArray); // ['A','B','C']
 *
 *   splice(myArray, insArray, 3);
 *   print(myArray); // [0,1,2,'A','B','C',3,4]
 * }
 */
exports.splice = function (list, value, index) {
	// note that splice returns spliced elements and not an array
	Array.prototype.splice.apply(list, [index, 0].concat(value));

	return list;
};

/**
 * Extracts an array of elements from an existing array. The list parameter
 * defines the array from which the elements will be copied, and the start
 * and count parameters specify which elements to extract. If no count is
 * given, elements will be extracted from the start to the end of the array.
 * When specifying the start, remember that the first array element is 0.
 * This function does not change the source array.
 *
 * @method subset
 * @param  {Array}  list    Array to extract from
 * @param  {Integer} start   position to begin
 * @param  {Integer} [count] number of values to extract
 * @return {Array}          Array of extracted elements
 *
 * @example
 * function setup() {
 *   var myArray = [1, 2, 3, 4, 5];
 *   print(myArray); // [1, 2, 3, 4, 5]
 *
 *   var sub1 = subset(myArray, 0, 3);
 *   var sub2 = subset(myArray, 2, 2);
 *   print(sub1); // [1,2,3]
 *   print(sub2); // [3,4]
 * }
 */
exports.subset = function (list, start, count) {
	if (typeof count !== 'undefined') {
		return list.slice(start, start + count);
	} else {
		return list.slice(start, list.length);
	}
};

/**********************************************************************************************************************
 * conversion functions
 */

/**
 * Converts a string to its floating point representation. The contents of a
 * string must resemble a number, or NaN (not a number) will be returned.
 * For example, float("1234.56") evaluates to 1234.56, but float("giraffe")
 * will return NaN.
 *
 * When an array of values is passed in, then an array of floats of the same
 * length is returned.
 *
 * @method float
 * @param {String}  str float string to parse
 * @return {Number}     floating point representation of string
 * @example
 * var str = '20';
 * var diameter = float(str);
 * ellipse(width / 2, height / 2, diameter, diameter);
 */
exports.float = function (str) {
	if (str instanceof Array) {
		return str.map(parseFloat);
	}
	return parseFloat(str);
};

/**
 * Converts a boolean, string, or float to its integer representation.
 * When an array of values is passed in, then an int array of the same length
 * is returned.
 *
 * @method int
 * @param {String|Boolean|Number}       n value to parse
 * @param {Integer}       [radix] the radix to convert to (default: 10)
 * @return {Number}                     integer representation of value
 *
 * @example
 * print(int('10')); // 10
 * print(int(10.31)); // 10
 * print(int(-10)); // -10
 * print(int(true)); // 1
 * print(int(false)); // 0
 * print(int([false, true, '10.3', 9.8])); // [0, 1, 10, 9]
 */
exports.int = function (n, radix) {
	radix = radix || 10;
	if (typeof n === 'string') {
		return parseInt(n, radix);
	} else if (typeof n === 'number') {
		return n | 0;
	} else if (typeof n === 'boolean') {
		return n ? 1 : 0;
	} else if (n instanceof Array) {
		return n.map(function (n) {
			return exports.int(n, radix);
		});
	}
};

/**
 * Converts a boolean, string or number to its string representation.
 * When an array of values is passed in, then an array of strings of the same
 * length is returned.
 *
 * @method str
 * @param {String|Boolean|Number|Array} n value to parse
 * @return {String}                     string representation of value
 * @example
 * print(str('10')); // "10"
 * print(str(10.31)); // "10.31"
 * print(str(-10)); // "-10"
 * print(str(true)); // "true"
 * print(str(false)); // "false"
 * print(str([true, '10.3', 9.8])); // [ "true", "10.3", "9.8" ]
 */
exports.str = function (n) {
	if (n instanceof Array) {
		return n.map(exports.str);
	} else {
		return String(n);
	}
};

/**
 * Converts a number or string to its boolean representation.
 * For a number, any non-zero value (positive or negative) evaluates to true,
 * while zero evaluates to false. For a string, the value "true" evaluates to
 * true, while any other value evaluates to false. When an array of number or
 * string values is passed in, then a array of booleans of the same length is
 * returned.
 *
 * @method boolean
 * @param {String|Boolean|Number|Array} n value to parse
 * @return {Boolean}                    boolean representation of value
 * @example
 * print(boolean(0)); // false
 * print(boolean(1)); // true
 * print(boolean('true')); // true
 * print(boolean('abcd')); // false
 * print(boolean([0, 12, 'true'])); // [false, true, false]
 */
exports.boolean = function (n) {
	if (typeof n === 'number') {
		return n !== 0;
	} else if (typeof n === 'string') {
		return n.toLowerCase() === 'true';
	} else if (typeof n === 'boolean') {
		return n;
	} else if (n instanceof Array) {
		return n.map(exports.boolean);
	}
};

/**
 * Converts a number, string representation of a number, or boolean to its byte
 * representation. A byte can be only a whole number between -128 and 127, so
 * when a value outside of this range is converted, it wraps around to the
 * corresponding byte representation. When an array of number, string or boolean
 * values is passed in, then an array of bytes the same length is returned.
 *
 * @method byte
 * @param {String|Boolean|Number}       n value to parse
 * @return {Number}                     byte representation of value
 *
 * @example
 * print(byte(127)); // 127
 * print(byte(128)); // -128
 * print(byte(23.4)); // 23
 * print(byte('23.4')); // 23
 * print(byte('hello')); // NaN
 * print(byte(true)); // 1
 * print(byte([0, 255, '100'])); // [0, -1, 100]
 */
exports.byte = function (n) {
	var nn = exports.int(n, 10);
	if (typeof nn === 'number') {
		return (nn + 128) % 256 - 128;
	} else if (nn instanceof Array) {
		return nn.map(exports.byte);
	}
};

/**
 * Converts a number or string to its corresponding single-character
 * string representation. If a string parameter is provided, it is first
 * parsed as an integer and then translated into a single-character string.
 * When an array of number or string values is passed in, then an array of
 * single-character strings of the same length is returned.
 *
 * @method char
 * @param {String|Number}       n value to parse
 * @return {String}             string representation of value
 *
 * @example
 * print(char(65)); // "A"
 * print(char('65')); // "A"
 * print(char([65, 66, 67])); // [ "A", "B", "C" ]
 * print(join(char([65, 66, 67]), '')); // "ABC"
 */
exports.char = function (n) {
	if (typeof n === 'number' && !isNaN(n)) {
		return String.fromCharCode(n);
	} else if (n instanceof Array) {
		return n.map(exports.char);
	} else if (typeof n === 'string') {
		return exports.char(parseInt(n, 10));
	}
};

/**
 * Converts a single-character string to its corresponding integer
 * representation. When an array of single-character string values is passed
 * in, then an array of integers of the same length is returned.
 *
 * @method unchar
 * @param {String} n     value to parse
 * @return {Number}      integer representation of value
 *
 * @example
 * print(unchar('A')); // 65
 * print(unchar(['A', 'B', 'C'])); // [ 65, 66, 67 ]
 * print(unchar(split('ABC', ''))); // [ 65, 66, 67 ]
 */
exports.unchar = function (n) {
	if (typeof n === 'string' && n.length === 1) {
		return n.charCodeAt(0);
	} else if (n instanceof Array) {
		return n.map(exports.unchar);
	}
};

/**
 * Converts a number to a string in its equivalent hexadecimal notation. If a
 * second parameter is passed, it is used to set the number of characters to
 * generate in the hexadecimal notation. When an array is passed in, an
 * array of strings in hexadecimal notation of the same length is returned.
 *
 * @method hex
 * @param {Number} n     value to parse
 * @param {Number} [digits]
 * @return {String}      hexadecimal string representation of value
 *
 * @example
 * print(hex(255)); // "000000FF"
 * print(hex(255, 6)); // "0000FF"
 * print(hex([0, 127, 255], 6)); // [ "000000", "00007F", "0000FF" ]
 */
exports.hex = function (n, digits) {
	digits = digits === undefined || digits === null ? (digits = 8) : digits;
	if (n instanceof Array) {
		return n.map(function (n) {
			return exports.hex(n, digits);
		});
	} else if (typeof n === 'number') {
		if (n < 0) {
			n = 0xffffffff + n + 1;
		}
		var hex = Number(n)
			.toString(16)
			.toUpperCase();
		while (hex.length < digits) {
			hex = '0' + hex;
		}
		if (hex.length >= digits) {
			hex = hex.substring(hex.length - digits, hex.length);
		}
		return hex;
	}
};

/**
 * Converts a string representation of a hexadecimal number to its equivalent
 * integer value. When an array of strings in hexadecimal notation is passed
 * in, an array of integers of the same length is returned.
 *
 * @method unhex
 * @param {String} n value to parse
 * @return {Number}      integer representation of hexadecimal value
 *
 * @example
 * print(unhex('A')); // 10
 * print(unhex('FF')); // 255
 * print(unhex(['FF', 'AA', '00'])); // [ 255, 170, 0 ]
 */
exports.unhex = function (n) {
	if (n instanceof Array) {
		return n.map(exports.unhex);
	} else {
		return parseInt('0x' + n, 16);
	}
};

/**********************************************************************************************************************
 * string functions
 */

/**
 * Combines an array of Strings into one String, each separated by the
 * character(s) used for the separator parameter. To join arrays of ints or
 * floats, it's necessary to first convert them to Strings using nf() or
 * nfs().
 *
 * @method join
 * @param  {Array}  list      array of Strings to be joined
 * @param  {String} separator String to be placed between each item
 * @return {String}           joined String
 * @example
 * var array = ['Hello', 'world!'];
 * var separator = ' ';
 * var message = join(array, separator);
 * text(message, 5, 50);
 */
exports.join = function (list, separator) {
	p5._validateParameters('join', arguments);
	return list.join(separator);
};

/**
 * This function is used to apply a regular expression to a piece of text,
 * and return matching groups (elements found inside parentheses) as a
 * String array. If there are no matches, a null value will be returned.
 * If no groups are specified in the regular expression, but the sequence
 * matches, an array of length 1 (with the matched text as the first element
 * of the array) will be returned.
 * <br><br>
 * To use the function, first check to see if the result is null. If the
 * result is null, then the sequence did not match at all. If the sequence
 * did match, an array is returned.
 * <br><br>
 * If there are groups (specified by sets of parentheses) in the regular
 * expression, then the contents of each will be returned in the array.
 * Element [0] of a regular expression match returns the entire matching
 * string, and the match groups start at element [1] (the first group is [1],
 * the second [2], and so on).
 *
 * @method match
 * @param  {String} str    the String to be searched
 * @param  {String} regexp the regexp to be used for matching
 * @return {String[]}      Array of Strings found
 * @example
 * var string = 'Hello p5js*!';
 * var regexp = 'p5js\\*';
 * var m = match(string, regexp);
 * text(m, 5, 50);
 */
exports.match = function (str, reg) {
	p5._validateParameters('match', arguments);
	return str.match(reg);
};

/**
 * This function is used to apply a regular expression to a piece of text,
 * and return a list of matching groups (elements found inside parentheses)
 * as a two-dimensional String array. If there are no matches, a null value
 * will be returned. If no groups are specified in the regular expression,
 * but the sequence matches, a two dimensional array is still returned, but
 * the second dimension is only of length one.
 * <br><br>
 * To use the function, first check to see if the result is null. If the
 * result is null, then the sequence did not match at all. If the sequence
 * did match, a 2D array is returned.
 * <br><br>
 * If there are groups (specified by sets of parentheses) in the regular
 * expression, then the contents of each will be returned in the array.
 * Assuming a loop with counter variable i, element [i][0] of a regular
 * expression match returns the entire matching string, and the match groups
 * start at element [i][1] (the first group is [i][1], the second [i][2],
 * and so on).
 *
 * @method matchAll
 * @param  {String} str    the String to be searched
 * @param  {String} regexp the regexp to be used for matching
 * @return {String[]}         2d Array of Strings found
 * @example
 * var string = 'Hello p5js*! Hello world!';
 * var regexp = 'Hello';
 * matchAll(string, regexp);
 */
exports.matchAll = function (str, reg) {
	p5._validateParameters('matchAll', arguments);
	var re = new RegExp(reg, 'g');
	var match = re.exec(str);
	var matches = [];
	while (match !== null) {
		matches.push(match);
		// matched text: match[0]
		// match start: match.index
		// capturing group n: match[n]
		match = re.exec(str);
	}
	return matches;
};

/**
 * Utility function for formatting numbers into strings. There are two
 * versions: one for formatting floats, and one for formatting ints.
 * The values for the digits, left, and right parameters should always
 * be positive integers.
 * (NOTE): Be cautious when using left and right parameters as it prepends numbers of 0's if the parameter
 * if greater than the current length of the number.
 * For example if number is 123.2 and left parameter passed is 4 which is greater than length of 123
 * (integer part) i.e 3 than result will be 0123.2. Same case for right parameter i.e. if right is 3 than
 * the result will be 123.200.
 *
 * @method nf
 * @param {Number|String}       num      the Number to format
 * @param {Integer|String}      [left]   number of digits to the left of the
 *                                decimal point
 * @param {Integer|String}      [right]  number of digits to the right of the
 *                                decimal point
 * @return {String}               formatted String
 *
 * @example
 * var myFont;
 * function preload() {
 *   myFont = loadFont('assets/fonts/inconsolata.ttf');
 * }
 * function setup() {
 *   background(200);
 *   var num1 = 321;
 *   var num2 = -1321;
 *
 *   noStroke();
 *   fill(0);
 *   textFont(myFont);
 *   textSize(22);
 *
 *   text(nf(num1, 4, 2), 10, 30);
 *   text(nf(num2, 4, 2), 10, 80);
 *   // Draw dividing line
 *   stroke(120);
 *   line(0, 50, width, 50);
 * }
 */
exports.nf = function (nums, left, right) {
	p5._validateParameters('nf', arguments);
	if (nums instanceof Array) {
		return nums.map(function (x) {
			return doNf(x, left, right);
		});
	} else {
		var typeOfFirst = Object.prototype.toString.call(nums);
		if (typeOfFirst === '[object Arguments]') {
			if (nums.length === 3) {
				return this.nf(nums[0], nums[1], nums[2]);
			} else if (nums.length === 2) {
				return this.nf(nums[0], nums[1]);
			} else {
				return this.nf(nums[0]);
			}
		} else {
			return doNf(nums, left, right);
		}
	}
};

function doNf(num, left, right) {
	var neg = num < 0;
	var n = neg ? num.toString().substring(1) : num.toString();
	var decimalInd = n.indexOf('.');
	var intPart = decimalInd !== -1 ? n.substring(0, decimalInd) : n;
	var decPart = decimalInd !== -1 ? n.substring(decimalInd + 1) : '';
	var str = neg ? '-' : '';
	if (typeof right !== 'undefined') {
		var decimal = '';
		if (decimalInd !== -1 || right - decPart.length > 0) {
			decimal = '.';
		}
		if (decPart.length > right) {
			decPart = decPart.substring(0, right);
		}
		for (var i = 0; i < left - intPart.length; i++) {
			str += '0';
		}
		str += intPart;
		str += decimal;
		str += decPart;
		for (var j = 0; j < right - decPart.length; j++) {
			str += '0';
		}
		return str;
	} else {
		for (var k = 0; k < Math.max(left - intPart.length, 0); k++) {
			str += '0';
		}
		str += n;
		return str;
	}
}

/**
 * Utility function for formatting numbers into strings and placing
 * appropriate commas to mark units of 1000. There are two versions: one
 * for formatting ints, and one for formatting an array of ints. The value
 * for the right parameter should always be a positive integer.
 *
 * @method nfc
 * @param  {Number|String}   num     the Number to format
 * @param  {Integer|String}  [right] number of digits to the right of the
 *                                  decimal point
 * @return {String}           formatted String
 *
 * @example
 * function setup() {
 *   background(200);
 *   var num = 11253106.115;
 *   var numArr = [1, 1, 2];
 *
 *   noStroke();
 *   fill(0);
 *   textSize(12);
 *
 *   // Draw formatted numbers
 *   text(nfc(num, 4), 10, 30);
 *   text(nfc(numArr, 2), 10, 80);
 *
 *   // Draw dividing line
 *   stroke(120);
 *   line(0, 50, width, 50);
 * }
 */
exports.nfc = function (num, right) {
	p5._validateParameters('nfc', arguments);
	if (num instanceof Array) {
		return num.map(function (x) {
			return doNfc(x, right);
		});
	} else {
		return doNfc(num, right);
	}
};

function doNfc(num, right) {
	num = num.toString();
	var dec = num.indexOf('.');
	var rem = dec !== -1 ? num.substring(dec) : '';
	var n = dec !== -1 ? num.substring(0, dec) : num;
	n = n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
	if (right === 0) {
		rem = '';
	} else if (typeof right !== 'undefined') {
		if (right > rem.length) {
			rem += dec === -1 ? '.' : '';
			var len = right - rem.length + 1;
			for (var i = 0; i < len; i++) {
				rem += '0';
			}
		} else {
			rem = rem.substring(0, right + 1);
		}
	}
	return n + rem;
}

/**
 * Utility function for formatting numbers into strings. Similar to nf() but
 * puts a "+" in front of positive numbers and a "-" in front of negative
 * numbers. There are two versions: one for formatting floats, and one for
 * formatting ints. The values for left, and right parameters
 * should always be positive integers.
 *
 * @method nfp
 * @param {Number} num      the Number to format
 * @param {Integer}      [left]   number of digits to the left of the decimal
 *                                point
 * @param {Integer}      [right]  number of digits to the right of the
 *                                decimal point
 * @return {String}         formatted String
 *
 * @example
 * function setup() {
 *   background(200);
 *   var num1 = 11253106.115;
 *   var num2 = -11253106.115;
 *
 *   noStroke();
 *   fill(0);
 *   textSize(12);
 *
 *   // Draw formatted numbers
 *   text(nfp(num1, 4, 2), 10, 30);
 *   text(nfp(num2, 4, 2), 10, 80);
 *
 *   // Draw dividing line
 *   stroke(120);
 *   line(0, 50, width, 50);
 * }
 */
exports.nfp = function () {
	p5._validateParameters('nfp', arguments);
	var nfRes = exports.nf.apply(this, arguments);
	if (nfRes instanceof Array) {
		return nfRes.map(addNfp);
	} else {
		return addNfp(nfRes);
	}
};

function addNfp(num) {
	return parseFloat(num) > 0 ? '+' + num.toString() : num.toString();
}

/**
 * Utility function for formatting numbers into strings. Similar to nf() but
 * puts an additional "_" (space) in front of positive numbers just in case to align it with negative
 * numbers which includes "-" (minus) sign.
 * The main usecase of nfs() can be seen when one wants to align the digits (place values) of a positive
 * number with some negative number (See the example to get a clear picture).
 * There are two versions: one for formatting float, and one for formatting int.
 * The values for the digits, left, and right parameters should always be positive integers.
 * (IMP): The result on the canvas basically the expected alignment can vary based on the typeface you are using.
 * (NOTE): Be cautious when using left and right parameters as it prepends numbers of 0's if the parameter
 * if greater than the current length of the number.
 * For example if number is 123.2 and left parameter passed is 4 which is greater than length of 123
 * (integer part) i.e 3 than result will be 0123.2. Same case for right parameter i.e. if right is 3 than
 * the result will be 123.200.
 *
 * @method nfs
 * @param {Number}       num      the Number to format
 * @param {Integer}      [left]   number of digits to the left of the decimal
 *                                point
 * @param {Integer}      [right]  number of digits to the right of the
 *                                decimal point
 * @return {String}         formatted String
 *
 * @example
 * var myFont;
 * function preload() {
 *   myFont = loadFont('assets/fonts/inconsolata.ttf');
 * }
 * function setup() {
 *   background(200);
 *   var num1 = 321;
 *   var num2 = -1321;
 *
 *   noStroke();
 *   fill(0);
 *   textFont(myFont);
 *   textSize(22);
 *
 *   // nfs() aligns num1 (positive number) with num2 (negative number) by
 *   // adding a blank space in front of the num1 (positive number)
 *   // [left = 4] in num1 add one 0 in front, to align the digits with num2
 *   // [right = 2] in num1 and num2 adds two 0's after both numbers
 *   // To see the differences check the example of nf() too.
 *   text(nfs(num1, 4, 2), 10, 30);
 *   text(nfs(num2, 4, 2), 10, 80);
 *   // Draw dividing line
 *   stroke(120);
 *   line(0, 50, width, 50);
 * }
 */
exports.nfs = function () {
	p5._validateParameters('nfs', arguments);
	var nfRes = exports.nf.apply(this, arguments);
	if (nfRes instanceof Array) {
		return nfRes.map(addNfs);
	} else {
		return addNfs(nfRes);
	}
};

function addNfs(num) {
	return parseFloat(num) > 0 ? ' ' + num.toString() : num.toString();
}

/**
 * The split() function maps to String.split(), it breaks a String into
 * pieces using a character or string as the delimiter. The delim parameter
 * specifies the character or characters that mark the boundaries between
 * each piece. A String[] array is returned that contains each of the pieces.
 *
 * The splitTokens() function works in a similar fashion, except that it
 * splits using a range of characters instead of a specific character or
 * sequence.
 *
 * @method split
 * @param  {String} value the String to be split
 * @param  {String} delim the String used to separate the data
 * @return {String[]}  Array of Strings
 * @example
 * var names = 'Pat,Xio,Alex';
 * var splitString = split(names, ',');
 * text(splitString[0], 5, 30);
 * text(splitString[1], 5, 50);
 * text(splitString[2], 5, 70);
 */
exports.split = function (str, delim) {
	p5._validateParameters('split', arguments);
	return str.split(delim);
};

/**
 * The splitTokens() function splits a String at one or many character
 * delimiters or "tokens." The delim parameter specifies the character or
 * characters to be used as a boundary.
 * <br><br>
 * If no delim characters are specified, any whitespace character is used to
 * split. Whitespace characters include tab (\t), line feed (\n), carriage
 * return (\r), form feed (\f), and space.
 *
 * @method splitTokens
 * @param  {String} value   the String to be split
 * @param  {String} [delim] list of individual Strings that will be used as
 *                          separators
 * @return {String[]}          Array of Strings
 * @example
 * function setup() {
 *   var myStr = 'Mango, Banana, Lime';
 *   var myStrArr = splitTokens(myStr, ',');
 *
 *   print(myStrArr); // prints : ["Mango"," Banana"," Lime"]
 * }
 */
exports.splitTokens = function (value, delims) {
	p5._validateParameters('splitTokens', arguments);
	var d;
	if (typeof delims !== 'undefined') {
		var str = delims;
		var sqc = /\]/g.exec(str);
		var sqo = /\[/g.exec(str);
		if (sqo && sqc) {
			str = str.slice(0, sqc.index) + str.slice(sqc.index + 1);
			sqo = /\[/g.exec(str);
			str = str.slice(0, sqo.index) + str.slice(sqo.index + 1);
			d = new RegExp('[\\[' + str + '\\]]', 'g');
		} else if (sqc) {
			str = str.slice(0, sqc.index) + str.slice(sqc.index + 1);
			d = new RegExp('[' + str + '\\]]', 'g');
		} else if (sqo) {
			str = str.slice(0, sqo.index) + str.slice(sqo.index + 1);
			d = new RegExp('[' + str + '\\[]', 'g');
		} else {
			d = new RegExp('[' + str + ']', 'g');
		}
	} else {
		d = /\s/g;
	}
	return value.split(d).filter(function (n) {
		return n;
	});
};

/**
 * Removes whitespace characters from the beginning and end of a String. In
 * addition to standard whitespace characters such as space, carriage return,
 * and tab, this function also removes the Unicode "nbsp" character.
 *
 * @method trim
 * @param  {String} str a String to be trimmed
 * @return {String}       a trimmed String
 *
 * @example
 * var string = trim('  No new lines\n   ');
 * text(string + ' here', 2, 50);
 */
exports.trim = function (str) {
	p5._validateParameters('trim', arguments);
	if (str instanceof Array) {
		return str.map(this.trim);
	} else {
		return str.trim();
	}
};

/**********************************************************************************************************************
 * date functions
 */

/**
 * The day() function
 * returns the current day as a value from 1 - 31.
 *
 * @method day
 * @return {Integer} the current day
 * @example
 * var d = day();
 * text('Current day: \n' + d, 5, 50);
 */
exports.day = function () {
	return new Date().getDate();
};

/**
 * The hour() function
 * returns the current hour as a value from 0 - 23.
 *
 * @method hour
 * @return {Integer} the current hour
 * @example
 * var h = hour();
 * text('Current hour:\n' + h, 5, 50);
 */
exports.hour = function () {
	return new Date().getHours();
};

/**
 * The minute() function
 * returns the current minute as a value from 0 - 59.
 *
 * @method minute
 * @return {Integer} the current minute
 * @example
 * var m = minute();
 * text('Current minute: \n' + m, 5, 50);
 */
exports.minute = function () {
	return new Date().getMinutes();
};

/**
 * Returns the number of milliseconds (thousandths of a second) since
 * starting the program. This information is often used for timing events and
 * animation sequences.
 *
 * @method millis
 * @return {Number} the number of milliseconds since starting the program
 * @example
 * var millisecond = millis();
 * text('Milliseconds \nrunning: \n' + millisecond, 5, 40);
 */
exports.millis = function () {
	return MsecTime();
};

/**
 * The month() function returns the current month as a value from 1 - 12.
 *
 * @method month
 * @return {Integer} the current month
 * @example
 * var m = month();
 * text('Current month: \n' + m, 5, 50);
 */
exports.month = function () {
	return new Date().getMonth() + 1; //January is 0!
};

/**
 * The second() function returns the current second as a value from 0 - 59.
 *
 * @method second
 * @return {Integer} the current second
 * @example
 * var s = second();
 * text('Current second: \n' + s, 5, 50);
 */
exports.second = function () {
	return new Date().getSeconds();
};

/**
 * The year() returns the current year as an integer (2014, 2015, 2016, etc).
 *
 * @method year
 * @return {Integer} the current year
 * @example
 * var y = year();
 * text('Current year: \n' + y, 5, 50);
 */
exports.year = function () {
	return new Date().getFullYear();
};

/**********************************************************************************************************************
 * dictionary
 */

/**
 *
 * Creates a new instance of p5.StringDict using the key-value pair
 * or the object you provide.
 *
 * @method createStringDict
 * @param {String} key
 * @param {String} value
 * @return {StringDict}
 *
 * @example
 * function setup() {
 *   let myDictionary = createStringDict('p5', 'js');
 *   print(myDictionary.hasKey('p5')); // logs true to console
 *
 *   let anotherDictionary = createStringDict({ happy: 'coding' });
 *   print(anotherDictionary.hasKey('happy')); // logs true to console
 * }
 */
exports.createStringDict = function (key, value) {
	return new StringDict(key, value);
};

/**
 *
 * Creates a new instance of NumberDict using the key-value pair
 * or object you provide.
 *
 * @method createNumberDict
 * @param {Number} key
 * @param {Number} value
 * @return {NumberDict}
 *
 * @example
 * function setup() {
 *   let myDictionary = createNumberDict(100, 42);
 *   print(myDictionary.hasKey(100)); // logs true to console
 *
 *   let anotherDictionary = createNumberDict({ 200: 84 });
 *   print(anotherDictionary.hasKey(200)); // logs true to console
 * }
 */
exports.createNumberDict = function (key, value) {
	return new NumberDict(key, value);
};

/**
* @module p5compat.TypedDict
*/

/**
 * Base class for all p5.Dictionary types. Specifically
 * typed Dictionary classes inherit from this class.
 *
 * @class TypedDict
 */
exports.TypedDict = function (key, value) {
	if (key instanceof Object) {
		this.data = key;
	} else {
		this.data = {};
		this.data[key] = value;
	}
	return this;
};

/**
 * Returns the number of key-value pairs currently stored in the Dictionary.
 *
 * @method size
 * @return {Integer} the number of key-value pairs in the Dictionary
 *
 * @example
 * function setup() {
 *   let myDictionary = createNumberDict(1, 10);
 *   myDictionary.create(2, 20);
 *   myDictionary.create(3, 30);
 *   print(myDictionary.size()); // logs 3 to the console
 * }
 */
exports.TypedDict.prototype.size = function () {
	return Object.keys(this.data).length;
};

/**
 * Returns true if the given key exists in the Dictionary,
 * otherwise returns false.
 *
 * @method hasKey
 * @param {Number|String} key that you want to look up
 * @return {Boolean} whether that key exists in Dictionary
 *
 * @example
 * function setup() {
 *   let myDictionary = createStringDict('p5', 'js');
 *   print(myDictionary.hasKey('p5')); // logs true to console
 * }
 */
exports.TypedDict.prototype.hasKey = function (key) {
	return this.data.hasOwnProperty(key);
};

/**
 * Returns the value stored at the given key.
 *
 * @method get
 * @param {Number|String} the key you want to access
 * @return {Number|String} the value stored at that key
 *
 * @example
 * function setup() {
 *   let myDictionary = createStringDict('p5', 'js');
 *   let myValue = myDictionary.get('p5');
 *   print(myValue === 'js'); // logs true to console
 * }
 */
exports.TypedDict.prototype.get = function (key) {
	if (this.data.hasOwnProperty(key)) {
		return this.data[key];
	} else {
		Println(key + ' does not exist in this Dictionary');
	}
};

/**
 * Updates the value associated with the given key in case it already exists
 * in the Dictionary. Otherwise a new key-value pair is added.
 *
 * @method set
 * @param {Number|String} key
 * @param {Number|String} value
 *
 * @example
 * function setup() {
 *   let myDictionary = createStringDict('p5', 'js');
 *   myDictionary.set('p5', 'JS');
 *   myDictionary.print(); // logs "key: p5 - value: JS" to console
 * }
 */

exports.TypedDict.prototype.set = function (key, value) {
	if (this._validate(value)) {
		this.data[key] = value;
	} else {
		Println('Those values dont work for this dictionary type.');
	}
};

/**
 * private helper function to handle the user passing in objects
 * during construction or calls to create()
 */
exports.TypedDict.prototype._addObj = function (obj) {
	for (var key in obj) {
		this.set(key, obj[key]);
	}
};

/**
 * Creates a new key-value pair in the Dictionary.
 *
 * @method create
 * @param {Number|String} key
 * @param {Number|String} value
 *
 * @example
 * function setup() {
 *   let myDictionary = createStringDict('p5', 'js');
 *   myDictionary.create('happy', 'coding');
 *   myDictionary.print();
 *   // above logs "key: p5 - value: js, key: happy - value: coding" to console
 * }
 */
exports.TypedDict.prototype.create = function (key, value) {
	if (key instanceof Object && typeof value === 'undefined') {
		this._addObj(key);
	} else if (typeof key !== 'undefined') {
		this.set(key, value);
	} else {
		Println(
			'In order to create a new Dictionary entry you must pass ' +
			'an object or a key, value pair'
		);
	}
};

/**
 * Removes all previously stored key-value pairs from the Dictionary.
 *
 * @method clear
 * @example
 * function setup() {
 *   let myDictionary = createStringDict('p5', 'js');
 *   print(myDictionary.hasKey('p5')); // prints 'true'
 *   myDictionary.clear();
 *   print(myDictionary.hasKey('p5')); // prints 'false'
 * }
 */
exports.TypedDict.prototype.clear = function () {
	this.data = {};
};

/**
 * Removes the key-value pair stored at the given key from the Dictionary.
 *
 * @method remove
 * @param {Number|String} key for the pair to remove
 *
 * @example
 * function setup() {
 *   let myDictionary = createStringDict('p5', 'js');
 *   myDictionary.create('happy', 'coding');
 *   myDictionary.print();
 *   // above logs "key: p5 - value: js, key: happy - value: coding" to console
 *   myDictionary.remove('p5');
 *   myDictionary.print();
 *   // above logs "key: happy value: coding" to console
 * }
 */
exports.TypedDict.prototype.remove = function (key) {
	if (this.data.hasOwnProperty(key)) {
		delete this.data[key];
	} else {
		throw new Error(key + ' does not exist in this Dictionary');
	}
};

/**
 * Logs the set of items currently stored in the Dictionary to the console.
 *
 * @method print
 *
 * @example
 * function setup() {
 *   let myDictionary = createStringDict('p5', 'js');
 *   myDictionary.create('happy', 'coding');
 *   myDictionary.print();
 *   // above logs "key: p5 - value: js, key: happy - value: coding" to console
 * }
 */
exports.TypedDict.prototype.print = function () {
	for (var item in this.data) {
		Println('key:' + item + ' value:' + this.data[item]);
	}
};

/**
 * Converts the Dictionary into a JSON file for local download.
 *
 * @method saveJSON
 * @example
 * function setup() {
 *   createCanvas(100, 100);
 *   background(200);
 *   text('click here to save', 10, 10, 70, 80);
 * }
 *
 * function mousePressed() {
 *   if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
 *     createStringDict({
 *       john: 1940,
 *       paul: 1942,
 *       george: 1943,
 *       ringo: 1940
 *     }).saveJSON('beatles');
 *   }
 * }
 */
exports.TypedDict.prototype.saveJSON = function (filename, opt) {
	prototype.saveJSON(this.data, filename, opt);
};

/**
 * private helper function to ensure that the user passed in valid
 * values for the Dictionary type
 */
exports.TypedDict.prototype._validate = function (value) {
	return true;
};

/**
* @module p5compat.StringDict
*/

/**
 * A simple Dictionary class for Strings.
 *
 * @class StringDict
 * @extends TypedDict
 */

exports.StringDict = function () {
	TypedDict.apply(this, arguments);
};

exports.StringDict.prototype = Object.create(exports.TypedDict.prototype);

exports.StringDict.prototype._validate = function (value) {
	return typeof value === 'string';
};

/**
* @module p5compat.NumberDict
*/

/**
 * A simple Dictionary class for Numbers.
 *
 * @class NumberDict
 * @extends TypedDict
 */
exports.NumberDict = function () {
	TypedDict.apply(this, arguments);
};

exports.NumberDict.prototype = Object.create(exports.TypedDict.prototype);

/**
 * private helper function to ensure that the user passed in valid
 * values for the Dictionary type
 */
exports.NumberDict.prototype._validate = function (value) {
	return typeof value === 'number';
};

/**
 * Add the given number to the value currently stored at the given key.
 * The sum then replaces the value previously stored in the Dictionary.
 *
 * @method add
 * @param {Number} Key for the value you wish to add to
 * @param {Number} Number to add to the value
 * @example
 * function setup() {
 *   let myDictionary = createNumberDict(2, 5);
 *   myDictionary.add(2, 2);
 *   print(myDictionary.get(2)); // logs 7 to console.
 * }
 */
exports.NumberDict.prototype.add = function (key, amount) {
	if (this.data.hasOwnProperty(key)) {
		this.data[key] += amount;
	} else {
		Println('The key - ' + key + ' does not exist in this dictionary.');
	}
};

/**
 * Subtract the given number from the value currently stored at the given key.
 * The difference then replaces the value previously stored in the Dictionary.
 *
 * @method sub
 * @param {Number} Key for the value you wish to subtract from
 * @param {Number} Number to subtract from the value
 * @example
 * function setup() {
 *   let myDictionary = createNumberDict(2, 5);
 *   myDictionary.sub(2, 2);
 *   print(myDictionary.get(2)); // logs 3 to console.
 * }
 */
exports.NumberDict.prototype.sub = function (key, amount) {
	this.add(key, -amount);
};

/**
 * Multiply the given number with the value currently stored at the given key.
 * The product then replaces the value previously stored in the Dictionary.
 *
 * @method mult
 * @param {Number} Key for value you wish to multiply
 * @param {Number} Amount to multiply the value by
 * @example
 * function setup() {
 *   let myDictionary = createNumberDict(2, 4);
 *   myDictionary.mult(2, 2);
 *   print(myDictionary.get(2)); // logs 8 to console.
 * }
 */
exports.NumberDict.prototype.mult = function (key, amount) {
	if (this.data.hasOwnProperty(key)) {
		this.data[key] *= amount;
	} else {
		Println('The key - ' + key + ' does not exist in this dictionary.');
	}
};

/**
 * Divide the given number with the value currently stored at the given key.
 * The quotient then replaces the value previously stored in the Dictionary.
 *
 * @method div
 * @param {Number} Key for value you wish to divide
 * @param {Number} Amount to divide the value by
 * @example
 * function setup() {
 *   let myDictionary = createNumberDict(2, 8);
 *   myDictionary.div(2, 2);
 *   print(myDictionary.get(2)); // logs 4 to console.
 * }
 */
exports.NumberDict.prototype.div = function (key, amount) {
	if (this.data.hasOwnProperty(key)) {
		this.data[key] /= amount;
	} else {
		Println('The key - ' + key + ' does not exist in this dictionary.');
	}
};

/**
 * private helper function for finding lowest or highest value
 * the argument 'flip' is used to flip the comparison arrow
 * from 'less than' to 'greater than'
 */
exports.NumberDict.prototype._valueTest = function (flip) {
	if (Object.keys(this.data).length === 0) {
		throw new Error(
			'Unable to search for a minimum or maximum value on an empty NumberDict'
		);
	} else if (Object.keys(this.data).length === 1) {
		return this.data[Object.keys(this.data)[0]];
	} else {
		var result = this.data[Object.keys(this.data)[0]];
		for (var key in this.data) {
			if (this.data[key] * flip < result * flip) {
				result = this.data[key];
			}
		}
		return result;
	}
};

/**
 * Return the lowest number currently stored in the Dictionary.
 *
 * @method minValue
 * @return {Number}
 * @example
 * function setup() {
 *   let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 });
 *   let lowestValue = myDictionary.minValue(); // value is -10
 *   print(lowestValue);
 * }
 *
 */
exports.NumberDict.prototype.minValue = function () {
	return this._valueTest(1);
};

/**
 * Return the highest number currently stored in the Dictionary.
 *
 * @method maxValue
 * @return {Number}
 * @example
 * function setup() {
 *   let myDictionary = createNumberDict({ 2: -10, 4: 0.65, 1.2: 3 });
 *   let highestValue = myDictionary.maxValue(); // value is 3
 *   print(highestValue);
 * }
 */
exports.NumberDict.prototype.maxValue = function () {
	return this._valueTest(-1);
};

/**
 * private helper function for finding lowest or highest key
 * the argument 'flip' is used to flip the comparison arrow
 * from 'less than' to 'greater than'
 *
 */
exports.NumberDict.prototype._keyTest = function (flip) {
	if (Object.keys(this.data).length === 0) {
		throw new Error('Unable to use minValue on an empty NumberDict');
	} else if (Object.keys(this.data).length === 1) {
		return Object.keys(this.data)[0];
	} else {
		var result = Object.keys(this.data)[0];
		for (var i = 1; i < Object.keys(this.data).length; i++) {
			if (Object.keys(this.data)[i] * flip < result * flip) {
				result = Object.keys(this.data)[i];
			}
		}
		return result;
	}
};

/**
 * Return the lowest key currently used in the Dictionary.
 *
 * @method minKey
 * @return {Number}
 * @example
 * function setup() {
 *   let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 });
 *   let lowestKey = myDictionary.minKey(); // value is 1.2
 *   print(lowestKey);
 * }
 */
exports.NumberDict.prototype.minKey = function () {
	return this._keyTest(1);
};

/**
 * Return the highest key currently used in the Dictionary.
 *
 * @method maxKey
 * @return {Number}
 * @example
 * function setup() {
 *   let myDictionary = createNumberDict({ 2: 4, 4: 6, 1.2: 3 });
 *   let highestKey = myDictionary.maxKey(); // value is 4
 *   print(highestKey);
 * }
 */
exports.NumberDict.prototype.maxKey = function () {
	return this._keyTest(-1);
};