<commit_before>/** @file
 *
 * Client/server program for the drive test.
 */
#include <cstdio>
#include "Chassis_Control/DriveData.h"

/* Manually disable client mode on ARM machines (e.g. the Raspberry Pi, which
   gives us enough grief anyway... */
#if defined(__arm__) && ! defined(DISABLE_CLIENT)
# define DISABLE_CLIENT 1
#endif

/* ****************************************************************
 * Help text stuff.
 */
#if ! defined(DISABLE_CLIENT)
#  define PRINT_USAGE(stream) fprintf(stream, "Usage: %s [OPTION]... ADDRESS PORT [EVDEV]\n", argv[0])

#  define HELP_TEXT "\
Simple controls-test program.\n\
\n\
Options:\n\
  -h	Show this help.\n\
\n\
Client mode is assumed when EVDEV argument is present; otherwise server mode is\n\
enabled.\n"
#else
#  define PRINT_USAGE(stream) fprintf(stream, "Usage: %s [OPTION]... ADDRESS PORT\n", argv[0])
#  define HELP_TEXT "\
Simple controls-test program: server-only build.\n\
\n\
Options:\n\
  -h	Show this help.\n\
\n"
#endif  /* ! defined(DISABLE_CLIENT) */
/* **************************************************************** */

#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <crisp/comms/BasicNode.hh>
#include <crisp/comms/NodeServer.hh>

/* Define some aliases for our communication-types for convenience. */
using namespace crisp::comms;
typedef BasicNode<boost::asio::ip::tcp> Node;
typedef typename Node::Protocol Protocol;
typedef typename Protocol::endpoint Endpoint;

#include "gnublin_i2c.hpp"

#define I2C_BUS "/dev/i2c-0"
#define I2C_SLAVE_ADDRESS 5

static int
run_server(boost::asio::io_service& service,
           const Endpoint& target_endpoint)

{
  crisp::comms::NodeServer<Node> server ( service, target_endpoint );

  gnublin_i2c i2c ( I2C_BUS, I2C_SLAVE_ADDRESS );

  /* I don't trust this "Gnublin" code (it's terribly written and I had to fix
     it to even get it to compile).   We'll be replacing it eventually. */
  i2c.setDevicefile(I2C_BUS);
  i2c.setAddress(I2C_SLAVE_ADDRESS);

  if ( i2c.fail() )             /* Won't happen because the Gnublin stuff opens
                                   the device on-demand.  /Le sigh.../ */
    return 1;

  DriveData data;
  unsigned char* data_pointer = reinterpret_cast<unsigned char*>(&data);
  memset(data_pointer, 0, sizeof(data));

  using namespace crisp::comms::keywords;

  /* Declare our interface configuration. */
  server.configuration.add_module( "drive", 2)
    .add_input<int8_t>({ "left",  { _neutral = 0, _minimum = -127, _maximum = 128 } })
    .add_input<int8_t>({ "right", { _neutral = 0, _minimum = -127, _maximum = 128 } });

  /* Override the handler run when we receive a module-control packet. */
  server.dispatcher.module_control.received.clear();
  server.dispatcher.module_control.received.connect(
    [&](Node& node, const crisp::comms::ModuleControl& mc)
    {
      fprintf(stderr, "[0x%0x] \033[1;33mModule-control received\033[0m:", THREAD_ID);

      const crisp::comms::DataValue<> *dvl ( nullptr ), *dvr ( nullptr );

      /* Update our input-value array. */
      if ( (dvl = mc.value_for("left")) != nullptr )
        {
          data.left_motor = dvl->get<int8_t>();
          fprintf(stderr, "%d", data.left_motor);
        }
      dvr = mc.value_for("right");

      if ( dvl && dvr )
        fputs(", ", stderr);

      if ( dvr )
        {
          data.right_motor = dvr->get<int8_t>();
          fprintf(stderr, "%d\n", data.right_motor);
        }

      /* This call here is just so EW EW EW EW EW.  There's a reason for it, of
         course, but that reason is irrelevant with the way we're using I2C. */
      i2c.send(data_pointer, sizeof(data));

      if ( i2c.fail() )
        {
          perror("write");
        }
    });

  server.run();
  return 0;
}

/* Disable client functionality on the Raspberry Pi.

   _With_ this code (and using the old controller code based on the
   terribly-bloated Boost.Signals2 library), I had trouble getting the program
   to compile on the Pi without running out of memory.

   We don't need to support client mode on the Pi, so we might as well keep it
   disabled anyway.
 */
#if ! defined(DISABLE_CLIENT)

#include <crisp/input/EvDevController.hh>
#include <float.h>

/** Update a ModuleControl instance to reflect the input coordinates.
 *
 * @param mc ModuleControl to update.
 *
 * @param left Value of left stick y axis.
 *
 * @param right Value of right stick y axis.
 *
 */
void
update_control(crisp::comms::ModuleControl& mc,
               float left, float right)
{
  mc.set<int8_t>("left", static_cast<int8_t>(left * 127));
  mc.set<int8_t>("right", static_cast<int8_t>(right * 127));
}

/** Run the client code, connecting to the specified endpoint and taking input
 *  from the specified Linux `evdev` input device.
 */
static int
run_client(boost::asio::io_service& service,
           const Endpoint& target_endpoint,
           const char* evdev)
{
  /* Create a socket, and try to connect it to the specified endpoint. */
  boost::system::error_code ec;
  typename Protocol::socket socket ( service );
  typename Protocol::resolver resolver ( service );

  boost::asio::connect(socket, resolver.resolve(target_endpoint), ec);

  if ( ec )
    { fprintf(stderr, "connect: %s\n", strerror(ec.value()));
      return 1; }
  else
    {
      /* We're going to be modifying the ModuleControl instance on-demand
         (whenever the input controller changes state), but sending it at a
         fixed rate.  Since this will be happening in different threads and
         there's no easy way to coordinate the memory access and modification
         otherwise,  we need a mutex to keep everything in sync.

         (If we didn't do this, we could e.g., get halfway through updating the
         module control instance, send a control packet based on that [likely
         invalid] data, and then finish updating.)
      */
      std::mutex mutex;
      ModuleControl mc;
      bool have_config ( false );
      float left, right;

      /* Create the input device. */
      using namespace crisp::input;
      EvDevController controller ( evdev );
      assert(controller.axes.size() >= 4);

      /* Make sure the axes we'll be using are in absolute mode. */
      for ( size_t i ( 0 ); i < 2; ++i )
        {
          controller.axes[i].set_coefficients({1, 0, 0, 0});
          if ( controller.axes[i].type != Axis::Type::ABSOLUTE )
            {                     /* Set up emulation for the axis. */
              controller.axes[i].mode = Axis::Type::ABSOLUTE;

              /* We need to manually initialize the absolute-axis raw-value
                 configuration when an axis isn't actually absolute... */
              controller.axes[i].raw.minimum =
                - (controller.axes[i].raw.maximum = 256);
              controller.axes[i].raw.neutral = 0;

              /* set cubic mapping: value = 1 * x^3 + 0 * x^2 + 0 * x + 0.  */
            }
        }

      /* Add event handlers for each of the axes we're interested in. */
      controller.axes[1].hook([&](const Axis& axis, Axis::State state)
                              {
                                if ( have_config )
                                  {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    left = -state.value;
                                    if ( mc.module )
                                      update_control(mc, left, right);
                                  }
                              });
      controller.axes[3].hook([&](const Axis& axis, Axis::State state)
                              {
                                if ( have_config )
                                  {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    right = -state.value;
                                    if ( mc.module )
                                      update_control(mc, left, right);
                                  }
                              });

      /* Instantiate the network node. */
      Node node ( std::move(socket), NodeRole::MASTER );

      /* Override the default configuration-response-recieved handler with
         this:

         - First, make sure that the received configuration is at least similar to what
           we expect; shut down the node if it isn't.

         - Store the received configuration.

         - Set up the module-control object to
      */
      node.dispatcher.configuration_response.received.connect(
        [&](Node& _node, const Configuration& configuration)
        {
          have_config = true;
          node.configuration = configuration;
          mc.reset(&(node.configuration.modules[0]));

          using namespace crisp::util::literals;

          /* Set up the control packet to be sent at 25 Hz. */
          fprintf(stderr, "Setting up send action... ");
          node.scheduler.schedule(10_Hz,
                                  [&](crisp::util::PeriodicAction&) {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    node.send(mc);
                                    return;
                                  });
          fprintf(stderr, "done.");
        });

      /* clear the module-control-sent handler -- it's just a lot of spam. */
      node.dispatcher.module_control.sent.clear();

      std::atomic<bool> controller_run_flag ( true );

      /* On user interrupt (Ctrl+C), shut down the node and stop the
         controller's main loop.

         If the program doesn't stop, wiggle the controller a bit. (Current
         EvDevController implementation uses a blocking read, so it won't notice
         that the run flag has changed until it gets another event.)
      */
      boost::asio::signal_set ss ( service, SIGINT );
      ss.async_wait([&](const boost::system::error_code& error, int sig)
                    {
                      if ( ! error )
                        {
                          node.halt();
                          controller_run_flag = false;
                        }
                    });

      /* Queue a configuration-query message send. */
      node.send(MessageType::CONFIGURATION_QUERY);

      /* Start the controller and the network node. */
      std::thread controller_thread([&]() { controller.run(controller_run_flag); });
      node.run();
    }

  return 0;
}
#endif  /* ! defined(DISABLE_CLIENT) */

