def build_contig_regions(self, num_regions, cardinality, w,
                                maxiter, compact, max_swaps):
        if compact:
            grow_region = self.grow_compact
        else:
            grow_region = self.grow_free
        iter = 0
        while iter < maxiter:

            # regionalization setup
            regions = []
            size_pre = 0
            counter = -1
            area2region = {}
            self.feasible = False
            swap_count = 0
            cards = copy.copy(cardinality)
            cards.sort()  # try to build largest regions first (pop from end of list)
            candidates = copy.copy(self.ids)  # these are already shuffled

            # begin building regions
            while candidates and swap_count < max_swaps:
                # setup test to determine if swapping is needed
                if size_pre == len(regions):
                    counter += 1
                else:
                    counter = 0
                    size_pre = len(regions)
                # test if swapping is needed
                if counter == len(candidates):

                    # start swapping
                    # swapping simply changes the candidate list
                    swap_in = None   # area to become new candidate
                    while swap_in is None:  # PEP8 E711
                        swap_count += 1
                        swap_out = candidates.pop(0)  # area to remove from candidates
                        swap_neighs = copy.copy(w.neighbors[swap_out])
                        swap_neighs = list(np.random.permutation(swap_neighs))
                        # select area to add to candidates (i.e. remove from an existing region)
                        for i in swap_neighs:
                            if i not in candidates:
                                join = i  # area linking swap_in to swap_out
                                swap_index = area2region[join]
                                swap_region = regions[swap_index]
                                swap_region = list(np.random.permutation(swap_region))
                                for j in swap_region:
                                    # test to ensure region connectivity after removing area
                                    swap_region_test = swap_region[:] + [swap_out]
                                    if check_contiguity(w, swap_region_test, j):
                                        swap_in = j
                                        break
                            if swap_in is not None:  # PEP8 E711
                                break
                        else:
                            candidates.append(swap_out)
                    # swapping cleanup
                    regions[swap_index].remove(swap_in)
                    regions[swap_index].append(swap_out)
                    area2region.pop(swap_in)
                    area2region[swap_out] = swap_index
                    candidates.append(swap_in)
                    counter = 0

                # setup to build a single region
                building = True
                seed = candidates.pop(0)
                region = [seed]
                potential = [i for i in w.neighbors[seed] if i in candidates]
                test_card = cards.pop()

                # begin building single region
                while building and len(region) < test_card:
                    if potential:
                        region, candidates, potential = grow_region(
                            w, test_card,
                                        region, candidates, potential)
                    else:
                        # not enough potential neighbors to reach test_card size
                        building = False
                        cards.append(test_card)
                        if len(region) in cards:
                            # constructed region matches another candidate region size
                            cards.remove(len(region))
                        else:
                            # constructed region doesn't match a candidate region size
                            candidates.extend(region)
                            region = []

                # cleanup when successful region built
                if region:
                    regions.append(region)
                    region_index = len(regions) - 1
                    for i in region:
                        area2region[i] = region_index   # area2region needed for swapping
            # handling of regionalization result
            if len(regions) < num_regions:
                # regionalization failed
                self.ids = list(np.random.permutation(self.ids))
                regions = []
                iter += 1
            else:
                # regionalization successful
                self.feasible = True
                iter = maxiter
        self.regions = regions
    def build_contig_regions(self, num_regions, cardinality, w, maxiter,
                             compact, max_swaps):
        if compact:
            grow_region = self.grow_compact
        else:
            grow_region = self.grow_free
        sizecheck = self.region_size_check
        iter = 0
        while iter < maxiter:

            # regionalization setup
            regions = []
            size_pre = 0
            counter = -1
            area2region = {}
            self.feasible = False
            swap_count = 0
            cards = copy.copy(cardinality)
            cards.sort(
            )  # try to build largest regions first (pop from end of list)
            candidates = copy.copy(self.ids)  # these are already shuffled

            # begin building regions
            while candidates and swap_count < max_swaps:
                # setup test to determine if swapping is needed
                #                print(counter, size_pre)
                if size_pre == len(regions):
                    counter += 1
                else:
                    counter = 0
                    size_pre = len(regions)
                # test if swapping is needed
