package com.dxhy.common.util;

import java.math.BigDecimal;

import org.apache.commons.lang3.StringUtils;

/**
 * 
 * 数字运算工具类,当前提供的共方法解决问题如下： 1、解决浮点数加、减、乘、除运算丢失精度的问题
 *
 * @author jiaohongyang
 * @date 2018-3-16
 * 
 */

public class MathUtil {

    /**
     * 默认运算精度，2位小数 根据需求传入值
     */
    private static final int DEFAULT_SCALE = 2;

    /**
     * 默认运算精度，2位小数 根据需求传入值
     */
    private static final int ROUND_MAX = 8;

    /**
     * 默认运算精度，2位小数 根据需求传入值
     */
    private static final int ROUND_MIN = 0;

    /**
     * 默认舍入模式 根据需求传入值 ROUND_CEILING 向正无穷方向舍入 ROUND_DOWN 向零方向舍入 ROUND_FLOOR 向负无穷方向舍入 ROUND_HALF_DOWN
     * 向（距离）最近的一边舍入，除非两边（的距离）是相等 如果是这样，向下舍入，例如1.55 保留一位小数结果为1.5 ROUND_HALF_EVEN 向（距离）最近的一边舍入，除非两边（的距离）是相等,
     * 如果保留位数是奇数，使用ROUND_HALF_UP， 如果保留位数是偶数，使用ROUND_HALF_DOWN ROUND_HALF_UP 向（距离）最近的一边舍入，除非两边（的距离）是相等,
     * 如果是这样，向上舍入，1.55保留一位小数结果为1.6 ROUND_UNNECESSARY 计算结果是精确的，不需要舍入模式
     * 
     * ROUND_UP 向远离0的方向舍入
     * 
     */
    private static final int DEFAULT_ROUND_MODE = BigDecimal.ROUND_HALF_UP;

    /**
     * 提供精确的加法运算 为了精确计算，用String来够造BigDecimal
     * 
     * @param v1
     *            参数1
     * @param v2
     *            参数2 两个参数加和，以字符串格式返回
     */
    public static String add(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2).toPlainString();
    }

    /**
     * 提供精确的减法运算 为了精确计算，用String来够造BigDecimal
     *
     * @param v1
     *            参数1
     * @param v2
     *            参数2 两个参数差v1-v2，以字符串格式返回
     */
    public static String sub(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2).toPlainString();
    }

    /**
     * 提供精确的乘法运算
     * 
     * @param v1
     *            参数1
     * @param v2
     *            参数2 两个参数的数学积，以字符串格式返回
     */

    public static String multi(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2).toPlainString();
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，则”精度“、”舍入模式“采用程序默认值
     *
     * @param v1
     *            参数1
     * @param v2
     *            参数2 两个参数的商v1/v2，以字符串格式返回
     */
    public static String divide(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.divide(b2, DEFAULT_SCALE, DEFAULT_ROUND_MODE).toPlainString();
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，则”舍入模式“采用程序默认值
     *
     * @param v1
     *            参数1
     * @param v2
     *            参数2
     * @param scale
     *            表示需要精确到小数点以后几位，传入不合法值时采用程序默认值 两个参数的商v1/v2，以字符串格式返回
     */
    public static String divide(String v1, String v2, int scale) {
        // 传入不合法值时采用程序默认值
        if (scale < 0) {
            scale = DEFAULT_SCALE;
        }

        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.divide(b2, scale, DEFAULT_ROUND_MODE).toPlainString();
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，则指定”精度“、”舍入模式“
     *
     * @param v1
     *            参数1
     * @param v2
     *            参数2
     * @param scale
     *            表示需要精确到小数点以后几位，传入不合法值时采用程序默认值
     * @param roundMode
     *            表示用户指定的舍入模式，传入不合法值时采用程序默认值 两个参数的商v1/v2，以字符串格式返回
     */
    public static String divide(String v1, String v2, int scale, int roundMode) {
        // 传入不合法值时采用程序默认值
        if (scale < 0) {
            scale = DEFAULT_SCALE;
        }

        // 传入不合法值时采用程序默认值
        boolean roundModeOk = roundMode < MathUtil.ROUND_MIN || roundMode > MathUtil.ROUND_MAX;
        if (roundModeOk) {
            roundMode = DEFAULT_ROUND_MODE;
        }

        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.divide(b2, scale, roundMode).toPlainString();
    }

    /**
     * 提供精确的小数位四舍五入处理，采用程序默认的舍入模式
     * 
     * @param v
     *            需要四舍五入的数字 ，传入不合法值时采用程序默认值
     * 
     *            四舍五入后的结果，以字符串格式返回
     */
    public static String round(String v) {
        if (StringUtils.isNotBlank(v)) {
            return round(v, DEFAULT_SCALE, DEFAULT_ROUND_MODE);
        } else {
            return "0";
        }

    }

    /**
     * 提供精确的小数位四舍五入处理，采用程序默认的舍入模式
     * 
     * @param v
     *            需要四舍五入的数字 ，传入不合法值时采用程序默认值
     * @param scale
     *            小数点后保留几位
     * 
     *            四舍五入后的结果，以字符串格式返回
     */
    public static String round(String v, int scale) {

        // 传入不合法值时采用程序默认值
        if (scale < 0) {
            scale = DEFAULT_SCALE;
        }

        return round(v, scale, DEFAULT_ROUND_MODE);
    }

    /**
     * 提供精确的小数位四舍五入处理
     * 
     * @param v
     *            需要四舍五入的数字
     * 
     * @param scale
     *            小数点后保留几位，传入不合法值时采用程序默认值
     * @param roundMode
     *            指定的舍入模式，传入不合法值时采用程序默认值
     * 
     *            四舍五入后的结果，以字符串格式返回
     */
    public static String round(String v, int scale, int roundMode) {

        // 传入不合法值时采用程序默认值
        if (scale < 0) {
            scale = DEFAULT_SCALE;
        }

        // 传入不合法值时采用程序默认值
        boolean roundModeOk = roundMode < MathUtil.ROUND_MIN || roundMode > MathUtil.ROUND_MAX;
        if (roundModeOk) {
            roundMode = DEFAULT_ROUND_MODE;
        }

        BigDecimal b = new BigDecimal(v);
        return b.setScale(scale, roundMode).toPlainString();
    }

    public static BigDecimal transToBigDecimal(String v) {
        return new BigDecimal(v);
    }
}