int
main(int argc, char* argv[])
{
  /* Parse user options. */
  int c;
  while ( (c = getopt(argc, argv, "h")) != -1 )
    switch ( c )
      {
      case 'h':
	PRINT_USAGE(stdout);
	fprintf(stdout, HELP_TEXT);
	return 0;

      default:
	abort();
      }

  if ( argc - optind < 2 )
    {
      PRINT_USAGE(stderr);
      return 1;
    }

  /* Parse the address and port passed via the command line, and inform the user
     if they're doin' it wrong. */
  char* tail;
  boost::system::error_code pec;

  namespace ip = boost::asio::ip;

  ip::address address ( ip::address::from_string(argv[optind], pec) );

  int port ( strtoul(argv[optind + 1], &tail, 0) );

  if ( pec )
    { fprintf(stderr, "%s: %s (failed to parse IP address).\n", argv[optind], pec.message().c_str());
      return 1; }

  if ( *tail != '\0' )
    { fprintf(stderr, "%s: Need integer port number.\n", argv[optind + 1]);
      return 1; }

  /* Construct the target endpoint (IP address + port number) object. */
  using Protocol = Node::Protocol;
  typename Protocol::endpoint target_endpoint(address, port);

  /* Asio's io_service manages all the low-level details of parceling out work
     to IO worker threads in a thread-safe manner.  It's required for pretty
     much any network-related function, so we'll create one here.  */
  boost::asio::io_service service;

#if ! defined(DISABLE_CLIENT)
  return ( argc - optind < 3 )
    ? run_server(service, target_endpoint)
    : run_client(service, target_endpoint, argv[optind+2]);
#else
  return run_server(service, target_endpoint);
#endif
}

<commit_msg>drive-test: update I2C_SLAVE_ADDRESS and I2C_BUS for Rasp. Pi<commit_after>/** @file
 *
 * Client/server program for the drive test.
 */
#include <cstdio>
#include "Chassis_Control/DriveData.h"

/* Manually disable client mode on ARM machines (e.g. the Raspberry Pi, which
   gives us enough grief anyway... */
#if defined(__arm__) && ! defined(DISABLE_CLIENT)
# define DISABLE_CLIENT 1
#endif

/* ****************************************************************
 * Help text stuff.
 */
#if ! defined(DISABLE_CLIENT)
#  define PRINT_USAGE(stream) fprintf(stream, "Usage: %s [OPTION]... ADDRESS PORT [EVDEV]\n", argv[0])

#  define HELP_TEXT "\
Simple controls-test program.\n\
\n\
Options:\n\
  -h	Show this help.\n\
\n\
Client mode is assumed when EVDEV argument is present; otherwise server mode is\n\
enabled.\n"
#else
#  define PRINT_USAGE(stream) fprintf(stream, "Usage: %s [OPTION]... ADDRESS PORT\n", argv[0])
#  define HELP_TEXT "\
Simple controls-test program: server-only build.\n\
\n\
Options:\n\
  -h	Show this help.\n\
\n"
#endif  /* ! defined(DISABLE_CLIENT) */
/* **************************************************************** */

#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <crisp/comms/BasicNode.hh>
#include <crisp/comms/NodeServer.hh>

/* Define some aliases for our communication-types for convenience. */
using namespace crisp::comms;
typedef BasicNode<boost::asio::ip::tcp> Node;
typedef typename Node::Protocol Protocol;
typedef typename Protocol::endpoint Endpoint;

#include "gnublin_i2c.hpp"

#define I2C_BUS "/dev/i2c-1"
#define I2C_SLAVE_ADDRESS 4

static int
run_server(boost::asio::io_service& service,
           const Endpoint& target_endpoint)

{
  crisp::comms::NodeServer<Node> server ( service, target_endpoint );

  gnublin_i2c i2c ( I2C_BUS, I2C_SLAVE_ADDRESS );

  /* I don't trust this "Gnublin" code (it's terribly written and I had to fix
     it to even get it to compile).   We'll be replacing it eventually. */
  i2c.setDevicefile(I2C_BUS);
  i2c.setAddress(I2C_SLAVE_ADDRESS);

  if ( i2c.fail() )             /* Won't happen because the Gnublin stuff opens
                                   the device on-demand.  /Le sigh.../ */
    return 1;

  DriveData data;
  unsigned char* data_pointer = reinterpret_cast<unsigned char*>(&data);
  memset(data_pointer, 0, sizeof(data));

  using namespace crisp::comms::keywords;

  /* Declare our interface configuration. */
  server.configuration.add_module( "drive", 2)
    .add_input<int8_t>({ "left",  { _neutral = 0, _minimum = -127, _maximum = 128 } })
    .add_input<int8_t>({ "right", { _neutral = 0, _minimum = -127, _maximum = 128 } });

  /* Override the handler run when we receive a module-control packet. */
  server.dispatcher.module_control.received.clear();
  server.dispatcher.module_control.received.connect(
    [&](Node& node, const crisp::comms::ModuleControl& mc)
    {
      fprintf(stderr, "[0x%0x] \033[1;33mModule-control received\033[0m:", THREAD_ID);

      const crisp::comms::DataValue<> *dvl ( nullptr ), *dvr ( nullptr );

      /* Update our input-value array. */
      if ( (dvl = mc.value_for("left")) != nullptr )
        {
          data.left_motor = dvl->get<int8_t>();
          fprintf(stderr, "%d", data.left_motor);
        }
      dvr = mc.value_for("right");

      if ( dvl && dvr )
        fputs(", ", stderr);

      if ( dvr )
        {
          data.right_motor = dvr->get<int8_t>();
          fprintf(stderr, "%d\n", data.right_motor);
        }

      /* This call here is just so EW EW EW EW EW.  There's a reason for it, of
         course, but that reason is irrelevant with the way we're using I2C. */
      i2c.send(data_pointer, sizeof(data));

      if ( i2c.fail() )
        {
          perror("write");
        }
    });

  server.run();
  return 0;
}

/* Disable client functionality on the Raspberry Pi.

   _With_ this code (and using the old controller code based on the
   terribly-bloated Boost.Signals2 library), I had trouble getting the program
   to compile on the Pi without running out of memory.

   We don't need to support client mode on the Pi, so we might as well keep it
   disabled anyway.
 */
#if ! defined(DISABLE_CLIENT)

#include <crisp/input/EvDevController.hh>
#include <float.h>

/** Update a ModuleControl instance to reflect the input coordinates.
 *
 * @param mc ModuleControl to update.
 *
 * @param left Value of left stick y axis.
 *
 * @param right Value of right stick y axis.
 *
 */
void
update_control(crisp::comms::ModuleControl& mc,
               float left, float right)
{
  mc.set<int8_t>("left", static_cast<int8_t>(left * 127));
  mc.set<int8_t>("right", static_cast<int8_t>(right * 127));
}

/** Run the client code, connecting to the specified endpoint and taking input
 *  from the specified Linux `evdev` input device.
 */
static int
run_client(boost::asio::io_service& service,
           const Endpoint& target_endpoint,
           const char* evdev)
{
  /* Create a socket, and try to connect it to the specified endpoint. */
  boost::system::error_code ec;
  typename Protocol::socket socket ( service );
  typename Protocol::resolver resolver ( service );

  boost::asio::connect(socket, resolver.resolve(target_endpoint), ec);

  if ( ec )
    { fprintf(stderr, "connect: %s\n", strerror(ec.value()));
      return 1; }
  else
    {
      /* We're going to be modifying the ModuleControl instance on-demand
         (whenever the input controller changes state), but sending it at a
         fixed rate.  Since this will be happening in different threads and
         there's no easy way to coordinate the memory access and modification
         otherwise,  we need a mutex to keep everything in sync.

         (If we didn't do this, we could e.g., get halfway through updating the
         module control instance, send a control packet based on that [likely
         invalid] data, and then finish updating.)
      */
      std::mutex mutex;
      ModuleControl mc;
      bool have_config ( false );
      float left, right;

      /* Create the input device. */
      using namespace crisp::input;
      EvDevController controller ( evdev );
      assert(controller.axes.size() >= 4);

      /* Make sure the axes we'll be using are in absolute mode. */
      for ( size_t i ( 0 ); i < 2; ++i )
        {
          controller.axes[i].set_coefficients({1, 0, 0, 0});
          if ( controller.axes[i].type != Axis::Type::ABSOLUTE )
            {                     /* Set up emulation for the axis. */
              controller.axes[i].mode = Axis::Type::ABSOLUTE;

              /* We need to manually initialize the absolute-axis raw-value
                 configuration when an axis isn't actually absolute... */
              controller.axes[i].raw.minimum =
                - (controller.axes[i].raw.maximum = 256);
              controller.axes[i].raw.neutral = 0;

              /* set cubic mapping: value = 1 * x^3 + 0 * x^2 + 0 * x + 0.  */
            }
        }

      /* Add event handlers for each of the axes we're interested in. */
      controller.axes[1].hook([&](const Axis& axis, Axis::State state)
                              {
                                if ( have_config )
                                  {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    left = -state.value;
                                    if ( mc.module )
                                      update_control(mc, left, right);
                                  }
                              });
      controller.axes[3].hook([&](const Axis& axis, Axis::State state)
                              {
                                if ( have_config )
                                  {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    right = -state.value;
                                    if ( mc.module )
                                      update_control(mc, left, right);
                                  }
                              });

      /* Instantiate the network node. */
      Node node ( std::move(socket), NodeRole::MASTER );

      /* Override the default configuration-response-recieved handler with
         this:

         - First, make sure that the received configuration is at least similar to what
           we expect; shut down the node if it isn't.

         - Store the received configuration.

         - Set up the module-control object to
      */
      node.dispatcher.configuration_response.received.connect(
        [&](Node& _node, const Configuration& configuration)
        {
          have_config = true;
          node.configuration = configuration;
          mc.reset(&(node.configuration.modules[0]));

          using namespace crisp::util::literals;

          /* Set up the control packet to be sent at 25 Hz. */
          fprintf(stderr, "Setting up send action... ");
          node.scheduler.schedule(10_Hz,
                                  [&](crisp::util::PeriodicAction&) {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    node.send(mc);
                                    return;
                                  });
          fprintf(stderr, "done.");
        });

      /* clear the module-control-sent handler -- it's just a lot of spam. */
      node.dispatcher.module_control.sent.clear();

      std::atomic<bool> controller_run_flag ( true );

      /* On user interrupt (Ctrl+C), shut down the node and stop the
         controller's main loop.

         If the program doesn't stop, wiggle the controller a bit. (Current
         EvDevController implementation uses a blocking read, so it won't notice
         that the run flag has changed until it gets another event.)
      */
      boost::asio::signal_set ss ( service, SIGINT );
      ss.async_wait([&](const boost::system::error_code& error, int sig)
                    {
                      if ( ! error )
                        {
                          node.halt();
                          controller_run_flag = false;
                        }
                    });

      /* Queue a configuration-query message send. */
      node.send(MessageType::CONFIGURATION_QUERY);

      /* Start the controller and the network node. */
      std::thread controller_thread([&]() { controller.run(controller_run_flag); });
      node.run();
    }

  return 0;
}
#endif  /* ! defined(DISABLE_CLIENT) */

