judge.mjs

import Decimal from 'decimal.js'
import get from 'lodash-es/get.js'
import isnum from './isnum.mjs'
import isbol from './isbol.mjs'
import preciseNum from './preciseNum.mjs'


function gte(x1, x2) {
    return new Decimal(x1).gte(x2)
}

function gt(x1, x2) {
    return new Decimal(x1).gt(x2)
}

function lte(x1, x2) {
    return new Decimal(x1).lte(x2)
}

function lt(x1, x2) {
    return new Decimal(x1).lt(x2)
}

function eq(x1, x2) {
    return new Decimal(x1).eq(x2)
}

function neq(x1, x2) {
    return !eq(x1, x2)
}

/**
 * 使用指定條件判斷兩數字成立與否
 *
 * Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/judge.test.mjs Github}
 * @memberOf wsemi
 * @param {String|Number} x1 輸入數字字串或數字,若要精準判識得要用字串
 * @param {String} op 輸入操作符號字串,可使用'==='、'!=='、'>='、'<='、'>'、'<'
 * @param {NuString|Number} x2 輸入數字字串或數字,若要精準判識得要用字串
 * @param {Object} [opt={}] 輸設定物件,預設{}
 * @param {Boolean} [opt.withPreciseNum=true] 輸是否自動轉原始精度之數字字串布林值,預設true
 * @returns {Number} 回傳判斷布林值
 * @example
 *
 * let x1
 * let op
 * let x2
 * let b
 *
 * x1 = 0
 * op = '>='
 * x2 = 0
 * b = judge(x1, op, x2, { withPreciseNum: false })
 * console.log(`${x1}, '${op}', ${x2}`, b)
 * // => 0, '>=', 0 true
 *
 * x1 = 1
 * op = '>='
 * x2 = 1
 * b = judge(x1, op, x2, { withPreciseNum: false })
 * console.log(`${x1}, '${op}', ${x2}`, b)
 * // => 1, '>=', 1 true
 *
 * //浮點數誤差
 * x1 = 0.3
 * op = '>='
 * x2 = 0.1 + 0.2
 * b = judge(x1, op, x2, { withPreciseNum: false })
 * console.log(`${x1}, '${op}', ${x2}`, b)
 * // => 0.3, '>=', 0.30000000000000004 false
 *
 * x1 = 0.3
 * op = '>='
 * x2 = 0.1 + 0.2
 * b = judge(x1, op, x2, { withPreciseNum: true }) //default
 * console.log(`${x1}, '${op}', ${x2}`, b)
 * // => 0.3, '>=', 0.30000000000000004 true
 *
 * x1 = 0.3
 * op = '>='
 * x2 = Decimal.sum(0.1, 0.2).toString()
 * b = judge(x1, op, x2, { withPreciseNum: false })
 * console.log(`${x1}, '${op}', ${x2}`, b)
 * // => 0.3, '>=', 0.3 true
 *
 * x1 = Decimal.sum(0.1, 0.2).toString()
 * op = '>='
 * x2 = 0.3
 * b = judge(x1, op, x2, { withPreciseNum: false })
 * console.log(`${x1}, '${op}', ${x2}`, b)
 * // => 0.3, '>=', 0.3 true
 *
 * //加法順序會導致不同的誤差
 * x1 = 4.67 + 7.12 + 94.4
 * op = '>='
 * x2 = 94.4 + 7.12 + 4.67
 * b = judge(x1, op, x2, { withPreciseNum: false })
 * console.log(`${x1}, '${op}', ${x2}`, b)
 * // => 106.19, '>=', 106.19000000000001 false
 *
 * x1 = 4.67 + 7.12 + 94.4
 * op = '>='
 * x2 = 94.4 + 7.12 + 4.67
 * b = judge(x1, op, x2, { withPreciseNum: true }) //default
 * console.log(`${x1}, '${op}', ${x2}`, b)
 * // => 106.19, '>=', 106.19000000000001 true
 *
 * x1 = Decimal.sum(4.67, 7.12, 94.4).toString()
 * op = '>='
 * x2 = Decimal.sum(94.4, 7.12, 4.67).toString()
 * b = judge(x1, op, x2, { withPreciseNum: false })
 * console.log(`${x1}, '${op}', ${x2}`, b)
 * // => 106.19, '>=', 106.19 true
 *
 */
function judge(x1, op, x2, opt = {}) {

    //check
    if (!isnum(x1)) {
        throw new Error(`x1 is not a number`)
    }
    if (!isnum(x2)) {
        throw new Error(`x2 is not a number`)
    }

    //withPreciseNum
    let withPreciseNum = get(opt, 'withPreciseNum')
    if (!isbol(withPreciseNum)) {
        withPreciseNum = true
    }

    //preciseNum
    if (withPreciseNum) {
        x1 = preciseNum(x1)
        x2 = preciseNum(x2)
    }

    if (op === '===') {
        if (x1 === x2) { //預先判斷可加速
            return true
        }
        return eq(x1, x2)
    }
    else if (op === '!==') {
        return neq(x1, x2)
    }
    else if (op === '>=') {
        if (x1 === x2) { //預先判斷可加速
            return true
        }
        return gte(x1, x2)
    }
    else if (op === '>') {
        return gt(x1, x2)
    }
    else if (op === '<=') {
        if (x1 === x2) { //預先判斷可加速
            return true
        }
        return lte(x1, x2)
    }
    else if (op === '<') {
        return lt(x1, x2)
    }
    else {
        console.log(op)
        throw new Error(`invalid op[${op}]`)
    }
}


export default judge