def CP_model_restricted(params):
    ##
    NUM_COLORS, NUM_ITEMS, BIN_SIZE, DISCREPANCY, ITEM_SIZES, COLORS, NUM_BINS, Timelimit, SEED = params
    mdl = CpoModel()

    X = [mdl.integer_var(min=0, max=NUM_BINS) for item in range(NUM_ITEMS)]

    CP = mdl.integer_var_list(NUM_BINS, min=0, max=BIN_SIZE)

    for bin in range(NUM_BINS - 1):
        mdl.add(CP[bin] >= CP[bin + 1])

    mdl.add(pack(CP, [X[item] for item in range(NUM_ITEMS)], ITEM_SIZES))

    CC = [
        mdl.integer_var_list(NUM_COLORS, min=0, max=BIN_SIZE)
        for bin in range(NUM_BINS)
    ]

    for color in range(NUM_COLORS):
        mdl.add(
            distribute([CC[bin][color] for bin in range(NUM_BINS)], [
                X[item] for item in range(NUM_ITEMS) if COLORS[item] == color
            ], [bin for bin in range(NUM_BINS)]))

    for bin in range(NUM_BINS):

        TC = mdl.sum(CC[bin])
        MC = mdl.max(CC[bin])
        # mdl.add_kpi(TC, name="TC_{}".format(bin))
        # mdl.add_kpi(MC, name="MC_{}".format(bin))

        # mdl.add(
        #     (TC+mdl.mod(TC, 2))/2  >= MC
        #
        # )
        # mdl.add(
        #     (TC - mdl.mod(TC, NUM_COLORS))/NUM_COLORS >= MC
        # )

        # mdl.add(
        #     mdl.sum(
        #         CC[bin][color] > 0 for color in range(NUM_COLORS)
        #     ) <= MAX_COLORS_IN_BIN
        # )

    for item in range(NUM_ITEMS):
        for item2 in range(item + 1, NUM_ITEMS):
            if COLORS[item] == COLORS[item2]:
                mdl.add(
                    mdl.if_then(
                        X[item] == X[item2],
                        mdl.any([
                            X[i] == X[item]
                            for i in range(item + 1, item2 + 1)
                            if COLORS[i] != COLORS[item]
                        ])
                        # mdl.count([mdl.element(COLORS, i) for i in range(item, item2+1)], abs(COLORS[item] -1)) > 0
                    ))

    # mdl.add(
    #     mdl.minimize(count_different(X))
    # )

    # bins_used = mdl.sum(CP[bin] > 0 for bin in range(NUM_BINS))
    # mdl.add(
    #     mdl.minimize(
    #         bins_used
    #     )
    # )

    mdl.add(mdl.minimize(mdl.max(X) + 1))

    try:
        msol = mdl.solve(TimeLimit=20)
        mdl._myInstance = (NUM_COLORS, NUM_ITEMS, BIN_SIZE, DISCREPANCY, SEED)
        #
        # print(ITEM_SIZES)
        # print(COLORS)
        #

        # print([msol[X[item]] for item in range(NUM_ITEMS)])

        Xs = np.array([msol[X[i]] for i in range(NUM_ITEMS)])
        if solution_checker(Xs, COLORS, ITEM_SIZES, BIN_SIZE, SEED,
                            'restricted_cp_binary'):
            write_to_global_cp(msol, mdl, 'restricted_binary')

    except Exception as err:
        print(err)
        write_to_global_failed(NUM_COLORS,
                               NUM_ITEMS,
                               BIN_SIZE,
                               DISCREPANCY,
                               SEED,
                               'restricted_cp_binary',
                               is_bad_solution=False)
