Package eu.bges

Source Code of eu.bges.MathUtil

/*
* Copyright 2014 Matthias Braun, Martin Gangl
*
* This library is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.bges;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Objects;

/**
* Constants and utility functions for dealing with numbers.
* <p>
* Enable strict floating point calculations (in accordance with <a
* href="https://en.wikipedia.org/wiki/IEEE_754"> IEEE 754</a>) to ensure
* portability between JVMs.
*
* @author Matthias Braun
* @author Martin Gangl
*
*/
public final strictfp class MathUtil {

  /**
   * Matches a single hexadecimal digit.
   */
  public static final String HEX_REGEX = "[0-9a-fA-F]";
  /**
   * Use this number to signal that the value is unknown
   */
  public static final BigDecimal UNKNOWN_VAL = BigDecimal
      .valueOf(-Double.MAX_VALUE);
  /**
   * This scale is used in {@link #div(int, int)} to round the result.
   */
  public static final int DEFAULT_SCALE = 10;

  private static final String NO_NULL_PARAMS = "Only non-null input is allowed";

  /** This is a utility class not meant to be instantiated by others. */
  private MathUtil() {
  }

  /**
   * Converts the string representation of a binary number to its hexadecimal
   * equivalent.
   * <p>
   * If {@code omitLeadingZeros} is false, the leading zeros of {@code binary}
   * are converted to hex as well.
   * <p>
   * Examples:
   *
   * <pre>
   * binToHex("1111", true) → "F"
   * binToHex("0001", true) → "1"
   * binToHex("0000 0001", false) → "01"
   * binToHex("0 0001", false) → "01"
   * binToHex("1101 0100 0101 1000 1001", true) → "D4589"
   * binToHex("0 0000 0011 0110", false) → "0036"
   * </pre>
   *
   * @param binary
   *            string containing a binary coded value (little endian)
   * @param omitLeadingZeros
   *            whether the resulting hex should not have the leading zeros of
   *            {@code binary}
   * @return a hexadecimal representation of the given binary string
   * @throws NullPointerException
   *             if {@code binary} is null
   * @throws NumberFormatException
   *             if {@code binary} does not represent a binary number
   */
  public static String binToHex(final String binary,
      final boolean omitLeadingZeros) {

    Objects.requireNonNull(binary, NO_NULL_PARAMS);

    final String binNoSpace = binary.replaceAll("\\s+", "");

    // Convert binary to integer
    final BigInteger integer = new BigInteger(binNoSpace, 2);

    // Convert to uppercase hex (radix sixteen); no leading zeros
    final StringBuilder hex = new StringBuilder(integer.toString(16)
        .toUpperCase(Locale.ENGLISH));

    // Prepend leading zeroes to the hex string
    if (!omitLeadingZeros) {
      /*
       * Get a binary string with a complete last nibble but no further
       * leading zeros
       */
      final String binWithoutLeadZeros = hexToBin(hex.toString(), true);

      final int nrOfLeadingBinZeros = binNoSpace.length()
          - binWithoutLeadZeros.length();

      // Nr of complete nibbles that are all zero
      int nrOfLeadingHexZeroes = nrOfLeadingBinZeros / 4;

      // Last nibble is incomplete --> add another zero to the result
      if (nrOfLeadingBinZeros % 4 != 0) {
        nrOfLeadingHexZeroes++;
      }
      for (int i = 0; i < nrOfLeadingHexZeroes; i++) {
        hex.insert(0, '0');
      }
    }
    return hex.toString();
  }

  /**
   * Converts a boolean {@code value} to a short.
   *
   * @param value
   *            boolean value to be converted
   * @return 1 if true; 0 if false
   */
  public static short booleanToShort(final boolean value) {
    if (value) {
      return 1;
    } else {
      return 0;
    }
  }

  /**
   * Converts a decimal number to hexadecimal and prepends zeroes if the hex
   * number has less digits than {@code minNrOfDigits}.
   * <p>
   * This is useful when converting RGB values (ranging from 0 to 255) to
   * their hexadecimal representation that need to have six digits in total.
   *
   * @param dec
   *            the decimal number to convert
   * @param minNrOfDigits
   *            the minimum number of digits the resulting hex number should
   *            have
   * @return the converted and padded number in hexadecimal (all upper case)
   */
  public static String decToPaddedHex(final long dec, final int minNrOfDigits) {
    // Convert the decimal to hex and put it in a string builder
    final StringBuilder builder = new StringBuilder(Long.toHexString(dec));

    final int zeroesToPad = minNrOfDigits - builder.length();
    // Prepend some zeroes
    for (int i = 0; i < zeroesToPad; i++) {
      builder.insert(0, "0");
    }
    return builder.toString().toUpperCase(Locale.ENGLISH);
  }

  /**
   * Divides two integers.
   * <p>
   * If the result has more than {@value #DEFAULT_SCALE} digits after the
   * decimal point, {@link RoundingMode#HALF_UP} is used.
   *
   * @param dividend
   *            integer that's divided by the divisor
   * @param divisor
   *            integer that divides the dividend
   * @return the quotient of {@code dividend} and {@code divisor} as a
   *         {@link BigDecimal} with no trailing zeros
   * @throws ArithmeticException
   *             if {@code divisor} is zero
   */
  public static BigDecimal div(final int dividend, final int divisor) {
    return div(dividend, divisor, DEFAULT_SCALE);
  }

  /**
   * Divides two integers.
   * <p>
   * If the result exceeds the specified {@code scale},
   * {@link RoundingMode#HALF_UP} is used.
   *
   * @param dividend
   *            integer that's divided by the divisor
   * @param divisor
   *            integer that divides the dividend
   * @param scale
   *            round the result to this place after the comma
   * @return the quotient of {@code dividend} and {@code divisor} as a
   *         {@link BigDecimal} with no trailing zeros
   * @throws ArithmeticException
   *             if {@code divisor} is zero
   */
  public static BigDecimal div(final int dividend, final int divisor,
      final int scale) {
    final BigDecimal bigDividend = BigDecimal.valueOf(dividend);
    final BigDecimal bigDivisor = BigDecimal.valueOf(divisor);
    final BigDecimal quotient = bigDividend.divide(bigDivisor, scale,
        RoundingMode.HALF_UP);
    return quotient.stripTrailingZeros();

  }

  /**
   * Converts the string representation of a hexadecimal number to its binary
   * equivalent.
   * <p>
   * If {@code completeNibble} is true, zeros are prepended to the resulting
   * string until the last nibble is complete. If false, the returned string
   * has no zeros before the leftmost 1 bit.
   * <p>
   * Examples:
   *
   * <pre>
   * hexToBin("F", false) → "1111"
   * hexToBin("a", false) → "1010"
   * hexToBin("1", false) → "1"
   * hexToBin("1", true) → "0001"
   * hexToBin("7A BC 90 1F", true) → "01111010101111001001000000011111"
   * </pre>
   *
   * @param hex
   *            string containing a hexadecimal coded value (little endian)
   * @param completeNibble
   *            whether the last half byte is prepended with zeros
   * @return a binary representation of the given hexadecimal string
   * @throws NullPointerException
   *             if {@code hex} is null
   * @throws NumberFormatException
   *             if {@code hex} does not represent a hexadecimal number
   */
  public static String hexToBin(final String hex, final boolean completeNibble) {

    Objects.requireNonNull(hex, NO_NULL_PARAMS);

    final String hexNoSpace = hex.replaceAll("\\s+", "");

    // Convert hex to integer
    final BigInteger integer = new BigInteger(hexNoSpace, 16);
    // Convert to binary (radix two); has no leading zeros
    final StringBuilder bin = new StringBuilder(integer.toString(2));

    if (completeNibble) {
      // Insert zeros until the nibble is complete
      while (bin.length() % 4 != 0) {
        bin.insert(0, '0');
      }
    }
    return bin.toString();
  }

  /**
   * Gets the biggest number among some integers.
   *
   * @param firstNr
   *            there has to be at least one number to get the maximum from
   * @param rest
   *            the other numbers
   * @return the greatest among {@code firstNr} and the {@code rest}
   */
  public static int max(final int firstNr, final int... rest) {
    int currMaximum = firstNr;
    for (final int i : rest) {
      if (i > currMaximum) {
        currMaximum = i;
      }
    }
    return currMaximum;
  }

  /**
   * Gets the smallest number among some integers.
   *
   * @param firstNr
   *            there has to be at least one number to get the minimum from
   * @param rest
   *            the other numbers
   * @return the smallest among {@code firstNr} and the {@code rest}
   */
  public static int min(final int firstNr, final int... rest) {
    int currMinimum = firstNr;
    for (final int i : rest) {
      if (i < currMinimum) {
        currMinimum = i;
      }
    }
    return currMinimum;
  }

  /**
   * Parses a {@link BigDecimal} from a {@code string}.
   * <p>
   * Uses the rightmost comma or dot as the decimal point.
   * <p>
   * If the string is empty or consists of whitespace, {@link #UNKNOWN_VAL} is
   * returned.
   * <p>
   * Acceptable input:
   *
   * <pre>
   * 1.005,4
   * 1 000 123
   * 1,005,49
   * 1,005,0500,4
   * 1,,,,005.4
   * </pre>
   *
   * @param string
   *            to be parsed (comma symbol . and , are allowed; preceding
   *            occurrences of . or , will be ignored)
   * @return a double value representation of the current string
   * @throws NumberFormatException
   *             if an non-parsable string has been given (e.g., it contains
   *             letters or is null)
   */
  public static BigDecimal parseDec(final String string) {
    if (string == null) {
      throw new NumberFormatException("Can't parse null");
    }

    final String noSpace = string.replaceAll("\\s+", "");
    final String noSpaceAndOnlyDots = noSpace.replace(',', '.');
    if (noSpaceAndOnlyDots.isEmpty()) {
      return UNKNOWN_VAL;
    }

    boolean decimalPointFound = false;

    // Number with commas and dots removed except for the first one
    final StringBuilder cleanNr = new StringBuilder(
        noSpaceAndOnlyDots.length());

    // Create the clean number by parsing the input backwards
    for (int i = noSpaceAndOnlyDots.length() - 1; i >= 0; i--) {

      final char currChar = noSpaceAndOnlyDots.charAt(i);
      if (currChar == '.') {
        // The rightmost decimal point is accepted, others are ignored
        if (!decimalPointFound) {
          decimalPointFound = true;
          cleanNr.append(currChar);
        }
      } else {
        // Current char is not a decimal point
        cleanNr.append(currChar);
      }
    }

    return new BigDecimal(cleanNr.reverse().toString());
  }

  /**
   * Parses a long from a string.
   * <p>
   * This works if the string represents a double, a long or an integer.
   * <p>
   * If the string represents a double, it's rounded.
   *
   * @param str
   *            the string to parse
   * @return the parsed long
   * @throws NumberFormatException
   *             if {@code str} does not represent a number
   * @throws NullPointerException
   *             if {@code str} is null
   */
  public static long parseToLong(final String str) {
    Objects.requireNonNull(str, NO_NULL_PARAMS);

    final double d = Double.parseDouble(str);

    // Rounded to a long
    return Math.round(d);
  }

  /**
   * Rounds a double to a certain {@code decimalPlace.}
   * <p>
   * Examples:
   *
   * <pre>
   * round(1.46, 1) → 1.5
   * round(1.005, 2) → 1.01
   * round(123.0054, 3) → 123.005
   * </pre>
   *
   * Note that {@code double}'s precision is about 16 decimal digits after the
   * comma: Rounding to a decimal place after that will probably yield
   * unexpected results.
   *
   * @param roundThis
   *            the double that is rounded
   * @param decimalPlace
   *            the decimal place to which the number is rounded
   * @return the rounded number as a {@link BigDecimal}
   */
  public static BigDecimal round(final double roundThis,
      final int decimalPlace) {
    final BigDecimal rounded = BigDecimal.valueOf(roundThis).setScale(
        decimalPlace, RoundingMode.HALF_UP);
    return rounded.stripTrailingZeros();
  }

  /**
   * Converts a short value to boolean.
   * <p>
   * 1 → true; 0 → false
   *
   * @param value
   *            short value to be converted
   * @return true if 1; false if 0
   * @throws IllegalArgumentException
   *             if {@code value} is neither 1 nor 0
   */
  public static boolean shortToBoolean(final short value) {
    if (value != 0 && value != 1) {
      throw new IllegalArgumentException("Input has to be either 1 or 0");
    }
    return value == 1;
  }

  /**
   * Converts a value to its percent representation.
   * <p>
   * The resulting percentage is rounded to three decimal places.
   * <p>
   * The output is locale-dependent: The decimal separator and the position of
   * the comma will vary according to the current locale of the JVM.
   * <p>
   * Example for English (UK) locale:
   *
   * <pre>
   * 0.99 → "99%"
   * 0.053 → "5.3%"
   * 0.053335 → "5.334%"
   * </pre>
   *
   * @param val
   *            to be formatted as a percent value
   * @return a percent representation of the given value
   */
  public static String toPercent(final BigDecimal val) {
    final NumberFormat nf = NumberFormat.getPercentInstance();
    // The default rounding mode is HALF_EVEN
    nf.setRoundingMode(RoundingMode.HALF_UP);
    nf.setMaximumFractionDigits(3);
    return nf.format(val);
  }
}
TOP

Related Classes of eu.bges.MathUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.