UInt192.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.statistics.descriptive;

  18. import java.math.BigInteger;
  19. import java.nio.ByteBuffer;

  20. /**
  21.  * A mutable 192-bit unsigned integer.
  22.  *
  23.  * <p>This is a specialised class to implement an accumulator of squared {@code long} values.
  24.  *
  25.  * @since 1.1
  26.  */
  27. final class UInt192 {
  28.     /** Mask for the lower 32-bits of a long. */
  29.     private static final long MASK32 = 0xffff_ffffL;

  30.     // Data is stored using integers to allow efficient sum-with-carry addition

  31.     /** bits 32-1 (low 32-bits). */
  32.     private int f;
  33.     /** bits 64-33. */
  34.     private int e;
  35.     /** bits 96-65. */
  36.     private int d;
  37.     /** bits 128-97. */
  38.     private int c;
  39.     /** bits 192-129 (high 64-bits). */
  40.     private long ab;

  41.     /**
  42.      * Create an instance.
  43.      */
  44.     private UInt192() {
  45.         // No-op
  46.     }

  47.     /**
  48.      * Create an instance using a direct binary representation.
  49.      * This is package-private for testing.
  50.      *
  51.      * @param hi High 64-bits.
  52.      * @param mid Middle 64-bits.
  53.      * @param lo Low 64-bits.
  54.      */
  55.     UInt192(long hi, long mid, long lo) {
  56.         this.f = (int) lo;
  57.         this.e = (int) (lo >>> Integer.SIZE);
  58.         this.d = (int) mid;
  59.         this.c = (int) (mid >>> Integer.SIZE);
  60.         this.ab = hi;
  61.     }

  62.     /**
  63.      * Create an instance using a direct binary representation.
  64.      *
  65.      * @param ab bits 192-129 (high 64-bits).
  66.      * @param c bits 128-97.
  67.      * @param d bits 96-65.
  68.      * @param e bits 64-33.
  69.      * @param f bits 32-1.
  70.      */
  71.     private UInt192(long ab, int c, int d, int e, int f) {
  72.         this.ab = ab;
  73.         this.c = c;
  74.         this.d = d;
  75.         this.e = e;
  76.         this.f = f;
  77.     }

  78.     /**
  79.      * Create an instance. The initial value is zero.
  80.      *
  81.      * @return the instance
  82.      */
  83.     static UInt192 create() {
  84.         return new UInt192();
  85.     }

  86.     /**
  87.      * Adds the squared value {@code x * x}.
  88.      *
  89.      * @param x Value.
  90.      */
  91.     void addSquare(long x) {
  92.         final long lo = x * x;
  93.         final long hi = IntMath.squareHigh(x);

  94.         // Sum with carry.
  95.         long s = (lo & MASK32) + (f & MASK32);
  96.         f = (int) s;
  97.         s = (s >>> Integer.SIZE) + (lo >>> Integer.SIZE) + (e & MASK32);
  98.         e = (int) s;
  99.         s = (s >>> Integer.SIZE) + (hi & MASK32) + (d & MASK32);
  100.         d = (int) s;
  101.         s = (s >>> Integer.SIZE) + (hi >>> Integer.SIZE) + (c & MASK32);
  102.         c = (int) s;
  103.         ab += s >>> Integer.SIZE;
  104.     }

  105.     /**
  106.      * Adds the value.
  107.      *
  108.      * @param x Value.
  109.      */
  110.     void add(UInt192 x) {
  111.         // Avoid issues adding to itself
  112.         final int ff = x.f;
  113.         final int ee = x.e;
  114.         final int dd = x.d;
  115.         final int cc = x.c;
  116.         final long aabb = x.ab;
  117.         // Sum with carry.
  118.         long s = (ff & MASK32) + (f & MASK32);
  119.         f = (int) s;
  120.         s = (s >>> Integer.SIZE) + (ee & MASK32) + (e & MASK32);
  121.         e = (int) s;
  122.         s = (s >>> Integer.SIZE) + (dd & MASK32) + (d & MASK32);
  123.         d = (int) s;
  124.         s = (s >>> Integer.SIZE) + (cc & MASK32) + (c & MASK32);
  125.         c = (int) s;
  126.         ab += (s >>> Integer.SIZE) + aabb;
  127.     }


  128.     /**
  129.      * Multiply by the unsigned value.
  130.      * Any overflow bits are lost.
  131.      *
  132.      * @param x Value.
  133.      * @return the product
  134.      */
  135.     UInt192 unsignedMultiply(int x) {
  136.         final long xx = x & MASK32;
  137.         // Multiply with carry.
  138.         long product = xx * (f & MASK32);
  139.         final int ff = (int) product;
  140.         product = (product >>> Integer.SIZE) + xx * (e & MASK32);
  141.         final int ee = (int) product;
  142.         product = (product >>> Integer.SIZE) + xx * (d & MASK32);
  143.         final int dd = (int) product;
  144.         product = (product >>> Integer.SIZE) + xx * (c & MASK32);
  145.         final int cc = (int) product;
  146.         // Possible overflow here and bits are lost
  147.         final long aabb = (product >>> Integer.SIZE) + xx * ab;
  148.         return new UInt192(aabb, cc, dd, ee, ff);
  149.     }

  150.     /**
  151.      * Subtracts the value.
  152.      * Any overflow bits (negative result) are lost.
  153.      *
  154.      * @param x Value.
  155.      * @return the difference
  156.      */
  157.     UInt192 subtract(UInt128 x) {
  158.         // Difference with carry.
  159.         // Subtract common part.
  160.         long diff = (f & MASK32) - (x.lo32()  & MASK32);
  161.         final int ff = (int) diff;
  162.         diff = (diff >> Integer.SIZE) + (e & MASK32) - (x.mid32() & MASK32);
  163.         final int ee = (int) diff;
  164.         diff = (diff >> Integer.SIZE) + (d & MASK32) - (x.hi64() & MASK32);
  165.         final int dd = (int) diff;
  166.         diff = (diff >> Integer.SIZE) + (c & MASK32) - (x.hi64() >>> Integer.SIZE);
  167.         final int cc = (int) diff;
  168.         // Possible overflow here and bits are lost containing info on the
  169.         // magnitude of the true negative value
  170.         final long aabb = (diff >> Integer.SIZE) + ab;
  171.         return new UInt192(aabb, cc, dd, ee, ff);
  172.     }

  173.     /**
  174.      * Convert to a BigInteger.
  175.      *
  176.      * @return the value
  177.      */
  178.     BigInteger toBigInteger() {
  179.         final ByteBuffer bb = ByteBuffer.allocate(Integer.BYTES * 6)
  180.             .putLong(ab)
  181.             .putInt(c)
  182.             .putInt(d)
  183.             .putInt(e)
  184.             .putInt(f);
  185.         // Sign is always positive. This works for zero.
  186.         return new BigInteger(1, bb.array());
  187.     }

  188.     /**
  189.      * Convert to a double.
  190.      *
  191.      * @return the value
  192.      */
  193.     double toDouble() {
  194.         final long h = hi64();
  195.         final long m = mid64();
  196.         final long l = lo64();
  197.         if (h == 0) {
  198.             return IntMath.uint128ToDouble(m, l);
  199.         }
  200.         // For correct rounding we use a sticky bit to represent magnitude
  201.         // lost from the low 64-bits. The result is scaled by 2^64.
  202.         return IntMath.uint128ToDouble(h, m | ((l == 0) ? 0 : 1)) * 0x1.0p64;
  203.     }

  204.     /**
  205.      * Convert to an {@code int}; throwing an exception if the value overflows an {@code int}.
  206.      *
  207.      * @return the value
  208.      * @throws ArithmeticException if the value overflows an {@code int}.
  209.      * @see Math#toIntExact(long)
  210.      */
  211.     int toIntExact() {
  212.         return Math.toIntExact(toLongExact());
  213.     }

  214.     /**
  215.      * Convert to a {@code long}; throwing an exception if the value overflows a {@code long}.
  216.      *
  217.      * @return the value
  218.      * @throws ArithmeticException if the value overflows a {@code long}.
  219.      */
  220.     long toLongExact() {
  221.         // Test if we have more than 63-bits
  222.         if ((ab | c | d) != 0 || e < 0) {
  223.             throw new ArithmeticException("long integer overflow");
  224.         }
  225.         return lo64();
  226.     }

  227.     /**
  228.      * Return the lower 64-bits as a {@code long} value.
  229.      *
  230.      * @return the low 64-bits
  231.      */
  232.     long lo64() {
  233.         return (f & MASK32) | ((e & MASK32) << Integer.SIZE);
  234.     }

  235.     /**
  236.      * Return the middle 64-bits as a {@code long} value.
  237.      *
  238.      * @return bits 128-65
  239.      */
  240.     long mid64() {
  241.         return (d & MASK32) | ((c & MASK32) << Integer.SIZE);
  242.     }

  243.     /**
  244.      * Return the higher 64-bits as a {@code long} value.
  245.      *
  246.      * @return bits 192-129
  247.      */
  248.     long hi64() {
  249.         return ab;
  250.     }
  251. }