    /*
    	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
    */

    // internal variables
    exports._background = EGA.BLACK;

    /**********************************************************************************************************************
     * conversion
     */

    /**
     * Conversions adapted from <http://www.easyrgb.com/en/math.php>.
     *
     * In these functions, hue is always in the range [0, 1], just like all other
     * components are in the range [0, 1]. 'Brightness' and 'value' are used
     * interchangeably.
     */

    exports.ColorConversion = {};

    /**
     * Convert an HSBA array to HSLA.
     */
    exports.ColorConversion._hsbaToHSLA = function (hsba) {
    	var hue = hsba[0];
    	var sat = hsba[1];
    	var val = hsba[2];

    	// Calculate lightness.
    	var li = (2 - sat) * val / 2;

    	// Convert saturation.
    	if (li !== 0) {
    		if (li === 1) {
    			sat = 0;
    		} else if (li < 0.5) {
    			sat = sat / (2 - sat);
    		} else {
    			sat = sat * val / (2 - li * 2);
    		}
    	}

    	// Hue and alpha stay the same.
    	return [hue, sat, li, hsba[3]];
    };

    /**
     * Convert an HSBA array to RGBA.
     */
    exports.ColorConversion._hsbaToRGBA = function (hsba) {
    	var hue = hsba[0] * 6; // We will split hue into 6 sectors.
    	var sat = hsba[1];
    	var val = hsba[2];

    	var RGBA = [];

    	if (sat === 0) {
    		RGBA = [val, val, val, hsba[3]]; // Return early if grayscale.
    	} else {
    		var sector = Math.floor(hue);
    		var tint1 = val * (1 - sat);
    		var tint2 = val * (1 - sat * (hue - sector));
    		var tint3 = val * (1 - sat * (1 + sector - hue));
    		var red, green, blue;
    		if (sector === 1) {
    			// Yellow to green.
    			red = tint2;
    			green = val;
    			blue = tint1;
    		} else if (sector === 2) {
    			// Green to cyan.
    			red = tint1;
    			green = val;
    			blue = tint3;
    		} else if (sector === 3) {
    			// Cyan to blue.
    			red = tint1;
    			green = tint2;
    			blue = val;
    		} else if (sector === 4) {
    			// Blue to magenta.
    			red = tint3;
    			green = tint1;
    			blue = val;
    		} else if (sector === 5) {
    			// Magenta to red.
    			red = val;
    			green = tint1;
    			blue = tint2;
    		} else {
    			// Red to yellow (sector could be 0 or 6).
    			red = val;
    			green = tint3;
    			blue = tint1;
    		}
    		RGBA = [red, green, blue, hsba[3]];
    	}

    	return RGBA;
    };

    /**
     * Convert an HSLA array to HSBA.
     */
    exports.ColorConversion._hslaToHSBA = function (hsla) {
    	var hue = hsla[0];
    	var sat = hsla[1];
    	var li = hsla[2];

    	// Calculate brightness.
    	var val;
    	if (li < 0.5) {
    		val = (1 + sat) * li;
    	} else {
    		val = li + sat - li * sat;
    	}

    	// Convert saturation.
    	sat = 2 * (val - li) / val;

    	// Hue and alpha stay the same.
    	return [hue, sat, val, hsla[3]];
    };

    /**
     * Convert an HSLA array to RGBA.
     *
     * We need to change basis from HSLA to something that can be more easily be
     * projected onto RGBA. We will choose hue and brightness as our first two
     * components, and pick a convenient third one ('zest') so that we don't need
     * to calculate formal HSBA saturation.
     */
    exports.ColorConversion._hslaToRGBA = function (hsla) {
    	var hue = hsla[0] * 6; // We will split hue into 6 sectors.
    	var sat = hsla[1];
    	var li = hsla[2];

    	var RGBA = [];

    	if (sat === 0) {
    		RGBA = [li, li, li, hsla[3]]; // Return early if grayscale.
    	} else {
    		// Calculate brightness.
    		var val;
    		if (li < 0.5) {
    			val = (1 + sat) * li;
    		} else {
    			val = li + sat - li * sat;
    		}

    		// Define zest.
    		var zest = 2 * li - val;

    		// Implement projection (project onto green by default).
    		var hzvToRGB = function (hue, zest, val) {
    			if (hue < 0) {
    				// Hue must wrap to allow projection onto red and blue.
    				hue += 6;
    			} else if (hue >= 6) {
    				hue -= 6;
    			}
    			if (hue < 1) {
    				// Red to yellow (increasing green).
    				return zest + (val - zest) * hue;
    			} else if (hue < 3) {
    				// Yellow to cyan (greatest green).
    				return val;
    			} else if (hue < 4) {
    				// Cyan to blue (decreasing green).
    				return zest + (val - zest) * (4 - hue);
    			} else {
    				// Blue to red (least green).
    				return zest;
    			}
    		};

    		// Perform projections, offsetting hue as necessary.
    		RGBA = [
    			hzvToRGB(hue + 2, zest, val),
    			hzvToRGB(hue, zest, val),
    			hzvToRGB(hue - 2, zest, val),
    			hsla[3]
    		];
    	}

    	return RGBA;
    };

    /**
     * Convert an RGBA array to HSBA.
     */
    exports.ColorConversion._rgbaToHSBA = function (rgba) {
    	var red = rgba[0];
    	var green = rgba[1];
    	var blue = rgba[2];

    	var val = Math.max(red, green, blue);
    	var chroma = val - Math.min(red, green, blue);

    	var hue, sat;
    	if (chroma === 0) {
    		// Return early if grayscale.
    		hue = 0;
    		sat = 0;
    	} else {
    		sat = chroma / val;
    		if (red === val) {
    			// Magenta to yellow.
    			hue = (green - blue) / chroma;
    		} else if (green === val) {
    			// Yellow to cyan.
    			hue = 2 + (blue - red) / chroma;
    		} else if (blue === val) {
    			// Cyan to magenta.
    			hue = 4 + (red - green) / chroma;
    		}
    		if (hue < 0) {
    			// Confine hue to the interval [0, 1).
    			hue += 6;
    		} else if (hue >= 6) {
    			hue -= 6;
    		}
    	}

    	return [hue / 6, sat, val, rgba[3]];
    };

    /**
     * Convert an RGBA array to HSLA.
     */
    exports.ColorConversion._rgbaToHSLA = function (rgba) {
    	var red = rgba[0];
    	var green = rgba[1];
    	var blue = rgba[2];

    	var val = Math.max(red, green, blue);
    	var min = Math.min(red, green, blue);
    	var li = val + min; // We will halve this later.
    	var chroma = val - min;

    	var hue, sat;
    	if (chroma === 0) {
    		// Return early if grayscale.
    		hue = 0;
    		sat = 0;
    	} else {
    		if (li < 1) {
    			sat = chroma / li;
    		} else {
    			sat = chroma / (2 - li);
    		}
    		if (red === val) {
    			// Magenta to yellow.
    			hue = (green - blue) / chroma;
    		} else if (green === val) {
    			// Yellow to cyan.
    			hue = 2 + (blue - red) / chroma;
    		} else if (blue === val) {
    			// Cyan to magenta.
    			hue = 4 + (red - green) / chroma;
    		}
    		if (hue < 0) {
    			// Confine hue to the interval [0, 1).
    			hue += 6;
    		} else if (hue >= 6) {
    			hue -= 6;
    		}
    	}

    	return [hue / 6, sat, li / 2, rgba[3]];
    };

    /**********************************************************************************************************************
     * class Color
     */

    /**
    * Each color stores the color mode and level maxes that applied at the
    * time of its construction. These are used to interpret the input arguments
    * (at construction and later for that instance of color) and to format the
    * output e.g. when saturation() is requested.
    *
    * Internally we store an array representing the ideal RGBA values in floating
    * point form, normalized from 0 to 1. From this we calculate the closest
    * screen color (RGBA levels from 0 to 255) and expose this to the renderer.
    *
    * We also cache normalized, floating point components of the color in various
    * representations as they are calculated. This is done to prevent repeating a
    * conversion that has already been performed.
    *
    * @class Color
    */
    exports.p5Color = function (vals) {
    	// Record color mode and maxes at time of construction.
    	this._storeModeAndMaxes(_currentEnv._colorMode, _currentEnv._colorMaxes);

    	// Calculate normalized RGBA values.
    	if (
    		this.mode !== RGB &&
    		this.mode !== HSL &&
    		this.mode !== HSB
    	) {
    		throw new Error(this.mode + ' is an invalid colorMode.');
    	} else {
    		this._array = p5Color._parseInputs.apply(this, vals);
    	}

    	// Expose closest screen color.
    	this._calculateLevels();
    	return this;
    };

    exports.p5Color.prototype.toAllegro = function () {
    	var a = this.levels;
    	// FIX: alpha can never be 255 because the resulting integer for WHITE would be -1 and that is equal to 'no color'
    	return Color(a[0], a[1], a[2], a[3] == 255 ? 254 : a[3]);
    };

    /**
     * This function returns the color formatted as a string. This can be useful
     * for debugging, or for using p5.js with other libraries.
     * @method toString
     * @param {String} [format] How the color string will be formatted.
     * Leaving this empty formats the string as rgba(r, g, b, a).
     * '#rgb' '#rgba' '#rrggbb' and '#rrggbbaa' format as hexadecimal color codes.
     * 'rgb' 'hsb' and 'hsl' return the color formatted in the specified color mode.
     * 'rgba' 'hsba' and 'hsla' are the same as above but with alpha channels.
     * 'rgb%' 'hsb%' 'hsl%' 'rgba%' 'hsba%' and 'hsla%' format as percentages.
     * @return {String} the formatted string
     * @example
     * let myColor;
     * function setup() {
     *   createCanvas(200, 200);
     *   stroke(255);
     *   myColor = color(100, 100, 250);
     *   fill(myColor);
     * }
     *
     * function draw() {
     *   rotate(HALF_PI);
     *   text(myColor.toString(), 0, -5);
     *   text(myColor.toString('#rrggbb'), 0, -30);
     *   text(myColor.toString('rgba%'), 0, -55);
     * }
     */
    exports.p5Color.prototype.toString = function (format) {
    	var a = this.levels;
    	var f = this._array;
    	var alpha = f[3]; // String representation uses normalized alpha

    	switch (format) {
    		case '#rrggbb':
    			return '#'.concat(
    				a[0] < 16 ? '0'.concat(a[0].toString(16)) : a[0].toString(16),
    				a[1] < 16 ? '0'.concat(a[1].toString(16)) : a[1].toString(16),
    				a[2] < 16 ? '0'.concat(a[2].toString(16)) : a[2].toString(16)
    			);

    		case '#rrggbbaa':
    			return '#'.concat(
    				a[0] < 16 ? '0'.concat(a[0].toString(16)) : a[0].toString(16),
    				a[1] < 16 ? '0'.concat(a[1].toString(16)) : a[1].toString(16),
    				a[2] < 16 ? '0'.concat(a[2].toString(16)) : a[2].toString(16),
    				a[3] < 16 ? '0'.concat(a[2].toString(16)) : a[3].toString(16)
    			);

    		case '#rgb':
    			return '#'.concat(
    				Math.round(f[0] * 15).toString(16),
    				Math.round(f[1] * 15).toString(16),
    				Math.round(f[2] * 15).toString(16)
    			);

    		case '#rgba':
    			return '#'.concat(
    				Math.round(f[0] * 15).toString(16),
    				Math.round(f[1] * 15).toString(16),
    				Math.round(f[2] * 15).toString(16),
    				Math.round(f[3] * 15).toString(16)
    			);

    		case 'rgb':
    			return 'rgb('.concat(a[0], ', ', a[1], ', ', a[2], ')');

    		case 'rgb%':
    			return 'rgb('.concat(
    				(100 * f[0]).toPrecision(3),
    				'%, ',
    				(100 * f[1]).toPrecision(3),
    				'%, ',
    				(100 * f[2]).toPrecision(3),
    				'%)'
    			);

    		case 'rgba%':
    			return 'rgba('.concat(
    				(100 * f[0]).toPrecision(3),
    				'%, ',
    				(100 * f[1]).toPrecision(3),
    				'%, ',
    				(100 * f[2]).toPrecision(3),
    				'%, ',
    				(100 * f[3]).toPrecision(3),
    				'%)'
    			);

    		case 'hsb':
    		case 'hsv':
    			if (!this.hsba) this.hsba = ColorConversion._rgbaToHSBA(this._array);
    			return 'hsb('.concat(
    				this.hsba[0] * this.maxes[HSB][0],
    				', ',
    				this.hsba[1] * this.maxes[HSB][1],
    				', ',
    				this.hsba[2] * this.maxes[HSB][2],
    				')'
    			);

    		case 'hsb%':
    		case 'hsv%':
    			if (!this.hsba) this.hsba = ColorConversion._rgbaToHSBA(this._array);
    			return 'hsb('.concat(
    				(100 * this.hsba[0]).toPrecision(3),
    				'%, ',
    				(100 * this.hsba[1]).toPrecision(3),
    				'%, ',
    				(100 * this.hsba[2]).toPrecision(3),
    				'%)'
    			);

    		case 'hsba':
    		case 'hsva':
    			if (!this.hsba) this.hsba = ColorConversion._rgbaToHSBA(this._array);
    			return 'hsba('.concat(
    				this.hsba[0] * this.maxes[HSB][0],
    				', ',
    				this.hsba[1] * this.maxes[HSB][1],
    				', ',
    				this.hsba[2] * this.maxes[HSB][2],
    				', ',
    				alpha,
    				')'
    			);

    		case 'hsba%':
    		case 'hsva%':
    			if (!this.hsba) this.hsba = ColorConversion._rgbaToHSBA(this._array);
    			return 'hsba('.concat(
    				(100 * this.hsba[0]).toPrecision(3),
    				'%, ',
    				(100 * this.hsba[1]).toPrecision(3),
    				'%, ',
    				(100 * this.hsba[2]).toPrecision(3),
    				'%, ',
    				(100 * alpha).toPrecision(3),
    				'%)'
    			);

    		case 'hsl':
    			if (!this.hsla) this.hsla = ColorConversion._rgbaToHSLA(this._array);
    			return 'hsl('.concat(
    				this.hsla[0] * this.maxes[HSL][0],
    				', ',
    				this.hsla[1] * this.maxes[HSL][1],
    				', ',
    				this.hsla[2] * this.maxes[HSL][2],
    				')'
    			);

    		case 'hsl%':
    			if (!this.hsla) this.hsla = ColorConversion._rgbaToHSLA(this._array);
    			return 'hsl('.concat(
    				(100 * this.hsla[0]).toPrecision(3),
    				'%, ',
    				(100 * this.hsla[1]).toPrecision(3),
    				'%, ',
    				(100 * this.hsla[2]).toPrecision(3),
    				'%)'
    			);

    		case 'hsla':
    			if (!this.hsla) this.hsla = ColorConversion._rgbaToHSLA(this._array);
    			return 'hsla('.concat(
    				this.hsla[0] * this.maxes[HSL][0],
    				', ',
    				this.hsla[1] * this.maxes[HSL][1],
    				', ',
    				this.hsla[2] * this.maxes[HSL][2],
    				', ',
    				alpha,
    				')'
    			);

    		case 'hsla%':
    			if (!this.hsla) this.hsla = ColorConversion._rgbaToHSLA(this._array);
    			return 'hsl('.concat(
    				(100 * this.hsla[0]).toPrecision(3),
    				'%, ',
    				(100 * this.hsla[1]).toPrecision(3),
    				'%, ',
    				(100 * this.hsla[2]).toPrecision(3),
    				'%, ',
    				(100 * alpha).toPrecision(3),
    				'%)'
    			);

    		case 'rgba':
    		default:
    			return 'rgba('.concat(a[0], ',', a[1], ',', a[2], ',', alpha, ')');
    	}
    };

    /**
     * @method setRed
     * @param {Number} red the new red value
     * @example
     * let backgroundColor;
     *
     * function setup() {
     *   backgroundColor = color(100, 50, 150);
     * }
     *
     * function draw() {
     *   backgroundColor.setRed(128 + 128 * sin(millis() / 1000));
     *   background(backgroundColor);
     * }
     */
    exports.p5Color.prototype.setRed = function (new_red) {
    	this._array[0] = new_red / this.maxes[RGB][0];
    	this._calculateLevels();
    };

    /**
     * @method setGreen
     * @param {Number} green the new green value
     * @example
     * let backgroundColor;
     *
     * function setup() {
     *   backgroundColor = color(100, 50, 150);
     * }
     *
     * function draw() {
     *   backgroundColor.setGreen(128 + 128 * sin(millis() / 1000));
     *   background(backgroundColor);
     * }
     **/
    exports.p5Color.prototype.setGreen = function (new_green) {
    	this._array[1] = new_green / this.maxes[RGB][1];
    	this._calculateLevels();
    };

    /**
     * @method setBlue
     * @param {Number} blue the new blue value
     * @example
     * let backgroundColor;
     *
     * function setup() {
     *   backgroundColor = color(100, 50, 150);
     * }
     *
     * function draw() {
     *   backgroundColor.setBlue(128 + 128 * sin(millis() / 1000));
     *   background(backgroundColor);
     * }
     **/
    exports.p5Color.prototype.setBlue = function (new_blue) {
    	this._array[2] = new_blue / this.maxes[RGB][2];
    	this._calculateLevels();
    };

    /**
     * @method setAlpha
     * @param {Number} alpha the new alpha value
     * @example
     * let squareColor;
     *
     * function setup() {
     *   ellipseMode(CORNERS);
     *   strokeWeight(4);
     *   squareColor = color(100, 50, 150);
     * }
     *
     * function draw() {
     *   background(255);
     *
     *   noFill();
     *   stroke(0);
     *   ellipse(10, 10, width - 10, height - 10);
     *
     *   squareColor.setAlpha(128 + 128 * sin(millis() / 1000));
     *   fill(squareColor);
     *   noStroke();
     *   rect(13, 13, width - 26, height - 26);
     * }
     **/
    exports.p5Color.prototype.setAlpha = function (new_alpha) {
    	this._array[3] = new_alpha / this.maxes[this.mode][3];
    	this._calculateLevels();
    };

    // calculates and stores the closest screen levels
    exports.p5Color.prototype._calculateLevels = function () {
    	var array = this._array;
    	// (loop backwards for performance)
    	var levels = (this.levels = new Array(array.length));
    	for (var i = array.length - 1; i >= 0; --i) {
    		levels[i] = Math.round(array[i] * 255);
    	}
    };

    exports.p5Color.prototype._getAlpha = function () {
    	return this._array[3] * this.maxes[this.mode][3];
    };

    // stores the color mode and maxes in this instance of Color
    // for later use (by _parseInputs())
    exports.p5Color.prototype._storeModeAndMaxes = function (new_mode, new_maxes) {
    	this.mode = new_mode;
    	this.maxes = new_maxes;
    };

    exports.p5Color.prototype._getMode = function () {
    	return this.mode;
    };

    exports.p5Color.prototype._getMaxes = function () {
    	return this.maxes;
    };

    exports.p5Color.prototype._getBlue = function () {
    	return this._array[2] * this.maxes[RGB][2];
    };

    exports.p5Color.prototype._getBrightness = function () {
    	if (!this.hsba) {
    		this.hsba = ColorConversion._rgbaToHSBA(this._array);
    	}
    	return this.hsba[2] * this.maxes[HSB][2];
    };

    exports.p5Color.prototype._getGreen = function () {
    	return this._array[1] * this.maxes[RGB][1];
    };

    /**
     * Hue is the same in HSB and HSL, but the maximum value may be different.
     * This function will return the HSB-normalized saturation when supplied with
     * an HSB color object, but will default to the HSL-normalized saturation
     * otherwise.
     */
    exports.p5Color.prototype._getHue = function () {
    	if (this.mode === HSB) {
    		if (!this.hsba) {
    			this.hsba = ColorConversion._rgbaToHSBA(this._array);
    		}
    		return this.hsba[0] * this.maxes[HSB][0];
    	} else {
    		if (!this.hsla) {
    			this.hsla = ColorConversion._rgbaToHSLA(this._array);
    		}
    		return this.hsla[0] * this.maxes[HSL][0];
    	}
    };

    exports.p5Color.prototype._getLightness = function () {
    	if (!this.hsla) {
    		this.hsla = ColorConversion._rgbaToHSLA(this._array);
    	}
    	return this.hsla[2] * this.maxes[HSL][2];
    };

    exports.p5Color.prototype._getRed = function () {
    	return this._array[0] * this.maxes[RGB][0];
    };

    /**
     * Saturation is scaled differently in HSB and HSL. This function will return
     * the HSB saturation when supplied with an HSB color object, but will default
     * to the HSL saturation otherwise.
     */
    exports.p5Color.prototype._getSaturation = function () {
    	if (this.mode === HSB) {
    		if (!this.hsba) {
    			this.hsba = ColorConversion._rgbaToHSBA(this._array);
    		}
    		return this.hsba[1] * this.maxes[HSB][1];
    	} else {
    		if (!this.hsla) {
    			this.hsla = ColorConversion._rgbaToHSLA(this._array);
    		}
    		return this.hsla[1] * this.maxes[HSL][1];
    	}
    };

    /**
     * CSS named colors.
     */
    var namedColors = {
    	aliceblue: '#f0f8ff',
    	antiquewhite: '#faebd7',
    	aqua: '#00ffff',
    	aquamarine: '#7fffd4',
    	azure: '#f0ffff',
    	beige: '#f5f5dc',
    	bisque: '#ffe4c4',
    	black: '#000000',
    	blanchedalmond: '#ffebcd',
    	blue: '#0000ff',
    	blueviolet: '#8a2be2',
    	brown: '#a52a2a',
    	burlywood: '#deb887',
    	cadetblue: '#5f9ea0',
    	chartreuse: '#7fff00',
    	chocolate: '#d2691e',
    	coral: '#ff7f50',
    	cornflowerblue: '#6495ed',
    	cornsilk: '#fff8dc',
    	crimson: '#dc143c',
    	cyan: '#00ffff',
    	darkblue: '#00008b',
    	darkcyan: '#008b8b',
    	darkgoldenrod: '#b8860b',
    	darkgray: '#a9a9a9',
    	darkgreen: '#006400',
    	darkgrey: '#a9a9a9',
    	darkkhaki: '#bdb76b',
    	darkmagenta: '#8b008b',
    	darkolivegreen: '#556b2f',
    	darkorange: '#ff8c00',
    	darkorchid: '#9932cc',
    	darkred: '#8b0000',
    	darksalmon: '#e9967a',
    	darkseagreen: '#8fbc8f',
    	darkslateblue: '#483d8b',
    	darkslategray: '#2f4f4f',
    	darkslategrey: '#2f4f4f',
    	darkturquoise: '#00ced1',
    	darkviolet: '#9400d3',
    	deeppink: '#ff1493',
    	deepskyblue: '#00bfff',
    	dimgray: '#696969',
    	dimgrey: '#696969',
    	dodgerblue: '#1e90ff',
    	firebrick: '#b22222',
    	floralwhite: '#fffaf0',
    	forestgreen: '#228b22',
    	fuchsia: '#ff00ff',
    	gainsboro: '#dcdcdc',
    	ghostwhite: '#f8f8ff',
    	gold: '#ffd700',
    	goldenrod: '#daa520',
    	gray: '#808080',
    	green: '#008000',
    	greenyellow: '#adff2f',
    	grey: '#808080',
    	honeydew: '#f0fff0',
    	hotpink: '#ff69b4',
    	indianred: '#cd5c5c',
    	indigo: '#4b0082',
    	ivory: '#fffff0',
    	khaki: '#f0e68c',
    	lavender: '#e6e6fa',
    	lavenderblush: '#fff0f5',
    	lawngreen: '#7cfc00',
    	lemonchiffon: '#fffacd',
    	lightblue: '#add8e6',
    	lightcoral: '#f08080',
    	lightcyan: '#e0ffff',
    	lightgoldenrodyellow: '#fafad2',
    	lightgray: '#d3d3d3',
    	lightgreen: '#90ee90',
    	lightgrey: '#d3d3d3',
    	lightpink: '#ffb6c1',
    	lightsalmon: '#ffa07a',
    	lightseagreen: '#20b2aa',
    	lightskyblue: '#87cefa',
    	lightslategray: '#778899',
    	lightslategrey: '#778899',
    	lightsteelblue: '#b0c4de',
    	lightyellow: '#ffffe0',
    	lime: '#00ff00',
    	limegreen: '#32cd32',
    	linen: '#faf0e6',
    	magenta: '#ff00ff',
    	maroon: '#800000',
    	mediumaquamarine: '#66cdaa',
    	mediumblue: '#0000cd',
    	mediumorchid: '#ba55d3',
    	mediumpurple: '#9370db',
    	mediumseagreen: '#3cb371',
    	mediumslateblue: '#7b68ee',
    	mediumspringgreen: '#00fa9a',
    	mediumturquoise: '#48d1cc',
    	mediumvioletred: '#c71585',
    	midnightblue: '#191970',
    	mintcream: '#f5fffa',
    	mistyrose: '#ffe4e1',
    	moccasin: '#ffe4b5',
    	navajowhite: '#ffdead',
    	navy: '#000080',
    	oldlace: '#fdf5e6',
    	olive: '#808000',
    	olivedrab: '#6b8e23',
    	orange: '#ffa500',
    	orangered: '#ff4500',
    	orchid: '#da70d6',
    	palegoldenrod: '#eee8aa',
    	palegreen: '#98fb98',
    	paleturquoise: '#afeeee',
    	palevioletred: '#db7093',
    	papayawhip: '#ffefd5',
    	peachpuff: '#ffdab9',
    	peru: '#cd853f',
    	pink: '#ffc0cb',
    	plum: '#dda0dd',
    	powderblue: '#b0e0e6',
    	purple: '#800080',
    	red: '#ff0000',
    	rosybrown: '#bc8f8f',
    	royalblue: '#4169e1',
    	saddlebrown: '#8b4513',
    	salmon: '#fa8072',
    	sandybrown: '#f4a460',
    	seagreen: '#2e8b57',
    	seashell: '#fff5ee',
    	sienna: '#a0522d',
    	silver: '#c0c0c0',
    	skyblue: '#87ceeb',
    	slateblue: '#6a5acd',
    	slategray: '#708090',
    	slategrey: '#708090',
    	snow: '#fffafa',
    	springgreen: '#00ff7f',
    	steelblue: '#4682b4',
    	tan: '#d2b48c',
    	teal: '#008080',
    	thistle: '#d8bfd8',
    	tomato: '#ff6347',
    	turquoise: '#40e0d0',
    	violet: '#ee82ee',
    	wheat: '#f5deb3',
    	white: '#ffffff',
    	whitesmoke: '#f5f5f5',
    	yellow: '#ffff00',
    	yellowgreen: '#9acd32'
    };

    /**
     * These regular expressions are used to build up the patterns for matching
     * viable CSS color strings: fragmenting the regexes in this way increases the
     * legibility and comprehensibility of the code.
     *
     * Note that RGB values of .9 are not parsed by IE, but are supported here for
     * color string consistency.
     */
    var WHITESPACE = /\s*/; // Match zero or more whitespace characters.
    var INTEGER = /(\d{1,3})/; // Match integers: 79, 255, etc.
    var DECIMAL = /((?:\d+(?:\.\d+)?)|(?:\.\d+))/; // Match 129.6, 79, .9, etc.
    var PERCENT = new RegExp(DECIMAL.source + '%'); // Match 12.9%, 79%, .9%, etc.

    /**
     * Full color string patterns. The capture groups are necessary.
     */
    var colorPatterns = {
    	// Match colors in format #XXX, e.g. #416.
    	HEX3: /^#([a-f0-9])([a-f0-9])([a-f0-9])$/i,

    	// Match colors in format #XXXX, e.g. #5123.
    	HEX4: /^#([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])$/i,

    	// Match colors in format #XXXXXX, e.g. #b4d455.
    	HEX6: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i,

    	// Match colors in format #XXXXXXXX, e.g. #b4d45535.
    	HEX8: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i,

    	// Match colors in format rgb(R, G, B), e.g. rgb(255, 0, 128).
    	RGB: new RegExp(
    		[
    			'^rgb\\(',
    			INTEGER.source,
    			',',
    			INTEGER.source,
    			',',
    			INTEGER.source,
    			'\\)$'
    		].join(WHITESPACE.source),
    		'i'
    	),

    	// Match colors in format rgb(R%, G%, B%), e.g. rgb(100%, 0%, 28.9%).
    	RGB_PERCENT: new RegExp(
    		[
    			'^rgb\\(',
    			PERCENT.source,
    			',',
    			PERCENT.source,
    			',',
    			PERCENT.source,
    			'\\)$'
    		].join(WHITESPACE.source),
    		'i'
    	),

    	// Match colors in format rgb(R, G, B, A), e.g. rgb(255, 0, 128, 0.25).
    	RGBA: new RegExp(
    		[
    			'^rgba\\(',
    			INTEGER.source,
    			',',
    			INTEGER.source,
    			',',
    			INTEGER.source,
    			',',
    			DECIMAL.source,
    			'\\)$'
    		].join(WHITESPACE.source),
    		'i'
    	),

    	// Match colors in format rgb(R%, G%, B%, A), e.g. rgb(100%, 0%, 28.9%, 0.5).
    	RGBA_PERCENT: new RegExp(
    		[
    			'^rgba\\(',
    			PERCENT.source,
    			',',
    			PERCENT.source,
    			',',
    			PERCENT.source,
    			',',
    			DECIMAL.source,
    			'\\)$'
    		].join(WHITESPACE.source),
    		'i'
    	),

    	// Match colors in format hsla(H, S%, L%), e.g. hsl(100, 40%, 28.9%).
    	HSL: new RegExp(
    		[
    			'^hsl\\(',
    			INTEGER.source,
    			',',
    			PERCENT.source,
    			',',
    			PERCENT.source,
    			'\\)$'
    		].join(WHITESPACE.source),
    		'i'
    	),

    	// Match colors in format hsla(H, S%, L%, A), e.g. hsla(100, 40%, 28.9%, 0.5).
    	HSLA: new RegExp(
    		[
    			'^hsla\\(',
    			INTEGER.source,
    			',',
    			PERCENT.source,
    			',',
    			PERCENT.source,
    			',',
    			DECIMAL.source,
    			'\\)$'
    		].join(WHITESPACE.source),
    		'i'
    	),

    	// Match colors in format hsb(H, S%, B%), e.g. hsb(100, 40%, 28.9%).
    	HSB: new RegExp(
    		[
    			'^hsb\\(',
    			INTEGER.source,
    			',',
    			PERCENT.source,
    			',',
    			PERCENT.source,
    			'\\)$'
    		].join(WHITESPACE.source),
    		'i'
    	),

    	// Match colors in format hsba(H, S%, B%, A), e.g. hsba(100, 40%, 28.9%, 0.5).
    	HSBA: new RegExp(
    		[
    			'^hsba\\(',
    			INTEGER.source,
    			',',
    			PERCENT.source,
    			',',
    			PERCENT.source,
    			',',
    			DECIMAL.source,
    			'\\)$'
    		].join(WHITESPACE.source),
    		'i'
    	)
    };

    /**
     * For a number of different inputs, returns a color formatted as [r, g, b, a]
     * arrays, with each component normalized between 0 and 1.
     *
     * @private
     * @param {Array} [...args] An 'array-like' object that represents a list of
     *                          arguments
     * @return {Number[]}       a color formatted as [r, g, b, a]
     *                          Example:
     *                          input        ==> output
     *                          g            ==> [g, g, g, 255]
     *                          g,a          ==> [g, g, g, a]
     *                          r, g, b      ==> [r, g, b, 255]
     *                          r, g, b, a   ==> [r, g, b, a]
     *                          [g]          ==> [g, g, g, 255]
     *                          [g, a]       ==> [g, g, g, a]
     *                          [r, g, b]    ==> [r, g, b, 255]
     *                          [r, g, b, a] ==> [r, g, b, a]
     * @example
     * // todo
     */
    exports.p5Color._parseInputs = function (r, g, b, a) {
    	var numArgs = arguments.length;
    	var mode = this.mode;
    	var maxes = this.maxes[mode];
    	var results = [];
    	var i;

    	if (numArgs >= 3) {
    		// Argument is a list of component values.

    		results[0] = r / maxes[0];
    		results[1] = g / maxes[1];
    		results[2] = b / maxes[2];

    		// Alpha may be undefined, so default it to 100%.
    		if (typeof a === 'number') {
    			results[3] = a / maxes[3];
    		} else {
    			results[3] = 1;
    		}

    		// Constrain components to the range [0,1].
    		// (loop backwards for performance)
    		for (i = results.length - 1; i >= 0; --i) {
    			var result = results[i];
    			if (result < 0) {
    				results[i] = 0;
    			} else if (result > 1) {
    				results[i] = 1;
    			}
    		}

    		// Convert to RGBA and return.
    		if (mode === HSL) {
    			return ColorConversion._hslaToRGBA(results);
    		} else if (mode === HSB) {
    			return ColorConversion._hsbaToRGBA(results);
    		} else {
    			return results;
    		}
    	} else if (numArgs === 1 && typeof r === 'string') {
    		var str = r.trim().toLowerCase();

    		// Return if string is a named colour.
    		if (namedColors[str]) {
    			return p5Color._parseInputs.call(this, namedColors[str]);
    		}

    		// Try RGBA pattern matching.
    		if (colorPatterns.HEX3.test(str)) {
    			// #rgb
    			results = colorPatterns.HEX3.exec(str)
    				.slice(1)
    				.map(function (color) {
    					return parseInt(color + color, 16) / 255;
    				});
    			results[3] = 1;
    			return results;
    		} else if (colorPatterns.HEX6.test(str)) {
    			// #rrggbb
    			results = colorPatterns.HEX6.exec(str)
    				.slice(1)
    				.map(function (color) {
    					return parseInt(color, 16) / 255;
    				});
    			results[3] = 1;
    			return results;
    		} else if (colorPatterns.HEX4.test(str)) {
    			// #rgba
    			results = colorPatterns.HEX4.exec(str)
    				.slice(1)
    				.map(function (color) {
    					return parseInt(color + color, 16) / 255;
    				});
    			return results;
    		} else if (colorPatterns.HEX8.test(str)) {
    			// #rrggbbaa
    			results = colorPatterns.HEX8.exec(str)
    				.slice(1)
    				.map(function (color) {
    					return parseInt(color, 16) / 255;
    				});
    			return results;
    		} else if (colorPatterns.RGB.test(str)) {
    			// rgb(R,G,B)
    			results = colorPatterns.RGB.exec(str)
    				.slice(1)
    				.map(function (color) {
    					return color / 255;
    				});
    			results[3] = 1;
    			return results;
    		} else if (colorPatterns.RGB_PERCENT.test(str)) {
    			// rgb(R%,G%,B%)
    			results = colorPatterns.RGB_PERCENT.exec(str)
    				.slice(1)
    				.map(function (color) {
    					return parseFloat(color) / 100;
    				});
    			results[3] = 1;
    			return results;
    		} else if (colorPatterns.RGBA.test(str)) {
    			// rgba(R,G,B,A)
    			results = colorPatterns.RGBA.exec(str)
    				.slice(1)
    				.map(function (color, idx) {
    					if (idx === 3) {
    						return parseFloat(color);
    					}
    					return color / 255;
    				});
    			return results;
    		} else if (colorPatterns.RGBA_PERCENT.test(str)) {
    			// rgba(R%,G%,B%,A%)
    			results = colorPatterns.RGBA_PERCENT.exec(str)
    				.slice(1)
    				.map(function (color, idx) {
    					if (idx === 3) {
    						return parseFloat(color);
    					}
    					return parseFloat(color) / 100;
    				});
    			return results;
    		}

    		// Try HSLA pattern matching.
    		if (colorPatterns.HSL.test(str)) {
    			// hsl(H,S,L)
    			results = colorPatterns.HSL.exec(str)
    				.slice(1)
    				.map(function (color, idx) {
    					if (idx === 0) {
    						return parseInt(color, 10) / 360;
    					}
    					return parseInt(color, 10) / 100;
    				});
    			results[3] = 1;
    		} else if (colorPatterns.HSLA.test(str)) {
    			// hsla(H,S,L,A)
    			results = colorPatterns.HSLA.exec(str)
    				.slice(1)
    				.map(function (color, idx) {
    					if (idx === 0) {
    						return parseInt(color, 10) / 360;
    					} else if (idx === 3) {
    						return parseFloat(color);
    					}
    					return parseInt(color, 10) / 100;
    				});
    		}
    		results = results.map(function (value) {
    			return Math.max(Math.min(value, 1), 0);
    		});
    		if (results.length) {
    			return ColorConversion._hslaToRGBA(results);
    		}

    		// Try HSBA pattern matching.
    		if (colorPatterns.HSB.test(str)) {
    			// hsb(H,S,B)
    			results = colorPatterns.HSB.exec(str)
    				.slice(1)
    				.map(function (color, idx) {
    					if (idx === 0) {
    						return parseInt(color, 10) / 360;
    					}
    					return parseInt(color, 10) / 100;
    				});
    			results[3] = 1;
    		} else if (colorPatterns.HSBA.test(str)) {
    			// hsba(H,S,B,A)
    			results = colorPatterns.HSBA.exec(str)
    				.slice(1)
    				.map(function (color, idx) {
    					if (idx === 0) {
    						return parseInt(color, 10) / 360;
    					} else if (idx === 3) {
    						return parseFloat(color);
    					}
    					return parseInt(color, 10) / 100;
    				});
    		}

    		if (results.length) {
    			// (loop backwards for performance)
    			for (i = results.length - 1; i >= 0; --i) {
    				results[i] = Math.max(Math.min(results[i], 1), 0);
    			}

    			return ColorConversion._hsbaToRGBA(results);
    		}

    		// Input did not match any CSS color pattern: default to white.
    		results = [1, 1, 1, 1];
    	} else if ((numArgs === 1 || numArgs === 2) && typeof r === 'number') {
    		// 'Grayscale' mode.

    		/**
    		 * For HSB and HSL, interpret the gray level as a brightness/lightness
    		 * value (they are equivalent when chroma is zero). For RGB, normalize the
    		 * gray level according to the blue maximum.
    		 */
    		results[0] = r / maxes[2];
    		results[1] = r / maxes[2];
    		results[2] = r / maxes[2];

    		// Alpha may be undefined, so default it to 100%.
    		if (typeof g === 'number') {
    			results[3] = g / maxes[3];
    		} else {
    			results[3] = 1;
    		}

    		// Constrain components to the range [0,1].
    		results = results.map(function (value) {
    			return Math.max(Math.min(value, 1), 0);
    		});
    	} else {
    		throw new Error(arguments + 'is not a valid color representation.');
    	}

    	return results;
    };

    /**********************************************************************************************************************
     * creating/reading
     */

    /**
    * Extracts the alpha value from a color or pixel array.
    *
    * @method alpha
    * @param {Color|Number[]|String} color Color object, color components,
    *                                         or CSS color
    * @return {Number} the alpha value
    * @example
    * noStroke();
    * let c = color(0, 126, 255, 102);
    * fill(c);
    * rect(15, 15, 35, 70);
    * let value = alpha(c); // Sets 'value' to 102
    * fill(value);
    * rect(50, 15, 35, 70);
    */
    exports.alpha = function (c) {
    	return color(c)._getAlpha();
    };

    /**
     * Extracts the blue value from a color or pixel array.
     *
     * @method blue
     * @param {Color|Number[]|String} color Color object, color components,
     *                                         or CSS color
     * @return {Number} the blue value
     * @example
     * let c = color(175, 100, 220); // Define color 'c'
     * fill(c); // Use color variable 'c' as fill color
     * rect(15, 20, 35, 60); // Draw left rectangle
     *
     * let blueValue = blue(c); // Get blue in 'c'
     * print(blueValue); // Prints "220.0"
     * fill(0, 0, blueValue); // Use 'blueValue' in new fill
     * rect(50, 20, 35, 60); // Draw right rectangle
     */
    exports.blue = function (c) {
    	return color(c)._getBlue();
    };

    /**
     * Extracts the HSB brightness value from a color or pixel array.
     *
     * @method brightness
     * @param {Color|Number[]|String} color Color object, color components,
     *                                         or CSS color
     * @return {Number} the brightness value
     * @example
     * noStroke();
     * colorMode(HSB, 255);
     * let c = color(0, 126, 255);
     * fill(c);
     * rect(15, 20, 35, 60);
     * let value = brightness(c); // Sets 'value' to 255
     * fill(value);
     * rect(50, 20, 35, 60);
     */
    exports.brightness = function (c) {
    	return color(c)._getBrightness();
    };

    /**
     * Creates colors for storing in variables of the color datatype. The
     * parameters are interpreted as RGB or HSB values depending on the
     * current colorMode(). The default mode is RGB values from 0 to 255
     * and, therefore, the function call color(255, 204, 0) will return a
     * bright yellow color.
     * <br><br>
     * Note that if only one value is provided to color(), it will be interpreted
     * as a grayscale value. Add a second value, and it will be used for alpha
     * transparency. When three values are specified, they are interpreted as
     * either RGB or HSB values. Adding a fourth value applies alpha
     * transparency.
     * <br><br>
     * If a single string argument is provided, RGB, RGBA and Hex CSS color
     * strings and all named color strings are supported. In this case, an alpha
     * number value as a second argument is not supported, the RGBA form should be
     * used.
     *
     * @method color
     * @param  {Number}        gray    number specifying value between white
     *                                 and black.
     * @param  {Number}        [alpha] alpha value relative to current color range
     *                                 (default is 0-255)
     * @return {Color}              resulting color
     *
     * @example
     * let c = color(255, 204, 0); // Define color 'c'
     * fill(c); // Use color variable 'c' as fill color
     * noStroke(); // Don't draw a stroke around shapes
     * rect(30, 20, 55, 55); // Draw rectangle
     *
     * let c = color(255, 204, 0); // Define color 'c'
     * fill(c); // Use color variable 'c' as fill color
     * noStroke(); // Don't draw a stroke around shapes
     * ellipse(25, 25, 80, 80); // Draw left circle
     *
     * // Using only one value with color()
     * // generates a grayscale value.
     * c = color(65); // Update 'c' with grayscale value
     * fill(c); // Use updated 'c' as fill color
     * ellipse(75, 75, 80, 80); // Draw right circle
     *
     * // Named SVG & CSS colors may be used,
     * let c = color('magenta');
     * fill(c); // Use 'c' as fill color
     * noStroke(); // Don't draw a stroke around shapes
     * rect(20, 20, 60, 60); // Draw rectangle
     *
     * // as can hex color codes:
     * noStroke(); // Don't draw a stroke around shapes
     * let c = color('#0f0');
     * fill(c); // Use 'c' as fill color
     * rect(0, 10, 45, 80); // Draw rectangle
     *
     * c = color('#00ff00');
     * fill(c); // Use updated 'c' as fill color
     * rect(55, 10, 45, 80); // Draw rectangle
     *
     * // RGB and RGBA color strings are also supported:
     * // these all set to the same color (solid blue)
     * let c;
     * noStroke(); // Don't draw a stroke around shapes
     * c = color('rgb(0,0,255)');
     * fill(c); // Use 'c' as fill color
     * rect(10, 10, 35, 35); // Draw rectangle
     *
     * c = color('rgb(0%, 0%, 100%)');
     * fill(c); // Use updated 'c' as fill color
     * rect(55, 10, 35, 35); // Draw rectangle
     *
     * c = color('rgba(0, 0, 255, 1)');
     * fill(c); // Use updated 'c' as fill color
     * rect(10, 55, 35, 35); // Draw rectangle
     *
     * c = color('rgba(0%, 0%, 100%, 1)');
     * fill(c); // Use updated 'c' as fill color
     * rect(55, 55, 35, 35); // Draw rectangle
     *
     * // HSL color is also supported and can be specified
     * // by value
     * let c;
     * noStroke(); // Don't draw a stroke around shapes
     * c = color('hsl(160, 100%, 50%)');
     * fill(c); // Use 'c' as fill color
     * rect(0, 10, 45, 80); // Draw rectangle
     *
     * c = color('hsla(160, 100%, 50%, 0.5)');
     * fill(c); // Use updated 'c' as fill color
     * rect(55, 10, 45, 80); // Draw rectangle
     *
     * // HSB color is also supported and can be specified
     * // by value
     * let c;
     * noStroke(); // Don't draw a stroke around shapes
     * c = color('hsb(160, 100%, 50%)');
     * fill(c); // Use 'c' as fill color
     * rect(0, 10, 45, 80); // Draw rectangle
     *
     * c = color('hsba(160, 100%, 50%, 0.5)');
     * fill(c); // Use updated 'c' as fill color
     * rect(55, 10, 45, 80); // Draw rectangle
     *
     * let c; // Declare color 'c'
     * noStroke(); // Don't draw a stroke around shapes
     *
     * // If no colorMode is specified, then the
     * // default of RGB with scale of 0-255 is used.
     * c = color(50, 55, 100); // Create a color for 'c'
     * fill(c); // Use color variable 'c' as fill color
     * rect(0, 10, 45, 80); // Draw left rect
     *
     * colorMode(HSB, 100); // Use HSB with scale of 0-100
     * c = color(50, 55, 100); // Update 'c' with new color
     * fill(c); // Use updated 'c' as fill color
     * rect(55, 10, 45, 80); // Draw right rect
     */
    /**
     * @method color
     * @param  {Number}        v1      red or hue value relative to
     *                                 the current color range
     * @param  {Number}        v2      green or saturation value
     *                                 relative to the current color range
     * @param  {Number}        v3      blue or brightness value
     *                                 relative to the current color range
     * @param  {Number}        [alpha]
     * @return {Color}
     */

    /**
     * @method color
     * @param  {String}        value   a color string
     * @return {Color}
     */
    /**
     * @method color
     * @param  {Number[]}      values  an array containing the red,green,blue &
     *                                 and alpha components of the color
     * @return {Color}
     */
    /**
     * @method color
     * @param  {Color}     color
     * @return {Color}
     */

    exports.color = function () {
    	if (arguments[0] instanceof p5Color) {
    		return arguments[0]; // Do nothing if argument is already a color object.
    	}

    	var args = arguments[0] instanceof Array ? arguments[0] : arguments;
    	return new p5Color(args);
    };

    /**
     * Extracts the green value from a color or pixel array.
     *
     * @method green
     * @param {Color|Number[]|String} color Color object, color components,
     *                                         or CSS color
     * @return {Number} the green value
     * @example
     * let c = color(20, 75, 200); // Define color 'c'
     * fill(c); // Use color variable 'c' as fill color
     * rect(15, 20, 35, 60); // Draw left rectangle
     *
     * let greenValue = green(c); // Get green in 'c'
     * print(greenValue); // Print "75.0"
     * fill(0, greenValue, 0); // Use 'greenValue' in new fill
     * rect(50, 20, 35, 60); // Draw right rectangle
     */

    exports.green = function (c) {
    	return color(c)._getGreen();
    };

    /**
     * Extracts the hue value from a color or pixel array.
     *
     * Hue exists in both HSB and HSL. This function will return the
     * HSB-normalized hue when supplied with an HSB color object (or when supplied
     * with a pixel array while the color mode is HSB), but will default to the
     * HSL-normalized hue otherwise. (The values will only be different if the
     * maximum hue setting for each system is different.)
     *
     * @method hue
     * @param {Color|Number[]|String} color Color object, color components,
     *                                         or CSS color
     * @return {Number} the hue
     * @example
     * noStroke();
     * colorMode(HSB, 255);
     * let c = color(0, 126, 255);
     * fill(c);
     * rect(15, 20, 35, 60);
     * let value = hue(c); // Sets 'value' to "0"
     * fill(value);
     * rect(50, 20, 35, 60);
     */
    exports.hue = function (c) {
    	return color(c)._getHue();
    };

    /**
     * Blends two colors to find a third color somewhere between them. The amt
     * parameter is the amount to interpolate between the two values where 0.0
     * equal to the first color, 0.1 is very near the first color, 0.5 is halfway
     * in between, etc. An amount below 0 will be treated as 0. Likewise, amounts
     * above 1 will be capped at 1. This is different from the behavior of lerp(),
     * but necessary because otherwise numbers outside the range will produce
     * strange and unexpected colors.
     * <br><br>
     * The way that colours are interpolated depends on the current color mode.
     *
     * @method lerpColor
     * @param  {Color} c1  interpolate from this color
     * @param  {Color} c2  interpolate to this color
     * @param  {Number}       amt number between 0 and 1
     * @return {Color}     interpolated color
     * @example
     * colorMode(RGB);
     * stroke(255);
     * background(51);
     * let from = color(218, 165, 32);
     * let to = color(72, 61, 139);
     * colorMode(RGB); // Try changing to HSB.
     * let interA = lerpColor(from, to, 0.33);
     * let interB = lerpColor(from, to, 0.66);
     * fill(from);
     * rect(10, 20, 20, 60);
     * fill(interA);
     * rect(30, 20, 20, 60);
     * fill(interB);
     * rect(50, 20, 20, 60);
     * fill(to);
     * rect(70, 20, 20, 60);
     */
    exports.lerpColor = function (c1, c2, amt) {
    	var mode = _currentEnv._colorMode;
    	var maxes = _colorMaxes;
    	var l0, l1, l2, l3;
    	var fromArray, toArray;

    	if (mode === RGB) {
    		fromArray = c1.levels.map(function (level) {
    			return level / 255;
    		});
    		toArray = c2.levels.map(function (level) {
    			return level / 255;
    		});
    	} else if (mode === HSB) {
    		c1._getBrightness(); // Cache hsba so it definitely exists.
    		c2._getBrightness();
    		fromArray = c1.hsba;
    		toArray = c2.hsba;
    	} else if (mode === HSL) {
    		c1._getLightness(); // Cache hsla so it definitely exists.
    		c2._getLightness();
    		fromArray = c1.hsla;
    		toArray = c2.hsla;
    	} else {
    		throw new Error(mode + 'cannot be used for interpolation.');
    	}

    	// Prevent extrapolation.
    	amt = Math.max(Math.min(amt, 1), 0);

    	// Define lerp here itself if user isn't using math module.
    	// Maintains the definition as found in math/calculation.js
    	if (typeof this.lerp === 'undefined') {
    		this.lerp = function (start, stop, amt) {
    			return amt * (stop - start) + start;
    		};
    	}

    	// Perform interpolation.
    	l0 = this.lerp(fromArray[0], toArray[0], amt);
    	l1 = this.lerp(fromArray[1], toArray[1], amt);
    	l2 = this.lerp(fromArray[2], toArray[2], amt);
    	l3 = this.lerp(fromArray[3], toArray[3], amt);

    	// Scale components.
    	l0 *= maxes[mode][0];
    	l1 *= maxes[mode][1];
    	l2 *= maxes[mode][2];
    	l3 *= maxes[mode][3];

    	return color(l0, l1, l2, l3);
    };

    /**
     * Extracts the HSL lightness value from a color or pixel array.
     *
     * @method lightness
     * @param {Color|Number[]|String} color Color object, color components,
     *                                         or CSS color
     * @return {Number} the lightness
     * @example
     * noStroke();
     * colorMode(HSL);
     * let c = color(156, 100, 50, 1);
     * fill(c);
     * rect(15, 20, 35, 60);
     * let value = lightness(c); // Sets 'value' to 50
     * fill(value);
     * rect(50, 20, 35, 60);
     */
    exports.lightness = function (c) {
    	return color(c)._getLightness();
    };

    /**
     * Extracts the red value from a color or pixel array.
     *
     * @method red
     * @param {Color|Number[]|String} color Color object, color components,
     *                                         or CSS color
     * @return {Number} the red value
     * @example
     * let c = color(255, 204, 0); // Define color 'c'
     * fill(c); // Use color variable 'c' as fill color
     * rect(15, 20, 35, 60); // Draw left rectangle
     *
     * let redValue = red(c); // Get red in 'c'
     * print(redValue); // Print "255.0"
     * fill(redValue, 0, 0); // Use 'redValue' in new fill
     * rect(50, 20, 35, 60); // Draw right rectangle
     *
     * colorMode(RGB, 255);
     * let c = color(127, 255, 0);
     * colorMode(RGB, 1);
     * let myColor = red(c);
     * print(myColor);
     */
    exports.red = function (c) {
    	return color(c)._getRed();
    };

    /**
     * Extracts the saturation value from a color or pixel array.
     *
     * Saturation is scaled differently in HSB and HSL. This function will return
     * the HSB saturation when supplied with an HSB color object (or when supplied
     * with a pixel array while the color mode is HSB), but will default to the
     * HSL saturation otherwise.
     *
     * @method saturation
     * @param {Color|Number[]|String} color Color object, color components,
     *                                         or CSS color
     * @return {Number} the saturation value
     * @example
     * noStroke();
     * colorMode(HSB, 255);
     * let c = color(0, 126, 255);
     * fill(c);
     * rect(15, 20, 35, 60);
     * let value = saturation(c); // Sets 'value' to 126
     * fill(value);
     * rect(50, 20, 35, 60);
     */

    exports.saturation = function (c) {
    	return color(c)._getSaturation();
    };

    /**********************************************************************************************************************
     * setting
     */

    /**
    * The background() function sets the color used for the background of the
    * p5.js canvas. The default background is transparent. This function is
    * typically used within draw() to clear the display window at the beginning
    * of each frame, but it can be used inside setup() to set the background on
    * the first frame of animation or if the background need only be set once.
    * <br><br>
    * The color is either specified in terms of the RGB, HSB, or HSL color
    * depending on the current colorMode. (The default color space is RGB, with
    * each value in the range from 0 to 255). The alpha range by default is also 0 to 255.
    * <br><br>
    * If a single string argument is provided, RGB, RGBA and Hex CSS color strings
    * and all named color strings are supported. In this case, an alpha number
    * value as a second argument is not supported, the RGBA form should be used.
    * <br><br>
    * A Color object can also be provided to set the background color.
    * <br><br>
    * An Image can also be provided to set the background image.
    *
    * @method background
    * @param {Color} color     any value created by the color() function
    *
    * @example
    * // Grayscale integer value
    * background(51);
    *
    * // R, G & B integer values
    * background(255, 204, 0);
    *
    * // H, S & B integer values
    * colorMode(HSB);
    * background(255, 204, 100);
    *
    * // Named SVG/CSS color string
    * background('red');
    *
    * // three-digit hexadecimal RGB notation
    * background('#fae');
    *
    * // six-digit hexadecimal RGB notation
    * background('#222222');
    *
    * // integer RGB notation
    * background('rgb(0,255,0)');
    *
    * // integer RGBA notation
    * background('rgba(0,255,0, 0.25)');
    *
    * // percentage RGB notation
    * background('rgb(100%,0%,10%)');
    *
    * // percentage RGBA notation
    * background('rgba(100%,0%,100%,0.5)');
    *
    * // p5 Color object
    * background(color(0, 0, 255));
    */

    /**
     * @method background
     * @param {String} colorstring color string, possible formats include: integer
     *                         rgb() or rgba(), percentage rgb() or rgba(),
     *                         3-digit hex, 6-digit hex
     * @param {Number} [a]         opacity of the background relative to current
     *                             color range (default is 0-255)
     */

    /**
     * @method background
     * @param {Number} gray   specifies a value between white and black
     * @param {Number} [a]
     */

    /**
     * @method background
     * @param {Number} v1     red or hue value (depending on the current color
     *                        mode)
     * @param {Number} v2     green or saturation value (depending on the current
     *                        color mode)
     * @param {Number} v3     blue or brightness value (depending on the current
     *                        color mode)
     * @param  {Number} [a]
     */

    /**
     * @method background
     * @param  {Number[]}      values  an array containing the red,green,blue &
     *                                 and alpha components of the color
     */

    /**
     * @method background
     * @param {Bitmap} bitmap     image created with loadImage() or createImage(),
     *                             to set as background
     *                             (must be same size as the sketch window)
     * @param  {Number}  [a]
     */

    exports.background = function () {
    	if (arguments[0] instanceof Bitmap) {
    		arguments[0].Draw(0, 0);
    	} else {
    		if (arguments[0] instanceof p5Color) {
    			_background = arguments[0].toAllegro()
    		} else {
    			_background = new p5Color(arguments).toAllegro();
    		}
    		FilledBox(0, 0, SizeX(), SizeY(), _background);
    	}
    };

    /**
     * Clears the pixels within a buffer. This function only clears the canvas.
     * It will not clear objects created by createX() methods such as
     * createVideo() or createDiv().
     * Unlike the main graphics context, pixels in additional graphics areas created
     * with createGraphics() can be entirely
     * or partially transparent. This function clears everything to make all of
     * the pixels 100% transparent.
     *
     * @method clear
     * @example
     * // Clear the screen on mouse press.
     * function setup() {
     *   createCanvas(100, 100);
     * }
     *
     * function draw() {
     *   ellipse(mouseX, mouseY, 20, 20);
     * }
     *
     * function mousePressed() {
     *   clear();
     * }
     */
    exports.clear = function () {
    	ClearScreen(_background);
    };

    /**
     * colorMode() changes the way p5.js interprets color data. By default, the
     * parameters for fill(), stroke(), background(), and color() are defined by
     * values between 0 and 255 using the RGB color model. This is equivalent to
     * setting colorMode(RGB, 255). Setting colorMode(HSB) lets you use the HSB
     * system instead. By default, this is colorMode(HSB, 360, 100, 100, 1). You
     * can also use HSL.
     * <br><br>
     * Note: existing color objects remember the mode that they were created in,
     * so you can change modes as you like without affecting their appearance.
     *
     *
     * @method colorMode
     * @param {Constant} mode   either RGB, HSB or HSL, corresponding to
     *                          Red/Green/Blue and Hue/Saturation/Brightness
     *                          (or Lightness)
     * @param {Number}  [max]  range for all values
     *
     * @example
     * noStroke();
     * colorMode(RGB, 100);
     * for (let i = 0; i < 100; i++) {
     *   for (let j = 0; j < 100; j++) {
     *     stroke(i, j, 0);
     *     point(i, j);
     *   }
     * }
     *
     * noStroke();
     * colorMode(HSB, 100);
     * for (let i = 0; i < 100; i++) {
     *   for (let j = 0; j < 100; j++) {
     *     stroke(i, j, 100);
     *     point(i, j);
     *   }
     * }
     *
     * colorMode(RGB, 255);
     * let c = color(127, 255, 0);
     *
     * colorMode(RGB, 1);
     * let myColor = c._getRed();
     * text(myColor, 10, 10, 80, 80);
     *
     * noFill();
     * colorMode(RGB, 255, 255, 255, 1);
     * background(255);
     *
     * strokeWeight(4);
     * stroke(255, 0, 10, 0.3);
     * ellipse(40, 40, 50, 50);
     * ellipse(50, 50, 40, 40);
     */
    /**
     * @method colorMode
     * @param {Constant} mode
     * @param {Number} max1     range for the red or hue depending on the
     *                              current color mode
     * @param {Number} max2     range for the green or saturation depending
     *                              on the current color mode
     * @param {Number} max3     range for the blue or brightness/lightness
     *                              depending on the current color mode
     * @param {Number} [maxA]   range for the alpha
     */
    exports.colorMode = function (mode, max1, max2, max3, maxA) {
    	if (
    		mode === RGB ||
    		mode === HSB ||
    		mode === HSL
    	) {
    		// Set color mode.
    		_currentEnv._colorMode = mode;

    		// Set color maxes.
    		var maxes = _currentEnv._colorMaxes[mode];
    		if (arguments.length === 2) {
    			maxes[0] = max1; // Red
    			maxes[1] = max1; // Green
    			maxes[2] = max1; // Blue
    			maxes[3] = max1; // Alpha
    		} else if (arguments.length === 4) {
    			maxes[0] = max1; // Red
    			maxes[1] = max2; // Green
    			maxes[2] = max3; // Blue
    		} else if (arguments.length === 5) {
    			maxes[0] = max1; // Red
    			maxes[1] = max2; // Green
    			maxes[2] = max3; // Blue
    			maxes[3] = maxA; // Alpha
    		}
    	}
    };

    /**
     * Sets the color used to fill shapes. For example, if you run
     * fill(204, 102, 0), all subsequent shapes will be filled with orange. This
     * color is either specified in terms of the RGB or HSB color depending on
     * the current colorMode(). (The default color space is RGB, with each value
     * in the range from 0 to 255). The alpha range by default is also 0 to 255.
     * <br><br>
     * If a single string argument is provided, RGB, RGBA and Hex CSS color strings
     * and all named color strings are supported. In this case, an alpha number
     * value as a second argument is not supported, the RGBA form should be used.
     * <br><br>
     * A p5 Color object can also be provided to set the fill color.
     *
     * @method fill
     * @param  {Number}        v1      red or hue value relative to
     *                                 the current color range
     * @param  {Number}        v2      green or saturation value
     *                                 relative to the current color range
     * @param  {Number}        v3      blue or brightness value
     *                                 relative to the current color range
     * @param  {Number}        [alpha]
     * @example
     * // Grayscale integer value
     * fill(51);
     * rect(20, 20, 60, 60);
     *
     * // R, G & B integer values
     * fill(255, 204, 0);
     * rect(20, 20, 60, 60);
     *
     * // H, S & B integer values
     * colorMode(HSB);
     * fill(255, 204, 100);
     * rect(20, 20, 60, 60);
     *
     * // Named SVG/CSS color string
     * fill('red');
     * rect(20, 20, 60, 60);
     *
     * // three-digit hexadecimal RGB notation
     * fill('#fae');
     * rect(20, 20, 60, 60);
     *
     * // six-digit hexadecimal RGB notation
     * fill('#222222');
     * rect(20, 20, 60, 60);
     *
     * // integer RGB notation
     * fill('rgb(0,255,0)');
     * rect(20, 20, 60, 60);
     *
     * // integer RGBA notation
     * fill('rgba(0,255,0, 0.25)');
     * rect(20, 20, 60, 60);
     *
     * // percentage RGB notation
     * fill('rgb(100%,0%,10%)');
     * rect(20, 20, 60, 60);
     *
     * // percentage RGBA notation
     * fill('rgba(100%,0%,100%,0.5)');
     * rect(20, 20, 60, 60);
     *
     * // p5 Color object
     * fill(color(0, 0, 255));
     * rect(20, 20, 60, 60);
     */

    /**
     * @method fill
     * @param  {String}        value   a color string
     */

    /**
     * @method fill
     * @param  {Number}        gray   a gray value
     * @param  {Number}        [alpha]
     */

    /**
     * @method fill
     * @param  {Number[]}      values  an array containing the red,green,blue &
     *                                 and alpha components of the color
     */

    /**
     * @method fill
     * @param  {Color}      color   the fill color
     */
    exports.fill = function () {
    	if (arguments[0] instanceof p5Color) {
    		_currentEnv._fill = arguments[0].toAllegro();
    	} else {
    		_currentEnv._fill = new p5Color(arguments).toAllegro();
    	}
    };

    /**
     * Disables filling geometry. If both noStroke() and noFill() are called,
     * nothing will be drawn to the screen.
     *
     * @method noFill
     * @example
     * rect(15, 10, 55, 55);
     * noFill();
     * rect(20, 20, 60, 60);
     *
     * function setup() {
     *   createCanvas(100, 100, WEBGL);
     * }
     *
     * function draw() {
     *   background(0);
     *   noFill();
     *   stroke(100, 100, 240);
     *   rotateX(frameCount * 0.01);
     *   rotateY(frameCount * 0.01);
     *   box(45, 45, 45);
     * }
     */
    exports.noFill = function () {
    	_currentEnv._fill = NO_COLOR;
    };

    /**
     * Disables drawing the stroke (outline). If both noStroke() and noFill()
     * are called, nothing will be drawn to the screen.
     *
     * @method noStroke
     * @example
     * noStroke();
     * rect(20, 20, 60, 60);
     *
     * function setup() {
     *   createCanvas(100, 100, WEBGL);
     * }
     *
     * function draw() {
     *   background(0);
     *   noStroke();
     *   fill(240, 150, 150);
     *   rotateX(frameCount * 0.01);
     *   rotateY(frameCount * 0.01);
     *   box(45, 45, 45);
     * }
     */
    exports.noStroke = function () {
    	_currentEnv._stroke = NO_COLOR;
    };

    /**
     * Sets the color used to draw lines and borders around shapes. This color
     * is either specified in terms of the RGB or HSB color depending on the
     * current colorMode() (the default color space is RGB, with each value in
     * the range from 0 to 255). The alpha range by default is also 0 to 255.
     * <br><br>
     * If a single string argument is provided, RGB, RGBA and Hex CSS color
     * strings and all named color strings are supported. In this case, an alpha
     * number value as a second argument is not supported, the RGBA form should be
     * used.
     * <br><br>
     * A p5 Color object can also be provided to set the stroke color.
     *
     *
     * @method stroke
     * @param  {Number}        v1      red or hue value relative to
     *                                 the current color range
     * @param  {Number}        v2      green or saturation value
     *                                 relative to the current color range
     * @param  {Number}        v3      blue or brightness value
     *                                 relative to the current color range
     * @param  {Number}        [alpha]
     *
     * @example
     * // Grayscale integer value
     * strokeWeight(4);
     * stroke(51);
     * rect(20, 20, 60, 60);
     *
     * // R, G & B integer values
     * stroke(255, 204, 0);
     * strokeWeight(4);
     * rect(20, 20, 60, 60);
     *
     * // H, S & B integer values
     * colorMode(HSB);
     * strokeWeight(4);
     * stroke(255, 204, 100);
     * rect(20, 20, 60, 60);
     *
     * // Named SVG/CSS color string
     * stroke('red');
     * strokeWeight(4);
     * rect(20, 20, 60, 60);
     *
     * // three-digit hexadecimal RGB notation
     * stroke('#fae');
     * strokeWeight(4);
     * rect(20, 20, 60, 60);
     *
     * // six-digit hexadecimal RGB notation
     * stroke('#222222');
     * strokeWeight(4);
     * rect(20, 20, 60, 60);
     *
     * // integer RGB notation
     * stroke('rgb(0,255,0)');
     * strokeWeight(4);
     * rect(20, 20, 60, 60);
     *
     * // integer RGBA notation
     * stroke('rgba(0,255,0,0.25)');
     * strokeWeight(4);
     * rect(20, 20, 60, 60);
     *
     * // percentage RGB notation
     * stroke('rgb(100%,0%,10%)');
     * strokeWeight(4);
     * rect(20, 20, 60, 60);
     *
     * // percentage RGBA notation
     * stroke('rgba(100%,0%,100%,0.5)');
     * strokeWeight(4);
     * rect(20, 20, 60, 60);
     *
     * // p5 Color object
     * stroke(color(0, 0, 255));
     * strokeWeight(4);
     * rect(20, 20, 60, 60);
     */

    /**
     * @method stroke
     * @param  {String}        value   a color string
     */

    /**
     * @method stroke
     * @param  {Number}        gray   a gray value
     * @param  {Number}        [alpha]
     */

    /**
     * @method stroke
     * @param  {Number[]}      values  an array containing the red,green,blue &
     *                                 and alpha components of the color
     */

    /**
     * @method stroke
     * @param  {Color}      color   the stroke color
     */
    exports.stroke = function () {
    	if (arguments[0] instanceof p5Color) {
    		_currentEnv._stroke = arguments[0].toAllegro()
    	} else {
    		_currentEnv._stroke = new p5Color(arguments).toAllegro();
    	}
    };