int
main(int argc, char* argv[])
{
  /* Parse user options. */
  int c;
  while ( (c = getopt(argc, argv, "h")) != -1 )
    switch ( c )
      {
      case 'h':
	PRINT_USAGE(stdout);
	fprintf(stdout, HELP_TEXT);
	return 0;

      default:
	abort();
      }

  if ( argc - optind < 2 )
    {
      PRINT_USAGE(stderr);
      return 1;
    }

  /* Parse the address and port passed via the command line, and inform the user
     if they're doin' it wrong. */
  char* tail;
  boost::system::error_code pec;

  namespace ip = boost::asio::ip;

  ip::address address ( ip::address::from_string(argv[optind], pec) );

  int port ( strtoul(argv[optind + 1], &tail, 0) );

  if ( pec )
    { fprintf(stderr, "%s: %s (failed to parse IP address).\n", argv[optind], pec.message().c_str());
      return 1; }

  if ( *tail != '\0' )
    { fprintf(stderr, "%s: Need integer port number.\n", argv[optind + 1]);
      return 1; }

  /* Construct the target endpoint (IP address + port number) object. */
  using Protocol = Node::Protocol;
  typename Protocol::endpoint target_endpoint(address, port);

  /* Asio's io_service manages all the low-level details of parceling out work
     to IO worker threads in a thread-safe manner.  It's required for pretty
     much any network-related function, so we'll create one here.  */
  boost::asio::io_service service;

#if ! defined(DISABLE_CLIENT)
  return ( argc - optind < 3 )
    ? run_server(service, target_endpoint)
    : run_client(service, target_endpoint, argv[optind+2]);
#else
  return run_server(service, target_endpoint);
#endif
}

<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2014, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file Packet.cxx
 *
 * Defines a DCC Packet structure.
 *
 * @author Balazs Racz
 * @date 10 May 2014
 */

#define LOGLEVEL WARNING

#include "dcc/Packet.hxx"

#include "utils/logging.h"
#include "utils/macros.h"

namespace dcc
{
enum
{
    MARKLIN_DEFAULT_CMD = 0b00100110,
    // Direction change bits for marklin-old format.
    MARKLIN_CHANGE_DIR_B2 = 0b11000000,

    DCC_DEFAULT_CMD = 0,
    DCC_LONG_PREAMBLE_CMD = 0b00001100,
    DCC_SERVICE_MODE_5X_WITH_ACK_CMD = 0b00111000,
    // standard dcc packet with 5x repeat.
    DCC_SERVICE_MODE_5X_CMD = 0b00101000,
    DCC_SERVICE_MODE_1X_CMD = 0b00001000,
    DCC_LONG_ADDRESS_FIRST = 0b11000000,

    // Baseline packet: speed and direction.
    DCC_BASELINE_SPEED = 0b01000000,
    DCC_BASELINE_SPEED_FORWARD = 0b00100000,
    DCC_BASELINE_SPEED_LIGHT = 0b00010000,
    DCC_FUNCTION1 = 0b10000000,
    DCC_FUNCTION1_F0 = 0b00010000,
    DCC_FUNCTION2_F5 = 0b10110000,
    DCC_FUNCTION2_F9 = 0b10100000,
    DCC_FEATURE_EXP_F13 = 0b11011110,
    DCC_FEATURE_EXP_F21 = 0b11011111,

    // Extended packet: 128-step speed.
    DCC_EXT_SPEED = 0b00111111,
    DCC_EXT_SPEED_FORWARD = 0x80,
};

void Packet::add_dcc_checksum()
{
    HASSERT(dlc < MAX_PAYLOAD);
    // Protects against double call of add checksum.
    HASSERT(!packet_header.skip_ec);
    uint8_t cs = 0;
    for (int i = 0; i < dlc; ++i)
    {
        cs ^= payload[i];
    }
    payload[dlc++] = cs;
    packet_header.skip_ec = 1;
}

void Packet::set_dcc_idle()
{
    start_dcc_packet();
    dlc = 3;
    payload[0] = payload[2] = 0xFF;
    payload[1] = 0;
    packet_header.skip_ec = 1;
}

void Packet::set_dcc_reset_all_decoders()
{
    start_dcc_packet();
    dlc = 3;
    payload[0] = payload[1] = payload[2] = 0;
    packet_header.skip_ec = 1;
}

void Packet::add_dcc_address(DccShortAddress address)
{
    start_dcc_packet();
    payload[dlc++] = address.value & 0x7F;
}

void Packet::add_dcc_address(DccLongAddress address)
{
    start_dcc_packet();
    payload[dlc++] = DCC_LONG_ADDRESS_FIRST | (address.value >> 8);
    payload[dlc++] = address.value & 0xff;
}

void Packet::add_dcc_speed14(bool is_fwd, bool light, unsigned speed)
{
    /// TODO(balazs.racz) add unittests.
    uint8_t b1 = DCC_BASELINE_SPEED;
    if (is_fwd)
        b1 |= DCC_BASELINE_SPEED_FORWARD;
    if (light)
        b1 |= DCC_BASELINE_SPEED_LIGHT;
    if (speed == EMERGENCY_STOP)
    {
        b1 |= 1;
    }
    else if (speed == 0)
    {
    }
    else
    {
        HASSERT(speed <= 14);
        speed += 1; // avoids 01 (e-stop)
        b1 |= speed & 0xf;
    }
    payload[dlc++] = b1;
    add_dcc_checksum();
}

void Packet::add_dcc_speed28(bool is_fwd, unsigned speed)
{
    uint8_t b1 = DCC_BASELINE_SPEED;
    if (is_fwd)
        b1 |= DCC_BASELINE_SPEED_FORWARD;
    if (speed == EMERGENCY_STOP)
    {
        b1 |= 1;
    }
    else if (speed == 0)
    {
    }
    else
    {
        HASSERT(speed <= 28);
        speed += 3; // avoids 00, 01 (stop) and 10 11 (e-stop)
        if (speed & 1)
            b1 |= DCC_BASELINE_SPEED_LIGHT;
        b1 |= (speed >> 1) & 0xf;
    }
    payload[dlc++] = b1;
    add_dcc_checksum();
}

void Packet::add_dcc_speed128(bool is_fwd, unsigned speed)
{
    payload[dlc++] = DCC_EXT_SPEED;
    uint8_t b = 0;
    if (is_fwd)
        b |= DCC_EXT_SPEED_FORWARD;
    if (speed == EMERGENCY_STOP)
    {
        b |= 1;
    }
    else if (speed > 0)
    {
        HASSERT(speed <= 126);
        speed += 1; // avoids 00, 01 ([e]stop)
        b |= speed & 0x7f;
    }
    payload[dlc++] = b;
    add_dcc_checksum();
}

void Packet::add_dcc_function0_4(unsigned values)
{
    uint8_t b1 = DCC_FUNCTION1;
    if (values & 1)
        b1 |= DCC_FUNCTION1_F0;
    b1 |= (values >> 1) & 0xf;
    payload[dlc++] = b1;
    add_dcc_checksum();
}

void Packet::add_dcc_function5_8(unsigned values)
{
    uint8_t b1 = DCC_FUNCTION2_F5;
    b1 |= values & 0xf;
    payload[dlc++] = b1;
    add_dcc_checksum();
}

void Packet::add_dcc_function9_12(unsigned values)
{
    uint8_t b1 = DCC_FUNCTION2_F9;
    b1 |= values & 0xf;
    payload[dlc++] = b1;
    add_dcc_checksum();
}

void Packet::add_dcc_function13_20(unsigned values)
{
    payload[dlc++] = DCC_FEATURE_EXP_F13;
    payload[dlc++] = values & 0xff;
    add_dcc_checksum();
}

void Packet::add_dcc_function21_28(unsigned values)
{
    payload[dlc++] = DCC_FEATURE_EXP_F21;
    payload[dlc++] = values & 0xff;
    add_dcc_checksum();
}

void Packet::start_mm_packet()
{
    dlc = 3;
    payload[0] = 0;
    payload[1] = 0;
    payload[2] = 0;
    header_raw_data = MARKLIN_DEFAULT_CMD;
}

static const uint8_t marklin_address[3] = {0b00, 0b11, 0b10};
static const uint8_t marklin_fn_bits[5] = {0,          0b01010000, 0b00000100,
                                           0b00010100, 0b01010100};

/** Sets the address bits of an MM packet to a specific loco address. */
void Packet::add_mm_address(MMAddress a, bool light)
{
    uint8_t address = a.value;
    payload[0] |= marklin_address[address % 3];
    address /= 3;
    payload[1] |= marklin_address[address % 3] << 6;
    address /= 3;
    payload[1] |= marklin_address[address % 3] << 4;
    address /= 3;
    payload[1] |= marklin_address[address % 3] << 2;
    if (light)
    {
        payload[1] |= 0b11;
    }
}

unsigned Packet::set_mm_speed_bits(unsigned speed)
{
    payload[2] = 0;
    // avoids speed step 1.
    if (speed > 0)
        ++speed;
    // clips speed
    if (speed > 15)
        speed = 15;
    if (speed & 1)
        payload[2] |= 0x80;
    if (speed & 2)
        payload[2] |= 0x20;
    if (speed & 4)
        payload[2] |= 0x08;
    if (speed & 8)
        payload[2] |= 0x02;
    return speed;
}

/** Sets the packet to a 14-step MM speed-and-light packet. Max value of
 * speed is 14. A special value of speed == CHANGE_DIR will signal
 * direction change. */
void Packet::add_mm_speed(unsigned speed)
{
    if (speed == CHANGE_DIR || speed == EMERGENCY_STOP)
    {
        payload[2] = MARKLIN_CHANGE_DIR_B2;
        // Up the repeat count so that it will be surely heard.
        header_raw_data |= 0b01100000;
    }
    else
    {
        set_mm_speed_bits(speed);
        // The old marklin protocol needs all bits doubled.
        payload[2] |= (payload[2] >> 1);
    }
}

/** Sets the packet to a direction-aware 14-step MM speed-and-light
 * packet. Max value of speed is 14. */
void Packet::add_mm_new_speed(bool is_fwd, unsigned speed)
{
    if (speed == CHANGE_DIR || speed == EMERGENCY_STOP)
    {
        payload[2] = MARKLIN_CHANGE_DIR_B2;
        // Up the repeat count so that it will be surely heard.
        header_raw_data |= 0b01100000;
    }
    else
    {
        speed = set_mm_speed_bits(speed);
        if (is_fwd)
        {
            payload[2] |= 0x10;
        }
        else
        {
            payload[2] |= 0x44;
        }
        if (speed <= 7)
        {
            payload[2] |= 1;
        }
    }
}

void Packet::add_mm_new_fn(unsigned fn_num, bool value, unsigned speed)
{
    if (speed == CHANGE_DIR || speed == EMERGENCY_STOP || fn_num < 1 ||
        fn_num > 4)
    {
        return add_mm_new_speed(false, speed);
    }
    // Sets the base speed bits.
    speed = set_mm_speed_bits(speed);
    // Sets the function number bits.
    payload[2] |= marklin_fn_bits[fn_num];
    // And the function bit.
    if (value)
    {
        payload[2] |= 1;
    }
    // Checks for exceptions, when we generated a packet valid for the old
    // protocol. (aka all bits are doubled)
    if (((payload[2] & 0xaa) >> 1) == (payload[2] & 0x55))
    {
        payload[2] &= 0xaa;
        if (speed >= 8)
        {
            // High speeds. new bits are 0101.
            payload[2] |= 0b00010001;
        }
        else
        {
            // Low speeds. New bits are 1010.
            payload[2] |= 0b01000100;
        }
    }
}

void Packet::mm_shift() {
    HASSERT(dlc == 3);
    dlc = 6;
    payload[3] = payload[0];
    payload[4] = payload[1];
    payload[5] = payload[2];
}

} // namespace dcc
<commit_msg>Sets the feedback key to be the dcc address by default.<commit_after>/** \copyright
 * Copyright (c) 2014, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file Packet.cxx
 *
 * Defines a DCC Packet structure.
 *
 * @author Balazs Racz
 * @date 10 May 2014
 */