#                if counter == len(candidates):
#
#                    # start swapping
#                    # swapping simply changes the candidate list
#                    swap_in = None   # area to become new candidate
#                    while swap_in is None:  # PEP8 E711
#                        swap_count += 1
#                        swap_out = candidates.pop(0)  # area to remove from candidates
#                        swap_neighs = copy.copy(w.neighbors[swap_out])
#                        swap_neighs = list(np.random.permutation(swap_neighs))
#                        # select area to add to candidates (i.e. remove from an existing region)
#                        for i in swap_neighs:
#                            if i not in candidates:
#                                join = i  # area linking swap_in to swap_out
#                                swap_index = area2region[join]
#                                swap_region = regions[swap_index]
#                                print("before swap: ")
#                                print([self.cd_dev[j][0]-sum([self.wtdict[i] for i in swap_region]) for j in range(len(regions))])
#                                swap_region = list(np.random.permutation(swap_region))
#                                swap_dev = self.cd_dev[swap_index]
#                                for j in swap_region:
#                                    # test to ensure region connectivity after removing area
#                                    # j is an element in the region possibly to remove
#                                    swap_region_test = swap_region[:] + [swap_out]
#                                    delta = self.wtdict[swap_out]-self.wtdict[j]
#                                    if (check_contiguity(w, swap_region_test, j)
#                                    and sizecheck(swap_dev[0] + delta, swap_dev[0]-swap_dev[1]) == 0):
#                                        swap_in = j
#                                        break
#                            if swap_in is not None:  # PEP8 E711
#                                break
#                            else:
#                                candidates.append(swap_out)
#                    # swapping cleanup
#                    regions[swap_index].remove(swap_in)
#                    regions[swap_index].append(swap_out)
#                    area2region.pop(swap_in)
#                    area2region[swap_out] = swap_index
#                    self.cd_dev[swap_index] = tuple(i + delta for i in self.cd_dev[swap_index] )
#                    candidates.append(swap_in)
#                    counter = 0
#
# setup to build a single region
                building = True
                seed = candidates.pop(0)
                region = [seed]
                regpop = self.wtdict[seed]
                if not cards:
                    print(len(regions))
                    print(sum(list(map(len, regions))))
                    print("Ran out of cards")
                    break
                test_card = cards.pop()
                #test_card is the population target;
                potential = [
                    i for i in w.neighbors[seed]
                    if (i in candidates
                        and sizecheck(regpop + self.wtdict[i], test_card) != 1)
                ]

                # begin building single region
                while building and sizecheck(regpop, test_card) == -1:
                    if potential:
                        region, candidates, potential, regpop = grow_region(
                            w, test_card, region, candidates, potential,
                            regpop)
                    else:
                        # not enough potential neighbors to reach test_card size
                        building = False
                        cards.append(test_card)
                        if regpop in cards:
                            # constructed region matches another candidate region size
                            cards.remove(regpop)
                        else:
                            # constructed region doesn't match a candidate region size
                            candidates.extend(region)
                            region = []

                # cleanup when successful region built
                if region:
                    region_index = len(regions)
                    for i in region:
                        area2region[
                            i] = region_index  # area2region needed for swapping

                    regions.append(
                        region
                    )  #append the successful region to the region list
                    self.cd_dev[region_index] = np.array(
                        [regpop, regpop - test_card])
                    #print(self.cd_dev)
                    #                    print(building)
                    print(regpop)


#                    print(#[round(self.wtdict[i]/776646,2) for i in region],
#                           round(sum([self.wtdict[i] for i in region])/self.n*7,2))
#                    print(regpop/self.n*7)
#                    print(sum([self.wtdict[i] for i in candidates]))
#                    print(sum([len(i) for i in regions]))
#                    print(sum([sum(self.wtdict[j] for j in i) for i in regions]))
#                    print([self.cd_dev[j][0] for j in range(len(regions))])
#                    print([sum([self.wtdict[i] for i in regions[j]]) for j in range(len(regions))])

