content: /*
   * 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.arrow.vector.compare;
  
  import static org.apache.arrow.memory.util.LargeMemoryUtil.checkedCastToInt;
  
  import java.util.List;
  import java.util.function.BiFunction;
  import org.apache.arrow.memory.ArrowBuf;
  import org.apache.arrow.memory.util.ByteFunctionHelpers;
  import org.apache.arrow.util.Preconditions;
  import org.apache.arrow.vector.BaseFixedWidthVector;
  import org.apache.arrow.vector.BaseLargeVariableWidthVector;
  import org.apache.arrow.vector.BaseVariableWidthVector;
  import org.apache.arrow.vector.BaseVariableWidthViewVector;
  import org.apache.arrow.vector.BitVector;
  import org.apache.arrow.vector.ExtensionTypeVector;
  import org.apache.arrow.vector.FieldVector;
  import org.apache.arrow.vector.NullVector;
  import org.apache.arrow.vector.ValueVector;
  import org.apache.arrow.vector.complex.BaseLargeRepeatedValueViewVector;
  import org.apache.arrow.vector.complex.BaseRepeatedValueVector;
  import org.apache.arrow.vector.complex.BaseRepeatedValueViewVector;
  import org.apache.arrow.vector.complex.DenseUnionVector;
  import org.apache.arrow.vector.complex.FixedSizeListVector;
  import org.apache.arrow.vector.complex.LargeListVector;
  import org.apache.arrow.vector.complex.LargeListViewVector;
  import org.apache.arrow.vector.complex.ListVector;
  import org.apache.arrow.vector.complex.ListViewVector;
  import org.apache.arrow.vector.complex.NonNullableStructVector;
  import org.apache.arrow.vector.complex.RunEndEncodedVector;
  import org.apache.arrow.vector.complex.RunEndEncodedVector.RangeIterator;
  import org.apache.arrow.vector.complex.StructVector;
  import org.apache.arrow.vector.complex.UnionVector;
  
  /** Visitor to compare a range of values for vectors. */
  public class RangeEqualsVisitor implements VectorVisitor<Boolean, Range> {
  
    private ValueVector left;
    private ValueVector right;
  
    private BiFunction<ValueVector, ValueVector, Boolean> typeComparator;
    private boolean typeCompareResult;
  
    /** Default type comparator. */
    public static final BiFunction<ValueVector, ValueVector, Boolean> DEFAULT_TYPE_COMPARATOR =
        (v1, v2) -> new TypeEqualsVisitor(v2).equals(v1);
  
    /**
     * Constructs a new instance with default type comparator.
     *
     * @param left left vector
     * @param right right vector
     */
    public RangeEqualsVisitor(ValueVector left, ValueVector right) {
      this(left, right, DEFAULT_TYPE_COMPARATOR);
    }
  
    /**
     * Constructs a new instance.
     *
     * @param left left vector
     * @param right right vector
     * @param typeComparator type comparator to compare vector type.
     */
    public RangeEqualsVisitor(
        ValueVector left,
        ValueVector right,
        BiFunction<ValueVector, ValueVector, Boolean> typeComparator) {
      this.left = left;
      this.right = right;
      this.typeComparator = typeComparator;
  
      Preconditions.checkArgument(left != null, "left vector cannot be null");
      Preconditions.checkArgument(right != null, "right vector cannot be null");
  
      // type usually checks only once unless the left vector is changed.
      checkType();
    }
  
    private void checkType() {
      if (typeComparator == null || left == right) {
        typeCompareResult = true;
      } else {
        typeCompareResult = typeComparator.apply(left, right);
      }
    }
  
    /** Validate the passed left vector, if it is changed, reset and check type. */
    protected boolean validate(ValueVector left) {
      if (left != this.left) {
        this.left = left;
        checkType();
      }
      return typeCompareResult;
    }
  
    /** Check range equals. */
    public boolean rangeEquals(Range range) {
      if (!typeCompareResult) {
        return false;
      }
  
      Preconditions.checkArgument(
          range.getLeftStart() >= 0, "leftStart %s must be non negative.", range.getLeftStart());
      Preconditions.checkArgument(
          range.getRightStart() >= 0, "rightStart %s must be non negative.", range.getRightStart());
  
      Preconditions.checkArgument(
          range.getRightStart() + range.getLength() <= right.getValueCount(),
          "(rightStart + length) %s out of range[0, %s].",
          range.getRightStart() + range.getLength(),
          right.getValueCount());
      Preconditions.checkArgument(
          range.getLeftStart() + range.getLength() <= left.getValueCount(),
          "(leftStart + length) %s out of range[0, %s].",
          range.getLeftStart() + range.getLength(),
          left.getValueCount());
  
      return left.accept(this, range);
    }
  
    public ValueVector getLeft() {
      return left;
    }
  
    public ValueVector getRight() {
      return right;
    }
  
    @Override
    public Boolean visit(BaseFixedWidthVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareBaseFixedWidthVectors(range);
    }
  
    @Override
    public Boolean visit(BaseVariableWidthVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareBaseVariableWidthVectors(range);
    }
  
    @Override
    public Boolean visit(BaseLargeVariableWidthVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareBaseLargeVariableWidthVectors(range);
    }
  
    @Override
    public Boolean visit(BaseVariableWidthViewVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareBaseVariableWidthViewVectors(range);
    }
  
    @Override
    public Boolean visit(ListVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareListVectors(range);
    }
  
    @Override
    public Boolean visit(FixedSizeListVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareFixedSizeListVectors(range);
    }
  
    @Override
    public Boolean visit(LargeListVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareLargeListVectors(range);
    }
  
    @Override
    public Boolean visit(NonNullableStructVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareStructVectors(range);
    }
  
    @Override
    public Boolean visit(UnionVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareUnionVectors(range);
    }
  
    @Override
    public Boolean visit(DenseUnionVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareDenseUnionVectors(range);
    }
  
    @Override
    public Boolean visit(NullVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return true;
    }
  
    @Override
    public Boolean visit(RunEndEncodedVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareRunEndEncodedVectors(range);
    }
  
    @Override
    public Boolean visit(ExtensionTypeVector<?> left, Range range) {
      if (!(right instanceof ExtensionTypeVector<?>) || !validate(left)) {
        return false;
      }
      ValueVector rightUnderlying = ((ExtensionTypeVector<?>) right).getUnderlyingVector();
      TypeEqualsVisitor typeVisitor = new TypeEqualsVisitor(rightUnderlying);
      RangeEqualsVisitor underlyingVisitor =
          createInnerVisitor(
              left.getUnderlyingVector(), rightUnderlying, (l, r) -> typeVisitor.equals(l));
      return underlyingVisitor.rangeEquals(range);
    }
  
    @Override
    public Boolean visit(ListViewVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareListViewVectors(range);
    }
  
    @Override
    public Boolean visit(LargeListViewVector left, Range range) {
      if (!validate(left)) {
        return false;
      }
      return compareLargeListViewVectors(range);
    }
  
    protected boolean compareRunEndEncodedVectors(Range range) {
      RunEndEncodedVector leftVector = (RunEndEncodedVector) left;
      RunEndEncodedVector rightVector = (RunEndEncodedVector) right;
  
      final RunEndEncodedVector.RangeIterator leftIterator =
          new RunEndEncodedVector.RangeIterator(leftVector, range.getLeftStart(), range.getLength());
      final RunEndEncodedVector.RangeIterator rightIterator =
          new RunEndEncodedVector.RangeIterator(
              rightVector, range.getRightStart(), range.getLength());
  
      FieldVector leftValuesVector = leftVector.getValuesVector();
      FieldVector rightValuesVector = rightVector.getValuesVector();
  
      RangeEqualsVisitor innerVisitor = createInnerVisitor(leftValuesVector, rightValuesVector, null);
  
      while (nextRun(leftIterator, rightIterator)) {
        int leftPhysicalIndex = leftIterator.getRunIndex();
        int rightPhysicalIndex = rightIterator.getRunIndex();
  
        if (leftIterator.getRunLength() != rightIterator.getRunLength()
            || !leftValuesVector.accept(
                innerVisitor, new Range(leftPhysicalIndex, rightPhysicalIndex, 1))) {
          return false;
        }
      }
  
      return leftIterator.isEnd() && rightIterator.isEnd();
    }
  
    private static boolean nextRun(RangeIterator leftIterator, RangeIterator rightIterator) {
      boolean left = leftIterator.nextRun();
      boolean right = rightIterator.nextRun();
      return left && right;
    }
  
    protected RangeEqualsVisitor createInnerVisitor(
        ValueVector leftInner,
        ValueVector rightInner,
        BiFunction<ValueVector, ValueVector, Boolean> typeComparator) {
      return new RangeEqualsVisitor(leftInner, rightInner, typeComparator);
    }
  
    protected boolean compareUnionVectors(Range range) {
      UnionVector leftVector = (UnionVector) left;
      UnionVector rightVector = (UnionVector) right;
  
      Range subRange = new Range(0, 0, 1);
      for (int i = 0; i < range.getLength(); i++) {
        subRange.setLeftStart(range.getLeftStart() + i).setRightStart(range.getRightStart() + i);
        ValueVector leftSubVector = leftVector.getVector(range.getLeftStart() + i);
        ValueVector rightSubVector = rightVector.getVector(range.getRightStart() + i);
  
        if (leftSubVector == null || rightSubVector == null) {
          if (leftSubVector == rightSubVector) {
            continue;
          } else {
            return false;
          }
        }
        TypeEqualsVisitor typeVisitor = new TypeEqualsVisitor(rightSubVector);
        RangeEqualsVisitor visitor =
            createInnerVisitor(
                leftSubVector, rightSubVector, (left, right) -> typeVisitor.equals(left));
        if (!visitor.rangeEquals(subRange)) {
          return false;
        }
      }
      return true;
    }
  
    protected boolean compareDenseUnionVectors(Range range) {
      DenseUnionVector leftVector = (DenseUnionVector) left;
      DenseUnionVector rightVector = (DenseUnionVector) right;
  
      Range subRange = new Range(0, 0, 1);
      for (int i = 0; i < range.getLength(); i++) {
        boolean isLeftNull = leftVector.isNull(range.getLeftStart() + i);
        boolean isRightNull = rightVector.isNull(range.getRightStart() + i);
  
        // compare nullabilities
        if (isLeftNull || isRightNull) {
          if (isLeftNull != isRightNull) {
            // exactly one slot is null, unequal
            return false;
          } else {
            // both slots are null, pass this iteration
            continue;
          }
        }
  
        // compare type ids
        byte leftTypeId = leftVector.getTypeId(range.getLeftStart() + i);
        byte rightTypeId = rightVector.getTypeId(range.getRightStart() + i);
  
        if (leftTypeId != rightTypeId) {
          return false;
        }
  
        ValueVector leftSubVector = leftVector.getVectorByType(leftTypeId);
        ValueVector rightSubVector = rightVector.getVectorByType(rightTypeId);
  
        if (leftSubVector == null || rightSubVector == null) {
          if (leftSubVector != rightSubVector) {
            // exactly one of the sub-vectors is null, unequal
            return false;
          } else {
            // both sub-vectors are null, pass this iteration
            continue;
          }
        }
  
        // compare values
        int leftOffset = leftVector.getOffset(range.getLeftStart() + i);
        int rightOffset = rightVector.getOffset(range.getRightStart() + i);
        subRange.setLeftStart(leftOffset).setRightStart(rightOffset);
        TypeEqualsVisitor typeVisitor = new TypeEqualsVisitor(rightSubVector);
        RangeEqualsVisitor visitor =
            createInnerVisitor(
                leftSubVector, rightSubVector, (left, right) -> typeVisitor.equals(left));
        if (!visitor.rangeEquals(subRange)) {
          return false;
        }
      }
      return true;
    }
  
    private boolean compareStructVectorsInternal(
        NonNullableStructVector leftVector, NonNullableStructVector rightVector, Range range) {
      List<String> leftChildNames = leftVector.getChildFieldNames();
      for (String name : leftChildNames) {
        RangeEqualsVisitor visitor =
            createInnerVisitor(
                leftVector.getChild(name), rightVector.getChild(name), /*type comparator*/ null);
        if (!visitor.rangeEquals(range)) {
          return false;
        }
      }
      return true;
    }
  
    protected boolean compareStructVectors(Range range) {
      NonNullableStructVector leftVector = (NonNullableStructVector) left;
      NonNullableStructVector rightVector = (NonNullableStructVector) right;
  
      List<String> leftChildNames = leftVector.getChildFieldNames();
      if (!leftChildNames.equals(rightVector.getChildFieldNames())) {
        return false;
      }
  
      if (!(leftVector instanceof StructVector || rightVector instanceof StructVector)) {
        // neither struct vector is nullable
        return compareStructVectorsInternal(leftVector, rightVector, range);
      }
  
      Range subRange = new Range(0, 0, 0);
      boolean lastIsNull = true;
      int lastNullIndex = -1;
      for (int i = 0; i < range.getLength(); i++) {
        int leftIndex = range.getLeftStart() + i;
        int rightIndex = range.getRightStart() + i;
        boolean isLeftNull = leftVector.isNull(leftIndex);
        boolean isRightNull = rightVector.isNull(rightIndex);
  
        if (isLeftNull != isRightNull) {
          // exactly one slot is null, unequal
          return false;
        }
        if (isLeftNull) {
          // slots are null
          if (!lastIsNull) {
            subRange
                .setLeftStart(range.getLeftStart() + lastNullIndex + 1)
                .setRightStart(range.getRightStart() + lastNullIndex + 1)
                .setLength(i - (lastNullIndex + 1));
            if (!compareStructVectorsInternal(leftVector, rightVector, subRange)) {
              return false;
            }
          }
          lastIsNull = true;
          lastNullIndex = i;
        } else {
          // slots are not null
          lastIsNull = false;
        }
      }
      if (!lastIsNull) {
        subRange
            .setLeftStart(range.getLeftStart() + lastNullIndex + 1)
            .setRightStart(range.getRightStart() + lastNullIndex + 1)
            .setLength(range.getLength() - (lastNullIndex + 1));
        return compareStructVectorsInternal(leftVector, rightVector, subRange);
      }
      return true;
    }
  
    protected boolean compareBaseFixedWidthVectors(Range range) {
      BaseFixedWidthVector leftVector = (BaseFixedWidthVector) left;
      BaseFixedWidthVector rightVector = (BaseFixedWidthVector) right;
  
      for (int i = 0; i < range.getLength(); i++) {
        int leftIndex = range.getLeftStart() + i;
        int rightIndex = range.getRightStart() + i;
  
        boolean isNull = leftVector.isNull(leftIndex);
  
        if (isNull != rightVector.isNull(rightIndex)) {
          return false;
        }
  
        int typeWidth = leftVector.getTypeWidth();
        if (!isNull) {
          if (!(leftVector instanceof BitVector)) {
            int startIndexLeft = typeWidth * leftIndex;
            int endIndexLeft = typeWidth * (leftIndex + 1);
  
            int startIndexRight = typeWidth * rightIndex;
            int endIndexRight = typeWidth * (rightIndex + 1);
  
            int ret =
                ByteFunctionHelpers.equal(
                    leftVector.getDataBuffer(),
                    startIndexLeft,
                    endIndexLeft,
                    rightVector.getDataBuffer(),
                    startIndexRight,
                    endIndexRight);
  
            if (ret == 0) {
              return false;
            }
          } else {
            boolean ret =
                ((BitVector) leftVector).get(leftIndex) == ((BitVector) rightVector).get(rightIndex);
            if (!ret) {
              return false;
            }
          }
        }
      }
      return true;
    }
  
    protected boolean compareBaseVariableWidthVectors(Range range) {
      BaseVariableWidthVector leftVector = (BaseVariableWidthVector) left;
      BaseVariableWidthVector rightVector = (BaseVariableWidthVector) right;
  
      for (int i = 0; i < range.getLength(); i++) {
        int leftIndex = range.getLeftStart() + i;
        int rightIndex = range.getRightStart() + i;
  
        boolean isNull = leftVector.isNull(leftIndex);
        if (isNull != rightVector.isNull(rightIndex)) {
          return false;
        }
  
        int offsetWidth = BaseVariableWidthVector.OFFSET_WIDTH;
  
        if (!isNull) {
          final int startIndexLeft = leftVector.getOffsetBuffer().getInt(leftIndex * offsetWidth);
          final int endIndexLeft = leftVector.getOffsetBuffer().getInt((leftIndex + 1) * offsetWidth);
  
          final int startIndexRight = rightVector.getOffsetBuffer().getInt(rightIndex * offsetWidth);
          final int endIndexRight =
              rightVector.getOffsetBuffer().getInt((rightIndex + 1) * offsetWidth);
  
          int ret =
              ByteFunctionHelpers.equal(
                  leftVector.getDataBuffer(),
                  startIndexLeft,
                  endIndexLeft,
                  rightVector.getDataBuffer(),
                  startIndexRight,
                  endIndexRight);
  
          if (ret == 0) {
            return false;
          }
        }
      }
      return true;
    }
  
    protected boolean compareBaseLargeVariableWidthVectors(Range range) {
      BaseLargeVariableWidthVector leftVector = (BaseLargeVariableWidthVector) left;
      BaseLargeVariableWidthVector rightVector = (BaseLargeVariableWidthVector) right;
  
      for (int i = 0; i < range.getLength(); i++) {
        int leftIndex = range.getLeftStart() + i;
        int rightIndex = range.getRightStart() + i;
  
        boolean isNull = leftVector.isNull(leftIndex);
        if (isNull != rightVector.isNull(rightIndex)) {
          return false;
        }
  
        int offsetWidth = BaseLargeVariableWidthVector.OFFSET_WIDTH;
  
        if (!isNull) {
          final long startIndexLeft =
              leftVector.getOffsetBuffer().getLong((long) leftIndex * offsetWidth);
          final long endIndexLeft =
              leftVector.getOffsetBuffer().getLong((long) (leftIndex + 1) * offsetWidth);
  
          final long startIndexRight =
              rightVector.getOffsetBuffer().getLong((long) rightIndex * offsetWidth);
          final long endIndexRight =
              rightVector.getOffsetBuffer().getLong((long) (rightIndex + 1) * offsetWidth);
  
          int ret =
              ByteFunctionHelpers.equal(
                  leftVector.getDataBuffer(),
                  startIndexLeft,
                  endIndexLeft,
                  rightVector.getDataBuffer(),
                  startIndexRight,
                  endIndexRight);
  
          if (ret == 0) {
            return false;
          }
        }
      }
      return true;
    }
  
    protected boolean compareBaseVariableWidthViewVectors(Range range) {
      BaseVariableWidthViewVector leftVector = (BaseVariableWidthViewVector) left;
      BaseVariableWidthViewVector rightVector = (BaseVariableWidthViewVector) right;
  
      final ArrowBuf leftViewBuffer = leftVector.getDataBuffer();
      final ArrowBuf rightViewBuffer = rightVector.getDataBuffer();
  
      final int elementSize = BaseVariableWidthViewVector.ELEMENT_SIZE;
      final int lengthWidth = BaseVariableWidthViewVector.LENGTH_WIDTH;
      final int prefixWidth = BaseVariableWidthViewVector.PREFIX_WIDTH;
      final int bufIndexWidth = BaseVariableWidthViewVector.BUF_INDEX_WIDTH;
  
      List<ArrowBuf> leftDataBuffers = leftVector.getDataBuffers();
      List<ArrowBuf> rightDataBuffers = rightVector.getDataBuffers();
  
      for (int i = 0; i < range.getLength(); i++) {
        int leftIndex = range.getLeftStart() + i;
        int rightIndex = range.getRightStart() + i;
  
        boolean isNull = leftVector.isNull(leftIndex);
        if (isNull != rightVector.isNull(rightIndex)) {
          return false;
        }
  
        if (isNull) {
          continue;
        }
  
        int startLeftByteOffset = leftIndex * elementSize;
  
        int startRightByteOffset = rightIndex * elementSize;
  
        int leftDataBufferValueLength = leftVector.getValueLength(leftIndex);
        int rightDataBufferValueLength = rightVector.getValueLength(rightIndex);
  
        if (leftDataBufferValueLength != rightDataBufferValueLength) {
          return false;
        }
  
        if (leftDataBufferValueLength > BaseVariableWidthViewVector.INLINE_SIZE) {
          // if the value is stored in the dataBuffers
          int leftDataBufferIndex =
              leftViewBuffer.getInt(startLeftByteOffset + lengthWidth + prefixWidth);
          int rightDataBufferIndex =
              rightViewBuffer.getInt(startRightByteOffset + lengthWidth + prefixWidth);
  
          final int leftDataOffset =
              leftViewBuffer.getInt(startLeftByteOffset + lengthWidth + prefixWidth + bufIndexWidth);
          final int rightDataOffset =
              rightViewBuffer.getInt(
                  startRightByteOffset + lengthWidth + prefixWidth + bufIndexWidth);
  
          ArrowBuf leftDataBuffer = leftDataBuffers.get(leftDataBufferIndex);
          ArrowBuf rightDataBuffer = rightDataBuffers.get(rightDataBufferIndex);
  
          // check equality in the considered string stored in the dataBuffers
          int retDataBuf =
              ByteFunctionHelpers.equal(
                  leftDataBuffer,
                  leftDataOffset,
                  leftDataOffset + leftDataBufferValueLength,
                  rightDataBuffer,
                  rightDataOffset,
                  rightDataOffset + rightDataBufferValueLength);
  
          if (retDataBuf == 0) {
            return false;
          }
        } else {
          // if the value is stored in the view
          final int leftDataOffset = startLeftByteOffset + lengthWidth;
          final int rightDataOffset = startRightByteOffset + lengthWidth;
  
          // check equality in the considered string stored in the view
          int retDataBuf =
              ByteFunctionHelpers.equal(
                  leftViewBuffer,
                  leftDataOffset,
                  leftDataOffset + leftDataBufferValueLength,
                  rightViewBuffer,
                  rightDataOffset,
                  rightDataOffset + rightDataBufferValueLength);
  
          if (retDataBuf == 0) {
            return false;
          }
        }
      }
      return true;
    }
  
    protected boolean compareListVectors(Range range) {
      ListVector leftVector = (ListVector) left;
      ListVector rightVector = (ListVector) right;
  
      RangeEqualsVisitor innerVisitor =
          createInnerVisitor(
              leftVector.getDataVector(), rightVector.getDataVector(), /*type comparator*/ null);
      Range innerRange = new Range();
  
      for (int i = 0; i < range.getLength(); i++) {
        int leftIndex = range.getLeftStart() + i;
        int rightIndex = range.getRightStart() + i;
  
        boolean isNull = leftVector.isNull(leftIndex);
        if (isNull != rightVector.isNull(rightIndex)) {
          return false;
        }
  
        int offsetWidth = BaseRepeatedValueVector.OFFSET_WIDTH;
  
        if (!isNull) {
          final int startIndexLeft = leftVector.getOffsetBuffer().getInt(leftIndex * offsetWidth);
          final int endIndexLeft = leftVector.getOffsetBuffer().getInt((leftIndex + 1) * offsetWidth);
  
          final int startIndexRight = rightVector.getOffsetBuffer().getInt(rightIndex * offsetWidth);
          final int endIndexRight =
              rightVector.getOffsetBuffer().getInt((rightIndex + 1) * offsetWidth);
  
          if ((endIndexLeft - startIndexLeft) != (endIndexRight - startIndexRight)) {
            return false;
          }
  
          innerRange =
              innerRange
                  .setRightStart(startIndexRight)
                  .setLeftStart(startIndexLeft)
                  .setLength(endIndexLeft - startIndexLeft);
          if (!innerVisitor.rangeEquals(innerRange)) {
            return false;
          }
        }
      }
      return true;
    }
  
    protected boolean compareFixedSizeListVectors(Range range) {
      FixedSizeListVector leftVector = (FixedSizeListVector) left;
      FixedSizeListVector rightVector = (FixedSizeListVector) right;
  
      if (leftVector.getListSize() != rightVector.getListSize()) {
        return false;
      }
  
      int listSize = leftVector.getListSize();
      RangeEqualsVisitor innerVisitor =
          createInnerVisitor(
              leftVector.getDataVector(), rightVector.getDataVector(), /*type comparator*/ null);
      Range innerRange = new Range(0, 0, listSize);
  
      for (int i = 0; i < range.getLength(); i++) {
        int leftIndex = range.getLeftStart() + i;
        int rightIndex = range.getRightStart() + i;
  
        boolean isNull = leftVector.isNull(leftIndex);
        if (isNull != rightVector.isNull(rightIndex)) {
          return false;
        }
  
        if (!isNull) {
          final int startIndexLeft = leftIndex * listSize;
          final int endIndexLeft = (leftIndex + 1) * listSize;
  
          final int startIndexRight = rightIndex * listSize;
          final int endIndexRight = (rightIndex + 1) * listSize;
  
          if ((endIndexLeft - startIndexLeft) != (endIndexRight - startIndexRight)) {
            return false;
          }
  
          innerRange = innerRange.setLeftStart(startIndexLeft).setRightStart(startIndexRight);
          if (!innerVisitor.rangeEquals(innerRange)) {
            return false;
          }
        }
      }
      return true;
    }
  
    protected boolean compareLargeListVectors(Range range) {
      LargeListVector leftVector = (LargeListVector) left;
      LargeListVector rightVector = (LargeListVector) right;
  
      RangeEqualsVisitor innerVisitor =
          createInnerVisitor(
              leftVector.getDataVector(), rightVector.getDataVector(), /*type comparator*/ null);
      Range innerRange = new Range();
  
      for (int i = 0; i < range.getLength(); i++) {
        int leftIndex = range.getLeftStart() + i;
        int rightIndex = range.getRightStart() + i;
  
        boolean isNull = leftVector.isNull(leftIndex);
        if (isNull != rightVector.isNull(rightIndex)) {
          return false;
        }
  
        long offsetWidth = LargeListVector.OFFSET_WIDTH;
  
        if (!isNull) {
          final long startIndexLeft =
              leftVector.getOffsetBuffer().getLong((long) leftIndex * offsetWidth);
          final long endIndexLeft =
              leftVector.getOffsetBuffer().getLong((long) (leftIndex + 1) * offsetWidth);
  
          final long startIndexRight =
              rightVector.getOffsetBuffer().getLong((long) rightIndex * offsetWidth);
          final long endIndexRight =
              rightVector.getOffsetBuffer().getLong((long) (rightIndex + 1) * offsetWidth);
  
          if ((endIndexLeft - startIndexLeft) != (endIndexRight - startIndexRight)) {
            return false;
          }
  
          innerRange =
              innerRange // TODO revisit these casts when long indexing is finished
                  .setRightStart(checkedCastToInt(startIndexRight))
                  .setLeftStart(checkedCastToInt(startIndexLeft))
                  .setLength(checkedCastToInt(endIndexLeft - startIndexLeft));
          if (!innerVisitor.rangeEquals(innerRange)) {
            return false;
          }
        }
      }
      return true;
    }
  
    protected boolean compareListViewVectors(Range range) {
      ListViewVector leftVector = (ListViewVector) left;
      ListViewVector rightVector = (ListViewVector) right;
  
      RangeEqualsVisitor innerVisitor =
          createInnerVisitor(
              leftVector.getDataVector(), rightVector.getDataVector(), /*type comparator*/ null);
      Range innerRange = new Range();
  
      for (int i = 0; i < range.getLength(); i++) {
        int leftIndex = range.getLeftStart() + i;
        int rightIndex = range.getRightStart() + i;
  
        boolean isNull = leftVector.isNull(leftIndex);
        if (isNull != rightVector.isNull(rightIndex)) {
          return false;
        }
  
        int offsetWidth = BaseRepeatedValueViewVector.OFFSET_WIDTH;
        int sizeWidth = BaseRepeatedValueViewVector.SIZE_WIDTH;
  
        if (!isNull) {
          final int startIndexLeft =
              leftVector.getOffsetBuffer().getInt((long) leftIndex * offsetWidth);
          final int leftSize = leftVector.getSizeBuffer().getInt((long) leftIndex * sizeWidth);
  
          final int startIndexRight =
              rightVector.getOffsetBuffer().getInt((long) rightIndex * offsetWidth);
          final int rightSize = rightVector.getSizeBuffer().getInt((long) rightIndex * sizeWidth);
  
          if (leftSize != rightSize) {
            return false;
          }
  
          innerRange =
              innerRange
                  .setRightStart(startIndexRight)
                  .setLeftStart(startIndexLeft)
                  .setLength(leftSize);
          if (!innerVisitor.rangeEquals(innerRange)) {
            return false;
          }
        }
      }
      return true;
    }
  
    protected boolean compareLargeListViewVectors(Range range) {
      LargeListViewVector leftVector = (LargeListViewVector) left;
      LargeListViewVector rightVector = (LargeListViewVector) right;
  
      RangeEqualsVisitor innerVisitor =
          createInnerVisitor(
              leftVector.getDataVector(), rightVector.getDataVector(), /*type comparator*/ null);
      Range innerRange = new Range();
  
      for (int i = 0; i < range.getLength(); i++) {
        int leftIndex = range.getLeftStart() + i;
        int rightIndex = range.getRightStart() + i;
  
        boolean isNull = leftVector.isNull(leftIndex);
        if (isNull != rightVector.isNull(rightIndex)) {
          return false;
        }
  
        int offsetWidth = BaseLargeRepeatedValueViewVector.OFFSET_WIDTH;
        int sizeWidth = BaseLargeRepeatedValueViewVector.SIZE_WIDTH;
  
        if (!isNull) {
          final int startIndexLeft =
              leftVector.getOffsetBuffer().getInt((long) leftIndex * offsetWidth);
          final int leftSize = leftVector.getSizeBuffer().getInt((long) leftIndex * sizeWidth);
  
          final int startIndexRight =
              rightVector.getOffsetBuffer().getInt((long) rightIndex * offsetWidth);
          final int rightSize = rightVector.getSizeBuffer().getInt((long) rightIndex * sizeWidth);
  
          if (leftSize != rightSize) {
            return false;
          }
  
          innerRange =
              innerRange
                  .setRightStart(startIndexRight)
                  .setLeftStart(startIndexLeft)
                  .setLength(leftSize);
          if (!innerVisitor.rangeEquals(innerRange)) {
            return false;
          }
        }
      }
      return true;
    }
  }
  