text:
/*
 * Licensed 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 io.prestosql.spi.type;

import io.airlift.slice.Slice;
import io.airlift.slice.Slices;

import java.math.BigInteger;
import java.nio.ByteOrder;

import static io.airlift.slice.SizeOf.SIZE_OF_INT;
import static io.airlift.slice.SizeOf.SIZE_OF_LONG;
import static io.prestosql.spi.type.Decimals.MAX_PRECISION;
import static io.prestosql.spi.type.Decimals.longTenToNth;
import static java.lang.Integer.toUnsignedLong;
import static java.lang.Math.abs;
import static java.lang.System.arraycopy;
import static java.util.Arrays.fill;

/**
 * 128 bit unscaled decimal arithmetic. The representation is:
 *

 * [127_bit_unscaled_decimal_value sign_bit]
 * 0-bit...........64-bit............128-bit
 *

 * 127_bit_unscaled_decimal_value is stored in little endian format to provide easy conversion to/from long and int types.
 *

 * Based on: https://github.com/apache/hive/blob/master/common/src/java/org/apache/hadoop/hive/common/type/UnsignedInt128.java
 * and https://github.com/apache/hive/blob/master/common/src/java/org/apache/hadoop/hive/common/type/Decimal128.java
 */
public final class UnscaledDecimal128Arithmetic
{
    private static final int NUMBER_OF_LONGS = 2;
    private static final int NUMBER_OF_INTS = 2 * NUMBER_OF_LONGS;

    public static final int UNSCALED_DECIMAL_128_SLICE_LENGTH = NUMBER_OF_LONGS * SIZE_OF_LONG;

    private static final Slice[] POWERS_OF_TEN = new Slice[MAX_PRECISION];
    private static final Slice[] POWERS_OF_FIVE = new Slice[MAX_PRECISION];

    private static final int SIGN_LONG_INDEX = 1;
    private static final int SIGN_INT_INDEX = 3;
    private static final long SIGN_LONG_MASK = 1L << 63;
    private static final int SIGN_INT_MASK = 1 << 31;
    private static final int SIGN_BYTE_MASK = 1 << 7;
    private static final long ALL_BITS_SET_64 = 0xFFFFFFFFFFFFFFFFL;
    private static final long INT_BASE = 1L << 32;

    // Lowest 32 bits of a long
    private static final long LOW_32_BITS = 0xFFFFFFFFL;

    /**
     * 5^13 fits in 2^31.
     */
    private static final int MAX_POWER_OF_FIVE_INT = 13;
    /**
     * 5^x. All unsigned values.
     */
    private static final int[] POWERS_OF_FIVES_INT = new int[MAX_POWER_OF_FIVE_INT + 1];

    /**
     * 5^27 fits in 2^31.
     */
    private static final int MAX_POWER_OF_FIVE_LONG = 27;
    /**
     * 5^x. All unsigned values.
     */
    private static final long[] POWERS_OF_FIVE_LONG = new long[MAX_POWER_OF_FIVE_LONG + 1];
    /**
     * 10^9 fits in 2^31.
     */
    private static final int MAX_POWER_OF_TEN_INT = 9;
    /**
     * 10^18 fits in 2^63.
     */
    private static final int MAX_POWER_OF_TEN_LONG = 18;
    /**
     * 10^x. All unsigned values.
     */
    private static final int[] POWERS_OF_TEN_INT = new int[MAX_POWER_OF_TEN_INT + 1];

    static {
        for (int i = 0; i < POWERS_OF_FIVE.length; ++i) {
            POWERS_OF_FIVE[i] = unscaledDecimal(BigInteger.valueOf(5).pow(i));
        }
        for (int i = 0; i < POWERS_OF_TEN.length; ++i) {
            POWERS_OF_TEN[i] = unscaledDecimal(BigInteger.TEN.pow(i));
        }

        POWERS_OF_FIVES_INT[0] = 1;
        for (int i = 1; i < POWERS_OF_FIVES_INT.length; ++i) {
            POWERS_OF_FIVES_INT[i] = POWERS_OF_FIVES_INT[i - 1] * 5;
        }

        POWERS_OF_FIVE_LONG[0] = 1;
        for (int i = 1; i < POWERS_OF_FIVE_LONG.length; ++i) {
            POWERS_OF_FIVE_LONG[i] = POWERS_OF_FIVE_LONG[i - 1] * 5;
        }

        POWERS_OF_TEN_INT[0] = 1;
        for (int i = 1; i < POWERS_OF_TEN_INT.length; ++i) {
            POWERS_OF_TEN_INT[i] = POWERS_OF_TEN_INT[i - 1] * 10;
        }

        if (!ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
            throw new IllegalStateException("UnsignedDecimal128Arithmetic is supported on little-endian machines only");
        }
    }

    public static Slice unscaledDecimal()
    {
        return Slices.allocate(UNSCALED_DECIMAL_128_SLICE_LENGTH);
    }

    public static Slice unscaledDecimal(Slice decimal)
    {
        return Slices.copyOf(decimal);
    }

    public static Slice unscaledDecimal(String unscaledValue)
    {
        return unscaledDecimal(new BigInteger(unscaledValue));
    }

    public static Slice unscaledDecimal(BigInteger unscaledValue)
    {
        Slice decimal = Slices.allocate(UNSCALED_DECIMAL_128_SLICE_LENGTH);
        return pack(unscaledValue, decimal);
    }

    public static Slice pack(BigInteger unscaledValue, Slice result)
    {
        pack(0, 0, false, result);
        byte[] bytes = unscaledValue.abs().toByteArray();

        if (bytes.length > UNSCALED_DECIMAL_128_SLICE_LENGTH
                || (bytes.length == UNSCALED_DECIMAL_128_SLICE_LENGTH && (bytes[0] & SIGN_BYTE_MASK) != 0)) {
            throwOverflowException();
        }

        // convert to little-endian order
        reverse(bytes);

        result.setBytes(0, bytes);
        if (unscaledValue.signum() < 0) {
            setNegative(result, true);
        }

        throwIfOverflows(result);

        return result;
    }

    public static Slice unscaledDecimal(long unscaledValue)
    {
        long[] longs = new long[NUMBER_OF_LONGS];
        if (unscaledValue < 0) {
            longs[0] = -unscaledValue;
            longs[1] = SIGN_LONG_MASK;
        }
        else {
            longs[0] = unscaledValue;
        }
        return Slices.wrappedLongArray(longs);
    }

    public static BigInteger unscaledDecimalToBigInteger(Slice decimal)
    {
        byte[] bytes = decimal.getBytes(0, UNSCALED_DECIMAL_128_SLICE_LENGTH);
        // convert to big-endian order
        reverse(bytes);
        bytes[0] &= ~SIGN_BYTE_MASK;
        return new BigInteger(isNegative(decimal) ? -1 : 1, bytes);
    }

    public static long unscaledDecimalToUnscaledLong(Slice decimal)
    {
        long low = getLong(decimal, 0);
        long high = getLong(decimal, 1);
        boolean negative = isNegative(decimal);

        if (high != 0 || ((low > Long.MIN_VALUE || !negative) && low < 0)) {
            throwOverflowException();
        }

        return negative ? -low : low;
    }

    public static long unscaledDecimalToUnscaledLongUnsafe(Slice decimal)
    {
        long low = getLong(decimal, 0);
        return isNegative(decimal) ? -low : low;
    }

    public static Slice rescale(Slice decimal, int rescaleFactor)
    {
        if (rescaleFactor == 0) {
            return decimal;
        }
        else {
            Slice result = unscaledDecimal();
            rescale(decimal, rescaleFactor, result);
            return result;
        }
    }

    public static void rescale(Slice decimal, int rescaleFactor, Slice result)
    {
        if (rescaleFactor == 0) {
            copyUnscaledDecimal(decimal, result);
        }
        else if (rescaleFactor > 0) {
            if (rescaleFactor >= POWERS_OF_TEN.length) {
                throwOverflowException();
            }
            shiftLeftBy10(decimal, rescaleFactor, result);
        }
        else {
            scaleDownRoundUp(decimal, -rescaleFactor, result);
        }
    }

    public static Slice rescale(long decimal, int rescaleFactor)
    {
        Slice result = unscaledDecimal();
        if (rescaleFactor == 0) {
            return unscaledDecimal(decimal);
        }
        else if (rescaleFactor > 0) {
            if (rescaleFactor >= POWERS_OF_TEN.length) {
                throwOverflowException();
            }
            shiftLeftBy10(decimal, rescaleFactor, result);
        }
        else {
            scaleDownRoundUp(unscaledDecimal(decimal), -rescaleFactor, result);
        }

        return result;
    }

    public static Slice rescaleTruncate(Slice decimal, int rescaleFactor)
    {
        if (rescaleFactor == 0) {
            return decimal;
        }
        else {
            Slice result = unscaledDecimal();
            rescaleTruncate(decimal, rescaleFactor, result);
            return result;
        }
    }

    public static void rescaleTruncate(Slice decimal, int rescaleFactor, Slice result)
    {
        if (rescaleFactor == 0) {
            copyUnscaledDecimal(decimal, result);
        }
        else if (rescaleFactor > 0) {
            if (rescaleFactor >= POWERS_OF_TEN.length) {
                throwOverflowException();
            }
            shiftLeftBy10(decimal, rescaleFactor, result);
        }
        else {
            scaleDownTruncate(decimal, -rescaleFactor, result);
        }
    }

    // Multiplies by 10^rescaleFactor. Only positive rescaleFactor values are allowed
    private static void shiftLeftBy10(Slice decimal, int rescaleFactor, Slice result)
    {
        if (rescaleFactor <= MAX_POWER_OF_TEN_INT) {
            multiply(decimal, (int) longTenToNth(rescaleFactor), result);
        }
        else if (rescaleFactor <= MAX_POWER_OF_TEN_LONG) {
            multiply(decimal, longTenToNth(rescaleFactor), result);
        }
        else {
            multiply(POWERS_OF_TEN[rescaleFactor], decimal, result);
        }
    }

    // Multiplies by 10^rescaleFactor. Only positive rescaleFactor values are allowed
    private static void shiftLeftBy10(long decimal, int rescaleFactor, Slice result)
    {
        if (rescaleFactor <= MAX_POWER_OF_TEN_INT) {
            multiply(decimal, (int) longTenToNth(rescaleFactor), result);
        }
        else if (rescaleFactor <= MAX_POWER_OF_TEN_LONG) {
            multiply(decimal, longTenToNth(rescaleFactor), result);
        }
        else {
            multiply(POWERS_OF_TEN[rescaleFactor], decimal, result);
        }
    }

    private static void scaleDownTruncate(Slice decimal, int scaleFactor, Slice result)
    {
        // optimized path for smaller values
        long low = getLong(decimal, 0);
        long high = getLong(decimal, 1);
        if (scaleFactor <= MAX_POWER_OF_TEN_LONG && high == 0 && low >= 0) {
            long divisor = longTenToNth(scaleFactor);
            long newLow = low / divisor;
            pack(result, newLow, 0, isNegative(decimal));
            return;
        }

        // Scales down for 10**rescaleFactor.
        // Because divide by int has limited divisor, we choose code path with the least amount of divisions
        if ((scaleFactor - 1) / MAX_POWER_OF_FIVE_INT < (scaleFactor - 1) / MAX_POWER_OF_TEN_INT) {
            // scale down for 10**rescale is equivalent to scaling down with 5**rescaleFactor first, then with 2**rescaleFactor
            scaleDownFive(decimal, scaleFactor, result);
            shiftRightTruncate(result, scaleFactor, result);
        }
        else {
            scaleDownTenTruncate(decimal, scaleFactor, result);
        }
    }

