aboutsummaryrefslogtreecommitdiffstats
path: root/test/monniaux/glpk-4.65/src/draft/glpios03.c
blob: 21d6a000700018117d1b21295b36d41a25ff837f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
/* glpios03.c (branch-and-cut driver) */

/***********************************************************************
*  This code is part of GLPK (GNU Linear Programming Kit).
*
*  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
*  2009, 2010, 2011, 2013, 2018 Andrew Makhorin, Department for Applied
*  Informatics, Moscow Aviation Institute, Moscow, Russia. All rights
*  reserved. E-mail: <mao@gnu.org>.
*
*  GLPK is free software: you can redistribute it and/or modify it
*  under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  GLPK is distributed in the hope that it will be useful, but WITHOUT
*  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
*  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
*  License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
***********************************************************************/

#include "env.h"
#include "ios.h"

/***********************************************************************
*  show_progress - display current progress of the search
*
*  This routine displays some information about current progress of the
*  search.
*
*  The information includes:
*
*  the current number of iterations performed by the simplex solver;
*
*  the objective value for the best known integer feasible solution,
*  which is upper (minimization) or lower (maximization) global bound
*  for optimal solution of the original mip problem;
*
*  the best local bound for active nodes, which is lower (minimization)
*  or upper (maximization) global bound for optimal solution of the
*  original mip problem;
*
*  the relative mip gap, in percents;
*
*  the number of open (active) subproblems;
*
*  the number of completely explored subproblems, i.e. whose nodes have
*  been removed from the tree. */

static void show_progress(glp_tree *T, int bingo)
{     int p;
      double temp;
      char best_mip[50], best_bound[50], *rho, rel_gap[50];
      /* format the best known integer feasible solution */
      if (T->mip->mip_stat == GLP_FEAS)
         sprintf(best_mip, "%17.9e", T->mip->mip_obj);
      else
         sprintf(best_mip, "%17s", "not found yet");
      /* determine reference number of an active subproblem whose local
         bound is best */
      p = ios_best_node(T);
      /* format the best bound */
      if (p == 0)
         sprintf(best_bound, "%17s", "tree is empty");
      else
      {  temp = T->slot[p].node->bound;
         if (temp == -DBL_MAX)
            sprintf(best_bound, "%17s", "-inf");
         else if (temp == +DBL_MAX)
            sprintf(best_bound, "%17s", "+inf");
         else
         {  if (fabs(temp) < 1e-9)
               temp = 0;
            sprintf(best_bound, "%17.9e", temp);
         }
      }
      /* choose the relation sign between global bounds */
      if (T->mip->dir == GLP_MIN)
         rho = ">=";
      else if (T->mip->dir == GLP_MAX)
         rho = "<=";
      else
         xassert(T != T);
      /* format the relative mip gap */
      temp = ios_relative_gap(T);
      if (temp == 0.0)
         sprintf(rel_gap, "  0.0%%");
      else if (temp < 0.001)
         sprintf(rel_gap, "< 0.1%%");
      else if (temp <= 9.999)
         sprintf(rel_gap, "%5.1f%%", 100.0 * temp);
      else
         sprintf(rel_gap, "%6s", "");
      /* display progress of the search */
      xprintf("+%6d: %s %s %s %s %s (%d; %d)\n",
         T->mip->it_cnt, bingo ? ">>>>>" : "mip =", best_mip, rho,
         best_bound, rel_gap, T->a_cnt, T->t_cnt - T->n_cnt);
      T->tm_lag = xtime();
      return;
}

/***********************************************************************
*  is_branch_hopeful - check if specified branch is hopeful
*
*  This routine checks if the specified subproblem can have an integer
*  optimal solution which is better than the best known one.
*
*  The check is based on comparison of the local objective bound stored
*  in the subproblem descriptor and the incumbent objective value which
*  is the global objective bound.
*
*  If there is a chance that the specified subproblem can have a better
*  integer optimal solution, the routine returns non-zero. Otherwise, if
*  the corresponding branch can pruned, zero is returned. */

static int is_branch_hopeful(glp_tree *T, int p)
{     xassert(1 <= p && p <= T->nslots);
      xassert(T->slot[p].node != NULL);
      return ios_is_hopeful(T, T->slot[p].node->bound);
}

/***********************************************************************
*  check_integrality - check integrality of basic solution
*
*  This routine checks if the basic solution of LP relaxation of the
*  current subproblem satisfies to integrality conditions, i.e. that all
*  variables of integer kind have integral primal values. (The solution
*  is assumed to be optimal.)
*
*  For each variable of integer kind the routine computes the following
*  quantity:
*
*     ii(x[j]) = min(x[j] - floor(x[j]), ceil(x[j]) - x[j]),         (1)
*
*  which is a measure of the integer infeasibility (non-integrality) of
*  x[j] (for example, ii(2.1) = 0.1, ii(3.7) = 0.3, ii(5.0) = 0). It is
*  understood that 0 <= ii(x[j]) <= 0.5, and variable x[j] is integer
*  feasible if ii(x[j]) = 0. However, due to floating-point arithmetic
*  the routine checks less restrictive condition:
*
*     ii(x[j]) <= tol_int,                                           (2)
*
*  where tol_int is a given tolerance (small positive number) and marks
*  each variable which does not satisfy to (2) as integer infeasible by
*  setting its fractionality flag.
*
*  In order to characterize integer infeasibility of the basic solution
*  in the whole the routine computes two parameters: ii_cnt, which is
*  the number of variables with the fractionality flag set, and ii_sum,
*  which is the sum of integer infeasibilities (1). */

