40 #if (_MSC_VER == 1500 || _MSC_VER == 1600)
41 #define NO_INCLUDE_FENV
47 #define NO_INCLUDE_FENV
49 #ifdef NO_INCLUDE_FENV
50 #pragma message("Do not use fenv header.")
52 #pragma message("Use fenv header. If there is a warning about unknown #pragma STDC FENV_ACCESS, this can be ignored.")
53 #pragma STDC FENV_ACCESS on
65 #error The constant DBL_MANT_DIG could not be defined.
67 #define T_FLOAt_MANT_DIG DBL_MANT_DIG
73 #error The constant LONG_MAX could not be defined.
76 #error The constant INT_MAX could not be defined.
82 #define __STDC_LIMIT_MACROS
85 typedef __int32 int_fast32_t;
86 typedef __int64 int64_t;
89 #define __STDC_LIMIT_MACROS
94 #define FILL_N std::fill_n
98 #define FILL_N stdext::unchecked_fill_n
104 #pragma warning(disable : 4700)
107 #ifndef HAVE_DIAGNOSTIC
108 #if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6))
109 #define HAVE_DIAGNOSTIC 1
113 #ifndef HAVE_VISIBILITY
115 #define HAVE_VISIBILITY 1
126 #pragma GCC visibility push(hidden)
131 #define MAX_INDEX 0x7fffffffL
133 #define MAX_INDEX INT32_MAX
135 #if (LONG_MAX < MAX_INDEX)
136 #error The integer format "t_index" must not have a greater range than "long int".
138 #if (INT_MAX > MAX_INDEX)
139 #error The integer format "int" must not have a greater range than "t_index".
176 template <
typename type>
184 template <
typename index>
188 template <
typename index,
typename value>
199 template <
typename index>
202 ptr =
new type[size];
204 template <
typename index,
typename value>
205 void init(index
const size, value
const val)
210 inline operator type *()
const {
return ptr; }
233 Z[pos].node1 = node1;
234 Z[pos].node2 = node2;
246 for (
node *ZZ = Z; ZZ != Z + pos; ++ZZ) {
247 ZZ->dist = std::sqrt(ZZ->dist);
258 for (
node *ZZ = Z; ZZ != Z + pos; ++ZZ) {
259 ZZ->dist = std::sqrt(2 * ZZ->dist);
266 #define my_pow std::pow
272 for (
node *ZZ = Z; ZZ != Z + pos; ++ZZ) {
273 ZZ->dist =
my_pow(ZZ->dist, q);
279 for (
node *ZZ = Z; ZZ != Z + pos; ++ZZ) {
286 for (
node *ZZ = Z; ZZ != Z + pos; ++ZZ) {
313 :
start(0),
succ(size + 1), pred(size + 1)
315 for (
t_index i = 0; i < size; ++i) {
332 pred[
succ[idx]] = pred[idx];
343 #define D_(r_, c_) (D[(static_cast<std::ptrdiff_t>(2 * N - 3 - (r_)) * (r_) >> 1) + (c_)-1])
345 #define Z_(_r, _c) (Z[(_r)*4 + (_c)])
366 if (parent[idx] != 0) {
369 if (parent[idx] != 0) {
372 }
while (parent[idx] != 0);
377 }
while (parent[p] != idx);
415 min = std::numeric_limits<t_float>::infinity();
416 for (i = 1; i < N; ++i) {
419 #pragma GCC diagnostic push
420 #pragma GCC diagnostic ignored "-Wfloat-equal"
428 #pragma GCC diagnostic pop
433 for (
t_index j = 1; j < N - 1; ++j) {
435 active_nodes.remove(prev_node);
437 idx2 = active_nodes.succ[0];
439 for (i = idx2; i < prev_node; i = active_nodes.succ[i]) {
442 #pragma GCC diagnostic push
443 #pragma GCC diagnostic ignored "-Wfloat-equal"
450 #pragma GCC diagnostic pop
457 for (; i < N; i = active_nodes.succ[i]) {
460 #pragma GCC diagnostic push
461 #pragma GCC diagnostic ignored "-Wfloat-equal"
468 #pragma GCC diagnostic pop
475 Z2.
append(prev_node, idx2, min);
493 *b = s * a + t * (*b);
496 #pragma GCC diagnostic push
497 #pragma GCC diagnostic ignored "-Wfloat-equal"
503 #pragma GCC diagnostic pop
512 #pragma GCC diagnostic push
513 #pragma GCC diagnostic ignored "-Wfloat-equal"
519 #pragma GCC diagnostic pop
526 *b = ((v + s) * a - v *
c + (v + t) * (*b)) / (s + t + v);
530 #pragma GCC diagnostic push
531 #pragma GCC diagnostic ignored "-Wfloat-equal"
537 #pragma GCC diagnostic pop
543 *b = s * a - stc + t * (*b);
549 #pragma GCC diagnostic pop
555 *b = (a + (*b)) * .5 - c_4;
558 #pragma GCC diagnostic push
559 #pragma GCC diagnostic ignored "-Wfloat-equal"
565 #pragma GCC diagnostic pop
570 template <method_codes method,
typename t_members>
595 for (
t_float const *DD = D; DD != D + (
static_cast<std::ptrdiff_t
>(N) * (N - 1) >> 1); ++DD) {
597 #pragma GCC diagnostic push
598 #pragma GCC diagnostic ignored "-Wfloat-equal"
604 #pragma GCC diagnostic pop
609 if (feclearexcept(FE_INVALID))
613 for (
t_index j = 0; j < N - 1; ++j) {
614 if (NN_chain_tip <= 3) {
615 NN_chain[0] = idx1 = active_nodes.start;
618 idx2 = active_nodes.succ[idx1];
619 min =
D_(idx1, idx2);
620 for (i = active_nodes.succ[idx2]; i < N; i = active_nodes.succ[i]) {
621 if (
D_(idx1, i) < min) {
629 idx1 = NN_chain[NN_chain_tip - 1];
630 idx2 = NN_chain[NN_chain_tip];
631 min = idx1 < idx2 ?
D_(idx1, idx2) :
D_(idx2, idx1);
635 NN_chain[NN_chain_tip] = idx2;
637 for (i = active_nodes.start; i < idx2; i = active_nodes.succ[i]) {
638 if (
D_(i, idx2) < min) {
643 for (i = active_nodes.succ[idx2]; i < N; i = active_nodes.succ[i]) {
644 if (
D_(idx2, i) < min) {
651 idx1 = NN_chain[NN_chain_tip++];
653 }
while (idx2 != NN_chain[NN_chain_tip - 2]);
655 Z2.
append(idx1, idx2, min);
664 size1 =
static_cast<t_float>(members[idx1]);
665 size2 =
static_cast<t_float>(members[idx2]);
666 members[idx2] += members[idx1];
670 active_nodes.remove(idx1);
680 for (i = active_nodes.start; i < idx1; i = active_nodes.succ[i])
681 f_single(&
D_(i, idx2),
D_(i, idx1));
683 for (; i < idx2; i = active_nodes.succ[i])
684 f_single(&
D_(i, idx2),
D_(idx1, i));
686 for (i = active_nodes.succ[idx2]; i < N; i = active_nodes.succ[i])
687 f_single(&
D_(idx2, i),
D_(idx1, i));
697 for (i = active_nodes.start; i < idx1; i = active_nodes.succ[i])
698 f_complete(&
D_(i, idx2),
D_(i, idx1));
700 for (; i < idx2; i = active_nodes.succ[i])
701 f_complete(&
D_(i, idx2),
D_(idx1, i));
703 for (i = active_nodes.succ[idx2]; i < N; i = active_nodes.succ[i])
704 f_complete(&
D_(idx2, i),
D_(idx1, i));
714 t_float s = size1 / (size1 + size2);
715 t_float t = size2 / (size1 + size2);
716 for (i = active_nodes.start; i < idx1; i = active_nodes.succ[i])
717 f_average(&
D_(i, idx2),
D_(i, idx1), s, t);
719 for (; i < idx2; i = active_nodes.succ[i])
720 f_average(&
D_(i, idx2),
D_(idx1, i), s, t);
722 for (i = active_nodes.succ[idx2]; i < N; i = active_nodes.succ[i])
723 f_average(&
D_(idx2, i),
D_(idx1, i), s, t);
734 for (i = active_nodes.start; i < idx1; i = active_nodes.succ[i])
735 f_weighted(&
D_(i, idx2),
D_(i, idx1));
737 for (; i < idx2; i = active_nodes.succ[i])
738 f_weighted(&
D_(i, idx2),
D_(idx1, i));
740 for (i = active_nodes.succ[idx2]; i < N; i = active_nodes.succ[i])
741 f_weighted(&
D_(idx2, i),
D_(idx1, i));
753 for (i = active_nodes.start; i < idx1; i = active_nodes.succ[i])
754 f_ward(&
D_(i, idx2),
D_(i, idx1), min, size1, size2,
static_cast<t_float>(members[i]));
756 for (; i < idx2; i = active_nodes.succ[i])
757 f_ward(&
D_(i, idx2),
D_(idx1, i), min, size1, size2,
static_cast<t_float>(members[i]));
759 for (i = active_nodes.succ[idx2]; i < N; i = active_nodes.succ[i])
760 f_ward(&
D_(idx2, i),
D_(idx1, i), min, size1, size2,
static_cast<t_float>(members[i]));
763 default:
throw std::runtime_error(std::string(
"Invalid method."));
767 if (fetestexcept(FE_INVALID))
810 for (
t_index i = 0; i < size; ++i)
815 : A(A_), size(size1), I(size1), R(size2)
818 for (
t_index i = 0; i < size; ++i) {
835 for (idx = (size >> 1); idx > 0;) {
862 if (H(size) <= A[idx]) {
871 R[idxnew] = R[idxold];
872 I[R[idxnew]] = idxnew;
873 if (val <= A[idxold])
904 void update_leq_(
t_index i)
const
907 for (; (i > 0) && (H(i) < H(j = (i - 1) >> 1)); i = j)
911 void update_geq_(
t_index i)
const
914 for (; (j = 2 * i + 1) < size; i = j) {
917 if (j >= size || H(j) >= H(i))
919 }
else if (j + 1 < size && H(j + 1) < H(j))
938 template <method_codes method,
typename t_members>
964 for (i = 0; i < N; ++i)
973 for (i = 0; i < N_1; ++i) {
974 min = std::numeric_limits<t_float>::infinity();
975 for (idx = j = i + 1; j < N; ++j, ++DD) {
977 #pragma GCC diagnostic push
978 #pragma GCC diagnostic ignored "-Wfloat-equal"
987 #pragma GCC diagnostic pop
995 nn_distances.heapify();
998 if (feclearexcept(FE_INVALID))
1003 for (i = 0; i < N_1; ++i) {
1037 idx1 = nn_distances.argmin();
1039 while (mindist[idx1] <
D_(idx1, n_nghbr[idx1])) {
1041 n_nghbr[idx1] = j = active_nodes.succ[idx1];
1043 for (j = active_nodes.succ[j]; j < N; j = active_nodes.succ[j]) {
1044 if (
D_(idx1, j) < min) {
1051 nn_distances.update_geq(idx1, min);
1052 idx1 = nn_distances.argmin();
1056 nn_distances.heap_pop();
1057 idx2 = n_nghbr[idx1];
1060 node1 = row_repr[idx1];
1061 node2 = row_repr[idx2];
1064 size1 =
static_cast<t_float>(members[idx1]);
1065 size2 =
static_cast<t_float>(members[idx2]);
1066 members[idx2] += members[idx1];
1068 Z2.
append(node1, node2, mindist[idx1]);
1071 active_nodes.remove(idx1);
1073 row_repr[idx2] = N + i;
1084 for (j = active_nodes.start; j < idx1; j = active_nodes.succ[j]) {
1085 f_single(&
D_(j, idx2),
D_(j, idx1));
1086 if (n_nghbr[j] == idx1)
1090 for (; j < idx2; j = active_nodes.succ[j]) {
1091 f_single(&
D_(j, idx2),
D_(idx1, j));
1094 if (
D_(j, idx2) < mindist[j]) {
1095 nn_distances.update_leq(j,
D_(j, idx2));
1102 min = mindist[idx2];
1103 for (j = active_nodes.succ[idx2]; j < N; j = active_nodes.succ[j]) {
1104 f_single(&
D_(idx2, j),
D_(idx1, j));
1105 if (
D_(idx2, j) < min) {
1110 nn_distances.update_leq(idx2, min);
1121 for (j = active_nodes.start; j < idx1; j = active_nodes.succ[j]) {
1122 f_complete(&
D_(j, idx2),
D_(j, idx1));
1123 if (n_nghbr[j] == idx1)
1127 for (; j < idx2; j = active_nodes.succ[j])
1128 f_complete(&
D_(j, idx2),
D_(idx1, j));
1130 for (j = active_nodes.succ[idx2]; j < N; j = active_nodes.succ[j])
1131 f_complete(&
D_(idx2, j),
D_(idx1, j));
1141 t_float s = size1 / (size1 + size2);
1142 t_float t = size2 / (size1 + size2);
1143 for (j = active_nodes.start; j < idx1; j = active_nodes.succ[j]) {
1144 f_average(&
D_(j, idx2),
D_(j, idx1), s, t);
1145 if (n_nghbr[j] == idx1)
1149 for (; j < idx2; j = active_nodes.succ[j]) {
1150 f_average(&
D_(j, idx2),
D_(idx1, j), s, t);
1151 if (
D_(j, idx2) < mindist[j]) {
1152 nn_distances.update_leq(j,
D_(j, idx2));
1158 n_nghbr[idx2] = j = active_nodes.succ[idx2];
1159 f_average(&
D_(idx2, j),
D_(idx1, j), s, t);
1161 for (j = active_nodes.succ[j]; j < N; j = active_nodes.succ[j]) {
1162 f_average(&
D_(idx2, j),
D_(idx1, j), s, t);
1163 if (
D_(idx2, j) < min) {
1168 nn_distances.update(idx2, min);
1180 for (j = active_nodes.start; j < idx1; j = active_nodes.succ[j]) {
1181 f_weighted(&
D_(j, idx2),
D_(j, idx1));
1182 if (n_nghbr[j] == idx1)
1186 for (; j < idx2; j = active_nodes.succ[j]) {
1187 f_weighted(&
D_(j, idx2),
D_(idx1, j));
1188 if (
D_(j, idx2) < mindist[j]) {
1189 nn_distances.update_leq(j,
D_(j, idx2));
1195 n_nghbr[idx2] = j = active_nodes.succ[idx2];
1196 f_weighted(&
D_(idx2, j),
D_(idx1, j));
1198 for (j = active_nodes.succ[j]; j < N; j = active_nodes.succ[j]) {
1199 f_weighted(&
D_(idx2, j),
D_(idx1, j));
1200 if (
D_(idx2, j) < min) {
1205 nn_distances.update(idx2, min);
1217 for (j = active_nodes.start; j < idx1; j = active_nodes.succ[j]) {
1218 f_ward(&
D_(j, idx2),
D_(j, idx1), mindist[idx1], size1, size2,
static_cast<t_float>(members[j]));
1219 if (n_nghbr[j] == idx1)
1223 for (; j < idx2; j = active_nodes.succ[j]) {
1224 f_ward(&
D_(j, idx2),
D_(idx1, j), mindist[idx1], size1, size2,
static_cast<t_float>(members[j]));
1225 if (
D_(j, idx2) < mindist[j]) {
1226 nn_distances.update_leq(j,
D_(j, idx2));
1232 n_nghbr[idx2] = j = active_nodes.succ[idx2];
1233 f_ward(&
D_(idx2, j),
D_(idx1, j), mindist[idx1], size1, size2,
static_cast<t_float>(members[j]));
1235 for (j = active_nodes.succ[j]; j < N; j = active_nodes.succ[j]) {
1236 f_ward(&
D_(idx2, j),
D_(idx1, j), mindist[idx1], size1, size2,
static_cast<t_float>(members[j]));
1237 if (
D_(idx2, j) < min) {
1242 nn_distances.update(idx2, min);
1254 t_float s = size1 / (size1 + size2);
1255 t_float t = size2 / (size1 + size2);
1256 t_float stc = s * t * mindist[idx1];
1257 for (j = active_nodes.start; j < idx1; j = active_nodes.succ[j]) {
1258 f_centroid(&
D_(j, idx2),
D_(j, idx1), stc, s, t);
1259 if (
D_(j, idx2) < mindist[j]) {
1260 nn_distances.update_leq(j,
D_(j, idx2));
1262 }
else if (n_nghbr[j] == idx1)
1266 for (; j < idx2; j = active_nodes.succ[j]) {
1267 f_centroid(&
D_(j, idx2),
D_(idx1, j), stc, s, t);
1268 if (
D_(j, idx2) < mindist[j]) {
1269 nn_distances.update_leq(j,
D_(j, idx2));
1275 n_nghbr[idx2] = j = active_nodes.succ[idx2];
1276 f_centroid(&
D_(idx2, j),
D_(idx1, j), stc, s, t);
1278 for (j = active_nodes.succ[j]; j < N; j = active_nodes.succ[j]) {
1279 f_centroid(&
D_(idx2, j),
D_(idx1, j), stc, s, t);
1280 if (
D_(idx2, j) < min) {
1285 nn_distances.update(idx2, min);
1298 t_float c_4 = mindist[idx1] * .25;
1299 for (j = active_nodes.start; j < idx1; j = active_nodes.succ[j]) {
1300 f_median(&
D_(j, idx2),
D_(j, idx1), c_4);
1301 if (
D_(j, idx2) < mindist[j]) {
1302 nn_distances.update_leq(j,
D_(j, idx2));
1304 }
else if (n_nghbr[j] == idx1)
1308 for (; j < idx2; j = active_nodes.succ[j]) {
1309 f_median(&
D_(j, idx2),
D_(idx1, j), c_4);
1310 if (
D_(j, idx2) < mindist[j]) {
1311 nn_distances.update_leq(j,
D_(j, idx2));
1317 n_nghbr[idx2] = j = active_nodes.succ[idx2];
1318 f_median(&
D_(idx2, j),
D_(idx1, j), c_4);
1320 for (j = active_nodes.succ[j]; j < N; j = active_nodes.succ[j]) {
1321 f_median(&
D_(idx2, j),
D_(idx1, j), c_4);
1322 if (
D_(idx2, j) < min) {
1327 nn_distances.update(idx2, min);
1332 default:
throw std::runtime_error(std::string(
"Invalid method."));
1336 if (fetestexcept(FE_INVALID))
1345 template <
typename t_dissimilarity>
1368 min = std::numeric_limits<t_float>::infinity();
1369 for (i = 1; i < N; ++i) {
1372 #pragma GCC diagnostic push
1373 #pragma GCC diagnostic ignored "-Wfloat-equal"
1381 #pragma GCC diagnostic pop
1387 for (
t_index j = 1; j < N - 1; ++j) {
1389 active_nodes.remove(prev_node);
1391 idx2 = active_nodes.succ[0];
1394 for (i = idx2; i < N; i = active_nodes.succ[i]) {
1395 t_float tmp = dist(i, prev_node);
1397 #pragma GCC diagnostic push
1398 #pragma GCC diagnostic ignored "-Wfloat-equal"
1405 #pragma GCC diagnostic pop
1412 Z2.
append(prev_node, idx2, min);
1416 template <method_codes_vector method,
typename t_dissimilarity>
1441 for (i = 0; i < N; ++i)
1449 for (i = 0; i < N_1; ++i) {
1450 min = std::numeric_limits<t_float>::infinity();
1452 for (idx = j = i + 1; j < N; ++j) {
1456 default: tmp = dist.template sqeuclidean<true>(i, j);
1464 case METHOD_VECTOR_WARD: mindist[i] = t_dissimilarity::ward_initial_conversion(min);
break;
1465 default: mindist[i] = min;
1472 nn_distances.heapify();
1475 for (i = 0; i < N_1; ++i) {
1476 idx1 = nn_distances.argmin();
1478 while (active_nodes.is_inactive(n_nghbr[idx1])) {
1480 n_nghbr[idx1] = j = active_nodes.succ[idx1];
1483 min = dist.ward(idx1, j);
1484 for (j = active_nodes.succ[j]; j < N; j = active_nodes.succ[j]) {
1485 t_float const tmp = dist.ward(idx1, j);
1493 min = dist.template sqeuclidean<true>(idx1, j);
1494 for (j = active_nodes.succ[j]; j < N; j = active_nodes.succ[j]) {
1495 t_float const tmp = dist.template sqeuclidean<true>(idx1, j);
1504 nn_distances.update_geq(idx1, min);
1505 idx1 = nn_distances.argmin();
1508 nn_distances.heap_pop();
1509 idx2 = n_nghbr[idx1];
1512 node1 = row_repr[idx1];
1513 node2 = row_repr[idx2];
1515 Z2.
append(node1, node2, mindist[idx1]);
1521 default:
throw std::runtime_error(std::string(
"Invalid method."));
1525 row_repr[idx2] = N + i;
1527 active_nodes.remove(idx1);
1539 for (j = active_nodes.start; j < idx1; j = active_nodes.succ[j]) {
1540 if (n_nghbr[j] == idx2) {
1545 for (; j < idx2; j = active_nodes.succ[j]) {
1546 t_float const tmp = dist.ward(j, idx2);
1547 if (tmp < mindist[j]) {
1548 nn_distances.update_leq(j, tmp);
1550 }
else if (n_nghbr[j] == idx2) {
1556 n_nghbr[idx2] = j = active_nodes.succ[idx2];
1557 min = dist.ward(idx2, j);
1558 for (j = active_nodes.succ[j]; j < N; j = active_nodes.succ[j]) {
1559 t_float const tmp = dist.ward(idx2, j);
1565 nn_distances.update(idx2, min);
1576 for (j = active_nodes.start; j < idx2; j = active_nodes.succ[j]) {
1577 t_float const tmp = dist.template sqeuclidean<true>(j, idx2);
1578 if (tmp < mindist[j]) {
1579 nn_distances.update_leq(j, tmp);
1581 }
else if (n_nghbr[j] == idx2)
1586 n_nghbr[idx2] = j = active_nodes.succ[idx2];
1587 min = dist.template sqeuclidean<true>(idx2, j);
1588 for (j = active_nodes.succ[j]; j < N; j = active_nodes.succ[j]) {
1589 t_float const tmp = dist.template sqeuclidean<true>(idx2, j);
1595 nn_distances.update(idx2, min);
1601 template <method_codes_vector method,
typename t_dissimilarity>
1602 static void generic_linkage_vector_alternative(
const t_index N, t_dissimilarity &dist,
cluster_result &Z2)
1629 for (i = 1; i < N; ++i) {
1630 min = std::numeric_limits<t_float>::infinity();
1632 for (idx = j = 0; j < i; ++j) {
1636 default: tmp = dist.template sqeuclidean<true>(i, j);
1644 case METHOD_VECTOR_WARD: mindist[i] = t_dissimilarity::ward_initial_conversion(min);
break;
1645 default: mindist[i] = min;
1652 nn_distances.heapify();
1655 for (i = N; i < N + N_1; ++i) {
1675 idx1 = nn_distances.argmin();
1676 while (active_nodes.is_inactive(n_nghbr[idx1])) {
1678 n_nghbr[idx1] = j = active_nodes.start;
1681 min = dist.ward_extended(idx1, j);
1682 for (j = active_nodes.succ[j]; j < idx1; j = active_nodes.succ[j]) {
1683 t_float tmp = dist.ward_extended(idx1, j);
1691 min = dist.sqeuclidean_extended(idx1, j);
1692 for (j = active_nodes.succ[j]; j < idx1; j = active_nodes.succ[j]) {
1693 t_float const tmp = dist.sqeuclidean_extended(idx1, j);
1702 nn_distances.update_geq(idx1, min);
1703 idx1 = nn_distances.argmin();
1706 idx2 = n_nghbr[idx1];
1707 active_nodes.remove(idx1);
1708 active_nodes.remove(idx2);
1710 Z2.
append(idx1, idx2, mindist[idx1]);
1719 default:
throw std::runtime_error(std::string(
"Invalid method."));
1722 n_nghbr[i] = active_nodes.start;
1730 min = dist.ward_extended(active_nodes.start, i);
1731 for (j = active_nodes.succ[active_nodes.start]; j < i; j = active_nodes.succ[j]) {
1732 t_float tmp = dist.ward_extended(j, i);
1745 min = dist.sqeuclidean_extended(active_nodes.start, i);
1746 for (j = active_nodes.succ[active_nodes.start]; j < i; j = active_nodes.succ[j]) {
1747 t_float tmp = dist.sqeuclidean_extended(j, i);
1754 if (idx2 < active_nodes.start) {
1755 nn_distances.remove(active_nodes.start);
1757 nn_distances.remove(idx2);
1759 nn_distances.replace(idx1, i, min);
1765 #pragma GCC visibility pop