def concatenate(
    inputs: Sequence[ndarray],
    axis: int | None = 0,
    out: ndarray | None = None,
    dtype: npt.DTypeLike | None = None,
    casting: CastingKind = "same_kind",
) -> ndarray:
    """

    concatenate((a1, a2, ...), axis=0, out=None, dtype=None,
    casting="same_kind")

    Join a sequence of arrays along an existing axis.

    Parameters
    ----------
    a1, a2, ... : Sequence[array_like]
        The arrays must have the same shape, except in the dimension
        corresponding to `axis` (the first, by default).
    axis : int, optional
        The axis along which the arrays will be joined.  If axis is None,
        arrays are flattened before use.  Default is 0.
    out : ndarray, optional
        If provided, the destination to place the result. The shape must be
        correct, matching that of what concatenate would have returned if no
        out argument were specified.
    dtype : str or data-type
        If provided, the destination array will have this dtype. Cannot be
        provided together with `out`.
    casting : ``{'no', 'equiv', 'safe', 'same_kind', 'unsafe'}``, optional
        Controls what kind of data casting may occur. Defaults to 'same_kind'.

    Returns
    -------
    res : ndarray
        The concatenated array.

    See Also
    --------
    numpy.concatenate

    Availability
    --------
    Multiple GPUs, Multiple CPUs
    """
    if dtype is not None and out is not None:
        raise TypeError(
            "concatenate() only takes `out` or `dtype` as an argument,"
            "but both were provided."
        )

    if casting not in casting_kinds:
        raise ValueError(
            "casting must be one of 'no', 'equiv', "
            "'safe', 'same_kind', or 'unsafe'"
        )

    # flatten arrays if axis == None and concatenate arrays on the first axis
    if axis is None:
        # Reshape arrays in the `array_list` to handle scalars
        reshaped = _atleast_nd(1, inputs)
        if not isinstance(reshaped, list):
            reshaped = [reshaped]
        inputs = list(inp.ravel() for inp in reshaped)
        axis = 0

    # Check to see if we can build a new tuple of cuPyNumeric arrays
    cupynumeric_inputs, common_info = check_shape_dtype_without_axis(
        inputs, concatenate.__name__, dtype, casting
    )
    check_shape_with_axis(cupynumeric_inputs, concatenate.__name__, axis)

    return _concatenate(
        cupynumeric_inputs,
        common_info,
        axis,
        out,
        dtype,
        casting,
    )


    concatenate((a1, a2, ...), axis=0, out=None, dtype=None,
    casting="same_kind")

    Join a sequence of arrays along an existing axis.

    Parameters
    ----------
    a1, a2, ... : Sequence[array_like]
        The arrays must have the same shape, except in the dimension
        corresponding to `axis` (the first, by default).
    axis : int, optional
        The axis along which the arrays will be joined.  If axis is None,
        arrays are flattened before use.  Default is 0.
    out : ndarray, optional
        If provided, the destination to place the result. The shape must be
        correct, matching that of what concatenate would have returned if no
        out argument were specified.
    dtype : str or data-type
        If provided, the destination array will have this dtype. Cannot be
        provided together with `out`.
    casting : ``{'no', 'equiv', 'safe', 'same_kind', 'unsafe'}``, optional
        Controls what kind of data casting may occur. Defaults to 'same_kind'.

    Returns
    -------
    res : ndarray
        The concatenated array.

    See Also
    --------
    numpy.concatenate

    Availability
    --------
    Multiple GPUs, Multiple CPUs
    
cupynumeric/_module/array_joining.py