    public static Slice add(Slice left, Slice right)
    {
        Slice result = unscaledDecimal();
        add(left, right, result);
        return result;
    }

    public static void add(Slice left, Slice right, Slice result)
    {
        long overflow = addWithOverflow(left, right, result);
        if (overflow != 0) {
            throwOverflowException();
        }
    }

    /**
     * Instead of throwing overflow exception, this function returns:
     * 0 when there was no overflow
     * +1 when there was overflow
     * -1 when there was underflow
     */
    public static long addWithOverflow(Slice left, Slice right, Slice result)
    {
        boolean leftNegative = isNegative(left);
        boolean rightNegative = isNegative(right);
        long overflow = 0;
        if (leftNegative == rightNegative) {
            // either both negative or both positive
            overflow = addUnsignedReturnOverflow(left, right, result, leftNegative);
            if (leftNegative) {
                overflow = -overflow;
            }
        }
        else {
            int compare = compareAbsolute(left, right);
            if (compare > 0) {
                subtractUnsigned(left, right, result, leftNegative);
            }
            else if (compare < 0) {
                subtractUnsigned(right, left, result, !leftNegative);
            }
            else {
                setToZero(result);
            }
        }
        return overflow;
    }

    public static Slice subtract(Slice left, Slice right)
    {
        Slice result = unscaledDecimal();
        subtract(left, right, result);
        return result;
    }

    public static void subtract(Slice left, Slice right, Slice result)
    {
        boolean leftNegative = isNegative(left);
        boolean rightNegative = isNegative(right);

        if (leftNegative != rightNegative) {
            // only one is negative
            if (addUnsignedReturnOverflow(left, right, result, leftNegative) != 0) {
                throwOverflowException();
            }
        }
        else {
            int compare = compareAbsolute(left, right);
            if (compare > 0) {
                subtractUnsigned(left, right, result, leftNegative);
            }
            else if (compare < 0) {
                subtractUnsigned(right, left, result, !leftNegative);
            }
            else {
                setToZero(result);
            }
        }
    }

    /**
     * This method ignores signs of the left and right. Returns overflow value.
     */
    private static long addUnsignedReturnOverflow(Slice left, Slice right, Slice result, boolean resultNegative)
    {
        long l0 = getLong(left, 0);
        long l1 = getLong(left, 1);

        long r0 = getLong(right, 0);
        long r1 = getLong(right, 1);

        long z0 = l0 + r0;
        int overflow = unsignedIsSmaller(z0, l0) ? 1 : 0;

        long intermediateResult = l1 + r1 + overflow;
        long z1 = intermediateResult & (~SIGN_LONG_MASK);
        pack(result, z0, z1, resultNegative);

        return intermediateResult >>> 63;
    }

    /**
     * This method ignores signs of the left and right and assumes that left is greater then right
     */
    private static void subtractUnsigned(Slice left, Slice right, Slice result, boolean resultNegative)
    {
        long l0 = getLong(left, 0);
        long l1 = getLong(left, 1);

        long r0 = getLong(right, 0);
        long r1 = getLong(right, 1);

        long z0 = l0 - r0;
        int underflow = unsignedIsSmaller(l0, z0) ? 1 : 0;
        long z1 = l1 - r1 - underflow;

        pack(result, z0, z1, resultNegative);
    }

    public static Slice multiply(Slice left, Slice right)
    {
        Slice result = unscaledDecimal();
        multiply(left, right, result);
        return result;
    }

    public static Slice multiply(Slice left, long right)
    {
        Slice result = unscaledDecimal();
        multiply(left, right, result);
        return result;
    }

    public static Slice multiply(long left, long right)
    {
        Slice result = unscaledDecimal();
        multiply(left, right, result);
        return result;
    }

    public static void multiply(Slice left, Slice right, Slice result)
    {
        checkArgument(result.length() == NUMBER_OF_LONGS * Long.BYTES);

        multiply(getRawLong(left, 0), getRawLong(left, 1), getRawLong(right, 0), getRawLong(right, 1), result);
    }

    public static void multiply(long leftLow, long leftHigh, long rightLow, long rightHigh, Slice result)
    {
        checkArgument(result.length() == NUMBER_OF_LONGS * Long.BYTES);

        long l0 = low(leftLow);
        long l1 = high(leftLow);
        long l2 = low(leftHigh);
        boolean leftNegative = isNegative(leftHigh);
        long l3 = unpackUnsignedInt(high(leftHigh));

        long r0 = low(rightLow);
        long r1 = high(rightLow);
        long r2 = low(rightHigh);
        boolean rightNegative = isNegative(rightHigh);
        long r3 = unpackUnsignedInt(high(rightHigh));

        // the combinations below definitely result in an overflow
        if (((r3 != 0 && (l3 | l2 | l1) != 0) || (r2 != 0 && (l3 | l2) != 0) || (r1 != 0 && l3 != 0))) {
            throwOverflowException();
        }

        long z0 = 0;
        long z1 = 0;
        long z2 = 0;
        long z3 = 0;

        if (l0 != 0) {
            long accumulator = r0 * l0;
            z0 = low(accumulator);
            accumulator = high(accumulator) + r1 * l0;

            z1 = low(accumulator);
            accumulator = high(accumulator) + r2 * l0;

            z2 = low(accumulator);
            accumulator = high(accumulator) + r3 * l0;

            z3 = low(accumulator);

            if (high(accumulator) != 0) {
                throwOverflowException();
            }
        }

        if (l1 != 0) {
            long accumulator = r0 * l1 + z1;
            z1 = low(accumulator);
            accumulator = high(accumulator) + r1 * l1 + z2;

            z2 = low(accumulator);
            accumulator = high(accumulator) + r2 * l1 + z3;

            z3 = low(accumulator);

            if (high(accumulator) != 0) {
                throwOverflowException();
            }
        }

        if (l2 != 0) {
            long accumulator = r0 * l2 + z2;
            z2 = low(accumulator);
            accumulator = high(accumulator) + r1 * l2 + z3;

            z3 = low(accumulator);

            if (high(accumulator) != 0) {
                throwOverflowException();
            }
        }

        if (l3 != 0) {
            long accumulator = r0 * l3 + z3;
            z3 = low(accumulator);

            if (high(accumulator) != 0) {
                throwOverflowException();
            }
        }

        pack(result, (int) z0, (int) z1, (int) z2, (int) z3, leftNegative != rightNegative);
    }

    public static void multiply(Slice left, long right, Slice result)
    {
        checkArgument(result.length() == NUMBER_OF_LONGS * Long.BYTES);

        boolean rightNegative = right < 0;
        if (rightNegative) {
            right = -right;
        }

        multiply(getRawLong(left, 0), getRawLong(left, 1), right, rightNegative ? SIGN_LONG_MASK : 0, result);
    }

    public static void multiply(Slice left, int right, Slice result)
    {
        checkArgument(result.length() == NUMBER_OF_LONGS * Long.BYTES);

        long l0 = toUnsignedLong(getInt(left, 0));
        long l1 = toUnsignedLong(getInt(left, 1));
        long l2 = toUnsignedLong(getInt(left, 2));
        int l3raw = getRawInt(left, 3);
        boolean leftNegative = isNegative(l3raw);
        long l3 = toUnsignedLong(unpackUnsignedInt(l3raw));

        boolean rightNegative = right < 0;
        long r0 = abs(right);

        long z0;
        long z1;
        long z2;
        long z3;

        long accumulator = r0 * l0;
        z0 = low(accumulator);
        accumulator = high(accumulator) + l1 * r0;

        z1 = low(accumulator);
        accumulator = high(accumulator) + l2 * r0;

        z2 = low(accumulator);
        accumulator = high(accumulator) + l3 * r0;

        z3 = low(accumulator);

        if (high(accumulator) != 0) {
            throwOverflowException();
        }

        pack(result, (int) z0, (int) z1, (int) z2, (int) z3, leftNegative != rightNegative);
    }

    // Using multiply(long, long, long, long, Slice) here decreases performance by ~40%
    public static void multiply(long left, long right, Slice result)
    {
        checkArgument(result.length() == NUMBER_OF_LONGS * Long.BYTES);
        boolean rightNegative = right < 0;
        boolean leftNegative = left < 0;
        left = abs(left);
        right = abs(right);

        long l0 = low(left);
        long l1 = high(left);

        long r0 = low(right);
        long r1 = high(right);

        long z0;
        long z1;
        long z2;
        long z3;

        long accumulator = r0 * l0;
        z0 = low(accumulator);
        accumulator = high(accumulator) + r1 * l0;

        z1 = low(accumulator);
        z2 = accumulator >> 32;

        accumulator = r0 * l1 + z1;
        z1 = low(accumulator);
        accumulator = high(accumulator) + r1 * l1 + z2;

        z2 = low(accumulator);
        z3 = high(accumulator);

        pack(result, (int) z0, (int) z1, (int) z2, (int) z3, leftNegative != rightNegative);
    }

    public static void multiply(long left, int right, Slice result)
    {
        checkArgument(result.length() == NUMBER_OF_LONGS * Long.BYTES);
        boolean rightNegative = right < 0;
        boolean leftNegative = left < 0;
        left = abs(left);
        long r0 = abs(right);

        long l0 = low(left);
        long l1 = high(left);

        long z0;
        long z1;
        long z2;

        long accumulator = r0 * l0;
        z0 = low(accumulator);
        z1 = high(accumulator);

        accumulator = r0 * l1 + z1;
        z1 = low(accumulator);
        z2 = high(accumulator);

        pack(result, (int) z0, (int) z1, (int) z2, 0, leftNegative != rightNegative);
    }

    /**
     * This an unsigned operation. Supplying negative arguments will yield wrong results.
     * Assumes left array length to be >= 8. However only first 4 int values are multiplied
     */
    static void multiply256Destructive(int[] left, Slice right)
    {
        long l0 = toUnsignedLong(left[0]);
        long l1 = toUnsignedLong(left[1]);
        long l2 = toUnsignedLong(left[2]);
        long l3 = toUnsignedLong(left[3]);

        long r0 = toUnsignedLong(getInt(right, 0));
        long r1 = toUnsignedLong(getInt(right, 1));
        long r2 = toUnsignedLong(getInt(right, 2));
        long r3 = toUnsignedLong(getInt(right, 3));

        long z0 = 0;
        long z1 = 0;
        long z2 = 0;
        long z3 = 0;
        long z4 = 0;
        long z5 = 0;
        long z6 = 0;
        long z7 = 0;

        if (l0 != 0) {
            long accumulator = r0 * l0;
            z0 = low(accumulator);
            accumulator = high(accumulator) + r1 * l0;

            z1 = low(accumulator);
            accumulator = high(accumulator) + r2 * l0;

            z2 = low(accumulator);
            accumulator = high(accumulator) + r3 * l0;

            z3 = low(accumulator);
            z4 = high(accumulator);
        }

        if (l1 != 0) {
            long accumulator = r0 * l1 + z1;
            z1 = low(accumulator);
            accumulator = high(accumulator) + r1 * l1 + z2;

            z2 = low(accumulator);
            accumulator = high(accumulator) + r2 * l1 + z3;

            z3 = low(accumulator);
            accumulator = high(accumulator) + r3 * l1 + z4;

            z4 = low(accumulator);
            z5 = high(accumulator);
        }

        if (l2 != 0) {
            long accumulator = r0 * l2 + z2;
            z2 = low(accumulator);
            accumulator = high(accumulator) + r1 * l2 + z3;

            z3 = low(accumulator);
            accumulator = high(accumulator) + r2 * l2 + z4;

            z4 = low(accumulator);
            accumulator = high(accumulator) + r3 * l2 + z5;

            z5 = low(accumulator);
            z6 = high(accumulator);
        }

        if (l3 != 0) {
            long accumulator = r0 * l3 + z3;
            z3 = low(accumulator);
            accumulator = high(accumulator) + r1 * l3 + z4;

            z4 = low(accumulator);
            accumulator = high(accumulator) + r2 * l3 + z5;

            z5 = low(accumulator);
            accumulator = high(accumulator) + r3 * l3 + z6;

            z6 = low(accumulator);
            z7 = high(accumulator);
        }

        left[0] = (int) z0;
        left[1] = (int) z1;
        left[2] = (int) z2;
        left[3] = (int) z3;
        left[4] = (int) z4;
        left[5] = (int) z5;
        left[6] = (int) z6;
        left[7] = (int) z7;
    }

