function distribution(name) {
    if (!distributions.hasOwnProperty(name)) {
      throw new Error('Unknown distribution ' + name);
    }

    var args = Array.prototype.slice.call(arguments, 1);
    var distribution = distributions[name].apply(this, args);
    return function (distribution) {
      // This is the public API for all distributions
      var randFunctions = {
        random: function random(arg1, arg2, arg3) {
          var size, min, max;

          if (arguments.length > 3) {
            throw new ArgumentsError('random', arguments.length, 0, 3);
          } else if (arguments.length === 1) {
            // `random(max)` or `random(size)`
            if (isCollection(arg1)) {
              size = arg1;
            } else {
              max = arg1;
            }
          } else if (arguments.length === 2) {
            // `random(min, max)` or `random(size, max)`
            if (isCollection(arg1)) {
              size = arg1;
              max = arg2;
            } else {
              min = arg1;
              max = arg2;
            }
          } else {
            // `random(size, min, max)`
            size = arg1;
            min = arg2;
            max = arg3;
          } // TODO: validate type of size


          if (min !== undefined && !isNumber(min) || max !== undefined && !isNumber(max)) {
            throw new TypeError('Invalid argument in function random');
          }

          if (max === undefined) max = 1;
          if (min === undefined) min = 0;

          if (size !== undefined) {
            var res = _randomDataForMatrix(size.valueOf(), min, max, _random);

            return type.isMatrix(size) ? matrix(res) : res;
          }

          return _random(min, max);
        },
        randomInt: typed({
          'number | Array': function numberArray(arg) {
            var min = 0;

            if (isCollection(arg)) {
              var size = arg;
              var max = 1;

              var res = _randomDataForMatrix(size.valueOf(), min, max, _randomInt);

              return type.isMatrix(size) ? matrix(res) : res;
            } else {
              var _max = arg;
              return _randomInt(min, _max);
            }
          },
          'number | Array, number': function numberArrayNumber(arg1, arg2) {
            if (isCollection(arg1)) {
              var size = arg1;
              var max = arg2;
              var min = 0;

              var res = _randomDataForMatrix(size.valueOf(), min, max, _randomInt);

              return type.isMatrix(size) ? matrix(res) : res;
            } else {
              var _min = arg1;
              var _max2 = arg2;
              return _randomInt(_min, _max2);
            }
          },
          'Array, number, number': function ArrayNumberNumber(size, min, max) {
            var res = _randomDataForMatrix(size.valueOf(), min, max, _randomInt);

            return size && size.isMatrix === true ? matrix(res) : res;
          }
        }),
        pickRandom: typed({
          'Array': function Array(possibles) {
            return _pickRandom(possibles);
          },
          'Array, number | Array': function ArrayNumberArray(possibles, arg2) {
            var number, weights;

            if (Array.isArray(arg2)) {
              weights = arg2;
            } else if (isNumber(arg2)) {
              number = arg2;
            } else {
              throw new TypeError('Invalid argument in function pickRandom');
            }

            return _pickRandom(possibles, number, weights);
          },
          'Array, number | Array, Array | number': function ArrayNumberArrayArrayNumber(possibles, arg2, arg3) {
            var number, weights;

            if (Array.isArray(arg2)) {
              weights = arg2;
              number = arg3;
            } else {
              weights = arg3;
              number = arg2;
            }

            if (!Array.isArray(weights) || !isNumber(number)) {
              throw new TypeError('Invalid argument in function pickRandom');
            }

            return _pickRandom(possibles, number, weights);
          }
        })
      };

      function _pickRandom(possibles, number, weights) {
        var single = typeof number === 'undefined';

        if (single) {
          number = 1;
        }

        if (type.isMatrix(possibles)) {
          possibles = possibles.valueOf(); // get Array
        } else if (!Array.isArray(possibles)) {
          throw new TypeError('Unsupported type of value in function pickRandom');
        }

        if (array.size(possibles).length > 1) {
          throw new Error('Only one dimensional vectors supported');
        }

        var totalWeights = 0;

        if (typeof weights !== 'undefined') {
          if (weights.length !== possibles.length) {
            throw new Error('Weights must have the same length as possibles');
          }

          for (var i = 0, len = weights.length; i < len; i++) {
            if (!isNumber(weights[i]) || weights[i] < 0) {
              throw new Error('Weights must be an array of positive numbers');
            }

            totalWeights += weights[i];
          }
        }

        var length = possibles.length;

        if (length === 0) {
          return [];
        } else if (number >= length) {
          return number > 1 ? possibles : possibles[0];
        }

        var result = [];
        var pick;

        while (result.length < number) {
          if (typeof weights === 'undefined') {
            pick = possibles[Math.floor(rng() * length)];
          } else {
            var randKey = rng() * totalWeights;

            for (var _i = 0, _len = possibles.length; _i < _len; _i++) {
              randKey -= weights[_i];

              if (randKey < 0) {
                pick = possibles[_i];
                break;
              }
            }
          }

          if (result.indexOf(pick) === -1) {
            result.push(pick);
          }
        }

        return single ? result[0] : result; // TODO: add support for multi dimensional matrices
      }

      function _random(min, max) {
        return min + distribution() * (max - min);
      }

      function _randomInt(min, max) {
        return Math.floor(min + distribution() * (max - min));
      } // This is a function for generating a random matrix recursively.


      function _randomDataForMatrix(size, min, max, randFunc) {
        var data = [];
        size = size.slice(0);

        if (size.length > 1) {
          for (var i = 0, length = size.shift(); i < length; i++) {
            data.push(_randomDataForMatrix(size, min, max, randFunc));
          }
        } else {
          for (var _i2 = 0, _length = size.shift(); _i2 < _length; _i2++) {
            data.push(randFunc(min, max));
          }
        }

        return data;
      }

      return randFunctions;
    }(distribution);
  } // Each distribution is a function that takes no argument and when called returns
Create a distribution object with a set of random functions for given
random distribution.

Syntax:

    math.distribution(name)

Examples:

    const normalDist = math.distribution('normal') // create a normal distribution
    normalDist.random(0, 10)                     // get a random value between 0 and 10

See also:

    random, randomInt, pickRandom

@param {string} name   Name of a distribution. Choose from 'uniform', 'normal'.
@return {Object}       Returns a distribution object containing functions:
                       `random([size] [, min] [, max])`,
                       `randomInt([min] [, max])`,
                       `pickRandom(array)`