package shared;

import jdk.incubator.vector.ByteVector;
import jdk.incubator.vector.DoubleVector;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.LongVector;
import jdk.incubator.vector.ShortVector;
import jdk.incubator.vector.VectorMask;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
import ml.Cell;

/** 
 * Holds SIMD methods.
 * @author Brian Bushnell
 * @date Sep 12, 2013
 *
 */
public final class SIMD {
	
	//Example from https://medium.com/@Styp/java-18-vector-api-do-we-get-free-speed-up-c4510eda50d2
	@SuppressWarnings("restriction")
	private static final VectorSpecies<Float> FSPECIES=FloatVector.SPECIES_256;//FloatVector.SPECIES_PREFERRED; //This needs to be final or performance drops.
	private static final int FWIDTH=FSPECIES.length();
//	private static final int boundMask=~(FWIDTH-1);
	
	@SuppressWarnings("restriction")
	private static final VectorSpecies<Byte> BSPECIES=ByteVector.SPECIES_256;
	private static final int BWIDTH=BSPECIES.length();
	
	@SuppressWarnings("restriction")
	private static final VectorSpecies<Integer> ISPECIES=IntVector.SPECIES_256;
	private static final int IWIDTH=ISPECIES.length();
	
	@SuppressWarnings("restriction")
	private static final VectorSpecies<Short> SSPECIES=ShortVector.SPECIES_256;
	private static final int SWIDTH=SSPECIES.length();
	
	@SuppressWarnings("restriction")
	private static final VectorSpecies<Double> DSPECIES=DoubleVector.SPECIES_256;
	private static final int DWIDTH=DSPECIES.length();
	
	@SuppressWarnings("restriction")
	private static final VectorSpecies<Long> LSPECIES=LongVector.SPECIES_256;
	private static final int LWIDTH=LSPECIES.length();
	
	@SuppressWarnings("restriction")
	/** 
	 * Vectorized version of "c+=a[i]*b[i]" where a and b are equal-length arrays.
	 * @param a A vector to multiply.
	 * @param b A vector to multiply.
	 * @return Sum of products of vector elements.
	 */
	static final float fma(final float[] a, final float[] b){
		assert(a.length==b.length);
		
		//Note: FSPECIES=FloatVector.SPECIES_256 and FWIDTH=8
		final int limit=FSPECIES.loopBound(a.length);

		FloatVector sum=FloatVector.zero(FSPECIES);
		int i=0;
		for(; i<limit; i+=FWIDTH) {//SIMD loop
			FloatVector va=FloatVector.fromArray(FSPECIES, a, i);
			FloatVector vb=FloatVector.fromArray(FSPECIES, b, i);
			sum=va.fma(vb, sum);
		}
		float c=sum.reduceLanes(VectorOperators.ADD);
		for (; i<a.length; i++) {//Residual scalar loop
			c+=a[i]*b[i];
		}
		return c;
	}
	
	@SuppressWarnings("restriction")
	/** 
	 * Vectorized version of "c+=a[i]*b[bSet[i]]" where a and bSet are equal-length arrays,
	 * and bSet stores indices of b, in ascending contiguous blocks of 8.
	 * @param a A vector to multiply.
	 * @param b A vector to multiply.
	 * @return Sum of products of vector elements.
	 */
	static final float fmaSparse(final float[] a, final float[] b, int[] bSet){
		assert(a.length==bSet.length);
		assert(a.length<b.length);//Otherwise should do normal fma
		
		//Note: FSPECIES=FloatVector.SPECIES_256 and FWIDTH=8
		final int limit=FSPECIES.loopBound(bSet.length);
//		assert(FWIDTH==8);
//		int elements=0;
		
		FloatVector sum=FloatVector.zero(FSPECIES);
		int i=0;
		for(; i<limit; i+=FWIDTH) {//SIMD loop
			int idx=bSet[i];
//			elements+=FWIDTH;
//			assert(idx%8==0) : idx+", "+i+", "+Arrays.toString(bSet);
//			assert(bSet[i+1]==idx+1);
//			assert(bSet[i+7]==idx+7);
			FloatVector va=FloatVector.fromArray(FSPECIES, a, i);
			FloatVector vb=FloatVector.fromArray(FSPECIES, b, idx);
			sum=va.fma(vb, sum);
		}
		float c=sum.reduceLanes(VectorOperators.ADD);
		for (; i<bSet.length; i++) {//Residual scalar loop
//			elements++;
			c+=a[i]*b[bSet[i]];
		}
		
//		float c2=0;
//		for (int j=0; j<bSet.length; j++) {//Verification loop
//			c2+=a[j]*b[bSet[j]];
//		}
//		assert(Tools.absdif(c, c2)<0.0001f) : c+", "+c2;
//		assert(elements==bSet.length);
		
		return c;
	}
	
	//Claude Sonnet
	public static final float absDif(long[] a, long[] b, float inva, float invb) {
	    assert(a.length==b.length);
	    
	    final int length = a.length;
	    final int limit = LSPECIES.loopBound(length);
	    
	    FloatVector sumVec = FloatVector.zero(FSPECIES);
	    int i = 0;
	    
	    // SIMD loop for aligned portion
	    for (; i < limit; i += LWIDTH) {
	        LongVector va = LongVector.fromArray(LSPECIES, a, i);
	        LongVector vb = LongVector.fromArray(LSPECIES, b, i);
	        
	        // Convert longs to floats
	        FloatVector fa = (FloatVector) va.convertShape(VectorOperators.L2F, FSPECIES, 0);
	        FloatVector fb = (FloatVector) vb.convertShape(VectorOperators.L2F, FSPECIES, 0);
	        
	        // Apply scaling factors
	        fa = fa.mul(inva);
	        fb = fb.mul(invb);
	        
	        // Calculate absolute difference and accumulate
	        FloatVector diff = fa.sub(fb).abs();
	        sumVec = sumVec.add(diff);
	    }
	    
	    // For residual elements, just use scalar loop
	    float sum = sumVec.reduceLanes(VectorOperators.ADD);
	    for (; i < length; i++) {
	        float ai = a[i] * inva;
	        float bi = b[i] * invb;
	        sum += Math.abs(ai - bi);
	    }
	    
	    return sum;
	}
	
	//Claude Sonnet
	//Unfortunately, this dumps core, is very slow, and gives the wrong answer
	public static float absDifComp(long[] a, long[] b, int k, int[] gcmap) {
	    final int length = a.length;
	    
	    // Calculate GC bucket sums - this can't be easily vectorized
	    final float[] aSums = new float[k+1];
	    final float[] bSums = new float[k+1];
	    
	    for(int i=0; i<length; i++) {
	        int gc = gcmap[i];
	        aSums[gc] += a[i];
	        bSums[gc] += b[i];
	    }
	    
	    final float inv = 1f/(k+1);
	    
	    // Compute normalization factors
	    for(int i=0; i<=k; i++) {
	        aSums[i] = inv/Math.max(aSums[i], 1);
	        bSums[i] = inv/Math.max(bSums[i], 1);
	    }
	    
	    // Process by GC content - one efficient way to vectorize with different scaling factors
	    FloatVector sumVec = FloatVector.zero(FSPECIES);
	    
	    // Process elements grouped by their GC content
	    for(int gc=0; gc<=k; gc++) {
	        float aFactor = aSums[gc];
	        float bFactor = bSums[gc];
	        
	        // Find all indices with this GC content in chunks for efficient processing
	        int currentIndex = 0;
	        while(currentIndex < length) {
	            // Find start of a chunk with this GC
	            while(currentIndex < length && gcmap[currentIndex] != gc) {
	                currentIndex++;
	            }
	            
	            // If we found a starting point
	            if(currentIndex < length) {
	                int chunkStart = currentIndex;
	                
	                // Find end of the chunk
	                while(currentIndex < length && gcmap[currentIndex] == gc) {
	                    currentIndex++;
	                }
	                
	                int chunkEnd = currentIndex;
	                int chunkSize = chunkEnd - chunkStart;
	                
	                // Process this chunk with SIMD
	                if(chunkSize >= LWIDTH) {
	                    int limit = chunkStart + (chunkSize / LWIDTH) * LWIDTH;
	                    
	                    for(int i=chunkStart; i<limit; i+=LWIDTH) {
	                        LongVector va = LongVector.fromArray(LSPECIES, a, i);
	                        LongVector vb = LongVector.fromArray(LSPECIES, b, i);
	                        
	                        // Convert to float
	                        FloatVector fa = (FloatVector) va.convertShape(VectorOperators.L2F, FSPECIES, 0);
	                        FloatVector fb = (FloatVector) vb.convertShape(VectorOperators.L2F, FSPECIES, 0);
	                        
	                        // Apply GC-specific scaling
	                        fa = fa.mul(aFactor);
	                        fb = fb.mul(bFactor);
	                        
	                        // Calculate absolute difference
	                        sumVec = sumVec.add(fa.sub(fb).abs());
	                    }
	                    
	                    // Handle remainder
	                    for(int i=limit; i<chunkEnd; i++) {
	                        float aComp = a[i] * aFactor;
	                        float bComp = b[i] * bFactor;
	                        sumVec = sumVec.add(Math.abs(aComp - bComp));
	                    }
	                } 
	                else {
	                    // Small chunk - handle with scalar code
	                    for(int i=chunkStart; i<chunkEnd; i++) {
	                        float aComp = a[i] * aFactor;
	                        float bComp = b[i] * bFactor;
	                        sumVec = sumVec.add(Math.abs(aComp - bComp));
	                    }
	                }
	            }
	        }
	    }
	    
	    float sum = sumVec.reduceLanes(VectorOperators.ADD);
	    return Tools.mid(0, 1, (Float.isFinite(sum) && sum>0 ? sum : 0));
	}
	