    /**
     * This an unsigned operation. Supplying negative arguments will yield wrong results.
     * Assumes left array length to be >= 6. However only first 4 int values are multiplied
     */
    static void multiply256Destructive(int[] left, long right)
    {
        long l0 = toUnsignedLong(left[0]);
        long l1 = toUnsignedLong(left[1]);
        long l2 = toUnsignedLong(left[2]);
        long l3 = toUnsignedLong(left[3]);

        long r0 = low(right);
        long r1 = high(right);

        long z0 = 0;
        long z1 = 0;
        long z2 = 0;
        long z3 = 0;
        long z4 = 0;
        long z5 = 0;

        if (l0 != 0) {
            long accumulator = r0 * l0;
            z0 = low(accumulator);
            accumulator = high(accumulator) + r1 * l0;

            z1 = low(accumulator);
            z2 = high(accumulator);
        }

        if (l1 != 0) {
            long accumulator = r0 * l1 + z1;
            z1 = low(accumulator);
            accumulator = high(accumulator) + r1 * l1 + z2;

            z2 = low(accumulator);
            z3 = high(accumulator);
        }

        if (l2 != 0) {
            long accumulator = r0 * l2 + z2;
            z2 = low(accumulator);
            accumulator = high(accumulator) + r1 * l2 + z3;

            z3 = low(accumulator);
            z4 = high(accumulator);
        }

        if (l3 != 0) {
            long accumulator = r0 * l3 + z3;
            z3 = low(accumulator);
            accumulator = high(accumulator) + r1 * l3 + z4;

            z4 = low(accumulator);
            z5 = high(accumulator);
        }

        left[0] = (int) z0;
        left[1] = (int) z1;
        left[2] = (int) z2;
        left[3] = (int) z3;
        left[4] = (int) z4;
        left[5] = (int) z5;
    }

    /**
     * This an unsigned operation. Supplying negative arguments will yield wrong results.
     * Assumes left array length to be >= 5. However only first 4 int values are multiplied
     */
    static void multiply256Destructive(int[] left, int r0)
    {
        long l0 = toUnsignedLong(left[0]);
        long l1 = toUnsignedLong(left[1]);
        long l2 = toUnsignedLong(left[2]);
        long l3 = toUnsignedLong(left[3]);

        long z0;
        long z1;
        long z2;
        long z3;
        long z4;

        long accumulator = r0 * l0;
        z0 = low(accumulator);
        z1 = high(accumulator);

        accumulator = r0 * l1 + z1;
        z1 = low(accumulator);
        z2 = high(accumulator);

        accumulator = r0 * l2 + z2;
        z2 = low(accumulator);
        z3 = high(accumulator);

        accumulator = r0 * l3 + z3;
        z3 = low(accumulator);
        z4 = high(accumulator);

        left[0] = (int) z0;
        left[1] = (int) z1;
        left[2] = (int) z2;
        left[3] = (int) z3;
        left[4] = (int) z4;
    }

    public static int compare(Slice left, Slice right)
    {
        boolean leftStrictlyNegative = isStrictlyNegative(left);
        boolean rightStrictlyNegative = isStrictlyNegative(right);

        if (leftStrictlyNegative != rightStrictlyNegative) {
            return leftStrictlyNegative ? -1 : 1;
        }
        else {
            return compareAbsolute(left, right) * (leftStrictlyNegative ? -1 : 1);
        }
    }

    public static int compare(long leftRawLow, long leftRawHigh, long rightRawLow, long rightRawHigh)
    {
        boolean leftStrictlyNegative = isStrictlyNegative(leftRawLow, leftRawHigh);
        boolean rightStrictlyNegative = isStrictlyNegative(rightRawLow, rightRawHigh);

        if (leftStrictlyNegative != rightStrictlyNegative) {
            return leftStrictlyNegative ? -1 : 1;
        }
        else {
            long leftHigh = unpackUnsignedLong(leftRawHigh);
            long rightHigh = unpackUnsignedLong(rightRawHigh);
            return compareUnsigned(leftRawLow, leftHigh, rightRawLow, rightHigh) * (leftStrictlyNegative ? -1 : 1);
        }
    }

    public static int compareAbsolute(Slice left, Slice right)
    {
        long leftHigh = getLong(left, 1);
        long rightHigh = getLong(right, 1);
        if (leftHigh != rightHigh) {
            return Long.compareUnsigned(leftHigh, rightHigh);
        }

        long leftLow = getLong(left, 0);
        long rightLow = getLong(right, 0);
        if (leftLow != rightLow) {
            return Long.compareUnsigned(leftLow, rightLow);
        }

        return 0;
    }

    public static int compareUnsigned(long leftRawLow, long leftRawHigh, long rightRawLow, long rightRawHigh)
    {
        if (leftRawHigh != rightRawHigh) {
            return Long.compareUnsigned(leftRawHigh, rightRawHigh);
        }
        if (leftRawLow != rightRawLow) {
            return Long.compareUnsigned(leftRawLow, rightRawLow);
        }
        return 0;
    }

    public static void incrementUnsafe(Slice decimal)
    {
        long low = getLong(decimal, 0);
        if (low != ALL_BITS_SET_64) {
            setRawLong(decimal, 0, low + 1);
            return;
        }

        long high = getLong(decimal, 1);
        setNegativeLong(decimal, high + 1, isNegative(decimal));
    }

    public static void negate(Slice decimal)
    {
        setNegative(decimal, !isNegative(decimal));
    }

    public static boolean isStrictlyNegative(Slice decimal)
    {
        return isStrictlyNegative(getRawLong(decimal, 0), getRawLong(decimal, 1));
    }

    public static boolean isStrictlyNegative(long rawLow, long rawHigh)
    {
        return isNegative(rawHigh) && (rawLow != 0 || unpackUnsignedLong(rawHigh) != 0);
    }

    private static boolean isNegative(int lastRawHigh)
    {
        return lastRawHigh >>> 31 != 0;
    }

    public static boolean isNegative(Slice decimal)
    {
        return isNegative(getRawInt(decimal, SIGN_INT_INDEX));
    }

    public static boolean isNegative(long rawHigh)
    {
        return rawHigh >>> 63 != 0;
    }

    public static boolean isZero(Slice decimal)
    {
        return getLong(decimal, 0) == 0 && getLong(decimal, 1) == 0;
    }

    public static String toUnscaledString(Slice decimal)
    {
        if (isZero(decimal)) {
            return "0";
        }

        char[] buffer = new char[MAX_PRECISION + 1];
        int index = buffer.length;
        boolean negative = isNegative(decimal);
        decimal = unscaledDecimal(decimal);
        do {
            int remainder = divide(decimal, 10, decimal);
            buffer[--index] = (char) ('0' + remainder);
        }
        while (!isZero(decimal));

        if (negative) {
            buffer[--index] = '-';
        }

        return new String(buffer, index, buffer.length - index);
    }

    public static boolean overflows(Slice value, int precision)
    {
        if (precision == MAX_PRECISION) {
            return exceedsOrEqualTenToThirtyEight(value);
        }
        return precision < MAX_PRECISION && compareAbsolute(value, POWERS_OF_TEN[precision]) >= 0;
    }

    public static void throwIfOverflows(Slice decimal)
    {
        if (exceedsOrEqualTenToThirtyEight(decimal)) {
            throwOverflowException();
        }
    }

    public static void throwIfOverflows(Slice value, int precision)
    {
        if (overflows(value, precision)) {
            throwOverflowException();
        }
    }

    private static void scaleDownRoundUp(Slice decimal, int scaleFactor, Slice result)
    {
        // optimized path for smaller values
        long low = getLong(decimal, 0);
        long high = getLong(decimal, 1);
        if (scaleFactor <= MAX_POWER_OF_TEN_LONG && high == 0 && low >= 0) {
            long divisor = longTenToNth(scaleFactor);
            long newLow = low / divisor;
            if (low % divisor >= (divisor >> 1)) {
                newLow++;
            }
            pack(result, newLow, 0, isNegative(decimal));
            return;
        }

        // Scales down for 10**rescaleFactor.
        // Because divide by int has limited divisor, we choose code path with the least amount of divisions
        if ((scaleFactor - 1) / MAX_POWER_OF_FIVE_INT < (scaleFactor - 1) / MAX_POWER_OF_TEN_INT) {
            // scale down for 10**rescale is equivalent to scaling down with 5**rescaleFactor first, then with 2**rescaleFactor
            scaleDownFive(decimal, scaleFactor, result);
            shiftRightRoundUp(result, scaleFactor, result);
        }
        else {
            scaleDownTenRoundUp(decimal, scaleFactor, result);
        }
    }

    /**
     * Scale down the value for 5**fiveScale (result := decimal / 5**fiveScale).
     */
    private static void scaleDownFive(Slice decimal, int fiveScale, Slice result)
    {
        while (true) {
            int powerFive = Math.min(fiveScale, MAX_POWER_OF_FIVE_INT);
            fiveScale -= powerFive;

            int divisor = POWERS_OF_FIVES_INT[powerFive];
            divide(decimal, divisor, result);
            decimal = result;

            if (fiveScale == 0) {
                return;
            }
        }
    }

    /**
     * Scale down the value for 10**tenScale (this := this / 5**tenScale). This
     * method rounds-up, eg 44/10=4, 44/10=5.
     */
    private static void scaleDownTenRoundUp(Slice decimal, int tenScale, Slice result)
    {
        boolean round;
        do {
            int powerTen = Math.min(tenScale, MAX_POWER_OF_TEN_INT);
            tenScale -= powerTen;

            int divisor = POWERS_OF_TEN_INT[powerTen];
            round = divideCheckRound(decimal, divisor, result);
            decimal = result;
        }
        while (tenScale > 0);

        if (round) {
            incrementUnsafe(decimal);
        }
    }

    private static void scaleDownTenTruncate(Slice decimal, int tenScale, Slice result)
    {
        do {
            int powerTen = Math.min(tenScale, MAX_POWER_OF_TEN_INT);
            tenScale -= powerTen;

            int divisor = POWERS_OF_TEN_INT[powerTen];
            divide(decimal, divisor, result);
            decimal = result;
        }
        while (tenScale > 0);
    }

    private static void shiftRightRoundUp(Slice decimal, int rightShifts, Slice result)
    {
        shiftRight(decimal, rightShifts, true, result);
    }

    private static void shiftRightTruncate(Slice decimal, int rightShifts, Slice result)
    {
        shiftRight(decimal, rightShifts, false, result);
    }

