PermLib
primitivity_test.h
00001 // ---------------------------------------------------------------------------
00002 //
00003 //  This file is part of PermLib.
00004 //
00005 // Copyright (c) 2009-2011 Thomas Rehn <thomas@carmen76.de>
00006 // All rights reserved.
00007 // 
00008 // Redistribution and use in source and binary forms, with or without
00009 // modification, are permitted provided that the following conditions
00010 // are met:
00011 // 1. Redistributions of source code must retain the above copyright
00012 //    notice, this list of conditions and the following disclaimer.
00013 // 2. Redistributions in binary form must reproduce the above copyright
00014 //    notice, this list of conditions and the following disclaimer in the
00015 //    documentation and/or other materials provided with the distribution.
00016 // 3. The name of the author may not be used to endorse or promote products
00017 //    derived from this software without specific prior written permission.
00018 // 
00019 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00020 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00021 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00022 // IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00023 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00024 // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00025 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00026 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00028 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029 //
00030 // ---------------------------------------------------------------------------
00031 
00032 
00033 #ifndef PRIMITIVITY_TEST_H_
00034 #define PRIMITIVITY_TEST_H_
00035 
00036 #include <permlib/prime_helper.h>
00037 
00038 #include <boost/foreach.hpp>
00039 #include <boost/utility.hpp>
00040 #include <vector>
00041 #include <list>
00042 #include <set>
00043 
00044 namespace permlib {
00045 
00047 
00054 template<typename PERM>
00055 class PrimitivityTest {
00056 public:
00064         template<typename InputIterator>
00065         PrimitivityTest(const unsigned int n, InputIterator genBegin, InputIterator genEnd);
00066         
00074         bool blockOfImprimitivity(std::vector<dom_int>* minimalBlock) const;
00075         
00083         bool allBlocks(std::list<std::vector<dom_int> >* blocks, bool findOnlyOneBlock = false) const;
00084         
00088         bool isPrimitive() const { return blockOfImprimitivity(NULL); }
00089         
00090 private:
00091         const unsigned int m_n;
00092         unsigned int m_primeLimit;
00093         const std::list<typename PERM::ptr> m_generators;
00094         
00095         bool fillTrivialBlock(std::list<std::vector<dom_int> >* block) const;
00096         
00097         static dom_int rep(dom_int kappa, std::vector<dom_int>& p);
00098         
00099         bool merge(dom_int kappa, dom_int lambda, std::vector<dom_int>& c, std::vector<dom_int>& p, std::vector<dom_int>& q, unsigned int& l) const;
00100 };
00101 
00102 
00103 
00104 template<typename PERM>
00105 template<typename InputIterator>
00106 PrimitivityTest<PERM>::PrimitivityTest(const unsigned int n, InputIterator genBegin, InputIterator genEnd)
00107         : m_n(n), m_primeLimit(m_n), m_generators(genBegin, genEnd)
00108 {
00109         for (const unsigned int* p = PrimeHelper::firstPrime(); p != PrimeHelper::lastPrime(); ++p) {
00110                 if (m_n % (*p) == 0) {
00111                         m_primeLimit = m_n / (*p);
00112                         break;
00113                 }
00114         }
00115 }
00116 
00117 template<typename PERM>
00118 bool PrimitivityTest<PERM>::blockOfImprimitivity(std::vector<dom_int>* minimalBlock) const {
00119         if (minimalBlock) {
00120                 std::list<std::vector<dom_int> > blocks;
00121                 const bool is_primitive = allBlocks(&blocks, true);
00122                 minimalBlock->clear();
00123                 minimalBlock->reserve(blocks.front().size());
00124                 std::copy(blocks.front().begin(), blocks.front().end(), std::back_inserter(*minimalBlock));
00125                 return is_primitive;
00126         }
00127         return allBlocks(0, true);
00128 }
00129 
00130 
00131 template<typename PERM>
00132 bool PrimitivityTest<PERM>::allBlocks(std::list<std::vector<dom_int> >* blocks, bool findOnlyOneBlock) const {
00133         std::vector<dom_int> alphas(2);
00134         std::set<dom_int> workedAlphas;
00135         alphas[0] = 0;
00136         
00137         for (dom_int a = 1; a < m_n; ++a) {
00138                 if (workedAlphas.count(a))
00139                         continue;
00140                 alphas[1] = a;
00141                 
00142                 const unsigned int k = alphas.size();
00143                 unsigned int l = k - 1;
00144                 std::vector<dom_int> p(m_n);
00145                 std::vector<dom_int> q(m_n);
00146                 std::vector<dom_int> c(m_n);
00147                 
00148                 for (unsigned int i = 0; i < m_n; ++i) {
00149                         c[i] = 1;
00150                         p[i] = i;
00151                 }
00152                 
00153                 for (unsigned int i = 0; i < k - 1; ++i) {
00154                         p[alphas[i+1]] = alphas[0];
00155                         q[i] = alphas[i+1];
00156                 }
00157                 
00158                 c[alphas[0]] = k;
00159                 for (unsigned int i = 0; i < l; ++i) {
00160                         const dom_int gamma = q[i];
00161                         BOOST_FOREACH(const typename PERM::ptr& x, m_generators) {
00162                                 const dom_int delta = rep(gamma, p);
00163                                 if (merge(x->at(gamma), x->at(delta), c, p, q, l)) {
00164                                         goto TRY_NEXT_ALPHA;
00165                                 }
00166                         }
00167                 }
00168                 
00169                 for (unsigned int i = 0; i < m_n; ++i)
00170                         rep(i, p);
00171                 
00172                 if (c[rep(alphas[0], p)] < m_n) {
00173                         if (blocks) {
00174                                 std::vector<dom_int> block;
00175                                 block.reserve(c[rep(alphas[0], p)]);
00176                                 for (unsigned int i = 0; i < m_n; ++i) {
00177                                         if (p[i] == p[alphas[0]]) {
00178                                                 block.push_back(i);
00179                                                 if (i != alphas[0])
00180                                                         workedAlphas.insert(i);
00181                                         }
00182                                 }
00183                                 BOOST_ASSERT( block.size() == c[rep(alphas[0], p)] );
00184                                 blocks->push_back(block);
00185                         }
00186                         if (findOnlyOneBlock)
00187                                 return false;
00188                 }
00189                 
00190                 // end of alpha loop
00191 TRY_NEXT_ALPHA:
00192                 continue;
00193         }
00194         
00195         if (!blocks->empty())
00196                 return false;
00197         return fillTrivialBlock(blocks);
00198 }
00199 
00200 template<typename PERM>
00201 bool PrimitivityTest<PERM>::fillTrivialBlock(std::list<std::vector<dom_int> >* blocks) const {
00202         if (blocks) {
00203                 std::vector<dom_int> minimalBlock(m_n);
00204                 for (unsigned int i = 0; i < m_n; ++i)
00205                         minimalBlock[i] = i;
00206                 blocks->push_back(minimalBlock);
00207         }
00208         return true;
00209 }
00210 
00211 template<typename PERM>
00212 dom_int PrimitivityTest<PERM>::rep(dom_int kappa, std::vector<dom_int>& p) {
00213         dom_int lambda = kappa;
00214         dom_int rho = p[lambda];
00215         while (rho != lambda) {
00216                 lambda = rho;
00217                 rho = p[lambda];
00218         }
00219         
00220         dom_int mu = kappa;
00221         rho = p[mu];
00222         while (rho != lambda) {
00223                 p[mu] = lambda;
00224                 mu = rho;
00225                 rho = p[mu];
00226         }
00227         
00228         return lambda;
00229 }
00230 
00231 template<typename PERM>
00232 bool PrimitivityTest<PERM>::merge(dom_int kappa, dom_int lambda, std::vector<dom_int>& c, std::vector<dom_int>& p, std::vector<dom_int>& q, unsigned int& l) const {
00233         dom_int phi = rep(kappa, p);
00234         dom_int psi = rep(lambda, p);
00235         
00236         if (phi != psi) {
00237                 dom_int mu, nu;
00238                 if (c[phi] >= c[psi]) {
00239                         mu = phi;
00240                         nu = psi;
00241                 } else {
00242                         mu = psi;
00243                         nu = phi;
00244                 }
00245                 p[nu] = mu;
00246                 c[mu] += c[nu];
00247                 if (c[mu] > m_primeLimit)
00248                         return true;
00249                 
00250                 q[l] = nu;
00251                 ++l;
00252         }
00253         
00254         return false;
00255 }
00256 
00257 }
00258 
00259 #endif