#define LOGLEVEL WARNING

#include "dcc/Packet.hxx"

#include "utils/logging.h"
#include "utils/macros.h"

namespace dcc
{
enum
{
    MARKLIN_DEFAULT_CMD = 0b00100110,
    // Direction change bits for marklin-old format.
    MARKLIN_CHANGE_DIR_B2 = 0b11000000,

    DCC_DEFAULT_CMD = 0,
    DCC_LONG_PREAMBLE_CMD = 0b00001100,
    DCC_SERVICE_MODE_5X_WITH_ACK_CMD = 0b00111000,
    // standard dcc packet with 5x repeat.
    DCC_SERVICE_MODE_5X_CMD = 0b00101000,
    DCC_SERVICE_MODE_1X_CMD = 0b00001000,
    DCC_LONG_ADDRESS_FIRST = 0b11000000,

    // Baseline packet: speed and direction.
    DCC_BASELINE_SPEED = 0b01000000,
    DCC_BASELINE_SPEED_FORWARD = 0b00100000,
    DCC_BASELINE_SPEED_LIGHT = 0b00010000,
    DCC_FUNCTION1 = 0b10000000,
    DCC_FUNCTION1_F0 = 0b00010000,
    DCC_FUNCTION2_F5 = 0b10110000,
    DCC_FUNCTION2_F9 = 0b10100000,
    DCC_FEATURE_EXP_F13 = 0b11011110,
    DCC_FEATURE_EXP_F21 = 0b11011111,

    // Extended packet: 128-step speed.
    DCC_EXT_SPEED = 0b00111111,
    DCC_EXT_SPEED_FORWARD = 0x80,
};

void Packet::add_dcc_checksum()
{
    HASSERT(dlc < MAX_PAYLOAD);
    // Protects against double call of add checksum.
    HASSERT(!packet_header.skip_ec);
    uint8_t cs = 0;
    for (int i = 0; i < dlc; ++i)
    {
        cs ^= payload[i];
    }
    payload[dlc++] = cs;
    packet_header.skip_ec = 1;
}

void Packet::set_dcc_idle()
{
    start_dcc_packet();
    dlc = 3;
    payload[0] = payload[2] = 0xFF;
    payload[1] = 0;
    packet_header.skip_ec = 1;
}

void Packet::set_dcc_reset_all_decoders()
{
    start_dcc_packet();
    dlc = 3;
    payload[0] = payload[1] = payload[2] = 0;
    packet_header.skip_ec = 1;
}

void Packet::add_dcc_address(DccShortAddress address)
{
    start_dcc_packet();
    payload[dlc++] = address.value & 0x7F;
    feedback_key = address.value;
}

void Packet::add_dcc_address(DccLongAddress address)
{
    start_dcc_packet();
    payload[dlc++] = DCC_LONG_ADDRESS_FIRST | (address.value >> 8);
    payload[dlc++] = address.value & 0xff;
    feedback_key = address.value;
}

void Packet::add_dcc_speed14(bool is_fwd, bool light, unsigned speed)
{
    /// TODO(balazs.racz) add unittests.
    uint8_t b1 = DCC_BASELINE_SPEED;
    if (is_fwd)
        b1 |= DCC_BASELINE_SPEED_FORWARD;
    if (light)
        b1 |= DCC_BASELINE_SPEED_LIGHT;
    if (speed == EMERGENCY_STOP)
    {
        b1 |= 1;
    }
    else if (speed == 0)
    {
    }
    else
    {
        HASSERT(speed <= 14);
        speed += 1; // avoids 01 (e-stop)
        b1 |= speed & 0xf;
    }
    payload[dlc++] = b1;
    add_dcc_checksum();
}

void Packet::add_dcc_speed28(bool is_fwd, unsigned speed)
{
    uint8_t b1 = DCC_BASELINE_SPEED;
    if (is_fwd)
        b1 |= DCC_BASELINE_SPEED_FORWARD;
    if (speed == EMERGENCY_STOP)
    {
        b1 |= 1;
    }
    else if (speed == 0)
    {
    }
    else
    {
        HASSERT(speed <= 28);
        speed += 3; // avoids 00, 01 (stop) and 10 11 (e-stop)
        if (speed & 1)
            b1 |= DCC_BASELINE_SPEED_LIGHT;
        b1 |= (speed >> 1) & 0xf;
    }
    payload[dlc++] = b1;
    add_dcc_checksum();
}

void Packet::add_dcc_speed128(bool is_fwd, unsigned speed)
{
    payload[dlc++] = DCC_EXT_SPEED;
    uint8_t b = 0;
    if (is_fwd)
        b |= DCC_EXT_SPEED_FORWARD;
    if (speed == EMERGENCY_STOP)
    {
        b |= 1;
    }
    else if (speed > 0)
    {
        HASSERT(speed <= 126);
        speed += 1; // avoids 00, 01 ([e]stop)
        b |= speed & 0x7f;
    }
    payload[dlc++] = b;
    add_dcc_checksum();
}

void Packet::add_dcc_function0_4(unsigned values)
{
    uint8_t b1 = DCC_FUNCTION1;
    if (values & 1)
        b1 |= DCC_FUNCTION1_F0;
    b1 |= (values >> 1) & 0xf;
    payload[dlc++] = b1;
    add_dcc_checksum();
}

void Packet::add_dcc_function5_8(unsigned values)
{
    uint8_t b1 = DCC_FUNCTION2_F5;
    b1 |= values & 0xf;
    payload[dlc++] = b1;
    add_dcc_checksum();
}

void Packet::add_dcc_function9_12(unsigned values)
{
    uint8_t b1 = DCC_FUNCTION2_F9;
    b1 |= values & 0xf;
    payload[dlc++] = b1;
    add_dcc_checksum();
}

void Packet::add_dcc_function13_20(unsigned values)
{
    payload[dlc++] = DCC_FEATURE_EXP_F13;
    payload[dlc++] = values & 0xff;
    add_dcc_checksum();
}

void Packet::add_dcc_function21_28(unsigned values)
{
    payload[dlc++] = DCC_FEATURE_EXP_F21;
    payload[dlc++] = values & 0xff;
    add_dcc_checksum();
}

void Packet::start_mm_packet()
{
    dlc = 3;
    payload[0] = 0;
    payload[1] = 0;
    payload[2] = 0;
    header_raw_data = MARKLIN_DEFAULT_CMD;
}

static const uint8_t marklin_address[3] = {0b00, 0b11, 0b10};
static const uint8_t marklin_fn_bits[5] = {0,          0b01010000, 0b00000100,
                                           0b00010100, 0b01010100};

/** Sets the address bits of an MM packet to a specific loco address. */
void Packet::add_mm_address(MMAddress a, bool light)
{
    uint8_t address = a.value;
    payload[0] |= marklin_address[address % 3];
    address /= 3;
    payload[1] |= marklin_address[address % 3] << 6;
    address /= 3;
    payload[1] |= marklin_address[address % 3] << 4;
    address /= 3;
    payload[1] |= marklin_address[address % 3] << 2;
    if (light)
    {
        payload[1] |= 0b11;
    }
}

unsigned Packet::set_mm_speed_bits(unsigned speed)
{
    payload[2] = 0;
    // avoids speed step 1.
    if (speed > 0)
        ++speed;
    // clips speed
    if (speed > 15)
        speed = 15;
    if (speed & 1)
        payload[2] |= 0x80;
    if (speed & 2)
        payload[2] |= 0x20;
    if (speed & 4)
        payload[2] |= 0x08;
    if (speed & 8)
        payload[2] |= 0x02;
    return speed;
}

/** Sets the packet to a 14-step MM speed-and-light packet. Max value of
 * speed is 14. A special value of speed == CHANGE_DIR will signal
 * direction change. */
void Packet::add_mm_speed(unsigned speed)
{
    if (speed == CHANGE_DIR || speed == EMERGENCY_STOP)
    {
        payload[2] = MARKLIN_CHANGE_DIR_B2;
        // Up the repeat count so that it will be surely heard.
        header_raw_data |= 0b01100000;
    }
    else
    {
        set_mm_speed_bits(speed);
        // The old marklin protocol needs all bits doubled.
        payload[2] |= (payload[2] >> 1);
    }
}

/** Sets the packet to a direction-aware 14-step MM speed-and-light
 * packet. Max value of speed is 14. */
void Packet::add_mm_new_speed(bool is_fwd, unsigned speed)
{
    if (speed == CHANGE_DIR || speed == EMERGENCY_STOP)
    {
        payload[2] = MARKLIN_CHANGE_DIR_B2;
        // Up the repeat count so that it will be surely heard.
        header_raw_data |= 0b01100000;
    }
    else
    {
        speed = set_mm_speed_bits(speed);
        if (is_fwd)
        {
            payload[2] |= 0x10;
        }
        else
        {
            payload[2] |= 0x44;
        }
        if (speed <= 7)
        {
            payload[2] |= 1;
        }
    }
}

void Packet::add_mm_new_fn(unsigned fn_num, bool value, unsigned speed)
{
    if (speed == CHANGE_DIR || speed == EMERGENCY_STOP || fn_num < 1 ||
        fn_num > 4)
    {
        return add_mm_new_speed(false, speed);
    }
    // Sets the base speed bits.
    speed = set_mm_speed_bits(speed);
    // Sets the function number bits.
    payload[2] |= marklin_fn_bits[fn_num];
    // And the function bit.
    if (value)
    {
        payload[2] |= 1;
    }
    // Checks for exceptions, when we generated a packet valid for the old
    // protocol. (aka all bits are doubled)
    if (((payload[2] & 0xaa) >> 1) == (payload[2] & 0x55))
    {
        payload[2] &= 0xaa;
        if (speed >= 8)
        {
            // High speeds. new bits are 0101.
            payload[2] |= 0b00010001;
        }
        else
        {
            // Low speeds. New bits are 1010.
            payload[2] |= 0b01000100;
        }
    }
}

void Packet::mm_shift() {
    HASSERT(dlc == 3);
    dlc = 6;
    payload[3] = payload[0];
    payload[4] = payload[1];
    payload[5] = payload[2];
}

} // namespace dcc
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ReportComponent.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2007-07-09 11:56:15 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef RPT_REPORTCOMPONENT_HXX
#define RPT_REPORTCOMPONENT_HXX

