Source: genetic/linear/registerSet.js

var utils = require('./../../infrastructure/utils');
var clone = require('clone');

/**
 * Linear genetic programming register set
 * @constructor
 * @param {object} options - Register set options
 * @param {number} options.numInputs - The number of input registers
 * @param {number} options.numOutputs - The number of output registers
 * @param {number} [options.constMin=1] - Minimum constant value
 * @param {number} [options.constMax=100] - Maxmimum constant value
 * @param {number} [options.constDivider=1] - Value to divide each constant value by
 * @param {number} [options.defaultCalculationValue=0] - Value to divide each constant value by
 * @param {number} [options.defaultOutputValue=0] - Value to divide each constant value by
 * @param {number} [options.numCalculationRegisters=5] - Value to divide each constant value by
 * @property {object[]} input - Input registers
 * @property {number[]} const - Constant registers
 * @property {number[]} calc - Calculation registers
 * @property {object[]} out - Output registers
 */
var RegisterSet = function (options) {
    this.options = options;
    this.input = [];
    this.const = [];
    this.calc = [];
    this.out = [];
    this.validate();
};

/**
 * Resets all registers
 * @returns {RegisterSet} Reference to current object for chaining
 */
RegisterSet.prototype.reset = function () {
    var i;
    this.input = [];
    this.const = [];
    this.calc = [];
    this.out = [];
    for (i = this.options.constMin; i < this.options.constMax; i++) {
        this.const.push(i / this.options.constDivider);
    }
    for (i = 0; i < this.options.numCalculationRegisters; i++) {
        this.calc.push(this.options.defaultCalculationValue);
    }
    for (i = 0; i < this.options.numOutputs; i++) {
        this.out.push(this.options.defaultOutputValue);
    }
    return this;
};

/**
 * Resets all registers
 * @param {object[]} inputs - An array of inputs
 * @throws An exception is thrown if the length of the input array does not match options.numInputs
 * @returns {RegisterSet} Reference to current object for chaining
 */
RegisterSet.prototype.setInputs = function (inputs) {
    if (inputs.length !== this.options.numInputs) {
        throw "length of inputs does not equal expected length";
    }
    this.reset();
    this.input = inputs;
    return this;
};

/**
 * Validates the register set
 * @throws An exception is thrown if validation fails
 * @returns {RegisterSet} Reference to current object for chaining
 */
RegisterSet.prototype.validate = function () {
    this.setDefaultOptionsIfNotProvided().validateRequiredOptions().validateOptions().reset();
};

/**
 * Throws an exception if a required option is missing
 * @throws An exception is thrown if a required option is missing
 * @returns {RegisterSet} Reference to current object for chaining
 */
RegisterSet.prototype.validateRequiredOptions = function () {
    if (!this.options) {
        throw "options are required";
    } else if (!this.options.numInputs) {
        throw "option 'numInputs is required";
    } else if (!this.options.numOutputs) {
        throw "option 'numOutputs' is required";
    }
    return this;
};

/**
 * Throws an exception if any option values are invalid
 * @throws An exception is thrown if any option values are invalid
 * @returns {RegisterSet} Reference to current object for chaining
 */
RegisterSet.prototype.validateOptions = function () {
    if (this.options.constMin > this.options.constMax) {
        throw "value for option 'constMin' cannot be greater than 'constMax'";
    }
    return this;
};

/**
 * Sets default values for options that have not been defined
 * @returns {RegisterSet} Reference to current object for chaining
 */
RegisterSet.prototype.setDefaultOptionsIfNotProvided = function () {
    if (this.options.constMin === undefined) {
        this.options.constMin = 1;
    }
    if (this.options.constMax === undefined) {
        this.options.constMax = 100;
    }
    if (this.options.constDivider === undefined) {
        this.options.constDivider = 1;
    }
    if (this.options.defaultCalculationValue === undefined) {
        this.options.defaultCalculationValue = 0;
    }
    if (this.options.defaultOutputValue === undefined) {
        this.options.defaultOutputValue = 0;
    }
    if (this.options.numCalculationRegisters === undefined) {
        this.options.numCalculationRegisters = 5;
    }
    return this;
};

/**
 * Returns a deep copy of the register set output array
 * @returns {object[]} A deep copy of the register set output array
 */
RegisterSet.prototype.getOutputNodes = function () {
    return clone(this.out);
};

/**
 * Returns the total number of writable registers
 * @returns {number} The total number of writable registers
 */
RegisterSet.prototype.getTotalWritableRegisters = function () {
    return this.calc.length + this.out.length;
};

exports.RegisterSet = RegisterSet;