// Concatenate the digits of two numbers
function concatenate(a, b) {
// To get some kind of meaningful result from negative numbers, take the
// exclusive OR of the operands' signs
let sign = Math.sign(a) * Math.sign(b);
// Alias the operands as positive numbers
a = Math.abs(a);
b = Math.abs(b);
// Calculate the number of digits in the right operand
let digits = b == 0 ? 1 : Math.floor(Math.log(b) / Math.LN10 + 1);
// Shift the left operand the number of digits and incorporate the sign
return sign * (a * Math.pow(10, digits) + b);
}
// Conceptual operation descriptor
let Operation = function(type, precedence) {
this.precedence = precedence;
this.text = Operation.TEXT[type];
this.type = type;
};
// Evaluate the operation
Operation.prototype.evaluate = function() {
let a = typeof this.left == "number" ? this.left : this.left .evaluate();
let b = typeof this.right == "number" ? this.right : this.right.evaluate();
if (a === null || b === null)
return null;
let ret = Operation.EVAL[this.type](a, b);
return ret; // Comment this line to restrict to non-negative integers
return ret < 0 || ret != Math.floor(ret) ? null : ret;
};
// Format the operation as a string
Operation.prototype.print = function() {
let a = typeof this.left == "number" ? this.left : this.left .print();
let b = typeof this.right == "number" ? this.right : this.right.print();
return "(" + a + " " + this.text + " " + b + ")";
};
// Evaluation by type
Operation.EVAL = [
(a,b)=> a + b, // Add
(a,b)=> a - b, // Subtract
(a,b)=> a * b, // Multiply
(a,b)=> a / b, // Divide
(a,b)=> concatenate(a, b)
];
// Operator text
Operation.TEXT = [ "+", "-", "*", "/", "||" ];
// Operator types
Operation.ADD = 0;
Operation.SUBTRACT = 1;
Operation.MULTIPLY = 2;
Operation.DIVIDE = 3;
Operation.CONCATENATE = 4;
// Precedence ordering
let PRECEDENCE = [
[ 2, 1, 0 ],
[ 2, 0, 1 ],
[ 1, 2, 0 ],
[ 1, 0, 2 ],
[ 0, 2, 1 ],
[ 0, 1, 2 ]
];
// Extract left and right nodes in the expression for an operation
function node(nodes, offset, operation) {
let node = Object.create(operation);
node.right = nodes.splice(offset + 1)[0];
node.left = nodes[offset];
nodes[offset] = node;
}
// Process all combinations of operations
for (let x = 0; x < 5; x++)
for (let y = 0; y < 5; y++)
for (let z = 0; z < 5; z++)
for (let p of PRECEDENCE) {
// Produce operation objects for the current combination
let nodes = [
1,
new Operation(x, p[0]),
2,
new Operation(y, p[1]),
3,
new Operation(z, p[2]),
4
];
// Build an expression tree using the order of precedence
let nodes = [ 1, ops[0], 2, ops[1], 3, ops[2], 4 ];
for (let a = 2; a >= 0; a--) {
for (let n = 0; n < nodes.length; n++) {
let node = nodes[n];
if (node.precedence != a)
continue;
node.right = nodes.splice(n + 1, 1)[0];
node.left = nodes.splice(--n , 1)[0];
break;
}
}
// Evaluate the expression
if (nodes[0].evaluate() == 7)
console.log(nodes[0].print(), nodes[0].evaluate());
}
// Processing has completed
console.log("Done");