<commit_before>/*
* Number Theory Functions
* (C) 1999-2011,2016 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/numthry.h>
#include <botan/pow_mod.h>
#include <botan/reducer.h>
#include <botan/internal/bit_ops.h>
#include <botan/internal/mp_core.h>
#include <botan/internal/ct_utils.h>
#include <algorithm>

namespace Botan {

/*
* Return the number of 0 bits at the end of n
*/
size_t low_zero_bits(const BigInt& n)
   {
   size_t low_zero = 0;

   if(n.is_positive() && n.is_nonzero())
      {
      for(size_t i = 0; i != n.size(); ++i)
         {
         const word x = n.word_at(i);

         if(x)
            {
            low_zero += ctz(x);
            break;
            }
         else
            low_zero += BOTAN_MP_WORD_BITS;
         }
      }

   return low_zero;
   }

/*
* Calculate the GCD
*/
BigInt gcd(const BigInt& a, const BigInt& b)
   {
   if(a.is_zero() || b.is_zero()) return 0;
   if(a == 1 || b == 1)           return 1;

   BigInt x = a, y = b;
   x.set_sign(BigInt::Positive);
   y.set_sign(BigInt::Positive);
   size_t shift = std::min(low_zero_bits(x), low_zero_bits(y));

   x >>= shift;
   y >>= shift;

   while(x.is_nonzero())
      {
      x >>= low_zero_bits(x);
      y >>= low_zero_bits(y);
      if(x >= y) { x -= y; x >>= 1; }
      else       { y -= x; y >>= 1; }
      }

   return (y << shift);
   }

/*
* Calculate the LCM
*/
BigInt lcm(const BigInt& a, const BigInt& b)
   {
   return ((a * b) / gcd(a, b));
   }

/*
Sets result to a^-1 * 2^k mod a
with n <= k <= 2n
Returns k

"The Montgomery Modular Inverse - Revisited" Çetin Koç, E. Savas
https://citeseerx.ist.psu.edu/viewdoc/citations?doi=10.1.1.75.8377

A const time implementation of this algorithm is described in
"Constant Time Modular Inversion" Joppe W. Bos
http://www.joppebos.com/files/CTInversion.pdf
*/
size_t almost_montgomery_inverse(BigInt& result,
                                 const BigInt& a,
                                 const BigInt& p)
   {
   size_t k = 0;

   BigInt u = p, v = a, r = 0, s = 1;

   while(v > 0)
      {
      if(u.is_even())
         {
         u >>= 1;
         s <<= 1;
         }
      else if(v.is_even())
         {
         v >>= 1;
         r <<= 1;
         }
      else if(u > v)
         {
         u -= v;
         u >>= 1;
         r += s;
         s <<= 1;
         }
      else
         {
         v -= u;
         v >>= 1;
         s += r;
         r <<= 1;
         }

      ++k;
      }

   if(r >= p)
      {
      r = r - p;
      }

   result = p - r;

   return k;
   }

BigInt normalized_montgomery_inverse(const BigInt& a, const BigInt& p)
   {
   BigInt r;
   size_t k = almost_montgomery_inverse(r, a, p);

   for(size_t i = 0; i != k; ++i)
      {
      if(r.is_odd())
         r += p;
      r >>= 1;
      }

   return r;
   }

BigInt ct_inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod)
   {
   if(n.is_negative() || mod.is_negative())
      throw Invalid_Argument("ct_inverse_mod_odd_modulus: arguments must be non-negative");
   if(mod < 3 || mod.is_even())
      throw Invalid_Argument("Bad modulus to ct_inverse_mod_odd_modulus");

   /*
   This uses a modular inversion algorithm designed by Niels Möller
   and implemented in Nettle. The same algorithm was later also
   adapted to GMP in mpn_sec_invert.

   It can be easily implemented in a way that does not depend on
   secret branches or memory lookups, providing resistance against
   some forms of side channel attack.

   There is also a description of the algorithm in Appendix 5 of "Fast
   Software Polynomial Multiplication on ARM Processors using the NEON Engine"
   by Danilo Câmara, Conrado P. L. Gouvêa, Julio López, and Ricardo
   Dahab in LNCS 8182
      https://conradoplg.cryptoland.net/files/2010/12/mocrysen13.pdf

   Thanks to Niels for creating the algorithm, explaining some things
   about it, and the reference to the paper.
   */

   // todo allow this to be pre-calculated and passed in as arg
   BigInt mp1o2 = (mod + 1) >> 1;

   const size_t mod_words = mod.sig_words();
   BOTAN_ASSERT(mod_words > 0, "Not empty");

   BigInt a = n;
   BigInt b = mod;
   BigInt u = 1, v = 0;

   a.grow_to(mod_words);
   u.grow_to(mod_words);
   v.grow_to(mod_words);
   mp1o2.grow_to(mod_words);

   secure_vector<word>& a_w = a.get_word_vector();
   secure_vector<word>& b_w = b.get_word_vector();
   secure_vector<word>& u_w = u.get_word_vector();
   secure_vector<word>& v_w = v.get_word_vector();

   CT::poison(a_w.data(), a_w.size());
   CT::poison(b_w.data(), b_w.size());
   CT::poison(u_w.data(), u_w.size());
   CT::poison(v_w.data(), v_w.size());

   // Only n.bits() + mod.bits() iterations are required, but avoid leaking the size of n
   size_t bits = 2 * mod.bits();

   while(bits--)
      {
      /*
      const word odd = a.is_odd();
      a -= odd * b;
      const word underflow = a.is_negative();
      b += a * underflow;
      a.set_sign(BigInt::Positive);

      a >>= 1;

      if(underflow)
         {
         std::swap(u, v);
         }

      u -= odd * v;
      u += u.is_negative() * mod;

      const word odd_u = u.is_odd();

      u >>= 1;
      u += mp1o2 * odd_u;
      */

      const word odd_a = a_w[0] & 1;

      //if(odd_a) a -= b
      word underflow = bigint_cnd_sub(odd_a, a_w.data(), b_w.data(), mod_words);

      //if(underflow) { b -= a; a = abs(a); swap(u, v); }
      bigint_cnd_add(underflow, b_w.data(), a_w.data(), mod_words);
      bigint_cnd_abs(underflow, a_w.data(), mod_words);
      bigint_cnd_swap(underflow, u_w.data(), v_w.data(), mod_words);

      // a >>= 1
      bigint_shr1(a_w.data(), mod_words, 0, 1);

      //if(odd_a) u -= v;
      word borrow = bigint_cnd_sub(odd_a, u_w.data(), v_w.data(), mod_words);

      // if(borrow) u += p
      bigint_cnd_add(borrow, u_w.data(), mod.data(), mod_words);

      const word odd_u = u_w[0] & 1;

      // u >>= 1
      bigint_shr1(u_w.data(), mod_words, 0, 1);

      //if(odd_u) u += mp1o2;
      bigint_cnd_add(odd_u, u_w.data(), mp1o2.data(), mod_words);
      }

   CT::unpoison(a_w.data(), a_w.size());
   CT::unpoison(b_w.data(), b_w.size());
   CT::unpoison(u_w.data(), u_w.size());
   CT::unpoison(v_w.data(), v_w.size());

   BOTAN_ASSERT(a.is_zero(), "A is zero");

   if(b != 1)
      return 0;

   return v;
   }

/*
* Find the Modular Inverse
*/
BigInt inverse_mod(const BigInt& n, const BigInt& mod)
   {
   if(mod.is_zero())
      throw BigInt::DivideByZero();
   if(mod.is_negative() || n.is_negative())
      throw Invalid_Argument("inverse_mod: arguments must be non-negative");

   if(n.is_zero() || (n.is_even() && mod.is_even()))
      return 0; // fast fail checks

   if(mod.is_odd())
      return ct_inverse_mod_odd_modulus(n, mod);

   BigInt u = mod, v = n;
   BigInt A = 1, B = 0, C = 0, D = 1;

   while(u.is_nonzero())
      {
      const size_t u_zero_bits = low_zero_bits(u);
      u >>= u_zero_bits;
      for(size_t i = 0; i != u_zero_bits; ++i)
         {
         if(A.is_odd() || B.is_odd())
            { A += n; B -= mod; }
         A >>= 1; B >>= 1;
         }

      const size_t v_zero_bits = low_zero_bits(v);
      v >>= v_zero_bits;
      for(size_t i = 0; i != v_zero_bits; ++i)
         {
         if(C.is_odd() || D.is_odd())
            { C += n; D -= mod; }
         C >>= 1; D >>= 1;
         }

      if(u >= v) { u -= v; A -= C; B -= D; }
      else       { v -= u; C -= A; D -= B; }
      }

   if(v != 1)
      return 0; // no modular inverse

   while(D.is_negative()) D += mod;
   while(D >= mod) D -= mod;

   return D;
   }

word monty_inverse(word input)
   {
   if(input == 0)
      throw Exception("monty_inverse: divide by zero");

   word b = input;
   word x2 = 1, x1 = 0, y2 = 0, y1 = 1;

   // First iteration, a = n+1
   word q = bigint_divop(1, 0, b);
   word r = (MP_WORD_MAX - q*b) + 1;
   word x = x2 - q*x1;
   word y = y2 - q*y1;

   word a = b;
   b = r;
   x2 = x1;
   x1 = x;
   y2 = y1;
   y1 = y;

   while(b > 0)
      {
      q = a / b;
      r = a - q*b;
      x = x2 - q*x1;
      y = y2 - q*y1;

      a = b;
      b = r;
      x2 = x1;
      x1 = x;
      y2 = y1;
      y1 = y;
      }

   const word check = y2 * input;
   BOTAN_ASSERT_EQUAL(check, 1, "monty_inverse result is inverse of input");

   // Now invert in addition space
   y2 = (MP_WORD_MAX - y2) + 1;

   return y2;
   }

/*
* Modular Exponentiation
*/
BigInt power_mod(const BigInt& base, const BigInt& exp, const BigInt& mod)
   {
   Power_Mod pow_mod(mod);

   /*
   * Calling set_base before set_exponent means we end up using a
   * minimal window. This makes sense given that here we know that any
   * precomputation is wasted.
   */

   if(base.is_negative())
      {
      pow_mod.set_base(-base);
      pow_mod.set_exponent(exp);
      if(exp.is_even())
         return pow_mod.execute();
      else
         return (mod - pow_mod.execute());
      }
   else
      {
      pow_mod.set_base(base);
      pow_mod.set_exponent(exp);
      return pow_mod.execute();
      }
   }

namespace {

bool mr_witness(BigInt&& y,
                const Modular_Reducer& reducer_n,
                const BigInt& n_minus_1, size_t s)
   {
   if(y == 1 || y == n_minus_1)
      return false;

   for(size_t i = 1; i != s; ++i)
      {
      y = reducer_n.square(y);

      if(y == 1) // found a non-trivial square root
         return true;

      if(y == n_minus_1) // -1, trivial square root, so give up
         return false;
      }

   return true; // fails Fermat test
   }

size_t mr_test_iterations(size_t n_bits, size_t prob, bool random)
   {
   const size_t base = (prob + 2) / 2; // worst case 4^-t error rate

   /*
   * For randomly chosen numbers we can use the estimates from
   * http://www.math.dartmouth.edu/~carlp/PDF/paper88.pdf
   *
   * These values are derived from the inequality for p(k,t) given on
   * the second page.
   */
   if(random && prob <= 80)
      {
      if(n_bits >= 1536)
         return 2; // < 2^-89
      if(n_bits >= 1024)
         return 4; // < 2^-89
      if(n_bits >= 512)
         return 5; // < 2^-80
      if(n_bits >= 256)
         return 11; // < 2^-80
      }

   return base;
   }

}

