UNPKG

nowjs-core

Version:

NowCanDo Javascript Core [nowjs-core] is a library written by TypeScript code maintains under Apache 2.0 licence

1,176 lines (1,175 loc) 35.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const index_1 = require("./index"); function getUniqueArray(arr) { const result = []; const hash = {}; if (typeof arr !== 'object' || !arr.length) { return result; } for (let i = 0, len = arr.length; i < len; i++) { if (!hash[arr[i]]) { result.push(arr[i]); hash[arr[i]] = 1; } } return result; } function convertArrayValuesToHashMap(arr) { if (!arr || typeof arr !== 'object') { return {}; } const obj = {}; for (let i = 0, len = arr.length; i < len; i++) { obj[arr[i]] = 1; } return obj; } function sortArrayWithSubsetAtEnd(arr, subset) { if (!arr || typeof arr !== 'object' || !subset || typeof subset !== 'object') { return []; } const list = []; const hash = convertArrayValuesToHashMap(subset); for (let i = 0, len = arr.length; i < len; i++) { if (!hash[arr[i]]) { list.push(arr[i]); } } return list.sort().concat(subset.sort()); } function areObjectsSame(obj1, obj2) { let a; let b; if (obj1 === obj2) { return true; } if (!(obj1 instanceof obj2.constructor)) { return false; } for (const prop in obj1) { if (!obj1.hasOwnProperty(prop)) { continue; } a = obj1[prop]; b = obj2[prop]; if (typeof a === 'object') { if (typeof a !== typeof b) { return false; } if (!areObjectsSame(a, b)) { return false; } } else { if (a.toString() !== b.toString()) { return false; } } } return true; } const STANDARD_MAX = 'standardMax'; const STANDARD_MIN = 'standardMin'; const NONSTANDARD_MIN = 'nonstandardMin'; const NONSTANDARD_MAX = 'nonstandardMax'; class Simplex { constructor(problem) { this.problem = problem; this.checkForErrors(problem); this.input = new SimplexInput(problem.Type, problem.Objective, problem.Constraints); } get Definition() { return this.Definition; } solve() { return this.solveInternal(); } static solve(problem) { const simplex = new Simplex(problem); return simplex.solve(); } checkForErrors(obj) { const errMsg = Simplex.getErrors(obj); if (errMsg) { throw new Error(errMsg); } } static getErrors(obj) { if (typeof obj !== 'object') { return 'An object must be passed to Simplex.solve()'; } if (!obj.Type || !obj.Objective || !obj.Constraints) { return 'The object must have the properties `type`, `objective` and `constraints`.'; } } solveInternal() { this.tableau = new SimplexTableau(this.input); this.output = this.tableau.solve().getOutput(); return this.output; } } exports.Simplex = Simplex; class SimplexMatrix extends index_1.NumericMatrix { constructor(...rows) { super(0, 0, ...rows); } checkColumnIndex(col) { } checkRowIndex(row) { } checkIndexes(row, col) { } get ColSize() { return this.getSize()[1]; } get RowSize() { return this.getSize()[0]; } getSize() { let columns = 0; const rows = this.arr.length; let i = rows; let x; while (i--) { x = this.arr[i].length; columns = columns < x ? x : columns; } return [rows, columns]; } static scaleRow(scale, row) { if (!Array.isArray(row)) { return row; } row = row.concat(); for (let i = 0, len = row.length; i < len; i++) { row[i] *= scale; } return row; } static addRows(rowA, rowB) { return SimplexMatrix.scaleThenAddRows(1, rowA, 1, rowB); } static scaleThenAddRows(scaleA, rowA, scaleB, rowB) { rowA = Array.isArray(rowA) ? rowA.concat() : []; rowB = Array.isArray(rowB) ? rowB.concat() : []; const len = Math.max(rowA.length, rowB.length); for (let i = 0; i < len; i++) { rowA[i] = scaleA * (rowA[i] || 0) + scaleB * (rowB[i] || 0); } return rowA; } static inverseArray(arr) { if (!Array.isArray(arr)) { return arr; } let i = arr.length; while (i--) { arr[i] = -arr[i]; } return arr; } toString() { let str = ''; this.forEachRow((i, row) => { if (i) { str += ','; } str += '[' + row.toString() + ']'; }); str = '[' + str + ']'; return str; } setUniformedWidth() { const info = SimplexMatrix.getMaxArray(this.arr); for (let i = 0, len = this.arr.length; i < len; i++) { this.arr[i].length = info.max; } return this; } getGreatestValueFromLastRow(isPositive) { if (!this.arr || this.arr.length < 1) { return -1; } const row = this.arr[this.arr.length - 1]; const obj = SimplexMatrix.getGreatestElementInRow(row, row.length - 1, !!isPositive); if (isPositive) { return -1 < obj.value ? obj.index : -1; } else { return obj.value < 0 ? obj.index : -1; } } getRowIndexWithPosMinColumnRatio(colI, excludeLastRow) { const obj = { rowIndex: -1, minValue: Infinity, }; const len = this.arr.length + (excludeLastRow ? -1 : 0); let val; let row; if (colI < 0 || this.arr[0].length <= colI) { return null; } for (let i = 0; i < len; i++) { row = this.arr[i]; val = row[row.length - 1] / row[colI]; if (0 <= val && val < obj.minValue) { obj.rowIndex = i; obj.minValue = val; } } return obj; } getUnitValueForColumn(colI) { let nonZeroValues = 0; let val = 0; this.forEachRow((i, row) => { if (row[colI] === 1) { val = row[row.length - 1]; } if (row[colI]) { nonZeroValues++; } }); val = nonZeroValues === 1 ? val : 0; return val; } getLastElementOnLastRow() { const row = this.arr[this.arr.length - 1]; return row[row.length - 1]; } static getGreatestElementInRow(arr, excludeIndex, findPositive) { if (!arr || !Array.isArray(arr)) { return null; } const obj = { value: Infinity * (findPositive ? -1 : 1), index: -1, }; for (let i = 0, len = arr.length; i < len; i++) { if (excludeIndex === i) { continue; } if ((findPositive && obj.value < arr[i]) || (!findPositive && arr[i] < obj.value)) { obj.index = i; obj.value = arr[i]; } } return obj; } addRow(arr) { arr = Array.isArray(arr) ? arr : [arr]; this.arr.push(arr); return this; } addToRow(iRow, els) { if (this.arr[iRow]) { this.arr[iRow] = (this.arr[iRow] || []).concat(els); } else { this.addRow(els); } return this; } equals(obj) { return obj && obj instanceof SimplexMatrix && this.toString() === obj.toString(); } static getMaxArray(arrays) { const obj = { index: 0, max: 0, }; if (!Array.isArray(arrays)) { return null; } if (!Array.isArray(arrays[0])) { obj.max = arrays.length; return obj; } let i = arrays.length; while (i--) { if (obj.max < arrays[i].length) { obj.index = i; obj.max = arrays[i].length; } } return obj; } scaleRow(scaleA, rowI) { const row = this.arr[rowI] || []; for (let i = 0, len = row.length; i < len; i++) { row[i] *= scaleA; } return this; } pivot(rowI, colI) { if (!this.arr[rowI]) { return this; } const x = this.getElement(rowI, colI); let val; let pRow; this.scaleRow(1 / x, rowI); pRow = this.arr[rowI]; for (let i = 0, len = this.arr.length; i < len; i++) { if (i === rowI) { continue; } val = this.getElement(i, colI); this.arr[i] = SimplexMatrix.scaleThenAddRows(-val, pRow, 1, this.arr[i]); } return this; } } exports.SimplexMatrix = SimplexMatrix; class SimplexExpression { constructor(str) { this.terms = null; this.terms = {}; if (typeof str !== 'string' || !str.length) { return; } SimplexExpression.checkString(str); str = SimplexExpression.addSpaceBetweenTerms(str); this.terms = SimplexExpression.convertExpressionToObject(str); } get Terms() { return this.terms; } static encodeE(str) { str = (str || '').toString(); str = str.replace(/(\de)([+])(\d)/gi, '$1_plus_$3'); str = str.replace(/(\de)([\-])(\d)/gi, '$1_sub_$3'); return str; } static decodeE(str) { str = (str || '').toString(); str = str.replace(/_plus_/g, '+'); str = str.replace(/_sub_/g, '-'); return str; } static hasManyCompares(str) { const RE_compares = /[<>]=?|=/g; const matches = ('' + str).replace(/\s/g, '').match(RE_compares) || []; return 1 < matches.length; } static addSpaceBetweenTerms(str) { str = SimplexExpression.encodeE(str); str = str.replace(/([\+\-])/g, ' $1 '); str = str.replace(/\s{2,}/g, ' '); str = str.trim(); str = SimplexExpression.decodeE(str); return str; } static hasExcludedOperations(str) { return /[\*\/%]/.test(str); } static hasIncompleteBinaryOperator(str) { let hasError; const noSpaceStr = ('' + str).replace(/\s/g, ''); const RE_hasNoPlusOrMinus = /^[^\+\-]+$/; const RE_noLeftAndRightTerms = /[\+\-]{2}|[\+\-]$/; const hasMoreThanOneTerm = /\S+\s+\S+/.test(str); hasError = hasMoreThanOneTerm && RE_hasNoPlusOrMinus.test(noSpaceStr); hasError = hasError || RE_noLeftAndRightTerms.test(noSpaceStr); return hasError; } static hasComparison(str) { return /[><=]/.test(str); } static getErrorMessage(str) { let errMsg; if (SimplexExpression.hasComparison(str)) { errMsg = 'Comparison are not allowed within an expression.'; } if (!errMsg && SimplexExpression.hasExcludedOperations(str)) { errMsg = 'Addition and subtraction are only supported.'; } if (!errMsg && SimplexExpression.hasIncompleteBinaryOperator(str)) { errMsg = 'Exactly one math operators must be between terms.\n Good:(a+b). Bad:(a++ b+).'; } if (errMsg) { errMsg += '\n Input: `' + str + '`'; } return errMsg; } static checkString(str) { const errMsg = SimplexExpression.getErrorMessage(str); if (errMsg) { throw new Error(errMsg); } } static extractComponentsFromVariable(str) { str = '' + str; const re = /^[\+\-]?\d+(\.\d+)?(e[\+\-]?\d+)?/i; let coeff = '' + (str.match(re) || [''])[0]; let term = str.replace(re, '') || '1'; if (+str === 0) { coeff = 0; } if (coeff === '') { coeff = /^\-/.test(term) ? -1 : 1; term = term.replace(/^[\+\-]/, ''); } return [+coeff, term]; } static splitStrByTerms(str) { const RE_findSignForTerm = /([\+\-])\s+/g; const RE_spaceOrPlus = /\s+[\+]?/; return ('' + str) .replace(/^\s*\+/, '') .replace(RE_findSignForTerm, '$1') .split(RE_spaceOrPlus); } static convertExpressionToObject(str) { let term; const obj = {}; const matches = SimplexExpression.splitStrByTerms((str || '').trim()); let i = matches.length; while (i--) { term = SimplexExpression.extractComponentsFromVariable(matches[i]); if (!term[0]) { term = [0, 1]; } obj[term[1]] = obj[term[1]] ? obj[term[1]] + term[0] : term[0]; } return obj; } static termAtIndex(i, name, value) { let result = ''; if (value) { if (value < 0) { result += value === -1 ? '-' : value; } else { if (i) { result += '+'; } result += value === 1 ? '' : value; } result += name; } else { result += 0 < name && i ? '+' : ''; result += name; } return result; } getTermNames(excludeNumbers, excludeSlack) { const obj = this.terms; let terms = []; let key; const RE_slack = /^slack\d*$/i; for (key in obj) { if (!obj.hasOwnProperty(key) || (excludeSlack && RE_slack.test(key))) { continue; } if (isNaN(key)) { terms.push(key); } } terms = terms.sort(); if (!excludeNumbers && obj && obj[1]) { terms.push(obj[1].toString()); } return terms; } forEachTerm(fn) { if (typeof fn !== 'function') { return; } for (const prop in this.terms) { if (this.terms.hasOwnProperty(prop)) { fn(prop, this.terms[prop], this.terms); } } } forEachConstant(fn) { if (typeof fn !== 'function') { return; } const prop = '1'; if (this.terms[prop]) { fn(prop, this.terms[prop], this.terms); } } forEachVariable(fn) { if (typeof fn !== 'function') { return; } for (const prop in this.terms) { if (this.terms.hasOwnProperty(prop) && prop !== '1') { fn(prop, this.terms[prop], this.terms); } } } toString() { const arr = []; const names = this.getTermNames(); let i; let name; let len; const func = SimplexExpression.termAtIndex; if (!names.length) { return '0'; } for (i = 0, len = names.length; i < len; i++) { name = names[i]; arr.push(func(i, name, this.terms[name])); } return arr.join(' ').replace(/\s[\+\-]/g, '$& '); } inverse() { this.forEachTerm((termName, value, terms) => { terms[termName] = -value; }); return this; } addTerm(name, value) { if (typeof value !== 'undefined') { value += this.terms[name] || 0; if (value) { this.terms[name] = value; } else { this.removeTerm(name); } } else { this.addExpression(name); } return this; } setTerm(name, value) { if (value) { this.terms[name] = value; } else { this.removeTerm(name); } return this; } addExpression(obj) { if (!(obj instanceof SimplexExpression)) { obj = new SimplexExpression(obj); } this.addTerms(obj.toTermValueArray()); return this; } toTermValueArray() { const arr = []; for (const name in this.terms) { if (this.terms.hasOwnProperty(name)) { arr.push([name, this.terms[name]]); } } return arr; } addTerms(arr) { if (!arr || typeof arr !== 'object') { return this; } for (let i = 0, len = arr.length; i < len; i++) { if (arr[i] && typeof arr[i] === 'object') { this.addTerm(arr[i][0], arr[i][1]); } } return this; } removeTerm(name) { delete this.terms[name]; return this; } scale(factor) { factor = +factor; this.forEachTerm((name, value, terms) => { terms[name] = factor * value; }); return this; } hasTerm(name) { return !!this.terms[name]; } getTermValue(name) { return this.terms[name]; } getAllCoeffients(excludeNumbers, excludeSlack) { const arr = []; const names = this.getTermNames(excludeNumbers, excludeSlack); for (let i = 0, len = names.length; i < len; i++) { arr.push(+(this.terms[names[i]] || names[i])); } return arr; } getCoefficients(termNames) { const arr = []; let i = termNames.length; while (i--) { arr[i] = this.terms[termNames[i]] || 0; } return arr; } clon() { return new SimplexExpression(this.toString()); } } exports.SimplexExpression = SimplexExpression; class SimplexConstraint { constructor(str) { this.comparison = ''; this.specialTerms = {}; const obj = SimplexConstraint.parseToObject(str); if (obj) { this.comparison = obj.comparison; this.leftSide = obj.lhs; this.rightSide = obj.rhs; } this.specialTerms = {}; } equals(obj) { return areObjectsSame(this, obj); } get LeftSide() { return this.leftSide; } get RightSide() { return this.rightSide; } get Comparison() { return this.comparison; } static hasManyCompares(str) { const RE_compares = /[<>]=?|=/g; const matches = ('' + str).replace(/\s/g, '').match(RE_compares) || []; return 1 < matches.length; } static hasIncompleteBinaryOperator(str) { str = str.replace(/\s{2,}/g, ''); const noSpaceStr = ('' + str).replace(/\s/g, ''); const hasNoOperatorBetweenValues = /[^+\-><=]\s+[^+\-><=]/.test('' + str); const RE_noLeftAndRightTerms = /[+\-][><=+\-]|[><=+\-]$/; return RE_noLeftAndRightTerms.test(noSpaceStr) || hasNoOperatorBetweenValues; } static getErrorMessage(str) { let errMsg; if (SimplexConstraint.hasManyCompares(str)) { errMsg = 'Only 1 comparision (<,>,=, >=, <=) is allow in a Constraint.'; } if (!errMsg && SimplexConstraint.hasIncompleteBinaryOperator(str)) { errMsg = 'Math operators must be in between terms. Good:(a+b=c). Bad:(a b+=c)'; } return errMsg; } static checkInput(str) { const errMsg = SimplexConstraint.getErrorMessage(str); if (errMsg) { throw new Error(errMsg); } } static switchSides(sideA, sideB, forEachTermFunc) { forEachTermFunc.call(sideA, (name, value) => { sideB.addTerm(name, -value); sideA.removeTerm(name); }); } getTermNames(excludeNumbers) { const arr = [].concat(this.leftSide.getTermNames(excludeNumbers), this.rightSide.getTermNames(excludeNumbers)); return getUniqueArray(arr); } static parseToObject(str) { str = str.replace(/([><])(\s+)(=)/g, '$1$3'); SimplexConstraint.checkInput(str); const RE_comparison = /[><]=?|=/; const arr = ('' + str).split(RE_comparison); const obj = { rhs: new SimplexExpression('0'), comparison: '=', }; obj.lhs = new SimplexExpression(arr[0]); if (1 < arr.length) { obj.rhs = new SimplexExpression(arr[1]); obj.comparison = '' + RE_comparison.exec(str); } return obj; } static parse(str) { const obj = SimplexConstraint.parseToObject(str); let e; if (obj) { e = new SimplexConstraint(); e.comparison = obj.comparison; e.leftSide = obj.lhs; e.rightSide = obj.rhs; } return e; } toString() { return [this.leftSide, this.comparison, this.rightSide].join(' '); } getSwappedSides(doSwap) { return { a: !doSwap ? this.leftSide : this.rightSide, b: doSwap ? this.leftSide : this.rightSide, }; } moveTypeToOneSide(varSide, numSide) { let varSides; let numSides; if (/left|right/.test(varSide)) { varSides = this.getSwappedSides(/left/.test(varSide)); SimplexConstraint.switchSides(varSides.a, varSides.b, varSides.a.forEachVariable); } if (/left|right/.test(numSide)) { numSides = this.getSwappedSides(/left/.test(numSide)); SimplexConstraint.switchSides(numSides.a, numSides.b, numSides.a.forEachConstant); } return this; } inverse() { const oppositeCompare = { '=': '=', '>=': '<', '>': '<=', '<=': '>', '<': '>=', }; if (oppositeCompare[this.comparison]) { this.comparison = oppositeCompare[this.comparison]; this.leftSide.inverse(); this.rightSide.inverse(); } return this; } removeStrictInequality() { let eps; if (/^[<>]$/.test(this.comparison)) { eps = SimplexConstraint.EPSILON * ('>' === this.comparison ? 1 : -1); this.rightSide.addTerm('1', eps); this.comparison += '='; } return this; } normalize() { this.moveTypeToOneSide('left', 'right'); if (this.rightSide.getTermValue('1') < 0) { this.inverse(); } return this.removeStrictInequality(); } addSlack(val) { this.setSpecialTerm({ key: 'slack', name: 'slack', value: val, }); return this; } setSpecialTerm(obj) { if (!obj || typeof obj !== 'object' || !obj.name || !obj.key) { return this; } this.specialTerms[obj.key] = this.specialTerms[obj.key] || {}; const oldName = this.specialTerms[obj.key].name; if (oldName) { if (typeof obj.value === 'undefined') { obj.value = this.leftSide.getTermValue(oldName); } this.leftSide.removeTerm(oldName); } this.specialTerms[obj.key].name = obj.name; this.leftSide.setTerm(this.specialTerms[obj.key].name, +obj.value); return this; } addArtificalVariable(val) { this.setSpecialTerm({ key: 'artifical', name: 'artifical', value: val, }); return this; } hasSpecialTerm(name) { return !!this.specialTerms[name]; } renameSlack(name) { this.setSpecialTerm({ key: 'slack', name, }); return this; } renameArtificial(name) { this.setSpecialTerm({ key: 'artifical', name, }); return this; } convertToEquation() { this.normalize(); switch (this.comparison) { case '<=': this.addSlack(1); break; case '>=': this.addSlack(-1); this.addArtificalVariable(1); break; } this.comparison = '='; return this; } getSpecialTermNames() { const names = []; for (const prop in this.specialTerms) { if (this.specialTerms.hasOwnProperty(prop) && this.specialTerms[prop]) { names.push(this.specialTerms[prop].name); } } return names.length ? names : null; } getSpecialTermValue(name) { const obj = this.specialTerms[name]; if (!obj) { return null; } return this.getCoefficients([obj.name])[0]; } getArtificalName() { const obj = this.specialTerms.artifical; if (!obj) { return null; } return obj.name; } scale(factor) { this.leftSide.scale(factor); this.rightSide.scale(factor); return this; } varSwitchSide(name, moveTo) { if (!/left|right/.test(moveTo)) { return this; } name = isNaN(name) ? name : '1'; const sideA = 'left' === moveTo ? this.rightSide : this.leftSide; const sideB = 'left' !== moveTo ? this.rightSide : this.leftSide; if (sideA.hasTerm(name)) { sideB.addTerm(name, -sideA.getTermValue(name)); sideA.removeTerm(name); } return this; } getCoefficients(termNames) { if (!termNames) { return null; } const arr = new Array(termNames.length); let val; let i = arr.length; while (i--) { val = this.leftSide.getTermValue(termNames[i]); if (val === undefined) { val = this.rightSide.getTermValue(termNames[i]); } arr[i] = val || 0; } return arr; } getTermValuesFromLeftSide(termNames) { if (!termNames) { return null; } const arr = new Array(termNames.length); let val; let i = arr.length; while (i--) { val = this.leftSide.getTermValue(termNames[i]); if (val === undefined) { val = -this.rightSide.getTermValue(termNames[i]); } arr[i] = val || 0; } return arr; } } exports.SimplexConstraint = SimplexConstraint; SimplexConstraint.EPSILON = 1e-6; class SimplexTableau { constructor(input) { this.input = null; this.colNames = []; this.matrix = null; this.limit = 1e4; this.cycles = 0; this.input = input; this.input.convertToStandardForm(); this.setMatrixFromInput(); } static getErrorMessage(input) { if (!(input instanceof SimplexInput)) { return 'Must pass an instance of the Input class.'; } } checkForError(input) { const errMsg = SimplexTableau.getErrorMessage(input); if (errMsg) { throw new Error(errMsg); } } addZToMatrix(termNames) { const b = new SimplexConstraint('0 = ' + this.input.Z.toString()); b.moveTypeToOneSide('left', 'right'); let row = b.LeftSide.getCoefficients(termNames); row = row.concat(b.RightSide.getTermValue('1') || 0); this.matrix.addRow(row); } addConstraintsToMatrix(termNames) { const constraints = this.input.Constraints; for (let i = 0, len = constraints.length; i < len; i++) { this.matrix.addRow(constraints[i].getCoefficients(termNames)); } } getSortedTermNames() { const termNames = this.input.getTermNames(true); const specialNames = this.input.getAllSpecialTermNames(); return sortArrayWithSubsetAtEnd(termNames, specialNames); } setMatrixFromInput() { this.matrix = new SimplexMatrix(); this.colNames = this.getSortedTermNames(); this.addConstraintsToMatrix(this.colNames.concat('1')); this.addZToMatrix(this.colNames); } solve(isMin) { let point = this.getPivotPoint(this.matrix, isMin); let limit = this.limit; while (point && limit--) { this.matrix.pivot(point.row, point.column); point = this.getPivotPoint(this.matrix, isMin); this.cycles++; } return this; } getPivotPoint(matrix, isMin) { if (!(matrix instanceof SimplexMatrix)) { return null; } const point = { column: 0, row: 0 }; point.column = matrix.getGreatestValueFromLastRow(!!isMin); const obj = matrix.getRowIndexWithPosMinColumnRatio(point.column, true) || { rowIndex: -1, minValue: Infinity }; point.row = obj.rowIndex; if (point.column < 0 || point.row < 0) { return null; } return point; } getOutput() { const obj = { z: 0 }; const names = this.colNames.concat(); for (let i = 0, len = names.length; i < len; i++) { obj[names[i]] = this.matrix.getUnitValueForColumn(i); } obj.z = this.matrix.getLastElementOnLastRow(); return new SimplexOutput(obj); } toString() { let result = ''; if (this.matrix) { result += '[' + this.colNames.concat('Constant').toString() + '],'; result += this.matrix.toString(); } return result; } } exports.SimplexTableau = SimplexTableau; class SimplexInput { constructor(type, z, constraints) { this.z = null; this.type = null; this.terms = []; this.constraints = []; this.isStandardMode = false; this.type = type; this.z = new SimplexExpression(z); this.constraints = this.getArrOfConstraints(constraints); this.setTermNames(); this.checkConstraints(); } getZTermNotInAnyOfTheConstraints() { let varMissing = ''; const terms = this.z.getTermNames(); let term; let i = 0; const iLen = terms.length; for (; !varMissing && i < iLen; i++) { term = terms[i]; let j = 0; const jLen = this.constraints.length; for (; j < jLen; j++) { if (this.constraints[j].LeftSide.Terms[term]) { break; } } if (j === jLen) { varMissing = term; } } return varMissing; } checkConstraints() { const errMsg = []; const missingZVar = this.getZTermNotInAnyOfTheConstraints(); if (missingZVar) { errMsg.push('`' + missingZVar + '`, from the objective function, should appear least once in a constraint.'); } return errMsg; } getArrOfConstraints(arr) { arr = Array.isArray(arr) ? arr : [arr]; const constraints = []; let i = arr.length; while (i--) { constraints[i] = new SimplexConstraint(arr[i]); } return constraints; } computeType() { const hasLessThan = this.doAnyConstrainsHaveRelation(/<=?/); const hasGreaterThan = this.doAnyConstrainsHaveRelation(/>=?/); if (/max/.test(this.type)) { return hasGreaterThan ? NONSTANDARD_MAX : STANDARD_MAX; } if (/min/.test(this.type)) { return hasLessThan ? NONSTANDARD_MIN : STANDARD_MIN; } } doAnyConstrainsHaveRelation(comparison) { if (!comparison) { return false; } const comparisoned = new RegExp(comparison); return this.anyConstraints((i, constraint) => { return comparisoned.test(constraint.comparison); }); } doAllConstrainsHaveRelation(comparison) { const comparisoned = new RegExp(comparison); return this.allConstraints((i, constraint) => { return comparisoned.test(constraint.comparison); }); } anyConstraints(fn) { for (let i = 0, len = this.constraints.length; i < len; i++) { if (fn(i, this.constraints[i], this.constraints)) { return true; } } return false; } allConstraints(fn) { let result = true; for (let i = 0, len = this.constraints.length; i < len; i++) { result = result && !!fn(i, this.constraints[i], this.constraints); } return result; } getAllArtificalNames() { const names = []; this.forEachConstraint((i, constraint) => { const name = constraint.getArtificalName(); if (name) { names.push(name); } }); return names; } forEachConstraint(fn) { for (let i = 0, len = this.constraints.length; i < len; i++) { fn(i, this.constraints[i], this.constraints); } } addNumbersToSpecialTerms() { const c = this.constraints; let slackI = 1; let artificalI = 1; for (let i = 0, len = c.length; i < len; i++) { if (c[i].hasSpecialTerm('slack')) { c[i].renameSlack('slack' + slackI); slackI++; } if (c[i].hasSpecialTerm('artifical')) { c[i].renameArtificial('artifical' + artificalI); artificalI++; } } } getTermNames(onlyVariables) { let vars = []; let i = this.constraints.length; while (i--) { vars = vars.concat(this.constraints[i].getTermNames(onlyVariables)); } return getUniqueArray(vars).sort(); } getAllSpecialTermNames() { let names = []; this.forEachConstraint((i, constraint) => { names = names.concat(constraint.getSpecialTermNames()); }); return names; } setTermNames() { this.terms = this.getTermNames(); } get Constraints() { return this.constraints; } get Z() { return this.z; } toString() { return [this.type + ' z = ' + this.z, 'where ' + this.constraints.join(', ')].join(', '); } convertConstraintsToMaxForm() { const c = this.constraints; for (let i = 0, len = c.length; i < len; i++) { c[i] = c[i].convertToEquation(); } } convertToStandardForm() { if (this.isStandardMode) { return this; } this.convertConstraintsToMaxForm(); this.addNumbersToSpecialTerms(); this.setTermNames(); this.isStandardMode = true; return this; } } exports.SimplexInput = SimplexInput; class SimplexOutput { constructor(obj) { this.result = obj; this.checkForError(); } static getErrorMessage(obj) { let errMsg; if (typeof obj !== 'object') { errMsg = 'An object must be passed.'; } return errMsg; } checkForError() { const errMsg = SimplexOutput.getErrorMessage(this.result); if (errMsg) { throw new Error(errMsg); } } toString() { return JSON.stringify({ Result: this.result }); } get Result() { return this.result; } } exports.SimplexOutput = SimplexOutput;