function batchToSpaceND_(x, blockShape, crops) {
    const $x = (0, _tensorUtilEnv.convertToTensor)(x, "x", "batchToSpaceND");
    const prod = blockShape.reduce((a, b)=>a * b);
    _util.assert($x.rank >= 1 + blockShape.length, ()=>`input rank is ${$x.rank} but should be > than blockShape.length ${blockShape.length}`);
    _util.assert(crops.length === blockShape.length, ()=>`crops.length is ${crops.length} but should be equal to blockShape.length  ${blockShape.length}`);
    _util.assert($x.shape[0] % prod === 0, ()=>`input tensor batch is ${$x.shape[0]} but is not divisible by the product of ` + `the elements of blockShape ${blockShape.join(" * ")} === ${prod}`);
    const inputs = {
        x: $x
    };
    const attrs = {
        blockShape,
        crops
    };
    return (0, _engine.ENGINE).runKernel((0, _kernelNames.BatchToSpaceND), inputs, attrs);
}
This operation reshapes the "batch" dimension 0 into `M + 1` dimensions of
shape `blockShape + [batch]`, interleaves these blocks back into the grid
defined by the spatial dimensions `[1, ..., M]`, to obtain a result with
the same rank as the input. The spatial dimensions of this intermediate
result are then optionally cropped according to `crops` to produce the
output. This is the reverse of `tf.spaceToBatchND`. See below for a precise
description.

```js
const x = tf.tensor4d([1, 2, 3, 4], [4, 1, 1, 1]);
const blockShape = [2, 2];
const crops = [[0, 0], [0, 0]];

x.batchToSpaceND(blockShape, crops).print();
```

@param x A `tf.Tensor`. N-D with `x.shape` = `[batch] + spatialShape +
remainingShape`, where spatialShape has `M` dimensions.
@param blockShape A 1-D array. Must have shape `[M]`, all values must
be >= 1.
@param crops A 2-D array.  Must have shape `[M, 2]`, all values must be >= 0.
`crops[i] = [cropStart, cropEnd]` specifies the amount to crop from input
dimension `i + 1`, which corresponds to spatial dimension `i`. It is required
that `cropStart[i] + cropEnd[i] <= blockShape[i] * inputShape[i + 1]`

This operation is equivalent to the following steps:

1. Reshape `x` to `reshaped` of shape: `[blockShape[0], ...,
blockShape[M-1], batch / prod(blockShape), x.shape[1], ...,
x.shape[N-1]]`

2. Permute dimensions of `reshaped` to produce `permuted` of shape `[batch /
prod(blockShape),x.shape[1], blockShape[0], ..., x.shape[M],
blockShape[M-1],x.shape[M+1], ..., x.shape[N-1]]`

3. Reshape `permuted` to produce `reshapedPermuted` of shape `[batch /
prod(blockShape),x.shape[1] * blockShape[0], ..., x.shape[M] *
blockShape[M-1],x.shape[M+1], ..., x.shape[N-1]]`

4. Crop the start and end of dimensions `[1, ..., M]` of `reshapedPermuted`
according to `crops` to produce the output of shape: `[batch /
prod(blockShape),x.shape[1] * blockShape[0] - crops[0,0] - crops[0,1],
..., x.shape[M] * blockShape[M-1] - crops[M-1,0] -
crops[M-1,1],x.shape[M+1], ..., x.shape[N-1]]`

@doc {heading: 'Tensors', subheading: 'Transformations'}