/**
* @module utilities
* @desc The utilities module provides various helper functions for the fxSimplex library.
*/
import {getColumn} from './matrix';
/**
* @function trim
* @desc Helps to avoid floating point errors.
* @param {number} x The number to trim.
* @param {number} precision The number of digits to return - optional. **Default**: 7.
* @returns {number} The trimmed number.
*/
export function trim (x, precision = 7) {
return parseFloat(x.toFixed(precision));
}
/**
* @function testVariable
* @desc Tests a variable against an array of prefixes to determine if the variable starts with the prefix.
* @param {string} variable A variable in the form of x1 y2, z3, etc.
* @param {Array} prefixes An array (of strings) of prefixes (e.g., ['a','e'] ) against which to see if the variable matches.
* @returns {boolean} Whether the variable matches one of the supplied prefixes.
*/
export function testVariable (variable, prefixes) {
let regex = new RegExp(`[${prefixes.join('')}]\\d+`, 'i');
return variable.match(regex) != null;
}
/**
* @function testRegex
* @desc Tests the result of a regex match to ensure that the input is of the proper formatting
* and that no reserved variables (e.g., s1, e1, a1) have been used.
* @param {string} input The original string matched by the regex.
* @param {string} testRegex The regex to test for matches - if this returns null the statement
* is improperly formatted.
* @param {string} type The type of input ['constraint' | 'objective'].
* @result {boolean} Returns true if the regex match is not null (complete match), and none of
* the reserved variables are used. False otherwise - logs an error to the console.
* @since v2.0.0
*/
export function testRegex (input, testRegex, type) {
const regex = /\b(?:\d*\.*\d*)([a|e|s]{1}\d*)\b/g;
const badVariables = Array.from(input.matchAll(regex), d => d[1]).join(', ');
const match = input.match(testRegex);
if (match != null && badVariables == '') return true;
if (match == null) console.error(`The ${type} ${input} is not in the proper format for an ${type} statement.`);
if (badVariables != '') console.error(`The following variable(s) are reserved: ${badVariables}.`);
return false;
}
/**
* @function multipleSolutionTest
* @desc Tests whether the computed model has multiple solutions
* @param {Array} model The simplex tableau.
* @param {Array} variables An array of variable names (strings) corresponding to the tableau.
* @param {Array} basicVariables An array of variable names (strings) of those variables
* corresponding to the basis.
* @param {Array} nonBasicVariables An array of variable names (strings) of those variables
* corresponding to those not in the basis.
* @returns {boolean} Returns true if the there are multiple solutions, false otherwise.
*/
export function multipleSolutionTest(model, variables, basicVariables, nonBasicVariables) {
let primaryNonBasicVariables = nonBasicVariables.reduce((a, b) => {
return testVariable(b, ['s', 'e', 'a']) == false ?
a.concat(variables.indexOf(b)) : a;
}, []);
if (primaryNonBasicVariables.length == 0) return false;
let pivotColumns = [];
primaryNonBasicVariables.forEach (index => {
let column = getColumn(model, index);
if (trim(column.slice(-1)[0]) == 0 & column.some(d => trim(d) > 0)) {
pivotColumns.push(index);
};
});
return pivotColumns.length > 0 ?true : false;
};