Extend a permutation of blocks to a permutation for the internal signals of all blocks.
    E.g., say we have two blocks of sizes ('block structure') ``(2, 3)``,
    then a block permutation that switches the blocks would be given by the image tuple ``(1,0)``.
    However, to get a permutation of all 2+3 = 5 channels that realizes that block permutation we would need
    ``(2, 3, 4, 0, 1)``

    :param block_permutation: permutation image tuple of block indices
    :type block_permutation: tuple
    :param block_structure: The block channel dimensions, block structure
    :type block_structure: tuple
    :return: A single permutation for all channels of all blocks.
    :rtype: tuple

def full_block_perm(block_permutation, block_structure):
    """
    Extend a permutation of blocks to a permutation for the internal signals of all blocks.
    E.g., say we have two blocks of sizes ('block structure') ``(2, 3)``,
    then a block permutation that switches the blocks would be given by the image tuple ``(1,0)``.
    However, to get a permutation of all 2+3 = 5 channels that realizes that block permutation we would need
    ``(2, 3, 4, 0, 1)``

    :param block_permutation: permutation image tuple of block indices
    :type block_permutation: tuple
    :param block_structure: The block channel dimensions, block structure
    :type block_structure: tuple
    :return: A single permutation for all channels of all blocks.
    :rtype: tuple
    """
    fblockp = []
    bp_inv = invert_permutation(block_permutation)
    for k, block_length in enumerate(block_structure):
        p_k = block_permutation[k]
        offset = sum([block_structure[bp_inv[j]] for j in range(p_k)])
        fblockp += range(offset, offset + block_length)

    assert sorted(fblockp) == list(range(sum(block_structure)))

    return tuple(fblockp)