/**
 * Created by jimiz on 2017/3/28.
 * 经验证：10000次四舍五入，用num.toFixed为15毫秒，用roundTo为47毫秒，速度低一些，但可以接受
 * 另：经手工验证，用num.toString(2)将十进制浮点数转换为二进制浮点数时，最后一位有错误的情况出现，例子（10.0311）
 *
 * zhangyin 2018-02-28
 * 采用重复一次四舍五入解决浮点精度误差后，10000次roundTo的时间为94毫秒。
 *
 * zhangyin 2018-04-28
 * 因为js中的浮点数本身就有误差，经过运算后误差被放大，用加尾数的方式也不能消除。所以必须采用另一种思路。
 * 考虑误差会被放大，因此放弃一位有效位数来消除误差，二进制有效位数50位，十进制有效位数15位。
 * 原重复四舍五入的方法有缺陷，放弃。
 * 10000次roundTo时间恢复到47毫秒
 */

let scMathUtil = {
    innerRoundTo: function(num, digit){
        let lFactor = Math.pow(10, digit);
        let fOffSet = 0.5;
        let sign = '';
        // 处理符号
        if (num < 0){
            sign = '-';
            num = Math.abs(num);
        }
        // 计算
        let result = Math.floor((num / lFactor) + fOffSet).toString();
        let iLength = result.length;
        // 因为数值被转为整数计算，当目标位数在小数点后，且数值小于0，需要补齐前面的位数
        if (iLength < -digit){
            result = this.zeroString(-digit) + result;
        }
        // 当目标位数在小数点前，需要补齐后面的位数
        else if ((digit > 0) && (iLength < digit)){
            result = result + this.zeroString(digit);
        }
        iLength = result.length;
        // 获得小数点前的数字
        let r1 = result.substring(0, iLength + digit);
        // 获得小数点后的数字
        let r2 = result.substring(iLength + digit, iLength);
        // 拼出完整结果
        return Number(sign + r1 + '.' + r2);
    },
    // 原来直接用num.toString(2)，如果小数段最后位数是0，会被舍掉，导致进位计算bug
    // 改为自己计算二进制，固定为53位。
    // 经验证速度没有差别
    // 另：经手工验证，用num.toString(2)将十进制浮点数转换为二进制浮点数时，最后一位有错误的情况出现，例子（10.0311）
    floatToBin: function(num) {
        let sign = '';
        let dNum = num;
        // 符号位
        if (num < 0) {
            sign = '-';
            dNum = -num;
        };
        // 解析整数段
        let iNum = Math.floor(dNum);
        let iFactor;
        let sResult1 = '';
        // 计算二进制整数段
        while (iNum > 0){
            iFactor = iNum % 2;
            iNum = Math.floor(iNum / 2);
            sResult1 = iFactor + sResult1;
        }
        // 判断是否有整数段
        let bIntZero = sResult1 === '';
        if (bIntZero){
            sResult1 = '0';
        }
        // 解析小数段
        let fNum = dNum - Math.floor(dNum);
        let sResult2 = '';
        if (fNum > 0){
            // 双精度浮点数，尾数总长52位，因为第一位总是1，存储时已经被隐藏，所以有效位数为53位
            // 由于js未对浮点数做优化，所以在有运算的情况下，误差会被放大，因此放弃一位有效位数来消除误差，二进制有效位数50位，十进制有效位数15位
            const floatLength = 50;

            let iLength;
            // js的bug，浮点数直接取小数可能不能获得精确值，只有转成字符串，截取字符串中的小数段
            let sNum = dNum.toString(10);
            let iDot = sNum.indexOf('.');
            sNum = '0' + sNum.substring(iDot, sNum.length);
            fNum = Number(sNum);
            // 有整数段，则小数位数为全部位数-整数位数
            if (!bIntZero){
                iLength = floatLength - sResult1.length;
            }
            else{
                iLength = floatLength;
            }
            // 计算二进制小数段
            while (iLength > 0){
                fNum = fNum * 2;
                iFactor = Math.floor(fNum);
                fNum = fNum % 1;
                sResult2 = sResult2 + iFactor;
                if (iFactor > 0){
                    bIntZero = false;
                }
                if (bIntZero && (iFactor === 0)){
                    continue;
                }
                iLength--;
            }
        }
        return sign + sResult1 + '.' + sResult2;
    },
    binToFloat: function(bin) {
        let result = 0;
        let iLength = bin.length;
        let sign = '';
        if (iLength > 0 && bin[0]==='-'){
            sign = '-';
            bin = bin.substring(1, iLength);
        }
        iLength = bin.length;
        let iDot = bin.indexOf('.');
        if (iDot >= 0) {
            for (let i = 0; i < iLength; i++) {
                let num = Number(bin[i]);
                let idx = iDot - i;
                if (idx === 0) {
                    continue
                };
                if (idx > 0) {
                    idx -= 1
                };
                let r = Math.pow(2, idx);
                result += num * r;
            }
        }
        else {
            result = parseInt(bin, 2);
        };
        return sign + result;
    },
    zeroString: function(length){
        let result = '';
        for (let i = 0; i < length; i++){
            result = result + '0';
        };
        return result;
    },    
    incMantissa: function(bin){
        let result = bin;
        let iDot = bin.indexOf('.');
        if (iDot < 0){return result};
        let iLength = bin.length;
        iLength = bin.length;
        for (let i = iLength - 1; i > iDot; i--){
            let num = Number(bin[i]);
            if (num === 0){
                num = 1;
                let bin1 = bin.substring(0, i);
                let bin2 = this.zeroString(iLength - (i + 1));//bin.substring(i + 1, iLength);
                result = bin1 + num.toString() + bin2;
                break;
            }

        };
        return result;
    },

    roundTo: function(num, digit){
        let me = this;
        return me.innerRoundTo(me.binToFloat(me.incMantissa(me.floatToBin(num))), digit);
    },
    isNumber : function (obj) {
        return obj === +obj;
    },

    floatLength:function(num){
        let lenght = 0;
        let str =`${num}`;//me.isNumber(num)?num+"":num;
        let sub = str.split(".")
        if(sub.length == 2){
            return sub[1].length;
        }
        return lenght
    },

    innerRound:function(num,lenght,decimal){
        let value;
        let pre = 1;
        if(lenght === 0) return num;
        if(num<0) pre = -1;//负数的时候先变成正数
        num = num*pre;
        let n = Math.pow(10,lenght);
        value = Math.round(num * n)
        if(lenght <= decimal){
            return value/n*pre
        }else{
            value = Math.round(value/Math.pow(10,lenght-decimal))
            return value/Math.pow(10,decimal)*pre
        }
    },

    roundForObj:function(obj,decimal){
        let me = this;
        if(obj === undefined || obj === null || isNaN(obj)) return 0;
        let lenght = 10;
        let value;
        if(me.isNumber(obj)){
            value =  me.innerRound(obj,lenght,decimal);
        }else{
            value = me.innerRound(Number(obj),lenght,decimal);
        }
        return value;
    },
    roundToString:function(obj,decimal){
        let me = this;
        let value = me.roundForObj(obj,decimal);
        return value.toFixed(decimal);
    },
    isNumOrFormula:function (text) {
        let value = Number(text);
        if (!value) {
            try {
                let exp = new Expression('');
                exp.Expression(text);
                value = Number(exp.Evaluate());
            } catch (error) {
                value = null;
            }
        }
        if(text==null||text==""){
            value = null;
        }
        return value;
    },
    isDef:function (v) {
        return v !== undefined && v !== null;
    },
    //获取ID引用
    getFIDArr: function (exp) {
        let fidRex = /@[\d,a-z,A-Z,-]{36}/g;
        let fidArr = exp.match(fidRex);
        return this.isDef(fidArr) ? fidArr : [];
    }
};

Number.prototype.toDecimal = function (ADigit) {
    //return parseFloat(this.toFixed(ADigit));
    digit = ((ADigit!=null||ADigit!=undefined) && typeof(ADigit) === 'number' && Number.isInteger(ADigit) && ADigit >= 0) ? -ADigit : -2;
    // var s = scMathUtil.roundTo(this, digit);
    // console.log('Number: ' + this + '   Digit: ' + digit + '    Result: ' + s);
    // return parseFloat(s);
    //scMathUtil.roundTo(this, digit);
    return scMathUtil.roundForObj(this, -digit); 
};