pgsRealGen::pgsRealGen(const MAPM & min, const MAPM & max, const UCHAR & precision, const bool & sequence, const long & seed) : pgsObjectGen(seed), m_min(wxMin(min, max)), m_max(wxMax(min, max)), m_range(m_max - m_min), m_sequence(sequence) { m_pow = MAPM(10).pow(MAPM(precision)); m_int_max = pgsMapm::pgs_mapm_round(m_range * m_pow) + 1; m_randomizer = is_sequence() ? pgsRandomizer(pnew pgsIntegerGen::pgsSequentialIntGen(m_int_max, m_seed)) : pgsRandomizer(pnew pgsIntegerGen::pgsNormalIntGen(m_int_max, m_seed)); }
/** Rounds this Numeric to the given precision, and rounds a half to even */ Numeric::Ptr ATDecimalOrDerivedImpl::roundHalfToEven(const Numeric::Ptr &precision, const DynamicContext* context) const { ATDecimalOrDerived::Ptr decimal_precision = (const Numeric::Ptr)precision->castAs(this->getPrimitiveTypeIndex(), context); MAPM exp = MAPM(10).pow(((ATDecimalOrDerivedImpl*)(const ATDecimalOrDerived*)decimal_precision)->_decimal); MAPM value = _decimal * exp; bool halfVal = false; // check if we're rounding on a half value if((value-0.5) == (value.floor())) { halfVal = true; } value = _decimal * exp + 0.5; value = value.floor(); // if halfVal make sure what we return has the least significant digit even if (halfVal) { if(value.is_odd()) { value = value - 1; } } value = value / exp; // if integer, return xs:integer, otherwise xs:decimal if(_isInteger) { return context->getItemFactory()->createInteger(value, context); } return context->getItemFactory()->createDecimal(value, context); }
Numeric::Ptr ATFloatOrDerivedImpl::atan(const DynamicContext* context) const { switch (_state) { case NaN: return this; case INF: return newFloat((MM_HALF_PI), context); case NEG_INF: return newFloat(MAPM(MM_HALF_PI).neg(), context); case NEG_NUM: case NUM: return newFloat(_float.atan(), context); default: assert(false); return 0; // should never get here } }
/** Rounds this Numeric to the given precision, and rounds a half to even */ Numeric::Ptr ATFloatOrDerivedImpl::roundHalfToEven(const Numeric::Ptr &precision, const DynamicContext* context) const { switch (_state) { case NaN: return notANumber(context); case INF: return infinity(context); case NEG_INF: return negInfinity(context); case NEG_NUM: case NUM: break; default: { assert(false); return 0; // should never get here } } if (isZero() && isNegative()) return this; ATFloatOrDerived::Ptr float_precision = (const Numeric::Ptr)precision->castAs(this->getPrimitiveTypeIndex(), context); MAPM exp = MAPM(10).pow(((ATFloatOrDerivedImpl*)(const ATFloatOrDerived*)float_precision)->_float); MAPM value = _float * exp; bool halfVal = false; // check if we're rounding on a half value if((value-0.5) == (value.floor())) { halfVal = true; } value = _float * exp + 0.5; value = value.floor(); // if halfVal make sure what we return has the least significant digit even if (halfVal) { if(value.is_odd()) { value = value - 1; } } value = value / exp; // the spec doesn't actually say to do this, but djf believes this is the correct way to handle rounding of -ve values which will result in 0.0E0 // if (value == 0 && isNegative()) // return negZero(context); return newFloat(value, context); }
void pgsTestSuite::test_generator_real(void) { const int nb_iterations = 500; // Generate *unique* numbers between 1 and 2 with 3 digits // Test the expected average: 1.5 { pgsRealGen gen(1, 2, 3, true); TS_ASSERT(gen.is_sequence()); MAPM sum = 0, avg, result; pgsVectorMapm sav; for (int i = 0; i < 1001; i++) { result = MAPM(gen.random().mb_str()); TS_ASSERT(result >= 1 && result <= 2); TS_ASSERT(sav.Index(result) == wxNOT_FOUND); sum += result; sav.push_back(result); } avg = sum / (1001); TS_ASSERT(avg == MAPM("1500e-3")); } // Generate negative and positive *unique* integer numbers (precision == 0) // Expected average is of course 0 { pgsRealGen gen("-50", "50", 0, true); TS_ASSERT(gen.is_sequence()); MAPM sum = 0, result; pgsVectorMapm sav; for (int i = 0; i < 101; i++) { result = MAPM(gen.random().mb_str()); TS_ASSERT(result >= -50 && result <= 50); sum += result; TS_ASSERT(sav.Index(result) == wxNOT_FOUND); sav.push_back(result); } TS_ASSERT(sav.size() == 101 && sum == 0); } // Test that two generators with same seed generate same values { pgsRealGen gen(0, 10000.567890, 6, false, 123456789L); pgsRealGen comparator(0, 10000.567890, 6, false, 123456789L); TS_ASSERT(!gen.is_sequence() && !comparator.is_sequence()); wxString res_cmp; MAPM sum = 0, avg, result; for (int i = 0; i < nb_iterations; i++) { res_cmp = comparator.random(); result = MAPM(gen.random().mb_str()); TS_ASSERT(result >= 0 && result <= "1.0000567890e4"); TS_ASSERT(result == MAPM(res_cmp.mb_str())); sum += result; } avg = sum / nb_iterations; TS_ASSERT(avg > 4500 && avg < 5500); } // Same thing as previous with min > max and seed == 0 // min and max must be swapped // seed must be set to one otherwise we would have zeros only { pgsRealGen gen(10000.567890, 0, 6, true, 0); pgsRealGen comparator(10000.567890, 0, 6, true, 0); TS_ASSERT(gen.is_sequence() && comparator.is_sequence()); wxString res_cmp; MAPM sum = 0, avg, result; for (int i = 0; i < nb_iterations; i++) { res_cmp = comparator.random(); result = MAPM(gen.random().mb_str()); TS_ASSERT(result >= 0 && result <= "1.0000567890e4"); TS_ASSERT(result == MAPM(res_cmp.mb_str())); sum += result; } avg = sum / nb_iterations; TS_ASSERT(avg > 4500 && avg < 5500); } // Generate big numbers with a too low precision and test the string output // Last decimals should not be taken into account { pgsRealGen gen("123456789876.54321", "123456789876.5432134", 5, false); TS_ASSERT(!gen.is_sequence()); MAPM result; for (int i = 0; i < nb_iterations; i++) { result = MAPM(gen.random().mb_str()); TS_ASSERT(result == "123456789876.54321"); } } // Generate numbers with exponents // Test the expected average: 1.5 { pgsRealGen gen("0", "1e100", 0, false); TS_ASSERT(!gen.is_sequence()); MAPM sum = 0, avg, result; for (int i = 0; i < nb_iterations; i++) { result = MAPM(gen.random().mb_str()); TS_ASSERT(result >= "0" && result <= "1e100"); sum += result; } avg = sum / nb_iterations; TS_ASSERT(avg >= "4.5e99" && avg <= "5.5e99"); } // Test copy constructor { // Create a generator and generate values pgsRealGen gen(0, 10000.567890, 6, false); wxString result, res_cmp; for (int i = 0; i < nb_iterations / 2; i++) { result = gen.random(); } // Copy this generator to a new one // Both generators must generate the same values pgsRealGen comparator(gen); TS_ASSERT(!gen.is_sequence() && !comparator.is_sequence()); for (int i = nb_iterations / 2; i < nb_iterations; i++) { result = gen.random(); res_cmp = comparator.random(); TS_ASSERT(result == res_cmp); } } // Test assignment operator { // Create two different generators and generate values pgsRealGen gen(0, 10000.567890, 6, false); pgsRealGen comparator(1, 2, 3, true); wxString result, res_cmp; for (int i = 0; i < nb_iterations / 2; i++) { result = gen.random(); res_cmp = comparator.random(); } // Copy one of the generators to the other one // Both generators must generate the same values comparator = gen; TS_ASSERT(!gen.is_sequence() && !comparator.is_sequence()); for (int i = nb_iterations / 2; i < nb_iterations; i++) { result = gen.random(); res_cmp = comparator.random(); TS_ASSERT(result == res_cmp); } } }
void pgsTestSuite::test_generator_string(void) { const int nb_iterations = 100; // Generate empty strings because of inconsistent arguments { pgsStringGen gen1(0, 0, 10); pgsStringGen gen2(10, 20, 0); // pgsStringGen gen3(-10, -20, 10); // pgsStringGen gen4(10, 20, -10); TS_ASSERT(gen1.random() == wxT("")); TS_ASSERT(gen2.random() == wxT("")); // TS_ASSERT(gen3.random() == wxT("")); // TS_ASSERT(gen4.random() == wxT("")); } // Generate strings with 'a' only { pgsVectorChar chars; chars.Add(wxT('a')); chars.Add(wxT('a')); pgsStringGen gen(2, 2, 3, wxDateTime::GetTimeNow(), chars); for (int i = 0; i < nb_iterations; i++) { TS_ASSERT(gen.random() == wxT("aa aa aa")); } } // Test strings that are in fact numbers // Test the size of generated strings { pgsVectorChar chars; chars.Add(wxT('1')); chars.Add(wxT('2')); chars.Add(wxT('3')); chars.Add(wxT('4')); pgsStringGen gen(5, 5, 1, wxDateTime::GetTimeNow(), chars); wxString result; for (int i = 0; i < nb_iterations; i++) { result = gen.random(); long aux_res; result.ToLong(&aux_res); TS_ASSERT(MAPM(result.mb_str()) == aux_res && result.Length() == 5); } } // Test the size of generated strings { pgsStringGen gen(10, 11, 3, 123); pgsStringGen comparator(10, 11, 3, 123); wxString result; for (int i = 0; i < nb_iterations; i++) { result = gen.random(); TS_ASSERT(result == comparator.random()); TS_ASSERT(result.Length() >= 32 && result.Length() <= 35); } } // Test copy constructor { // Create a generator and generate values pgsStringGen gen(10, 11, 3, 123456789L); wxString result, res_cmp; for (int i = 0; i < nb_iterations / 2; i++) { result = gen.random(); } // Copy this generator to a new one // Both generators must generate the same values pgsStringGen comparator(gen); for (int i = nb_iterations / 2; i < nb_iterations; i++) { result = gen.random(); res_cmp = comparator.random(); TS_ASSERT(result == res_cmp); } } // Test assignment operator { // Create two different generators and generate values pgsStringGen gen(20, 30, 20); pgsStringGen comparator(0, 0, 10); wxString result, res_cmp; for (int i = 0; i < nb_iterations / 2; i++) { result = gen.random(); res_cmp = comparator.random(); } // Copy one of the generators to the other one // Both generators must generate the same values comparator = gen; for (int i = nb_iterations / 2; i < nb_iterations; i++) { result = gen.random(); res_cmp = comparator.random(); TS_ASSERT(result == res_cmp); } } }