# handling of regionalization result
            if len(regions) < num_regions:
                # regionalization failed
                print("fail")
                self.ids = list(np.random.permutation(self.ids))
                regions = []
                iter += 1
            else:
                # regionalization successful
                self.feasible = True
                iter = maxiter
        self.regions = regions
    def __init__(
        self, area_ids, num_regions=None, cardinality=None, contiguity=None,
                    maxiter=1000, compact=False, max_swaps=1000000):

        self.n = len(area_ids)
        ids = copy.copy(area_ids)
        self.ids = list(np.random.permutation(ids))
        self.area_ids = area_ids
        self.regions = []
        self.feasible = True

        # tests for input argument consistency
        if cardinality:
            if self.n != sum(cardinality):
                self.feasible = False
                raise Exception('number of areas does not match cardinality')
        if contiguity:
            if area_ids != contiguity.id_order:
                self.feasible = False
                raise Exception('order of area_ids must match order in contiguity')
        if num_regions and cardinality:
            if num_regions != len(cardinality):
                self.feasible = False
                raise Exception('number of regions does not match cardinality')

        # dispatches the appropriate algorithm
        if num_regions and cardinality and contiguity:
            # conditioning on cardinality and contiguity (number of regions implied)
            self.build_contig_regions(num_regions, cardinality, contiguity,
                                      maxiter, compact, max_swaps)
        elif num_regions and cardinality:
            # conditioning on cardinality (number of regions implied)
            region_breaks = self.cards2breaks(cardinality)
            self.build_noncontig_regions(num_regions, region_breaks)
        elif num_regions and contiguity:
            # conditioning on number of regions and contiguity
            cards = self.get_cards(num_regions)
            self.build_contig_regions(num_regions, cards, contiguity,
                                      maxiter, compact, max_swaps)
        elif cardinality and contiguity:
            # conditioning on cardinality and contiguity
            num_regions = len(cardinality)
            self.build_contig_regions(num_regions, cardinality, contiguity,
                                      maxiter, compact, max_swaps)
        elif num_regions:
            # conditioning on number of regions only
            region_breaks = self.get_region_breaks(num_regions)
            self.build_noncontig_regions(num_regions, region_breaks)
        elif cardinality:
            # conditioning on number of cardinality only
            num_regions = len(cardinality)
            region_breaks = self.cards2breaks(cardinality)
            self.build_noncontig_regions(num_regions, region_breaks)
        elif contiguity:
            # conditioning on number of contiguity only
            num_regions = self.get_num_regions()
            cards = self.get_cards(num_regions)
            self.build_contig_regions(num_regions, cards, contiguity,
                                      maxiter, compact, max_swaps)
        else:
            # unconditioned
            num_regions = self.get_num_regions()
            region_breaks = self.get_region_breaks(num_regions)
            self.build_noncontig_regions(num_regions, region_breaks)
    def __init__(self,
                 area_ids,
                 num_regions=None,
                 cardinality=None,
                 contiguity=None,
                 maxiter=1000,
                 compact=False,
                 max_swaps=1000000,
                 area_wts=None,
                 tol=0):

        if area_wts is None:
            area_wts = np.ones(len(area_ids))
        self.n = sum(area_wts)
        ids = copy.copy(area_ids)
        self.wtdict = dict(zip(ids, area_wts))
        self.ids = list(np.random.permutation(ids))
        self.area_ids = area_ids
        self.regions = []
        self.feasible = True
        self.tol = tol
        self.cd_dev = {}  #dictionary of deviations from intended cardinality

        # tests for input argument consistency
        if cardinality is not None:
            #            if self.n != sum(cardinality):
            if np.greater_equal(abs(self.n - sum(cardinality)),
                                .001 * abs(sum(cardinality))):
                self.feasible = False
                raise Exception(
                    'Sum of cardinalities does not equal total weight')
        if contiguity is not None:
            if area_ids != contiguity.id_order:
                self.feasible = False
                raise Exception(
                    'order of area_ids must match order in contiguity')
        if num_regions and cardinality is not None:
            if num_regions != len(cardinality):
                self.feasible = False
                raise Exception('number of regions does not match cardinality')

        # dispatches the appropriate algorithm
        if num_regions and cardinality is not None and contiguity is not None:
            # conditioning on cardinality and contiguity (number of regions implied)
            self.build_contig_regions(num_regions, cardinality, contiguity,
                                      maxiter, compact, max_swaps)
        elif num_regions and cardinality is not None:
            # conditioning on cardinality (number of regions implied)
            region_breaks = self.cards2breaks(cardinality)
            self.build_noncontig_regions(num_regions, region_breaks)
        elif num_regions and contiguity is not None:
            # conditioning on number of regions and contiguity
            cards = self.get_cards(num_regions)
            self.build_contig_regions(num_regions, cards, contiguity, maxiter,
                                      compact, max_swaps)
        elif cardinality is not None and contiguity is not None:
            # conditioning on cardinality and contiguity
            num_regions = len(cardinality)
            self.build_contig_regions(num_regions, cardinality, contiguity,
                                      maxiter, compact, max_swaps)
        elif num_regions:
            # conditioning on number of regions only
            region_breaks = self.get_region_breaks(num_regions)
            self.build_noncontig_regions(num_regions, region_breaks)
        elif cardinality is not None:
            # conditioning on number of cardinality only
            num_regions = len(cardinality)
            region_breaks = self.cards2breaks(cardinality)
            self.build_noncontig_regions(num_regions, region_breaks)
        elif contiguity is not None:
            # conditioning on number of contiguity only
            num_regions = self.get_num_regions()
            cards = self.get_cards(num_regions)
            self.build_contig_regions(num_regions, cards, contiguity, maxiter,
                                      compact, max_swaps)
        else:
            # unconditioned
            num_regions = self.get_num_regions()
            region_breaks = self.get_region_breaks(num_regions)
            self.build_noncontig_regions(num_regions, region_breaks)