    // visible for testing
    static void shiftRight(Slice decimal, int rightShifts, boolean roundUp, Slice result)
    {
        if (rightShifts == 0) {
            copyUnscaledDecimal(decimal, result);
            return;
        }

        int wordShifts = rightShifts / 64;
        int bitShiftsInWord = rightShifts % 64;
        int shiftRestore = 64 - bitShiftsInWord;

        // check round-ups before settings values to result.
        // be aware that result could be the same object as decimal.
        boolean roundCarry;
        if (bitShiftsInWord == 0) {
            roundCarry = roundUp && getLong(decimal, wordShifts - 1) < 0;
        }
        else {
            roundCarry = roundUp && (getLong(decimal, wordShifts) & (1L << (bitShiftsInWord - 1))) != 0;
        }

        // Store negative before settings values to result.
        boolean negative = isNegative(decimal);

        long low;
        long high;

        switch (wordShifts) {
            case 0:
                low = getLong(decimal, 0);
                high = getLong(decimal, 1);
                break;
            case 1:
                low = getLong(decimal, 1);
                high = 0;
                break;
            default:
                throw new IllegalArgumentException();
        }

        if (bitShiftsInWord > 0) {
            low = (low >>> bitShiftsInWord) | (high << shiftRestore);
            high = (high >>> bitShiftsInWord);
        }

        if (roundCarry) {
            if (low != ALL_BITS_SET_64) {
                low++;
            }
            else {
                low = 0;
                high++;
            }
        }

        pack(result, low, high, negative);
    }

    public static Slice divideRoundUp(long dividend, int dividendScaleFactor, long divisor)
    {
        return divideRoundUp(
                abs(dividend), dividend < 0 ? SIGN_LONG_MASK : 0, dividendScaleFactor,
                abs(divisor), divisor < 0 ? SIGN_LONG_MASK : 0);
    }

    public static Slice divideRoundUp(long dividend, int dividendScaleFactor, Slice divisor)
    {
        return divideRoundUp(
                abs(dividend), dividend < 0 ? SIGN_LONG_MASK : 0, dividendScaleFactor,
                getRawLong(divisor, 0), getRawLong(divisor, 1));
    }

    public static Slice divideRoundUp(Slice dividend, int dividendScaleFactor, long divisor)
    {
        return divideRoundUp(
                getRawLong(dividend, 0), getRawLong(dividend, 1), dividendScaleFactor,
                abs(divisor), divisor < 0 ? SIGN_LONG_MASK : 0);
    }

    public static Slice divideRoundUp(Slice dividend, int dividendScaleFactor, Slice divisor)
    {
        return divideRoundUp(
                getRawLong(dividend, 0), getRawLong(dividend, 1), dividendScaleFactor,
                getRawLong(divisor, 0), getRawLong(divisor, 1));
    }

    private static Slice divideRoundUp(long dividendLow, long dividendHigh, int dividendScaleFactor, long divisorLow, long divisorHigh)
    {
        Slice quotient = unscaledDecimal();
        Slice remainder = unscaledDecimal();
        divide(dividendLow, dividendHigh, dividendScaleFactor, divisorLow, divisorHigh, 0, quotient, remainder);

        // round
        boolean quotientIsNegative = isNegative(quotient);
        setNegative(quotient, false);
        setNegative(remainder, false);

        // if (2 * remainder >= divisor) - increment quotient by one
        shiftLeftDestructive(remainder, 1);
        long remainderLow = getRawLong(remainder, 0);
        long remainderHigh = getRawLong(remainder, 1);
        long divisorHighUnsigned = unpackUnsignedLong(divisorHigh);
        if (compareUnsigned(remainderLow, remainderHigh, divisorLow, divisorHighUnsigned) >= 0) {
            incrementUnsafe(quotient);
            throwIfOverflows(quotient);
        }

        setNegative(quotient, quotientIsNegative);
        return quotient;
    }

    // visible for testing
    static Slice shiftLeft(Slice decimal, int leftShifts)
    {
        Slice result = Slices.copyOf(decimal);
        shiftLeftDestructive(result, leftShifts);
        return result;
    }

    // visible for testing
    static void shiftLeftDestructive(Slice decimal, int leftShifts)
    {
        if (leftShifts == 0) {
            return;
        }

        int wordShifts = leftShifts / 64;
        int bitShiftsInWord = leftShifts % 64;
        int shiftRestore = 64 - bitShiftsInWord;

        // check overflow
        if (bitShiftsInWord != 0) {
            if ((getLong(decimal, 1 - wordShifts) & (-1L << shiftRestore)) != 0) {
                throwOverflowException();
            }
        }
        if (wordShifts == 1) {
            if (getLong(decimal, 1) != 0) {
                throwOverflowException();
            }
        }

        // Store negative before settings values to result.
        boolean negative = isNegative(decimal);

        long low;
        long high;

        switch (wordShifts) {
            case 0:
                low = getLong(decimal, 0);
                high = getLong(decimal, 1);
                break;
            case 1:
                low = 0;
                high = getLong(decimal, 0);
                break;
            default:
                throw new IllegalArgumentException();
        }

        if (bitShiftsInWord > 0) {
            high = (high << bitShiftsInWord) | (low >>> shiftRestore);
            low = (low << bitShiftsInWord);
        }

        pack(decimal, low, high, negative);
    }

    public static Slice remainder(long dividend, int dividendScaleFactor, long divisor, int divisorScaleFactor)
    {
        return remainder(
                abs(dividend), dividend < 0 ? SIGN_LONG_MASK : 0, dividendScaleFactor,
                abs(divisor), divisor < 0 ? SIGN_LONG_MASK : 0, divisorScaleFactor);
    }

    public static Slice remainder(long dividend, int dividendScaleFactor, Slice divisor, int divisorScaleFactor)
    {
        return remainder(
                abs(dividend), dividend < 0 ? SIGN_LONG_MASK : 0, dividendScaleFactor,
                getRawLong(divisor, 0), getRawLong(divisor, 1), divisorScaleFactor);
    }

    public static Slice remainder(Slice dividend, int dividendScaleFactor, long divisor, int divisorScaleFactor)
    {
        return remainder(
                getRawLong(dividend, 0), getRawLong(dividend, 1), dividendScaleFactor,
                abs(divisor), divisor < 0 ? SIGN_LONG_MASK : 0, divisorScaleFactor);
    }

    public static Slice remainder(Slice dividend, int dividendScaleFactor, Slice divisor, int divisorScaleFactor)
    {
        return remainder(
                getRawLong(dividend, 0), getRawLong(dividend, 1), dividendScaleFactor,
                getRawLong(divisor, 0), getRawLong(divisor, 1), divisorScaleFactor);
    }

    private static Slice remainder(long dividendLow, long dividendHigh, int dividendScaleFactor, long divisorLow, long divisorHigh, int divisorScaleFactor)
    {
        Slice quotient = unscaledDecimal();
        Slice remainder = unscaledDecimal();
        divide(dividendLow, dividendHigh, dividendScaleFactor, divisorLow, divisorHigh, divisorScaleFactor, quotient, remainder);
        return remainder;
    }

    // visible for testing
    static void divide(Slice dividend, int dividendScaleFactor, Slice divisor, int divisorScaleFactor, Slice quotient, Slice remainder)
    {
        divide(getRawLong(dividend, 0), getRawLong(dividend, 1), dividendScaleFactor, getRawLong(divisor, 0), getRawLong(divisor, 1), divisorScaleFactor, quotient, remainder);
    }

    private static void divide(long dividendLow, long dividendHigh, int dividendScaleFactor, long divisorLow, long divisorHigh, int divisorScaleFactor, Slice quotient, Slice remainder)
    {
        if (compare(divisorLow, divisorHigh, 0, 0) == 0) {
            throwDivisionByZeroException();
        }

        if (dividendScaleFactor >= MAX_PRECISION) {
            throwOverflowException();
        }

        if (divisorScaleFactor >= MAX_PRECISION) {
            throwOverflowException();
        }

        boolean dividendIsNegative = isNegative(dividendHigh);
        boolean divisorIsNegative = isNegative(divisorHigh);
        boolean quotientIsNegative = (dividendIsNegative != divisorIsNegative);

        // to fit 128b * 128b * 32b unsigned multiplication
        int[] dividend = new int[NUMBER_OF_INTS * 2 + 1];
        dividend[0] = lowInt(dividendLow);
        dividend[1] = highInt(dividendLow);
        dividend[2] = lowInt(dividendHigh);
        dividend[3] = (highInt(dividendHigh) & ~SIGN_INT_MASK);

        if (dividendScaleFactor > 0) {
            shiftLeftBy5Destructive(dividend, dividendScaleFactor);
            shiftLeftMultiPrecision(dividend, NUMBER_OF_INTS * 2, dividendScaleFactor);
        }

        int[] divisor = new int[NUMBER_OF_INTS * 2];
        divisor[0] = lowInt(divisorLow);
        divisor[1] = highInt(divisorLow);
        divisor[2] = lowInt(divisorHigh);
        divisor[3] = (highInt(divisorHigh) & ~SIGN_INT_MASK);

        if (divisorScaleFactor > 0) {
            shiftLeftBy5Destructive(divisor, divisorScaleFactor);
            shiftLeftMultiPrecision(divisor, NUMBER_OF_INTS * 2, divisorScaleFactor);
        }

        int[] multiPrecisionQuotient = new int[NUMBER_OF_INTS * 2];
        divideUnsignedMultiPrecision(dividend, divisor, multiPrecisionQuotient);

        packUnsigned(multiPrecisionQuotient, quotient);
        packUnsigned(dividend, remainder);
        setNegative(quotient, quotientIsNegative);
        setNegative(remainder, dividendIsNegative);
        throwIfOverflows(quotient);
        throwIfOverflows(remainder);
    }

    /**
     * Value must have a length of 8
     */
    private static void shiftLeftBy5Destructive(int[] value, int shift)
    {
        if (shift <= MAX_POWER_OF_FIVE_INT) {
            multiply256Destructive(value, POWERS_OF_FIVES_INT[shift]);
        }
        else if (shift < MAX_POWER_OF_TEN_LONG) {
            multiply256Destructive(value, POWERS_OF_FIVE_LONG[shift]);
        }
        else {
            multiply256Destructive(value, POWERS_OF_FIVE[shift]);
        }
    }

    /**
     * Divides mutableDividend / mutable divisor
     * Places quotient in first argument and reminder in first argument
     */
    private static void divideUnsignedMultiPrecision(int[] dividend, int[] divisor, int[] quotient)
    {
        checkArgument(dividend.length == NUMBER_OF_INTS * 2 + 1);
        checkArgument(divisor.length == NUMBER_OF_INTS * 2);
        checkArgument(quotient.length == NUMBER_OF_INTS * 2);

        int divisorLength = digitsInIntegerBase(divisor);
        int dividendLength = digitsInIntegerBase(dividend);

        if (dividendLength < divisorLength) {
            return;
        }

        if (divisorLength == 1) {
            int remainder = divideUnsignedMultiPrecision(dividend, dividendLength, divisor[0]);
            checkState(dividend[dividend.length - 1] == 0);
            arraycopy(dividend, 0, quotient, 0, quotient.length);
            fill(dividend, 0);
            dividend[0] = remainder;
            return;
        }

        // normalize divisor. Most significant divisor word must be > BASE/2
        // effectively it can be achieved by shifting divisor left until the leftmost bit is 1
        int nlz = Integer.numberOfLeadingZeros(divisor[divisorLength - 1]);
        shiftLeftMultiPrecision(divisor, divisorLength, nlz);
        int normalizedDividendLength = Math.min(dividend.length, dividendLength + 1);
        shiftLeftMultiPrecision(dividend, normalizedDividendLength, nlz);

        divideKnuthNormalized(dividend, normalizedDividendLength, divisor, divisorLength, quotient);

        // un-normalize remainder which is stored in dividend
        shiftRightMultiPrecision(dividend, normalizedDividendLength, nlz);
    }