	/** 
	 * This is here to keep all the vector operations in a single loop,
	 * to prevent going in and out of SIMD mode too often...  hopefully.
	 * ~20% measured speed increase compared to calling fma() for ScoreSequence.
	 */
	@SuppressWarnings("restriction")
	public static void feedForward(final Cell[] layer, final float[] b) {
		assert(false) : "This was giving incorrect results for nets made made with simd=f and vice versa.  Needs validation.";
		final int limit=FSPECIES.loopBound(b.length);
		
		for(int cnum=0; cnum<layer.length; cnum++) {
			final Cell cell=layer[cnum];
			final float[] a=cell.weights;
			FloatVector sum=FloatVector.zero(FSPECIES);
			for(int i=0; i<limit; i+=FWIDTH) {//SIMD loop
				FloatVector va=FloatVector.fromArray(FSPECIES, a, i);
				FloatVector vb=FloatVector.fromArray(FSPECIES, b, i);
				sum=va.fma(vb, sum);
			}
			cell.sum=sum.reduceLanes(VectorOperators.ADD);
		}
		
		for(int cnum=0; cnum<layer.length; cnum++) {
			final Cell cell=layer[cnum];
			final float[] a=cell.weights;
			float residual=cell.bias;
			for (int i=limit+FWIDTH; i<a.length; i++) {//Residual scalar loop
				residual+=a[i]*b[i];
			}
			cell.sum+=residual;
			final float v=(float)cell.activation(cell.sum);
			cell.setValue(v);
		}
	}
	
	/** 
	 * This is here to keep all the vector operations in a single loop,
	 * to prevent going in and out of SIMD mode too often...  hopefully.
	 * ~20% measured speed increase compared to calling fma() for Train. 
	 */
	public static void backPropFma(Cell[] layer, float[] a, float[][] bb) {
		final int limit=FSPECIES.loopBound(a.length);
		
		for(int cnum=0; cnum<layer.length; cnum++) {
			Cell cell=layer[cnum];
			float[] b=bb[cnum];
			FloatVector sum=FloatVector.zero(FSPECIES);
			for(int i=0; i<limit; i+=FWIDTH) {//SIMD loop
				FloatVector va=FloatVector.fromArray(FSPECIES, a, i);
				FloatVector vb=FloatVector.fromArray(FSPECIES, b, i);
				sum=va.fma(vb, sum);
			}
			cell.eTotalOverOut=sum.reduceLanes(VectorOperators.ADD);
		}
		
		if(limit+FWIDTH>=a.length) {return;}//Shortcut when length is divisible by 8.
		
		for(int cnum=0; cnum<layer.length; cnum++) {
			Cell cell=layer[cnum];
			float[] b=bb[cnum];
			float residual=0;
			for (int i=limit+FWIDTH; i<a.length; i++) {//Residual scalar loop
				residual+=a[i]*b[i];
			}
			cell.eTotalOverOut+=residual;
		}
	}
	
	/** 
	 * Performs "a+=incr" where a and incr are equal-length arrays.
	 * @param a A vector to increment.
	 * @param b Increment amount.
	 */
	@SuppressWarnings("restriction")
	static final void add(final float[] a, final float[] b){
		//final int width=SPECIES.length();
		final int limit=FSPECIES.loopBound(a.length);
//		final int limit=a.length&boundMask;
		
		int i=0;
		for(; i<limit; i+=FWIDTH) {//SIMD loop
			FloatVector va=FloatVector.fromArray(FSPECIES, a, i);
			FloatVector vb=FloatVector.fromArray(FSPECIES, b, i);
			FloatVector sum=va.add(vb);
			sum.intoArray(a, i);
		}
		for (; i<a.length; i++) {//Residual scalar loop; TODO: replace with vector mask
			a[i]+=b[i];
		}
	}

	/** 
	 * Performs "a+=b*mult" where a and b are equal-length arrays.
	 * @param a A vector to increment.
	 * @param b Increment amount.
	 * @param mult Increment multiplier.
	 */
	@SuppressWarnings("restriction")
	static final void addProduct(final float[] a, final float[] b, final float mult){
		//final int width=SPECIES.length();
		final int limit=FSPECIES.loopBound(a.length);
//		final int limit=a.length&boundMask;
		
		int i=0;
		for(; i<limit; i+=FWIDTH) {//SIMD loop
			FloatVector va=FloatVector.fromArray(FSPECIES, a, i);
			FloatVector vb=FloatVector.fromArray(FSPECIES, b, i);
			FloatVector sum=va.add(vb.mul(mult));
			sum.intoArray(a, i);
		}
		for (; i<a.length; i++) {//Residual scalar loop; TODO: replace with vector mask
			a[i]+=b[i]*mult;
		}
	}
	
	@SuppressWarnings("restriction")
	static final void addProductSparse(final float[] a, final float[] b, final int[] bSet, final float mult){
		//final int width=SPECIES.length();
		final int limit=FSPECIES.loopBound(bSet.length);
//		final int limit=a.length&boundMask;
		
		int i=0;
		for(; i<limit; i+=FWIDTH) {//SIMD loop
			int idx=bSet[i];
			FloatVector va=FloatVector.fromArray(FSPECIES, a, i);
			FloatVector vb=FloatVector.fromArray(FSPECIES, b, idx);
			FloatVector sum=va.add(vb.mul(mult));
			sum.intoArray(a, i);
		}
		for (; i<bSet.length; i++) {//Residual scalar loop; TODO: replace with vector mask
			a[i]+=b[bSet[i]]*mult;
		}
	}
	
	//a is dest
	@SuppressWarnings("restriction")
	static final void copy(final float[] a, final float[] b){
		final int length=Tools.min(a.length, b.length);
		//final int width=SPECIES.length();
		final int limit=FSPECIES.loopBound(length);
//		final int limit=a.length&boundMask;
		
		int i=0;
		for(; i<limit; i+=FWIDTH) {//SIMD loop
			FloatVector vb=FloatVector.fromArray(FSPECIES, b, i);
			vb.intoArray(a, i);
		}
		for (; i<length; i++) {//Residual scalar loop; TODO: replace with vector mask
			a[i]=b[i];
		}
	}
	
	/** Returns number of matches */
	@SuppressWarnings("restriction")
	static final int countMatches(final byte[] s1, final byte[] s2, int a1, int b1, int a2, int b2){
		final int length=b2-a2+1;
		final int limit0=BSPECIES.loopBound(length);
		final int limit=a2+limit0;
		
		int i=a1, j=a2;
		int matches=0;
		for(; j<limit; i+=BWIDTH, j+=BWIDTH) {//SIMD loop
			ByteVector v1=ByteVector.fromArray(BSPECIES, s1, i);
			ByteVector v2=ByteVector.fromArray(BSPECIES, s2, j);
			VectorMask<Byte> x=v1.eq(v2);
			matches+=x.trueCount();//This might be slow, or might not
		}
		for(; j<=b2; i++, j++) {
			final byte x=s1[i], y=s2[j];
			final int m=((x==y) ? 1 : 0);
			matches+=m;
		}
		return matches;
	}
	
	/** Returns index of symbol */
	@SuppressWarnings("restriction")
	static final int find(final byte[] a, final byte symbol, final int from, final int to){//15% Slower than scalar code, at least for ByteFile1
		final int length=to-from;//Intentionally exclusive
		final int limit0=BSPECIES.loopBound(length);
		final int limit=from+limit0;
		
		int pos=from;
		for(; pos<limit; pos+=BWIDTH) {//SIMD loop
			ByteVector v=ByteVector.fromArray(BSPECIES, a, pos);
			VectorMask<Byte> x=v.eq(symbol);
			int t=x.firstTrue();
			if(t<BWIDTH) {return pos+t;}
//			if(x.anyTrue()) {break;}
		}
		while(pos<to && a[pos]!=symbol){pos++;}
		return pos;
	}
	
