<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def unflatten(ary, shapes, reshape=scalar_reshape):
 r""" Inverse opertation of flatten. Given a flat (1d) array, and a list of shapes (represented as tuples), return a list of ndarrays with the specified shapes. Parameters ary : a 1d array A flat (1d) array. shapes : list of tuples A list of ndarray shapes (tuple of array dimensions) Returns ------- list of ndarrays A list of ndarrays with the specified shapes. See Also -------- revrand.utils.flatten : its inverse Notes ----- Equivalent to:: lambda ary, shapes, order='C': \ map(partial(custom_reshape, order=order), np.hsplit(ary, np.cumsum(map(partial(np.prod, dtype=int), shapes))), shapes) Examples -------- [array([7]), array([4]), array([5, 8, 9, 1]), array([[4, 2, 5], [3, 4, 3]])] [7, array([4]), array([5, 8, 9, 1]), array([[4, 2, 5], [3, 4, 3]])] [7, array([4]), array([5, 8, 9]), array([[1, 4, 2], [5, 3, 4]])] Traceback (most recent call last):
 ValueError: total size of new array must be unchanged (array([7, 4, 5, 8, 9, 1, 4, 2, 5, 3, 4, 3]), [(), (1,), (4,), (2, 3)]) [[array([7]), array([4])], array([5, 8, 9, 1]), array([[4, 2, 5], [3, 4, 3]])] (array([7, 4, 5, 8, 9, 1, 4, 2, 5, 3, 4, 3]), [(), (1,), [(4,), (2, 3)]]) """

    if isinstance(shapes, list):
        sizes = list(map(sumprod, shapes))
        ends = np.cumsum(sizes)
        begs = np.concatenate(([0], ends[:-1]))
        struct_arys = [unflatten(ary[b:e], s, reshape=reshape)
                       for b, e, s in zip(begs, ends, shapes)]
        return struct_arys
    else:
        struct_ary = reshape(ary, shapes)
        return struct_ary