    private static void divideKnuthNormalized(int[] remainder, int dividendLength, int[] divisor, int divisorLength, int[] quotient)
    {
        int v1 = divisor[divisorLength - 1];
        int v0 = divisor[divisorLength - 2];
        for (int reminderIndex = dividendLength - 1; reminderIndex >= divisorLength; reminderIndex--) {
            int qHat = estimateQuotient(remainder[reminderIndex], remainder[reminderIndex - 1], remainder[reminderIndex - 2], v1, v0);
            if (qHat != 0) {
                boolean overflow = multiplyAndSubtractUnsignedMultiPrecision(remainder, reminderIndex, divisor, divisorLength, qHat);
                // Add back - probability is 2**(-31). R += D. Q[digit] -= 1
                if (overflow) {
                    qHat--;
                    addUnsignedMultiPrecision(remainder, reminderIndex, divisor, divisorLength);
                }
            }
            quotient[reminderIndex - divisorLength] = qHat;
        }
    }

    /**
     * Use the Knuth notation
     *

     * u{x} - dividend
     * v{v} - divisor
     */
    private static int estimateQuotient(int u2, int u1, int u0, int v1, int v0)
    {
        // estimate qhat based on the first 2 digits of divisor divided by the first digit of a dividend
        long u21 = combineInts(u2, u1);
        long qhat;
        if (u2 == v1) {
            qhat = INT_BASE - 1;
        }
        else if (u21 >= 0) {
            qhat = u21 / toUnsignedLong(v1);
        }
        else {
            qhat = divideUnsignedLong(u21, v1);
        }

        if (qhat == 0) {
            return 0;
        }

        // Check if qhat is greater than expected considering only first 3 digits of a dividend
        // This step help to eliminate all the cases when the estimation is greater than q by 2
        // and eliminates most of the cases when qhat is greater than q by 1
        //
        // u2 * b * b + u1 * b + u0 >= (v1 * b + v0) * qhat
        // u2 * b * b + u1 * b + u0 >= v1 * b * qhat + v0 * qhat
        // u2 * b * b + u1 * b - v1 * b * qhat >=  v0 * qhat - u0
        // (u21 - v1 * qhat) * b >=  v0 * qhat - u0
        // (u21 - v1 * qhat) * b + u0 >=  v0 * qhat
        // When ((u21 - v1 * qhat) * b + u0) is less than (v0 * qhat) decrease qhat by one

        int iterations = 0;
        long rhat = u21 - toUnsignedLong(v1) * qhat;
        while (Long.compareUnsigned(rhat, INT_BASE) < 0 && Long.compareUnsigned(toUnsignedLong(v0) * qhat, combineInts(lowInt(rhat), u0)) > 0) {
            iterations++;
            qhat--;
            rhat += toUnsignedLong(v1);
        }

        if (iterations > 2) {
            throw new IllegalStateException("qhat is greater than q by more than 2: " + iterations);
        }

        return (int) qhat;
    }

    private static long divideUnsignedLong(long dividend, int divisor)
    {
        long unsignedDivisor = toUnsignedLong(divisor);

        if (dividend > 0) {
            return dividend / unsignedDivisor;
        }

        // HD 9-3, 4) q = divideUnsigned(n, 2) / d * 2
        long quotient = ((dividend >>> 1) / unsignedDivisor) * 2;
        long remainder = dividend - quotient * unsignedDivisor;

        if (Long.compareUnsigned(remainder, unsignedDivisor) >= 0) {
            quotient++;
        }

        return quotient;
    }

    /**
     * Calculate multi-precision [left - right * multiplier] with given left offset and length.
     * Return true when overflow occurred
     */
    private static boolean multiplyAndSubtractUnsignedMultiPrecision(int[] left, int leftOffset, int[] right, int length, int multiplier)
    {
        long unsignedMultiplier = toUnsignedLong(multiplier);
        int leftIndex = leftOffset - length;
        long multiplyAccumulator = 0;
        long subtractAccumulator = INT_BASE;
        for (int rightIndex = 0; rightIndex < length; rightIndex++, leftIndex++) {
            multiplyAccumulator = toUnsignedLong(right[rightIndex]) * unsignedMultiplier + multiplyAccumulator;
            subtractAccumulator = (subtractAccumulator + toUnsignedLong(left[leftIndex])) - toUnsignedLong(lowInt(multiplyAccumulator));
            multiplyAccumulator = high(multiplyAccumulator);
            left[leftIndex] = lowInt(subtractAccumulator);
            subtractAccumulator = high(subtractAccumulator) + INT_BASE - 1;
        }
        subtractAccumulator += toUnsignedLong(left[leftIndex]) - multiplyAccumulator;
        left[leftIndex] = lowInt(subtractAccumulator);
        return highInt(subtractAccumulator) == 0;
    }

    private static void addUnsignedMultiPrecision(int[] left, int leftOffset, int[] right, int length)
    {
        int leftIndex = leftOffset - length;
        int carry = 0;
        for (int rightIndex = 0; rightIndex < length; rightIndex++, leftIndex++) {
            long accumulator = toUnsignedLong(left[leftIndex]) + toUnsignedLong(right[rightIndex]) + toUnsignedLong(carry);
            left[leftIndex] = lowInt(accumulator);
            carry = highInt(accumulator);
        }
        left[leftIndex] += carry;
    }

    // visible for testing
    static int[] shiftLeftMultiPrecision(int[] number, int length, int shifts)
    {
        if (shifts == 0) {
            return number;
        }
        // wordShifts = shifts / 32
        int wordShifts = shifts >>> 5;
        // we don't wan't to loose any leading bits
        for (int i = 0; i < wordShifts; i++) {
            checkState(number[length - i - 1] == 0);
        }
        if (wordShifts > 0) {
            arraycopy(number, 0, number, wordShifts, length - wordShifts);
            fill(number, 0, wordShifts, 0);
        }
        // bitShifts = shifts % 32
        int bitShifts = shifts & 0b11111;
        if (bitShifts > 0) {
            // we don't wan't to loose any leading bits
            checkState(number[length - 1] >>> (Integer.SIZE - bitShifts) == 0);
            for (int position = length - 1; position > 0; position--) {
                number[position] = (number[position] << bitShifts) | (number[position - 1] >>> (Integer.SIZE - bitShifts));
            }
            number[0] = number[0] << bitShifts;
        }
        return number;
    }

    // visible for testing
    static int[] shiftRightMultiPrecision(int[] number, int length, int shifts)
    {
        if (shifts == 0) {
            return number;
        }
        // wordShifts = shifts / 32
        int wordShifts = shifts >>> 5;
        // we don't wan't to loose any trailing bits
        for (int i = 0; i < wordShifts; i++) {
            checkState(number[i] == 0);
        }
        if (wordShifts > 0) {
            arraycopy(number, wordShifts, number, 0, length - wordShifts);
            fill(number, length - wordShifts, length, 0);
        }
        // bitShifts = shifts % 32
        int bitShifts = shifts & 0b11111;
        if (bitShifts > 0) {
            // we don't wan't to loose any trailing bits
            checkState(number[0] << (Integer.SIZE - bitShifts) == 0);
            for (int position = 0; position < length - 1; position++) {
                number[position] = (number[position] >>> bitShifts) | (number[position + 1] << (Integer.SIZE - bitShifts));
            }
            number[length - 1] = number[length - 1] >>> bitShifts;
        }
        return number;
    }

    private static int divideUnsignedMultiPrecision(int[] dividend, int dividendLength, int divisor)
    {
        if (divisor == 0) {
            throwDivisionByZeroException();
        }

        if (dividendLength == 1) {
            long dividendUnsigned = toUnsignedLong(dividend[0]);
            long divisorUnsigned = toUnsignedLong(divisor);
            long quotient = dividendUnsigned / divisorUnsigned;
            long remainder = dividendUnsigned - (divisorUnsigned * quotient);
            dividend[0] = (int) quotient;
            return (int) remainder;
        }

        long divisorUnsigned = toUnsignedLong(divisor);
        long remainder = 0;
        for (int dividendIndex = dividendLength - 1; dividendIndex >= 0; dividendIndex--) {
            remainder = (remainder << 32) + toUnsignedLong(dividend[dividendIndex]);
            long quotient = divideUnsignedLong(remainder, divisor);
            dividend[dividendIndex] = (int) quotient;
            remainder = remainder - (quotient * divisorUnsigned);
        }
        return (int) remainder;
    }

    private static int digitsInIntegerBase(int[] digits)
    {
        int length = digits.length;
        while (length > 0 && digits[length - 1] == 0) {
            length--;
        }
        return length;
    }

    private static long combineInts(int high, int low)
    {
        return (((long) high) << 32) | toUnsignedLong(low);
    }

    private static long high(long value)
    {
        return value >>> 32;
    }

    private static long low(long value)
    {
        return value & LOW_32_BITS;
    }

    private static int highInt(long val)
    {
        return (int) (high(val));
    }

    private static int lowInt(long val)
    {
        return (int) val;
    }

    private static void packUnsigned(int[] digits, Slice decimal)
    {
        if (digitsInIntegerBase(digits) > NUMBER_OF_INTS) {
            throwOverflowException();
        }
        if ((digits[3] & SIGN_INT_MASK) != 0) {
            throwOverflowException();
        }
        pack(decimal, digits[0], digits[1], digits[2], digits[3], false);
    }

    private static boolean divideCheckRound(Slice decimal, int divisor, Slice result)
    {
        int remainder = divide(decimal, divisor, result);
        return (remainder >= (divisor >> 1));
    }

    // visible for testing
    static int divide(Slice decimal, int divisor, Slice result)
    {
        if (divisor == 0) {
            throwDivisionByZeroException();
        }
        checkArgument(divisor > 0);

        long remainder = getLong(decimal, 1);
        long high = remainder / divisor;
        remainder %= divisor;

        remainder = toUnsignedLong(getInt(decimal, 1)) + (remainder << 32);
        int z1 = (int) (remainder / divisor);
        remainder %= divisor;

        remainder = toUnsignedLong(getInt(decimal, 0)) + (remainder << 32);
        int z0 = (int) (remainder / divisor);

        pack(result, z0, z1, high, isNegative(decimal));
        return (int) (remainder % divisor);
    }

    private static void throwDivisionByZeroException()
    {
        throw new ArithmeticException("Division by zero");
    }

    private static void setNegative(Slice decimal, boolean negative)
    {
        setRawInt(decimal, SIGN_INT_INDEX, getInt(decimal, SIGN_INT_INDEX) | (negative ? SIGN_INT_MASK : 0));
    }

    private static void setNegativeInt(Slice decimal, int v3, boolean negative)
    {
        if (v3 < 0) {
            throwOverflowException();
        }
        setRawInt(decimal, SIGN_INT_INDEX, v3 | (negative ? SIGN_INT_MASK : 0));
    }

    private static void setNegativeLong(Slice decimal, long high, boolean negative)
    {
        setRawLong(decimal, SIGN_LONG_INDEX, high | (negative ? SIGN_LONG_MASK : 0));
    }

    private static void copyUnscaledDecimal(Slice from, Slice to)
    {
        setRawLong(to, 0, getRawLong(from, 0));
        setRawLong(to, 1, getRawLong(from, 1));
    }

    private static void pack(Slice decimal, int v0, int v1, int v2, int v3, boolean negative)
    {
        setRawInt(decimal, 0, v0);
        setRawInt(decimal, 1, v1);
        setRawInt(decimal, 2, v2);
        setNegativeInt(decimal, v3, negative);
    }

    private static void pack(Slice decimal, int v0, int v1, long high, boolean negative)
    {
        setRawInt(decimal, 0, v0);
        setRawInt(decimal, 1, v1);
        setNegativeLong(decimal, high, negative);
    }

    private static void pack(Slice decimal, long low, long high, boolean negative)
    {
        setRawLong(decimal, 0, low);
        setNegativeLong(decimal, high, negative);
    }

