SUNphi  1.0
Combinatorial.hpp
Go to the documentation of this file.
1 #ifndef _COMBINATORIAL_HPP
2 #define _COMBINATORIAL_HPP
3 
4 /// \file Combinatorial.hpp
5 ///
6 /// \brief Header file for the definition of combinatorial routines
7 ///
8 /// For the time being we limits ourselves to the combinatorial system
9 
10 #include <cstdint>
11 #include <numeric>
12 
13 #include <containers/Vector.hpp>
14 #include <debug/Crash.hpp>
15 #include <ios/Logger.hpp>
16 
17 namespace SUNphi
18 {
19  /// Implements a looper over all combinations of a given kind
20  template <typename Int=int64_t> // Integer type
22  {
23  /// Type used to enumerate the slots
24  using Slot=
25  typename Vector<Int>::Size;
26 
27  /// Maximal number of objects per slot
29 
30  /// Number of objects
31  Int nObj;
32 
33  /// Current combination
35 
36  /// Assign n elements n the range, putting the maximum available in each of them
37  ///
38  /// Returns the number of non-assigned elements
39  Int assignToSlots(Vector<Int>& res, ///< Result
40  Int nObjToAss, ///< Number of elements to assign
41  const Slot& firstSlot, ///< First slot where to assign
42  const Slot& pastLastSlot, ///< Slot before which to stop assigning
43  const Slot& dSlot) ///< Offset between slots
44  const
45  {
46  // Move across all slots
47  for(Slot iSlot=firstSlot;
48  iSlot!=pastLastSlot;
49  iSlot+=dSlot)
50  {
51  // Assign to current slot
52  res[iSlot]=
53  std::min(nObjToAss,nMaxPerSlot[iSlot]);
54 
55  // Remove the assigned
56  nObjToAss-=
57  res[iSlot];
58  }
59 
60  return
61  nObjToAss;
62  }
63 
64  /// Get the first or last combo
65  Vector<Int> getFirstOrLast(const FIRST_OR_LAST& firstLast)
66  const
67  {
68  /// Returned value
69  Vector<Int> res(nSlots());
70 
71  /// Number of objects to assign
72  const Int nObjToAss=
73  nObj;
74 
75  /// First slot to assign
76  const Slot firstSlot=
77  (firstLast==LAST)
78  ?
79  (nSlots()-1)
80  :
81  0;
82 
83  /// Last slot to assign
84  const Slot pastLastSlot=
85  (firstLast==LAST)
86  ?
87  -1
88  :
89  (nSlots());
90 
91  /// Offset to move
92  const Slot dSlot=
93  sign(pastLastSlot-firstSlot);
94 
95  /// Residual objects to assign
96  const Int nResObjToAss=
97  assignToSlots(res,nObjToAss,firstSlot,pastLastSlot,dSlot);
98 
99  // Check that all have been assigned
100  if(nResObjToAss>0)
101  CRASH<<"Should have ended without objects to assign, have: "<<nResObjToAss;
102 
103  return
104  res;
105  }
106 
107  public:
108 
109  /// Check if it is possible to combine the nObj into the slots
110  static bool isPossibleToAccomodate(const Vector<Int>& nMaxPerSlot, ///< Maximal number of objects per slot
111  const int nObj) ///< Number of objects
112  {
113  return
114  nMaxPerSlot.summatorial()>=nObj;
115  }
116 
117  /// Check if the slot is free
118  bool isSlotFree(const Slot& iSlot)
119  const
120  {
121  return
122  nPerSlot[iSlot]==0;
123  }
124 
125  /// Check if the slot is fully occupied
126  bool isSlotFullyOccupied(const Slot& iSlot)
127  const
128  {
129  return
130  nPerSlot[iSlot]==nMaxPerSlot[iSlot];
131  }
132 
133  /// Check if 1 object can be moved from the slot to the right one
134  bool canBeMovedRight(const Slot& iSlot)
135  const
136  {
137  return
138  (not (isSlotFullyOccupied(iSlot+1) or isSlotFree(iSlot)));
139  }
140 
141  /// Move right one element from the slot
142  void moveRight(const Slot& iSlot)
143  {
144  nPerSlot[iSlot]--;
145  nPerSlot[iSlot+1]++;
146  }
147 
148  /// Check if 1 object can be moved from the slot to the left one
149  bool canBeMovedLeft(const Slot& iSlot)
150  const
151  {
152  return
153  (not (isSlotFullyOccupied(iSlot-1) or isSlotFree(iSlot)));
154  }
155 
156  /// Move left one element from the slot
157  void moveLeft(const Slot& iSlot)
158  {
159  nPerSlot[iSlot-1]++;
160  nPerSlot[iSlot]--;
161  }
162 
163  /// Go to next combo, returning true if was possible to do it
164  bool advance()
165  {
166  /// Slot to move
167  Slot iSlot=
168  0;
169 
170  /// Returned value
171  bool found=
172  false;
173 
174  /// Number of object up to the slot
175  Int nUpToSlot=
176  0;
177 
178  while(not found and iSlot<nSlots()-1)
179  {
180  found=
181  canBeMovedRight(iSlot);
182 
183  nUpToSlot+=
184  nPerSlot[iSlot];
185 
186  if(not found)
187  iSlot++;
188  }
189 
190  if(found)
191  {
192  nPerSlot[iSlot+1]++;
193  assignToSlots(nPerSlot,nUpToSlot-1,0,iSlot+1,+1);
194  }
195 
196  return
197  found;
198  }
199 
200  /// Go to previous combo, returning true if was possible to do it
201  bool rewind()
202  {
203  /// Slot to move
204  Slot iSlot=
205  1;
206 
207  /// Returned value
208  bool found=
209  false;
210 
211  /// Number of object before the slot
212  Int nUpToPreviousSlot=
213  0;
214 
215  while(not found and iSlot<nSlots())
216  {
217  found=
218  canBeMovedLeft(iSlot);
219 
220  nUpToPreviousSlot+=
221  nPerSlot[iSlot-1];
222 
223  if(not found)
224  iSlot++;
225  }
226 
227  if(found)
228  {
229  nPerSlot[iSlot]--;
230  assignToSlots(nPerSlot,nUpToPreviousSlot+1,0,iSlot,+1);
231  }
232 
233  return
234  found;
235  }
236 
237  /// Go backward or advance depending on the passed parameter
238  bool rewindOrAdvance(const BACK_FORW& BackForw)
239  {
240  if(BackForw==BACK)
241  return
242  rewind();
243  else
244  return
245  advance();
246  }
247 
248  /// Const cast to the combinatorial
249  const Vector<Int>& operator()()
250  const
251  {
252  return
253  nPerSlot;
254  }
255 
257 
258  /// Get the firt combo
260  const
261  {
262  return
263  getFirstOrLast(FIRST);
264  }
265 
266  /// Set to first combo
267  void setToFirst()
268  {
269  nPerSlot=
270  getFirst();
271  }
272 
273  /// Set to last combo
274  void setToLast()
275  {
276  nPerSlot=
277  getLast();
278  }
279 
280  /// Get the last combo
282  const
283  {
284  return
285  getFirstOrLast(LAST);
286  }
287 
288  /// Number of slots
289  Slot nSlots()
290  const
291  {
292  return
293  nMaxPerSlot.size();
294  }
295 
296  /// Maximal number of objects that can be accommodated
297  Int nMaxObj()
298  const
299  {
300  return
301  nMaxPerSlot.summatorial();
302  }
303 
304  /// Constructor specifying the maximal number of objects per slot
305  Combinatorial(const Vector<Int>& nMaxPerSlot, ///< Maximal number of objects per slot
306  const int nObj, ///< Number of objects
307  const bool lastOrFirst=false) ///< Starts from first or last combo
308  : nMaxPerSlot(nMaxPerSlot),nObj(nObj)
309  {
310  // Check that the slots can accommodate the objects
311  if(not isPossibleToAccomodate(nMaxPerSlot,nObj))
312  CRASH<<"Can accommodate at most "<<nMaxObj()<<" objects but "<<nObj<<" asked";
313 
314  // Set first
315  if(lastOrFirst==false)
316  setToFirst();
317  else
318  setToLast();
319  }
320  };
321 
322  /// Loop on all combinations
323  ///
324  /// Run the provided function, passing the combo at each iteration,
325  /// and returning the total number of combinations. If BackForw is
326  /// true, loop forward, otherwise loop backward
327  template <typename Int,
328  typename Fun>
329  Int loopOnAllCombinations(const Vector<Int>& nMaxPerSlot, ///< Maximal number of objects per slot
330  const int nObj, ///< Number of objects
331  const Fun& fun, ///< Function to be called
332  BACK_FORW BackForw=FORW) ///< Loop direction
333  {
334  /// Count
335  Int n=
336  0;
337 
338  if(Combinatorial<Int>::isPossibleToAccomodate(nMaxPerSlot,nObj))
339  {
340 
341 
342  /// Combination generator
343  Combinatorial<Int> combo(nMaxPerSlot,nObj,
344  ((BackForw==FORW)?
345  FIRST:
346  LAST));
347 
348  if(BackForw==BACK)
349  combo.setToLast();
350  // else ... not needed, combo is automaticall
351 
352  do
353  {
354  fun(combo());
355  n++;
356  }
357  while(combo.rewindOrAdvance(BackForw));
358  }
359 
360  return
361  n;
362  }
363 
364  /// Loop on all submultiple of the passed number
365  ///
366  /// Run the provided function, passing the submultiple at each iteration,
367  /// and returning the total number of submultiple
368  template <typename I,
369  typename F>
370  I loopOnAllSubmultiplesOf(const I& i, ///< Number on which to run the loop
371  const F& fun, ///< Function to run at each iteration
372  const BACK_FORW& backForw=FORW) ///< Loop direction
373  {
374  if(i==1)
375  {
376  fun(i);
377 
378  return
379  1;
380  }
381 
382  /// List all factors
383  const Vector<I> facts=
384  factorize(i);
385 
386  /// Independent factors and maximal number they can be taken
387  const std::map<I,I> indepFactsAndMax=
388  facts.group();
389 
390  /// Independent factors
391  const Vector<I> indepFacts=
392  getAllKeys(indepFactsAndMax);
393 
394  /// Maximal number of times a factor can be taken
395  const Vector<I> nMaxPerFact=
396  getAllVal(indepFactsAndMax);
397 
398  /// Number of factors
399  const int nFacts=
400  facts.size();
401 
402  /// Number of independent factors
403  const int nIndepFacts=
404  indepFacts.size();
405 
406  /// Number of submultiples
407  I nSubMultiples=
408  0;
409 
410  for(int nFactsTaken=0;
411  nFactsTaken<=nFacts;
412  nFactsTaken++)
413  nSubMultiples+=
414  loopOnAllCombinations(nMaxPerFact,
415  nFactsTaken,
416  [&fun,&nIndepFacts,&indepFacts](const Vector<I>& takenPerIndepFact)
417  {
418  /// Multiple
419  I val=
420  1;
421 
422  for(I iIndepFact=0;
423  iIndepFact<nIndepFacts;
424  iIndepFact++)
425  for(I iTaken=1;
426  iTaken<=takenPerIndepFact[iIndepFact];
427  iTaken++)
428  val*=
429  indepFacts[iIndepFact];
430 
431  fun(val);
432  },
433  backForw);
434 
435  return
436  nSubMultiples;
437  }
438 
439  /// Loop on all additive partitioning of \c n
440  ///
441  /// Example:
442  ///
443  /// \code
444  ///
445  /// loopOnAllAdditivePartitioningOf(2,7,foo); /// foo(0,7);foo(1,6);...
446  ///
447  /// \endcode
448  template <typename Fun,
449  typename INpart,
450  typename INToPart>
451  auto loopOnAllAdditivePartitioningOf(const INpart& nOfPart, ///< Number of partitions
452  const INToPart& nToPart, ///< Number to partition
453  const Fun& fun) ///< Function invocated
454  {
455  return
456  loopOnAllCombinations(Vector<INToPart>(nOfPart,nToPart),nToPart,fun);
457  }
458 }
459 
460 #endif
void moveLeft(const Slot &iSlot)
Move left one element from the slot.
Int loopOnAllCombinations(const Vector< Int > &nMaxPerSlot, const int nObj, const Fun &fun, BACK_FORW BackForw=FORW)
Combinatorial(const Vector< Int > &nMaxPerSlot, const int nObj, const bool lastOrFirst=false)
Constructor specifying the maximal number of objects per slot.
Int nObj
Number of objects.
#define CRASH
Initialize the crasher.
Definition: Crash.hpp:13
Int nMaxObj() const
Maximal number of objects that can be accommodated.
bool canBeMovedRight(const Slot &iSlot) const
Check if 1 object can be moved from the slot to the right one.
bool isSlotFullyOccupied(const Slot &iSlot) const
Check if the slot is fully occupied.
bool canBeMovedLeft(const Slot &iSlot) const
Check if 1 object can be moved from the slot to the left one.
BACK_FORW
Enumerate the possibity to loop backward or forward.
Definition: Position.hpp:41
I loopOnAllSubmultiplesOf(const I &i, const F &fun, const BACK_FORW &backForw=FORW)
decltype(auto) operator()(Ts &&...ts)
Vector< Int > getFirst() const
Get the firt combo.
void divWithMod(Vector< TOut > &quotient, Vector< TOut > &remainder, const Vector &divisor) const
Returns the result and remainder of the division.
Definition: Vector.hpp:310
bool isSlotFree(const Slot &iSlot) const
Check if the slot is free.
void setToLast()
Set to last combo.
Vector< Int > nMaxPerSlot
Maximal number of objects per slot.
bool rewind()
Go to previous combo, returning true if was possible to do it.
Vector< Int > getLast() const
Get the last combo.
static bool isPossibleToAccomodate(const Vector< Int > &nMaxPerSlot, const int nObj)
Check if it is possible to combine the nObj into the slots.
const Vector< Int > & operator()() const
Const cast to the combinatorial.
FIRST_OR_LAST
Enumerate first and last.
Definition: Position.hpp:38
void moveRight(const Slot &iSlot)
Move right one element from the slot.
#define PROVIDE_ALSO_NON_CONST_METHOD(NAME)
Definition: TypeTraits.hpp:376
void setToFirst()
Set to first combo.
Vector< Int > getFirstOrLast(const FIRST_OR_LAST &firstLast) const
Get the first or last combo.
bool rewindOrAdvance(const BACK_FORW &BackForw)
Go backward or advance depending on the passed parameter.
auto loopOnAllAdditivePartitioningOf(const INpart &nOfPart, const INToPart &nToPart, const Fun &fun)
Slot nSlots() const
Number of slots.
bool advance()
Go to next combo, returning true if was possible to do it.
Vector< Int > nPerSlot
Current combination.
Int assignToSlots(Vector< Int > &res, Int nObjToAss, const Slot &firstSlot, const Slot &pastLastSlot, const Slot &dSlot) const