content: package com.alibaba.fastjson2.util;
  
  import java.lang.invoke.*;
  import java.math.BigInteger;
  import java.nio.charset.StandardCharsets;
  
  import static com.alibaba.fastjson2.util.IOUtils.*;
  import static com.alibaba.fastjson2.util.JDKUtils.ANDROID;
  
  /**
   * Author: wangy
   */
  public final class NumberUtils {
      @FunctionalInterface
      interface LongBiFunction {
          long multiplyHigh(long x, long y);
      }
  
      static long multiplyHigh(long x, long y) {
          long x1 = x >> 32;
          long x2 = x & 0xFFFFFFFFL;
          long y1 = y >> 32;
          long y2 = y & 0xFFFFFFFFL;
  
          long z2 = x2 * y2;
          long t = x1 * y2 + (z2 >>> 32);
          long z1 = t & 0xFFFFFFFFL;
          long z0 = t >> 32;
          z1 += x2 * y1;
  
          return x1 * y1 + z0 + (z1 >> 32);
      }
  
      static final LongBiFunction MULTIPLY_HIGH;
  
      static {
          LongBiFunction function = null;
          if (JDKUtils.JVM_VERSION > 8 && !ANDROID) {
              try {
                  MethodHandles.Lookup lookup = JDKUtils.trustedLookup(NumberUtils.class);
                  MethodType methodType = MethodType.methodType(long.class, long.class, long.class);
                  MethodHandle methodHandle = lookup.findStatic(Math.class, "multiplyHigh", methodType);
                  CallSite callSite = LambdaMetafactory.metafactory(
                          lookup,
                          "multiplyHigh",
                          MethodType.methodType(LongBiFunction.class),
                          methodType,
                          methodHandle,
                          methodType
                  );
                  function = (LongBiFunction) callSite.getTarget().invokeExact();
              } catch (Throwable ignored) {
                  // ignored
              }
          }
          if (function == null) {
              function = NumberUtils::multiplyHigh;
          }
          MULTIPLY_HIGH = function;
      }
  
      private NumberUtils() {
      }
  
      static final long INFI;
      static final long NITY;
      static final long INFINITY;
  
      static {
          String str = "Infinity";
          INFINITY = IOUtils.getLongUnaligned(str.getBytes(StandardCharsets.ISO_8859_1), 0);
          char[] chars = str.toCharArray();
          INFI = IOUtils.getLongUnaligned(chars, 0);
          NITY = IOUtils.getLongUnaligned(chars, 4);
      }
  
      static final double[] POSITIVE_DECIMAL_POWER = new double[325];
      static final double[] NEGATIVE_DECIMAL_POWER = new double[325];
      static final long[] POW10_LONG_VALUES = new long[]{
              10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000L, 100000000000L, 1000000000000L, 10000000000000L, 100000000000000L, 1000000000000000L, 10000000000000000L, 100000000000000000L, 1000000000000000000L, 9223372036854775807L
      };
  
      static final long[] POW5_LONG_VALUES = new long[27];
      static final BigInteger[] POW5_BI_VALUES = new BigInteger[343];
  
      static {
          // e0 ~ e360(e306)
          for (int i = 0, len = POSITIVE_DECIMAL_POWER.length; i < len; ++i) {
              POSITIVE_DECIMAL_POWER[i] = Double.valueOf("1.0E" + i);
              NEGATIVE_DECIMAL_POWER[i] = Double.valueOf("1.0E-" + i);
          }
          // 4.9e-324
          NEGATIVE_DECIMAL_POWER[NEGATIVE_DECIMAL_POWER.length - 1] = Double.MIN_VALUE;
  
          long val = 1;
          for (int i = 0; i < POW5_LONG_VALUES.length; ++i) {
              POW5_LONG_VALUES[i] = val;
              val *= 5;
          }
  
          BigInteger five = BigInteger.valueOf(5);
          POW5_BI_VALUES[0] = BigInteger.ONE;
          for (int i = 1; i < POW5_BI_VALUES.length; ++i) {
              BigInteger pow5Value = five.pow(i);
              POW5_BI_VALUES[i] = pow5Value;
          }
      }
  
      static final int MOD_DOUBLE_EXP = (1 << 11) - 1;
      static final long MOD_DOUBLE_MANTISSA = (1L << 52) - 1;
  
      /**
       * multiplyOutput
       *
       * @param x > 0
       * @param y > 0
       * @param shift > 0
       * @return
       */
      static long multiplyHighAndShift(long x, long y, int shift) {
          long H = MULTIPLY_HIGH.multiplyHigh(x, y);
          if (shift >= 64) {
              int sr = shift - 64;
              return H >>> sr;
          }
          long L = x * y;
          return H << (64 - shift) | (L >>> shift);
      }
  
      /**
       * Unsigned algorithm:  (H * 2^64 * 2^n + L * 2^n + H1 * 2^64 + L1) / 2^(n+s) = (H * 2^64 + L + (H1 * 2^64 + L1) / 2^n) / 2^s
       * <p>
       * use BigInteger:  BigInteger.valueOf(pd.y).shiftLeft(n).add(BigInteger.valueOf(f & MASK_32_BITS)).multiply(BigInteger.valueOf(x)).shiftRight(s + n).longValue()
       *
       * @param x 63bits
       * @param y 63bits
       * @param y32 unsigned int32 f(32bits)
       * @param s s > 0
       * @return
       */
      static long multiplyHighAndShift(long x, long y, long y32, int s) {
          int sr = s - 64;
          long H = MULTIPLY_HIGH.multiplyHigh(x, y);
          long L = x * y;
  
          // cal x * f -> H1, L1
          long H1 = MULTIPLY_HIGH.multiplyHigh(x, y32);
          long L1 = x * y32;
  
          // carry = (H1 * 2^64 + L1) / 2^n
          long carry = (H1 << 32) + (L1 >>> 32);
          long L2 = L + carry;
          if ((L | carry) < 0 && ((L & carry) < 0 || L2 >= 0)) {
              ++H;
          }
          L = L2;
          if (sr >= 0) {
              return H >>> sr;
          }
          return H << -sr | (L >>> s);
      }
  
      /**
       * Conversion of ieee floating point numbers to Scientific notation
       *
       * <p> Using the difference estimation method </p>
       * <p> The output may not be the shortest, but the general result is correct </p>
       *
       * @param doubleValue &gt; 0
       */
      public static Scientific doubleToScientific(double doubleValue) {
          if (doubleValue == Double.MIN_VALUE) {
              // Double.MIN_VALUE The minimum double value converted by JDK is 4.9e-324. This method converts it to 5.0e-324.
              // Due to the special value, it is specially processed to be consistent with JDK conversion.
              return Scientific.DOUBLE_MIN;
          }
          long bits = Double.doubleToRawLongBits(doubleValue);
          int e2 = (int) (bits >> 52) & MOD_DOUBLE_EXP;
          long mantissa0 = bits & MOD_DOUBLE_MANTISSA;
          // boolean flagForUp = mantissa0 < MOD_DOUBLE_MANTISSA;
          boolean flagForDown = mantissa0 > 0;
          int e52;
          long output;
          long rawOutput, /*d2, */d3, d4;
          int e10, adl;
          if (e2 > 0) {
              // Double.NaN/Double.POSITIVE_INFINITY/Double.NEGATIVE_INFINITY -> NULL
              if (e2 == 2047) {
                  return Scientific.SCIENTIFIC_NULL;
              }
              mantissa0 = 1L << 52 | mantissa0;
              e52 = e2 - 1075;
          } else {
              if (mantissa0 == 0) {
                  // Dealing with error issues with doubleValue=0.0(-0.0) Or make sure doubleValue is greater than 0 before calling
                  return bits == 0 ? Scientific.ZERO : Scientific.NEGATIVE_ZERO;
              }
              int lz52 = Long.numberOfLeadingZeros(mantissa0) - 11;
              mantissa0 <<= lz52;
              e52 = -1074 - lz52;
          }
          boolean /*tflag = true,*/ accurate = false;
          if (e52 >= 0) {
              ED d = ED.E2_D_A[e52];
              e10 = d.e10;   // e10 > 15
              adl = d.adl;
              // d2 = d.d2;
              d3 = d.d3;
              d4 = d.d4;
              if (d.b && mantissa0 >= d.bv) {
                  if (mantissa0 > d.bv) {
                      ++e10;
                      ++adl;
                  } else {
                      if (doubleValue == POSITIVE_DECIMAL_POWER[e10 + 1]) {
                          return new Scientific(e10 + 1, true);
                      }
                  }
              }
              int o5 = d.o5;  // adl + 2 - e10
              int sb = e52 + o5;
              if (o5 < 0) {
                  // mantissa0 * 2^(e52 + o5) * 5^o5 -> mantissa0 * 2^sb / 5^(-o5)
                  ED5 d5 = ED5.ED5_A[-o5];
                  int rb = sb - 10 - d5.ob;
                  // rawOutput = BigInteger.valueOf(mantissa0).shiftLeft(sb).divide(POW5_BI_VALUES[-o5]).longValue();
                  rawOutput = multiplyHighAndShift(mantissa0 << 10, d5.oy, d5.of, 32 - rb);
                  accurate = o5 == -1 && sb < 11;
              } else {
                  // o5 > 0 -> sb > 0
                  // accurate
                  rawOutput = (mantissa0 * POW5_LONG_VALUES[o5]) << sb;
                  accurate = true;
              }
          } else {
              // e52 >= -1074 -> p5 <= 1074
              int e5 = -e52;
              ED d = ED.E5_D_A[e5];
              e10 = d.e10;
              adl = d.adl;
              // d2 = d.d2;
              d3 = d.d3;
              d4 = d.d4;
              if (d.b && mantissa0 >= d.bv) {
                  if (mantissa0 > d.bv) {
                      ++e10;
                      ++adl;
                  } else {
                      if (e10 >= -1 && doubleValue == POSITIVE_DECIMAL_POWER[e10 + 1]) {
                          return new Scientific(e10 + 1, true);
                      }
                      if (e10 < -1 && doubleValue == NEGATIVE_DECIMAL_POWER[-e10 - 1]) {
                          return new Scientific(e10 + 1, true);
                      }
                  }
              }
  
              int o5 = d.o5; // adl + 2 - e10; // o5 > 0
              int sb = o5 + e52;
              if (sb < 0) {
                  if (o5 < POW5_LONG_VALUES.length) {
                      rawOutput = multiplyHighAndShift(mantissa0, POW5_LONG_VALUES[o5], -sb);
                  } else if (o5 < POW5_LONG_VALUES.length + 4) {
                      rawOutput = multiplyHighAndShift(mantissa0 * POW5_LONG_VALUES[o5 - POW5_LONG_VALUES.length + 1], POW5_LONG_VALUES[POW5_LONG_VALUES.length - 1], -sb);
                  } else {
                      ED5 ed5 = ED5.ED5_A[o5];
                      rawOutput = multiplyHighAndShift(mantissa0 << 10, ed5.y, ed5.f, -(ed5.dfb + sb) + 10);
                  }
              } else {
                  rawOutput = POW5_LONG_VALUES[o5] * mantissa0 << sb;
              }
          }
          if (accurate) {
              rawOutput = rawOutput / 10;
              if (adl == 16) {
                  --adl;
                  rawOutput = (rawOutput + 5) / 10; // rawOutput = rawOutput / 10 + ((rawOutput % 10) >= 5 ? 1 : 0);
              }
              return new Scientific(rawOutput, adl + 2, e10);
          }
  
          // rem <= Actual Rem Value
          long div = rawOutput / 1000, rem = rawOutput - div * 1000;
          long remUp = (10001 - rem * 10) << 1;
          boolean up;
          if ((up = (remUp <= d4)) || ((rem + 1) << (flagForDown ? 1 : 2)) <= d3) {
              output = div + (up ? 1 : 0);
              --adl;
          } else {
              if (flagForDown) {
                  output = (rawOutput + 50) / 100; // rawOutput / 100 + ((rawOutput % 100) >= 50 ? 1 : 0)
              } else {
                  output = (rawOutput + 5) / 10; // rawOutput / 10 + ((rawOutput % 10) >= 5 ? 1 : 0)
                  ++adl;
              }
          }
          return new Scientific(output, adl + 1, e10);
      }
  
      static final char[][] POSITIVE_DECIMAL_POWER_CHARS = new char[325][];
      static final char[][] NEGATIVE_DECIMAL_POWER_CHARS = new char[325][];
  
      static {
          for (int i = 0, len = POSITIVE_DECIMAL_POWER_CHARS.length; i < len; ++i) {
              String positive = "1.0E" + i;
              String negative = "1.0E-" + i;
              POSITIVE_DECIMAL_POWER_CHARS[i] = positive.toCharArray();
              NEGATIVE_DECIMAL_POWER_CHARS[i] = negative.toCharArray();
          }
          NEGATIVE_DECIMAL_POWER_CHARS[NEGATIVE_DECIMAL_POWER_CHARS.length - 1] = "4.9E-324".toCharArray();
      }
  
      public static int writeDouble(byte[] buf, int off, double doubleValue, boolean json) {
          long bits;
          if (doubleValue == 0) {
              bits = Double.doubleToLongBits(doubleValue);
              if (bits == 0x8000000000000000L) {
                  buf[off++] = '-';
              }
              buf[off] = '0';
              IOUtils.putShortUnaligned(buf, off + 1, DOT_ZERO_16);
              return off + 3;
          }
          boolean sign = doubleValue < 0;
          if (sign) {
              if (!json || doubleValue != Double.NEGATIVE_INFINITY) {
                  buf[off++] = '-';
              }
              doubleValue = -doubleValue;
          }
          if (doubleValue == (long) doubleValue) {
              long output = (long) doubleValue;
              int numLength = stringSize(output);
              return writeDecimal(output, numLength, numLength - 1, buf, off);
          }
          Scientific scientific = NumberUtils.doubleToScientific(doubleValue);
          int e10 = scientific.e10;
          if (!scientific.b) {
              return writeDecimal(scientific.output, scientific.count, scientific.e10, buf, off);
          }
          if (scientific == Scientific.SCIENTIFIC_NULL) {
              if (json) {
                  IOUtils.putIntUnaligned(buf, off, IOUtils.NULL_32);
                  return off + 4;
              } else {
                  if (doubleValue == Double.POSITIVE_INFINITY) {
                      IOUtils.putLongUnaligned(buf, off, INFINITY);
                      return off + 8;
                  } else {
                      buf[off] = 'N';
                      buf[off + 1] = 'a';
                      buf[off + 2] = 'N';
                      return off + 3;
                  }
              }
          }
          if (e10 >= 0) {
              char[] chars = POSITIVE_DECIMAL_POWER_CHARS[e10];
              for (char c : chars) {
                  buf[off++] = (byte) c;
              }
              return off;
          } else {
              char[] chars = NEGATIVE_DECIMAL_POWER_CHARS[-e10];
              for (char c : chars) {
                  buf[off++] = (byte) c;
              }
              return off;
          }
      }
  
      public static int writeDouble(char[] buf, int off, double doubleValue, boolean json) {
          long bits;
          if (doubleValue == 0) {
              bits = Double.doubleToLongBits(doubleValue);
              if (bits == 0x8000000000000000L) {
                  buf[off++] = '-';
              }
              buf[off] = '0';
              IOUtils.putIntUnaligned(buf, off + 1, DOT_ZERO_32);
              return off + 3;
          }
          boolean sign = doubleValue < 0;
          if (sign) {
              if (!json || doubleValue != Double.NEGATIVE_INFINITY) {
                  buf[off++] = '-';
              }
              doubleValue = -doubleValue;
          }
  
          if (doubleValue == (long) doubleValue) {
              long output = (long) doubleValue;
              int numLength = stringSize(output);
              return writeDecimal(output, numLength, numLength - 1, buf, off);
          }
  
          Scientific scientific = NumberUtils.doubleToScientific(doubleValue);
          int e10 = scientific.e10;
          if (!scientific.b) {
              return writeDecimal(scientific.output, scientific.count, e10, buf, off);
          }
          if (scientific == Scientific.SCIENTIFIC_NULL) {
              if (json) {
                  IOUtils.putLongUnaligned(buf, off, NULL_64);
                  return off + 4;
              } else {
                  if (doubleValue == Double.POSITIVE_INFINITY) {
                      IOUtils.putLongUnaligned(buf, off, INFI);
                      IOUtils.putLongUnaligned(buf, off + 4, NITY);
                      return off + 8;
                  } else {
                      buf[off] = 'N';
                      buf[off + 1] = 'a';
                      buf[off + 2] = 'N';
                      return off + 3;
                  }
              }
          }
          if (e10 >= 0) {
              char[] chars = POSITIVE_DECIMAL_POWER_CHARS[e10];
              System.arraycopy(chars, 0, buf, off, chars.length);
              return off + chars.length;
          } else {
              char[] chars = NEGATIVE_DECIMAL_POWER_CHARS[-e10];
              System.arraycopy(chars, 0, buf, off, chars.length);
              return off + chars.length;
          }
      }
  
      public static int writeFloat(byte[] buf, int off, float floatValue, boolean json) {
          if (Float.isNaN(floatValue) || floatValue == Float.POSITIVE_INFINITY || floatValue == Float.NEGATIVE_INFINITY) {
              return writeSpecial(buf, off, floatValue, json);
          }
          int bits;
          if (floatValue == 0) {
              bits = Float.floatToIntBits(floatValue);
              if (bits == 0x80000000) {
                  buf[off++] = '-';
              }
              buf[off] = '0';
              IOUtils.putShortUnaligned(buf, off + 1, DOT_ZERO_16);
              return off + 3;
          }
          boolean sign = floatValue < 0;
          if (sign) {
              buf[off++] = '-';
              floatValue = -floatValue;
          }
  
          Scientific scientific = floatToScientific(floatValue);
          return writeDecimal(scientific.output, scientific.count, scientific.e10, buf, off);
      }
  
      public static int writeFloat(char[] buf, int off, float floatValue, boolean json) {
          if (Float.isNaN(floatValue) || floatValue == Float.POSITIVE_INFINITY || floatValue == Float.NEGATIVE_INFINITY) {
              return writeSpecial(buf, off, floatValue, json);
          }
          int bits;
          if (floatValue == 0) {
              bits = Float.floatToIntBits(floatValue);
              if (bits == 0x80000000) {
                  buf[off++] = '-';
              }
              buf[off] = '0';
              IOUtils.putIntUnaligned(buf, off + 1, DOT_ZERO_32); // .0
              return off + 3;
          }
          boolean sign = floatValue < 0;
          if (sign) {
              buf[off++] = '-';
              floatValue = -floatValue;
          }
  
          Scientific scientific = NumberUtils.floatToScientific(floatValue);
          return writeDecimal(scientific.output, scientific.count, scientific.e10, buf, off);
      }
  
      private static int writeSpecial(byte[] buf, int off, float floatValue, boolean json) {
          if (json) {
              IOUtils.putIntUnaligned(buf, off, NULL_32);
              return off + 4;
          }
  
          if (Float.isNaN(floatValue)) {
              buf[off] = 'N';
              buf[off + 1] = 'a';
              buf[off + 2] = 'N';
              return off + 3;
          }
  
          if (floatValue == Float.NEGATIVE_INFINITY) {
              buf[off++] = '-';
          }
  
          IOUtils.putLongUnaligned(buf, off, INFINITY);
          return off + 8;
      }
  
      private static int writeSpecial(char[] buf, int off, float floatValue, boolean json) {
          if (json) {
              IOUtils.putLongUnaligned(buf, off, NULL_64);
              return off + 4;
          }
  
          if (Float.isNaN(floatValue)) {
              buf[off] = 'N';
              buf[off + 1] = 'a';
              buf[off + 2] = 'N';
              return off + 3;
          }
  
          if (floatValue == Float.NEGATIVE_INFINITY) {
              buf[off++] = '-';
          }
  
          IOUtils.putLongUnaligned(buf, off, INFI);
          IOUtils.putLongUnaligned(buf, off + 4, NITY);
          return off + 8;
      }
  
      static final int MOD_FLOAT_EXP = (1 << 8) - 1;
      static final int MOD_FLOAT_MANTISSA = (1 << 23) - 1;
  
      public static Scientific floatToScientific(float floatValue) {
          final int bits = Float.floatToRawIntBits(floatValue);
          int e2 = (bits >> 23) & MOD_FLOAT_EXP;
          int mantissa0 = bits & MOD_FLOAT_MANTISSA;
          boolean nonZeroFlag = mantissa0 > 0;
          int e23;
          long output, rawOutput;
          long d4;
          int e10, adl;
          boolean accurate = false;
          if (e2 > 0) {
              if (e2 == MOD_FLOAT_EXP) {
                  return Scientific.SCIENTIFIC_NULL;
              }
              mantissa0 = 1 << 23 | mantissa0;
              e23 = e2 - 150;   // 1023 - 52
          } else {
              // e2 == 0
              if (mantissa0 == 0) {
                  return bits == 0 ? Scientific.ZERO : Scientific.NEGATIVE_ZERO;
              }
              int l = Integer.numberOfLeadingZeros(mantissa0) - 8;
              mantissa0 <<= l;
              e23 = -149 - l;
          }
          if (e23 >= 0) {
              ED d = EF.E2_F_A[e23];
              e10 = d.e10;
              adl = d.adl;
              d4 = d.d4;
              if (d.b && mantissa0 > d.bv) {
                  ++e10;
                  ++adl;
              }
              int o5 = d.o5 + 6;  // Compared to double (adl + 2 - e10), adding 6 more numbers increases the probability of hitting
              int sb = e23 + o5;
              if (o5 < 0) {
                  // mantissa0 * 2^(e23 + o5) * 5^o5 -> mantissa0 * 2^sb / 5^(-o5)
                  // rawOutput = BigInteger.valueOf(mantissa0).shiftLeft(sb).divide(POW5_BI_VALUES[-o5]).longValue();
                  if (sb < 40) {
                      rawOutput = ((long) mantissa0 << sb) / POW5_LONG_VALUES[-o5];
                  } else {
                      ED5 d5 = ED5.ED5_A[-o5];
                      rawOutput = multiplyHighAndShift((long) mantissa0 << 39, d5.oy, d5.of, 71 + d5.ob - sb);
                  }
              } else {
                  // o5 > 0 -> sb > 0
                  // accurate
                  rawOutput = mantissa0 * POW5_LONG_VALUES[o5] << sb;
                  accurate = true;
              }
          } else {
              // e52 >= -149 -> p5 <= 149
              int e5 = -e23;
              ED d = EF.E5_F_A[e5];
              e10 = d.e10;
              adl = d.adl;
              d4 = d.d4;
              if (d.b && mantissa0 > d.bv) {
                  ++e10;
                  ++adl;
              }
  
              int o5 = d.o5 + 6; // Compared to double (adl + 2 - e10), adding 6 more numbers increases the probability of hitting
              int sb = o5 + e23;
              if (sb < 0) {
                  // todo To be optimized
                  if (o5 < 17) {
                      rawOutput = mantissa0 * POW5_LONG_VALUES[o5] >> -sb;
                  } else if (o5 < POW5_LONG_VALUES.length) {
                      rawOutput = multiplyHighAndShift(mantissa0, POW5_LONG_VALUES[o5], -sb);
                  } else if (o5 < POW5_LONG_VALUES.length + 4) {
                      rawOutput = multiplyHighAndShift(mantissa0 * POW5_LONG_VALUES[o5 - POW5_LONG_VALUES.length + 1], POW5_LONG_VALUES[POW5_LONG_VALUES.length - 1], -sb);
                  } else {
                      ED5 ed5 = ED5.ED5_A[o5];
                      rawOutput = multiplyHighAndShift((long) mantissa0 << 39, ed5.y, ed5.f, -(ed5.dfb + sb) + 39);   // 39 = 63 - 24
                  }
              } else {
                  rawOutput = POW5_LONG_VALUES[o5] * mantissa0 << sb;
                  accurate = true;
              }
          }
  
          if (accurate) {
              // If we pursue performance, we can return it here, but it may return a non-shortest sequence of numbers (the result is correct)
              // rawOutput = MULTIPLY_HIGH.multiplyHigh(rawOutput, 0x6b5fca6af2bd215fL) >> 22; // rawOutput / 10000000;
              // if (adl == 7) {
              //     --adl;
              //     rawOutput = (rawOutput + 5) / 10; // rawOutput = rawOutput / 10 + ((rawOutput % 10) >= 5 ? 1 : 0);
              // }
              // return new Scientific(rawOutput, adl + 2, e10);
          }
  
          if (rawOutput < 1000000000) {
              return new Scientific(MULTIPLY_HIGH.multiplyHigh(rawOutput, 0x6b5fca6af2bd215fL) >> 22, 2, e10); // rawOutput / 10000000
          }
          long div = MULTIPLY_HIGH.multiplyHigh(rawOutput, 0x44b82fa09b5a52ccL) >> 28; // rawOutput / 1000000000;
          long rem = rawOutput - div * 1000000000;
          long remUp = (1000000001 - rem) << 1;
          boolean up = remUp <= d4;
          if (up || ((rem + 1) << (nonZeroFlag ? 1 : 2)) <= d4) {
              output = div + (up ? 1 : 0);
              --adl;
              if (up) {
                  if (POW10_LONG_VALUES[adl] == output) {
                      ++e10;
                      output = 1;
                      adl = 0;
                  }
              }
          } else {
              if (nonZeroFlag) {
                  long div0 = MULTIPLY_HIGH.multiplyHigh(rawOutput, 0x55e63b88c230e77fL) >> 25; // rawOutput / 100000000
                  output = div0 + (rem % 100000000 >= 50000000 ? 1 : 0);
              } else {
                  long div0 = MULTIPLY_HIGH.multiplyHigh(rawOutput, 0x6b5fca6af2bd215fL) >> 22; // rawOutput / 10000000
                  output = div0 + (rem % 10000000 >= 5000000 ? 1 : 0);
                  ++adl;
              }
          }
  
          return new Scientific(output, adl + 1, e10);
      }
  
      static final int[] TWO_DIGITS_32_BITS = new int[100];
      static final short[] TWO_DIGITS_16_BITS = new short[100];
  
      static {
          for (long d1 = 0; d1 < 10; ++d1) {
              for (long d2 = 0; d2 < 10; ++d2) {
                  long intVal64;
                  int intVal32;
                  if (JDKUtils.BIG_ENDIAN) {
                      intVal64 = (d1 + 48) << 16 | (d2 + 48);
                      intVal32 = ((int) d1 + 48) << 8 | ((int) d2 + 48);
                  } else {
                      intVal64 = (d2 + 48) << 16 | (d1 + 48);
                      intVal32 = ((int) d2 + 48) << 8 | ((int) d1 + 48);
                  }
                  int k = (int) (d1 * 10 + d2);
                  TWO_DIGITS_32_BITS[k] = (int) intVal64;
                  TWO_DIGITS_16_BITS[k] = (short) intVal32;
              }
          }
      }
  
      private static int writeDecimal(long value, int digitCnt, int e10, byte[] buf, int off) {
          if ((value & 1) == 0 && value % 5 == 0) {
              while (value % 100 == 0) {
                  digitCnt -= 2;
                  value /= 100;
                  if (digitCnt == 1) {
                      break;
                  }
              }
              if ((value & 1) == 0 && value % 5 == 0) {
                  if (value > 0) {
                      --digitCnt;
                      value /= 10;
                  }
              }
          }
          // Whether to use Scientific notation
          boolean useScientific = e10 < -3 || e10 >= 7; // !((e10 >= -3) && (e10 < 7));
          if (useScientific) {
              if (digitCnt == 1) {
                  buf[off] = (byte) (value + 48);
                  IOUtils.putShortUnaligned(buf, off + 1, DOT_ZERO_16);
                  off += 3; // .0
              } else {
                  int pos = digitCnt - 2;
                  // get the first digit
                  long tl = POW10_LONG_VALUES[pos];
                  int fd = (int) (value / tl);
                  buf[off] = (byte) (fd + 48);
                  buf[off + 1] = '.';
                  off += 2;
  
                  long pointAfter = value - fd * tl;
                  // fill zeros
                  while (--pos > -1 && pointAfter < POW10_LONG_VALUES[pos]) {
                      buf[off++] = '0';
                  }
                  off = IOUtils.writeInt64(buf, off, pointAfter);
              }
              buf[off++] = 'E';
              if (e10 < 0) {
                  buf[off++] = '-';
                  e10 = -e10;
              }
              if (e10 > 99) {
                  int n = (int) (e10 * 1374389535L >> 37); //e10 / 100;
                  buf[off] = (byte) (n + 48);
                  e10 = e10 - n * 100;
                  IOUtils.putShortUnaligned(buf, off + 1, TWO_DIGITS_16_BITS[e10]);
                  off += 3;
              } else {
                  if (e10 > 9) {
                      IOUtils.putShortUnaligned(buf, off, TWO_DIGITS_16_BITS[e10]);
                      off += 2;
                  } else {
                      buf[off++] = (byte) (e10 + 48);
                  }
              }
          } else {
              // for non-scientific notation such as 12345, write a decimal point when size = decimalExp.
              if (e10 < 0) {
                  // -1/-2/-3
                  IOUtils.putShortUnaligned(buf, off, ZERO_DOT_16); // 0.
                  off += 2;
                  if (e10 == -2) {
                      buf[off++] = '0';
                  } else if (e10 == -3) {
                      IOUtils.putShortUnaligned(buf, off, ZERO_ZERO_16); // 00
                      off += 2;
                  }
                  off = writeInt64(buf, off, value);
              } else {
                  // 0 - 6
                  int decimalPointPos = (digitCnt - 1) - e10;
                  if (decimalPointPos > 0) {
                      int pos = decimalPointPos - 1;
                      long tl = POW10_LONG_VALUES[pos];
                      int pointBefore = (int) (value / tl);
                      off = writeInt32(buf, off, pointBefore);
                      buf[off++] = '.';
                      long pointAfter = value - pointBefore * tl;
                      // fill zeros
                      while (--pos > -1 && pointAfter < POW10_LONG_VALUES[pos]) {
                          buf[off++] = '0';
                      }
                      off = writeInt64(buf, off, pointAfter);
                  } else {
                      off = writeInt64(buf, off, value);
                      int zeroCnt = -decimalPointPos;
                      if (zeroCnt > 0) {
                          for (int i = 0; i < zeroCnt; ++i) {
                              buf[off++] = '0';
                          }
                      }
                      IOUtils.putShortUnaligned(buf, off, DOT_ZERO_16); // .0
                      off += 2;
                  }
              }
          }
  
          return off;
      }
  
      private static int writeDecimal(long value, int digitCnt, int e10, char[] buf, int off) {
          if ((value & 1) == 0 && value % 5 == 0) {
              while (value % 100 == 0) {
                  digitCnt -= 2;
                  value /= 100;
                  if (digitCnt == 1) {
                      break;
                  }
              }
              if ((value & 1) == 0 && value % 5 == 0) {
                  if (value > 0) {
                      --digitCnt;
                      value /= 10;
                  }
              }
          }
          // whether to use scientific notation
          boolean useScientific = e10 < -3 || e10 >= 7; // !((e10 >= -3) && (e10 < 7));
          if (useScientific) {
              if (digitCnt == 1) {
                  buf[off] = (char) (value + 48);
                  IOUtils.putIntUnaligned(buf, off + 1, DOT_ZERO_32);
                  off += 3;
              } else {
                  int pos = digitCnt - 2;
                  // get the first digit
                  long tl = POW10_LONG_VALUES[pos];
                  int fd = (int) (value / tl);
                  buf[off] = (char) (fd + 48);
                  buf[off + 1] = '.';
                  off += 2;
  
                  long pointAfter = value - fd * tl;
                  // fill zeros
                  while (--pos > -1 && pointAfter < POW10_LONG_VALUES[pos]) {
                      buf[off++] = '0';
                  }
                  off = IOUtils.writeInt64(buf, off, pointAfter);
              }
              buf[off++] = 'E';
              if (e10 < 0) {
                  buf[off++] = '-';
                  e10 = -e10;
              }
              if (e10 > 99) {
                  int n = (int) (e10 * 1374389535L >> 37); //
                  buf[off] = (char) (n + 48);
                  e10 = e10 - n * 100;
                  IOUtils.putIntUnaligned(buf, off + 1, TWO_DIGITS_32_BITS[e10]);
                  off += 3;
              } else {
                  if (e10 > 9) {
                      IOUtils.putIntUnaligned(buf, off, TWO_DIGITS_32_BITS[e10]);
                      off += 2;
                  } else {
                      buf[off++] = (char) (e10 + 48);
                  }
              }
          } else {
              // for non-scientific notation such as 12345, write a decimal point when size = decimalExp.
              if (e10 < 0) {
                  // -1/-2/-3
                  IOUtils.putIntUnaligned(buf, off, ZERO_DOT_32); // 0.
                  off += 2;
                  if (e10 == -2) {
                      buf[off++] = '0';
                  } else if (e10 == -3) {
                      IOUtils.putIntUnaligned(buf, off, ZERO_ZERO_32); // 00
                      off += 2;
                  }
                  off = writeInt64(buf, off, value);
              } else {
                  // 0 - 6
                  int decimalPointPos = (digitCnt - 1) - e10;
                  if (decimalPointPos > 0) {
                      int pos = decimalPointPos - 1;
                      long tl = POW10_LONG_VALUES[pos];
                      int pointBefore = (int) (value / tl);
                      off = writeInt64(buf, off, pointBefore);
                      buf[off++] = '.';
                      long pointAfter = value - pointBefore * tl;
                      // fill zeros
                      while (--pos > -1 && pointAfter < POW10_LONG_VALUES[pos]) {
                          buf[off++] = '0';
                      }
                      off = writeInt64(buf, off, pointAfter);
                  } else {
                      off = writeInt64(buf, off, value);
                      int zeroCnt = -decimalPointPos;
                      if (zeroCnt > 0) {
                          for (int i = 0; i < zeroCnt; ++i) {
                              buf[off++] = '0';
                          }
                      }
                      IOUtils.putIntUnaligned(buf, off, DOT_ZERO_32); // .0
                      off += 2;
                  }
              }
          }
  
          return off;
      }
  }
  