diff --git a/CEP/DP3/DPPP/include/DPPP/Averager.h b/CEP/DP3/DPPP/include/DPPP/Averager.h index 88c248d0143d60904288170647e687ef5054119f..c3ea2b13936531fd4de4082d3925c325fb9a7a51 100644 --- a/CEP/DP3/DPPP/include/DPPP/Averager.h +++ b/CEP/DP3/DPPP/include/DPPP/Averager.h @@ -65,7 +65,10 @@ namespace LOFAR { // Copy the preAvg flags in the input buffer to the correct // time index in the output buffer. - void copyPreAvgFlags (const casa::Cube<bool>&, int timeIndex); + // If a flag is set, set all flags in corresponding PreAvg window. + void copyPreAvgFlags (const casa::Cube<bool>& preAvgFlags, + const casa::Cube<bool>& flags, + int timeIndex); //# Data members. DPInput* itsInput; diff --git a/CEP/DP3/DPPP/include/DPPP/DPBuffer.h b/CEP/DP3/DPPP/include/DPPP/DPBuffer.h index 11d55b787b23e2a1af93e7bb28703e63e362a144..2ddcb819613209092b1f398cfa91e4925068d7e6 100644 --- a/CEP/DP3/DPPP/include/DPPP/DPBuffer.h +++ b/CEP/DP3/DPPP/include/DPPP/DPBuffer.h @@ -97,6 +97,8 @@ namespace LOFAR { { itsUVW.reference (uvw); } const casa::Matrix<double>& getUVW() const { return itsUVW; } + casa::Matrix<double>& getUVW() + { return itsUVW; } bool hasNoFlags() const { return itsFlags.empty(); } diff --git a/CEP/DP3/DPPP/src/Averager.cc b/CEP/DP3/DPPP/src/Averager.cc index fbd073c6bd1ef6a3f688f9612a25c8bb85c61136..e5c8c483ace639f5d60d3ab0b3f5cd0c041ed658 100644 --- a/CEP/DP3/DPPP/src/Averager.cc +++ b/CEP/DP3/DPPP/src/Averager.cc @@ -67,93 +67,79 @@ namespace LOFAR { bool Averager::process (const DPBuffer& buf) { RefRows rowNrs(buf.getRowNrs()); - // If first time, resize and clear the buffer. if (itsNTimes == 0) { // The first time we assign because that is faster than first clearing // and adding thereafter. + itsBuf.getUVW() = itsInput->fetchUVW (buf, rowNrs); itsBuf.getWeights() = itsInput->fetchWeights (buf, rowNrs); itsBuf.getData() = buf.getData(); - IPosition shapeIn = buf.getData().shape(); + IPosition shapeIn = buf.getData().shape(); itsNPoints.resize (shapeIn); // Take care of the preAvg flags. - // If not averaging in time, we can simply use them from the input. - // Note that this is possible because we do not keep nChanAvg in the - // preAvgFlags cube and because AverageInfo.cc checks if averaging - // in channel fits integrally. - if (itsNTimeAvg == 1) { - itsBuf.setPreAvgFlags (itsInput->fetchPreAvgFlags (buf, rowNrs)); - } else { - // We have to shape the output array and copy to a part of it. - Array<bool> preAvgFlags(itsInput->fetchPreAvgFlags (buf, rowNrs)); - IPosition ofShape = preAvgFlags.shape(); - ofShape[1] *= itsNTimeAvg; // more time entries, same chan and bl - // Make it unique in case PreAvg is referenced elsewhere. - itsBuf.getPreAvgFlags().unique(); - itsBuf.getPreAvgFlags().resize (ofShape); - itsBuf.getPreAvgFlags() = true; // initialize for times missing at end - copyPreAvgFlags (preAvgFlags, 0); - } + // We have to shape the output array and copy to a part of it. + Cube<bool> preAvgFlags(itsInput->fetchPreAvgFlags (buf, rowNrs)); + IPosition ofShape = preAvgFlags.shape(); + ofShape[1] *= itsNTimeAvg; // more time entries, same chan and bl + // Make it unique in case PreAvg is referenced elsewhere. + itsBuf.getPreAvgFlags().unique(); + itsBuf.getPreAvgFlags().resize (ofShape); + itsBuf.getPreAvgFlags() = true; // initialize for times missing at end + copyPreAvgFlags (preAvgFlags, buf.getFlags(), 0); // Set middle of new interval. double time = buf.getTime() + 0.5*(itsNTimeAvg-1)*itsTimeInterval; itsBuf.setTime (time); // Only set. itsNPoints = 1; - if (! buf.hasNoFlags()) { - Array<bool>::const_contiter infIter = buf.getFlags().cbegin(); - Array<Complex>::contiter dataIter = itsBuf.getData().cbegin(); - Array<float>::contiter wghtIter = itsBuf.getWeights().cbegin(); - Array<int>::contiter outnIter = itsNPoints.cbegin(); - Array<int>::contiter outnIterEnd = itsNPoints.cend(); - while (outnIter != outnIterEnd) { - if (*infIter) { - // Flagged data point - *outnIter = 0; - *dataIter = Complex(); - *wghtIter = 0; - } else { - // Weight the data point - *dataIter *= *wghtIter; - } - ++infIter; - ++dataIter; - ++wghtIter; - ++outnIter; + // Set flagged points to zero. + Array<bool>::const_contiter infIter = buf.getFlags().cbegin(); + Array<Complex>::contiter dataIter = itsBuf.getData().cbegin(); + Array<float>::contiter wghtIter = itsBuf.getWeights().cbegin(); + Array<int>::contiter outnIter = itsNPoints.cbegin(); + Array<int>::contiter outnIterEnd = itsNPoints.cend(); + while (outnIter != outnIterEnd) { + if (*infIter) { + // Flagged data point + *outnIter = 0; + *dataIter = Complex(); + *wghtIter = 0; + } else { + // Weigh the data point + *dataIter *= *wghtIter; } + ++infIter; + ++dataIter; + ++wghtIter; + ++outnIter; } } else { // Not the first time. // For now we assume that all timeslots have the same nr of baselines, // so check if the buffer sizes are the same. ASSERT (itsBuf.getData().shape() == buf.getData().shape()); - copyPreAvgFlags (itsInput->fetchPreAvgFlags(buf, rowNrs), itsNTimes); + itsBuf.getUVW() += itsInput->fetchUVW (buf, rowNrs); + copyPreAvgFlags (itsInput->fetchPreAvgFlags(buf, rowNrs), + buf.getFlags(), itsNTimes); Cube<float> weights(itsInput->fetchWeights(buf, rowNrs)); - if (buf.hasNoFlags()) { - // No flags, so we can simply add. - itsBuf.getData() += buf.getData() * weights; - itsBuf.getWeights() += weights; - itsNPoints += 1; - } else { - // Ignore flagged points. - Array<Complex>::const_contiter indIter = buf.getData().cbegin(); - Array<float>::const_contiter inwIter = weights.cbegin(); - Array<bool>::const_contiter infIter = buf.getFlags().cbegin(); - Array<Complex>::contiter outdIter = itsBuf.getData().cbegin(); - Array<float>::contiter outwIter = itsBuf.getWeights().cbegin(); - Array<int>::contiter outnIter = itsNPoints.cbegin(); - Array<int>::contiter outnIterEnd = itsNPoints.cend(); - while (outnIter != outnIterEnd) { - if (!*infIter) { - *outdIter += *indIter * *inwIter; - *outwIter += *inwIter; - (*outnIter)++; - } - ++indIter; - ++inwIter; - ++infIter; - ++outdIter; - ++outwIter; - ++outnIter; + // Ignore flagged points. + Array<Complex>::const_contiter indIter = buf.getData().cbegin(); + Array<float>::const_contiter inwIter = weights.cbegin(); + Array<bool>::const_contiter infIter = buf.getFlags().cbegin(); + Array<Complex>::contiter outdIter = itsBuf.getData().cbegin(); + Array<float>::contiter outwIter = itsBuf.getWeights().cbegin(); + Array<int>::contiter outnIter = itsNPoints.cbegin(); + Array<int>::contiter outnIterEnd = itsNPoints.cend(); + while (outnIter != outnIterEnd) { + if (!*infIter) { + *outdIter += *indIter * *inwIter; + *outwIter += *inwIter; + (*outnIter)++; } + ++indIter; + ++inwIter; + ++infIter; + ++outdIter; + ++outwIter; + ++outnIter; } } // Do the averaging if enough time steps have been processed. @@ -168,11 +154,13 @@ namespace LOFAR { void Averager::finish() { + // Average remaining entries. if (itsNTimes > 0) { DPBuffer buf = average(); getNextStep()->process (buf); itsNTimes = 0; } + // Let the next steps finish. getNextStep()->finish(); } @@ -233,31 +221,59 @@ namespace LOFAR { // Make sure the loops ended correctly. DBGASSERT (indata == itsBuf.getData().data() + itsBuf.getData().size()); DBGASSERT (outdata == buf.getData().data() + buf.getData().size()); + // Set the remaining values in the output buffer. buf.setTime (itsBuf.getTime()); buf.setPreAvgFlags (itsBuf.getPreAvgFlags()); - DPBuffer::mergePreAvgFlags (buf.getPreAvgFlags(), itsBuf.getFlags()); + // The result UVWs are the average of the input. + // If ever needed, UVWCalculator can be used to calculate the UVWs. + buf.setUVW (itsBuf.getUVW() / double(itsNTimes)); return buf; } void Averager::copyPreAvgFlags (const Cube<bool>& preAvgFlags, + const Cube<bool>& flags, int timeIndex) { - // Copy the preAvg flags. + // Copy the preAvg flags to the given index. + // Furthermore the appropriate PreAvg flags are set for a + // flagged data point. It can be the case that an input data point + // has been averaged before, thus has fewer channels than PreAvgFlags. // nrchan and nrbl are the same for in and out. - // nrtimout is a multiple of nrtimin. - IPosition shapeIn = preAvgFlags.shape(); + // nrtimout is a multiple of nrtimavg. + IPosition shapeIn = preAvgFlags.shape(); IPosition shapeOut = itsBuf.getPreAvgFlags().shape(); - uint nrchan = shapeIn[0]; - uint nrtimin= shapeIn[1]; - uint nrtimout = shapeOut[1]; - uint nrbl = shapeIn[2]; + IPosition shapeFlg = flags.shape(); + uint nchan = shapeIn[0]; // original nr of channels + uint ntimavg = shapeIn[1]; // nr of averaged times in input data + uint nchanavg = nchan / shapeFlg[1]; // nr of avg chan in input data + uint ntimout = shapeOut[1]; // nr of averaged times in output data + uint nbl = shapeIn[2]; // nr of baselines + uint ncorr = shapeFlg[0]; // nr of correlations (in FLAG) // in has to be copied to the correct time index in out. - bool* outPtr = itsBuf.getPreAvgFlags().data() + nrchan*nrtimin*timeIndex; - const bool* inPtr = preAvgFlags.data(); - for (uint i=0; i<nrbl; ++i) { - memcpy (outPtr, inPtr, nrchan*nrtimin*sizeof(bool)); - outPtr += nrchan*nrtimout; - inPtr += nrchan*nrtimin; + bool* outPtr = itsBuf.getPreAvgFlags().data() + nchan*ntimavg*timeIndex; + const bool* inPtr = preAvgFlags.data(); + const bool* flagPtr = flags.data(); + for (uint k=0; k<nbl; ++k) { + memcpy (outPtr, inPtr, nchan*ntimavg*sizeof(bool)); + // Applying the flags only needs to be done if the input data + // was already averaged before. + if (ntimavg > 1 && nchanavg > 1) { + for (int j=0; j<shapeFlg[1]; ++j) { + // If a data point is flagged, the flags in the corresponding + // PreAvg window have to be set. + // Only look at the flags of the first correlation. + if (*flagPtr) { + bool* avgPtr = outPtr + j*nchanavg; + for (uint i=0; i<ntimavg; ++i) { + std::fill (avgPtr, avgPtr+nchanavg, true); + avgPtr += nchan; + } + } + flagPtr += ncorr; + } + } + outPtr += nchan*ntimout; + inPtr += nchan*ntimavg; } } diff --git a/CEP/DP3/DPPP/src/DPBuffer.cc b/CEP/DP3/DPPP/src/DPBuffer.cc index 1d7bbd087553445470354eaca15ffbbd79c89d59..956b1ab565699921dd34a1a6b4b040313c0a5093 100644 --- a/CEP/DP3/DPPP/src/DPBuffer.cc +++ b/CEP/DP3/DPPP/src/DPBuffer.cc @@ -45,6 +45,7 @@ namespace LOFAR { itsAmpl.reference (that.itsAmpl); itsFlags.reference (that.itsFlags); itsWeights.reference (that.itsWeights); + itsUVW.reference (that.itsUVW); itsPreAvgFlags.reference (that.itsPreAvgFlags); } return *this; diff --git a/CEP/DP3/DPPP/test/tAverager.cc b/CEP/DP3/DPPP/test/tAverager.cc index a1e776c5ce7f85ce8b13600b9c32bf246cf03b2c..2e060a037df3b255920f8fc8382f1698c2d2b831 100644 --- a/CEP/DP3/DPPP/test/tAverager.cc +++ b/CEP/DP3/DPPP/test/tAverager.cc @@ -37,6 +37,7 @@ using namespace LOFAR::DPPP; using namespace casa; using namespace std; + // Simple class to generate input arrays. // It can only set all flags to true or all false. // Weights are always 1. @@ -73,6 +74,9 @@ private: Cube<bool> preAvgFlags(itsNChan, 1, itsNBl); preAvgFlags = itsFlag; buf.setPreAvgFlags (preAvgFlags); + Matrix<double> uvw(3,itsNBl); + indgen (uvw, double(itsCount*100)); + buf.setUVW (uvw); getNextStep()->process (buf); ++itsCount; return true; @@ -145,6 +149,11 @@ private: ASSERT (near(buf.getTime(), 2+5*(itsCount*itsNAvgTime + (itsNAvgTime-1)/2.))); ASSERT (allNear(buf.getWeights(), resultw, 1e-5)); + if (navgtime == itsNAvgTime) { + Matrix<double> uvw(3,itsNBl); + indgen (uvw, 100*(itsCount*itsNAvgTime + 0.5*(itsNAvgTime-1))); + ASSERT (allNear(buf.getUVW(), uvw, 1e-5)); + } ///cout <<buf.getPreAvgFlags()<< preAvgFlags; ASSERT (allEQ(buf.getPreAvgFlags(), preAvgFlags)); ++itsCount; @@ -171,10 +180,10 @@ private: // More elaborate class which can set different flags and weights. -class TestInput2: public DPInput +class TestInput3: public DPInput { public: - TestInput2(int nrtime, int nrbl, int nrchan, int nrcorr) + TestInput3(int nrtime, int nrbl, int nrchan, int nrcorr) : itsCount(0), itsNrTime(nrtime), itsNrBl(nrbl), itsNrChan(nrchan), itsNrCorr(nrcorr) { @@ -214,6 +223,12 @@ private: return true; } + virtual casa::Matrix<double> getUVW (const casa::RefRows&) + { + Matrix<double> uvw(3,itsNrBl); + indgen (uvw); + return uvw; + } virtual casa::Cube<bool> getPreAvgFlags (const casa::RefRows&) { return itsPreAvgFlags; @@ -228,13 +243,13 @@ private: Cube<bool> itsPreAvgFlags; }; -// Class to check result of averaging TestInput2. +// Class to check result of averaging TestInput3. // All input must be averaged (in one or more steps) to a single value // per corr/baseline. -class TestOutput2: public DPStep +class TestOutput3: public DPStep { public: - TestOutput2(int nrtime, int nrbl, int nrchan, int nrcorr) + TestOutput3(int nrtime, int nrbl, int nrchan, int nrcorr) : itsNrTime(nrtime), itsNrBl(nrbl), itsNrChan(nrchan), itsNrCorr(nrcorr) {} private: @@ -247,7 +262,7 @@ private: weights = float(0); flags = true; preAvgFlags = true; - // Create data in the same way as in TestInput2. + // Create data in the same way as in TestInput3. for (int it=0; it<itsNrTime; ++it) { int i = 0; for (int ib=0; ib<itsNrBl; ++ib) { @@ -276,6 +291,9 @@ private: ASSERT (allEQ(buf.getFlags(), flags)); ASSERT (near(buf.getTime(), 2.+5*(itsNrTime-1)/2.)); ASSERT (allNear(buf.getWeights(), weights, 1e-5)); + Matrix<double> uvw(3,itsNrBl); + indgen (uvw); + ASSERT (allNear(buf.getUVW(), uvw, 1e-5)); ///cout <<buf.getPreAvgFlags()<< preAvgFlags; ASSERT (allEQ(buf.getPreAvgFlags(), preAvgFlags)); return true; @@ -297,6 +315,121 @@ private: int itsNrTime, itsNrBl, itsNrChan, itsNrCorr; }; +// Simple class to flag every step-th XX point. +class TestFlagger: public DPStep +{ +public: + TestFlagger(int step) + : itsCount(0), itsStep(step) + {} +private: + virtual bool process (const DPBuffer& buf) + { + DPBuffer buf2(buf); + int ncorr = buf2.getFlags().shape()[0]; + int np = buf2.getFlags().size() / ncorr; + bool* flagPtr = buf2.getFlags().data(); + for (int i=0; i<np; ++i) { + if ((i+itsCount)%itsStep == 0) { + ///cout << "flagged " <<itsCount <<' '<< i << endl; + for (int j=0; j<ncorr; ++j) { + flagPtr[i*ncorr + j] = true; + } + } + } + getNextStep()->process (buf2); + ++itsCount; + return true; + } + + virtual void finish() {getNextStep()->finish();} + virtual void show (std::ostream&) {} + + int itsCount, itsStep; +}; + +// Class to check result of averaging and flagging TestInput3. +// First the data are averaged from 8,4 to 4,2, then every step-th point +// is flagged, and finally it is averaged to 1,1. +class TestOutput4: public DPStep +{ +public: + TestOutput4(int nrtime, int nrbl, int nrchan, int nrcorr, int step) + : itsNrTime(nrtime), itsNrBl(nrbl), itsNrChan(nrchan), itsNrCorr(nrcorr), + itsStep(step) + {} +private: + virtual bool process (const DPBuffer& buf) + { + Cube<Complex> result(itsNrCorr,1,itsNrBl); + Cube<float> weights(itsNrCorr,1,itsNrBl); + Cube<bool> flags(itsNrCorr,1,itsNrBl); + Cube<bool> preAvgFlags(itsNrChan,itsNrTime,itsNrBl); + weights = float(0); + flags = true; + preAvgFlags = true; + // Create data in the same way as in TestInput3. + for (int it=0; it<itsNrTime; ++it) { + int i = 0; + for (int ib=0; ib<itsNrBl; ++ib) { + for (int ic=0; ic<itsNrChan; ++ic) { + // TestFlagger flags every step-th point of 2x2 averaged data. + int tf = it/2; // same as itsCount in testFlagger + if (((ib*itsNrChan + ic)/2 + tf) % itsStep == 0) { + ///cout << "out4 flagged "<< tf<<' '<< i/itsNrCorr<<' ' <<ib<<' '<<ic/2 << endl; + i += itsNrCorr; + } else { + for (int ip=0; ip<itsNrCorr; ++ip) { + if ((it+2*ib+3*ic) % 7 != 0) { + float weight = (1 + (it+ib+ic)%5) / 5.; + result(ip,0,ib) += weight * Complex(i+it*10,i-1000+it*6); + weights(ip,0,ib) += weight; + /// cout << result(ip,0,ib) << weight << endl; + flags(ip,0,ib) = false; + preAvgFlags(ic,it,ib) = false; + } + i++; + } + } + } + } + } + for (uint i=0; i<result.size(); ++i) { + if (!flags.data()[i]) { + result.data()[i] /= weights.data()[i]; + } + } + // Check the averaged result. + ///cout << real(buf.getData()) << endl<<real(result); + ASSERT (allNear(real(buf.getData()), real(result), 1e-5)); + ASSERT (allNear(imag(buf.getData()), imag(result), 1e-5)); + ASSERT (allEQ(buf.getFlags(), flags)); + ASSERT (near(buf.getTime(), 2.+5*(itsNrTime-1)/2.)); + ASSERT (allNear(buf.getWeights(), weights, 1e-5)); + Matrix<double> uvw(3,itsNrBl); + indgen (uvw); + ASSERT (allNear(buf.getUVW(), uvw, 1e-5)); + ///cout <<buf.getPreAvgFlags()<< preAvgFlags; + ASSERT (allEQ(buf.getPreAvgFlags(), preAvgFlags)); + return true; + } + + virtual void finish() {} + virtual void show (std::ostream&) {} + virtual void updateAverageInfo (AverageInfo& avgInfo) + { + ASSERT (avgInfo.startChan()==0); + ASSERT (int(avgInfo.origNChan())==itsNrChan); + ASSERT (avgInfo.nchan()==1); + ASSERT (avgInfo.ntime()==1); + ASSERT (avgInfo.timeInterval()==5*itsNrTime); + ASSERT (int(avgInfo.nchanAvg())==itsNrChan); + ASSERT (int(avgInfo.ntimeAvg())==itsNrTime); + } + + int itsNrTime, itsNrBl, itsNrChan, itsNrCorr, itsStep; +}; + // Execute steps. void execute (const DPStep::ShPtr& step1) @@ -359,25 +492,28 @@ void test2(int ntime, int nbl, int nchan, int ncorr, bool flag) // Do tests with weighting and some flagged points. void test3(int nrbl, int nrcorr) { - cout << "test3: ntime=2 nrbl=" << nrbl << " nchan=2 ncorr=" << nrcorr << endl; { + cout << "test3: ntime=2 nrbl=" << nrbl << " nchan=2 ncorr=" << nrcorr + << endl; cout << " navgtime=2 navgchan=2" << endl; // Create the steps. - TestInput2* in = new TestInput2(2, nrbl, 2, nrcorr); + TestInput3* in = new TestInput3(2, nrbl, 2, nrcorr); DPStep::ShPtr step1(in); ParameterSet parset1; parset1.add ("freqstep", "2"); parset1.add ("timestep", "2"); DPStep::ShPtr step2a(new Averager(in, parset1, "")); - DPStep::ShPtr step3(new TestOutput2(2, nrbl, 2, nrcorr)); + DPStep::ShPtr step3(new TestOutput3(2, nrbl, 2, nrcorr)); step1->setNextStep (step2a); step2a->setNextStep (step3); execute (step1); } { + cout << "test3: ntime=4 nrbl=" << nrbl << " nchan=8 ncorr=" << nrcorr + << endl; cout << " [navgtime=2 navgchan=4], [navgtime=2 navgchan=2]" << endl; // Create the steps. - TestInput2* in = new TestInput2(4, nrbl, 8, nrcorr); + TestInput3* in = new TestInput3(4, nrbl, 8, nrcorr); DPStep::ShPtr step1(in); ParameterSet parset1, parset2; parset1.add ("freqstep", "4"); @@ -386,7 +522,7 @@ void test3(int nrbl, int nrcorr) parset2.add ("timestep", "2"); DPStep::ShPtr step2a(new Averager(in, parset1, "")); DPStep::ShPtr step2b(new Averager(in, parset2, "")); - DPStep::ShPtr step3(new TestOutput2(4, nrbl, 8, nrcorr)); + DPStep::ShPtr step3(new TestOutput3(4, nrbl, 8, nrcorr)); step1->setNextStep (step2a); step2a->setNextStep (step2b); step2b->setNextStep (step3); @@ -394,6 +530,35 @@ void test3(int nrbl, int nrcorr) } } +// Do tests with averaging and flagging steps to see if the flags are +// promoted to the PREAVG flags. +void test4(int nrbl, int nrcorr, int flagstep) +{ + { + cout << "test4: ntime=4 nrbl=" << nrbl << " nchan=8 ncorr=" << nrcorr + << endl; + cout << " [navgtime=2 navgchan=2], [flagstep=" << flagstep + << "] [navgtime=2 navgchan=4]" << endl; + // Create the steps. + TestInput3* in = new TestInput3(4, nrbl, 8, nrcorr); + DPStep::ShPtr step1(in); + ParameterSet parset1, parset2; + parset1.add ("freqstep", "2"); + parset1.add ("timestep", "2"); + parset2.add ("freqstep", "4"); + parset2.add ("timestep", "2"); + DPStep::ShPtr step2a(new Averager(in, parset1, "")); + DPStep::ShPtr step2b(new TestFlagger(flagstep)); + DPStep::ShPtr step2c(new Averager(in, parset2, "")); + DPStep::ShPtr step3(new TestOutput4(4, nrbl, 8, nrcorr, flagstep)); + step1->setNextStep (step2a); + step2a->setNextStep (step2b); + step2b->setNextStep (step2c); + step2c->setNextStep (step3); + execute (step1); + } +} + int main() { @@ -406,7 +571,9 @@ int main() test2(10, 3, 32, 2, true); test2(10, 3, 32, 2, false); test3(1, 1); - test3(4, 10); + test3(10, 4); + test4(1, 4, 3); + test4(20, 4, 5); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1;