/*
* Test for primaility using Miller-Rabin
*/
bool is_prime(const BigInt& n, RandomNumberGenerator& rng,
              size_t prob, bool is_random)
   {
   if(n == 2)
      return true;
   if(n <= 1 || n.is_even())
      return false;

   // Fast path testing for small numbers (<= 65521)
   if(n <= PRIMES[PRIME_TABLE_SIZE-1])
      {
      const uint16_t num = static_cast<uint16_t>(n.word_at(0));

      return std::binary_search(PRIMES, PRIMES + PRIME_TABLE_SIZE, num);
      }

   const size_t test_iterations = mr_test_iterations(n.bits(), prob, is_random);

   const BigInt n_minus_1 = n - 1;
   const size_t s = low_zero_bits(n_minus_1);

   Fixed_Exponent_Power_Mod pow_mod(n_minus_1 >> s, n);
   Modular_Reducer reducer(n);

   for(size_t i = 0; i != test_iterations; ++i)
      {
      const BigInt a = BigInt::random_integer(rng, 2, n_minus_1);
      BigInt y = pow_mod(a);

      if(mr_witness(std::move(y), reducer, n_minus_1, s))
         return false;
      }

   return true;
   }

}
<commit_msg>Add consts<commit_after>/*
* Number Theory Functions
* (C) 1999-2011,2016 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/numthry.h>
#include <botan/pow_mod.h>
#include <botan/reducer.h>
#include <botan/internal/bit_ops.h>
#include <botan/internal/mp_core.h>
#include <botan/internal/ct_utils.h>
#include <algorithm>

namespace Botan {

/*
* Return the number of 0 bits at the end of n
*/
size_t low_zero_bits(const BigInt& n)
   {
   size_t low_zero = 0;

   if(n.is_positive() && n.is_nonzero())
      {
      for(size_t i = 0; i != n.size(); ++i)
         {
         const word x = n.word_at(i);

         if(x)
            {
            low_zero += ctz(x);
            break;
            }
         else
            low_zero += BOTAN_MP_WORD_BITS;
         }
      }

   return low_zero;
   }

/*
* Calculate the GCD
*/
BigInt gcd(const BigInt& a, const BigInt& b)
   {
   if(a.is_zero() || b.is_zero()) return 0;
   if(a == 1 || b == 1)           return 1;

   BigInt x = a, y = b;
   x.set_sign(BigInt::Positive);
   y.set_sign(BigInt::Positive);
   size_t shift = std::min(low_zero_bits(x), low_zero_bits(y));

   x >>= shift;
   y >>= shift;

   while(x.is_nonzero())
      {
      x >>= low_zero_bits(x);
      y >>= low_zero_bits(y);
      if(x >= y) { x -= y; x >>= 1; }
      else       { y -= x; y >>= 1; }
      }

   return (y << shift);
   }

/*
* Calculate the LCM
*/
BigInt lcm(const BigInt& a, const BigInt& b)
   {
   return ((a * b) / gcd(a, b));
   }

/*
Sets result to a^-1 * 2^k mod a
with n <= k <= 2n
Returns k

"The Montgomery Modular Inverse - Revisited" Çetin Koç, E. Savas
https://citeseerx.ist.psu.edu/viewdoc/citations?doi=10.1.1.75.8377

A const time implementation of this algorithm is described in
"Constant Time Modular Inversion" Joppe W. Bos
http://www.joppebos.com/files/CTInversion.pdf
*/
size_t almost_montgomery_inverse(BigInt& result,
                                 const BigInt& a,
                                 const BigInt& p)
   {
   size_t k = 0;

   BigInt u = p, v = a, r = 0, s = 1;

   while(v > 0)
      {
      if(u.is_even())
         {
         u >>= 1;
         s <<= 1;
         }
      else if(v.is_even())
         {
         v >>= 1;
         r <<= 1;
         }
      else if(u > v)
         {
         u -= v;
         u >>= 1;
         r += s;
         s <<= 1;
         }
      else
         {
         v -= u;
         v >>= 1;
         s += r;
         r <<= 1;
         }

      ++k;
      }

   if(r >= p)
      {
      r = r - p;
      }

   result = p - r;

   return k;
   }

BigInt normalized_montgomery_inverse(const BigInt& a, const BigInt& p)
   {
   BigInt r;
   size_t k = almost_montgomery_inverse(r, a, p);

   for(size_t i = 0; i != k; ++i)
      {
      if(r.is_odd())
         r += p;
      r >>= 1;
      }

   return r;
   }

BigInt ct_inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod)
   {
   if(n.is_negative() || mod.is_negative())
      throw Invalid_Argument("ct_inverse_mod_odd_modulus: arguments must be non-negative");
   if(mod < 3 || mod.is_even())
      throw Invalid_Argument("Bad modulus to ct_inverse_mod_odd_modulus");

   /*
   This uses a modular inversion algorithm designed by Niels Möller
   and implemented in Nettle. The same algorithm was later also
   adapted to GMP in mpn_sec_invert.

   It can be easily implemented in a way that does not depend on
   secret branches or memory lookups, providing resistance against
   some forms of side channel attack.

   There is also a description of the algorithm in Appendix 5 of "Fast
   Software Polynomial Multiplication on ARM Processors using the NEON Engine"
   by Danilo Câmara, Conrado P. L. Gouvêa, Julio López, and Ricardo
   Dahab in LNCS 8182
      https://conradoplg.cryptoland.net/files/2010/12/mocrysen13.pdf

   Thanks to Niels for creating the algorithm, explaining some things
   about it, and the reference to the paper.
   */

   // todo allow this to be pre-calculated and passed in as arg
   BigInt mp1o2 = (mod + 1) >> 1;

   const size_t mod_words = mod.sig_words();
   BOTAN_ASSERT(mod_words > 0, "Not empty");

   BigInt a = n;
   BigInt b = mod;
   BigInt u = 1, v = 0;

   a.grow_to(mod_words);
   u.grow_to(mod_words);
   v.grow_to(mod_words);
   mp1o2.grow_to(mod_words);

   secure_vector<word>& a_w = a.get_word_vector();
   secure_vector<word>& b_w = b.get_word_vector();
   secure_vector<word>& u_w = u.get_word_vector();
   secure_vector<word>& v_w = v.get_word_vector();

   CT::poison(a_w.data(), a_w.size());
   CT::poison(b_w.data(), b_w.size());
   CT::poison(u_w.data(), u_w.size());
   CT::poison(v_w.data(), v_w.size());

   // Only n.bits() + mod.bits() iterations are required, but avoid leaking the size of n
   size_t bits = 2 * mod.bits();

   while(bits--)
      {
      /*
      const word odd = a.is_odd();
      a -= odd * b;
      const word underflow = a.is_negative();
      b += a * underflow;
      a.set_sign(BigInt::Positive);

      a >>= 1;

      if(underflow)
         {
         std::swap(u, v);
         }

      u -= odd * v;
      u += u.is_negative() * mod;

      const word odd_u = u.is_odd();

      u >>= 1;
      u += mp1o2 * odd_u;
      */

      const word odd_a = a_w[0] & 1;

      //if(odd_a) a -= b
      word underflow = bigint_cnd_sub(odd_a, a_w.data(), b_w.data(), mod_words);

      //if(underflow) { b -= a; a = abs(a); swap(u, v); }
      bigint_cnd_add(underflow, b_w.data(), a_w.data(), mod_words);
      bigint_cnd_abs(underflow, a_w.data(), mod_words);
      bigint_cnd_swap(underflow, u_w.data(), v_w.data(), mod_words);

      // a >>= 1
      bigint_shr1(a_w.data(), mod_words, 0, 1);

      //if(odd_a) u -= v;
      word borrow = bigint_cnd_sub(odd_a, u_w.data(), v_w.data(), mod_words);

      // if(borrow) u += p
      bigint_cnd_add(borrow, u_w.data(), mod.data(), mod_words);

      const word odd_u = u_w[0] & 1;

      // u >>= 1
      bigint_shr1(u_w.data(), mod_words, 0, 1);

      //if(odd_u) u += mp1o2;
      bigint_cnd_add(odd_u, u_w.data(), mp1o2.data(), mod_words);
      }

   CT::unpoison(a_w.data(), a_w.size());
   CT::unpoison(b_w.data(), b_w.size());
   CT::unpoison(u_w.data(), u_w.size());
   CT::unpoison(v_w.data(), v_w.size());

   BOTAN_ASSERT(a.is_zero(), "A is zero");

   if(b != 1)
      return 0;

   return v;
   }

/*
* Find the Modular Inverse
*/
BigInt inverse_mod(const BigInt& n, const BigInt& mod)
   {
   if(mod.is_zero())
      throw BigInt::DivideByZero();
   if(mod.is_negative() || n.is_negative())
      throw Invalid_Argument("inverse_mod: arguments must be non-negative");

   if(n.is_zero() || (n.is_even() && mod.is_even()))
      return 0; // fast fail checks

   if(mod.is_odd())
      return ct_inverse_mod_odd_modulus(n, mod);

   BigInt u = mod, v = n;
   BigInt A = 1, B = 0, C = 0, D = 1;

   while(u.is_nonzero())
      {
      const size_t u_zero_bits = low_zero_bits(u);
      u >>= u_zero_bits;
      for(size_t i = 0; i != u_zero_bits; ++i)
         {
         if(A.is_odd() || B.is_odd())
            { A += n; B -= mod; }
         A >>= 1; B >>= 1;
         }

      const size_t v_zero_bits = low_zero_bits(v);
      v >>= v_zero_bits;
      for(size_t i = 0; i != v_zero_bits; ++i)
         {
         if(C.is_odd() || D.is_odd())
            { C += n; D -= mod; }
         C >>= 1; D >>= 1;
         }

      if(u >= v) { u -= v; A -= C; B -= D; }
      else       { v -= u; C -= A; D -= B; }
      }

   if(v != 1)
      return 0; // no modular inverse

   while(D.is_negative()) D += mod;
   while(D >= mod) D -= mod;

   return D;
   }

word monty_inverse(word input)
   {
   if(input == 0)
      throw Exception("monty_inverse: divide by zero");

   word b = input;
   word x2 = 1, x1 = 0, y2 = 0, y1 = 1;

   // First iteration, a = n+1
   word q = bigint_divop(1, 0, b);
   word r = (MP_WORD_MAX - q*b) + 1;
   word x = x2 - q*x1;
   word y = y2 - q*y1;

   word a = b;
   b = r;
   x2 = x1;
   x1 = x;
   y2 = y1;
   y1 = y;

   while(b > 0)
      {
      q = a / b;
      r = a - q*b;
      x = x2 - q*x1;
      y = y2 - q*y1;

      a = b;
      b = r;
      x2 = x1;
      x1 = x;
      y2 = y1;
      y1 = y;
      }

   const word check = y2 * input;
   BOTAN_ASSERT_EQUAL(check, 1, "monty_inverse result is inverse of input");

   // Now invert in addition space
   y2 = (MP_WORD_MAX - y2) + 1;

   return y2;
   }

/*
* Modular Exponentiation
*/
BigInt power_mod(const BigInt& base, const BigInt& exp, const BigInt& mod)
   {
   Power_Mod pow_mod(mod);

   /*
   * Calling set_base before set_exponent means we end up using a
   * minimal window. This makes sense given that here we know that any
   * precomputation is wasted.
   */

   if(base.is_negative())
      {
      pow_mod.set_base(-base);
      pow_mod.set_exponent(exp);
      if(exp.is_even())
         return pow_mod.execute();
      else
         return (mod - pow_mod.execute());
      }
   else
      {
      pow_mod.set_base(base);
      pow_mod.set_exponent(exp);
      return pow_mod.execute();
      }
   }

