/** @license
 *
 *     Colour Palette Generator script.
 *     Copyright (c) 2014 Google Inc.
 *
 *     Licensed under the Apache License, Version 2.0 (the "License"); you may
 *     not use this file except in compliance with the License.  You may
 *     obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 *     implied.  See the License for the specific language governing
 *     permissions and limitations under the License.
 *
 * Furthermore, ColorBrewer colour schemes are covered by the following:
 *
 *     Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and
 *                        The Pennsylvania State University.
 *
 *     Licensed under the Apache License, Version 2.0 (the "License"); you may
 *     not use this file except in compliance with the License. You may obtain
 *     a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 *     implied. See the License for the specific language governing
 *     permissions and limitations under the License.
 *
 *     Redistribution and use in source and binary forms, with or without
 *     modification, are permitted provided that the following conditions are
 *     met:
 *
 *     1. Redistributions as source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *     2. The end-user documentation included with the redistribution, if any,
 *     must include the following acknowledgment: "This product includes color
 *     specifications and designs developed by Cynthia Brewer
 *     (http://colorbrewer.org/)." Alternately, this acknowledgment may appear
 *     in the software itself, if and wherever such third-party
 *     acknowledgments normally appear.
 *
 *     4. The name "ColorBrewer" must not be used to endorse or promote
 *     products derived from this software without prior written
 *     permission. For written permission, please contact Cynthia Brewer at
 *     cbrewer@psu.edu.
 *
 *     5. Products derived from this software may not be called "ColorBrewer",
 *     nor may "ColorBrewer" appear in their name, without prior written
 *     permission of Cynthia Brewer.
 *
 * Furthermore, Solarized colour schemes are covered by the following:
 *
 *     Copyright (c) 2011 Ethan Schoonover
 *
 *     Permission is hereby granted, free of charge, to any person obtaining
 *     a copy of this software and associated documentation files (the
 *     "Software"), to deal in the Software without restriction, including
 *     without limitation the rights to use, copy, modify, merge, publish,
 *     distribute, sublicense, and/or sell copies of the Software, and to
 *     permit persons to whom the Software is furnished to do so, subject to
 *     the following conditions:
 *
 *     The above copyright notice and this permission notice shall be included
 *     in all copies or substantial portions of the Software.
 *
 *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 *     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *     LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *     OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

'use strict';

var palette = (function() {

    var proto = Array.prototype;
    var slice = function(arr, opt_begin, opt_end) {
        return proto.slice.apply(arr, proto.slice.call(arguments, 1));
    };

    var extend = function(arr, arr2) {
        return proto.push.apply(arr, arr2);
    };

    var function_type = typeof function() {};

    var INF = 1000000000;  // As far as we're concerned, that's infinity. ;)


    /**
     * Generate a colour palette from given scheme.
     *
     * If scheme argument is not a function it is passed to palettes.listSchemes
     * function (along with the number argument).  This may result in an array
     * of more than one available scheme.  If that is the case, scheme at
     * opt_index position is taken.
     *
     * This allows using different palettes for different data without having to
     * name the schemes specifically, for example:
     *
     *     palette_for_foo = palette('sequential', 10, 0);
     *     palette_for_bar = palette('sequential', 10, 1);
     *     palette_for_baz = palette('sequential', 10, 2);
     *
     * @param {!palette.SchemeType|string|palette.Palette} scheme Scheme to
     *     generate palette for.  Either a function constructed with
     *     palette.Scheme object, or anything that palette.listSchemes accepts
     *     as name argument.
     * @param {number} number Number of colours to return.  If negative,
     *     absolute value is taken and colours will be returned in reverse
     *     order.
     * @param {number=} opt_index If scheme is a name of a group or an array and
     *     results in more than one scheme, index of the scheme to use.  The
     *     index wraps around.
     * @param {...*} varargs Additional arguments to pass to palette or colour
     *     generator (if the chosen scheme uses those).
     * @return {Array.<string>} Array of abs(number) 'RRGGBB' strings or null if
     *     no matching scheme was found.
     */
    var palette = function(scheme, number, opt_index, varargs) {
        number |= 0;
        if (number == 0) {
            return [];
        }

        if (typeof scheme !== function_type) {
            var arr = palette.listSchemes(
                /** @type {string|palette.Palette} */ (scheme), number);
            if (!arr.length) {
                return null;
            }
            scheme = arr[(opt_index || 0) % arr.length];
        }

        var args = slice(arguments, 2);
        args[0] = number;
        return scheme.apply(scheme, args);
    };


    /**
     * Returns a callable colour scheme object.
     *
     * Just after being created, the scheme has no colour palettes and no way of
     * generating any, thus generate method will return null.  To turn scheme
     * into a useful object, addPalette, addPalettes or setColorFunction methods
     * need to be used.
     *
     * To generate a colour palette with given number colours using function
     * returned by this method, just call it with desired number of colours.
     *
     * Since this function *returns* a callable object, it must *not* be used
     * with the new operator.
     *
     * @param {string} name Name of the scheme.
     * @param {string|!Array.<string>=} opt_groups A group name or list of
     *     groups the scheme should be categorised under.  Three typical groups
     *     to use are 'qualitative', 'sequential' and 'diverging', but any
     *     groups may be created.
     * @return {!palette.SchemeType} A colour palette generator function, which
     *     in addition has methods and properties like a regular object.  Think
     *     of it as a callable object.
     */
    palette.Scheme = function(name, opt_groups) {
        /**
         * A map from a number to a colour palettes with given number of colours.
         * @type {!Object.<number, palette.Palette>}
         */
        var palettes = {};

        /**
         * The biggest palette in palettes map.
         * @type {number}
         */
        var palettes_max = 0;

        /**
         * The smallest palette in palettes map.
         * @type {number}
         */
        var palettes_min = INF;

        var makeGenerator = function() {
            if (arguments.length <= 1) {
                return self.color_func.bind(self);
            } else {
                var args = slice(arguments);
                return function(x) {
                    args[0] = x;
                    return self.color_func.apply(self, args);
                };
            }
        };

        /**
         * Generate a colour palette from the scheme.
         *
         * If there was a palette added with addPalette (or addPalettes) with
         * enough colours, that palette will be used.  Otherwise, if colour
         * function has been set using setColorFunction method, that function will
         * be used to generate the palette.  Otherwise null is returned.
         *
         * @param {number} number Number of colours to return.  If negative,
         *     absolute value is taken and colours will be returned in reverse
         *     order.
         * @param {...*} varargs Additional arguments to pass to palette or colour
         *     generator (if the chosen scheme uses those).
         */
        var self = function(number, varargs) {
            number |= 0;
            if (!number) {
                return [];
            }

            var _number = number;
            number = Math.abs(number);

            if (number <= palettes_max) {
                for (var i = Math.max(number, palettes_min); !(i in palettes); ++i) {
                    /* nop */
                }
                var colors = palettes[i];
                if (i > number) {
                    var take_head =
                        'shrinking_takes_head' in colors ?
                            colors.shrinking_takes_head : self.shrinking_takes_head;
                    if (take_head) {
                        colors = colors.slice(0, number);
                        i = number;
                    } else {
                        return palette.generate(
                            function(x) { return colors[Math.round(x)]; },
                            _number, 0, colors.length - 1);
                    }
                }
                colors = colors.slice();
                if (_number < 0) {
                    colors.reverse();
                }
                return colors;

            } else if (self.color_func) {
                return palette.generate(makeGenerator.apply(self, arguments),
                    _number, 0, 1, self.color_func_cyclic);

            } else {
                return null;
            }
        };

        /**
         * The name of the palette.
         * @type {string}
         */
        self.scheme_name = name;

        /**
         * A list of groups the palette belongs to.
         * @type {!Array.<string>}
         */
        self.groups = opt_groups ?
            typeof opt_groups === 'string' ? [opt_groups] : opt_groups : [];

        /**
         * The biggest palette this scheme can generate.
         * @type {number}
         */
        self.max = 0;

        /**
         * The biggest palette this scheme can generate that is colour-blind
         * friendly.
         * @type {number}
         */
        self.cbf_max = INF;


        /**
         * Adds a colour palette to the colour scheme.
         *
         * @param {palette.Palette} palette An array of 'RRGGBB' strings
         *     representing the palette to add.
         * @param {boolean=} opt_is_cbf Whether the palette is colourblind friendly.
         */
        self.addPalette = function(palette, opt_is_cbf) {
            var len = palette.length;
            if (len) {
                palettes[len] = palette;
                palettes_min = Math.min(palettes_min, len);
                palettes_max = Math.max(palettes_max, len);
                self.max = Math.max(self.max, len);
                if (!opt_is_cbf && len != 1) {
                    self.cbf_max = Math.min(self.cbf_max, len - 1);
                }
            }
        };

        /**
         * Adds number of colour palettes to the colour scheme.
         *
         * @param {palette.PalettesList} palettes A map or an array of colour
         *     palettes to add.  If map, i.e.  object, is used, properties should
         *     use integer property names.
         * @param {number=} opt_max Size of the biggest palette in palettes set.
         *     If not set, palettes must have a length property which will be used.
         * @param {number=} opt_cbf_max Size of the biggest palette which is still
         *     colourblind friendly.  1 by default.
         */
        self.addPalettes = function(palettes, opt_max, opt_cbf_max) {
            opt_max = opt_max || palettes.length;
            for (var i = 0; i < opt_max; ++i) {
                if (i in palettes) {
                    self.addPalette(palettes[i], true);
                }
            }
            self.cbf_max = Math.min(self.cbf_max, opt_cbf_max || 1);
        };

        /**
         * Enable shrinking palettes taking head of the list of colours.
         *
         * When user requests n-colour palette but the smallest palette added with
         * addPalette (or addPalettes) is m-colour one (where n < m), n colours
         * across the palette will be returned.  For example:
         *     var ex = palette.Scheme('ex');
         *     ex.addPalette(['000000', 'bcbcbc', 'ffffff']);
         *     var pal = ex(2);
         *     // pal == ['000000', 'ffffff']
         *
         * This works for palettes where the distance between colours is
         * correlated to distance in the palette array, which is true in gradients
         * such as the one above.
         *
         * To turn this feature off shrinkByTakingHead can be set to true either
         * for all palettes in the scheme (if opt_idx is not given) or for palette
         * with given number of colours only.  In general, setting the option for
         * given palette overwrites whatever has been set for the scheme.  The
         * default, as described above, is false.
         *
         * Alternatively, the feature can be enabled by setting
         * shrinking_takes_head property for the palette Array or the scheme
         * object.
         *
         * For example, all of the below give equivalent results:
         *     var pal = ['ff0000', '00ff00', '0000ff'];
         *
         *     var ex = palette.Scheme('ex');
         *     ex.addPalette(pal);               // ex(2) == ['ff0000', '0000ff']
         *     ex.shrinkByTakingHead(true);      // ex(2) == ['ff0000', '00ff00']
         *
         *     ex = palette.Scheme('ex');
         *     ex.addPalette(pal);               // ex(2) == ['ff0000', '0000ff']
         *     ex.shrinkByTakingHead(true, 3);   // ex(2) == ['ff0000', '00ff00']
         *
         *     ex = palette.Scheme('ex');
         *     ex.addPalette(pal);
         *     ex.addPalette(pal);               // ex(2) == ['ff0000', '0000ff']
         *     pal.shrinking_takes_head = true;  // ex(2) == ['ff0000', '00ff00']
         *
         * @param {boolean} enabled Whether to enable or disable the “shrinking
         *     takes head” feature.  It is disabled by default.
         * @param {number=} opt_idx If given, the “shrinking takes head” option
         *     for palette with given number of colours is set.  If such palette
         *     does not exist, nothing happens.
         */
        self.shrinkByTakingHead = function(enabled, opt_idx) {
            if (opt_idx !== void(0)) {
                if (opt_idx in palettes) {
                    palettes[opt_idx].shrinking_takes_head = !!enabled;
                }
            } else {
                self.shrinking_takes_head = !!enabled;
            }
        };

        /**
         * Sets a colour generation function of the colour scheme.
         *
         * The function must accept a singe number argument whose value can be
         * from 0.0 to 1.0, and return a colour as an 'RRGGBB' string.  This
         * function will be used when generating palettes, i.e. if 11-colour
         * palette is requested, this function will be called with arguments 0.0,
         * 0.1, …, 1.0.
         *
         * If the palette generated by the function is colourblind friendly,
         * opt_is_cbf should be set to true.
         *
         * In some cases, it is not desirable to reach 1.0 when generating
         * a palette.  This happens for hue-rainbows where the 0–1 range corresponds
         * to a 0°–360° range in hues, and since hue at 0° is the same as at 360°,
         * it's desired to stop short the end of the range when generating
         * a palette.  To accomplish this, opt_cyclic should be set to true.
         *
         * @param {palette.ColorFunction} func A colour generator function.
         * @param {boolean=} opt_is_cbf Whether palette generate with the function
         *     is colour-blind friendly.
         * @param {boolean=} opt_cyclic Whether colour at 0.0 is the same as the
         *     one at 1.0.
         */
        self.setColorFunction = function(func, opt_is_cbf, opt_cyclic) {
            self.color_func = func;
            self.color_func_cyclic = !!opt_cyclic;
            self.max = INF;
            if (!opt_is_cbf && self.cbf_max === INF) {
                self.cbf_max = 1;
            }
        };

        self.color = function(x, varargs) {
            if (self.color_func) {
                return self.color_func.apply(this, arguments);
            } else {
                return null;
            }
        };

        return self;
    };


    /**
     * Creates a new palette.Scheme and initialises it by calling addPalettes
     * method with the rest of the arguments.
     *
     * @param {string} name Name of the scheme.
     * @param {string|!Array.<string>} groups A group name or list of group
     *     names the scheme belongs to.
     * @param {!Object.<number, palette.Palette>|!Array.<palette.Palette>}
     *     palettes A map or an array of colour palettes to add.  If map, i.e.
     *     object, is used, properties should use integer property names.
     * @param {number=} opt_max Size of the biggest palette in palettes set.
     *     If not set, palettes must have a length property which will be used.
     * @param {number=} opt_cbf_max Size of the biggest palette which is still
     *     colourblind friendly.  1 by default.
     * @return {!palette.SchemeType} A colour palette generator function, which
     *     in addition has methods and properties like a regular object.  Think
     *     of it as a callable object.
     */
    palette.Scheme.fromPalettes = function(name, groups,
                                           palettes, opt_max, opt_cbf_max) {
        var scheme = palette.Scheme(name, groups);
        scheme.addPalettes.apply(scheme, slice(arguments, 2));
        return scheme;
    };


    /**
     * Creates a new palette.Scheme and initialises it by calling
     * setColorFunction method with the rest of the arguments.
     *
     * @param {string} name Name of the scheme.
     * @param {string|!Array.<string>} groups A group name or list of group
     *     names the scheme belongs to.
     * @param {palette.ColorFunction} func A colour generator function.
     * @param {boolean=} opt_is_cbf Whether palette generate with the function
     *     is colour-blind friendly.
     * @param {boolean=} opt_cyclic Whether colour at 0.0 is the same as the
     *     one at 1.0.
     * @return {!palette.SchemeType} A colour palette generator function, which
     *     in addition has methods and properties like a regular object.  Think
     *     of it as a callable object.
     */
    palette.Scheme.withColorFunction = function(name, groups,
                                                func, opt_is_cbf, opt_cyclic) {
        var scheme = palette.Scheme(name, groups);
        scheme.setColorFunction.apply(scheme, slice(arguments, 2));
        return scheme;
    };


    /**
     * A map of registered schemes.  Maps a scheme or group name to a list of
     * scheme objects.  Property name is either 'n-<name>' for single scheme
     * names or 'g-<name>' for scheme group names.
     *
     * @type {!Object.<string, !Array.<!Object>>}
     */
    var registered_schemes = {};


    /**
     * Registers a new colour scheme.
     *
     * @param {!palette.SchemeType} scheme The scheme to add.
     */
    palette.register = function(scheme) {
        registered_schemes['n-' + scheme.scheme_name] = [scheme];
        scheme.groups.forEach(function(g) {
            (registered_schemes['g-' + g] =
                registered_schemes['g-' + g] || []).push(scheme);
        });
        (registered_schemes['g-all'] =
            registered_schemes['g-all'] || []).push(scheme);
    };


    /**
     * List all schemes that match given name and number of colours.
     *
     * name argument can be either a string or an array of strings.  In the
     * former case, the function acts as if the argument was an array with name
     * as a single argument (i.e. “palette.listSchemes('foo')” is exactly the same
     * as “palette.listSchemes(['foo'])”).
     *
     * Each name can be either name of a palette (e.g. 'tol-sq' for Paul Tol's
     * sequential palette), or a name of a group (e.g. 'sequential' for all
     * sequential palettes).  Name can therefore map to a single scheme or
     * several schemes.
     *
     * Furthermore, name can be suffixed with '-cbf' to indicate that only
     * schemes that are colourblind friendly should be returned.  For example,
     * 'rainbow' returns a HSV rainbow scheme, but because it is not colourblind
     * friendly, 'rainbow-cbf' returns no schemes.
     *
     * Some schemes may produce colourblind friendly palettes for some number of
     * colours.  For example ColorBrewer's Dark2 scheme is colourblind friendly
     * if no more than 3 colours are generated.  If opt_number is not specified,
     * 'qualitative-cbf' will include 'cb-Dark2' but if opt_number is given as,
     * say, 5 it won't.
     *
     * Name can also be 'all' which will return all registered schemes.
     * Naturally, 'all-cbf' will return all colourblind friendly schemes.
     *
     * Schemes are added to the library using palette.register.  Schemes are
     * created using palette.Scheme function.  By default, the following schemes
     * are available:
     *
     *     Name            Description
     *     --------------  -----------------------------------------------------
     *     tol             Paul Tol's qualitative scheme, cbf, max 12 colours.
     *     tol-dv          Paul Tol's diverging scheme, cbf.
     *     tol-seq         Paul Tol's sequential scheme, cbf.
     *     tol-rainbow     Paul Tol's qualitative scheme, cbf.
     *
     *     rainbow         A rainbow palette.
     *
     *     cb-YlGn         ColorBrewer sequential schemes.
     *     cb-YlGnBu
     *     cb-GnBu
     *     cb-BuGn
     *     cb-PuBuGn
     *     cb-PuBu
     *     cb-BuPu
     *     cb-RdPu
     *     cb-PuRd
     *     cb-OrRd
     *     cb-YlOrRd
     *     cb-YlOrBr
     *     cb-Purples
     *     cb-Blues
     *     cb-Greens
     *     cb-Oranges
     *     cb-Reds
     *     cb-Greys
     *
     *     cb-PuOr         ColorBrewer diverging schemes.
     *     cb-BrBG
     *     cb-PRGn
     *     cb-PiYG
     *     cb-RdBu
     *     cb-RdGy
     *     cb-RdYlBu
     *     cb-Spectral
     *     cb-RdYlGn
     *
     *     cb-Accent       ColorBrewer qualitative schemes.
     *     cb-Dark2
     *     cb-Paired
     *     cb-Pastel1
     *     cb-Pastel2
     *     cb-Set1
     *     cb-Set2
     *     cb-Set3
     *
     *     sol-base        Solarized base colours.
     *     sol-accent      Solarized accent colours.
     *
     * The following groups are also available by default:
     *
     *     Name            Description
     *     --------------  -----------------------------------------------------
     *     all             All registered schemes.
     *     sequential      All sequential schemes.
     *     diverging       All diverging schemes.
     *     qualitative     All qualitative schemes.
     *     cb-sequential   All ColorBrewer sequential schemes.
     *     cb-diverging    All ColorBrewer diverging schemes.
     *     cb-qualitative  All ColorBrewer qualitative schemes.
     *
     * You can read more about Paul Tol's palettes at http://www.sron.nl/~pault/.
     * You can read more about ColorBrewer at http://colorbrewer2.org.
     *
     * @param {string|!Array.<string>} name A name of a colour scheme, of
     *     a group of colour schemes, or an array of any of those.
     * @param {number=} opt_number When requesting only colourblind friendly
     *     schemes, number of colours the scheme must provide generating such
     *     that the palette is still colourblind friendly.  2 by default.
     * @return {!Array.<!palette.SchemeType>} An array of colour scheme objects
     *     matching the criteria.  Sorted by scheme name.
     */
    palette.listSchemes = function(name, opt_number) {
        if (!opt_number) {
            opt_number = 2;
        } else if (opt_number < 0) {
            opt_number = -opt_number;
        }

        var ret = [];
        (typeof name === 'string' ? [name] : name).forEach(function(n) {
            var cbf = n.substring(n.length - 4) === '-cbf';
            if (cbf) {
                n = n.substring(0, n.length - 4);
            }
            var schemes =
                registered_schemes['g-' + n] ||
                registered_schemes['n-' + n] ||
                [];
            for (var i = 0, scheme; (scheme = schemes[i]); ++i) {
                if ((cbf ? scheme.cbf : scheme.max) >= opt_number) {
                    ret.push(scheme);
                }
            }
        });

        ret.sort(function(a, b) {
            return a.scheme_name >= b.scheme_name ?
                a.scheme_name > b.scheme_name ? 1 : 0 : -1;
        });
        return ret;
    };


    /**
     * Generates a palette using given colour generating function.
     *
     * The color_func callback must accept a singe number argument whose value
     * can vary from 0.0 to 1.0 (or in general from opt_start to opt_end), and
     * return a colour as an 'RRGGBB' string.  This function will be used when
     * generating palettes, i.e. if 11-colour palette is requested, this
     * function will be called with arguments 0.0, 0.1, …, 1.0.
     *
     * In some cases, it is not desirable to reach 1.0 when generating
     * a palette.  This happens for hue-rainbows where the 0–1 range corresponds
     * to a 0°–360° range in hues, and since hue at 0° is the same as at 360°,
     * it's desired to stop short the end of the range when generating
     * a palette.  To accomplish this, opt_cyclic should be set to true.
     *
     * opt_start and opt_end may be used to change the range the colour
     * generation function is called with.  opt_end may be less than opt_start
     * which will case to traverse the range in reverse.  Another way to reverse
     * the palette is requesting negative number of colours.  The two methods do
     * not always lead to the same results (especially if opt_cyclic is set).
     *
     * @param {palette.ColorFunction} color_func A colours generating callback
     *     function.
     * @param {number} number Number of colours to generate in the palette.  If
     *     number is negative, colours in the palette will be reversed.  If only
     *     one colour is requested, colour at opt_start will be returned.
     * @param {number=} opt_start Optional starting point for the palette
     *     generation function.  Zero by default.
     * @param {number=} opt_end Optional ending point for the palette generation
     *     function.  One by default.
     * @param {boolean=} opt_cyclic If true, function will assume colour at
     *     point opt_start is the same as one at opt_end.
     * @return {palette.Palette} An array of 'RRGGBB' colours.
     */
    palette.generate = function(color_func, number, opt_start, opt_end,
                                opt_cyclic) {
        if (Math.abs(number) < 1) {
            return [];
        }

        opt_start = opt_start === void(0) ? 0 : opt_start;
        opt_end = opt_end === void(0) ? 1 : opt_end;

        if (Math.abs(number) < 2) {
            return [color_func(opt_start)];
        }

        var i = Math.abs(number);
        var x = opt_start;
        var ret = [];
        var step = (opt_end - opt_start) / (opt_cyclic ? i : (i - 1));

        for (; --i >= 0; x += step) {
            ret.push(color_func(x));
        }
        if (number < 0) {
            ret.reverse();
        }
        return ret;
    };


    /**
     * Clamps value to [0, 1] range.
     * @param {number} v Number to limit value of.
     * @return {number} If v is inside of [0, 1] range returns v, otherwise
     *     returns 0 or 1 depending which side of the range v is closer to.
     */
    var clamp = function(v) {
        return v > 0 ? (v < 1 ? v : 1) : 0;
    };

    /**
     * Converts r, g, b triple into RRGGBB hex representation.
     * @param {number} r Red value of the colour in the range [0, 1].
     * @param {number} g Green value of the colour in the range [0, 1].
     * @param {number} b Blue value of the colour in the range [0, 1].
     * @return {string} A lower-case RRGGBB representation of the colour.
     */
    palette.rgbColor = function(r, g, b) {
        return [r, g, b].map(function(v) {
            v = Number(Math.round(clamp(v) * 255)).toString(16);
            return v.length == 1 ? '0' + v : v;
        }).join('');
    };

    /**
     * Converts a linear r, g, b triple into RRGGBB hex representation.
     * @param {number} r Linear red value of the colour in the range [0, 1].
     * @param {number} g Linear green value of the colour in the range [0, 1].
     * @param {number} b Linear blue value of the colour in the range [0, 1].
     * @return {string} A lower-case RRGGBB representation of the colour.
     */
    palette.linearRgbColor = function(r, g, b) {
        // http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html
        return [r, g, b].map(function(v) {
            v = clamp(v);
            if (v <= 0.0031308) {
                v = 12.92 * v;
            } else {
                v = 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
            }
            v = Number(Math.round(v * 255)).toString(16);
            return v.length == 1 ? '0' + v : v;
        }).join('');
    };

    /**
     * Converts an HSV colours to RRGGBB hex representation.
     * @param {number} h Hue in the range [0, 1].
     * @param {number=} opt_s Saturation in the range [0, 1].  One by default.
     * @param {number=} opt_v Value in the range [0, 1].  One by default.
     * @return {string} An RRGGBB representation of the colour.
     */
    palette.hsvColor = function(h, opt_s, opt_v) {
        h *= 6;
        var s = opt_s === void(0) ? 1 : clamp(opt_s);
        var v = opt_v === void(0) ? 1 : clamp(opt_v);
        var x = v * (1 - s * Math.abs(h % 2 - 1));
        var m = v * (1 - s);
        switch (Math.floor(h) % 6) {
            case 0: return palette.rgbColor(v, x, m);
            case 1: return palette.rgbColor(x, v, m);
            case 2: return palette.rgbColor(m, v, x);
            case 3: return palette.rgbColor(m, x, v);
            case 4: return palette.rgbColor(x, m, v);
            default: return palette.rgbColor(v, m, x);
        }
    };

    palette.register(palette.Scheme.withColorFunction(
        'rainbow', 'qualitative', palette.hsvColor, false, true));

    return palette;
})();