	@SuppressWarnings("restriction")
	/** 
	 * Sums the array.
	 * @param a A vector.
	 * @return The sum.
	 */
	static final float sum(final float[] a, final int from, final int to){
		final int length=to-from+1;//Intentionally inclusive
		final int limit0=FSPECIES.loopBound(length);
		final int limit=from+limit0;

		FloatVector sum=FloatVector.zero(FSPECIES);
		int i=from;
		for(; i<limit; i+=FWIDTH) {//SIMD loop
			FloatVector va=FloatVector.fromArray(FSPECIES, a, i);
			sum=sum.add(va);
		}
		float c=sum.reduceLanes(VectorOperators.ADD);
		for (; i<=to; i++) {c+=a[i];}//Residual scalar loop
		return c;
	}
	
	@SuppressWarnings("restriction")
	/** 
	 * Sums the array.
	 * @param a A vector.
	 * @return The sum.
	 */
	static final long sum(final long[] a, final int from, final int to){
		final int length=to-from+1;
		final int limit0=LSPECIES.loopBound(length);
		final int limit=from+limit0;

		LongVector sum=LongVector.zero(LSPECIES);
		int i=from;
		for(; i<limit; i+=LWIDTH) {//SIMD loop
			LongVector va=LongVector.fromArray(LSPECIES, a, i);
			sum=sum.add(va);
		}
		long c=sum.reduceLanes(VectorOperators.ADD);
		for (; i<=to; i++) {c+=a[i];}//Residual scalar loop
		return c;
	}
	
	@SuppressWarnings("restriction")
	/** 
	 * Sums the array.
	 * @param a A vector.
	 * @return The sum.
	 */
	static final long sum(final int[] a, final int from, final int to){//Tested as 1.5x scalar speed
		final int length=to-from+1;
		final int limit0=ISPECIES.loopBound(length);
		final int limit=from+limit0;
		
		int i=from;
		long c=0;
		for(; i<limit; i+=IWIDTH) {//SIMD loop
			IntVector va=IntVector.fromArray(ISPECIES, a, i);
			c+=va.reduceLanesToLong(VectorOperators.ADD);//This is probably slow
		}
		for (; i<=to; i++) {c+=a[i];}//Residual scalar loop
		return c;
	}
	
	@SuppressWarnings("restriction")
	/** 
	 * Sums the array.
	 * @param a A vector.
	 * @return The sum.
	 */
	static final long sum(final byte[] a, final int from, final int to){//Tested as 4x scalar speed
		//TODO: Test speed.
		final int length=to-from+1;
		final int limit0=BSPECIES.loopBound(length);
		final int limit=from+limit0;
		
		int i=from;
		long c=0;
		for(; i<limit; i+=BWIDTH) {//SIMD loop
			ByteVector va=ByteVector.fromArray(BSPECIES, a, i);
			c+=va.reduceLanesToLong(VectorOperators.ADD);
		}
		for (; i<=to; i++) {c+=a[i];}//Residual scalar loop
		return c;
	}
	
	@SuppressWarnings("restriction")
	/** 
	 * Sums the array.
	 * @param a A vector.
	 * @return The sum.
	 */
	static final double sum(final double[] a, final int from, final int to){
		final int length=to-from+1;
		final int limit0=DSPECIES.loopBound(length);
		final int limit=from+limit0;

		DoubleVector sum=DoubleVector.zero(DSPECIES);
		int i=from;
		for(; i<limit; i+=DWIDTH) {//SIMD loop
			DoubleVector va=DoubleVector.fromArray(DSPECIES, a, i);
			sum=sum.add(va);
		}
		double c=sum.reduceLanes(VectorOperators.ADD);
		for (; i<=to; i++) {c+=a[i];}//Residual scalar loop
		return c;
	}
	
//	public static float absDifFloat(float[] a, float[] b) {
//		assert(a.length==b.length);
//		float sum=0;
//		for(int i=0; i<a.length; i++){
//			sum+=Math.abs(a[i]-b[i]);
//		}
//		return (float)sum;
//	}
	
	/**
     * Calculates the sum of the absolute differences between corresponding elements of two float arrays.
     *
     * @param a the first float array
     * @param b the second float array
     * @return the sum of the absolute differences between corresponding elements of the two arrays
     * @throws IllegalArgumentException if the lengths of the arrays do not match
     */
    public static float absDifFloat(float[] a, float[] b) {
        if(a.length!=b.length){
            throw new IllegalArgumentException("Arrays must have the same length");
        }

        final int length=a.length;
        final int limit0=FSPECIES.loopBound(length);
        final int limit=limit0;

        FloatVector sumVec=FloatVector.zero(FSPECIES);
        int i=0;
        for (; i<limit; i+=FWIDTH) { // SIMD loop
            FloatVector va=FloatVector.fromArray(FSPECIES, a, i);
            FloatVector vb=FloatVector.fromArray(FSPECIES, b, i);
            FloatVector diff=va.sub(vb).abs();
            sumVec=sumVec.add(diff);
        }
        
        //Scalar residual loop
//        float sum=sumVec.reduceLanes(VectorOperators.ADD);
//        for (; i<length; i++) { // Residual scalar loop
//            sum+=Math.abs(a[i]-b[i]);
//        }
//        return sum;
        
        // Handle the residual elements using lanewise masking
        // Lightly tested and seems to work
        if (i < length) {
            VectorMask<Float> mask = FSPECIES.indexInRange(i, length);
            FloatVector va = FloatVector.fromArray(FSPECIES, a, i, mask);
            FloatVector vb = FloatVector.fromArray(FSPECIES, b, i, mask);
            FloatVector diff = va.sub(vb).abs();
            sumVec = sumVec.add(diff, mask);
        }
        float sum = sumVec.reduceLanes(VectorOperators.ADD);
        return sum;
    }
    
    //Claude Sonnet
    public static float cosineSimilarity(int[] a, int[] b, float inva, float invb) {
        assert(a.length == b.length);
        
        int length = a.length;
        int upperBound = ISPECIES.loopBound(length);
        
        // Accumulation vectors
        FloatVector dotProductVec = FloatVector.zero(FSPECIES);
        FloatVector normVec1Vec = FloatVector.zero(FSPECIES);
        FloatVector normVec2Vec = FloatVector.zero(FSPECIES);
        
        int i = 0;
        for (; i < upperBound; i += IWIDTH) {
            IntVector va = IntVector.fromArray(ISPECIES, a, i);
            IntVector vb = IntVector.fromArray(ISPECIES, b, i);
            
            FloatVector fa = (FloatVector) va.convertShape(VectorOperators.I2F, FSPECIES, 0);
            FloatVector fb = (FloatVector) vb.convertShape(VectorOperators.I2F, FSPECIES, 0);
            fa = fa.mul(inva);
            fb = fb.mul(invb);
            
            // Accumulate in vector space
            dotProductVec = dotProductVec.add(fa.mul(fb));
            normVec1Vec = normVec1Vec.add(fa.mul(fa));
            normVec2Vec = normVec2Vec.add(fb.mul(fb));
        }
        
        // Reduce once at the end
        float dotProduct = dotProductVec.reduceLanes(VectorOperators.ADD);
        float normVec1 = normVec1Vec.reduceLanes(VectorOperators.ADD);
        float normVec2 = normVec2Vec.reduceLanes(VectorOperators.ADD);
        
        // Handle remaining elements
        for (; i < length; i++) {
            float ai = a[i] * inva;
            float bi = b[i] * invb;
            dotProduct += ai * bi;
            normVec1 += ai * ai;
            normVec2 += bi * bi;
        }
        
        normVec1 = Math.max(normVec1, 1e-15f);
        normVec2 = Math.max(normVec2, 1e-15f);
        
        return (float)(dotProduct / (Math.sqrt(normVec1) * Math.sqrt(normVec2)));
    }
	
	@SuppressWarnings("restriction")
	/** 
	 * Finds the maximum value.
	 * @param a A vector.
	 * @return The max.
	 */
	static final int max(final int[] a, final int from, final int to){//Tested as 5x scalar speed
		final int length=to-from+1;
		final int limit0=ISPECIES.loopBound(length);
		final int limit=from+limit0;
		
		int i=from;
		IntVector max=IntVector.broadcast(ISPECIES, a[from]);
		for(; i<limit; i+=IWIDTH) {//SIMD loop
			IntVector va=IntVector.fromArray(ISPECIES, a, i);
			max=max.max(va);
		}
		int c=max.reduceLanes(VectorOperators.MAX);
		for (; i<=to; i++) {//Residual scalar loop
			final int x=a[i];
			c=(x>c ? x : c);
		}
		return c;
	}
	
