/*
 * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package jdk.incubator.vector;

import jdk.internal.vm.annotation.ForceInline;

import static jdk.incubator.vector.VectorOperators.*;

abstract class AbstractMask<E> extends VectorMask<E> {
    AbstractMask(boolean[] bits) {
        super(bits);
    }

    /*package-private*/
    abstract boolean[] getBits();

    // Unary operator

    interface MUnOp {
        boolean apply(int i, boolean a);
    }

    abstract AbstractMask<E> uOp(MUnOp f);

    // Binary operator

    interface MBinOp {
        boolean apply(int i, boolean a, boolean b);
    }

    abstract AbstractMask<E> bOp(VectorMask<E> o, MBinOp f);

    /*package-private*/
    abstract AbstractSpecies<E> vspecies();

    @Override
    @ForceInline
    public final VectorSpecies<E> vectorSpecies() {
        return vspecies();
    }

    @Override
    public boolean laneIsSet(int i) {
        return getBits()[i];
    }

    @Override
    public long toLong() {
        // FIXME: This should be an intrinsic.
        if (length() > Long.SIZE) {
            throw new UnsupportedOperationException("too many lanes for one long");
        }
        long res = 0;
        long set = 1;
        boolean[] bits = getBits();
        for (int i = 0; i < bits.length; i++) {
            res = bits[i] ? res | set : res;
            set = set << 1;
        }
        return res;
    }

    @Override
    public void intoArray(boolean[] bits, int i) {
        System.arraycopy(getBits(), 0, bits, i, length());
    }

    @Override
    public boolean[] toArray() {
        return getBits().clone();
    }

    @Override
    @ForceInline
    @SuppressWarnings("unchecked")
    public
    <F> VectorMask<F> check(Class<F> elementType) {
        if (vectorSpecies().elementType() != elementType) {
            throw AbstractSpecies.checkFailed(this, elementType);
        }
        return (VectorMask<F>) this;
    }

    @Override
    @ForceInline
    @SuppressWarnings("unchecked")
    public
    <F> VectorMask<F> check(VectorSpecies<F> species) {
        if (species != vectorSpecies()) {
            throw AbstractSpecies.checkFailed(this, species);
        }
        return (VectorMask<F>) this;
    }

    @Override
    public int trueCount() {
        //FIXME: use a population count intrinsic here
        int c = 0;
        for (boolean i : getBits()) {
            if (i) c++;
        }
        return c;
    }

    @Override
    public int firstTrue() {
        //FIXME: use a count trailing zeros intrinsic here
        boolean[] bits = getBits();
        for (int i = 0; i < bits.length; i++) {
            if (bits[i])  return i;
        }
        return bits.length;
    }

    @Override
    public int lastTrue() {
        //FIXME: use a count leading zeros intrinsic here
        boolean[] bits = getBits();
        for (int i = bits.length-1; i >= 0; i--) {
            if (bits[i])  return i;
        }
        return -1;
    }

    @Override
    public VectorMask<E> eq(VectorMask<E> m) {
        // FIXME: Generate good code here.
        return bOp(m, (i, a, b) -> a == b);
    }

    @Override
    public VectorMask<E> andNot(VectorMask<E> m) {
        // FIXME: Generate good code here.
        return bOp(m, (i, a, b) -> a && !b);
    }

    /*package-private*/
    static boolean anyTrueHelper(boolean[] bits) {
        // FIXME: Maybe use toLong() != 0 here.
        for (boolean i : bits) {
            if (i) return true;
        }
        return false;
    }

    /*package-private*/
    static boolean allTrueHelper(boolean[] bits) {
        // FIXME: Maybe use not().toLong() == 0 here.
        for (boolean i : bits) {
            if (!i) return false;
        }
        return true;
    }

    @Override
    @ForceInline
    public VectorMask<E> indexInRange(int offset, int limit) {
        int vlength = length();
        Vector<E> iota = vectorSpecies().zero().addIndex(1);
        VectorMask<E> badMask = checkIndex0(offset, limit, iota, vlength);
        return this.andNot(badMask);
    }

    /*package-private*/
    @ForceInline
    AbstractVector<E>
    toVectorTemplate() {
        AbstractSpecies<E> vsp = vspecies();
        Vector<E> zero = vsp.broadcast(0);
        Vector<E> mone = vsp.broadcast(-1);
        // -1 will result in the most significant bit being set in
        // addition to some or all other lane bits.
        // For integral types, *all* lane bits will be set.
        // The bits for -1.0 are like {0b10111*0000*}.
        // FIXME: Use a conversion intrinsic for this operation.
        // https://bugs.openjdk.java.net/browse/JDK-8225740
        return (AbstractVector<E>) zero.blend(mone, this);
    }

    
