/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.sysml.runtime.matrix.data;
import java.util.ArrayList;
import java.util.Arrays;
import org.apache.sysml.parser.Expression.ValueType;
import org.apache.sysml.runtime.DMLRuntimeException;
import org.apache.sysml.runtime.compress.CompressedMatrixBlock;
import org.apache.sysml.runtime.controlprogram.caching.CacheBlock;
import org.apache.sysml.runtime.controlprogram.caching.MatrixObject.UpdateType;
import org.apache.sysml.runtime.functionobjects.Builtin;
import org.apache.sysml.runtime.instructions.spark.utils.SparkUtils;
import org.apache.sysml.lops.PartialAggregate.CorrectionLocationType;
import org.apache.sysml.runtime.matrix.mapred.IndexedMatrixValue;
import org.apache.sysml.runtime.matrix.operators.AggregateBinaryOperator;
import org.apache.sysml.runtime.matrix.operators.AggregateOperator;
import org.apache.sysml.runtime.matrix.operators.AggregateUnaryOperator;
import org.apache.sysml.runtime.matrix.operators.BinaryOperator;
import org.apache.sysml.runtime.matrix.operators.Operator;
import org.apache.sysml.runtime.matrix.operators.ReorgOperator;
import org.apache.sysml.runtime.matrix.operators.ScalarOperator;
import org.apache.sysml.runtime.matrix.operators.UnaryOperator;
import org.apache.sysml.runtime.util.IndexRange;
import org.apache.sysml.runtime.util.UtilFunctions;
public class OperationsOnMatrixValues
{
public static void performScalarIgnoreIndexes(MatrixValue valueIn, MatrixValue valueOut, ScalarOperator op)
throws DMLRuntimeException
{
valueIn.scalarOperations(op, valueOut);
}
public static void performUnaryIgnoreIndexes(MatrixValue valueIn, MatrixValue valueOut, UnaryOperator op)
throws DMLRuntimeException
{
valueIn.unaryOperations(op, valueOut);
}
public static void performReorg(MatrixIndexes indexesIn, MatrixValue valueIn, MatrixIndexes indexesOut,
MatrixValue valueOut, ReorgOperator op, int startRow, int startColumn, int length)
throws DMLRuntimeException
{
//operate on the value indexes first
op.fn.execute(indexesIn, indexesOut);
//operation on the cells inside the value
valueIn.reorgOperations(op, valueOut, startRow, startColumn, length);
}
public static void performAppend(MatrixValue valueIn1, MatrixValue valueIn2,
ArrayList<IndexedMatrixValue> outlist, int blockRowFactor, int blockColFactor, boolean cbind, boolean m2IsLast, int nextNCol)
throws DMLRuntimeException
{
valueIn1.appendOperations(valueIn2, outlist, blockRowFactor, blockColFactor, cbind, m2IsLast, nextNCol);
}
public static void performZeroOut(MatrixIndexes indexesIn, MatrixValue valueIn,
MatrixIndexes indexesOut, MatrixValue valueOut, IndexRange range, boolean complementary)
throws DMLRuntimeException
{
valueIn.zeroOutOperations(valueOut, range, complementary);
indexesOut.setIndexes(indexesIn);
}
// ------------- Ternary Operations -------------
// tertiary where all three inputs are matrices
public static void performTernary(MatrixIndexes indexesIn1, MatrixValue valueIn1, MatrixIndexes indexesIn2, MatrixValue valueIn2,
MatrixIndexes indexesIn3, MatrixValue valueIn3, CTableMap resultMap, MatrixBlock resultBlock, Operator op )
throws DMLRuntimeException
{
//operation on the cells inside the value
valueIn1.ternaryOperations(op, valueIn2, valueIn3, resultMap, resultBlock);
}
// tertiary where first two inputs are matrices, and third input is a scalar (double)
public static void performTernary(MatrixIndexes indexesIn1, MatrixValue valueIn1, MatrixIndexes indexesIn2, MatrixValue valueIn2,
double scalarIn3, CTableMap resultMap, MatrixBlock resultBlock, Operator op)
throws DMLRuntimeException
{
//operation on the cells inside the value
valueIn1.ternaryOperations(op, valueIn2, scalarIn3, false, resultMap, resultBlock);
}
// tertiary where first input is a matrix, and second and third inputs are scalars (double)
public static void performTernary(MatrixIndexes indexesIn1, MatrixValue valueIn1, double scalarIn2,
double scalarIn3, CTableMap resultMap, MatrixBlock resultBlock, Operator op )
throws DMLRuntimeException
{
//operation on the cells inside the value
valueIn1.ternaryOperations(op, scalarIn2, scalarIn3, resultMap, resultBlock);
}
// tertiary where first input is a matrix, and second is scalars (double)
public static void performTernary(MatrixIndexes indexesIn1, MatrixValue valueIn1, double scalarIn2, boolean left,
int brlen, CTableMap resultMap, MatrixBlock resultBlock, Operator op )
throws DMLRuntimeException
{
//operation on the cells inside the value
valueIn1.ternaryOperations(op, indexesIn1, scalarIn2, left, brlen, resultMap, resultBlock);
}
// tertiary where first and third inputs are matrices, and second is a scalars (double)
public static void performTernary(MatrixIndexes indexesIn1, MatrixValue valueIn1, double scalarIn2,
MatrixIndexes indexesIn3, MatrixValue valueIn3, CTableMap resultMap, MatrixBlock resultBlock, Operator op )
throws DMLRuntimeException
{
//operation on the cells inside the value
valueIn1.ternaryOperations(op, scalarIn2, valueIn3, resultMap, resultBlock);
}
// -----------------------------------------------------
//binary operations are those that the indexes of both cells have to be matched
public static void performBinaryIgnoreIndexes(MatrixValue value1, MatrixValue value2,
MatrixValue valueOut, BinaryOperator op)
throws DMLRuntimeException
{
value1.binaryOperations(op, value2, valueOut);
}
public static void startAggregation(MatrixValue valueOut, MatrixValue correction, AggregateOperator op,
int rlen, int clen, boolean sparseHint, boolean imbededCorrection)
throws DMLRuntimeException
{
int outRow=0, outCol=0, corRow=0, corCol=0;
if(op.correctionExists)
{
if(!imbededCorrection)
{
switch(op.correctionLocation)
{
case NONE:
outRow=rlen;
outCol=clen;
corRow=rlen;
corCol=clen;
break;
case LASTROW:
outRow=rlen-1;
outCol=clen;
corRow=1;
corCol=clen;
break;
case LASTCOLUMN:
if(op.increOp.fn instanceof Builtin
&& ( ((Builtin)(op.increOp.fn)).bFunc == Builtin.BuiltinCode.MAXINDEX
|| ((Builtin)(op.increOp.fn)).bFunc == Builtin.BuiltinCode.MININDEX) )
{
outRow = rlen;
outCol = 1;
corRow = rlen;
corCol = 1;
}
else{
outRow=rlen;
outCol=clen-1;
corRow=rlen;
corCol=1;
}
break;
case LASTTWOROWS:
outRow=rlen-2;
outCol=clen;
corRow=2;
corCol=clen;
break;
case LASTTWOCOLUMNS:
outRow=rlen;
outCol=clen-2;
corRow=rlen;
corCol=2;
break;
case LASTFOURROWS:
outRow=rlen-4;
outCol=clen;
corRow=4;
corCol=clen;
break;
case LASTFOURCOLUMNS:
outRow=rlen;
outCol=clen-4;
corRow=rlen;
corCol=4;
break;
default:
throw new DMLRuntimeException("unrecognized correctionLocation: "+op.correctionLocation);
}
}else
{
outRow=rlen;
outCol=clen;
corRow=rlen;
corCol=clen;
}
//set initial values according to operator
if(op.initialValue==0) {
valueOut.reset(outRow, outCol, sparseHint);
correction.reset(corRow, corCol, false);
}
else {
valueOut.reset(outRow, outCol, op.initialValue);
correction.reset(corRow, corCol, op.initialValue);
}
}
else
{
if(op.initialValue==0)
valueOut.reset(rlen, clen, sparseHint);
else
valueOut.reset(rlen, clen, op.initialValue);
}
}
public static void incrementalAggregation(MatrixValue valueAgg, MatrixValue correction, MatrixValue valueAdd,
AggregateOperator op, boolean imbededCorrection) throws DMLRuntimeException
{
if(op.correctionExists)
{
if(!imbededCorrection || op.correctionLocation==CorrectionLocationType.NONE)
valueAgg.incrementalAggregate(op, correction, valueAdd);
else
valueAgg.incrementalAggregate(op, valueAdd);
}
else
valueAgg.binaryOperationsInPlace(op.increOp, valueAdd);
}
public static void performAggregateUnary(MatrixIndexes indexesIn, MatrixValue valueIn, MatrixIndexes indexesOut,
MatrixValue valueOut, AggregateUnaryOperator op,int brlen, int bclen)
throws DMLRuntimeException
{
//operate on the value indexes first
op.indexFn.execute(indexesIn, indexesOut);
//perform on the value
valueIn.aggregateUnaryOperations(op, valueOut, brlen, bclen, indexesIn);
}
public static void performAggregateBinary(MatrixIndexes indexes1, MatrixValue value1, MatrixIndexes indexes2, MatrixValue value2,
MatrixIndexes indexesOut, MatrixValue valueOut, AggregateBinaryOperator op)
throws DMLRuntimeException
{
//compute output index
indexesOut.setIndexes(indexes1.getRowIndex(), indexes2.getColumnIndex());
//perform on the value
if( value2 instanceof CompressedMatrixBlock )
value2.aggregateBinaryOperations(value1, value2, valueOut, op);
else //default
value1.aggregateBinaryOperations(indexes1, value1, indexes2, value2, valueOut, op);
}
public static MatrixValue performAggregateBinaryIgnoreIndexes(
MatrixValue value1, MatrixValue value2,
MatrixValue valueOut, AggregateBinaryOperator op)
throws DMLRuntimeException {
//perform on the value
if( value2 instanceof CompressedMatrixBlock )
value2.aggregateBinaryOperations(value1, value2, valueOut, op);
else
value1.aggregateBinaryOperations(value1, value2, valueOut, op);
return valueOut;
}
@SuppressWarnings("rawtypes")
public static ArrayList performSlice(IndexRange ixrange, int brlen, int bclen, int iix, int jix, CacheBlock in)
throws DMLRuntimeException
{
if( in instanceof MatrixBlock )
return performSlice(ixrange, brlen, bclen, iix, jix, (MatrixBlock)in);
else if( in instanceof FrameBlock )
return performSlice(ixrange, brlen, bclen, iix, jix, (FrameBlock)in);
throw new DMLRuntimeException("Unsupported cache block type: "+in.getClass().getName());
}
@SuppressWarnings("rawtypes")
public static ArrayList performSlice(IndexRange ixrange, int brlen, int bclen, int iix, int jix, MatrixBlock in)
throws DMLRuntimeException
{
IndexedMatrixValue imv = new IndexedMatrixValue(new MatrixIndexes(iix, jix), (MatrixBlock)in);
ArrayList<IndexedMatrixValue> outlist = new ArrayList<IndexedMatrixValue>();
performSlice(imv, ixrange, brlen, bclen, outlist);
return SparkUtils.fromIndexedMatrixBlockToPair(outlist);
}
public static void performSlice(IndexedMatrixValue in, IndexRange ixrange, int brlen, int bclen, ArrayList<IndexedMatrixValue> outlist)
throws DMLRuntimeException
{
long cellIndexTopRow = UtilFunctions.computeCellIndex(in.getIndexes().getRowIndex(), brlen, 0);
long cellIndexBottomRow = UtilFunctions.computeCellIndex(in.getIndexes().getRowIndex(), brlen, in.getValue().getNumRows()-1);
long cellIndexLeftCol = UtilFunctions.computeCellIndex(in.getIndexes().getColumnIndex(), bclen, 0);
long cellIndexRightCol = UtilFunctions.computeCellIndex(in.getIndexes().getColumnIndex(), bclen, in.getValue().getNumColumns()-1);
long cellIndexOverlapTop = Math.max(cellIndexTopRow, ixrange.rowStart);
long cellIndexOverlapBottom = Math.min(cellIndexBottomRow, ixrange.rowEnd);
long cellIndexOverlapLeft = Math.max(cellIndexLeftCol, ixrange.colStart);
long cellIndexOverlapRight = Math.min(cellIndexRightCol, ixrange.colEnd);
//check if block is outside the indexing range
if(cellIndexOverlapTop>cellIndexOverlapBottom || cellIndexOverlapLeft>cellIndexOverlapRight) {
return;
}
IndexRange tmpRange = new IndexRange(
UtilFunctions.computeCellInBlock(cellIndexOverlapTop, brlen),
UtilFunctions.computeCellInBlock(cellIndexOverlapBottom, brlen),
UtilFunctions.computeCellInBlock(cellIndexOverlapLeft, bclen),
UtilFunctions.computeCellInBlock(cellIndexOverlapRight, bclen));
int rowCut=UtilFunctions.computeCellInBlock(ixrange.rowStart, brlen);
int colCut=UtilFunctions.computeCellInBlock(ixrange.colStart, bclen);
int rowsInLastBlock = (int)((ixrange.rowEnd-ixrange.rowStart+1)%brlen);
if(rowsInLastBlock==0)
rowsInLastBlock=brlen;
int colsInLastBlock = (int)((ixrange.colEnd-ixrange.colStart+1)%bclen);
if(colsInLastBlock==0)
colsInLastBlock=bclen;
long resultBlockIndexTop=UtilFunctions.computeBlockIndex(cellIndexOverlapTop-ixrange.rowStart+1, brlen);
long resultBlockIndexBottom=UtilFunctions.computeBlockIndex(cellIndexOverlapBottom-ixrange.rowStart+1, brlen);
long resultBlockIndexLeft=UtilFunctions.computeBlockIndex(cellIndexOverlapLeft-ixrange.colStart+1, bclen);
long resultBlockIndexRight=UtilFunctions.computeBlockIndex(cellIndexOverlapRight-ixrange.colStart+1, bclen);
int boundaryRlen = brlen;
int boundaryClen = bclen;
long finalBlockIndexBottom=UtilFunctions.computeBlockIndex(ixrange.rowEnd-ixrange.rowStart+1, brlen);
long finalBlockIndexRight=UtilFunctions.computeBlockIndex(ixrange.colEnd-ixrange.colStart+1, bclen);
if(resultBlockIndexBottom==finalBlockIndexBottom)
boundaryRlen=rowsInLastBlock;
if(resultBlockIndexRight==finalBlockIndexRight)
boundaryClen=colsInLastBlock;
//allocate space for the output value
for(long r=resultBlockIndexTop; r<=resultBlockIndexBottom; r++)
for(long c=resultBlockIndexLeft; c<=resultBlockIndexRight; c++)
{
IndexedMatrixValue out=new IndexedMatrixValue(new MatrixIndexes(), new MatrixBlock());
out.getIndexes().setIndexes(r, c);
outlist.add(out);
}
//execute actual slice operation
in.getValue().sliceOperations(outlist, tmpRange, rowCut, colCut, brlen, bclen, boundaryRlen, boundaryClen);
}
public static void performShift(IndexedMatrixValue in, IndexRange ixrange, int brlen, int bclen, long rlen, long clen, ArrayList<IndexedMatrixValue> outlist)
throws DMLRuntimeException
{
MatrixIndexes ix = in.getIndexes();
MatrixBlock mb = (MatrixBlock)in.getValue();
long start_lhs_globalRowIndex = ixrange.rowStart + (ix.getRowIndex()-1)*brlen;
long start_lhs_globalColIndex = ixrange.colStart + (ix.getColumnIndex()-1)*bclen;
long end_lhs_globalRowIndex = start_lhs_globalRowIndex + mb.getNumRows() - 1;
long end_lhs_globalColIndex = start_lhs_globalColIndex + mb.getNumColumns() - 1;
long start_lhs_rowIndex = UtilFunctions.computeBlockIndex(start_lhs_globalRowIndex, brlen);
long end_lhs_rowIndex = UtilFunctions.computeBlockIndex(end_lhs_globalRowIndex, brlen);
long start_lhs_colIndex = UtilFunctions.computeBlockIndex(start_lhs_globalColIndex, bclen);
long end_lhs_colIndex = UtilFunctions.computeBlockIndex(end_lhs_globalColIndex, bclen);
for(long leftRowIndex = start_lhs_rowIndex; leftRowIndex <= end_lhs_rowIndex; leftRowIndex++) {
for(long leftColIndex = start_lhs_colIndex; leftColIndex <= end_lhs_colIndex; leftColIndex++) {
// Calculate global index of right hand side block
long lhs_rl = Math.max((leftRowIndex-1)*brlen+1, start_lhs_globalRowIndex);
long lhs_ru = Math.min(leftRowIndex*brlen, end_lhs_globalRowIndex);
long lhs_cl = Math.max((leftColIndex-1)*bclen+1, start_lhs_globalColIndex);
long lhs_cu = Math.min(leftColIndex*bclen, end_lhs_globalColIndex);
int lhs_lrl = UtilFunctions.computeCellInBlock(lhs_rl, brlen);
int lhs_lru = UtilFunctions.computeCellInBlock(lhs_ru, brlen);
int lhs_lcl = UtilFunctions.computeCellInBlock(lhs_cl, bclen);
int lhs_lcu = UtilFunctions.computeCellInBlock(lhs_cu, bclen);
long rhs_rl = lhs_rl - ixrange.rowStart + 1;
long rhs_ru = rhs_rl + (lhs_ru - lhs_rl);
long rhs_cl = lhs_cl - ixrange.colStart + 1;
long rhs_cu = rhs_cl + (lhs_cu - lhs_cl);
int rhs_lrl = UtilFunctions.computeCellInBlock(rhs_rl, brlen);
int rhs_lru = UtilFunctions.computeCellInBlock(rhs_ru, brlen);
int rhs_lcl = UtilFunctions.computeCellInBlock(rhs_cl, bclen);
int rhs_lcu = UtilFunctions.computeCellInBlock(rhs_cu, bclen);
MatrixBlock slicedRHSBlk = mb.sliceOperations(rhs_lrl, rhs_lru, rhs_lcl, rhs_lcu, new MatrixBlock());
int lbrlen = UtilFunctions.computeBlockSize(rlen, leftRowIndex, brlen);
int lbclen = UtilFunctions.computeBlockSize(clen, leftColIndex, bclen);
MatrixBlock resultBlock = new MatrixBlock(lbrlen, lbclen, false);
resultBlock = resultBlock.leftIndexingOperations(slicedRHSBlk, lhs_lrl, lhs_lru, lhs_lcl, lhs_lcu, null, UpdateType.COPY);
outlist.add(new IndexedMatrixValue(new MatrixIndexes(leftRowIndex, leftColIndex), resultBlock));
}
}
}
public static void performMapGroupedAggregate( Operator op, IndexedMatrixValue inTarget, MatrixBlock groups, int ngroups, int brlen, int bclen, ArrayList<IndexedMatrixValue> outlist ) throws DMLRuntimeException
{
MatrixIndexes ix = inTarget.getIndexes();
MatrixBlock target = (MatrixBlock)inTarget.getValue();
//execute grouped aggregate operations
MatrixBlock out = groups.groupedAggOperations(target, null, new MatrixBlock(), ngroups, op);
if( out.getNumRows()<=brlen && out.getNumColumns()<=bclen )
{
//single output block
outlist.add( new IndexedMatrixValue(new MatrixIndexes(1,ix.getColumnIndex()), out) );
}
else
{
//multiple output blocks (by op def, single column block )
for(int blockRow = 0; blockRow < (int)Math.ceil(out.getNumRows()/(double)brlen); blockRow++)
{
int maxRow = (blockRow*brlen + brlen < out.getNumRows()) ? brlen : out.getNumRows() - blockRow*brlen;
int row_offset = blockRow*brlen;
//copy submatrix to block
MatrixBlock tmp = out.sliceOperations( row_offset, row_offset+maxRow-1,
0, out.getNumColumns()-1, new MatrixBlock() );
//append block to result cache
outlist.add(new IndexedMatrixValue(new MatrixIndexes(blockRow+1,ix.getColumnIndex()), tmp));
}
}
}
@SuppressWarnings("rawtypes")
public static ArrayList performSlice(IndexRange ixrange, int brlen, int bclen, int iix, int jix, FrameBlock in)
throws DMLRuntimeException
{
Pair<Long, FrameBlock> lfp = new Pair<Long, FrameBlock>(new Long(((iix-1)*brlen)+1), in);
ArrayList<Pair<Long, FrameBlock>> outlist = new ArrayList<Pair<Long, FrameBlock>>();
performSlice(lfp, ixrange, brlen, bclen, outlist);
return outlist;
}
/**
* This function will get slice of the input frame block overlapping in overall slice(Range), slice has requested for.
*
* @param in ?
* @param ixrange index range
* @param brlen number of rows in a block
* @param bclen number of columns in a block
* @param outlist list of pairs of frame blocks
* @throws DMLRuntimeException if DMLRuntimeException occurs
*/
public static void performSlice(Pair<Long,FrameBlock> in, IndexRange ixrange, int brlen, int bclen, ArrayList<Pair<Long,FrameBlock>> outlist)
throws DMLRuntimeException
{
long index = in.getKey();
FrameBlock block = in.getValue();
// Get Block indexes (rows and columns boundaries)
long cellIndexTopRow = index;
long cellIndexBottomRow = index+block.getNumRows()-1;
long cellIndexLeftCol = 1;
long cellIndexRightCol = block.getNumColumns();
// Calculate block boundaries with range of slice to be performed (Global index)
long cellIndexOverlapTop = Math.max(cellIndexTopRow, ixrange.rowStart);
long cellIndexOverlapBottom = Math.min(cellIndexBottomRow, ixrange.rowEnd);
long cellIndexOverlapLeft = Math.max(cellIndexLeftCol, ixrange.colStart);
long cellIndexOverlapRight = Math.min(cellIndexRightCol, ixrange.colEnd);
//check if block is outside the indexing range
if(cellIndexOverlapTop>cellIndexOverlapBottom || cellIndexOverlapLeft>cellIndexOverlapRight) {
return;
}
// Create IndexRange for the slice to be performed on this block.
IndexRange tmpRange = new IndexRange(
cellIndexOverlapTop - index,
cellIndexOverlapBottom - index,
cellIndexOverlapLeft -1,
cellIndexOverlapRight - 1);
// Get Top Row and Left column cutting point.
int rowCut=(int)(ixrange.rowStart-index);
// Get indices for result block
long resultBlockIndexTop=UtilFunctions.computeBlockIndex(cellIndexOverlapTop, brlen);
long resultBlockIndexBottom=UtilFunctions.computeBlockIndex(cellIndexOverlapBottom, brlen);
//allocate space for the output value
for(long r=resultBlockIndexTop; r<=resultBlockIndexBottom; r++)
{
ValueType[] schema = Arrays.copyOfRange(block.getSchema(), (int)tmpRange.colStart, (int)tmpRange.colEnd+1);
long iResultIndex = Math.max(((r-1)*brlen - ixrange.rowStart + 1), 0);
Pair<Long,FrameBlock> out=new Pair<Long,FrameBlock>(new Long(iResultIndex+1), new FrameBlock(schema));
outlist.add(out);
}
//execute actual slice operation
block.sliceOperations(outlist, tmpRange, rowCut);
}
public static void performShift(Pair<Long,FrameBlock> in, IndexRange ixrange, int brlenLeft, int clenLeft/*, int bclen*/, long rlen, long clen, ArrayList<Pair<Long,FrameBlock>> outlist)
throws DMLRuntimeException
{
Long ix = in.getKey();
FrameBlock fb = in.getValue();
long start_lhs_globalRowIndex = ixrange.rowStart + (ix-1);
long start_lhs_globalColIndex = ixrange.colStart;
long end_lhs_globalRowIndex = start_lhs_globalRowIndex + fb.getNumRows() - 1;
long end_lhs_globalColIndex = ixrange.colEnd;
long start_lhs_rowIndex = UtilFunctions.computeBlockIndex(start_lhs_globalRowIndex, brlenLeft);
long end_lhs_rowIndex = UtilFunctions.computeBlockIndex(end_lhs_globalRowIndex, brlenLeft);
for(long leftRowIndex = start_lhs_rowIndex; leftRowIndex <= end_lhs_rowIndex; leftRowIndex++) {
// Calculate global index of right hand side block
long lhs_rl = Math.max((leftRowIndex-1)*brlenLeft+1, start_lhs_globalRowIndex);
long lhs_ru = Math.min(leftRowIndex*brlenLeft, end_lhs_globalRowIndex);
long lhs_cl = start_lhs_globalColIndex;
long lhs_cu = end_lhs_globalColIndex;
int lhs_lrl = UtilFunctions.computeCellInBlock(lhs_rl, brlenLeft);
int lhs_lru = UtilFunctions.computeCellInBlock(lhs_ru, brlenLeft);
int lhs_lcl = (int)lhs_cl-1;
int lhs_lcu = (int)lhs_cu-1;
long rhs_rl = lhs_rl - (ixrange.rowStart-1) - (ix-1);
long rhs_ru = rhs_rl + (lhs_ru - lhs_rl);
long rhs_cl = lhs_cl - ixrange.colStart + 1;
long rhs_cu = rhs_cl + (lhs_cu - lhs_cl);
// local indices are 0 (zero) based.
int rhs_lrl = (int) (UtilFunctions.computeCellInBlock(rhs_rl, fb.getNumRows()));
int rhs_lru = (int) (UtilFunctions.computeCellInBlock(rhs_ru, fb.getNumRows()));
int rhs_lcl = (int)rhs_cl-1;
int rhs_lcu = (int)rhs_cu-1;
FrameBlock slicedRHSBlk = fb.sliceOperations(rhs_lrl, rhs_lru, rhs_lcl, rhs_lcu, new FrameBlock());
int lbclen = clenLeft;
ValueType[] schemaPartialLeft = UtilFunctions.nCopies(lhs_lcl, ValueType.STRING);
ValueType[] schemaRHS = Arrays.copyOfRange(fb.getSchema(), (int)(rhs_lcl), (int)(rhs_lcl-lhs_lcl+lhs_lcu+1));
ValueType[] schema = UtilFunctions.copyOf(schemaPartialLeft, schemaRHS);
ValueType[] schemaPartialRight = UtilFunctions.nCopies(lbclen-schema.length, ValueType.STRING);
schema = UtilFunctions.copyOf(schema, schemaPartialRight);
FrameBlock resultBlock = new FrameBlock(schema);
int iRHSRows = (int)(leftRowIndex<=rlen/brlenLeft?brlenLeft:rlen-(rlen/brlenLeft)*brlenLeft);
resultBlock.ensureAllocatedColumns(iRHSRows);
resultBlock = resultBlock.leftIndexingOperations(slicedRHSBlk, lhs_lrl, lhs_lru, lhs_lcl, lhs_lcu, new FrameBlock());
outlist.add(new Pair<Long, FrameBlock>((leftRowIndex-1)*brlenLeft+1, resultBlock));
}
}
}