class Arrays2D {

  private static final Arrays2D INSTANCE = new Arrays2D();

  /**
   * Returns the singleton instance of this class based on {@link StandardComparisonStrategy}.
   *
   * @return the singleton instance of this class based on {@link StandardComparisonStrategy}.
   */
  public static Arrays2D instance() {
    return INSTANCE;
  }

  @VisibleForTesting
  public void assertNullOrEmpty(AssertionInfo info, Failures failures, Object array) {
    if (array == null) return;
    if (countArrayElements(array) > 0) throw failures.failure(info, shouldBeNullOrEmpty(array));
  }

  @VisibleForTesting
  public void assertEmpty(AssertionInfo info, Failures failures, Object array) {
    assertNotNull(info, array);
    // need to check that all rows are empty
    int numberOfRows = sizeOf(array);
    for (int i = 0; i < numberOfRows; i++) {
      Object actualArrayRow = Array.get(array, i);
      if (sizeOf(actualArrayRow) > 0) throw failures.failure(info, shouldBeEmpty(array));
    }
  }

  @VisibleForTesting
  public void assertHasDimensions(AssertionInfo info, Failures failures, Object array2d, int expectedNumberOfRows,
                                  int expectedRowSize) {
    assertNumberOfRows(info, failures, array2d, expectedNumberOfRows);
    for (int i = 0; i < expectedNumberOfRows; i++) {
      Object actualRow = Array.get(array2d, i);
      assertSecondDimension(info, failures, actualRow, expectedRowSize, i);
    }
  }

  @VisibleForTesting
  public void assertNumberOfRows(AssertionInfo info, Failures failures, Object array, int expectedSize) {<FILL_FUNCTION_BODY>}

  private void assertSecondDimension(AssertionInfo info, Failures failures, Object actual, int expectedSize, int rowIndex) {
    assertNotNull(info, actual);
    checkArraySizes(actual, failures, sizeOf(actual), expectedSize, info, rowIndex);
  }

  private static void checkArraySizes(Object actual, Failures failures, int sizeOfActual, int sizeOfOther, AssertionInfo info,
                                      int rowIndex) {
    if (sizeOfActual != sizeOfOther) {
      throw failures.failure(info, shouldHaveSize(actual, sizeOfActual, sizeOfOther, rowIndex));
    }
  }

  @VisibleForTesting
  public void assertHasSameDimensionsAs(AssertionInfo info, Object actual, Object other) {
    assertNotNull(info, actual);
    assertIsArray(info, actual);
    assertIsArray(info, other);
    // check first dimension
    int actualFirstDimension = sizeOf(actual);
    int otherFirstDimension = sizeOf(other);
    if (actualFirstDimension != otherFirstDimension) {
      throw Failures.instance().failure(info,
                                        shouldHaveSameDimensionsAs(actual, other, actualFirstDimension, otherFirstDimension));
    }
    // check second dimensions
    for (int i = 0; i < actualFirstDimension; i++) {
      Object actualRow = Array.get(actual, i);
      assertIsArray(info, actualRow);
      Object otherRow = Array.get(other, i);
      assertIsArray(info, otherRow);
      hasSameRowSizeAsCheck(info, i, actual, other, actualRow, otherRow, sizeOf(actualRow));
    }
  }

  static void hasSameRowSizeAsCheck(AssertionInfo info, int rowIndex, Object actual, Object other, Object actualRow,
                                    Object otherRow, int actualRowSize) {
    requireNonNull(other, format("The array to compare %s size with should not be null", actual));
    int expectedRowSize = Array.getLength(otherRow);
    if (actualRowSize != expectedRowSize)
      throw Failures.instance().failure(info, shouldHaveSameDimensionsAs(rowIndex, actualRowSize, expectedRowSize, actualRow,
                                                                         otherRow, actual, other));
  }

  @VisibleForTesting
  public void assertContains(AssertionInfo info, Failures failures, Object array, Object value, Index index) {
    assertNotNull(info, array);
    assertNotEmpty(info, failures, array);
    checkIndexValueIsValid(index, sizeOf(array) - 1);
    Object actualElement = Array.get(array, index.value);
    if (!deepEquals(actualElement, value)) {
      throw failures.failure(info, shouldContainAtIndex(array, value, index, Array.get(array, index.value)));
    }
  }

  @VisibleForTesting
  public void assertNotEmpty(AssertionInfo info, Failures failures, Object array) {
    assertNotNull(info, array);
    if (countArrayElements(array) == 0) throw failures.failure(info, shouldNotBeEmpty());
  }

  private static int countArrayElements(Object array) {
    // even if array has many rows, they could all be empty
    int numberOfRows = sizeOf(array);
    // if any rows is not empty, the assertion succeeds.
    int allRowsElementsCount = 0;
    for (int i = 0; i < numberOfRows; i++) {
      Object actualRow = Array.get(array, i);
      allRowsElementsCount += sizeOf(actualRow);
    }
    return allRowsElementsCount;
  }

  @VisibleForTesting
  public void assertDoesNotContain(AssertionInfo info, Failures failures, Object array, Object value, Index index) {
    assertNotNull(info, array);
    checkIndexValueIsValid(index, Integer.MAX_VALUE);
    if (index.value >= sizeOf(array)) return;
    if (deepEquals(Array.get(array, index.value), value)) {
      throw failures.failure(info, shouldNotContainAtIndex(array, value, index));
    }
  }

}

    assertNotNull(info, array);
    int sizeOfActual = sizeOf(array);
    if (sizeOfActual != expectedSize)
      throw failures.failure(info, ShouldHaveDimensions.shouldHaveFirstDimension(array, sizeOfActual, expectedSize));
  
<no_super_class>