static void check_integrality(glp_tree *T)
{     glp_prob *mip = T->mip;
      int j, type, ii_cnt = 0;
      double lb, ub, x, temp1, temp2, ii_sum = 0.0;
      /* walk through the set of columns (structural variables) */
      for (j = 1; j <= mip->n; j++)
      {  GLPCOL *col = mip->col[j];
         T->non_int[j] = 0;
         /* if the column is not integer, skip it */
         if (col->kind != GLP_IV) continue;
         /* if the column is non-basic, it is integer feasible */
         if (col->stat != GLP_BS) continue;
         /* obtain the type and bounds of the column */
         type = col->type, lb = col->lb, ub = col->ub;
         /* obtain value of the column in optimal basic solution */
         x = col->prim;
         /* if the column's primal value is close to the lower bound,
            the column is integer feasible within given tolerance */
         if (type == GLP_LO || type == GLP_DB || type == GLP_FX)
         {  temp1 = lb - T->parm->tol_int;
            temp2 = lb + T->parm->tol_int;
            if (temp1 <= x && x <= temp2) continue;
#if 0
            /* the lower bound must not be violated */
            xassert(x >= lb);
#else
            if (x < lb) continue;
#endif
         }
         /* if the column's primal value is close to the upper bound,
            the column is integer feasible within given tolerance */
         if (type == GLP_UP || type == GLP_DB || type == GLP_FX)
         {  temp1 = ub - T->parm->tol_int;
            temp2 = ub + T->parm->tol_int;
            if (temp1 <= x && x <= temp2) continue;
#if 0
            /* the upper bound must not be violated */
            xassert(x <= ub);
#else
            if (x > ub) continue;
#endif
         }
         /* if the column's primal value is close to nearest integer,
            the column is integer feasible within given tolerance */
         temp1 = floor(x + 0.5) - T->parm->tol_int;
         temp2 = floor(x + 0.5) + T->parm->tol_int;
         if (temp1 <= x && x <= temp2) continue;
         /* otherwise the column is integer infeasible */
         T->non_int[j] = 1;
         /* increase the number of fractional-valued columns */
         ii_cnt++;
         /* compute the sum of integer infeasibilities */
         temp1 = x - floor(x);
         temp2 = ceil(x) - x;
         xassert(temp1 > 0.0 && temp2 > 0.0);
         ii_sum += (temp1 <= temp2 ? temp1 : temp2);
      }
      /* store ii_cnt and ii_sum to the current problem descriptor */
      xassert(T->curr != NULL);
      T->curr->ii_cnt = ii_cnt;
      T->curr->ii_sum = ii_sum;
      /* and also display these parameters */
      if (T->parm->msg_lev >= GLP_MSG_DBG)
      {  if (ii_cnt == 0)
            xprintf("There are no fractional columns\n");
         else if (ii_cnt == 1)
            xprintf("There is one fractional column, integer infeasibil"
               "ity is %.3e\n", ii_sum);
         else
            xprintf("There are %d fractional columns, integer infeasibi"
               "lity is %.3e\n", ii_cnt, ii_sum);
      }
      return;
}

/***********************************************************************
*  record_solution - record better integer feasible solution
*
*  This routine records optimal basic solution of LP relaxation of the
*  current subproblem, which being integer feasible is better than the
*  best known integer feasible solution. */

static void record_solution(glp_tree *T)
{     glp_prob *mip = T->mip;
      int i, j;
      mip->mip_stat = GLP_FEAS;
      mip->mip_obj = mip->obj_val;
      for (i = 1; i <= mip->m; i++)
      {  GLPROW *row = mip->row[i];
         row->mipx = row->prim;
      }
      for (j = 1; j <= mip->n; j++)
      {  GLPCOL *col = mip->col[j];
         if (col->kind == GLP_CV)
            col->mipx = col->prim;
         else if (col->kind == GLP_IV)
         {  /* value of the integer column must be integral */
            col->mipx = floor(col->prim + 0.5);
         }
         else
            xassert(col != col);
      }
      T->sol_cnt++;
      return;
}

/***********************************************************************
*  fix_by_red_cost - fix non-basic integer columns by reduced costs
*
*  This routine fixes some non-basic integer columns if their reduced
*  costs indicate that increasing (decreasing) the column at least by
*  one involves the objective value becoming worse than the incumbent
*  objective value. */

static void fix_by_red_cost(glp_tree *T)
{     glp_prob *mip = T->mip;
      int j, stat, fixed = 0;
      double obj, lb, ub, dj;
      /* the global bound must exist */
      xassert(T->mip->mip_stat == GLP_FEAS);
      /* basic solution of LP relaxation must be optimal */
      xassert(mip->pbs_stat == GLP_FEAS && mip->dbs_stat == GLP_FEAS);
      /* determine the objective function value */
      obj = mip->obj_val;
      /* walk through the column list */
      for (j = 1; j <= mip->n; j++)
      {  GLPCOL *col = mip->col[j];
         /* if the column is not integer, skip it */
         if (col->kind != GLP_IV) continue;
         /* obtain bounds of j-th column */
         lb = col->lb, ub = col->ub;
         /* and determine its status and reduced cost */
         stat = col->stat, dj = col->dual;
         /* analyze the reduced cost */
         switch (mip->dir)
         {  case GLP_MIN:
               /* minimization */
               if (stat == GLP_NL)
               {  /* j-th column is non-basic on its lower bound */
                  if (dj < 0.0) dj = 0.0;
                  if (obj + dj >= mip->mip_obj)
                     glp_set_col_bnds(mip, j, GLP_FX, lb, lb), fixed++;
               }
               else if (stat == GLP_NU)
               {  /* j-th column is non-basic on its upper bound */
                  if (dj > 0.0) dj = 0.0;
                  if (obj - dj >= mip->mip_obj)
                     glp_set_col_bnds(mip, j, GLP_FX, ub, ub), fixed++;
               }
               break;
            case GLP_MAX:
               /* maximization */
               if (stat == GLP_NL)
               {  /* j-th column is non-basic on its lower bound */
                  if (dj > 0.0) dj = 0.0;
                  if (obj + dj <= mip->mip_obj)
                     glp_set_col_bnds(mip, j, GLP_FX, lb, lb), fixed++;
               }
               else if (stat == GLP_NU)
               {  /* j-th column is non-basic on its upper bound */
                  if (dj < 0.0) dj = 0.0;
                  if (obj - dj <= mip->mip_obj)
                     glp_set_col_bnds(mip, j, GLP_FX, ub, ub), fixed++;
               }
               break;
            default:
               xassert(T != T);
         }
      }
      if (T->parm->msg_lev >= GLP_MSG_DBG)
      {  if (fixed == 0)
            /* nothing to say */;
         else if (fixed == 1)
            xprintf("One column has been fixed by reduced cost\n");
         else
            xprintf("%d columns have been fixed by reduced costs\n",
               fixed);
      }
      /* fixing non-basic columns on their current bounds does not
         change the basic solution */
      xassert(mip->pbs_stat == GLP_FEAS && mip->dbs_stat == GLP_FEAS);
      return;
}

/***********************************************************************
*  branch_on - perform branching on specified variable
*
*  This routine performs branching on j-th column (structural variable)
*  of the current subproblem. The specified column must be of integer
*  kind and must have a fractional value in optimal basic solution of
*  LP relaxation of the current subproblem (i.e. only columns for which
*  the flag non_int[j] is set are valid candidates to branch on).
*
*  Let x be j-th structural variable, and beta be its primal fractional
*  value in the current basic solution. Branching on j-th variable is
*  dividing the current subproblem into two new subproblems, which are
*  identical to the current subproblem with the following exception: in
*  the first subproblem that begins the down-branch x has a new upper
*  bound x <= floor(beta), and in the second subproblem that begins the
*  up-branch x has a new lower bound x >= ceil(beta).
*
*  Depending on estimation of local bounds for down- and up-branches
*  this routine returns the following:
*
*  0 - both branches have been created;
*  1 - one branch is hopeless and has been pruned, so now the current
*      subproblem is other branch;
*  2 - both branches are hopeless and have been pruned; new subproblem
*      selection is needed to continue the search. */

