content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to you under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.calcite.util;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  import org.checkerframework.checker.nullness.qual.PolyNull;
  
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.math.RoundingMode;
  import java.text.DecimalFormat;
  import java.text.DecimalFormatSymbols;
  import java.text.NumberFormat;
  import java.util.Locale;
  
  import static org.apache.calcite.linq4j.Nullness.castNonNull;
  
  /**
   * Utility functions for working with numbers.
   */
  public class NumberUtil {
    private NumberUtil() {}
  
    //~ Static fields/initializers ---------------------------------------------
  
    private static final DecimalFormat FLOAT_FORMATTER;
    private static final DecimalFormat DOUBLE_FORMATTER;
    private static final BigInteger[] BIG_INT_TEN_POW;
    private static final BigInteger[] BIG_INT_MIN_UNSCALED;
    private static final BigInteger[] BIG_INT_MAX_UNSCALED;
  
    static {
      // TODO: DecimalFormat uses ROUND_HALF_EVEN, not ROUND_HALF_UP
      // Float: precision of 7 (6 digits after .)
      FLOAT_FORMATTER = decimalFormat("0.######E0");
  
      // Double: precision of 16 (15 digits after .)
      DOUBLE_FORMATTER = decimalFormat("0.###############E0");
  
      BIG_INT_TEN_POW = new BigInteger[20];
      BIG_INT_MIN_UNSCALED = new BigInteger[20];
      BIG_INT_MAX_UNSCALED = new BigInteger[20];
  
      for (int i = 0; i < BIG_INT_TEN_POW.length; i++) {
        BIG_INT_TEN_POW[i] = BigInteger.TEN.pow(i);
        if (i < 19) {
          BIG_INT_MAX_UNSCALED[i] = BIG_INT_TEN_POW[i].subtract(BigInteger.ONE);
          BIG_INT_MIN_UNSCALED[i] = BIG_INT_MAX_UNSCALED[i].negate();
        } else {
          BIG_INT_MAX_UNSCALED[i] = BigInteger.valueOf(Long.MAX_VALUE);
          BIG_INT_MIN_UNSCALED[i] = BigInteger.valueOf(Long.MIN_VALUE);
        }
      }
    }
  
    //~ Methods ----------------------------------------------------------------
  
    /** Creates a format. Locale-independent. */
    public static DecimalFormat decimalFormat(String pattern) {
      return new DecimalFormat(pattern,
          DecimalFormatSymbols.getInstance(Locale.ROOT));
    }
  
    public static BigInteger powTen(int exponent) {
      if ((exponent >= 0) && (exponent < BIG_INT_TEN_POW.length)) {
        return BIG_INT_TEN_POW[exponent];
      } else {
        return BigInteger.TEN.pow(exponent);
      }
    }
  
    public static BigInteger getMaxUnscaled(int precision) {
      return BIG_INT_MAX_UNSCALED[precision];
    }
  
    public static BigInteger getMinUnscaled(int precision) {
      return BIG_INT_MIN_UNSCALED[precision];
    }
  
    /** Sets the scale of a BigDecimal {@code bd} if it is not null;
     * always returns {@code bd}. */
    public static @PolyNull BigDecimal rescaleBigDecimal(@PolyNull BigDecimal bd,
        int scale) {
      if (bd != null) {
        bd = bd.setScale(scale, RoundingMode.HALF_UP);
      }
      return bd;
    }
  
    public static BigDecimal toBigDecimal(Number number, int scale) {
      BigDecimal bd = toBigDecimal(number);
      return rescaleBigDecimal(bd, scale);
    }
  
    /** Converts a number to a BigDecimal with the same value;
     * returns null if and only if the number is null. */
    public static @PolyNull BigDecimal toBigDecimal(@PolyNull Number number) {
      if (number == null) {
        return castNonNull(null);
      }
      if (number instanceof BigDecimal) {
        return (BigDecimal) number;
      } else if ((number instanceof Double)
          || (number instanceof Float)) {
        return BigDecimal.valueOf(number.doubleValue());
      } else if (number instanceof BigInteger) {
        return new BigDecimal((BigInteger) number);
      } else {
        return new BigDecimal(number.longValue());
      }
    }
  
    /** Returns whether a {@link BigDecimal} is a valid Farrago decimal. If a
     * BigDecimal's unscaled value overflows a long, then it is not a valid
     * Farrago decimal. */
    public static boolean isValidDecimal(BigDecimal bd) {
      BigInteger usv = bd.unscaledValue();
      long usvl = usv.longValue();
      return usv.equals(BigInteger.valueOf(usvl));
    }
  
    public static NumberFormat getApproxFormatter(boolean isFloat) {
      return isFloat ? FLOAT_FORMATTER : DOUBLE_FORMATTER;
    }
  
    public static long round(double d) {
      if (d < 0) {
        return (long) (d - 0.5);
      } else {
        return (long) (d + 0.5);
      }
    }
  
    /** Returns the sum of two numbers, or null if either is null. */
    public static @PolyNull Double add(@PolyNull Double a, @PolyNull Double b) {
      if (a == null || b == null) {
        return null;
      }
  
      return a + b;
    }
  
    /** Returns the difference of two numbers,
     * or null if either is null. */
    public static @PolyNull Double subtract(@PolyNull Double a, @PolyNull Double b) {
      if (a == null || b == null) {
        return castNonNull(null);
      }
  
      return a - b;
    }
  
    /** Returns the quotient of two numbers,
     * or null if either is null or the divisor is zero. */
    public static @Nullable Double divide(@Nullable Double a, @Nullable Double b) {
      if ((a == null) || (b == null) || (b == 0D)) {
        return castNonNull(null);
      }
  
      return a / b;
    }
  
    /** Returns the product of two numbers,
     * or null if either is null. */
    public static @PolyNull Double multiply(@PolyNull Double a, @PolyNull Double b) {
      if (a == null || b == null) {
        return castNonNull(null);
      }
  
      return a * b;
    }
  
    /** Like {@link Math#min} but null safe;
     * returns the lesser of two numbers,
     * ignoring numbers that are null,
     * or null if both are null. */
    public static @PolyNull Double min(@PolyNull Double a, @PolyNull Double b) {
      if (a == null) {
        return b;
      } else if (b == null) {
        return a;
      } else {
        return Math.min(a, b);
      }
    }
  
    /** Like {@link Math#max} but null safe;
     * returns the greater of two numbers,
     * or null if either is null. */
    public static @PolyNull Double max(@PolyNull Double a, @PolyNull Double b) {
      if (a == null || b == null) {
        return castNonNull(null);
      }
      return Math.max(a, b);
    }
  }
  