	@SuppressWarnings("restriction")
	/** 
	 * Finds the maximum value.
	 * @param a A vector.
	 * @return The max.
	 */
	static final long max(final long[] a, final int from, final int to){
		final int length=to-from+1;
		final int limit0=LSPECIES.loopBound(length);
		final int limit=from+limit0;
		
		int i=from;
		LongVector max=LongVector.broadcast(LSPECIES, a[from]);
		for(; i<limit; i+=LWIDTH) {//SIMD loop
			LongVector va=LongVector.fromArray(LSPECIES, a, i);
			max=max.max(va);
		}
		long c=max.reduceLanes(VectorOperators.MAX);
		for (; i<=to; i++) {//Residual scalar loop
			final long x=a[i];
			c=(x>c ? x : c);
		}
		return c;
	}
	
	public static void alignBandVector(long q, long[] ref, int bandStart, int bandEnd, 
			long[] prev, long[] curr, 
			long MATCH, long N_SCORE, long SUB, long INS) {

		// Calculate vector-aligned bounds
		int alignedStart = bandStart;
		int alignedEnd = bandStart + ((bandEnd - bandStart + 1)/LWIDTH)*LWIDTH;

		// Broadcast constants to vectors
		LongVector vq = LongVector.broadcast(LSPECIES, q);
		LongVector vMATCH = LongVector.broadcast(LSPECIES, MATCH);
		LongVector vN_SCORE = LongVector.broadcast(LSPECIES, N_SCORE);
		LongVector vSUB = LongVector.broadcast(LSPECIES, SUB);
		LongVector vINS = LongVector.broadcast(LSPECIES, INS);

		// Process in LWIDTH chunks
		for(int j=alignedStart; j<alignedEnd; j+=LWIDTH) {
			// Load reference values
			LongVector refVec = LongVector.fromArray(LSPECIES, ref, j-1);

			// Calculate match conditions with 4-bit encoding
			LongVector andResult = vq.lanewise(VectorOperators.AND, refVec);
			VectorMask<Long> isCompatible = andResult.compare(VectorOperators.GT, 0);
			VectorMask<Long> isEqual = vq.compare(VectorOperators.EQ, refVec);
			VectorMask<Long> isMatchMask = isEqual.and(isCompatible);

			// Check for N values
			VectorMask<Long> qIsN = vq.compare(VectorOperators.EQ, 15);
			VectorMask<Long> refIsN = refVec.compare(VectorOperators.EQ, 31);
			VectorMask<Long> hasNMask = qIsN.or(refIsN);

			// Start with SUB score for all positions
			LongVector scoreAddVec = vSUB;

			// Apply MATCH score where matches occur (this overwrites SUB scores)
			scoreAddVec = scoreAddVec.blend(vMATCH, isMatchMask);

			// Apply N_SCORE where Ns occur (this overwrites any previous scores)
			scoreAddVec = scoreAddVec.blend(vN_SCORE, hasNMask);

			// Load previous scores
			LongVector prevJ1Vec = LongVector.fromArray(LSPECIES, prev, j-1);
			LongVector prevJVec = LongVector.fromArray(LSPECIES, prev, j);

			// Calculate diagonal and up scores
			LongVector diagScoreVec = prevJ1Vec.add(scoreAddVec);
			LongVector upScoreVec = prevJVec.add(vINS);

			// Find max score between diagonal and up
			LongVector maxVec = diagScoreVec.max(upScoreVec);

			// Store results
			maxVec.intoArray(curr, j);
		}

		// Handle remaining elements (scalar code)
		for(int j=alignedEnd; j<=bandEnd; j++) {
			final long r=ref[j-1];
			final boolean isMatch=((q&r)>0 && q==r);
			final boolean hasN=(q==15 || r==31);
			final long scoreAdd=isMatch ? MATCH : (hasN ? N_SCORE : SUB);

			final long pj1=prev[j-1], pj=prev[j];
			final long diagScore=pj1+scoreAdd;
			final long upScore=pj+INS;

			curr[j]=Math.max(diagScore, upScore);
		}
	}

	public static void alignBandVectorInt(int q, int[] ref, int bandStart, int bandEnd,
			int[] prev, int[] curr,
			int MATCH, int N_SCORE, int SUB, int INS){

		// Calculate vector-aligned bounds
		int alignedStart=bandStart;
		int alignedEnd=bandStart+((bandEnd-bandStart+1)/IWIDTH)*IWIDTH;

		// Broadcast constants to vectors
		IntVector vq=IntVector.broadcast(ISPECIES, q);
		IntVector vMATCH=IntVector.broadcast(ISPECIES, MATCH);
		IntVector vN_SCORE=IntVector.broadcast(ISPECIES, N_SCORE);
		IntVector vSUB=IntVector.broadcast(ISPECIES, SUB);
		IntVector vINS=IntVector.broadcast(ISPECIES, INS);

		// Process in IWIDTH chunks
		for(int j=alignedStart; j<alignedEnd; j+=IWIDTH){
			// Load reference values
			IntVector refVec=IntVector.fromArray(ISPECIES, ref, j-1);

			// Calculate match conditions with 4-bit encoding
			IntVector andResult=vq.lanewise(VectorOperators.AND, refVec);
			VectorMask<Integer> isCompatible=andResult.compare(VectorOperators.GT, 0);
			VectorMask<Integer> isEqual=vq.compare(VectorOperators.EQ, refVec);
			VectorMask<Integer> isMatchMask=isEqual.and(isCompatible);

			// Check for N values
			VectorMask<Integer> qIsN=vq.compare(VectorOperators.EQ, 15);
			VectorMask<Integer> refIsN=refVec.compare(VectorOperators.EQ, 31);
			VectorMask<Integer> hasNMask=qIsN.or(refIsN);

			// Start with SUB score for all positions
			IntVector scoreAddVec=vSUB;

			// Apply MATCH score where matches occur (this overwrites SUB scores)
			scoreAddVec=scoreAddVec.blend(vMATCH, isMatchMask);

			// Apply N_SCORE where Ns occur (this overwrites any previous scores)
			scoreAddVec=scoreAddVec.blend(vN_SCORE, hasNMask);

			// Load previous scores
			IntVector prevJ1Vec=IntVector.fromArray(ISPECIES, prev, j-1);
			IntVector prevJVec=IntVector.fromArray(ISPECIES, prev, j);

			// Calculate diagonal and up scores
			IntVector diagScoreVec=prevJ1Vec.add(scoreAddVec);
			IntVector upScoreVec=prevJVec.add(vINS);

			// Find max score between diagonal and up
			IntVector maxVec=diagScoreVec.max(upScoreVec);

			// Store results
			maxVec.intoArray(curr, j);
		}

		// Handle remaining elements (scalar code)
		for(int j=alignedEnd; j<=bandEnd; j++){
			final int r=ref[j-1];
			final boolean isMatch=((q&r)>0 && q==r);
			final boolean hasN=(q==15 || r==31);
			final int scoreAdd=isMatch ? MATCH : (hasN ? N_SCORE : SUB);

			final int pj1=prev[j-1], pj=prev[j];
			final int diagScore=pj1+scoreAdd;
			final int upScore=pj+INS;

			curr[j]=Math.max(diagScore, upScore);
		}
	}
	