namespace {

bool mr_witness(BigInt&& y,
                const Modular_Reducer& reducer_n,
                const BigInt& n_minus_1, size_t s)
   {
   if(y == 1 || y == n_minus_1)
      return false;

   for(size_t i = 1; i != s; ++i)
      {
      y = reducer_n.square(y);

      if(y == 1) // found a non-trivial square root
         return true;

      if(y == n_minus_1) // -1, trivial square root, so give up
         return false;
      }

   return true; // fails Fermat test
   }

size_t mr_test_iterations(size_t n_bits, size_t prob, bool random)
   {
   const size_t base = (prob + 2) / 2; // worst case 4^-t error rate

   /*
   * For randomly chosen numbers we can use the estimates from
   * http://www.math.dartmouth.edu/~carlp/PDF/paper88.pdf
   *
   * These values are derived from the inequality for p(k,t) given on
   * the second page.
   */
   if(random && prob <= 80)
      {
      if(n_bits >= 1536)
         return 2; // < 2^-89
      if(n_bits >= 1024)
         return 4; // < 2^-89
      if(n_bits >= 512)
         return 5; // < 2^-80
      if(n_bits >= 256)
         return 11; // < 2^-80
      }

   return base;
   }

}

/*
* Test for primaility using Miller-Rabin
*/
bool is_prime(const BigInt& n, RandomNumberGenerator& rng,
              size_t prob, bool is_random)
   {
   if(n == 2)
      return true;
   if(n <= 1 || n.is_even())
      return false;

   // Fast path testing for small numbers (<= 65521)
   if(n <= PRIMES[PRIME_TABLE_SIZE-1])
      {
      const uint16_t num = static_cast<uint16_t>(n.word_at(0));

      return std::binary_search(PRIMES, PRIMES + PRIME_TABLE_SIZE, num);
      }

   const size_t test_iterations = mr_test_iterations(n.bits(), prob, is_random);

   const BigInt n_minus_1 = n - 1;
   const size_t s = low_zero_bits(n_minus_1);

   const Modular_Reducer mod_n(n);
   const Fixed_Exponent_Power_Mod pow_mod(n_minus_1 >> s, n);

   for(size_t i = 0; i != test_iterations; ++i)
      {
      const BigInt a = BigInt::random_integer(rng, 2, n_minus_1);
      BigInt y = pow_mod(a);

      if(mr_witness(std::move(y), mod_n, n_minus_1, s))
         return false;
      }

   return true;
   }

}
<|endoftext|>


<commit_before>/****************************************************************************
 * Copyright (c) 2012-2018 by the DataTransferKit authors                   *
 * All rights reserved.                                                     *
 *                                                                          *
 * This file is part of the DataTransferKit library. DataTransferKit is     *
 * distributed under a BSD 3-clause license. For the licensing terms see    *
 * the LICENSE file in the top-level directory.                             *
 *                                                                          *
 * SPDX-License-Identifier: BSD-3-Clause                                    *
 ****************************************************************************/

#ifndef DTK_DETAILS_TREE_CONSTRUCTION_DEF_HPP
#define DTK_DETAILS_TREE_CONSTRUCTION_DEF_HPP

#include "DTK_ConfigDefs.hpp"

#include <DTK_DetailsAlgorithms.hpp>
#include <DTK_DetailsUtils.hpp>  // iota
#include <DTK_KokkosHelpers.hpp> // sgn, min, max

#include <Kokkos_Atomic.hpp>
#include <Kokkos_Sort.hpp>

#include <cassert>

namespace DataTransferKit
{
namespace Details
{

template <typename DeviceType>
class ExpandBoxWithBoxFunctor
{
  public:
    ExpandBoxWithBoxFunctor(
        Kokkos::View<Box const *, DeviceType> bounding_boxes )
        : _bounding_boxes( bounding_boxes )
    {
    }

    KOKKOS_INLINE_FUNCTION
    void init( Box &box ) const { box = Box(); }

    KOKKOS_INLINE_FUNCTION
    void operator()( int const i, Box &box ) const
    {
        expand( box, _bounding_boxes( i ) );
    }

    KOKKOS_INLINE_FUNCTION
    void join( volatile Box &dst, volatile Box const &src ) const
    {
        expand( dst, src );
    }

  private:
    Kokkos::View<Box const *, DeviceType> _bounding_boxes;
};

template <typename DeviceType>
class AssignMortonCodesFunctor
{
  public:
    AssignMortonCodesFunctor(
        Kokkos::View<Box const *, DeviceType> bounding_boxes,
        Kokkos::View<unsigned int *, DeviceType> morton_codes,
        Box const &scene_bounding_box )
        : _bounding_boxes( bounding_boxes )
        , _morton_codes( morton_codes )
        , _scene_bounding_box( scene_bounding_box )
    {
    }

    KOKKOS_INLINE_FUNCTION
    void operator()( int const i ) const
    {
        Point xyz;
        double a, b;
        centroid( _bounding_boxes[i], xyz );
        // scale coordinates with respect to bounding box of the scene
        for ( int d = 0; d < 3; ++d )
        {
            a = _scene_bounding_box.minCorner()[d];
            b = _scene_bounding_box.maxCorner()[d];
            xyz[d] = ( a != b ? ( xyz[d] - a ) / ( b - a ) : 0 );
        }
        _morton_codes[i] =
            TreeConstruction<DeviceType>::morton3D( xyz[0], xyz[1], xyz[2] );
    }

  private:
    Kokkos::View<Box const *, DeviceType> _bounding_boxes;
    Kokkos::View<unsigned int *, DeviceType> _morton_codes;
    Box const &_scene_bounding_box;
};

template <typename DeviceType>
class GenerateHierarchyFunctor
{
  public:
    GenerateHierarchyFunctor(
        Kokkos::View<unsigned int *, DeviceType> sorted_morton_codes,
        Kokkos::View<Node *, DeviceType> leaf_nodes,
        Kokkos::View<Node *, DeviceType> internal_nodes )
        : _sorted_morton_codes( sorted_morton_codes )
        , _leaf_nodes( leaf_nodes )
        , _internal_nodes( internal_nodes )
    {
    }

    // from "Thinking Parallel, Part III: Tree Construction on the GPU" by
    // Karras
    KOKKOS_INLINE_FUNCTION
    void operator()( int const i ) const
    {
        // Construct internal nodes.
        // Find out which range of objects the node corresponds to.
        // (This is where the magic happens!)

        auto range = TreeConstruction<DeviceType>::determineRange(
            _sorted_morton_codes, i );
        int first = range.first;
        int last = range.second;

        // Determine where to split the range.

        int split = TreeConstruction<DeviceType>::findSplit(
            _sorted_morton_codes, first, last );

        // Select childA.

        Node *childA;
        if ( split == first )
            childA = &_leaf_nodes[split];
        else
            childA = &_internal_nodes[split];

        // Select childB.

        Node *childB;
        if ( split + 1 == last )
            childB = &_leaf_nodes[split + 1];
        else
            childB = &_internal_nodes[split + 1];

        // Record parent-child relationships.

        _internal_nodes[i].children.first = childA;
        _internal_nodes[i].children.second = childB;
        childA->parent = &_internal_nodes[i];
        childB->parent = &_internal_nodes[i];
    }

  private:
    Kokkos::View<unsigned int *, DeviceType> _sorted_morton_codes;
    Kokkos::View<Node *, DeviceType> _leaf_nodes;
    Kokkos::View<Node *, DeviceType> _internal_nodes;
};

template <typename DeviceType>
class CalculateBoundingBoxesFunctor
{
  public:
    CalculateBoundingBoxesFunctor( Kokkos::View<Node *, DeviceType> leaf_nodes,
                                   Node *root,
                                   Kokkos::View<int *, DeviceType> ready_flags )
        : _leaf_nodes( leaf_nodes )
        , _root( root )
        , _ready_flags( ready_flags )
    {
    }

    KOKKOS_INLINE_FUNCTION
    void operator()( int const i ) const
    {
        Node *node = _leaf_nodes[i].parent;
        while ( node != _root )
        {
            if ( Kokkos::atomic_compare_exchange_strong(
                     &_ready_flags[node - _root], 0, 1 ) )
                break;
            for ( Node *child : {node->children.first, node->children.second} )
                expand( node->bounding_box, child->bounding_box );
            node = node->parent;
        }
        // NOTE: could stop at node != root and then just check that what we
        // computed earlier (bounding box of the scene) is indeed the union of
        // the two children.
    }

  private:
    Kokkos::View<Node *, DeviceType> _leaf_nodes;
    Node *_root;
    Kokkos::View<int *, DeviceType> _ready_flags;
};

template <typename DeviceType>
void TreeConstruction<DeviceType>::calculateBoundingBoxOfTheScene(
    Kokkos::View<Box const *, DeviceType> bounding_boxes,
    Box &scene_bounding_box )
{
    int const n = bounding_boxes.extent( 0 );
    ExpandBoxWithBoxFunctor<DeviceType> functor( bounding_boxes );
    Kokkos::parallel_reduce( "calculate_bouding_of_the_scene",
                             Kokkos::RangePolicy<ExecutionSpace>( 0, n ),
                             functor, scene_bounding_box );
    Kokkos::fence();
}

template <typename DeviceType>
void TreeConstruction<DeviceType>::assignMortonCodes(
    Kokkos::View<Box const *, DeviceType> bounding_boxes,
    Kokkos::View<unsigned int *, DeviceType> morton_codes,
    Box const &scene_bounding_box )
{
    int const n = morton_codes.extent( 0 );
    AssignMortonCodesFunctor<DeviceType> functor( bounding_boxes, morton_codes,
                                                  scene_bounding_box );
    Kokkos::parallel_for( DTK_MARK_REGION( "assign_morton_codes" ),
                          Kokkos::RangePolicy<ExecutionSpace>( 0, n ),
                          functor );
    Kokkos::fence();
}

template <typename DeviceType>
void TreeConstruction<DeviceType>::sortObjects(
    Kokkos::View<unsigned int *, DeviceType> morton_codes,
    Kokkos::View<int *, DeviceType> object_ids )
{
    int const n = morton_codes.extent( 0 );

    typedef Kokkos::BinOp1D<Kokkos::View<unsigned int *, DeviceType>> CompType;

    Kokkos::Experimental::MinMaxScalar<unsigned int> result;
    Kokkos::Experimental::MinMax<unsigned int> reducer( result );
    parallel_reduce(
        Kokkos::RangePolicy<ExecutionSpace>( 0, n ),
        Kokkos::Impl::min_max_functor<Kokkos::View<unsigned int *, DeviceType>>(
            morton_codes ),
        reducer );
    if ( result.min_val == result.max_val )
        return;
    Kokkos::BinSort<Kokkos::View<unsigned int *, DeviceType>, CompType>
        bin_sort( morton_codes,
                  CompType( n / 2, result.min_val, result.max_val ), true );
    bin_sort.create_permute_vector();
    bin_sort.sort( morton_codes );
    // TODO: We might be able to just use `bin_sort.get_permute_vector()`
    // instead of initializing the indices with iota() and sorting the vector
    bin_sort.sort( object_ids );
    Kokkos::fence();
}

template <typename DeviceType>
Node *TreeConstruction<DeviceType>::generateHierarchy(
    Kokkos::View<unsigned int *, DeviceType> sorted_morton_codes,
    Kokkos::View<Node *, DeviceType> leaf_nodes,
    Kokkos::View<Node *, DeviceType> internal_nodes )
{
    GenerateHierarchyFunctor<DeviceType> functor( sorted_morton_codes,
                                                  leaf_nodes, internal_nodes );

    int const n = sorted_morton_codes.extent( 0 );
    Kokkos::parallel_for( DTK_MARK_REGION( "generate_hierarchy" ),
                          Kokkos::RangePolicy<ExecutionSpace>( 0, n - 1 ),
                          functor );
    Kokkos::fence();

    // Node 0 is the root.
    return &( internal_nodes.data()[0] );
}

template <typename DeviceType>
void TreeConstruction<DeviceType>::calculateBoundingBoxes(
    Kokkos::View<Node *, DeviceType> leaf_nodes,
    Kokkos::View<Node *, DeviceType> internal_nodes )
{
    int const n = leaf_nodes.extent( 0 );

    // Use int instead of bool because CAS on CUDA does not support boolean
    Kokkos::View<int *, DeviceType> ready_flags( "ready_flags", n - 1 );
    Kokkos::parallel_for( DTK_MARK_REGION( "fill_ready_flags" ),
                          Kokkos::RangePolicy<ExecutionSpace>( 0, n - 1 ),
                          KOKKOS_LAMBDA( int i ) { ready_flags[i] = 0; } );
    Kokkos::fence();

    Node *root = &internal_nodes[0];

    CalculateBoundingBoxesFunctor<DeviceType> calc_functor( leaf_nodes, root,
                                                            ready_flags );
    Kokkos::parallel_for( DTK_MARK_REGION( "calculate_bounding_boxes" ),
                          Kokkos::RangePolicy<ExecutionSpace>( 0, n ),
                          calc_functor );
    Kokkos::fence();
}

template <typename DeviceType>
int TreeConstruction<DeviceType>::findSplit(
    Kokkos::View<unsigned int *, DeviceType> sorted_morton_codes, int first,
    int last )
{
    // Calculate the number of highest bits that are the same
    // for all objects, using the count-leading-zeros intrinsic.

    int common_prefix = commonPrefix( sorted_morton_codes, first, last );

    // Use binary search to find where the next bit differs.
    // Specifically, we are looking for the highest object that
    // shares more than commonPrefix bits with the first one.

    int split = first; // initial guess
    int step = last - first;

    do
    {
        step = ( step + 1 ) >> 1;     // exponential decrease
        int new_split = split + step; // proposed new position

        if ( new_split < last )
        {
            if ( commonPrefix( sorted_morton_codes, first, new_split ) >
                 common_prefix )
                split = new_split; // accept proposal
        }
    } while ( step > 1 );

    return split;
}

template <typename DeviceType>
Kokkos::pair<int, int> TreeConstruction<DeviceType>::determineRange(
    Kokkos::View<unsigned int *, DeviceType> sorted_morton_codes, int i )
{
    // determine direction of the range (+1 or -1)
    int direction =
        KokkosHelpers::sgn( commonPrefix( sorted_morton_codes, i, i + 1 ) -
                            commonPrefix( sorted_morton_codes, i, i - 1 ) );
    assert( direction == +1 || direction == -1 );

    // compute upper bound for the length of the range
    int max_step = 2;
    int common_prefix = commonPrefix( sorted_morton_codes, i, i - direction );
    while ( commonPrefix( sorted_morton_codes, i, i + direction * max_step ) >
            common_prefix )
    {
        max_step = max_step << 1;
    }

    // find the other end using binary search
    int split = 0;
    int step = max_step;
    do
    {
        step = step >> 1;
        if ( commonPrefix( sorted_morton_codes, i,
                           i + ( split + step ) * direction ) > common_prefix )
            split += step;
    } while ( step > 1 );
    int j = i + split * direction;

    return {KokkosHelpers::min( i, j ), KokkosHelpers::max( i, j )};
}
}
}

