# Buggy code
def _tile_both_dataframes(cls, op):
    # if both of the inputs are DataFrames, axis is just ignored
    left, right = op.inputs
    df = op.outputs[0]
    nsplits = [[], []]
    splits = _MinMaxSplitInfo()

    # first, we decide the chunk size on each axis
    # we perform the same logic for both index and columns
    for axis, index_type in enumerate(["index_value", "columns"]):
        # if both of the indexes are monotonic increasing or decreasing
        left_chunk_index_min_max = cls._get_chunk_index_min_max(left, index_type, axis)
        right_chunk_index_min_max = cls._get_chunk_index_min_max(
            right, index_type, axis
        )
        if (
            left_chunk_index_min_max is not None
            and right_chunk_index_min_max is not None
        ):
            # no need to do shuffle on this axis
            if (
                len(left_chunk_index_min_max[0]) == 1
                and len(right_chunk_index_min_max[0]) == 1
            ):
                # both left and right has only 1 chunk
                left_splits, right_splits = (
                    [left_chunk_index_min_max[0]],
                    [right_chunk_index_min_max[0]],
                )
            else:
                left_splits, right_splits = split_monotonic_index_min_max(
                    *(left_chunk_index_min_max + right_chunk_index_min_max)
                )
            left_increase = left_chunk_index_min_max[1]
            right_increase = right_chunk_index_min_max[1]
            splits[axis] = _AxisMinMaxSplitInfo(
                left_splits, left_increase, right_splits, right_increase
            )
            nsplits[axis].extend(np.nan for _ in itertools.chain(*left_splits))
        else:
            # do shuffle
            left_chunk_size = left.chunk_shape[axis]
            right_chunk_size = right.chunk_shape[axis]
            out_chunk_size = max(left_chunk_size, right_chunk_size)
            nsplits[axis].extend(np.nan for _ in range(out_chunk_size))

    out_shape = tuple(len(ns) for ns in nsplits)
    if splits.all_axes_can_split():
        # no shuffle for all axes
        out_chunks = cls._gen_out_chunks_without_shuffle(
            op, splits, out_shape, left, right
        )
    elif splits.one_axis_can_split():
        # one axis needs shuffle
        out_chunks = cls._gen_out_chunks_with_one_shuffle(
            op, splits, out_shape, left, right
        )
    else:
        # all axes need shuffle
        assert splits.no_axis_can_split()
        out_chunks = cls._gen_out_chunks_with_all_shuffle(op, out_shape, left, right)

    new_op = op.copy()
    return new_op.new_dataframes(
        op.inputs,
        df.shape,
        nsplits=tuple(tuple(ns) for ns in nsplits),
        chunks=out_chunks,
        dtypes=df.dtypes,
        index_value=df.index_value,
        columns_value=df.columns,
    )