	public static void alignBandVector2(long q, long[] ref, int bandStart, int bandEnd, 
			long[] prev, long[] curr, 
			long MATCH, long N_SCORE, long SUB, long INS) {

		// Calculate vector-aligned bounds
		int alignedStart = bandStart;
		int alignedEnd = bandStart + ((bandEnd - bandStart + 1)/LWIDTH)*LWIDTH;

		// Create broadcast vectors for constants
		LongVector vq = LongVector.broadcast(LSPECIES, q);
		LongVector vMATCH = LongVector.broadcast(LSPECIES, MATCH);
		LongVector vN_SCORE = LongVector.broadcast(LSPECIES, N_SCORE);
		LongVector vSUB = LongVector.broadcast(LSPECIES, SUB);
		LongVector vINS = LongVector.broadcast(LSPECIES, INS);
		LongVector v15 = LongVector.broadcast(LSPECIES, 15);

		// Process in LWIDTH chunks with prefetching
		for(int j=alignedStart; j<alignedEnd; j+=LWIDTH) {
			// Prefetch next data (helps with memory latency)
//			if(j+LWIDTH*2 <= alignedEnd) {
//				LSPECIES.prefetch(ref, j+LWIDTH-1);
//				LSPECIES.prefetch(prev, j+LWIDTH-1);
//			}

			// Load reference values
			LongVector refVec = LongVector.fromArray(LSPECIES, ref, j-1);

			// Calculate match condition (exact equality)
			VectorMask<Long> isMatchMask = vq.compare(VectorOperators.EQ, refVec);

			// Calculate hasN using OR operation: (q|r)>=15
			LongVector orResult = vq.lanewise(VectorOperators.OR, refVec);
			VectorMask<Long> hasNMask = orResult.compare(VectorOperators.GE, v15);

			// Load previous scores
			LongVector prevJ1Vec = LongVector.fromArray(LSPECIES, prev, j-1);
			LongVector prevJVec = LongVector.fromArray(LSPECIES, prev, j);

			// Compute score additions efficiently
			// Start with SUB scores
			LongVector scoreAddVec = vSUB;

			// Apply N_SCORE where hasN is true (but not where isMatch is true)
			VectorMask<Long> useNScoreMask = hasNMask.andNot(isMatchMask);
			scoreAddVec = scoreAddVec.blend(vN_SCORE, useNScoreMask);

			// Apply MATCH where isMatch is true (overrides everything)
			scoreAddVec = scoreAddVec.blend(vMATCH, isMatchMask);

			// Calculate diagonal and up scores
			LongVector diagScoreVec = prevJ1Vec.add(scoreAddVec);
			LongVector upScoreVec = prevJVec.add(vINS);

			// Find max and store
			diagScoreVec.max(upScoreVec).intoArray(curr, j);
		}

		// Handle remaining elements (scalar code)
		for(int j=alignedEnd; j<=bandEnd; j++) {
			final long r=ref[j-1];
			final boolean isMatch=(q==r);
			final boolean hasN=((q|r)>=15);
			final long scoreAdd=isMatch ? MATCH : (hasN ? N_SCORE : SUB);

			final long pj1=prev[j-1], pj=prev[j];
			final long diagScore=pj1+scoreAdd;
			final long upScore=pj+INS;

			curr[j]=Math.max(diagScore, upScore);
		}
	}

//	static void alignBandVector2(long q, long[] ref, int bandStart, int bandEnd, 
//			long[] prev, long[] curr, 
//			long MATCH, long N_SCORE, long SUB, long INS) {
//
//		final long N_VAL = 'N';
//
//		// Calculate vector-aligned bounds
//		int alignedStart = bandStart;
//		int alignedEnd = bandStart + ((bandEnd - bandStart + 1)/LWIDTH)*LWIDTH;
//
//		// Broadcast constants to vectors
//		LongVector vq = LongVector.broadcast(LSPECIES, q);
//		LongVector vN = LongVector.broadcast(LSPECIES, N_VAL);
//		LongVector vMATCH = LongVector.broadcast(LSPECIES, MATCH);
//		LongVector vN_SCORE = LongVector.broadcast(LSPECIES, N_SCORE);
//		LongVector vSUB = LongVector.broadcast(LSPECIES, SUB);
//		LongVector vINS = LongVector.broadcast(LSPECIES, INS);
//
//		// Process in LWIDTH chunks
//		for(int j=alignedStart; j<alignedEnd; j+=LWIDTH) {
//			// Load reference values
//			LongVector refVec = LongVector.fromArray(LSPECIES, ref, j-1);
//
//			// Calculate match and N conditions
//			VectorMask<Long> isMatchMask = vq.eq(refVec).and(vq.ne(vN)).and(refVec.ne(vN));
//			VectorMask<Long> hasNMask = vq.eq(vN).or(refVec.eq(vN));
//
//			// Calculate score addition vector using blend operations
//			LongVector scoreAddVec = vSUB;
//			scoreAddVec = scoreAddVec.blend(vMATCH, isMatchMask);
//			scoreAddVec = scoreAddVec.blend(vN_SCORE, hasNMask);
//
//			// Load previous scores
//			LongVector prevJ1Vec = LongVector.fromArray(LSPECIES, prev, j-1);
//			LongVector prevJVec = LongVector.fromArray(LSPECIES, prev, j);
//
//			// Calculate diagonal and up scores
//			LongVector diagScoreVec = prevJ1Vec.add(scoreAddVec);
//			LongVector upScoreVec = prevJVec.add(vINS);
//
//			// Find max and store
//			LongVector maxVec = diagScoreVec.max(upScoreVec);
//			maxVec.intoArray(curr, j);
//		}
//
//		// Handle remaining elements
//		for(int j=alignedEnd; j<=bandEnd; j++) {
//			final long r=ref[j-1];
//			final boolean isMatch=(q==r && q!='N' && r!='N');
//			final boolean hasN=(q=='N' || r=='N');
//			final long scoreAdd=isMatch ? MATCH : (hasN ? N_SCORE : SUB);
//
//			final long pj1=prev[j-1], pj=prev[j];
//			final long diagScore=pj1+scoreAdd;
//			final long upScore=pj+INS;
//
//			curr[j]=Math.max(diagScore, upScore);
//		}
//	}
	
//	public static void processInnerDiagonalVectorized(
//	        int innerMinI, int innerMaxI,
//	        long[] revQuery, long[] ref, int jm1Start,
//	        long[] diag_km2, long[] diag_km1, long[] diag_k,
//	        int km2DiagOffset, int km1DiagOffset,
//	        int qLen, boolean debug, long MATCH, long N_SCORE, long SUB,
//	        long INS, long DEL_INCREMENT, long SCORE_MASK) {
//	    
//	    // Calculate vector-aligned bounds
//	    int diagonalLength = innerMaxI - innerMinI + 1;
//	    int vectorizedLength = (diagonalLength / LWIDTH) * LWIDTH;
//	    int alignedMax = innerMinI + vectorizedLength;
//	    
//	    // Create constant vectors
//	    LongVector vMATCH = LongVector.broadcast(LSPECIES, MATCH);
//	    LongVector vN_SCORE = LongVector.broadcast(LSPECIES, N_SCORE);
//	    LongVector vSUB = LongVector.broadcast(LSPECIES, SUB);
//	    LongVector vINS = LongVector.broadcast(LSPECIES, INS);
//	    LongVector vDEL_INCREMENT = LongVector.broadcast(LSPECIES, DEL_INCREMENT);
//	    LongVector vSCORE_MASK = LongVector.broadcast(LSPECIES, SCORE_MASK);
//	    LongVector v15 = LongVector.broadcast(LSPECIES, 15);
//	    
//	    // Process elements in LWIDTH chunks
//	    for (int i = innerMinI; i < alignedMax; i += LWIDTH) {
//	        int offset = i - innerMinI;
//	        
//	        // Load query and reference values (contiguous access)
//	        LongVector vQuery = LongVector.fromArray(LSPECIES, revQuery, i);
//	        LongVector vRef = LongVector.fromArray(LSPECIES, ref, jm1Start + offset);
//	        
//	        // Load diagonal values (also contiguous)
//	        int upDiagIdx = offset + km2DiagOffset;
//	        int leftIdx = offset + km1DiagOffset - 1;
//	        
//	        LongVector vDiagValue = LongVector.fromArray(LSPECIES, diag_km2, upDiagIdx);
//	        LongVector vUpValue = LongVector.fromArray(LSPECIES, diag_km1, upDiagIdx);
//	        LongVector vLeftValue = LongVector.fromArray(LSPECIES, diag_km1, leftIdx);
//	        
//	        // Calculate match/N conditions
//	        VectorMask<Long> isMatchMask = vQuery.compare(VectorOperators.EQ, vRef);
//	        VectorMask<Long> hasNMask = vQuery.or(vRef).compare(VectorOperators.GT, v15);
//	        
//	        // Calculate score additions
//	        LongVector vScoreAdd = vSUB;
//	        vScoreAdd = vScoreAdd.blend(vMATCH, isMatchMask);
//	        vScoreAdd = vScoreAdd.blend(vN_SCORE, hasNMask);
//	        
//	        // Calculate scores
//	        LongVector vDiagScore = vDiagValue.add(vScoreAdd);
//	        LongVector vUpScore = vUpValue.add(vINS);
//	        LongVector vLeftScore = vLeftValue.add(vDEL_INCREMENT);
//	        
//	        // Find maximum scores
//	        LongVector vMaxDiagUp = vDiagScore.max(vUpScore);
//	        VectorMask<Long> vUseDiagUp = vMaxDiagUp.and(vSCORE_MASK)
//	                                     .compare(VectorOperators.GE, vLeftScore);
//	        LongVector vMaxValue = vLeftScore.blend(vMaxDiagUp, vUseDiagUp);
//	        
//	        // Store results (contiguous)
//	        vMaxValue.intoArray(diag_k, offset);
//	        
//	        if (debug) {
//	            for (int j = 0; j < LWIDTH; j++) {
//	                int idx = i + j;
//	                if (idx <= innerMaxI) {
//	                    int matrixRow = qLen - idx;
//	                    int jm1 = jm1Start + (idx - innerMinI);
//	                    System.err.println("Setting ("+matrixRow+","+(jm1+1)+") to "+diag_k[offset + j]);
//	                }
//	            }
//	        }
//	    }
//	    
//	    // Handle remaining elements with scalar code
//	    for (int i = alignedMax, offset = alignedMax - innerMinI, jm1 = jm1Start + offset; 
//	            i <= innerMaxI; 
//	            i++, offset++, jm1++) {
//	        
//	        long q = revQuery[i];
//	        long r = ref[jm1];
//	        
//	        boolean isMatch = (q==r);
//	        boolean hasN = (q|r)>15;
//	        long scoreAdd = isMatch?MATCH:(hasN?N_SCORE:SUB);
//	        
//	        int upDiagIdx = offset + km2DiagOffset;
//	        int leftIdx = offset + km1DiagOffset - 1;
//	        
//	        long diagValue = diag_km2[upDiagIdx];
//	        long upValue = diag_km1[upDiagIdx];
//	        long leftValue = diag_km1[leftIdx];
//	        
//	        long diagScore = diagValue + scoreAdd;
//	        long upScore = upValue + INS;
//	        long leftScore = leftValue + DEL_INCREMENT;
//	        
//	        final long maxDiagUp = Math.max(diagScore, upScore);
//	        final long maxValue = (maxDiagUp&SCORE_MASK)>=leftScore?maxDiagUp:leftScore;
//	        
//	        diag_k[offset] = maxValue;
//	        
//	        if (debug) {
//	            int matrixRow = qLen - i;
//	            System.err.println("Setting ("+matrixRow+","+(jm1+1)+") to "+maxValue);
//	        }
//	    }
//	}
	
//	public static void processInnerDiagonalVectorized(
//	        int innerMinI, int innerMaxI,
//	        long[] revQuery, long[] ref, int jm1Start,
//	        long[] diag_km2, long[] diag_km1, long[] diag_k,
//	        int km2DiagOffset, int km1DiagOffset,
//	        int qLen, boolean debug, long MATCH, long N_SCORE, long SUB,
//	        long INS, long DEL_INCREMENT, long SCORE_MASK) {
//	    
//	    // Calculate vector-aligned bounds
//	    int diagonalLength = innerMaxI - innerMinI + 1;
//	    if (diagonalLength <= 0) return; // Nothing to process
//	    
//	    // Process scalar first to handle any potential edge cases
//	    int vectorStart = innerMinI;
//	    
//	    // Determine how far we need to go to avoid edge cases
//	    int requiredOffset = Math.max(0, 1 - km1DiagOffset);
//	    if (requiredOffset > 0) {
//	        vectorStart = innerMinI + requiredOffset;
//	    }
//	    
//	    // Make sure vectorStart is aligned
//	    vectorStart = ((vectorStart - innerMinI + LWIDTH - 1) / LWIDTH) * LWIDTH + innerMinI;
//	    
//	    // Arrays length check
//	    int maxArrayIdx = diag_k.length - 1;
//	    int maxLengthForI = innerMinI + (maxArrayIdx - Math.max(0, km2DiagOffset));
//	    
//	    // Process initial portion with scalar code (edge cases)
//	    for (int i = innerMinI, offset = 0, jm1 = jm1Start; 
//	            i < Math.min(vectorStart, innerMaxI + 1) && i <= maxLengthForI; 
//	            i++, offset++, jm1++) {
//	        
//	        processScalarCell(i, offset, jm1, revQuery, ref, 
//	                         diag_km2, diag_km1, diag_k, 
//	                         km2DiagOffset, km1DiagOffset, 
//	                         qLen, debug, MATCH, N_SCORE, SUB,
//	                         INS, DEL_INCREMENT, SCORE_MASK, maxArrayIdx);
//	    }
//	    
//	    // Calculate aligned end for vector processing
//	    int alignedMax = Math.min(innerMaxI + 1, maxLengthForI);
//	    if (vectorStart < alignedMax) {
//	        alignedMax = vectorStart + ((alignedMax - vectorStart) / LWIDTH) * LWIDTH;
//	    } else {
//	        alignedMax = vectorStart; // No full vectors to process
//	    }
//	    
//	    // Create constant vectors
//	    LongVector vMATCH = LongVector.broadcast(LSPECIES, MATCH);
//	    LongVector vN_SCORE = LongVector.broadcast(LSPECIES, N_SCORE);
//	    LongVector vSUB = LongVector.broadcast(LSPECIES, SUB);
//	    LongVector vINS = LongVector.broadcast(LSPECIES, INS);
//	    LongVector vDEL_INCREMENT = LongVector.broadcast(LSPECIES, DEL_INCREMENT);
//	    LongVector vSCORE_MASK = LongVector.broadcast(LSPECIES, SCORE_MASK);
//	    LongVector v15 = LongVector.broadcast(LSPECIES, 15);
//	    
//	    // Process clean middle portion with vectors - no edge cases or conditionals
//	    for (int i = vectorStart; i < alignedMax; i += LWIDTH) {
//	        int offset = i - innerMinI;
//	        
//	        // All indices are guaranteed to be valid here
//	        LongVector vQuery = LongVector.fromArray(LSPECIES, revQuery, i);
//	        LongVector vRef = LongVector.fromArray(LSPECIES, ref, jm1Start + offset);
//	        
//	        int upDiagIdx = offset + km2DiagOffset;
//	        int leftIdx = offset + km1DiagOffset - 1;
//	        
//	        LongVector vDiagValue = LongVector.fromArray(LSPECIES, diag_km2, upDiagIdx);
//	        LongVector vUpValue = LongVector.fromArray(LSPECIES, diag_km1, upDiagIdx);
//	        LongVector vLeftValue = LongVector.fromArray(LSPECIES, diag_km1, leftIdx);
//	        
//	        // Calculate match/N conditions
//	        VectorMask<Long> isMatchMask = vQuery.compare(VectorOperators.EQ, vRef);
//	        VectorMask<Long> hasNMask = vQuery.or(vRef).compare(VectorOperators.GT, 15);
//	        
//	        // Calculate score additions
//	        LongVector vScoreAdd = vSUB;
//	        vScoreAdd = vScoreAdd.blend(vMATCH, isMatchMask);
//	        vScoreAdd = vScoreAdd.blend(vN_SCORE, hasNMask);
//	        
//	        // Calculate scores
//	        LongVector vDiagScore = vDiagValue.add(vScoreAdd);
//	        LongVector vUpScore = vUpValue.add(vINS);
//	        LongVector vLeftScore = vLeftValue.add(vDEL_INCREMENT);
//	        
//	        // Find maximum scores
//	        LongVector vMaxDiagUp = vDiagScore.max(vUpScore);
//	        VectorMask<Long> vUseDiagUp = vMaxDiagUp.and(LongVector.broadcast(LSPECIES, SCORE_MASK))
//	                                    .compare(VectorOperators.GE, vLeftScore);
//	        LongVector vMaxValue = vLeftScore.blend(vMaxDiagUp, vUseDiagUp);
//	        
//	        // Store results
//	        vMaxValue.intoArray(diag_k, offset);
//	        
//	        if (debug) {
//	            for (int j = 0; j < LWIDTH; j++) {
//	                int idx = i + j;
//	                if (idx <= innerMaxI) {
//	                    int matrixRow = qLen - idx;
//	                    int jm1Local = jm1Start + (idx - innerMinI);
//	                    System.err.println("Vector Setting ("+matrixRow+","+(jm1Local+1)+") to "+diag_k[offset + j]);
//	                }
//	            }
//	        }
//	    }
//	    
//	    // Handle remaining elements with scalar code
//	    for (int i = alignedMax, offset = alignedMax - innerMinI, jm1 = jm1Start + offset; 
//	            i <= innerMaxI && i <= maxLengthForI; 
//	            i++, offset++, jm1++) {
//	        
//	        processScalarCell(i, offset, jm1, revQuery, ref, 
//	                         diag_km2, diag_km1, diag_k, 
//	                         km2DiagOffset, km1DiagOffset, 
//	                         qLen, debug, MATCH, N_SCORE, SUB,
//	                         INS, DEL_INCREMENT, SCORE_MASK, maxArrayIdx);
//	    }
//	}
//
//	// Helper method to process a single cell using scalar code
//	private static void processScalarCell(
//	        int i, int offset, int jm1,
//	        long[] revQuery, long[] ref,
//	        long[] diag_km2, long[] diag_km1, long[] diag_k,
//	        int km2DiagOffset, int km1DiagOffset,
//	        int qLen, boolean debug, long MATCH, long N_SCORE, long SUB,
//	        long INS, long DEL_INCREMENT, long SCORE_MASK, int maxArrayIdx) {
//	    
//	    long q = revQuery[i];
//	    long r = ref[jm1];
//	    
//	    boolean isMatch = (q==r);
//	    boolean hasN = (q|r)>15;
//	    long scoreAdd = isMatch?MATCH:(hasN?N_SCORE:SUB);
//	    
//	    int upDiagIdx = offset + km2DiagOffset;
//	    
//	    // Check array bounds
//	    if (upDiagIdx > maxArrayIdx) {
//	        if (debug) System.err.println("Skipping out of bounds cell: " + (qLen-i) + "," + (jm1+1));
//	        return;
//	    }
//	    
//	    long diagValue = diag_km2[upDiagIdx];
//	    long upValue = diag_km1[upDiagIdx];
//	    
//	    // Handle boundary condition for leftValue
//	    long leftValue;
//	    int leftIdx = offset + km1DiagOffset - 1;
//	    if (leftIdx < 0) {
//	        // First column case
//	        int currentRow = qLen - i;
//	        leftValue = currentRow * INS;
//	    } else if (leftIdx > maxArrayIdx) {
//	        // Out of bounds - use a safe default
//	        leftValue = 0;
//	    } else {
//	        leftValue = diag_km1[leftIdx];
//	    }
//	    
//	    long diagScore = diagValue + scoreAdd;
//	    long upScore = upValue + INS;
//	    long leftScore = leftValue + DEL_INCREMENT;
//	    
//	    final long maxDiagUp = Math.max(diagScore, upScore);
//	    final long maxValue = (maxDiagUp&SCORE_MASK)>=leftScore?maxDiagUp:leftScore;
//	    
//	    // Check if the result index is within bounds
//	    if (offset <= maxArrayIdx) {
//	        diag_k[offset] = maxValue;
//	    }
//	    
//	    if (debug) {
//	        int matrixRow = qLen - i;
//	        System.err.println("Scalar Setting ("+matrixRow+","+(jm1+1)+") to "+maxValue);
//	    }
//	}
	