// Explicit instantiation macro
#define DTK_TREECONSTRUCTION_INSTANT( NODE )                                   \
    namespace Details                                                          \
    {                                                                          \
    template struct TreeConstruction<typename NODE::device_type>;              \
    }

#endif
<commit_msg>Rename ExpandBoxWithBoxFunctor -> CalculateBoundingBoxOfTheSceneFunctor<commit_after>/****************************************************************************
 * Copyright (c) 2012-2018 by the DataTransferKit authors                   *
 * All rights reserved.                                                     *
 *                                                                          *
 * This file is part of the DataTransferKit library. DataTransferKit is     *
 * distributed under a BSD 3-clause license. For the licensing terms see    *
 * the LICENSE file in the top-level directory.                             *
 *                                                                          *
 * SPDX-License-Identifier: BSD-3-Clause                                    *
 ****************************************************************************/

#ifndef DTK_DETAILS_TREE_CONSTRUCTION_DEF_HPP
#define DTK_DETAILS_TREE_CONSTRUCTION_DEF_HPP

#include "DTK_ConfigDefs.hpp"

#include <DTK_DetailsAlgorithms.hpp>
#include <DTK_DetailsUtils.hpp>  // iota
#include <DTK_KokkosHelpers.hpp> // sgn, min, max

#include <Kokkos_Atomic.hpp>
#include <Kokkos_Sort.hpp>

#include <cassert>

namespace DataTransferKit
{
namespace Details
{

template <typename DeviceType>
class CalculateBoundingBoxOfTheSceneFunctor
{
  public:
    CalculateBoundingBoxOfTheSceneFunctor(
        Kokkos::View<Box const *, DeviceType> bounding_boxes )
        : _bounding_boxes( bounding_boxes )
    {
    }

    KOKKOS_INLINE_FUNCTION
    void init( Box &box ) const { box = Box(); }

    KOKKOS_INLINE_FUNCTION
    void operator()( int const i, Box &box ) const
    {
        expand( box, _bounding_boxes( i ) );
    }

    KOKKOS_INLINE_FUNCTION
    void join( volatile Box &dst, volatile Box const &src ) const
    {
        expand( dst, src );
    }

  private:
    Kokkos::View<Box const *, DeviceType> _bounding_boxes;
};

template <typename DeviceType>
class AssignMortonCodesFunctor
{
  public:
    AssignMortonCodesFunctor(
        Kokkos::View<Box const *, DeviceType> bounding_boxes,
        Kokkos::View<unsigned int *, DeviceType> morton_codes,
        Box const &scene_bounding_box )
        : _bounding_boxes( bounding_boxes )
        , _morton_codes( morton_codes )
        , _scene_bounding_box( scene_bounding_box )
    {
    }

    KOKKOS_INLINE_FUNCTION
    void operator()( int const i ) const
    {
        Point xyz;
        double a, b;
        centroid( _bounding_boxes[i], xyz );
        // scale coordinates with respect to bounding box of the scene
        for ( int d = 0; d < 3; ++d )
        {
            a = _scene_bounding_box.minCorner()[d];
            b = _scene_bounding_box.maxCorner()[d];
            xyz[d] = ( a != b ? ( xyz[d] - a ) / ( b - a ) : 0 );
        }
        _morton_codes[i] =
            TreeConstruction<DeviceType>::morton3D( xyz[0], xyz[1], xyz[2] );
    }

  private:
    Kokkos::View<Box const *, DeviceType> _bounding_boxes;
    Kokkos::View<unsigned int *, DeviceType> _morton_codes;
    Box const &_scene_bounding_box;
};

template <typename DeviceType>
class GenerateHierarchyFunctor
{
  public:
    GenerateHierarchyFunctor(
        Kokkos::View<unsigned int *, DeviceType> sorted_morton_codes,
        Kokkos::View<Node *, DeviceType> leaf_nodes,
        Kokkos::View<Node *, DeviceType> internal_nodes )
        : _sorted_morton_codes( sorted_morton_codes )
        , _leaf_nodes( leaf_nodes )
        , _internal_nodes( internal_nodes )
    {
    }

    // from "Thinking Parallel, Part III: Tree Construction on the GPU" by
    // Karras
    KOKKOS_INLINE_FUNCTION
    void operator()( int const i ) const
    {
        // Construct internal nodes.
        // Find out which range of objects the node corresponds to.
        // (This is where the magic happens!)

        auto range = TreeConstruction<DeviceType>::determineRange(
            _sorted_morton_codes, i );
        int first = range.first;
        int last = range.second;

        // Determine where to split the range.

        int split = TreeConstruction<DeviceType>::findSplit(
            _sorted_morton_codes, first, last );

        // Select childA.

        Node *childA;
        if ( split == first )
            childA = &_leaf_nodes[split];
        else
            childA = &_internal_nodes[split];

        // Select childB.

        Node *childB;
        if ( split + 1 == last )
            childB = &_leaf_nodes[split + 1];
        else
            childB = &_internal_nodes[split + 1];

        // Record parent-child relationships.

        _internal_nodes[i].children.first = childA;
        _internal_nodes[i].children.second = childB;
        childA->parent = &_internal_nodes[i];
        childB->parent = &_internal_nodes[i];
    }

  private:
    Kokkos::View<unsigned int *, DeviceType> _sorted_morton_codes;
    Kokkos::View<Node *, DeviceType> _leaf_nodes;
    Kokkos::View<Node *, DeviceType> _internal_nodes;
};

template <typename DeviceType>
class CalculateBoundingBoxesFunctor
{
  public:
    CalculateBoundingBoxesFunctor( Kokkos::View<Node *, DeviceType> leaf_nodes,
                                   Node *root,
                                   Kokkos::View<int *, DeviceType> ready_flags )
        : _leaf_nodes( leaf_nodes )
        , _root( root )
        , _ready_flags( ready_flags )
    {
    }

    KOKKOS_INLINE_FUNCTION
    void operator()( int const i ) const
    {
        Node *node = _leaf_nodes[i].parent;
        while ( node != _root )
        {
            if ( Kokkos::atomic_compare_exchange_strong(
                     &_ready_flags[node - _root], 0, 1 ) )
                break;
            for ( Node *child : {node->children.first, node->children.second} )
                expand( node->bounding_box, child->bounding_box );
            node = node->parent;
        }
        // NOTE: could stop at node != root and then just check that what we
        // computed earlier (bounding box of the scene) is indeed the union of
        // the two children.
    }

