From e8351b26c8f8cc538892a8bedbd91931e6eae581 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Wed, 6 Nov 2019 19:31:43 +0000 Subject: [PATCH 01/14] Initial commit of pretty-formatter. --- doc/fp_utilities/formatting.qbk | 179 ++++++ doc/math.qbk | 1 + example/Jamfile.v2 | 21 +- example/formatter_docbook_output.cpp | 84 +++ example/formatter_docbook_output.qbk | 32 ++ example/formatter_docbook_output_test.qbk | 21 + example/formatter_html_output.cpp | 93 +++ example/formatter_html_output.html | 38 ++ example/formatter_latex_output.cpp | 90 +++ example/formatter_latex_output.pdf | Bin 0 -> 24395 bytes example/formatter_latex_output.tex | 44 ++ example/formatter_text_output.cpp | 72 +++ example/formatter_text_output.txt | 25 + include/boost/math/tools/formatting.hpp | 652 ++++++++++++++++++++++ 14 files changed, 1351 insertions(+), 1 deletion(-) create mode 100644 doc/fp_utilities/formatting.qbk create mode 100644 example/formatter_docbook_output.cpp create mode 100644 example/formatter_docbook_output.qbk create mode 100644 example/formatter_docbook_output_test.qbk create mode 100644 example/formatter_html_output.cpp create mode 100644 example/formatter_html_output.html create mode 100644 example/formatter_latex_output.cpp create mode 100644 example/formatter_latex_output.pdf create mode 100644 example/formatter_latex_output.tex create mode 100644 example/formatter_text_output.cpp create mode 100644 example/formatter_text_output.txt create mode 100644 include/boost/math/tools/formatting.hpp diff --git a/doc/fp_utilities/formatting.qbk b/doc/fp_utilities/formatting.qbk new file mode 100644 index 0000000000..b1673e228c --- /dev/null +++ b/doc/fp_utilities/formatting.qbk @@ -0,0 +1,179 @@ +[section:formatting Numeric Formatters (Pretty Printers)] + +[heading Synopsis] + + #include + + enum output_format_t + { + text_format, + docbook_format, + latex_format, + html_format + }; + + template > + class basic_numeric_printer; + + typedef basic_numeric_printer<> text_printer; + typedef basic_numeric_printer docbook_printer; + typedef basic_numeric_printer latex_printer; + typedef basic_numeric_printer html_printer; + + typedef basic_numeric_printer wtext_printer; + typedef basic_numeric_printer wdocbook_printer; + typedef basic_numeric_printer wlatex_printer; + typedef basic_numeric_printer whtml_printer; + +[heading Description] + +Class `basic_numeric_printer` provides "pretty printing" capabilities for numeric data, it's use mirrors that of the iostream +library, but fullfills a very different role: there is no "input streaming" and the output is intended to be human readable +and formatted for printing and not for consumption by machine. + +Supported input number types are integers, floating point number, complex numbers, rationals(TODO), polynomials(TODO), intervals(TODO) +and containers thereof(TODO). Other types, plus all the standard library manipulators can also be streamed to `basic_numeric_printer` +but are simply formwarded to the underlying stream and ignored by the pretty printer. + +Output formats include plain text (not so useful), Docbook XML markup, HTML markup, and Latex. MathML may come later(TODO). + +[h4 Examples:] + +Integer values have the same representation as the underlying stream would give them, but can have styling applied to their XML wrapper +in HTML and Docbook output modes. + +Floats in scientific mode, are styled somewhat differently from the underlying iostream: '''-1.23457×10-24''' +and can be customised to change the default multipication operator: '''-1.235⋅10-24'''. + +Complex numbers inherit all the properties of floats, and in addition allow the unit "i" to be customised, for example +'''1.23 - 1.23×10-24i''' or +'''1.23 - 1.2345678765⋅10-24'''. + +[heading Class `basic_numeric_printer`] + + enum output_format_t + { + text_format, + docbook_format, + latex_format, + html_format + }; + + template > + class basic_numeric_printer + { + basic_numeric_printer(std::basic_ostream& os); + std::basic_ostream& stream(); + }; + + template + basic_numeric_printer& operator << (basic_numeric_printer& os, const Unspecified& data); + + typedef basic_numeric_printer<> text_printer; + typedef basic_numeric_printer docbook_printer; + typedef basic_numeric_printer latex_printer; + typedef basic_numeric_printer html_printer; + + typedef basic_numeric_printer wtext_printer; + typedef basic_numeric_printer wdocbook_printer; + typedef basic_numeric_printer wlatex_printer; + typedef basic_numeric_printer whtml_printer; + +Class `basic_numeric_printer` is a `std::basic_ostream` lookalike which is constructable from an underlying +stream, and has a single public member function which returns a reference to the underlying stream. + +It's usage via `<<` manipulators is identical to that of `std::basic_ostream`: anything that `basic_numeric_printer` doesn't +understand is passed unchanged to the underlying stream, while numeric data alone is intercepted and pretty-formtted. + +[heading Manipulators] + +There are some stream manipulators which are specific to `basic_numeric_printer` and control how the "pretty printing" is conduced: + + enum styling_level_t + { + no_styling = 0, + minimal_styling = 1, + full_styling = 6 + }; + +Controls the level of styling applied to XML based output formats. + +[variablelist + [[no_styling] [As it's name suggests, results in no styling applied to the output.]] + [[minimal_styling] [Adds minimal styling to the output: for Docbook this wraps the number in `...` + and for HTML it adds `...` around each number.]] + [[full_styling] [This is the default. It adds styling around each complete number, plus styling to indicate the type of number, and styling + around some individual components. In HTML, an integer would be formatted as `-23`, + the inner class name changes to "float" or "complex" depending on the number type, and the XML container to `` in Docbook. In addition + complex numbers get the "i" wrapped in `...`.]] +] + + enum multiplyer_t + { + multiply_times = 0, + multiply_dot = 1, + multiply_x = 2 + }; + +Controls the character used for the multiplication symbol: + +[variablelist + [[multiply_times] [The default. Uses the character U+x00D7 (or [^$\times\$] in Latex): \u00D7.]] + [[multiply_dot] [Uses the character U+x22C5 (or [^$\cdot\$] in Latex): \u22C5.]] + [[multiply_x] [Uses a literal `x` character, this is generally not such a good choice, but is the only one available for plain text output.]] +] + + enum imaginary_i_t + { + upright_i = 0, + slanted_i = 1, + doublestruck_i = 2 + }; + +Controls how the imaginary unit is formatted (plain text output ignores this option): + +[variablelist + [[upright_i] [The default. Formats "i" as a regular upright character, this matches "ISO 80000-2:2009, Quantities and units---part2: Mathematical signs and symbols to be used in the natural sciences and technology".]] + [[slanted_i] [Uses an italic character ['"i"], this style has strong historical precedent.]] + [[doublestruck_i] [Uses the double struct letter i: \u2148. This is the form used by Mathematica. This is not available in Latex output.]] +] + + constexpr const unspecified latex_as_equation; + constexpr const unspecified latex_as_text; + +Controls how latex output is handled: the default is to format all numbers in equation mode, but streaming `latex_as_text` to the stream +will result in numbers being formatted in text mode instead. Note that even in text mode some entities need to drop into math mode temporarily +for example to access symbols such as `\times`. + +[heading Format Gallery] + +The following section (using Docbook output format) illustrates how each of the number categories are formatted with various options: + +[import ../../example/formatter_docbook_output.qbk] + +[integer_formatting_examples] + +[float_formatting_examples] + +[complex_formatting_examples] + +[heading Output Format Gallery] + +The above tables of sample output are available in various other formats + +[@../../example/formatter_html_output.html HTML sample page], generated with [@../../example/formatter_html_output.cpp formatter_html_output.cpp]. + +[@../../example/formatter_text_output.txt Plain text sample page], generated with [@../../example/formatter_text_output.cpp formatter_text_output.cpp]. + +[@../../example/formatter_latex_output.tex Latex source output], generated with [@../../example/formatter_latex_output.cpp formatter_latex_output.cpp]. + +[@../../example/formatter_latex_output.pdf Latex PDF output], generated with [@../../example/formatter_latex_output.cpp formatter_latex_output.cpp] and then converted to PDF. + +[endsect] [/section:formatting] + + +[/ Copyright 2017 John Maddock. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] \ No newline at end of file diff --git a/doc/math.qbk b/doc/math.qbk index bd60c40f44..0baee4eb3e 100644 --- a/doc/math.qbk +++ b/doc/math.qbk @@ -569,6 +569,7 @@ and as a CD ISBN 0-9504833-2-X 978-0-9504833-2-0, Classification 519.2-dc22. [include fp_utilities/float_next.qbk] [include fp_utilities/float_comparison.qbk] [include fp_utilities/condition_numbers.qbk] +[include fp_utilities/formatting.qbk] [endmathpart] [mathpart cstdfloat..Specified-width floating-point typedefs] diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index fd40f3982d..362e9911d8 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -8,6 +8,9 @@ # bring in the rules for testing import testing ; import ../../config/checks/config : requires ; +using quickbook ; + +path-constant here : . ; project : requirements @@ -147,7 +150,11 @@ test-suite examples : [ run ooura_fourier_integrals_example.cpp : : : [ requires cxx11_hdr_mutex cxx11_lambdas cxx11_inline_namespaces cxx11_auto_declarations ] ] [ run ooura_fourier_integrals_cosine_example.cpp : : : [ requires cxx11_hdr_mutex cxx11_inline_namespaces cxx11_auto_declarations cxx17_std_apply ] ] [ run ooura_fourier_integrals_multiprecision_example.cpp : : : [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : -lquadmath ] [ requires cxx11_hdr_mutex cxx11_inline_namespaces cxx11_auto_declarations cxx17_std_apply ] ] - + + [ run formatter_latex_output.cpp : $(here)/formatter_latex_output.tex : : [ requires cxx11_constexpr cxx14_variable_templates ] ] + [ run formatter_html_output.cpp : $(here)/formatter_html_output.html : : [ requires cxx11_constexpr cxx14_variable_templates ] ] + [ run formatter_docbook_output.cpp : $(here)/formatter_docbook_output.qbk : : [ requires cxx11_constexpr cxx14_variable_templates ] ] + [ run formatter_text_output.cpp : $(here)/formatter_text_output.txt : : [ requires cxx11_constexpr cxx14_variable_templates ] ] ; run root_elliptic_finding.cpp /boost/timer : : : release static [ requires cxx11_unified_initialization_syntax cxx11_defaulted_functions ] freebsd:"-lrt" linux:"-lrt -lpthread" ; @@ -157,3 +164,15 @@ run root_n_finding_algorithms.cpp /boost/timer : : : release static [ req explicit root_elliptic_finding ; explicit root_finding_algorithms ; explicit root_n_finding_algorithms ; + +xml formatting_test : formatter_docbook_output_test.qbk : formatter_docbook_output +; +boostbook standalone + : + formatting_test + : +; + +explicit standalone ; +install pdfinstall : standalone/pdf : . PDF formatting.pdf ; +explicit pdfinstall ; # b2 pdf pdfinstall to do this pdf file copy. diff --git a/example/formatter_docbook_output.cpp b/example/formatter_docbook_output.cpp new file mode 100644 index 0000000000..e6752ea2ec --- /dev/null +++ b/example/formatter_docbook_output.cpp @@ -0,0 +1,84 @@ +// (C) Copyright John Maddock 2019. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +void print(std::ostream& os) +{ + boost::math::tools::docbook_printer printer(os); + + printer << "[template integer_formatting_examples[]\n"; + + int ival = 0; + printer << "[table:integer_fmt_examples Basic Integer Values\n[[Value][Base][Result]]\n"; + printer.stream() << "[[" << ival << "][default]['''"; + printer << ival << "''']]\n"; + ival = -23; + printer.stream() << "[[" << ival << "][default]['''"; + printer << ival << "''']]\n"; + ival = 23; + printer.stream() << std::dec << "[[" << ival << "][hex]['''"; + printer << std::hex << ival << "''']]\n"; + printer.stream() << std::dec << "[[" << ival << "][oct]['''"; + printer << std::oct << ival << "''']]\n]\n]\n"; + + printer << "[template float_formatting_examples[]\n"; + double fval = 3; + printer << "[table:float_fmt_examples Basic Float Values\n[[Value][Precision][Format][Result]]"; + printer.stream() << "[[" << fval << "][default][default]['''"; + printer << fval << "''']]\n"; + fval = 3.14; + printer.stream() << "[[" << fval << "][default][default]['''"; + printer << fval << "''']]\n"; + fval = -1.2345678765e-24; + printer.stream() << "[[" << fval << "][default][default]['''"; + printer << fval << "''']]\n"; + printer.stream() << "[[" << fval << "][3][scientific]['''"; + printer << std::setprecision(3) << std::scientific << fval << "''']]\n"; + printer.stream() << "[[" << fval << "][3][scientific + multiply_x]['''"; + printer << std::setprecision(3) << std::scientific << boost::math::tools::multiply_x << fval << "''']]\n"; + printer.stream() << "[[" << fval << "][3][scientific + multiply_dot]['''"; + printer << std::setprecision(3) << std::scientific << boost::math::tools::multiply_dot << fval << "''']]\n" << boost::math::tools::multiply_times; + + printer << "]\n]\n" << std::defaultfloat; + + printer << "[template complex_formatting_examples[]\n"; + std::complex cval(3.25, 4.67); + printer << "[table:complex_fmt_examples Basic Complex Values\n[[Value][Precision][Format][Result]]"; + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + cval = 3.14; + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + cval = std::complex(1.23, -1.2345678765e-24); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + printer.stream() << "[[" << cval << "][3][scientific]['''"; + printer << std::scientific << std::setprecision(3) << cval << "''']]\n"; + printer.stream() << "[[" << cval << "][12][default + slanted_i]['''"; + printer << boost::math::tools::slanted_i << std::defaultfloat << std::setprecision(12) << cval << "''']]\n"; + printer.stream() << "[[" << cval << "][12][default + doublestruck_i]['''"; + printer << boost::math::tools::doublestruck_i << std::defaultfloat << std::setprecision(12) << cval << "''']]\n"; + printer.stream() << "[[" << cval << "][12][default + doublestruck_i + multiply_x]['''"; + printer << boost::math::tools::doublestruck_i << std::defaultfloat << std::setprecision(12) << boost::math::tools::multiply_x << cval << "''']]\n"; + printer.stream() << "[[" << cval << "][12][default + doublestruck_i + multiply_dot]['''"; + printer << boost::math::tools::doublestruck_i << std::defaultfloat << std::setprecision(12) << boost::math::tools::multiply_dot << cval << "''']]\n" << boost::math::tools::multiply_x; + printer << "\n]\n]\n"; +} + + +int main(int argc, const char* argv[]) +{ + if (argc > 1) + { + std::ofstream ofs(argv[1]); + print(ofs); + } + else + print(std::cout); + return 0; +} diff --git a/example/formatter_docbook_output.qbk b/example/formatter_docbook_output.qbk new file mode 100644 index 0000000000..cd58fc3a5e --- /dev/null +++ b/example/formatter_docbook_output.qbk @@ -0,0 +1,32 @@ +[template integer_formatting_examples[] +[table:integer_fmt_examples Basic Integer Values +[[Value][Base][Result]] +[[0][default]['''0''']] +[[-23][default]['''-23''']] +[[23][hex]['''17''']] +[[23][oct]['''27''']] +] +] +[template float_formatting_examples[] +[table:float_fmt_examples Basic Float Values +[[Value][Precision][Format][Result]][[3][default][default]['''3''']] +[[3.14][default][default]['''3.14''']] +[[-1.23457e-24][default][default]['''-1.23457×10-24''']] +[[-1.23457e-24][3][scientific]['''-1.235×10-24''']] +[[-1.235e-24][3][scientific + multiply_x]['''-1.235x10-24''']] +[[-1.235e-24][3][scientific + multiply_dot]['''-1.235⋅10-24''']] +] +] +[template complex_formatting_examples[] +[table:complex_fmt_examples Basic Complex Values +[[Value][Precision][Format][Result]][[(3.25,4.67)][default][default]['''3.25 + 4.67i''']] +[[(3.14,0)][default][default]['''3.14 + 0i''']] +[[(1.23,-1.23e-24)][default][default]['''1.23 - 1.23×10-24i''']] +[[(1.23,-1.23e-24)][3][scientific]['''1.230×10+00 - 1.235×10-24i''']] +[[(1.230e+00,-1.235e-24)][12][default + slanted_i]['''1.23 - 1.2345678765×10-24i''']] +[[(1.23,-1.2345678765e-24)][12][default + doublestruck_i]['''1.23 - 1.2345678765×10-24''']] +[[(1.23,-1.2345678765e-24)][12][default + doublestruck_i + multiply_x]['''1.23 - 1.2345678765x10-24''']] +[[(1.23,-1.2345678765e-24)][12][default + doublestruck_i + multiply_dot]['''1.23 - 1.2345678765⋅10-24''']] + +] +] diff --git a/example/formatter_docbook_output_test.qbk b/example/formatter_docbook_output_test.qbk new file mode 100644 index 0000000000..eb24ce4eb2 --- /dev/null +++ b/example/formatter_docbook_output_test.qbk @@ -0,0 +1,21 @@ + +[article Docbook Formatting Test + [quickbook 1.7] + [copyright 2019 John Maddock] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] + [authors [Maddock, John]] + [/last-revision $Date$] +] + +[import formatter_docbook_output.qbk] + +[integer_formatting_examples] + +[float_formatting_examples] + +[complex_formatting_examples] + diff --git a/example/formatter_html_output.cpp b/example/formatter_html_output.cpp new file mode 100644 index 0000000000..4cc40cb043 --- /dev/null +++ b/example/formatter_html_output.cpp @@ -0,0 +1,93 @@ +// (C) Copyright John Maddock 2019. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +void print(std::ostream& os) +{ + boost::math::tools::html_printer printer(os); + + printer << "\n\n" << std::endl; + printer << "" << std::endl; + + printer << "

Basic Integers Values:

\n" << boost::math::tools::full_styling; + + int ival = 0; + printer << ""; + printer.stream() << "\n"; + ival = -23; + printer.stream() << "\n"; + ival = 23; + printer.stream() << std::dec << "\n"; + printer.stream() << std::dec << "\n
ValueBaseResult
" << ival << "default"; + printer << ival << "
" << ival << "default"; + printer << ival << "
" << ival << "hex"; + printer << std::hex << ival << "
" << ival << "oct"; + printer << std::oct << ival << "
\n\n"; + + printer << "

Basic Floating Point Values:

\n\n"; + double fval = 3; + printer << ""; + printer.stream() << "\n"; + fval = 3.14; + printer.stream() << "\n"; + fval = -1.2345678765e-24; + printer.stream() << "\n"; + printer.stream() << "\n"; + printer.stream() << "\n"; + printer.stream() << "\n"; + printer.stream() << "\n"; + printer.stream() << "\n" << boost::math::tools::multiply_times; + + printer << "
ValuePrecisionFormatResult
" << fval << "defaultdefault"; + printer << fval << "
" << fval << "defaultdefault"; + printer << fval << "
" << fval << "defaultdefault"; + printer << fval << "
" << fval << "3scientific"; + printer << std::setprecision(3) << std::scientific << fval << "
" << fval << "defaultmultiply_x"; + printer << boost::math::tools::multiply_x << fval << "
" << fval << "3scientific + multiply_x"; + printer << std::setprecision(3) << std::scientific << fval << "
" << fval << "defaultmultiply_dot"; + printer << boost::math::tools::multiply_dot << fval << "
" << fval << "3scientific + multiply_dot"; + printer << std::setprecision(3) << std::scientific << fval << "
\n\n" << std::defaultfloat; + + printer << "

Complex Values:

\n\n"; + std::complex cval(3.25, 4.67); + printer << ""; + printer.stream() << "\n"; + cval = 3.14; + printer.stream() << "\n"; + cval = std::complex(1.23, -1.2345678765e-24); + printer.stream() << "\n"; + printer.stream() << "\n"; + printer.stream() << "\n"; + printer.stream() << "\n"; + + printer.stream() << "\n"; + printer.stream() << "\n" << boost::math::tools::multiply_times; + printer << "\n
ValuePrecisionFormatResult
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "3scientific"; + printer << std::scientific << std::setprecision(3) << cval << "
" << cval << "12default + slanted_i"; + printer << boost::math::tools::slanted_i << std::defaultfloat << std::setprecision(12) << cval << "
" << cval << "12default + doublestruck_i"; + printer << boost::math::tools::doublestruck_i << std::defaultfloat << std::setprecision(12) << cval << "
" << cval << "12default + doublestruck_i + multiply_x"; + printer << boost::math::tools::doublestruck_i << boost::math::tools::multiply_x << std::defaultfloat << std::setprecision(12) << cval << "
" << cval << "12default + doublestruck_i + multiply_dot"; + printer << boost::math::tools::doublestruck_i << std::defaultfloat << std::setprecision(12) << boost::math::tools::multiply_dot << cval << "
\n\n"; + + printer << "\n\n"; +} + +int main(int argc, const char* argv[]) +{ + if (argc > 1) + { + std::ofstream ofs(argv[1]); + print(ofs); + } + else + print(std::cout); + return 0; +} diff --git a/example/formatter_html_output.html b/example/formatter_html_output.html new file mode 100644 index 0000000000..8779d21aa6 --- /dev/null +++ b/example/formatter_html_output.html @@ -0,0 +1,38 @@ + + + + +

Basic Integers Values:

+ + + + +
ValueBaseResult
0default0
-23default-23
23hex17
23oct27
+ +

Basic Floating Point Values:

+ + + + + + + + + +
ValuePrecisionFormatResult
3defaultdefault3
3.14defaultdefault3.14
-1.23457e-24defaultdefault-1.23457×10-24
-1.23457e-243scientific-1.235×10-24
-1.235e-24defaultmultiply_x-1.235x10-24
-1.235e-243scientific + multiply_x-1.235x10-24
-1.235e-24defaultmultiply_dot-1.235⋅10-24
-1.235e-243scientific + multiply_dot-1.235⋅10-24
+ +

Complex Values:

+ + + + + + + + + + +
ValuePrecisionFormatResult
(3.25,4.67)defaultdefault3.25 + 4.67i
(3.14,0)defaultdefault3.14 + 0i
(1.23,-1.23e-24)defaultdefault1.23 - 1.23×10-24i
(1.23,-1.23e-24)3scientific1.230×10+00 - 1.235×10-24i
(1.230e+00,-1.235e-24)12default + slanted_i1.23 - 1.2345678765×10-24i
(1.23,-1.2345678765e-24)12default + doublestruck_i1.23 - 1.2345678765×10-24
(1.23,-1.2345678765e-24)12default + doublestruck_i + multiply_x1.23 - 1.2345678765x10-24
(1.23,-1.2345678765e-24)12default + doublestruck_i + multiply_dot1.23 - 1.2345678765⋅10-24
+ + + diff --git a/example/formatter_latex_output.cpp b/example/formatter_latex_output.cpp new file mode 100644 index 0000000000..0620e1bdc7 --- /dev/null +++ b/example/formatter_latex_output.cpp @@ -0,0 +1,90 @@ +// (C) Copyright John Maddock 2019. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +void print(std::ostream& os) +{ + boost::math::tools::latex_printer printer(os); + + printer << "\\documentclass{article}\n \\begin{ document }\n\n"; + + int ival = 0; + printer << "\\textbf{Basic Integer Values}\n\n\\begin{tabular}{r r r}\nValue & Base & Result \\\\ \\hline \n"; + printer.stream() << ival << " & default & "; + printer << ival << "\\\\\n"; + ival = -23; + printer.stream() << ival << " & default & "; + printer << ival << " \\\\\n"; + ival = 23; + printer.stream() << std::dec << ival << " & hex & "; + printer << std::hex << ival << "\\\\\n"; + printer.stream() << std::dec << ival << " & oct & "; + printer << std::oct << ival << "\\\\\n\\end{tabular}\n\n"; + + double fval = 3; + printer << "\\textbf{Basic Float Values}\n\n\\begin{tabular}{r r r r}\nValue & Precision & Format & Result \\\\\n"; + printer.stream() << fval << " & default & default & "; + printer << fval << " \\\\\n"; + fval = 3.14; + printer.stream() << fval << " & default & default & "; + printer << fval << " \\\\\n"; + fval = -1.2345678765e-24; + printer.stream() << fval << " & default & default & "; + printer << fval << " \\\\\n"; + printer.stream() << fval << " & 3 & scientific & "; + printer << std::setprecision(3) << std::scientific << fval << " \\\\\n"; + printer.stream() << fval << " & 10 & scientific + latex\\_as\\_text & "; + printer << std::setprecision(10) << boost::math::tools::latex_as_text << std::scientific << fval << " \\\\\n" << boost::math::tools::latex_as_equation; + + printer.stream() << fval << " & 3 & scientific + multiply\\_x & "; + printer << std::setprecision(3) << std::scientific << boost::math::tools::multiply_x << fval << " \\\\\n"; + printer.stream() << fval << " & 10 & scientific + latex\\_as\\_text + multiply\\_x & "; + printer << std::setprecision(10) << boost::math::tools::latex_as_text << boost::math::tools::multiply_x << std::scientific << fval << " \\\\\n" << boost::math::tools::latex_as_equation; + printer.stream() << fval << " & 3 & scientific + multiply\\_dot & "; + printer << std::setprecision(3) << std::scientific << boost::math::tools::multiply_dot << fval << " \\\\\n"; + printer.stream() << fval << " & 10 & scientific + latex\\_as\\_text + multiply\\_dot & "; + printer << std::setprecision(10) << boost::math::tools::latex_as_text << boost::math::tools::multiply_dot << std::scientific << fval << " \\\\\n" << boost::math::tools::latex_as_equation << boost::math::tools::multiply_times; + + printer << "\\end{tabular}\n\n" << std::defaultfloat; + + std::complex cval(3.25, 4.67); + printer << "\\textbf{Basic Complex Values}\n\n\\begin{tabular}{r r r r}\nValue & Precision & Format & Result \\\\\n"; + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + cval = 3.14; + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + cval = std::complex(1.23, -1.2345678765e-24); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + printer.stream() << cval << " & 3 & scientific & "; + printer << std::scientific << std::setprecision(3) << cval << " \\\\\n"; + printer.stream() << cval << " & 12 & default + slanted\\_i & "; + printer << boost::math::tools::slanted_i << std::defaultfloat << std::setprecision(12) << cval << " \\\\\n"; + printer.stream() << cval << " & 12 & default + upright\\_i & "; + printer << boost::math::tools::upright_i << std::defaultfloat << std::setprecision(12) << cval << " \\\\\n"; + printer.stream() << cval << " & 12 & default + slanted\\_i + latex\\_as\\_text & "; + printer << boost::math::tools::slanted_i << boost::math::tools::latex_as_text << std::defaultfloat << std::setprecision(12) << cval << " \\\\\n"; + printer.stream() << cval << " & 12 & default + upright\\_i + latex\\_as\\_text& "; + printer << boost::math::tools::upright_i << std::defaultfloat << std::setprecision(12) << cval << " \\\\\n"; + printer << "\\end{tabular}\n\n" << std::defaultfloat << boost::math::tools::multiply_times; + + printer << "\\end{document}\n\n"; +} + +int main(int argc, const char* argv[]) +{ + if (argc > 1) + { + std::ofstream ofs(argv[1]); + print(ofs); + } + else + print(std::cout); + return 0; +} diff --git a/example/formatter_latex_output.pdf b/example/formatter_latex_output.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4ce1e41dfb3e3acddd0c79546b5f7d905220a8cf GIT binary patch literal 24395 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 literal 0 HcmV?d00001 diff --git a/example/formatter_latex_output.tex b/example/formatter_latex_output.tex new file mode 100644 index 0000000000..6783c64e94 --- /dev/null +++ b/example/formatter_latex_output.tex @@ -0,0 +1,44 @@ +\documentclass{article} + \begin{ document } + +\textbf{Basic Integer Values} + +\begin{tabular}{r r r} +Value & Base & Result \\ \hline +0 & default & $0$\\ +-23 & default & $-23$ \\ +23 & hex & $17$\\ +23 & oct & $27$\\ +\end{tabular} + +\textbf{Basic Float Values} + +\begin{tabular}{r r r r} +Value & Precision & Format & Result \\ +3 & default & default & $3$ \\ +3.14 & default & default & $3.14$ \\ +-1.23457e-24 & default & default & $-1.23457\times 10^{-24}$ \\ +-1.23457e-24 & 3 & scientific & $-1.235\times 10^{-24}$ \\ +-1.235e-24 & 10 & scientific + latex\_as\_text & -1.2345678765$\times$10\textsuperscript{-24} \\ +-1.2345678765e-24 & 3 & scientific + multiply\_x & $-1.235\mathrm{x} 10^{-24}$ \\ +-1.235e-24 & 10 & scientific + latex\_as\_text + multiply\_x & -1.2345678765x10\textsuperscript{-24} \\ +-1.2345678765e-24 & 3 & scientific + multiply\_dot & $-1.235\cdot 10^{-24}$ \\ +-1.235e-24 & 10 & scientific + latex\_as\_text + multiply\_dot & -1.2345678765$\cdot$10\textsuperscript{-24} \\ +\end{tabular} + +\textbf{Basic Complex Values} + +\begin{tabular}{r r r r} +Value & Precision & Format & Result \\ +(3.25,4.67) & default & default & $3.25 + 4.67\mathrm{i}$ \\ +(3.14,0) & default & default & $3.14 + 0\mathrm{i}$ \\ +(1.23,-1.234567876e-24) & default & default & $1.23 - 1.234567876\times 10^{-24}\mathrm{i}$ \\ +(1.23,-1.234567876e-24) & 3 & scientific & $1.230\times 10^{+00} - 1.235\times 10^{-24}\mathrm{i}$ \\ +(1.230e+00,-1.235e-24) & 12 & default + slanted\_i & $1.23 - 1.2345678765\times 10^{-24}i$ \\ +(1.23,-1.2345678765e-24) & 12 & default + upright\_i & $1.23 - 1.2345678765\times 10^{-24}\mathrm{i}$ \\ +(1.23,-1.2345678765e-24) & 12 & default + slanted\_i + latex\_as\_text & 1.23 - 1.2345678765$\times$10\textsuperscript{-24}\textit{i} \\ +(1.23,-1.2345678765e-24) & 12 & default + upright\_i + latex\_as\_text& 1.23 - 1.2345678765$\times$10\textsuperscript{-24}i \\ +\end{tabular} + +\end{document} + diff --git a/example/formatter_text_output.cpp b/example/formatter_text_output.cpp new file mode 100644 index 0000000000..81eef0d28d --- /dev/null +++ b/example/formatter_text_output.cpp @@ -0,0 +1,72 @@ +// (C) Copyright John Maddock 2019. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +void print(std::ostream& os) +{ + boost::math::tools::text_printer printer(os); + + printer << "Integers:\n\n"; + + int ival = 0; + printer << std::setw(20) << "Value" << std::setw(20) << "Base" << std::setw(20) << "Result" << std::endl; + printer.stream() << std::setw(20) << ival << std::setw(20) << "default" << std::setw(20); + printer << ival << std::endl; + ival = -23; + printer.stream() << std::setw(20) << ival << std::setw(20) << "default" << std::setw(20); + printer << ival << std::endl; + ival = 23; + printer.stream() << std::setw(20) << ival << std::setw(20) << "hex" << std::setw(20); + printer << std::hex << ival << std::endl; + printer.stream() << std::setw(20) << ival << std::setw(20) << "oct" << std::setw(20); + printer << std::oct << ival << std::endl; + + printer << std::endl << std::endl; + + printer << "Basic floating point values:\n\n"; + double fval = 3; + printer << std::setw(20) << "Value" << std::setw(20) << "Precision" << std::setw(20) << "Format" << std::setw(20) << "Result" << std::endl; + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer << fval << std::endl; + fval = 3.14; + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer << fval << std::endl; + fval = -1.2345678765e-24; + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer << fval << std::endl; + printer.stream() << std::setw(20) << fval << std::setw(20) << "3" << std::setw(20) << "scientific" << std::setw(20); + printer << std::scientific << std::setprecision(3) << fval << std::endl; + + printer << std::endl << std::endl << std::defaultfloat; + + printer << "Complex Values:\n\n"; + std::complex cval(3.25, 4.67); + printer << std::setw(20) << "Value" << std::setw(20) << "Precision" << std::setw(20) << "Format" << std::setw(30) << "Result" << std::endl; + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(30); + printer << cval << std::endl; + cval = 3.14; + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(30); + printer << cval << std::endl; + cval = std::complex(1.23, -1.2345678765e-24); + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(30); + printer << cval << std::endl; + printer.stream() << std::setw(20) << cval << std::setw(20) << "3" << std::setw(20) << "scientific" << std::setw(30); + printer << std::scientific << std::setprecision(3) << cval << std::endl; +} + +int main(int argc, const char* argv[]) +{ + if (argc > 1) + { + std::ofstream ofs(argv[1]); + print(ofs); + } + else + print(std::cout); + return 0; +} diff --git a/example/formatter_text_output.txt b/example/formatter_text_output.txt new file mode 100644 index 0000000000..a5555ba51f --- /dev/null +++ b/example/formatter_text_output.txt @@ -0,0 +1,25 @@ +Integers: + + Value Base Result + 0 default 0 + -23 default -23 + 23 hex 17 + 17 oct 27 + + +Basic floating point values: + + Value Precision Format Result + 3 default default 3 + 3.14 default default 3.14 + -1.23457e-24 default default -1.23457x10^-24 + -1.23457e-24 3 scientific -1.235x10^-24 + + +Complex Values: + + Value Precision Format Result + (3.25,4.67) default default 3.25 + 4.67i + (3.14,0) default default 3.14 + 0i + (1.23,-1.23e-24) default default 1.23 - 1.23x10^-24i + (1.23,-1.23e-24) 3 scientific 1.230x10^+00 - 1.235x10^-24i diff --git a/include/boost/math/tools/formatting.hpp b/include/boost/math/tools/formatting.hpp new file mode 100644 index 0000000000..7d266ffd6a --- /dev/null +++ b/include/boost/math/tools/formatting.hpp @@ -0,0 +1,652 @@ +// (C) Copyright John Maddock 2019. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_MATH_TOOLS_FORMATTING_HPP +#define BOOST_MATH_TOOLS_FORMATTING_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma once +#endif + +namespace boost { + namespace math { + namespace tools { + + template ::value || boost::is_class::value> + struct is_integer_like + { + static const bool value = false; + }; + template + struct is_integer_like + { + static const bool value = std::numeric_limits::is_integer; + }; + + template ::value || boost::is_class::value> + struct is_float_like + { + // The point "floats": + static const bool value = false; + }; + template + struct is_float_like + { + // The point "floats": + static const bool value = std::numeric_limits::max_exponent != std::numeric_limits::min_exponent; + }; + + template + using real_t = decltype(std::declval().real(std::declval().imag())); + template + using imag_t = decltype(std::declval().imag(std::declval().real())); + template + using polynomial_t = decltype(std::declval()(std::declval()[0] + std::declval().order())); + + template + struct is_complex_like + { + static const bool value = boost::is_detected_v && boost::is_detected_v; + }; + + template + struct is_unhandled_type + { + static const bool value = !is_complex_like::value && !is_float_like::value && !is_integer_like::value; + }; + + + enum output_format_t + { + text_format, + docbook_format, + latex_format, + html_format + }; + + enum styling_level_t + { + no_styling = 0, + minimal_styling = 1, + full_styling = 6 + }; + + enum multiplyer_t + { + multiply_times = 0, + multiply_dot = 1, + multiply_x = 2 + }; + + enum imaginary_i_t + { + upright_i = 0, + slanted_i = 1, + doublestruck_i = 2 + }; + + struct latex_as_text_t {}; + struct latex_as_equation_t {}; + + constexpr const latex_as_text_t* latex_as_text = nullptr; + constexpr const latex_as_equation_t* latex_as_equation = nullptr; + + template + class basic_numeric_formatter; + + template + class basic_numeric_formatter_base + { + styling_level_t styling_level; + imaginary_i_t i_style; + multiplyer_t multiply_style; + bool requires_parenthesis; + protected: + template + static void decompose_float(std::basic_ostream& os, const Float& f, std::basic_string& mantissa, std::basic_string& exponent) + { + static const charT exponent_string[2] = { 'e', 'E' }; + std::basic_stringstream ss; + ss.copyfmt(os); + ss.imbue(os.getloc()); + ss << f; + std::basic_string s = ss.str(); + typename std::basic_string::size_type pos = s.find_first_of(exponent_string); + if (pos != std::basic_string::npos) + { + mantissa.assign(s, 0, pos); + exponent.assign(s, pos + 1, s.size() - pos - 1); + } + else + mantissa = s; + } + public: + basic_numeric_formatter_base() : styling_level(full_styling), requires_parenthesis(false), multiply_style(multiply_times) {} + + void styling(styling_level_t i) + { + styling_level = i; + } + styling_level_t styling()const + { + return styling_level; + } + void imaginary_style(imaginary_i_t i) + { + i_style = i; + } + imaginary_i_t imaginary_style()const + { + return i_style; + } + void multiply(multiplyer_t t) + { + multiply_style = t; + } + multiplyer_t multiply()const + { + return multiply_style; + } + void parenthesis(bool b) + { + requires_parenthesis = b; + } + bool parenthesis()const + { + return requires_parenthesis; + } + template + static std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) + { + return os << i; + } + template + static std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) + { + std::basic_string mantissa, exponent; + decompose_float(os, f, mantissa, exponent); + if (exponent.size()) + return os << mantissa << "x10^" << exponent; + return os << mantissa; + } + template + static std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) + { + format_float(os, f.real()); + typename Complex::value_type i(f.imag()); + bool isneg = i < 0; + if (isneg) + { + i = -i; + os << " - "; + } + else + os << " + "; + format_float(os, i); + os.put(os.widen('i')); + return os; + } + void latex_as_equation(); + }; + + template + class basic_numeric_formatter : public basic_numeric_formatter_base + { + }; + + template + class basic_numeric_formatter : public basic_numeric_formatter_base + { + protected: + template + std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) + { + if (this->styling() >= minimal_styling) + os << ""; + if (this->styling() > minimal_styling) + os << ""; + os << i; + if (this->styling() > minimal_styling) + os << ""; + if (this->styling() >= minimal_styling) + os << ""; + return os; + } + template + std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) + { + std::basic_string mantissa, exponent; + basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); + if (this->styling() >= minimal_styling) + os << ""; + if (this->styling() > minimal_styling) + os << ""; + + if (exponent.size()) + { + os << mantissa; + if (this->multiply() == multiply_x) + os.put(os.widen('x')); + else if (this->multiply() == multiply_dot) + os << "⋅"; + else + os << "×"; + os << "10" << exponent << ""; + } + else + os << mantissa; + + if (this->styling() > minimal_styling) + os << ""; + if (this->styling() >= minimal_styling) + os << ""; + + return os; + } + template + std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) + { + if (this->styling() >= minimal_styling) + os << ""; + if (this->styling() > minimal_styling) + os << ""; + + styling_level_t saved_style = this->styling(); + this->styling(no_styling); + + format_float(os, f.real()); + typename Complex::value_type i(f.imag()); + bool isneg = i < 0; + if (isneg) + { + i = -i; + os << " - "; + } + else + os << " + "; + format_float(os, i); + + if (saved_style >= minimal_styling) + os << ""; + if (this->imaginary_style() == doublestruck_i) + os << "ⅈ"; + else if (this->imaginary_style() == slanted_i) + os << "i"; + else + os.put(os.widen('i')); + if (saved_style >= minimal_styling) + os << ""; + + this->styling(saved_style); + + if (this->styling() > minimal_styling) + os << ""; + if (this->styling() >= minimal_styling) + os << ""; + + return os; + } + }; + + template + class basic_numeric_formatter : public basic_numeric_formatter_base + { + bool is_latex_as_equation; + bool inside_equation; + public: + basic_numeric_formatter() : is_latex_as_equation(true), inside_equation(false) {} + void latex_as_equation(bool b) { is_latex_as_equation = b; } + bool latex_as_equation()const { return is_latex_as_equation; } + + template + std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) + { + if (!inside_equation && latex_as_equation()) + os.put(os.widen('$')); + basic_numeric_formatter_base::format_integer(os, i); + if (!inside_equation && latex_as_equation()) + os.put(os.widen('$')); + return os; + } + + template + std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) + { + std::basic_string mantissa, exponent; + basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); + + if (latex_as_equation()) + { + if (!inside_equation) + os.put(os.widen('$')); + os << mantissa; + if (exponent.size()) + { + if (this->multiply() == multiply_x) + os << "\\mathrm{x}"; + else if (this->multiply() == multiply_dot) + os << "\\cdot"; + else + os << "\\times"; + os << " 10^{" << exponent << "}"; + } + if (!inside_equation) + os.put(os.widen('$')); + } + else + { + os << mantissa; + if (exponent.size()) + { + if (this->multiply() == multiply_x) + os << "x"; + else if (this->multiply() == multiply_dot) + os << "$\\cdot$"; + else + os << "$\\times$"; + os << "10\\textsuperscript{" << exponent << "}"; + } + } + return os; + } + template + std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) + { + if (!inside_equation && latex_as_equation()) + os.put(os.widen('$')); + bool saved_inside_equation = inside_equation; + inside_equation = true; + + format_float(os, f.real()); + typename Complex::value_type i(f.imag()); + bool isneg = i < 0; + if (isneg) + { + i = -i; + os << " - "; + } + else + os << " + "; + format_float(os, i); + + if (this->imaginary_style() == doublestruck_i) + BOOST_THROW_EXCEPTION(std::runtime_error("Doublestruck imaginary i not implemented for LaTex output.")); + else if ((this->imaginary_style() == upright_i) && latex_as_equation()) + os << "\\mathrm{i}"; + else if ((this->imaginary_style() == slanted_i) && !latex_as_equation()) + os << "\\textit{i}"; + else + os.put(os.widen('i')); + + inside_equation = saved_inside_equation; + if (!inside_equation && latex_as_equation()) + os.put(os.widen('$')); + + return os; + } + }; + + template + class basic_numeric_formatter : public basic_numeric_formatter_base + { + protected: + template + std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) + { + if (this->styling() >= minimal_styling) + os << ""; + if (this->styling() > minimal_styling) + os << ""; + os << i; + if (this->styling() > minimal_styling) + os << ""; + if (this->styling() >= minimal_styling) + os << ""; + return os; + } + template + std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) + { + std::basic_string mantissa, exponent; + basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); + + if (this->styling() >= minimal_styling) + os << ""; + if (this->styling() > minimal_styling) + os << ""; + + if (exponent.size()) + { + os << mantissa; + if (this->multiply() == multiply_x) + os.put(os.widen('x')); + else if (this->multiply() == multiply_dot) + os << "⋅"; + else + os << "×"; + os << "10" << exponent << ""; + } + else + os << mantissa; + + if (this->styling() > minimal_styling) + os << ""; + if (this->styling() >= minimal_styling) + os << ""; + + return os; + } + template + std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) + { + if (this->styling() >= minimal_styling) + os << ""; + if (this->styling() > minimal_styling) + os << ""; + + styling_level_t saved_style = this->styling(); + this->styling(no_styling); + + format_float(os, f.real()); + typename Complex::value_type i(f.imag()); + bool isneg = i < 0; + if (isneg) + { + i = -i; + os << " - "; + } + else + os << " + "; + format_float(os, i); + + if (saved_style >= minimal_styling) + os << ""; + if (this->imaginary_style() == doublestruck_i) + os << "ⅈ"; + else if (this->imaginary_style() == slanted_i) + os << "i"; + else + os.put(os.widen('i')); + if (saved_style >= minimal_styling) + os << ""; + + this->styling(saved_style); + + if (this->styling() > minimal_styling) + os << ""; + if (this->styling() >= minimal_styling) + os << ""; + + return os; + } + }; + + + template > + class basic_numeric_printer : private basic_numeric_formatter + { + std::basic_ostream* p_stream; + typedef basic_numeric_formatter base_type; + + public: + basic_numeric_printer(std::basic_ostream& os) : p_stream(&os) {} + + using base_type::format_integer; + using base_type::format_float; + using base_type::format_complex; + using base_type::styling; + using base_type::imaginary_style; + using base_type::latex_as_equation; + using base_type::parenthesis; + using base_type::multiply; + + std::basic_ostream& stream() + { + return *p_stream; + } + }; + + typedef basic_numeric_printer<> text_printer; + typedef basic_numeric_printer docbook_printer; + typedef basic_numeric_printer latex_printer; + typedef basic_numeric_printer html_printer; + + typedef basic_numeric_printer wtext_printer; + typedef basic_numeric_printer wdocbook_printer; + typedef basic_numeric_printer wlatex_printer; + typedef basic_numeric_printer whtml_printer; + + template + typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Integer& i) + { + if (os.stream().width()) + { + std::basic_stringstream ss; + ss.copyfmt(os.stream()); + ss.imbue(os.stream().getloc()); + ss.width(0); + basic_numeric_printer fmt(ss); + fmt.format_integer(fmt.stream(), i); + os.stream() << ss.str(); + } + else + os.format_integer(os.stream(), i); + return os; + } + + template + typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Float& f) + { + if (os.stream().width()) + { + std::basic_stringstream ss; + ss.copyfmt(os.stream()); + ss.imbue(os.stream().getloc()); + ss.width(0); + basic_numeric_printer fmt(ss); + fmt.format_float(fmt.stream(), f); + os.stream() << ss.str(); + } + else + os.format_float(os.stream(), f); + return os; + } + + template + typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Complex& f) + { + if (os.stream().width()) + { + std::basic_stringstream ss; + ss.copyfmt(os.stream()); + ss.imbue(os.stream().getloc()); + ss.width(0); + basic_numeric_printer fmt(ss); + fmt.format_complex(fmt.stream(), f); + os.stream() << ss.str(); + } + else + os.format_complex(os.stream(), f); + return os; + } + + template + basic_numeric_printer& operator << (basic_numeric_printer& os, styling_level_t s) + { + os.styling(s); + return os; + } + + template + basic_numeric_printer& operator << (basic_numeric_printer& os, multiplyer_t s) + { + os.multiply(s); + return os; + } + + template + basic_numeric_printer& operator << (basic_numeric_printer& os, const latex_as_equation_t* const&) + { + os.latex_as_equation(true); + return os; + } + + template + basic_numeric_printer& operator << (basic_numeric_printer& os, const latex_as_text_t* const&) + { + os.latex_as_equation(false); + return os; + } + + template + basic_numeric_printer& operator << (basic_numeric_printer& os, const imaginary_i_t i) + { + os.imaginary_style(i); + return os; + } + + // + // Output formatters: + // + template + typename boost::enable_if_c::value, basic_numeric_printer&>::type operator<< (basic_numeric_printer& os, const T& unhandled) + { + os.stream() << unhandled; + return os; + } + template + basic_numeric_printer& operator<< (basic_numeric_printer& os, std::basic_ostream& (*pf)(std::basic_ostream&)) + { + pf(os.stream()); + return os; + } + template + basic_numeric_printer& operator<< (basic_numeric_printer& os, std::basic_ios& (*pf)(std::basic_ios&)) + { + pf(os.stream()); + return os; + } + template + basic_numeric_printer& operator<< (basic_numeric_printer& os, std::ios_base& (*pf)(std::ios_base&)) + { + pf(os.stream()); + return os; + } + } + } +} + +#endif // BOOST_MATH_TOOLS_FORMATTING_HPP + From d325ad6080bab3d190b0a795b26a8f4c2ebbb2ce Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sat, 9 Nov 2019 18:32:14 +0000 Subject: [PATCH 02/14] Correctly handle zeros, infinities and NaN's. --- example/formatter_docbook_output.cpp | 77 ++++- example/formatter_docbook_output.qbk | 37 ++- example/formatter_docbook_output_test.qbk | 1 + example/formatter_html_output.cpp | 91 +++++- example/formatter_html_output.html | 34 +- example/formatter_latex_output.cpp | 86 ++++- example/formatter_text_output.cpp | 30 +- include/boost/math/tools/formatting.hpp | 373 ++++++++++++++++------ 8 files changed, 623 insertions(+), 106 deletions(-) diff --git a/example/formatter_docbook_output.cpp b/example/formatter_docbook_output.cpp index e6752ea2ec..62a58e83a5 100644 --- a/example/formatter_docbook_output.cpp +++ b/example/formatter_docbook_output.cpp @@ -42,7 +42,25 @@ void print(std::ostream& os) printer.stream() << "[[" << fval << "][3][scientific + multiply_x]['''"; printer << std::setprecision(3) << std::scientific << boost::math::tools::multiply_x << fval << "''']]\n"; printer.stream() << "[[" << fval << "][3][scientific + multiply_dot]['''"; - printer << std::setprecision(3) << std::scientific << boost::math::tools::multiply_dot << fval << "''']]\n" << boost::math::tools::multiply_times; + printer << std::setprecision(3) << std::scientific << boost::math::tools::multiply_dot << fval << "''']]\n" << boost::math::tools::multiply_times << std::defaultfloat; + fval = 0; + printer.stream() << "[[" << fval << "][default][default]['''"; + printer << fval << "''']]\n"; + fval = -fval; + printer.stream() << "[[" << fval << "][default][default]['''"; + printer << fval << "''']]\n"; + fval = std::numeric_limits::infinity(); + printer.stream() << "[[" << fval << "][default][default]['''"; + printer << fval << "''']]\n"; + fval = -std::numeric_limits::infinity(); + printer.stream() << "[[" << fval << "][default][default]['''"; + printer << fval << "''']]\n"; + fval = std::numeric_limits::quiet_NaN(); + printer.stream() << "[[" << fval << "][default][default]['''"; + printer << fval << "''']]\n"; + fval = -std::numeric_limits::quiet_NaN(); + printer.stream() << "[[" << fval << "][default][default]['''"; + printer << fval << "''']]\n"; printer << "]\n]\n" << std::defaultfloat; @@ -66,7 +84,62 @@ void print(std::ostream& os) printer.stream() << "[[" << cval << "][12][default + doublestruck_i + multiply_x]['''"; printer << boost::math::tools::doublestruck_i << std::defaultfloat << std::setprecision(12) << boost::math::tools::multiply_x << cval << "''']]\n"; printer.stream() << "[[" << cval << "][12][default + doublestruck_i + multiply_dot]['''"; - printer << boost::math::tools::doublestruck_i << std::defaultfloat << std::setprecision(12) << boost::math::tools::multiply_dot << cval << "''']]\n" << boost::math::tools::multiply_x; + printer << boost::math::tools::doublestruck_i << std::defaultfloat << std::setprecision(12) << boost::math::tools::multiply_dot << cval << "''']]\n" << boost::math::tools::multiply_times << boost::math::tools::upright_i; + printer << "\n]\n]\n"; + + printer << "[template complex_formatting_examples_2[]\n"; + printer << "[table:complex_fmt_examples Complex Special Values\n[[Value][Precision][Format][Result]]"; + cval = std::complex(0, 0); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << boost::math::tools::show_zero_components << cval << "''']]\n" << boost::math::tools::hide_zero_components; + cval = std::complex(2.5, 0); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << boost::math::tools::show_zero_components << cval << "''']]\n" << boost::math::tools::hide_zero_components; + cval = std::complex(-2.5, 0); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << boost::math::tools::show_zero_components << cval << "''']]\n" << boost::math::tools::hide_zero_components; + cval = std::complex(0, 3.25); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << boost::math::tools::show_zero_components << cval << "''']]\n" << boost::math::tools::hide_zero_components; + cval = std::complex(0, -3.25); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << boost::math::tools::show_zero_components << cval << "''']]\n" << boost::math::tools::hide_zero_components; + // Infinites and NaN's: + cval = std::complex(std::numeric_limits::infinity(), 2.5); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + cval = std::complex(-std::numeric_limits::infinity(), 2.5); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + cval = std::complex(2.5, std::numeric_limits::infinity()); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + cval = std::complex(2.5, -std::numeric_limits::infinity()); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + cval = std::complex(2.5, std::numeric_limits::quiet_NaN()); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + cval = std::complex(std::numeric_limits::quiet_NaN(), 2.5); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + cval = std::complex(std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + cval = std::complex(std::numeric_limits::quiet_NaN(), std::numeric_limits::infinity()); + printer.stream() << "[[" << cval << "][default][default]['''"; + printer << cval << "''']]\n"; + printer << "\n]\n]\n"; } diff --git a/example/formatter_docbook_output.qbk b/example/formatter_docbook_output.qbk index cd58fc3a5e..07017d7243 100644 --- a/example/formatter_docbook_output.qbk +++ b/example/formatter_docbook_output.qbk @@ -15,14 +15,20 @@ [[-1.23457e-24][3][scientific]['''-1.235×10-24''']] [[-1.235e-24][3][scientific + multiply_x]['''-1.235x10-24''']] [[-1.235e-24][3][scientific + multiply_dot]['''-1.235⋅10-24''']] +[[0][default][default]['''0''']] +[[-0][default][default]['''-0''']] +[[inf][default][default]['''''']] +[[-inf][default][default]['''-∞''']] +[[nan][default][default]['''NaN''']] +[[-nan(ind)][default][default]['''NaN''']] ] ] [template complex_formatting_examples[] [table:complex_fmt_examples Basic Complex Values -[[Value][Precision][Format][Result]][[(3.25,4.67)][default][default]['''3.25 + 4.67i''']] -[[(3.14,0)][default][default]['''3.14 + 0i''']] -[[(1.23,-1.23e-24)][default][default]['''1.23 - 1.23×10-24i''']] -[[(1.23,-1.23e-24)][3][scientific]['''1.230×10+00 - 1.235×10-24i''']] +[[Value][Precision][Format][Result]][[(3.25,4.67)][default][default]['''3.25 + 4.67''']] +[[(3.14,0)][default][default]['''3.14''']] +[[(1.23,-1.23e-24)][default][default]['''1.23 - 1.23×10-24''']] +[[(1.23,-1.23e-24)][3][scientific]['''1.230×10+00 - 1.235×10-24''']] [[(1.230e+00,-1.235e-24)][12][default + slanted_i]['''1.23 - 1.2345678765×10-24i''']] [[(1.23,-1.2345678765e-24)][12][default + doublestruck_i]['''1.23 - 1.2345678765×10-24''']] [[(1.23,-1.2345678765e-24)][12][default + doublestruck_i + multiply_x]['''1.23 - 1.2345678765x10-24''']] @@ -30,3 +36,26 @@ ] ] +[template complex_formatting_examples_2[] +[table:complex_fmt_examples Complex Special Values +[[Value][Precision][Format][Result]][[(0,0)][default][default]['''0''']] +[[(0,0)][default][default]['''0 + 0i''']] +[[(2.5,0)][default][default]['''2.5''']] +[[(2.5,0)][default][default]['''2.5 + 0i''']] +[[(-2.5,0)][default][default]['''-2.5''']] +[[(-2.5,0)][default][default]['''-2.5 + 0i''']] +[[(0,3.25)][default][default]['''3.25i''']] +[[(0,3.25)][default][default]['''0 + 3.25i''']] +[[(0,-3.25)][default][default]['''-3.25i''']] +[[(0,-3.25)][default][default]['''0 - 3.25i''']] +[[(inf,2.5)][default][default]['''∞̃''']] +[[(-inf,2.5)][default][default]['''∞̃''']] +[[(2.5,inf)][default][default]['''∞̃''']] +[[(2.5,-inf)][default][default]['''∞̃''']] +[[(2.5,nan)][default][default]['''NaN''']] +[[(nan,2.5)][default][default]['''NaN''']] +[[(inf,nan)][default][default]['''NaN''']] +[[(nan,inf)][default][default]['''NaN''']] + +] +] diff --git a/example/formatter_docbook_output_test.qbk b/example/formatter_docbook_output_test.qbk index eb24ce4eb2..baf2da0e1d 100644 --- a/example/formatter_docbook_output_test.qbk +++ b/example/formatter_docbook_output_test.qbk @@ -19,3 +19,4 @@ [complex_formatting_examples] +[complex_formatting_examples_2] diff --git a/example/formatter_html_output.cpp b/example/formatter_html_output.cpp index 4cc40cb043..d39523e23a 100644 --- a/example/formatter_html_output.cpp +++ b/example/formatter_html_output.cpp @@ -49,7 +49,26 @@ void print(std::ostream& os) printer.stream() << "" << fval << "defaultmultiply_dot"; printer << boost::math::tools::multiply_dot << fval << "\n"; printer.stream() << "" << fval << "3scientific + multiply_dot"; - printer << std::setprecision(3) << std::scientific << fval << "\n" << boost::math::tools::multiply_times; + printer << std::setprecision(3) << std::scientific << fval << "\n" << boost::math::tools::multiply_times << std::defaultfloat; + + // Infinities: + fval = std::numeric_limits::infinity(); + printer.stream() << "" << fval << "defaultdefault"; + printer << fval << "\n"; + fval = -std::numeric_limits::infinity(); + printer.stream() << "" << fval << "defaultdefault"; + printer << fval << "\n"; + // NaN's: + fval = std::numeric_limits::quiet_NaN(); + printer.stream() << "" << fval << "defaultdefault"; + printer << fval << "\n"; + // Zeros: + fval = 0; + printer.stream() << "" << fval << "defaultdefault"; + printer << fval << "\n"; + fval = -fval; + printer.stream() << "" << fval << "defaultdefault"; + printer << fval << "\n"; printer << "\n\n" << std::defaultfloat; @@ -74,7 +93,75 @@ void print(std::ostream& os) printer.stream() << "" << cval << "12default + doublestruck_i + multiply_x"; printer << boost::math::tools::doublestruck_i << boost::math::tools::multiply_x << std::defaultfloat << std::setprecision(12) << cval << "\n"; printer.stream() << "" << cval << "12default + doublestruck_i + multiply_dot"; - printer << boost::math::tools::doublestruck_i << std::defaultfloat << std::setprecision(12) << boost::math::tools::multiply_dot << cval << "\n" << boost::math::tools::multiply_times; + printer << boost::math::tools::doublestruck_i << std::defaultfloat << std::setprecision(12) << boost::math::tools::multiply_dot << cval << "\n" << boost::math::tools::multiply_times << boost::math::tools::upright_i; + printer << "\n\n\n"; + + printer << "

Complex Special Values:

\n\n"; + printer << ""; + cval = std::complex(0, 0); + printer.stream() << "\n"; + printer.stream() << "\n"; + cval = std::complex(3.14, 0); + printer.stream() << "\n"; + printer.stream() << "\n"; + cval = std::complex(-3.14, 0); + printer.stream() << "\n"; + printer.stream() << "\n"; + cval = std::complex(0, 25.5); + printer.stream() << "\n"; + printer.stream() << "\n"; + cval = std::complex(0, -25.5); + printer.stream() << "\n"; + printer.stream() << "\n"; + cval = std::complex(std::numeric_limits::infinity(), 0); + printer.stream() << "\n"; + cval = std::complex(-std::numeric_limits::infinity(), 0); + printer.stream() << "\n"; + cval = std::complex(25.5, std::numeric_limits::infinity()); + printer.stream() << "\n"; + cval = std::complex(25.5, -std::numeric_limits::infinity()); + printer.stream() << "\n"; + + cval = std::complex(std::numeric_limits::quiet_NaN(), 0); + printer.stream() << "\n"; + cval = std::complex(-std::numeric_limits::quiet_NaN(), 0); + printer.stream() << "\n"; + cval = std::complex(25.5, std::numeric_limits::quiet_NaN()); + printer.stream() << "\n"; + cval = std::complex(25.5, -std::numeric_limits::quiet_NaN()); + printer.stream() << "\n"; + + cval = std::complex(std::numeric_limits::quiet_NaN(), std::numeric_limits::infinity()); + printer.stream() << "\n"; + cval = std::complex(-std::numeric_limits::quiet_NaN(), std::numeric_limits::infinity()); + printer.stream() << "\n"; + cval = std::complex(std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()); + printer.stream() << "\n"; + cval = std::complex(std::numeric_limits::infinity(), -std::numeric_limits::quiet_NaN()); + printer.stream() << "\n"; + printer << "\n
ValuePrecisionFormatResult
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultshow_zero_components"; + printer << boost::math::tools::show_zero_components << cval << boost::math::tools::hide_zero_components << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultshow_zero_components"; + printer << boost::math::tools::show_zero_components << cval << boost::math::tools::hide_zero_components << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultshow_zero_components"; + printer << boost::math::tools::show_zero_components << cval << boost::math::tools::hide_zero_components << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultshow_zero_components"; + printer << boost::math::tools::show_zero_components << cval << boost::math::tools::hide_zero_components << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultshow_zero_components"; + printer << boost::math::tools::show_zero_components << cval << boost::math::tools::hide_zero_components << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
" << cval << "defaultdefault"; + printer << cval << "
\n\n"; printer << "\n\n"; diff --git a/example/formatter_html_output.html b/example/formatter_html_output.html index 8779d21aa6..4101bac4fb 100644 --- a/example/formatter_html_output.html +++ b/example/formatter_html_output.html @@ -19,12 +19,17 @@

Basic Floating Point Values:

-1.235e-243scientific + multiply_x-1.235x10-24 -1.235e-24defaultmultiply_dot-1.235⋅10-24 -1.235e-243scientific + multiply_dot-1.235⋅10-24 +infdefaultdefault +-infdefaultdefault-∞ +nandefaultdefaultNaN +0defaultdefault0 +-0defaultdefault-0

Complex Values:

- + @@ -34,5 +39,32 @@

Complex Values:

ValuePrecisionFormatResult
(3.25,4.67)defaultdefault3.25 + 4.67i
(3.14,0)defaultdefault3.14 + 0i
(3.14,0)defaultdefault3.14
(1.23,-1.23e-24)defaultdefault1.23 - 1.23×10-24i
(1.23,-1.23e-24)3scientific1.230×10+00 - 1.235×10-24i
(1.230e+00,-1.235e-24)12default + slanted_i1.23 - 1.2345678765×10-24i
+

Complex Special Values:

+ + + + + + + + + + + + + + + + + + + + + + + + +
ValuePrecisionFormatResult
(0,0)defaultdefault0
(0,0)defaultshow_zero_components0 + 0i
(3.14,0)defaultdefault3.14
(3.14,0)defaultshow_zero_components3.14 + 0i
(-3.14,0)defaultdefault-3.14
(-3.14,0)defaultshow_zero_components-3.14 + 0i
(0,25.5)defaultdefault25.5i
(0,25.5)defaultshow_zero_components0 + 25.5i
(0,-25.5)defaultdefault-25.5i
(0,-25.5)defaultshow_zero_components0 - 25.5i
(inf,0)defaultdefault∞̃
(-inf,0)defaultdefault∞̃
(25.5,inf)defaultdefault∞̃
(25.5,-inf)defaultdefault∞̃
(nan,0)defaultdefaultNaN
(-nan(ind),0)defaultdefaultNaN
(25.5,nan)defaultdefaultNaN
(25.5,-nan(ind))defaultdefaultNaN
(nan,inf)defaultdefaultNaN
(-nan(ind),inf)defaultdefaultNaN
(inf,nan)defaultdefaultNaN
(inf,-nan(ind))defaultdefaultNaN
+ diff --git a/example/formatter_latex_output.cpp b/example/formatter_latex_output.cpp index 0620e1bdc7..54b1c5e92a 100644 --- a/example/formatter_latex_output.cpp +++ b/example/formatter_latex_output.cpp @@ -48,7 +48,42 @@ void print(std::ostream& os) printer.stream() << fval << " & 3 & scientific + multiply\\_dot & "; printer << std::setprecision(3) << std::scientific << boost::math::tools::multiply_dot << fval << " \\\\\n"; printer.stream() << fval << " & 10 & scientific + latex\\_as\\_text + multiply\\_dot & "; - printer << std::setprecision(10) << boost::math::tools::latex_as_text << boost::math::tools::multiply_dot << std::scientific << fval << " \\\\\n" << boost::math::tools::latex_as_equation << boost::math::tools::multiply_times; + printer << std::setprecision(10) << boost::math::tools::latex_as_text << boost::math::tools::multiply_dot << std::scientific << fval << " \\\\\n" + << boost::math::tools::latex_as_equation << boost::math::tools::multiply_times << std::defaultfloat; + + // Special Values: + fval = 0; + printer.stream() << fval << " & default & default & "; + printer << fval << " \\\\\n"; + fval = -fval; + printer.stream() << fval << " & default & default & "; + printer << fval << " \\\\\n"; + fval = std::numeric_limits::infinity(); + printer.stream() << fval << " & default & default & "; + printer << fval << " \\\\\n"; + fval = -fval; + printer.stream() << fval << " & default & default & "; + printer << fval << " \\\\\n"; + fval = std::numeric_limits::quiet_NaN(); + printer.stream() << fval << " & default & default & "; + printer << fval << " \\\\\n"; + printer << boost::math::tools::latex_as_text; + fval = 0; + printer.stream() << fval << " & default & latex\\_as\\_text & "; + printer << fval << " \\\\\n"; + fval = -fval; + printer.stream() << fval << " & default & latex\\_as\\_text & "; + printer << fval << " \\\\\n"; + fval = std::numeric_limits::infinity(); + printer.stream() << fval << " & default & latex\\_as\\_text & "; + printer << fval << " \\\\\n"; + fval = -fval; + printer.stream() << fval << " & default & latex\\_as\\_text & "; + printer << fval << " \\\\\n"; + fval = std::numeric_limits::quiet_NaN(); + printer.stream() << fval << " & default & latex\\_as\\_text & "; + printer << fval << " \\\\\n"; + printer << boost::math::tools::latex_as_equation; printer << "\\end{tabular}\n\n" << std::defaultfloat; @@ -74,6 +109,55 @@ void print(std::ostream& os) printer << boost::math::tools::upright_i << std::defaultfloat << std::setprecision(12) << cval << " \\\\\n"; printer << "\\end{tabular}\n\n" << std::defaultfloat << boost::math::tools::multiply_times; + printer << "\\textbf{Complex Special Values}\n\n\\begin{tabular}{r r r r}\nValue & Precision & Format & Result \\\\\n"; + cval = std::complex(0, 0); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + printer.stream() << cval << " & default & show\\_zero\\_components & "; + printer << boost::math::tools::show_zero_components << cval << " \\\\\n" << boost::math::tools::hide_zero_components; + cval = std::complex(2.5, 0); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + printer.stream() << cval << " & default & show\\_zero\\_components & " << boost::math::tools::show_zero_components; + printer << boost::math::tools::show_zero_components << cval << " \\\\\n" << boost::math::tools::hide_zero_components; + cval = std::complex(-2.5, 0); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + printer.stream() << cval << " & default & show\\_zero\\_components & "; + printer << boost::math::tools::show_zero_components << cval << " \\\\\n" << boost::math::tools::hide_zero_components; + cval = std::complex(0, 2.5); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + printer.stream() << cval << " & default & show\\_zero\\_components & "; + printer << boost::math::tools::show_zero_components << cval << " \\\\\n" << boost::math::tools::hide_zero_components; + cval = std::complex(0, -2.5); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + printer.stream() << cval << " & default & show\\_zero\\_components & "; + printer << boost::math::tools::show_zero_components << cval << " \\\\\n" << boost::math::tools::hide_zero_components; + // Infinity: + cval = std::complex(std::numeric_limits::infinity(), 2.5); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + cval = std::complex(2.5, std::numeric_limits::infinity()); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + // NaN: + cval = std::complex(std::numeric_limits::quiet_NaN(), 2.5); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + cval = std::complex(2.5, std::numeric_limits::quiet_NaN()); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + cval = std::complex(std::numeric_limits::quiet_NaN(), std::numeric_limits::infinity()); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + cval = std::complex(std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()); + printer.stream() << cval << " & default & default & "; + printer << cval << " \\\\\n"; + printer << "\\end{tabular}\n\n" << std::defaultfloat << boost::math::tools::multiply_times; + + printer << "\\end{document}\n\n"; } diff --git a/example/formatter_text_output.cpp b/example/formatter_text_output.cpp index 81eef0d28d..5a1ce4304d 100644 --- a/example/formatter_text_output.cpp +++ b/example/formatter_text_output.cpp @@ -56,7 +56,35 @@ void print(std::ostream& os) printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(30); printer << cval << std::endl; printer.stream() << std::setw(20) << cval << std::setw(20) << "3" << std::setw(20) << "scientific" << std::setw(30); - printer << std::scientific << std::setprecision(3) << cval << std::endl; + printer << std::scientific << std::setprecision(3) << cval << std::endl << std::endl << std::defaultfloat; + + printer << "Complex Zeros:\n\n"; + cval = std::complex(0); + printer << std::setw(20) << "Value" << std::setw(20) << "Precision" << std::setw(23) << "Format" << std::setw(30) << "Result" << std::endl; + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "show_zero_components" << std::setw(30); + printer << boost::math::tools::show_zero_components << cval << std::endl << boost::math::tools::hide_zero_components; + cval = 3.14; + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "show_zero_components" << std::setw(30); + printer << boost::math::tools::show_zero_components << cval << std::endl << boost::math::tools::hide_zero_components; + cval = -3.14; + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "show_zero_components" << std::setw(30); + printer << boost::math::tools::show_zero_components << cval << std::endl << boost::math::tools::hide_zero_components; + cval = std::complex(0, 25.25); + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "show_zero_components" << std::setw(30); + printer << boost::math::tools::show_zero_components << cval << std::endl << boost::math::tools::hide_zero_components; + cval = std::complex(0, -25.25); + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "show_zero_components" << std::setw(30); + printer << boost::math::tools::show_zero_components << cval << std::endl << boost::math::tools::hide_zero_components; } int main(int argc, const char* argv[]) diff --git a/include/boost/math/tools/formatting.hpp b/include/boost/math/tools/formatting.hpp index 7d266ffd6a..b9ee2521b3 100644 --- a/include/boost/math/tools/formatting.hpp +++ b/include/boost/math/tools/formatting.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,12 @@ namespace boost { doublestruck_i = 2 }; + enum zero_component_t + { + hide_zero_components = 0, + show_zero_components = 1 + }; + struct latex_as_text_t {}; struct latex_as_equation_t {}; @@ -113,6 +120,7 @@ namespace boost { imaginary_i_t i_style; multiplyer_t multiply_style; bool requires_parenthesis; + bool m_show_zero_components; protected: template static void decompose_float(std::basic_ostream& os, const Float& f, std::basic_string& mantissa, std::basic_string& exponent) @@ -133,7 +141,7 @@ namespace boost { mantissa = s; } public: - basic_numeric_formatter_base() : styling_level(full_styling), requires_parenthesis(false), multiply_style(multiply_times) {} + basic_numeric_formatter_base() : styling_level(full_styling), requires_parenthesis(false), multiply_style(multiply_times), m_show_zero_components(false) {} void styling(styling_level_t i) { @@ -167,6 +175,14 @@ namespace boost { { return requires_parenthesis; } + void show_zero_components(bool b) + { + m_show_zero_components = b; + } + bool show_zero_components()const + { + return m_show_zero_components; + } template static std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) { @@ -182,8 +198,22 @@ namespace boost { return os << mantissa; } template - static std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) + std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) { + if (!this->show_zero_components()) + { + if (f.imag() == 0) + { + format_float(os, f.real()); + return os; + } + else if (f.real() == 0) + { + format_float(os, f.imag()); + os.put(os.widen('i')); + return os; + } + } format_float(os, f.real()); typename Complex::value_type i(f.imag()); bool isneg = i < 0; @@ -198,7 +228,8 @@ namespace boost { os.put(os.widen('i')); return os; } - void latex_as_equation(); + bool latex_as_equation()const { return false; } + void latex_as_equation(bool) {} }; template @@ -227,26 +258,40 @@ namespace boost { template std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) { - std::basic_string mantissa, exponent; - basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); if (this->styling() >= minimal_styling) os << ""; if (this->styling() > minimal_styling) os << ""; - if (exponent.size()) + if ((std::isinf)(f)) { - os << mantissa; - if (this->multiply() == multiply_x) - os.put(os.widen('x')); - else if (this->multiply() == multiply_dot) - os << "⋅"; - else - os << "×"; - os << "10" << exponent << ""; + if (f < 0) + os.put(os.widen('-')); + os << "∞"; + } + else if ((std::isnan(f))) + { + os << "NaN"; } else - os << mantissa; + { + std::basic_string mantissa, exponent; + basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); + + if (exponent.size()) + { + os << mantissa; + if (this->multiply() == multiply_x) + os.put(os.widen('x')); + else if (this->multiply() == multiply_dot) + os << "⋅"; + else + os << "×"; + os << "10" << exponent << ""; + } + else + os << mantissa; + } if (this->styling() > minimal_styling) os << ""; @@ -263,33 +308,59 @@ namespace boost { if (this->styling() > minimal_styling) os << ""; - styling_level_t saved_style = this->styling(); - this->styling(no_styling); - - format_float(os, f.real()); - typename Complex::value_type i(f.imag()); - bool isneg = i < 0; - if (isneg) + if ((std::isnan(f.real())) || (std::isnan)(f.imag())) { - i = -i; - os << " - "; + os << "NaN"; + } + else if ((std::isinf)(f.real()) || (std::isinf)(f.imag())) + { + os << "∞̃"; } else - os << " + "; - format_float(os, i); + { + styling_level_t saved_style = this->styling(); + this->styling(no_styling); - if (saved_style >= minimal_styling) - os << ""; - if (this->imaginary_style() == doublestruck_i) - os << "ⅈ"; - else if (this->imaginary_style() == slanted_i) - os << "i"; - else - os.put(os.widen('i')); - if (saved_style >= minimal_styling) - os << ""; + bool need_i = true; - this->styling(saved_style); + if (!this->show_zero_components() && (f.imag() == 0)) + { + format_float(os, f.real()); + need_i = false; + } + else if (!this->show_zero_components() && (f.real() == 0)) + { + format_float(os, f.imag()); + } + else + { + format_float(os, f.real()); + typename Complex::value_type i(f.imag()); + bool isneg = i < 0; + if (isneg) + { + i = -i; + os << " - "; + } + else + os << " + "; + format_float(os, i); + } + if (need_i) + { + if (saved_style >= minimal_styling) + os << ""; + if (this->imaginary_style() == doublestruck_i) + os << "ⅈ"; + else if (this->imaginary_style() == slanted_i) + os << "i"; + else + os.put(os.widen('i')); + if (saved_style >= minimal_styling) + os << ""; + } + this->styling(saved_style); + } if (this->styling() > minimal_styling) os << ""; @@ -327,6 +398,26 @@ namespace boost { std::basic_string mantissa, exponent; basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); + if ((std::isinf)(f)) + { + if (!inside_equation) + os.put(os.widen('$')); + if (f < 0) + os.put(os.widen('-')); + os << "\\infty"; + if (!inside_equation) + os.put(os.widen('$')); + return os; + } + else if ((std::isnan)(f)) + { + if (inside_equation) + os << "\\mathrm{NaN}"; + else + os << "NaN"; + return os; + } + if (latex_as_equation()) { if (!inside_equation) @@ -367,29 +458,64 @@ namespace boost { if (!inside_equation && latex_as_equation()) os.put(os.widen('$')); bool saved_inside_equation = inside_equation; - inside_equation = true; - - format_float(os, f.real()); - typename Complex::value_type i(f.imag()); - bool isneg = i < 0; - if (isneg) + inside_equation = latex_as_equation(); + if ((std::isnan(f.real())) || (std::isnan)(f.imag())) { - i = -i; - os << " - "; + if (inside_equation) + os << "\\mathrm{NaN}"; + else + os << "NaN"; + return os; + } + else if ((std::isinf)(f.real()) || (std::isinf)(f.imag())) + { + if (!inside_equation) + os.put(os.widen('$')); + os << "\\tilde{\\infty}"; + if (!inside_equation) + os.put(os.widen('$')); + return os; } else - os << " + "; - format_float(os, i); + { + bool need_i = true; - if (this->imaginary_style() == doublestruck_i) - BOOST_THROW_EXCEPTION(std::runtime_error("Doublestruck imaginary i not implemented for LaTex output.")); - else if ((this->imaginary_style() == upright_i) && latex_as_equation()) - os << "\\mathrm{i}"; - else if ((this->imaginary_style() == slanted_i) && !latex_as_equation()) - os << "\\textit{i}"; - else - os.put(os.widen('i')); + if (!this->show_zero_components() && (f.imag() == 0)) + { + format_float(os, f.real()); + need_i = false; + } + else if (!this->show_zero_components() && (f.real() == 0)) + { + format_float(os, f.imag()); + } + else + { + format_float(os, f.real()); + typename Complex::value_type i(f.imag()); + bool isneg = i < 0; + if (isneg) + { + i = -i; + os << " - "; + } + else + os << " + "; + format_float(os, i); + } + if (need_i) + { + if (this->imaginary_style() == doublestruck_i) + BOOST_THROW_EXCEPTION(std::runtime_error("Doublestruck imaginary i not implemented for LaTex output.")); + else if ((this->imaginary_style() == upright_i) && latex_as_equation()) + os << "\\mathrm{i}"; + else if ((this->imaginary_style() == slanted_i) && !latex_as_equation()) + os << "\\textit{i}"; + else + os.put(os.widen('i')); + } + } inside_equation = saved_inside_equation; if (!inside_equation && latex_as_equation()) os.put(os.widen('$')); @@ -419,27 +545,40 @@ namespace boost { template std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) { - std::basic_string mantissa, exponent; - basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); - if (this->styling() >= minimal_styling) os << ""; if (this->styling() > minimal_styling) os << ""; - if (exponent.size()) + if ((std::isinf)(f)) { - os << mantissa; - if (this->multiply() == multiply_x) - os.put(os.widen('x')); - else if (this->multiply() == multiply_dot) - os << "⋅"; - else - os << "×"; - os << "10" << exponent << ""; + if (f < 0) + os.put(os.widen('-')); + os << "∞"; + } + else if ((std::isnan(f))) + { + os << "NaN"; } else - os << mantissa; + { + std::basic_string mantissa, exponent; + basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); + + if (exponent.size()) + { + os << mantissa; + if (this->multiply() == multiply_x) + os.put(os.widen('x')); + else if (this->multiply() == multiply_dot) + os << "⋅"; + else + os << "×"; + os << "10" << exponent << ""; + } + else + os << mantissa; + } if (this->styling() > minimal_styling) os << ""; @@ -456,33 +595,59 @@ namespace boost { if (this->styling() > minimal_styling) os << ""; - styling_level_t saved_style = this->styling(); - this->styling(no_styling); - - format_float(os, f.real()); - typename Complex::value_type i(f.imag()); - bool isneg = i < 0; - if (isneg) + if ((std::isnan(f.real())) || (std::isnan)(f.imag())) { - i = -i; - os << " - "; + os << "NaN"; + } + else if ((std::isinf)(f.real()) || (std::isinf)(f.imag())) + { + os << "∞̃"; } else - os << " + "; - format_float(os, i); - - if (saved_style >= minimal_styling) - os << ""; - if (this->imaginary_style() == doublestruck_i) - os << "ⅈ"; - else if (this->imaginary_style() == slanted_i) - os << "i"; - else - os.put(os.widen('i')); - if (saved_style >= minimal_styling) - os << ""; - - this->styling(saved_style); + { + styling_level_t saved_style = this->styling(); + this->styling(no_styling); + + bool need_i = true; + + if(!this->show_zero_components() && (f.imag() == 0)) + { + format_float(os, f.real()); + need_i = false; + } + else if (!this->show_zero_components() && (f.real() == 0)) + { + format_float(os, f.imag()); + } + else + { + format_float(os, f.real()); + typename Complex::value_type i(f.imag()); + bool isneg = i < 0; + if (isneg) + { + i = -i; + os << " - "; + } + else + os << " + "; + format_float(os, i); + } + if (need_i) + { + if (saved_style >= minimal_styling) + os << ""; + if (this->imaginary_style() == doublestruck_i) + os << "ⅈ"; + else if (this->imaginary_style() == slanted_i) + os << "i"; + else + os.put(os.widen('i')); + if (saved_style >= minimal_styling) + os << ""; + } + this->styling(saved_style); + } if (this->styling() > minimal_styling) os << ""; @@ -502,6 +667,16 @@ namespace boost { public: basic_numeric_printer(std::basic_ostream& os) : p_stream(&os) {} + basic_numeric_printer(std::basic_ostream& os, const basic_numeric_printer& printer) + : p_stream(&os) + { + styling(printer.styling()); + imaginary_style(printer.imaginary_style()); + latex_as_equation(printer.latex_as_equation()); + parenthesis(printer.parenthesis()); + multiply(printer.multiply()); + show_zero_components(printer.show_zero_components()); + } using base_type::format_integer; using base_type::format_float; @@ -511,6 +686,7 @@ namespace boost { using base_type::latex_as_equation; using base_type::parenthesis; using base_type::multiply; + using base_type::show_zero_components; std::basic_ostream& stream() { @@ -537,7 +713,7 @@ namespace boost { ss.copyfmt(os.stream()); ss.imbue(os.stream().getloc()); ss.width(0); - basic_numeric_printer fmt(ss); + basic_numeric_printer fmt(ss, os); fmt.format_integer(fmt.stream(), i); os.stream() << ss.str(); } @@ -555,7 +731,7 @@ namespace boost { ss.copyfmt(os.stream()); ss.imbue(os.stream().getloc()); ss.width(0); - basic_numeric_printer fmt(ss); + basic_numeric_printer fmt(ss, os); fmt.format_float(fmt.stream(), f); os.stream() << ss.str(); } @@ -573,7 +749,7 @@ namespace boost { ss.copyfmt(os.stream()); ss.imbue(os.stream().getloc()); ss.width(0); - basic_numeric_printer fmt(ss); + basic_numeric_printer fmt(ss, os); fmt.format_complex(fmt.stream(), f); os.stream() << ss.str(); } @@ -617,6 +793,13 @@ namespace boost { return os; } + template + basic_numeric_printer& operator << (basic_numeric_printer& os, const zero_component_t z) + { + os.show_zero_components(z == ::boost::math::tools::show_zero_components ? true : false); + return os; + } + // // Output formatters: // From a19e335115b3ebfa880114849266ea5cfd61107b Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sat, 9 Nov 2019 19:44:56 +0000 Subject: [PATCH 03/14] Fix up sample Latex document structure. [CI SKIP] --- example/formatter_latex_output.cpp | 8 +++-- example/formatter_latex_output.pdf | Bin 24395 -> 75796 bytes example/formatter_latex_output.tex | 47 +++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/example/formatter_latex_output.cpp b/example/formatter_latex_output.cpp index 54b1c5e92a..07f79bbe5e 100644 --- a/example/formatter_latex_output.cpp +++ b/example/formatter_latex_output.cpp @@ -11,7 +11,11 @@ void print(std::ostream& os) { boost::math::tools::latex_printer printer(os); - printer << "\\documentclass{article}\n \\begin{ document }\n\n"; + printer << "\\documentclass[10pt]{article}" << std::endl; + printer << "\\usepackage{lingmacros}" << std::endl; + printer << "\\usepackage{tree-dvips}" << std::endl; + printer << "\\usepackage[a4paper, left = { 0.5in }]{ geometry }" << std::endl; + printer << "\\begin{document}" << std::endl << std::endl; int ival = 0; printer << "\\textbf{Basic Integer Values}\n\n\\begin{tabular}{r r r}\nValue & Base & Result \\\\ \\hline \n"; @@ -118,7 +122,7 @@ void print(std::ostream& os) cval = std::complex(2.5, 0); printer.stream() << cval << " & default & default & "; printer << cval << " \\\\\n"; - printer.stream() << cval << " & default & show\\_zero\\_components & " << boost::math::tools::show_zero_components; + printer.stream() << cval << " & default & show\\_zero\\_components & "; printer << boost::math::tools::show_zero_components << cval << " \\\\\n" << boost::math::tools::hide_zero_components; cval = std::complex(-2.5, 0); printer.stream() << cval << " & default & default & "; diff --git a/example/formatter_latex_output.pdf b/example/formatter_latex_output.pdf index 4ce1e41dfb3e3acddd0c79546b5f7d905220a8cf..5ad48a589c57f974a0fb505d2415660dcc0e2736 100644 GIT binary patch literal 75796 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%0DQi@uxQtp@uJpE}~dSVTZ%>sVbS=hYQ)6!;Be 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

(fP zR3>(!i$sOHcj55CF}Xtw4uRp_s!zW9|Mr;>#oWx7YwYTsh!DP4@d zfzE2VH_KNoUnrSLND$}J@sa3eWxr(OTF32GfM6w0@?;Dh{ZyJQn9z*=jgx`j_ntCS z24p$~eT)#%;d<`N{dK71UHsR#bD-ZPuJVmid406Ay4@x=Q%q8Ja{aXf8^31D9jeOH z)b1f8m{lfXjltvlEttMSR4A^ixwU});Z2C<$HDl~rHma|v0eCw#t)7=VTN@})_i7J z9oc=J+Y23@!&q2o%t6eZH7=R~@^QsW;TE^crpTsba%s(nf`{hcKop&) z&Cpr3Hj)W~M&CLMNPc%GS!%bi=Zi~}SF?QjCHR<{Kh8}sa5$_i0_B4YOZ+~!(BBKM zQ=@yYdUy9>ZQOCVtL$r{6{XU#I}6Dk%C0k{;E+Wbs#4P3Vl98tOT1?%IGT{{C%BFq zC1+9DvPpcMS^Scv!Yfu8YuKaBJYy3W`#|Lh-nEMNddxjS>X?SWwzGG>g_|s1GN^P* zJW@6e>+L01!O*p?teB{Pn_4}?_|Tm_7FAhxT0j1>F+(Uq%U)e|*{fUSFNO4?`s^+WUGp8B zJ(;cR@138Es2CHUz|b@M6x2UTEN|<2+|G|zT{w29E1Zj~H|#OSAYsbCvj zykdLQV~QnbO4I4p>m5Hj#BHD2)?hhC-%yHdTm+O`TW#CU4`ntDD|!t5Y;j zoh$W^O2=x`pJ6{1?--D(wWvV;j<0!Qt-NR}IGAou+V#D@4qbk2n_``K(#Jf;6!6G) zN^9pb2*?Eg=pC{}^R2*8ueo*`fu_BInwoWYV&>@J)Zk01$LS9~VG!JU?jo)dbb`%A z3=!kfHTvn|LDw~1RZ$s}x}B5=?D@FNGX34_-^LjHmZeq0j7y`J>8#sP?R~szDH9Xu ztBKcM^^c-|({tSsbTyoBV^a{jjHP`$o#xa-lzh7u0S)U(aaMedhl*SW-p5tOtmUz^ ze6bB#-W4v-wxE%_uWV?sb8+Exq9vsnuyJ0`b2f2TSE{G;Z{Sjg?~y5sJ#A^Z&fIfV zX{96JW!mt(V@ttVm|k^z$@d;F~Fg8+PYOg4^gUCiDXtDt2vR{JO~s!xnj7s*AW5%8O@_o5`GK!+qCA z$YfzRMl~1Gbt-*(Qn$jSH=9p`n~+p8`TLk*QE=))?{yPb<)TcloRTPB5(-08$v&FL zr_#ydUM2)B8iJn`X0nW0Kh&|&i@8HH$lHzc^*)X3%YAFcWjx}0Jj|kXqoWCz73+~) zIBU}Pq)!&EHvDzVzM8o6AWyuFUHP5m86UrP-pmQ|BI7y@TCjS1$ADp5LJvN zoizieEgmZ93(G63U!Vg`M2vl-`wBtatuMs0%+XECUDCcbL`u<^F8C!xIX&BR=25aA z>iRvN1RamXlC&WA#vC6EET#WN7?;Q6Rg&2C8$ru%cKp!}phfKglXWHGMyMKT(DCmw zWFmGMn+0p}Ton~*KJQjz$Qjwscu5M9q_H!85l;L*(KCBIxVGm_q36=;Ev{Ua z5pbk&P~;eN*Ys1s81!K$0eXb>vMW;XUR1|sx+rK ze|uQw;qgmT;82o=8Z#bV(V5lLbnq7wMjP{k4TBkzubJD+-nIq>e7*{kQ&9CCN3iFp z%p|>{xnhyfRmk&sD%#mCRp_CouGE*npp)VJ^WoTc124QzSurv_Yv;m> z=P7Sn&|7FUnwaKoNGpX4GX3KH9#Q|^OYz|~?7f{EkVlDMR1Pd$RM+71Y2a7_`=K&x<_V&GFLI5ED)x_cLF9SK= zK?0Y>p z_h4YHagHG1`v)A6Kj46%qG(YhN(>AV_zT$ieM)e1u(YBCgJ391@ZTR05`h5DX)Dm5 zG$>dMP{iH`Wd9cpf<^#ldXNSN1MXP|X<{%0>Tq8%7z+6l4Gy?b{nS@%@6bFjhZs;* z{--(M0LxE&@V&)5)E5xz5Dg4PqYsYz^w4YvO&a0tR(Q~lt-sf} z3^I_|n_WQx5ELm3xh$h_86gXY!)4H73NSf@3h($ literal 24395 zcmeIaby$>5_W-OCDh+~w5)0A-TWrwM-AIGf(%lG%iU@+z(vm6?g3=}3DT08MG$<`f z$G3}x@x0Ibeb?{${(4#0z3;ha&di*eGv~~Gn3csPxxm~!gv^su-Mxf(JOB{D-q?yz zKmaIjX@@d$vb>Hm1%QF_00gaMXFB~Wh&;@`&yl~7T zeIX%2l$|LC4B*c{LOc&Alo_Tc9uOfO=-VF{4mi33*nKYpLqXs_%iw>O!H|$Y>cPQ( zmLWjMKk6ZPLBE&5A@D!i!r|aQ+9E)(KgNsz!(qR}L%?~Vf0Xe;{^%D8g7f}f4+;LW z4MDvNaXMI1tZ|lKk6aTnE7pP5D*XYcl?2lr054dT%3$7ZBR~xcxZuCL(3w7 zhX<%?Z|?#?j~Ac{l(93j2cWyhT#D-o@Q8yX;o_o_2qa7r0vG3zK%$uxiI5P5L!shO zNgg2qgt!P8BE~C%fQln|VLTvSNjMxXA`asPNq|HVVqhVCpt2Ln4I|Y^I9iVJoL!8Z zT)s*yT3nf#C6puy@qU+Tv@~M|@lA0^TH3gvoPd%xMlL9El!?753Mc~r^MHWzC_8f( z3ji3x15^XRKte*!E>0*TTS7dGF=InRWt5?zrO}B~c>x5^Zc+qlQz5ypl-<95+nAbG z`csy2M-8C4rhUinR;vFy6Rzw#U_aqQY_S0Mj&oxO!7wL}hH%F&2Biju`;GyR>zF1& zitL!J@Gr%OtRh~`tqm@Yz%8*Zj)@Ldwqo{nE+{(} zXI;nN6i7yBZvU#w`Ul|i++Y?f6Nb>D*&qcKrttK2T^+uT}(qB1RUKI zp$}9*nOYkCQL1d@gzoFx&{R>*_O4DQC}%X+NO-tNskxv@0XTvUlthmTa0DMsX?qhj zl#4D9y$Ar+Q64U6&5h=+qspMK^TSN|=j1RDz9B{x0pK~B!Oy1{px>XOEe##K>5Yj#pchQSN5H` zwSy%z@~c<&lQun*=e9|dH{7DG07$qDin@H7#;@fKv7E61zI%7Tft|J@JeoXvB1F`o zQ26s3eE+VkMx=U3?E@3`)GH*Uk;O;|he7dWCiP`FuR~BmMMg$5Pe~lq%%hwu3b)-2 z2UooGIbK)*t{m3%UV;N+rKLu{_LE0RtFze5r)MSaJuboFZr5+}m|cI&IX%kWJ7VnK zDmL1W(~RA|fqlBnQ$n(3ROn0?ZUrF)!e@SSQra>~l#bI`mx7v{GdP9K%Bdl(g)L6= zz8)zFB|Q~+rL%(ce7XL_#V61a^RDa88@hDPEJ~i8jzbH*>{hTZctv-0e*`RMXioYmIN3t_5S}mK6_#>7T&5ZoLOiH%s9sC*5GS#U}(mv_7 z@Cem(c2+9_n16Ffe#R%9uG@lc_w{d&`b?ahP}ud~K+wvk@iJoV^Xb z8$Y(~TT}_4*+dG_3EI09M3(fXf~!pELcY*>`HUnwZcpX%oAdfEd+XYmtMR`pud#Br zxEh<-lY83FORu5%H4*)b2I*Z6BX&wfglTdgEjGT3C6T2PvdZ{L497$)w&t5^y`9xb z|1`n{WW=8E4fk^`S9IG`5QIL4g!b~>_|P1Gi5H#1P864_KHXsfUZ$6uA>oVY%oRloY%Q zb6$9`Fby2JsqgV{K1h+8k@{I$qmn33n|oQT!>}A9jqlusu)U`NOPz5mhtmV*De+;R z?Js17;}>>}7Ct{&vHg_D=9r)P&}^6>GNqi-+RMLql&WbGKQbwz$86l^a@xzQ&_9w-}ru#*qVV#)X=X^X<~u$ zB@PvxV}4IiuYB>^b4bB@FCFKSE2(e|4qU1@f#uxkiPEUNPmzJu7ti>FETk@SKf)oZ zlToNo_5#I>NzJho+il@e(R^tSZAtU^C}s>K6^=c9^2SR%NhP8eRAK{(i#fb!J2dIL z8Hr3klZE=%85E@p1m_oYVgPQ|E>|vxphFq6ZrsOkb=9=mbe6;gz zmxoS;SUwOKaoIJ$mK4$d^t@c%$J=iyz*-EjN9aqL1ySRhM#Qp^6a0 z#}so^*wvrMgUuMY3l&V)C@y>AXts&#C}j>uaY{Ny_PsXk*vxzolLgeD?rQ7rtpqIV z;kmaXlPqN;(;luYt`yLkCpbKC&2Tgh_j_m%k@UWGlOmiY^2Q2uE$#hhPv3AU4Jpb1 zM6!<)MIV&eltYl%Y@A1tW|N`t>VS70K}axrZBT_mH2?DEkV4mUP4DGYscua(%9o$X zH+<@6vFQlrPc&_<9jq|Xw^e>9f;@z9E@*KF3|m)kMNO%YmG}1VLbagnc&c*p&WxW19trva>Zx&VoT^-xE=ObMql4}oaWbB z!@J0IeP~j@O4pqy5!pKwoaDK;osH{hOF=%I@9%-F_HL*?fIpkFzbP7v_TBx5A^p-t zJplg)^WgP^5>9 z)5r5>cT%{cLA3TlA%lv0EQ?EF{H16tpU>Pk?3ud0km*BN$UrzkR3wYXF7M)LQ`D%evAexD z>)u+5I3v;IG|4*c9}pQ{OS$m+kLO24!-nDq1tF6*t5QL z+DEdUr&!-LB^kx^AU_bFav!?k$&yfyO_Xuz!amob=f|GF2K&hG;wu=#D1;wA`Z*RA@}zc z%ngmjjhv9r!Xfnbz4SNlW9eCGc;mHDd0H;k#$(;hdiv;R`Gx~)SoI?khAMt>Lu0pf;*0%gj=pLPk%_(`$DX6aDiw& zul+Ts(1BoodA!bw)8wVoV3vKb!}-ce*NdyR$uNstQz_D23_+GZ9Z zHY%DY)_s(^A3kkh(SGuHP1(ebS2(vUWq_jq@-VE8De7_bd5vk!=qLR+jXRlc*bXPZ zoV<<$>U~u?_63TQuqmw?F(({JxiMw}uF6qBrkNBW+|cr$$z^NM{n@rp~0sXo&xu=KXk z2a2>&#Ny$#luswB-=3`{5IR_ALVjtdV5zFkkRNQEW6vcnng<=O`2+`VA)=<#&IemQ zyJH+#rL2jCYq*@^bAG!*vt!sae_91^H|68UW`~DmO5%tXrV#H9d+FEDsv9mkL-nqk zb#zPL^px(MJmi>Rt}+^!AUPB8;v)`HjS7oSefVq)|3OTx zhO}_po=;mz?iyuDe_q~rMv5ET!}0lyu9#%*4{oHsp>j58>%(^SX7aMyTDskJA~pRD zOMMFA7lhqLI?)e@IY|$rO!zzcTknTY1U;K)Q!6YQvdNjctFcVE`kd4a-%;k!X+tT6 zFiP-RfzWPL7lGM)AJ2ZKg5tg!C=Z)37ov@tHk7}x z)d>^k*3>_RN@?#L?Us6)cL;k%yzM#X3&}pru!!}xHVR)^p+2m554|x5x@>FZpZWeR zlf`o5DIt^HDNRoAgl*gTf#DUeXxthrE%H|;J9c>;o~uNhEspkazMq%!jQJ;cyiII& zQ*&82IrL1V&uj+~-iFO??Y6Bu5DVQ7ey#6Neo9Jz%>3^4`H;L=y?MfQBWBKIBq0%kv-XIXtC{!^c}<)yO$WSV?hl>q$9hN6pD9Xy)?BvdE)~ z#`ZbV(l7FJZoSJN)1KCv)KGYTnr6*jzsEZ7?mkG=pgEWk>WJ$!r&OQ5#^!VwjU)~5 zx9|8YbaE&E?&Nv7N@gB?EFGBU_J z8J{k7p+Lxc>Fsv*TtM8URSvFN=RMo725tTdxeK=}dD9(~-sU{mh?Mqm%R~*i*xqNs zdQ(KLGuR$@I;zc^ZE8q-AYm+7)ZkOl>iRY14V7n)8G5a5*UOtkF)*6B=fA(d5|eu3 z(el`S+*HZkh0A$W#x%H8*9wPDdEDX8T|JY|x;vYq$2q-mCs^mRekD_39lOPL0rlv0 z(?_Q36EC~v-!=46t z{0~>d4YCK$pH#bMmYXFD3SbT}vq>I>Qfy}rIC<ebfGb!}BF0q=r9pELZ8@0q7Gbe%q)JJb<&ELo_2VvlyfBNdI-` zNP?$Z)8i6K#n6H6V!Npz)dbU7Y{@+j+XkDJR$$fM`Wx2yRk4-42W=*`&-D_)FC7|5 z%~VeWo6KHqc9ov+Tf?Wc`IPu-D{{=)mD1jAmErBf`3KdW7qN-E&#vLN8j28?-+@4g z9=T8H)C`=Yq-~Nw;x<1dm`pMMRPZ_L)$+=@wF`F8mvYRa2Hmq1;XUSo^`4NY_MQwN zPu?BxR(d%T#%j0AAGJ%f8hRFbj9?*g`|Q46>L^rZUrJ_R%pP)^GY*G|s%7udv>+rJ zneq5jhZ;01F{iSQKRJEt(lXN!MLLPyD&sYw=D_9N83DzP?;*l?~;lTE+B7s1eFcr$2!q`-Gkt3H&`r^0d7EcEgU z*PQ-m?2S*GEgO4Lv}CH=)n1Pjq|eP2igO)eO*OvKd4L%_s*1L@&Th|6zkhG*vrh3E(w=JU9^YylgH%s`mW%hjUM!%}o#{iAV{!fa zfC;atdda9R&{NRrpMR^Rlc=fO^1g;Pzo4k~GNbV7FpEm&R#txO{gIrJN$Y{IS=(2{ zn#t$Ljmk)@9bjG|QF*j_G)Z_nNqCO??8PikIT#sPSQyhdo<4n=m37xyHP$A$9hAn_ z&BVwbWVz6iq%tDyMeH|qrdLR+na*#@B522+g690)zc|=1p3v`p7ZGO@jN=Lc1EHO1 z4$>$~a|;&$90>)ASr|E?y;E#JQJ^GH8K?@>02%>JfF|~~wnjixAPRU?6=;Tb@Sy*> zp@3#+pBK;^XaThFbg)3#0WEm=ShXOhP9WX938c0xDQKJEJ{YnwF+$cPR`60;;*7Y&FmxHFUrQLXX#`B-upU0wo|H1vq1O1iOzj!~H6}zo_m1%tQ?j>|-`Ce&+*uFPG(}Sz>8tNfU0o{4M zbAI7eB2-f!^llV|$s{-O3I%U3ovFSAf_pYCP{uNWWCUj8E~e-OOkVYHB|J;DkkFZE zka=O|L4?HlD2a$xvlU=?#Rr#Y0hLS@4bunHsBH18@9^4n^Rq6atgg1!)kmPN(s2TZ ziyuW?g+94>G4y;)pnkFJG!86tEhINGqueZfE?>5FyQHsd#-%9VQpIoji>sBz8UJgH zsTW$Lgu?<~A~*Mm-#@4WlPj)PpF3$#*BonqJ>D!nem1UZ#q$OuAN5Anr@b@v0wp+f z0m&~@&RBmDX}5i`)!$#->!Rz!dbV-gmE?`14p;wu{(#|t)4BXqOom){$k>+04tBYN ziHWZJ#1y&nw+bRkPsi2SYu$RN`)GMi>Cy7(l2Nl=%H0PBuU-~RA^S zYO2bqO30uUq?-1Btsy}?KQ+-$4S7r}9*fUlf(S=i@z;*NiyYuc7FB^i^&)yIzbIY+ z_-hpIhgyXEDkwjP@?9x@S7_+p<3fKduJT_;HekqaDF0{iJ{FMq58{o9#{FY(!^G6k z$k@=-)YN_B%NHJ=6X2^iq@f34Mmr4cBcrQ6b4m*Rc676LbkPrm8}3xGp1Bb^dq~G! zQ`6BBdu?bk@tU%BR9uouT1t3MrlEzUWp28msi9%!cx-}BR9s?g#(08OM0k9H)~V@f zHtac8^QY1392SG7QBmYt9~NR_ViuTZ&o9hSJ{h+B$QETDZOVrIP7?WHknBaO<#}9|lOd>;x^3Wwx_)CxZ&S z=$bP7PUg{G4o60FnPRe6gCZCk6C*Ehqk z(k=&4&dpGuzg*k~FOAnGDV)5Y;QJz#65vx_%c8~a@AVF9I_l3xPZ@)gyX(tL8HtIp zuuk9{JdyjGEFX)OeUoMMDTZURe029+mMy>W(oeDF0)xL9H<;i(0)E8$-z^-pIRHVQ zYCwbdMSj0V6)`gMRenXq;U>5BWtOeE3xsevan51b<)T|7ITiUz9<4c#dfX$e)HXoELGNikQ3aR1^h@ zp-p9(Z-z1oZ43W2fgO&lTNjKadyL2s(03|BzuJ7?DU5yq;Dy4_Y8oAJ#$fHM4U0Ll zfsWJuAhEobf~<-tn#7`7|1F6z2Lb*<<6}lF>=?cOX2ky5>34enVIln_Jp3#7{#*|L zM|l0ko<-Z($CcpEA$})35*?;S9)=AdjiIV17dCXIw~%X?&f@X^_d zHU?=n+2YY>4N+{|eQ?tk++lV>acG|M8h& zhj!iYb%iJrHoZJHiAx2m1*dzMsJb-1@RC`VYQ5zYPt4E_r@5j2g636CW$a`IFnvhxWyZc}PE zE#^G)yBkOPnj=GVVb&y)wqXCQ1x=z54Cj^=g!H4SoeO)t}kCOTIv zzDU^`T0RBWwd(Bbphw;pnoic0NoR$N1mVBc{E{O{8Nf1zu)6?@5moz~w#;mv5nn)< zNWFQ-olWE7#i~%(-9!BNYQYA1LC>3`!R0N`)N1?w?!!7D<4cRuwe-l(t=Yq)^{nYx za>Og79%l}2y>*#S3ktrf?I?P|La^4vU{fyHo50Wr7vYy2$hnvn5(|WvGAezEOgy(} z{bEbePh>a_%GGvy4_kT;>Q-$tVM?Wc@QJ z)r#ra-iDd1_&6Htxr-t$ktRttw(qw7-a#uY! zmZU^&Kvc0GiMt<2w{OI`m@Sn0-QkQLM2GY9&4N6Mq&$D1MNXIC&RDzt?j~3G!pvCZ zgS%XX!TFtU4oD48bUBXF{AC@59NQ!P*S6WW4cR|hW|-4v-}mPKEUOSW@2^VfmyOpC z%glL$FofsyOZ8)h*}ik@=1k70K*y*6Cw{d%o=+&*9$nX=p!m&OapB;+(pKK;lzcX* zV7j=-C2h6Z*KgkFN!7O0)Tq{i5k+k1ovKnT;w9517~&;%ORLJ6E~PGcu1xjUFIw5y z*`h7D>tT&*^EstFfzMmW=kgR(o%6n|E$Pdfo+BPy*RM z-+o?RT2X4DvPt7vHQ6Y<6!T&~I=QPVz?yh7XY1PM|a;B&ZDi&&t>r+wk!xF`uGw8c6>cNBIL34P(e&WL>03hs%azsOP>xH z@pG;Dxk4RV0)N{aVAfvXFa3O90nvx{&}{V|H!P5^hpT=VJpaDx@j{NTF~@ck-;GKH z&oQi`&yxN$M~?h)-+XaL{xysz?KdA97>qW`j(liv82YH4fqU4$ z?iP_p4kaj>?J$M}?8u<_@&7Th99bpb#{19e1orFF->0Dv+;@e9BD9u1+yqzV-Iup- zmsD2s5irCWpo04h|Ltev-6eeVkzI;Z6pkvhAc@ZG7|pABy-+=vAnQpDOE2*j(lT1R z_!52Jd8`*~r0yPP039r4e&EjVC+VcM&_^$;%6>M&qk=nhy!F>ted$z9^!KNBuxs2-ykQC$fU=?dBA&kPD&&V=gfQu z_hoV1~R3(CA%q*R>9_jyMmscyHIt6J`DvVdA<*4wBT|R!GX}Ie1?)%-=5DD-{~#U ziVSA*E8(4j%C82^MGr;bn>CyFK+|_nmB4`6t71Y*t2o!K?f7g$lSsSg*_{U}Erk$O z_ddl{=Y}o?PuK_}MRd$hxb4O``o|7U%zye(F~#8j;t+387c zuR^VmQ;%e8525o-gmbeOHhJQh-gM_ZDs{(lwM1O>{gN^_e%k8B%R*snsuSa}ub2L! zdyfvd{wq7eG0y7m2Y{vTEo#(;^nnN@XtMVMX`rGo$3-NO6chKz&9djn6db0htG1XwE z969iD|KLb@^Q;NGP{)MhBfeV^GHUwf=NYNHS8YQoM?e>eJR^ROLM2YmB?7P#wTC#26ycXdVV_dV& zgWWKNRP6ZILE8I`gBJPgnfEpD)gxF<0y_>~1o!vlv>sBul!5Imm^BZVR`ZMuC>+d% zQ7Y6kR1Lc4@ztqc=}WDxot-kIE|QOj4vZ!&Tbs}63peLw`*~FfC!C^T=%Pwt)5N_( zULrimIdVTj;d7lOvDb-oIt7c?&7sz;W3?tL zNkRf&nPqqq`}19%<2^21R-`9|Vn6b3j9Qt=PAoR_y>gAm;N8a6g%tPTtV^ka z<5STx>1TzhM`~d`HdyL37wE)0U_C*nBY2&C2dJ=^6d#^^A(WMC`5C79I?eB0Ql5)L zZs*Q9S{qg|zQmlWg-veSU#6V+} z;1CR`xY)hu+|aVLY=Y$@XMMtenF2fX*3f9x_Bc0Uc%1njNBOYAsyylFrm86enb0sT z<&~V1{@!h-u@!P+9;)#$aVF|`$_&kd6Hp5?iFP#5kIMa=fR zMKeqD(PiT^aV$u%=v&FDCXxb5?Vti20?GK%l6)ZZ)5pukHF6B)N(HOEyiaZv)K14m z-cKUneVw+ejWQJD>{V&&-em?=EM)C^n_Z3bMyirO*b1e4{gg1MYLtgDjgYqFW{tt% zQ&N?jQ))9S%{?Rn310|m)2|4JP*jU#J$nWvo?HyKqKnG;B+SDWr*9~ee!k*Hh-|_e zHS&gcNQy^NUxaWv|EMWAoJ>KWBPiI% zT`=hk5QLjWcC$Z96IRUpvR3Vdg!@wOgCY0w`>oqFFTOCnJ?HjT!e%?Av}E*=f~V`X z3VZqKLV~vew@ZlT%5U)RE;W&yRk(?`h*NO0X{Wm0(>VH_E{qCF;6YNp@#2atlSKH_Za(Gb4MGQ1)oH5m`+_qG=MAha&NRXSLN7>4-x4V{1OF<=VGgE7CmC=X$3u|H8T= zLoQFll}~A~J789<>-X=9CznXsctt4?HfC7N+Yh_i-71tHeyhdZFITz>W~EI(Yp}$hAvdtvPsKk&?wr0AK&chH z@VN{A-1+>#Or}9qm6uJRNKT#w-Q<@BT-|7?0!LBpSB|uO@5U3)pQfZ6^m!Nb=$=#{ z`6}izT z3hj)lR~T3wjFTe%?6cCv9ibGhYI(nrc7Bk$KA{U_$Dy1vy=iZ=#Z`0Stt6S>-}vy@ zF8`nPJDi92I3HGCZN=z!37Xx@JA>)3n~H{J0~`D=f)Ei{jM$9Kfn0}5Wic-^DkSc5 zwJ|U&z4e;8J~OZz)7d*vW9uZNQZ{-GCcX%0>Mz-CpS9hz8Ss)~I$7U%qh-StUY+Mg)|^b{3=&nU z>~2U58}+M)x&-LMVKNp4My|DXqZ?9Mp-oln;zDKi>9GldZ*D%fSIB>T6Exp?!E|26 zk0FYALe}qu9N~>!?nqpU;9Q;*H(t1dfxxF|tB)j?l*@E_0_7@Ub@Q==3tH(5sVp22 z+tFQQ!kQ7FcBX5kYB576x8sLk7Fc^(TzV;OPa9rD=iAc*@V} zSKH*(0ruZ zPc2KbQ}z9l`fNj4_F3aK(X&&tnMof#Zg=f1iO|^ZecXzj^wTzAs2KR9PLm%JozYaW zTKP=(srk)MHQ| z^~T~=Is0=W1d=qHnv0H%TF@K!o9jvM$=P4AdySPw&QTPY(a_yokg%xuhLFQ5lq~=m zvcJ9Gxsl^lwq)Qce?RQ{n^Unx)sFs*(n&Ck<*ys?GB0^_ARytOvDj;4$oD*(EXV#v zetqj>ckG(SAt$ICca{`3;d-{Xoa~~}Hxtg(B|b^C@8$?9WQ!8y)W})#-xYjMKpLy| zw6Z`bP$ukQ&f;^+yF}hJ{zOj9SL6!br(bAHBg-gaOD`4Q_Bwfr*;q?k}eDJgM zKy}%cV2kXbCV1zhjtQB_w7}fDhZTW&SS8kD~D}xYZGgR+Mn-Bh2XOL2M2{aLDH%Dr3hoxZb>i( zYzEKeHSJyJ4Ow-DmSSI!^H7yINq$y1OQOrl^mISrc+3OmyDuK%sbo+DOW8mzAvpzf z_d>lLZ@rXaPsxPPGiM0oYp<_#%$-q2@{7Lu2!ST&Q9m=%9k{3CMzKYopADe)4E0r_ zXfQ5SS5`eK{PyW>q0RoA);Z=K{O3g! z`s=>`v8td_1Bl=u)%s<~>>aB`<|d;Uu)7mcrYOS3v+-M*C^Di&%TY(q6}|OJ8L47mpurS%;lYe(k?!ZR#p3g|#*v9>!z`#2?b7j=72=r+Y{;o7 zCVL5)dz|f!@RI7mDQ9(x^JnwrobWVyfIB*kEU8VsjAseqC2WiGm&eW)G%pcKrzySg zh!?+xFDh-z38Uo?QKlOT_Gnzw;NvRkDCh7BAk`^9}dH$*|aNAcYH13XtJmJ9?r?(=zXwXwnsBmz?pSU{QB))2+t$Gb7n`TPO)mmB!*wr^zVh1x4(ldeqbA>X{! z$4R~aJZZZEOq;C4ogwtf@BM{DXN$P$$PtKI>x^c}u8EQqS$fG+;D=-%ZSO?v&g8N- zyvQfBck8c|%a}htcfGbi2@K)S#qLsK(@ZBH+fuw7xka7~C^SYjj~k@y6Ww9F{>gba ze&|-@Ob-6g<}2c>0l`C>Gb!^&{KlUs)ZB&R_CN@c$Q9{}E?%-ymH3;{??HffB{{6HxDc@qI!m_UIZ9 z1@(>sZPkU1|GxyjjtRTn9wuDA?N1bI(a@ zCl(&g#INY=JJl%--G~b0W>41B)lg2l##8M%yEn70(X#fCdv8Ku$;)wN(ch-}T*k}1 zNxp8b=0;2cnZqRVLOel~61+|r$St6_gN4LU+G@^*aL>7#au1f7;39kkh~LoG_@N!v)i zxrCX9h@w?cYfeJ+baq04p~$%WsGjd3PWjA-ro=ubvAf(OhH&a$LbDlE0XGE7{?>x~ zwSulGB%$E^&i2gQTsQpj9NI4;qdm#57L#lR|G;>^tSM2zB%hlJTA!n9B8~V+CxzHu zD`bQUS_i+2O~>z+htHZSlfxJ;u{ld4M%s!e@c<=vEXrwBF{87UEr&|V=W zYHH2SKP4oACvwRzEA(`p8_zV?j7)D{ygK;3Epi5WiGz_ZVPH%oh@m}5|I7X2Ls@1f z{0no;ji_oYYi8olB1+!)$*crB9#$v;cHvqhuDcN8Q0^dIvWr6cSIWc0vX)2N9Rl`w z#<3vD^h7kvAR`nK-=}h8^*Kr}|1|i`u5os)U})3PZ?L>q)XE zQZ9gN9(`B8+ZosD6!Ur5?!~4U$|gro@15K4HCRXh6PT6q|*+hT@Dd@dLvXztMDRT`yD}3GEb*Y zbuEHh&G4#pU$17{Eo|=SORisDpQRmz=(!mAF0TN;euZPLJn_wjca4`p$^y6S7 zx6jkwit9HUKfJ$j`_%PqVZc~>8k{l~XDP;y;4Gbpa&0#oL2xxLUwU{ig;us>+YWw^ z??N8Vu7D$be@-8dLHrl{_+o9tKas<~%<~uJXz(c@LEZUOaHSLq*S? zlTy(}UuQ{$tzP><<@7YQ4PU*tBgm)>U=)yKV2}uU6k03U*Mp>wkEUTT(Z1K;0%j)K zj)RBh8q^bxq>czs96U{Fc%?&qor-Cr&0N{N;)f(^u3de9~5m7-hB zaoxG_t4t>o6wR>Kc7faK*0`poXd1w#x{txIXHko0PFZ5fq;HMn{e+udQ8Lq%yz{=QF?m1=n*GKJdhM>YfJRM`fgY%)p zH`Y-6>AMc?1C{-62oOf&xnwg|Vbctjyh43Vq}$}Rh5uDbIhqe-xq`O}Xy|N_Zf8T9Z-!r4H!a~GF$>(ic*vrE4W~Ii zC`DT9k|>PlWo#3{bMKffNs`Vn&jda$`$QY-c|oJhqM_~RAoxqX!Qn@DpNMj#Kh)5X znX&s+_n3N1y_Vk7cI-6Z3(rQrKQbVpwPW>i+dajP0uRW$p7W~Z&NpB3_X%aXn(nRr z;F<|_fQh5AtAfOg)HpMkWwgAlYdLnpdg|WL;w~e0+t|gZ+zJHNK+*4`Ln2QqTnKNEII5e$+v=Uo(o^dDbmTtdEhOd zs;|Y*?VXd_n4CQ$E+Kl6xb`BeoLyrgse!9FEul&@F1APHn&}%vucJ`c(r(qpGaOlb z+b6nmcPa-13zZv~f#;_p1+5mnhi7Y$x%v2ngFBdQ=Voi4=2`Y}By3 zWl-_52JH#h!yB*OJkl^FZ1V%bcj_R0Caa@zE6HqC<>h{%Hrn`5fbN82W@)CAIVFw1 z*lvhAjbUCGAs%o(avpDjz4c;?_~b!bP0dWU@+FJa*?C9)I|p>`6n&#%pZadxHRBDs zBy!2s<%9V>0d}GxuL#kUUC7EWcw4JnYZABS&e?{n0u8X@= zdE>rJkG`17A-0OxZs*?&sbc|7C1nLnWs<`C0?$CFFj212~4tzkMySqEL zJCxhr$s7pg<>du}AVBmU8C`?R+0)L&$b-wy`O;S)-{E{eGGu9Q2f)BJGPZYh5oBci zCF|PPc{v>G$j+JjXpY?I7bJn`2S6}46!^DBXw>`|-7k$ij2zI7K>yT8LE%3)v$g$e zOH-2{%^X~vY>qHtY63*rplmV8(a=4DfjgeCW(%rmiL^Cm|_id4QCKy|c^L zmmL8}ZUk5WGrgZxe&R&{JvBZBbW$)gPjPg@Ga)qH&=do6f#B+3Bp(RO2Lg$7=o!hP{)zy}gZ)shg#PGn+L#nz49!5ojKB z@H~3)6vI>qfU1$HrHPRZK*AFRP_s94aYw(RdJOtE7{7u4E+&5r^0zsDOI>$V?N<;A z3Vh=BCaxF)$cPJ>qCgNh4-b+HiZnGwn21>@y0f|{5Zp-pSU;Maam(0sVfgkqcTVFw_?S{(SUneHTj?8`P0deizZN(kWu&@@wgLakH_+ zNG+d@k)639qX(BM%FGD;-Xx=t13C?&CCZ%}O+W!4Mh8}do2qIczpp_Nyhqn3^{*UqhA7A{$*Z(o#-GQ8^{*&8Ug3Y}I*yHh zz5XA%{uM>XEBp^#$FcFR*Z)J;zoO`Ph5v*uyr26Wbl6Ui(H*^;`O9YQ*rB;2BNS~E zVT?RBaXyU23&y1ChJZjv)+#Fq#LAA&BaAj`?VZ?;j8Q!{YrX#$<|sQ5ZBhRY9tlF{ z;MQTsWDq{G)7k!Jr?UgqF~%6%F-wa5=ce#mPH6zo_k`~ln-hJw1cpA8fld#PxdR>L vng1hcHVld0gJS-EBrJzOd6DRMHolkrnKb+VEIT%O{Kr#bgTe40NwNPQN@JTq diff --git a/example/formatter_latex_output.tex b/example/formatter_latex_output.tex index 6783c64e94..2bc3d640bc 100644 --- a/example/formatter_latex_output.tex +++ b/example/formatter_latex_output.tex @@ -1,5 +1,8 @@ -\documentclass{article} - \begin{ document } +\documentclass[10pt]{article} +\usepackage{lingmacros} +\usepackage{tree-dvips} +\usepackage[a4paper, left = { 0.5in }]{ geometry } +\begin{document} \textbf{Basic Integer Values} @@ -24,21 +27,53 @@ -1.235e-24 & 10 & scientific + latex\_as\_text + multiply\_x & -1.2345678765x10\textsuperscript{-24} \\ -1.2345678765e-24 & 3 & scientific + multiply\_dot & $-1.235\cdot 10^{-24}$ \\ -1.235e-24 & 10 & scientific + latex\_as\_text + multiply\_dot & -1.2345678765$\cdot$10\textsuperscript{-24} \\ +0 & default & default & $0$ \\ +-0 & default & default & $-0$ \\ +inf & default & default & $\infty$ \\ +-inf & default & default & $-\infty$ \\ +nan & default & default & NaN \\ +0 & default & latex\_as\_text & 0 \\ +-0 & default & latex\_as\_text & -0 \\ +inf & default & latex\_as\_text & $\infty$ \\ +-inf & default & latex\_as\_text & $-\infty$ \\ +nan & default & latex\_as\_text & NaN \\ \end{tabular} \textbf{Basic Complex Values} \begin{tabular}{r r r r} Value & Precision & Format & Result \\ -(3.25,4.67) & default & default & $3.25 + 4.67\mathrm{i}$ \\ -(3.14,0) & default & default & $3.14 + 0\mathrm{i}$ \\ -(1.23,-1.234567876e-24) & default & default & $1.23 - 1.234567876\times 10^{-24}\mathrm{i}$ \\ -(1.23,-1.234567876e-24) & 3 & scientific & $1.230\times 10^{+00} - 1.235\times 10^{-24}\mathrm{i}$ \\ +(3.25,4.67) & default & default & $3.25 + 4.67i$ \\ +(3.14,0) & default & default & $3.14$ \\ +(1.23,-1.234567876e-24) & default & default & $1.23 - 1.234567876\times 10^{-24}i$ \\ +(1.23,-1.234567876e-24) & 3 & scientific & $1.230\times 10^{+00} - 1.235\times 10^{-24}i$ \\ (1.230e+00,-1.235e-24) & 12 & default + slanted\_i & $1.23 - 1.2345678765\times 10^{-24}i$ \\ (1.23,-1.2345678765e-24) & 12 & default + upright\_i & $1.23 - 1.2345678765\times 10^{-24}\mathrm{i}$ \\ (1.23,-1.2345678765e-24) & 12 & default + slanted\_i + latex\_as\_text & 1.23 - 1.2345678765$\times$10\textsuperscript{-24}\textit{i} \\ (1.23,-1.2345678765e-24) & 12 & default + upright\_i + latex\_as\_text& 1.23 - 1.2345678765$\times$10\textsuperscript{-24}i \\ \end{tabular} +\textbf{Complex Special Values} + +\begin{tabular}{r r r r} +Value & Precision & Format & Result \\ +(0,0) & default & default & 0 \\ +(0,0) & default & show\_zero\_components & 0 + 0i \\ +(2.5,0) & default & default & 2.5 \\ +(2.5,0) & default & show\_zero\_components & 2.5 + 0i \\ +(-2.5,0) & default & default & -2.5 \\ +(-2.5,0) & default & show\_zero\_components & -2.5 + 0i \\ +(0,2.5) & default & default & 2.5i \\ +(0,2.5) & default & show\_zero\_components & 0 + 2.5i \\ +(0,-2.5) & default & default & -2.5i \\ +(0,-2.5) & default & show\_zero\_components & 0 - 2.5i \\ +(inf,2.5) & default & default & $\tilde{\infty}$ \\ +(2.5,inf) & default & default & $\tilde{\infty}$ \\ +(nan,2.5) & default & default & NaN \\ +(2.5,nan) & default & default & NaN \\ +(nan,inf) & default & default & NaN \\ +(inf,nan) & default & default & NaN \\ +\end{tabular} + \end{document} From d7a682d0416b02081df520d4daf2aa7199a8a856 Mon Sep 17 00:00:00 2001 From: Nick Thompson Date: Sun, 10 Nov 2019 11:11:12 -0500 Subject: [PATCH 04/14] Quick documentation cleanup. --- doc/fp_utilities/formatting.qbk | 39 +++++++++++++++++---------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/doc/fp_utilities/formatting.qbk b/doc/fp_utilities/formatting.qbk index b1673e228c..a25a50e743 100644 --- a/doc/fp_utilities/formatting.qbk +++ b/doc/fp_utilities/formatting.qbk @@ -27,25 +27,26 @@ [heading Description] -Class `basic_numeric_printer` provides "pretty printing" capabilities for numeric data, it's use mirrors that of the iostream -library, but fullfills a very different role: there is no "input streaming" and the output is intended to be human readable -and formatted for printing and not for consumption by machine. +The class `basic_numeric_printer` provides "pretty printing" capabilities for numeric data. +Its use mirrors that of the `iostream` library, but fulfills a very different role: +There is no "input streaming" and the output is human readable--and not intended for machine consumption. -Supported input number types are integers, floating point number, complex numbers, rationals(TODO), polynomials(TODO), intervals(TODO) -and containers thereof(TODO). Other types, plus all the standard library manipulators can also be streamed to `basic_numeric_printer` -but are simply formwarded to the underlying stream and ignored by the pretty printer. +Supported types are integers, floating point numbers, complex numbers, rationals(TODO), polynomials(TODO), intervals(TODO) +and containers thereof(TODO). +Other types, plus all the standard library manipulators can also be streamed to `basic_numeric_printer` +but are simply forwarded to the underlying stream. Output formats include plain text (not so useful), Docbook XML markup, HTML markup, and Latex. MathML may come later(TODO). [h4 Examples:] -Integer values have the same representation as the underlying stream would give them, but can have styling applied to their XML wrapper -in HTML and Docbook output modes. +Integer values have the same representation as the underlying stream would give them, +but can have styling applied to their XML wrapper in HTML and Docbook output modes. -Floats in scientific mode, are styled somewhat differently from the underlying iostream: '''-1.23457×10-24''' -and can be customised to change the default multipication operator: '''-1.235⋅10-24'''. +Floats in scientific mode are styled somewhat differently from the underlying `iostream`: '''-1.23457×10-24''' +and can be customised to change the default multiplication operator: '''-1.235⋅10-24'''. -Complex numbers inherit all the properties of floats, and in addition allow the unit "i" to be customised, for example +Complex numbers inherit all the properties of floats, and in addition allow the unit "i" to be customised, for example '''1.23 - 1.23×10-24i''' or '''1.23 - 1.2345678765⋅10-24'''. @@ -79,11 +80,11 @@ Complex numbers inherit all the properties of floats, and in addition allow the typedef basic_numeric_printer wlatex_printer; typedef basic_numeric_printer whtml_printer; -Class `basic_numeric_printer` is a `std::basic_ostream` lookalike which is constructable from an underlying +Class `basic_numeric_printer` is a `std::basic_ostream` lookalike which is constructible from an underlying stream, and has a single public member function which returns a reference to the underlying stream. -It's usage via `<<` manipulators is identical to that of `std::basic_ostream`: anything that `basic_numeric_printer` doesn't -understand is passed unchanged to the underlying stream, while numeric data alone is intercepted and pretty-formtted. +Its usage via `<<` manipulators is identical to that of `std::basic_ostream`: anything that `basic_numeric_printer` doesn't +understand is passed unchanged to the underlying stream, while numeric data alone is intercepted and pretty-formatted. [heading Manipulators] @@ -141,9 +142,9 @@ Controls how the imaginary unit is formatted (plain text output ignores this opt constexpr const unspecified latex_as_equation; constexpr const unspecified latex_as_text; -Controls how latex output is handled: the default is to format all numbers in equation mode, but streaming `latex_as_text` to the stream -will result in numbers being formatted in text mode instead. Note that even in text mode some entities need to drop into math mode temporarily -for example to access symbols such as `\times`. +Controls how Latex output is handled: the default is to format all numbers in equation mode, but streaming `latex_as_text` to the stream +will result in numbers being formatted in text mode instead. +Note that even in text mode some entities need to drop into math mode temporarily for example to access symbols such as `\times`. [heading Format Gallery] @@ -172,8 +173,8 @@ The above tables of sample output are available in various other formats [endsect] [/section:formatting] -[/ Copyright 2017 John Maddock. +[/ Copyright 2019 John Maddock. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt). -] \ No newline at end of file +] From c10624951107eb18220888cbc9889d6273f7cdac Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Mon, 11 Nov 2019 19:30:54 +0000 Subject: [PATCH 05/14] Add Unicode output option for the text formatter. --- example/formatter_text_output.cpp | 72 ++++- example/formatter_text_output.txt | 45 ++- include/boost/math/tools/formatting.hpp | 357 +++++++++++++++++++++--- 3 files changed, 427 insertions(+), 47 deletions(-) diff --git a/example/formatter_text_output.cpp b/example/formatter_text_output.cpp index 5a1ce4304d..68cbb3bbdf 100644 --- a/example/formatter_text_output.cpp +++ b/example/formatter_text_output.cpp @@ -4,12 +4,14 @@ // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include #include #include -void print(std::ostream& os) +template +void print(std::basic_ostream& os) { - boost::math::tools::text_printer printer(os); + boost::math::tools::basic_numeric_printer printer(os); printer << "Integers:\n\n"; @@ -39,10 +41,31 @@ void print(std::ostream& os) fval = -1.2345678765e-24; printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); printer << fval << std::endl; - printer.stream() << std::setw(20) << fval << std::setw(20) << "3" << std::setw(20) << "scientific" << std::setw(20); - printer << std::scientific << std::setprecision(3) << fval << std::endl; + printer.stream() << std::scientific << std::setw(20) << fval << std::setw(20) << "3" << std::setw(20) << "scientific" << std::setw(20); + printer << std::setprecision(3) << fval << std::endl << std::defaultfloat; + fval = 0; + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer << fval << std::endl; + fval = -fval; + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer << fval << std::endl; + fval = 0; + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer << std::scientific << fval << std::endl; + fval = -fval; + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer << fval << std::endl << std::defaultfloat; + fval = std::numeric_limits::infinity(); + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer << fval << std::endl; + fval = -std::numeric_limits::infinity(); + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer << fval << std::endl; + fval = std::numeric_limits::quiet_NaN(); + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer << fval << std::endl; - printer << std::endl << std::endl << std::defaultfloat; + printer << std::endl << std::endl; printer << "Complex Values:\n\n"; std::complex cval(3.25, 4.67); @@ -57,6 +80,12 @@ void print(std::ostream& os) printer << cval << std::endl; printer.stream() << std::setw(20) << cval << std::setw(20) << "3" << std::setw(20) << "scientific" << std::setw(30); printer << std::scientific << std::setprecision(3) << cval << std::endl << std::endl << std::defaultfloat; + printer.stream() << std::setw(20) << cval << std::setw(20) << "slanted_i" << std::setw(20) << "default" << std::setw(30); + printer << boost::math::tools::slanted_i << cval << std::endl; + printer.stream() << std::setw(20) << cval << std::setw(20) << "doublestruck_i" << std::setw(20) << "default" << std::setw(30); + printer << boost::math::tools::doublestruck_i << cval << std::endl << boost::math::tools::upright_i; + + std::cout << std::endl << std::endl; printer << "Complex Zeros:\n\n"; cval = std::complex(0); @@ -85,6 +114,36 @@ void print(std::ostream& os) printer << cval << std::endl; printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "show_zero_components" << std::setw(30); printer << boost::math::tools::show_zero_components << cval << std::endl << boost::math::tools::hide_zero_components; + // Infinities: + cval = std::complex(std::numeric_limits::infinity(), -25.25); + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + cval = std::complex(2, std::numeric_limits::infinity()); + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + cval = std::complex(-std::numeric_limits::infinity(), std::numeric_limits::infinity()); + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + // NaN's: + cval = std::complex(std::numeric_limits::quiet_NaN(), -25.25); + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + cval = std::complex(3, std::numeric_limits::quiet_NaN()); + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + cval = std::complex(std::numeric_limits::quiet_NaN(), std::numeric_limits::infinity()); + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + cval = std::complex(std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()); + printer.stream() << std::setw(20) << cval << std::setw(20) << "default" << std::setw(23) << "default" << std::setw(30); + printer << cval << std::endl; + + + printer << "\nPolynomials:\n\n"; + boost::math::tools::polynomial poly1 = { 2, -3, 4, 5 }; + printer << "Integer: " << poly1 << std::endl; + boost::math::tools::polynomial poly2 = { 2.4, -34.25, 4.2e-6, -5.34e-67 }; + printer << "Integer: " << poly2 << std::endl; } int main(int argc, const char* argv[]) @@ -95,6 +154,9 @@ int main(int argc, const char* argv[]) print(ofs); } else + { print(std::cout); + print(std::wcout); + } return 0; } diff --git a/example/formatter_text_output.txt b/example/formatter_text_output.txt index a5555ba51f..b696c57acf 100644 --- a/example/formatter_text_output.txt +++ b/example/formatter_text_output.txt @@ -12,14 +12,49 @@ Basic floating point values: Value Precision Format Result 3 default default 3 3.14 default default 3.14 - -1.23457e-24 default default -1.23457x10^-24 - -1.23457e-24 3 scientific -1.235x10^-24 + -1.23457e-24 default default -1.23457×10⁻²⁴ + -1.234568e-24 3 scientific -1.235×10⁻²⁴ + 0 default default 0 + -0 default default -0 + 0 default default 0.000×10⁺⁰⁰ + -0.000e+00 default default -0.000×10⁺⁰⁰ + inf default default ∞ + -inf default default -∞ + nan default default NaN Complex Values: Value Precision Format Result (3.25,4.67) default default 3.25 + 4.67i - (3.14,0) default default 3.14 + 0i - (1.23,-1.23e-24) default default 1.23 - 1.23x10^-24i - (1.23,-1.23e-24) 3 scientific 1.230x10^+00 - 1.235x10^-24i + (3.14,0) default default 3.14 + (1.23,-1.23e-24) default default 1.23 - 1.23×10⁻²⁴i + (1.23,-1.23e-24) 3 scientific 1.230×10⁺⁰⁰ - 1.235×10⁻²⁴i + + (1.23,-1.23e-24) slanted_i default 1.23 - 1.23×10⁻²⁴𝑖 + (1.23,-1.23e-24) doublestruck_i default 1.23 - 1.23×10⁻²⁴ⅈ +Complex Zeros: + + Value Precision Format Result + (0,0) default default 0 + (0,0) default show_zero_components 0 + 0i + (3.14,0) default default 3.14 + (3.14,0) default show_zero_components 3.14 + 0i + (-3.14,0) default default -3.14 + (-3.14,0) default show_zero_components -3.14 + 0i + (0,25.3) default default 25.3i + (0,25.3) default show_zero_components 0 + 25.3i + (0,-25.3) default default -25.3i + (0,-25.3) default show_zero_components 0 - 25.3i + (inf,-25.3) default default ∞̃ + (2,inf) default default ∞̃ + (-inf,inf) default default ∞̃ + (nan,-25.3) default default NaN + (3,nan) default default NaN + (nan,inf) default default NaN + (inf,nan) default default NaN + +Polynomials: + +Integer: 2 - 3x + 4x² + 5x³ +Integer: 2.4 - 34.3x + 4.2×10⁻⁰⁶x² - 5.34×10⁻⁶⁶x³ diff --git a/include/boost/math/tools/formatting.hpp b/include/boost/math/tools/formatting.hpp index b9ee2521b3..8b0cc1c606 100644 --- a/include/boost/math/tools/formatting.hpp +++ b/include/boost/math/tools/formatting.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef _MSC_VER #pragma once @@ -54,7 +55,7 @@ namespace boost { template using imag_t = decltype(std::declval().imag(std::declval().real())); template - using polynomial_t = decltype(std::declval()(std::declval()[0] + std::declval().order())); + using polynomial_t = decltype(std::declval()(std::declval()[0] + std::declval().degree())); template struct is_complex_like @@ -62,10 +63,16 @@ namespace boost { static const bool value = boost::is_detected_v && boost::is_detected_v; }; + template + struct is_polynomial_like + { + static const bool value = boost::is_detected_v; + }; + template struct is_unhandled_type { - static const bool value = !is_complex_like::value && !is_float_like::value && !is_integer_like::value; + static const bool value = !is_complex_like::value && !is_float_like::value && !is_integer_like::value && !is_polynomial_like::value; }; @@ -104,11 +111,117 @@ namespace boost { show_zero_components = 1 }; - struct latex_as_text_t {}; - struct latex_as_equation_t {}; + enum latex_t + { + latex_as_text = 1, + latex_as_equation = 0 + }; + + enum unicode_text_t + { + unicode_text_output = 0, + ascii_text_output = 1 + }; - constexpr const latex_as_text_t* latex_as_text = nullptr; - constexpr const latex_as_equation_t* latex_as_equation = nullptr; + template + typename boost::enable_if_c::type write_unicode_char(std::basic_ostream & os, std::uint32_t code_point) + { + std::uint32_t u[2] = { code_point, 0 }; + boost::u32_to_u8_iterator i(u), j(&u[0] + 1); + while (i != j) + { + os.put(*i++); + } + } + template + typename boost::enable_if_c::type write_unicode_char(std::basic_ostream & os, std::uint32_t code_point) + { + std::uint32_t u[2] = { code_point, 0 }; + boost::u32_to_u16_iterator i(u), j(&u[0] + 1); + while (i != j) + { + os.put(*i++); + } + } + template + typename boost::enable_if_c::type write_unicode_char(std::basic_ostream & os, std::uint32_t code_point) + { + os.put(code_point); + } + + template + void write_unicode_superscript(std::basic_ostream & os, std::basic_string digits) + { + for (std::size_t i = 0; i < digits.size(); ++i) + { + switch (digits[i]) + { + case '0': + write_unicode_char(os, 0x2070); + break; + case '1': + write_unicode_char(os, 0xB9); + break; + case '2': + write_unicode_char(os, 0xB2); + break; + case '3': + write_unicode_char(os, 0xB3); + break; + case '4': + write_unicode_char(os, 0x2074); + break; + case '5': + write_unicode_char(os, 0x2075); + break; + case '6': + write_unicode_char(os, 0x2076); + break; + case '7': + write_unicode_char(os, 0x2076); + break; + case '8': + write_unicode_char(os, 0x2078); + break; + case '9': + write_unicode_char(os, 0x2079); + break; + case '+': + write_unicode_char(os, 0x207A); + break; + case '-': + write_unicode_char(os, 0x207B); + break; + } + } + } + + template + typename boost::enable_if_c::type unicode_character_len(const charT* p) + { + std::size_t l = 0; + for (unsigned i = 0; p && p[i]; ++i) + ++l; + boost::u8_to_u32_iterator i(p), j(p + l); + return std::distance(i, j); + } + template + typename boost::enable_if_c::type unicode_character_len(const charT* p) + { + std::size_t l = 0; + for (unsigned i = 0; p && p[i]; ++i) + ++l; + boost::u16_to_u32_iterator i(p), j(p + l); + return std::distance(i, j); + } + template + typename boost::enable_if_c::type unicode_character_len(const charT* p) + { + std::size_t l = 0; + for (unsigned i = 0; p && p[i]; ++i) + ++l; + return l; + } template class basic_numeric_formatter; @@ -121,11 +234,12 @@ namespace boost { multiplyer_t multiply_style; bool requires_parenthesis; bool m_show_zero_components; + bool m_use_unicode; protected: template static void decompose_float(std::basic_ostream& os, const Float& f, std::basic_string& mantissa, std::basic_string& exponent) { - static const charT exponent_string[2] = { 'e', 'E' }; + static const charT exponent_string[3] = { 'e', 'E', 0 }; std::basic_stringstream ss; ss.copyfmt(os); ss.imbue(os.getloc()); @@ -141,7 +255,7 @@ namespace boost { mantissa = s; } public: - basic_numeric_formatter_base() : styling_level(full_styling), requires_parenthesis(false), multiply_style(multiply_times), m_show_zero_components(false) {} + basic_numeric_formatter_base() : styling_level(full_styling), requires_parenthesis(false), multiply_style(multiply_times), m_show_zero_components(false), m_use_unicode(true) {} void styling(styling_level_t i) { @@ -183,53 +297,169 @@ namespace boost { { return m_show_zero_components; } + void use_unicode(bool b) + { + m_use_unicode = b; + } + bool use_unicode()const + { + return m_use_unicode; + } template static std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) { return os << i; } template - static std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) + std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) { + if ((std::isinf)(f)) + { + if (f < 0) + os << "-"; + if (use_unicode()) + { + write_unicode_char(os, 0x221E); + } + else + os << "INFINITY"; + return os; + } + if ((std::isnan)(f)) + { + return os << "NaN"; + } std::basic_string mantissa, exponent; decompose_float(os, f, mantissa, exponent); + os << mantissa; if (exponent.size()) - return os << mantissa << "x10^" << exponent; - return os << mantissa; + { + if (use_unicode()) + { + if (multiply() == multiply_times) + write_unicode_char(os, 0xD7); + else if (multiply() == multiply_dot) + write_unicode_char(os, 0x22C5); + else + os << "x"; + os << "10"; + write_unicode_superscript(os, exponent); + } + else + os << "x10^" << exponent; + } + return os; } template std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) { - if (!this->show_zero_components()) + if ((std::isnan)(f.real()) || (std::isnan)(f.imag())) + { + return os << "NaN"; + } + if ((std::isinf)(f.real()) || (std::isinf)(f.imag())) { - if (f.imag() == 0) + if (use_unicode()) { - format_float(os, f.real()); - return os; + write_unicode_char(os, 0x221E); + write_unicode_char(os, 0x0303); } - else if (f.real() == 0) + else + os << "COMPLEX INFINITY"; + return os; + } + if ((!this->show_zero_components()) && (f.imag() == 0)) + { + format_float(os, f.real()); + return os; + } + else if ((!this->show_zero_components()) && (f.real() == 0)) + { + format_float(os, f.imag()); + } + else + { + format_float(os, f.real()); + typename Complex::value_type i(f.imag()); + bool isneg = i < 0; + if (isneg) { - format_float(os, f.imag()); - os.put(os.widen('i')); - return os; + i = -i; + os << " - "; } + else + os << " + "; + format_float(os, i); } - format_float(os, f.real()); - typename Complex::value_type i(f.imag()); - bool isneg = i < 0; - if (isneg) + if ((use_unicode()) && (this->imaginary_style() == doublestruck_i)) + write_unicode_char(os, 0x2148); + else if ((use_unicode()) && (this->imaginary_style() == slanted_i)) + write_unicode_char(os, 0x1D456); + else + os.put(os.widen('i')); + return os; + } + template + std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f) + { + bool have_first = false; + for (unsigned i = 0; i <= f.degree(); ++i) { - i = -i; - os << " - "; + auto coef = f[i]; + if (show_zero_components() || (coef != 0)) + { + if (have_first) + { + if (coef > 0) + os << " + "; + else + os << " - "; + } + print(*this, os, coef > 0 ? coef : -coef); + have_first = true; + if (i) + { + os << "x"; + if (i > 1) + { + if (use_unicode()) + { + std::basic_stringstream ss; + ss << i; + std::basic_string s = ss.str(); + write_unicode_superscript(os, s); + } + else + os << "^" << i; + } + } + } } - else - os << " + "; - format_float(os, i); - os.put(os.widen('i')); return os; } bool latex_as_equation()const { return false; } void latex_as_equation(bool) {} + + template + typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + { + printer.format_integer(os, value); + } + template + typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + { + printer.format_float(os, value); + } + template + typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + { + printer.format_complex(os, value); + } + template + typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + { + printer.format_polynomial(os, value); + } }; template @@ -369,6 +599,8 @@ namespace boost { return os; } + template + std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f); }; template @@ -522,6 +754,8 @@ namespace boost { return os; } + template + std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f); }; template @@ -656,6 +890,8 @@ namespace boost { return os; } + template + std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f); }; @@ -681,12 +917,15 @@ namespace boost { using base_type::format_integer; using base_type::format_float; using base_type::format_complex; + using base_type::format_polynomial; using base_type::styling; using base_type::imaginary_style; using base_type::latex_as_equation; using base_type::parenthesis; using base_type::multiply; using base_type::show_zero_components; + using base_type::use_unicode; + using base_type::print; std::basic_ostream& stream() { @@ -704,6 +943,32 @@ namespace boost { typedef basic_numeric_printer wlatex_printer; typedef basic_numeric_printer whtml_printer; + template + typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Integer& i) + { + std::size_t w = (std::size_t)os.stream().width(); + if (w) + { + std::basic_stringstream ss; + ss.copyfmt(os.stream()); + ss.imbue(os.stream().getloc()); + ss.width(0); + basic_numeric_printer fmt(ss, os); + fmt.print(fmt, fmt.stream(), i); + std::basic_string s = ss.str(); + std::size_t len = unicode_character_len(s.c_str()); + if (len < w) + { + auto pos = os.stream().flags() & std::ios_base::adjustfield; + s.insert(pos == std::ios_base::left ? s.end() : s.begin(), w - len, os.stream().fill()); + } + os.stream() << s; + } + else + os.print(os, os.stream(), i); + return os; + } +#if 0 template typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Integer& i) { @@ -758,6 +1023,24 @@ namespace boost { return os; } + template + typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Polynomial& f) + { + if (os.stream().width()) + { + std::basic_stringstream ss; + ss.copyfmt(os.stream()); + ss.imbue(os.stream().getloc()); + ss.width(0); + basic_numeric_printer fmt(ss, os); + fmt.format_polynomial(fmt.stream(), f); + os.stream() << ss.str(); + } + else + os.format_polynomial(os.stream(), f); + return os; + } +#endif template basic_numeric_printer& operator << (basic_numeric_printer& os, styling_level_t s) { @@ -773,30 +1056,30 @@ namespace boost { } template - basic_numeric_printer& operator << (basic_numeric_printer& os, const latex_as_equation_t* const&) + basic_numeric_printer& operator << (basic_numeric_printer& os, latex_t l) { - os.latex_as_equation(true); + os.latex_as_equation(l == latex_as_equation); return os; } template - basic_numeric_printer& operator << (basic_numeric_printer& os, const latex_as_text_t* const&) + basic_numeric_printer& operator << (basic_numeric_printer& os, const imaginary_i_t i) { - os.latex_as_equation(false); + os.imaginary_style(i); return os; } template - basic_numeric_printer& operator << (basic_numeric_printer& os, const imaginary_i_t i) + basic_numeric_printer& operator << (basic_numeric_printer& os, const zero_component_t z) { - os.imaginary_style(i); + os.show_zero_components(z == ::boost::math::tools::show_zero_components ? true : false); return os; } template - basic_numeric_printer& operator << (basic_numeric_printer& os, const zero_component_t z) + basic_numeric_printer& operator << (basic_numeric_printer& os, const unicode_text_t z) { - os.show_zero_components(z == ::boost::math::tools::show_zero_components ? true : false); + os.use_unicode(z == ::boost::math::tools::unicode_text_output ? true : false); return os; } From 24e0fbe9e7a68362f97f2dedfb15963cced40cfe Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Wed, 13 Nov 2019 18:31:14 +0000 Subject: [PATCH 06/14] Formatting: Complete polynomial support and update docs. --- doc/fp_utilities/formatting.qbk | 26 ++- doc/images/console1.png | Bin 0 -> 1597 bytes doc/images/console2.png | Bin 0 -> 1584 bytes doc/images/polynomial_latex_1.svg | 171 ++++++++++++++++ doc/images/polynomial_latex_2.svg | 186 +++++++++++++++++ example/formatter_docbook_output.cpp | 23 ++- example/formatter_docbook_output.qbk | 24 ++- example/formatter_docbook_output_test.qbk | 2 + example/formatter_html_output.cpp | 16 +- example/formatter_html_output.html | 12 +- example/formatter_latex_output.cpp | 11 + example/formatter_latex_output.pdf | Bin 75796 -> 78639 bytes example/formatter_latex_output.tex | 17 +- example/formatter_snips.cpp | 82 ++++++++ example/formatter_text_output.cpp | 4 +- example/formatter_text_output.txt | 3 +- include/boost/math/tools/formatting.hpp | 233 ++++++++++++++++++++-- 17 files changed, 771 insertions(+), 39 deletions(-) create mode 100644 doc/images/console1.png create mode 100644 doc/images/console2.png create mode 100644 doc/images/polynomial_latex_1.svg create mode 100644 doc/images/polynomial_latex_2.svg create mode 100644 example/formatter_snips.cpp diff --git a/doc/fp_utilities/formatting.qbk b/doc/fp_utilities/formatting.qbk index b1673e228c..8e0bb9c4ba 100644 --- a/doc/fp_utilities/formatting.qbk +++ b/doc/fp_utilities/formatting.qbk @@ -31,13 +31,11 @@ Class `basic_numeric_printer` provides "pretty printing" capabilities for numeri library, but fullfills a very different role: there is no "input streaming" and the output is intended to be human readable and formatted for printing and not for consumption by machine. -Supported input number types are integers, floating point number, complex numbers, rationals(TODO), polynomials(TODO), intervals(TODO) +Supported input number types are integers, floating point number, complex numbers, rationals(TODO), polynomials, intervals(TODO) and containers thereof(TODO). Other types, plus all the standard library manipulators can also be streamed to `basic_numeric_printer` but are simply formwarded to the underlying stream and ignored by the pretty printer. -Output formats include plain text (not so useful), Docbook XML markup, HTML markup, and Latex. MathML may come later(TODO). - -[h4 Examples:] +Output formats include plain text (including formatted output to console), Docbook XML markup, HTML markup, and Latex. MathML may come later(TODO). Integer values have the same representation as the underlying stream would give them, but can have styling applied to their XML wrapper in HTML and Docbook output modes. @@ -49,6 +47,24 @@ Complex numbers inherit all the properties of floats, and in addition allow the '''1.23 - 1.23×10-24i''' or '''1.23 - 1.2345678765⋅10-24'''. +Polynomials can be polynomials of integers, floats, or complex numbers and inherit all the properties of each of those: +'''(2.4 + 3.25i) - 34.25x + 4.2×10-06ix2 - (5.34×10-67 - 4.65×10-20i)x3'''. + +[import ../../example/formatter_snips.cpp] + +[formatting_eg1] + +[formatting_eg2] + +[formatting_eg3] + +Here's the revised result: + +[$../images/polynomial_latex_2.svg] + + +[h4 Examples:] + [heading Class `basic_numeric_printer`] enum output_format_t @@ -157,6 +173,8 @@ The following section (using Docbook output format) illustrates how each of the [complex_formatting_examples] +[polynomial_formatting_examples] + [heading Output Format Gallery] The above tables of sample output are available in various other formats diff --git a/doc/images/console1.png b/doc/images/console1.png new file mode 100644 index 0000000000000000000000000000000000000000..10f1dc53a72898ce9d62d901d21e3b5010036383 GIT binary patch literal 1597 zcmV-D2EzG?P)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00pHQk=rQDPtEP|AHw}K;-Y3TVNguF^p%h zO_Es>Qh01;X05&Fv1f)z7Z(=<0s;a80s;a|_#-F~5D*X$5D){YfPjF2fI$5NsU(~5 zJQK1c31&_RNs?iD5uz`+UA^bQlNO*|C!utu%if3_OLZc_#X#r@$Bkoq&|#?@g4_1U4Pd2<&7U& zdscl23D8%AtybM0!kR{aNFeH-2dBdGz`G-K`oXX=S#riuhUe z0jt?S%&6^8$Dj3VsH?kS zi$u1T`?>k}1+mhtCB>Jgza+`%bpfaIfbrtI_0??lw9|EO2pvBDO;2Ah9{nZRg!d_( zrr8mq$c9!hMaRld5q){%C(k}lA6kB7(?ggIV5|cUkAJbYuWbLL<&Ok?sH%#N;iF|e zi*1O!f%N*ejYd4BNGn~T*#MS{Ib(6W_Vo-XJV-|OP*oL%pHc)_J%do5{)8kL-S-Qm z8_!$cJk5tX;6IL0My+}DC&>s8h7agNn6i}lXA0k_D+PR5U*7on%Knf(H2kdWSE*0M z3K2#1?MD}FU)lag%b%AG#G$JL{v&m^={Mo8B+0mKH;~TWj(+&=$%2ylFeYq;<=HoM z7m=PlDY!>)s@2STA0zdR!kyHq>i_nhqS3B)52A?vW%);)`rrMh1HOS#LLgnSeMg!4 zlVpTPzYTZzgeH_tUF$%ezP#~s$o`l<$M_-12>0r!)=rkEeP#RKci`_^45aHnj;$hO z(}Sg?@C~+3BF{b<-D6iL&5qzcgW4H3oAjGk_7B9+q_2YA)Q0U)?^W^E|3i9ju>{)1 zXSuxXr_rBP-!l2z-Md*WIkp`N&0dbA*|1tZi~)PwtI&TweuUq4p~Ht!V714~53RmD z@pH(YRv%V=s^X8geP#P!S^WLVfpnF(PjZ9E)d9c2Are;m$ux##HF1?&oBbKpDQ8TR zX$-4-30abuCJaUEzl-b37Ofj3NMgbZM+)&?n zb@iO}P(>N~U-yR4mku0QaGw^`gA^}6%<;<`Kbid%r9Q0uRK*`}`^xseviSQ=_(Mp7 z*I9~2^D1{%b}Wdtca8QBLD1OmrWt!(1)$omEho*{>ngx~u0N0}PxSgjNU}ap9q``94vKLEbHr-&FrLC>VnY!ck3@8AaH_#l#l=e!$+&z6Th`uWhW}h=yh8) v-VGG`3;hKI1i0c@{6A6w0RaI40e1Wk>^S+EryTbR00000NkvXXu0mjft(Oxv literal 0 HcmV?d00001 diff --git a/doc/images/console2.png b/doc/images/console2.png new file mode 100644 index 0000000000000000000000000000000000000000..77b22cb95916a2c877eb969fd38c5912e2b2a517 GIT binary patch literal 1584 zcmV-02G9A4P)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00o#yL_t(|+U;Gva;jVuUEH~4 z-hj~*7V-*=DKKT|DNJU*7U?N;#ws&X&@1hoi5B!&MhYEX0gGFN49~$=phiMaIENo| zbJmPSG~09bUTdG9eGUnCb8{1cfq{X6f#DnA7YhLc0|NsCo3t1h7#J8Bs-CnsLBnF9 zLEtzRof5%u0n?_|3TEl z7YhpS!Ti)mT75Ll|LKJNVkzK58|u?M6@!1Bf71M=&7ZvXr2HiGi?l7Re(M^4n$jn2 zd};NOYU}hOhHSVPyr;iVHeB3o`WQLUK5#v3)hEpSC^sqR>;-Ls37%^oq|PNM%(%aK zMNvlo1#JPVwS+Ws8u2}ge@-^A*4wb{0E&Xtq(!TbYvBKobgjUH*nTfODEtfjljbjN z{^YTz;pdd2Wy?xwoL0Yej6c_)Pty37sgFc?kP|cvL=VgPF7->!Ncw1C_lj-Eqj%rU z-3YCHorod_8xLsTc1tDHb=~-7$eF*ln=j#sgGS0kS^RUHfZ+fz`%jp^T{pk7;bNf) zM7G`G1PW~Y*;JFzr2KP&hJ~B-OEz4%t^v7V8HY4JMfj!7pDg<+b)+61!rrOlsn_LulM7oR9{Fp`qx z*2oz_a;h2Msu+Ko)@K>O#;K(8Un8N|umW_WtY<(k$tq^@7}iJswaV z-$`E<$WsDwPPe~?nfq*kw*QF|(OUrYim%RS{m;WcDZf?5FFtQOIpq~UlGDpkH|Jyn zdfSFNCilG#{&(|d*uDKhcY#$ZZG7_bOOrq6?DO(N8XsEymK9%G|Ety~ZG36^ua9zjw70!i}pWJrn_we!KP5V zW%+-O|1$Y4Gk&M5x~z*NXLk;FBEXBV*2>Ad@y{ONVR04wcZU-wfe8~jtm#|sgr|)U zasJZg&)&Y1lONLf(CW9W_|p1cwLWR%Tc$pK#alcduiFrOtCP8!xI`$&$uQin4~X;F zpO&hI0};q|zE(}CY&Z~s;4O76Hp{;<``>Bt2;cwK>hmT3^Q<;$^wA{$&PakJeZ2an z7ne`^pKot9$A28(0Oojw)!2k5>M-Q`F6$8GFKzzp?ep+M8=oTn)jj@PgFegDUzR?I zc3QjFEkf%KGPg=UgR|fMD%KiG!4lLZ`xUzT=YKwILR*DY)Ai?%=a0zRzhs~OT!-uG zfS2ALbDWGjF~s&y{fEF={C}=T1Ax9>LqGEOK&cO*QU0se4=w+P@{?!w_px*^Ht(?3 z*DyeSZ8*Pw)#fjC{!zw^zkj + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/images/polynomial_latex_2.svg b/doc/images/polynomial_latex_2.svg new file mode 100644 index 0000000000..fa55dff20c --- /dev/null +++ b/doc/images/polynomial_latex_2.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/formatter_docbook_output.cpp b/example/formatter_docbook_output.cpp index 62a58e83a5..44404b9781 100644 --- a/example/formatter_docbook_output.cpp +++ b/example/formatter_docbook_output.cpp @@ -6,6 +6,7 @@ #include #include #include +#include void print(std::ostream& os) { @@ -92,27 +93,27 @@ void print(std::ostream& os) cval = std::complex(0, 0); printer.stream() << "[[" << cval << "][default][default]['''"; printer << cval << "''']]\n"; - printer.stream() << "[[" << cval << "][default][default]['''"; + printer.stream() << "[[" << cval << "][default][show_zero_components]['''"; printer << boost::math::tools::show_zero_components << cval << "''']]\n" << boost::math::tools::hide_zero_components; cval = std::complex(2.5, 0); printer.stream() << "[[" << cval << "][default][default]['''"; printer << cval << "''']]\n"; - printer.stream() << "[[" << cval << "][default][default]['''"; + printer.stream() << "[[" << cval << "][default][show_zero_components]['''"; printer << boost::math::tools::show_zero_components << cval << "''']]\n" << boost::math::tools::hide_zero_components; cval = std::complex(-2.5, 0); printer.stream() << "[[" << cval << "][default][default]['''"; printer << cval << "''']]\n"; - printer.stream() << "[[" << cval << "][default][default]['''"; + printer.stream() << "[[" << cval << "][default][show_zero_components]['''"; printer << boost::math::tools::show_zero_components << cval << "''']]\n" << boost::math::tools::hide_zero_components; cval = std::complex(0, 3.25); printer.stream() << "[[" << cval << "][default][default]['''"; printer << cval << "''']]\n"; - printer.stream() << "[[" << cval << "][default][default]['''"; + printer.stream() << "[[" << cval << "][default][show_zero_components]['''"; printer << boost::math::tools::show_zero_components << cval << "''']]\n" << boost::math::tools::hide_zero_components; cval = std::complex(0, -3.25); printer.stream() << "[[" << cval << "][default][default]['''"; printer << cval << "''']]\n"; - printer.stream() << "[[" << cval << "][default][default]['''"; + printer.stream() << "[[" << cval << "][default][show_zero_components]['''"; printer << boost::math::tools::show_zero_components << cval << "''']]\n" << boost::math::tools::hide_zero_components; // Infinites and NaN's: cval = std::complex(std::numeric_limits::infinity(), 2.5); @@ -141,6 +142,18 @@ void print(std::ostream& os) printer << cval << "''']]\n"; printer << "\n]\n]\n"; + + + printer << "[template polynomial_formatting_examples[]\n"; + printer << "[table:poly_fmt_examples Polynomial Values\n[[Kind][Result]]"; + boost::math::tools::polynomial poly1 = { 2, -3, 4, 5 }; + printer << "[[Integer]['''" << poly1 << "''']]" << std::endl; + boost::math::tools::polynomial poly2 = { 2.4, -34.25, 4.2e-6, -5.34e-67 }; + printer << "[[Float]['''" << poly2 << "''']]" << std::endl; + boost::math::tools::polynomial > poly3 = { { 2.4, 3.25 }, {-34.25 }, { 0, 4.2e-6 }, { -5.34e-67, 4.65e-20 } }; + printer << "[[Complex]['''" << poly3 << "''']]" << std::endl; + + printer << "\n]\n]\n"; } diff --git a/example/formatter_docbook_output.qbk b/example/formatter_docbook_output.qbk index 07017d7243..ec6d91eedf 100644 --- a/example/formatter_docbook_output.qbk +++ b/example/formatter_docbook_output.qbk @@ -25,10 +25,10 @@ ] [template complex_formatting_examples[] [table:complex_fmt_examples Basic Complex Values -[[Value][Precision][Format][Result]][[(3.25,4.67)][default][default]['''3.25 + 4.67''']] +[[Value][Precision][Format][Result]][[(3.25,4.67)][default][default]['''3.25 + 4.67i''']] [[(3.14,0)][default][default]['''3.14''']] -[[(1.23,-1.23e-24)][default][default]['''1.23 - 1.23×10-24''']] -[[(1.23,-1.23e-24)][3][scientific]['''1.230×10+00 - 1.235×10-24''']] +[[(1.23,-1.23e-24)][default][default]['''1.23 - 1.23×10-24i''']] +[[(1.23,-1.23e-24)][3][scientific]['''1.230×10+00 - 1.235×10-24i''']] [[(1.230e+00,-1.235e-24)][12][default + slanted_i]['''1.23 - 1.2345678765×10-24i''']] [[(1.23,-1.2345678765e-24)][12][default + doublestruck_i]['''1.23 - 1.2345678765×10-24''']] [[(1.23,-1.2345678765e-24)][12][default + doublestruck_i + multiply_x]['''1.23 - 1.2345678765x10-24''']] @@ -39,15 +39,15 @@ [template complex_formatting_examples_2[] [table:complex_fmt_examples Complex Special Values [[Value][Precision][Format][Result]][[(0,0)][default][default]['''0''']] -[[(0,0)][default][default]['''0 + 0i''']] +[[(0,0)][default][show_zero_components]['''0 + 0i''']] [[(2.5,0)][default][default]['''2.5''']] -[[(2.5,0)][default][default]['''2.5 + 0i''']] +[[(2.5,0)][default][show_zero_components]['''2.5 + 0i''']] [[(-2.5,0)][default][default]['''-2.5''']] -[[(-2.5,0)][default][default]['''-2.5 + 0i''']] +[[(-2.5,0)][default][show_zero_components]['''-2.5 + 0i''']] [[(0,3.25)][default][default]['''3.25i''']] -[[(0,3.25)][default][default]['''0 + 3.25i''']] +[[(0,3.25)][default][show_zero_components]['''0 + 3.25i''']] [[(0,-3.25)][default][default]['''-3.25i''']] -[[(0,-3.25)][default][default]['''0 - 3.25i''']] +[[(0,-3.25)][default][show_zero_components]['''0 - 3.25i''']] [[(inf,2.5)][default][default]['''∞̃''']] [[(-inf,2.5)][default][default]['''∞̃''']] [[(2.5,inf)][default][default]['''∞̃''']] @@ -59,3 +59,11 @@ ] ] +[template polynomial_formatting_examples[] +[table:poly_fmt_examples Polynomial Values +[[Kind][Result]][[Integer]['''2 - 3x + 4x2 + 5x3''']] +[[Float]['''2.4 - 34.25x + 4.2×10-06x2 - 5.34×10-67x3''']] +[[Complex]['''(2.4 + 3.25i) - 34.25x + 4.2×10-06ix2 - (5.34×10-67 - 4.65×10-20i)x3''']] + +] +] diff --git a/example/formatter_docbook_output_test.qbk b/example/formatter_docbook_output_test.qbk index baf2da0e1d..38d0234267 100644 --- a/example/formatter_docbook_output_test.qbk +++ b/example/formatter_docbook_output_test.qbk @@ -20,3 +20,5 @@ [complex_formatting_examples] [complex_formatting_examples_2] + +[polynomial_formatting_examples] diff --git a/example/formatter_html_output.cpp b/example/formatter_html_output.cpp index d39523e23a..904349a5b7 100644 --- a/example/formatter_html_output.cpp +++ b/example/formatter_html_output.cpp @@ -6,13 +6,16 @@ #include #include #include +#include void print(std::ostream& os) { boost::math::tools::html_printer printer(os); printer << "\n\n" << std::endl; - printer << "" << std::endl; + printer << "" << std::endl; + + printer << "

Synopsis

\nSample HTML output for various number types, these are then styled in bold, and various (somewhat untasteful!) colors just because we can, and to check that our markup generation is working correctly.\n"; printer << "

Basic Integers Values:

\n" << boost::math::tools::full_styling; @@ -164,6 +167,17 @@ void print(std::ostream& os) printer << "\n\n\n"; + printer << "

Polynomials:

\n\n"; + + printer << ""; + boost::math::tools::polynomial poly1 = { 2, -3, 4, 5 }; + printer << "\n"; + boost::math::tools::polynomial poly2 = { 2.4, -34.25, 4.2e-6, -5.34e-67 }; + printer << "\n"; + boost::math::tools::polynomial > poly3 = { { 2.4, 3.25 }, {-34.25 }, { 0, 4.2e-6 }, { -5.34e-67, 4.65e-20 } }; + printer << "\n"; + printer << "\n
TypeResult
Integer" << poly1 << "
Float" << poly2 << "
Complex" << poly3 << "
\n\n"; + printer << "\n\n"; } diff --git a/example/formatter_html_output.html b/example/formatter_html_output.html index 4101bac4fb..1eb3313d1d 100644 --- a/example/formatter_html_output.html +++ b/example/formatter_html_output.html @@ -1,7 +1,9 @@ - + +

Synopsis

+Sample HTML output for various number types, these are then styled in bold, and various (somewhat untasteful!) colors just because we can, and to check that our markup generation is working correctly.

Basic Integers Values:

@@ -66,5 +68,13 @@

Complex Special Values:

ValueBaseResult
0default0
-23default-23
+

Polynomials:

+ + + + + +
TypeResult
Integer2 - 3x + 4x2 + 5x3
Float2.4 - 34.25x + 4.2×10-06x2 - 5.34×10-67x3
Complex(2.4 + 3.25i) - 34.25x + 4.2×10-06ix2 - (5.34×10-67 - 4.65×10-20i)x3
+ diff --git a/example/formatter_latex_output.cpp b/example/formatter_latex_output.cpp index 07f79bbe5e..c2e865018b 100644 --- a/example/formatter_latex_output.cpp +++ b/example/formatter_latex_output.cpp @@ -6,6 +6,7 @@ #include #include #include +#include void print(std::ostream& os) { @@ -161,6 +162,16 @@ void print(std::ostream& os) printer << cval << " \\\\\n"; printer << "\\end{tabular}\n\n" << std::defaultfloat << boost::math::tools::multiply_times; + printer << "\\textbf{Polynomial Values}\n\n\\begin{tabular}{r r}\nType & Result \\\\\n"; + boost::math::tools::polynomial poly1 = { 2, -3, 4, 5 }; + printer << "Integer & " << poly1 << " \\\\\n"; + boost::math::tools::polynomial poly2 = { 2.4, -34.25, 4.2e-6, -5.34e-67 }; + printer << "Float & " << poly2 << " \\\\\n"; + boost::math::tools::polynomial > poly3 = { { 2.4, 3.25 }, {-34.25 }, { 0, 4.2e-6 }, { -5.34e-67, 4.65e-20 } }; + printer << "Complex & " << poly3 << " \\\\\n"; + printer << "Complex (latex\\_as\\_text) & " << boost::math::tools::latex_as_text << poly3 << " \\\\\n" << boost::math::tools::latex_as_equation; + printer << "Complex (multiply\\_dot) & " << boost::math::tools::multiply_dot << poly3 << " \\\\\n" << boost::math::tools::multiply_times; + printer << "\\end{tabular}\n\n" << std::defaultfloat << boost::math::tools::multiply_times; printer << "\\end{document}\n\n"; } diff --git a/example/formatter_latex_output.pdf b/example/formatter_latex_output.pdf index 5ad48a589c57f974a0fb505d2415660dcc0e2736..7c46a92e54484e186635020785978a6e74e8d44f 100644 GIT binary patch delta 59429 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%0D<%3Zu=Qn()`ga@; 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 diff --git a/example/formatter_latex_output.tex b/example/formatter_latex_output.tex index 2bc3d640bc..645012f185 100644 --- a/example/formatter_latex_output.tex +++ b/example/formatter_latex_output.tex @@ -43,10 +43,10 @@ \begin{tabular}{r r r r} Value & Precision & Format & Result \\ -(3.25,4.67) & default & default & $3.25 + 4.67i$ \\ +(3.25,4.67) & default & default & $3.25 + 4.67\mathrm{i}$ \\ (3.14,0) & default & default & $3.14$ \\ -(1.23,-1.234567876e-24) & default & default & $1.23 - 1.234567876\times 10^{-24}i$ \\ -(1.23,-1.234567876e-24) & 3 & scientific & $1.230\times 10^{+00} - 1.235\times 10^{-24}i$ \\ +(1.23,-1.234567876e-24) & default & default & $1.23 - 1.234567876\times 10^{-24}\mathrm{i}$ \\ +(1.23,-1.234567876e-24) & 3 & scientific & $1.230\times 10^{+00} - 1.235\times 10^{-24}\mathrm{i}$ \\ (1.230e+00,-1.235e-24) & 12 & default + slanted\_i & $1.23 - 1.2345678765\times 10^{-24}i$ \\ (1.23,-1.2345678765e-24) & 12 & default + upright\_i & $1.23 - 1.2345678765\times 10^{-24}\mathrm{i}$ \\ (1.23,-1.2345678765e-24) & 12 & default + slanted\_i + latex\_as\_text & 1.23 - 1.2345678765$\times$10\textsuperscript{-24}\textit{i} \\ @@ -75,5 +75,16 @@ (inf,nan) & default & default & NaN \\ \end{tabular} +\textbf{Polynomial Values} + +\begin{tabular}{r r} +Type & Result \\ +Integer & 2 - 3\textit{x} + 4\textit{x}\textsuperscript{2} + 5\textit{x}\textsuperscript{3} \\ +Float & 2.4 - 34.25\textit{x} + 4.2$\times$10\textsuperscript{-06}\textit{x}\textsuperscript{2} - 5.34$\times$10\textsuperscript{-67}\textit{x}\textsuperscript{3} \\ +Complex & (2.4 + 3.25i) - 34.25\textit{x} + 4.2$\times$10\textsuperscript{-06}i\textit{x}\textsuperscript{2} - (5.34$\times$10\textsuperscript{-67} - 4.65$\times$10\textsuperscript{-20}i)\textit{x}\textsuperscript{3} \\ +Complex (latex\_as\_text) & (2.4 + 3.25i) - 34.25\textit{x} + 4.2$\times$10\textsuperscript{-06}i\textit{x}\textsuperscript{2} - (5.34$\times$10\textsuperscript{-67} - 4.65$\times$10\textsuperscript{-20}i)\textit{x}\textsuperscript{3} \\ +Complex (multiply\_dot) & $(2.4 + 3.25\mathrm{i}) - 34.25x + 4.2\cdot 10^{-06}\mathrm{i}x^{2} - (5.34\cdot 10^{-67} - 4.65\cdot 10^{-20}\mathrm{i})x^{3}$ \\ +\end{tabular} + \end{document} diff --git a/example/formatter_snips.cpp b/example/formatter_snips.cpp new file mode 100644 index 0000000000..0e37a8a717 --- /dev/null +++ b/example/formatter_snips.cpp @@ -0,0 +1,82 @@ +// (C) Copyright John Maddock 2019. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + + +int main(int argc, const char* argv[]) +{ + + //[formatting_eg1 + //` By way of example, lets suppose we have a polynomial with complex coefficients: + boost::math::tools::polynomial > poly; + //` And then do something to populate those coefficients... + //<- + poly = boost::math::tools::polynomial >{ {2.25, -3.5 }, {-12.5, -4.5 }, { 23.34 }, { 0, 34.5 } }; + //-> + //` For debugging purposes, we may wish to output the polynomial to std::cout. + //` Using `text_printer` we can do just that: + + boost::math::tools::text_printer printer(std::cout); + printer << poly << std::endl; + + //` By default, `text_printer` outputs using Unicode, so even console output is properly formatted: + //` + //` [$../images/console1.png] + //` + //` Note however, that not all platforms support Unicode console output, so if required + //` we can use the `ascii_text_output` format manipulator to switch to plain ASCII output: + + printer << boost::math::tools::ascii_text_output << poly << std::endl; + + //` The result is rather less pretty, but the best we can do under the circumstances: + //` + //` [$../images/console2.png] + //` + //] + { + //[formatting_eg2 + //` + //`Later, lets suppose we've now finalised our polynomial and wish to format it for print, + //`in that case we can simply change the printer to `latex_printer` and stream out as before: + //` + boost::math::tools::latex_printer printer(std::cout); + printer << poly << std::endl; + //` + //` After running the output through your favorite Tex processor the result is a nicely formatted + //` equation: + //` + //` [$../images/polynomial_latex_1.svg] + //` + //] + } + { + //[formatting_eg3 + //` + //` The output though may not quite be as we would like, let's suppose + //` instead we wish to write a complete LaTex document to file and change the + //` formatting so that the values are printed to 2 decimal places, and the + //` imaginary unit i is italisized as is common in many historical texts. + //` The code now looks like: + //` + std::ofstream fs("polynomial.tex"); + boost::math::tools::latex_printer printer(fs); + // Document preamble: + printer << "\\documentclass{article}" << std::endl; + printer << "\\begin{document}" << std::endl << std::endl; + // + // Stream out some manipulators to set the formatting: + // + printer << std::setprecision(2) << std::fixed << boost::math::tools::slanted_i; + printer << poly << std::endl; + printer << "\\end{document}\n\n"; + + //] + } + return 0; +} diff --git a/example/formatter_text_output.cpp b/example/formatter_text_output.cpp index 68cbb3bbdf..0a8fe7cb1c 100644 --- a/example/formatter_text_output.cpp +++ b/example/formatter_text_output.cpp @@ -143,7 +143,9 @@ void print(std::basic_ostream& os) boost::math::tools::polynomial poly1 = { 2, -3, 4, 5 }; printer << "Integer: " << poly1 << std::endl; boost::math::tools::polynomial poly2 = { 2.4, -34.25, 4.2e-6, -5.34e-67 }; - printer << "Integer: " << poly2 << std::endl; + printer << "Float: " << poly2 << std::endl; + boost::math::tools::polynomial > poly3 = { { 2.4, 3.25 }, {-34.25 }, { 0, 4.2e-6 }, { -5.34e-67, 4.65e-20 } }; + printer << "Complex: " << poly3 << std::endl; } int main(int argc, const char* argv[]) diff --git a/example/formatter_text_output.txt b/example/formatter_text_output.txt index b696c57acf..43fd026ade 100644 --- a/example/formatter_text_output.txt +++ b/example/formatter_text_output.txt @@ -57,4 +57,5 @@ Complex Zeros: Polynomials: Integer: 2 - 3x + 4x² + 5x³ -Integer: 2.4 - 34.3x + 4.2×10⁻⁰⁶x² - 5.34×10⁻⁶⁶x³ +Float: 2.4 - 34.3x + 4.2×10⁻⁰⁶x² - 5.34×10⁻⁶⁶x³ +Complex: (2.4 + 3.25i) - 34.3x + 4.2×10⁻⁰⁶ix² - (5.34×10⁻⁶⁶ - 4.65×10⁻²⁰i)x³ diff --git a/include/boost/math/tools/formatting.hpp b/include/boost/math/tools/formatting.hpp index 8b0cc1c606..81efc2f06b 100644 --- a/include/boost/math/tools/formatting.hpp +++ b/include/boost/math/tools/formatting.hpp @@ -55,7 +55,9 @@ namespace boost { template using imag_t = decltype(std::declval().imag(std::declval().real())); template - using polynomial_t = decltype(std::declval()(std::declval()[0] + std::declval().degree())); + using polynomial_t = decltype(std::declval()(std::declval()[0])); + template + using polynomial2_t = decltype(std::declval().degree()); template struct is_complex_like @@ -66,7 +68,7 @@ namespace boost { template struct is_polynomial_like { - static const bool value = boost::is_detected_v; + static const bool value = boost::is_detected_v && boost::is_detected_v; }; template @@ -223,6 +225,27 @@ namespace boost { return l; } + template + typename boost::enable_if_c::value, bool>::type iszero(const T& val) + { + return (val.real() == 0) && (val.imag() == 0); + } + template + typename boost::disable_if_c::value, bool>::type iszero(const T& val) + { + return val == 0; + } + template + typename boost::enable_if_c::value, bool>::type isneg(const T& val) + { + return (val.real() < 0); + } + template + typename boost::disable_if_c::value, bool>::type isneg(const T& val) + { + return val < 0; + } + template class basic_numeric_formatter; @@ -232,7 +255,7 @@ namespace boost { styling_level_t styling_level; imaginary_i_t i_style; multiplyer_t multiply_style; - bool requires_parenthesis; + std::size_t m_parenthesis; bool m_show_zero_components; bool m_use_unicode; protected: @@ -255,7 +278,7 @@ namespace boost { mantissa = s; } public: - basic_numeric_formatter_base() : styling_level(full_styling), requires_parenthesis(false), multiply_style(multiply_times), m_show_zero_components(false), m_use_unicode(true) {} + basic_numeric_formatter_base() : styling_level(full_styling), i_style(upright_i), multiply_style(multiply_times), m_parenthesis(0), m_show_zero_components(false), m_use_unicode(true) {} void styling(styling_level_t i) { @@ -281,13 +304,13 @@ namespace boost { { return multiply_style; } - void parenthesis(bool b) + void parenthesis(std::size_t b) { - requires_parenthesis = b; + m_parenthesis = b; } - bool parenthesis()const + std::size_t parenthesis()const { - return requires_parenthesis; + return m_parenthesis; } void show_zero_components(bool b) { @@ -305,6 +328,20 @@ namespace boost { { return m_use_unicode; } + + struct scoped_parenthesis + { + basic_numeric_formatter_base* m_formatter; + scoped_parenthesis(basic_numeric_formatter_base* formatter) : m_formatter(formatter) + { + m_formatter->parenthesis(1 + m_formatter->parenthesis()); + } + ~scoped_parenthesis() + { + m_formatter->parenthesis(m_formatter->parenthesis() - 1); + } + }; + template static std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) { @@ -379,6 +416,8 @@ namespace boost { } else { + if (parenthesis()) + os << "("; format_float(os, f.real()); typename Complex::value_type i(f.imag()); bool isneg = i < 0; @@ -397,25 +436,28 @@ namespace boost { write_unicode_char(os, 0x1D456); else os.put(os.widen('i')); + if (parenthesis() && !((!this->show_zero_components()) && (f.real() == 0))) + os << ")"; return os; } template std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f) { + scoped_parenthesis scoped(this); bool have_first = false; for (unsigned i = 0; i <= f.degree(); ++i) { auto coef = f[i]; - if (show_zero_components() || (coef != 0)) + if (show_zero_components() || (!iszero(coef))) { if (have_first) { - if (coef > 0) + if (!isneg(coef)) os << " + "; else os << " - "; } - print(*this, os, coef > 0 ? coef : -coef); + print(*this, os, !isneg(coef) ? coef : -coef); have_first = true; if (i) { @@ -470,6 +512,7 @@ namespace boost { template class basic_numeric_formatter : public basic_numeric_formatter_base { + friend class basic_numeric_formatter_base; protected: template std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) @@ -552,6 +595,7 @@ namespace boost { this->styling(no_styling); bool need_i = true; + bool need_paren = false; if (!this->show_zero_components() && (f.imag() == 0)) { @@ -564,6 +608,11 @@ namespace boost { } else { + if (this->parenthesis()) + { + os << "("; + need_paren = true; + } format_float(os, f.real()); typename Complex::value_type i(f.imag()); bool isneg = i < 0; @@ -589,6 +638,8 @@ namespace boost { if (saved_style >= minimal_styling) os << "
"; } + if (need_paren) + os << ")"; this->styling(saved_style); } @@ -600,14 +651,58 @@ namespace boost { return os; } template - std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f); + std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f) + { + typename basic_numeric_formatter_base::scoped_parenthesis scoped(this); + + if (this->styling() >= minimal_styling) + os << ""; + if (this->styling() > minimal_styling) + os << ""; + + bool have_first = false; + for (unsigned i = 0; i <= f.degree(); ++i) + { + auto coef = f[i]; + if (this->show_zero_components() || !iszero(coef)) + { + if (have_first) + { + if (!isneg(coef)) + os << " + "; + else + os << " - "; + } + basic_numeric_formatter_base::print(*this, os, !isneg(coef) ? coef : -coef); + have_first = true; + if (i) + { + os << "x"; + if (i > 1) + { + os << "" << i << ""; + } + } + } + } + + if (this->styling() > minimal_styling) + os << ""; + if (this->styling() >= minimal_styling) + os << ""; + + return os; + } }; template class basic_numeric_formatter : public basic_numeric_formatter_base { + friend class basic_numeric_formatter_base; + bool is_latex_as_equation; bool inside_equation; + public: basic_numeric_formatter() : is_latex_as_equation(true), inside_equation(false) {} void latex_as_equation(bool b) { is_latex_as_equation = b; } @@ -711,6 +806,7 @@ namespace boost { else { bool need_i = true; + bool need_paren = false; if (!this->show_zero_components() && (f.imag() == 0)) { @@ -723,7 +819,11 @@ namespace boost { } else { - + if (this->parenthesis()) + { + os << "("; + need_paren = true; + } format_float(os, f.real()); typename Complex::value_type i(f.imag()); bool isneg = i < 0; @@ -747,6 +847,8 @@ namespace boost { else os.put(os.widen('i')); } + if (need_paren) + os << ")"; } inside_equation = saved_inside_equation; if (!inside_equation && latex_as_equation()) @@ -755,12 +857,59 @@ namespace boost { return os; } template - std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f); + std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f) + { + typename basic_numeric_formatter_base::scoped_parenthesis scoped(this); + + if (!inside_equation && latex_as_equation()) + os.put(os.widen('$')); + bool saved_inside_equation = inside_equation; + inside_equation = latex_as_equation(); + bool have_first = false; + + for (unsigned i = 0; i <= f.degree(); ++i) + { + auto coef = f[i]; + if (this->show_zero_components() || !iszero(coef)) + { + if (have_first) + { + if (!isneg(coef)) + os << " + "; + else + os << " - "; + } + basic_numeric_formatter_base::print(*this, os, !isneg(coef) ? coef : -coef); + have_first = true; + if (i) + { + if (inside_equation) + os << "x"; + else + os << "\\textit{x}"; + if (i > 1) + { + if (inside_equation) + os << "^{" << i << "}"; + else + os << "\\textsuperscript{" << i << "}"; + } + } + } + } + + inside_equation = saved_inside_equation; + if (!inside_equation && latex_as_equation()) + os.put(os.widen('$')); + + return os; + } }; template class basic_numeric_formatter : public basic_numeric_formatter_base { + friend class basic_numeric_formatter_base; protected: template std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) @@ -843,6 +992,7 @@ namespace boost { this->styling(no_styling); bool need_i = true; + bool need_paren = false; if(!this->show_zero_components() && (f.imag() == 0)) { @@ -855,6 +1005,11 @@ namespace boost { } else { + if (this->parenthesis()) + { + os << "("; + need_paren = true; + } format_float(os, f.real()); typename Complex::value_type i(f.imag()); bool isneg = i < 0; @@ -880,6 +1035,8 @@ namespace boost { if (saved_style >= minimal_styling) os << ""; } + if (need_paren) + os << ")"; this->styling(saved_style); } @@ -891,7 +1048,53 @@ namespace boost { return os; } template - std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f); + std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f) + { + typename basic_numeric_formatter_base::scoped_parenthesis scoped(this); + + if (this->styling() >= minimal_styling) + os << ""; + if (this->styling() > minimal_styling) + os << ""; + + styling_level_t saved_style = this->styling(); + this->styling(no_styling); + + bool have_first = false; + for (unsigned i = 0; i <= f.degree(); ++i) + { + auto coef = f[i]; + if (this->show_zero_components() || !iszero(coef)) + { + if (have_first) + { + if (!isneg(coef)) + os << " + "; + else + os << " - "; + } + basic_numeric_formatter_base::print(*this, os, !isneg(coef) ? coef : -coef); + have_first = true; + if (i) + { + os << "x"; + if (i > 1) + { + os << "" << i << ""; + } + } + } + } + + this->styling(saved_style); + + if (this->styling() > minimal_styling) + os << ""; + if (this->styling() >= minimal_styling) + os << ""; + + return os; + } }; From 046f56b8afb8da7d1dcbe3f5667c5fd47b680299 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sat, 16 Nov 2019 18:20:52 +0000 Subject: [PATCH 07/14] Formatting: Add rational numbers support. [CI SKIP] --- doc/fp_utilities/formatting.qbk | 4 +- example/formatter_docbook_output.cpp | 35 +++- example/formatter_docbook_output.qbk | 13 ++ example/formatter_docbook_output_test.qbk | 2 + example/formatter_html_output.cpp | 37 +++- example/formatter_html_output.html | 14 +- example/formatter_latex_output.cpp | 26 ++- example/formatter_latex_output.pdf | Bin 78639 -> 80477 bytes example/formatter_latex_output.tex | 13 ++ example/formatter_text_output.cpp | 29 ++- example/formatter_text_output.txt | 9 + include/boost/math/tools/formatting.hpp | 239 +++++++++++++++++++--- 12 files changed, 390 insertions(+), 31 deletions(-) diff --git a/doc/fp_utilities/formatting.qbk b/doc/fp_utilities/formatting.qbk index bc61e6e9ea..60aeb9af74 100644 --- a/doc/fp_utilities/formatting.qbk +++ b/doc/fp_utilities/formatting.qbk @@ -31,7 +31,7 @@ The class `basic_numeric_printer` provides "pretty printing" capabilities for nu Its use mirrors that of the `iostream` library, but fulfills a very different role: There is no "input streaming" and the output is human readable--and not intended for machine consumption. -Supported types are integers, floating point numbers, complex numbers, rationals(TODO), polynomials(TODO), intervals(TODO) +Supported types are integers, floating point numbers, complex numbers, rationals, polynomials, intervals(TODO) and containers thereof(TODO). Other types, plus all the standard library manipulators can also be streamed to `basic_numeric_printer` but are simply forwarded to the underlying stream. @@ -174,6 +174,8 @@ The following section (using Docbook output format) illustrates how each of the [complex_formatting_examples] +[rational_formatting_examples] + [polynomial_formatting_examples] [heading Output Format Gallery] diff --git a/example/formatter_docbook_output.cpp b/example/formatter_docbook_output.cpp index 44404b9781..7a07db009d 100644 --- a/example/formatter_docbook_output.cpp +++ b/example/formatter_docbook_output.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include void print(std::ostream& os) { @@ -25,7 +27,7 @@ void print(std::ostream& os) printer.stream() << std::dec << "[[" << ival << "][hex]['''"; printer << std::hex << ival << "''']]\n"; printer.stream() << std::dec << "[[" << ival << "][oct]['''"; - printer << std::oct << ival << "''']]\n]\n]\n"; + printer << std::oct << ival << "''']]\n]\n]\n" << std::dec; printer << "[template float_formatting_examples[]\n"; double fval = 3; @@ -143,6 +145,35 @@ void print(std::ostream& os) printer << "\n]\n]\n"; + printer << "[template rational_formatting_examples[]\n"; + printer << "[table:rat_fmt_examples Rational Values\n[[Value][Result]]\n"; + + boost::rational rat(1, 3); + printer.stream() << "[[" << rat << "]['''"; + printer << rat << "''']]\n"; + rat = -rat; + printer.stream() << "[[" << rat; + printer << "]['''" << rat << "''']]\n"; + rat *= 345634; + rat /= 565; + printer.stream() << "[[" << rat; + printer << "]['''" << rat << "''']]\n"; + rat = 0; + printer.stream() << "[[" << rat; + printer << "]['''" << rat << "''']]\n"; + rat = -23; + printer.stream() << "[[" << rat; + printer << "]['''" << rat << "''']]\n"; + boost::multiprecision::cpp_rational rat2(1); + for (unsigned i = 1; i < 20; i += 2) + { + rat2 *= i; + rat2 /= i + 1; + } + printer.stream() << "[[" << rat2; + printer << "]['''" << rat2 << "''']]\n"; + printer << "\n]\n]\n"; + printer << "[template polynomial_formatting_examples[]\n"; printer << "[table:poly_fmt_examples Polynomial Values\n[[Kind][Result]]"; @@ -152,6 +183,8 @@ void print(std::ostream& os) printer << "[[Float]['''" << poly2 << "''']]" << std::endl; boost::math::tools::polynomial > poly3 = { { 2.4, 3.25 }, {-34.25 }, { 0, 4.2e-6 }, { -5.34e-67, 4.65e-20 } }; printer << "[[Complex]['''" << poly3 << "''']]" << std::endl; + boost::math::tools::polynomial> poly4 = { {2, 3}, {-3, 23}, {4, 56}, {5, 32} }; + printer << "[[Rational]['''" << poly4 << "''']]" << std::endl; printer << "\n]\n]\n"; } diff --git a/example/formatter_docbook_output.qbk b/example/formatter_docbook_output.qbk index ec6d91eedf..48e4a0c163 100644 --- a/example/formatter_docbook_output.qbk +++ b/example/formatter_docbook_output.qbk @@ -57,6 +57,18 @@ [[(inf,nan)][default][default]['''NaN''']] [[(nan,inf)][default][default]['''NaN''']] +] +] +[template rational_formatting_examples[] +[table:rat_fmt_examples Rational Values +[[Value][Result]] +[[1/3]['''13''']] +[[-1/3]['''-13''']] +[[-345634/1695]['''-3456341695''']] +[[0/1]['''0''']] +[[-23/1]['''-23''']] +[[46189/262144]['''46189262144''']] + ] ] [template polynomial_formatting_examples[] @@ -64,6 +76,7 @@ [[Kind][Result]][[Integer]['''2 - 3x + 4x2 + 5x3''']] [[Float]['''2.4 - 34.25x + 4.2×10-06x2 - 5.34×10-67x3''']] [[Complex]['''(2.4 + 3.25i) - 34.25x + 4.2×10-06ix2 - (5.34×10-67 - 4.65×10-20i)x3''']] +[[Rational]['''(23) - (323)x + (114)x2 + (532)x3''']] ] ] diff --git a/example/formatter_docbook_output_test.qbk b/example/formatter_docbook_output_test.qbk index 38d0234267..8a9553804f 100644 --- a/example/formatter_docbook_output_test.qbk +++ b/example/formatter_docbook_output_test.qbk @@ -22,3 +22,5 @@ [complex_formatting_examples_2] [polynomial_formatting_examples] + +[rational_formatting_examples] diff --git a/example/formatter_html_output.cpp b/example/formatter_html_output.cpp index 904349a5b7..e9dc7443eb 100644 --- a/example/formatter_html_output.cpp +++ b/example/formatter_html_output.cpp @@ -7,13 +7,15 @@ #include #include #include +#include +#include void print(std::ostream& os) { boost::math::tools::html_printer printer(os); printer << "\n\n" << std::endl; - printer << "" << std::endl; + printer << "" << std::endl; printer << "

Synopsis

\nSample HTML output for various number types, these are then styled in bold, and various (somewhat untasteful!) colors just because we can, and to check that our markup generation is working correctly.\n"; @@ -30,7 +32,7 @@ void print(std::ostream& os) printer.stream() << std::dec << "" << ival << "hex"; printer << std::hex << ival << "\n"; printer.stream() << std::dec << "" << ival << "oct"; - printer << std::oct << ival << "\n\n\n"; + printer << std::oct << ival << "\n\n\n" << std::dec; printer << "

Basic Floating Point Values:

\n\n"; double fval = 3; @@ -167,6 +169,35 @@ void print(std::ostream& os) printer << "\n\n\n"; + printer << "

Rationals:

\n\n"; + + printer << ""; + boost::rational rat(1, 3); + printer.stream() << "\n"; + rat = -rat; + printer.stream() << "\n"; + rat *= 345634; + rat /= 565; + printer.stream() << "\n"; + rat = 0; + printer.stream() << "\n"; + rat = -23; + printer.stream() << "\n"; + boost::multiprecision::cpp_rational rat2(1); + for (unsigned i = 1; i < 20; i += 2) + { + rat2 *= i; + rat2 /= i + 1; + } + printer.stream() << "\n"; + printer << "\n
ValueResult
" << rat << ""; + printer << rat << "
" << rat; + printer << "" << rat << "
" << rat; + printer << "" << rat << "
" << rat; + printer << "" << rat << "
" << rat; + printer << "" << rat << "
" << rat2; + printer << "" << rat2 << "
\n\n"; + printer << "

Polynomials:

\n\n"; printer << ""; @@ -176,6 +207,8 @@ void print(std::ostream& os) printer << "\n"; boost::math::tools::polynomial > poly3 = { { 2.4, 3.25 }, {-34.25 }, { 0, 4.2e-6 }, { -5.34e-67, 4.65e-20 } }; printer << "\n"; + boost::math::tools::polynomial> poly4 = { {2, 3}, {-3, 23}, {4, 56}, {5, 32} }; + printer << "\n"; printer << "\n
TypeResult
Float" << poly2 << "
Complex" << poly3 << "
Polynomial" << poly4 << "
\n\n"; printer << "\n\n"; diff --git a/example/formatter_html_output.html b/example/formatter_html_output.html index 1eb3313d1d..837c3b7215 100644 --- a/example/formatter_html_output.html +++ b/example/formatter_html_output.html @@ -1,7 +1,7 @@ - +

Synopsis

Sample HTML output for various number types, these are then styled in bold, and various (somewhat untasteful!) colors just because we can, and to check that our markup generation is working correctly.

Basic Integers Values:

@@ -68,11 +68,23 @@

Complex Special Values:

+

Rationals:

+ + + + + + + + +
ValueResult
1/313
-1/3-13
-345634/1695-3456341695
0/10
-23/1-23
46189/26214446189262144
+

Polynomials:

+
TypeResult
Integer2 - 3x + 4x2 + 5x3
Float2.4 - 34.25x + 4.2×10-06x2 - 5.34×10-67x3
Complex(2.4 + 3.25i) - 34.25x + 4.2×10-06ix2 - (5.34×10-67 - 4.65×10-20i)x3
Polynomial(23) - (323)x + (114)x2 + (532)x3
diff --git a/example/formatter_latex_output.cpp b/example/formatter_latex_output.cpp index c2e865018b..5d05b8fe82 100644 --- a/example/formatter_latex_output.cpp +++ b/example/formatter_latex_output.cpp @@ -7,6 +7,7 @@ #include #include #include +#include void print(std::ostream& os) { @@ -29,7 +30,7 @@ void print(std::ostream& os) printer.stream() << std::dec << ival << " & hex & "; printer << std::hex << ival << "\\\\\n"; printer.stream() << std::dec << ival << " & oct & "; - printer << std::oct << ival << "\\\\\n\\end{tabular}\n\n"; + printer << std::oct << ival << "\\\\\n\\end{tabular}\n\n" << std::dec; double fval = 3; printer << "\\textbf{Basic Float Values}\n\n\\begin{tabular}{r r r r}\nValue & Precision & Format & Result \\\\\n"; @@ -162,6 +163,27 @@ void print(std::ostream& os) printer << cval << " \\\\\n"; printer << "\\end{tabular}\n\n" << std::defaultfloat << boost::math::tools::multiply_times; + printer << "\\textbf{Rational Values}\n\n\\begin{tabular}{r r r}\nValue & Format & Result \\\\\n"; + boost::rational r(2, 3); + printer.stream() << r << " & default &"; + printer << r << " \\\\\n"; + r = -r; + printer.stream() << r << " & default &"; + printer << r << " \\\\\n"; + r = 0; + printer.stream() << r << " & default &"; + printer << r << " \\\\\n"; + r = -23; + printer.stream() << r << " & default &"; + printer << r << " \\\\\n"; + r *= 345634; + r /= 565; + printer.stream() << r << " & default &"; + printer << r << " \\\\\n"; + printer.stream() << r << " & latex\\_as\\_text &"; + printer << boost::math::tools::latex_as_text << r << " \\\\\n"; + printer << "\\end{tabular}\n\n"; + printer << "\\textbf{Polynomial Values}\n\n\\begin{tabular}{r r}\nType & Result \\\\\n"; boost::math::tools::polynomial poly1 = { 2, -3, 4, 5 }; printer << "Integer & " << poly1 << " \\\\\n"; @@ -171,6 +193,8 @@ void print(std::ostream& os) printer << "Complex & " << poly3 << " \\\\\n"; printer << "Complex (latex\\_as\\_text) & " << boost::math::tools::latex_as_text << poly3 << " \\\\\n" << boost::math::tools::latex_as_equation; printer << "Complex (multiply\\_dot) & " << boost::math::tools::multiply_dot << poly3 << " \\\\\n" << boost::math::tools::multiply_times; + boost::math::tools::polynomial> poly4 = { {2, 3}, {-3, 23}, {4, 56}, {5, 32} }; + printer << "Rational & " << poly4 << " \\\\\n"; printer << "\\end{tabular}\n\n" << std::defaultfloat << boost::math::tools::multiply_times; printer << "\\end{document}\n\n"; diff --git a/example/formatter_latex_output.pdf b/example/formatter_latex_output.pdf index 7c46a92e54484e186635020785978a6e74e8d44f..6b4f896993261344cfea68357d12f5ff7aa4fe79 100644 GIT binary patch delta 43437 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 delta 41532 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 ZSh{)mxY^p_L!nS05TBi0K~oX`zW|~nZ65#t diff --git a/example/formatter_latex_output.tex b/example/formatter_latex_output.tex index 645012f185..c8b21b81b1 100644 --- a/example/formatter_latex_output.tex +++ b/example/formatter_latex_output.tex @@ -75,6 +75,18 @@ (inf,nan) & default & default & NaN \\ \end{tabular} +\textbf{Rational Values} + +\begin{tabular}{r r r} +Value & Format & Result \\ +2/3 & default &\textsuperscript{ 2}/\textsubscript{3} \\ +-2/3 & default &\textsuperscript{ -2}/\textsubscript{3} \\ +0/1 & default &0 \\ +-23/1 & default &-23 \\ +-7949582/565 & default &\textsuperscript{ -7949582}/\textsubscript{565} \\ +-7949582/565 & latex\_as\_text &\textsuperscript{ -7949582}/\textsubscript{565} \\ +\end{tabular} + \textbf{Polynomial Values} \begin{tabular}{r r} @@ -84,6 +96,7 @@ Complex & (2.4 + 3.25i) - 34.25\textit{x} + 4.2$\times$10\textsuperscript{-06}i\textit{x}\textsuperscript{2} - (5.34$\times$10\textsuperscript{-67} - 4.65$\times$10\textsuperscript{-20}i)\textit{x}\textsuperscript{3} \\ Complex (latex\_as\_text) & (2.4 + 3.25i) - 34.25\textit{x} + 4.2$\times$10\textsuperscript{-06}i\textit{x}\textsuperscript{2} - (5.34$\times$10\textsuperscript{-67} - 4.65$\times$10\textsuperscript{-20}i)\textit{x}\textsuperscript{3} \\ Complex (multiply\_dot) & $(2.4 + 3.25\mathrm{i}) - 34.25x + 4.2\cdot 10^{-06}\mathrm{i}x^{2} - (5.34\cdot 10^{-67} - 4.65\cdot 10^{-20}\mathrm{i})x^{3}$ \\ +Rational & $(\frac{2}{3}) - (\frac{3}{23})x + (\frac{1}{14})x^{2} + (\frac{5}{32})x^{3}$ \\ \end{tabular} \end{document} diff --git a/example/formatter_text_output.cpp b/example/formatter_text_output.cpp index 0a8fe7cb1c..0b6621b28c 100644 --- a/example/formatter_text_output.cpp +++ b/example/formatter_text_output.cpp @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include @@ -26,7 +28,7 @@ void print(std::basic_ostream& os) printer.stream() << std::setw(20) << ival << std::setw(20) << "hex" << std::setw(20); printer << std::hex << ival << std::endl; printer.stream() << std::setw(20) << ival << std::setw(20) << "oct" << std::setw(20); - printer << std::oct << ival << std::endl; + printer << std::oct << ival << std::endl << std::dec; printer << std::endl << std::endl; @@ -139,6 +141,27 @@ void print(std::basic_ostream& os) printer << cval << std::endl; + printer << "\nRationals:\n\n"; + boost::rational rat(1, 3); + printer << std::setw(20) << "Value" << std::setw(20) << "Result" << std::endl; + printer.stream() << std::setw(20) << rat; + printer << std::setw(20) << rat << std::endl; + rat = -rat; + printer.stream() << std::setw(20) << rat; + printer << std::setw(20) << rat << std::endl; + rat *= 345634; + rat /= 565; + printer.stream() << std::setw(20) << rat; + printer << std::setw(20) << rat << std::endl; + boost::multiprecision::cpp_rational rat2(1); + for (unsigned i = 1; i < 20; i += 2) + { + rat2 *= i; + rat2 /= i + 1; + } + printer.stream() << std::setw(20) << rat2; + printer << std::setw(20) << rat2 << std::endl; + printer << "\nPolynomials:\n\n"; boost::math::tools::polynomial poly1 = { 2, -3, 4, 5 }; printer << "Integer: " << poly1 << std::endl; @@ -146,6 +169,8 @@ void print(std::basic_ostream& os) printer << "Float: " << poly2 << std::endl; boost::math::tools::polynomial > poly3 = { { 2.4, 3.25 }, {-34.25 }, { 0, 4.2e-6 }, { -5.34e-67, 4.65e-20 } }; printer << "Complex: " << poly3 << std::endl; + boost::math::tools::polynomial> poly4 = { {2, 3}, {-3, 23}, {4, 56}, {5, 32} }; + printer << "Rational: " << poly4 << std::endl; } int main(int argc, const char* argv[]) @@ -158,7 +183,7 @@ int main(int argc, const char* argv[]) else { print(std::cout); - print(std::wcout); + //print(std::wcout); } return 0; } diff --git a/example/formatter_text_output.txt b/example/formatter_text_output.txt index 43fd026ade..737bec7691 100644 --- a/example/formatter_text_output.txt +++ b/example/formatter_text_output.txt @@ -54,8 +54,17 @@ Complex Zeros: (nan,inf) default default NaN (inf,nan) default default NaN +Rationals: + + Value Result + 1/3 ¹/₃ + -1/3 ⁻¹/₃ + -345634/1695 ⁻³⁴⁵⁶³⁴/₁₆₉₅ + 46189/262144 ⁴⁶¹⁸⁹/₂₆₂₁₄₄ + Polynomials: Integer: 2 - 3x + 4x² + 5x³ Float: 2.4 - 34.3x + 4.2×10⁻⁰⁶x² - 5.34×10⁻⁶⁶x³ Complex: (2.4 + 3.25i) - 34.3x + 4.2×10⁻⁰⁶ix² - (5.34×10⁻⁶⁶ - 4.65×10⁻²⁰i)x³ +Rational: (²/₃) - (³/₂₃)x + (¹/₁₄)x² + (⁵/₃₂)x³ diff --git a/include/boost/math/tools/formatting.hpp b/include/boost/math/tools/formatting.hpp index 81efc2f06b..2f3ced3e65 100644 --- a/include/boost/math/tools/formatting.hpp +++ b/include/boost/math/tools/formatting.hpp @@ -23,9 +23,18 @@ #endif namespace boost { + + template + struct rational; + namespace math { namespace tools { + template + T numerator(const boost::rational& r) { return r.numerator(); } + template + T denominator(const boost::rational& r) { return r.denominator(); } + template ::value || boost::is_class::value> struct is_integer_like { @@ -58,6 +67,8 @@ namespace boost { using polynomial_t = decltype(std::declval()(std::declval()[0])); template using polynomial2_t = decltype(std::declval().degree()); + template + using rational_t = decltype(numerator(std::declval()) + denominator(std::declval())); template struct is_complex_like @@ -72,11 +83,16 @@ namespace boost { }; template - struct is_unhandled_type + struct is_rational_like { - static const bool value = !is_complex_like::value && !is_float_like::value && !is_integer_like::value && !is_polynomial_like::value; + static const bool value = boost::is_detected_v; }; + template + struct is_unhandled_type + { + static const bool value = !is_complex_like::value && !is_float_like::value && !is_integer_like::value && !is_polynomial_like::value && !is_rational_like::value; + }; enum output_format_t { @@ -198,6 +214,22 @@ namespace boost { } } + template + void write_unicode_subscript(std::basic_ostream & os, std::basic_string digits) + { + for (std::size_t i = 0; i < digits.size(); ++i) + { + if ((digits[i] >= '0') && (digits[i] <= '9')) + write_unicode_char(os, 0x2080 + (digits[i] - '0')); + else if (digits[i] == '-') + write_unicode_char(os, 0x208B); + else if (digits[i] == '+') + write_unicode_char(os, 0x208A); + else + os.put(digits[i]); + } + } + template typename boost::enable_if_c::type unicode_character_len(const charT* p) { @@ -258,6 +290,7 @@ namespace boost { std::size_t m_parenthesis; bool m_show_zero_components; bool m_use_unicode; + std::basic_ostream* p_stream; protected: template static void decompose_float(std::basic_ostream& os, const Float& f, std::basic_string& mantissa, std::basic_string& exponent) @@ -278,7 +311,12 @@ namespace boost { mantissa = s; } public: - basic_numeric_formatter_base() : styling_level(full_styling), i_style(upright_i), multiply_style(multiply_times), m_parenthesis(0), m_show_zero_components(false), m_use_unicode(true) {} + basic_numeric_formatter_base(std::basic_ostream& os) + : styling_level(full_styling), i_style(upright_i), multiply_style(multiply_times), + m_parenthesis(0), m_show_zero_components(false), m_use_unicode(true), p_stream(&os) {} + basic_numeric_formatter_base(std::basic_ostream& os, const basic_numeric_formatter_base& o) + : styling_level(o.styling_level), i_style(o.i_style), multiply_style(o.multiply_style), + m_parenthesis(o.m_parenthesis), m_show_zero_components(o.m_show_zero_components), m_use_unicode(o.m_use_unicode), p_stream(&os) {} void styling(styling_level_t i) { @@ -329,6 +367,11 @@ namespace boost { return m_use_unicode; } + std::basic_ostream& stream() + { + return *p_stream; + } + struct scoped_parenthesis { basic_numeric_formatter_base* m_formatter; @@ -479,34 +522,88 @@ namespace boost { } return os; } + + template + std::basic_ostream& format_rational(std::basic_ostream& os, const Rational& rat) + { + auto num = numerator(rat); + auto denom = denominator(rat); + + + if (num == 0) + { + return os << "0"; + } + else if (denom == 1) + { + format_integer(os, num); + return os; + } + else + { + if (parenthesis()) + os << "("; + auto s1 = part_as_string(*this, num); + auto s2 = part_as_string(*this, denom); + + write_unicode_superscript(os, s1); + os << "/"; + write_unicode_subscript(os, s2); + + if (parenthesis()) + os << ")"; + } + + return os; + } + bool latex_as_equation()const { return false; } void latex_as_equation(bool) {} template - typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + static std::basic_string part_as_string(Printer& printer, const Value& value) + { + std::basic_ostringstream ss; + ss.copyfmt(printer.stream()); + ss.imbue(printer.stream().getloc()); + ss.width(0); + Printer fmt(ss, printer); + fmt.print(fmt, fmt.stream(), value); + return ss.str(); + } + + template + static typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) { printer.format_integer(os, value); } template - typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + static typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) { printer.format_float(os, value); } template - typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + static typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) { printer.format_complex(os, value); } template - typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + static typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) { printer.format_polynomial(os, value); } + template + static typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + { + printer.format_rational(os, value); + } }; template class basic_numeric_formatter : public basic_numeric_formatter_base { + public: + basic_numeric_formatter(std::basic_ostream& os) : basic_numeric_formatter_base(os) {} }; template @@ -514,6 +611,8 @@ namespace boost { { friend class basic_numeric_formatter_base; protected: + basic_numeric_formatter(std::basic_ostream& os) : basic_numeric_formatter_base(os) {} + template std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) { @@ -693,6 +792,39 @@ namespace boost { return os; } + template + std::basic_ostream& format_rational(std::basic_ostream& os, const Rational& rat) + { + auto num = numerator(rat); + auto denom = denominator(rat); + + if ((denom == 1) || (num == 0)) + { + format_integer(os, num); + } + else + { + if (this->styling() >= minimal_styling) + os << ""; + if (this->styling() > minimal_styling) + os << ""; + + if (this->parenthesis()) + os << "("; + + os << "" << num << ""; + os << "⁄" << denom << ""; + + if (this->parenthesis()) + os << ")"; + + if (this->styling() > minimal_styling) + os << ""; + if (this->styling() >= minimal_styling) + os << ""; + } + return os; + } }; template @@ -704,7 +836,7 @@ namespace boost { bool inside_equation; public: - basic_numeric_formatter() : is_latex_as_equation(true), inside_equation(false) {} + basic_numeric_formatter(std::basic_ostream& os) : basic_numeric_formatter_base(os), is_latex_as_equation(true), inside_equation(false) {} void latex_as_equation(bool b) { is_latex_as_equation = b; } bool latex_as_equation()const { return is_latex_as_equation; } @@ -902,6 +1034,42 @@ namespace boost { if (!inside_equation && latex_as_equation()) os.put(os.widen('$')); + return os; + } + template + std::basic_ostream& format_rational(std::basic_ostream& os, const Rational& rat) + { + auto num = numerator(rat); + auto denom = denominator(rat); + + + if ((num == 0) || (denom == 1)) + { + format_integer(os, num); + } + else + { + if (!inside_equation && latex_as_equation()) + os.put(os.widen('$')); + if (this->parenthesis()) + os << "("; + + if (latex_as_equation()) + { + if(num < 0) + os << "-\\frac{" << -num << "}{" << denom << "}"; + else + os << "\\frac{" << num << "}{" << denom << "}"; + } + else + os << "\\textsuperscript{ " << num << "}/\\textsubscript{" << denom << "}"; + + if (this->parenthesis()) + os << ")"; + if (!inside_equation && latex_as_equation()) + os.put(os.widen('$')); + } + return os; } }; @@ -911,6 +1079,8 @@ namespace boost { { friend class basic_numeric_formatter_base; protected: + basic_numeric_formatter(std::basic_ostream& os) : basic_numeric_formatter_base(os) {} + template std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) { @@ -1093,6 +1263,39 @@ namespace boost { if (this->styling() >= minimal_styling) os << ""; + return os; + } + template + std::basic_ostream& format_rational(std::basic_ostream& os, const Rational& rat) + { + auto num = numerator(rat); + auto denom = denominator(rat); + + if ((num == 0) || (denom == 1)) + { + format_integer(os, num); + } + else + { + if (this->styling() >= minimal_styling) + os << ""; + if (this->styling() > minimal_styling) + os << ""; + + if (this->parenthesis()) + os << "("; + os << "" << num << ""; + os << "⁄" << denom << ""; + + if (this->parenthesis()) + os << ")"; + + if (this->styling() > minimal_styling) + os << ""; + if (this->styling() >= minimal_styling) + os << ""; + } + return os; } }; @@ -1101,13 +1304,12 @@ namespace boost { template > class basic_numeric_printer : private basic_numeric_formatter { - std::basic_ostream* p_stream; typedef basic_numeric_formatter base_type; public: - basic_numeric_printer(std::basic_ostream& os) : p_stream(&os) {} + basic_numeric_printer(std::basic_ostream& os) : basic_numeric_formatter(os) {} basic_numeric_printer(std::basic_ostream& os, const basic_numeric_printer& printer) - : p_stream(&os) + : basic_numeric_formatter(os) { styling(printer.styling()); imaginary_style(printer.imaginary_style()); @@ -1121,6 +1323,7 @@ namespace boost { using base_type::format_float; using base_type::format_complex; using base_type::format_polynomial; + using base_type::format_rational; using base_type::styling; using base_type::imaginary_style; using base_type::latex_as_equation; @@ -1129,11 +1332,7 @@ namespace boost { using base_type::show_zero_components; using base_type::use_unicode; using base_type::print; - - std::basic_ostream& stream() - { - return *p_stream; - } + using base_type::stream; }; typedef basic_numeric_printer<> text_printer; @@ -1152,13 +1351,7 @@ namespace boost { std::size_t w = (std::size_t)os.stream().width(); if (w) { - std::basic_stringstream ss; - ss.copyfmt(os.stream()); - ss.imbue(os.stream().getloc()); - ss.width(0); - basic_numeric_printer fmt(ss, os); - fmt.print(fmt, fmt.stream(), i); - std::basic_string s = ss.str(); + std::basic_string s = basic_numeric_formatter_base::part_as_string(os, i); std::size_t len = unicode_character_len(s.c_str()); if (len < w) { From 543c495072f2d8bce802e365148f3c91ed875e8e Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sun, 17 Nov 2019 17:58:39 +0000 Subject: [PATCH 08/14] formatting: Update plain text output of infinities. Get windows console output working. --- example/formatter_text_output.cpp | 8 +++++++- example/formatter_text_output.txt | 6 +++--- include/boost/math/tools/formatting.hpp | 3 ++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/example/formatter_text_output.cpp b/example/formatter_text_output.cpp index 0b6621b28c..3fb658a23e 100644 --- a/example/formatter_text_output.cpp +++ b/example/formatter_text_output.cpp @@ -173,6 +173,10 @@ void print(std::basic_ostream& os) printer << "Rational: " << poly4 << std::endl; } +#ifdef _WIN32 +#include +#endif + int main(int argc, const char* argv[]) { if (argc > 1) @@ -182,8 +186,10 @@ int main(int argc, const char* argv[]) } else { +#ifdef _WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif print(std::cout); - //print(std::wcout); } return 0; } diff --git a/example/formatter_text_output.txt b/example/formatter_text_output.txt index 737bec7691..1aca8be364 100644 --- a/example/formatter_text_output.txt +++ b/example/formatter_text_output.txt @@ -46,9 +46,9 @@ Complex Zeros: (0,25.3) default show_zero_components 0 + 25.3i (0,-25.3) default default -25.3i (0,-25.3) default show_zero_components 0 - 25.3i - (inf,-25.3) default default ∞̃ - (2,inf) default default ∞̃ - (-inf,inf) default default ∞̃ + (inf,-25.3) default default ∞ + (2,inf) default default ∞ + (-inf,inf) default default ∞ (nan,-25.3) default default NaN (3,nan) default default NaN (nan,inf) default default NaN diff --git a/include/boost/math/tools/formatting.hpp b/include/boost/math/tools/formatting.hpp index 2f3ced3e65..de00940722 100644 --- a/include/boost/math/tools/formatting.hpp +++ b/include/boost/math/tools/formatting.hpp @@ -442,7 +442,8 @@ namespace boost { if (use_unicode()) { write_unicode_char(os, 0x221E); - write_unicode_char(os, 0x0303); + // Combining characters not supported in plain text? + //write_unicode_char(os, 0x0303); } else os << "COMPLEX INFINITY"; From 837504cbbc7a548b19a3a2ee7dd24b69d95960ed Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sat, 23 Nov 2019 19:34:00 +0000 Subject: [PATCH 09/14] formatting: big restructuring with public API for custom types. Add cubic B spline as example. --- doc/fp_utilities/formatting.qbk | 289 +++ example/formatter_b_spline_output.cpp | 32 + example/formatter_docbook_output.qbk | 54 +- example/formatter_html_output.html | 56 +- example/formatter_latex_output.cpp | 6 +- example/formatter_latex_output.pdf | Bin 80477 -> 80655 bytes example/formatter_latex_output.tex | 68 +- example/formatter_text_output.cpp | 6 +- example/formatter_text_output.txt | 14 +- .../interpolators/cardinal_cubic_b_spline.hpp | 6 + .../detail/cardinal_cubic_b_spline_detail.hpp | 83 +- include/boost/math/tools/formatting.hpp | 1694 ++++++----------- 12 files changed, 1146 insertions(+), 1162 deletions(-) create mode 100644 example/formatter_b_spline_output.cpp diff --git a/doc/fp_utilities/formatting.qbk b/doc/fp_utilities/formatting.qbk index 60aeb9af74..555db1b979 100644 --- a/doc/fp_utilities/formatting.qbk +++ b/doc/fp_utilities/formatting.qbk @@ -174,6 +174,8 @@ The following section (using Docbook output format) illustrates how each of the [complex_formatting_examples] +[complex_formatting_examples_2] + [rational_formatting_examples] [polynomial_formatting_examples] @@ -190,6 +192,293 @@ The above tables of sample output are available in various other formats [@../../example/formatter_latex_output.pdf Latex PDF output], generated with [@../../example/formatter_latex_output.cpp formatter_latex_output.cpp] and then converted to PDF. +[heading Extender Manual] + +This component is (somewhat) extendable to other output formats and other numeric types. + +The class `basic_numeric_printer` inherits from `basic_numeric_printer_base` like so: + + template + class basic_numeric_printer : public basic_numeric_printer_base {/*details*/ }; + +The task of enabling a new output format is to define a partial specialisation of `basic_numeric_printer` which inherits from +`basic_numeric_printer_base` and overrides it's virtual member functions to tailor the output. Conversely, the task +of enabling support for a new number type, is to provide a free function `print`: + + template + void print(basic_numeric_printer_base& os, MyNumber const& value); + +Which can be found via ADL, and which calls `basic_numeric_printer_base`'s member functions to +perform the formatted output, it may also make unqualified calls to `print` where the type +is a collection of number types which are supported. This is prefered to overloading `operator<<` directly, since the +default `operator<<` will handle stream padding (as a result of `std::setw()`) for you. + + template + class basic_numeric_printer_base + { + public: + + void styling(styling_level_t i); + styling_level_t styling()const; + + void imaginary_style(imaginary_i_t i); + imaginary_i_t imaginary_style()const; + + void multiply(multiplyer_t t); + multiplyer_t multiply()const; + + void parenthesis(std::size_t b); + std::size_t parenthesis()const; + + void show_zero_components(bool b); + bool show_zero_components()const; + + void use_unicode(bool b); + bool use_unicode()const; + + bool latex_as_equation()const; + void latex_as_equation(bool); + + std::basic_ostream& stream(); + + struct scoped_parenthesis + { + scoped_parenthesis(basic_numeric_printer_base* formatter); + ~scoped_parenthesis(); + }; + struct scoped_styling + { + scoped_styling(basic_numeric_printer_base* formatter); + ~scoped_styling(); + }; + struct scoped_prolog + { + scoped_prolog(basic_numeric_printer_base* formatter, const char* number_type); + ~scoped_prolog(); + }; + + virtual void print_prolog(const char*); + virtual void print_epilog(); + virtual void print_special_character(boost::uint32_t unicode_value); + virtual void print_times(); + virtual void print_superscript(const std::basic_string& s); + virtual void print_imaginary_unit(); + virtual void print_complex_infinity(); + virtual void print_variable(charT c); + virtual void print_name(const std::basic_string& s); + virtual void print_fraction(const std::basic_string& s1, const std::basic_string& s2); + virtual void print_subscript(const std::basic_string& s); + + template + void print_integer(const Integer& i); + template + void print_float(const Float& f); + template + void print_complex(const Complex& f); + template + void print_polynomial(const Polynomial& f); + template + void print_rational(const Rational& rat); + + template + static std::basic_string part_as_string(Printer& printer, const Value& value); + }; + +Details are as follows: + + void styling(styling_level_t i); + styling_level_t styling()const; + +Gets and sets the XML styling level. + + void imaginary_style(imaginary_i_t i); + imaginary_i_t imaginary_style()const; + +Gets and sets the character used for the imaginary unit /i/. + + void multiply(multiplyer_t t); + multiplyer_t multiply()const; + +Gets and sets the character used as the multiplication symbol. + + void parenthesis(std::size_t b); + std::size_t parenthesis()const; + +Gets and sets the current parenthesis level, should typically only be set via `scoped_parenthesis`. + +Compound types should always check the current parenthesis level and if it is greater than zero, wrap themselves in +parenthesis. They should do this before constructing a `scoped_parenthesis` of course. + + void show_zero_components(bool b); + bool show_zero_components()const; + +Gets and sets whether zero sub-components are displayed or not. + + void use_unicode(bool b); + bool use_unicode()const; + +Gets and sets whether plain text shoud use Unicode output or not: when `true` then the output is UTF-N where +N is either 8, 16 or 32 and is determined by the size of `charT`. + + bool latex_as_equation()const; + void latex_as_equation(bool); + +Gets and sets whether latex output formats numbers using math mode. + + std::basic_ostream& stream(); + +Returns the underlying output stream. + + struct scoped_parenthesis + { + scoped_parenthesis(basic_numeric_printer_base* formatter); + ~scoped_parenthesis(); + }; + +A scoped parenthesis object should be constructed, whenever sub-objects need to wrap themselves in parenthesis if +they themselves contain sub-objects. For example the `print` function for polynomials declares a `scoped_parenthesis` +right at it's start, if the coefficients are "atomic" types such as integers then they ignore the parenthesis level +and are printed normally. Compound coefficients such as complex numbers will make use of the parenthesis level though +and wrap themselves in parenthesis. + + struct scoped_styling + { + scoped_styling(basic_numeric_printer_base* formatter); + ~scoped_styling(); + }; + +XML output will wrap numbers in styling tags - for example `...` in html output. +If the current type being formatted should be treated as a single number then a `scoped_styling` object should be +constructed at the start of the types `print` function. This will then suppress styling tags being added to +any printed sub-objects. + + struct scoped_prolog + { + scoped_prolog(basic_numeric_printer_base* formatter, const char* number_type); + ~scoped_prolog(); + }; + +Any type that provides a `print` function should construct a `scoped_prolog` object at it's start - it calls +`print_prolog` during construction and `print_epilog` during destruction. Set `number_type` to the empty +string if you're printing an expression that contains multiple sub-components, and you're not constructing +a `scoped_styling` object. ie if sub-objects are styled, but this object is not. + + virtual void print_prolog(const char*); + +Prints any prolog to the formatted output - for example styling tags in HTML/XML output. + +`basic_numeric_printer_base<>::print_prolog` does nothing, specializations of `basic_numeric_printer` +override that behaviour according to need. + + virtual void print_epilog(); + +Prints any elilog to the formatted output - for example closing styling tags in HTML/XML output. + +`basic_numeric_printer_base<>::print_epilog` does nothing, specializations of `basic_numeric_printer` +override that behaviour according to need. + + virtual void print_special_character(boost::uint32_t unicode_value); + +Prints a special math character denoted by it's unicode code point. + +`basic_numeric_printer_base<>::print_special_character` outputs the character as UTF-N where N is determined by +the size of `charT` if `use_unicode()` is true, otherwise prints the closest plain text approximation, +specializations of `basic_numeric_printer` +override that behaviour according to need. + + + virtual void print_times(); + +Prints the multiplication character. + +`basic_numeric_printer_base<>::print_times` outputs the character as UTF-N where N is determined by +the size of `charT` if `use_unicode()` is true, otherwise prints an "x", specializations of `basic_numeric_printer` +override that behaviour according to need. + + virtual void print_superscript(const std::basic_string& s); + +Prints `s` as superscript. + +`basic_numeric_printer_base<>::print_superscript` outputs the characters as UTF-N superscript where N is determined by +the size of `charT` if `use_unicode()` is true, otherwise prints a "^" followed by `s`, specializations of `basic_numeric_printer` +override that behaviour according to need. + + virtual void print_imaginary_unit(); + +Prints the imaginary unit i. + +`basic_numeric_printer_base<>::print_imaginary_unit` outputs the character as UTF-N where N is determined by +the size of `charT` if `use_unicode()` is true, otherwise prints a literal "i", specializations of `basic_numeric_printer` +override that behaviour according to need. + + virtual void print_complex_infinity(); + +Prints complex infinity. + +`basic_numeric_printer_base<>::print_complex_infinity` outputs the infinity symbol as UTF-N where N is determined by +the size of `charT` if `use_unicode()` is true, otherwise prints a literal "INFINITY", specializations of `basic_numeric_printer` +override that behaviour according to need. + + virtual void print_variable(charT c); + +Prints a character `c` that represents a named variable, typically rendered in italic text. + +`basic_numeric_printer_base<>::print_variable` outputs the character "as is", specializations of `basic_numeric_printer` +override that behaviour according to need. + + virtual void print_name(const std::basic_string& s); + +Prints a string name `s` that represents a named quantity, typically rendered in upright (non-italic) text. + +`basic_numeric_printer_base<>::print_variable` outputs the string "as is", specializations of `basic_numeric_printer` +override that behaviour according to need. + + virtual void print_fraction(const std::basic_string& s1, const std::basic_string& s2); + +Prints the fraction `s1/s2`. + +`basic_numeric_printer_base<>::print_fraction` outputs the fraction as plain text s1/s2, specializations of `basic_numeric_printer` +override that behaviour according to need. + + virtual void print_subscript(const std::basic_string& s); + +Prints `s` as subscript. + +`basic_numeric_printer_base<>::print_subscript` outputs the characters as UTF-N subscript where N is determined by +the size of `charT` if `use_unicode()` is true, otherwise prints a "_" followed by `s`, specializations of `basic_numeric_printer` +override that behaviour according to need. + + template + void print_integer(const Integer& i); + +Prints integer value i. + + template + void print_float(const Float& f); + +Prints floating point value f. + + template + void print_complex(const Complex& f); + +Prints complex number f. + + template + void print_polynomial(const Polynomial& f); + +Prints polynomial f. + + template + void print_rational(const Rational& rat); + +Prints the rational value rat. + + template + static std::basic_string part_as_string(Printer& printer, const Value& value); + +Helper function. Returns `value` as a string. Uses a copy of `printer` which contains the same formatting flags (on both itself and +the std::ostream it points to) but which outputs to a `std::basic_ostringstream` and then returns the result. + [endsect] [/section:formatting] diff --git a/example/formatter_b_spline_output.cpp b/example/formatter_b_spline_output.cpp new file mode 100644 index 0000000000..36f6445101 --- /dev/null +++ b/example/formatter_b_spline_output.cpp @@ -0,0 +1,32 @@ +// (C) Copyright John Maddock 2019. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +int main() { + std::vector v(10); + for (size_t i = 0; i < v.size(); ++i) + { + v[i] = 5 + 6 * i; + } + + double step = 0.1; + double a = 5; + boost::math::interpolators::cardinal_cubic_b_spline spline(v.data(), v.size(), a, step); + + boost::math::tools::text_printer printer(std::cout); + printer << spline << std::endl; + + boost::math::tools::latex_printer lprinter(std::cout); + lprinter << spline << std::endl; + + boost::math::tools::html_printer hprinter(std::cout); + hprinter << spline << std::endl; + + return 0; +} diff --git a/example/formatter_docbook_output.qbk b/example/formatter_docbook_output.qbk index 48e4a0c163..77f735c0de 100644 --- a/example/formatter_docbook_output.qbk +++ b/example/formatter_docbook_output.qbk @@ -11,43 +11,43 @@ [table:float_fmt_examples Basic Float Values [[Value][Precision][Format][Result]][[3][default][default]['''3''']] [[3.14][default][default]['''3.14''']] -[[-1.23457e-24][default][default]['''-1.23457×10-24''']] -[[-1.23457e-24][3][scientific]['''-1.235×10-24''']] +[[-1.23457e-24][default][default]['''-1.23457×10-24''']] +[[-1.23457e-24][3][scientific]['''-1.235×10-24''']] [[-1.235e-24][3][scientific + multiply_x]['''-1.235x10-24''']] -[[-1.235e-24][3][scientific + multiply_dot]['''-1.235⋅10-24''']] +[[-1.235e-24][3][scientific + multiply_dot]['''-1.235⋅10-24''']] [[0][default][default]['''0''']] [[-0][default][default]['''-0''']] -[[inf][default][default]['''''']] -[[-inf][default][default]['''-∞''']] +[[inf][default][default]['''''']] +[[-inf][default][default]['''-∞''']] [[nan][default][default]['''NaN''']] [[-nan(ind)][default][default]['''NaN''']] ] ] [template complex_formatting_examples[] [table:complex_fmt_examples Basic Complex Values -[[Value][Precision][Format][Result]][[(3.25,4.67)][default][default]['''3.25 + 4.67i''']] +[[Value][Precision][Format][Result]][[(3.25,4.67)][default][default]['''3.25 + 4.67i''']] [[(3.14,0)][default][default]['''3.14''']] -[[(1.23,-1.23e-24)][default][default]['''1.23 - 1.23×10-24i''']] -[[(1.23,-1.23e-24)][3][scientific]['''1.230×10+00 - 1.235×10-24i''']] -[[(1.230e+00,-1.235e-24)][12][default + slanted_i]['''1.23 - 1.2345678765×10-24i''']] -[[(1.23,-1.2345678765e-24)][12][default + doublestruck_i]['''1.23 - 1.2345678765×10-24''']] -[[(1.23,-1.2345678765e-24)][12][default + doublestruck_i + multiply_x]['''1.23 - 1.2345678765x10-24''']] -[[(1.23,-1.2345678765e-24)][12][default + doublestruck_i + multiply_dot]['''1.23 - 1.2345678765⋅10-24''']] +[[(1.23,-1.23e-24)][default][default]['''1.23 - 1.23×10-24i''']] +[[(1.23,-1.23e-24)][3][scientific]['''1.230×10+00 - 1.235×10-24i''']] +[[(1.230e+00,-1.235e-24)][12][default + slanted_i]['''1.23 - 1.2345678765×10-24i''']] +[[(1.23,-1.2345678765e-24)][12][default + doublestruck_i]['''1.23 - 1.2345678765×10-24''']] +[[(1.23,-1.2345678765e-24)][12][default + doublestruck_i + multiply_x]['''1.23 - 1.2345678765x10-24''']] +[[(1.23,-1.2345678765e-24)][12][default + doublestruck_i + multiply_dot]['''1.23 - 1.2345678765⋅10-24''']] ] ] [template complex_formatting_examples_2[] [table:complex_fmt_examples Complex Special Values [[Value][Precision][Format][Result]][[(0,0)][default][default]['''0''']] -[[(0,0)][default][show_zero_components]['''0 + 0i''']] +[[(0,0)][default][show_zero_components]['''0 + 0i''']] [[(2.5,0)][default][default]['''2.5''']] -[[(2.5,0)][default][show_zero_components]['''2.5 + 0i''']] +[[(2.5,0)][default][show_zero_components]['''2.5 + 0i''']] [[(-2.5,0)][default][default]['''-2.5''']] -[[(-2.5,0)][default][show_zero_components]['''-2.5 + 0i''']] -[[(0,3.25)][default][default]['''3.25i''']] -[[(0,3.25)][default][show_zero_components]['''0 + 3.25i''']] -[[(0,-3.25)][default][default]['''-3.25i''']] -[[(0,-3.25)][default][show_zero_components]['''0 - 3.25i''']] +[[(-2.5,0)][default][show_zero_components]['''-2.5 + 0i''']] +[[(0,3.25)][default][default]['''3.25i''']] +[[(0,3.25)][default][show_zero_components]['''0 + 3.25i''']] +[[(0,-3.25)][default][default]['''-3.25i''']] +[[(0,-3.25)][default][show_zero_components]['''0 - 3.25i''']] [[(inf,2.5)][default][default]['''∞̃''']] [[(-inf,2.5)][default][default]['''∞̃''']] [[(2.5,inf)][default][default]['''∞̃''']] @@ -63,20 +63,20 @@ [table:rat_fmt_examples Rational Values [[Value][Result]] [[1/3]['''13''']] -[[-1/3]['''-13''']] -[[-345634/1695]['''-3456341695''']] -[[0/1]['''0''']] -[[-23/1]['''-23''']] +[[-1/3]['''-13''']] +[[-345634/1695]['''-3456341695''']] +[[0/1]['''0''']] +[[-23/1]['''-23''']] [[46189/262144]['''46189262144''']] ] ] [template polynomial_formatting_examples[] [table:poly_fmt_examples Polynomial Values -[[Kind][Result]][[Integer]['''2 - 3x + 4x2 + 5x3''']] -[[Float]['''2.4 - 34.25x + 4.2×10-06x2 - 5.34×10-67x3''']] -[[Complex]['''(2.4 + 3.25i) - 34.25x + 4.2×10-06ix2 - (5.34×10-67 - 4.65×10-20i)x3''']] -[[Rational]['''(23) - (323)x + (114)x2 + (532)x3''']] +[[Kind][Result]][[Integer]['''2 - 3x + 4x2 + 5x3''']] +[[Float]['''2.4 - 34.25x + 4.2×10-06x2 - 5.34×10-67x3''']] +[[Complex]['''(2.4 + 3.25i) - 34.25x + 4.2×10-06ix2 - (5.34×10-67 - 4.65×10-20i)x3''']] +[[Rational]['''(23) - (323)x + (114)x2 + (532)x3''']] ] ] diff --git a/example/formatter_html_output.html b/example/formatter_html_output.html index 837c3b7215..dd8b06e6b5 100644 --- a/example/formatter_html_output.html +++ b/example/formatter_html_output.html @@ -15,14 +15,14 @@

Basic Floating Point Values:

- - + + - - - - + + + + @@ -30,29 +30,29 @@

Basic Floating Point Values:

Complex Values:

-
ValuePrecisionFormatResult
3defaultdefault3
3.14defaultdefault3.14
-1.23457e-24defaultdefault-1.23457×10-24
-1.23457e-243scientific-1.235×10-24
-1.23457e-24defaultdefault-1.23457×10-24
-1.23457e-243scientific-1.235×10-24
-1.235e-24defaultmultiply_x-1.235x10-24
-1.235e-243scientific + multiply_x-1.235x10-24
-1.235e-24defaultmultiply_dot-1.235⋅10-24
-1.235e-243scientific + multiply_dot-1.235⋅10-24
infdefaultdefault
-infdefaultdefault-∞
-1.235e-24defaultmultiply_dot-1.235⋅10-24
-1.235e-243scientific + multiply_dot-1.235⋅10-24
infdefaultdefault
-infdefaultdefault-∞
nandefaultdefaultNaN
0defaultdefault0
-0defaultdefault-0
+
ValuePrecisionFormatResult
(3.25,4.67)defaultdefault3.25 + 4.67i
- - - - - - + + + + + +
ValuePrecisionFormatResult
(3.25,4.67)defaultdefault3.25 + 4.67i
(3.14,0)defaultdefault3.14
(1.23,-1.23e-24)defaultdefault1.23 - 1.23×10-24i
(1.23,-1.23e-24)3scientific1.230×10+00 - 1.235×10-24i
(1.230e+00,-1.235e-24)12default + slanted_i1.23 - 1.2345678765×10-24i
(1.23,-1.2345678765e-24)12default + doublestruck_i1.23 - 1.2345678765×10-24
(1.23,-1.2345678765e-24)12default + doublestruck_i + multiply_x1.23 - 1.2345678765x10-24
(1.23,-1.2345678765e-24)12default + doublestruck_i + multiply_dot1.23 - 1.2345678765⋅10-24
(1.23,-1.23e-24)defaultdefault1.23 - 1.23×10-24i
(1.23,-1.23e-24)3scientific1.230×10+00 - 1.235×10-24i
(1.230e+00,-1.235e-24)12default + slanted_i1.23 - 1.2345678765×10-24i
(1.23,-1.2345678765e-24)12default + doublestruck_i1.23 - 1.2345678765×10-24
(1.23,-1.2345678765e-24)12default + doublestruck_i + multiply_x1.23 - 1.2345678765x10-24
(1.23,-1.2345678765e-24)12default + doublestruck_i + multiply_dot1.23 - 1.2345678765⋅10-24

Complex Special Values:

- + - + - - - - - + + + + + @@ -71,20 +71,20 @@

Complex Special Values:

Rationals:

ValuePrecisionFormatResult
(0,0)defaultdefault0
(0,0)defaultshow_zero_components0 + 0i
(0,0)defaultshow_zero_components0 + 0i
(3.14,0)defaultdefault3.14
(3.14,0)defaultshow_zero_components3.14 + 0i
(3.14,0)defaultshow_zero_components3.14 + 0i
(-3.14,0)defaultdefault-3.14
(-3.14,0)defaultshow_zero_components-3.14 + 0i
(0,25.5)defaultdefault25.5i
(0,25.5)defaultshow_zero_components0 + 25.5i
(0,-25.5)defaultdefault-25.5i
(0,-25.5)defaultshow_zero_components0 - 25.5i
(-3.14,0)defaultshow_zero_components-3.14 + 0i
(0,25.5)defaultdefault25.5i
(0,25.5)defaultshow_zero_components0 + 25.5i
(0,-25.5)defaultdefault-25.5i
(0,-25.5)defaultshow_zero_components0 - 25.5i
(inf,0)defaultdefault∞̃
(-inf,0)defaultdefault∞̃
(25.5,inf)defaultdefault∞̃
- - - - + + + +
ValueResult
1/313
-1/3-13
-345634/1695-3456341695
0/10
-23/1-23
-1/3-13
-345634/1695-3456341695
0/10
-23/1-23
46189/26214446189262144

Polynomials:

- - - - +
TypeResult
Integer2 - 3x + 4x2 + 5x3
Float2.4 - 34.25x + 4.2×10-06x2 - 5.34×10-67x3
Complex(2.4 + 3.25i) - 34.25x + 4.2×10-06ix2 - (5.34×10-67 - 4.65×10-20i)x3
Polynomial(23) - (323)x + (114)x2 + (532)x3
+ + +
TypeResult
Integer2 - 3x + 4x2 + 5x3
Float2.4 - 34.25x + 4.2×10-06x2 - 5.34×10-67x3
Complex(2.4 + 3.25i) - 34.25x + 4.2×10-06ix2 - (5.34×10-67 - 4.65×10-20i)x3
Polynomial(23) - (323)x + (114)x2 + (532)x3
diff --git a/example/formatter_latex_output.cpp b/example/formatter_latex_output.cpp index 5d05b8fe82..1f26dde66b 100644 --- a/example/formatter_latex_output.cpp +++ b/example/formatter_latex_output.cpp @@ -55,7 +55,7 @@ void print(std::ostream& os) printer << std::setprecision(3) << std::scientific << boost::math::tools::multiply_dot << fval << " \\\\\n"; printer.stream() << fval << " & 10 & scientific + latex\\_as\\_text + multiply\\_dot & "; printer << std::setprecision(10) << boost::math::tools::latex_as_text << boost::math::tools::multiply_dot << std::scientific << fval << " \\\\\n" - << boost::math::tools::latex_as_equation << boost::math::tools::multiply_times << std::defaultfloat; + << boost::math::tools::latex_as_equation << boost::math::tools::multiply_times << std::defaultfloat << boost::math::tools::latex_as_equation; // Special Values: fval = 0; @@ -113,7 +113,7 @@ void print(std::ostream& os) printer << boost::math::tools::slanted_i << boost::math::tools::latex_as_text << std::defaultfloat << std::setprecision(12) << cval << " \\\\\n"; printer.stream() << cval << " & 12 & default + upright\\_i + latex\\_as\\_text& "; printer << boost::math::tools::upright_i << std::defaultfloat << std::setprecision(12) << cval << " \\\\\n"; - printer << "\\end{tabular}\n\n" << std::defaultfloat << boost::math::tools::multiply_times; + printer << "\\end{tabular}\n\n" << std::defaultfloat << boost::math::tools::multiply_times << boost::math::tools::latex_as_equation; printer << "\\textbf{Complex Special Values}\n\n\\begin{tabular}{r r r r}\nValue & Precision & Format & Result \\\\\n"; cval = std::complex(0, 0); @@ -182,7 +182,7 @@ void print(std::ostream& os) printer << r << " \\\\\n"; printer.stream() << r << " & latex\\_as\\_text &"; printer << boost::math::tools::latex_as_text << r << " \\\\\n"; - printer << "\\end{tabular}\n\n"; + printer << "\\end{tabular}\n\n" << boost::math::tools::latex_as_equation; printer << "\\textbf{Polynomial Values}\n\n\\begin{tabular}{r r}\nType & Result \\\\\n"; boost::math::tools::polynomial poly1 = { 2, -3, 4, 5 }; diff --git a/example/formatter_latex_output.pdf b/example/formatter_latex_output.pdf index 6b4f896993261344cfea68357d12f5ff7aa4fe79..414e7a56dc48edc79946cdd242a4cd9b948a303a 100644 GIT binary patch delta 4613 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 os) printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); printer << fval << std::endl; fval = 0; - printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); - printer << std::scientific << fval << std::endl; + printer.stream() << std::scientific << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "scientific" << std::setw(20); + printer << fval << std::endl; fval = -fval; - printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); + printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "scientific" << std::setw(20); printer << fval << std::endl << std::defaultfloat; fval = std::numeric_limits::infinity(); printer.stream() << std::setw(20) << fval << std::setw(20) << "default" << std::setw(20) << "default" << std::setw(20); diff --git a/example/formatter_text_output.txt b/example/formatter_text_output.txt index 1aca8be364..046ce52491 100644 --- a/example/formatter_text_output.txt +++ b/example/formatter_text_output.txt @@ -16,8 +16,8 @@ Basic floating point values: -1.234568e-24 3 scientific -1.235×10⁻²⁴ 0 default default 0 -0 default default -0 - 0 default default 0.000×10⁺⁰⁰ - -0.000e+00 default default -0.000×10⁺⁰⁰ + 0.000e+00 default scientific 0.000×10⁺⁰⁰ + -0.000e+00 default scientific -0.000×10⁺⁰⁰ inf default default ∞ -inf default default -∞ nan default default NaN @@ -57,14 +57,14 @@ Complex Zeros: Rationals: Value Result - 1/3 ¹/₃ - -1/3 ⁻¹/₃ - -345634/1695 ⁻³⁴⁵⁶³⁴/₁₆₉₅ - 46189/262144 ⁴⁶¹⁸⁹/₂₆₂₁₄₄ + 1/3 ¹⁄₃ + -1/3 -¹⁄₃ + -345634/1695 -³⁴⁵⁶³⁴⁄₁₆₉₅ + 46189/262144 ⁴⁶¹⁸⁹⁄₂₆₂₁₄₄ Polynomials: Integer: 2 - 3x + 4x² + 5x³ Float: 2.4 - 34.3x + 4.2×10⁻⁰⁶x² - 5.34×10⁻⁶⁶x³ Complex: (2.4 + 3.25i) - 34.3x + 4.2×10⁻⁰⁶ix² - (5.34×10⁻⁶⁶ - 4.65×10⁻²⁰i)x³ -Rational: (²/₃) - (³/₂₃)x + (¹/₁₄)x² + (⁵/₃₂)x³ +Rational: (²⁄₃) - (³⁄₂₃)x + (¹⁄₁₄)x² + (⁵⁄₃₂)x³ diff --git a/include/boost/math/interpolators/cardinal_cubic_b_spline.hpp b/include/boost/math/interpolators/cardinal_cubic_b_spline.hpp index b1bd6666ad..259226e556 100644 --- a/include/boost/math/interpolators/cardinal_cubic_b_spline.hpp +++ b/include/boost/math/interpolators/cardinal_cubic_b_spline.hpp @@ -47,6 +47,12 @@ class cardinal_cubic_b_spline Real double_prime(Real x) const; + template + friend void print(boost::math::tools::basic_numeric_printer_base& os, const cardinal_cubic_b_spline& v) + { + print(os, *v.m_imp); + } + private: std::shared_ptr> m_imp; }; diff --git a/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp b/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp index 4b543641a2..5cbfe98fec 100644 --- a/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp +++ b/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp @@ -14,7 +14,18 @@ #include #include -namespace boost{ namespace math{ namespace interpolators{ namespace detail{ +namespace boost{ namespace math{ + + namespace tools + { + // Forward declaration only, we don't need the formatter's + // definition to be able to define a print function (only if + // we actually use it). + template + class basic_numeric_printer_base; + } + +namespace interpolators{ namespace detail{ template @@ -34,6 +45,76 @@ class cardinal_cubic_b_spline_imp Real double_prime(Real x) const; + template + friend void print(boost::math::tools::basic_numeric_printer_base& os, const cardinal_cubic_b_spline_imp& spline) + { + typedef boost::math::tools::basic_numeric_printer_base printer_t; + // + // scoped_prolog is responsible for writing the prolog/epilog code + // to the stream, if the output format requires it. Not a number in it's own + // right, but an expression, so set the name to the empty string and let each + // individual number style itself. + // + typename printer_t::scoped_prolog prolog(&os, ""); + // + // scoped_parenthesis instructs nested types to wrap themselves in () + // if they are compound rather than atomic number types (for example complex numbers): + // + typename printer_t::scoped_parenthesis param(&os); + // + // scoped_styling: turns off styling for nested types: + // this means that this whole thing will be styled as one + // "number" rather than as several seperate ones. + // Disabled here, as we'll leave the sub-components to style themselves. + // + // typename printer_t::scoped_styling style(&os); + + print(os, spline.m_avg); + for (int64_t k = 0; k < int64_t(spline.m_beta.size()); ++k) + { + if (spline.m_beta[k] < 0) + { + os.stream() << " - "; + print(os, -spline.m_beta[k]); + } + else + { + os.stream() << " + "; + print(os, spline.m_beta[k]); + } + os.print_times(); + os.print_name("B"); + os.print_subscript("3"); + os.stream() << "("; + print(os, spline.m_h_inv); + os.stream() << "("; + os.print_variable('x'); + if (spline.m_a < 0) + { + os.stream() << " + "; + print(os, -spline.m_a); + } + else + { + os.stream() << " - "; + print(os, spline.m_a); + } + os.stream() << ")"; + auto t = 1 - k; + if (t > 0) + { + os.stream() << " + "; + print(os, t); + } + else if (t < 0) + { + os.stream() << " - "; + print(os, -t); + } + os.stream() << ")"; + } + } + private: std::vector m_beta; Real m_h_inv; diff --git a/include/boost/math/tools/formatting.hpp b/include/boost/math/tools/formatting.hpp index de00940722..885ff54a2f 100644 --- a/include/boost/math/tools/formatting.hpp +++ b/include/boost/math/tools/formatting.hpp @@ -35,66 +35,207 @@ namespace boost { template T denominator(const boost::rational& r) { return r.denominator(); } - template ::value || boost::is_class::value> - struct is_integer_like - { - static const bool value = false; - }; - template - struct is_integer_like - { - static const bool value = std::numeric_limits::is_integer; - }; + namespace detail { - template ::value || boost::is_class::value> - struct is_float_like - { - // The point "floats": - static const bool value = false; - }; - template - struct is_float_like - { - // The point "floats": - static const bool value = std::numeric_limits::max_exponent != std::numeric_limits::min_exponent; - }; + template ::value || boost::is_class::value> + struct is_integer_like + { + static const bool value = false; + }; + template + struct is_integer_like + { + static const bool value = std::numeric_limits::is_integer; + }; - template - using real_t = decltype(std::declval().real(std::declval().imag())); - template - using imag_t = decltype(std::declval().imag(std::declval().real())); - template - using polynomial_t = decltype(std::declval()(std::declval()[0])); - template - using polynomial2_t = decltype(std::declval().degree()); - template - using rational_t = decltype(numerator(std::declval()) + denominator(std::declval())); + template ::value || boost::is_class::value> + struct is_float_like + { + // The point "floats": + static const bool value = false; + }; + template + struct is_float_like + { + // The point "floats": + static const bool value = std::numeric_limits::max_exponent != std::numeric_limits::min_exponent; + }; - template - struct is_complex_like - { - static const bool value = boost::is_detected_v && boost::is_detected_v; - }; + template + using real_t = decltype(std::declval().real(std::declval().imag())); + template + using imag_t = decltype(std::declval().imag(std::declval().real())); + template + using polynomial_t = decltype(std::declval()(std::declval()[0])); + template + using polynomial2_t = decltype(std::declval().degree()); + template + using rational_t = decltype(numerator(std::declval()) + denominator(std::declval())); + + template + struct is_complex_like + { + static const bool value = boost::is_detected_v && boost::is_detected_v; + }; - template - struct is_polynomial_like - { - static const bool value = boost::is_detected_v && boost::is_detected_v; - }; + template + struct is_polynomial_like + { + static const bool value = boost::is_detected_v && boost::is_detected_v; + }; - template - struct is_rational_like - { - static const bool value = boost::is_detected_v; - }; + template + struct is_rational_like + { + static const bool value = boost::is_detected_v; + }; - template - struct is_unhandled_type - { - static const bool value = !is_complex_like::value && !is_float_like::value && !is_integer_like::value && !is_polynomial_like::value && !is_rational_like::value; - }; + template + struct is_unhandled_type + { + static const bool value = !is_complex_like::value && !is_float_like::value && !is_integer_like::value && !is_polynomial_like::value && !is_rational_like::value; + }; + + template + typename boost::enable_if_c::type print_unicode_char(std::basic_ostream & os, std::uint32_t code_point) + { + std::uint32_t u[2] = { code_point, 0 }; + boost::u32_to_u8_iterator i(u), j(&u[0] + 1); + while (i != j) + { + os.put(*i++); + } + } + template + typename boost::enable_if_c::type print_unicode_char(std::basic_ostream & os, std::uint32_t code_point) + { + std::uint32_t u[2] = { code_point, 0 }; + boost::u32_to_u16_iterator i(u), j(&u[0] + 1); + while (i != j) + { + os.put(*i++); + } + } + template + typename boost::enable_if_c::type print_unicode_char(std::basic_ostream & os, std::uint32_t code_point) + { + os.put(code_point); + } + + template + void print_unicode_superscript(std::basic_ostream & os, std::basic_string digits) + { + for (std::size_t i = 0; i < digits.size(); ++i) + { + switch (digits[i]) + { + case '0': + print_unicode_char(os, 0x2070); + break; + case '1': + print_unicode_char(os, 0xB9); + break; + case '2': + print_unicode_char(os, 0xB2); + break; + case '3': + print_unicode_char(os, 0xB3); + break; + case '4': + print_unicode_char(os, 0x2074); + break; + case '5': + print_unicode_char(os, 0x2075); + break; + case '6': + print_unicode_char(os, 0x2076); + break; + case '7': + print_unicode_char(os, 0x2076); + break; + case '8': + print_unicode_char(os, 0x2078); + break; + case '9': + print_unicode_char(os, 0x2079); + break; + case '+': + print_unicode_char(os, 0x207A); + break; + case '-': + print_unicode_char(os, 0x207B); + break; + } + } + } + + template + void print_unicode_subscript(std::basic_ostream & os, std::basic_string digits) + { + for (std::size_t i = 0; i < digits.size(); ++i) + { + if ((digits[i] >= '0') && (digits[i] <= '9')) + print_unicode_char(os, 0x2080 + (digits[i] - '0')); + else if (digits[i] == '-') + print_unicode_char(os, 0x208B); + else if (digits[i] == '+') + print_unicode_char(os, 0x208A); + else + os.put(digits[i]); + } + } + + template + typename boost::enable_if_c::type unicode_character_len(const charT* p) + { + std::size_t l = 0; + for (unsigned i = 0; p && p[i]; ++i) + ++l; + boost::u8_to_u32_iterator i(p), j(p + l); + return std::distance(i, j); + } + template + typename boost::enable_if_c::type unicode_character_len(const charT* p) + { + std::size_t l = 0; + for (unsigned i = 0; p && p[i]; ++i) + ++l; + boost::u16_to_u32_iterator i(p), j(p + l); + return std::distance(i, j); + } + template + typename boost::enable_if_c::type unicode_character_len(const charT* p) + { + std::size_t l = 0; + for (unsigned i = 0; p && p[i]; ++i) + ++l; + return l; + } + + template + typename boost::enable_if_c::value, bool>::type iszero(const T& val) + { + return (val.real() == 0) && (val.imag() == 0); + } + template + typename boost::disable_if_c::value, bool>::type iszero(const T& val) + { + return val == 0; + } + template + typename boost::enable_if_c::value, bool>::type isneg(const T& val) + { + return (val.real() < 0); + } + template + typename boost::disable_if_c::value, bool>::type isneg(const T& val) + { + return val < 0; + } + + } // namespace detail - enum output_format_t + enum output_print_t { text_format, docbook_format, @@ -141,148 +282,49 @@ namespace boost { ascii_text_output = 1 }; - template - typename boost::enable_if_c::type write_unicode_char(std::basic_ostream & os, std::uint32_t code_point) - { - std::uint32_t u[2] = { code_point, 0 }; - boost::u32_to_u8_iterator i(u), j(&u[0] + 1); - while (i != j) - { - os.put(*i++); - } - } - template - typename boost::enable_if_c::type write_unicode_char(std::basic_ostream & os, std::uint32_t code_point) - { - std::uint32_t u[2] = { code_point, 0 }; - boost::u32_to_u16_iterator i(u), j(&u[0] + 1); - while (i != j) - { - os.put(*i++); - } - } - template - typename boost::enable_if_c::type write_unicode_char(std::basic_ostream & os, std::uint32_t code_point) - { - os.put(code_point); - } + template > + class basic_numeric_printer; + template + class basic_numeric_printer_base; - template - void write_unicode_superscript(std::basic_ostream & os, std::basic_string digits) + template + typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& value) { - for (std::size_t i = 0; i < digits.size(); ++i) - { - switch (digits[i]) - { - case '0': - write_unicode_char(os, 0x2070); - break; - case '1': - write_unicode_char(os, 0xB9); - break; - case '2': - write_unicode_char(os, 0xB2); - break; - case '3': - write_unicode_char(os, 0xB3); - break; - case '4': - write_unicode_char(os, 0x2074); - break; - case '5': - write_unicode_char(os, 0x2075); - break; - case '6': - write_unicode_char(os, 0x2076); - break; - case '7': - write_unicode_char(os, 0x2076); - break; - case '8': - write_unicode_char(os, 0x2078); - break; - case '9': - write_unicode_char(os, 0x2079); - break; - case '+': - write_unicode_char(os, 0x207A); - break; - case '-': - write_unicode_char(os, 0x207B); - break; - } - } + os.print_integer(value); } - - template - void write_unicode_subscript(std::basic_ostream & os, std::basic_string digits) + template + typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& value) { - for (std::size_t i = 0; i < digits.size(); ++i) - { - if ((digits[i] >= '0') && (digits[i] <= '9')) - write_unicode_char(os, 0x2080 + (digits[i] - '0')); - else if (digits[i] == '-') - write_unicode_char(os, 0x208B); - else if (digits[i] == '+') - write_unicode_char(os, 0x208A); - else - os.put(digits[i]); - } + os.print_float(value); } - - template - typename boost::enable_if_c::type unicode_character_len(const charT* p) + template + typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& value) { - std::size_t l = 0; - for (unsigned i = 0; p && p[i]; ++i) - ++l; - boost::u8_to_u32_iterator i(p), j(p + l); - return std::distance(i, j); + os.print_complex(value); } - template - typename boost::enable_if_c::type unicode_character_len(const charT* p) + template + typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& value) { - std::size_t l = 0; - for (unsigned i = 0; p && p[i]; ++i) - ++l; - boost::u16_to_u32_iterator i(p), j(p + l); - return std::distance(i, j); + os.print_polynomial(value); } - template - typename boost::enable_if_c::type unicode_character_len(const charT* p) + template + typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& value) { - std::size_t l = 0; - for (unsigned i = 0; p && p[i]; ++i) - ++l; - return l; + os.print_rational(value); } - template - typename boost::enable_if_c::value, bool>::type iszero(const T& val) - { - return (val.real() == 0) && (val.imag() == 0); - } - template - typename boost::disable_if_c::value, bool>::type iszero(const T& val) - { - return val == 0; - } - template - typename boost::enable_if_c::value, bool>::type isneg(const T& val) - { - return (val.real() < 0); - } - template - typename boost::disable_if_c::value, bool>::type isneg(const T& val) + template + using printable_t = decltype(print(std::declval(), std::declval())); + + template + struct is_printable { - return val < 0; - } + static const bool value = boost::is_detected_v; + }; - template - class basic_numeric_formatter; template - class basic_numeric_formatter_base + class basic_numeric_printer_base { styling_level_t styling_level; imaginary_i_t i_style; @@ -311,10 +353,10 @@ namespace boost { mantissa = s; } public: - basic_numeric_formatter_base(std::basic_ostream& os) + basic_numeric_printer_base(std::basic_ostream& os) : styling_level(full_styling), i_style(upright_i), multiply_style(multiply_times), m_parenthesis(0), m_show_zero_components(false), m_use_unicode(true), p_stream(&os) {} - basic_numeric_formatter_base(std::basic_ostream& os, const basic_numeric_formatter_base& o) + basic_numeric_printer_base(std::basic_ostream& os, const basic_numeric_printer_base& o) : styling_level(o.styling_level), i_style(o.i_style), multiply_style(o.multiply_style), m_parenthesis(o.m_parenthesis), m_show_zero_components(o.m_show_zero_components), m_use_unicode(o.m_use_unicode), p_stream(&os) {} @@ -374,8 +416,8 @@ namespace boost { struct scoped_parenthesis { - basic_numeric_formatter_base* m_formatter; - scoped_parenthesis(basic_numeric_formatter_base* formatter) : m_formatter(formatter) + basic_numeric_printer_base* m_formatter; + scoped_parenthesis(basic_numeric_printer_base* formatter) : m_formatter(formatter) { m_formatter->parenthesis(1 + m_formatter->parenthesis()); } @@ -384,178 +426,267 @@ namespace boost { m_formatter->parenthesis(m_formatter->parenthesis() - 1); } }; + struct scoped_styling + { + basic_numeric_printer_base* m_formatter; + styling_level_t saved_style; + scoped_styling(basic_numeric_printer_base* formatter) : m_formatter(formatter) + { + saved_style = m_formatter->styling(); + m_formatter->styling(no_styling); + } + ~scoped_styling() + { + m_formatter->styling(saved_style); + } + }; + struct scoped_prolog + { + basic_numeric_printer_base* m_formatter; + scoped_prolog(basic_numeric_printer_base* formatter, const char* number_type) : m_formatter(formatter) + { + m_formatter->print_prolog(number_type); + } + ~scoped_prolog() + { + m_formatter->print_epilog(); + } + }; + + virtual void print_prolog(const char*) {} + virtual void print_epilog() {} + virtual void print_special_character(boost::uint32_t unicode_value) + { + if (use_unicode()) + detail::print_unicode_char(this->stream(), unicode_value); + else + { + switch (unicode_value) + { + case 0x221E: + stream() << "INFINITY"; + break; + case 0xD7: + case 0x22C5: + stream() << "x"; + break; + case 0x2044: + stream() << "/"; + default: + throw std::runtime_error("Unsuported Unicode character encountered in plain text output"); + } + } + } + virtual void print_times() + { + if (multiply() == multiply_times) + print_special_character(0xD7); + else if (multiply() == multiply_dot) + print_special_character(0x22C5); + else + print_name("x"); + } + virtual void print_superscript(const std::basic_string& s) + { + if (use_unicode()) + detail::print_unicode_superscript(stream(), s); + else + stream() << "^" << s; + } + virtual void print_imaginary_unit() + { + if ((use_unicode()) && (this->imaginary_style() == doublestruck_i)) + detail::print_unicode_char(stream(), 0x2148); + else if ((use_unicode()) && (this->imaginary_style() == slanted_i)) + detail::print_unicode_char(stream(), 0x1D456); + else + stream().put(stream().widen('i')); + } + virtual void print_complex_infinity() + { + if (use_unicode()) + { + detail::print_unicode_char(stream(), 0x221E); + } + else + stream() << "COMPLEX INFINITY"; + } + virtual void print_variable(charT c) + { + stream().put(c); + } + virtual void print_name(const std::basic_string& s) + { + stream() << s; + } + virtual void print_fraction(const std::basic_string& s1, const std::basic_string& s2) + { + print_superscript(s1); + print_special_character(0x2044); + print_subscript(s2); + } + virtual void print_subscript(const std::basic_string& s) + { + detail::print_unicode_subscript(stream(), s); + } template - static std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) + void print_integer(const Integer& i) { - return os << i; + scoped_prolog s(this, "integer"); + this->stream() << i; } template - std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) + void print_float(const Float& f) { - if ((std::isinf)(f)) + using std::isinf; + using std::isnan; + + scoped_prolog s(this, "float"); + + if ((isinf)(f)) { if (f < 0) - os << "-"; - if (use_unicode()) - { - write_unicode_char(os, 0x221E); - } - else - os << "INFINITY"; - return os; + stream() << "-"; + print_special_character(0x221E); + return; } - if ((std::isnan)(f)) + if ((isnan)(f)) { - return os << "NaN"; + print_name("NaN"); + return; } std::basic_string mantissa, exponent; - decompose_float(os, f, mantissa, exponent); - os << mantissa; + decompose_float(stream(), f, mantissa, exponent); + stream() << mantissa; if (exponent.size()) { - if (use_unicode()) - { - if (multiply() == multiply_times) - write_unicode_char(os, 0xD7); - else if (multiply() == multiply_dot) - write_unicode_char(os, 0x22C5); - else - os << "x"; - os << "10"; - write_unicode_superscript(os, exponent); - } - else - os << "x10^" << exponent; + print_times(); + stream() << "10"; + print_superscript(exponent); } - return os; + return; } template - std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) + void print_complex(const Complex& f) { - if ((std::isnan)(f.real()) || (std::isnan)(f.imag())) + using std::isnan; + using std::isinf; + + scoped_prolog s1(this, "complex"); + scoped_styling s2(this); + + if ((isnan)(f.real()) || (isnan)(f.imag())) { - return os << "NaN"; + print_name("NaN"); + return; } - if ((std::isinf)(f.real()) || (std::isinf)(f.imag())) + else if ((isinf)(f.real()) || (isinf)(f.imag())) { - if (use_unicode()) - { - write_unicode_char(os, 0x221E); - // Combining characters not supported in plain text? - //write_unicode_char(os, 0x0303); - } - else - os << "COMPLEX INFINITY"; - return os; + print_complex_infinity(); + return; } - if ((!this->show_zero_components()) && (f.imag() == 0)) + else if ((!this->show_zero_components()) && (f.imag() == 0)) { - format_float(os, f.real()); - return os; + this->print_float(f.real()); + return; } else if ((!this->show_zero_components()) && (f.real() == 0)) { - format_float(os, f.imag()); + this->print_float(f.imag()); } else { if (parenthesis()) - os << "("; - format_float(os, f.real()); + this->stream() << "("; + this->print_float(f.real()); typename Complex::value_type i(f.imag()); bool isneg = i < 0; if (isneg) { i = -i; - os << " - "; + this->stream() << " - "; } else - os << " + "; - format_float(os, i); + this->stream() << " + "; + this->print_float(i); } - if ((use_unicode()) && (this->imaginary_style() == doublestruck_i)) - write_unicode_char(os, 0x2148); - else if ((use_unicode()) && (this->imaginary_style() == slanted_i)) - write_unicode_char(os, 0x1D456); - else - os.put(os.widen('i')); + print_imaginary_unit(); if (parenthesis() && !((!this->show_zero_components()) && (f.real() == 0))) - os << ")"; - return os; + this->stream() << ")"; + return; } template - std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f) + void print_polynomial(const Polynomial& f) { - scoped_parenthesis scoped(this); + scoped_prolog s1(this, "polynomial"); + scoped_parenthesis s2(this); + scoped_styling s3(this); + bool have_first = false; for (unsigned i = 0; i <= f.degree(); ++i) { auto coef = f[i]; - if (show_zero_components() || (!iszero(coef))) + if (show_zero_components() || (!detail::iszero(coef))) { if (have_first) { - if (!isneg(coef)) - os << " + "; + if (!detail::isneg(coef)) + stream() << " + "; else - os << " - "; + stream() << " - "; } - print(*this, os, !isneg(coef) ? coef : -coef); + print(*this, !detail::isneg(coef) ? coef : -coef); have_first = true; if (i) { - os << "x"; + print_variable('x'); if (i > 1) { - if (use_unicode()) - { - std::basic_stringstream ss; - ss << i; - std::basic_string s = ss.str(); - write_unicode_superscript(os, s); - } - else - os << "^" << i; + std::basic_stringstream ss; + ss << i; + std::basic_string s = ss.str(); + print_superscript(s); } } } } - return os; } template - std::basic_ostream& format_rational(std::basic_ostream& os, const Rational& rat) + void print_rational(const Rational& rat) { + scoped_prolog s1(this, "rational"); + scoped_styling s3(this); + auto num = numerator(rat); auto denom = denominator(rat); - - if (num == 0) - { - return os << "0"; - } - else if (denom == 1) + if ((num == 0) || (denom == 1)) { - format_integer(os, num); - return os; + print_integer(num); + return; } else { + if (detail::isneg(num)) + { + stream() << "-"; + num = -num; + } if (parenthesis()) - os << "("; - auto s1 = part_as_string(*this, num); - auto s2 = part_as_string(*this, denom); - - write_unicode_superscript(os, s1); - os << "/"; - write_unicode_subscript(os, s2); + stream() << "("; + { + scoped_parenthesis s2(this); + + auto str1 = part_as_string(*this, num); + auto str2 = part_as_string(*this, denom); + print_fraction(str1, str2); + } if (parenthesis()) - os << ")"; + stream() << ")"; } - - return os; } bool latex_as_equation()const { return false; } @@ -569,771 +700,285 @@ namespace boost { ss.imbue(printer.stream().getloc()); ss.width(0); Printer fmt(ss, printer); - fmt.print(fmt, fmt.stream(), value); + print(fmt, value); return ss.str(); } + }; - template - static typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + template + class basic_numeric_printer : public basic_numeric_printer_base + { + public: + basic_numeric_printer(std::basic_ostream& os) : basic_numeric_printer_base(os) {} + basic_numeric_printer(std::basic_ostream& os, basic_numeric_printer const& fmt) : basic_numeric_printer_base(os, fmt) {} + }; + + template + class basic_numeric_printer : public basic_numeric_printer_base + { + friend class basic_numeric_printer_base; + bool need_epilog; + public: + basic_numeric_printer(std::basic_ostream& os) : basic_numeric_printer_base(os), need_epilog(true) {} + basic_numeric_printer(std::basic_ostream& os, basic_numeric_printer const& fmt) : basic_numeric_printer_base(os, fmt), need_epilog(true) {} + + virtual void print_prolog(const char* name) { - printer.format_integer(os, value); + if (name && *name) + { + if (this->styling() >= minimal_styling) + this->stream() << ""; + if (this->styling() > minimal_styling) + this->stream() << ""; + } + else + need_epilog = false; } - template - static typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + virtual void print_epilog() { - printer.format_float(os, value); + if (need_epilog) + { + if (this->styling() > minimal_styling) + this->stream() << ""; + if (this->styling() >= minimal_styling) + this->stream() << ""; + } } - template - static typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + + virtual void print_special_character(boost::uint32_t unicode_value) { - printer.format_complex(os, value); + std::ios_base::fmtflags f = this->stream().flags() & std::ios_base::basefield; + this->stream() << std::hex << "&#x" << unicode_value << ";"; + this->stream().setf(f, std::ios_base::basefield); + BOOST_ASSERT(f == (this->stream().flags() & std::ios_base::basefield)); } - template - static typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + virtual void print_superscript(const std::basic_string& s) { - printer.format_polynomial(os, value); + this->stream() << "" << s << ""; } - template - static typename boost::enable_if_c::value>::type print(Printer& printer, std::basic_ostream& os, const Value& value) + virtual void print_imaginary_unit() + { + if (this->styling() >= minimal_styling) + this->stream() << ""; + if (this->imaginary_style() == doublestruck_i) + this->stream() << "ⅈ"; + else if (this->imaginary_style() == slanted_i) + this->stream() << "i"; + else + this->stream().put(this->stream().widen('i')); + if (this->styling() >= minimal_styling) + this->stream() << ""; + } + virtual void print_complex_infinity() + { + this->stream() << "∞̃"; + } + virtual void print_variable(charT c) + { + this->stream() << ""; + this->stream().put(c); + this->stream() << ""; + } + virtual void print_subscript(const std::basic_string& s) { - printer.format_rational(os, value); + this->stream() << "" << s << ""; } }; template - class basic_numeric_formatter : public basic_numeric_formatter_base + class basic_numeric_printer : public basic_numeric_printer_base { + friend class basic_numeric_printer_base; + + bool is_latex_as_equation; + unsigned inside_equation; + public: - basic_numeric_formatter(std::basic_ostream& os) : basic_numeric_formatter_base(os) {} - }; + basic_numeric_printer(std::basic_ostream& os) : basic_numeric_printer_base(os), is_latex_as_equation(true), inside_equation(false) {} + basic_numeric_printer(std::basic_ostream& os, basic_numeric_printer const& fmt) : basic_numeric_printer_base(os, fmt), is_latex_as_equation(fmt.is_latex_as_equation), inside_equation(fmt.inside_equation) {} - template - class basic_numeric_formatter : public basic_numeric_formatter_base - { - friend class basic_numeric_formatter_base; - protected: - basic_numeric_formatter(std::basic_ostream& os) : basic_numeric_formatter_base(os) {} + void latex_as_equation(bool b) { is_latex_as_equation = b; } + bool latex_as_equation()const { return is_latex_as_equation; } - template - std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) + virtual void print_prolog(const char*) { - if (this->styling() >= minimal_styling) - os << ""; - if (this->styling() > minimal_styling) - os << ""; - os << i; - if (this->styling() > minimal_styling) - os << ""; - if (this->styling() >= minimal_styling) - os << ""; - return os; + if (latex_as_equation()) + { + if(inside_equation++ == 0) + this->stream().put(this->stream().widen('$')); + } } - template - std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) + virtual void print_epilog() { - if (this->styling() >= minimal_styling) - os << ""; - if (this->styling() > minimal_styling) - os << ""; - - if ((std::isinf)(f)) + if (latex_as_equation()) { - if (f < 0) - os.put(os.widen('-')); - os << "∞"; + if(--inside_equation == 0) + this->stream().put(this->stream().widen('$')); } - else if ((std::isnan(f))) + } + + virtual void print_special_character(boost::uint32_t unicode_value) + { + if (!inside_equation) + this->stream() << "$"; + switch (unicode_value) { - os << "NaN"; - } - else - { - std::basic_string mantissa, exponent; - basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); - - if (exponent.size()) - { - os << mantissa; - if (this->multiply() == multiply_x) - os.put(os.widen('x')); - else if (this->multiply() == multiply_dot) - os << "⋅"; - else - os << "×"; - os << "10" << exponent << ""; - } - else - os << mantissa; + case 0x221E: + this->stream() << "\\infty"; + break; + case 0xD7: + this->stream() << "\\times"; + break; + case 0x22C5: + this->stream() << "\\cdot"; + break; + default: + throw std::runtime_error("Unsuported Unicode character encountered in LaTex output"); } - - if (this->styling() > minimal_styling) - os << ""; - if (this->styling() >= minimal_styling) - os << ""; - - return os; + if (!inside_equation) + this->stream() << "$"; } - template - std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) + virtual void print_superscript(const std::basic_string& s) { - if (this->styling() >= minimal_styling) - os << ""; - if (this->styling() > minimal_styling) - os << ""; - - if ((std::isnan(f.real())) || (std::isnan)(f.imag())) - { - os << "NaN"; - } - else if ((std::isinf)(f.real()) || (std::isinf)(f.imag())) - { - os << "∞̃"; - } + if (inside_equation) + this->stream() << "^{" << s << "}"; else - { - styling_level_t saved_style = this->styling(); - this->styling(no_styling); - - bool need_i = true; - bool need_paren = false; - - if (!this->show_zero_components() && (f.imag() == 0)) - { - format_float(os, f.real()); - need_i = false; - } - else if (!this->show_zero_components() && (f.real() == 0)) - { - format_float(os, f.imag()); - } - else - { - if (this->parenthesis()) - { - os << "("; - need_paren = true; - } - format_float(os, f.real()); - typename Complex::value_type i(f.imag()); - bool isneg = i < 0; - if (isneg) - { - i = -i; - os << " - "; - } - else - os << " + "; - format_float(os, i); - } - if (need_i) - { - if (saved_style >= minimal_styling) - os << ""; - if (this->imaginary_style() == doublestruck_i) - os << "ⅈ"; - else if (this->imaginary_style() == slanted_i) - os << "i"; - else - os.put(os.widen('i')); - if (saved_style >= minimal_styling) - os << ""; - } - if (need_paren) - os << ")"; - this->styling(saved_style); - } - - if (this->styling() > minimal_styling) - os << ""; - if (this->styling() >= minimal_styling) - os << ""; - - return os; + this->stream() << "\\textsuperscript{" << s << "}"; } - template - std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f) + virtual void print_imaginary_unit() { - typename basic_numeric_formatter_base::scoped_parenthesis scoped(this); - - if (this->styling() >= minimal_styling) - os << ""; - if (this->styling() > minimal_styling) - os << ""; - - bool have_first = false; - for (unsigned i = 0; i <= f.degree(); ++i) - { - auto coef = f[i]; - if (this->show_zero_components() || !iszero(coef)) - { - if (have_first) - { - if (!isneg(coef)) - os << " + "; - else - os << " - "; - } - basic_numeric_formatter_base::print(*this, os, !isneg(coef) ? coef : -coef); - have_first = true; - if (i) - { - os << "x"; - if (i > 1) - { - os << "" << i << ""; - } - } - } - } - - if (this->styling() > minimal_styling) - os << ""; - if (this->styling() >= minimal_styling) - os << ""; - - return os; - } - template - std::basic_ostream& format_rational(std::basic_ostream& os, const Rational& rat) - { - auto num = numerator(rat); - auto denom = denominator(rat); - - if ((denom == 1) || (num == 0)) - { - format_integer(os, num); - } + if (this->imaginary_style() == doublestruck_i) + BOOST_THROW_EXCEPTION(std::runtime_error("Doublestruck imaginary i not implemented for LaTex output.")); + else if ((this->imaginary_style() == upright_i) && inside_equation) + this->stream() << "\\mathrm{i}"; + else if ((this->imaginary_style() == slanted_i) && !inside_equation) + this->stream() << "\\textit{i}"; else - { - if (this->styling() >= minimal_styling) - os << ""; - if (this->styling() > minimal_styling) - os << ""; - - if (this->parenthesis()) - os << "("; - - os << "" << num << ""; - os << "⁄" << denom << ""; - - if (this->parenthesis()) - os << ")"; - - if (this->styling() > minimal_styling) - os << ""; - if (this->styling() >= minimal_styling) - os << ""; - } - return os; + this->stream().put(this->stream().widen('i')); } - }; - - template - class basic_numeric_formatter : public basic_numeric_formatter_base - { - friend class basic_numeric_formatter_base; - - bool is_latex_as_equation; - bool inside_equation; - - public: - basic_numeric_formatter(std::basic_ostream& os) : basic_numeric_formatter_base(os), is_latex_as_equation(true), inside_equation(false) {} - void latex_as_equation(bool b) { is_latex_as_equation = b; } - bool latex_as_equation()const { return is_latex_as_equation; } - - template - std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) + virtual void print_complex_infinity() { - if (!inside_equation && latex_as_equation()) - os.put(os.widen('$')); - basic_numeric_formatter_base::format_integer(os, i); - if (!inside_equation && latex_as_equation()) - os.put(os.widen('$')); - return os; + if (!inside_equation) + this->stream().put(this->stream().widen('$')); + this->stream() << "\\tilde{\\infty}"; + if (!inside_equation) + this->stream().put(this->stream().widen('$')); } - - template - std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) + virtual void print_variable(charT c) { - std::basic_string mantissa, exponent; - basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); - - if ((std::isinf)(f)) - { - if (!inside_equation) - os.put(os.widen('$')); - if (f < 0) - os.put(os.widen('-')); - os << "\\infty"; - if (!inside_equation) - os.put(os.widen('$')); - return os; - } - else if ((std::isnan)(f)) - { - if (inside_equation) - os << "\\mathrm{NaN}"; - else - os << "NaN"; - return os; - } - - if (latex_as_equation()) - { - if (!inside_equation) - os.put(os.widen('$')); - os << mantissa; - if (exponent.size()) - { - if (this->multiply() == multiply_x) - os << "\\mathrm{x}"; - else if (this->multiply() == multiply_dot) - os << "\\cdot"; - else - os << "\\times"; - os << " 10^{" << exponent << "}"; - } - if (!inside_equation) - os.put(os.widen('$')); - } + if (inside_equation) + this->stream().put(c); else { - os << mantissa; - if (exponent.size()) - { - if (this->multiply() == multiply_x) - os << "x"; - else if (this->multiply() == multiply_dot) - os << "$\\cdot$"; - else - os << "$\\times$"; - os << "10\\textsuperscript{" << exponent << "}"; - } + this->stream() << "\\textit{"; + this->stream().put(c); + this->stream() << "}"; } - return os; } - template - std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) + virtual void print_name(const std::basic_string& s) { - if (!inside_equation && latex_as_equation()) - os.put(os.widen('$')); - bool saved_inside_equation = inside_equation; - inside_equation = latex_as_equation(); - if ((std::isnan(f.real())) || (std::isnan)(f.imag())) - { - if (inside_equation) - os << "\\mathrm{NaN}"; - else - os << "NaN"; - return os; - } - else if ((std::isinf)(f.real()) || (std::isinf)(f.imag())) - { - if (!inside_equation) - os.put(os.widen('$')); - os << "\\tilde{\\infty}"; - if (!inside_equation) - os.put(os.widen('$')); - return os; - } + if (!inside_equation) + this->stream() << s; else { - bool need_i = true; - bool need_paren = false; - - if (!this->show_zero_components() && (f.imag() == 0)) - { - format_float(os, f.real()); - need_i = false; - } - else if (!this->show_zero_components() && (f.real() == 0)) - { - format_float(os, f.imag()); - } - else - { - if (this->parenthesis()) - { - os << "("; - need_paren = true; - } - format_float(os, f.real()); - typename Complex::value_type i(f.imag()); - bool isneg = i < 0; - if (isneg) - { - i = -i; - os << " - "; - } - else - os << " + "; - format_float(os, i); - } - if (need_i) - { - if (this->imaginary_style() == doublestruck_i) - BOOST_THROW_EXCEPTION(std::runtime_error("Doublestruck imaginary i not implemented for LaTex output.")); - else if ((this->imaginary_style() == upright_i) && latex_as_equation()) - os << "\\mathrm{i}"; - else if ((this->imaginary_style() == slanted_i) && !latex_as_equation()) - os << "\\textit{i}"; - else - os.put(os.widen('i')); - } - if (need_paren) - os << ")"; + this->stream() << "\\mathrm{"; + this->stream() << s; + this->stream() << "}"; } - inside_equation = saved_inside_equation; - if (!inside_equation && latex_as_equation()) - os.put(os.widen('$')); - - return os; } - template - std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f) + virtual void print_fraction(const std::basic_string& s1, const std::basic_string& s2) { - typename basic_numeric_formatter_base::scoped_parenthesis scoped(this); - - if (!inside_equation && latex_as_equation()) - os.put(os.widen('$')); - bool saved_inside_equation = inside_equation; - inside_equation = latex_as_equation(); - bool have_first = false; - - for (unsigned i = 0; i <= f.degree(); ++i) + if (inside_equation) { - auto coef = f[i]; - if (this->show_zero_components() || !iszero(coef)) - { - if (have_first) - { - if (!isneg(coef)) - os << " + "; - else - os << " - "; - } - basic_numeric_formatter_base::print(*this, os, !isneg(coef) ? coef : -coef); - have_first = true; - if (i) - { - if (inside_equation) - os << "x"; - else - os << "\\textit{x}"; - if (i > 1) - { - if (inside_equation) - os << "^{" << i << "}"; - else - os << "\\textsuperscript{" << i << "}"; - } - } - } - } - - inside_equation = saved_inside_equation; - if (!inside_equation && latex_as_equation()) - os.put(os.widen('$')); - - return os; - } - template - std::basic_ostream& format_rational(std::basic_ostream& os, const Rational& rat) - { - auto num = numerator(rat); - auto denom = denominator(rat); - - - if ((num == 0) || (denom == 1)) - { - format_integer(os, num); + this->stream() << "\\frac{" << s1 << "}{" << s2 << "}"; } else { - if (!inside_equation && latex_as_equation()) - os.put(os.widen('$')); - if (this->parenthesis()) - os << "("; - - if (latex_as_equation()) - { - if(num < 0) - os << "-\\frac{" << -num << "}{" << denom << "}"; - else - os << "\\frac{" << num << "}{" << denom << "}"; - } - else - os << "\\textsuperscript{ " << num << "}/\\textsubscript{" << denom << "}"; - - if (this->parenthesis()) - os << ")"; - if (!inside_equation && latex_as_equation()) - os.put(os.widen('$')); + this->print_superscript(s1); + this->stream() << "/"; + this->print_subscript(s2); } - - return os; } + virtual void print_subscript(const std::basic_string& s) + { + if (inside_equation) + this->stream() << "_{" << s << "}"; + else + this->stream() << "\\textsubscript{" << s << "}"; + } + }; template - class basic_numeric_formatter : public basic_numeric_formatter_base + class basic_numeric_printer : public basic_numeric_printer_base { - friend class basic_numeric_formatter_base; - protected: - basic_numeric_formatter(std::basic_ostream& os) : basic_numeric_formatter_base(os) {} + friend class basic_numeric_printer_base; + bool need_epilog; + public: + basic_numeric_printer(std::basic_ostream& os) : basic_numeric_printer_base(os), need_epilog(true){} + basic_numeric_printer(std::basic_ostream& os, basic_numeric_printer const & fmt) : basic_numeric_printer_base(os, fmt), need_epilog(true){} - template - std::basic_ostream& format_integer(std::basic_ostream& os, const Integer& i) - { - if (this->styling() >= minimal_styling) - os << ""; - if (this->styling() > minimal_styling) - os << ""; - os << i; - if (this->styling() > minimal_styling) - os << ""; - if (this->styling() >= minimal_styling) - os << ""; - return os; - } - template - std::basic_ostream& format_float(std::basic_ostream& os, const Float& f) + virtual void print_prolog(const char* name) { - if (this->styling() >= minimal_styling) - os << ""; - if (this->styling() > minimal_styling) - os << ""; - - if ((std::isinf)(f)) - { - if (f < 0) - os.put(os.widen('-')); - os << "∞"; - } - else if ((std::isnan(f))) + if (name && *name) { - os << "NaN"; + if (this->styling() >= minimal_styling) + this->stream() << ""; + if (this->styling() > minimal_styling) + this->stream() << ""; } else + need_epilog = false; + } + virtual void print_epilog() + { + if (need_epilog) { - std::basic_string mantissa, exponent; - basic_numeric_formatter_base::decompose_float(os, f, mantissa, exponent); - - if (exponent.size()) - { - os << mantissa; - if (this->multiply() == multiply_x) - os.put(os.widen('x')); - else if (this->multiply() == multiply_dot) - os << "⋅"; - else - os << "×"; - os << "10" << exponent << ""; - } - else - os << mantissa; + if (this->styling() > minimal_styling) + this->stream() << ""; + if (this->styling() >= minimal_styling) + this->stream() << ""; } + } - if (this->styling() > minimal_styling) - os << ""; - if (this->styling() >= minimal_styling) - os << ""; - - return os; + virtual void print_special_character(boost::uint32_t unicode_value) + { + std::ios_base::fmtflags f = this->stream().flags() & std::ios_base::basefield; + this->stream() << std::hex << "&#x" << unicode_value << ";"; + this->stream().setf(f, std::ios_base::basefield); + BOOST_ASSERT(f == (this->stream().flags() & std::ios_base::basefield)); } - template - std::basic_ostream& format_complex(std::basic_ostream& os, const Complex& f) + virtual void print_superscript(const std::basic_string& s) + { + this->stream() << "" << s << ""; + } + virtual void print_imaginary_unit() { if (this->styling() >= minimal_styling) - os << ""; - if (this->styling() > minimal_styling) - os << ""; - - if ((std::isnan(f.real())) || (std::isnan)(f.imag())) - { - os << "NaN"; - } - else if ((std::isinf)(f.real()) || (std::isinf)(f.imag())) - { - os << "∞̃"; - } + this->stream() << ""; + if (this->imaginary_style() == doublestruck_i) + this->stream() << "ⅈ"; + else if (this->imaginary_style() == slanted_i) + this->stream() << "i"; else - { - styling_level_t saved_style = this->styling(); - this->styling(no_styling); - - bool need_i = true; - bool need_paren = false; - - if(!this->show_zero_components() && (f.imag() == 0)) - { - format_float(os, f.real()); - need_i = false; - } - else if (!this->show_zero_components() && (f.real() == 0)) - { - format_float(os, f.imag()); - } - else - { - if (this->parenthesis()) - { - os << "("; - need_paren = true; - } - format_float(os, f.real()); - typename Complex::value_type i(f.imag()); - bool isneg = i < 0; - if (isneg) - { - i = -i; - os << " - "; - } - else - os << " + "; - format_float(os, i); - } - if (need_i) - { - if (saved_style >= minimal_styling) - os << ""; - if (this->imaginary_style() == doublestruck_i) - os << "ⅈ"; - else if (this->imaginary_style() == slanted_i) - os << "i"; - else - os.put(os.widen('i')); - if (saved_style >= minimal_styling) - os << ""; - } - if (need_paren) - os << ")"; - this->styling(saved_style); - } - - if (this->styling() > minimal_styling) - os << ""; + this->stream().put(this->stream().widen('i')); if (this->styling() >= minimal_styling) - os << ""; - - return os; + this->stream() << ""; } - template - std::basic_ostream& format_polynomial(std::basic_ostream& os, const Polynomial& f) + virtual void print_complex_infinity() { - typename basic_numeric_formatter_base::scoped_parenthesis scoped(this); - - if (this->styling() >= minimal_styling) - os << ""; - if (this->styling() > minimal_styling) - os << ""; - - styling_level_t saved_style = this->styling(); - this->styling(no_styling); - - bool have_first = false; - for (unsigned i = 0; i <= f.degree(); ++i) - { - auto coef = f[i]; - if (this->show_zero_components() || !iszero(coef)) - { - if (have_first) - { - if (!isneg(coef)) - os << " + "; - else - os << " - "; - } - basic_numeric_formatter_base::print(*this, os, !isneg(coef) ? coef : -coef); - have_first = true; - if (i) - { - os << "x"; - if (i > 1) - { - os << "" << i << ""; - } - } - } - } - - this->styling(saved_style); - - if (this->styling() > minimal_styling) - os << ""; - if (this->styling() >= minimal_styling) - os << ""; - - return os; + this->stream() << "∞̃"; } - template - std::basic_ostream& format_rational(std::basic_ostream& os, const Rational& rat) + virtual void print_variable(charT c) { - auto num = numerator(rat); - auto denom = denominator(rat); - - if ((num == 0) || (denom == 1)) - { - format_integer(os, num); - } - else - { - if (this->styling() >= minimal_styling) - os << ""; - if (this->styling() > minimal_styling) - os << ""; - - if (this->parenthesis()) - os << "("; - os << "" << num << ""; - os << "⁄" << denom << ""; - - if (this->parenthesis()) - os << ")"; - - if (this->styling() > minimal_styling) - os << ""; - if (this->styling() >= minimal_styling) - os << ""; - } - - return os; + this->stream() << ""; + this->stream().put(c); + this->stream() << ""; + } + virtual void print_subscript(const std::basic_string& s) + { + this->stream() << "" << s << ""; } - }; - - - template > - class basic_numeric_printer : private basic_numeric_formatter - { - typedef basic_numeric_formatter base_type; - - public: - basic_numeric_printer(std::basic_ostream& os) : basic_numeric_formatter(os) {} - basic_numeric_printer(std::basic_ostream& os, const basic_numeric_printer& printer) - : basic_numeric_formatter(os) - { - styling(printer.styling()); - imaginary_style(printer.imaginary_style()); - latex_as_equation(printer.latex_as_equation()); - parenthesis(printer.parenthesis()); - multiply(printer.multiply()); - show_zero_components(printer.show_zero_components()); - } - - using base_type::format_integer; - using base_type::format_float; - using base_type::format_complex; - using base_type::format_polynomial; - using base_type::format_rational; - using base_type::styling; - using base_type::imaginary_style; - using base_type::latex_as_equation; - using base_type::parenthesis; - using base_type::multiply; - using base_type::show_zero_components; - using base_type::use_unicode; - using base_type::print; - using base_type::stream; }; typedef basic_numeric_printer<> text_printer; @@ -1346,14 +991,14 @@ namespace boost { typedef basic_numeric_printer wlatex_printer; typedef basic_numeric_printer whtml_printer; - template - typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Integer& i) + template + typename boost::enable_if_c, Value>::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Value& i) { std::size_t w = (std::size_t)os.stream().width(); if (w) { - std::basic_string s = basic_numeric_formatter_base::part_as_string(os, i); - std::size_t len = unicode_character_len(s.c_str()); + std::basic_string s = basic_numeric_printer_base::part_as_string(os, i); + std::size_t len = detail::unicode_character_len(s.c_str()); if (len < w) { auto pos = os.stream().flags() & std::ios_base::adjustfield; @@ -1362,118 +1007,55 @@ namespace boost { os.stream() << s; } else - os.print(os, os.stream(), i); - return os; - } -#if 0 - template - typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Integer& i) - { - if (os.stream().width()) - { - std::basic_stringstream ss; - ss.copyfmt(os.stream()); - ss.imbue(os.stream().getloc()); - ss.width(0); - basic_numeric_printer fmt(ss, os); - fmt.format_integer(fmt.stream(), i); - os.stream() << ss.str(); - } - else - os.format_integer(os.stream(), i); - return os; - } - - template - typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Float& f) - { - if (os.stream().width()) - { - std::basic_stringstream ss; - ss.copyfmt(os.stream()); - ss.imbue(os.stream().getloc()); - ss.width(0); - basic_numeric_printer fmt(ss, os); - fmt.format_float(fmt.stream(), f); - os.stream() << ss.str(); - } - else - os.format_float(os.stream(), f); + print(os, i); return os; } - template - typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Complex& f) + template + typename boost::disable_if_c, T>::value, basic_numeric_printer&>::type operator<< (basic_numeric_printer& os, const T& unhandled) { - if (os.stream().width()) - { - std::basic_stringstream ss; - ss.copyfmt(os.stream()); - ss.imbue(os.stream().getloc()); - ss.width(0); - basic_numeric_printer fmt(ss, os); - fmt.format_complex(fmt.stream(), f); - os.stream() << ss.str(); - } - else - os.format_complex(os.stream(), f); - return os; - } - - template - typename boost::enable_if_c::value, basic_numeric_printer&>::type operator << (basic_numeric_printer& os, const Polynomial& f) - { - if (os.stream().width()) - { - std::basic_stringstream ss; - ss.copyfmt(os.stream()); - ss.imbue(os.stream().getloc()); - ss.width(0); - basic_numeric_printer fmt(ss, os); - fmt.format_polynomial(fmt.stream(), f); - os.stream() << ss.str(); - } - else - os.format_polynomial(os.stream(), f); + os.stream() << unhandled; return os; } -#endif - template + // + // Manipulators: + // + template basic_numeric_printer& operator << (basic_numeric_printer& os, styling_level_t s) { os.styling(s); return os; } - template + template basic_numeric_printer& operator << (basic_numeric_printer& os, multiplyer_t s) { os.multiply(s); return os; } - template + template basic_numeric_printer& operator << (basic_numeric_printer& os, latex_t l) { os.latex_as_equation(l == latex_as_equation); return os; } - template + template basic_numeric_printer& operator << (basic_numeric_printer& os, const imaginary_i_t i) { os.imaginary_style(i); return os; } - template + template basic_numeric_printer& operator << (basic_numeric_printer& os, const zero_component_t z) { os.show_zero_components(z == ::boost::math::tools::show_zero_components ? true : false); return os; } - template + template basic_numeric_printer& operator << (basic_numeric_printer& os, const unicode_text_t z) { os.use_unicode(z == ::boost::math::tools::unicode_text_output ? true : false); @@ -1483,25 +1065,19 @@ namespace boost { // // Output formatters: // - template - typename boost::enable_if_c::value, basic_numeric_printer&>::type operator<< (basic_numeric_printer& os, const T& unhandled) - { - os.stream() << unhandled; - return os; - } - template + template basic_numeric_printer& operator<< (basic_numeric_printer& os, std::basic_ostream& (*pf)(std::basic_ostream&)) { pf(os.stream()); return os; } - template + template basic_numeric_printer& operator<< (basic_numeric_printer& os, std::basic_ios& (*pf)(std::basic_ios&)) { pf(os.stream()); return os; } - template + template basic_numeric_printer& operator<< (basic_numeric_printer& os, std::ios_base& (*pf)(std::ios_base&)) { pf(os.stream()); From 6ee72c7371c5a1e6be7cd123133d9c55cec4cb5e Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Wed, 27 Nov 2019 19:48:41 +0000 Subject: [PATCH 10/14] Formatter: simplify API, add interval support. --- doc/fp_utilities/formatting.qbk | 72 ++--- example/formatter_interval_output.cpp | 39 +++ example/formatter_latex_output.pdf | Bin 80655 -> 80655 bytes include/boost/math/tools/formatting.hpp | 404 +++++++++++++----------- test/.temps/boost-no-inspect | 0 5 files changed, 287 insertions(+), 228 deletions(-) create mode 100644 example/formatter_interval_output.cpp delete mode 100644 test/.temps/boost-no-inspect diff --git a/doc/fp_utilities/formatting.qbk b/doc/fp_utilities/formatting.qbk index 555db1b979..9455d3aa5f 100644 --- a/doc/fp_utilities/formatting.qbk +++ b/doc/fp_utilities/formatting.qbk @@ -31,7 +31,7 @@ The class `basic_numeric_printer` provides "pretty printing" capabilities for nu Its use mirrors that of the `iostream` library, but fulfills a very different role: There is no "input streaming" and the output is human readable--and not intended for machine consumption. -Supported types are integers, floating point numbers, complex numbers, rationals, polynomials, intervals(TODO) +Supported types are integers, floating point numbers, complex numbers, rationals, polynomials, intervals and containers thereof(TODO). Other types, plus all the standard library manipulators can also be streamed to `basic_numeric_printer` but are simply forwarded to the underlying stream. @@ -180,6 +180,12 @@ The following section (using Docbook output format) illustrates how each of the [polynomial_formatting_examples] +[table:intervals Interval Values +[[Value][Result]] +[[`boost::math::constants::pi()`]['''3.1415926535897932384626433832795028841971693993751 ± 5.3455294201843912922810729343029637576303937602101×10-51''']] +[[Polynomial of `mpfi_float_50`]['''2 - (1.6666666666666666666666666666666666666666666666667 ± 2.672764710092195646140536467151481878815196880105×10-51)x + (0.66666666666666666666666666666666666666666666666666 ± 1.3363823550460978230702682335757409394075984400525×10-51)x2 + 15x3 + 11x4 - (14.666666666666666666666666666666666666666666666667 ± 2.138211768073756516912429173721185503052157504084×10-50)x5 - (5.6666666666666666666666666666666666666666666666667 ± 1.069105884036878258456214586860592751526078752042×10-50)x6 + (9.3333333333333333333333333333333333333333333333334 ± 2.138211768073756516912429173721185503052157504084×10-50)x7 + 20x8''']] +] + [heading Output Format Gallery] The above tables of sample output are available in various other formats @@ -209,15 +215,22 @@ of enabling support for a new number type, is to provide a free function `print` void print(basic_numeric_printer_base& os, MyNumber const& value); Which can be found via ADL, and which calls `basic_numeric_printer_base`'s member functions to -perform the formatted output, it may also make unqualified calls to `print` where the type -is a collection of number types which are supported. This is prefered to overloading `operator<<` directly, since the +perform the formatted output, it may also make unqualified calls to `print` where the sub-type +is already supported (integers, floats, complex numbers, polynomials). This is prefered to overloading `operator<<` directly, since the default `operator<<` will handle stream padding (as a result of `std::setw()`) for you. +Note that it is not required to include the header ``, a simple forward declaration +of `basic_numeric_printer_base` will suffice: + + namespace boost { namespace math{ namespace tools{ template class basic_numeric_printer_base; }}} + +Details of the public interface of `basic_numeric_printer_base` are as follows: + template class basic_numeric_printer_base { public: - + // Manipulator support: void styling(styling_level_t i); styling_level_t styling()const; @@ -238,9 +251,11 @@ default `operator<<` will handle stream padding (as a result of `std::setw()`) f bool latex_as_equation()const; void latex_as_equation(bool); - + + // stream access: std::basic_ostream& stream(); + // scoped helper objects struct scoped_parenthesis { scoped_parenthesis(basic_numeric_printer_base* formatter); @@ -257,6 +272,7 @@ default `operator<<` will handle stream padding (as a result of `std::setw()`) f ~scoped_prolog(); }; + // virtual functions overridden in derived classes: virtual void print_prolog(const char*); virtual void print_epilog(); virtual void print_special_character(boost::uint32_t unicode_value); @@ -269,19 +285,8 @@ default `operator<<` will handle stream padding (as a result of `std::setw()`) f virtual void print_fraction(const std::basic_string& s1, const std::basic_string& s2); virtual void print_subscript(const std::basic_string& s); - template - void print_integer(const Integer& i); - template - void print_float(const Float& f); - template - void print_complex(const Complex& f); - template - void print_polynomial(const Polynomial& f); - template - void print_rational(const Rational& rat); - - template - static std::basic_string part_as_string(Printer& printer, const Value& value); + template + std::basic_string part_as_string(const Value& value); }; Details are as follows: @@ -448,35 +453,10 @@ Prints `s` as subscript. the size of `charT` if `use_unicode()` is true, otherwise prints a "_" followed by `s`, specializations of `basic_numeric_printer` override that behaviour according to need. - template - void print_integer(const Integer& i); - -Prints integer value i. - - template - void print_float(const Float& f); - -Prints floating point value f. - - template - void print_complex(const Complex& f); - -Prints complex number f. - - template - void print_polynomial(const Polynomial& f); - -Prints polynomial f. - - template - void print_rational(const Rational& rat); - -Prints the rational value rat. - - template - static std::basic_string part_as_string(Printer& printer, const Value& value); + template + std::basic_string part_as_string(const Value& value); -Helper function. Returns `value` as a string. Uses a copy of `printer` which contains the same formatting flags (on both itself and +Helper function. Returns `value` as a string. Uses a copy of `this` which contains the same formatting flags (on both itself and the std::ostream it points to) but which outputs to a `std::basic_ostringstream` and then returns the result. [endsect] [/section:formatting] diff --git a/example/formatter_interval_output.cpp b/example/formatter_interval_output.cpp new file mode 100644 index 0000000000..8ad47907ae --- /dev/null +++ b/example/formatter_interval_output.cpp @@ -0,0 +1,39 @@ +// (C) Copyright John Maddock 2019. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +int main() +{ + boost::multiprecision::mpfi_float_50 f = boost::math::constants::pi(); + + std::cout << std::setprecision(std::numeric_limits::digits10); + + boost::math::tools::text_printer t_print(std::cout); + t_print << f << std::endl; + + boost::math::tools::latex_printer l_print(std::cout); + l_print << f << std::endl; + + boost::math::tools::docbook_printer d_print(std::cout); + d_print << f << std::endl; + + boost::math::tools::polynomial poly1 = { 2, 3, 4, -5, -4, 12 }; + boost::math::tools::polynomial poly2 = { -3, 2, 4, 5 }; + poly1 *= poly2; + poly1 /= 3; + + t_print << poly1 << std::endl; + + l_print << poly1 << std::endl; + + d_print << poly1 << std::endl; + + return 0; +} diff --git a/example/formatter_latex_output.pdf b/example/formatter_latex_output.pdf index 414e7a56dc48edc79946cdd242a4cd9b948a303a..8b22a131c7ea4c20f359fda5185b0ffb3ae7d53b 100644 GIT binary patch delta 150 zcmeDG#?t?drC|%>Uo#a`LrViA1Jfu?E`8tp6qm%3R0RzeD_=@f=Xf)?CiLTOA?DpDvDCm RxXdlhjV-uTRbBnvxB&XuCSm{p delta 150 zcmeDG#?t?drC|%>Uo#a$LrW7Q6Qd|iE`8tp6qm%3R0RzeD2g5Cu1{L12bm}Qxiusa|=fob4McsH#-Fzf=Xf)?CiLTOA?DpDvDCm RxXdlhjV-uTRbBnvxBwSeCdvQ+ diff --git a/include/boost/math/tools/formatting.hpp b/include/boost/math/tools/formatting.hpp index 885ff54a2f..8ea7412174 100644 --- a/include/boost/math/tools/formatting.hpp +++ b/include/boost/math/tools/formatting.hpp @@ -54,12 +54,6 @@ namespace boost { // The point "floats": static const bool value = false; }; - template - struct is_float_like - { - // The point "floats": - static const bool value = std::numeric_limits::max_exponent != std::numeric_limits::min_exponent; - }; template using real_t = decltype(std::declval().real(std::declval().imag())); @@ -71,6 +65,8 @@ namespace boost { using polynomial2_t = decltype(std::declval().degree()); template using rational_t = decltype(numerator(std::declval()) + denominator(std::declval())); + template + using interval_t = decltype(singleton(std::declval()) + intersect(std::declval(), std::declval()) + median(std::declval())); template struct is_complex_like @@ -90,6 +86,19 @@ namespace boost { static const bool value = boost::is_detected_v; }; + template + struct is_interval_like + { + static const bool value = boost::is_detected_v; + }; + + template + struct is_float_like + { + // The point "floats": + static const bool value = (std::numeric_limits::max_exponent != std::numeric_limits::min_exponent) && !is_interval_like::value; + }; + template struct is_unhandled_type { @@ -290,27 +299,191 @@ namespace boost { template typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& value) { - os.print_integer(value); + typename basic_numeric_printer_base::scoped_prolog s(&os, "integer"); + os.stream() << value; } template - typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& value) + typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& f) { - os.print_float(value); + using std::isinf; + using std::isnan; + + typename basic_numeric_printer_base::scoped_prolog s(&os, "float"); + + if ((isinf)(f)) + { + if (f < 0) + os.stream() << "-"; + os.print_special_character(0x221E); + return; + } + if ((isnan)(f)) + { + os.print_name("NaN"); + return; + } + std::basic_string mantissa, exponent; + os.decompose_float(os.stream(), f, mantissa, exponent); + os.stream() << mantissa; + if (exponent.size()) + { + os.print_times(); + os.stream() << "10"; + os.print_superscript(exponent); + } + return; + } + template + typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Complex& f) + { + using std::isnan; + using std::isinf; + + typename basic_numeric_printer_base::scoped_prolog s1(&os, "complex"); + typename basic_numeric_printer_base::scoped_styling s2(&os); + + if ((isnan)(f.real()) || (isnan)(f.imag())) + { + os.print_name("NaN"); + return; + } + else if ((isinf)(f.real()) || (isinf)(f.imag())) + { + os.print_complex_infinity(); + return; + } + else if ((!os.show_zero_components()) && (f.imag() == 0)) + { + print(os, f.real()); + return; + } + else if ((!os.show_zero_components()) && (f.real() == 0)) + { + print(os, f.imag()); + } + else + { + if (os.parenthesis()) + os.stream() << "("; + print(os, f.real()); + typename Complex::value_type i(f.imag()); + bool isneg = i < 0; + if (isneg) + { + i = -i; + os.stream() << " - "; + } + else + os.stream() << " + "; + print(os, i); + } + os.print_imaginary_unit(); + if (os.parenthesis() && !((!os.show_zero_components()) && (f.real() == 0))) + os.stream() << ")"; + return; } template - typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& value) + typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& f) { - os.print_complex(value); + typename basic_numeric_printer_base::scoped_prolog s1(&os, "polynomial"); + typename basic_numeric_printer_base::scoped_parenthesis s2(&os); + typename basic_numeric_printer_base::scoped_styling s3(&os); + + bool have_first = false; + for (unsigned i = 0; i <= f.degree(); ++i) + { + auto coef = f[i]; + if (os.show_zero_components() || (!detail::iszero(coef))) + { + if (have_first) + { + if (!detail::isneg(coef)) + os.stream() << " + "; + else + os.stream() << " - "; + } + print(os, !detail::isneg(coef) ? coef : -coef); + have_first = true; + if (i) + { + os.print_variable('x'); + if (i > 1) + { + std::basic_stringstream ss; + ss << i; + std::basic_string s = ss.str(); + os.print_superscript(s); + } + } + } + } } template - typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& value) + typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& rat) { - os.print_polynomial(value); + typename basic_numeric_printer_base::scoped_prolog s1(&os, "rational"); + typename basic_numeric_printer_base::scoped_styling s3(&os); + + auto num = numerator(rat); + auto denom = denominator(rat); + + if ((num == 0) || (denom == 1)) + { + print(os, num); + return; + } + else + { + if (detail::isneg(num)) + { + os.stream() << "-"; + num = -num; + } + if (os.parenthesis()) + os.stream() << "("; + { + typename basic_numeric_printer_base::scoped_parenthesis s2(&os); + + auto str1 = os.part_as_string(num); + auto str2 = os.part_as_string(denom); + + os.print_fraction(str1, str2); + } + if (os.parenthesis()) + os.stream() << ")"; + } } + template - typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& value) + typename boost::enable_if_c::value>::type print(basic_numeric_printer_base& os, const Value& val) { - os.print_rational(value); + typename basic_numeric_printer_base::scoped_prolog s1(&os, "interval"); + typename basic_numeric_printer_base::scoped_styling s3(&os); + + if (singleton(val)) + { + print(os, lower(val)); + return; + } + else + { + auto med = median(val); + auto rad = width(val); + rad /= 2; + + if (os.parenthesis()) + os.stream() << "("; + { + typename basic_numeric_printer_base::scoped_parenthesis s2(&os); + print(os, med); + os.stream() << " "; + os.print_special_character(0xB1); + os.stream() << " "; + print(os, rad); + } + if (os.parenthesis()) + os.stream() << ")"; + } } template @@ -333,7 +506,7 @@ namespace boost { bool m_show_zero_components; bool m_use_unicode; std::basic_ostream* p_stream; - protected: + public: template static void decompose_float(std::basic_ostream& os, const Float& f, std::basic_string& mantissa, std::basic_string& exponent) { @@ -352,7 +525,6 @@ namespace boost { else mantissa = s; } - public: basic_numeric_printer_base(std::basic_ostream& os) : styling_level(full_styling), i_style(upright_i), multiply_style(multiply_times), m_parenthesis(0), m_show_zero_components(false), m_use_unicode(true), p_stream(&os) {} @@ -530,176 +702,17 @@ namespace boost { detail::print_unicode_subscript(stream(), s); } - template - void print_integer(const Integer& i) - { - scoped_prolog s(this, "integer"); - this->stream() << i; - } - template - void print_float(const Float& f) - { - using std::isinf; - using std::isnan; - - scoped_prolog s(this, "float"); - - if ((isinf)(f)) - { - if (f < 0) - stream() << "-"; - print_special_character(0x221E); - return; - } - if ((isnan)(f)) - { - print_name("NaN"); - return; - } - std::basic_string mantissa, exponent; - decompose_float(stream(), f, mantissa, exponent); - stream() << mantissa; - if (exponent.size()) - { - print_times(); - stream() << "10"; - print_superscript(exponent); - } - return; - } - template - void print_complex(const Complex& f) - { - using std::isnan; - using std::isinf; - - scoped_prolog s1(this, "complex"); - scoped_styling s2(this); - - if ((isnan)(f.real()) || (isnan)(f.imag())) - { - print_name("NaN"); - return; - } - else if ((isinf)(f.real()) || (isinf)(f.imag())) - { - print_complex_infinity(); - return; - } - else if ((!this->show_zero_components()) && (f.imag() == 0)) - { - this->print_float(f.real()); - return; - } - else if ((!this->show_zero_components()) && (f.real() == 0)) - { - this->print_float(f.imag()); - } - else - { - if (parenthesis()) - this->stream() << "("; - this->print_float(f.real()); - typename Complex::value_type i(f.imag()); - bool isneg = i < 0; - if (isneg) - { - i = -i; - this->stream() << " - "; - } - else - this->stream() << " + "; - this->print_float(i); - } - print_imaginary_unit(); - if (parenthesis() && !((!this->show_zero_components()) && (f.real() == 0))) - this->stream() << ")"; - return; - } - template - void print_polynomial(const Polynomial& f) - { - scoped_prolog s1(this, "polynomial"); - scoped_parenthesis s2(this); - scoped_styling s3(this); - - bool have_first = false; - for (unsigned i = 0; i <= f.degree(); ++i) - { - auto coef = f[i]; - if (show_zero_components() || (!detail::iszero(coef))) - { - if (have_first) - { - if (!detail::isneg(coef)) - stream() << " + "; - else - stream() << " - "; - } - print(*this, !detail::isneg(coef) ? coef : -coef); - have_first = true; - if (i) - { - print_variable('x'); - if (i > 1) - { - std::basic_stringstream ss; - ss << i; - std::basic_string s = ss.str(); - print_superscript(s); - } - } - } - } - } - - template - void print_rational(const Rational& rat) - { - scoped_prolog s1(this, "rational"); - scoped_styling s3(this); - - auto num = numerator(rat); - auto denom = denominator(rat); - - if ((num == 0) || (denom == 1)) - { - print_integer(num); - return; - } - else - { - if (detail::isneg(num)) - { - stream() << "-"; - num = -num; - } - if (parenthesis()) - stream() << "("; - { - scoped_parenthesis s2(this); - - auto str1 = part_as_string(*this, num); - auto str2 = part_as_string(*this, denom); - - print_fraction(str1, str2); - } - if (parenthesis()) - stream() << ")"; - } - } - bool latex_as_equation()const { return false; } void latex_as_equation(bool) {} - template - static std::basic_string part_as_string(Printer& printer, const Value& value) + template + std::basic_string part_as_string(const Value& value) { std::basic_ostringstream ss; - ss.copyfmt(printer.stream()); - ss.imbue(printer.stream().getloc()); + ss.copyfmt(stream()); + ss.imbue(stream().getloc()); ss.width(0); - Printer fmt(ss, printer); + basic_numeric_printer_base fmt(ss, *this); print(fmt, value); return ss.str(); } @@ -832,6 +845,33 @@ namespace boost { case 0x22C5: this->stream() << "\\cdot"; break; + case 0xB1: + this->stream() << "\\pm"; + break; + case 0x2213: + this->stream() << "\\mp"; + break; + case 0x221A: + this->stream() << "\\surd"; + break; + case 0x3B4: + this->stream() << "\\delta"; + break; + case 0x394: + this->stream() << "\\Delta"; + break; + case 0x1D70B: + this->stream() << "\\pi"; + break; + case 0x03C0: + this->stream() << "\\pi"; + break; + case 0x1D70E: + this->stream() << "\\sigma"; + break; + case 0x3c3: + this->stream() << "\\sigma"; + break; default: throw std::runtime_error("Unsuported Unicode character encountered in LaTex output"); } @@ -997,7 +1037,7 @@ namespace boost { std::size_t w = (std::size_t)os.stream().width(); if (w) { - std::basic_string s = basic_numeric_printer_base::part_as_string(os, i); + std::basic_string s = os.part_as_string(i); std::size_t len = detail::unicode_character_len(s.c_str()); if (len < w) { diff --git a/test/.temps/boost-no-inspect b/test/.temps/boost-no-inspect deleted file mode 100644 index e69de29bb2..0000000000 From a87c27f4de28f3da33a031c5b7149ddb86e1fa23 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sat, 30 Nov 2019 16:37:33 +0000 Subject: [PATCH 11/14] formatting: rework the api a little more, fix some bugs, add more B-Spline examples. --- doc/fp_utilities/formatting.qbk | 45 ++++++++- example/formatter_b_spline_output.cpp | 55 +++++++++-- example/formatter_interval_output.cpp | 9 +- .../cardinal_quadratic_b_spline.hpp | 5 + .../cardinal_quintic_b_spline.hpp | 6 ++ .../detail/cardinal_cubic_b_spline_detail.hpp | 12 ++- .../cardinal_quadratic_b_spline_detail.hpp | 92 ++++++++++++++++++- .../cardinal_quintic_b_spline_detail.hpp | 78 ++++++++++++++++ include/boost/math/tools/formatting.hpp | 73 ++++++++++----- 9 files changed, 331 insertions(+), 44 deletions(-) diff --git a/doc/fp_utilities/formatting.qbk b/doc/fp_utilities/formatting.qbk index 9455d3aa5f..b580f27348 100644 --- a/doc/fp_utilities/formatting.qbk +++ b/doc/fp_utilities/formatting.qbk @@ -182,8 +182,24 @@ The following section (using Docbook output format) illustrates how each of the [table:intervals Interval Values [[Value][Result]] -[[`boost::math::constants::pi()`]['''3.1415926535897932384626433832795028841971693993751 ± 5.3455294201843912922810729343029637576303937602101×10-51''']] -[[Polynomial of `mpfi_float_50`]['''2 - (1.6666666666666666666666666666666666666666666666667 ± 2.672764710092195646140536467151481878815196880105×10-51)x + (0.66666666666666666666666666666666666666666666666666 ± 1.3363823550460978230702682335757409394075984400525×10-51)x2 + 15x3 + 11x4 - (14.666666666666666666666666666666666666666666666667 ± 2.138211768073756516912429173721185503052157504084×10-50)x5 - (5.6666666666666666666666666666666666666666666666667 ± 1.069105884036878258456214586860592751526078752042×10-50)x6 + (9.3333333333333333333333333333333333333333333333334 ± 2.138211768073756516912429173721185503052157504084×10-50)x7 + 20x8''']] +[[`boost::math::constants::pi>>()`]['''3.1415926535897932385 ± 6.7762635780344027125×10-21''']] +[[Polynomial of `number>`]['''2 - (1.6666666666666666667 ± 3.3881317890172013563×10-21)x + (0.66666666666666666666 ± 1.6940658945086006781×10-21)x2 + 15x3 + 11x4 - (14.666666666666666667 ± 2.710505431213761085×10-20)x5 - (5.6666666666666666667 ± 1.3552527156068805425×10-20)x6 + (9.3333333333333333334 ± 2.710505431213761085×10-20)x7 + 20x8''']] +] + +[table:misc Misc Types +[[Type][Comments][Result]] +[[cardinal_quadratic_b_spline][Formatted output of this type is primarily for debugging purposes since only + 4 consecutive values of the sum are used for any given abscissa value, where as the + printed form shows all the value of the sum.] + ['''-B2(10(x - 5) + 1) + 5×B2(10(x - 5)) + 11×B2(10(x - 5) - 1) + 17×B2(10(x - 5) - 2) + 23×B2(10(x - 5) - 3) + 29×B2(10(x - 5) - 4) + 35×B2(10(x - 5) - 5) + 41×B2(10(x - 5) - 6) + 47×B2(10(x - 5) - 7) + 53×B2(10(x - 5) - 8) + 59×B2(10(x - 5) - 9) + 65×B2(10(x - 5) - 10)''']] +[[cardinal_cubic_b_spline][Formatted output of this type is primarily for debugging purposes since only + 4 consecutive values of the sum are used for any given abscissa value, where as the + printed form shows all the value of the sum.] + ['''32 - 33×B3(10(x - 5) + 1) - 27×B3(10(x - 5)) - 21×B3(10(x - 5) - 1) - 15×B3(10(x - 5) - 2) - 9×B3(10(x - 5) - 3) - 3×B3(10(x - 5) - 4) + 3×B3(10(x - 5) - 5) + 9×B3(10(x - 5) - 6) + 15×B3(10(x - 5) - 7) + 21×B3(10(x - 5) - 8) + 27×B3(10(x - 5) - 9) + 33×B3(10(x - 5) - 10)''']] +[[cardinal_quintic_b_spline][Formatted output of this type is primarily for debugging purposes since only + 6 consecutive values of the sum are used for any given abscissa value, where as the + printed form shows all the value of the sum.] + ['''-7×B5(10(x - 5) + 2) - B5(10(x - 5) + 3) + 5×B5(10(x - 5) + 4) + 11×B5(10(x - 5) + 5) + 17×B5(10(x - 5) + 6) + 23×B5(10(x - 5) + 7) + 29×B5(10(x - 5) + 8) + 35×B5(10(x - 5) + 9) + 41×B5(10(x - 5) + 10) + 47×B5(10(x - 5) + 11) + 53×B5(10(x - 5) + 12) + 59×B5(10(x - 5) + 13) + 65×B5(10(x - 5) + 14) + 71×B5(10(x - 5) + 15)''']] ] [heading Output Format Gallery] @@ -274,7 +290,7 @@ Details of the public interface of `basic_numeric_printer_base` are as follows: // virtual functions overridden in derived classes: virtual void print_prolog(const char*); - virtual void print_epilog(); + virtual void print_epilog(const char*); virtual void print_special_character(boost::uint32_t unicode_value); virtual void print_times(); virtual void print_superscript(const std::basic_string& s); @@ -284,9 +300,12 @@ Details of the public interface of `basic_numeric_printer_base` are as follows: virtual void print_name(const std::basic_string& s); virtual void print_fraction(const std::basic_string& s1, const std::basic_string& s2); virtual void print_subscript(const std::basic_string& s); + virtual std::unique_ptr clone(std::basic_ostream& os) = 0; template std::basic_string part_as_string(const Value& value); + template + std::basic_string part_as_plain_text(const Value& value); }; Details are as follows: @@ -375,9 +394,9 @@ Prints any prolog to the formatted output - for example styling tags in HTML/XML `basic_numeric_printer_base<>::print_prolog` does nothing, specializations of `basic_numeric_printer` override that behaviour according to need. - virtual void print_epilog(); + virtual void print_epilog(const char*); -Prints any elilog to the formatted output - for example closing styling tags in HTML/XML output. +Prints any epilog to the formatted output - for example closing styling tags in HTML/XML output. `basic_numeric_printer_base<>::print_epilog` does nothing, specializations of `basic_numeric_printer` override that behaviour according to need. @@ -453,12 +472,28 @@ Prints `s` as subscript. the size of `charT` if `use_unicode()` is true, otherwise prints a "_" followed by `s`, specializations of `basic_numeric_printer` override that behaviour according to need. + virtual std::unique_ptr clone(std::basic_ostream& os); + +Returns a (smart-) pointer to a copy of this object but which uses `os` as the underlying output stream. + +All derived classes must override this method. + template std::basic_string part_as_string(const Value& value); Helper function. Returns `value` as a string. Uses a copy of `this` which contains the same formatting flags (on both itself and the std::ostream it points to) but which outputs to a `std::basic_ostringstream` and then returns the result. +Typically used to format numerator and denominator of a fraction. + + template + std::basic_string part_as_plain_text(const Value& value); + +Helper function. Returns `value` as a string. Differs from `part_as_string` in that it uses a plain text printer +internally with `use_unicode()` set to `false`. + +Typically used to check if a value will be "trivial" such as "1" or "0" once printed to current precision. + [endsect] [/section:formatting] diff --git a/example/formatter_b_spline_output.cpp b/example/formatter_b_spline_output.cpp index 36f6445101..a8e90e50b1 100644 --- a/example/formatter_b_spline_output.cpp +++ b/example/formatter_b_spline_output.cpp @@ -7,26 +7,65 @@ #include #include #include +#include +#include int main() { std::vector v(10); for (size_t i = 0; i < v.size(); ++i) { - v[i] = 5 + 6 * i; + v[i] = static_cast(5 + 6 * i); } double step = 0.1; double a = 5; - boost::math::interpolators::cardinal_cubic_b_spline spline(v.data(), v.size(), a, step); + { + std::cout << "Quartic B Spline:\n\n"; + boost::math::interpolators::cardinal_quadratic_b_spline spline(v.data(), v.size(), a, step); + + boost::math::tools::text_printer printer(std::cout); + printer << spline << std::endl << std::endl; + + boost::math::tools::latex_printer lprinter(std::cout); + lprinter << spline << std::endl << std::endl; + + boost::math::tools::html_printer hprinter(std::cout); + hprinter << spline << std::endl << std::endl; + + boost::math::tools::docbook_printer dprinter(std::cout); + dprinter << spline << std::endl << std::endl; + } + { + std::cout << "Cubic B Spline:\n\n"; + boost::math::interpolators::cardinal_cubic_b_spline spline(v.data(), v.size(), a, step); - boost::math::tools::text_printer printer(std::cout); - printer << spline << std::endl; + boost::math::tools::text_printer printer(std::cout); + printer << spline << std::endl << std::endl; - boost::math::tools::latex_printer lprinter(std::cout); - lprinter << spline << std::endl; + boost::math::tools::latex_printer lprinter(std::cout); + lprinter << spline << std::endl << std::endl; - boost::math::tools::html_printer hprinter(std::cout); - hprinter << spline << std::endl; + boost::math::tools::html_printer hprinter(std::cout); + hprinter << spline << std::endl << std::endl; + boost::math::tools::docbook_printer dprinter(std::cout); + dprinter << spline << std::endl << std::endl; + } + { + std::cout << "Quintic B Spline:\n\n"; + boost::math::interpolators::cardinal_quintic_b_spline spline(v.data(), v.size(), a, step); + + boost::math::tools::text_printer printer(std::cout); + printer << spline << std::endl << std::endl; + + boost::math::tools::latex_printer lprinter(std::cout); + lprinter << spline << std::endl << std::endl; + + boost::math::tools::html_printer hprinter(std::cout); + hprinter << spline << std::endl << std::endl; + + boost::math::tools::docbook_printer dprinter(std::cout); + dprinter << spline << std::endl << std::endl; + } return 0; } diff --git a/example/formatter_interval_output.cpp b/example/formatter_interval_output.cpp index 8ad47907ae..a7f78f060a 100644 --- a/example/formatter_interval_output.cpp +++ b/example/formatter_interval_output.cpp @@ -11,9 +11,10 @@ int main() { - boost::multiprecision::mpfi_float_50 f = boost::math::constants::pi(); + typedef boost::multiprecision::number > mp_type; + mp_type f = boost::math::constants::pi(); - std::cout << std::setprecision(std::numeric_limits::digits10); + std::cout << std::setprecision(std::numeric_limits::digits10); boost::math::tools::text_printer t_print(std::cout); t_print << f << std::endl; @@ -24,8 +25,8 @@ int main() boost::math::tools::docbook_printer d_print(std::cout); d_print << f << std::endl; - boost::math::tools::polynomial poly1 = { 2, 3, 4, -5, -4, 12 }; - boost::math::tools::polynomial poly2 = { -3, 2, 4, 5 }; + boost::math::tools::polynomial poly1 = { 2, 3, 4, -5, -4, 12 }; + boost::math::tools::polynomial poly2 = { -3, 2, 4, 5 }; poly1 *= poly2; poly1 /= 3; diff --git a/include/boost/math/interpolators/cardinal_quadratic_b_spline.hpp b/include/boost/math/interpolators/cardinal_quadratic_b_spline.hpp index a5b150f2f1..9530bbf20e 100644 --- a/include/boost/math/interpolators/cardinal_quadratic_b_spline.hpp +++ b/include/boost/math/interpolators/cardinal_quadratic_b_spline.hpp @@ -49,6 +49,11 @@ class cardinal_quadratic_b_spline return impl_->t_max(); } + template + friend void print(boost::math::tools::basic_numeric_printer_base& os, const cardinal_quadratic_b_spline& spline) + { + print(os, *spline.impl_); + } private: std::shared_ptr> impl_; }; diff --git a/include/boost/math/interpolators/cardinal_quintic_b_spline.hpp b/include/boost/math/interpolators/cardinal_quintic_b_spline.hpp index 3d72865d93..9b2df809d2 100644 --- a/include/boost/math/interpolators/cardinal_quintic_b_spline.hpp +++ b/include/boost/math/interpolators/cardinal_quintic_b_spline.hpp @@ -54,6 +54,12 @@ class cardinal_quintic_b_spline return impl_->t_max(); } + template + friend void print(boost::math::tools::basic_numeric_printer_base& os, const cardinal_quintic_b_spline& spline) + { + print(os, *spline.impl_); + } + private: std::shared_ptr> impl_; }; diff --git a/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp b/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp index 5cbfe98fec..d83161fd79 100644 --- a/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp +++ b/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp @@ -72,17 +72,21 @@ class cardinal_cubic_b_spline_imp print(os, spline.m_avg); for (int64_t k = 0; k < int64_t(spline.m_beta.size()); ++k) { - if (spline.m_beta[k] < 0) + auto alpha = spline.m_beta[k]; + if (alpha < 0) { os.stream() << " - "; - print(os, -spline.m_beta[k]); + alpha = -alpha; } else { os.stream() << " + "; - print(os, spline.m_beta[k]); } - os.print_times(); + if (os.part_as_plain_text(alpha) != "1") + { + print(os, alpha); + os.print_times(); + } os.print_name("B"); os.print_subscript("3"); os.stream() << "("; diff --git a/include/boost/math/interpolators/detail/cardinal_quadratic_b_spline_detail.hpp b/include/boost/math/interpolators/detail/cardinal_quadratic_b_spline_detail.hpp index 4998348ce6..73145b6022 100644 --- a/include/boost/math/interpolators/detail/cardinal_quadratic_b_spline_detail.hpp +++ b/include/boost/math/interpolators/detail/cardinal_quadratic_b_spline_detail.hpp @@ -8,7 +8,18 @@ #define BOOST_MATH_INTERPOLATORS_CARDINAL_QUADRATIC_B_SPLINE_DETAIL_HPP #include -namespace boost{ namespace math{ namespace interpolators{ namespace detail{ +namespace boost{ namespace math{ + +namespace tools +{ + // Forward declaration only, we don't need the formatter's + // definition to be able to define a print function (only if + // we actually use it). + template + class basic_numeric_printer_base; +} + +namespace interpolators{ namespace detail{ template Real b2_spline(Real x) { @@ -196,6 +207,85 @@ class cardinal_quadratic_b_spline_detail return m_t0 + (m_alpha.size()-3)/m_inv_h; } + template + friend void print(boost::math::tools::basic_numeric_printer_base& os, const cardinal_quadratic_b_spline_detail& spline) + { + typedef boost::math::tools::basic_numeric_printer_base printer_t; + // + // scoped_prolog is responsible for writing the prolog/epilog code + // to the stream, if the output format requires it. Not a number in it's own + // right, but an expression, so set the name to the empty string and let each + // individual number style itself. + // + typename printer_t::scoped_prolog prolog(&os, ""); + // + // scoped_parenthesis instructs nested types to wrap themselves in () + // if they are compound rather than atomic number types (for example complex numbers): + // + typename printer_t::scoped_parenthesis param(&os); + + bool have_first = false; + + for (size_t j = 0; j < spline.m_alpha.size(); ++j) + { + auto alpha = spline.m_alpha[j]; + if (have_first) + { + if (alpha < 0) + { + alpha = -alpha; + os.stream() << " - "; + } + else + os.stream() << " + "; + } + else + have_first = true; + + auto sa = os.part_as_plain_text(alpha); + if (sa == "1") { } + else if (sa == "-1") + { + os.stream() << "-"; + } + else + { + print(os, alpha); + os.print_times(); + } + os.print_name("B"); + os.print_subscript("2"); + os.stream() << "("; + print(os, spline.m_inv_h); + os.stream() << "("; + os.print_variable('x'); + if (spline.m_t0 < 0) + { + os.stream() << " + "; + print(os, -spline.m_t0); + } + else + { + os.stream() << " - "; + print(os, spline.m_t0); + } + os.stream() << ")"; + std::ptrdiff_t k = 1 - j; + if (k < 0) + { + os.stream() << " - "; + print(os, -k); + } + else if (k > 0) + { + os.stream() << " + "; + print(os, k); + } + os.stream() << ")"; + } + } + + private: std::vector m_alpha; Real m_inv_h; diff --git a/include/boost/math/interpolators/detail/cardinal_quintic_b_spline_detail.hpp b/include/boost/math/interpolators/detail/cardinal_quintic_b_spline_detail.hpp index 09ce63d827..c86c97c4a5 100644 --- a/include/boost/math/interpolators/detail/cardinal_quintic_b_spline_detail.hpp +++ b/include/boost/math/interpolators/detail/cardinal_quintic_b_spline_detail.hpp @@ -186,6 +186,84 @@ class cardinal_quintic_b_spline_detail return s; } + template + friend void print(boost::math::tools::basic_numeric_printer_base& os, const cardinal_quintic_b_spline_detail& spline) + { + typedef boost::math::tools::basic_numeric_printer_base printer_t; + // + // scoped_prolog is responsible for writing the prolog/epilog code + // to the stream, if the output format requires it. Not a number in it's own + // right, but an expression, so set the name to the empty string and let each + // individual number style itself. + // + typename printer_t::scoped_prolog prolog(&os, ""); + // + // scoped_parenthesis instructs nested types to wrap themselves in () + // if they are compound rather than atomic number types (for example complex numbers): + // + typename printer_t::scoped_parenthesis param(&os); + + bool have_first = false; + + for (size_t j = 0; j < spline.m_alpha.size(); ++j) + { + auto alpha = spline.m_alpha[j]; + if (have_first) + { + if (alpha < 0) + { + alpha = -alpha; + os.stream() << " - "; + } + else + os.stream() << " + "; + } + else + have_first = true; + + auto sa = os.part_as_plain_text(alpha); + if (sa == "1") {} + else if (sa == "-1") + { + os.stream() << "-"; + } + else + { + print(os, alpha); + os.print_times(); + } + os.print_name("B"); + os.print_subscript("5"); + os.stream() << "("; + print(os, spline.m_inv_h); + os.stream() << "("; + os.print_variable('x'); + if (spline.m_t0 < 0) + { + os.stream() << " + "; + print(os, -spline.m_t0); + } + else + { + os.stream() << " - "; + print(os, spline.m_t0); + } + os.stream() << ")"; + std::ptrdiff_t k = j + 2; + if (k < 0) + { + os.stream() << " - "; + print(os, -k); + } + else if (k > 0) + { + os.stream() << " + "; + print(os, k); + } + os.stream() << ")"; + } + } + Real prime(Real t) const { using std::ceil; using std::floor; diff --git a/include/boost/math/tools/formatting.hpp b/include/boost/math/tools/formatting.hpp index 8ea7412174..716866ad36 100644 --- a/include/boost/math/tools/formatting.hpp +++ b/include/boost/math/tools/formatting.hpp @@ -400,9 +400,14 @@ namespace boost { if (!detail::isneg(coef)) os.stream() << " + "; else + { os.stream() << " - "; + coef = -coef; + } } - print(os, !detail::isneg(coef) ? coef : -coef); + auto s = os.part_as_plain_text(coef); + if((s != "1") || !i) + print(os, coef); have_first = true; if (i) { @@ -615,18 +620,19 @@ namespace boost { struct scoped_prolog { basic_numeric_printer_base* m_formatter; - scoped_prolog(basic_numeric_printer_base* formatter, const char* number_type) : m_formatter(formatter) + const char* m_type; + scoped_prolog(basic_numeric_printer_base* formatter, const char* number_type) : m_formatter(formatter), m_type(number_type) { m_formatter->print_prolog(number_type); } ~scoped_prolog() { - m_formatter->print_epilog(); + m_formatter->print_epilog(m_type); } }; virtual void print_prolog(const char*) {} - virtual void print_epilog() {} + virtual void print_epilog(const char*) {} virtual void print_special_character(boost::uint32_t unicode_value) { if (use_unicode()) @@ -644,6 +650,7 @@ namespace boost { break; case 0x2044: stream() << "/"; + break; default: throw std::runtime_error("Unsuported Unicode character encountered in plain text output"); } @@ -701,6 +708,7 @@ namespace boost { { detail::print_unicode_subscript(stream(), s); } + virtual std::unique_ptr clone(std::basic_ostream&) = 0; bool latex_as_equation()const { return false; } void latex_as_equation(bool) {} @@ -712,7 +720,19 @@ namespace boost { ss.copyfmt(stream()); ss.imbue(stream().getloc()); ss.width(0); - basic_numeric_printer_base fmt(ss, *this); + std::unique_ptr fmt(clone(ss)); + print(*fmt, value); + return ss.str(); + } + template + std::basic_string part_as_plain_text(const Value& value) + { + std::basic_ostringstream ss; + ss.copyfmt(stream()); + ss.imbue(stream().getloc()); + ss.width(0); + basic_numeric_printer fmt(ss, *this); + fmt.use_unicode(false); print(fmt, value); return ss.str(); } @@ -723,17 +743,20 @@ namespace boost { { public: basic_numeric_printer(std::basic_ostream& os) : basic_numeric_printer_base(os) {} - basic_numeric_printer(std::basic_ostream& os, basic_numeric_printer const& fmt) : basic_numeric_printer_base(os, fmt) {} + basic_numeric_printer(std::basic_ostream& os, basic_numeric_printer_base const& fmt) : basic_numeric_printer_base(os, fmt) {} + virtual std::unique_ptr > clone(std::basic_ostream& os) + { + return std::unique_ptr >(new basic_numeric_printer(os, *this)); + } }; template class basic_numeric_printer : public basic_numeric_printer_base { friend class basic_numeric_printer_base; - bool need_epilog; public: - basic_numeric_printer(std::basic_ostream& os) : basic_numeric_printer_base(os), need_epilog(true) {} - basic_numeric_printer(std::basic_ostream& os, basic_numeric_printer const& fmt) : basic_numeric_printer_base(os, fmt), need_epilog(true) {} + basic_numeric_printer(std::basic_ostream& os) : basic_numeric_printer_base(os) {} + basic_numeric_printer(std::basic_ostream& os, basic_numeric_printer const& fmt) : basic_numeric_printer_base(os, fmt) {} virtual void print_prolog(const char* name) { @@ -744,12 +767,10 @@ namespace boost { if (this->styling() > minimal_styling) this->stream() << ""; } - else - need_epilog = false; } - virtual void print_epilog() + virtual void print_epilog(const char* name) { - if (need_epilog) + if (name && *name) { if (this->styling() > minimal_styling) this->stream() << ""; @@ -796,6 +817,10 @@ namespace boost { { this->stream() << "" << s << ""; } + virtual std::unique_ptr > clone(std::basic_ostream& os) + { + return std::unique_ptr >(new basic_numeric_printer(os, *this)); + } }; template @@ -821,7 +846,7 @@ namespace boost { this->stream().put(this->stream().widen('$')); } } - virtual void print_epilog() + virtual void print_epilog(const char*) { if (latex_as_equation()) { @@ -946,17 +971,19 @@ namespace boost { else this->stream() << "\\textsubscript{" << s << "}"; } - + virtual std::unique_ptr > clone(std::basic_ostream& os) + { + return std::unique_ptr >(new basic_numeric_printer(os, *this)); + } }; template class basic_numeric_printer : public basic_numeric_printer_base { friend class basic_numeric_printer_base; - bool need_epilog; public: - basic_numeric_printer(std::basic_ostream& os) : basic_numeric_printer_base(os), need_epilog(true){} - basic_numeric_printer(std::basic_ostream& os, basic_numeric_printer const & fmt) : basic_numeric_printer_base(os, fmt), need_epilog(true){} + basic_numeric_printer(std::basic_ostream& os) : basic_numeric_printer_base(os) {} + basic_numeric_printer(std::basic_ostream& os, basic_numeric_printer const & fmt) : basic_numeric_printer_base(os, fmt) {} virtual void print_prolog(const char* name) { @@ -967,12 +994,10 @@ namespace boost { if (this->styling() > minimal_styling) this->stream() << ""; } - else - need_epilog = false; } - virtual void print_epilog() + virtual void print_epilog(const char* name) { - if (need_epilog) + if (name && *name) { if (this->styling() > minimal_styling) this->stream() << ""; @@ -1019,6 +1044,10 @@ namespace boost { { this->stream() << "" << s << ""; } + virtual std::unique_ptr > clone(std::basic_ostream& os) + { + return std::unique_ptr >(new basic_numeric_printer(os, *this)); + } }; typedef basic_numeric_printer<> text_printer; From a9b6639fbad8b82d6507a4ed96fc2fdfd39480da Mon Sep 17 00:00:00 2001 From: Nick Thompson Date: Sat, 30 Nov 2019 16:01:00 -0500 Subject: [PATCH 12/14] Remove a typo [CI SKIP] --- example/formatter_b_spline_output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/formatter_b_spline_output.cpp b/example/formatter_b_spline_output.cpp index a8e90e50b1..234ad85177 100644 --- a/example/formatter_b_spline_output.cpp +++ b/example/formatter_b_spline_output.cpp @@ -20,7 +20,7 @@ int main() { double step = 0.1; double a = 5; { - std::cout << "Quartic B Spline:\n\n"; + std::cout << "Quadratic B Spline:\n\n"; boost::math::interpolators::cardinal_quadratic_b_spline spline(v.data(), v.size(), a, step); boost::math::tools::text_printer printer(std::cout); From 417ac588644267095975f885b3c469d53da2cb0d Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sun, 1 Dec 2019 17:56:03 +0000 Subject: [PATCH 13/14] formatter: Fix some breakages (missing forward declarations. Tidy up some std::isnan usage to prevent macro expansion. --- .../interpolators/barycentric_rational.hpp | 5 +++++ .../detail/cardinal_cubic_b_spline_detail.hpp | 10 ++++----- .../cardinal_quadratic_b_spline_detail.hpp | 4 ++-- .../cardinal_quintic_b_spline_detail.hpp | 22 ++++++++++++++----- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/include/boost/math/interpolators/barycentric_rational.hpp b/include/boost/math/interpolators/barycentric_rational.hpp index 5419c70af3..e175679b55 100644 --- a/include/boost/math/interpolators/barycentric_rational.hpp +++ b/include/boost/math/interpolators/barycentric_rational.hpp @@ -56,6 +56,11 @@ class barycentric_rational return m_imp->return_y(); } + template + friend void print(boost::math::tools::basic_numeric_printer_base& os, const barycentric_rational& bar) + { + print(os, *bar.m_imp); + } private: std::shared_ptr> m_imp; }; diff --git a/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp b/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp index d83161fd79..82b781008d 100644 --- a/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp +++ b/include/boost/math/interpolators/detail/cardinal_cubic_b_spline_detail.hpp @@ -197,7 +197,7 @@ cardinal_cubic_b_spline_imp::cardinal_cubic_b_spline_imp(BidiIterator f, B if (length < 5) { - if (boost::math::isnan(left_endpoint_derivative) || boost::math::isnan(right_endpoint_derivative)) + if ((boost::math::isnan)(left_endpoint_derivative) || (boost::math::isnan)(right_endpoint_derivative)) { throw std::logic_error("Interpolation using a cubic b spline with derivatives estimated at the endpoints requires at least 5 points.\n"); } @@ -207,7 +207,7 @@ cardinal_cubic_b_spline_imp::cardinal_cubic_b_spline_imp(BidiIterator f, B } } - if (boost::math::isnan(left_endpoint)) + if ((boost::math::isnan)(left_endpoint)) { throw std::logic_error("Left endpoint is NAN; this is disallowed.\n"); } @@ -229,7 +229,7 @@ cardinal_cubic_b_spline_imp::cardinal_cubic_b_spline_imp(BidiIterator f, B // to construct high-order estimates for one-sided derivatives: // https://en.wikipedia.org/wiki/Finite_difference_coefficient#Forward_and_backward_finite_difference // Here, we estimate then to O(h^4), as that is the maximum accuracy we could obtain from this method. - if (boost::math::isnan(a1)) + if ((boost::math::isnan)(a1)) { // For simple functions (linear, quadratic, so on) // almost all the error comes from derivative estimation. @@ -240,7 +240,7 @@ cardinal_cubic_b_spline_imp::cardinal_cubic_b_spline_imp(BidiIterator f, B } Real b1 = right_endpoint_derivative; - if (boost::math::isnan(b1)) + if ((boost::math::isnan)(b1)) { size_t n = length - 1; Real t0 = 4*(f[n-3] + third()*f[n - 1]); @@ -262,7 +262,7 @@ cardinal_cubic_b_spline_imp::cardinal_cubic_b_spline_imp(BidiIterator f, B Real t = 1; for (size_t i = 0; i < length; ++i) { - if (boost::math::isnan(f[i])) + if ((boost::math::isnan)(f[i])) { std::string err = "This function you are trying to interpolate is a nan at index " + std::to_string(i) + "\n"; throw std::logic_error(err); diff --git a/include/boost/math/interpolators/detail/cardinal_quadratic_b_spline_detail.hpp b/include/boost/math/interpolators/detail/cardinal_quadratic_b_spline_detail.hpp index 73145b6022..cc105ec92b 100644 --- a/include/boost/math/interpolators/detail/cardinal_quadratic_b_spline_detail.hpp +++ b/include/boost/math/interpolators/detail/cardinal_quadratic_b_spline_detail.hpp @@ -83,7 +83,7 @@ class cardinal_quadratic_b_spline_detail using std::isnan; Real a; - if (isnan(left_endpoint_derivative)) { + if ((isnan)(left_endpoint_derivative)) { // http://web.media.mit.edu/~crtaylor/calculator.html a = -3*y[0] + 4*y[1] - y[2]; } @@ -92,7 +92,7 @@ class cardinal_quadratic_b_spline_detail } Real b; - if (isnan(right_endpoint_derivative)) { + if ((isnan)(right_endpoint_derivative)) { b = 3*y[n-1] - 4*y[n-2] + y[n-3]; } else { diff --git a/include/boost/math/interpolators/detail/cardinal_quintic_b_spline_detail.hpp b/include/boost/math/interpolators/detail/cardinal_quintic_b_spline_detail.hpp index c86c97c4a5..a4f325348c 100644 --- a/include/boost/math/interpolators/detail/cardinal_quintic_b_spline_detail.hpp +++ b/include/boost/math/interpolators/detail/cardinal_quintic_b_spline_detail.hpp @@ -11,7 +11,19 @@ #include #include -namespace boost{ namespace math{ namespace interpolators{ namespace detail{ +namespace boost{ namespace math{ + +namespace tools +{ + // Forward declaration only, we don't need the formatter's + // definition to be able to define a print function (only if + // we actually use it). + template + class basic_numeric_printer_base; +} + + +namespace interpolators{ namespace detail{ template @@ -42,19 +54,19 @@ class cardinal_quintic_b_spline_detail using std::isnan; // This interpolator has error of order h^6, so the derivatives should be estimated with the same error. // See: https://en.wikipedia.org/wiki/Finite_difference_coefficient - if (isnan(left_endpoint_derivatives.first)) { + if ((isnan)(left_endpoint_derivatives.first)) { Real tmp = -49*y[0]/20 + 6*y[1] - 15*y[2]/2 + 20*y[3]/3 - 15*y[4]/4 + 6*y[5]/5 - y[6]/6; left_endpoint_derivatives.first = tmp/h; } - if (isnan(right_endpoint_derivatives.first)) { + if ((isnan)(right_endpoint_derivatives.first)) { Real tmp = 49*y[n-1]/20 - 6*y[n-2] + 15*y[n-3]/2 - 20*y[n-4]/3 + 15*y[n-5]/4 - 6*y[n-6]/5 + y[n-7]/6; right_endpoint_derivatives.first = tmp/h; } - if(isnan(left_endpoint_derivatives.second)) { + if((isnan)(left_endpoint_derivatives.second)) { Real tmp = 469*y[0]/90 - 223*y[1]/10 + 879*y[2]/20 - 949*y[3]/18 + 41*y[4] - 201*y[5]/10 + 1019*y[6]/180 - 7*y[7]/10; left_endpoint_derivatives.second = tmp/(h*h); } - if (isnan(right_endpoint_derivatives.second)) { + if ((isnan)(right_endpoint_derivatives.second)) { Real tmp = 469*y[n-1]/90 - 223*y[n-2]/10 + 879*y[n-3]/20 - 949*y[n-4]/18 + 41*y[n-5] - 201*y[n-6]/10 + 1019*y[n-7]/180 - 7*y[n-8]/10; right_endpoint_derivatives.second = tmp/(h*h); } From c8c07a5325aad3963fcc8aa2162cb6d5d38eaba7 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Sun, 1 Dec 2019 19:48:49 +0000 Subject: [PATCH 14/14] formatting: add barycentric_rational support. --- doc/fp_utilities/formatting.qbk | 24 +- doc/images/barycentric_latex.svg | 5236 +++++++++++++++++ example/Jamfile.v2 | 2 + ...tter_barycentric_interpolation_example.cpp | 95 + .../interpolators/barycentric_rational.hpp | 2 +- .../detail/barycentric_rational_detail.hpp | 168 +- include/boost/math/tools/formatting.hpp | 37 + 7 files changed, 5553 insertions(+), 11 deletions(-) create mode 100644 doc/images/barycentric_latex.svg create mode 100644 example/formatter_barycentric_interpolation_example.cpp diff --git a/doc/fp_utilities/formatting.qbk b/doc/fp_utilities/formatting.qbk index b580f27348..05e3a1facf 100644 --- a/doc/fp_utilities/formatting.qbk +++ b/doc/fp_utilities/formatting.qbk @@ -188,18 +188,24 @@ The following section (using Docbook output format) illustrates how each of the [table:misc Misc Types [[Type][Comments][Result]] -[[cardinal_quadratic_b_spline][Formatted output of this type is primarily for debugging purposes since only - 4 consecutive values of the sum are used for any given abscissa value, where as the - printed form shows all the value of the sum.] +[[cardinal_quadratic_b_spline][Formatted output of this type is primarily for debugging purposes since + despite appearances, only a few terms of the sum are nonzero, since the basis function B_n has compact support.] ['''-B2(10(x - 5) + 1) + 5×B2(10(x - 5)) + 11×B2(10(x - 5) - 1) + 17×B2(10(x - 5) - 2) + 23×B2(10(x - 5) - 3) + 29×B2(10(x - 5) - 4) + 35×B2(10(x - 5) - 5) + 41×B2(10(x - 5) - 6) + 47×B2(10(x - 5) - 7) + 53×B2(10(x - 5) - 8) + 59×B2(10(x - 5) - 9) + 65×B2(10(x - 5) - 10)''']] -[[cardinal_cubic_b_spline][Formatted output of this type is primarily for debugging purposes since only - 4 consecutive values of the sum are used for any given abscissa value, where as the - printed form shows all the value of the sum.] +[[cardinal_cubic_b_spline][Formatted output of this type is primarily for debugging purposes since + despite appearances, only a few terms of the sum are nonzero, since the basis function B_n has compact support.] ['''32 - 33×B3(10(x - 5) + 1) - 27×B3(10(x - 5)) - 21×B3(10(x - 5) - 1) - 15×B3(10(x - 5) - 2) - 9×B3(10(x - 5) - 3) - 3×B3(10(x - 5) - 4) + 3×B3(10(x - 5) - 5) + 9×B3(10(x - 5) - 6) + 15×B3(10(x - 5) - 7) + 21×B3(10(x - 5) - 8) + 27×B3(10(x - 5) - 9) + 33×B3(10(x - 5) - 10)''']] -[[cardinal_quintic_b_spline][Formatted output of this type is primarily for debugging purposes since only - 6 consecutive values of the sum are used for any given abscissa value, where as the - printed form shows all the value of the sum.] +[[cardinal_quintic_b_spline][Formatted output of this type is primarily for debugging purposes since + despite appearances, only a few terms of the sum are nonzero, since the basis function B_n has compact support.] ['''-7×B5(10(x - 5) + 2) - B5(10(x - 5) + 3) + 5×B5(10(x - 5) + 4) + 11×B5(10(x - 5) + 5) + 17×B5(10(x - 5) + 6) + 23×B5(10(x - 5) + 7) + 29×B5(10(x - 5) + 8) + 35×B5(10(x - 5) + 9) + 41×B5(10(x - 5) + 10) + 47×B5(10(x - 5) + 11) + 53×B5(10(x - 5) + 12) + 59×B5(10(x - 5) + 13) + 65×B5(10(x - 5) + 14) + 71×B5(10(x - 5) + 15)''']] +[[barycentric_rational][While usefule for debugging purposes, the formatted output can be rather verbose, + users may find it convenient to output to LaTex and cut and paste into an online processor for improved display.] + [Docbook: + + '''P(x)Q(x) ; P(x) = 5.727×-20833.3(x - 0.02) + 5.544×83333.3(x - 0.04) + 5.45×-145833(x - 0.06) + 5.351×166667(x - 0.08) + 5.253×-166667(x - 0.1) + 5.157×166667(x - 0.12) + 5.058×-166667(x - 0.14) + 4.96×161458(x - 0.16) + 4.862×-133333(x - 0.18) + 4.762×70312.5(x - 0.2) + 4.563×-26041.7(x - 0.24) + 4.36×21354.2(x - 0.28) + 4.1584×-20833.3(x - 0.32) + 3.9463×20833.3(x - 0.36) + 3.736×-20833.3(x - 0.4) + 3.5429×20833.3(x - 0.44) + 3.3797×-20833.3(x - 0.48) + 3.2417×20182.3(x - 0.52) + 3.1209×-16666.7(x - 0.56) + 3.0138×8789.06(x - 0.6) + 2.8342×-3255.21(x - 0.68) + 2.6881×2669.27(x - 0.76) + 2.5662×-2604.17(x - 0.84) + 2.4242×2604.17(x - 0.92) + 2.3766×-2604.17(x - 1) + 2.3058×2604.17(x - 1.08) + 2.2458×-2604.17(x - 1.16) + 2.2035×2604.17(x - 1.24) + 2.1661×-2522.79(x - 1.32) + 2.135×2083.33(x - 1.4) + 2.109×-1098.63(x - 1.48) + 2.0697×406.901(x - 1.64) + 2.0466×-333.659(x - 1.8) + 2.0325×325.521(x - 1.96) + 2.0288×-325.521(x - 2.12) + 2.0292×325.521(x - 2.28) + 2.0228×-325.521(x - 2.44) + 2.0124×325.521(x - 2.6) + 2.0065×-325.521(x - 2.76) + 2.0031×325.521(x - 2.92) + 2.0015×-325.521(x - 3.08) + 2.0008×325.521(x - 3.24) + 2.0004×-284.831(x - 3.4) + 2.0002×162.76(x - 3.56) + 2.0001×-40.6901(x - 3.72) ∧ Q(x) = 5.727(x - 0.02) + 5.544(x - 0.04) + 5.45(x - 0.06) + 5.351(x - 0.08) + 5.253(x - 0.1) + 5.157(x - 0.12) + 5.058(x - 0.14) + 4.96(x - 0.16) + 4.862(x - 0.18) + 4.762(x - 0.2) + 4.563(x - 0.24) + 4.36(x - 0.28) + 4.1584(x - 0.32) + 3.9463(x - 0.36) + 3.736(x - 0.4) + 3.5429(x - 0.44) + 3.3797(x - 0.48) + 3.2417(x - 0.52) + 3.1209(x - 0.56) + 3.0138(x - 0.6) + 2.8342(x - 0.68) + 2.6881(x - 0.76) + 2.5662(x - 0.84) + 2.4242(x - 0.92) + 2.3766(x - 1) + 2.3058(x - 1.08) + 2.2458(x - 1.16) + 2.2035(x - 1.24) + 2.1661(x - 1.32) + 2.135(x - 1.4) + 2.109(x - 1.48) + 2.0697(x - 1.64) + 2.0466(x - 1.8) + 2.0325(x - 1.96) + 2.0288(x - 2.12) + 2.0292(x - 2.28) + 2.0228(x - 2.44) + 2.0124(x - 2.6) + 2.0065(x - 2.76) + 2.0031(x - 2.92) + 2.0015(x - 3.08) + 2.0008(x - 3.24) + 2.0004(x - 3.4) + 2.0002(x - 3.56) + 2.0001(x - 3.72)''' + + Latex: + + [$../images/barycentric_latex.svg]]] ] [heading Output Format Gallery] diff --git a/doc/images/barycentric_latex.svg b/doc/images/barycentric_latex.svg new file mode 100644 index 0000000000..a189eada26 --- /dev/null +++ b/doc/images/barycentric_latex.svg @@ -0,0 +1,5236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index 362e9911d8..3f38040f23 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -155,6 +155,8 @@ test-suite examples : [ run formatter_html_output.cpp : $(here)/formatter_html_output.html : : [ requires cxx11_constexpr cxx14_variable_templates ] ] [ run formatter_docbook_output.cpp : $(here)/formatter_docbook_output.qbk : : [ requires cxx11_constexpr cxx14_variable_templates ] ] [ run formatter_text_output.cpp : $(here)/formatter_text_output.txt : : [ requires cxx11_constexpr cxx14_variable_templates ] ] + [ run formatter_barycentric_interpolation_example.cpp : : : [ requires cxx11_constexpr cxx14_variable_templates ] ] + [ run formatter_b_spline_output.cpp : : : [ requires cxx11_constexpr cxx14_variable_templates ] ] ; run root_elliptic_finding.cpp /boost/timer : : : release static [ requires cxx11_unified_initialization_syntax cxx11_defaulted_functions ] freebsd:"-lrt" linux:"-lrt -lpthread" ; diff --git a/example/formatter_barycentric_interpolation_example.cpp b/example/formatter_barycentric_interpolation_example.cpp new file mode 100644 index 0000000000..3597942f72 --- /dev/null +++ b/example/formatter_barycentric_interpolation_example.cpp @@ -0,0 +1,95 @@ + +// Copyright Nick Thompson, 2017 + +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or +// copy at http://www.boost.org/LICENSE_1_0.txt). + +#include +#include +#include +#include + +//[barycentric_rational_example + +/*` +This example shows how to use barycentric rational interpolation, using Walter Kohn's classic paper +"Solution of the Schrodinger Equation in Periodic Lattices with an Application to Metallic Lithium" +In this paper, Kohn needs to repeatedly solve an ODE (the radial Schrodinger equation) given a potential +which is only known at non-equally samples data. + +If he'd only had the barycentric rational interpolant of Boost.Math! + +References: Kohn, W., and N. Rostoker. "Solution of the Schrodinger equation in periodic lattices with an application to metallic lithium." Physical Review 94.5 (1954): 1111. +*/ + +#include + +int main() +{ + // The lithium potential is given in Kohn's paper, Table I: + std::vector r(45); + std::vector mrV(45); + + r[0] = 0.02; mrV[0] = 5.727; + r[1] = 0.04, mrV[1] = 5.544; + r[2] = 0.06, mrV[2] = 5.450; + r[3] = 0.08, mrV[3] = 5.351; + r[4] = 0.10, mrV[4] = 5.253; + r[5] = 0.12, mrV[5] = 5.157; + r[6] = 0.14, mrV[6] = 5.058; + r[7] = 0.16, mrV[7] = 4.960; + r[8] = 0.18, mrV[8] = 4.862; + r[9] = 0.20, mrV[9] = 4.762; + r[10] = 0.24, mrV[10] = 4.563; + r[11] = 0.28, mrV[11] = 4.360; + r[12] = 0.32, mrV[12] = 4.1584; + r[13] = 0.36, mrV[13] = 3.9463; + r[14] = 0.40, mrV[14] = 3.7360; + r[15] = 0.44, mrV[15] = 3.5429; + r[16] = 0.48, mrV[16] = 3.3797; + r[17] = 0.52, mrV[17] = 3.2417; + r[18] = 0.56, mrV[18] = 3.1209; + r[19] = 0.60, mrV[19] = 3.0138; + r[20] = 0.68, mrV[20] = 2.8342; + r[21] = 0.76, mrV[21] = 2.6881; + r[22] = 0.84, mrV[22] = 2.5662; + r[23] = 0.92, mrV[23] = 2.4242; + r[24] = 1.00, mrV[24] = 2.3766; + r[25] = 1.08, mrV[25] = 2.3058; + r[26] = 1.16, mrV[26] = 2.2458; + r[27] = 1.24, mrV[27] = 2.2035; + r[28] = 1.32, mrV[28] = 2.1661; + r[29] = 1.40, mrV[29] = 2.1350; + r[30] = 1.48, mrV[30] = 2.1090; + r[31] = 1.64, mrV[31] = 2.0697; + r[32] = 1.80, mrV[32] = 2.0466; + r[33] = 1.96, mrV[33] = 2.0325; + r[34] = 2.12, mrV[34] = 2.0288; + r[35] = 2.28, mrV[35] = 2.0292; + r[36] = 2.44, mrV[36] = 2.0228; + r[37] = 2.60, mrV[37] = 2.0124; + r[38] = 2.76, mrV[38] = 2.0065; + r[39] = 2.92, mrV[39] = 2.0031; + r[40] = 3.08, mrV[40] = 2.0015; + r[41] = 3.24, mrV[41] = 2.0008; + r[42] = 3.40, mrV[42] = 2.0004; + r[43] = 3.56, mrV[43] = 2.0002; + r[44] = 3.72, mrV[44] = 2.0001; + + // Now we want to interpolate this potential at any r: + boost::math::barycentric_rational b(r.data(), mrV.data(), r.size()); + + boost::math::tools::text_printer printer(std::cout); + printer << b << std::endl << std::endl; + + boost::math::tools::latex_printer lprinter(std::cout); + lprinter << b << std::endl << std::endl; + + boost::math::tools::html_printer hprinter(std::cout); + hprinter << b << std::endl << std::endl; + + boost::math::tools::docbook_printer dprinter(std::cout); + dprinter << b << std::endl << std::endl; + +} diff --git a/include/boost/math/interpolators/barycentric_rational.hpp b/include/boost/math/interpolators/barycentric_rational.hpp index e175679b55..cccf85c7f9 100644 --- a/include/boost/math/interpolators/barycentric_rational.hpp +++ b/include/boost/math/interpolators/barycentric_rational.hpp @@ -56,7 +56,7 @@ class barycentric_rational return m_imp->return_y(); } - template + template friend void print(boost::math::tools::basic_numeric_printer_base& os, const barycentric_rational& bar) { print(os, *bar.m_imp); diff --git a/include/boost/math/interpolators/detail/barycentric_rational_detail.hpp b/include/boost/math/interpolators/detail/barycentric_rational_detail.hpp index b4199ea266..d0f278a553 100644 --- a/include/boost/math/interpolators/detail/barycentric_rational_detail.hpp +++ b/include/boost/math/interpolators/detail/barycentric_rational_detail.hpp @@ -16,7 +16,18 @@ #include #include -namespace boost{ namespace math{ namespace detail{ +namespace boost{ namespace math{ + +namespace tools +{ + // Forward declaration only, we don't need the formatter's + // definition to be able to define a print function (only if + // we actually use it). + template + class basic_numeric_printer_base; +} + +namespace detail{ template class barycentric_rational_imp @@ -44,6 +55,161 @@ class barycentric_rational_imp return std::move(m_y); } + template + friend void print(boost::math::tools::basic_numeric_printer_base& os, const barycentric_rational_imp& bar) + { + typedef boost::math::tools::basic_numeric_printer_base printer_t; + // + // scoped_prolog is responsible for writing the prolog/epilog code + // to the stream, if the output format requires it. Not a number in it's own + // right, but an expression, so set the name to the empty string and let each + // individual number style itself. + // + typename printer_t::scoped_prolog prolog(&os, ""); + // + // scoped_parenthesis instructs nested types to wrap themselves in () + // if they are compound rather than atomic number types (for example complex numbers): + // + typename printer_t::scoped_parenthesis param(&os); + + { + // This just prints: + // P(x) / Q(x) + std::basic_stringstream ss; + ss.copyfmt(os.stream()); + ss.imbue(os.stream().getloc()); + ss.width(0); + std::unique_ptr > fmt(os.clone(ss)); + fmt->print_name("P"); + fmt->stream() << "("; + fmt->print_variable('x'); + fmt->stream() << ")"; + + std::basic_stringstream ss2; + ss2.copyfmt(os.stream()); + ss2.imbue(os.stream().getloc()); + ss2.width(0); + std::unique_ptr > fmt2(os.clone(ss2)); + fmt2->print_name("Q"); + fmt2->stream() << "("; + fmt2->print_variable('x'); + fmt2->stream() << ")"; + + os.print_fraction(ss.str(), ss2.str()); + } + // " ; P(x) = " + os.stream() << " ; "; + os.print_name("P"); + os.stream() << "("; + os.print_variable('x'); + os.stream() << ") = "; + + for (size_t i = 0; i < bar.m_x.size(); ++i) + { + auto y = bar.m_y[i]; + if (i) + { + if (y > 0) + os.stream() << " + "; + else + { + y = -y; + os.stream() << " - "; + } + } + std::basic_string num, denom; + { + // m_y[i] * m_w[i] + // + std::basic_stringstream ss; + ss.copyfmt(os.stream()); + ss.imbue(os.stream().getloc()); + ss.width(0); + std::unique_ptr > fmt(os.clone(ss)); + print(*fmt, y); + fmt->print_times(); + print(*fmt, bar.m_w[i]); + num = ss.str(); + } + { + // (x - m_x[i]) + // + auto x = bar.m_x[i]; + std::basic_stringstream ss; + ss.copyfmt(os.stream()); + ss.imbue(os.stream().getloc()); + ss.width(0); + std::unique_ptr > fmt(os.clone(ss)); + ss << "("; + fmt->print_variable('x'); + if (x < 0) + { + x = -x; + ss << " + "; + } + else + ss << " - "; + print(*fmt, x); + ss << ")"; + denom = ss.str(); + } + os.print_fraction(num, denom); + } + // " Q(x) = " + os.stream() << " "; + os.print_special_character(0x2227); + os.stream() << " "; + os.print_name("Q"); + os.stream() << "("; + os.print_variable('x'); + os.stream() << ") = "; + + for (size_t i = 0; i < bar.m_x.size(); ++i) + { + auto y = bar.m_y[i]; + if (i) + { + if (y > 0) + os.stream() << " + "; + else + { + y = -y; + os.stream() << " - "; + } + } + std::basic_string num, denom; + num = os.part_as_string(y); + { + // (x - m_x[i]) + // + auto x = bar.m_x[i]; + std::basic_stringstream ss; + ss.copyfmt(os.stream()); + ss.imbue(os.stream().getloc()); + ss.width(0); + std::unique_ptr > fmt(os.clone(ss)); + ss << "("; + fmt->print_variable('x'); + if (x < 0) + { + x = -x; + ss << " + "; + } + else + ss << " - "; + print(*fmt, x); + ss << ")"; + denom = ss.str(); + } + os.print_fraction(num, denom); + } +#if 0 + Real t = m_w[i] / (x - m_x[i]); + numerator += t * m_y[i]; + denominator += t; +#endif + } + private: void calculate_weights(size_t approximation_order); diff --git a/include/boost/math/tools/formatting.hpp b/include/boost/math/tools/formatting.hpp index 716866ad36..126acb44c4 100644 --- a/include/boost/math/tools/formatting.hpp +++ b/include/boost/math/tools/formatting.hpp @@ -174,6 +174,29 @@ namespace boost { case '-': print_unicode_char(os, 0x207B); break; + case '(': + print_unicode_char(os, 0x207D); + break; + case ')': + print_unicode_char(os, 0x207E); + break; + case 'n': + print_unicode_char(os, 0x207F); + break; + case '=': + print_unicode_char(os, 0x207C); + break; + case 'i': + print_unicode_char(os, 0x2071); + break; + case 'x': + print_unicode_char(os, 0x02e3); + break; + case '.': + print_unicode_char(os, 0x22C5); + break; + default: + os.put(digits[i]); } } } @@ -189,6 +212,14 @@ namespace boost { print_unicode_char(os, 0x208B); else if (digits[i] == '+') print_unicode_char(os, 0x208A); + else if (digits[i] == '(') + print_unicode_char(os, 0x208D); + else if (digits[i] == ')') + print_unicode_char(os, 0x208E); + else if (digits[i] == 'n') + print_unicode_char(os, 0x2099); + else if (digits[i] == 'x') + print_unicode_char(os, 0x2093); else os.put(digits[i]); } @@ -897,6 +928,12 @@ namespace boost { case 0x3c3: this->stream() << "\\sigma"; break; + case 0x2227: + this->stream() << "\\land"; + break; + case 0x2228: + this->stream() << "\\lor"; + break; default: throw std::runtime_error("Unsuported Unicode character encountered in LaTex output"); }