	public static void processInnerDiagonalVectorized(
	        int innerMinI, int innerMaxI,
	        long[] revQuery, long[] ref, int jm1Start,
	        long[] diag_km2, long[] diag_km1, long[] diag_k,
	        int km2DiagOffset, int km1DiagOffset,
	        int qLen, boolean debug, long MATCH, long N_SCORE, long SUB,
	        long INS, long DEL_INCREMENT, long SCORE_MASK) {
	    
	    // For now, just implement a scalar version that exactly matches the original
	    for(int i=innerMinI, offset=0, jm1=jm1Start; i<=innerMaxI; i++, offset++, jm1++) {
	        long q = revQuery[i];
	        long r = ref[jm1];
	        
	        boolean isMatch = (q == r);
	        boolean hasN = (q | r) > 15;
	        long scoreAdd = isMatch ? MATCH : (hasN ? N_SCORE : SUB);
	        
	        // Get adjacent cell values using incrementing indices
	        int upDiagIdx = offset + km2DiagOffset;
	        int leftIdx = offset + km1DiagOffset - 1;
	        
	        long diagValue = diag_km2[upDiagIdx];
	        long upValue = diag_km1[upDiagIdx];
	        
	        // Handle first column special case
	        long leftValue;
	        if (leftIdx < 0) {
	            int currentRow = qLen - i;
	            leftValue = currentRow * INS;
	        } else {
	            leftValue = diag_km1[leftIdx];
	        }
	        
	        // Calculate scores directly
	        long diagScore = diagValue + scoreAdd;
	        long upScore = upValue + INS;
	        long leftScore = leftValue + DEL_INCREMENT;
	        
	        // Find max using conditional expressions - exactly as original
	        final long maxDiagUp = Math.max(diagScore, upScore);
	        final long maxValue = ((maxDiagUp & SCORE_MASK) >= leftScore) ? maxDiagUp : leftScore;
	        
	        diag_k[offset] = maxValue;
	        
	        if(debug) {
	            int matrixRow = qLen - i;
	            System.err.println("Scalar Setting (" + matrixRow + "," + (jm1+1) + ") to " + maxValue);
	        }
	    }
	}
	
//	@SuppressWarnings("restriction")
//	public static void processInnerLoopSIMD(
//	        long[] revQuery, long[] ref, int k, 
//	        int innerMinCol, int innerMaxCol, int qLen,
//	        long[] diag_km2, long[] diag_km1, long[] diag_k,
//	        long MATCH, long SUB, long INS, long DEL_INCREMENT, 
//	        long N_SCORE, long SCORE_MASK, boolean debug) {
//	    
//	    // Calculate the number of vector operations needed
//	    final int vectorBound=innerMaxCol-LWIDTH+1;
//	    
//	    // Create constant vectors once
//	    final LongVector matchVector=LongVector.broadcast(LSPECIES, MATCH);
//	    final LongVector subVector=LongVector.broadcast(LSPECIES, SUB);
//	    final LongVector nScoreVector=LongVector.broadcast(LSPECIES, N_SCORE);
//	    final LongVector insVector=LongVector.broadcast(LSPECIES, INS);
//	    final LongVector delIncrementVector=LongVector.broadcast(LSPECIES, DEL_INCREMENT);
//	    final LongVector fifteenVector=LongVector.broadcast(LSPECIES, 15);
//	    final LongVector scoreMaskVector=LongVector.broadcast(LSPECIES, SCORE_MASK);
//	    
//	    // Handle vector-sized blocks - use separate index from the loop counter
//	    for(int col=innerMinCol; col<=vectorBound; col+=LWIDTH) {
//	        // Calculate base index for revQuery
//	        final int queryBaseIdx=qLen+col-k;
//	        
//	        // Load LWIDTH elements from each array with their respective offsets
//	        final LongVector qVector=LongVector.fromArray(LSPECIES, revQuery, queryBaseIdx);
//	        final LongVector rVector=LongVector.fromArray(LSPECIES, ref, col-1);
//	        final LongVector diagVector=LongVector.fromArray(LSPECIES, diag_km2, col-2);
//	        final LongVector upVector=LongVector.fromArray(LSPECIES, diag_km1, col-1);
//	        final LongVector leftVector=LongVector.fromArray(LSPECIES, diag_km1, col-2);
//	        
//	        // Check for matches and Ns
//	        final VectorMask<Long> isMatchMask=qVector.compare(VectorOperators.EQ, rVector);
//	        final LongVector orVector=qVector.or(rVector);
//	        final VectorMask<Long> hasNMask=orVector.compare(VectorOperators.GT, fifteenVector);
//	        
//	        // Calculate score to add - start with SUB
//	        LongVector scoreAddVector=subVector;
//	        
//	        // Apply the masks using vector blend operations
//	        scoreAddVector=scoreAddVector.blend(nScoreVector, hasNMask);
//	        scoreAddVector=scoreAddVector.blend(matchVector, isMatchMask);
//	        
//	        // Calculate scores
//	        final LongVector diagScoreVector=diagVector.add(scoreAddVector);
//	        final LongVector upScoreVector=upVector.add(insVector);
//	        final LongVector leftScoreVector=leftVector.add(delIncrementVector);
//	        
//	        // Find maximum scores
//	        final LongVector maxDiagUpVector=diagScoreVector.max(upScoreVector);
//	        
//	        // Masked comparison 
//	        final LongVector maskedMaxVector=maxDiagUpVector.and(scoreMaskVector);
//	        final LongVector maskedLeftVector=leftScoreVector.and(scoreMaskVector);
//	        final VectorMask<Long> comparisonMask=maskedMaxVector.compare(
//	                VectorOperators.GE, maskedLeftVector);
//	        
//	        // Select final max - CORRECT BLENDING
//	        final LongVector resultVector=leftScoreVector.blend(maxDiagUpVector, comparisonMask);
//	        
//	        // Store results directly - aligned, contiguous store
//	        resultVector.intoArray(diag_k, col-1);
//	        
//	        // Debug output if needed
//	        if(debug) {
//	            for(int i=0; i<LWIDTH; i++) {
//	                int currentCol=col+i;
//	                int row=k-currentCol;
//	                if(row >= 2 && row <= qLen && currentCol>=2 && currentCol<=innerMaxCol) {
//	                    System.err.println("Cell ("+row+","+currentCol+") calculation (SIMD):");
//	                    System.err.println("*Setting ("+row+","+currentCol+") (diag_k["+(currentCol-1)+"]) to "+
//	                            resultVector.lane(i)+" ("+(resultVector.lane(i)>>42)+")");
//	                }
//	            }
//	        }
//	        
//	        // REMOVED: col+=LWIDTH-1; // This was causing cells to be skipped!
//	    }
//	    
//	    // Handle remaining columns with scalar code - start after the last SIMD block
//	    int lastVectorCol = Math.min(innerMaxCol, vectorBound + LWIDTH - 1);
//	    for(int col=lastVectorCol+1; col<=innerMaxCol; col++) {
//	        int row=k-col;
//	        
//	        if(row < 2 || row > qLen) continue; // Skip invalid rows
//	        
//	        final long q=revQuery[qLen-row];
//	        final long r=ref[col-1];
//	        
//	        final boolean isMatch=(q==r);
//	        final boolean hasN=(q|r)>15;
//	        long scoreAdd=isMatch?MATCH:(hasN?N_SCORE:SUB);
//	        
//	        long diagValue=diag_km2[col-2];
//	        long upValue=diag_km1[col-1];
//	        long leftValue=diag_km1[col-2];
//	        
//	        long diagScore=diagValue+scoreAdd;
//	        long upScore=upValue+INS;
//	        long leftScore=leftValue+DEL_INCREMENT;
//	        
//	        final long maxDiagUp=Math.max(diagScore, upScore);
//	        final long maxValue=(maxDiagUp&SCORE_MASK)>=leftScore?maxDiagUp:leftScore;
//	        
//	        diag_k[col-1]=maxValue;
//	        
//	        if(debug) {
//	            System.err.println("Cell ("+row+","+col+") calculation (scalar):");
//	            System.err.println("*Setting ("+row+","+col+") (diag_k["+(col-1)+"]) to "+maxValue+" ("+(maxValue>>42)+")");
//	        }
//	    }
//	}
	