/** @typedef {function(number): string} */
palette.ColorFunction;

/** @typedef {!Array.<string>} */
palette.Palette;

/** @typedef {!Object.<number, palette.Palette>|!Array.<palette.Palette>} */
palette.PalettesList;

/**
 * @typedef {
 *   function(number, ...[?]): Array.<string>|
 *   {
 *     scheme_name: string,
 *     groups: !Array.<string>,
 *     max: number,
 *     cbf_max: number,
 *     addPalette: function(!Array.<string>, boolean=),
 *     addPalettes: function(palette.PalettesList, number=, number=),
 *     shrinkByTakingHead: function(boolean, number=),
 *     setColorFunction: function(palette.ColorFunction, boolean=, boolean=),
 *     color: function(number, ...[?]): ?string}}
 */
palette.SchemeType;


/* Paul Tol's schemes start here. *******************************************/
/* See http://www.sron.nl/~pault/ */

(function() {
    var rgb = palette.rgbColor;

    /**
     * Calculates value of a polynomial at given point.
     * For example, poly(x, 1, 2, 3) calculates value of “1 + 2*x + 2*X^2”.
     * @param {number} x Value to calculate polynomial for.
     * @param {...number} varargs Coefficients of the polynomial specified in
     *     the order of rising powers of x including constant as the first
     *     variable argument.
     */
    var poly = function(x, varargs) {
        var i = arguments.length - 1, n = arguments[i];
        while (i > 1) {
            n = n * x + arguments[--i];
        }
        return n;
    };

    /**
     * Calculate approximate value of error function with maximum error of 0.0005.
     * See <https://en.wikipedia.org/wiki/Error_function>.
     * @param {number} x Argument of the error function.
     * @return {number} Value of error function for x.
     */
    var erf = function(x) {
        // https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions
        // This produces a maximum error of 0.0005 which is more then we need.  In
        // the worst case, that error is multiplied by four and then divided by
        // two before being multiplied by 255, so in the end, the error is
        // multiplied by 510 which produces 0.255 which is less than a single
        // colour step.
        var y = poly(Math.abs(x), 1, 0.278393, 0.230389, 0.000972, 0.078108);
        y *= y; // y^2
        y *= y; // y^4
        y = 1 - 1 / y;
        return x < 0 ? -y : y;
    };

    palette.register(palette.Scheme.fromPalettes('tol', 'qualitative', [
        ['4477aa'],
        ['4477aa', 'cc6677'],
        ['4477aa', 'ddcc77', 'cc6677'],
        ['4477aa', '117733', 'ddcc77', 'cc6677'],
        ['332288', '88ccee', '117733', 'ddcc77', 'cc6677'],
        ['332288', '88ccee', '117733', 'ddcc77', 'cc6677', 'aa4499'],
        ['332288', '88ccee', '44aa99', '117733', 'ddcc77', 'cc6677', 'aa4499'],
        ['332288', '88ccee', '44aa99', '117733', '999933', 'ddcc77', 'cc6677',
            'aa4499'],
        ['332288', '88ccee', '44aa99', '117733', '999933', 'ddcc77', 'cc6677',
            '882255', 'aa4499'],
        ['332288', '88ccee', '44aa99', '117733', '999933', 'ddcc77', '661100',
            'cc6677', '882255', 'aa4499'],
        ['332288', '6699cc', '88ccee', '44aa99', '117733', '999933', 'ddcc77',
            '661100', 'cc6677', '882255', 'aa4499'],
        ['332288', '6699cc', '88ccee', '44aa99', '117733', '999933', 'ddcc77',
            '661100', 'cc6677', 'aa4466', '882255', 'aa4499']
    ], 12, 12));

    /**
     * Calculates a colour along Paul Tol's sequential colours axis.
     * See <http://www.sron.nl/~pault/colourschemes.pdf> figure 7 and equation 1.
     * @param {number} x Position of the colour on the axis in the [0, 1] range.
     * @return {string} An RRGGBB representation of the colour.
     */
    palette.tolSequentialColor = function(x) {
        return rgb(1 - 0.392 * (1 + erf((x - 0.869) / 0.255)),
            1.021 - 0.456 * (1 + erf((x - 0.527) / 0.376)),
            1 - 0.493 * (1 + erf((x - 0.272) / 0.309)));
    };

    palette.register(palette.Scheme.withColorFunction(
        'tol-sq', 'sequential', palette.tolSequentialColor, true));

    /**
     * Calculates a colour along Paul Tol's diverging colours axis.
     * See <http://www.sron.nl/~pault/colourschemes.pdf> figure 8 and equation 2.
     * @param {number} x Position of the colour on the axis in the [0, 1] range.
     * @return {string} An RRGGBB representation of the colour.
     */
    palette.tolDivergingColor = function(x) {
        var g = poly(x, 0.572, 1.524, -1.811) / poly(x, 1, -0.291, 0.1574);
        return rgb(poly(x, 0.235, -2.13, 26.92, -65.5, 63.5, -22.36),
            g * g,
            1 / poly(x, 1.579, -4.03, 12.92, -31.4, 48.6, -23.36));
    };

    palette.register(palette.Scheme.withColorFunction(
        'tol-dv', 'diverging', palette.tolDivergingColor, true));

    /**
     * Calculates a colour along Paul Tol's rainbow colours axis.
     * See <http://www.sron.nl/~pault/colourschemes.pdf> figure 13 and equation 3.
     * @param {number} x Position of the colour on the axis in the [0, 1] range.
     * @return {string} An RRGGBB representation of the colour.
     */
    palette.tolRainbowColor = function(x) {
        return rgb(poly(x, 0.472, -0.567, 4.05) / poly(x, 1, 8.72, -19.17, 14.1),
            poly(x, 0.108932, -1.22635, 27.284, -98.577, 163.3, -131.395,
                40.634),
            1 / poly(x, 1.97, 3.54, -68.5, 243, -297, 125));
    };

    palette.register(palette.Scheme.withColorFunction(
        'tol-rainbow', 'qualitative', palette.tolRainbowColor, true));
})();


