import torch


def permute(x, in_shape='BCD', out_shape='BCD', **kw):
    """ Permute the dimensions of a tensor.\n
    -  `x: Tensor`; The nd-tensor to be permuted.
    -  `in_shape: str`; The dimension shape of `x`. Can only have characters `'B'` or `'C'` or `'D'`,
        which stand for Batch, Channel, or extra Dimensions. The default value `'BCD'` means
        the input tensor `x` should be at lest 2-d with shape `(Batch, Channel, Dim0, Dim1, Dim2, ...)`,
        where `Dim0, Dim1, Dim2 ...` stand for any number of extra dimensions.
    -  `out_shape: str or tuple or None`; The dimension shape of returned tensor.  Default: `'BCD'`.
        If a `str`, it is restricted to the same three characters `'B'`, `'C'` or `'D'` as the `in_shape`.
        If a `tuple`, `in_shape` is ignored, and simply `x.permute(out_shape)` is returned.
        If `None`, no permution will be performed.
    -  `return: Tensor`; Permuted nd-tensor. """
    if (in_shape == out_shape) or (out_shape is None):
        return x
    if isinstance(out_shape, (list, tuple, torch.Size)):
        return x.permute(*out_shape)
    if isinstance(in_shape, str) and isinstance(out_shape, str) :
        assert set(in_shape) == set(out_shape) <= {'B', 'C', 'D'}, 'In and out shapes must have save set of chars among B, C, and D.'
        in_shape = in_shape.lower().replace('d', '...')
        out_shape = out_shape.lower().replace('d', '...')
        return torch.einsum(f'{in_shape}->{out_shape}', x)
    return x