Source: number.js

/**
 * @method
 * @file number.js
 * @desc Perform common expression operations and provide fractional classes.
 * @createDate 2018.7.11.
 * @author yhzheng
 */
"use strict";
// require
var dataStruct = require("./dataStruct");
/**
 * @var inf
 * @desc This is for Express infinity
 */
// set default the value of inf
var inf = 100;
/**
 * @class fractions
 * @classdesc This is for Provides a fractional class object so that fractions can be computed.
 * @desc It's for create object and init object.
 */
class fractions
{
    constructor(info){
        var denominator = info.denominator;
        var numerator = info.numerator;
        this.denominator = denominator;
        this.numerator = numerator;
        if (this.denominator === 0){
            var err = new Error("Denominator can't is 0!");
            throw err;
        }
    }
    /**
     * @method
     * @returns number The value of this fractions
     * @desc For get value of this fractions
     */
    value(){
        return (this.numerator / this.denominator);
    }
    /**
     * @method
     * @returns {fractions} This is the reduction fraction of this fraction
     * @desc For get the reduction fraction of this fraction
     */
    reduction(){
        var newFractions = new fractions({
            denominator: (this.denominator / maximum(this.denominator,this.numerator)),
            numerator: (this.numerator / maximum(this.denominator,this.numerator))
        });
        return newFractions;
    }
    /**
     * @method
     * @desc For Set this fraction to he reduction fraction of this fraction
     */
    toReduction(){
        var reductionNumber = this.reduction();
        this.denominator = reductionNumber.denominator;
        this.numerator = reductionNumber.numerator;
    }
}
/**
 * @method
 * @param {string} exp The expression of the string arithmetic.
 * @returns number This is the result of your expression
 * @desc For parse the expression of the string arithmetic and get the result.
 */
function expression(exp) {
    var lines = exp.split("\n");
    var exps = exp.split(/={5,}/g);
    if (exps.length > 1){// many expressions
        var reses = [];
        for (var i = 0 ; i < exps.length ; i++){
            reses.push(expression(exps[i]));
        }
        return reses;
    } else {// just one expressions
        if (exp.search(/[_\\]/g) === -1 && exp.search(/[a-zA-z]\s*\([\s\S]*\)\s*=/g) === -1 && exp.search(/={2,}/g) === -1 && exp.search(/-{2,}/g) === -1){// four arithmetic operation
            /*
            * {
            *   opt: "xx",
            *   priority: xx
            * }
            */
            exp = exp.replace(/\(\s*(-\d+\.{0,1}\d*\s*)\)/g,"0" + "$1");
            var optArr = [
                {opt: '+',priority: 1},
                {opt: '-',priority: 1},
                {opt: '*',priority: 2},
                {opt: '/',priority: 2},
                {opt: '%',priority: 2},
            ];
            function priority(opt){
                for(var i = 0 ; i < 4 ; i++)
                    if(opt == optArr[i].opt)
                        return optArr[i].priority;
                return -1;
            }
            function isNum(c){
                return c >= '0' && c <= '9';
            }
            function toNum(str,pos) {
                var sztmp = "";
                do {
                    sztmp += str.charAt(pos);
                    pos++;
                } while((str[pos] >='0' && str[pos]<='9') || str[pos] == '.' || str[pos] == "^");
                var sztmpArr = sztmp.split("^");
                sztmp = sztmpArr[sztmpArr.length - 1] * 1;
                for (var i = sztmpArr.length - 2 ; i >= 0 ; i--){
                    sztmp = Math.pow(sztmpArr[i] * 1,sztmp);
                }
                return {num: sztmp * 1,index: pos};
            }

            var num = new dataStruct.stack();
            var opt = new dataStruct.stack();
            var i = 0;
            while (true){
                if (exp.charAt(i).search(/\s/g) !== -1){
                    i++;
                } else if (isNum(exp.charAt(i))){
                    var tmp = toNum(exp,i);
                    num.push(tmp.num);
                    i = tmp.index;
                } else {
                    if (exp.length <= i && opt.empty()){
                        break;
                    }
                    if (opt.empty()){
                        opt.push(exp.charAt(i));
                        i++;
                    } else {
                        if (exp.charAt(i) == "(" || priority(exp.charAt(i)) > priority(opt.top())){
                            opt.push(exp.charAt(i));
                            i++;
                        } else if (exp.charAt(i) == ")" && opt.top() == "("){
                            opt.pop();
                            i++;
                        } else {
                            var opttmp = opt.top();
                            opt.pop();
                            var numa = num.top();
                            num.pop();
                            var numb = num.top();
                            num.pop();
                            var res;
                            switch (opttmp){
                                case "+":
                                    res = numb + numa;
                                    break;
                                case "-":
                                    res = numb - numa;
                                    break;
                                case "*":
                                    res = numb * numa;
                                    break;
                                case "/":
                                    res = numb / numa;
                                    break;
                                case "%":
                                    res = numb % numa;
                                    break;
                                default:
                                    var err = new Error("Don't have this operator!");
                                    throw err;
                                    break;
                            }
                            num.push(res);
                        }
                    }
                }
            }
            return num.top();
        } else if (exp.search(/==/g) !== -1) {// sigma
            /*
            * style of sigma:
            * z
            * ==
            * \
            * |  expression
            * /
            * ==
            * x=y
            */
            if (lines[1] != "==") {
                var index;
                var fills = [];
                var tmp;
                for (var i = 0; i < lines.length; i++) {
                    if (lines[i].search(/\s*==\s*/) !== -1) {
                        index = i - 1;
                        break;
                    }
                }
                for (var i = 0; i < index; i++) {
                    if (lines[i].search(/^\s*$/) !== -1)
                        continue;
                    tmp = lines[i].replace(/\s*(\S)\s*=\s*(\S)\s*/g, "$1=$2").split("=");
                    fills.push({
                        name: tmp[0],
                        value: tmp[1]
                    });
                }
                var count = lines[index];
                if (count.search(/inf/g) !== -1 || count.search(/∞/g) !== -1) {
                    count = inf;
                } else {
                    count *= 1;
                }
                var expression_ = "";
                for (var i = (index + 3); i < (lines.length - 3); i++) {
                    expression_ += lines[i].replace(/^\s\|\s*([\s\S]*)/g, "$1");
                    if ((i + 1) < (lines.length - 3)) {
                        expression_ += "\n";
                    }
                }
                for (var i = 0 ; i < fills.length ; i++){
                    expression_ = expression.replace(new RegExp(fills[i].name),fills[i].value);
                }
                var lastLine = lines[lines.length - 1].split(/\s*=\s*/g);
                return sigma(expression_, count, {
                    name: lastLine[0],
                    value: lastLine[1]
                });
            } else {
                var count = lines[0];
                if (count.search(/inf/g) !== -1 || count.search(/∞/g) !== -1) {
                    count = inf;
                } else {
                    count *= 1;
                }
                var expression_ = "";
                for (var i = 3; i < (lines.length - 3); i++) {
                    expression_ += lines[i].replace(/^\s\|\s*([\s\S]*)/g, "$1");
                    if ((i + 1) < (lines.length - 3)) {
                        expression_ += "\n";
                    }
                }
                var lastLine = lines[lines.length - 1].split(/\s*=\s*/g);
                count++;
                return sigma(expression_, count, {
                    name: lastLine[0],
                    value: lastLine[1]
                });
            }
        } else if (exp.search(/-{3,}/g) !== -1){
            var maxLine = "";
            for (var i = 0 ; i < lines.length ; i++){
                if (lines[i].search(/^-+$/g) !== -1){
                    if (lines[i].length > maxLine.length){
                        maxLine = lines[i];
                    }
                }
            }
            var tmp = exp.split(maxLine);
            var up = tmp[0],down = tmp[1];
            return expression(up) / expression(down);
        } else {
            console.log("Now that this method is not available, you can send a brief introduction of the detailed information of this method to zhengyh2018@gmail.com by email, and we will understand the message as soon as possible and make improvements (please set the title to [emath add methods).)");
        }
    }
}
/**
 * @method
 * @param {number} num The value of inf
 * @desc Set the value of inf
 */
