# Path: recordlinkage/utils.py
# def listify(x, none_value=[]):
#     """Make a list of the argument if it is not a list."""
# 
#     if isinstance(x, list):
#         return x
#     elif isinstance(x, tuple):
#         return list(x)
#     elif x is None:
#         return none_value
#     else:
#         return [x]
# 
# Path: recordlinkage/index.py
# class Block(BaseIndexAlgorithm):
#     """Make candidate record pairs that agree on one or more variables.
# 
#     Returns all record pairs that agree on the given variable(s). This
#     method is known as *blocking*. Blocking is an effective way to make a
#     subset of the record space (A * B).
# 
#     Parameters
#     ----------
#     left_on : label, optional
#         A column name or a list of column names of dataframe A. These
#         columns are used to block on.
#     right_on : label, optional
#         A column name or a list of column names of dataframe B. These
#         columns are used to block on. If 'right_on' is None, the `left_on`
#         value is used. Default None.
#     **kwargs :
#         Additional keyword arguments to pass to
#         :class:`recordlinkage.base.BaseIndexAlgorithm`.
# 
#     Examples
#     --------
#     In the following example, the record pairs are made for two historical
#     datasets with census data. The datasets are named ``census_data_1980``
#     and ``census_data_1990``.
# 
#     >>> indexer = recordlinkage.BlockIndex(on='first_name')
#     >>> indexer.index(census_data_1980, census_data_1990)
# 
#     """
# 
#     def __init__(self, left_on=None, right_on=None, **kwargs):
#         on = kwargs.pop('on', None)
#         super(Block, self).__init__(**kwargs)
# 
#         # variables to block on
#         self.left_on = left_on
#         self.right_on = right_on
# 
#         if on is not None:
#             warnings.warn(
#                 "The argument 'on' is deprecated. Use 'left_on=...' and "
#                 "'right_on=None' to simulate the behaviour of 'on'.",
#                 DeprecationWarning,
#                 stacklevel=2)
#             self.left_on, self.right_on = on, on
# 
#     def __repr__(self):
# 
#         class_name = self.__class__.__name__
#         left_on, right_on = self._get_left_and_right_on()
# 
#         return "<{} left_on={!r}, right_on={!r}>".format(
#             class_name, left_on, right_on)
# 
#     def _get_left_and_right_on(self):
# 
#         if self.right_on is None:
#             return (self.left_on, self.left_on)
#         else:
#             return (self.left_on, self.right_on)
# 
#     def _link_index(self, df_a, df_b):
# 
#         left_on, right_on = self._get_left_and_right_on()
#         left_on = listify(left_on)
#         right_on = listify(right_on)
# 
#         blocking_keys = ["blocking_key_%d" % i for i, v in enumerate(left_on)]
# 
#         # make a dataset for the data on the left
#         # 1. make a dataframe
#         # 2. rename columns
#         # 3. add index col
#         # 4. drop na (last step to presever index)
#         data_left = pandas.DataFrame(df_a[left_on], copy=False)
#         data_left.columns = blocking_keys
#         data_left['index_x'] = numpy.arange(len(df_a))
#         data_left.dropna(axis=0, how='any', subset=blocking_keys, inplace=True)
# 
#         # make a dataset for the data on the right
#         data_right = pandas.DataFrame(df_b[right_on], copy=False)
#         data_right.columns = blocking_keys
#         data_right['index_y'] = numpy.arange(len(df_b))
#         data_right.dropna(
#             axis=0, how='any', subset=blocking_keys, inplace=True)
# 
#         # merge the dataframes
#         pairs_df = data_left.merge(data_right, how='inner', on=blocking_keys)
# 
#         return pandas.MultiIndex(
#             levels=[df_a.index.values, df_b.index.values],
#             codes=[pairs_df['index_x'].values, pairs_df['index_y'].values],
#             verify_integrity=False)