static int branch_on(glp_tree *T, int j, int next)
{     glp_prob *mip = T->mip;
      IOSNPD *node;
      int m = mip->m;
      int n = mip->n;
      int type, dn_type, up_type, dn_bad, up_bad, p, ret, clone[1+2];
      double lb, ub, beta, new_ub, new_lb, dn_lp, up_lp, dn_bnd, up_bnd;
      /* determine bounds and value of x[j] in optimal solution to LP
         relaxation of the current subproblem */
      xassert(1 <= j && j <= n);
      type = mip->col[j]->type;
      lb = mip->col[j]->lb;
      ub = mip->col[j]->ub;
      beta = mip->col[j]->prim;
      /* determine new bounds of x[j] for down- and up-branches */
      new_ub = floor(beta);
      new_lb = ceil(beta);
      switch (type)
      {  case GLP_FR:
            dn_type = GLP_UP;
            up_type = GLP_LO;
            break;
         case GLP_LO:
            xassert(lb <= new_ub);
            dn_type = (lb == new_ub ? GLP_FX : GLP_DB);
            xassert(lb + 1.0 <= new_lb);
            up_type = GLP_LO;
            break;
         case GLP_UP:
            xassert(new_ub <= ub - 1.0);
            dn_type = GLP_UP;
            xassert(new_lb <= ub);
            up_type = (new_lb == ub ? GLP_FX : GLP_DB);
            break;
         case GLP_DB:
            xassert(lb <= new_ub && new_ub <= ub - 1.0);
            dn_type = (lb == new_ub ? GLP_FX : GLP_DB);
            xassert(lb + 1.0 <= new_lb && new_lb <= ub);
            up_type = (new_lb == ub ? GLP_FX : GLP_DB);
            break;
         default:
            xassert(type != type);
      }
      /* compute local bounds to LP relaxation for both branches */
      ios_eval_degrad(T, j, &dn_lp, &up_lp);
      /* and improve them by rounding */
      dn_bnd = ios_round_bound(T, dn_lp);
      up_bnd = ios_round_bound(T, up_lp);
      /* check local bounds for down- and up-branches */
      dn_bad = !ios_is_hopeful(T, dn_bnd);
      up_bad = !ios_is_hopeful(T, up_bnd);
      if (dn_bad && up_bad)
      {  if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("Both down- and up-branches are hopeless\n");
         ret = 2;
         goto done;
      }
      else if (up_bad)
      {  if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("Up-branch is hopeless\n");
         glp_set_col_bnds(mip, j, dn_type, lb, new_ub);
         T->curr->lp_obj = dn_lp;
         if (mip->dir == GLP_MIN)
         {  if (T->curr->bound < dn_bnd)
                T->curr->bound = dn_bnd;
         }
         else if (mip->dir == GLP_MAX)
         {  if (T->curr->bound > dn_bnd)
                T->curr->bound = dn_bnd;
         }
         else
            xassert(mip != mip);
         ret = 1;
         goto done;
      }
      else if (dn_bad)
      {  if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("Down-branch is hopeless\n");
         glp_set_col_bnds(mip, j, up_type, new_lb, ub);
         T->curr->lp_obj = up_lp;
         if (mip->dir == GLP_MIN)
         {  if (T->curr->bound < up_bnd)
                T->curr->bound = up_bnd;
         }
         else if (mip->dir == GLP_MAX)
         {  if (T->curr->bound > up_bnd)
                T->curr->bound = up_bnd;
         }
         else
            xassert(mip != mip);
         ret = 1;
         goto done;
      }
      /* both down- and up-branches seem to be hopeful */
      if (T->parm->msg_lev >= GLP_MSG_DBG)
         xprintf("Branching on column %d, primal value is %.9e\n",
            j, beta);
      /* determine the reference number of the current subproblem */
      xassert(T->curr != NULL);
      p = T->curr->p;
      T->curr->br_var = j;
      T->curr->br_val = beta;
      /* freeze the current subproblem */
      ios_freeze_node(T);
      /* create two clones of the current subproblem; the first clone
         begins the down-branch, the second one begins the up-branch */
      ios_clone_node(T, p, 2, clone);
      if (T->parm->msg_lev >= GLP_MSG_DBG)
         xprintf("Node %d begins down branch, node %d begins up branch "
            "\n", clone[1], clone[2]);
      /* set new upper bound of j-th column in the down-branch */
      node = T->slot[clone[1]].node;
      xassert(node != NULL);
      xassert(node->up != NULL);
      xassert(node->b_ptr == NULL);
      node->b_ptr = dmp_get_atom(T->pool, sizeof(IOSBND));
      node->b_ptr->k = m + j;
      node->b_ptr->type = (unsigned char)dn_type;
      node->b_ptr->lb = lb;
      node->b_ptr->ub = new_ub;
      node->b_ptr->next = NULL;
      node->lp_obj = dn_lp;
      if (mip->dir == GLP_MIN)
      {  if (node->bound < dn_bnd)
             node->bound = dn_bnd;
      }
      else if (mip->dir == GLP_MAX)
      {  if (node->bound > dn_bnd)
             node->bound = dn_bnd;
      }
      else
         xassert(mip != mip);
      /* set new lower bound of j-th column in the up-branch */
      node = T->slot[clone[2]].node;
      xassert(node != NULL);
      xassert(node->up != NULL);
      xassert(node->b_ptr == NULL);
      node->b_ptr = dmp_get_atom(T->pool, sizeof(IOSBND));
      node->b_ptr->k = m + j;
      node->b_ptr->type = (unsigned char)up_type;
      node->b_ptr->lb = new_lb;
      node->b_ptr->ub = ub;
      node->b_ptr->next = NULL;
      node->lp_obj = up_lp;
      if (mip->dir == GLP_MIN)
      {  if (node->bound < up_bnd)
             node->bound = up_bnd;
      }
      else if (mip->dir == GLP_MAX)
      {  if (node->bound > up_bnd)
             node->bound = up_bnd;
      }
      else
         xassert(mip != mip);
      /* suggest the subproblem to be solved next */
      xassert(T->child == 0);
      if (next == GLP_NO_BRNCH)
         T->child = 0;
      else if (next == GLP_DN_BRNCH)
         T->child = clone[1];
      else if (next == GLP_UP_BRNCH)
         T->child = clone[2];
      else
         xassert(next != next);
      ret = 0;
done: return ret;
}

/***********************************************************************
*  cleanup_the_tree - prune hopeless branches from the tree
*
*  This routine walks through the active list and checks the local
*  bound for every active subproblem. If the local bound indicates that
*  the subproblem cannot have integer optimal solution better than the
*  incumbent objective value, the routine deletes such subproblem that,
*  in turn, involves pruning the corresponding branch of the tree. */