    public static Slice pack(long low, long high, boolean negative)
    {
        Slice decimal = unscaledDecimal();
        pack(low, high, negative, decimal);
        return decimal;
    }

    public static void pack(long low, long high, boolean negative, Slice result)
    {
        setRawLong(result, 0, low);
        setRawLong(result, 1, high | (negative ? SIGN_LONG_MASK : 0));
    }

    public static void pack(long low, long high, boolean negative, Slice result, int resultOffset)
    {
        result.setLong(resultOffset, low);

        long value = high | (negative ? SIGN_LONG_MASK : 0);
        result.setLong(resultOffset + SIZE_OF_LONG, value);
    }

    public static void pack(long low, long high, boolean negative, long[] result, int resultOffset)
    {
        result[resultOffset] = low;
        long value = high | (negative ? SIGN_LONG_MASK : 0);
        result[resultOffset + 1] = value;
    }

    public static void throwOverflowException()
    {
        throw new ArithmeticException("Decimal overflow");
    }

    public static int getInt(Slice decimal, int index)
    {
        int value = getRawInt(decimal, index);
        if (index == SIGN_INT_INDEX) {
            value &= ~SIGN_INT_MASK;
        }
        return value;
    }

    public static long getLong(Slice decimal, int index)
    {
        long value = getRawLong(decimal, index);
        if (index == SIGN_LONG_INDEX) {
            return unpackUnsignedLong(value);
        }
        return value;
    }

    private static boolean exceedsOrEqualTenToThirtyEight(Slice decimal)
    {
        // 10**38=
        // i0 = 0(0), i1 = 160047680(98a22400), i2 = 1518781562(5a86c47a), i3 = 1262177448(4b3b4ca8)
        // low = 0x98a2240000000000l, high = 0x4b3b4ca85a86c47al
        long high = getLong(decimal, 1);
        if (high >= 0 && high < 0x4b3b4ca85a86c47aL) {
            return false;
        }
        if (high != 0x4b3b4ca85a86c47aL) {
            return true;
        }

        long low = getLong(decimal, 0);
        return low < 0 || low >= 0x098a224000000000L;
    }

    private static void reverse(final byte[] a)
    {
        final int length = a.length;
        for (int i = length / 2; i-- != 0; ) {
            final byte t = a[length - i - 1];
            a[length - i - 1] = a[i];
            a[i] = t;
        }
    }

    private static void setToZero(Slice decimal)
    {
        for (int i = 0; i < NUMBER_OF_LONGS; i++) {
            setRawLong(decimal, i, 0);
        }
    }

    private static int unpackUnsignedInt(int value)
    {
        return value & ~SIGN_INT_MASK;
    }

    private static long unpackUnsignedInt(long value)
    {
        return value & ~SIGN_INT_MASK;
    }

    private static long unpackUnsignedLong(long value)
    {
        return value & ~SIGN_LONG_MASK;
    }

    private static int getRawInt(Slice decimal, int index)
    {
        return decimal.getInt(SIZE_OF_INT * index);
    }

    private static void setRawInt(Slice decimal, int index, int value)
    {
        decimal.setInt(SIZE_OF_INT * index, value);
    }

    private static long getRawLong(Slice decimal, int index)
    {
        return decimal.getLong(SIZE_OF_LONG * index);
    }

    private static void setRawLong(Slice decimal, int index, long value)
    {
        decimal.setLong(SIZE_OF_LONG * index, value);
    }

    /**
     * Based on Long.compareUnsigned()
     */
    private static boolean unsignedIsSmaller(long first, long second)
    {
        return first + Long.MIN_VALUE < second + Long.MIN_VALUE;
    }

    private static void checkArgument(boolean condition)
    {
        if (!condition) {
            throw new IllegalArgumentException();
        }
    }

    private static void checkState(boolean condition)
    {
        if (!condition) {
            throw new IllegalStateException();
        }
    }

    private UnscaledDecimal128Arithmetic() {}
}


text:
Subject tag: Road safety
How do we share equitably the public spaces between buildings that we call streets?
Rod King, the founder of 20's Plenty for Us, sets out the case for a standard urban and village speed limit of 20mph and describes the road to getting there.
What future for e-scooters in London?
E-scooters could play a role in ensuring a green and sustainable future for London, helping people to use private cars less. Yet many worry about their use, fearing an increase in accidents and reduction in physical activity.
Cabinet asked to approve Healthy Streets Framework
At their meeting on Wednesday 16th June Enfield's Cabinet will be invited to endorse a newly drafted Healthy Streets Framework, intended for use in developing and delivering Healthy Streets projects designed to enable more walking and cycling and reduce road danger.
Climate Safe Streets
For transport to become environmentally sustainable we need to shift a significant proportion of journeys to non-polluting modes - walking, cycling or public transport. But doing so can also improve our environment in other ways, making it safer, cleaner, quieter, more sociable. A short film, partly filmed in Palmers Green, gives a glimpse of how we can have better streets.
'Challenge your friends, your family, your colleagues not to speed, and make it unacceptable'
In the week that we were all shocked by the killing of a tiny baby by a dangerous driver, we need to hammer home the message of London's former top traffic cop in this interview for Crimewatch Live: 'Don't speed, challenge your friends, your family, your colleagues not to speed, and make it unacceptable. '
The next mayor should create a 'more natural capital'
Ahead of the mayoral election on 6th May, a coalition of leading environmental groups have published a joint manifesto outlining ambitious programmes of action needed to reduce pollution and waste, improve health and wellbeing, secure nature's recovery and increase resilience to climate change. There will be an opportunity to discuss these proposals with the candidates at the Mayoral Environment Debate, which will take place online on 12th April.
Please support LTNs – they are for everyone's safety and wellbeing
An article by Better Streets for Enfield presenting data showing that low-traffic neighbourhoods reduce road danger, allow more people to cycle, reduce car ownership, are not socially unjust and do not hinder emergency vehicles.
120 doctors and nurses urge continuation of low traffic neighbourhoods and cycle lanes schemes
More than 120 medical professionals, concerned about 'the adverse impact that motor vehicles have on our patients and the broader community', have written to the Mayor of London in support of low-traffic neighbourhoods and cycle lanes.
Calling primary school kids: Design a greener school run poster!
Sometimes it can take a childs-eye view to see how we can do things better, and a new competition for young budding transport planners, launched today, is intended to take advantage of this to help us speed up the decarbonisation of transport and in particular of the school run. The national walking charity Living Streets is teaming up with the Transport Planning Society to offer a prize for the best poster designed by a child of primary school age showing environmentally friendly ways to travel to school.
'Our streets, our journeys'
All of us, to greater or lesser extent, shape the world that children have to grow up in. And, without our realising it, our lifestyle and behaviour might be contributing to the problems that lead to so many children growing up obese, unfit, unhealthy and with poor mental health. Instead of blaming their parents, we should consider whether we are part of the problem.
New school street in Winchmore Hill starts on Monday
A school street scheme comes into force on Monday 7th September outside St Paul's primary school in Ringwood Way, Winchmore Hill. Between 8.15 and 9.15 in the morning and 2.45 and 3.45 in the afternoon Ringwood Way will be a pedestrian and cycle zone between its junctions with Station Road and Shrubbery Gardens. This normally busy cut-through will be closed to all vehicles other than those with specific exemptions.
Fox Lane low-traffic neighbourhood moves a step closer
A public notice published in this week's Enfield Independent gives notice that five experimental traffic orders relating to the Fox Lane Quieter Neighbourhood Area will come into effect on 7th September.
Taking to the streets in 2020
Hal Haines draws a parallel between last week's protests against Enfield's first low-traffic neighbourhood and the situation in the Netherlands 50 years ago, when Amsterdam's streets too were dominated by cars. The Stop the Child Killings campaign that began in 1971 has shaped road design in the Netherlands ever since, making it unthinkable not to design out the possibility of taking a short cut through residential areas. The UK solved the child deaths problem by turning streets into no-go areas for children, but at a cost in terms of health, both physical and mental.
National Road Victims Month: Time to start doing something about the terror on our roads
London's top traffic cop has reminded us that August is National Road Victims Month and asked us to watch a short film made by the charity RoadPeace. In the UK five people a day are killed on the roads and more than 60 seriously injured. Scandinavian cities, pursuing a Vision Zero policy, have almost eliminated road deaths.
New plans for Fox Lane quieter neighbourhood revealed
Enfield Council's revised plans for the Fox Lane quieter neighbourhood, which were published today, will dramatically reduce traffic and associated pollution, noise and road danger over a large area of residential streets stretching from Palmers Green to Southgate. Subject to approval by the deputy council leader, the proposals will be implemented on a trial basis for six months starting this summer.
Covid Streetspace: Bike lanes, school streets, low-traffic neighbourhoods and more
Enfield Council has published information about its progress in obtaining funding for urgent Streetspace schemes, designed to enable more walking and cycling post-lockdown in a situation where people will be unable or reluctant to use public transport. A document recently published on the council's Let's Talk website also includes updates about the status of planned low-traffic neighbourhoods in the Fox Lane, Connaught Gardens and Bowes Primary areas.
Making more Streetspace for people
Enfield Council has submitted a bid to TfL for funding for a number of Covid Streetspace measures: pop-up cycle lanes, quieter neighbourhoods, modal filters to block through traffic, school streets and temporary footway widening. A revised design has been developed for the Fox Lane Quieter Neighbourhood and will be shared with the community prior to implementation.
Introducing Healthy Streets Bounds Green
Viola Rondeboom of Healthy Streets Bounds Green introduces the new group and its ideas for creating a healthier environment in the residential streets between Bounds Green Road and Bowes Road.
Government tells councils to reallocate roadspace for walking and cycling 'as swiftly as possible'
The government has told local authorities that it expects them to take urgent measures to reallocate roadspace away from cars to provide more room for walking and cycling 'as swiftly as possible, and in any event within weeks'. They are to include 'pop-up' cycle lanes with light segregation features, more school streets, lower speed limits, pedestrian and cycle zones that exclude motor traffic, low-traffic residential neighbourhoods, and bus and cycle corridors along key routes into town and city centres. Enfield Council's 'Streetspace Plan', announced last week, will incorporate measures of this sort - residents are able to upload their own suggestions on the council's Consultation Hub.
Freeing up streetspace throughout London - including Enfield
Transport for London's Streetspace Programme, announced on Wednesday, is intended to rapidly transform the capital's streets to accommodate a possible ten-fold increase in cycling and five-fold increase in walking when lockdown restrictions are eased. Many boroughs have already started on their own similar schemes - Enfield's was announced on Tuesday. Measures of this kind have already been endorsed by the prime minister and business secretary.

redpajama_set_name: RedPajamaCommonCrawl