  private:
    Kokkos::View<Node *, DeviceType> _leaf_nodes;
    Node *_root;
    Kokkos::View<int *, DeviceType> _ready_flags;
};

template <typename DeviceType>
void TreeConstruction<DeviceType>::calculateBoundingBoxOfTheScene(
    Kokkos::View<Box const *, DeviceType> bounding_boxes,
    Box &scene_bounding_box )
{
    auto const n = bounding_boxes.extent( 0 );
    Kokkos::parallel_reduce(
        DTK_MARK_REGION( "calculate_bouding_of_the_scene" ),
        Kokkos::RangePolicy<ExecutionSpace>( 0, n ),
        CalculateBoundingBoxOfTheSceneFunctor<DeviceType>( bounding_boxes ),
        scene_bounding_box );
    Kokkos::fence();
}

template <typename DeviceType>
void TreeConstruction<DeviceType>::assignMortonCodes(
    Kokkos::View<Box const *, DeviceType> bounding_boxes,
    Kokkos::View<unsigned int *, DeviceType> morton_codes,
    Box const &scene_bounding_box )
{
    int const n = morton_codes.extent( 0 );
    AssignMortonCodesFunctor<DeviceType> functor( bounding_boxes, morton_codes,
                                                  scene_bounding_box );
    Kokkos::parallel_for( DTK_MARK_REGION( "assign_morton_codes" ),
                          Kokkos::RangePolicy<ExecutionSpace>( 0, n ),
                          functor );
    Kokkos::fence();
}

template <typename DeviceType>
void TreeConstruction<DeviceType>::sortObjects(
    Kokkos::View<unsigned int *, DeviceType> morton_codes,
    Kokkos::View<int *, DeviceType> object_ids )
{
    int const n = morton_codes.extent( 0 );

    typedef Kokkos::BinOp1D<Kokkos::View<unsigned int *, DeviceType>> CompType;

    Kokkos::Experimental::MinMaxScalar<unsigned int> result;
    Kokkos::Experimental::MinMax<unsigned int> reducer( result );
    parallel_reduce(
        Kokkos::RangePolicy<ExecutionSpace>( 0, n ),
        Kokkos::Impl::min_max_functor<Kokkos::View<unsigned int *, DeviceType>>(
            morton_codes ),
        reducer );
    if ( result.min_val == result.max_val )
        return;
    Kokkos::BinSort<Kokkos::View<unsigned int *, DeviceType>, CompType>
        bin_sort( morton_codes,
                  CompType( n / 2, result.min_val, result.max_val ), true );
    bin_sort.create_permute_vector();
    bin_sort.sort( morton_codes );
    // TODO: We might be able to just use `bin_sort.get_permute_vector()`
    // instead of initializing the indices with iota() and sorting the vector
    bin_sort.sort( object_ids );
    Kokkos::fence();
}

template <typename DeviceType>
Node *TreeConstruction<DeviceType>::generateHierarchy(
    Kokkos::View<unsigned int *, DeviceType> sorted_morton_codes,
    Kokkos::View<Node *, DeviceType> leaf_nodes,
    Kokkos::View<Node *, DeviceType> internal_nodes )
{
    GenerateHierarchyFunctor<DeviceType> functor( sorted_morton_codes,
                                                  leaf_nodes, internal_nodes );

    int const n = sorted_morton_codes.extent( 0 );
    Kokkos::parallel_for( DTK_MARK_REGION( "generate_hierarchy" ),
                          Kokkos::RangePolicy<ExecutionSpace>( 0, n - 1 ),
                          functor );
    Kokkos::fence();

    // Node 0 is the root.
    return &( internal_nodes.data()[0] );
}

template <typename DeviceType>
void TreeConstruction<DeviceType>::calculateBoundingBoxes(
    Kokkos::View<Node *, DeviceType> leaf_nodes,
    Kokkos::View<Node *, DeviceType> internal_nodes )
{
    int const n = leaf_nodes.extent( 0 );

    // Use int instead of bool because CAS on CUDA does not support boolean
    Kokkos::View<int *, DeviceType> ready_flags( "ready_flags", n - 1 );
    Kokkos::parallel_for( DTK_MARK_REGION( "fill_ready_flags" ),
                          Kokkos::RangePolicy<ExecutionSpace>( 0, n - 1 ),
                          KOKKOS_LAMBDA( int i ) { ready_flags[i] = 0; } );
    Kokkos::fence();

    Node *root = &internal_nodes[0];

    CalculateBoundingBoxesFunctor<DeviceType> calc_functor( leaf_nodes, root,
                                                            ready_flags );
    Kokkos::parallel_for( DTK_MARK_REGION( "calculate_bounding_boxes" ),
                          Kokkos::RangePolicy<ExecutionSpace>( 0, n ),
                          calc_functor );
    Kokkos::fence();
}

template <typename DeviceType>
int TreeConstruction<DeviceType>::findSplit(
    Kokkos::View<unsigned int *, DeviceType> sorted_morton_codes, int first,
    int last )
{
    // Calculate the number of highest bits that are the same
    // for all objects, using the count-leading-zeros intrinsic.

    int common_prefix = commonPrefix( sorted_morton_codes, first, last );

    // Use binary search to find where the next bit differs.
    // Specifically, we are looking for the highest object that
    // shares more than commonPrefix bits with the first one.

    int split = first; // initial guess
    int step = last - first;

    do
    {
        step = ( step + 1 ) >> 1;     // exponential decrease
        int new_split = split + step; // proposed new position

        if ( new_split < last )
        {
            if ( commonPrefix( sorted_morton_codes, first, new_split ) >
                 common_prefix )
                split = new_split; // accept proposal
        }
    } while ( step > 1 );

    return split;
}

template <typename DeviceType>
Kokkos::pair<int, int> TreeConstruction<DeviceType>::determineRange(
    Kokkos::View<unsigned int *, DeviceType> sorted_morton_codes, int i )
{
    // determine direction of the range (+1 or -1)
    int direction =
        KokkosHelpers::sgn( commonPrefix( sorted_morton_codes, i, i + 1 ) -
                            commonPrefix( sorted_morton_codes, i, i - 1 ) );
    assert( direction == +1 || direction == -1 );

    // compute upper bound for the length of the range
    int max_step = 2;
    int common_prefix = commonPrefix( sorted_morton_codes, i, i - direction );
    while ( commonPrefix( sorted_morton_codes, i, i + direction * max_step ) >
            common_prefix )
    {
        max_step = max_step << 1;
    }

    // find the other end using binary search
    int split = 0;
    int step = max_step;
    do
    {
        step = step >> 1;
        if ( commonPrefix( sorted_morton_codes, i,
                           i + ( split + step ) * direction ) > common_prefix )
            split += step;
    } while ( step > 1 );
    int j = i + split * direction;

    return {KokkosHelpers::min( i, j ), KokkosHelpers::max( i, j )};
}
}
}

// Explicit instantiation macro
#define DTK_TREECONSTRUCTION_INSTANT( NODE )                                   \
    namespace Details                                                          \
    {                                                                          \
    template struct TreeConstruction<typename NODE::device_type>;              \
    }

#endif
<|endoftext|>


<commit_before>/*****************************************************************************
 * icon_view.cpp : Icon view for the Playlist
 ****************************************************************************
 * Copyright © 2010 the VideoLAN team
 * $Id$
 *
 * Authors:         Jean-Baptiste Kempf <jb@videolan.org>
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "components/playlist/icon_view.hpp"
#include "components/playlist/playlist_model.hpp"
#include "input_manager.hpp"

#include <QPainter>
#include <QRect>
#include <QStyleOptionViewItem>
#include <QApplication>

#include "assert.h"

#define RECT_SIZE           100
#define ART_SIZE            64
#define OFFSET              (100-64)/2
#define ITEMS_SPACING       10
#define ART_RADIUS          7

void PlListViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    painter->setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );

    /*if( option.state & QStyle::State_Selected )
         painter->fillRect(option.rect, option.palette.highlight());*/
    QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option, painter );

    PLItem *currentItem = static_cast<PLItem*>( index.internalPointer() );
    assert( currentItem );

    QPixmap pix;
    QString url = InputManager::decodeArtURL( currentItem->inputItem() );

    if( !url.isEmpty() && pix.load( url ) )
    {
        pix = pix.scaled( ART_SIZE, ART_SIZE, Qt::KeepAspectRatioByExpanding );
    }
    else
    {
        pix = QPixmap( ":/noart64" );
    }

    QRect artRect = option.rect.adjusted( OFFSET - 1, 0, - OFFSET, - OFFSET *2 );
    QPainterPath artRectPath;
    artRectPath.addRoundedRect( artRect, ART_RADIUS, ART_RADIUS );

    // Draw the drop shadow
    painter->save();
    painter->setOpacity( 0.7 );
    painter->setBrush( QBrush( Qt::gray ) );
    painter->drawRoundedRect( artRect.adjusted( 2, 2, 2, 2 ), ART_RADIUS, ART_RADIUS );
    painter->restore();

    // Draw the art pixmap
    painter->drawPixmap( artRect, pix );
    painter->setClipPath( artRectPath );
    painter->drawPixmap( artRect, pix );
    painter->setClipping( false );

    painter->setFont( QFont( "Verdana", 7 ) );

    QRect textRect = option.rect.adjusted( 1, ART_SIZE + 2, -1, -1 );
    painter->drawText( textRect, qfu( input_item_GetTitle( currentItem->inputItem() ) ),
                       QTextOption( Qt::AlignCenter ) );

}

QSize PlListViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    return QSize( RECT_SIZE, RECT_SIZE);
}

PlIconView::PlIconView( PLModel *model, QWidget *parent ) : QListView( parent )
{
    setModel( model );
    setViewMode( QListView::IconMode );
    setMovement( QListView::Static );
    setResizeMode( QListView::Adjust );
    setGridSize( QSize( 100, 100 ) );
    setSpacing( ITEMS_SPACING );
    setWrapping( true );

    PlListViewItemDelegate *pl = new PlListViewItemDelegate();
    setItemDelegate( pl );
}
<commit_msg>Qt: don't use 2 drawPixmap<commit_after>/*****************************************************************************
 * icon_view.cpp : Icon view for the Playlist
 ****************************************************************************
 * Copyright © 2010 the VideoLAN team
 * $Id$
 *
 * Authors:         Jean-Baptiste Kempf <jb@videolan.org>
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "components/playlist/icon_view.hpp"
#include "components/playlist/playlist_model.hpp"
#include "input_manager.hpp"

#include <QPainter>
#include <QRect>
#include <QStyleOptionViewItem>
#include <QApplication>

#include "assert.h"

#define RECT_SIZE           100
#define ART_SIZE            64
#define OFFSET              (100-64)/2
#define ITEMS_SPACING       10
#define ART_RADIUS          7

void PlListViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    painter->setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );

    /*if( option.state & QStyle::State_Selected )
         painter->fillRect(option.rect, option.palette.highlight());*/
    QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option, painter );

    PLItem *currentItem = static_cast<PLItem*>( index.internalPointer() );
    assert( currentItem );

    QPixmap pix;
    QString url = InputManager::decodeArtURL( currentItem->inputItem() );

    if( !url.isEmpty() && pix.load( url ) )
    {
        pix = pix.scaled( ART_SIZE, ART_SIZE, Qt::KeepAspectRatioByExpanding );
    }
    else
    {
        pix = QPixmap( ":/noart64" );
    }

    QRect artRect = option.rect.adjusted( OFFSET - 1, 0, - OFFSET, - OFFSET *2 );
    QPainterPath artRectPath;
    artRectPath.addRoundedRect( artRect, ART_RADIUS, ART_RADIUS );

    // Draw the drop shadow
    painter->save();
    painter->setOpacity( 0.7 );
    painter->setBrush( QBrush( Qt::gray ) );
    painter->drawRoundedRect( artRect.adjusted( 2, 2, 2, 2 ), ART_RADIUS, ART_RADIUS );
    painter->restore();

    // Draw the art pixmap
    painter->setClipPath( artRectPath );
    painter->drawPixmap( artRect, pix );
    painter->setClipping( false );

    painter->setFont( QFont( "Verdana", 7 ) );

    QRect textRect = option.rect.adjusted( 1, ART_SIZE + 2, -1, -1 );
    painter->drawText( textRect, qfu( input_item_GetTitle( currentItem->inputItem() ) ),
                       QTextOption( Qt::AlignCenter ) );

}

QSize PlListViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    return QSize( RECT_SIZE, RECT_SIZE);
}

PlIconView::PlIconView( PLModel *model, QWidget *parent ) : QListView( parent )
{
    setModel( model );
    setViewMode( QListView::IconMode );
    setMovement( QListView::Static );
    setResizeMode( QListView::Adjust );
    setGridSize( QSize( 100, 100 ) );
    setSpacing( ITEMS_SPACING );
    setWrapping( true );

    PlListViewItemDelegate *pl = new PlListViewItemDelegate();
    setItemDelegate( pl );
}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkInterpolateTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 2001 Insight Consortium
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

 * The name of the Insight Consortium, nor the names of any consortium members,
   nor of any contributors, may be used to endorse or promote products derived
   from this software without specific prior written permission.

  * Modified source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

=========================================================================*/

#include <iostream>

#include "itkPhysicalImage.h"
#include "itkSize.h"
#include "itkLinearInterpolateImageFunction.h"

#include "vnl/vnl_math.h"

typedef itk::Size<3>                               SizeType;
typedef itk::PhysicalImage<unsigned short, 3>              ImageType;
typedef itk::LinearInterpolateImageFunction<ImageType>  InterpolatorType;
typedef InterpolatorType::IndexType                 IndexType;
typedef InterpolatorType::PointType                 PointType;
typedef InterpolatorType::ContinuousIndexType       ContinuousIndexType;

/* Define the image size and physical coordinates */
SizeType size = {{20, 40, 80}};
double origin [3] = { 0.5,   0.5,   0.5};
double spacing[3] = { 0.1,   0.05 , 0.025};

/**
 * This function convert points from Image space to
 * geometric space
 */
PointType ConvertContinuousIndexToPoint(
const ContinuousIndexType& index )
{
	PointType point;
  for( int j = 0; j < PointType::PointDimension; j++ )
		{
		point[j] = index[j] * spacing[j] + origin[j];
		}

  return point;
}

