# Path: src/qinfer/utils.py
# def join_struct_arrays(arrays):
#     """
#     Takes a list of possibly structured arrays, concatenates their
#     dtypes, and returns one big array with that dtype. Does the
#     inverse of ``separate_struct_array``.
# 
#     :param list arrays: List of ``np.ndarray``s
#     """
#     # taken from http://stackoverflow.com/questions/5355744/numpy-joining-structured-arrays
#     sizes = np.array([a.itemsize for a in arrays])
#     offsets = np.r_[0, sizes.cumsum()]
#     shape = arrays[0].shape
#     joint = np.empty(shape + (offsets[-1],), dtype=np.uint8)
#     for a, size, offset in zip(arrays, sizes, offsets):
#         joint[...,offset:offset+size] = np.atleast_1d(a).view(np.uint8).reshape(shape + (size,))
#     dtype = sum((a.dtype.descr for a in arrays), [])
#     return joint.ravel().view(dtype)
# 
# def separate_struct_array(array, dtypes):
#     """
#     Takes an array with a structured dtype, and separates it out into
#     a list of arrays with dtypes coming from the input ``dtypes``.
#     Does the inverse of ``join_struct_arrays``.
# 
#     :param np.ndarray array: Structured array.
#     :param dtypes: List of ``np.dtype``, or just a ``np.dtype`` and the number of
#         them is figured out automatically by counting bytes.
#     """
#     try:
#         offsets = np.cumsum([np.dtype(dtype).itemsize for dtype in dtypes])
#     except TypeError:
#         dtype_size = np.dtype(dtypes).itemsize
#         num_fields = int(array.nbytes / (array.size * dtype_size))
#         offsets = np.cumsum([dtype_size] * num_fields)
#         dtypes = [dtypes] * num_fields
#     offsets = np.concatenate([[0], offsets]).astype(int)
#     uint_array = array.view(np.uint8).reshape(array.shape + (-1,))
#     return [
#         uint_array[..., offsets[idx]:offsets[idx+1]].flatten().view(dtype)
#         for idx, dtype in enumerate(dtypes)
#     ]


from builtins import range
from future.utils import with_metaclass
from functools import reduce
from operator import mul
from scipy.special import binom
from math import factorial
from itertools import combinations_with_replacement, product
from .utils import join_struct_arrays, separate_struct_array
import numpy as np
import abc
import warnings
        some values from the domain.
        For domains where `is_finite` is ``True``, all elements
        of the domain will be yielded exactly once.

        :rtype: `np.ndarray`
        """
        separate_values = [domain.values for domain in self._domains]
        return np.concatenate([
            join_struct_arrays(list(map(np.array, value))) 
            for value in product(*separate_values)
        ])

    ## METHODS ##
    
    def _mytype(self, array):
        # astype does weird stuff with struct names, and possibly 
        # depends on numpy version; hopefully 
        # the following is a bit more predictable since it passes through 
        # uint8
        return separate_struct_array(array, self.dtype)[0]
    
    def to_regular_arrays(self, array):
        """
        Expands from an array of type `self.dtype` into a list of
        arrays with dtypes corresponding to the factor domains.

        :param np.ndarray array: An `np.array` of type `self.dtype`.

        :rtype: ``list``
        """

# Path: src/qinfer/utils.py
# def join_struct_arrays(arrays):
#     """
#     Takes a list of possibly structured arrays, concatenates their
#     dtypes, and returns one big array with that dtype. Does the
#     inverse of ``separate_struct_array``.
# 
#     :param list arrays: List of ``np.ndarray``s
#     """
#     # taken from http://stackoverflow.com/questions/5355744/numpy-joining-structured-arrays
#     sizes = np.array([a.itemsize for a in arrays])
#     offsets = np.r_[0, sizes.cumsum()]
#     shape = arrays[0].shape
#     joint = np.empty(shape + (offsets[-1],), dtype=np.uint8)
#     for a, size, offset in zip(arrays, sizes, offsets):
#         joint[...,offset:offset+size] = np.atleast_1d(a).view(np.uint8).reshape(shape + (size,))
#     dtype = sum((a.dtype.descr for a in arrays), [])
#     return joint.ravel().view(dtype)
# 
# def separate_struct_array(array, dtypes):
#     """
#     Takes an array with a structured dtype, and separates it out into
#     a list of arrays with dtypes coming from the input ``dtypes``.
#     Does the inverse of ``join_struct_arrays``.
# 
#     :param np.ndarray array: Structured array.
#     :param dtypes: List of ``np.dtype``, or just a ``np.dtype`` and the number of
#         them is figured out automatically by counting bytes.
#     """
#     try:
#         offsets = np.cumsum([np.dtype(dtype).itemsize for dtype in dtypes])
#     except TypeError:
#         dtype_size = np.dtype(dtypes).itemsize
#         num_fields = int(array.nbytes / (array.size * dtype_size))
#         offsets = np.cumsum([dtype_size] * num_fields)
#         dtypes = [dtypes] * num_fields
#     offsets = np.concatenate([[0], offsets]).astype(int)
#     uint_array = array.view(np.uint8).reshape(array.shape + (-1,))
#     return [
#         uint_array[..., offsets[idx]:offsets[idx+1]].flatten().view(dtype)
#         for idx, dtype in enumerate(dtypes)
#     ]


# Path: src/qinfer/domains.py
from builtins import range
from future.utils import with_metaclass
from functools import reduce
from operator import mul
from scipy.special import binom
from math import factorial
from itertools import combinations_with_replacement, product
from .utils import join_struct_arrays, separate_struct_array
import numpy as np
import abc
import warnings

        some values from the domain.
        For domains where `is_finite` is ``True``, all elements
        of the domain will be yielded exactly once.

        :rtype: `np.ndarray`
        """
        separate_values = [domain.values for domain in self._domains]
        return np.concatenate([
            join_struct_arrays(list(map(np.array, value))) 
            for value in product(*separate_values)
        ])

    ## METHODS ##
    
    def _mytype(self, array):
        # astype does weird stuff with struct names, and possibly 
        # depends on numpy version; hopefully 
        # the following is a bit more predictable since it passes through 
        # uint8
        return separate_struct_array(array, self.dtype)[0]
    
    def to_regular_arrays(self, array):
        """
        Expands from an array of type `self.dtype` into a list of
        arrays with dtypes corresponding to the factor domains.

        :param np.ndarray array: An `np.array` of type `self.dtype`.

        :rtype: ``list``
        """

        return separate_struct_array(self._mytype(array), self._dtypes)