static void cleanup_the_tree(glp_tree *T)
{     IOSNPD *node, *next_node;
      int count = 0;
      /* the global bound must exist */
      xassert(T->mip->mip_stat == GLP_FEAS);
      /* walk through the list of active subproblems */
      for (node = T->head; node != NULL; node = next_node)
      {  /* deleting some active problem node may involve deleting its
            parents recursively; however, all its parents being created
            *before* it are always *precede* it in the node list, so
            the next problem node is never affected by such deletion */
         next_node = node->next;
         /* if the branch is hopeless, prune it */
         if (!is_branch_hopeful(T, node->p))
            ios_delete_node(T, node->p), count++;
      }
      if (T->parm->msg_lev >= GLP_MSG_DBG)
      {  if (count == 1)
            xprintf("One hopeless branch has been pruned\n");
         else if (count > 1)
            xprintf("%d hopeless branches have been pruned\n", count);
      }
      return;
}

/***********************************************************************
*  round_heur - simple rounding heuristic
*
*  This routine attempts to guess an integer feasible solution by
*  simple rounding values of all integer variables in basic solution to
*  nearest integers. */

static int round_heur(glp_tree *T)
{     glp_prob *P = T->mip;
      /*int m = P->m;*/
      int n = P->n;
      int i, j, ret;
      double *x;
      /* compute rounded values of variables */
      x = talloc(1+n, double);
      for (j = 1; j <= n; j++)
      {  GLPCOL *col = P->col[j];
         if (col->kind == GLP_IV)
         {  /* integer variable */
            x[j] = floor(col->prim + 0.5);
         }
         else if (col->type == GLP_FX)
         {  /* fixed variable */
            x[j] = col->prim;
         }
         else
         {  /* non-integer non-fixed variable */
            ret = 3;
            goto done;
         }
      }
      /* check that no constraints are violated */
      for (i = 1; i <= T->orig_m; i++)
      {  int type = T->orig_type[i];
         GLPAIJ *aij;
         double sum;
         if (type == GLP_FR)
            continue;
         /* compute value of linear form */
         sum = 0.0;
         for (aij = P->row[i]->ptr; aij != NULL; aij = aij->r_next)
            sum += aij->val * x[aij->col->j];
         /* check lower bound */
         if (type == GLP_LO || type == GLP_DB || type == GLP_FX)
         {  if (sum < T->orig_lb[i] - 1e-9)
            {  /* lower bound is violated */
               ret = 2;
               goto done;
            }
         }
         /* check upper bound */
         if (type == GLP_UP || type == GLP_DB || type == GLP_FX)
         {  if (sum > T->orig_ub[i] + 1e-9)
            {  /* upper bound is violated */
               ret = 2;
               goto done;
            }
         }
      }
      /* rounded solution is integer feasible */
      if (glp_ios_heur_sol(T, x) == 0)
      {  /* solution is accepted */
         ret = 0;
      }
      else
      {  /* solution is rejected */
         ret = 1;
      }
done: tfree(x);
      return ret;
}

/**********************************************************************/

#if 1 /* 08/III-2016 */
static void gmi_gen(glp_tree *T)
{     /* generate Gomory's mixed integer cuts */
      glp_prob *P, *pool;
      P = T->mip;
      pool = glp_create_prob();
      glp_add_cols(pool, P->n);
      glp_gmi_gen(P, pool, 50);
      if (pool->m > 0)
      {  int i, len, *ind;
         double *val;
         ind = xcalloc(1+P->n, sizeof(int));
         val = xcalloc(1+P->n, sizeof(double));
         for (i = 1; i <= pool->m; i++)
         {  len = glp_get_mat_row(pool, i, ind, val);
            glp_ios_add_row(T, NULL, GLP_RF_GMI, 0, len, ind, val,
               GLP_LO, pool->row[i]->lb);
         }
         xfree(ind);
         xfree(val);
      }
      glp_delete_prob(pool);
      return;
}
#endif

#ifdef NEW_COVER /* 13/II-2018 */
static void cov_gen(glp_tree *T)
{     /* generate cover cuts */
      glp_prob *P, *pool;
      if (T->cov_gen == NULL)
         return;
      P = T->mip;
      pool = glp_create_prob();
      glp_add_cols(pool, P->n);
      glp_cov_gen1(P, T->cov_gen, pool);
      if (pool->m > 0)
      {  int i, len, *ind;
         double *val;
         ind = xcalloc(1+P->n, sizeof(int));
         val = xcalloc(1+P->n, sizeof(double));
         for (i = 1; i <= pool->m; i++)
         {  len = glp_get_mat_row(pool, i, ind, val);
            glp_ios_add_row(T, NULL, GLP_RF_COV, 0, len, ind, val,
               GLP_UP, pool->row[i]->ub);
         }
         xfree(ind);
         xfree(val);
      }
      glp_delete_prob(pool);
      return;
}
#endif

#if 1 /* 08/III-2016 */
static void mir_gen(glp_tree *T)
{     /* generate mixed integer rounding cuts */
      glp_prob *P, *pool;
      P = T->mip;
      pool = glp_create_prob();
      glp_add_cols(pool, P->n);
      glp_mir_gen(P, T->mir_gen, pool);
      if (pool->m > 0)
      {  int i, len, *ind;
         double *val;
         ind = xcalloc(1+P->n, sizeof(int));
         val = xcalloc(1+P->n, sizeof(double));
         for (i = 1; i <= pool->m; i++)
         {  len = glp_get_mat_row(pool, i, ind, val);
            glp_ios_add_row(T, NULL, GLP_RF_MIR, 0, len, ind, val,
               GLP_UP, pool->row[i]->ub);
         }
         xfree(ind);
         xfree(val);
      }
      glp_delete_prob(pool);
      return;
}
#endif

#if 1 /* 08/III-2016 */
static void clq_gen(glp_tree *T, glp_cfg *G)
{     /* generate clique cut from conflict graph */
      glp_prob *P = T->mip;
      int n = P->n;
      int len, *ind;
      double *val;
      ind = talloc(1+n, int);
      val = talloc(1+n, double);
      len = glp_clq_cut(T->mip, G, ind, val);
      if (len > 0)
         glp_ios_add_row(T, NULL, GLP_RF_CLQ, 0, len, ind, val, GLP_UP,
            val[0]);
      tfree(ind);
      tfree(val);
      return;
}
#endif