# Traceback (KeyError)
Traceback (most recent call last):
File "/Users/hetao/mars/mars/tiles.py", line 111, in _dispatch
handler = self._handlers[op_cls]
KeyError: <class 'mars.dataframe.expressions.arithmetic.add.DataFrameAdd'>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/hetao/mars/mars/core.py", line 554, in execute
return session.run(self, **kw)
File "/Users/hetao/mars/mars/session.py", line 115, in run
result = self._sess.run(*tileables, **kw)
File "/Users/hetao/mars/mars/session.py", line 56, in run
res = self._executor.execute_tileables(tileables, **kw)
File "/Users/hetao/mars/mars/utils.py", line 364, in _wrapped
return func(*args, **kwargs)
File "/Users/hetao/mars/mars/executor.py", line 499, in execute_tileables
tileable.tiles()
File "/Users/hetao/mars/mars/core.py", line 474, in tiles
return handler.tiles(self)
File "/Users/hetao/mars/mars/tiles.py", line 174, in tiles
tiled = self._dispatch(node.op)
File "/Users/hetao/mars/mars/utils.py", line 364, in _wrapped
return func(*args, **kwargs)
File "/Users/hetao/mars/mars/tiles.py", line 116, in _dispatch
return op_cls.tile(op)
File "/Users/hetao/mars/mars/dataframe/expressions/arithmetic/core.py", line 527, in tile
return cls._tile_both_dataframes(op)
File "/Users/hetao/mars/mars/dataframe/expressions/arithmetic/core.py", line 516, in _tile_both_dataframes
out_chunks = cls._gen_out_chunks_with_all_shuffle(op, out_shape, left, right)
File "/Users/hetao/mars/mars/dataframe/expressions/arithmetic/core.py", line 461, in _gen_out_chunks_with_all_shuffle
reduce_op.new_chunk([proxy_chunk], shape=(np.nan, np.nan), index=out_idx))
File "/Users/hetao/mars/mars/core.py", line 650, in new_chunk
return self.new_chunks(inputs, kws=kws, **kw)[0]
File "/Users/hetao/mars/mars/core.py", line 644, in new_chunks
return self._new_chunks(inputs, kws=kws, **kwargs)
File "/Users/hetao/mars/mars/core.py", line 618, in _new_chunks
chunk = self._create_chunk(j, index, **create_chunk_kw)
File "/Users/hetao/mars/mars/dataframe/expressions/arithmetic/core.py", line 188, in _create_chunk
all_dtypes = [c.op.column_shuffle_segments[index[1]] for c in index_align_map_chunks
File "/Users/hetao/mars/mars/dataframe/expressions/arithmetic/core.py", line 189, in <listcomp>
if c.index[0] == index_align_map_chunks[0].index[0]]
IndexError: list index out of range


# Fixed code
def _tile_both_dataframes(cls, op):
    # if both of the inputs are DataFrames, axis is just ignored
    left, right = op.inputs
    df = op.outputs[0]
    nsplits = [[], []]
    splits = _MinMaxSplitInfo()

    # first, we decide the chunk size on each axis
    # we perform the same logic for both index and columns
    for axis, index_type in enumerate(["index_value", "columns"]):
        if not cls._need_shuffle_on_axis(left, right, index_type, axis):
            left_chunk_index_min_max = cls._get_chunk_index_min_max(
                left, index_type, axis
            )
            right_chunk_index_min_max = cls._get_chunk_index_min_max(
                right, index_type, axis
            )
            # no need to do shuffle on this axis
            if (
                len(left_chunk_index_min_max[0]) == 1
                and len(right_chunk_index_min_max[0]) == 1
            ):
                # both left and right has only 1 chunk
                left_splits, right_splits = (
                    [left_chunk_index_min_max[0]],
                    [right_chunk_index_min_max[0]],
                )
            else:
                left_splits, right_splits = split_monotonic_index_min_max(
                    *(left_chunk_index_min_max + right_chunk_index_min_max)
                )
            left_increase = left_chunk_index_min_max[1]
            right_increase = right_chunk_index_min_max[1]
            splits[axis] = _AxisMinMaxSplitInfo(
                left_splits, left_increase, right_splits, right_increase
            )
            nsplits[axis].extend(np.nan for _ in itertools.chain(*left_splits))
        else:
            # do shuffle
            left_chunk_size = left.chunk_shape[axis]
            right_chunk_size = right.chunk_shape[axis]
            out_chunk_size = max(left_chunk_size, right_chunk_size)
            nsplits[axis].extend(np.nan for _ in range(out_chunk_size))

    out_shape = tuple(len(ns) for ns in nsplits)
    if splits.all_axes_can_split():
        # no shuffle for all axes
        out_chunks = cls._gen_out_chunks_without_shuffle(
            op, splits, out_shape, left, right
        )
    elif splits.one_axis_can_split():
        # one axis needs shuffle
        out_chunks = cls._gen_out_chunks_with_one_shuffle(
            op, splits, out_shape, left, right
        )
    else:
        # all axes need shuffle
        assert splits.no_axis_can_split()
        out_chunks = cls._gen_out_chunks_with_all_shuffle(op, out_shape, left, right)

    new_op = op.copy()
    return new_op.new_dataframes(
        op.inputs,
        df.shape,
        nsplits=tuple(tuple(ns) for ns in nsplits),
        chunks=out_chunks,
        dtypes=df.dtypes,
        index_value=df.index_value,
        columns_value=df.columns,
    )