def autocrop_array_shapes(input_shapes, cropping):
    """Computes the shapes of the given arrays after auto-cropping is applied.

    For more information on cropping, see the :func:`autocrop` function
    documentation.

    # Arguments
        input_shapes: the shapes of input arrays prior to cropping in
            the form of a list of tuples
        cropping: a list of cropping modes, one for each axis. If length of
            `cropping` is less than the number of axes in the inputs, it is
            padded with `None`. If `cropping` is None, `input_shapes` is returned
            as is. For more information on their values and operation, see the
            :func:`autocrop` documentation.

    # Returns
         shapes of the given arrays after auto-cropping is applied.
    """
    if cropping is None:
        return input_shapes
    else:
        # Check for consistent number of dimensions
        ndim = len(input_shapes[0])
        if not all(len(sh) == ndim for sh in input_shapes):
            raise ValueError("Not all inputs are of the same "
                             "dimensionality. Got {0} inputs of "
                             "dimensionalities {1}.".format(len(input_shapes), [len(sh) for sh in input_shapes]))

        result = []

        # If there are more dimensions than cropping entries, pad
        # the cropping
        cropping = list(cropping)
        if ndim > len(cropping):
            cropping = list(cropping) + [None] * (ndim - len(cropping))

        for sh, cr in zip(zip(*input_shapes), cropping):
            if cr is None:
                result.append(sh)
            elif cr in {'lower', 'center', 'upper'}:
                min_sh = None if any(inputs is None for inputs in sh) else min(sh)
                result.append([min_sh] * len(sh))
            else:
                raise ValueError('Unknown crop mode \'{0}\''.format(cr))
        return [tuple(sh) for sh in zip(*result)]