/**
 * Test a geometric point. Returns true if test has passed,
 * returns false otherwise
 */
bool TestGeometricPoint(
const InterpolatorType * interp,
const PointType& point,
bool isInside,
double trueValue )
{

  std::cout << " Point: " << point;

  bool bvalue = interp->IsInsideBuffer( point );
  std::cout << " Inside: " << bvalue;

  if( bvalue != isInside )
		{
    std::cout << "*** Error: inside should be " << isInside << std::endl;
    return false;
    }

  if( isInside )
		{
		double value = interp->Evaluate( point );
    std::cout << " Value: " << value;

    if( vnl_math_abs( value - trueValue ) > 1e-9 )
			{
			std::cout << "*** Error: value should be " << trueValue << std::endl;
      return false;
			}
		}

  std::cout << std::endl;
  return true;

}

/**
 * Test a continuous index. Returns true if test has passed,
 * returns false otherwise
 */
bool TestContinuousIndex(
const InterpolatorType * interp,
const ContinuousIndexType& index,
bool isInside,
double trueValue )
{

  std::cout << " Index: " << index;

  bool bvalue = interp->IsInsideBuffer( index );
  std::cout << " Inside: " << bvalue;

  if( bvalue != isInside )
		{
    std::cout << "*** Error: inside should be " << isInside << std::endl;
    return false;
    }

  if( isInside )
		{
		double value = interp->Evaluate( index );
    std::cout << " Value: " << value;

    if( vnl_math_abs( value - trueValue ) > 1e-9 )
			{
			std::cout << "*** Error: value should be " << trueValue << std::endl;
      return false;
			}
		}

  std::cout << std::endl;
  return true;

}

int
main(
    int argc,
    char *argv[])
{
    int flag = 0;           /* Did this test program work? */

    std::cout << "Testing image interpolation methods:\n";

    /* Allocate a simple test image */
    ImageType::Pointer image = ImageType::New();
    ImageType::RegionType region;
    region.SetSize(size);
    image->SetLargestPossibleRegion(region);
    image->SetBufferedRegion(region);
    image->Allocate();

    /* Set origin and spacing of physical coordinates */
    image->SetOrigin(origin);
    image->SetSpacing(spacing);

    /* Initialize the image contents */
    IndexType index;
    for (int slice = 0; slice < 80; slice++) {
        index[2] = slice;
        for (int row = 0; row < 40; row++) {
            index[1] = row;
            for (int col = 0; col < 20; col++) {
                index[0] = col;
                image->SetPixel(index, slice+row+col);
            }
        }
    }

    /* Create and initialize the interpolator */
    InterpolatorType::Pointer interp = InterpolatorType::New();
    interp->SetInputImage(image);

    //
    // FIXME: remove these when GetSpacing/GetOrigin has been
    // added to itk::Image
    interp->SetImageSpacing( image->GetSpacing() );
    interp->SetImageOrigin( image->GetOrigin() );

    /* Test evaluation at continuous indices and corresponding
       gemetric points */
    std::cout << "Evaluate at: " << std::endl;
    ContinuousIndexType cindex;
    PointType point;
    bool passed;

    // an integer position inside the image
    double darray1[3] = { 10, 20, 40};
    cindex = ContinuousIndexType(darray1);
    passed = TestContinuousIndex( interp, cindex, true, 70 );

    if( !passed ) flag = 1;

    point = ConvertContinuousIndexToPoint( cindex );
    passed = TestGeometricPoint( interp, point, true, 70 );

    if( !passed ) flag = 1;

    // position at the image border
    double darray2[3] = {0, 20, 40};
    cindex = ContinuousIndexType(darray2);
    passed = TestContinuousIndex( interp, cindex, true, 60 );

    if( !passed ) flag = 1;

    point = ConvertContinuousIndexToPoint( cindex );
    passed = TestGeometricPoint( interp, point, true, 60 );

    if( !passed ) flag = 1;

    // position near image border
    double darray3[3] = {19, 20, 40};
    cindex = ContinuousIndexType(darray3);
    passed = TestContinuousIndex( interp, cindex, true, 79 );

    if( !passed ) flag = 1;

    point = ConvertContinuousIndexToPoint( cindex );
    passed = TestGeometricPoint( interp, point, true, 79 );

    if( !passed ) flag = 1;

    // position outside the image
    double darray4[3] = {20, 20, 40};
    cindex = ContinuousIndexType(darray4);
    passed = TestContinuousIndex( interp, cindex, false, 0 );

    if( !passed ) flag = 1;

    point = ConvertContinuousIndexToPoint( cindex );
    passed = TestGeometricPoint( interp, point, false, 0 );

    if( !passed ) flag = 1;

    // at non-integer position
    double darray5[3] = {5.25, 12.5, 42.0};
    cindex = ContinuousIndexType(darray5);
    passed = TestContinuousIndex( interp, cindex, true, 59.75 );

    if( !passed ) flag = 1;

    point = ConvertContinuousIndexToPoint( cindex );
    passed = TestGeometricPoint( interp, point, true, 59.75 );

    if( !passed ) flag = 1;

    /* Return results of test */
    if (flag != 0) {
        std::cout << "*** Some test failed" << std::endl;
        return flag; }
    else {
        std::cout << "All tests successfully passed" << std::endl;
        return 0; }

}

<commit_msg>interpolater now get spacing and origin directly from the image<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkInterpolateTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 2001 Insight Consortium
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

 * The name of the Insight Consortium, nor the names of any consortium members,
   nor of any contributors, may be used to endorse or promote products derived
   from this software without specific prior written permission.

  * Modified source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

=========================================================================*/

#include <iostream>

#include "itkPhysicalImage.h"
#include "itkSize.h"
#include "itkLinearInterpolateImageFunction.h"

#include "vnl/vnl_math.h"

typedef itk::Size<3>                               SizeType;
typedef itk::PhysicalImage<unsigned short, 3>              ImageType;
typedef itk::LinearInterpolateImageFunction<ImageType>  InterpolatorType;
typedef InterpolatorType::IndexType                 IndexType;
typedef InterpolatorType::PointType                 PointType;
typedef InterpolatorType::ContinuousIndexType       ContinuousIndexType;

/* Define the image size and physical coordinates */
SizeType size = {{20, 40, 80}};
double origin [3] = { 0.5,   0.5,   0.5};
double spacing[3] = { 0.1,   0.05 , 0.025};

/**
 * This function convert points from Image space to
 * geometric space
 */
PointType ConvertContinuousIndexToPoint(
const ContinuousIndexType& index )
{
	PointType point;
  for( int j = 0; j < PointType::PointDimension; j++ )
		{
		point[j] = index[j] * spacing[j] + origin[j];
		}

  return point;
}

/**
 * Test a geometric point. Returns true if test has passed,
 * returns false otherwise
 */
bool TestGeometricPoint(
const InterpolatorType * interp,
const PointType& point,
bool isInside,
double trueValue )
{

  std::cout << " Point: " << point;

  bool bvalue = interp->IsInsideBuffer( point );
  std::cout << " Inside: " << bvalue;

  if( bvalue != isInside )
		{
    std::cout << "*** Error: inside should be " << isInside << std::endl;
    return false;
    }

  if( isInside )
		{
		double value = interp->Evaluate( point );
    std::cout << " Value: " << value;

    if( vnl_math_abs( value - trueValue ) > 1e-9 )
			{
			std::cout << "*** Error: value should be " << trueValue << std::endl;
      return false;
			}
		}

  std::cout << std::endl;
  return true;

}

/**
 * Test a continuous index. Returns true if test has passed,
 * returns false otherwise
 */
bool TestContinuousIndex(
const InterpolatorType * interp,
const ContinuousIndexType& index,
bool isInside,
double trueValue )
{

  std::cout << " Index: " << index;

  bool bvalue = interp->IsInsideBuffer( index );
  std::cout << " Inside: " << bvalue;

  if( bvalue != isInside )
		{
    std::cout << "*** Error: inside should be " << isInside << std::endl;
    return false;
    }

  if( isInside )
		{
		double value = interp->Evaluate( index );
    std::cout << " Value: " << value;

    if( vnl_math_abs( value - trueValue ) > 1e-9 )
			{
			std::cout << "*** Error: value should be " << trueValue << std::endl;
      return false;
			}
		}

  std::cout << std::endl;
  return true;

}

int
main(
    int argc,
    char *argv[])
{
    int flag = 0;           /* Did this test program work? */

    std::cout << "Testing image interpolation methods:\n";

    /* Allocate a simple test image */
    ImageType::Pointer image = ImageType::New();
    ImageType::RegionType region;
    region.SetSize(size);
    image->SetLargestPossibleRegion(region);
    image->SetBufferedRegion(region);
    image->Allocate();

    /* Set origin and spacing of physical coordinates */
    image->SetOrigin(origin);
    image->SetSpacing(spacing);

    /* Initialize the image contents */
    IndexType index;
    for (int slice = 0; slice < 80; slice++) {
        index[2] = slice;
        for (int row = 0; row < 40; row++) {
            index[1] = row;
            for (int col = 0; col < 20; col++) {
                index[0] = col;
                image->SetPixel(index, slice+row+col);
            }
        }
    }

    /* Create and initialize the interpolator */
    InterpolatorType::Pointer interp = InterpolatorType::New();
    interp->SetInputImage(image);

    /* Test evaluation at continuous indices and corresponding
       gemetric points */
    std::cout << "Evaluate at: " << std::endl;
    ContinuousIndexType cindex;
    PointType point;
    bool passed;

    // an integer position inside the image
    double darray1[3] = { 10, 20, 40};
    cindex = ContinuousIndexType(darray1);
    passed = TestContinuousIndex( interp, cindex, true, 70 );

    if( !passed ) flag = 1;

    point = ConvertContinuousIndexToPoint( cindex );
    passed = TestGeometricPoint( interp, point, true, 70 );

    if( !passed ) flag = 1;

    // position at the image border
    double darray2[3] = {0, 20, 40};
    cindex = ContinuousIndexType(darray2);
    passed = TestContinuousIndex( interp, cindex, true, 60 );

    if( !passed ) flag = 1;

    point = ConvertContinuousIndexToPoint( cindex );
    passed = TestGeometricPoint( interp, point, true, 60 );

    if( !passed ) flag = 1;

    // position near image border
    double darray3[3] = {19, 20, 40};
    cindex = ContinuousIndexType(darray3);
    passed = TestContinuousIndex( interp, cindex, true, 79 );

    if( !passed ) flag = 1;

    point = ConvertContinuousIndexToPoint( cindex );
    passed = TestGeometricPoint( interp, point, true, 79 );

    if( !passed ) flag = 1;

    // position outside the image
    double darray4[3] = {20, 20, 40};
    cindex = ContinuousIndexType(darray4);
    passed = TestContinuousIndex( interp, cindex, false, 0 );

    if( !passed ) flag = 1;

    point = ConvertContinuousIndexToPoint( cindex );
    passed = TestGeometricPoint( interp, point, false, 0 );

    if( !passed ) flag = 1;

    // at non-integer position
    double darray5[3] = {5.25, 12.5, 42.0};
    cindex = ContinuousIndexType(darray5);
    passed = TestContinuousIndex( interp, cindex, true, 59.75 );

    if( !passed ) flag = 1;

    point = ConvertContinuousIndexToPoint( cindex );
    passed = TestGeometricPoint( interp, point, true, 59.75 );

    if( !passed ) flag = 1;

    /* Return results of test */
    if (flag != 0) {
        std::cout << "*** Some test failed" << std::endl;
        return flag; }
    else {
        std::cout << "All tests successfully passed" << std::endl;
        return 0; }

}

<|endoftext|>


