blob: 33f638f908aa2279252d923648c058a38cf7e55e [file] [log] [blame]
/*
* 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.sysds.runtime.matrix.data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.sysds.common.Types.CorrectionLocationType;
import org.apache.sysds.common.Types.ValueType;
import org.apache.sysds.runtime.DMLRuntimeException;
import org.apache.sysds.runtime.compress.CompressedMatrixBlock;
import org.apache.sysds.runtime.controlprogram.caching.CacheBlock;
import org.apache.sysds.runtime.controlprogram.caching.MatrixObject.UpdateType;
import org.apache.sysds.runtime.functionobjects.Builtin;
import org.apache.sysds.runtime.instructions.spark.data.IndexedMatrixValue;
import org.apache.sysds.runtime.instructions.spark.utils.SparkUtils;
import org.apache.sysds.runtime.matrix.operators.AggregateBinaryOperator;
import org.apache.sysds.runtime.matrix.operators.AggregateOperator;
import org.apache.sysds.runtime.matrix.operators.AggregateUnaryOperator;
import org.apache.sysds.runtime.matrix.operators.BinaryOperator;
import org.apache.sysds.runtime.matrix.operators.Operator;
import org.apache.sysds.runtime.matrix.operators.ReorgOperator;
import org.apache.sysds.runtime.util.IndexRange;
import org.apache.sysds.runtime.util.UtilFunctions;
public class OperationsOnMatrixValues
{
public static void performReorg(MatrixIndexes indexesIn, MatrixValue valueIn, MatrixIndexes indexesOut,
MatrixValue valueOut, ReorgOperator op, int startRow, int startColumn, int length) {
//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 blen, boolean cbind, boolean m2IsLast, int nextNCol) {
valueIn1.append(valueIn2, outlist, blen, cbind, m2IsLast, nextNCol);
}
public static void performZeroOut(MatrixIndexes indexesIn, MatrixValue valueIn,
MatrixIndexes indexesOut, MatrixValue valueOut, IndexRange range, boolean complementary) {
valueIn.zeroOutOperations(valueOut, range, complementary);
indexesOut.setIndexes(indexesIn);
}
// ------------- Ternary Operations -------------
public static void performCtable(MatrixIndexes indexesIn1, MatrixValue valueIn1, MatrixIndexes indexesIn2, MatrixValue valueIn2,
MatrixIndexes indexesIn3, MatrixValue valueIn3, CTableMap resultMap, MatrixBlock resultBlock, Operator op ) {
//operation on the cells inside the value
valueIn1.ctableOperations(op, valueIn2, valueIn3, resultMap, resultBlock);
}
public static void performCtable(MatrixIndexes indexesIn1, MatrixValue valueIn1, MatrixIndexes indexesIn2, MatrixValue valueIn2,
double scalarIn3, CTableMap resultMap, MatrixBlock resultBlock, Operator op) {
//operation on the cells inside the value
valueIn1.ctableOperations(op, valueIn2, scalarIn3, false, resultMap, resultBlock);
}
public static void performCtable(MatrixIndexes indexesIn1, MatrixValue valueIn1, double scalarIn2,
double scalarIn3, CTableMap resultMap, MatrixBlock resultBlock, Operator op ) {
//operation on the cells inside the value
valueIn1.ctableOperations(op, scalarIn2, scalarIn3, resultMap, resultBlock);
}
public static void performCtable(MatrixIndexes indexesIn1, MatrixValue valueIn1, double scalarIn2, boolean left,
int blen, CTableMap resultMap, MatrixBlock resultBlock, Operator op ) {
//operation on the cells inside the value
valueIn1.ctableOperations(op, indexesIn1, scalarIn2, left, blen, resultMap, resultBlock);
}
public static void performCtable(MatrixIndexes indexesIn1, MatrixValue valueIn1, double scalarIn2,
MatrixIndexes indexesIn3, MatrixValue valueIn3, CTableMap resultMap, MatrixBlock resultBlock, Operator op ) {
//operation on the cells inside the value
valueIn1.ctableOperations(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) {
value1.binaryOperations(op, value2, valueOut);
}
public static void startAggregation(MatrixValue valueOut, MatrixValue correction, AggregateOperator op,
int rlen, int clen, boolean sparseHint, boolean embeddedCorrection) {
int outRow=0, outCol=0, corRow=0, corCol=0;
if(!embeddedCorrection || op.existsCorrection())
{
if( !embeddedCorrection ) {
switch(op.correction)
{
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.correction);
}
}else
{
outRow=rlen;
outCol=clen;
corRow=rlen;
corCol=clen;
}
//set initial values according to operator
if(op.initialValue==0) {
valueOut.reset(Math.max(outRow,0), Math.max(outCol,0), sparseHint);
correction.reset(Math.max(corRow,0), Math.max(corCol,0), false);
}
else {
valueOut.reset(Math.max(outRow, 0), Math.max(outCol,0), op.initialValue);
correction.reset(Math.max(corRow,0), Math.max(corCol,0), 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 embeddedCorrection) {
incrementalAggregation(valueAgg, correction, valueAdd, op, embeddedCorrection, true);
}
public static void incrementalAggregation(MatrixValue valueAgg, MatrixValue correction, MatrixValue valueAdd,
AggregateOperator op, boolean embeddedCorrection, boolean deep)
{
if(!embeddedCorrection || op.existsCorrection()) {
if( op.correction==CorrectionLocationType.NONE )
valueAgg.incrementalAggregate(op, correction, valueAdd, deep);
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 blen) {
//operate on the value indexes first
op.indexFn.execute(indexesIn, indexesOut);
//perform on the value
valueIn.aggregateUnaryOperations(op, valueOut, blen, indexesIn);
}
public static MatrixBlock matMult(MatrixIndexes indexes1, MatrixBlock value1, MatrixIndexes indexes2, MatrixBlock value2,
MatrixIndexes indexesOut, MatrixBlock valueOut, AggregateBinaryOperator op) {
//compute output index
indexesOut.setIndexes(indexes1.getRowIndex(), indexes2.getColumnIndex());
//perform on the value
if (value2 instanceof CompressedMatrixBlock)
return value2.aggregateBinaryOperations(value1, value2, valueOut, op);
else
return value1.aggregateBinaryOperations(indexes1, value1, indexes2, value2, valueOut, op);
}
public static MatrixBlock matMult(MatrixBlock value1, MatrixBlock value2,
MatrixBlock valueOut, AggregateBinaryOperator op) {
//perform on the value
if( value2 instanceof CompressedMatrixBlock )
return value2.aggregateBinaryOperations(value1, value2, valueOut, op);
else
return value1.aggregateBinaryOperations(value1, value2, valueOut, op);
}
@SuppressWarnings("rawtypes")
public static List performSlice(IndexRange ixrange, int blen, int iix, int jix, CacheBlock in) {
if( in instanceof MatrixBlock )
return performSlice(ixrange, blen, iix, jix, (MatrixBlock)in);
else if( in instanceof FrameBlock )
return performSlice(ixrange, blen, iix, jix, (FrameBlock)in);
throw new DMLRuntimeException("Unsupported cache block type: "+in.getClass().getName());
}
@SuppressWarnings("rawtypes")
public static List performSlice(IndexRange ixrange, int blen, int iix, int jix, MatrixBlock in) {
IndexedMatrixValue imv = new IndexedMatrixValue(new MatrixIndexes(iix, jix), in);
ArrayList<IndexedMatrixValue> outlist = new ArrayList<>();
performSlice(imv, ixrange, blen, outlist);
return SparkUtils.fromIndexedMatrixBlockToPair(outlist);
}
public static void performSlice(IndexedMatrixValue in, IndexRange ixrange, int blen, ArrayList<IndexedMatrixValue> outlist) {
long cellIndexTopRow = UtilFunctions.computeCellIndex(in.getIndexes().getRowIndex(), blen, 0);
long cellIndexBottomRow = UtilFunctions.computeCellIndex(in.getIndexes().getRowIndex(), blen, in.getValue().getNumRows()-1);
long cellIndexLeftCol = UtilFunctions.computeCellIndex(in.getIndexes().getColumnIndex(), blen, 0);
long cellIndexRightCol = UtilFunctions.computeCellIndex(in.getIndexes().getColumnIndex(), blen, 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, blen),
UtilFunctions.computeCellInBlock(cellIndexOverlapBottom, blen),
UtilFunctions.computeCellInBlock(cellIndexOverlapLeft, blen),
UtilFunctions.computeCellInBlock(cellIndexOverlapRight, blen));
int rowCut=UtilFunctions.computeCellInBlock(ixrange.rowStart, blen);
int colCut=UtilFunctions.computeCellInBlock(ixrange.colStart, blen);
int rowsInLastBlock = (int)((ixrange.rowEnd-ixrange.rowStart+1)%blen);
if(rowsInLastBlock==0)
rowsInLastBlock=blen;
int colsInLastBlock = (int)((ixrange.colEnd-ixrange.colStart+1)%blen);
if(colsInLastBlock==0)
colsInLastBlock=blen;
long resultBlockIndexTop=UtilFunctions.computeBlockIndex(cellIndexOverlapTop-ixrange.rowStart+1, blen);
long resultBlockIndexBottom=UtilFunctions.computeBlockIndex(cellIndexOverlapBottom-ixrange.rowStart+1, blen);
long resultBlockIndexLeft=UtilFunctions.computeBlockIndex(cellIndexOverlapLeft-ixrange.colStart+1, blen);
long resultBlockIndexRight=UtilFunctions.computeBlockIndex(cellIndexOverlapRight-ixrange.colStart+1, blen);
int boundaryRlen = blen;
int boundaryClen = blen;
long finalBlockIndexBottom=UtilFunctions.computeBlockIndex(ixrange.rowEnd-ixrange.rowStart+1, blen);
long finalBlockIndexRight=UtilFunctions.computeBlockIndex(ixrange.colEnd-ixrange.colStart+1, blen);
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().slice(outlist, tmpRange, rowCut, colCut, blen, boundaryRlen, boundaryClen);
}
public static void performShift(IndexedMatrixValue in, IndexRange ixrange, int blen, long rlen, long clen, ArrayList<IndexedMatrixValue> outlist) {
MatrixIndexes ix = in.getIndexes();
MatrixBlock mb = (MatrixBlock)in.getValue();
long start_lhs_globalRowIndex = ixrange.rowStart + (ix.getRowIndex()-1)*blen;
long start_lhs_globalColIndex = ixrange.colStart + (ix.getColumnIndex()-1)*blen;
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, blen);
long end_lhs_rowIndex = UtilFunctions.computeBlockIndex(end_lhs_globalRowIndex, blen);
long start_lhs_colIndex = UtilFunctions.computeBlockIndex(start_lhs_globalColIndex, blen);
long end_lhs_colIndex = UtilFunctions.computeBlockIndex(end_lhs_globalColIndex, blen);
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)*blen+1, start_lhs_globalRowIndex);
long lhs_ru = Math.min(leftRowIndex*blen, end_lhs_globalRowIndex);
long lhs_cl = Math.max((leftColIndex-1)*blen+1, start_lhs_globalColIndex);
long lhs_cu = Math.min(leftColIndex*blen, end_lhs_globalColIndex);
int lhs_lrl = UtilFunctions.computeCellInBlock(lhs_rl, blen);
int lhs_lru = UtilFunctions.computeCellInBlock(lhs_ru, blen);
int lhs_lcl = UtilFunctions.computeCellInBlock(lhs_cl, blen);
int lhs_lcu = UtilFunctions.computeCellInBlock(lhs_cu, blen);
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, blen);
int rhs_lru = UtilFunctions.computeCellInBlock(rhs_ru, blen);
int rhs_lcl = UtilFunctions.computeCellInBlock(rhs_cl, blen);
int rhs_lcu = UtilFunctions.computeCellInBlock(rhs_cu, blen);
MatrixBlock slicedRHSBlk = mb.slice(rhs_lrl, rhs_lru, rhs_lcl, rhs_lcu, new MatrixBlock());
int lbrlen = UtilFunctions.computeBlockSize(rlen, leftRowIndex, blen);
int lbclen = UtilFunctions.computeBlockSize(clen, leftColIndex, blen);
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 blen, ArrayList<IndexedMatrixValue> outlist )
{
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()<=blen && out.getNumColumns()<=blen )
{
//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)blen); blockRow++)
{
int maxRow = (blockRow*blen + blen < out.getNumRows()) ? blen : out.getNumRows() - blockRow*blen;
int row_offset = blockRow*blen;
//copy submatrix to block
MatrixBlock tmp = out.slice(row_offset, row_offset+maxRow-1);
//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 blen, int iix, int jix, FrameBlock in) {
Pair<Long, FrameBlock> lfp = new Pair<>(new Long(((iix-1)*blen)+1), in);
ArrayList<Pair<Long, FrameBlock>> outlist = new ArrayList<>();
performSlice(lfp, ixrange, blen, 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 blen block length
* @param outlist list of pairs of frame blocks
*/
public static void performSlice(Pair<Long,FrameBlock> in, IndexRange ixrange, int blen, ArrayList<Pair<Long,FrameBlock>> outlist) {
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, blen);
long resultBlockIndexBottom=UtilFunctions.computeBlockIndex(cellIndexOverlapBottom, blen);
//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)*blen - ixrange.rowStart + 1), 0);
Pair<Long,FrameBlock> out=new Pair<>(new Long(iResultIndex+1), new FrameBlock(schema));
outlist.add(out);
}
//execute actual slice operation
block.slice(outlist, tmpRange, rowCut);
}
public static void performShift(Pair<Long,FrameBlock> in, IndexRange ixrange, int blenLeft, long rlen, long clen, ArrayList<Pair<Long,FrameBlock>> outlist) {
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, blenLeft);
long end_lhs_rowIndex = UtilFunctions.computeBlockIndex(end_lhs_globalRowIndex, blenLeft);
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)*blenLeft+1, start_lhs_globalRowIndex);
long lhs_ru = Math.min(leftRowIndex*blenLeft, end_lhs_globalRowIndex);
long lhs_cl = start_lhs_globalColIndex;
long lhs_cu = end_lhs_globalColIndex;
int lhs_lrl = UtilFunctions.computeCellInBlock(lhs_rl, blenLeft);
int lhs_lru = UtilFunctions.computeCellInBlock(lhs_ru, blenLeft);
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 = UtilFunctions.computeCellInBlock(rhs_rl, fb.getNumRows());
int rhs_lru = UtilFunctions.computeCellInBlock(rhs_ru, fb.getNumRows());
int rhs_lcl = (int)rhs_cl-1;
int rhs_lcu = (int)rhs_cu-1;
FrameBlock slicedRHSBlk = fb.slice(rhs_lrl, rhs_lru, rhs_lcl, rhs_lcu, new FrameBlock());
int lblen = blenLeft;
ValueType[] schemaPartialLeft = UtilFunctions.nCopies(lhs_lcl, ValueType.STRING);
ValueType[] schemaRHS = Arrays.copyOfRange(fb.getSchema(), rhs_lcl, rhs_lcl-lhs_lcl+lhs_lcu+1);
ValueType[] schema = UtilFunctions.copyOf(schemaPartialLeft, schemaRHS);
ValueType[] schemaPartialRight = UtilFunctions.nCopies(lblen-schema.length, ValueType.STRING);
schema = UtilFunctions.copyOf(schema, schemaPartialRight);
FrameBlock resultBlock = new FrameBlock(schema);
int iRHSRows = (int)(leftRowIndex<=rlen/blenLeft?blenLeft:rlen-(rlen/blenLeft)*blenLeft);
resultBlock.ensureAllocatedColumns(iRHSRows);
resultBlock = resultBlock.leftIndexingOperations(slicedRHSBlk, lhs_lrl, lhs_lru, lhs_lcl, lhs_lcu, new FrameBlock());
outlist.add(new Pair<>((leftRowIndex-1)*blenLeft+1, resultBlock));
}
}
}