#ifndef _COM_SUN_STAR_UNO_XCOMPONENTCONTEXT_HPP_
#include <com/sun/star/uno/XComponentContext.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XCHILD_HPP_
#include <com/sun/star/container/XChild.hpp>
#endif
#ifndef _COM_SUN_STAR_DRAWING_XSHAPE_HPP_
#include <com/sun/star/drawing/XShape.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XSERVICEINFO_HPP_
#include <com/sun/star/lang/XServiceInfo.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XTYPEPROVIDER_HPP_
#include <com/sun/star/lang/XTypeProvider.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XUNOTUNNEL_HPP_
#include <com/sun/star/lang/XUnoTunnel.hpp>
#endif
#ifndef _COM_SUN_STAR_UNO_XAGGREGATION_HPP_
#include <com/sun/star/uno/XAggregation.hpp>
#endif
#ifndef _COM_SUN_STAR_REPORT_XREPORTCOMPONENT_HPP_
#include <com/sun/star/report/XReportComponent.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif
#ifndef _CPPUHELPER_WEAKREF_HXX_
#include <cppuhelper/weakref.hxx>
#endif
#ifndef _COMPHELPER_UNO3_HXX_
#include <comphelper/uno3.hxx>
#endif

namespace reportdesign
{
    class OReportComponentProperties
    {
    public:
        ::com::sun::star::uno::WeakReference< ::com::sun::star::container::XChild > m_xParent;
        ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext > m_xContext;
        ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >
                                                                                m_xFactory;
        ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >       m_xShape;
        ::com::sun::star::uno::Reference< ::com::sun::star::uno::XAggregation >     m_xProxy;
        ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >   m_xProperty;
        ::com::sun::star::uno::Reference< ::com::sun::star::lang::XTypeProvider >   m_xTypeProvider;
        ::com::sun::star::uno::Reference< ::com::sun::star::lang::XUnoTunnel >      m_xUnoTunnel;
        ::com::sun::star::uno::Reference< ::com::sun::star::lang::XServiceInfo >    m_xServiceInfo;
        ::rtl::OUString                                                             m_sName;
        ::sal_Int32                                                                 m_nHeight;
        ::sal_Int32                                                                 m_nWidth;
        ::sal_Int32                                                                 m_nPosX;
        ::sal_Int32                                                                 m_nPosY;
        ::sal_Int32                                                                 m_nBorderColor;
        ::sal_Int16                                                                 m_nBorder;
        ::sal_Bool                                                                  m_bPrintRepeatedValues;

        OReportComponentProperties(::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext > const & _xContext
            )
            :m_xContext(_xContext)
            ,m_nHeight(0)
            ,m_nWidth(0)
            ,m_nPosX(0)
            ,m_nPosY(0)
            ,m_nBorderColor(0)
            ,m_nBorder(2)
            ,m_bPrintRepeatedValues(sal_True)
        {}
        ~OReportComponentProperties();

        void setShape(::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >& _xShape
                    ,const ::com::sun::star::uno::Reference< ::com::sun::star::report::XReportComponent>& _xTunnel
                    ,oslInterlockedCount& _rRefCount);

        void dispose(oslInterlockedCount& _rRefCount);
    };
}
#endif // RPT_REPORTCOMPONENT_HXX

<commit_msg>INTEGRATION: CWS rptchart01_DEV300 (1.2.70); FILE MERGED 2008/02/13 07:12:47 oj 1.2.70.1: #i85225# impl chart readhandler and oleproducer<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ReportComponent.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: kz $ $Date: 2008-03-05 17:55:33 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef RPT_REPORTCOMPONENT_HXX
#define RPT_REPORTCOMPONENT_HXX

#ifndef _COM_SUN_STAR_UNO_XCOMPONENTCONTEXT_HPP_
#include <com/sun/star/uno/XComponentContext.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XCHILD_HPP_
#include <com/sun/star/container/XChild.hpp>
#endif
#ifndef _COM_SUN_STAR_DRAWING_XSHAPE_HPP_
#include <com/sun/star/drawing/XShape.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XSERVICEINFO_HPP_
#include <com/sun/star/lang/XServiceInfo.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XTYPEPROVIDER_HPP_
#include <com/sun/star/lang/XTypeProvider.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XUNOTUNNEL_HPP_
#include <com/sun/star/lang/XUnoTunnel.hpp>
#endif
#ifndef _COM_SUN_STAR_UNO_XAGGREGATION_HPP_
#include <com/sun/star/uno/XAggregation.hpp>
#endif
#ifndef _COM_SUN_STAR_REPORT_XREPORTCOMPONENT_HPP_
#include <com/sun/star/report/XReportComponent.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif
#ifndef _CPPUHELPER_WEAKREF_HXX_
#include <cppuhelper/weakref.hxx>
#endif
#ifndef _COMPHELPER_UNO3_HXX_
#include <comphelper/uno3.hxx>
#endif

namespace reportdesign
{
    class OReportComponentProperties
    {
    public:
        ::com::sun::star::uno::WeakReference< ::com::sun::star::container::XChild > m_xParent;
        ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext > m_xContext;
        ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >
                                                                                m_xFactory;
        ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >       m_xShape;
        ::com::sun::star::uno::Reference< ::com::sun::star::uno::XAggregation >     m_xProxy;
        ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >   m_xProperty;
        ::com::sun::star::uno::Reference< ::com::sun::star::lang::XTypeProvider >   m_xTypeProvider;
        ::com::sun::star::uno::Reference< ::com::sun::star::lang::XUnoTunnel >      m_xUnoTunnel;
        ::com::sun::star::uno::Reference< ::com::sun::star::lang::XServiceInfo >    m_xServiceInfo;
        ::com::sun::star::uno::Sequence< ::rtl::OUString >                          m_aMasterFields;
        ::com::sun::star::uno::Sequence< ::rtl::OUString >                          m_aDetailFields;
        ::rtl::OUString                                                             m_sName;
        ::sal_Int32                                                                 m_nHeight;
        ::sal_Int32                                                                 m_nWidth;
        ::sal_Int32                                                                 m_nPosX;
        ::sal_Int32                                                                 m_nPosY;
        ::sal_Int32                                                                 m_nBorderColor;
        ::sal_Int16                                                                 m_nBorder;
        ::sal_Bool                                                                  m_bPrintRepeatedValues;