function setInf(num) {
    inf = num;
}
/**
 * @method
 * @param {string} exp The string expression of sigma
 * @param {number} count The while count
 * @param {object} init The information for init.
 * @returns The sum of sigma
 * @desc The sigma function<br/>
 * Demo: sigma("i^2",3,{
 *      name: "i",
 *      value: 0
 * });// return: 7
 */
function sigma(exp,count,init) {
    var sum = 0;
    var newExp;
    for (var i = 0 ; i < count ; i++){
        newExp = exp.replace(new RegExp(init.name),init.value);
        sum += expression(newExp);
        init.value++;
    }
    return sum;
}
/**
 * @method
 * @param {number} a First number
 * @param {number} b Second number
 * @returns The maximum common factor of a and b
 * @desc To get the maximum common factor of a and b
 */
function MCF(a,b) {
    var primeOfA = primeFact(a);
    var primeOfB = primeFact(b);
    var commonFactors = commonFactor(primeOfA,primeOfB);
    var max = commonFactors[0];
    for (var i = 1 ; i < commonFactors.length ; i++){
        if (max < commonFactors[i]){
            max = commonFactors[i];
        }
    }
    return max;
}
/**
 * @method
 * @param {number} a First number
 * @param {number} b Second number
 * @returns The least common multiple of a and b
 * @desc To get the least common multiple of a and b
 */
function LCM(a,b) {
    var MCFA = MCF(a,b);
    a /= MCFA;
    b /= MCFA;
    return (a * b * MCFA);
}
/**
 * @method
 * @param {number} a First number
 * @param {number} b Second number
 * @returns The common factors of a and b
 * @desc To get the common factors of a and b
 */
function commonFactor(a,b) {
    var commonFactors = [];
    for (var i in a){
        for (var j in b){
            if (a[i] == b[j]){
                commonFactors.push(a[i]);
                break;
            }
        }
    }
    return commonFactors;
}
/**
 * @method
 * @param {number} n Number
 * @returns The primes factor of number
 * @desc To get the primes factor of number
 */
function primeFact(n) {
   var primeNumbers = [];
   var arr = prime(Math.sqrt(n));
   for (var i in arr){
       if (n % i == 0){
           primeNumbers.push(arr[i]);
           n /= i;
       }
   }
   if (n != 1){
       primeNumbers.push(n);
   }
   return primeNumbers;
}
/**
 * @method
 * @param {number} max Number
 * @returns The primes(2~max)
 * @desc To get the primes(2~max)
 */
function prime(max) {
    var primes = [];
    for (var i = 2 ; i < max ; i++){
        if (isPrime(i)){
            primes.push(i);
        }
    }
    return primes;
}
/**
 * @method
 * @param {number} n Number
 * @returns bool the number is prime
 * @desc To get the number is prime
 */
function isPrime(n) {
    var yes = true;
    for (var i = 2 ; i < Math.sqrt(n) ; i++){
        if (n % i == 0){
            yes = false;
            break;
        }
    }
    return yes;
}

module.exports.fractions = fractions;
module.exports.expression = expression;
module.exports.setInf = setInf;
module.exports.sigma = sigma;
module.exports.MCF = MCF;
module.exports.LCM = LCM;
module.exports.commonFactor = commonFactor;
module.exports.primeFact = primeFact;
module.exports.prime = prime;
module.exports.isPrime = isPrime;