Commit 772f3dd4 authored by Michael Tesch's avatar Michael Tesch
Browse files

Merge branch 'master' of gitlab.com:tesch1/cppduals

parents bdf4984d 8a9dec54
Loading
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -120,7 +120,7 @@ if (CPPDUALS_TESTING)

  # build the thirdparty
  message ("***************************************************************")
  message ("********** Building '${PROJECT_BINARY_DIR}/thirdparty'...")
  message ("** Building '${PROJECT_BINARY_DIR}/thirdparty'...")
  message ("***************************************************************")
  execute_process (
    WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/thirdparty"
@@ -133,6 +133,7 @@ if (CPPDUALS_TESTING)
  if (DEPS_BUILD_RESULT)
    message ("***************************************************************")
    message (FATAL_ERROR "Building dependencies failed: ${DEPS_BUILD_RESULT}")
    message ("***************************************************************")
  endif ()

  add_subdirectory (thirdparty)
@@ -220,4 +221,3 @@ if (ETAGS)
    COMMAND ${ETAGS} --language=c++ --append `find ${PROJECT_BINARY_DIR}/thirdparty/eigenX/src/eigenX -type f`
    )
endif (ETAGS)
+23 −8
Original line number Diff line number Diff line
@@ -103,15 +103,11 @@ family of commands and modifying the global preprocessor search path:
  include(ExternalProject)

  # Have CMake download the library
  set (CPPDUALS_VER 0.1.2)
  set (CPPDUALS_MD5 48d9276482e5f07a768875ef692d14cd)
  set (CPPDUALS_MD5 2c4049fa5738ba82bb6c4ba13a77ff43)
  ExternalProject_Add (cppduals
    URL https://gitlab.com/tesch1/cppduals/-/archive/v${CPPDUALS_VER}/cppduals-v${CPPDUALS_VER}.tar.bz2
    URL https://gitlab.com/tesch1/cppduals/uploads/f2e1f3490fcbe862124c89394097eae3/cppduals-0.2.0.tgz
    URL_HASH MD5=${CPPDUALS_MD5}
    CONFIGURE_COMMAND ""
    BUILD_COMMAND ""
    INSTALL_COMMAND ""
    )
    CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" )

  # Make include directory globally visible
  ExternalProject_Get_Property (cppduals source_dir)
@@ -155,7 +151,7 @@ The benchmark compares cppduals against a local BLAS implementation,
by default OpenBLAS (whose development package is required;
RedHat-flavor: `openblas-devel`, Debian-flavor: `openblas-dev`).  If
you wish to build the benchmarks against a different installation of
BLAS, the following CMake variables can be used at configuration:
BLAS, the following CMake variables can be set at configuration time:

- [BLA_VENDOR](https://cmake.org/cmake/help/latest/module/FindBLAS.html)
- BLAS_DIR
@@ -295,6 +291,25 @@ derived from the [Eigen
project](http://eigen.tuxfamily.org/index.php?title=Main_Page), and
thus licensed under [MPL-2](http://www.mozilla.org/MPL/2.0/FAQ.html) .

ChangeLog
=========

v0.3.0
======

- vastly improved cmake support, thanks to
  [Jeff](https://gitlab.com/flying-tiger).  The improvements required
  changing some CMake target names.
- Added basic optional [libfmt](https://github.com/fmtlib/fmt) support for
  duals::dual<> and std::complex<>, enabled with `#define`\ s

v0.2.0
======

- fixed build on VS2017
- save and restore signam and errno in {t,l}gamma
- fixes from Nestor D. for https://gitlab.com/tesch1/cppduals/issues/5 (spurious nan)

Todo
====

+146 −4
Original line number Diff line number Diff line
@@ -1071,12 +1071,20 @@ operator>>(std::basic_istream<CharT, Traits> & is, dual<T> & x)
      T r;
      is >> r;
      if (!is.fail()) {
        ws(is);
        CharT c = is.peek();
        if (c == CharT('+') || c == CharT('-')) {
        if (c != CharT('_')) {
          ws(is);
          c = is.peek();
        }
        if (c == CharT('+') || c == CharT('-') || c == CharT('_')) {
          if (c == CharT('+'))
            is.get();
          T d;
          if (c == CharT('_')) {
            d = r;
            r = 0;
          }
          else
            is >> d;
          if (!is.fail()) {
            ws(is);
@@ -1136,7 +1144,6 @@ operator>>(std::basic_istream<CharT, Traits> & is, dual<T> & x)
  return is;
}


#if __cpp_user_defined_literals >= 200809
/// Dual number literals in namespace duals::literals
inline namespace literals
@@ -1181,6 +1188,141 @@ typedef std::complex<dualld> cdualld;

} // namespace duals

#ifdef CPPDUALS_LIBFMT
#include <fmt/format.h>
/// duals::dual<> Formatter for libfmt https://github.com/fmtlib/fmt
///
/// Formats a dual number (r,d) as (r+d_e), offering the same
/// formatting options as the underlying type - with the addition of
/// three optional format options, only one of which may appear
/// directly after the ':' in the format spec: '$', '*', and ',".  The
/// '*' flag changes the separating _ to a *, producing (r+d*e), where
/// r and d are the formatted value_type values.  The ',' flag simply
/// prints the real and dual parts separated by a comma.  As a
/// concrete exmple, this formatter can produce either (3+5.4_e) or
/// (3+5.4*e) or (3,5.4) for a dual<double> using the specs {:g},
/// {:*g}, or {:,g}, respectively.  When the '*' is NOT specified, the
/// output should be compatible with the input operator>> and the dual
/// literals below. (this implementation is a bit hacky - glad for
/// cleanups).
template <typename T, typename Char>
struct fmt::formatter<duals::dual<T>,Char> : public fmt::formatter<T,Char>
{
  typedef fmt::formatter<T,Char> base;
  enum style { expr, star, pair } style_ = expr;
  internal::dynamic_format_specs<Char> specs_;
  constexpr auto parse(format_parse_context & ctx) -> decltype(ctx.begin()) {
    using handler_type = internal::dynamic_specs_handler<format_parse_context>;
    auto type = internal::type_constant<T, Char>::value;
    internal::specs_checker<handler_type> handler(handler_type(specs_, ctx), type);
    auto it = ctx.begin();
    switch (*it) {
    case '$': style_ = style::expr; ctx.advance_to(++it); break;
    case '*': style_ = style::star; ctx.advance_to(++it); break;
    case ',': style_ = style::pair; ctx.advance_to(++it); break;
    default: break;
    }
    parse_format_specs(ctx.begin(), ctx.end(), handler);
    return base::parse(ctx);
  }
  template <typename FormatCtx>
  auto format(const duals::dual<T> & x, FormatCtx & ctx) -> decltype(ctx.out()) {
    format_to(ctx.out(), "(");
    if (x.rpart() || !x.dpart())
      base::format(x.rpart(), ctx);
    if (x.dpart()) {
      if (style_ == style::pair)
        format_to(ctx.out(), ",");
      else
        if (x.rpart() && x.dpart() >= 0 && specs_.sign != sign::plus)
          format_to(ctx.out(), "+");
      base::format(x.dpart(), ctx);
      if (style_ != style::pair) {
        if (style_ == style::star)
          format_to(ctx.out(), "*e");
        else
          format_to(ctx.out(), "_e");
        if (std::is_same<typename std::decay<T>::type,float>::value)       format_to(ctx.out(), "f");
        if (std::is_same<typename std::decay<T>::type,long double>::value) format_to(ctx.out(), "l");
      }
    }
    return format_to(ctx.out(), ")");
  }
};
#endif

#ifdef CPPDUALS_LIBFMT_COMPLEX
#ifndef CPPDUALS_LIBFMT
#include <fmt/format.h>
#endif
/// std::complex<> Formatter for libfmt https://github.com/fmtlib/fmt
///
/// libfmt does not provide a formatter for std::complex<>, although
/// one is proposed for c++20.  Anyway, at the expense of a k or two,
/// you can define CPPDUALS_LIBFMT_COMPLEX and get this one.
///
/// The standard iostreams formatting of complex numbers is (a,b),
/// where a and b are the real and imaginary parts.  This formats a
/// complex number (a+bi) as (a+bi), offering the same formatting
/// options as the underlying type - with the addition of three
/// optional format options, only one of which may appear directly
/// after the ':' in the format spec (before any fill or align): '$'
/// (the default if no flag is specified), '*', and ',".  The '*' flag
/// adds a * before the 'i', producing (a+b*i), where a and b are the
/// formatted value_type values.  The ',' flag simply prints the real
/// and complex parts separated by a comma (same as iostreams' format).
/// As a concrete exmple, this formatter can produce either (3+5.4i)
/// or (3+5.4*i) or (3,5.4) for a complex<double> using the specs {:g}
/// | {:$g}, {:*g}, or {:,g}, respectively.  (this implementation is a
/// bit hacky - glad for cleanups).
///
template <typename T, typename Char>
struct fmt::formatter<std::complex<T>,Char> : public fmt::formatter<T,Char>
{
  typedef fmt::formatter<T,Char> base;
  enum style { expr, star, pair } style_ = expr;
  internal::dynamic_format_specs<Char> specs_;
  constexpr auto parse(format_parse_context & ctx) -> decltype(ctx.begin()) {
    using handler_type = internal::dynamic_specs_handler<format_parse_context>;
    auto type = internal::type_constant<T, Char>::value;
    internal::specs_checker<handler_type> handler(handler_type(specs_, ctx), type);
    auto it = ctx.begin();
    switch (*it) {
    case '$': style_ = style::expr; ctx.advance_to(++it); break;
    case '*': style_ = style::star; ctx.advance_to(++it); break;
    case ',': style_ = style::pair; ctx.advance_to(++it); break;
    default: break;
    }
    parse_format_specs(ctx.begin(), ctx.end(), handler);
    //todo: fixup alignment
    return base::parse(ctx);
  }
  template <typename FormatCtx>
  auto format(const std::complex<T> & x, FormatCtx & ctx) -> decltype(ctx.out()) {
    format_to(ctx.out(), "(");
    if (x.real() || !x.imag())
      base::format(x.real(), ctx);
    if (x.imag()) {
      if (style_ == style::pair)
        format_to(ctx.out(), ",");
      else
        if (x.real() && x.imag() >= 0 && specs_.sign != sign::plus)
          format_to(ctx.out(), "+");
      base::format(x.imag(), ctx);
      if (style_ != style::pair) {
        if (style_ == style::star)
          format_to(ctx.out(), "*i");
        else
          format_to(ctx.out(), "i");
        if (std::is_same<typename std::decay<T>::type,float>::value)       format_to(ctx.out(), "f");
        if (std::is_same<typename std::decay<T>::type,long double>::value) format_to(ctx.out(), "l");
      }
    }
    return format_to(ctx.out(), ")");
  }
};
#endif

#ifdef _LIBCPP_BEGIN_NAMESPACE_STD
_LIBCPP_BEGIN_NAMESPACE_STD
#else
+3 −2
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ set (OPT_FLAGS "${OPT_FLAGS};-DCPPDUALS_VECTORIZE_CDUAL")

set (ALL_TESTS
  test_dual test_funcs test_eigen test_packets
  test_vectorize test_solve test_expm test_1
  test_vectorize test_solve test_expm test_1 test_fmt
  example
  )
set (ALL_TEST_BINS )
@@ -73,7 +73,7 @@ foreach (TEST_ ${ALL_TESTS})
    string (REPLACE ";" ", " L2 "${CMAKE_CXX_FLAGS}")
    target_compile_options (${TEST} PUBLIC ${PHASE_FLAGS})
    target_compile_definitions (${TEST} PRIVATE "OPT_FLAGS=${L2}")
    target_link_libraries (${TEST} gtest_main cppduals_coverage_config )
    target_link_libraries (${TEST} gtest_main cppduals_coverage_config fmt::fmt)
    #target_link_libraries (${TEST} -lasan)
    add_dependencies (${TEST} eigenX expokitX)
    gtest_discover_tests (${TEST} TEST_LIST ${TEST}_targets)
@@ -82,6 +82,7 @@ foreach (TEST_ ${ALL_TESTS})
  endforeach (PHASE)
endforeach (TEST_)

target_compile_features (test_fmt_1 PUBLIC cxx_std_14)

if (CPPDUALS_BENCHMARK)
  #
+42 −2
Original line number Diff line number Diff line
@@ -642,18 +642,28 @@ TEST(non_class, putto) {
    s << 2 + 3_ef;
    EXPECT_EQ(s.str(), "(2+3_ef)");
  }
  //#ifndef _MSC_VER // hoping for a patch to fix this
  {
    std::stringstream s;
    s << 2 + 3_e;
    EXPECT_EQ(s.str(), "(2+3_e)");
  }
  //#endif
  {
    std::stringstream s;
    s << 2 + 3_el;
    EXPECT_EQ(s.str(), "(2+3_el)");
  }
#if 0 // seems reasonable
  {
    std::stringstream s;
    s << 3_e;
    EXPECT_EQ(s.str(), "(3_e)");
  }
#endif
  {
    std::stringstream s;
    s << std::fixed << std::setprecision(10) << 10 + 2_e;
    EXPECT_EQ(s.str(), "(10.0000000000+2.0000000000_e)");
  }
  {
    std::stringstream s;
    s << cdualf(2 + 3_ef, 4 + 5_ef);
@@ -702,6 +712,22 @@ TEST(non_class, getfro) {
    EXPECT_EQ(x.rpart(), 2);
    EXPECT_EQ(x.dpart(), 0);
  }
  {
    std::stringstream s("(2_e) ");
    duald x(1,8);
    s >> x;
    EXPECT_TRUE(s);
    EXPECT_EQ(x.rpart(), 0);
    EXPECT_EQ(x.dpart(), 2);
  }
  {
    std::stringstream s("(3_e ) ");
    duald x(1,8);
    s >> x;
    EXPECT_TRUE(s);
    EXPECT_EQ(x.rpart(), 0);
    EXPECT_EQ(x.dpart(), 3);
  }
  {
    std::stringstream s(" (2.3+ 3_el)");
    dualld x;
@@ -855,6 +881,20 @@ TEST(non_class, getfro) {
    EXPECT_FALSE(s);
    EXPECT_EQ(x, cdualf());
  }
  {
    std::stringstream s("(1_) ");
    cdualf x;
    s >> x;
    EXPECT_FALSE(s);
    EXPECT_EQ(x, cdualf());
  }
  {
    std::stringstream s("(1.3 _e) ");
    cduald x;
    s >> x;
    EXPECT_FALSE(s);
    EXPECT_EQ(x, cduald());
  }

}

Loading