	@SuppressWarnings("restriction")
	public static void processInnerLoopSIMD(
	        long[] revQuery, long[] ref, int k, 
	        int innerMinCol, int innerMaxCol, int qLen,
	        long[] diag_km2, long[] diag_km1, long[] diag_k,
	        long MATCH, long SUB, long INS, long DEL_INCREMENT, 
	        long N_SCORE, long SCORE_MASK, boolean debug) {
	    
	    if(debug) {System.err.print(".");}
	    
	    // Calculate the number of vector operations needed
	    final int vectorBound=innerMaxCol-LWIDTH+1;
	    
	    // Flag to track if any SIMD processing occurred
	    boolean didSIMD=false;
	    
	    // Create constant vectors once
	    final LongVector matchVector=LongVector.broadcast(LSPECIES, MATCH);
	    final LongVector subVector=LongVector.broadcast(LSPECIES, SUB);
	    final LongVector nScoreVector=LongVector.broadcast(LSPECIES, N_SCORE);
	    final LongVector insVector=LongVector.broadcast(LSPECIES, INS);
	    final LongVector delIncrementVector=LongVector.broadcast(LSPECIES, DEL_INCREMENT);
	    final LongVector fifteenVector=LongVector.broadcast(LSPECIES, 15);
	    final LongVector scoreMaskVector=LongVector.broadcast(LSPECIES, SCORE_MASK);
	    
	    // Handle vector-sized blocks
	    for(int col=innerMinCol; col<=vectorBound; col+=LWIDTH) {
	        didSIMD=true;
	        
	        // Calculate base index for revQuery
	        final int queryBaseIdx=qLen+col-k;
	        
	        // Load LWIDTH elements from each array with their respective offsets
	        final LongVector qVector=LongVector.fromArray(LSPECIES, revQuery, queryBaseIdx);
	        final LongVector rVector=LongVector.fromArray(LSPECIES, ref, col-1);
	        final LongVector diagVector=LongVector.fromArray(LSPECIES, diag_km2, col-2);
	        final LongVector upVector=LongVector.fromArray(LSPECIES, diag_km1, col-1);
	        final LongVector leftVector=LongVector.fromArray(LSPECIES, diag_km1, col-2);
	        
	        // Check for matches and Ns
	        final VectorMask<Long> isMatchMask=qVector.compare(VectorOperators.EQ, rVector);
	        final LongVector orVector=qVector.or(rVector);
	        final VectorMask<Long> hasNMask=orVector.compare(VectorOperators.GT, fifteenVector);
	        
	        // Calculate score to add - start with SUB
	        LongVector scoreAddVector=subVector;
	        
	        // Apply the masks using vector blend operations
	        scoreAddVector=scoreAddVector.blend(nScoreVector, hasNMask);
	        scoreAddVector=scoreAddVector.blend(matchVector, isMatchMask);
	        
	        // Calculate scores
	        final LongVector diagScoreVector=diagVector.add(scoreAddVector);
	        final LongVector upScoreVector=upVector.add(insVector);
	        final LongVector leftScoreVector=leftVector.add(delIncrementVector);
	        
	        // Find maximum scores
	        final LongVector maxDiagUpVector=diagScoreVector.max(upScoreVector);
	        
	        // Masked comparison 
	        final LongVector maskedMaxVector=maxDiagUpVector.and(scoreMaskVector);
	        final LongVector maskedLeftVector=leftScoreVector.and(scoreMaskVector);
	        final VectorMask<Long> comparisonMask=maskedMaxVector.compare(
	                VectorOperators.GE, maskedLeftVector);
	        
	        // Select final max - CORRECT BLENDING
	        final LongVector resultVector=leftScoreVector.blend(maxDiagUpVector, comparisonMask);
	        
	        // Store results directly - aligned, contiguous store
	        resultVector.intoArray(diag_k, col-1);
	        
	        // Debug output if needed
	        if(debug) {
	            for(int i=0; i<LWIDTH; i++) {
	                int currentCol=col+i;
	                int row=k-currentCol;
	                if(row >= 2 && row <= qLen && currentCol>=2 && currentCol<=innerMaxCol) {
	                    System.err.println("Cell ("+row+","+currentCol+") calculation (SIMD):");
	                    System.err.println("*Setting ("+row+","+currentCol+") (diag_k["+(currentCol-1)+"]) to "+
	                            resultVector.lane(i)+" ("+(resultVector.lane(i)>>42)+")");
	                }
	            }
	        }
	    }
	    
	    // Handle remaining columns with scalar code
	    int startCol;
	    if(didSIMD) {
	        // If SIMD was used, start after the last vector-processed block
	        int lastVectorCol=Math.min(innerMaxCol, vectorBound + LWIDTH - 1);
	        startCol=lastVectorCol + 1;
	    } else {
	        // If no SIMD processing occurred, process all cells using scalar code
	        startCol=innerMinCol;
	    }
	    
	    for(int col=startCol; col<=innerMaxCol; col++) {
	        int row=k-col;
	        
	        if(row < 2 || row > qLen) continue; // Skip invalid rows
	        
	        final long q=revQuery[qLen-row];
	        final long r=ref[col-1];
	        
	        final boolean isMatch=(q==r);
	        final boolean hasN=(q|r)>15;
	        long scoreAdd=isMatch?MATCH:(hasN?N_SCORE:SUB);
	        
	        long diagValue=diag_km2[col-2];
	        long upValue=diag_km1[col-1];
	        long leftValue=diag_km1[col-2];
	        
	        long diagScore=diagValue+scoreAdd;
	        long upScore=upValue+INS;
	        long leftScore=leftValue+DEL_INCREMENT;
	        
	        final long maxDiagUp=Math.max(diagScore, upScore);
	        final long maxValue=(maxDiagUp&SCORE_MASK)>=leftScore?maxDiagUp:leftScore;
	        
	        diag_k[col-1]=maxValue;
	        
	        if(debug) {
	            System.err.println("Cell ("+row+","+col+") calculation (scalar):");
	            System.err.println("*Setting ("+row+","+col+") (diag_k["+(col-1)+"]) to "+maxValue+" ("+(maxValue>>42)+")");
	        }
	    }
	}
	
	@SuppressWarnings("restriction")
	/** 
	 * Finds the maximum value.
	 * @param a A vector.
	 * @return The max.
	 */
	static final float max(final float[] a, final int from, final int to){
		final int length=to-from+1;
		final int limit0=FSPECIES.loopBound(length);
		final int limit=from+limit0;
		
		int i=from;
		FloatVector max=FloatVector.broadcast(FSPECIES, a[from]);
		for(; i<limit; i+=FWIDTH) {//SIMD loop
			FloatVector va=FloatVector.fromArray(FSPECIES, a, i);
			max=max.max(va);
		}
		float c=max.reduceLanes(VectorOperators.MAX);
		for (; i<=to; i++) {//Residual scalar loop
			final float x=a[i];
			c=(x>c ? x : c);
		}
		return c;
	}
	
	
}
