All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squarespace.cldrengine.plurals.NumberOperands Maven / Gradle / Ivy

package com.squarespace.cldrengine.plurals;

import static com.squarespace.cldrengine.decimal.DecimalMath.digitCount;

import com.squarespace.cldrengine.api.Decimal;
import com.squarespace.cldrengine.decimal.Constants;

import lombok.ToString;

/**
 * Operands for use in evaluating localized plural rules: See:
 * http://www.unicode.org/reports/tr35/tr35-numbers.html#Plural_Operand_Meanings
 *
 * symbol value ---------------- n absolute value of the source number (integer and decimals) i integer digits of n v
 * number of visible fraction digits in n, with trailing zeros w number of visible fraction digits in n, without
 * trailing zeros f visible fractional digits in n, with trailing zeros t visible fractional digits in n, without
 * trailing zeros
 */
@ToString
public class NumberOperands {

  // When a number crosses this limit we reduce it to avoid overflow.
  // This limit is chosen so that a number <= this limit multiplied
  // by 10 will still be < JavaScript MAX_SAFE_INTEGER.
  private static final long LIMIT = 10000000000000L;

  private final Decimal d;
  public long n = 0;
  public long i = 0;
  public long v = 0;
  public long w = 0;
  public long f = 0;
  public long t = 0;
  public long c = 0;

  public NumberOperands(Decimal d) {
    this(d, 0);
  }

  public NumberOperands(Decimal d, int c) {
    this.d = d;

    Decimal.Properties props = d.properties();
    if (props.flag != 0) {
      return;
    }

    long[] data = props.data;
    int exp = props.exp;

    int len = data.length;
    int last = len - 1;
    int precision = (last * Constants.RDIGITS) + digitCount(data[last]);

    // Local operands
    long n = 0;
    long v = exp < 0 ? -exp : 0;
    long w = 0;
    long f = 0;
    long t = 0;

    // Compact decimal exponent value
    // See https://www.unicode.org/reports/tr35/tr35-numbers.html#Operands
    if (c < 0) {
      c = 0;
    }

    // Count trailing zeros
    int trail = 0;

    // Index of radix digit;
    int x = last;

    // Index of decimal digit in radix digit
    int y = 0;

    int intdigits = precision + exp;

    // Leading decimal zeros aren't part of the operands.
    if (intdigits < 0) {
      intdigits = 0;
    }

    outer:
    // Start at most-significant digit to last
    while (x >= 0) {
      long r = data[x];
      int count = x != last ? Constants.RDIGITS : digitCount(r);
      y = count - 1;

      // Scan each decimal digit of the radix number from
      // most- to least- significant.
      while (y >= 0) {
        long p = Constants.POWERS10[y];
        long q = (r / Constants.POWERS10[y]);

        if (intdigits > 0) {
          // Integer part
          n = (n * 10) + q;

          // If the integer digits exceed the limit we apply modulus.
          if (n > LIMIT) {
            // Stay below the limit but preseve (a) the magnitude and (b) as
            // many of the least-significant digits as possible
            n = (n % LIMIT) + LIMIT;
          }
          intdigits--;

        } else {
          // Decimal part
          if (q == 0) {
            trail++;
          } else {
            trail = 0;
          }
          f = (f * 10) + q;
        }

        // If the decimal digits exceed our limit we bail out early
        if (f > LIMIT) {
          break outer;
        }

        r %= p;
        y--;
      }
      x--;
    }

    // Trailing integer zeros
    while (exp > 0) {
      n *= 10;
      if (n > LIMIT) {
        // Stay below the limit but preserve (a) the magnitude and (b) as
        // many of the least-significant digits as possible
        n = (n % LIMIT) + LIMIT;
      }
      exp--;
    }

    // Special case for zero with exponent, e.g. '0.00'.
    if (len == 1 && data[0] == 0 && exp < 0) {
      w = 0;
    } else {
      w = v - trail;
      t = f;
      while (trail > 0) {
        t /= 10;
        trail--;
      }
    }

    this.n = n;
    this.i = n;
    this.v = v;
    this.w = w;
    this.f = f;
    this.t = t;
    this.c = c;
  }

  public long get(char op) {
    switch (op) {
      case 'n':
        return n;
      case 'i':
        return i;
      case 'v':
        return v;
      case 'w':
        return w;
      case 'f':
        return f;
      case 't':
        return t;
      case 'c':
        return c;
      default:
        return 0;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy