void SolveKarpSubproblems()
{
    Node *N;
    int i;
    double EntryTime = GetTime();

    AllocateStructures();
    ReadPenalties();
    /* Compute upper bound for the original problem */
    GlobalBestCost = 0;
    N = FirstNode;
    do {
        if (!Fixed(N, N->SubproblemSuc))
            GlobalBestCost += Distance(N, N->SubproblemSuc);
        N->Subproblem = 0;
    }
    while ((N = N->BestSuc = N->SubproblemSuc) != FirstNode);
    if (TraceLevel >= 1) {
        if (TraceLevel >= 2)
            printff("\n");
        printff("*** Karp partitioning *** [Cost = " GainFormat "]\n",
                GlobalBestCost);
    }
    if (WeightType == GEO || WeightType == GEOM ||
        WeightType == GEO_MEEUS || WeightType == GEOM_MEEUS) {
        N = FirstNode;
        do {
            N->Xc = N->X;
            N->Yc = N->Y;
            N->Zc = N->Z;
            if (WeightType == GEO || WeightType == GEO_MEEUS)
                GEO2XYZ(N->Xc, N->Yc, &N->X, &N->Y, &N->Z);
            else
                GEOM2XYZ(N->Xc, N->Yc, &N->X, &N->Y, &N->Z);
        } while ((N = N->SubproblemSuc) != FirstNode);
        CoordType = THREED_COORDS;
    }
    KDTree = BuildKDTree();
    if (WeightType == GEO || WeightType == GEOM ||
        WeightType == GEO_MEEUS || WeightType == GEOM_MEEUS) {
        N = FirstNode;
        do {
            N->X = N->Xc;
            N->Y = N->Yc;
            N->Z = N->Zc;
        } while ((N = N->SubproblemSuc) != FirstNode);
        CoordType = TWOD_COORDS;
    }

    CurrentSubproblem = 0;
    Subproblems = 1;
    for (i = Dimension - 1; i > SubproblemSize; i /= 2)
        Subproblems *= 2;
    RestrictedSearchSaved = RestrictedSearch;
    KarpPartition(0, Dimension - 1, 0);
    free(KDTree);
    printff("\nCost = " GainFormat, GlobalBestCost);
    if (Optimum != MINUS_INFINITY && Optimum != 0)
        printff(", Gap = %0.3f%%",
                100.0 * (GlobalBestCost - Optimum) / Optimum);
    printff(", Time = %0.1f sec. %s\n", fabs(GetTime() - EntryTime),
            GlobalBestCost < Optimum ? "<" : GlobalBestCost ==
            Optimum ? "=" : "");
    if (SubproblemBorders && Subproblems > 1)
        SolveSubproblemBorderProblems(Subproblems);
}
static void MarkBorderPoints(int CurrentSubproblem, LKH::LKHAlg * Alg)
{
    double Min[3], Max[3];
    int dMin, dMax, d, i, axis, ActualSubproblemSize = 0, Size = 0;
    LKH::LKHAlg::Node **A, *N;

    assert(A = (LKH::LKHAlg::Node **) malloc(Alg->DimensionSaved * sizeof(LKH::LKHAlg::Node *)));
    Min[0] = Min[1] = Min[2] = DBL_MAX;
    Max[0] = Max[1] = Max[2] = -DBL_MAX;
    if (Alg->WeightType == LKH::GEO || Alg->WeightType == LKH::GEOM ||
        Alg->WeightType == LKH::GEO_MEEUS || Alg->WeightType == LKH::GEOM_MEEUS) {
        N = Alg->FirstNode;
        do {
            N->Xc = N->X;
            N->Yc = N->Y;
            N->Zc = N->Z;
            if (Alg->WeightType == LKH::GEO || Alg->WeightType == LKH::GEO_MEEUS)
                GEO2XYZ(N->Xc, N->Yc, &N->X, &N->Y, &N->Z);
            else
                GEOM2XYZ(N->Xc, N->Yc, &N->X, &N->Y, &N->Z);
        } while ((N = N->SubproblemSuc) != Alg->FirstNode);
        Alg->CoordType = LKH::THREED_COORDS;
    }
    N = Alg->FirstNode;
    do {
        if (N->Subproblem == CurrentSubproblem) {
            for (i = Alg->CoordType == LKH::THREED_COORDS ? 2 : 1; i >= 0; i--) {
                if (Coord(N, i) < Min[i])
                    Min[i] = Coord(N, i);
                if (Coord(N, i) > Max[i])
                    Max[i] = Coord(N, i);
            }
            ActualSubproblemSize++;
        }
    } while ((N = N->SubproblemSuc) != Alg->FirstNode);
    do {
        if (N->Subproblem == CurrentSubproblem ||
            (N->X >= Min[0] && N->X <= Max[0] &&
             N->Y >= Min[1] && N->Y <= Max[1] &&
             (Alg->CoordType == LKH::TWOD_COORDS ||
              (N->Z >= Min[2] && N->Z <= Max[2])))) {
            N->Rank = INT_MAX;
            for (i = Alg->CoordType == LKH::THREED_COORDS ? 2 : 1; i >= 0; i--) {
                dMin = (int) (fabs(Coord(N, i) - Min[i]) + 0.5);
                dMax = (int) (fabs(Coord(N, i) - Max[i]) + 0.5);
                d = dMin < dMax ? dMin : dMax;
                if (d < N->Rank)
                    N->Rank = d;
            }
        } else {
            axis = -1;
            if (Alg->CoordType == LKH::TWOD_COORDS) {
                if (N->X >= Min[0] && N->X <= Max[0])
                    axis = 1;
                else if (N->Y >= Min[1] && N->Y <= Max[1])
                    axis = 0;
            } else if (N->X >= Min[0] && N->X <= Max[0]) {
                if (N->Y >= Min[1] && N->Y <= Max[1])
                    axis = 2;
                else if (N->Z >= Min[2] && N->Z <= Max[2])
                    axis = 1;
            } else if (N->Y >= Min[1] && N->Y <= Max[1] &&
                       N->Z >= Min[2] && N->Z <= Max[2])
                axis = 0;
            if (axis != -1) {
                dMin = (int) (fabs(Coord(N, axis) - Min[axis]) + 0.5);
                dMax = (int) (fabs(Coord(N, axis) - Max[axis]) + 0.5);
                N->Rank = dMin < dMax ? dMin : dMax;
            } else {
                N->Rank = 0;
                for (i = Alg->CoordType == LKH::THREED_COORDS ? 2 : 1; i >= 0; i--) {
                    dMin = (int) (fabs(Coord(N, i) - Min[i]) + 0.5);
                    dMax = (int) (fabs(Coord(N, i) - Max[i]) + 0.5);
                    d = dMin < dMax ? dMin : dMax;
                    if (d > N->Rank)
                        N->Rank = d;
                }
            }
        }
        N->Subproblem = 0;
        if (!Alg->SubproblemsCompressed ||
            ((N->SubproblemPred != N->SubBestPred ||
              N->SubproblemSuc != N->SubBestSuc) &&
             (N->SubproblemPred != N->SubBestSuc ||
              N->SubproblemSuc != N->SubBestPred)))
            A[Size++] = N;
    } while ((N = N->SubproblemSuc) != Alg->FirstNode);
    if (ActualSubproblemSize > Size)
        ActualSubproblemSize = Size;
    else
        QuickSelect(A, Size, ActualSubproblemSize);
    for (Size = 0; Size < ActualSubproblemSize; Size++)
        A[Size]->Subproblem = CurrentSubproblem;
    free(A);
    if (Alg->WeightType == LKH::GEO || Alg->WeightType == LKH::GEOM ||
       Alg-> WeightType == LKH::GEO_MEEUS || Alg->WeightType == LKH::GEOM_MEEUS) {
        N = Alg->FirstNode;
        do {
            N->X = N->Xc;
            N->Y = N->Yc;
            N->Z = N->Zc;
        } while ((N = N->SubproblemSuc) != Alg->FirstNode);
        Alg->CoordType = LKH::TWOD_COORDS;
    }
}
void SolveRoheSubproblems()
{
    Node *N;
    int CurrentSubproblem, Subproblems, Remaining, i;
    GainType GlobalBestCost, OldGlobalBestCost;
    double XMin, XMax, YMin, YMax, ZMin, ZMax, DX, DY, DZ, CLow, CMid,
        CHigh;
    double EntryTime = GetTime();

    AllocateStructures();
    ReadPenalties();
    Subproblems = 0;

    /* Compute upper bound for the original problem */
    GlobalBestCost = 0;
    N = FirstNode;
    do {
        if (!Fixed(N, N->SubproblemSuc))
            GlobalBestCost += Distance(N, N->SubproblemSuc);
        N->Subproblem = 0;
    }
    while ((N = N->SubproblemSuc) != FirstNode);
    if (TraceLevel >= 1) {
        if (TraceLevel >= 2)
            printff("\n");
        printff("*** Rohe partitioning *** [Cost = " GainFormat "]\n",
                GlobalBestCost);
    }
    if (WeightType == GEO || WeightType == GEOM ||
        WeightType == GEO_MEEUS || WeightType == GEOM_MEEUS) {
        N = FirstNode;
        do {
            N->Xc = N->X;
            N->Yc = N->Y;
            N->Zc = N->Z;
            if (WeightType == GEO || WeightType == GEO_MEEUS)
                GEO2XYZ(N->Xc, N->Yc, &N->X, &N->Y, &N->Z);
            else
                GEOM2XYZ(N->Xc, N->Yc, &N->X, &N->Y, &N->Z);
        } while ((N = N->SubproblemSuc) != FirstNode);
        CoordType = THREED_COORDS;
    }
    N = FirstNode;
    XMin = XMax = N->X;
    YMin = YMax = N->Y;
    ZMin = ZMax = N->Z;
    while ((N = N->SubproblemSuc) != FirstNode) {
        if (N->X < XMin)
            XMin = N->X;
        else if (N->X > XMax)
            XMax = N->X;
        if (N->Y < YMin)
            YMin = N->Y;
        else if (N->Y > YMax)
            YMax = N->Y;
        if (N->Z < ZMin)
            ZMin = N->Z;
        else if (N->Z > ZMax)
            ZMax = N->Z;
    }
    KDTree = BuildKDTree(SubproblemSize);
    Remaining = Dimension;
    while (Remaining > SubproblemSize) {
        N = FirstNode;
        i = Random() % Remaining;
        while (i--)
            N = N->Suc;
        DX = (0.5 + 0.5 * Random() / (PRANDMAX - 1)) * (XMax - XMin);
        DY = (0.5 + 0.5 * Random() / (PRANDMAX - 1)) * (YMax - YMin);
        DZ = (0.5 + 0.5 * Random() / (PRANDMAX - 1)) * (ZMax - ZMin);
        CLow = 0;
        CHigh = 2;
        /* Binary search */
        do {
            CMid = (CLow + CHigh) / 2;
            Size = 0;
            WindowSize(N->X - CMid * DX, N->X + CMid * DX,
                       N->Y - CMid * DY, N->Y + CMid * DY,
                       N->Z - CMid * DZ, N->Z + CMid * DZ,
                       0, Dimension - 1);
            if (Size >= 2.0 / 3 * SubproblemSize && Size <= SubproblemSize)
                break;
            if (Size < 2.0 / 3 * SubproblemSize)
                CLow = CMid;
            else
                CHigh = CMid;
        }
        while (CHigh - CLow > DBL_EPSILON);
        MakeSubproblem(N->X - CMid * DX, N->X + CMid * DX,
                       N->Y - CMid * DY, N->Y + CMid * DY,
                       N->Z - CMid * DZ, N->Z + CMid * DZ,
                       ++Subproblems, 0, Dimension - 1);
        Remaining -= Size;
    }
    if (Remaining > 3) {
        Subproblems++;
        N = FirstNode;
        do
            N->Subproblem = Subproblems;
        while ((N = N->Suc) != FirstNode);
    }
    if (WeightType == GEO || WeightType == GEOM || WeightType == GEO_MEEUS
        || WeightType == GEOM_MEEUS) {
        N = FirstNode;
        do {
            N->X = N->Xc;
            N->Y = N->Yc;
            N->Z = N->Zc;
        } while ((N = N->SubproblemSuc) != FirstNode);
        CoordType = TWOD_COORDS;
    }
    free(KDTree);
    for (CurrentSubproblem = 1;
         CurrentSubproblem <= Subproblems; CurrentSubproblem++) {
        OldGlobalBestCost = GlobalBestCost;
        SolveSubproblem(CurrentSubproblem, Subproblems, &GlobalBestCost);
        if (SubproblemsCompressed && GlobalBestCost == OldGlobalBestCost)
            SolveCompressedSubproblem(CurrentSubproblem, Subproblems,
                                      &GlobalBestCost);
    }
    printff("\nCost = " GainFormat, GlobalBestCost);
    if (Optimum != MINUS_INFINITY && Optimum != 0)
        printff(", Gap = %0.4f%%",
                100.0 * (GlobalBestCost - Optimum) / Optimum);
    printff(", Time = %0.1f sec. %s\n", fabs(GetTime() - EntryTime),
            GlobalBestCost < Optimum ? "<" : GlobalBestCost ==
            Optimum ? "=" : "");
    if (SubproblemBorders && Subproblems > 1)
        SolveSubproblemBorderProblems(Subproblems, &GlobalBestCost);
}