<commit_before>{
  TProof::Open("pod://");
  if (!gProof) return;

  TDSet *manual_dset = new TDSet("TTree", "deuterons");
  TString user[3] = {"m/mpuccio","m/masera","s/sbufalin"};
  for (int i = 0; i < 3; ++i)
  {
    TString ddset = Form("Find;BasePath=/alice/cern.ch/user/%s/NucleiPbPb2011test/output;FileName=*/root_archive.zip;Anchor=mpuccio_Flowdnt.root;Tree=/deuterons;Mode=local",user[i].Data());

    TFileCollection *fc = gProof->GetDataSet(ddset.Data());

  // Create TDSet manually (terrible hack): apparently PROOF is stupid
  // and cannot create it correctly
    TFileInfo *fi;
    TIter next( fc->GetList() );
    while (( fi = (TFileInfo *)next() )) {
      const char *fn = fi->GetCurrentUrl()->GetUrl();
      Printf("adding: %s", fn);
      manual_dset->Add( fn );
    }
  }

  gROOT->LoadMacro("AODSelector.cxx+g");

  const double kCent[5] = {0.,10.,20.,40.,60.};
  const double kBins[29] = {
    0.4f,0.5f,0.6f,0.7f,0.8f,0.9f,1.0f,1.1f,1.2f,1.4f,
    1.6f,1.8f,2.0f,2.2f,2.4f,2.6f,2.8f,3.0f,3.2f,3.4f,
    3.6f,3.8f,4.0f,4.2f,4.4f,5.0f,6.0f,8.0f,10.f
  };
  enum cutsName {kEtaMin=0,kEtaMax,kYMin,kYMax,kTPCsig,kTPCchi2,kSPDrec,kDCAxy,kDCAz};
  double cuts[9] = {-0.8,0.8,-0.5,0.5,70,4.,1,0.5,1};
  TArrayD *ptBins = new TArrayD(5,kCent);
  TArrayD *centBins = new TArrayD(29,kBins);
  TArrayD *cuts = new TArrayD(9,cuts);
  ptBins->SetName("ptbins");
  centBins->SetName("centbins");
  cuts->SetName("cuts");
  gProof->AddInput(ptBins);
  gProof->AddInput(centBins);
  gProof->AddInput(cuts);

  // Process the TDset
  gProof->Process(manual_dset, "AODSelector.cxx+g");

}
<commit_msg>Fixed typo<commit_after>{
  TProof::Open("pod://");
  if (!gProof) return;

  TDSet *manual_dset = new TDSet("TTree", "deuterons");
  TString user[3] = {"m/mpuccio","m/masera","s/sbufalin"};
  for (int i = 0; i < 3; ++i)
  {
    TString ddset = Form("Find;BasePath=/alice/cern.ch/user/%s/NucleiPbPb2011test/output;FileName=*/root_archive.zip;Anchor=mpuccio_Flowdnt.root;Tree=/deuterons;Mode=local",user[i].Data());

    TFileCollection *fc = gProof->GetDataSet(ddset.Data());

  // Create TDSet manually (terrible hack): apparently PROOF is stupid
  // and cannot create it correctly
    TFileInfo *fi;
    TIter next( fc->GetList() );
    while (( fi = (TFileInfo *)next() )) {
      const char *fn = fi->GetCurrentUrl()->GetUrl();
      Printf("adding: %s", fn);
      manual_dset->Add( fn );
    }
  }

  gROOT->LoadMacro("AODSelector.cxx+g");

  const double kCent[5] = {0.,10.,20.,40.,60.};
  const double kBins[29] = {
    0.4f,0.5f,0.6f,0.7f,0.8f,0.9f,1.0f,1.1f,1.2f,1.4f,
    1.6f,1.8f,2.0f,2.2f,2.4f,2.6f,2.8f,3.0f,3.2f,3.4f,
    3.6f,3.8f,4.0f,4.2f,4.4f,5.0f,6.0f,8.0f,10.f
  };
  enum cutsName {kEtaMin=0,kEtaMax,kYMin,kYMax,kTPCsig,kTPCchi2,kSPDrec,kDCAxy,kDCAz};
  double cutsA[9] = {-0.8,0.8,-0.5,0.5,70,4.,1,0.5,1};
  TArrayD *ptBins = new TArrayD(5,kCent);
  TArrayD *centBins = new TArrayD(29,kBins);
  TArrayD *cuts = new TArrayD(9,cutsA);
  ptBins->SetName("ptbins");
  centBins->SetName("centbins");
  cuts->SetName("cuts");
  gProof->AddInput(ptBins);
  gProof->AddInput(centBins);
  gProof->AddInput(cuts);

  // Process the TDset
  gProof->Process(manual_dset, "AODSelector.cxx+g");

}
<|endoftext|>


<commit_before>/*****************************************************************************
 * icon_view.cpp : Icon view for the Playlist
 ****************************************************************************
 * Copyright © 2010 the VideoLAN team
 * $Id$
 *
 * Authors:         Jean-Baptiste Kempf <jb@videolan.org>
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "components/playlist/icon_view.hpp"
#include "components/playlist/playlist_model.hpp"
#include "components/playlist/sorting.h"
#include "input_manager.hpp"

#include <QApplication>
#include <QPainter>
#include <QRect>
#include <QStyleOptionViewItem>
#include <QFontMetrics>
#include <QPixmapCache>

#include "assert.h"

#define ART_SIZE_W          110
#define ART_SIZE_H          80
#define ART_RADIUS          5
#define SPACER              5

QString AbstractPlViewItemDelegate::getMeta( const QModelIndex & index, int meta ) const
{
    return index.model()->index( index.row(),
                                  PLModel::columnFromMeta( meta ),
                                  index.parent() )
                                .data().toString();
}

void AbstractPlViewItemDelegate::paintPlayingItemBg( QPainter *painter, const QStyleOptionViewItem & option ) const
{
    painter->save();
    painter->setOpacity( 0.5 );
    painter->setBrush( QBrush( Qt::gray ) );
    painter->fillRect( option.rect, option.palette.color( QPalette::Dark ) );
    painter->restore();
}

QPixmap AbstractPlViewItemDelegate::getArtPixmap( const QModelIndex & index, const QSize & size ) const
{
    PLItem *item = static_cast<PLItem*>( index.internalPointer() );
    assert( item );

    QString artUrl = InputManager::decodeArtURL( item->inputItem() );

    if( artUrl.isEmpty() )
    {
        for( int i = 0; i < item->childCount(); i++ )
        {
            artUrl = InputManager::decodeArtURL( item->child( i )->inputItem() );
            if( !artUrl.isEmpty() )
                break;
        }
    }

    QPixmap artPix;

    QString key = artUrl + QString("%1%2").arg(size.width()).arg(size.height());

    if( !QPixmapCache::find( key, artPix ))
    {
        if( artUrl.isEmpty() || !artPix.load( artUrl ) )
        {
            artPix = QPixmap( ":/noart" ).scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
        }
        else
        {
            artPix = artPix.scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
            QPixmapCache::insert( key, artPix );
        }
    }

    return artPix;
}

void PlIconViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    QString title = getMeta( index, COLUMN_TITLE );
    QString artist = getMeta( index, COLUMN_ARTIST );

    QPixmap artPix = getArtPixmap( index, QSize( ART_SIZE_W, ART_SIZE_H ) );

    QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option,
                                          painter );

    painter->save();

    if( index.data( PLModel::IsCurrentRole ).toBool() )
    {
       painter->save();
       painter->setOpacity( 0.2 );
       painter->setBrush( QBrush( Qt::gray ) );
       painter->drawRoundedRect( option.rect.adjusted( 0, 0, -1, -1 ), ART_RADIUS, ART_RADIUS );
       painter->restore();
    }

    QRect artRect( option.rect.x() + 5 + ( ART_SIZE_W - artPix.width() ) / 2,
                   option.rect.y() + 5 + ( ART_SIZE_H - artPix.height() ) / 2,
                   artPix.width(), artPix.height() );

    // Draw the drop shadow
    painter->save();
    painter->setOpacity( 0.7 );
    painter->setBrush( QBrush( Qt::darkGray ) );
    painter->setPen( Qt::NoPen );
    painter->drawRoundedRect( artRect.adjusted( 0, 0, 2, 2 ), ART_RADIUS, ART_RADIUS );
    painter->restore();

    // Draw the art pixmap
    QPainterPath artRectPath;
    artRectPath.addRoundedRect( artRect, ART_RADIUS, ART_RADIUS );
    painter->setClipPath( artRectPath );
    painter->drawPixmap( artRect, artPix );
    painter->setClipping( false );

    if( option.state & QStyle::State_Selected )
        painter->setPen( option.palette.color( QPalette::HighlightedText ) );

    QFont font( index.data( Qt::FontRole ).value<QFont>() );
    font.setPointSize( 7 );

    // Draw title
    font.setItalic( true );
    painter->setFont( font );

    QFontMetrics fm = painter->fontMetrics();
    QRect textRect = option.rect.adjusted( 1, ART_SIZE_H + 10, 0, -1 );
    textRect.setHeight( fm.height() );

    painter->drawText( textRect,
                      fm.elidedText( title, Qt::ElideRight, textRect.width() ),
                      QTextOption( Qt::AlignCenter ) );

    // Draw artist
    painter->setPen( painter->pen().color().lighter( 150 ) );
    font.setItalic( false );
    painter->setFont( font );
    fm = painter->fontMetrics();

    textRect.moveTop( textRect.bottom() + 1 );

    painter->drawText(  textRect,
                        fm.elidedText( artist, Qt::ElideRight, textRect.width() ),
                        QTextOption( Qt::AlignCenter ) );

    painter->restore();
}

QSize PlIconViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    QFont f;
    f.setPointSize( 7 );
    f.setBold( true );
    QFontMetrics fm( f );
    int textHeight = fm.height();
    QSize sz ( ART_SIZE_W + 2 * SPACER,
               ART_SIZE_H + 3 * SPACER + 2 * textHeight + 1 );
    return sz;
}

#define LISTVIEW_ART_SIZE 45

void PlListViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    QModelIndex parent = index.parent();
    QModelIndex i;

    QString title = getMeta( index, COLUMN_TITLE );
    QString duration = getMeta( index, COLUMN_DURATION );
    if( !duration.isEmpty() ) title += QString(" [%1]").arg( duration );

    QString artist = getMeta( index, COLUMN_ARTIST );
    QString album = getMeta( index, COLUMN_ALBUM );
    QString trackNum = getMeta( index, COLUMN_TRACK_NUMBER );
    QString artistAlbum = artist
                          + ( artist.isEmpty() ? QString() : QString( ": " ) )
                          + album
                          + ( album.isEmpty() || trackNum.isEmpty() ?
                              QString() : QString( " [#%1]" ).arg( trackNum ) );

    QPixmap artPix = getArtPixmap( index, QSize( LISTVIEW_ART_SIZE, LISTVIEW_ART_SIZE ) );

    //Draw selection rectangle
    QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option, painter );

    //Paint background if item is playing
    if( index.data( PLModel::IsCurrentRole ).toBool() )
        paintPlayingItemBg( painter, option );

    QRect artRect( artPix.rect() );
    artRect.moveCenter( QPoint( artRect.center().x() + 3,
                                option.rect.center().y() ) );
    //Draw album art
    painter->drawPixmap( artRect, artPix );

    //Start drawing text
    painter->save();

    if( option.state & QStyle::State_Selected )
        painter->setPen( option.palette.color( QPalette::HighlightedText ) );

    QTextOption textOpt( Qt::AlignVCenter | Qt::AlignLeft );
    textOpt.setWrapMode( QTextOption::NoWrap );

    QFont f( index.data( Qt::FontRole ).value<QFont>() );

    //Draw title info
    f.setItalic( true );
    painter->setFont( f );
    QFontMetrics fm( painter->fontMetrics() );

    QRect textRect = option.rect.adjusted( LISTVIEW_ART_SIZE + 10, 0, -10, 0 );
    if( !artistAlbum.isEmpty() )
    {
        textRect.setHeight( fm.height() );
        textRect.moveBottom( option.rect.center().y() - 1 );
    }

    painter->drawText( textRect,
                       fm.elidedText( title, Qt::ElideRight, textRect.width() ),
                       textOpt );

    // Draw artist and album info
    if( !artistAlbum.isEmpty() )
    {
        f.setItalic( false );
        painter->setFont( f );
        fm = painter->fontMetrics();

        textRect.moveTop( textRect.bottom() + 2 );

        painter->drawText( textRect,
                           fm.elidedText( artistAlbum, Qt::ElideRight, textRect.width() ),
                           textOpt );
    }

    painter->restore();
}

QSize PlListViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
  QFont f;
  f.setBold( true );
  QFontMetrics fm( f );
  int height = qMax( LISTVIEW_ART_SIZE, 2 * fm.height() + 2 ) + 6;
  return QSize( 0, height );
}

PlIconView::PlIconView( PLModel *model, QWidget *parent ) : QListView( parent )
{
    PlIconViewItemDelegate *delegate = new PlIconViewItemDelegate( this );

    setModel( model );
    setViewMode( QListView::IconMode );
    setMovement( QListView::Static );
    setResizeMode( QListView::Adjust );
    setGridSize( delegate->sizeHint() );
    setWrapping( true );
    setUniformItemSizes( true );
    setSelectionMode( QAbstractItemView::ExtendedSelection );
    setDragEnabled(true);
    /* dropping in QListView::IconMode does not seem to work */
    //setAcceptDrops( true );
    //setDropIndicatorShown(true);

    setItemDelegate( delegate );
}