/* Solarized colour schemes start here. *************************************/
/* See http://ethanschoonover.com/solarized */

(function() {
    /*
     * Those are not really designed to be used in graphs, but we're keeping
     * them here in case someone cares.
     */
    palette.register(palette.Scheme.fromPalettes('sol-base', 'sequential', [
        ['002b36', '073642', '586e75', '657b83', '839496', '93a1a1', 'eee8d5',
            'fdf6e3']
    ], 1, 8));
    palette.register(palette.Scheme.fromPalettes('sol-accent', 'qualitative', [
        ['b58900', 'cb4b16', 'dc322f', 'd33682', '6c71c4', '268bd2', '2aa198',
            '859900']
    ]));
})();


/* ColorBrewer colour schemes start here. ***********************************/
/* See http://colorbrewer2.org/ */

(function() {
    var schemes = {
        YlGn: {
            type: 'sequential',
            cbf: 42,
            3: ['f7fcb9', 'addd8e', '31a354'],
            4: ['ffffcc', 'c2e699', '78c679', '238443'],
            5: ['ffffcc', 'c2e699', '78c679', '31a354', '006837'],
            6: ['ffffcc', 'd9f0a3', 'addd8e', '78c679', '31a354', '006837'],
            7: ['ffffcc', 'd9f0a3', 'addd8e', '78c679', '41ab5d', '238443',
                '005a32'],
            8: ['ffffe5', 'f7fcb9', 'd9f0a3', 'addd8e', '78c679', '41ab5d',
                '238443', '005a32'],
            9: ['ffffe5', 'f7fcb9', 'd9f0a3', 'addd8e', '78c679', '41ab5d',
                '238443', '006837', '004529']
        },
        YlGnBu: {
            type: 'sequential',
            cbf: 42,
            3: ['edf8b1', '7fcdbb', '2c7fb8'],
            4: ['ffffcc', 'a1dab4', '41b6c4', '225ea8'],
            5: ['ffffcc', 'a1dab4', '41b6c4', '2c7fb8', '253494'],
            6: ['ffffcc', 'c7e9b4', '7fcdbb', '41b6c4', '2c7fb8', '253494'],
            7: ['ffffcc', 'c7e9b4', '7fcdbb', '41b6c4', '1d91c0', '225ea8',
                '0c2c84'],
            8: ['ffffd9', 'edf8b1', 'c7e9b4', '7fcdbb', '41b6c4', '1d91c0',
                '225ea8', '0c2c84'],
            9: ['ffffd9', 'edf8b1', 'c7e9b4', '7fcdbb', '41b6c4', '1d91c0',
                '225ea8', '253494', '081d58']
        },
        GnBu: {
            type: 'sequential',
            cbf: 42,
            3: ['e0f3db', 'a8ddb5', '43a2ca'],
            4: ['f0f9e8', 'bae4bc', '7bccc4', '2b8cbe'],
            5: ['f0f9e8', 'bae4bc', '7bccc4', '43a2ca', '0868ac'],
            6: ['f0f9e8', 'ccebc5', 'a8ddb5', '7bccc4', '43a2ca', '0868ac'],
            7: ['f0f9e8', 'ccebc5', 'a8ddb5', '7bccc4', '4eb3d3', '2b8cbe',
                '08589e'],
            8: ['f7fcf0', 'e0f3db', 'ccebc5', 'a8ddb5', '7bccc4', '4eb3d3',
                '2b8cbe', '08589e'],
            9: ['f7fcf0', 'e0f3db', 'ccebc5', 'a8ddb5', '7bccc4', '4eb3d3',
                '2b8cbe', '0868ac', '084081']
        },
        BuGn: {
            type: 'sequential',
            cbf: 42,
            3: ['e5f5f9', '99d8c9', '2ca25f'],
            4: ['edf8fb', 'b2e2e2', '66c2a4', '238b45'],
            5: ['edf8fb', 'b2e2e2', '66c2a4', '2ca25f', '006d2c'],
            6: ['edf8fb', 'ccece6', '99d8c9', '66c2a4', '2ca25f', '006d2c'],
            7: ['edf8fb', 'ccece6', '99d8c9', '66c2a4', '41ae76', '238b45',
                '005824'],
            8: ['f7fcfd', 'e5f5f9', 'ccece6', '99d8c9', '66c2a4', '41ae76',
                '238b45', '005824'],
            9: ['f7fcfd', 'e5f5f9', 'ccece6', '99d8c9', '66c2a4', '41ae76',
                '238b45', '006d2c', '00441b']
        },
        PuBuGn: {
            type: 'sequential',
            cbf: 42,
            3: ['ece2f0', 'a6bddb', '1c9099'],
            4: ['f6eff7', 'bdc9e1', '67a9cf', '02818a'],
            5: ['f6eff7', 'bdc9e1', '67a9cf', '1c9099', '016c59'],
            6: ['f6eff7', 'd0d1e6', 'a6bddb', '67a9cf', '1c9099', '016c59'],
            7: ['f6eff7', 'd0d1e6', 'a6bddb', '67a9cf', '3690c0', '02818a',
                '016450'],
            8: ['fff7fb', 'ece2f0', 'd0d1e6', 'a6bddb', '67a9cf', '3690c0',
                '02818a', '016450'],
            9: ['fff7fb', 'ece2f0', 'd0d1e6', 'a6bddb', '67a9cf', '3690c0',
                '02818a', '016c59', '014636']
        },
        PuBu: {
            type: 'sequential',
            cbf: 42,
            3: ['ece7f2', 'a6bddb', '2b8cbe'],
            4: ['f1eef6', 'bdc9e1', '74a9cf', '0570b0'],
            5: ['f1eef6', 'bdc9e1', '74a9cf', '2b8cbe', '045a8d'],
            6: ['f1eef6', 'd0d1e6', 'a6bddb', '74a9cf', '2b8cbe', '045a8d'],
            7: ['f1eef6', 'd0d1e6', 'a6bddb', '74a9cf', '3690c0', '0570b0',
                '034e7b'],
            8: ['fff7fb', 'ece7f2', 'd0d1e6', 'a6bddb', '74a9cf', '3690c0',
                '0570b0', '034e7b'],
            9: ['fff7fb', 'ece7f2', 'd0d1e6', 'a6bddb', '74a9cf', '3690c0',
                '0570b0', '045a8d', '023858']
        },
        BuPu: {
            type: 'sequential',
            cbf: 42,
            3: ['e0ecf4', '9ebcda', '8856a7'],
            4: ['edf8fb', 'b3cde3', '8c96c6', '88419d'],
            5: ['edf8fb', 'b3cde3', '8c96c6', '8856a7', '810f7c'],
            6: ['edf8fb', 'bfd3e6', '9ebcda', '8c96c6', '8856a7', '810f7c'],
            7: ['edf8fb', 'bfd3e6', '9ebcda', '8c96c6', '8c6bb1', '88419d',
                '6e016b'],
            8: ['f7fcfd', 'e0ecf4', 'bfd3e6', '9ebcda', '8c96c6', '8c6bb1',
                '88419d', '6e016b'],
            9: ['f7fcfd', 'e0ecf4', 'bfd3e6', '9ebcda', '8c96c6', '8c6bb1',
                '88419d', '810f7c', '4d004b']
        },
        RdPu: {
            type: 'sequential',
            cbf: 42,
            3: ['fde0dd', 'fa9fb5', 'c51b8a'],
            4: ['feebe2', 'fbb4b9', 'f768a1', 'ae017e'],
            5: ['feebe2', 'fbb4b9', 'f768a1', 'c51b8a', '7a0177'],
            6: ['feebe2', 'fcc5c0', 'fa9fb5', 'f768a1', 'c51b8a', '7a0177'],
            7: ['feebe2', 'fcc5c0', 'fa9fb5', 'f768a1', 'dd3497', 'ae017e',
                '7a0177'],
            8: ['fff7f3', 'fde0dd', 'fcc5c0', 'fa9fb5', 'f768a1', 'dd3497',
                'ae017e', '7a0177'],
            9: ['fff7f3', 'fde0dd', 'fcc5c0', 'fa9fb5', 'f768a1', 'dd3497',
                'ae017e', '7a0177', '49006a']
        },
        PuRd: {
            type: 'sequential',
            cbf: 42,
            3: ['e7e1ef', 'c994c7', 'dd1c77'],
            4: ['f1eef6', 'd7b5d8', 'df65b0', 'ce1256'],
            5: ['f1eef6', 'd7b5d8', 'df65b0', 'dd1c77', '980043'],
            6: ['f1eef6', 'd4b9da', 'c994c7', 'df65b0', 'dd1c77', '980043'],
            7: ['f1eef6', 'd4b9da', 'c994c7', 'df65b0', 'e7298a', 'ce1256',
                '91003f'],
            8: ['f7f4f9', 'e7e1ef', 'd4b9da', 'c994c7', 'df65b0', 'e7298a',
                'ce1256', '91003f'],
            9: ['f7f4f9', 'e7e1ef', 'd4b9da', 'c994c7', 'df65b0', 'e7298a',
                'ce1256', '980043', '67001f']
        },
        OrRd: {
            type: 'sequential',
            cbf: 42,
            3: ['fee8c8', 'fdbb84', 'e34a33'],
            4: ['fef0d9', 'fdcc8a', 'fc8d59', 'd7301f'],
            5: ['fef0d9', 'fdcc8a', 'fc8d59', 'e34a33', 'b30000'],
            6: ['fef0d9', 'fdd49e', 'fdbb84', 'fc8d59', 'e34a33', 'b30000'],
            7: ['fef0d9', 'fdd49e', 'fdbb84', 'fc8d59', 'ef6548', 'd7301f',
                '990000'],
            8: ['fff7ec', 'fee8c8', 'fdd49e', 'fdbb84', 'fc8d59', 'ef6548',
                'd7301f', '990000'],
            9: ['fff7ec', 'fee8c8', 'fdd49e', 'fdbb84', 'fc8d59', 'ef6548',
                'd7301f', 'b30000', '7f0000']
        },
        YlOrRd: {
            type: 'sequential',
            cbf: 42,
            3: ['ffeda0', 'feb24c', 'f03b20'],
            4: ['ffffb2', 'fecc5c', 'fd8d3c', 'e31a1c'],
            5: ['ffffb2', 'fecc5c', 'fd8d3c', 'f03b20', 'bd0026'],
            6: ['ffffb2', 'fed976', 'feb24c', 'fd8d3c', 'f03b20', 'bd0026'],
            7: ['ffffb2', 'fed976', 'feb24c', 'fd8d3c', 'fc4e2a', 'e31a1c',
                'b10026'],
            8: ['ffffcc', 'ffeda0', 'fed976', 'feb24c', 'fd8d3c', 'fc4e2a',
                'e31a1c', 'b10026'],
            9: ['ffffcc', 'ffeda0', 'fed976', 'feb24c', 'fd8d3c', 'fc4e2a',
                'e31a1c', 'bd0026', '800026']
        },
        YlOrBr: {
            type: 'sequential',
            cbf: 42,
            3: ['fff7bc', 'fec44f', 'd95f0e'],
            4: ['ffffd4', 'fed98e', 'fe9929', 'cc4c02'],
            5: ['ffffd4', 'fed98e', 'fe9929', 'd95f0e', '993404'],
            6: ['ffffd4', 'fee391', 'fec44f', 'fe9929', 'd95f0e', '993404'],
            7: ['ffffd4', 'fee391', 'fec44f', 'fe9929', 'ec7014', 'cc4c02',
                '8c2d04'],
            8: ['ffffe5', 'fff7bc', 'fee391', 'fec44f', 'fe9929', 'ec7014',
                'cc4c02', '8c2d04'],
            9: ['ffffe5', 'fff7bc', 'fee391', 'fec44f', 'fe9929', 'ec7014',
                'cc4c02', '993404', '662506']
        },
        Purples: {
            type: 'sequential',
            cbf: 42,
            3: ['efedf5', 'bcbddc', '756bb1'],
            4: ['f2f0f7', 'cbc9e2', '9e9ac8', '6a51a3'],
            5: ['f2f0f7', 'cbc9e2', '9e9ac8', '756bb1', '54278f'],
            6: ['f2f0f7', 'dadaeb', 'bcbddc', '9e9ac8', '756bb1', '54278f'],
            7: ['f2f0f7', 'dadaeb', 'bcbddc', '9e9ac8', '807dba', '6a51a3',
                '4a1486'],
            8: ['fcfbfd', 'efedf5', 'dadaeb', 'bcbddc', '9e9ac8', '807dba',
                '6a51a3', '4a1486'],
            9: ['fcfbfd', 'efedf5', 'dadaeb', 'bcbddc', '9e9ac8', '807dba',
                '6a51a3', '54278f', '3f007d']
        },
        Blues: {
            type: 'sequential',
            cbf: 42,
            3: ['deebf7', '9ecae1', '3182bd'],
            4: ['eff3ff', 'bdd7e7', '6baed6', '2171b5'],
            5: ['eff3ff', 'bdd7e7', '6baed6', '3182bd', '08519c'],
            6: ['eff3ff', 'c6dbef', '9ecae1', '6baed6', '3182bd', '08519c'],
            7: ['eff3ff', 'c6dbef', '9ecae1', '6baed6', '4292c6', '2171b5',
                '084594'],
            8: ['f7fbff', 'deebf7', 'c6dbef', '9ecae1', '6baed6', '4292c6',
                '2171b5', '084594'],
            9: ['f7fbff', 'deebf7', 'c6dbef', '9ecae1', '6baed6', '4292c6',
                '2171b5', '08519c', '08306b']
        },
        Greens: {
            type: 'sequential',
            cbf: 42,
            3: ['e5f5e0', 'a1d99b', '31a354'],
            4: ['edf8e9', 'bae4b3', '74c476', '238b45'],
            5: ['edf8e9', 'bae4b3', '74c476', '31a354', '006d2c'],
            6: ['edf8e9', 'c7e9c0', 'a1d99b', '74c476', '31a354', '006d2c'],
            7: ['edf8e9', 'c7e9c0', 'a1d99b', '74c476', '41ab5d', '238b45',
                '005a32'],
            8: ['f7fcf5', 'e5f5e0', 'c7e9c0', 'a1d99b', '74c476', '41ab5d',
                '238b45', '005a32'],
            9: ['f7fcf5', 'e5f5e0', 'c7e9c0', 'a1d99b', '74c476', '41ab5d',
                '238b45', '006d2c', '00441b']
        },
        Oranges: {
            type: 'sequential',
            cbf: 42,
            3: ['fee6ce', 'fdae6b', 'e6550d'],
            4: ['feedde', 'fdbe85', 'fd8d3c', 'd94701'],
            5: ['feedde', 'fdbe85', 'fd8d3c', 'e6550d', 'a63603'],
            6: ['feedde', 'fdd0a2', 'fdae6b', 'fd8d3c', 'e6550d', 'a63603'],
            7: ['feedde', 'fdd0a2', 'fdae6b', 'fd8d3c', 'f16913', 'd94801',
                '8c2d04'],
            8: ['fff5eb', 'fee6ce', 'fdd0a2', 'fdae6b', 'fd8d3c', 'f16913',
                'd94801', '8c2d04'],
            9: ['fff5eb', 'fee6ce', 'fdd0a2', 'fdae6b', 'fd8d3c', 'f16913',
                'd94801', 'a63603', '7f2704']
        },
        Reds: {
            type: 'sequential',
            cbf: 42,
            3: ['fee0d2', 'fc9272', 'de2d26'],
            4: ['fee5d9', 'fcae91', 'fb6a4a', 'cb181d'],
            5: ['fee5d9', 'fcae91', 'fb6a4a', 'de2d26', 'a50f15'],
            6: ['fee5d9', 'fcbba1', 'fc9272', 'fb6a4a', 'de2d26', 'a50f15'],
            7: ['fee5d9', 'fcbba1', 'fc9272', 'fb6a4a', 'ef3b2c', 'cb181d',
                '99000d'],
            8: ['fff5f0', 'fee0d2', 'fcbba1', 'fc9272', 'fb6a4a', 'ef3b2c',
                'cb181d', '99000d'],
            9: ['fff5f0', 'fee0d2', 'fcbba1', 'fc9272', 'fb6a4a', 'ef3b2c',
                'cb181d', 'a50f15', '67000d']
        },
        Greys: {
            type: 'sequential',
            cbf: 42,
            3: ['f0f0f0', 'bdbdbd', '636363'],
            4: ['f7f7f7', 'cccccc', '969696', '525252'],
            5: ['f7f7f7', 'cccccc', '969696', '636363', '252525'],
            6: ['f7f7f7', 'd9d9d9', 'bdbdbd', '969696', '636363', '252525'],
            7: ['f7f7f7', 'd9d9d9', 'bdbdbd', '969696', '737373', '525252',
                '252525'],
            8: ['ffffff', 'f0f0f0', 'd9d9d9', 'bdbdbd', '969696', '737373',
                '525252', '252525'],
            9: ['ffffff', 'f0f0f0', 'd9d9d9', 'bdbdbd', '969696', '737373',
                '525252', '252525', '000000']
        },
        PuOr: {
            type: 'diverging',
            cbf: 42,
            3: ['f1a340', 'f7f7f7', '998ec3'],
            4: ['e66101', 'fdb863', 'b2abd2', '5e3c99'],
            5: ['e66101', 'fdb863', 'f7f7f7', 'b2abd2', '5e3c99'],
            6: ['b35806', 'f1a340', 'fee0b6', 'd8daeb', '998ec3', '542788'],
            7: ['b35806', 'f1a340', 'fee0b6', 'f7f7f7', 'd8daeb', '998ec3',
                '542788'],
            8: ['b35806', 'e08214', 'fdb863', 'fee0b6', 'd8daeb', 'b2abd2',
                '8073ac', '542788'],
            9: ['b35806', 'e08214', 'fdb863', 'fee0b6', 'f7f7f7', 'd8daeb',
                'b2abd2', '8073ac', '542788'],
            10: ['7f3b08', 'b35806', 'e08214', 'fdb863', 'fee0b6', 'd8daeb',
                'b2abd2', '8073ac', '542788', '2d004b'],
            11: ['7f3b08', 'b35806', 'e08214', 'fdb863', 'fee0b6', 'f7f7f7',
                'd8daeb', 'b2abd2', '8073ac', '542788', '2d004b']
        },
        BrBG: {
            type: 'diverging',
            cbf: 42,
            3: ['d8b365', 'f5f5f5', '5ab4ac'],
            4: ['a6611a', 'dfc27d', '80cdc1', '018571'],
            5: ['a6611a', 'dfc27d', 'f5f5f5', '80cdc1', '018571'],
            6: ['8c510a', 'd8b365', 'f6e8c3', 'c7eae5', '5ab4ac', '01665e'],
            7: ['8c510a', 'd8b365', 'f6e8c3', 'f5f5f5', 'c7eae5', '5ab4ac',
                '01665e'],
            8: ['8c510a', 'bf812d', 'dfc27d', 'f6e8c3', 'c7eae5', '80cdc1',
                '35978f', '01665e'],
            9: ['8c510a', 'bf812d', 'dfc27d', 'f6e8c3', 'f5f5f5', 'c7eae5',
                '80cdc1', '35978f', '01665e'],
            10: ['543005', '8c510a', 'bf812d', 'dfc27d', 'f6e8c3', 'c7eae5',
                '80cdc1', '35978f', '01665e', '003c30'],
            11: ['543005', '8c510a', 'bf812d', 'dfc27d', 'f6e8c3', 'f5f5f5',
                'c7eae5', '80cdc1', '35978f', '01665e', '003c30']
        },
        PRGn: {
            type: 'diverging',
            cbf: 42,
            3: ['af8dc3', 'f7f7f7', '7fbf7b'],
            4: ['7b3294', 'c2a5cf', 'a6dba0', '008837'],
            5: ['7b3294', 'c2a5cf', 'f7f7f7', 'a6dba0', '008837'],
            6: ['762a83', 'af8dc3', 'e7d4e8', 'd9f0d3', '7fbf7b', '1b7837'],
            7: ['762a83', 'af8dc3', 'e7d4e8', 'f7f7f7', 'd9f0d3', '7fbf7b',
                '1b7837'],
            8: ['762a83', '9970ab', 'c2a5cf', 'e7d4e8', 'd9f0d3', 'a6dba0',
                '5aae61', '1b7837'],
            9: ['762a83', '9970ab', 'c2a5cf', 'e7d4e8', 'f7f7f7', 'd9f0d3',
                'a6dba0', '5aae61', '1b7837'],
            10: ['40004b', '762a83', '9970ab', 'c2a5cf', 'e7d4e8', 'd9f0d3',
                'a6dba0', '5aae61', '1b7837', '00441b'],
            11: ['40004b', '762a83', '9970ab', 'c2a5cf', 'e7d4e8', 'f7f7f7',
                'd9f0d3', 'a6dba0', '5aae61', '1b7837', '00441b']
        },
        PiYG: {
            type: 'diverging',
            cbf: 42,
            3: ['e9a3c9', 'f7f7f7', 'a1d76a'],
            4: ['d01c8b', 'f1b6da', 'b8e186', '4dac26'],
            5: ['d01c8b', 'f1b6da', 'f7f7f7', 'b8e186', '4dac26'],
            6: ['c51b7d', 'e9a3c9', 'fde0ef', 'e6f5d0', 'a1d76a', '4d9221'],
            7: ['c51b7d', 'e9a3c9', 'fde0ef', 'f7f7f7', 'e6f5d0', 'a1d76a',
                '4d9221'],
            8: ['c51b7d', 'de77ae', 'f1b6da', 'fde0ef', 'e6f5d0', 'b8e186',
                '7fbc41', '4d9221'],
            9: ['c51b7d', 'de77ae', 'f1b6da', 'fde0ef', 'f7f7f7', 'e6f5d0',
                'b8e186', '7fbc41', '4d9221'],
            10: ['8e0152', 'c51b7d', 'de77ae', 'f1b6da', 'fde0ef', 'e6f5d0',
                'b8e186', '7fbc41', '4d9221', '276419'],
            11: ['8e0152', 'c51b7d', 'de77ae', 'f1b6da', 'fde0ef', 'f7f7f7',
                'e6f5d0', 'b8e186', '7fbc41', '4d9221', '276419']
        },
        RdBu: {
            type: 'diverging',
            cbf: 42,
            3: ['ef8a62', 'f7f7f7', '67a9cf'],
            4: ['ca0020', 'f4a582', '92c5de', '0571b0'],
            5: ['ca0020', 'f4a582', 'f7f7f7', '92c5de', '0571b0'],
            6: ['b2182b', 'ef8a62', 'fddbc7', 'd1e5f0', '67a9cf', '2166ac'],
            7: ['b2182b', 'ef8a62', 'fddbc7', 'f7f7f7', 'd1e5f0', '67a9cf',
                '2166ac'],
            8: ['b2182b', 'd6604d', 'f4a582', 'fddbc7', 'd1e5f0', '92c5de',
                '4393c3', '2166ac'],
            9: ['b2182b', 'd6604d', 'f4a582', 'fddbc7', 'f7f7f7', 'd1e5f0',
                '92c5de', '4393c3', '2166ac'],
            10: ['67001f', 'b2182b', 'd6604d', 'f4a582', 'fddbc7', 'd1e5f0',
                '92c5de', '4393c3', '2166ac', '053061'],
            11: ['67001f', 'b2182b', 'd6604d', 'f4a582', 'fddbc7', 'f7f7f7',
                'd1e5f0', '92c5de', '4393c3', '2166ac', '053061']
        },
        RdGy: {
            type: 'diverging',
            cbf: 42,
            3: ['ef8a62', 'ffffff', '999999'],
            4: ['ca0020', 'f4a582', 'bababa', '404040'],
            5: ['ca0020', 'f4a582', 'ffffff', 'bababa', '404040'],
            6: ['b2182b', 'ef8a62', 'fddbc7', 'e0e0e0', '999999', '4d4d4d'],
            7: ['b2182b', 'ef8a62', 'fddbc7', 'ffffff', 'e0e0e0', '999999',
                '4d4d4d'],
            8: ['b2182b', 'd6604d', 'f4a582', 'fddbc7', 'e0e0e0', 'bababa',
                '878787', '4d4d4d'],
            9: ['b2182b', 'd6604d', 'f4a582', 'fddbc7', 'ffffff', 'e0e0e0',
                'bababa', '878787', '4d4d4d'],
            10: ['67001f', 'b2182b', 'd6604d', 'f4a582', 'fddbc7', 'e0e0e0',
                'bababa', '878787', '4d4d4d', '1a1a1a'],
            11: ['67001f', 'b2182b', 'd6604d', 'f4a582', 'fddbc7', 'ffffff',
                'e0e0e0', 'bababa', '878787', '4d4d4d', '1a1a1a']
        },
        RdYlBu: {
            type: 'diverging',
            cbf: 42,
            3: ['fc8d59', 'ffffbf', '91bfdb'],
            4: ['d7191c', 'fdae61', 'abd9e9', '2c7bb6'],
            5: ['d7191c', 'fdae61', 'ffffbf', 'abd9e9', '2c7bb6'],
            6: ['d73027', 'fc8d59', 'fee090', 'e0f3f8', '91bfdb', '4575b4'],
            7: ['d73027', 'fc8d59', 'fee090', 'ffffbf', 'e0f3f8', '91bfdb',
                '4575b4'],
            8: ['d73027', 'f46d43', 'fdae61', 'fee090', 'e0f3f8', 'abd9e9',
                '74add1', '4575b4'],
            9: ['d73027', 'f46d43', 'fdae61', 'fee090', 'ffffbf', 'e0f3f8',
                'abd9e9', '74add1', '4575b4'],
            10: ['a50026', 'd73027', 'f46d43', 'fdae61', 'fee090', 'e0f3f8',
                'abd9e9', '74add1', '4575b4', '313695'],
            11: ['a50026', 'd73027', 'f46d43', 'fdae61', 'fee090', 'ffffbf',
                'e0f3f8', 'abd9e9', '74add1', '4575b4', '313695']
        },
        Spectral: {
            type: 'diverging',
            cbf: 0,
            3: ['fc8d59', 'ffffbf', '99d594'],
            4: ['d7191c', 'fdae61', 'abdda4', '2b83ba'],
            5: ['d7191c', 'fdae61', 'ffffbf', 'abdda4', '2b83ba'],
            6: ['d53e4f', 'fc8d59', 'fee08b', 'e6f598', '99d594', '3288bd'],
            7: ['d53e4f', 'fc8d59', 'fee08b', 'ffffbf', 'e6f598', '99d594',
                '3288bd'],
            8: ['d53e4f', 'f46d43', 'fdae61', 'fee08b', 'e6f598', 'abdda4',
                '66c2a5', '3288bd'],
            9: ['d53e4f', 'f46d43', 'fdae61', 'fee08b', 'ffffbf', 'e6f598',
                'abdda4', '66c2a5', '3288bd'],
            10: ['9e0142', 'd53e4f', 'f46d43', 'fdae61', 'fee08b', 'e6f598',
                'abdda4', '66c2a5', '3288bd', '5e4fa2'],
            11: ['9e0142', 'd53e4f', 'f46d43', 'fdae61', 'fee08b', 'ffffbf',
                'e6f598', 'abdda4', '66c2a5', '3288bd', '5e4fa2']
        },
        RdYlGn: {
            type: 'diverging',
            cbf: 0,
            3: ['fc8d59', 'ffffbf', '91cf60'],
            4: ['d7191c', 'fdae61', 'a6d96a', '1a9641'],
            5: ['d7191c', 'fdae61', 'ffffbf', 'a6d96a', '1a9641'],
            6: ['d73027', 'fc8d59', 'fee08b', 'd9ef8b', '91cf60', '1a9850'],
            7: ['d73027', 'fc8d59', 'fee08b', 'ffffbf', 'd9ef8b', '91cf60',
                '1a9850'],
            8: ['d73027', 'f46d43', 'fdae61', 'fee08b', 'd9ef8b', 'a6d96a',
                '66bd63', '1a9850'],
            9: ['d73027', 'f46d43', 'fdae61', 'fee08b', 'ffffbf', 'd9ef8b',
                'a6d96a', '66bd63', '1a9850'],
            10: ['a50026', 'd73027', 'f46d43', 'fdae61', 'fee08b', 'd9ef8b',
                'a6d96a', '66bd63', '1a9850', '006837'],
            11: ['a50026', 'd73027', 'f46d43', 'fdae61', 'fee08b', 'ffffbf',
                'd9ef8b', 'a6d96a', '66bd63', '1a9850', '006837']
        },
        Accent: {
            type: 'qualitative',
            cbf: 0,
            3: ['7fc97f', 'beaed4', 'fdc086'],
            4: ['7fc97f', 'beaed4', 'fdc086', 'ffff99'],
            5: ['7fc97f', 'beaed4', 'fdc086', 'ffff99', '386cb0'],
            6: ['7fc97f', 'beaed4', 'fdc086', 'ffff99', '386cb0', 'f0027f'],
            7: ['7fc97f', 'beaed4', 'fdc086', 'ffff99', '386cb0', 'f0027f',
                'bf5b17'],
            8: ['7fc97f', 'beaed4', 'fdc086', 'ffff99', '386cb0', 'f0027f',
                'bf5b17', '666666']
        },
        Dark2: {
            type: 'qualitative',
            cbf: 3,
            3: ['1b9e77', 'd95f02', '7570b3'],
            4: ['1b9e77', 'd95f02', '7570b3', 'e7298a'],
            5: ['1b9e77', 'd95f02', '7570b3', 'e7298a', '66a61e'],
            6: ['1b9e77', 'd95f02', '7570b3', 'e7298a', '66a61e', 'e6ab02'],
            7: ['1b9e77', 'd95f02', '7570b3', 'e7298a', '66a61e', 'e6ab02',
                'a6761d'],
            8: ['1b9e77', 'd95f02', '7570b3', 'e7298a', '66a61e', 'e6ab02',
                'a6761d', '666666']
        },
        Paired: {
            type: 'qualitative',
            cbf: 4,
            3: ['a6cee3', '1f78b4', 'b2df8a'],
            4: ['a6cee3', '1f78b4', 'b2df8a', '33a02c'],
            5: ['a6cee3', '1f78b4', 'b2df8a', '33a02c', 'fb9a99'],
            6: ['a6cee3', '1f78b4', 'b2df8a', '33a02c', 'fb9a99', 'e31a1c'],
            7: ['a6cee3', '1f78b4', 'b2df8a', '33a02c', 'fb9a99', 'e31a1c',
                'fdbf6f'],
            8: ['a6cee3', '1f78b4', 'b2df8a', '33a02c', 'fb9a99', 'e31a1c',
                'fdbf6f', 'ff7f00'],
            9: ['a6cee3', '1f78b4', 'b2df8a', '33a02c', 'fb9a99', 'e31a1c',
                'fdbf6f', 'ff7f00', 'cab2d6'],
            10: ['a6cee3', '1f78b4', 'b2df8a', '33a02c', 'fb9a99', 'e31a1c',
                'fdbf6f', 'ff7f00', 'cab2d6', '6a3d9a'],
            11: ['a6cee3', '1f78b4', 'b2df8a', '33a02c', 'fb9a99', 'e31a1c',
                'fdbf6f', 'ff7f00', 'cab2d6', '6a3d9a', 'ffff99'],
            12: ['a6cee3', '1f78b4', 'b2df8a', '33a02c', 'fb9a99', 'e31a1c',
                'fdbf6f', 'ff7f00', 'cab2d6', '6a3d9a', 'ffff99', 'b15928']
        },
        Pastel1: {
            type: 'qualitative',
            cbf: 0,
            3: ['fbb4ae', 'b3cde3', 'ccebc5'],
            4: ['fbb4ae', 'b3cde3', 'ccebc5', 'decbe4'],
            5: ['fbb4ae', 'b3cde3', 'ccebc5', 'decbe4', 'fed9a6'],
            6: ['fbb4ae', 'b3cde3', 'ccebc5', 'decbe4', 'fed9a6', 'ffffcc'],
            7: ['fbb4ae', 'b3cde3', 'ccebc5', 'decbe4', 'fed9a6', 'ffffcc',
                'e5d8bd'],
            8: ['fbb4ae', 'b3cde3', 'ccebc5', 'decbe4', 'fed9a6', 'ffffcc',
                'e5d8bd', 'fddaec'],
            9: ['fbb4ae', 'b3cde3', 'ccebc5', 'decbe4', 'fed9a6', 'ffffcc',
                'e5d8bd', 'fddaec', 'f2f2f2']
        },
        Pastel2: {
            type: 'qualitative',
            cbf: 0,
            3: ['b3e2cd', 'fdcdac', 'cbd5e8'],
            4: ['b3e2cd', 'fdcdac', 'cbd5e8', 'f4cae4'],
            5: ['b3e2cd', 'fdcdac', 'cbd5e8', 'f4cae4', 'e6f5c9'],
            6: ['b3e2cd', 'fdcdac', 'cbd5e8', 'f4cae4', 'e6f5c9', 'fff2ae'],
            7: ['b3e2cd', 'fdcdac', 'cbd5e8', 'f4cae4', 'e6f5c9', 'fff2ae',
                'f1e2cc'],
            8: ['b3e2cd', 'fdcdac', 'cbd5e8', 'f4cae4', 'e6f5c9', 'fff2ae',
                'f1e2cc', 'cccccc']
        },
        Set1: {
            type: 'qualitative',
            cbf: 0,
            3: ['e41a1c', '377eb8', '4daf4a'],
            4: ['e41a1c', '377eb8', '4daf4a', '984ea3'],
            5: ['e41a1c', '377eb8', '4daf4a', '984ea3', 'ff7f00'],
            6: ['e41a1c', '377eb8', '4daf4a', '984ea3', 'ff7f00', 'ffff33'],
            7: ['e41a1c', '377eb8', '4daf4a', '984ea3', 'ff7f00', 'ffff33',
                'a65628'],
            8: ['e41a1c', '377eb8', '4daf4a', '984ea3', 'ff7f00', 'ffff33',
                'a65628', 'f781bf'],
            9: ['e41a1c', '377eb8', '4daf4a', '984ea3', 'ff7f00', 'ffff33',
                'a65628', 'f781bf', '999999']
        },
        Set2: {
            type: 'qualitative',
            cbf: 3,
            3: ['66c2a5', 'fc8d62', '8da0cb'],
            4: ['66c2a5', 'fc8d62', '8da0cb', 'e78ac3'],
            5: ['66c2a5', 'fc8d62', '8da0cb', 'e78ac3', 'a6d854'],
            6: ['66c2a5', 'fc8d62', '8da0cb', 'e78ac3', 'a6d854', 'ffd92f'],
            7: ['66c2a5', 'fc8d62', '8da0cb', 'e78ac3', 'a6d854', 'ffd92f',
                'e5c494'],
            8: ['66c2a5', 'fc8d62', '8da0cb', 'e78ac3', 'a6d854', 'ffd92f',
                'e5c494', 'b3b3b3']
        },
        Set3: {
            type: 'qualitative',
            cbf: 0,
            3: ['8dd3c7', 'ffffb3', 'bebada'],
            4: ['8dd3c7', 'ffffb3', 'bebada', 'fb8072'],
            5: ['8dd3c7', 'ffffb3', 'bebada', 'fb8072', '80b1d3'],
            6: ['8dd3c7', 'ffffb3', 'bebada', 'fb8072', '80b1d3', 'fdb462'],
            7: ['8dd3c7', 'ffffb3', 'bebada', 'fb8072', '80b1d3', 'fdb462',
                'b3de69'],
            8: ['8dd3c7', 'ffffb3', 'bebada', 'fb8072', '80b1d3', 'fdb462',
                'b3de69', 'fccde5'],
            9: ['8dd3c7', 'ffffb3', 'bebada', 'fb8072', '80b1d3', 'fdb462',
                'b3de69', 'fccde5', 'd9d9d9'],
            10: ['8dd3c7', 'ffffb3', 'bebada', 'fb8072', '80b1d3', 'fdb462',
                'b3de69', 'fccde5', 'd9d9d9', 'bc80bd'],
            11: ['8dd3c7', 'ffffb3', 'bebada', 'fb8072', '80b1d3', 'fdb462',
                'b3de69', 'fccde5', 'd9d9d9', 'bc80bd', 'ccebc5'],
            12: ['8dd3c7', 'ffffb3', 'bebada', 'fb8072', '80b1d3', 'fdb462',
                'b3de69', 'fccde5', 'd9d9d9', 'bc80bd', 'ccebc5', 'ffed6f']
        }
    };

    for (var name in schemes) {
        var scheme = schemes[name];
        scheme = palette.Scheme.fromPalettes(
            'cb-' + name, [scheme.type, 'cb-' + scheme.type], scheme, 12, scheme.cbf);
        palette.register(scheme);
    }
})();