def CP_model_subsequence_restricted(params):
##
    NUM_COLORS, NUM_ITEMS, BIN_SIZE, DISCREPANCY, ITEM_SIZES, COLORS, NUM_BINS, Timelimit, SEED = params


    print("*** Running CP Subseq Restricted with instance NUM_COLORS{} NUM_ITEMS{} BIN_SIZE {} DISCREPANCY {} SEED {}".format(
        NUM_COLORS, NUM_ITEMS, BIN_SIZE, DISCREPANCY, SEED))

    mdl = CpoModel()

    ITEMS = [mdl.interval_var(start=[0, sum(ITEM_SIZES)], size=ITEM_SIZES[item], optional=False, name="item_"+str(item)) for item in range(NUM_ITEMS)]
    ITEMS_TO_BINS = [[mdl.interval_var(start=(0, BIN_SIZE-min(ITEM_SIZES)), end=(min(ITEM_SIZES), BIN_SIZE), size=ITEM_SIZES[item], optional=True, name="item{}InBin{}".format(item, bin)) for bin in range(NUM_BINS)] for item in range(NUM_ITEMS)]

    ITEMS_S = mdl.sequence_var(ITEMS, types=COLORS, name="itemSequence")
    BIN_S = [mdl.sequence_var([ITEMS_TO_BINS[item][bin] for item in range(NUM_ITEMS)], types=[COLORS[item] for item in range(NUM_ITEMS)], name="bin{}Sequence".format(bin)) for bin in range(NUM_BINS)]

    # COLORS_S = [[mdl.sequence_var(ITEMS[item][bin] for item in range(NUM_ITEMS) if COLORS[item] == color)] for color in range(NUM_COLORS)]
    BINS_USED = [mdl.binary_var() for bin in range(NUM_BINS)]


    for item in range(NUM_ITEMS):
        # for bin in range(NUM_BINS):
        #     mdl.add_kpi(
        #         mdl.presence_of(ITEMS_TO_BINS[item][bin]),
        #         "item_"+str(item)+" "+str(bin)
        #     )
        mdl.add(
            mdl.sum(
                [mdl.presence_of(
                    ITEMS_TO_BINS[item][bin])
                    for bin in range(NUM_BINS)]
                ) == 1
        )

    # for item in range(NUM_ITEMS):
    #     mdl.add(
    #         mdl.alternative(ITEMS[item], [ITEMS_TO_BINS[item][bin] for bin in range(NUM_BINS)])
    #     )

    for bin in range(NUM_BINS):
        mdl.add(mdl.no_overlap(BIN_S[bin]))


    for item in range(NUM_ITEMS-1):
        mdl.add(
            mdl.end_before_start(ITEMS[item], ITEMS[item+1])
        )

    mdl.add(mdl.no_overlap(ITEMS_S))

    for bin in range(NUM_BINS):
        mdl.add(
            mdl.same_common_subsequence(
                ITEMS_S,
                BIN_S[bin],
            )
        )

    for item in range(NUM_ITEMS):
        for bin in range(NUM_BINS):
            # if (item, bin) in [(17,5), (20, 5), (29, 5)]:
            #     mdl.add_kpi(mdl.type_of_next(BIN_S[bin], ITEMS_TO_BINS[item][bin], lastValue=-1, absentValue=-1), name="typeofnext{}_{}".format(item, bin))
            #
            #     mdl.add_kpi(mdl.type_of_prev(BIN_S[bin], ITEMS_TO_BINS[item][bin], firstValue=-1, absentValue=-1), name="typeofprev{}_{}".format(item, bin))

            mdl.add(
                COLORS[item] != mdl.type_of_next(BIN_S[bin], ITEMS_TO_BINS[item][bin], lastValue=-1, absentValue=-1)
            )

            mdl.add(
                COLORS[item] != mdl.type_of_prev(BIN_S[bin], ITEMS_TO_BINS[item][bin], firstValue=-1, absentValue=-1)
            )

    for bin in range(NUM_BINS):
        mdl.add(
            BINS_USED[bin] ==  mdl.any([
                mdl.presence_of(ITEMS_TO_BINS[item][bin]) for item in range(NUM_ITEMS)
            ])
        )
    for bin in range(NUM_BINS-1):
        mdl.add(
            BINS_USED[bin] >= BINS_USED[bin+1]
        )

    mdl.add(
        mdl.minimize(
            mdl.sum(
                BINS_USED
            )
        )
    )

    # mdl.add(
    #         mdl.sum(
    #             BINS_USED
    #         ) < 48
    # )

    try:

        msol = mdl.solve(TimeLimit = Timelimit)
        mdl._myInstance = (NUM_COLORS, NUM_ITEMS, BIN_SIZE, DISCREPANCY, SEED)

        if msol:

            ITEMS_TO_BINS_S = []
            for bin in range(NUM_BINS):
                b_ = []
                for item in range(NUM_ITEMS):
                    s = msol[ITEMS_TO_BINS[item][bin]]
                    if s:
                        b_.append(s)
                ITEMS_TO_BINS_S.append(b_)
            # return mdl

            seq = msol.get_var_solution(BIN_S[0])

            print(ITEM_SIZES)
            print("CL: ",COLORS)

            Xs = []
            for item in range(NUM_ITEMS):
                for bin in range(NUM_BINS):
                    s = msol[ITEMS_TO_BINS[item][bin]]
                    if s:
                        # print(s)
                        Xs.append(bin)
            print("Xs: ", Xs)

            Xs = np.array(Xs)
            if solution_checker(Xs, params, "restricted_cp_subsequence"):
                write_to_global_cp(msol, mdl, 'restricted_subsequence')

    except Exception as err:
        print(err)
        write_to_global_failed(params, 'restricted_cp_subsequence', is_bad_solution=False)