import numpy
import pandas
from recordlinkage.utils import listify
from recordlinkage.index import Block

        def split_to_match(a, to_match):
            ndx_bounds = numpy.r_[0, numpy.cumsum([len(x) for x in to_match])]
            assert len(a) == ndx_bounds[-1]
            return [
                a[start:stop]
                for start, stop in zip(ndx_bounds, ndx_bounds[1:])
            ]

        def deduped_blocks_and_indices(blocks, indices=None):
            if indices is None:
                indices = [numpy.arange(len(blocks))]
            deduped_blocks, index_tx = numpy.unique(
                blocks, axis=0, return_inverse=True)
            return deduped_blocks, [index_tx[raw_ndx] for raw_ndx in indices]

        def get_normalized_linkage_params():
            def default_on_possibilities():
                yield self.left_on
                yield self.right_on
                yield [
                    c for c in dfs[0].columns
                    if all(c in df.columns for df in dfs)
                ]

            default_on = next(
                iter(
                    filter(lambda x: x is not None,
                           default_on_possibilities())))
            key_columns = [

# Path: recordlinkage/utils.py
# def listify(x, none_value=[]):
#     """Make a list of the argument if it is not a list."""
# 
#     if isinstance(x, list):
#         return x
#     elif isinstance(x, tuple):
#         return list(x)
#     elif x is None:
#         return none_value
#     else:
#         return [x]
# 
# Path: recordlinkage/index.py
# class Block(BaseIndexAlgorithm):
#     """Make candidate record pairs that agree on one or more variables.
# 
#     Returns all record pairs that agree on the given variable(s). This
#     method is known as *blocking*. Blocking is an effective way to make a
#     subset of the record space (A * B).
# 
#     Parameters
#     ----------
#     left_on : label, optional
#         A column name or a list of column names of dataframe A. These
#         columns are used to block on.
#     right_on : label, optional
#         A column name or a list of column names of dataframe B. These
#         columns are used to block on. If 'right_on' is None, the `left_on`
#         value is used. Default None.
#     **kwargs :
#         Additional keyword arguments to pass to
#         :class:`recordlinkage.base.BaseIndexAlgorithm`.
# 
#     Examples
#     --------
#     In the following example, the record pairs are made for two historical
#     datasets with census data. The datasets are named ``census_data_1980``
#     and ``census_data_1990``.
# 
#     >>> indexer = recordlinkage.BlockIndex(on='first_name')
#     >>> indexer.index(census_data_1980, census_data_1990)
# 
#     """
# 
#     def __init__(self, left_on=None, right_on=None, **kwargs):
#         on = kwargs.pop('on', None)
#         super(Block, self).__init__(**kwargs)
# 
#         # variables to block on
#         self.left_on = left_on
#         self.right_on = right_on
# 
#         if on is not None:
#             warnings.warn(
#                 "The argument 'on' is deprecated. Use 'left_on=...' and "
#                 "'right_on=None' to simulate the behaviour of 'on'.",
#                 DeprecationWarning,
#                 stacklevel=2)
#             self.left_on, self.right_on = on, on
# 
#     def __repr__(self):
# 
#         class_name = self.__class__.__name__
#         left_on, right_on = self._get_left_and_right_on()
# 
#         return "<{} left_on={!r}, right_on={!r}>".format(
#             class_name, left_on, right_on)
# 
#     def _get_left_and_right_on(self):
# 
#         if self.right_on is None:
#             return (self.left_on, self.left_on)
#         else:
#             return (self.left_on, self.right_on)
# 
#     def _link_index(self, df_a, df_b):
# 
#         left_on, right_on = self._get_left_and_right_on()
#         left_on = listify(left_on)
#         right_on = listify(right_on)
# 
#         blocking_keys = ["blocking_key_%d" % i for i, v in enumerate(left_on)]
# 
#         # make a dataset for the data on the left
#         # 1. make a dataframe
#         # 2. rename columns
#         # 3. add index col
#         # 4. drop na (last step to presever index)
#         data_left = pandas.DataFrame(df_a[left_on], copy=False)
#         data_left.columns = blocking_keys
#         data_left['index_x'] = numpy.arange(len(df_a))
#         data_left.dropna(axis=0, how='any', subset=blocking_keys, inplace=True)
# 
#         # make a dataset for the data on the right
#         data_right = pandas.DataFrame(df_b[right_on], copy=False)
#         data_right.columns = blocking_keys
#         data_right['index_y'] = numpy.arange(len(df_b))
#         data_right.dropna(
#             axis=0, how='any', subset=blocking_keys, inplace=True)
# 
#         # merge the dataframes
#         pairs_df = data_left.merge(data_right, how='inner', on=blocking_keys)
# 
#         return pandas.MultiIndex(
#             levels=[df_a.index.values, df_b.index.values],
#             codes=[pairs_df['index_x'].values, pairs_df['index_y'].values],
#             verify_integrity=False)


# Path: recordlinkage/contrib/index/neighbourhoodblock/neighbourhoodblock.py
import numpy
import pandas
from recordlinkage.utils import listify
from recordlinkage.index import Block


        def split_to_match(a, to_match):
            ndx_bounds = numpy.r_[0, numpy.cumsum([len(x) for x in to_match])]
            assert len(a) == ndx_bounds[-1]
            return [
                a[start:stop]
                for start, stop in zip(ndx_bounds, ndx_bounds[1:])
            ]

        def deduped_blocks_and_indices(blocks, indices=None):
            if indices is None:
                indices = [numpy.arange(len(blocks))]
            deduped_blocks, index_tx = numpy.unique(
                blocks, axis=0, return_inverse=True)
            return deduped_blocks, [index_tx[raw_ndx] for raw_ndx in indices]

        def get_normalized_linkage_params():
            def default_on_possibilities():
                yield self.left_on
                yield self.right_on
                yield [
                    c for c in dfs[0].columns
                    if all(c in df.columns for df in dfs)
                ]

            default_on = next(
                iter(
                    filter(lambda x: x is not None,
                           default_on_possibilities())))
            key_columns = [

                listify(side_on or default_on)