        OReportComponentProperties(::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext > const & _xContext
            )
            :m_xContext(_xContext)
            ,m_nHeight(0)
            ,m_nWidth(0)
            ,m_nPosX(0)
            ,m_nPosY(0)
            ,m_nBorderColor(0)
            ,m_nBorder(2)
            ,m_bPrintRepeatedValues(sal_True)
        {}
        ~OReportComponentProperties();

        void setShape(::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >& _xShape
                    ,const ::com::sun::star::uno::Reference< ::com::sun::star::report::XReportComponent>& _xTunnel
                    ,oslInterlockedCount& _rRefCount);

        void dispose(oslInterlockedCount& _rRefCount);
    };
}
#endif // RPT_REPORTCOMPONENT_HXX

<|endoftext|>


<commit_before>#ifndef ALEPH_MATH_PIECEWISE_LINEAR_FUNCTION_HH__
#define ALEPH_MATH_PIECEWISE_LINEAR_FUNCTION_HH__

#include <aleph/math/KahanSummation.hh>

#include <functional>
#include <iterator>
#include <limits>
#include <map>
#include <set>
#include <stdexcept>
#include <vector>

#include <cassert>
#include <cmath>

namespace aleph
{

namespace math
{

namespace detail
{

/** Performs linear interpolation between two points */
template <class D, class I> I lerp( D x, D x0, I y0, D x1, I y1 )
{
  return y0 + (y1-y0) * (x-x0) / (x1-x0);
}

} // namespace detail

/**
  @class PiecewiseLinearFunction
  @brief Models a piecewise linear function

  A piecewise linear function is fully defined by a set of pairs of
  coordinates, specifying values in the *domain* and the *image* of
  the function. This class permits various arithmetical operations,
  such as addition and subtraction, with piecewise linear functions
  and provides a number of other common operations.

  @tparam D Type of the *domain* of the function
  @tparam I Type of the *image* of the function
*/

template <class D, class I = D> class PiecewiseLinearFunction
{
public:
  using Domain = D;
  using Image  = I;

  /** Creates an empty piecewise linear function */
  PiecewiseLinearFunction() = default;

  /**
    Creates a new piecewise linear function from a range of values. The values
    must consist of pairs that are convertible to double. Duplicates are not
    permitted and will result in an exception being thrown.

    @param begin Input iterator to begin of range
    @param end   Input iterator to end of range
  */

  template <class InputIterator> PiecewiseLinearFunction( InputIterator begin,
                                                          InputIterator end )
  {
    for( InputIterator it = begin; it != end; ++it )
    {
      auto x       = it->first;
      auto y       = it->second;
      bool success = false;

      std::tie( std::ignore, success )
          = _data.insert( std::make_pair(x,y) );

      if( !success )
        throw std::runtime_error( "Duplicate value pairs not permitted for piecewise linear functions" );
    }

    // Inserts new points whenever the function intersects the x-axis in
    // order to ensure that the integration procedure works.
    if( !_data.empty() )
      this->insertIntersectionPoints();
  }

  // Evaluation --------------------------------------------------------

  /**
    Evaluates the piecewise linear function at a certain position. The
    function must not be evaluated beyond its domain. This will result
    in zeroes. For every other evaluation point, the function performs
    interpolation between the nearest values.

    @param x Coordinate at which to evaluate the function

    @returns Interpolated y value
  */

  Image operator()( Domain x ) const noexcept
  {
    auto range = _data.equal_range( x );
    auto begin = range.first;
    auto end   = range.second;

    // Beyond the domain
    if( begin == _data.end() || end == _data.begin() )
      return Image();

    // One of the stored values
    else if( begin->first == x )
      return begin->second;

    // Interpolation required
    auto right = begin;
    auto left  = std::prev( begin );

    return detail::lerp( x, left->first, left->second, right->first, right->second );
  }

  // Operations --------------------------------------------------------

  /** Calculates the sum of two piecewise linear functions */
  PiecewiseLinearFunction& operator+=( const PiecewiseLinearFunction& rhs ) noexcept
  {
    return this->apply( rhs, std::plus<Image>() );
  }

  /** Calculates the sum of two piecewise linear functions */
  PiecewiseLinearFunction operator+( const PiecewiseLinearFunction& rhs ) const noexcept
  {
    auto lhs = *this;
    lhs += rhs;
    return lhs;
  }

  /** Calculates the difference of two piecewise linear functions */
  PiecewiseLinearFunction& operator-=( const PiecewiseLinearFunction& rhs ) noexcept
  {
    return this->apply( rhs, std::minus<Image>() );
  }

  /** Calculates the difference of two piecewise linear functions */
  PiecewiseLinearFunction operator-( const PiecewiseLinearFunction& rhs ) const noexcept
  {
    auto lhs = *this;
    lhs -= rhs;
    return lhs;
  }

  /** Unary minus: negates all values in the image of the piecewise linear function */
  PiecewiseLinearFunction operator-() const noexcept
  {
    PiecewiseLinearFunction f;

    for( auto&& pair : _data )
      f._data.insert( std::make_pair( pair.first, -pair.second ) );

    return f;
  }

  /** Multiplies the given piecewise linear function with a scalar value */
  PiecewiseLinearFunction& operator*=( Image lambda ) noexcept
  {
    for( auto&& pair : _data )
      pair.second *= lambda;

    return *this;
  }

  /** Multiplies the given piecewise linear function with a scalar value */
  PiecewiseLinearFunction operator*( Image lambda ) const noexcept
  {
    auto f = *this;
    f *= lambda;
    return f;
  }

  /** Divides the given step function by a scalar value */
  PiecewiseLinearFunction& operator/=( I lambda )
  {
    if( lambda == I() )
      throw std::runtime_error( "Attempted division by zero" );

    return this->operator*=( 1/lambda );
  }

  /** Divides the given step function by a scalar value */
  PiecewiseLinearFunction operator/( I lambda ) const noexcept
  {
    auto f = *this;
    f /= lambda;
    return f;
  }

  // Queries -----------------------------------------------------------

  /** Copies the domain values to an output iterator */
  template <class OutputIterator> void domain( OutputIterator result ) const noexcept
  {
    for( auto&& pair : _data )
      *result++ = pair.first;
  }

  /** Copies the image values to an output iterator */
  template <class OutputIterator> void image( OutputIterator result ) const noexcept
  {
    for( auto&& pair : _data )
      *result++ = pair.second;
  }

  /**
    Compares two piecewise linear functions. The functions are
    considered to be *equal* when the take the same values for
    the same interval. This method will evaluate the functions
    over *all* points of the domain.

    @param rhs Other function to compare against

    @returns true if both functions are equal, regardless of whether one
    of the functions has a subdivided range of values.
  */

  bool operator==( const PiecewiseLinearFunction& rhs ) const noexcept
  {
    std::set<Domain> domain;

    this->domain( std::inserter( domain, domain.begin() ) );
      rhs.domain( std::inserter( domain, domain.begin() ) );

    bool result = true;

    for( auto&& x : domain )
    {
      auto y0 = this->operator()( x );
      auto y1 = rhs( x );

      if( y0 != y1 )
        return false;
    }

    return result;
  }

  /** Negates the comparison between two functions */
  bool operator!=( const PiecewiseLinearFunction& rhs ) const noexcept
  {
    return !this->operator==( rhs );
  }

  // Transformations ---------------------------------------------------

  /** Calculates the absolute value of the function */
  PiecewiseLinearFunction& abs() noexcept
  {
    for( auto&& pair : _data )
      pair.second = std::abs( pair.second );

    return *this;
  }

  /** Calculates the maximum (supremum) of the function */
  Image max() const noexcept
  {
    if( _data.empty() )
      return Image();

    auto max = std::numeric_limits<Image>::lowest();

    for( auto&& pair : _data )
      max = std::max( max, pair.second );

    return max;
  }

  /** Calculates the supremum (maximum) of the function */
  Image sup() const noexcept
  {
    return this->max();
  }

  /**
    Calculates the integral over the (absolute value of the) function,
    raised to the \f$p\f$-th power.
  */

  Image integral( Image p = Image(1) ) const noexcept
  {
    if( _data.empty() )
      return Image();

    // The Kahan summation ensures that small parts of the integral do
    // not disappear amidst the remaining values.
    KahanSummation<Image> norm = Image();

    auto previous = _data.begin();
    auto current  = std::next( _data.begin() );

    while( current != _data.end() )
    {
      // Coefficients of the line that connect the previous point and the current
      // point.
      auto x1 = previous->first;
      auto y1 = previous->second;
      auto x2 = current->first;
      auto y2 = current->second;
      auto m  = (y2 - y1) / (x2 - x1);
      auto c  = y1 - m * x1;

      // Evaluator for the integral. This is an application of Cavalieri's
      // quadrature formula.
      auto evaluator = [&]( Image x )
      {
        // Horizontal segments
        if( m == Image() )
          return std::pow( c, p ) * x;

        // Regular lines
        else
          return std::pow( m*x + c, p+1 ) / ( m * (p+1) );
      };

      auto integral   = std::abs( evaluator( x2 ) - evaluator( x1 ) );
      norm           += integral;

      previous = current;
      ++current;
    }

    return std::pow( norm, 1/p );
  }

private:

  /**
    Applies a binary operation to the current piecewise linear function
    and another function. To this, the domains of *both* functions will
    be merged, and the operation will be applied to  their values, with
    a suitable interpolation scheme in place.

    @param other     Second piecewise linear function
    @param operation Operation to apply to both functions

    @returns Reference to modified piecewise linear function. The
    original piecewise linear function ceases to exist.
  */

  template <class BinaryOperation> PiecewiseLinearFunction& apply( const PiecewiseLinearFunction& other,
                                                                   BinaryOperation operation )
  {
    std::set<Domain> xValues;

    for( auto&& pair : _data )
      xValues.insert( pair.first );

    for( auto&& pair : other._data )
      xValues.insert( pair.first );

    // Oherwise, the loop below will turn in an infinite loop since or
    // the validity of the `current` iterator would have to be checked
    // before-hand.
    if( xValues.empty() )
      return *this;

    // Intersection handling. This is required to ensure that the combination of
    // the two functions contains shared segments.
    {
      // This closure checks whether two line segments intersect. If this is the
      // case, the intersection point needs to be stored as an additional point
      // in the set of values.
      auto intersection = []( Domain x0, Image y0, Domain x1, Image y1,
                              Domain x2, Image y2, Domain x3, Image y3 )
      {
        auto s1x = x1 - x0;
        auto s1y = y1 - y0;
        auto s2x = x3 - x2;
        auto s2y = y3 - y2;

        auto s = ( -s1y * (x0-x2) + s1x * (y0-y2) ) / ( -s2x * s1y + s1x * s2y );
        auto t = (  s2x * (y0-y2) - s2y * (x0-x2) ) / ( -s2x * s1y + s1x * s2y );

        if( s >= 0 && s <= 1 && t >= 0 && t <= 1 )
          return x0 + t * s1x;

        return x0;
      };

      std::set<Domain> intersections;

      auto current = xValues.begin();
      auto next    = std::next( current );

      for( ; next != xValues.end(); )
      {
        auto x0 = *current;
        auto x1 = *next;

        auto y0 = this->operator()( x0 );
        auto y1 = this->operator()( x1 );
        auto y2 = other( x0 );
        auto y3 = other( x1 );

        auto x = intersection( x0, y0, x1, y1, x0, y2, x1, y3 );

        if( x != x0 )
          intersections.insert( x );

        current = next;
        next    = std::next( current );
      }

      xValues.insert( intersections.begin(), intersections.end() );
    }

    // Apply the operation to all points -------------------------------

    std::map<Domain, Image> data;

    for( auto&& x : xValues )
    {
      auto y1 = this->operator()( x );
      auto y2 =            other( x );

      data.insert( std::make_pair( x, operation( y1, y2 ) ) );
    }

    _data.swap( data );
    return *this;
  }

  /**
    Checks the segments of the piecewise linear function for intersections with
    the x-axis. In these cases, a new point needs to be inserted into the list
    of function values. Else, the integration procedure will not work because a
    segment might be considered to have an area of zero. The following example
    illustrates this problem:

    \verbatim
     /\
    /  \
    ---------
        \  /
         \/
    \endverbatim

    If the intersection in the middle is not being counted as a point on the PL
    function, the area of the corresponding segment will be zero. This is not
    the desired and expected behaviour.
  */

  void insertIntersectionPoints()
  {
    auto current = _data.begin();
    auto next    = std::next( current );

    std::set<Domain> intersections;

    for( ; next != _data.end(); )
    {
      auto x0 = current->first;
      auto y0 = current->second;

      auto x1 = next->first;
      auto y1 = next->second;

      // We do not need to check the other cases. If either one of the values is
      // zero, we already have an intersection.
      if( y0 * y1 < Image() )
      {
        auto m = ( y1 - y0 ) / ( x1 - x0 );
        auto c = y0 - m * x0;
        auto x = - c / m;

        intersections.insert( x );
      }

      current = next;
      next    = std::next( current );
    }

    for( auto&& x : intersections )
      _data.insert( std::make_pair( x, Image() ) );
  }

  /** Maps values in the domain to values in the image */
  std::map<Domain, Image> _data;
};

} // namespace math

} // namespace aleph

