Release 1.39.0
[boost.git] / Boost_1_39_0 / libs / math / test / test_erf.cpp
blob70880439c14bfddae186623bb374e5880ee295e3
1 // Copyright John Maddock 2006.
2 // Copyright Paul A. Bristow 2007
3 // Use, modification and distribution are subject to the
4 // Boost Software License, Version 1.0. (See accompanying file
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 #include <boost/math/concepts/real_concept.hpp>
8 #include <boost/test/included/test_exec_monitor.hpp>
9 #include <boost/test/floating_point_comparison.hpp>
10 #include <boost/math/special_functions/erf.hpp>
11 #include <boost/math/constants/constants.hpp>
12 #include <boost/type_traits/is_floating_point.hpp>
13 #include <boost/array.hpp>
14 #include "functor.hpp"
16 #include "test_erf_hooks.hpp"
17 #include "handle_test_result.hpp"
20 // DESCRIPTION:
21 // ~~~~~~~~~~~~
23 // This file tests the functions erf, erfc, and the inverses
24 // erf_inv and erfc_inv. There are two sets of tests, spot
25 // tests which compare our results with selected values computed
26 // using the online special function calculator at
27 // functions.wolfram.com, while the bulk of the accuracy tests
28 // use values generated with NTL::RR at 1000-bit precision
29 // and our generic versions of these functions.
31 // Note that when this file is first run on a new platform many of
32 // these tests will fail: the default accuracy is 1 epsilon which
33 // is too tight for most platforms. In this situation you will
34 // need to cast a human eye over the error rates reported and make
35 // a judgement as to whether they are acceptable. Either way please
36 // report the results to the Boost mailing list. Acceptable rates of
37 // error are marked up below as a series of regular expressions that
38 // identify the compiler/stdlib/platform/data-type/test-data/test-function
39 // along with the maximum expected peek and RMS mean errors for that
40 // test.
43 void expected_results()
46 // Define the max and mean errors expected for
47 // various compilers and platforms.
49 const char* largest_type;
50 #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
51 if(boost::math::policies::digits<double, boost::math::policies::policy<> >() == boost::math::policies::digits<long double, boost::math::policies::policy<> >())
53 largest_type = "(long\\s+)?double|real_concept";
55 else
57 largest_type = "long double|real_concept";
59 #else
60 largest_type = "(long\\s+)?double";
61 #endif
63 // On MacOS X erfc has much higher error levels than
64 // expected: given that the implementation is basically
65 // just a rational function evaluation combined with
66 // exponentiation, we conclude that exp and pow are less
67 // accurate on this platform, especially when the result
68 // is outside the range of a double.
70 add_expected_result(
71 ".*", // compiler
72 ".*", // stdlib
73 "Mac OS", // platform
74 largest_type, // test type(s)
75 "Erf Function:.*Large.*", // test data group
76 "boost::math::erfc", 4300, 1300); // test function
77 add_expected_result(
78 ".*", // compiler
79 ".*", // stdlib
80 "Mac OS", // platform
81 largest_type, // test type(s)
82 "Erf Function:.*", // test data group
83 "boost::math::erfc", 40, 10); // test function
85 add_expected_result(
86 ".*", // compiler
87 ".*", // stdlib
88 ".*", // platform
89 "real_concept", // test type(s)
90 "Erf Function:.*", // test data group
91 "boost::math::erfc?", 20, 6); // test function
92 add_expected_result(
93 ".*", // compiler
94 ".*", // stdlib
95 ".*", // platform
96 "real_concept", // test type(s)
97 "Inverse Erfc.*", // test data group
98 "boost::math::erfc_inv", 80, 10); // test function
102 // Catch all cases come last:
104 add_expected_result(
105 ".*", // compiler
106 ".*", // stdlib
107 ".*", // platform
108 ".*", // test type(s)
109 "Erf Function:.*", // test data group
110 "boost::math::erfc?", 2, 2); // test function
111 add_expected_result(
112 ".*aCC.*", // compiler
113 ".*", // stdlib
114 ".*", // platform
115 ".*", // test type(s)
116 "Inverse Erfc.*", // test data group
117 "boost::math::erfc_inv", 80, 10); // test function
118 add_expected_result(
119 ".*", // compiler
120 ".*", // stdlib
121 ".*", // platform
122 ".*", // test type(s)
123 "Inverse Erf.*", // test data group
124 "boost::math::erfc?_inv", 18, 4); // test function
127 // Finish off by printing out the compiler/stdlib/platform names,
128 // we do this to make it easier to mark up expected error rates.
130 std::cout << "Tests run with " << BOOST_COMPILER << ", "
131 << BOOST_STDLIB << ", " << BOOST_PLATFORM << std::endl;
134 template <class T>
135 void do_test_erf(const T& data, const char* type_name, const char* test_name)
137 typedef typename T::value_type row_type;
138 typedef typename row_type::value_type value_type;
140 typedef value_type (*pg)(value_type);
141 #if defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
142 pg funcp = boost::math::erf<value_type>;
143 #else
144 pg funcp = boost::math::erf;
145 #endif
147 boost::math::tools::test_result<value_type> result;
149 std::cout << "Testing " << test_name << " with type " << type_name
150 << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
153 // test erf against data:
155 result = boost::math::tools::test(
156 data,
157 bind_func(funcp, 0),
158 extract_result(1));
159 handle_test_result(result, data[result.worst()], result.worst(), type_name, "boost::math::erf", test_name);
160 #ifdef TEST_OTHER
161 if(::boost::is_floating_point<value_type>::value){
162 funcp = other::erf;
163 result = boost::math::tools::test(
164 data,
165 bind_func(funcp, 0),
166 extract_result(1));
167 print_test_result(result, data[result.worst()], result.worst(), type_name, "other::erf");
169 #endif
171 // test erfc against data:
173 #if defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
174 funcp = boost::math::erfc<value_type>;
175 #else
176 funcp = boost::math::erfc;
177 #endif
178 result = boost::math::tools::test(
179 data,
180 bind_func(funcp, 0),
181 extract_result(2));
182 handle_test_result(result, data[result.worst()], result.worst(), type_name, "boost::math::erfc", test_name);
183 #ifdef TEST_OTHER
184 if(::boost::is_floating_point<value_type>::value){
185 funcp = other::erfc;
186 result = boost::math::tools::test(
187 data,
188 bind(funcp, 0),
189 extract_result(2));
190 print_test_result(result, data[result.worst()], result.worst(), type_name, "other::erfc");
192 #endif
193 std::cout << std::endl;
196 template <class T>
197 void do_test_erf_inv(const T& data, const char* type_name, const char* test_name)
199 typedef typename T::value_type row_type;
200 typedef typename row_type::value_type value_type;
202 typedef value_type (*pg)(value_type);
204 boost::math::tools::test_result<value_type> result;
205 std::cout << "Testing " << test_name << " with type " << type_name
206 << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
208 // test erf_inv against data:
210 #if defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
211 pg funcp = boost::math::erf_inv<value_type>;
212 #else
213 pg funcp = boost::math::erf_inv;
214 #endif
215 result = boost::math::tools::test(
216 data,
217 bind_func(funcp, 0),
218 extract_result(1));
219 handle_test_result(result, data[result.worst()], result.worst(), type_name, "boost::math::erf_inv", test_name);
220 std::cout << std::endl;
223 template <class T>
224 void do_test_erfc_inv(const T& data, const char* type_name, const char* test_name)
226 typedef typename T::value_type row_type;
227 typedef typename row_type::value_type value_type;
229 typedef value_type (*pg)(value_type);
230 #if defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
231 pg funcp = boost::math::erf<value_type>;
232 #else
233 pg funcp = boost::math::erf;
234 #endif
236 boost::math::tools::test_result<value_type> result;
237 std::cout << "Testing " << test_name << " with type " << type_name
238 << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
240 // test erfc_inv against data:
242 #if defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
243 funcp = boost::math::erfc_inv<value_type>;
244 #else
245 funcp = boost::math::erfc_inv;
246 #endif
247 result = boost::math::tools::test(
248 data,
249 bind_func(funcp, 0),
250 extract_result(1));
251 handle_test_result(result, data[result.worst()], result.worst(), type_name, "boost::math::erfc_inv", test_name);
252 std::cout << std::endl;
255 template <class T>
256 void test_erf(T, const char* name)
259 // The actual test data is rather verbose, so it's in a separate file
261 // The contents are as follows, each row of data contains
262 // three items, input value a, input value b and erf(a, b):
264 # include "erf_small_data.ipp"
266 do_test_erf(erf_small_data, name, "Erf Function: Small Values");
268 # include "erf_data.ipp"
270 do_test_erf(erf_data, name, "Erf Function: Medium Values");
272 # include "erf_large_data.ipp"
274 do_test_erf(erf_large_data, name, "Erf Function: Large Values");
276 # include "erf_inv_data.ipp"
278 do_test_erf_inv(erf_inv_data, name, "Inverse Erf Function");
280 # include "erfc_inv_data.ipp"
282 do_test_erfc_inv(erfc_inv_data, name, "Inverse Erfc Function");
284 # include "erfc_inv_big_data.ipp"
286 if(std::numeric_limits<T>::min_exponent <= -4500)
288 do_test_erfc_inv(erfc_inv_big_data, name, "Inverse Erfc Function: extreme values");
292 template <class T>
293 void test_spots(T, const char* t)
295 std::cout << "Testing basic sanity checks for type " << t << std::endl;
297 // basic sanity checks, tolerance is 10 epsilon expressed as a percentage:
299 T tolerance = boost::math::tools::epsilon<T>() * 1000;
300 BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(0.125)), static_cast<T>(0.85968379519866618260697055347837660181302041685015L), tolerance);
301 BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(0.5)), static_cast<T>(0.47950012218695346231725334610803547126354842424204L), tolerance);
302 BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(1)), static_cast<T>(0.15729920705028513065877936491739074070393300203370L), tolerance);
303 BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(5)), static_cast<T>(1.5374597944280348501883434853833788901180503147234e-12L), tolerance);
304 BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(-0.125)), static_cast<T>(1.1403162048013338173930294465216233981869795831498L), tolerance);
305 BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(-0.5)), static_cast<T>(1.5204998778130465376827466538919645287364515757580L), tolerance);
306 BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(0)), static_cast<T>(1), tolerance);
308 BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(0.125)), static_cast<T>(0.14031620480133381739302944652162339818697958314985L), tolerance);
309 BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(0.5)), static_cast<T>(0.52049987781304653768274665389196452873645157575796L), tolerance);
310 BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(1)), static_cast<T>(0.84270079294971486934122063508260925929606699796630L), tolerance);
311 BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(5)), static_cast<T>(0.9999999999984625402055719651498116565146166211099L), tolerance);
312 BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(-0.125)), static_cast<T>(-0.14031620480133381739302944652162339818697958314985L), tolerance);
313 BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(-0.5)), static_cast<T>(-0.52049987781304653768274665389196452873645157575796L), tolerance);
314 BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(0)), static_cast<T>(0), tolerance);
316 tolerance = boost::math::tools::epsilon<T>() * 100 * 200; // 200 eps %.
317 #if defined(__CYGWIN__)
318 // some platforms long double is only reliably accurate to double precision:
319 if(sizeof(T) == sizeof(long double))
320 tolerance = boost::math::tools::epsilon<double>() * 100 * 200; // 200 eps %.
321 #endif
323 for(T i = -0.95f; i < 1; i += 0.125f)
325 T inv = boost::math::erf_inv(i);
326 T b = boost::math::erf(inv);
327 BOOST_CHECK_CLOSE(b, i, tolerance);
329 for(T j = 0.125f; j < 2; j += 0.125f)
331 T inv = boost::math::erfc_inv(j);
332 T b = boost::math::erfc(inv);
333 BOOST_CHECK_CLOSE(b, j, tolerance);
337 int test_main(int, char* [])
339 BOOST_MATH_CONTROL_FP;
340 test_spots(0.0F, "float");
341 test_spots(0.0, "double");
342 #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
343 test_spots(0.0L, "long double");
344 #if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
345 test_spots(boost::math::concepts::real_concept(0.1), "real_concept");
346 #endif
347 #endif
349 expected_results();
351 test_erf(0.1F, "float");
352 test_erf(0.1, "double");
353 #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
354 test_erf(0.1L, "long double");
355 #ifndef BOOST_MATH_NO_REAL_CONCEPT_TESTS
356 #if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
357 test_erf(boost::math::concepts::real_concept(0.1), "real_concept");
358 #endif
359 #endif
360 #else
361 std::cout << "<note>The long double tests have been disabled on this platform "
362 "either because the long double overloads of the usual math functions are "
363 "not available at all, or because they are too inaccurate for these tests "
364 "to pass.</note>" << std::cout;
365 #endif
366 return 0;
371 Output:
373 test_erf.cpp
374 Compiling manifest to resources...
375 Linking...
376 Embedding manifest...
377 Autorun "i:\boost-06-05-03-1300\libs\math\test\Math_test\debug\test_erf.exe"
378 Running 1 test case...
379 Testing basic sanity checks for type float
380 Testing basic sanity checks for type double
381 Testing basic sanity checks for type long double
382 Testing basic sanity checks for type real_concept
383 Tests run with Microsoft Visual C++ version 8.0, Dinkumware standard library version 405, Win32
384 Testing Erf Function: Small Values with type float
385 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
386 boost::math::erf<float> Max = 0 RMS Mean=0
387 boost::math::erfc<float> Max = 0 RMS Mean=0
388 Testing Erf Function: Medium Values with type float
389 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
390 boost::math::erf<float> Max = 0 RMS Mean=0
391 boost::math::erfc<float> Max = 0 RMS Mean=0
392 Testing Erf Function: Large Values with type float
393 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
394 boost::math::erf<float> Max = 0 RMS Mean=0
395 boost::math::erfc<float> Max = 0 RMS Mean=0
396 Testing Inverse Erf Function with type float
397 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
398 boost::math::erf_inv<float> Max = 0 RMS Mean=0
399 Testing Inverse Erfc Function with type float
400 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
401 boost::math::erfc_inv<float> Max = 0 RMS Mean=0
402 Testing Erf Function: Small Values with type double
403 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
404 boost::math::erf<double> Max = 0 RMS Mean=0
405 boost::math::erfc<double> Max = 0.7857 RMS Mean=0.06415
406 worst case at row: 149
407 { 0.3343, 0.3636, 0.6364 }
408 Testing Erf Function: Medium Values with type double
409 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
410 boost::math::erf<double> Max = 0.9219 RMS Mean=0.1016
411 worst case at row: 273
412 { 0.5252, 0.5424, 0.4576 }
413 boost::math::erfc<double> Max = 1.08 RMS Mean=0.3224
414 worst case at row: 287
415 { 0.8461, 0.7685, 0.2315 }
416 Testing Erf Function: Large Values with type double
417 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
418 boost::math::erf<double> Max = 0 RMS Mean=0
419 boost::math::erfc<double> Max = 1.048 RMS Mean=0.2032
420 worst case at row: 50
421 { 20.96, 1, 4.182e-193 }
422 Testing Inverse Erf Function with type double
423 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
424 boost::math::erf_inv<double> Max = 1.124 RMS Mean=0.5082
425 worst case at row: 98
426 { 0.9881, 1.779 }
427 Testing Inverse Erfc Function with type double
428 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
429 boost::math::erfc_inv<double> Max = 1.124 RMS Mean=0.5006
430 worst case at row: 98
431 { 1.988, -1.779 }
432 Testing Erf Function: Small Values with type long double
433 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
434 boost::math::erf<long double> Max = 0 RMS Mean=0
435 boost::math::erfc<long double> Max = 0.7857 RMS Mean=0.06415
436 worst case at row: 149
437 { 0.3343, 0.3636, 0.6364 }
438 Testing Erf Function: Medium Values with type long double
439 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
440 boost::math::erf<long double> Max = 0.9219 RMS Mean=0.1016
441 worst case at row: 273
442 { 0.5252, 0.5424, 0.4576 }
443 boost::math::erfc<long double> Max = 1.08 RMS Mean=0.3224
444 worst case at row: 287
445 { 0.8461, 0.7685, 0.2315 }
446 Testing Erf Function: Large Values with type long double
447 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
448 boost::math::erf<long double> Max = 0 RMS Mean=0
449 boost::math::erfc<long double> Max = 1.048 RMS Mean=0.2032
450 worst case at row: 50
451 { 20.96, 1, 4.182e-193 }
452 Testing Inverse Erf Function with type long double
453 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
454 boost::math::erf_inv<long double> Max = 1.124 RMS Mean=0.5082
455 worst case at row: 98
456 { 0.9881, 1.779 }
457 Testing Inverse Erfc Function with type long double
458 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
459 boost::math::erfc_inv<long double> Max = 1.124 RMS Mean=0.5006
460 worst case at row: 98
461 { 1.988, -1.779 }
462 Testing Erf Function: Small Values with type real_concept
463 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
464 boost::math::erf<real_concept> Max = 1.271 RMS Mean=0.5381
465 worst case at row: 144
466 { 0.0109, 0.0123, 0.9877 }
467 boost::math::erfc<real_concept> Max = 0.7857 RMS Mean=0.07777
468 worst case at row: 149
469 { 0.3343, 0.3636, 0.6364 }
470 Testing Erf Function: Medium Values with type real_concept
471 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
472 boost::math::erf<real_concept> Max = 22.5 RMS Mean=4.224
473 worst case at row: 233
474 { -0.7852, -0.7332, 1.733 }
475 Peak error greater than expected value of 20
476 i:/boost-sandbox/math_toolkit/libs/math/test/handle_test_result.hpp(146): error in "test_main_caller( argc, argv )": check bounds.first >= max_error_found failed
477 boost::math::erfc<real_concept> Max = 97.77 RMS Mean=8.373
478 worst case at row: 289
479 { 0.9849, 0.8363, 0.1637 }
480 Peak error greater than expected value of 20
481 i:/boost-sandbox/math_toolkit/libs/math/test/handle_test_result.hpp(146): error in "test_main_caller( argc, argv )": check bounds.first >= max_error_found failed
482 Mean error greater than expected value of 6
483 i:/boost-sandbox/math_toolkit/libs/math/test/handle_test_result.hpp(151): error in "test_main_caller( argc, argv )": check bounds.second >= mean_error_found failed
484 Testing Erf Function: Large Values with type real_concept
485 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
486 boost::math::erf<real_concept> Max = 0 RMS Mean=0
487 boost::math::erfc<real_concept> Max = 1.395 RMS Mean=0.2908
488 worst case at row: 11
489 { 10.99, 1, 1.87e-054 }
490 Testing Inverse Erf Function with type real_concept
491 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
492 boost::math::erf_inv<real_concept> Max = 1.124 RMS Mean=0.5082
493 worst case at row: 98
494 { 0.9881, 1.779 }
495 Testing Inverse Erfc Function with type real_concept
496 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
497 boost::math::erfc_inv<real_concept> Max = 1.124 RMS Mean=0.5006
498 worst case at row: 98
499 { 1.988, -1.779 }
500 Test suite "Test Program" failed with:
501 181 assertions out of 184 passed
502 3 assertions out of 184 failed
503 1 test case out of 1 failed
504 Test case "test_main_caller( argc, argv )" failed with:
505 181 assertions out of 184 passed
506 3 assertions out of 184 failed
507 Build Time 0:15