text:
Chickasaw Community Bank embraces mission to build better lives for everyone
Jim Stafford
For The Oklahoman
Chickasaw Community Bank was established in 2002 with a mission of building better lives for everyone, says T.W. Shannon, the bank's president and CEO.
The Oklahoman's special section on Native Pride:Read all the stories here
"It sounds a little hokey or altruistic. The reality is we're on the ground floor of helping people's dreams come true, whether it's the commercial lending space or helping someone with their first home or their dream home," Shannon said. "It's very, very rewarding."
The ongoing COVID-19 pandemic provided the Oklahoma City-based bank an opportunity over the past year to prove that mission was more than a slogan both to its tribal customers and the community at large.
Chickasaw Community Bank was an early proponent of the Small Business Administration's Paycheck Protection Program (PPP) during the pandemic. PPP is a federally backed small business loan program intended to help small businesses survive the economic fallout brought on by social distancing requirements to counter the spread of COVID-19 virus.
"Here we are almost 20 years of the bank's history, and we got to see in real action the importance and value of the bank during COVID," Shannon said. "We were one of the first banks to be involved with the PPP program, and we were one of the first banks to expand it outside of our customers."
Chickasaw Gov. Bill Anoatubby had a vision for a tribe-owned bank in the Oklahoma City area when Chickasaw Community Bank came to life as Bank 2 in 2002. It started with $7.5 million in assets and has grown to more than $300 million today.
The bank has only one branch location at 909 S Meridian Ave., along with loan production offices in Tulsa and Nichols Hills. It employs about 110 people.
Before joining the bank in 2017, Shannon served eight years in the Oklahoma Legislature as a state representative from Lawton and is a former speaker of the House. An attorney, he was teaching a course at Harvard University as a Harvard Fellow when he received a call from Anoatubby about the banking position.
"Gov. Anoatubby asked me if I would be interested in leading the bank," Shannon said. "I said to him, 'Governor, you know I've never run a bank before.' He said 'We know that, but what we do know about you, TW, is that you understand relationships and understand leadership, and we think your skillset would be a great addition to the bank'."
After some prayerful consideration, Shannon accepted the position.
"I thought, 'Who am I to question Gov. Anoatubby's judgment,'" Shannon said. "He's probably the most visionary leader in this generation and maybe in Oklahoma of all time. I came on board in 2017, and we put together a great team, and we've had tremendous success."
Shannon oversaw the name change from Bank 2 to Chickasaw Community Bank in part to align it with the well-known Chickasaw Nation brand, he said. The bank has become a national leader in providing home loans to tribal members under the federal HUD 184 Indian Home Loan Guarantee Program.
"We are the foremost expert in the state, and really nationally, for the HUD 184 loan program," he said. "We have customers in nearly all 50 states. So, we have a special mission in Indian Country for that reason, especially for Chickasaw citizens."
Chickasaw Community Bank not only survived the challenges of the COVID-19 pandemic, but thrived over the past year as it tackled the PPP loan program, Shannon said.
"While many of the too-big-to-fail banks were turning people away, we were embracing them because we felt like it was part of our mission to do it," he said. "We had a terrific year and are expecting great things for the bank and continuing our mission of building better lives for everyone."


text:
Rowena Green
In this interview, Rowena discusses her general childhood experiences in the Southfield-Plymouth area during the 1960's and 70's.
Rowena Green was born May 20, 1962, and grew up in the Southfield-Plymouth neighborhood during the 60's and 70's. She currently lives in Lincoln Park, Michigan, where she moved in the late 1980's.
Kenny Lowe
[Start of Track 1]
[INITIALS OF INTERVIEWEE: R.G.]
[INITIALS OF INTERVIEWER: K.L.]
K.L.: This is Kenny Lowe conducting an oral history with Rowena Green at 5:17 P.M. on October 12, 2018 [at] the Detroit Historical Society.
K.L.: So, when were you born and where were you born?
R.G.: I was born on May 20, 1962 at St. Joseph Hospital in Detroit, Michigan.
K.L.: Alright, what neighborhood did you grow up in?
R.G.: I grew up in the neighborhood of Plymouth and Southfield.
K.L.: How would you describe the physical boundaries of Southfield-Plymouth, as in the roads and where exactly the borders of the neighborhood came to?
R.G.: We'll go Plymouth to Wadsworth and Greenfield to Southfield.
K.L.: That's how you describe Southfield-Plymouth?
R.G.: Yeah.
K.L.: What do you recall about, what was it like? What was the social environment in Southfield-Plymouth at the time that you can recall?
R.G.: It was just, it was your average neighborhood. We had a party store and we had a movie theater and a hardware store. We would, you know, walk up to the hardware store and party store and get things, or go to the movie theater on Saturday afternoons. We had a community center at the end of the block where we would go roller skating and ice skating. I learned how to tap dance there. I learned how to do ballet there.
K.L.: So [those were] the main ways that you would seek entertainment as a child?
R.G.: Yup.
K.L.: So, in what respects does [Southfield-Plymouth] distinguish itself from the rest of Detroit, do you think?
R.G.: I think, back then, it was known for the trees because they had huge elm trees in front of each one of the houses, so when you drive down the street, it was like a canopy of trees.
K.L.: Yes?
R.G.: And that would be the one thing that people would always say when they'd come over and visit, you know, "Your streets are so beautiful 'cause it's just a canopy of trees," because they all kind of came over and kind of just made this beautiful tunnel.
K.L.: What social groups do you think were represented and in what proportions in Southfield-Plymouth? Such as like ethnic groups, religious groups, occupational groups, socioeconomic?
R.G.: Well, when I was growing up, until, I'm going to say, the first ten years of my life, my neighborhood was all-white.
K.L.: All-white?
R.G.: We had no African-Americans, we had no Asians, it was just all-white. The gentlemen either worked in the plants, or they worked in a factory, or they went to work in an office. And the moms generally stayed home.
K.L.: Okay. Has the size and shape or image of [Southfield-Plymouth] changed over the years?
R.G.: I went back a couple years ago. It's really sad.
K.L.: Yeah, how do you think it's changed? And how drastic do you think that change was?
R.G.: It was a huge change. There's no more community center at the end of the street, it's been replaced with solar panels. The houses, a lot of them are gone, a lot of them are boarded up, there's no movie theater anymore. There's no little party store at the end of the street. Unfortunately, it looks like a lot of Detroit – it looks like a lot of the neighborhoods have been forgotten in the city.
K.L.: So, I'm going to move on to your childhood. What were your parents' occupations during your childhood in the Southfield-Plymouth area?
R.G.: My mom worked at a grocery store, and my dad was in the military.
K.L.: Military?
R.G.: He was in the army.
K.L.: Where did you shop? What kind of stores did you go to, aside from the aforementioned party store?
R.G.: We had a Farmer Jack at the corner of Plymouth and Southfield. And there was a Kowalski deli store and a plaza on Joy Road and Greenfield.
K.L.: Okay, where do you go to school during your childhood?
R.G.: My elementary school was Coolidge Elementary, and then I went to Lessenger Middle School and then I went to Ruddiman Middle School. And I graduated from Cody High School in 1980.
K.L.: 1980, okay.
K.L.: Do you recall anything about the schools that you did go to?
R.G.: I recall that the elementary school was an elementary school, I mean, there was nothing really special about that. When I started going to the middle school, Lessenger, that was when I really first started having friends – African-American friends – because before, it was always white friends.
K.L.: When were your middle school years?
R.G.: '74, '72, '74?
K.L.: Okay.
R.G.: Yeah, right around there.
K.L.: Alrighty.
R.G.: Yep.
K.L.: So, did you venture to other neighborhoods in Detroit or did you prefer to stay within the confines of the [Southfield-Plymouth] area?
R.G.: No, we would go out on Sunday drives, and we would, you know, drive up and down the boulevard, we would go to Belle Isle. We would drive up and down Jefferson. We [inaudible] all kinds of different neighborhoods, just drive around and see things.
K.L: Yeah, do you remember what Detroit was like in the late '60's, early '70's?
R.G.: Prior to '67, we would go down to Woodward Avenue, we would do our shopping there sometimes. We would go to Belle Isle. We would go to Cobo – Cobo used to have these huge Christmas events. After '67, things changed, people didn't want to come downtown as much, they didn't feel safe.
K.L: Mhm.
R.G: So, we didn't do as much down here – we still did, we came down, but I don't think we came down as much. And it changed a lot as far as the way people looked at it. So, I just remember driving up and down the boulevard – Westgrand Boulevard – and the houses and how beautiful they were and driving up and down Jefferson, especially wintertime, you know, Christmas, you drive around and look at all the lights and everything.
K.L.: Mhm.
R.G.: Yeah, so it was different after '67 because so many people left.
K.L.: Yeah, because of how dangerous it felt to be – how unsafe and dangerous it felt to be in Detroit?
R.G.: Right. And we never felt that way. We never felt when we would come down that we were in any type of danger or anything, but there weren't a lot – some of the things changed, like we used to go – there was a Sears at Open Boulevard, we used to go every Christmas, that had this huge train set. Well, as the neighborhood started to change, Sears closed down, so you didn't have that place to go in the wintertime. And then, Cobo stopped having their winter events for the kids, and then it was just kind of like a snowball effect.
K.L.: Yeah, I see. Going back to your parents for a brief moment, do you know when your parents were born?
R.G.: My mom was born in 1936, and – well, they both born the same year; they went to high school together.
K.L: Oh, wow!
R.G.: Yeah, they went to high school together. My mom graduated from Mackenzie High School in 1955 and my dad did the same year.
K.L.: And then, do you have any siblings?
R.G.: Nope, I'm an only child.
K.L.: Do you know when they got married?
R.G.: I'm going to say they got married in 1961? Because I was married [born] in 1962.
K.L.: Are there any childhood stories that took place in [Southfield-Plymouth] that you'd like to share with me?
R.G.: I just remember how much fun it was, you know, walking up to the corner store. And I used to go up there with my grandfather, and we'd go up there and get a pop or something or go to the movies. At the time, a bunch of us would go to the movies, and we'd walk to the movies, and we'd see a scary movie and then come home. And I remember too, back then, you could burn your leaves, so you would gather all your leaves up and you could burn your leaves in front of your house.
K.L.: Oh, wow!
R.G.: Yeah, they didn't come by and pick them up. And I know this sounds really crazy, but the street used to flood when it rained a lot, so all of us kids would get in there and run around and ride our bikes through it and things like that.
K.L.: Through puddles and whatnot?
R.G.: Yeah, flooded streets, all that sewer water and stuff. Back then, it was different for us 'cause we didn't have all the chemicals and stuff that they have now, so you'd ride your bikes through it.
K.L.: That's so neat. Do you remember what the movie theater and ice skating rink that you went to, do you recall what they were named?
R.G.: I don't remember what the movie theater was, but I want to say that the community center was Stoepel Park; I could be wrong. It was right at the end of my street, like I said, it's not there anymore. When I went by there the last time, there was all – they have all kinds of solar panels there now.
K.L.: Yeah, so, do you think that there's any more childhood stories that you could think of? Like do you have anything specific that you have in mind, or is that kind of at the end of it?
R.G.: Well, it's not really childhood, but I know that when I started going to Cody High School, it was during the time where the schools were having a lot of issues, and they were calling them "riots." And at the time, I remember us being in front of the school one morning, waiting to go in – and you could see a bunch of kids coming up across the football field – and we tried to get back into the school and they wouldn't let us in. So, you know, there were altercations there.
K.L.: Do you think that these were connected to the '67 riots?
R.G.: No, I think it was a lot of frustration because at the time, you didn't have enough books or your teachers – I had a teacher that told me one time, "As long as you come to class, you get an A." So, students were frustrated; they weren't getting the education they deserved, they didn't have the supplies – no different than it is now – so there was a lot of frustration, and there was a lot of frustration that kids were being taken from their neighborhood – they were being bused across town.
K.L.: Yeah, I was informed about the busing situation prior to interviewing you, and I was suggested to ask about it. What exactly was busing?
R.G.: Busing was where they had taken the whole city, and they looked at the whole city, and they were like, "Ugh, well, we got a lot of kids over here, so let's bus them over to this school, so we can fill this school," not thinking that you're taking the kids out of their neighborhoods, out of their environment, and busing them across town because they said, "Well, we're going to save money, this is going to be a way to increase our pupils." But you wasted all the money with the buses taking them across town, and the drivers – and a lot of the drivers were not quipped to take care of these kids. I mean, we had a girl one time have a seizure on the bus and they had to call an ambulance. And she's [the bus driver] kind of just standing there like, "Well, okay, what do I do?" 'Cause she thought the girl was acting out when in fact, she was having a seizure.
K.L.: Mm. Yeah.
R.G.: So, I mean, if you go back and you look at busing, I don't think busing did anybody any good. Yes, you met a lot of new people, no doubt about that, but I don't think it helped the way they thought it was going to.
K.L.: So, you would say it was more of a failure than a success?
R.G.: Oh yeah, I would say busing was a failure.
K.L.: Alright, moving on from your childhood, have you ever considered moving away from the Southfield-Plymouth area during your younger years?
R.G.: No, we moved in 1980 simply because the neighborhood had changed so much, and my mom was afraid she would [inaudible] ever get any money for the house. So, we moved to Southgate.
K.L.: Southgate?
R.G.: Yup, we moved to Southgate, Michigan in 1980.
K.L.: So, that said, you gave the information as to why – when and why you moved there, do you remember what Southgate was like in the '80's?
R.G.: Sounds terrible to say, but it was just an all-white neighborhood. When I got there, a lot of kids in the neighborhood had already grown up and moved away, and you know, you're coming into a neighborhood where friendships have already been established and relationships have already been established, so it was different moving there than if I had moved there when I was younger.
R.G.: The neighbors were nice, but it still was an all-white neighborhood – and it was totally different from where I came from.
K.L.: Yeah. And going back to Southfield-Plymouth, I have a couple questions about that. So, what does the word "neighborhood" mean to you?
R.G.: Neighborhood means to me the smell of burning leaves, the trees when you're driving down the street and they're all kind of – they all come together like this beautiful tunnel. Going to the end of my block and roller-skating on a Friday night at the community center, or ice-skating, or dance there. That's what it means to me. I have really good memories of growing up there.
K.L.: So, how do you feel about the state of Southfield-Plymouth today?
R.G.: It makes me cry.
K.L.: Yeah. Why would you say that it makes you upset to think about?
R.G.: You come downtown and there's so much going on, and there's so many wonderful things down here, but if you go out to the neighborhoods, to the people that have stayed here and have not turned their backs on the city, a lot of times they feel they've been forgotten. Because you go through the neighborhoods and there's still a lot of empty houses which are dangerous to the people that are here; there may be one or two people that have stayed, but those one or two people that are just as important as the people that are living downtown, and I think the city needs to recognize that, just like with the kids are going through right now with the schools; they're having to bring in water filtrations because they can't drink the water.
K.L.: Mhm, so what would you like to see happen with Southfield-Plymouth?
R.G.: I would like to see it come back as a neighborhood where families want to move back in to.
K.L.: Do you think that Southfield-Plymouth has the potential to become what it used to be again, someday, maybe not exactly?
R.G.: I hope so. Yeah, I hope so.
K.L.: If you could a project done in Southfield-Plymouth, what would it be? What kind of project would you set up to improve the neighborhood?
R.G.: I think a grocery store is very important for the citizens of Detroit – a good grocery store – Meijer has gone into Eight Mile and Woodward Area, but I think a good grocery store where they're going to get fresh food at a reasonable price, and a place where the kids can go and feel safe, where they can go down the street and play basketball or play baseball. Things like that to bring the community back together.
K.L.: How do you feel about the state of Detroit altogether?
R.G.: It's come a long way. I'm very happy about it. I love coming downtown, I mean, my son lives downtown, I try and get down here at least once a week. I come to the Eastern Market all the time, I just wander around the library and the DIA for hours. I love the city, I'm not ashamed to say that I was born and raised in the city of Detroit. I think it's a fabulous place. It's got a wonderful history, it's got a crazy history, it's got a horrible history. But I think – at least I hope – that it will continue to move forward and bring people back to it.
K.L.: Alright, so I'm going to end this segment of the interview right now, it is 5:37 in the afternoon.
[END OF INTERVIEW 00:18:59]
Detroit, Michigan, Southfield-Plymouth, Lincoln Park, 1967 riots.
"Rowena Green," Detroit Historical Society Oral History Archive, accessed January 17, 2022, https://detroit1967.detroithistorical.org/items/show/761.