Test if a masked memory access at a given offset into an array of the given length will stay within the array. The per-lane offsets are iota*esize.
/** * Test if a masked memory access at a given offset into an array * of the given length will stay within the array. * The per-lane offsets are iota*esize. */
/*package-private*/ @ForceInline void checkIndexByLane(int offset, int alength, Vector<E> iota, int esize) { if (VectorIntrinsics.VECTOR_ACCESS_OOB_CHECK == 0) { return; } // Although the specification is simple, the implementation is // tricky, because the value iota*esize might possibly // overflow. So we calculate our test values as scalars, // clipping to the range [-1..VLENGTH], and test them against // the unscaled iota vector, whose values are in [0..VLENGTH-1]. int vlength = length(); VectorMask<E> badMask; if (esize == 1) { badMask = checkIndex0(offset, alength, iota, vlength); } else if (offset >= 0) { // Masked access to multi-byte lanes in byte array. // It could be aligned anywhere. int elemCount = Math.min(vlength, (alength - offset) / esize); badMask = checkIndex0(0, elemCount, iota, vlength); } else { // This requires a split test. int clipOffset = Math.max(offset, -(vlength * esize)); int elemCount = Math.min(vlength, (alength - clipOffset) / esize); badMask = checkIndex0(0, elemCount, iota, vlength); clipOffset &= (esize - 1); // power of two, so OK VectorMask<E> badMask2 = checkIndex0(clipOffset / esize, vlength, iota, vlength); badMask = badMask.or(badMask2); } badMask = badMask.and(this); if (badMask.anyTrue()) { int badLane = badMask.firstTrue(); throw ((AbstractMask<E>)badMask) .checkIndexFailed(offset, badLane, alength, esize); } } private @ForceInline VectorMask<E> checkIndex0(int offset, int alength, Vector<E> iota, int vlength) { // An active lane is bad if its number is greater than // alength-offset, since when added to offset it will step off // of the end of the array. To avoid overflow when // converting, clip the comparison value to [0..vlength] // inclusive. int indexLimit = Math.max(0, Math.min(alength - offset, vlength)); VectorMask<E> badMask = iota.compare(GE, iota.broadcast(indexLimit)); if (offset < 0) { // An active lane is bad if its number is less than // -offset, because when added to offset it will then // address an array element at a negative index. To avoid // overflow when converting, clip the comparison value at // vlength. This specific expression works correctly even // when offset is Integer.MIN_VALUE. int firstGoodIndex = -Math.max(offset, -vlength); VectorMask<E> badMask2 = iota.compare(LT, iota.broadcast(firstGoodIndex)); if (indexLimit >= vlength) { badMask = badMask2; // 1st badMask is all true } else { badMask = badMask.or(badMask2); } } return badMask; } private IndexOutOfBoundsException checkIndexFailed(int offset, int lane, int alength, int esize) { String msg = String.format("Masked range check failed: "+ "vector mask %s out of bounds at "+ "index %d+%d in array of length %d", this, offset, lane * esize, alength); if (esize != 1) { msg += String.format(" (each lane spans %d array elements)", esize); } throw new IndexOutOfBoundsException(msg); } }