PlListView::PlListView( PLModel *model, QWidget *parent ) : QListView( parent )
{
    setModel( model );
    setViewMode( QListView::ListMode );
    setUniformItemSizes( true );
    setSelectionMode( QAbstractItemView::ExtendedSelection );
    setAlternatingRowColors( true );
    setDragEnabled(true);
    setAcceptDrops( true );
    setDropIndicatorShown(true);

    PlListViewItemDelegate *delegate = new PlListViewItemDelegate( this );
    setItemDelegate( delegate );
}
<commit_msg>Qt: cache "no-art" pixmap as well<commit_after>/*****************************************************************************
 * icon_view.cpp : Icon view for the Playlist
 ****************************************************************************
 * Copyright © 2010 the VideoLAN team
 * $Id$
 *
 * Authors:         Jean-Baptiste Kempf <jb@videolan.org>
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "components/playlist/icon_view.hpp"
#include "components/playlist/playlist_model.hpp"
#include "components/playlist/sorting.h"
#include "input_manager.hpp"

#include <QApplication>
#include <QPainter>
#include <QRect>
#include <QStyleOptionViewItem>
#include <QFontMetrics>
#include <QPixmapCache>

#include "assert.h"

#define ART_SIZE_W          110
#define ART_SIZE_H          80
#define ART_RADIUS          5
#define SPACER              5

QString AbstractPlViewItemDelegate::getMeta( const QModelIndex & index, int meta ) const
{
    return index.model()->index( index.row(),
                                  PLModel::columnFromMeta( meta ),
                                  index.parent() )
                                .data().toString();
}

void AbstractPlViewItemDelegate::paintPlayingItemBg( QPainter *painter, const QStyleOptionViewItem & option ) const
{
    painter->save();
    painter->setOpacity( 0.5 );
    painter->setBrush( QBrush( Qt::gray ) );
    painter->fillRect( option.rect, option.palette.color( QPalette::Dark ) );
    painter->restore();
}

QPixmap AbstractPlViewItemDelegate::getArtPixmap( const QModelIndex & index, const QSize & size ) const
{
    PLItem *item = static_cast<PLItem*>( index.internalPointer() );
    assert( item );

    QString artUrl = InputManager::decodeArtURL( item->inputItem() );

    if( artUrl.isEmpty() )
    {
        for( int i = 0; i < item->childCount(); i++ )
        {
            artUrl = InputManager::decodeArtURL( item->child( i )->inputItem() );
            if( !artUrl.isEmpty() )
                break;
        }
    }

    QPixmap artPix;

    QString key = artUrl + QString("%1%2").arg(size.width()).arg(size.height());

    if( !QPixmapCache::find( key, artPix ))
    {
        if( artUrl.isEmpty() || !artPix.load( artUrl ) )
        {
            key = QString("noart%1%2").arg(size.width()).arg(size.height());
            if( !QPixmapCache::find( key, artPix ) )
            {
                artPix = QPixmap( ":/noart" ).scaled( size,
                                                      Qt::KeepAspectRatio,
                                                      Qt::SmoothTransformation );
                QPixmapCache::insert( key, artPix );
            }
        }
        else
        {
            artPix = artPix.scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
            QPixmapCache::insert( key, artPix );
        }
    }

    return artPix;
}

void PlIconViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    QString title = getMeta( index, COLUMN_TITLE );
    QString artist = getMeta( index, COLUMN_ARTIST );

    QPixmap artPix = getArtPixmap( index, QSize( ART_SIZE_W, ART_SIZE_H ) );

    QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option,
                                          painter );

    painter->save();

    if( index.data( PLModel::IsCurrentRole ).toBool() )
    {
       painter->save();
       painter->setOpacity( 0.2 );
       painter->setBrush( QBrush( Qt::gray ) );
       painter->drawRoundedRect( option.rect.adjusted( 0, 0, -1, -1 ), ART_RADIUS, ART_RADIUS );
       painter->restore();
    }

    QRect artRect( option.rect.x() + 5 + ( ART_SIZE_W - artPix.width() ) / 2,
                   option.rect.y() + 5 + ( ART_SIZE_H - artPix.height() ) / 2,
                   artPix.width(), artPix.height() );

    // Draw the drop shadow
    painter->save();
    painter->setOpacity( 0.7 );
    painter->setBrush( QBrush( Qt::darkGray ) );
    painter->setPen( Qt::NoPen );
    painter->drawRoundedRect( artRect.adjusted( 0, 0, 2, 2 ), ART_RADIUS, ART_RADIUS );
    painter->restore();

    // Draw the art pixmap
    QPainterPath artRectPath;
    artRectPath.addRoundedRect( artRect, ART_RADIUS, ART_RADIUS );
    painter->setClipPath( artRectPath );
    painter->drawPixmap( artRect, artPix );
    painter->setClipping( false );

    if( option.state & QStyle::State_Selected )
        painter->setPen( option.palette.color( QPalette::HighlightedText ) );

    QFont font( index.data( Qt::FontRole ).value<QFont>() );
    font.setPointSize( 7 );

    // Draw title
    font.setItalic( true );
    painter->setFont( font );

    QFontMetrics fm = painter->fontMetrics();
    QRect textRect = option.rect.adjusted( 1, ART_SIZE_H + 10, 0, -1 );
    textRect.setHeight( fm.height() );

    painter->drawText( textRect,
                      fm.elidedText( title, Qt::ElideRight, textRect.width() ),
                      QTextOption( Qt::AlignCenter ) );

    // Draw artist
    painter->setPen( painter->pen().color().lighter( 150 ) );
    font.setItalic( false );
    painter->setFont( font );
    fm = painter->fontMetrics();

    textRect.moveTop( textRect.bottom() + 1 );

    painter->drawText(  textRect,
                        fm.elidedText( artist, Qt::ElideRight, textRect.width() ),
                        QTextOption( Qt::AlignCenter ) );

    painter->restore();
}

QSize PlIconViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    QFont f;
    f.setPointSize( 7 );
    f.setBold( true );
    QFontMetrics fm( f );
    int textHeight = fm.height();
    QSize sz ( ART_SIZE_W + 2 * SPACER,
               ART_SIZE_H + 3 * SPACER + 2 * textHeight + 1 );
    return sz;
}

#define LISTVIEW_ART_SIZE 45

void PlListViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    QModelIndex parent = index.parent();
    QModelIndex i;

    QString title = getMeta( index, COLUMN_TITLE );
    QString duration = getMeta( index, COLUMN_DURATION );
    if( !duration.isEmpty() ) title += QString(" [%1]").arg( duration );

    QString artist = getMeta( index, COLUMN_ARTIST );
    QString album = getMeta( index, COLUMN_ALBUM );
    QString trackNum = getMeta( index, COLUMN_TRACK_NUMBER );
    QString artistAlbum = artist
                          + ( artist.isEmpty() ? QString() : QString( ": " ) )
                          + album
                          + ( album.isEmpty() || trackNum.isEmpty() ?
                              QString() : QString( " [#%1]" ).arg( trackNum ) );

    QPixmap artPix = getArtPixmap( index, QSize( LISTVIEW_ART_SIZE, LISTVIEW_ART_SIZE ) );

    //Draw selection rectangle
    QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option, painter );

    //Paint background if item is playing
    if( index.data( PLModel::IsCurrentRole ).toBool() )
        paintPlayingItemBg( painter, option );

    QRect artRect( artPix.rect() );
    artRect.moveCenter( QPoint( artRect.center().x() + 3,
                                option.rect.center().y() ) );
    //Draw album art
    painter->drawPixmap( artRect, artPix );

    //Start drawing text
    painter->save();

    if( option.state & QStyle::State_Selected )
        painter->setPen( option.palette.color( QPalette::HighlightedText ) );

    QTextOption textOpt( Qt::AlignVCenter | Qt::AlignLeft );
    textOpt.setWrapMode( QTextOption::NoWrap );

    QFont f( index.data( Qt::FontRole ).value<QFont>() );

    //Draw title info
    f.setItalic( true );
    painter->setFont( f );
    QFontMetrics fm( painter->fontMetrics() );

    QRect textRect = option.rect.adjusted( LISTVIEW_ART_SIZE + 10, 0, -10, 0 );
    if( !artistAlbum.isEmpty() )
    {
        textRect.setHeight( fm.height() );
        textRect.moveBottom( option.rect.center().y() - 1 );
    }

    painter->drawText( textRect,
                       fm.elidedText( title, Qt::ElideRight, textRect.width() ),
                       textOpt );

    // Draw artist and album info
    if( !artistAlbum.isEmpty() )
    {
        f.setItalic( false );
        painter->setFont( f );
        fm = painter->fontMetrics();

        textRect.moveTop( textRect.bottom() + 2 );

        painter->drawText( textRect,
                           fm.elidedText( artistAlbum, Qt::ElideRight, textRect.width() ),
                           textOpt );
    }

    painter->restore();
}

QSize PlListViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
  QFont f;
  f.setBold( true );
  QFontMetrics fm( f );
  int height = qMax( LISTVIEW_ART_SIZE, 2 * fm.height() + 2 ) + 6;
  return QSize( 0, height );
}

PlIconView::PlIconView( PLModel *model, QWidget *parent ) : QListView( parent )
{
    PlIconViewItemDelegate *delegate = new PlIconViewItemDelegate( this );

    setModel( model );
    setViewMode( QListView::IconMode );
    setMovement( QListView::Static );
    setResizeMode( QListView::Adjust );
    setGridSize( delegate->sizeHint() );
    setWrapping( true );
    setUniformItemSizes( true );
    setSelectionMode( QAbstractItemView::ExtendedSelection );
    setDragEnabled(true);
    /* dropping in QListView::IconMode does not seem to work */
    //setAcceptDrops( true );
    //setDropIndicatorShown(true);

    setItemDelegate( delegate );
}

PlListView::PlListView( PLModel *model, QWidget *parent ) : QListView( parent )
{
    setModel( model );
    setViewMode( QListView::ListMode );
    setUniformItemSizes( true );
    setSelectionMode( QAbstractItemView::ExtendedSelection );
    setAlternatingRowColors( true );
    setDragEnabled(true);
    setAcceptDrops( true );
    setDropIndicatorShown(true);

    PlListViewItemDelegate *delegate = new PlListViewItemDelegate( this );
    setItemDelegate( delegate );
}
<|endoftext|>