static void generate_cuts(glp_tree *T)
{     /* generate generic cuts with built-in generators */
      if (!(T->parm->mir_cuts == GLP_ON ||
            T->parm->gmi_cuts == GLP_ON ||
            T->parm->cov_cuts == GLP_ON ||
            T->parm->clq_cuts == GLP_ON)) goto done;
#if 1 /* 20/IX-2008 */
      {  int i, max_cuts, added_cuts;
         max_cuts = T->n;
         if (max_cuts < 1000) max_cuts = 1000;
         added_cuts = 0;
         for (i = T->orig_m+1; i <= T->mip->m; i++)
         {  if (T->mip->row[i]->origin == GLP_RF_CUT)
               added_cuts++;
         }
         /* xprintf("added_cuts = %d\n", added_cuts); */
         if (added_cuts >= max_cuts) goto done;
      }
#endif
      /* generate and add to POOL all cuts violated by x* */
      if (T->parm->gmi_cuts == GLP_ON)
      {  if (T->curr->changed < 7)
#if 0 /* 08/III-2016 */
            ios_gmi_gen(T);
#else
            gmi_gen(T);
#endif
      }
      if (T->parm->mir_cuts == GLP_ON)
      {  xassert(T->mir_gen != NULL);
#if 0 /* 08/III-2016 */
         ios_mir_gen(T, T->mir_gen);
#else
         mir_gen(T);
#endif
      }
      if (T->parm->cov_cuts == GLP_ON)
      {  /* cover cuts works well along with mir cuts */
#ifdef NEW_COVER /* 13/II-2018 */
         cov_gen(T);
#else
         ios_cov_gen(T);
#endif
      }
      if (T->parm->clq_cuts == GLP_ON)
      {  if (T->clq_gen != NULL)
#if 0 /* 29/VI-2013 */
         {  if (T->curr->level == 0 && T->curr->changed < 50 ||
                T->curr->level >  0 && T->curr->changed < 5)
#else /* FIXME */
         {  if (T->curr->level == 0 && T->curr->changed < 500 ||
                T->curr->level >  0 && T->curr->changed < 50)
#endif
#if 0 /* 08/III-2016 */
               ios_clq_gen(T, T->clq_gen);
#else
               clq_gen(T, T->clq_gen);
#endif
         }
      }
done: return;
}

/**********************************************************************/

static void remove_cuts(glp_tree *T)
{     /* remove inactive cuts (some valueable globally valid cut might
         be saved in the global cut pool) */
      int i, cnt = 0, *num = NULL;
      xassert(T->curr != NULL);
      for (i = T->orig_m+1; i <= T->mip->m; i++)
      {  if (T->mip->row[i]->origin == GLP_RF_CUT &&
             T->mip->row[i]->level == T->curr->level &&
             T->mip->row[i]->stat == GLP_BS)
         {  if (num == NULL)
               num = xcalloc(1+T->mip->m, sizeof(int));
            num[++cnt] = i;
         }
      }
      if (cnt > 0)
      {  glp_del_rows(T->mip, cnt, num);
#if 0
         xprintf("%d inactive cut(s) removed\n", cnt);
#endif
         xfree(num);
         xassert(glp_factorize(T->mip) == 0);
      }
      return;
}

/**********************************************************************/

static void display_cut_info(glp_tree *T)
{     glp_prob *mip = T->mip;
      int i, gmi = 0, mir = 0, cov = 0, clq = 0, app = 0;
      for (i = mip->m; i > 0; i--)
      {  GLPROW *row;
         row = mip->row[i];
         /* if (row->level < T->curr->level) break; */
         if (row->origin == GLP_RF_CUT)
         {  if (row->klass == GLP_RF_GMI)
               gmi++;
            else if (row->klass == GLP_RF_MIR)
               mir++;
            else if (row->klass == GLP_RF_COV)
               cov++;
            else if (row->klass == GLP_RF_CLQ)
               clq++;
            else
               app++;
         }
      }
      xassert(T->curr != NULL);
      if (gmi + mir + cov + clq + app > 0)
      {  xprintf("Cuts on level %d:", T->curr->level);
         if (gmi > 0) xprintf(" gmi = %d;", gmi);
         if (mir > 0) xprintf(" mir = %d;", mir);
         if (cov > 0) xprintf(" cov = %d;", cov);
         if (clq > 0) xprintf(" clq = %d;", clq);
         if (app > 0) xprintf(" app = %d;", app);
         xprintf("\n");
      }
      return;
}

/***********************************************************************
*  NAME
*
*  ios_driver - branch-and-cut driver
*
*  SYNOPSIS
*
*  #include "glpios.h"
*  int ios_driver(glp_tree *T);
*
*  DESCRIPTION
*
*  The routine ios_driver is a branch-and-cut driver. It controls the
*  MIP solution process.
*
*  RETURNS
*
*  0  The MIP problem instance has been successfully solved. This code
*     does not necessarily mean that the solver has found optimal
*     solution. It only means that the solution process was successful.
*
*  GLP_EFAIL
*     The search was prematurely terminated due to the solver failure.
*
*  GLP_EMIPGAP
*     The search was prematurely terminated, because the relative mip
*     gap tolerance has been reached.
*
*  GLP_ETMLIM
*     The search was prematurely terminated, because the time limit has
*     been exceeded.
*
*  GLP_ESTOP
*     The search was prematurely terminated by application. */

int ios_driver(glp_tree *T)
{     int p, curr_p, p_stat, d_stat, ret;
#if 1 /* carry out to glp_tree */
      int pred_p = 0;
      /* if the current subproblem has been just created due to
         branching, pred_p is the reference number of its parent
         subproblem, otherwise pred_p is zero */
#endif
#if 1 /* 18/VII-2013 */
      int bad_cut;
      double old_obj;
#endif
#if 0 /* 10/VI-2013 */
      glp_long ttt = T->tm_beg;
#else
      double ttt = T->tm_beg;
#endif
#if 1 /* 27/II-2016 by Chris */
      int root_done = 0;
#endif
#if 0
      ((glp_iocp *)T->parm)->msg_lev = GLP_MSG_DBG;
#endif
#if 1 /* 16/III-2016 */
      if (((glp_iocp *)T->parm)->flip)
#if 0 /* 20/I-2018 */
         xprintf("WARNING: LONG-STEP DUAL SIMPLEX WILL BE USED\n");
#else
         xprintf("Long-step dual simplex will be used\n");
#endif
#endif
      /* on entry to the B&B driver it is assumed that the active list
         contains the only active (i.e. root) subproblem, which is the
         original MIP problem to be solved */
loop: /* main loop starts here */
      /* at this point the current subproblem does not exist */
      xassert(T->curr == NULL);
      /* if the active list is empty, the search is finished */
      if (T->head == NULL)
      {  if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("Active list is empty!\n");
#if 0 /* 10/VI-2013 */
         xassert(dmp_in_use(T->pool).lo == 0);
#else
         xassert(dmp_in_use(T->pool) == 0);
#endif
         ret = 0;
         goto done;
      }
      /* select some active subproblem to continue the search */
      xassert(T->next_p == 0);
      /* let the application program select subproblem */
      if (T->parm->cb_func != NULL)
      {  xassert(T->reason == 0);
         T->reason = GLP_ISELECT;
         T->parm->cb_func(T, T->parm->cb_info);
         T->reason = 0;
         if (T->stop)
         {  ret = GLP_ESTOP;
            goto done;
         }
      }
      if (T->next_p != 0)
      {  /* the application program has selected something */
         ;
      }
      else if (T->a_cnt == 1)
      {  /* the only active subproblem exists, so select it */
         xassert(T->head->next == NULL);
         T->next_p = T->head->p;
      }
      else if (T->child != 0)
      {  /* select one of branching childs suggested by the branching
            heuristic */
         T->next_p = T->child;
      }
      else
      {  /* select active subproblem as specified by the backtracking
            technique option */
         T->next_p = ios_choose_node(T);
      }
      /* the active subproblem just selected becomes current */
      ios_revive_node(T, T->next_p);
      T->next_p = T->child = 0;
      /* invalidate pred_p, if it is not the reference number of the
         parent of the current subproblem */
      if (T->curr->up != NULL && T->curr->up->p != pred_p) pred_p = 0;
      /* determine the reference number of the current subproblem */
      p = T->curr->p;
      if (T->parm->msg_lev >= GLP_MSG_DBG)
      {  xprintf("-----------------------------------------------------"
            "-------------------\n");
         xprintf("Processing node %d at level %d\n", p, T->curr->level);
      }
#if 0
      if (p == 1)
         glp_write_lp(T->mip, NULL, "root.lp");
#endif
#if 1 /* 24/X-2015 */
      if (p == 1)
      {  if (T->parm->sr_heur == GLP_OFF)
         {  if (T->parm->msg_lev >= GLP_MSG_ALL)
               xprintf("Simple rounding heuristic disabled\n");
         }
      }
#endif
      /* if it is the root subproblem, initialize cut generators */
      if (p == 1)
      {  if (T->parm->gmi_cuts == GLP_ON)
         {  if (T->parm->msg_lev >= GLP_MSG_ALL)
               xprintf("Gomory's cuts enabled\n");
         }
         if (T->parm->mir_cuts == GLP_ON)
         {  if (T->parm->msg_lev >= GLP_MSG_ALL)
               xprintf("MIR cuts enabled\n");
            xassert(T->mir_gen == NULL);
#if 0 /* 06/III-2016 */
            T->mir_gen = ios_mir_init(T);
#else
            T->mir_gen = glp_mir_init(T->mip);
#endif
         }
         if (T->parm->cov_cuts == GLP_ON)
         {  if (T->parm->msg_lev >= GLP_MSG_ALL)
               xprintf("Cover cuts enabled\n");
#ifdef NEW_COVER /* 13/II-2018 */
            xassert(T->cov_gen == NULL);
            T->cov_gen = glp_cov_init(T->mip);
#endif
         }
         if (T->parm->clq_cuts == GLP_ON)
         {  xassert(T->clq_gen == NULL);
            if (T->parm->msg_lev >= GLP_MSG_ALL)
               xprintf("Clique cuts enabled\n");
#if 0 /* 08/III-2016 */
            T->clq_gen = ios_clq_init(T);
#else
            T->clq_gen = glp_cfg_init(T->mip);
#endif
         }
      }
#if 1 /* 18/VII-2013 */
      bad_cut = 0;
#endif
more: /* minor loop starts here */
      /* at this point the current subproblem needs either to be solved
         for the first time or re-optimized due to reformulation */
      /* display current progress of the search */
      if (T->parm->msg_lev >= GLP_MSG_DBG ||
          T->parm->msg_lev >= GLP_MSG_ON &&
        (double)(T->parm->out_frq - 1) <=
            1000.0 * xdifftime(xtime(), T->tm_lag))
         show_progress(T, 0);
      if (T->parm->msg_lev >= GLP_MSG_ALL &&
            xdifftime(xtime(), ttt) >= 60.0)
#if 0 /* 16/II-2012 */
      {  glp_long total;
         glp_mem_usage(NULL, NULL, &total, NULL);
         xprintf("Time used: %.1f secs.  Memory used: %.1f Mb.\n",
            xdifftime(xtime(), T->tm_beg), xltod(total) / 1048576.0);
         ttt = xtime();
      }
#else
      {  size_t total;
         glp_mem_usage(NULL, NULL, &total, NULL);
         xprintf("Time used: %.1f secs.  Memory used: %.1f Mb.\n",
            xdifftime(xtime(), T->tm_beg), (double)total / 1048576.0);
         ttt = xtime();
      }
#endif
      /* check the mip gap */
      if (T->parm->mip_gap > 0.0 &&
          ios_relative_gap(T) <= T->parm->mip_gap)
      {  if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("Relative gap tolerance reached; search terminated "
               "\n");
         ret = GLP_EMIPGAP;
         goto done;
      }
      /* check if the time limit has been exhausted */
      if (T->parm->tm_lim < INT_MAX &&
         (double)(T->parm->tm_lim - 1) <=
         1000.0 * xdifftime(xtime(), T->tm_beg))
      {  if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("Time limit exhausted; search terminated\n");
         ret = GLP_ETMLIM;
         goto done;
      }
      /* let the application program preprocess the subproblem */
      if (T->parm->cb_func != NULL)
      {  xassert(T->reason == 0);
         T->reason = GLP_IPREPRO;
         T->parm->cb_func(T, T->parm->cb_info);
         T->reason = 0;
         if (T->stop)
         {  ret = GLP_ESTOP;
            goto done;
         }
      }
      /* perform basic preprocessing */
      if (T->parm->pp_tech == GLP_PP_NONE)
         ;
      else if (T->parm->pp_tech == GLP_PP_ROOT)
#if 0 /* 27/II-2016 by Chris */
      {  if (T->curr->level == 0)
#else
      {  if (!root_done)
#endif
         {  if (ios_preprocess_node(T, 100))
               goto fath;
         }
      }
      else if (T->parm->pp_tech == GLP_PP_ALL)
#if 0 /* 27/II-2016 by Chris */
      {  if (ios_preprocess_node(T, T->curr->level == 0 ? 100 : 10))
#else
      {  if (ios_preprocess_node(T, !root_done ? 100 : 10))
#endif
            goto fath;
      }
      else
         xassert(T != T);
      /* preprocessing may improve the global bound */
      if (!is_branch_hopeful(T, p))
      {  xprintf("*** not tested yet ***\n");
         goto fath;
      }
      /* solve LP relaxation of the current subproblem */
      if (T->parm->msg_lev >= GLP_MSG_DBG)
         xprintf("Solving LP relaxation...\n");
      ret = ios_solve_node(T);
      if (ret == GLP_ETMLIM)
         goto done;
      else if (!(ret == 0 || ret == GLP_EOBJLL || ret == GLP_EOBJUL))
      {  if (T->parm->msg_lev >= GLP_MSG_ERR)
            xprintf("ios_driver: unable to solve current LP relaxation;"
               " glp_simplex returned %d\n", ret);
         ret = GLP_EFAIL;
         goto done;
      }
      /* analyze status of the basic solution to LP relaxation found */
      p_stat = T->mip->pbs_stat;
      d_stat = T->mip->dbs_stat;
      if (p_stat == GLP_FEAS && d_stat == GLP_FEAS)
      {  /* LP relaxation has optimal solution */
         if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("Found optimal solution to LP relaxation\n");
      }
      else if (d_stat == GLP_NOFEAS)
      {  /* LP relaxation has no dual feasible solution */
         /* since the current subproblem cannot have a larger feasible
            region than its parent, there is something wrong */
         if (T->parm->msg_lev >= GLP_MSG_ERR)
            xprintf("ios_driver: current LP relaxation has no dual feas"
               "ible solution\n");
         ret = GLP_EFAIL;
         goto done;
      }
      else if (p_stat == GLP_INFEAS && d_stat == GLP_FEAS)
      {  /* LP relaxation has no primal solution which is better than
            the incumbent objective value */
         xassert(T->mip->mip_stat == GLP_FEAS);
         if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("LP relaxation has no solution better than incumben"
               "t objective value\n");
         /* prune the branch */
         goto fath;
      }
      else if (p_stat == GLP_NOFEAS)
      {  /* LP relaxation has no primal feasible solution */
         if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("LP relaxation has no feasible solution\n");
         /* prune the branch */
         goto fath;
      }
      else
      {  /* other cases cannot appear */
         xassert(T->mip != T->mip);
      }
      /* at this point basic solution to LP relaxation of the current
         subproblem is optimal */
      xassert(p_stat == GLP_FEAS && d_stat == GLP_FEAS);
      xassert(T->curr != NULL);
      T->curr->lp_obj = T->mip->obj_val;
      /* thus, it defines a local bound to integer optimal solution of
         the current subproblem */
      {  double bound = T->mip->obj_val;
         /* some local bound to the current subproblem could be already
            set before, so we should only improve it */
         bound = ios_round_bound(T, bound);
         if (T->mip->dir == GLP_MIN)
         {  if (T->curr->bound < bound)
               T->curr->bound = bound;
         }
         else if (T->mip->dir == GLP_MAX)
         {  if (T->curr->bound > bound)
               T->curr->bound = bound;
         }
         else
            xassert(T->mip != T->mip);
         if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("Local bound is %.9e\n", bound);
      }
      /* if the local bound indicates that integer optimal solution of
         the current subproblem cannot be better than the global bound,
         prune the branch */
      if (!is_branch_hopeful(T, p))
      {  if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("Current branch is hopeless and can be pruned\n");
         goto fath;
      }
      /* let the application program generate additional rows ("lazy"
         constraints) */
      xassert(T->reopt == 0);
      xassert(T->reinv == 0);
      if (T->parm->cb_func != NULL)
      {  xassert(T->reason == 0);
         T->reason = GLP_IROWGEN;
         T->parm->cb_func(T, T->parm->cb_info);
         T->reason = 0;
         if (T->stop)
         {  ret = GLP_ESTOP;
            goto done;
         }
         if (T->reopt)
         {  /* some rows were added; re-optimization is needed */
            T->reopt = T->reinv = 0;
            goto more;
         }
         if (T->reinv)
         {  /* no rows were added, however, some inactive rows were
               removed */
            T->reinv = 0;
            xassert(glp_factorize(T->mip) == 0);
         }
      }
      /* check if the basic solution is integer feasible */
      check_integrality(T);
      /* if the basic solution satisfies to all integrality conditions,
         it is a new, better integer feasible solution */
      if (T->curr->ii_cnt == 0)
      {  if (T->parm->msg_lev >= GLP_MSG_DBG)
            xprintf("New integer feasible solution found\n");
         if (T->parm->msg_lev >= GLP_MSG_ALL)
            display_cut_info(T);
         record_solution(T);
         if (T->parm->msg_lev >= GLP_MSG_ON)
            show_progress(T, 1);
#if 1 /* 11/VII-2013 */
         ios_process_sol(T);
#endif
         /* make the application program happy */
         if (T->parm->cb_func != NULL)
         {  xassert(T->reason == 0);
            T->reason = GLP_IBINGO;
            T->parm->cb_func(T, T->parm->cb_info);
            T->reason = 0;
            if (T->stop)
            {  ret = GLP_ESTOP;
               goto done;
            }
         }
         /* since the current subproblem has been fathomed, prune its
            branch */
         goto fath;
      }
      /* at this point basic solution to LP relaxation of the current
         subproblem is optimal, but integer infeasible */
      /* try to fix some non-basic structural variables of integer kind
         on their current bounds due to reduced costs */
      if (T->mip->mip_stat == GLP_FEAS)
         fix_by_red_cost(T);
      /* let the application program try to find some solution to the
         original MIP with a primal heuristic */
      if (T->parm->cb_func != NULL)
      {  xassert(T->reason == 0);
         T->reason = GLP_IHEUR;
         T->parm->cb_func(T, T->parm->cb_info);
         T->reason = 0;
         if (T->stop)
         {  ret = GLP_ESTOP;
            goto done;
         }
         /* check if the current branch became hopeless */
         if (!is_branch_hopeful(T, p))
         {  if (T->parm->msg_lev >= GLP_MSG_DBG)
               xprintf("Current branch became hopeless and can be prune"
                  "d\n");
            goto fath;
         }
      }
      /* try to find solution with the feasibility pump heuristic */
#if 0 /* 27/II-2016 by Chris */
      if (T->parm->fp_heur)
#else
      if (T->parm->fp_heur && !root_done)
#endif
      {  xassert(T->reason == 0);
         T->reason = GLP_IHEUR;
         ios_feas_pump(T);
         T->reason = 0;
         /* check if the current branch became hopeless */
         if (!is_branch_hopeful(T, p))
         {  if (T->parm->msg_lev >= GLP_MSG_DBG)
               xprintf("Current branch became hopeless and can be prune"
                  "d\n");
            goto fath;
         }
      }
#if 1 /* 25/V-2013 */
      /* try to find solution with the proximity search heuristic */
#if 0 /* 27/II-2016 by Chris */
      if (T->parm->ps_heur)
#else
      if (T->parm->ps_heur && !root_done)
#endif
      {  xassert(T->reason == 0);
         T->reason = GLP_IHEUR;
         ios_proxy_heur(T);
         T->reason = 0;
         /* check if the current branch became hopeless */
         if (!is_branch_hopeful(T, p))
         {  if (T->parm->msg_lev >= GLP_MSG_DBG)
               xprintf("Current branch became hopeless and can be prune"
                  "d\n");
            goto fath;
         }
      }
#endif
#if 1 /* 24/X-2015 */
      /* try to find solution with a simple rounding heuristic */
      if (T->parm->sr_heur)
      {  xassert(T->reason == 0);
         T->reason = GLP_IHEUR;
         round_heur(T);
         T->reason = 0;
         /* check if the current branch became hopeless */
         if (!is_branch_hopeful(T, p))
         {  if (T->parm->msg_lev >= GLP_MSG_DBG)
               xprintf("Current branch became hopeless and can be prune"
                  "d\n");
            goto fath;
         }
      }
#endif
      /* it's time to generate cutting planes */
      xassert(T->local != NULL);
#ifdef NEW_LOCAL /* 02/II-2018 */
      xassert(T->local->m == 0);
#else
      xassert(T->local->size == 0);
#endif
      /* let the application program generate some cuts; note that it
         can add cuts either to the local cut pool or directly to the
         current subproblem */
      if (T->parm->cb_func != NULL)
      {  xassert(T->reason == 0);
         T->reason = GLP_ICUTGEN;
         T->parm->cb_func(T, T->parm->cb_info);
         T->reason = 0;
         if (T->stop)
         {  ret = GLP_ESTOP;
            goto done;
         }
      }
#if 1 /* 18/VII-2013 */
      if (T->curr->changed > 0)
      {  double degrad = fabs(T->curr->lp_obj - old_obj);
         if (degrad < 1e-4 * (1.0 + fabs(old_obj)))
            bad_cut++;
         else
            bad_cut = 0;
      }
      old_obj = T->curr->lp_obj;
#if 0 /* 27/II-2016 by Chris */
      if (bad_cut == 0 || (T->curr->level == 0 && bad_cut <= 3))
#else
      if (bad_cut == 0 || (!root_done && bad_cut <= 3))
#endif
#endif
      /* try to generate generic cuts with built-in generators
         (as suggested by Prof. Fischetti et al. the built-in cuts are
         not generated at each branching node; an intense attempt of
         generating new cuts is only made at the root node, and then
         a moderate effort is spent after each backtracking step) */
#if 0 /* 27/II-2016 by Chris */
      if (T->curr->level == 0 || pred_p == 0)
#else
      if (!root_done || pred_p == 0)
#endif
      {  xassert(T->reason == 0);
         T->reason = GLP_ICUTGEN;
         generate_cuts(T);
         T->reason = 0;
      }
      /* if the local cut pool is not empty, select useful cuts and add
         them to the current subproblem */
#ifdef NEW_LOCAL /* 02/II-2018 */
      if (T->local->m > 0)
#else
      if (T->local->size > 0)
#endif
      {  xassert(T->reason == 0);
         T->reason = GLP_ICUTGEN;
         ios_process_cuts(T);
         T->reason = 0;
      }
      /* clear the local cut pool */
      ios_clear_pool(T, T->local);
      /* perform re-optimization, if necessary */
      if (T->reopt)
      {  T->reopt = 0;
         T->curr->changed++;
         goto more;
      }
      /* no cuts were generated; remove inactive cuts */
      remove_cuts(T);
#if 0 /* 27/II-2016 by Chris */
      if (T->parm->msg_lev >= GLP_MSG_ALL && T->curr->level == 0)
#else
      if (T->parm->msg_lev >= GLP_MSG_ALL && !root_done)
#endif
         display_cut_info(T);
#if 1 /* 27/II-2016 by Chris */
      /* the first node will not be treated as root any more */
      if (!root_done) root_done = 1;
#endif
      /* update history information used on pseudocost branching */
      if (T->pcost != NULL) ios_pcost_update(T);
      /* it's time to perform branching */
      xassert(T->br_var == 0);
      xassert(T->br_sel == 0);
      /* let the application program choose variable to branch on */
      if (T->parm->cb_func != NULL)
      {  xassert(T->reason == 0);
         xassert(T->br_var == 0);
         xassert(T->br_sel == 0);
         T->reason = GLP_IBRANCH;
         T->parm->cb_func(T, T->parm->cb_info);
         T->reason = 0;
         if (T->stop)
         {  ret = GLP_ESTOP;
            goto done;
         }
      }
      /* if nothing has been chosen, choose some variable as specified
         by the branching technique option */
      if (T->br_var == 0)
         T->br_var = ios_choose_var(T, &T->br_sel);
      /* perform actual branching */
      curr_p = T->curr->p;
      ret = branch_on(T, T->br_var, T->br_sel);
      T->br_var = T->br_sel = 0;
      if (ret == 0)
      {  /* both branches have been created */
         pred_p = curr_p;
         goto loop;
      }
      else if (ret == 1)
      {  /* one branch is hopeless and has been pruned, so now the
            current subproblem is other branch */
         /* the current subproblem should be considered as a new one,
            since one bound of the branching variable was changed */
         T->curr->solved = T->curr->changed = 0;
#if 1 /* 18/VII-2013 */
         /* bad_cut = 0; */
#endif
         goto more;
      }
      else if (ret == 2)
      {  /* both branches are hopeless and have been pruned; new
            subproblem selection is needed to continue the search */
         goto fath;
      }
      else
         xassert(ret != ret);
fath: /* the current subproblem has been fathomed */
      if (T->parm->msg_lev >= GLP_MSG_DBG)
         xprintf("Node %d fathomed\n", p);
      /* freeze the current subproblem */
      ios_freeze_node(T);
      /* and prune the corresponding branch of the tree */
      ios_delete_node(T, p);
      /* if a new integer feasible solution has just been found, other
         branches may become hopeless and therefore must be pruned */
      if (T->mip->mip_stat == GLP_FEAS) cleanup_the_tree(T);
      /* new subproblem selection is needed due to backtracking */
      pred_p = 0;
      goto loop;
done: /* display progress of the search on exit from the solver */
      if (T->parm->msg_lev >= GLP_MSG_ON)
         show_progress(T, 0);
      if (T->mir_gen != NULL)
#if 0 /* 06/III-2016 */
         ios_mir_term(T->mir_gen), T->mir_gen = NULL;
#else
         glp_mir_free(T->mir_gen), T->mir_gen = NULL;
#endif
#ifdef NEW_COVER /* 13/II-2018 */
      if (T->cov_gen != NULL)
         glp_cov_free(T->cov_gen), T->cov_gen = NULL;
#endif
      if (T->clq_gen != NULL)
#if 0 /* 08/III-2016 */
         ios_clq_term(T->clq_gen), T->clq_gen = NULL;
#else
         glp_cfg_free(T->clq_gen), T->clq_gen = NULL;
#endif
      /* return to the calling program */
      return ret;
}

/* eof */