/** Multiplies a given piecewise linear function by a scalar value */
template <class D, class I, class T> aleph::math::PiecewiseLinearFunction<D, I> operator*( T lambda, const aleph::math::PiecewiseLinearFunction<D, I>& f )
{
  return f * lambda;
}

/** Divides a given piecewise linear function by a scalar value */
template <class D, class I, class T> aleph::math::PiecewiseLinearFunction<D, I> operator/( T lambda, const aleph::math::PiecewiseLinearFunction<D, I>& f )
{
  return f / lambda;
}

/**
  Output operator of a piecewise linear function. Permits printing the
  function values to an *output stream* in order to permit their usage
  by other functions and/or programs.
*/

template <class D, class I> std::ostream& operator<<( std::ostream& o, const aleph::math::PiecewiseLinearFunction<D, I>& f )
{
  std::vector<D> X;
  std::vector<I> Y;

  f.domain( std::back_inserter(X) );
  f.image ( std::back_inserter(Y) );

  assert( X.size() == Y.size() );

  for( std::size_t i = 0; i < X.size() && i < Y.size(); i++ )
    o << X[i] << "\t" << Y[i] << "\n";

  return o;
}

#endif
<commit_msg>Providing overloads for piecewise linear function broadcasting<commit_after>#ifndef ALEPH_MATH_PIECEWISE_LINEAR_FUNCTION_HH__
#define ALEPH_MATH_PIECEWISE_LINEAR_FUNCTION_HH__

#include <aleph/math/KahanSummation.hh>

#include <functional>
#include <iterator>
#include <limits>
#include <map>
#include <set>
#include <stdexcept>
#include <vector>

#include <cassert>
#include <cmath>

