# Path: core/kover/utils.py
# def _minimum_uint_size(max_value):
#     """
#     Find the minimum size unsigned integer type that can store values of at most max_value
#     """
#     if max_value <= np.iinfo(np.uint8).max:
#         return np.uint8
#     elif max_value <= np.iinfo(np.uint16).max:
#         return np.uint16
#     elif max_value <= np.iinfo(np.uint32).max:
#         return np.uint32
#     elif max_value <= np.iinfo(np.uint64).max:
#         return np.uint64
#     else:
#         return np.uint128
# 
# def _unpack_binary_bytes_from_ints(a):
#     """
#     Unpacks binary values stored in bytes into ints
#     """
#     type = a.dtype
# 
#     if type == np.uint32:
#         pack_size = 32
#     elif type == np.uint64:
#         pack_size = 64
#     else:
#         raise ValueError("Supported data types are 32-bit and 64-bit integers.")
# 
#     unpacked_n_rows = a.shape[0] * pack_size
#     unpacked_n_columns = a.shape[1] if len(a.shape) > 1 else 1
#     b = np.zeros((unpacked_n_rows, a.shape[1]) if len(a.shape) > 1 else unpacked_n_rows, dtype=np.uint8)
# 
#     packed_rows = 0
#     packing_row = 0
#     for i in xrange(b.shape[0]):
#         if packed_rows == pack_size:
#             packed_rows = 0
#             packing_row += 1
#         tmp = np.left_shift(np.ones(unpacked_n_columns, dtype=type), pack_size - (i - pack_size * packing_row)-1)
#         np.bitwise_and(a[packing_row], tmp, tmp)
#         b[i] = tmp > 0
#         packed_rows += 1
# 
#     return b


import numpy as np
from math import ceil
from .popcount import inplace_popcount_32, inplace_popcount_64
from ...utils import _minimum_uint_size, _unpack_binary_bytes_from_ints

    def get_columns(self, columns):
        """
        Columns can be an integer (or any object that implements __index__) or a sorted list/ndarray.
        """
        #TODO: Support slicing, make this more efficient than getting the columns individually.
        columns_is_int = False
        if hasattr(columns, "__index__"):  # All int types implement the __index__ method (PEP 357)
            columns = [columns.__index__()]
            columns_is_int = True
        elif isinstance(columns, np.ndarray):
            columns = columns.tolist()
        elif isinstance(columns, list):
            pass
        else:
            columns = list(columns)

        # Detect where an inversion is needed (columns corresponding to absence rules)
        columns, invert_result = zip(* (((column if column < self.dataset.shape[1] else column % self.dataset.shape[1]),
                                         (True if column > self.dataset.shape[1] else False)) for column in columns))
        columns = list(columns)
        invert_result = np.array(invert_result)

        # Don't return rows that have been deleted
        row_mask = np.ones(self.dataset.shape[0] * self.dataset_pack_size, dtype=np.bool)
        row_mask[self.dataset_initial_n_rows:] = False
        row_mask[self.dataset_removed_rows] = False

        # h5py requires that the column indices are sorted
        unique, inverse = np.unique(columns, return_inverse=True)

# Path: core/kover/utils.py
# def _minimum_uint_size(max_value):
#     """
#     Find the minimum size unsigned integer type that can store values of at most max_value
#     """
#     if max_value <= np.iinfo(np.uint8).max:
#         return np.uint8
#     elif max_value <= np.iinfo(np.uint16).max:
#         return np.uint16
#     elif max_value <= np.iinfo(np.uint32).max:
#         return np.uint32
#     elif max_value <= np.iinfo(np.uint64).max:
#         return np.uint64
#     else:
#         return np.uint128
# 
# def _unpack_binary_bytes_from_ints(a):
#     """
#     Unpacks binary values stored in bytes into ints
#     """
#     type = a.dtype
# 
#     if type == np.uint32:
#         pack_size = 32
#     elif type == np.uint64:
#         pack_size = 64
#     else:
#         raise ValueError("Supported data types are 32-bit and 64-bit integers.")
# 
#     unpacked_n_rows = a.shape[0] * pack_size
#     unpacked_n_columns = a.shape[1] if len(a.shape) > 1 else 1
#     b = np.zeros((unpacked_n_rows, a.shape[1]) if len(a.shape) > 1 else unpacked_n_rows, dtype=np.uint8)
# 
#     packed_rows = 0
#     packing_row = 0
#     for i in xrange(b.shape[0]):
#         if packed_rows == pack_size:
#             packed_rows = 0
#             packing_row += 1
#         tmp = np.left_shift(np.ones(unpacked_n_columns, dtype=type), pack_size - (i - pack_size * packing_row)-1)
#         np.bitwise_and(a[packing_row], tmp, tmp)
#         b[i] = tmp > 0
#         packed_rows += 1
# 
#     return b


# Path: core/kover/learning/common/rules.py
import numpy as np
from math import ceil
from .popcount import inplace_popcount_32, inplace_popcount_64
from ...utils import _minimum_uint_size, _unpack_binary_bytes_from_ints


    def get_columns(self, columns):
        """
        Columns can be an integer (or any object that implements __index__) or a sorted list/ndarray.
        """
        #TODO: Support slicing, make this more efficient than getting the columns individually.
        columns_is_int = False
        if hasattr(columns, "__index__"):  # All int types implement the __index__ method (PEP 357)
            columns = [columns.__index__()]
            columns_is_int = True
        elif isinstance(columns, np.ndarray):
            columns = columns.tolist()
        elif isinstance(columns, list):
            pass
        else:
            columns = list(columns)

        # Detect where an inversion is needed (columns corresponding to absence rules)
        columns, invert_result = zip(* (((column if column < self.dataset.shape[1] else column % self.dataset.shape[1]),
                                         (True if column > self.dataset.shape[1] else False)) for column in columns))
        columns = list(columns)
        invert_result = np.array(invert_result)

        # Don't return rows that have been deleted
        row_mask = np.ones(self.dataset.shape[0] * self.dataset_pack_size, dtype=np.bool)
        row_mask[self.dataset_initial_n_rows:] = False
        row_mask[self.dataset_removed_rows] = False

        # h5py requires that the column indices are sorted
        unique, inverse = np.unique(columns, return_inverse=True)

        result = _unpack_binary_bytes_from_ints(self.dataset[:, unique.tolist()])[row_mask]