#include #include #ifdef HPCG_CUBIC_RADICAL_SEARCH #include #endif #include #include "ComputeOptimalShapeXYZ.hpp" #include "MixedBaseCounter.hpp" #ifdef HPCG_CUBIC_RADICAL_SEARCH static int min3(int a, int b, int c) { return std::min(a, std::min(b, c)); } static int max3(int a, int b, int c) { return std::max(a, std::max(b, c)); } static void cubic_radical_search(int n, int& x, int& y, int& z) { double best = 0.0; for (int f1 = (int) (pow(n, 1.0 / 3.0) + 0.5); f1 > 0; --f1) if (n % f1 == 0) { int n1 = n / f1; for (int f2 = (int) (pow(n1, 0.5) + 0.5); f2 > 0; --f2) if (n1 % f2 == 0) { int f3 = n1 / f2; double current = (double) min3(f1, f2, f3) / max3(f1, f2, f3); if (current > best) { best = current; x = f1; y = f2; z = f3; } } } } #else static void ComputePrimeFactors(int n, std::map& factors) { int d, sq = int((sqrt(double(n))) + 1L); div_t r; // remove 2 as a factor with shifts instead "/" and "%" for (; n > 1 && (n & 1) == 0; n >>= 1) { factors[2]++; } // keep removing subsequent odd numbers for (d = 3; d <= sq; d += 2) { while (1) { r = div(n, d); if (r.rem == 0) { factors[d]++; n = r.quot; continue; } break; } } if (n > 1 || factors.size() == 0) // left with a prime or x==1 factors[n]++; } static int pow_i(int x, int p) { int v; if (0 == x || 1 == x) return x; if (p < 0) return 0; for (v = 1; p; p >>= 1) { if (1 & p) v *= x; x *= x; } return v; } #endif void ComputeOptimalShapeXYZ(int xyz, int& x, int& y, int& z) { #ifdef HPCG_CUBIC_RADICAL_SEARCH cubic_radical_search(xyz, x, y, z); #else std::map factors; ComputePrimeFactors(xyz, factors); // factors are sorted: ascending order std::map::iterator iter = factors.begin(); // there is at least one prime factor x = (iter++)->first; // cache the first factor, move to the next one y = iter != factors.end() ? (iter++)->first : y; // try to cache the second factor in "y" if (factors.size() == 1) { // only a single factor z = pow_i(x, factors[x] / 3); y = pow_i(x, factors[x] / 3 + ((factors[x] % 3) >= 2 ? 1 : 0)); x = pow_i(x, factors[x] / 3 + ((factors[x] % 3) >= 1 ? 1 : 0)); } else if (factors.size() == 2 && factors[x] == 1 && factors[y] == 1) { // two distinct prime factors z = 1; } else if (factors.size() == 2 && factors[x] + factors[y] == 3) { // three prime factors, one repeated z = factors[x] == 2 ? x : y; // test which factor is repeated } else if (factors.size() == 3 && factors[x] == 1 && factors[y] == 1 && iter->second == 1) { // three distinct and single prime factors z = iter->first; } else { // 3 or more prime factors so try all possible 3-subsets int i, distinct_factors[32 + 1], count_factors[32 + 1]; i = 0; for (std::map::iterator iter = factors.begin(); iter != factors.end(); ++iter, ++i) { distinct_factors[i] = iter->first; count_factors[i] = iter->second; } // count total number of prime factors in "c_main" and distribute some factors into "c1" MixedBaseCounter c_main(count_factors, factors.size()), c1(count_factors, factors.size()); // at the beginning, minimum area is the maximum area double area, min_area = 2.0 * xyz + 1.0; for (c1.next(); !c1.is_zero(); c1.next()) { MixedBaseCounter c2(c_main, c1); // "c2" gets the factors remaining in "c_main" that "c1" doesn't have for (c2.next(); !c2.is_zero(); c2.next()) { int tf1 = c1.product(distinct_factors); int tf2 = c2.product(distinct_factors); int tf3 = xyz / tf1 / tf2; // we derive the third dimension, we don't keep track of the factors it has area = tf1 * double(tf2) + tf2 * double(tf3) + tf1 * double(tf3); if (area < min_area) { min_area = area; x = tf1; y = tf2; z = tf3; } } } } #endif }