namespace aleph
{

namespace math
{

namespace detail
{

/** Performs linear interpolation between two points */
template <class D, class I> I lerp( D x, D x0, I y0, D x1, I y1 )
{
  return y0 + (y1-y0) * (x-x0) / (x1-x0);
}

} // namespace detail

/**
  @class PiecewiseLinearFunction
  @brief Models a piecewise linear function

  A piecewise linear function is fully defined by a set of pairs of
  coordinates, specifying values in the *domain* and the *image* of
  the function. This class permits various arithmetical operations,
  such as addition and subtraction, with piecewise linear functions
  and provides a number of other common operations.

  @tparam D Type of the *domain* of the function
  @tparam I Type of the *image* of the function
*/

template <class D, class I = D> class PiecewiseLinearFunction
{
public:
  using Domain = D;
  using Image  = I;

  /** Creates an empty piecewise linear function */
  PiecewiseLinearFunction() = default;

  /**
    Creates a new piecewise linear function from a range of values. The values
    must consist of pairs that are convertible to double. Duplicates are not
    permitted and will result in an exception being thrown.

    @param begin Input iterator to begin of range
    @param end   Input iterator to end of range
  */

  template <class InputIterator> PiecewiseLinearFunction( InputIterator begin,
                                                          InputIterator end )
  {
    for( InputIterator it = begin; it != end; ++it )
    {
      auto x       = it->first;
      auto y       = it->second;
      bool success = false;

      std::tie( std::ignore, success )
          = _data.insert( std::make_pair(x,y) );

      if( !success )
        throw std::runtime_error( "Duplicate value pairs not permitted for piecewise linear functions" );
    }

    // Inserts new points whenever the function intersects the x-axis in
    // order to ensure that the integration procedure works.
    if( !_data.empty() )
      this->insertIntersectionPoints();
  }

  // Evaluation --------------------------------------------------------

  /**
    Evaluates the piecewise linear function at a certain position. The
    function must not be evaluated beyond its domain. This will result
    in zeroes. For every other evaluation point, the function performs
    interpolation between the nearest values.

    @param x Coordinate at which to evaluate the function

    @returns Interpolated y value
  */

  Image operator()( Domain x ) const noexcept
  {
    auto range = _data.equal_range( x );
    auto begin = range.first;
    auto end   = range.second;

    // Beyond the domain
    if( begin == _data.end() || end == _data.begin() )
      return Image();

    // One of the stored values
    else if( begin->first == x )
      return begin->second;

    // Interpolation required
    auto right = begin;
    auto left  = std::prev( begin );

    return detail::lerp( x, left->first, left->second, right->first, right->second );
  }

  // Operations --------------------------------------------------------

  /** Calculates the sum of two piecewise linear functions */
  PiecewiseLinearFunction& operator+=( const PiecewiseLinearFunction& rhs ) noexcept
  {
    return this->apply( rhs, std::plus<Image>() );
  }

  /** Calculates the sum of two piecewise linear functions */
  PiecewiseLinearFunction operator+( const PiecewiseLinearFunction& rhs ) const noexcept
  {
    auto lhs = *this;
    lhs += rhs;
    return lhs;
  }

  /** Adds a given value to all points in the image of the function */
  PiecewiseLinearFunction operator+( Image lambda ) const noexcept
  {
    auto f = *this;
    return f.apply( lambda, std::plus<Image>() );
  }

  /** Adds a given value to all points in the image of the function */
  PiecewiseLinearFunction& operator+=( Image lambda ) const noexcept
  {
    return this->apply( lambda, std::plus<Image>() );
  }

  /** Calculates the difference of two piecewise linear functions */
  PiecewiseLinearFunction& operator-=( const PiecewiseLinearFunction& rhs ) noexcept
  {
    return this->apply( rhs, std::minus<Image>() );
  }

  /** Calculates the difference of two piecewise linear functions */
  PiecewiseLinearFunction operator-( const PiecewiseLinearFunction& rhs ) const noexcept
  {
    auto lhs = *this;
    lhs -= rhs;
    return lhs;
  }

  /** Unary minus: negates all values in the image of the piecewise linear function */
  PiecewiseLinearFunction operator-() const noexcept
  {
    PiecewiseLinearFunction f;

    for( auto&& pair : _data )
      f._data.insert( std::make_pair( pair.first, -pair.second ) );

    return f;
  }

  /** Subtracts a given value from all points in the image of the function */
  PiecewiseLinearFunction operator-( Image lambda ) const noexcept
  {
    auto f = *this;
    return f.apply( lambda, std::minus<Image>() );
  }

  /** Subtracts a given value from all points in the image of the function */
  PiecewiseLinearFunction& operator-=( Image lambda ) const noexcept
  {
    return this->apply( lambda, std::minus<Image>() );
  }

  /** Multiplies the given piecewise linear function with a scalar value */
  PiecewiseLinearFunction& operator*=( Image lambda ) noexcept
  {
    for( auto&& pair : _data )
      pair.second *= lambda;

    return *this;
  }

  /** Multiplies the given piecewise linear function with a scalar value */
  PiecewiseLinearFunction operator*( Image lambda ) const noexcept
  {
    auto f = *this;
    f *= lambda;
    return f;
  }

  /** Divides the given step function by a scalar value */
  PiecewiseLinearFunction& operator/=( I lambda )
  {
    if( lambda == I() )
      throw std::runtime_error( "Attempted division by zero" );

    return this->operator*=( 1/lambda );
  }

  /** Divides the given step function by a scalar value */
  PiecewiseLinearFunction operator/( I lambda ) const noexcept
  {
    auto f = *this;
    f /= lambda;
    return f;
  }

  // Queries -----------------------------------------------------------

  /** Copies the domain values to an output iterator */
  template <class OutputIterator> void domain( OutputIterator result ) const noexcept
  {
    for( auto&& pair : _data )
      *result++ = pair.first;
  }

  /** Copies the image values to an output iterator */
  template <class OutputIterator> void image( OutputIterator result ) const noexcept
  {
    for( auto&& pair : _data )
      *result++ = pair.second;
  }

  /**
    Compares two piecewise linear functions. The functions are
    considered to be *equal* when the take the same values for
    the same interval. This method will evaluate the functions
    over *all* points of the domain.

    @param rhs Other function to compare against

    @returns true if both functions are equal, regardless of whether one
    of the functions has a subdivided range of values.
  */

  bool operator==( const PiecewiseLinearFunction& rhs ) const noexcept
  {
    std::set<Domain> domain;

    this->domain( std::inserter( domain, domain.begin() ) );
      rhs.domain( std::inserter( domain, domain.begin() ) );

    bool result = true;

    for( auto&& x : domain )
    {
      auto y0 = this->operator()( x );
      auto y1 = rhs( x );

      if( y0 != y1 )
        return false;
    }

    return result;
  }

  /** Negates the comparison between two functions */
  bool operator!=( const PiecewiseLinearFunction& rhs ) const noexcept
  {
    return !this->operator==( rhs );
  }

  // Transformations ---------------------------------------------------

  /** Calculates the absolute value of the function */
  PiecewiseLinearFunction& abs() noexcept
  {
    for( auto&& pair : _data )
      pair.second = std::abs( pair.second );

    return *this;
  }

  /** Calculates the maximum (supremum) of the function */
  Image max() const noexcept
  {
    if( _data.empty() )
      return Image();

    auto max = std::numeric_limits<Image>::lowest();

    for( auto&& pair : _data )
      max = std::max( max, pair.second );

    return max;
  }

  /** Calculates the supremum (maximum) of the function */
  Image sup() const noexcept
  {
    return this->max();
  }

  /**
    Calculates the integral over the (absolute value of the) function,
    raised to the \f$p\f$-th power.
  */

  Image integral( Image p = Image(1) ) const noexcept
  {
    if( _data.empty() )
      return Image();

    // The Kahan summation ensures that small parts of the integral do
    // not disappear amidst the remaining values.
    KahanSummation<Image> norm = Image();

    auto previous = _data.begin();
    auto current  = std::next( _data.begin() );

    while( current != _data.end() )
    {
      // Coefficients of the line that connect the previous point and the current
      // point.
      auto x1 = previous->first;
      auto y1 = previous->second;
      auto x2 = current->first;
      auto y2 = current->second;
      auto m  = (y2 - y1) / (x2 - x1);
      auto c  = y1 - m * x1;

      // Evaluator for the integral. This is an application of Cavalieri's
      // quadrature formula.
      auto evaluator = [&]( Image x )
      {
        // Horizontal segments
        if( m == Image() )
          return std::pow( c, p ) * x;

        // Regular lines
        else
          return std::pow( m*x + c, p+1 ) / ( m * (p+1) );
      };

      auto integral   = std::abs( evaluator( x2 ) - evaluator( x1 ) );
      norm           += integral;

      previous = current;
      ++current;
    }

    return std::pow( norm, 1/p );
  }

private:

  /**
    Applies a binary operation to the current piecewise linear function
    and a given scalar. This is tantamount to *broadcasting* the result
    to a function that is entirely constant over a range of values. The
    operation does not require conversions or merges of the *domain* or
    the *range* of the input because only a scalar is involved.
  */

  template <class BinaryOperation> PiecewiseLinearFunction& apply( Image lambda,
                                                                   BinaryOperation operation )
  {
    for( auto&& pair : _data )
      pair.second = operation( pair.second, lambda );

    return *this;
  }

  /**
    Applies a binary operation to the current piecewise linear function
    and another function. To this, the domains of *both* functions will
    be merged, and the operation will be applied to  their values, with
    a suitable interpolation scheme in place.

    @param other     Second piecewise linear function
    @param operation Operation to apply to both functions

    @returns Reference to modified piecewise linear function. The
    original piecewise linear function ceases to exist.
  */

  template <class BinaryOperation> PiecewiseLinearFunction& apply( const PiecewiseLinearFunction& other,
                                                                   BinaryOperation operation )
  {
    std::set<Domain> xValues;

    for( auto&& pair : _data )
      xValues.insert( pair.first );

    for( auto&& pair : other._data )
      xValues.insert( pair.first );

    // Oherwise, the loop below will turn in an infinite loop since or
    // the validity of the `current` iterator would have to be checked
    // before-hand.
    if( xValues.empty() )
      return *this;

    // Intersection handling. This is required to ensure that the combination of
    // the two functions contains shared segments.
    {
      // This closure checks whether two line segments intersect. If this is the
      // case, the intersection point needs to be stored as an additional point
      // in the set of values.
      auto intersection = []( Domain x0, Image y0, Domain x1, Image y1,
                              Domain x2, Image y2, Domain x3, Image y3 )
      {
        auto s1x = x1 - x0;
        auto s1y = y1 - y0;
        auto s2x = x3 - x2;
        auto s2y = y3 - y2;

        auto s = ( -s1y * (x0-x2) + s1x * (y0-y2) ) / ( -s2x * s1y + s1x * s2y );
        auto t = (  s2x * (y0-y2) - s2y * (x0-x2) ) / ( -s2x * s1y + s1x * s2y );

        if( s >= 0 && s <= 1 && t >= 0 && t <= 1 )
          return x0 + t * s1x;

        return x0;
      };

      std::set<Domain> intersections;

      auto current = xValues.begin();
      auto next    = std::next( current );

      for( ; next != xValues.end(); )
      {
        auto x0 = *current;
        auto x1 = *next;

        auto y0 = this->operator()( x0 );
        auto y1 = this->operator()( x1 );
        auto y2 = other( x0 );
        auto y3 = other( x1 );

        auto x = intersection( x0, y0, x1, y1, x0, y2, x1, y3 );

        if( x != x0 )
          intersections.insert( x );

        current = next;
        next    = std::next( current );
      }

      xValues.insert( intersections.begin(), intersections.end() );
    }

    // Apply the operation to all points -------------------------------

    std::map<Domain, Image> data;

    for( auto&& x : xValues )
    {
      auto y1 = this->operator()( x );
      auto y2 =            other( x );

      data.insert( std::make_pair( x, operation( y1, y2 ) ) );
    }

    _data.swap( data );
    return *this;
  }

  /**
    Checks the segments of the piecewise linear function for intersections with
    the x-axis. In these cases, a new point needs to be inserted into the list
    of function values. Else, the integration procedure will not work because a
    segment might be considered to have an area of zero. The following example
    illustrates this problem:

    \verbatim
     /\
    /  \
    ---------
        \  /
         \/
    \endverbatim

    If the intersection in the middle is not being counted as a point on the PL
    function, the area of the corresponding segment will be zero. This is not
    the desired and expected behaviour.
  */

  void insertIntersectionPoints()
  {
    auto current = _data.begin();
    auto next    = std::next( current );

    std::set<Domain> intersections;

    for( ; next != _data.end(); )
    {
      auto x0 = current->first;
      auto y0 = current->second;

      auto x1 = next->first;
      auto y1 = next->second;

      // We do not need to check the other cases. If either one of the values is
      // zero, we already have an intersection.
      if( y0 * y1 < Image() )
      {
        auto m = ( y1 - y0 ) / ( x1 - x0 );
        auto c = y0 - m * x0;
        auto x = - c / m;

        intersections.insert( x );
      }

      current = next;
      next    = std::next( current );
    }

    for( auto&& x : intersections )
      _data.insert( std::make_pair( x, Image() ) );
  }

  /** Maps values in the domain to values in the image */
  std::map<Domain, Image> _data;
};

} // namespace math

} // namespace aleph

/** Multiplies a given piecewise linear function by a scalar value */
template <class D, class I, class T> aleph::math::PiecewiseLinearFunction<D, I> operator*( T lambda, const aleph::math::PiecewiseLinearFunction<D, I>& f )
{
  return f * lambda;
}

/** Divides a given piecewise linear function by a scalar value */
template <class D, class I, class T> aleph::math::PiecewiseLinearFunction<D, I> operator/( T lambda, const aleph::math::PiecewiseLinearFunction<D, I>& f )
{
  return f / lambda;
}

/**
  Output operator of a piecewise linear function. Permits printing the
  function values to an *output stream* in order to permit their usage
  by other functions and/or programs.
*/

template <class D, class I> std::ostream& operator<<( std::ostream& o, const aleph::math::PiecewiseLinearFunction<D, I>& f )
{
  std::vector<D> X;
  std::vector<I> Y;

  f.domain( std::back_inserter(X) );
  f.image ( std::back_inserter(Y) );

  assert( X.size() == Y.size() );

  for( std::size_t i = 0; i < X.size() && i < Y.size(); i++ )
    o << X[i] << "\t" << Y[i] << "\n";

  return o;
}

#endif
<|endoftext|>