Clown Loach Tropical Fish Learn all about the Clown Loach's feeding habits and food types, its behaviour, its origins, its natural habitats, is it male or female, breeding advice and information, suitable tank mates, its sizing and growth range, minimum tank size, water PH and more. Use our fish community creator tool to plan your tank set up and ensure that the Clown Loach is the right fish for your aquarium.
The Clown Loach is arguably one of the most popular and well known of the loaches due to its character and bold markings. In the aquarium Clown Loaches are an active, playful fish with plenty of character, which can be mixed with medium sized robust community fish. During feeding the Clown Loach can become a little boisterous so timid fish are best avoided as tank mates. Provide plenty of hiding spots in caves, wood, rocks and hardy plants. The substrate should be sandy or fine, sharp or chunky substrates will damage the fish's barbells. Apart from being prone to whitespot after transport or in fluctuating conditions, the Clown Loach is a robust fish that is undemanding of water hardness and pH. Provide well-filtered water, a variety of foods, and keep in groups of six or more. Can potentially reach up to 30cm.
If you have a question about keeping Clown Loach's then please use our Clown Loach help forum. Join 100's of other fish enthusiasts with 1000's of years experience between them caring for and keeping fish like these.
You are in the thinkfish community creator for the Clown Loach fish species, you can learn all about how the tool works in assessing fish tank mate compatibility at the link provided or use the tool tips provided to guide you. Create a test fish community that includes the Clown Loach species and any other types of fish or crustacean you'd like to introduce - You can assess the best companions, tank size suitability, stocking levels based on filter type and volume and possible negative interactions and warnings as you build up your aquarium.
Please do check your water type for compatibility with your fish, which can be found on the Clown Loach information box above and your local water company providers website. Don't waste your money and endanger your fish!
If you'd like to talk about the "Clown Loach" with our users, then we have a buzzing community of fish enthusiasts where you can talk about tank issues, Clown Loach fish behaviour, Clown Loach tank stocking and any other issues related to the Clown Loach fish or other types you may have.


text:
Q: Асинхронный метод Помогите! Почему метод onComplete выполняется синхронно, а не асинхронно?
findViewById(R.id.button8).setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v)
                {
                    final ProgressDialog pd = new ProgressDialog(MainActivity.this);
                    final Handler h = new Handler() {
                        public void handleMessage(Message msg)
                        {
                            pd.setMessage("Загрузка..."+msg.what);
                            if(msg.what==20) pd.dismiss();
                        }
                    };
                    Thread t = new Thread(new Runnable() {
                            public void run()
                            {
                                for(int i =0; i<=10; i++){
                                try
                                {
                                    TimeUnit.SECONDS.sleep(1);
                                }
                                catch (InterruptedException e)
                                {}
                                h.sendEmptyMessage(i);
                                }
                                VKRequest request = VKApi.wall().get(
                                    VKParameters
                                    .from("domain", "serd_donbassa", "count", "200", "offset", "0"));
                                request.executeWithListener(new VKRequestListener() {
                                        public void onComplete(VKResponse response)
                                        {
                                            for(int i =10; i<=20; i++){
                                                try
                                                {
                                                    TimeUnit.SECONDS.sleep(1);
                                                }
                                                catch (InterruptedException e)
                                                {}
                                                h.sendEmptyMessage(i);
                                            }
                                        }
                                    });
                            }
                        });

                    pd.setMessage("Загрузка...");
                    pd.setCancelable(false);
                    pd.show();
                    t.start();

                }
            });

Этот код обновляет только до 10, а потом зависает на 10 сек(перестает крутится ProgressDialog), а потом исчезает. Из выше сказанного следует, что метод выполняется асинхронно. Как это исправить?

A: Метод onComplete из VKSDK всегда выполняется в основном (UI) потоке приложения. Так уж оно сделано. Если вам в этот момент надо выполнить ещё одну асинхронную операцию, то запускайте её в новом потоке так же, как вы уже делаете или через AsyncTask.

A: Вместо executeWithListener нужно использовать executeSyncWithListener

redpajama_set_name: RedPajamaStackExchange


text:
William Rae (died 1367) was a 14th-century bishop of Glasgow. His background is obscure, although it is known that before ascending to the bishopric he was a precentor of the diocese of Glasgow. On the death of John Wishart in 1338, William was elected to the see. His election was confirmed by Pope Benedict XII, who on 11 February 1339 ordered Annibald de Ceccano, bishop of Tusculum, to consecrate William. William was consecrated at Avignon a short while later. Curiously, his predecessor Wishart had been consecrated by the same man, in the same location. William's episcopate was comparatively long, and he died on 27 January 1367. He was succeeded by Walter Wardlaw.

Glasgow Old Bridge

Bishop Rae's main contribution to his city was the erection of a stone bridge in around 1345 to replace an earlier wooden bridge. This bridge stood at around the same location as the current Victoria Bridge, near the bottom of the Bridgegate.

References

Dowden, John, The Bishops of Scotland, ed. J. Maitland Thomson, (Glasgow, 1912)

1367 deaths
Bishops of Glasgow
14th-century Scottish Roman Catholic bishops
Year of birth unknown

redpajama_set_name: RedPajamaWikipedia


text:
Tag: club
When 'Aunt Nan' dominated golf
May 22, 2019 May 21, 2019 Hugh Townsend
I find it rather fascinating how anniversaries — particularly milestone celebrations — can resurrect memories of people and things we would never otherwise think about in our busy day-to-day lives. It often happens to me and, once again, it's happening this year with the Abercrombie Country Club hosting its centennial birthday party just weeks from...
The top dynasty: '70s rugby club
March 27, 2019 March 26, 2019 Hugh Townsend
Statistics don't lie. One plus one is always two. Those are mathematical facts I learned when I was in elementary school in New Glasgow way back in the post-war days. I've been fascinated with numbers ever since. In a much more recent explanation, I read an online definition that statistics "can be used to determine...
Albion boxing coaches add judging
March 7, 2018 March 7, 2018 Steve Goodwin
TRENTON — It has been a transforming time for the Albion Amateur Boxing Club. Two of the club's coaches — Walter Linthorne and Erin Simpson — have become Boxing Nova Scotia's newest officials by judging bouts recently, and their next step would be to start refereeing bouts. "I like it — it's different," said Linthorne,...
Club remains popular option
October 11, 2017 October 11, 2017 Steve Goodwin
NEW GLASGOW — More than 400 members are once again registered for the Pictou County Gymnastics Club as its full program resumes. Many gymnasts took advantage of summer events, but the 2017-18 season has begun in earnest at the club's locations on South Frederick Street. Recreational director Ally Forbes explained competitive gymnasts kept attending while...
Ancient Mariners make waves
October 11, 2017 Amanda Jess
PICTOU — The Ancient Mariners Masters Swim Club is asking previous and potential members to give the club a try on Thursday, Oct. 12. Peggy MacLean, a member for approximately 15 years, said the group wants to promote the club, which operates out of the Fisheries Training Pool in Pictou, and "let people know we're...
The Great Annual Lion and Skunk Bridge Competition this weekend
October 28, 2016 Pictou Advocate
The annual rivalry between New Glasgow and Truro bridge clubs is happening Satrday, Oct. 29, in Truro at the Lions Club. Every year the clubs challenge one another to an extra-ordinary nail-biting bridge competition which sees the winner take away the coveted LION, leaving the losers with the stinky little skunk. New Glasgow is revved...
Bluenose Curling Club to host open house
October 5, 2016 Steve Goodwin
NEW GLASGOW – Bluenose Curling Club is hosting an open house week from Oct. 11 to 13 to encourage membership. On-ice instruction will be available for anyone looking to give curling a try. The club's registration desk will be open Tuesday, Wednesday and Thursday from 6:30 to 8:30 p.m., as well as Thursday afternoon from...